151 lines
4.9 KiB
C++
151 lines
4.9 KiB
C++
/* This file is part of Flowee
|
|
*
|
|
* Copyright (C) 2017 Nathan Osman
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* For the full copy of the License see <http://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
#include "parser.h"
|
|
|
|
#include "proxysocket.h"
|
|
|
|
using namespace HttpEngine;
|
|
|
|
ProxySocket::ProxySocket(Socket *socket, const QString &path, const QHostAddress &address, quint16 port)
|
|
: QObject(socket),
|
|
mDownstreamSocket(socket),
|
|
mPath(path),
|
|
mHeadersParsed(false),
|
|
mHeadersWritten(false)
|
|
{
|
|
connect(mDownstreamSocket, &Socket::readyRead, this, &ProxySocket::onDownstreamReadyRead);
|
|
connect(mDownstreamSocket, &Socket::disconnected, this, &ProxySocket::onDownstreamDisconnected);
|
|
|
|
connect(&mUpstreamSocket, &QTcpSocket::connected, this, &ProxySocket::onUpstreamConnected);
|
|
connect(&mUpstreamSocket, &QTcpSocket::readyRead, this, &ProxySocket::onUpstreamReadyRead);
|
|
connect(&mUpstreamSocket, &QAbstractSocket::errorOccurred, this, &ProxySocket::onUpstreamError);
|
|
|
|
mUpstreamSocket.connectToHost(address, port);
|
|
}
|
|
|
|
void ProxySocket::onDownstreamReadyRead()
|
|
{
|
|
if (mHeadersWritten) {
|
|
mUpstreamSocket.write(mDownstreamSocket->readAll());
|
|
} else {
|
|
mUpstreamWrite.append(mDownstreamSocket->readAll());
|
|
}
|
|
}
|
|
|
|
void ProxySocket::onDownstreamDisconnected()
|
|
{
|
|
mUpstreamSocket.disconnectFromHost();
|
|
}
|
|
|
|
void ProxySocket::onUpstreamConnected()
|
|
{
|
|
// Write the status line using the stripped path from the handler
|
|
mUpstreamSocket.write(
|
|
QString("%1 /%2 HTTP/1.1\r\n")
|
|
.arg(methodToString(mDownstreamSocket->method()))
|
|
.arg(mPath)
|
|
.toUtf8()
|
|
);
|
|
|
|
// Use the existing headers but insert proxy-related ones
|
|
Socket::HeaderMap headers = mDownstreamSocket->headers();
|
|
QByteArray peerIP = mDownstreamSocket->peerAddress().toString().toUtf8();
|
|
QByteArray origFwd = headers.value("X-Forwarded-For");
|
|
if (origFwd.isNull()) {
|
|
headers.insert("X-Forwarded-For", peerIP);
|
|
} else {
|
|
headers.insert("X-Forwarded-For", origFwd + ", " + peerIP);
|
|
}
|
|
if (!headers.contains("X-Real-IP")) {
|
|
headers.insert("X-Real-IP", peerIP);
|
|
}
|
|
|
|
// Write the headers to the socket with the terminating CRLF
|
|
for (auto i = headers.constBegin(); i != headers.constEnd(); ++i) {
|
|
mUpstreamSocket.write(i.key() + ": " + i.value() + "\r\n");
|
|
}
|
|
mUpstreamSocket.write("\r\n");
|
|
mHeadersWritten = true;
|
|
|
|
// If there is any data buffered for writing, write it
|
|
if (mUpstreamWrite.size()) {
|
|
mUpstreamSocket.write(mUpstreamWrite);
|
|
mUpstreamWrite.clear();
|
|
}
|
|
}
|
|
|
|
void ProxySocket::onUpstreamReadyRead()
|
|
{
|
|
// If the headers have not yet been parsed, then check to see if the end
|
|
// has been reached yet; if they have, just dump data
|
|
|
|
if (!mHeadersParsed) {
|
|
|
|
// Add to the buffer and check to see if the end was reached
|
|
mUpstreamRead.append(mUpstreamSocket.readAll());
|
|
int index = mUpstreamRead.indexOf("\r\n\r\n");
|
|
if (index != -1) {
|
|
|
|
// Parse the headers
|
|
int statusCode;
|
|
QByteArray statusReason;
|
|
Socket::HeaderMap headers;
|
|
if (!Parser::parseResponseHeaders(mUpstreamRead.left(index), statusCode, statusReason, headers)) {
|
|
mDownstreamSocket->writeError(Socket::BadGateway);
|
|
return;
|
|
}
|
|
|
|
// Dump the headers back downstream
|
|
mDownstreamSocket->setStatusCode(statusCode, statusReason);
|
|
mDownstreamSocket->setHeaders(headers);
|
|
mDownstreamSocket->writeHeaders();
|
|
mDownstreamSocket->write(mUpstreamRead.mid(index + 4));
|
|
|
|
// Remember that headers were parsed and empty the buffer
|
|
mHeadersParsed = true;
|
|
mUpstreamRead.clear();
|
|
}
|
|
} else {
|
|
mDownstreamSocket->write(mUpstreamSocket.readAll());
|
|
}
|
|
}
|
|
|
|
void ProxySocket::onUpstreamError(QAbstractSocket::SocketError socketError)
|
|
{
|
|
if (mHeadersParsed) {
|
|
mDownstreamSocket->close();
|
|
} else {
|
|
mDownstreamSocket->writeError(Socket::BadGateway);
|
|
}
|
|
}
|
|
|
|
QString ProxySocket::methodToString(Socket::Method method) const
|
|
{
|
|
switch (method) {
|
|
case Socket::OPTIONS: return "OPTIONS";
|
|
case Socket::GET: return "GET";
|
|
case Socket::HEAD: return "HEAD";
|
|
case Socket::POST: return "POST";
|
|
case Socket::PUT: return "PUT";
|
|
case Socket::DELETE: return "DELETE";
|
|
case Socket::TRACE: return "TRACE";
|
|
case Socket::CONNECT: return "CONNECT";
|
|
default: return QString();
|
|
}
|
|
}
|