PDA

View Full Version : A Web Proxy multithread



giusepped
29th July 2011, 18:17
I found within the Web a good example to implement an Http Proxy.
It works great, but the problem is that it is single threaded.
I tried to change it to a multithread version, but for some odd reason the code fires a segfault.
The original single thread version follows:


/*

*/

#include "webproxy.h"
#include <QtNetwork>
#include <QMessageBox>
#include <QtGui>
#include <QHash>


WebProxy::WebProxy(QObject *parent,int port): QObject(parent)

{

QTcpServer *proxyServer = new QTcpServer(this);
if (!proxyServer->listen(QHostAddress::Any, port)) {
emit error(1);

return;
}

connect(proxyServer, SIGNAL(newConnection()), this, SLOT(manageQuery()));
qDebug() << "Proxy server running at port" << proxyServer->serverPort();


}

void WebProxy::setAddress(const QHostAddress &a, int port)
{
proxyAddress = a;
proxyPort = port;
port = port < 0 ? 8081 : port;


}

void WebProxy::manageQuery() {
QTcpServer *proxyServer = qobject_cast<QTcpServer*>(sender());
QTcpSocket *socket = proxyServer->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), this, SLOT(processQuery()));
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
qDebug()<<"New connection started..."<<socket->peerAddress();
}
QUrl WebProxy::getUrl(QList<QByteArray > &entries)
{

QByteArray method = entries.value(0);
QByteArray address = entries.value(1);
QByteArray version = entries.value(2);


QUrl url = QUrl::fromEncoded(address);
if (!url.isValid()) {

qWarning() << "Invalid URL:" << url;
return QString();
}
return url;
}

void WebProxy::processQuery() {

QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
QByteArray requestData = socket->readAll();

int pos = requestData.indexOf("\r\n");

QByteArray requestLine = requestData.left(pos);
requestData.remove(0, pos + 2);

QList<QByteArray> entries = requestLine.split(' ');
QByteArray method = entries.value(0);
QByteArray address = entries.value(1);
QByteArray version = entries.value(2);

QByteArray auth;
QByteArray authMethod;

QUrl url = QUrl::fromEncoded(address);
if (!url.isValid()) {
qWarning() << "Invalid URL:" << url;
socket->disconnectFromHost();
return;
}

QString host = url.host();

int port = (url.port() <= 0) ? 80 : url.port();
QByteArray req = url.encodedPath();
if (url.hasQuery())
req.append('?').append(url.encodedQuery());

requestLine = method + " " + req + " " + version + "\r\n";


QString key = host + ':' + QString::number(port);
QTcpSocket *proxySocket = socket->findChild<QTcpSocket*>(key);
if (proxySocket) {
proxySocket->setObjectName(key);
proxySocket->setProperty("url", url);
proxySocket->setProperty("requestData", requestData);
proxySocket->write(requestData);
} else {
proxySocket = new QTcpSocket(socket);
proxySocket->setObjectName(key);
proxySocket->setProperty("url", url);
proxySocket->setProperty("requestData", requestData);
connect(proxySocket, SIGNAL(connected()), this, SLOT(sendRequest()));
connect(proxySocket, SIGNAL(readyRead()), this, SLOT(transferData()));
connect(proxySocket, SIGNAL(disconnected()), this, SLOT(closeConnection()));
connect(proxySocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(closeConnection()));
proxySocket->connectToHost(host, port);
}
}

void WebProxy::sendRequest() {
QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
QByteArray requestData = proxySocket->property("requestData").toByteArray();
proxySocket->write(requestData);
}

void WebProxy::transferData() {


QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
QByteArray data = proxySocket->readAll();
QString host = proxySocket->peerAddress().toString();

QTcpSocket *socket = qobject_cast<QTcpSocket*>(proxySocket->parent());
socket->write(data);

}

void WebProxy::closeConnection() {
QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
if (proxySocket) {
QTcpSocket *socket = qobject_cast<QTcpSocket*>(proxySocket->parent());
if (socket)
socket->disconnectFromHost();
if (proxySocket->error() != QTcpSocket::RemoteHostClosedError)
qWarning() << "Error for:" << proxySocket->property("url").toUrl()
<< proxySocket->errorString();
proxySocket->deleteLater();;
}
}




