response.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
  3. Distributed under MIT license, or public domain if desired and
  4. recognized in your jurisdiction.
  5. See file LICENSE for detail.
  6. */
  7. #include "response.h"
  8. #include <QDateTime>
  9. #include "configuration.h"
  10. CWF_BEGIN_NAMESPACE
  11. Response::Response(QTcpSocket &socket, const Configuration &configuration)
  12. : socket(socket)
  13. , configuration(configuration)
  14. {}
  15. static void sendBytes(QTcpSocket &socket, const QByteArray &text, int timeOut)
  16. {
  17. const QAbstractSocket::SocketState &state = socket.state();
  18. if (state == QAbstractSocket::ConnectedState && text.size() > 0) {
  19. socket.write(text, text.size());
  20. socket.flush();
  21. if (state == QAbstractSocket::ConnectedState) {
  22. socket.waitForBytesWritten(timeOut);
  23. }
  24. }
  25. }
  26. static void buildHeadersString(QByteArray &temp, const QMap<QByteArray, QByteArray> &headers)
  27. {
  28. QList<QByteArray> headersList(headers.keys());
  29. for (const auto &i : headersList) {
  30. temp.push_back(i);
  31. temp.push_back(HTTP::SEPARATOR);
  32. temp.push_back(headers.value(i));
  33. temp.push_back(HTTP::END_LINE);
  34. }
  35. }
  36. static void buildCookiesString(QByteArray &temp, const QVector<QNetworkCookie> &cookies)
  37. {
  38. for (const auto &i : cookies) {
  39. temp.push_back(HTTP::SET_COOKIE);
  40. temp.push_back(i.toRawForm());
  41. temp.push_back(HTTP::END_LINE);
  42. }
  43. }
  44. static void sendHeaders(int statusCode,
  45. int timeOut,
  46. const QByteArray &statusText,
  47. QMap<QByteArray, QByteArray> &headers,
  48. QVector<QNetworkCookie> &cookies,
  49. QTcpSocket &socket)
  50. {
  51. QByteArray temp(HTTP::HTTP_1_1);
  52. temp.reserve(100);
  53. temp.push_back(QByteArray::number(statusCode));
  54. temp.push_back(' ');
  55. temp.push_back(statusText);
  56. temp.push_back(HTTP::END_LINE);
  57. if (!headers.contains(HTTP::CONTENT_TYPE)) {
  58. headers.insert(HTTP::CONTENT_TYPE, HTTP::TEXT_HTML_UTF8);
  59. }
  60. buildHeadersString(temp, headers);
  61. buildCookiesString(temp, cookies);
  62. temp.push_back(HTTP::END_LINE);
  63. sendBytes(socket, temp, timeOut);
  64. }
  65. void Response::flushBuffer()
  66. {
  67. const int max = 32768;
  68. if (!content.isEmpty()) {
  69. static QLocale englishLocale(QLocale::English);
  70. int timeOut = configuration.getTimeOut();
  71. bool biggerThanLimit = content.size() > max;
  72. headers.insert(HTTP::CONTENT_LENGTH, QByteArray::number(content.size()));
  73. headers.insert(HTTP::SERVER, HTTP::SERVER_VERSION);
  74. headers.insert(HTTP::DATA,
  75. QByteArray(
  76. englishLocale
  77. .toString(QDateTime::currentDateTime(), "ddd, dd MMM yyyy hh:mm:ss")
  78. .toUtf8()
  79. + " GMT"));
  80. if (!biggerThanLimit) {
  81. sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket);
  82. sendBytes(socket, content, timeOut);
  83. } else {
  84. headers.insert(HTTP::TRANSFER_ENCODING, HTTP::CHUNKED);
  85. sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket);
  86. int total = (content.size() / max) + 1, last = 0;
  87. QVector<QByteArray> vetor;
  88. for (int i = 0; i < total; ++i) {
  89. vetor.push_back(content.mid(last, max));
  90. last += max;
  91. }
  92. for (auto &i : vetor) {
  93. QByteArray data(std::move(i));
  94. if (!data.isEmpty()) {
  95. sendBytes(socket,
  96. (QByteArray::number(data.size(), 16) + HTTP::END_LINE),
  97. timeOut);
  98. sendBytes(socket, data, timeOut);
  99. sendBytes(socket, HTTP::END_LINE, timeOut);
  100. }
  101. }
  102. sendBytes(socket, HTTP::END_OF_MESSAGE_WITH_ZERO, timeOut);
  103. }
  104. socket.disconnectFromHost();
  105. // content.clear();
  106. }
  107. }
  108. void Response::sendError(int sc, const QByteArray &msg)
  109. {
  110. int timeOut = configuration.getTimeOut();
  111. sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket);
  112. sendBytes(socket,
  113. "<html><body><h1>" + QByteArray::number(sc) + " " + msg + "</h1></body></html>",
  114. timeOut);
  115. }
  116. void Response::sendJsonError(int sc, const QByteArray &msg)
  117. {
  118. int timeOut = configuration.getTimeOut();
  119. sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket);
  120. QString data = QString(R"__({"code": 500, "data": -1, "message": "%1", "status": false})__")
  121. .arg((QString::number(sc) + " " + msg));
  122. sendBytes(socket, data.toUtf8(), timeOut);
  123. }
  124. void Response::write(const QJsonObject &json, bool writeContentType)
  125. {
  126. if (writeContentType)
  127. addHeader(CWF::HTTP::CONTENT_TYPE, CWF::HTTP::APPLICATION_JSON);
  128. content = QJsonDocument(json).toJson();
  129. flushBuffer();
  130. }
  131. void Response::write(const QJsonArray &array, bool writeContentType)
  132. {
  133. if (writeContentType)
  134. addHeader(CWF::HTTP::CONTENT_TYPE, CWF::HTTP::APPLICATION_JSON);
  135. content = QJsonDocument(array).toJson();
  136. flushBuffer();
  137. }
  138. void Response::write(QByteArray &&data)
  139. {
  140. content = std::move(data);
  141. flushBuffer();
  142. }
  143. void Response::write(const QByteArray &data, bool flush)
  144. {
  145. content += data;
  146. if (flush)
  147. flushBuffer();
  148. }
  149. void Response::setStatus(int statusCode, const QByteArray &description)
  150. {
  151. this->statusCode = statusCode;
  152. statusText = description;
  153. }
  154. void Response::sendRedirect(const QByteArray &url)
  155. {
  156. setStatus(Response::SC_SEE_OTHER, HTTP::SEE_OTHER);
  157. addHeader(HTTP::LOCATION, url);
  158. write(HTTP::REDIRECT, true);
  159. }
  160. CWF_END_NAMESPACE