separate httpserver
This commit is contained in:
parent
1d49b038a1
commit
e069da37c6
@ -263,6 +263,8 @@ set(SOURCES
|
|||||||
network-web/googlesuggest.h
|
network-web/googlesuggest.h
|
||||||
network-web/httpresponse.cpp
|
network-web/httpresponse.cpp
|
||||||
network-web/httpresponse.h
|
network-web/httpresponse.h
|
||||||
|
network-web/httpserver.cpp
|
||||||
|
network-web/httpserver.h
|
||||||
network-web/networkfactory.cpp
|
network-web/networkfactory.cpp
|
||||||
network-web/networkfactory.h
|
network-web/networkfactory.h
|
||||||
network-web/oauth2service.cpp
|
network-web/oauth2service.cpp
|
||||||
|
276
src/librssguard/network-web/httpserver.cpp
Normal file
276
src/librssguard/network-web/httpserver.cpp
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
|
#include "network-web/httpserver.h"
|
||||||
|
|
||||||
|
#include "definitions/definitions.h"
|
||||||
|
|
||||||
|
HttpServer::HttpServer(QObject* parent) : QObject(parent), m_listenAddress(QHostAddress()), m_listenPort(0) {
|
||||||
|
connect(&m_httpServer, &QTcpServer::newConnection, this, &HttpServer::clientConnected);
|
||||||
|
|
||||||
|
// NOTE: We do not want to start handler immediately, sometimes
|
||||||
|
// we want to start it later, perhaps when correct redirect URL/port comes in.
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpServer::~HttpServer() {
|
||||||
|
if (m_httpServer.isListening()) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Redirection OAuth handler is listening. Stopping it now.";
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpServer::isListening() const {
|
||||||
|
return m_httpServer.isListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::setListenAddressPort(const QString& full_uri, bool start_handler) {
|
||||||
|
QUrl url = QUrl::fromUserInput(full_uri);
|
||||||
|
QHostAddress listen_address;
|
||||||
|
quint16 listen_port = quint16(url.port(80));
|
||||||
|
|
||||||
|
if (url.host() == QL1S("localhost")) {
|
||||||
|
listen_address = QHostAddress(QHostAddress::SpecialAddress::LocalHost);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listen_address = QHostAddress(url.host());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen_address == m_listenAddress && listen_port == m_listenPort && start_handler == m_httpServer.isListening()) {
|
||||||
|
// NOTE: We do not need to change listener's settings or re-start it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_httpServer.isListening()) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Redirection OAuth handler is listening. Stopping it now.";
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_listenAddress = listen_address;
|
||||||
|
m_listenPort = listen_port;
|
||||||
|
m_listenAddressPort = full_uri;
|
||||||
|
|
||||||
|
if (!start_handler) {
|
||||||
|
qDebugNN << LOGSEC_NETWORK << "User does not want handler to be running.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_httpServer.listen(listen_address, listen_port)) {
|
||||||
|
qCriticalNN << LOGSEC_NETWORK << "OAuth redirect handler FAILED TO START TO LISTEN on address"
|
||||||
|
<< QUOTE_W_SPACE(listen_address.toString()) << "and port" << QUOTE_W_SPACE(listen_port) << "with error"
|
||||||
|
<< QUOTE_W_SPACE_DOT(m_httpServer.errorString());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebugNN << LOGSEC_NETWORK << "OAuth redirect handler IS LISTENING on address"
|
||||||
|
<< QUOTE_W_SPACE(m_listenAddress.toString()) << "and port" << QUOTE_W_SPACE_DOT(m_listenPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::clientConnected() {
|
||||||
|
QTcpSocket* socket = m_httpServer.nextPendingConnection();
|
||||||
|
|
||||||
|
QObject::connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
|
||||||
|
QObject::connect(socket, &QTcpSocket::readyRead, [this, socket]() {
|
||||||
|
readReceivedData(socket);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::readReceivedData(QTcpSocket* socket) {
|
||||||
|
if (!m_connectedClients.contains(socket)) {
|
||||||
|
m_connectedClients[socket].m_address = QSL(URI_SCHEME_HTTP) + m_httpServer.serverAddress().toString();
|
||||||
|
m_connectedClients[socket].m_port = m_httpServer.serverPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHttpRequest* request = &m_connectedClients[socket];
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
if (Q_LIKELY(request->m_state == QHttpRequest::State::ReadingMethod)) {
|
||||||
|
if (Q_UNLIKELY(error = !request->readMethod(socket))) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Invalid method.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Q_LIKELY(!error && request->m_state == QHttpRequest::State::ReadingUrl)) {
|
||||||
|
if (Q_UNLIKELY(error = !request->readUrl(socket))) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Invalid URL.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Q_LIKELY(!error && request->m_state == QHttpRequest::State::ReadingStatus)) {
|
||||||
|
if (Q_UNLIKELY(error = !request->readStatus(socket))) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Invalid status.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Q_LIKELY(!error && request->m_state == QHttpRequest::State::ReadingHeader)) {
|
||||||
|
if (Q_UNLIKELY(error = !request->readHeader(socket))) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Invalid header.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
socket->disconnectFromHost();
|
||||||
|
m_connectedClients.remove(socket);
|
||||||
|
}
|
||||||
|
else if (!request->m_url.isEmpty()) {
|
||||||
|
Q_ASSERT(request->m_state != QHttpRequest::State::ReadingUrl);
|
||||||
|
|
||||||
|
answerClient(socket, *request);
|
||||||
|
m_connectedClients.remove(socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHostAddress HttpServer::listenAddress() const {
|
||||||
|
return m_listenAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HttpServer::listenAddressPort() const {
|
||||||
|
return m_listenAddressPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 HttpServer::listenPort() const {
|
||||||
|
return m_listenPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpServer::QHttpRequest::readMethod(QTcpSocket* socket) {
|
||||||
|
bool finished = false;
|
||||||
|
|
||||||
|
while ((socket->bytesAvailable() != 0) && !finished) {
|
||||||
|
const auto c = socket->read(1).at(0);
|
||||||
|
|
||||||
|
if ((std::isupper(c) != 0) && m_fragment.size() < 6) {
|
||||||
|
m_fragment += c;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finished) {
|
||||||
|
if (m_fragment == "HEAD") {
|
||||||
|
m_method = Method::Head;
|
||||||
|
}
|
||||||
|
else if (m_fragment == "GET") {
|
||||||
|
m_method = Method::Get;
|
||||||
|
}
|
||||||
|
else if (m_fragment == "PUT") {
|
||||||
|
m_method = Method::Put;
|
||||||
|
}
|
||||||
|
else if (m_fragment == "POST") {
|
||||||
|
m_method = Method::Post;
|
||||||
|
}
|
||||||
|
else if (m_fragment == "DELETE") {
|
||||||
|
m_method = Method::Delete;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Invalid operation:" << QUOTE_W_SPACE_DOT(m_fragment.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = State::ReadingUrl;
|
||||||
|
m_fragment.clear();
|
||||||
|
|
||||||
|
return m_method != Method::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpServer::QHttpRequest::readUrl(QTcpSocket* socket) {
|
||||||
|
bool finished = false;
|
||||||
|
|
||||||
|
while ((socket->bytesAvailable() != 0) && !finished) {
|
||||||
|
const auto c = socket->read(1).at(0);
|
||||||
|
|
||||||
|
if (std::isspace(c) != 0) {
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_fragment += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finished) {
|
||||||
|
if (!m_fragment.startsWith("/")) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Invalid URL path" << QUOTE_W_SPACE_DOT(m_fragment);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_url.setUrl(m_address + QString::number(m_port) + QString::fromUtf8(m_fragment));
|
||||||
|
m_state = State::ReadingStatus;
|
||||||
|
|
||||||
|
if (!m_url.isValid()) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Invalid URL" << QUOTE_W_SPACE_DOT(m_fragment);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fragment.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpServer::QHttpRequest::readStatus(QTcpSocket* socket) {
|
||||||
|
bool finished = false;
|
||||||
|
|
||||||
|
while ((socket->bytesAvailable() != 0) && !finished) {
|
||||||
|
m_fragment += socket->read(1);
|
||||||
|
|
||||||
|
if (m_fragment.endsWith("\r\n")) {
|
||||||
|
finished = true;
|
||||||
|
m_fragment.resize(m_fragment.size() - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finished) {
|
||||||
|
if ((std::isdigit(m_fragment.at(m_fragment.size() - 3)) == 0) ||
|
||||||
|
(std::isdigit(m_fragment.at(m_fragment.size() - 1)) == 0)) {
|
||||||
|
qWarningNN << LOGSEC_NETWORK << "Invalid version";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_version = qMakePair(m_fragment.at(m_fragment.size() - 3) - '0', m_fragment.at(m_fragment.size() - 1) - '0');
|
||||||
|
m_state = State::ReadingHeader;
|
||||||
|
m_fragment.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpServer::QHttpRequest::readHeader(QTcpSocket* socket) {
|
||||||
|
while (socket->bytesAvailable() != 0) {
|
||||||
|
m_fragment += socket->read(1);
|
||||||
|
|
||||||
|
if (m_fragment.endsWith("\r\n")) {
|
||||||
|
if (m_fragment == "\r\n") {
|
||||||
|
m_state = State::ReadingBody;
|
||||||
|
m_fragment.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_fragment.chop(2);
|
||||||
|
const int index = m_fragment.indexOf(':');
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray key = m_fragment.mid(0, index).trimmed();
|
||||||
|
const QByteArray value = m_fragment.mid(index + 1).trimmed();
|
||||||
|
|
||||||
|
m_headers.insert(key, value);
|
||||||
|
m_fragment.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::stop() {
|
||||||
|
m_httpServer.close();
|
||||||
|
m_connectedClients.clear();
|
||||||
|
m_listenAddress = QHostAddress();
|
||||||
|
m_listenPort = 0;
|
||||||
|
m_listenAddressPort = QString();
|
||||||
|
|
||||||
|
qDebugNN << LOGSEC_NETWORK << "Stopped redirection handler.";
|
||||||
|
}
|
86
src/librssguard/network-web/httpserver.h
Normal file
86
src/librssguard/network-web/httpserver.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_H
|
||||||
|
#define HTTPSERVER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class HttpServer : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HttpServer(QObject* parent = nullptr);
|
||||||
|
virtual ~HttpServer();
|
||||||
|
|
||||||
|
bool isListening() const;
|
||||||
|
|
||||||
|
// Stops server and clear all connections.
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
// Returns listening portnumber.
|
||||||
|
quint16 listenPort() const;
|
||||||
|
|
||||||
|
// Returns listening IP address, usually something like "127.0.0.1".
|
||||||
|
QHostAddress listenAddress() const;
|
||||||
|
|
||||||
|
// Returns full URL string.
|
||||||
|
QString listenAddressPort() const;
|
||||||
|
|
||||||
|
// Sets full URL string, for example "http://localhost:123456".
|
||||||
|
void setListenAddressPort(const QString& full_uri, bool start_handler);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct QHttpRequest {
|
||||||
|
bool readMethod(QTcpSocket* socket);
|
||||||
|
bool readUrl(QTcpSocket* socket);
|
||||||
|
bool readStatus(QTcpSocket* socket);
|
||||||
|
bool readHeader(QTcpSocket* socket);
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
ReadingMethod,
|
||||||
|
ReadingUrl,
|
||||||
|
ReadingStatus,
|
||||||
|
ReadingHeader,
|
||||||
|
ReadingBody,
|
||||||
|
AllDone
|
||||||
|
} m_state = State::ReadingMethod;
|
||||||
|
|
||||||
|
enum class Method {
|
||||||
|
Unknown,
|
||||||
|
Head,
|
||||||
|
Get,
|
||||||
|
Put,
|
||||||
|
Post,
|
||||||
|
Delete,
|
||||||
|
} m_method = Method::Unknown;
|
||||||
|
|
||||||
|
QString m_address;
|
||||||
|
quint16 m_port = 0;
|
||||||
|
QByteArray m_fragment;
|
||||||
|
QUrl m_url;
|
||||||
|
QPair<quint8, quint8> m_version;
|
||||||
|
QMap<QByteArray, QByteArray> m_headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void answerClient(QTcpSocket* socket, const QHttpRequest& request) = 0;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void clientConnected();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void readReceivedData(QTcpSocket* socket);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QTcpSocket*, QHttpRequest> m_connectedClients;
|
||||||
|
QTcpServer m_httpServer;
|
||||||
|
QHostAddress m_listenAddress;
|
||||||
|
quint16 m_listenPort;
|
||||||
|
QString m_listenAddressPort;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HTTPSERVER_H
|
@ -11,74 +11,9 @@
|
|||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
|
|
||||||
OAuthHttpHandler::OAuthHttpHandler(const QString& success_text, QObject* parent)
|
OAuthHttpHandler::OAuthHttpHandler(const QString& success_text, QObject* parent)
|
||||||
: QObject(parent), m_listenAddress(QHostAddress()), m_listenPort(0), m_successText(success_text) {
|
: HttpServer(parent), m_successText(success_text) {}
|
||||||
connect(&m_httpServer, &QTcpServer::newConnection, this, &OAuthHttpHandler::clientConnected);
|
|
||||||
|
|
||||||
// NOTE: We do not want to start handler immediately, sometimes
|
OAuthHttpHandler::~OAuthHttpHandler() {}
|
||||||
// we want to start it later, perhaps when correct redirect URL/port comes in.
|
|
||||||
}
|
|
||||||
|
|
||||||
OAuthHttpHandler::~OAuthHttpHandler() {
|
|
||||||
if (m_httpServer.isListening()) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Redirection OAuth handler is listening. Stopping it now.";
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OAuthHttpHandler::isListening() const {
|
|
||||||
return m_httpServer.isListening();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OAuthHttpHandler::setListenAddressPort(const QString& full_uri, bool start_handler) {
|
|
||||||
QUrl url = QUrl::fromUserInput(full_uri);
|
|
||||||
QHostAddress listen_address;
|
|
||||||
quint16 listen_port = quint16(url.port(80));
|
|
||||||
|
|
||||||
if (url.host() == QL1S("localhost")) {
|
|
||||||
listen_address = QHostAddress(QHostAddress::SpecialAddress::LocalHost);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listen_address = QHostAddress(url.host());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listen_address == m_listenAddress && listen_port == m_listenPort && start_handler == m_httpServer.isListening()) {
|
|
||||||
// NOTE: We do not need to change listener's settings or re-start it.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_httpServer.isListening()) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Redirection OAuth handler is listening. Stopping it now.";
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_listenAddress = listen_address;
|
|
||||||
m_listenPort = listen_port;
|
|
||||||
m_listenAddressPort = full_uri;
|
|
||||||
|
|
||||||
if (!start_handler) {
|
|
||||||
qDebugNN << LOGSEC_OAUTH << "User does not want handler to be running.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_httpServer.listen(listen_address, listen_port)) {
|
|
||||||
qCriticalNN << LOGSEC_OAUTH << "OAuth redirect handler FAILED TO START TO LISTEN on address"
|
|
||||||
<< QUOTE_W_SPACE(listen_address.toString()) << "and port" << QUOTE_W_SPACE(listen_port) << "with error"
|
|
||||||
<< QUOTE_W_SPACE_DOT(m_httpServer.errorString());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebugNN << LOGSEC_OAUTH << "OAuth redirect handler IS LISTENING on address"
|
|
||||||
<< QUOTE_W_SPACE(m_listenAddress.toString()) << "and port" << QUOTE_W_SPACE_DOT(m_listenPort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OAuthHttpHandler::clientConnected() {
|
|
||||||
QTcpSocket* socket = m_httpServer.nextPendingConnection();
|
|
||||||
|
|
||||||
QObject::connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
|
|
||||||
QObject::connect(socket, &QTcpSocket::readyRead, [this, socket]() {
|
|
||||||
readReceivedData(socket);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void OAuthHttpHandler::handleRedirection(const QVariantMap& data) {
|
void OAuthHttpHandler::handleRedirection(const QVariantMap& data) {
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
@ -109,22 +44,19 @@ void OAuthHttpHandler::handleRedirection(const QVariantMap& data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OAuthHttpHandler::answerClient(QTcpSocket* socket, const QUrl& url) {
|
void OAuthHttpHandler::answerClient(QTcpSocket* socket, const QHttpRequest& request) {
|
||||||
if (!url.path().remove(QL1C('/')).isEmpty()) {
|
if (!request.m_url.path().remove(QL1C('/')).isEmpty()) {
|
||||||
qCriticalNN << LOGSEC_OAUTH << "Invalid request:" << QUOTE_W_SPACE_DOT(url.toString());
|
qCriticalNN << LOGSEC_OAUTH << "Invalid request:" << QUOTE_W_SPACE_DOT(request.m_url.toString());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
QVariantMap received_data;
|
QVariantMap received_data;
|
||||||
const QUrlQuery query(url.query());
|
const QUrlQuery query(request.m_url.query());
|
||||||
const auto items = query.queryItems();
|
const auto items = query.queryItems();
|
||||||
|
|
||||||
for (const auto& item : items) {
|
for (const auto& item : items) {
|
||||||
received_data.insert(item.first, item.second);
|
received_data.insert(item.first, item.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray res = socket->readAll();
|
|
||||||
QString res_utf = QString::fromUtf8(res);
|
|
||||||
|
|
||||||
handleRedirection(received_data);
|
handleRedirection(received_data);
|
||||||
|
|
||||||
const QString html = QSL("<html><head><title>") + qApp->applicationName() + QSL("</title></head><body>") +
|
const QString html = QSL("<html><head><title>") + qApp->applicationName() + QSL("</title></head><body>") +
|
||||||
@ -141,205 +73,3 @@ void OAuthHttpHandler::answerClient(QTcpSocket* socket, const QUrl& url) {
|
|||||||
|
|
||||||
socket->disconnectFromHost();
|
socket->disconnectFromHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OAuthHttpHandler::readReceivedData(QTcpSocket* socket) {
|
|
||||||
if (!m_connectedClients.contains(socket)) {
|
|
||||||
m_connectedClients[socket].m_address = QSL(URI_SCHEME_HTTP) + m_httpServer.serverAddress().toString();
|
|
||||||
m_connectedClients[socket].m_port = m_httpServer.serverPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHttpRequest* request = &m_connectedClients[socket];
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
if (Q_LIKELY(request->m_state == QHttpRequest::State::ReadingMethod)) {
|
|
||||||
if (Q_UNLIKELY(error = !request->readMethod(socket))) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Invalid method.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Q_LIKELY(!error && request->m_state == QHttpRequest::State::ReadingUrl)) {
|
|
||||||
if (Q_UNLIKELY(error = !request->readUrl(socket))) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Invalid URL.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Q_LIKELY(!error && request->m_state == QHttpRequest::State::ReadingStatus)) {
|
|
||||||
if (Q_UNLIKELY(error = !request->readStatus(socket))) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Invalid status.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Q_LIKELY(!error && request->m_state == QHttpRequest::State::ReadingHeader)) {
|
|
||||||
if (Q_UNLIKELY(error = !request->readHeader(socket))) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Invalid header.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
socket->disconnectFromHost();
|
|
||||||
m_connectedClients.remove(socket);
|
|
||||||
}
|
|
||||||
else if (!request->m_url.isEmpty()) {
|
|
||||||
Q_ASSERT(request->m_state != QHttpRequest::State::ReadingUrl);
|
|
||||||
|
|
||||||
answerClient(socket, request->m_url);
|
|
||||||
m_connectedClients.remove(socket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QHostAddress OAuthHttpHandler::listenAddress() const {
|
|
||||||
return m_listenAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString OAuthHttpHandler::listenAddressPort() const {
|
|
||||||
return m_listenAddressPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint16 OAuthHttpHandler::listenPort() const {
|
|
||||||
return m_listenPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OAuthHttpHandler::QHttpRequest::readMethod(QTcpSocket* socket) {
|
|
||||||
bool finished = false;
|
|
||||||
|
|
||||||
while ((socket->bytesAvailable() != 0) && !finished) {
|
|
||||||
const auto c = socket->read(1).at(0);
|
|
||||||
|
|
||||||
if ((std::isupper(c) != 0) && m_fragment.size() < 6) {
|
|
||||||
m_fragment += c;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
finished = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finished) {
|
|
||||||
if (m_fragment == "HEAD") {
|
|
||||||
m_method = Method::Head;
|
|
||||||
}
|
|
||||||
else if (m_fragment == "GET") {
|
|
||||||
m_method = Method::Get;
|
|
||||||
}
|
|
||||||
else if (m_fragment == "PUT") {
|
|
||||||
m_method = Method::Put;
|
|
||||||
}
|
|
||||||
else if (m_fragment == "POST") {
|
|
||||||
m_method = Method::Post;
|
|
||||||
}
|
|
||||||
else if (m_fragment == "DELETE") {
|
|
||||||
m_method = Method::Delete;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Invalid operation:" << QUOTE_W_SPACE_DOT(m_fragment.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_state = State::ReadingUrl;
|
|
||||||
m_fragment.clear();
|
|
||||||
|
|
||||||
return m_method != Method::Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OAuthHttpHandler::QHttpRequest::readUrl(QTcpSocket* socket) {
|
|
||||||
bool finished = false;
|
|
||||||
|
|
||||||
while ((socket->bytesAvailable() != 0) && !finished) {
|
|
||||||
const auto c = socket->read(1).at(0);
|
|
||||||
|
|
||||||
if (std::isspace(c) != 0) {
|
|
||||||
finished = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_fragment += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finished) {
|
|
||||||
if (!m_fragment.startsWith("/")) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Invalid URL path" << QUOTE_W_SPACE_DOT(m_fragment);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_url.setUrl(m_address + QString::number(m_port) + QString::fromUtf8(m_fragment));
|
|
||||||
m_state = State::ReadingStatus;
|
|
||||||
|
|
||||||
if (!m_url.isValid()) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Invalid URL" << QUOTE_W_SPACE_DOT(m_fragment);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_fragment.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OAuthHttpHandler::QHttpRequest::readStatus(QTcpSocket* socket) {
|
|
||||||
bool finished = false;
|
|
||||||
|
|
||||||
while ((socket->bytesAvailable() != 0) && !finished) {
|
|
||||||
m_fragment += socket->read(1);
|
|
||||||
|
|
||||||
if (m_fragment.endsWith("\r\n")) {
|
|
||||||
finished = true;
|
|
||||||
m_fragment.resize(m_fragment.size() - 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finished) {
|
|
||||||
if ((std::isdigit(m_fragment.at(m_fragment.size() - 3)) == 0) ||
|
|
||||||
(std::isdigit(m_fragment.at(m_fragment.size() - 1)) == 0)) {
|
|
||||||
qWarningNN << LOGSEC_OAUTH << "Invalid version";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_version = qMakePair(m_fragment.at(m_fragment.size() - 3) - '0', m_fragment.at(m_fragment.size() - 1) - '0');
|
|
||||||
m_state = State::ReadingHeader;
|
|
||||||
m_fragment.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OAuthHttpHandler::QHttpRequest::readHeader(QTcpSocket* socket) {
|
|
||||||
while (socket->bytesAvailable() != 0) {
|
|
||||||
m_fragment += socket->read(1);
|
|
||||||
|
|
||||||
if (m_fragment.endsWith("\r\n")) {
|
|
||||||
if (m_fragment == "\r\n") {
|
|
||||||
m_state = State::ReadingBody;
|
|
||||||
m_fragment.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_fragment.chop(2);
|
|
||||||
const int index = m_fragment.indexOf(':');
|
|
||||||
|
|
||||||
if (index == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QByteArray key = m_fragment.mid(0, index).trimmed();
|
|
||||||
const QByteArray value = m_fragment.mid(index + 1).trimmed();
|
|
||||||
|
|
||||||
m_headers.insert(key, value);
|
|
||||||
m_fragment.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OAuthHttpHandler::stop() {
|
|
||||||
m_httpServer.close();
|
|
||||||
m_connectedClients.clear();
|
|
||||||
m_listenAddress = QHostAddress();
|
|
||||||
m_listenPort = 0;
|
|
||||||
m_listenAddressPort = QString();
|
|
||||||
|
|
||||||
qDebugNN << LOGSEC_OAUTH << "Stopped redirection handler.";
|
|
||||||
}
|
|
||||||
|
@ -3,85 +3,26 @@
|
|||||||
#ifndef OAUTHHTTPHANDLER_H
|
#ifndef OAUTHHTTPHANDLER_H
|
||||||
#define OAUTHHTTPHANDLER_H
|
#define OAUTHHTTPHANDLER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include "network-web/httpserver.h"
|
||||||
|
|
||||||
#include <QTcpServer>
|
class OAuthHttpHandler : public HttpServer {
|
||||||
#include <QUrl>
|
Q_OBJECT
|
||||||
|
|
||||||
class OAuthHttpHandler : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit OAuthHttpHandler(const QString& success_text, QObject* parent = nullptr);
|
explicit OAuthHttpHandler(const QString& success_text, QObject* parent = nullptr);
|
||||||
virtual ~OAuthHttpHandler();
|
virtual ~OAuthHttpHandler();
|
||||||
|
|
||||||
bool isListening() const;
|
|
||||||
|
|
||||||
// Stops server and clear all connections.
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
// Returns listening portnumber.
|
|
||||||
quint16 listenPort() const;
|
|
||||||
|
|
||||||
// Returns listening IP address, usually something like "127.0.0.1".
|
|
||||||
QHostAddress listenAddress() const;
|
|
||||||
|
|
||||||
// Returns full URL string.
|
|
||||||
QString listenAddressPort() const;
|
|
||||||
|
|
||||||
// Sets full URL string, for example "http://localhost:123456".
|
|
||||||
void setListenAddressPort(const QString& full_uri, bool start_handler);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void authRejected(const QString& error_description, const QString& state);
|
void authRejected(const QString& error_description, const QString& state);
|
||||||
void authGranted(const QString& auth_code, const QString& state);
|
void authGranted(const QString& auth_code, const QString& state);
|
||||||
|
|
||||||
private slots:
|
protected:
|
||||||
void clientConnected();
|
virtual void answerClient(QTcpSocket* socket, const QHttpRequest& request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleRedirection(const QVariantMap& data);
|
void handleRedirection(const QVariantMap& data);
|
||||||
void answerClient(QTcpSocket* socket, const QUrl& url);
|
|
||||||
void readReceivedData(QTcpSocket* socket);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct QHttpRequest {
|
|
||||||
bool readMethod(QTcpSocket* socket);
|
|
||||||
bool readUrl(QTcpSocket* socket);
|
|
||||||
bool readStatus(QTcpSocket* socket);
|
|
||||||
bool readHeader(QTcpSocket* socket);
|
|
||||||
|
|
||||||
enum class State {
|
|
||||||
ReadingMethod,
|
|
||||||
ReadingUrl,
|
|
||||||
ReadingStatus,
|
|
||||||
ReadingHeader,
|
|
||||||
ReadingBody,
|
|
||||||
AllDone
|
|
||||||
} m_state = State::ReadingMethod;
|
|
||||||
|
|
||||||
enum class Method {
|
|
||||||
Unknown,
|
|
||||||
Head,
|
|
||||||
Get,
|
|
||||||
Put,
|
|
||||||
Post,
|
|
||||||
Delete,
|
|
||||||
} m_method = Method::Unknown;
|
|
||||||
|
|
||||||
QString m_address;
|
|
||||||
quint16 m_port = 0;
|
|
||||||
QByteArray m_fragment;
|
|
||||||
QUrl m_url;
|
|
||||||
QPair<quint8, quint8> m_version;
|
|
||||||
QMap<QByteArray, QByteArray> m_headers;
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QTcpSocket*, QHttpRequest> m_connectedClients;
|
|
||||||
QTcpServer m_httpServer;
|
|
||||||
QHostAddress m_listenAddress;
|
|
||||||
quint16 m_listenPort;
|
|
||||||
QString m_listenAddressPort;
|
|
||||||
QString m_successText;
|
QString m_successText;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user