I call "new WebProxy " in the main.
In the multithreaded version, I put all functions from manageQuery() inside a new threaded Class. But when a connection start, I got a segfault.
Any help appreciated.
Does anybody know

tbscope
30th July 2011, 09:11
In the multithreaded version, I put all functions from manageQuery() inside a new threaded Class. But when a connection start, I got a segfault.
Any help appreciated.

Please post the code that causes the problem.

giusepped
30th July 2011, 14:01
Ok, but it is as I said.



WebProxy::WebProxy(QObject *parent,int port): QTcpServer(parent)

{
qDebug()<<" Listen...";
authMethod = "";
proxyServer = new QTcpServer;
if (!proxyServer->listen(QHostAddress::Any, port)) {
emit error(1);

return;
}

connect(proxyServer, SIGNAL(newConnection()), this, SLOT(go()));
qDebug() << "Proxy server running at port" << proxyServer->serverPort() << proxyServer->serverAddress();

thr = 0;
}
void WebProxy::addFilter(const QHash<QString, QString> &user, const QHash<QString, QString> &pass, const QHash<QString, QString> &u_format, const QHash<QString, QString> &p_format, const QHash<QString, int> &dvr,const QHash<QString,QString > &submit, const QHash<QString,QString> &search)
{
qDebug() << "Server status..............";
qDebug() << "Server status"<<proxyServer->isListening();
hashUsername = user;
hashPassword = pass;
hashUFormat = u_format;
hashPFormat = p_format;
hashDvr = dvr;
hashSubmit = submit;
hashSearch = search;


}

void WebProxy::setAddress(const QHostAddress &a, int port)
{
proxyAddress = a;
proxyPort = port;
port = port < 0 ? 8081 : port;


}
void WebProxy::go()
{
// Create a new Thread
int sck = socketDescriptor();
WebProxyThread *wpt = new WebProxyThread(sck,
hashUsername,
hashPassword,
hashUFormat,
hashPFormat,
hashDvr,
hashSubmit,
hashSearch,this);
connect(wpt, SIGNAL(finished()), wpt, SLOT(deleteLater()));
wpt->start();
qDebug() << "New thread"<<thr++;


}


and the threaded proxy is in another class:



#include "webproxythreaded.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QtGui>


WebProxyThread::WebProxyThread(int socketDescriptor,
const QHash<QString, QString> &user,
const QHash<QString, QString> &pass,
const QHash<QString, QString> &u_format,
const QHash<QString, QString> &p_format,
const QHash<QString, int> &dvr,
const QHash<QString, QString> &submit_str,
const QHash<QString, QString> &submit_search,
QObject *parent) :
QThread(parent), socketDescriptor(socketDescriptor)
{
hashUsername = user;
hashPassword = pass;
hashUFormat = u_format;
hashPFormat = p_format;
hashDvr = dvr;
hashSubmit = submit_str;
hashSearch = submit_search;
}




void WebProxyThread::run()
{
QTcpServer *proxyServer = qobject_cast<QTcpServer*>(sender());
QTcpSocket *socket = proxyServer->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), this, SLOT(processQuery()));
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));

qDebug()<<"New connection started..run." ;
//! [1] //! [2]




}
void WebProxyThread::manageQuery() {
QTcpServer *proxyServer = qobject_cast<QTcpServer*>(sender());
QTcpSocket *socket = proxyServer->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), this, SLOT(processQuery()));
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
qDebug()<<"New connection started..."<<socket->peerAddress();
}
QUrl WebProxyThread::getUrl(QList<QByteArray > &entries)
{

QByteArray method = entries.value(0);
QByteArray address = entries.value(1);
QByteArray version = entries.value(2);

qDebug()<<method;
qDebug()<<address;
qDebug()<<version;
QUrl url = QUrl::fromEncoded(address);
if (!url.isValid()) {

qWarning() << "Invalid URL:" << url;

return QString();
}


return url;
}

