added http request
parent
be230d9a3b
commit
c614bbd54c
|
|
@ -6,8 +6,10 @@ TARGET = $$qtLibraryTarget(nymea_devicepluginhttpcommander)
|
|||
|
||||
SOURCES += \
|
||||
devicepluginhttpcommander.cpp \
|
||||
httprequest.cpp
|
||||
|
||||
HEADERS += \
|
||||
devicepluginhttpcommander.h \
|
||||
httprequest.h
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,238 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* nymea is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* nymea is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with nymea. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
//This file has been copied from the libnymea-core library
|
||||
//TODO create a http library
|
||||
|
||||
#include "httprequest.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
#include <QUrlQuery>
|
||||
|
||||
/*! Construct an empty \l{HttpRequest}. */
|
||||
HttpRequest::HttpRequest() :
|
||||
m_rawData(QByteArray()),
|
||||
m_valid(false),
|
||||
m_isComplete(false)
|
||||
{
|
||||
}
|
||||
|
||||
/*! Construct a \l{HttpRequest} with the given \a rawData. The \a rawData will be parsed in this constructor. You can check
|
||||
if the data is valid with \l{isValid()}. You can check if the request is complete with \l{isComplete}.
|
||||
\sa isValid(), isComplete()
|
||||
*/
|
||||
HttpRequest::HttpRequest(QByteArray rawData) :
|
||||
m_rawData(rawData),
|
||||
m_valid(false),
|
||||
m_isComplete(false)
|
||||
{
|
||||
validate();
|
||||
}
|
||||
|
||||
/*! Returns the raw header of this request.*/
|
||||
QByteArray HttpRequest::rawHeader() const
|
||||
{
|
||||
return m_rawHeader;
|
||||
}
|
||||
|
||||
/*! Returns the list of raw header as key and value pairs.*/
|
||||
QHash<QByteArray, QByteArray> HttpRequest::rawHeaderList() const
|
||||
{
|
||||
return m_rawHeaderList;
|
||||
}
|
||||
|
||||
/*! Returns the \l{RequestMethod} of this request.
|
||||
\sa RequestMethod
|
||||
*/
|
||||
HttpRequest::RequestMethod HttpRequest::method() const
|
||||
{
|
||||
return m_method;
|
||||
}
|
||||
|
||||
/*! Returns the method as human readable string.*/
|
||||
QString HttpRequest::methodString() const
|
||||
{
|
||||
return m_methodString;
|
||||
}
|
||||
|
||||
/*! Returns the HTTP version of this \l{HttpRequest}.*/
|
||||
QByteArray HttpRequest::httpVersion() const
|
||||
{
|
||||
return m_httpVersion;
|
||||
}
|
||||
|
||||
/*! Returns the URL of this \l{HttpRequest}.*/
|
||||
QUrl HttpRequest::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
/*! Returns the URL query of this \l{HttpRequest}.*/
|
||||
QUrlQuery HttpRequest::urlQuery() const
|
||||
{
|
||||
return m_urlQuery;
|
||||
}
|
||||
|
||||
/*! Returns the payload (content) of this \l{HttpRequest}.*/
|
||||
QByteArray HttpRequest::payload() const
|
||||
{
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
/*! Returns true if this \l{HttpRequest} is valid. A HTTP request is valid if the header and the payload were paresed successfully without errors.*/
|
||||
bool HttpRequest::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
/*! Returns true if this \l{HttpRequest} is complete. A HTTP request is complete if "Content-Length" header value matches the actual payload size. Bigger packages will be sent in multiple TCP packages. */
|
||||
bool HttpRequest::isComplete() const
|
||||
{
|
||||
return m_isComplete;
|
||||
}
|
||||
|
||||
/*! Returns true if this \l{HttpRequest} has a payload.*/
|
||||
bool HttpRequest::hasPayload() const
|
||||
{
|
||||
return !m_payload.isEmpty();
|
||||
}
|
||||
|
||||
/*! Appends the given \a data to the current raw data of this \l{HttpRequest}.
|
||||
* This method will be used if a \l{HttpRequest} is not complete yet.
|
||||
*
|
||||
* \sa isComplete()
|
||||
*/
|
||||
void HttpRequest::appendData(const QByteArray &data)
|
||||
{
|
||||
m_rawData.append(data);
|
||||
validate();
|
||||
}
|
||||
|
||||
void HttpRequest::validate()
|
||||
{
|
||||
m_isComplete = true; m_valid = false;
|
||||
|
||||
// Parese the HTTP request. The request is invalid, until the end of the parse process.
|
||||
if (m_rawData.isEmpty())
|
||||
return;
|
||||
|
||||
// split the data into header and payload
|
||||
int headerEndIndex = m_rawData.indexOf("\r\n\r\n");
|
||||
if (headerEndIndex < 0) {
|
||||
qCWarning(dcWebServer()) << "Could not parse end of HTTP header (empty line between header and body):" << m_rawData;
|
||||
return;
|
||||
}
|
||||
|
||||
m_rawHeader = m_rawData.left(headerEndIndex);
|
||||
m_payload = m_rawData.right(m_rawData.length() - headerEndIndex).simplified();
|
||||
|
||||
// parse status line
|
||||
QStringList headerLines = QString(m_rawHeader).split(QRegExp("\r\n"));
|
||||
QString statusLine = headerLines.takeFirst();
|
||||
QStringList statusLineTokens = statusLine.split(QRegExp("[ \r\n][ \r\n]*"));
|
||||
if (statusLineTokens.count() != 3) {
|
||||
qCWarning(dcWebServer()) << "Could not parse HTTP status line:" << statusLine;
|
||||
return;
|
||||
}
|
||||
|
||||
// verify http version
|
||||
m_httpVersion = statusLineTokens.at(2).toUtf8().simplified();
|
||||
if (!m_httpVersion.contains("HTTP")) {
|
||||
qCWarning(dcWebServer()) << "Unknown HTTP version:" << m_httpVersion;
|
||||
return;
|
||||
}
|
||||
m_methodString = statusLineTokens.at(0).simplified();
|
||||
m_method = getRequestMethodType(m_methodString);
|
||||
|
||||
m_url = QUrl("http://example.com" + statusLineTokens.at(1).simplified());
|
||||
|
||||
if (m_url.hasQuery())
|
||||
m_urlQuery = QUrlQuery(m_url.query());
|
||||
|
||||
// verify header formating
|
||||
foreach (const QString &line, headerLines) {
|
||||
if (!line.contains(":")) {
|
||||
qCWarning(dcWebServer()) << "Invalid HTTP header:" << line;
|
||||
return;
|
||||
}
|
||||
int index = line.indexOf(":");
|
||||
QByteArray key = line.left(index).toUtf8().simplified();
|
||||
QByteArray value = line.right(line.count() - index - 1).toUtf8().simplified();
|
||||
m_rawHeaderList.insert(key, value);
|
||||
}
|
||||
|
||||
// check User-Agent
|
||||
if (!m_rawHeaderList.contains("User-Agent"))
|
||||
qCWarning(dcWebServer()) << "User-Agent header is missing";
|
||||
|
||||
|
||||
// verify content length with actual payload
|
||||
if (m_rawHeaderList.contains("Content-Length")) {
|
||||
bool ok = false;
|
||||
int contentLength = m_rawHeaderList.value("Content-Length").toInt(&ok);
|
||||
if (!ok) {
|
||||
qCWarning(dcWebServer()) << "Could not parse Content-Length.";
|
||||
return;
|
||||
}
|
||||
// check if we have all data
|
||||
if (m_payload.size() < contentLength) {
|
||||
qCDebug(dcWebServer()) << "Request incomplete:";
|
||||
qCDebug(dcWebServer()) << " -> Content-Length:" << contentLength;
|
||||
qCDebug(dcWebServer()) << " -> Payload size :" << payload().size();
|
||||
m_isComplete = false;
|
||||
return;
|
||||
}
|
||||
// check if the content length bigger than header Content-Length
|
||||
if (m_payload.size() > contentLength) {
|
||||
qCWarning(dcWebServer()) << "Payload size greater than header Content-Length:";
|
||||
qCWarning(dcWebServer()) << " -> Content-Length:" << contentLength;
|
||||
qCWarning(dcWebServer()) << " -> Payload size :" << payload().size();
|
||||
m_isComplete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
HttpRequest::RequestMethod HttpRequest::getRequestMethodType(const QString &methodString)
|
||||
{
|
||||
if (methodString == "GET") {
|
||||
return RequestMethod::Get;
|
||||
} else if (methodString == "POST") {
|
||||
return RequestMethod::Post;
|
||||
} else if (methodString == "PUT") {
|
||||
return RequestMethod::Put;
|
||||
} else if (methodString == "DELETE") {
|
||||
return RequestMethod::Delete;
|
||||
} else if (methodString == "OPTIONS") {
|
||||
return RequestMethod::Options;
|
||||
}
|
||||
qCWarning(dcWebServer()) << "Method" << methodString << "will not be handled.";
|
||||
return RequestMethod::Unhandled;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const HttpRequest &httpRequest)
|
||||
{
|
||||
debug << "HttpRequest:" << endl;
|
||||
debug << qUtf8Printable(httpRequest.rawHeader());
|
||||
debug << qUtf8Printable(httpRequest.payload());
|
||||
return debug;
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* nymea is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* nymea is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with nymea. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef HTTPREQUEST_H
|
||||
#define HTTPREQUEST_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QUrlQuery>
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
|
||||
class HttpRequest
|
||||
{
|
||||
public:
|
||||
enum RequestMethod {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Options,
|
||||
Unhandled
|
||||
};
|
||||
|
||||
HttpRequest();
|
||||
HttpRequest(QByteArray rawData);
|
||||
|
||||
QByteArray rawHeader() const;
|
||||
QHash<QByteArray, QByteArray> rawHeaderList() const;
|
||||
|
||||
RequestMethod method() const;
|
||||
QString methodString() const;
|
||||
QByteArray httpVersion() const;
|
||||
|
||||
QUrl url() const;
|
||||
QUrlQuery urlQuery() const;
|
||||
|
||||
QByteArray payload() const;
|
||||
|
||||
bool isValid() const;
|
||||
bool isComplete() const;
|
||||
bool hasPayload() const;
|
||||
|
||||
void appendData(const QByteArray &data);
|
||||
|
||||
private:
|
||||
QByteArray m_rawData;
|
||||
QByteArray m_rawHeader;
|
||||
QHash<QByteArray, QByteArray> m_rawHeaderList;
|
||||
|
||||
RequestMethod m_method;
|
||||
QString m_methodString;
|
||||
QByteArray m_httpVersion;
|
||||
|
||||
QUrl m_url;
|
||||
QUrlQuery m_urlQuery;
|
||||
|
||||
QByteArray m_payload;
|
||||
|
||||
bool m_valid;
|
||||
bool m_isComplete;
|
||||
|
||||
void validate();
|
||||
RequestMethod getRequestMethodType(const QString &methodString);
|
||||
};
|
||||
|
||||
QDebug operator<< (QDebug debug, const HttpRequest &httpRequest);
|
||||
|
||||
#endif // HTTPREQUEST_H
|
||||
Loading…
Reference in New Issue