#include #include #include #include #include #include #include #include #include #include #include #include namespace httq { HttpRequest::HttpRequest(QTcpSocket *cli, AbstractServer *parent) : QObject(parent) , mCli(cli) , mLogger(parent->getLoggerFactory()->createLogger(this)) { mLogger->debug(QStringLiteral("HttpRequest c_tor")); http_parser_init(&mParser, HTTP_REQUEST); mData.mHttpRquest = this; mParser.data = static_cast(&mData); connect(cli, &QTcpSocket::readyRead, this, &HttpRequest::slotReadyRead); connect(cli, &QTcpSocket::disconnected, this, [this]() { qWarning() << "Client disconnected himself!!"; }); connect(cli, &QTcpSocket::disconnected, this, &HttpRequest::deleteLater); mParserSettings.on_message_begin = &HttpRequest::on_message_begin; mParserSettings.on_url = &HttpRequest::on_url; mParserSettings.on_status = &HttpRequest::on_status; mParserSettings.on_header_field = &HttpRequest::on_header_field; mParserSettings.on_header_value = &HttpRequest::on_header_value; mParserSettings.on_headers_complete = &HttpRequest::on_headers_complete; mParserSettings.on_body = &HttpRequest::on_body; mParserSettings.on_message_complete = &HttpRequest::on_message_complete; mParserSettings.on_chunk_header = &HttpRequest::on_chunk_header; mParserSettings.on_chunk_complete = &HttpRequest::on_chunk_complete; } HttpRequest::~HttpRequest() { mLogger->debug(QStringLiteral("~HttpRequest")); if (mCli == nullptr) return; mLogger->debug(QStringLiteral("mCli flush & close")); mCli->flush(); } QString HttpRequest::toString() const { return QStringLiteral("HttpRequest(url = '%1', method = %2)").arg(url().toString()).arg(methodString()); } int HttpRequest::on_message_begin(http_parser *parser) { dataLogger(parser)->debug(QStringLiteral("on_message_begin")); return 0; } int HttpRequest::on_url(http_parser *parser, const char *at, size_t length) { dataLogger(parser)->debug(QStringLiteral("on_url '%1'").arg(QString::fromUtf8(at, length))); http_parser_url parserUrl; if (http_parser_parse_url(at, length, false, &parserUrl) != 0) { dataLogger(parser)->debug(QStringLiteral("could not determine url! %1").arg(QString::fromUtf8(at, length))); return 1; } QUrl url; #define isUrlFieldSet(field) parserUrl.field_set & (1 << field) if (isUrlFieldSet(UF_SCHEMA)) url.setScheme(QString::fromUtf8(at + parserUrl.field_data[UF_SCHEMA].off, parserUrl.field_data[UF_SCHEMA].len)); if (parserUrl.field_set & (1 << UF_HOST)) url.setHost(QString::fromUtf8(at + parserUrl.field_data[UF_HOST].off, parserUrl.field_data[UF_HOST].len)); if (parserUrl.field_set & (1 << UF_PORT)) { bool k = false; QString portString(QString::fromUtf8(at + parserUrl.field_data[UF_PORT].off, parserUrl.field_data[UF_PORT].len)); qint16 port = portString.toShort(&k); if (!k) { dataLogger(parser)->warning(QStringLiteral("could not determine port! %1").arg(portString)); return 1; } url.setPort(static_cast(port)); } if (parserUrl.field_set & (1 << UF_PATH)) url.setPath(QString::fromUtf8(at + parserUrl.field_data[UF_PATH].off, parserUrl.field_data[UF_PATH].len)); HttpRequestData *reqData = data(parser); if (isUrlFieldSet(UF_QUERY)) { QUrlQuery q(QString::fromUtf8(at + parserUrl.field_data[UF_QUERY].off, parserUrl.field_data[UF_QUERY].len)); url.setQuery(q); reqData->mQuery = q; } if (isUrlFieldSet(UF_USERINFO)) url.setUserInfo(QString::fromUtf8(at + parserUrl.field_data[UF_USERINFO].off, parserUrl.field_data[UF_USERINFO].len)); #undef isUrlFieldSet dataLogger(parser)->debug(QStringLiteral("final url = %1").arg(url.toString())); reqData->mUrl = url; return 0; } int HttpRequest::on_status(http_parser *parser, const char *, size_t ) { dataLogger(parser)->debug(QStringLiteral("on_status")); return 0; } int HttpRequest::on_header_field(http_parser *parser, const char *at, size_t length) { dataLogger(parser)->debug(QStringLiteral("on_header_field")); data(parser)->mCurrentHeaderField = QString::fromUtf8(at, length); return 0; } int HttpRequest::on_header_value(http_parser *parser, const char *at, size_t length) { dataLogger(parser)->debug(QStringLiteral("on_header_value")); QString value(QString::fromUtf8(at, length)); HttpRequestData *reqdata = data(parser); reqdata->mHeaders.insert(reqdata->mCurrentHeaderField, value); dataLogger(parser)->debug(QStringLiteral("added header: %1 = %2").arg(reqdata->mCurrentHeaderField).arg(value)); reqdata->mCurrentHeaderField.clear(); return 0; } int HttpRequest::on_headers_complete(http_parser *parser) { dataLogger(parser)->debug(QStringLiteral("on_headers_complete")); return 0; } int HttpRequest::on_body(http_parser *parser, const char *at, size_t length) { dataLogger(parser)->debug(QStringLiteral("on_body")); data(parser)->mBodyPartial = QByteArray(at, length); data(parser)->mDone = true; return 0; } int HttpRequest::on_message_complete(http_parser *parser) { dataLogger(parser)->debug(QStringLiteral("on_message_complete")); data(parser)->mDone = true; return 0; } int HttpRequest::on_chunk_header(http_parser *parser) { dataLogger(parser)->debug(QStringLiteral("on_chunk_header")); return 0; } int HttpRequest::on_chunk_complete(http_parser *parser) { dataLogger(parser)->debug(QStringLiteral("on_chunk_complete")); return 0; } qint64 HttpRequest::availableBytes() const { return (mContentLength - alreadyRead); } DataStream *HttpRequest::createDataStreamToClient(int status, const QString &contentType, QIODevice *from, qint64 fileSize, qint64 bufferSize) { mLogger->debug(QStringLiteral("createDataStreamToClient")); writeStatusHeader(status); addHeader(QStringLiteral("Content-Type"), contentType); addHeader(QStringLiteral("Content-Length"), QString::number(fileSize)); writeHeaders(); mCli->write("\r\n"); return new DataStream(from, mCli, {}, fileSize, bufferSize, mLogger->getLoggerFactory(), this); } DataStream *HttpRequest::createDataStreamFromClient(QIODevice *to, qint64 fileSize, qint64 bufferSize) { mLogger->debug(QStringLiteral("createDataStreamFromClient")); return new DataStream(mCli, to, mData.mBodyPartial, fileSize, bufferSize, mLogger->getLoggerFactory(), this); } void HttpRequest::write(int status, const QJsonValue &jsonValue) { QJsonDocument doc; if (jsonValue.isArray()) doc = QJsonDocument(jsonValue.toArray()); else doc = QJsonDocument(jsonValue.toObject()); write(status, doc.toJson(), QStringLiteral("application/json")); } void HttpRequest::write(int status, const QByteArray &data, const QString &contentType) { mLogger->debug(QStringLiteral("write to client: status = %1, data (size) = %2, contentType = %3").arg(status).arg(data.size()).arg(contentType)); writeStatusHeader(status); addHeader(QStringLiteral("Content-Type"), contentType); addHeader(QStringLiteral("Content-Length"), QString::number(data.size())); writeHeaders(); mCli->write("\r\n"); mCli->write(data); } void HttpRequest::writeStatusHeader(int status) { QString header(QStringLiteral("HTTP/1.1 %1 %2\r\n").arg(status).arg(QStringLiteral("OK") )); mLogger->debug(QStringLiteral("write to client: header = %1").arg(header)); mCli->write(header.toUtf8()); } void HttpRequest::addHeader(const QString &key, const QString &value) { mHeaders.insert(key, value); } void HttpRequest::writeHeaders() { QString headers; QMapIterator it(mHeaders); while (it.hasNext()) { it.next(); headers += QStringLiteral("%1: %2\r\n").arg(it.key(), it.value()); } mLogger->debug(QStringLiteral("write to client: headers = %1").arg(headers)); mCli->write(headers.toUtf8()); } void HttpRequest::slotReadyRead() { mLogger->debug(QStringLiteral("slotReadyRead")); if (mDone) { mLogger->debug(QStringLiteral("slotReadyRead: already marked as done")); return; } mCli->startTransaction(); while (mCli->bytesAvailable() && !mData.mDone) { mLogger->debug(QStringLiteral("slotReadyRead: bytes available")); const QByteArray data(mCli->read(10000)); int dataSize = data.size(); size_t size = http_parser_execute(&mParser, &mParserSettings, data.constData(), static_cast(dataSize)); if (size < static_cast(dataSize)) { mLogger->error(QStringLiteral("http parser error: %1").arg(QString::fromUtf8(http_errno_name(HTTP_PARSER_ERRNO(&mParser))))); mCli->commitTransaction(); deleteLater(); return; } } if (mData.mDone) { mLogger->debug(QStringLiteral("slotReadyRead: now marked as done")); mDone = true; mLogger->debug(QStringLiteral("slotReadyRead: method = %1").arg(methodString())); auto it = mData.mHeaders.constFind(QStringLiteral("Upgrade")); if (it != mData.mHeaders.constEnd() && (*it).toLower() == QStringLiteral("websocket")) { mLogger->debug(QStringLiteral("slotReadyRead: Upgrade: %1").arg(*it)); disconnect(mCli, &QTcpSocket::readyRead, nullptr, nullptr); mCli->rollbackTransaction(); mCli->setParent(nullptr); auto cli = mCli; mCli = nullptr; emit signalUpgrade(cli); deleteLater(); return; } it = mData.mHeaders.constFind(QStringLiteral("Content-Length")); if (it != mData.mHeaders.constEnd()) { bool k = false; mContentLength = (*it).toLongLong(&k); if (!k) { mLogger->error(QStringLiteral("slotReadyRead: invalid content length: ").arg(*it)); deleteLater(); return; } } alreadyRead = mData.mBodyPartial.size(); mLogger->debug(QStringLiteral("slotReadyRead: already read %1 / %2 bytes").arg(alreadyRead).arg(contentLength())); mCli->commitTransaction(); emit signalReady(); return; } mLogger->debug(QStringLiteral("slotReadyRead: commit transaction")); mCli->commitTransaction(); } }