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