/* Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler Distributed under MIT license, or public domain if desired and recognized in your jurisdiction. See file LICENSE for detail. */ #include "response.h" #include #include "configuration.h" CWF_BEGIN_NAMESPACE Response::Response(QTcpSocket &socket, const Configuration &configuration) : socket(socket) , configuration(configuration) {} static void sendBytes(QTcpSocket &socket, const QByteArray &text, int timeOut) { const QAbstractSocket::SocketState &state = socket.state(); if (state == QAbstractSocket::ConnectedState && text.size() > 0) { socket.write(text, text.size()); socket.flush(); if (state == QAbstractSocket::ConnectedState) { socket.waitForBytesWritten(timeOut); } } } static void buildHeadersString(QByteArray &temp, const QMap &headers) { QList headersList(headers.keys()); for (const auto &i : headersList) { temp.push_back(i); temp.push_back(HTTP::SEPARATOR); temp.push_back(headers.value(i)); temp.push_back(HTTP::END_LINE); } } static void buildCookiesString(QByteArray &temp, const QVector &cookies) { for (const auto &i : cookies) { temp.push_back(HTTP::SET_COOKIE); temp.push_back(i.toRawForm()); temp.push_back(HTTP::END_LINE); } } static void sendHeaders(int statusCode, int timeOut, const QByteArray &statusText, QMap &headers, QVector &cookies, QTcpSocket &socket) { QByteArray temp(HTTP::HTTP_1_1); temp.reserve(100); temp.push_back(QByteArray::number(statusCode)); temp.push_back(' '); temp.push_back(statusText); temp.push_back(HTTP::END_LINE); if (!headers.contains(HTTP::CONTENT_TYPE)) { headers.insert(HTTP::CONTENT_TYPE, HTTP::TEXT_HTML_UTF8); } buildHeadersString(temp, headers); buildCookiesString(temp, cookies); temp.push_back(HTTP::END_LINE); sendBytes(socket, temp, timeOut); } void Response::flushBuffer() { const int max = 32768; if (!content.isEmpty()) { static QLocale englishLocale(QLocale::English); int timeOut = configuration.getTimeOut(); bool biggerThanLimit = content.size() > max; headers.insert(HTTP::CONTENT_LENGTH, QByteArray::number(content.size())); headers.insert(HTTP::SERVER, HTTP::SERVER_VERSION); headers.insert(HTTP::DATA, QByteArray( englishLocale .toString(QDateTime::currentDateTime(), "ddd, dd MMM yyyy hh:mm:ss") .toUtf8() + " GMT")); if (!biggerThanLimit) { sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket); sendBytes(socket, content, timeOut); } else { headers.insert(HTTP::TRANSFER_ENCODING, HTTP::CHUNKED); sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket); int total = (content.size() / max) + 1, last = 0; QVector vetor; for (int i = 0; i < total; ++i) { vetor.push_back(content.mid(last, max)); last += max; } for (auto &i : vetor) { QByteArray data(std::move(i)); if (!data.isEmpty()) { sendBytes(socket, (QByteArray::number(data.size(), 16) + HTTP::END_LINE), timeOut); sendBytes(socket, data, timeOut); sendBytes(socket, HTTP::END_LINE, timeOut); } } sendBytes(socket, HTTP::END_OF_MESSAGE_WITH_ZERO, timeOut); } socket.disconnectFromHost(); // content.clear(); } } void Response::sendError(int sc, const QByteArray &msg) { int timeOut = configuration.getTimeOut(); sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket); sendBytes(socket, "

" + QByteArray::number(sc) + " " + msg + "

", timeOut); } void Response::sendJsonError(int sc, const QByteArray &msg) { int timeOut = configuration.getTimeOut(); sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket); QString data = QString(R"__({"code": 500, "data": -1, "message": "%1", "status": false})__") .arg((QString::number(sc) + " " + msg)); sendBytes(socket, data.toUtf8(), timeOut); } void Response::write(const QJsonObject &json, bool writeContentType) { if (writeContentType) addHeader(CWF::HTTP::CONTENT_TYPE, CWF::HTTP::APPLICATION_JSON); content = QJsonDocument(json).toJson(); flushBuffer(); } void Response::write(const QJsonArray &array, bool writeContentType) { if (writeContentType) addHeader(CWF::HTTP::CONTENT_TYPE, CWF::HTTP::APPLICATION_JSON); content = QJsonDocument(array).toJson(); flushBuffer(); } void Response::write(QByteArray &&data) { content = std::move(data); flushBuffer(); } void Response::write(const QByteArray &data, bool flush) { content += data; if (flush) flushBuffer(); } void Response::setStatus(int statusCode, const QByteArray &description) { this->statusCode = statusCode; statusText = description; } void Response::sendRedirect(const QByteArray &url) { setStatus(Response::SC_SEE_OTHER, HTTP::SEE_OTHER); addHeader(HTTP::LOCATION, url); write(HTTP::REDIRECT, true); } CWF_END_NAMESPACE