void WebProxyThread::processQuery() {


QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
QByteArray requestData = socket->readAll();
int pos = requestData.indexOf("\r\n");
QByteArray requestLine = requestData.left(pos);
requestData.remove(0, pos + 2);
QList<QByteArray> entries = requestLine.split(' ');
QByteArray method = entries.value(0);
QByteArray address = entries.value(1);
QByteArray version = entries.value(2);

QUrl url = QUrl::fromEncoded(address);
if (!url.isValid()) {
qWarning() << "Invalid URL:" << url;
socket->disconnectFromHost();
return;
}

QString host = url.host();

int port = (url.port() <= 0) ? 80 : url.port();
QByteArray req = url.encodedPath();
if (url.hasQuery())
req.append('?').append(url.encodedQuery());

requestLine = method + " " + req + " " + version + "\r\n";
requestData.prepend(requestLine);
QString key = host + ':' + QString::number(port);
QTcpSocket *proxySocket = socket->findChild<QTcpSocket*>(key);
if (proxySocket) {
proxySocket->setObjectName(key);
proxySocket->setProperty("url", url);
proxySocket->setProperty("requestData", requestData);
proxySocket->write(requestData);
} else {
proxySocket = new QTcpSocket(socket);
proxySocket->setObjectName(key);
proxySocket->setProperty("url", url);
proxySocket->setProperty("requestData", requestData);
connect(proxySocket, SIGNAL(connected()), this, SLOT(sendRequest()));
connect(proxySocket, SIGNAL(readyRead()), this, SLOT(transferData()));
connect(proxySocket, SIGNAL(disconnected()), this, SLOT(closeConnection()));
connect(proxySocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(closeConnection()));
proxySocket->connectToHost(host, port);
}
}

void WebProxyThread::sendRequest() {
QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
QByteArray requestData = proxySocket->property("requestData").toByteArray();
proxySocket->write(requestData);
}

void WebProxyThread::transferData() {
QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
QByteArray data = proxySocket->readAll();
QString host = proxySocket->peerAddress().toString();
QByteArray filtered(data);
if (hashDvr.value(host) == 1)
{

QString name = hashUFormat.value(host);
QString pass = hashPFormat.value(host);
QString repU = "name=" + name + " value=" + hashUsername.value(host);
QString repP = "name="+ pass + " value=" + hashPassword.value(host);
QString submit = hashSubmit.value(host);
QString search = hashSearch.value(host);
filtered.replace("name=" + name,repU.toLocal8Bit());
filtered.replace("name=" + pass,repP.toLocal8Bit());
filtered.replace(search.toLocal8Bit(),submit.toLoc al8Bit());
}
QTcpSocket *socket = qobject_cast<QTcpSocket*>(proxySocket->parent());
socket->write(filtered);
}

void WebProxyThread::closeConnection() {
QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
if (proxySocket) {
QTcpSocket *socket = qobject_cast<QTcpSocket*>(proxySocket->parent());
if (socket)
socket->disconnectFromHost();
if (proxySocket->error() != QTcpSocket::RemoteHostClosedError)
qWarning() << "Error for:" << proxySocket->property("url").toUrl()
<< proxySocket->errorString();
proxySocket->deleteLater();;
}
}

tbscope
30th July 2011, 14:21
That's completely incorrect. There are lots of errors.

One thing I'm pretty sure off is that the proxyServer pointer points to nothing or nothing usable.

What does the following code do?

QTcpServer *proxyServer = qobject_cast<QTcpServer*>(sender());
Can you explain this?

giusepped
1st August 2011, 16:10
It should triy to guess who is the sender of the signal....
G

NullPointer
2nd August 2011, 06:42
tbscope is right...

When you connect proxyServer::newConnection and WebProxy::go(), they are on the same thread, so it is a direct connection. When this happens, Qt doesn't fill up the sender() value... so, when you do it in your wpt->start(), which calls run(), you are using a null value.
As a try, you can replace

QTcpServer *proxyServer = qobject_cast<QTcpServer*>(sender());
by

QTcpServer *proxyServer = qobject_cast<QTcpServer*>(parent());

BTW, I think you will need at least one mutex to protect the following


QTcpServer *proxyServer = qobject_cast<QTcpServer*>(sender()); //this you should review
QTcpSocket *socket = proxyServer->nextPendingConnection();
to avoid lost connections.
Try making this a piece of WebProxy implementation, instead of doing this in the thread start...

Or better: catch the nextPendigConnection, and pass it to constructor of the thread object (using socket descriptor, not sure if you can use a QTcpSocket across different threads)... and then use the QAbstractSocket::setSocketDescriptor at thread startup...

hth.