1 // This file is part of Qt Bitcoin Trader
2 // https://github.com/JulyIGHOR/QtBitcoinTrader
3 // Copyright (C) 2013-2021 July Ighor <julyighor@gmail.com>
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // In addition, as a special exception, the copyright holders give
11 // permission to link the code of portions of this program with the
12 // OpenSSL library under certain conditions as described in each
13 // individual source file, and distribute linked combinations including
14 // the two.
15 //
16 // You must obey the GNU General Public License in all respects for all
17 // of the code used other than OpenSSL. If you modify file(s) with this
18 // exception, you may extend this exception to your version of the
19 // file(s), but you are not obligated to do so. If you do not wish to do
20 // so, delete this exception statement from your version. If you delete
21 // this exception statement from all source files in the program, then
22 // also delete it here.
23 //
24 // This program is distributed in the hope that it will be useful,
25 // but WITHOUT ANY WARRANTY; without even the implied warranty of
26 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 // GNU General Public License for more details.
28 //
29 // You should have received a copy of the GNU General Public License
30 // along with this program. If not, see <http://www.gnu.org/licenses/>.
31
32 #include "julyhttp.h"
33 #include "main.h"
34 #include <QTimer>
35 #include <zlib.h>
36 #include <QFile>
37 #include <QMutex>
38 #include <QWaitCondition>
39
40 #ifdef Q_OS_WIN
41 #include <winsock2.h>
42 #else
43 #include <sys/socket.h>
44 #include <fcntl.h>
45 #include <netdb.h>
46 #include <net/if.h>
47 #endif
48
JulyHttp(const QString & hostN,const QByteArray & restLine,QObject * parent,const bool & secure,const bool & keepAlive,const QByteArray & contentType)49 JulyHttp::JulyHttp(const QString& hostN, const QByteArray& restLine, QObject* parent, const bool& secure,
50 const bool& keepAlive, const QByteArray& contentType)
51 : QSslSocket(parent),
52 ignoreError(false),
53 waitForReadyReadCount(0)
54 {
55 destroyClass = false;
56 noReconnect = false;
57 forcedPort = 0U;
58 noReconnectCount = 0;
59 secureConnection = secure;
60 isDataPending = false;
61 httpState = 999;
62 connectionClose = false;
63 bytesDone = 0;
64 contentLength = 0;
65 chunkedSize = -1;
66 readingHeader = false;
67 waitingReplay = false;
68 isDisabled = false;
69 outGoingPacketsCount = 0;
70 contentGzipped = false;
71
72 setupSocket();
73
74 requestTimeOut.restart();
75 hostName = hostN;
76 httpHeader.append(" HTTP/1.1\r\n");
77
78 if (baseValues.customUserAgent.length() > 0)
79 httpHeader.append("User-Agent: " + baseValues.customUserAgent + "\r\n");
80 else
81 httpHeader.append("User-Agent: Qt Bitcoin Trader v" + baseValues.appVerStr + "\r\n");
82
83 httpHeader.append("Host: " + hostName + "\r\n");
84 httpHeader.append("Accept: */*\r\n");
85
86 if (baseValues.gzipEnabled)
87 httpHeader.append("Accept-Encoding: gzip\r\n");
88
89 contentTypeLine = "Content-Type: " + contentType + "\r\n";
90
91 if (keepAlive)
92 httpHeader.append("Connection: keep-alive\r\n");
93 else
94 httpHeader.append("Connection: close\r\n");
95
96 apiDownState = false;
97 apiDownCounter = 0;
98 restKeyLine = restLine;
99
100 if (baseValues.customCookies.length() > 0)
101 {
102 baseValues.customCookies.append(" ");
103 QStringList cookieListStr = baseValues.customCookies.split("; ");
104
105 for (int n = 0; n < cookieListStr.size(); n++)
106 {
107 QStringList nameValue = cookieListStr.at(n).split("=");
108
109 if (nameValue.size() != 2 || nameValue.first().isEmpty() || nameValue.last().isEmpty())
110 continue;
111
112 cookiesMap.insert(nameValue.first().toLatin1(), nameValue.last().toLatin1());
113 }
114 }
115
116 saveCookies();
117
118 secondTimer = new QTimer(this);
119 connect(secondTimer, SIGNAL(timeout()), this, SLOT(sendPendingData()));
120 secondTimer->start(300);
121 }
122
~JulyHttp()123 JulyHttp::~JulyHttp()
124 {
125 delete secondTimer;
126 abortSocket();
127 }
128
setupSocket()129 void JulyHttp::setupSocket()
130 {
131 // static QMutex mutex;
132 // mutex.lock();
133 // static QList<QSslCertificate> certs;
134
135 // if (certs.size() == 0)
136 // {
137 // QFile readCerts(":/Resources/CertBase.cer");
138
139 // if (readCerts.open(QIODevice::ReadOnly))
140 // {
141 // QByteArray certData = readCerts.readAll() + "{SPLIT}";
142 // readCerts.close();
143
144 // do
145 // {
146 // int nextCert = certData.indexOf("{SPLIT}");
147
148 // if (nextCert > -1)
149 // {
150 // QByteArray currentCert = certData.left(nextCert);
151 // QSslCertificate derCert(currentCert, QSsl::Der);
152
153 // if (!derCert.isNull())
154 // certs << derCert;
155
156 // certData.remove(0, nextCert + 7);
157 // }
158 // else
159 // certData.clear();
160 // }
161 // while (certData.size());
162 // }
163 // }
164
165 // setCaCertificates(certs);
166
167 setPeerVerifyMode(QSslSocket::VerifyPeer);
168 connect(this, SIGNAL(readyRead()), SLOT(readSocket()));
169 connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorSlot(QAbstractSocket::SocketError)), Qt::QueuedConnection);
170 connect(this, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(sslErrorsSlot(const QList<QSslError>&)), Qt::QueuedConnection);
171
172 // mutex.unlock();
173 }
174
clearPendingData()175 void JulyHttp::clearPendingData()
176 {
177 for (int n = requestList.size() - 1; n >= 0; n--)
178 takeRequestAt(n);
179
180 reConnect();
181 }
182
reConnect(bool forceAbort)183 void JulyHttp::reConnect(bool forceAbort)
184 {
185 if (isDisabled)
186 return;
187
188 reconnectSocket(forceAbort);
189 }
190
abortSocket()191 void JulyHttp::abortSocket()
192 {
193 blockSignals(true);
194 abort();
195 blockSignals(false);
196 }
197
reconnectSocket(bool forceAbort)198 void JulyHttp::reconnectSocket(bool forceAbort)
199 {
200 if (destroyClass)
201 return;//{qDebug("delete reconnectSocket1"); delete this; qDebug("delete reconnectSocket2");}
202
203 if (isDisabled)
204 return;
205
206 if (forceAbort)
207 abortSocket();
208
209 if (state() == QAbstractSocket::UnconnectedState)
210 {
211 if (secureConnection)
212 connectToHostEncrypted(hostName, forcedPort ? forcedPort : 443, QIODevice::ReadWrite);
213 else
214 connectToHost(hostName, forcedPort ? forcedPort : 80, QIODevice::ReadWrite);
215
216 waitForConnected(((noReconnect && noReconnectCount++ > 5) ? 1000 : baseValues.httpRequestTimeout));
217
218 #ifdef Q_OS_WIN
219 setsockopt(static_cast<SOCKET>(this->socketDescriptor()),
220 SOL_SOCKET, SO_RCVTIMEO,
221 (const char*)&baseValues.httpRequestTimeout, sizeof(int));
222 #else
223 struct timeval vtime;
224 vtime.tv_sec = baseValues.httpRequestTimeout / 1000;
225 vtime.tv_usec = baseValues.httpRequestTimeout * 1000 - vtime.tv_sec * 1000000;
226 setsockopt(this->socketDescriptor(), SOL_SOCKET, SO_RCVTIMEO, &vtime, sizeof(struct timeval));
227 #endif
228 }
229 }
230
setApiDown(bool httpError)231 void JulyHttp::setApiDown(bool httpError)
232 {
233 if (httpError)
234 apiDownCounter++;
235 else
236 apiDownCounter = 0;
237
238 bool currentApiDownState = apiDownCounter > baseValues.apiDownCount;
239
240 if (apiDownState != currentApiDownState)
241 {
242 apiDownState = currentApiDownState;
243 emit apiDown(apiDownState);
244 }
245 }
246
saveCookies()247 void JulyHttp::saveCookies()
248 {
249 cookieLine.clear();
250 int cookiesCount = 0;
251
252 for (const QByteArray& name : cookiesMap.keys())
253 {
254 if (cookiesCount > 0)
255 cookieLine.append("; ");
256
257 cookieLine.append(name + "=" + cookiesMap.value(name));
258 cookiesCount++;
259 }
260
261 if (!cookieLine.isEmpty())
262 {
263 cookieLine.prepend("Cookie: ");
264 cookieLine.append("\r\n");
265 }
266 }
267
readSocket()268 void JulyHttp::readSocket()
269 {
270 if (isDisabled || requestList.isEmpty())
271 return;
272
273 requestTimeOut.restart();
274
275 if (!waitingReplay)
276 {
277 httpState = 999;
278 bytesDone = 0;
279 connectionClose = false;
280 buffer.clear();
281 contentGzipped = false;
282 waitingReplay = true;
283 readingHeader = true;
284 contentLength = 0;
285 }
286
287 bool lineReaded = false;
288
289 while (readingHeader)
290 {
291 bool endFound = false;
292 QByteArray currentLine;
293
294 while (!endFound && canReadLine())
295 {
296 lineReaded = true;
297
298 if (outBuffer.size())
299 {
300 currentLine = outBuffer + readLine();
301 outBuffer.clear();
302 }
303 else
304 currentLine = readLine();
305
306 if (currentLine == "\r\n" || currentLine == "\n" || currentLine.isEmpty())
307 endFound = true;
308 else
309 {
310 QString currentLineLow = currentLine.toLower();
311
312 if (currentLineLow.startsWith("http/1.1 "))
313 {
314 if (currentLineLow.length() > 12)
315 httpState = currentLineLow.midRef(9, 3).toInt();
316
317 if (debugLevel && httpState != 200)
318 {
319 logThread->writeLog(currentLine + readAll());
320 takeFirstRequest();
321 clearRequest();
322 return;
323 }
324 }
325 else if (currentLineLow.startsWith("set-cookie"))
326 {
327 int cookieNameEnds = currentLine.indexOf(";");
328
329 if (cookieNameEnds == -1)
330 cookieNameEnds = currentLine.size() - 2;
331
332 if (cookieNameEnds > 13)
333 {
334 int cookieSplitPos = currentLine.indexOf("=");
335
336 if (cookieSplitPos != -1 && 0 < cookieSplitPos - 12 && 0 < cookieNameEnds - cookieSplitPos)
337 {
338 QByteArray cookieName = currentLine.mid(12, cookieSplitPos - 12);
339 QByteArray cookieValue = currentLine.mid(cookieSplitPos + 1, cookieNameEnds - cookieSplitPos - 1);
340
341 if (cookiesMap.value(cookieName) != cookieValue)
342 {
343 if (cookieValue.isEmpty())
344 cookiesMap.remove(cookieName);
345 else
346 cookiesMap[cookieName] = cookieValue;
347
348 saveCookies();
349 }
350 }
351 }
352 }
353 else if (currentLineLow.startsWith("transfer-encoding") &&
354 currentLineLow.endsWith("chunked\r\n"))
355 chunkedSize = 0;
356 else if (currentLineLow.startsWith(QLatin1String("content-length")))
357 {
358 QStringList pairList = currentLineLow.split(":");
359
360 if (pairList.size() == 2)
361 contentLength = pairList.last().trimmed().toUInt();
362 }
363 else if (currentLineLow.startsWith(QLatin1String("connection")) &&
364 currentLineLow.endsWith(QLatin1String("close\r\n")))
365 connectionClose = true;
366 else if (currentLineLow.startsWith(QLatin1String("content-encoding")) &&
367 currentLineLow.contains(QLatin1String("gzip")))
368 contentGzipped = true;
369 }
370 }
371
372 if (!endFound)
373 {
374 if (!lineReaded)
375 {
376 if (outBuffer.size() > 30000)
377 outBuffer.clear();
378
379 outBuffer.append(readAll());
380 return;
381 }
382
383 ++waitForReadyReadCount;
384
385 if (waitForReadyReadCount > baseValues.httpRetryCount)
386 retryRequest();
387
388 return;
389 }
390
391 waitForReadyReadCount = 0;
392 readingHeader = false;
393 }
394
395 if (httpState < 400)
396 emit anyDataReceived();
397
398 bool allDataReaded = false;
399
400 qint64 readSize = bytesAvailable();
401
402 addSpeedSize(readSize);
403
404 QScopedPointer<QByteArray> dataArray;
405
406 if (chunkedSize != -1)
407 {
408 while (true)
409 {
410 if (chunkedSize == 0)
411 {
412 if (!canReadLine())
413 break;
414
415 QString sizeString = readLine();
416 int tPos = sizeString.indexOf(QLatin1Char(';'));
417
418 if (tPos != -1)
419 sizeString.truncate(tPos);
420
421 bool ok;
422 chunkedSize = sizeString.toInt(&ok, 16);
423
424 if (!ok)
425 {
426 if (debugLevel)
427 logThread->writeLog("Invalid size", 2);
428
429 retryRequest();
430 return;
431 }
432
433 if (chunkedSize == 0)
434 chunkedSize = -2;
435 }
436
437 while (chunkedSize == -2 && canReadLine())
438 {
439 QString currentLine = readLine();
440
441 if (currentLine == QLatin1String("\r\n") ||
442 currentLine == QLatin1String("\n"))
443 chunkedSize = -1;
444 }
445
446 if (chunkedSize == -1)
447 {
448 allDataReaded = true;
449 break;
450 }
451
452 readSize = bytesAvailable();
453
454 if (readSize == 0)
455 break;
456
457 if (readSize == chunkedSize || readSize == chunkedSize + 1)
458 {
459 readSize = chunkedSize - 1;
460
461 if (readSize == 0)
462 break;
463 }
464
465 qint64 bytesToRead = chunkedSize < 0 ? readSize : qMin(readSize, chunkedSize);
466
467 if (!dataArray)
468 dataArray.reset(new QByteArray);
469
470 qint64 oldDataSize = dataArray->size();
471 dataArray->resize(static_cast<int>(oldDataSize + bytesToRead));
472 qint64 read = this->read(dataArray->data() + oldDataSize, bytesToRead);
473 dataArray->resize(static_cast<int>(oldDataSize + read));
474
475 chunkedSize -= read;
476
477 if (chunkedSize == 0 && readSize - read >= 2)
478 {
479 char twoBytes[2];
480 this->read(twoBytes, 2);
481
482 if (twoBytes[0] != '\r' || twoBytes[1] != '\n')
483 {
484 if (debugLevel)
485 logThread->writeLog("Invalid HTTP chunked body", 2);
486
487 retryRequest();
488 return;
489 }
490 }
491 }
492 }
493 else if (contentLength > 0)
494 {
495 readSize = qMin(qint64(contentLength - bytesDone), readSize);
496
497 if (readSize > 0)
498 {
499 dataArray.reset(new QByteArray);
500 dataArray->resize(static_cast<int>(readSize));
501 dataArray->resize(static_cast<int>(read(dataArray->data(), readSize)));
502 }
503
504 if (bytesDone +/* bytesAvailable() + */readSize == contentLength)
505 allDataReaded = true;
506 }
507 else if (readSize > 0)
508 {
509 if (!dataArray)
510 {
511 dataArray.reset(new QByteArray);
512 dataArray->resize(static_cast<int>(readSize));
513 dataArray->resize(static_cast<int>(read(dataArray->data(), readSize)));
514 }
515 }
516
517 if (dataArray)
518 {
519 readSize = dataArray->size();
520
521 if (readSize > 0)
522 buffer.append(*dataArray);
523
524 dataArray.reset();
525
526 if (contentLength > 0)
527 {
528 bytesDone += readSize;
529 emit dataProgress(static_cast<int>(100 * bytesDone / contentLength));
530 }
531 }
532
533 dataArray.reset();
534
535 if (allDataReaded)
536 {
537 if (!buffer.isEmpty() && !requestList.empty())
538 {
539 if (contentGzipped)
540 gzipUncompress(&buffer);
541
542 bool apiMaybeDown = buffer[0] == '<';
543 setApiDown(apiMaybeDown);
544
545 if (debugLevel && buffer.isEmpty())
546 logThread->writeLog("Response is EMPTY", 2);
547
548 emit dataReceived(buffer, requestList.first().reqType, requestList.first().pairChangeCount);
549 }
550
551 waitingReplay = false;
552 readingHeader = true;
553
554 if (!requestList.empty())
555 requestList[0].retryCount = 0;
556
557 takeFirstRequest();
558 clearRequest();
559
560 if (connectionClose)
561 {
562 if (debugLevel)
563 logThread->writeLog("HTTP: connection closed");
564
565 reConnect(true);
566 }
567
568 sendPendingData();
569 }
570 }
571
gzipUncompress(QByteArray * data)572 void JulyHttp::gzipUncompress(QByteArray* data)
573 {
574 if (data->size() <= 4)
575 {
576 if (debugLevel)
577 logThread->writeLog("GZIP: Input data is truncated", 2);
578
579 return;
580 }
581
582 QByteArray result;
583
584 static const int CHUNK_SIZE = 1024;
585 char out[CHUNK_SIZE];
586
587 z_stream strm;
588 strm.zalloc = nullptr;
589 strm.zfree = nullptr;
590 strm.opaque = nullptr;
591 strm.avail_in = static_cast<unsigned int>(data->size());
592 strm.next_in = (Bytef*)(data->data());
593
594 int ret = inflateInit2(&strm, 47);
595
596 if (ret != Z_OK)
597 return;
598
599 do
600 {
601 strm.avail_out = CHUNK_SIZE;
602 strm.next_out = (Bytef*)(out);
603
604 ret = inflate(&strm, Z_NO_FLUSH);
605 Q_ASSERT(ret != Z_STREAM_ERROR);
606
607 switch (ret)
608 {
609 case Z_NEED_DICT:
610 ret = Z_DATA_ERROR;
611 break;
612
613 case Z_DATA_ERROR:
614 case Z_MEM_ERROR:
615 (void)inflateEnd(&strm);
616 return;
617 }
618
619 result.append(out, CHUNK_SIZE - static_cast<int>(strm.avail_out));
620 }
621 while (strm.avail_out == 0);
622
623 inflateEnd(&strm);
624 (*data) = result;
625 }
626
isReqTypePending(int val)627 bool JulyHttp::isReqTypePending(int val)
628 {
629 return reqTypePending.value(val, 0) > 0;
630 }
631
retryRequest()632 void JulyHttp::retryRequest()
633 {
634 if (isDisabled || requestList.isEmpty())
635 return;
636
637 if (requestList.first().retryCount <= 0)
638 takeFirstRequest();
639 else
640 {
641 if (debugLevel)
642 logThread->writeLog("Warning: Request resent due timeout", 2);
643
644 requestList[0].retryCount--;
645 }
646
647 sendPendingData();
648 }
649
clearRequest()650 void JulyHttp::clearRequest()
651 {
652 buffer.clear();
653 chunkedSize = -1;
654 nextPacketMustBeSize = false;
655 endOfPacket = false;
656 }
657
prepareData(int reqType,int pairChangeCount,const QByteArray & method,QByteArray postData,const QByteArray & restSignLine,const int & forceRetryCount)658 void JulyHttp::prepareData(int reqType, int pairChangeCount, const QByteArray& method, QByteArray postData,
659 const QByteArray& restSignLine, const int& forceRetryCount)
660 {
661 if (isDisabled)
662 return;
663
664 auto* data = new QByteArray(method + httpHeader + cookieLine);
665
666 if (!restSignLine.isEmpty())
667 data->append(restKeyLine + restSignLine);
668
669 if (!postData.isEmpty())
670 {
671 data->append(contentTypeLine);
672 data->append("Content-Length: " + QByteArray::number(postData.size()) + "\r\n\r\n");
673 data->append(postData);
674 }
675
676 PacketItem newPacket;
677 newPacket.data = data;
678 newPacket.reqType = reqType;
679 newPacket.pairChangeCount = pairChangeCount;
680 newPacket.retryCount = 0;
681 newPacket.skipOnce = false;
682
683 if (forceRetryCount == -1)
684 {
685 if (reqType > 300)
686 newPacket.retryCount = baseValues.httpRetryCount - 1;
687 }
688 else
689 newPacket.retryCount = forceRetryCount;
690
691 reqTypePending[reqType] = reqTypePending.value(reqType, 0) + 1;
692
693 preparedList << newPacket;
694 }
695
prepareDataSend()696 void JulyHttp::prepareDataSend()
697 {
698 if (isDisabled || preparedList.isEmpty())
699 return;
700
701 for (int n = 1; n < preparedList.size(); n++)
702 {
703 preparedList[0].data->append(*(preparedList[n].data)) + "\r\n\r\n";
704 preparedList[n].skipOnce = true;
705 }
706
707 for (int n = 0; n < preparedList.size(); n++)
708 requestList << preparedList.at(n);
709
710 preparedList.clear();
711
712 if (!isDataPending)
713 {
714 emit setDataPending(true);
715 isDataPending = true;
716 }
717 }
718
prepareDataClear()719 void JulyHttp::prepareDataClear()
720 {
721 if (isDisabled)
722 return;
723
724 for (int n = 0; n < preparedList.size(); n++)
725 {
726 PacketItem preparingPacket = preparedList.at(n);
727 reqTypePending[preparingPacket.reqType] = reqTypePending.value(preparingPacket.reqType, 1) - 1;
728
729
730 delete preparingPacket.data;
731 }
732
733 preparedList.clear();
734 }
735
sendData(int reqType,int pairChangeCount,const QByteArray & method,QByteArray postData,const QByteArray & restSignLine,const int & forceRetryCount)736 void JulyHttp::sendData(int reqType, int pairChangeCount, const QByteArray& method, QByteArray postData,
737 const QByteArray& restSignLine, const int& forceRetryCount)
738 {
739 if (isDisabled)
740 return;
741
742 auto* data = new QByteArray(method + httpHeader + cookieLine);
743
744 if (!restSignLine.isEmpty())
745 data->append(restKeyLine + restSignLine);
746
747 if (!postData.isEmpty())
748 {
749 data->append(contentTypeLine);
750 data->append("Content-Length: " + QByteArray::number(postData.size()) + "\r\n\r\n");
751 data->append(postData);
752 }
753 else
754 data->append("\r\n");
755
756 if (reqType > 300)
757 for (int n = requestList.size() - 1; n >= 1; n--)
758 if (requestList.at(n).reqType < 300 && !requestList[n].skipOnce)
759 takeRequestAt(n);
760
761 PacketItem newPacket;
762 newPacket.data = data;
763 newPacket.reqType = reqType;
764 newPacket.pairChangeCount = pairChangeCount;
765 newPacket.retryCount = 0;
766 newPacket.skipOnce = false;
767
768 if (forceRetryCount == -1)
769 {
770 if (reqType > 300)
771 newPacket.retryCount = baseValues.httpRetryCount - 1;
772 else
773 newPacket.retryCount = 0;
774 }
775 else
776 newPacket.retryCount = forceRetryCount;
777
778 if (newPacket.retryCount > 0 && debugLevel && newPacket.reqType > 299)
779 logThread->writeLog("Added to Query RetryCount=" + QByteArray::number(newPacket.retryCount), 2);
780
781 requestList << newPacket;
782
783 if (!isDataPending)
784 {
785 emit setDataPending(true);
786 isDataPending = true;
787 }
788
789 reqTypePending[reqType] = reqTypePending.value(reqType, 0) + 1;
790 sendPendingData();
791 }
792
takeRequestAt(int pos)793 void JulyHttp::takeRequestAt(int pos)
794 {
795 if (requestList.size() <= pos)
796 return;
797
798 if (pos == 0)
799 currentPendingRequest = nullptr;
800
801 PacketItem packetTake = requestList.at(pos);
802 reqTypePending[packetTake.reqType] = reqTypePending.value(packetTake.reqType, 1) - 1;
803
804 delete packetTake.data;
805 packetTake.data = nullptr;
806 requestList.removeAt(pos);
807
808 if (requestList.isEmpty())
809 {
810 reqTypePending.clear();
811
812 if (isDataPending)
813 {
814 emit setDataPending(false);
815 isDataPending = false;
816 }
817 }
818 }
819
takeFirstRequest()820 void JulyHttp::takeFirstRequest()
821 {
822 if (requestList.isEmpty())
823 return;
824
825 takeRequestAt(0);
826 }
827
errorSlot(QAbstractSocket::SocketError socketError)828 void JulyHttp::errorSlot(QAbstractSocket::SocketError socketError)
829 {
830 if (ignoreError)
831 return;
832
833 if (noReconnect && noReconnectCount++ > 5)
834 {
835 isDisabled = true;
836 return;
837 }
838
839 if (socketError != QAbstractSocket::RemoteHostClosedError &&
840 socketError != QAbstractSocket::UnfinishedSocketOperationError)
841 setApiDown(true);
842
843 if (debugLevel)
844 logThread->writeLog("SocketError: " + errorString().toUtf8(), 2);
845
846 if (socketError == QAbstractSocket::ProxyAuthenticationRequiredError)
847 {
848 isDisabled = true;
849 emit errorSignal(errorString());
850 abortSocket();
851 }
852 else
853 {
854 QMutex mutex;
855 mutex.lock();
856
857 QWaitCondition waitCondition;
858 waitCondition.wait(&mutex, 1000);
859
860 mutex.unlock();
861
862 reconnectSocket(false);
863 }
864 }
865
isSocketConnected()866 bool JulyHttp::isSocketConnected()
867 {
868 return state() == QAbstractSocket::ConnectedState;
869 }
870
sendPendingData()871 void JulyHttp::sendPendingData()
872 {
873 if (isDisabled || requestList.isEmpty())
874 return;
875
876 if (!isSocketConnected())
877 reconnectSocket(false);
878
879 if (state() != QAbstractSocket::UnconnectedState)
880 {
881 if (state() == QAbstractSocket::ConnectingState || state() == QAbstractSocket::HostLookupState)
882 waitForConnected(((noReconnect && noReconnectCount++ > 5) ? 1000 : baseValues.httpRequestTimeout + 1000));
883 }
884
885 if (!noReconnect)
886 {
887 if (!isSocketConnected())
888 {
889 setApiDown(true);
890
891 if (debugLevel)
892 logThread->writeLog("Socket state: " + errorString().toUtf8(), 2);
893
894 reconnectSocket(false);
895
896 if (state() == QAbstractSocket::ConnectingState)
897 waitForConnected(baseValues.httpRequestTimeout + 1000);
898 }
899 else
900 reconnectSocket(false);
901 }
902
903 if (!isSocketConnected())
904 return;
905
906 if (currentPendingRequest == requestList.first().data)
907 {
908 if (requestTimeOut.elapsed() < ((noReconnect && noReconnectCount++ > 5) ? 1000 : baseValues.httpRequestTimeout))
909 return;
910
911 if (debugLevel)
912 logThread->writeLog(QString("Request timeout: %0>%1").arg(requestTimeOut.elapsed()).arg(
913 baseValues.httpRequestTimeout).toLatin1(), 2);
914
915 reconnectSocket(true);
916 setApiDown(true);
917
918 if (requestList.first().retryCount > 0)
919 {
920 retryRequest();
921 return;
922 }
923
924 }
925 else
926 {
927 currentPendingRequest = requestList.first().data;
928
929 if (debugLevel && requestList.first().reqType > 299)
930 logThread->writeLog("Sending request ID: " + QByteArray::number(requestList.first().reqType), 2);
931 }
932
933 clearRequest();
934
935 requestTimeOut.restart();
936
937 if (debugLevel && currentPendingRequest)
938 logThread->writeLog("SND: " + QByteArray(*currentPendingRequest).replace(baseValues.restKey, "REST_KEY"));
939
940 if (currentPendingRequest)
941 {
942 if (requestList.first().skipOnce)
943 requestList[0].skipOnce = false;
944 else
945 {
946 if (bytesAvailable())
947 {
948 if (debugLevel)
949 logThread->writeLog("Cleared previous data: " + readAll());
950 else
951 readAll();
952 }
953
954 waitingReplay = false;
955 addSpeedSize(currentPendingRequest->size());
956 write(*currentPendingRequest);
957 flush();
958 }
959 }
960 else if (debugLevel)
961 logThread->writeLog("PendingRequest pointer not exist", 2);
962 }
963
addSpeedSize(qint64 size)964 void JulyHttp::addSpeedSize(qint64 size)
965 {
966 baseValues.trafficSpeed += size;
967 }
968
sslErrorsSlot(const QList<QSslError> & val)969 void JulyHttp::sslErrorsSlot(const QList<QSslError>& val)
970 {
971 emit sslErrorSignal(val);
972 }
973
requestWait(const QUrl & url,QByteArray & result,QString * errorString)974 bool JulyHttp::requestWait(const QUrl& url, QByteArray& result, QString* errorString)
975 {
976 if (errorString)
977 errorString->clear();
978
979 QNetworkAccessManager mng;
980
981 QNetworkRequest request(url);
982 request.setRawHeader("Accept", "*/*");
983 request.setRawHeader("Accept-Language", "en");
984 request.setHeader(QNetworkRequest::UserAgentHeader, QCoreApplication::applicationName());
985
986 QNetworkReply* reply = mng.get(request);
987
988 if (reply == nullptr)
989 return false;
990
991 QEventLoop loop;
992 QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
993 loop.exec();
994 result = reply->readAll();
995
996 if (result.isEmpty() && errorString)
997 *errorString = reply->errorString();
998
999 reply->deleteLater();
1000
1001 return !result.isEmpty();
1002 }
1003