mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2026-03-11 08:54:48 +00:00
286 lines
7.2 KiB
C++
286 lines
7.2 KiB
C++
#include "private/qhttpclient_private.hpp"
|
|
|
|
#include <QTimerEvent>
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
namespace qhttp {
|
|
namespace client {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
QHttpClient::QHttpClient(QObject *parent)
|
|
: QObject(parent), d_ptr(new QHttpClientPrivate(this)) {
|
|
QHTTP_LINE_LOG
|
|
}
|
|
|
|
QHttpClient::QHttpClient(QHttpClientPrivate &dd, QObject *parent)
|
|
: QObject(parent), d_ptr(&dd) {
|
|
QHTTP_LINE_LOG
|
|
}
|
|
|
|
QHttpClient::~QHttpClient() {
|
|
QHTTP_LINE_LOG
|
|
}
|
|
|
|
quint32
|
|
QHttpClient::timeOut() const {
|
|
return d_func()->itimeOut;
|
|
}
|
|
|
|
void
|
|
QHttpClient::setTimeOut(quint32 t) {
|
|
d_func()->itimeOut = t;
|
|
}
|
|
|
|
bool
|
|
QHttpClient::isOpen() const {
|
|
return d_func()->isocket.isOpen();
|
|
}
|
|
|
|
void
|
|
QHttpClient::killConnection() {
|
|
Q_D(QHttpClient);
|
|
|
|
d->iconnectingTimer.stop();
|
|
d->itimer.stop();
|
|
d->isocket.close();
|
|
}
|
|
|
|
TBackend
|
|
QHttpClient::backendType() const {
|
|
return d_func()->isocket.ibackendType;
|
|
}
|
|
|
|
QTcpSocket*
|
|
QHttpClient::tcpSocket() const {
|
|
return d_func()->isocket.itcpSocket;
|
|
}
|
|
|
|
QLocalSocket*
|
|
QHttpClient::localSocket() const {
|
|
return d_func()->isocket.ilocalSocket;
|
|
}
|
|
|
|
void
|
|
QHttpClient::setConnectingTimeOut(quint32 timeout) {
|
|
Q_D(QHttpClient);
|
|
|
|
if ( timeout == 0 ) {
|
|
d->iconnectingTimer.stop();
|
|
|
|
} else {
|
|
d->iconnectingTimer.start(timeout,
|
|
Qt::CoarseTimer,
|
|
this
|
|
);
|
|
}
|
|
}
|
|
|
|
bool
|
|
QHttpClient::request(THttpMethod method, QUrl url,
|
|
const TRequstHandler &reqHandler,
|
|
const TResponseHandler &resHandler) {
|
|
Q_D(QHttpClient);
|
|
|
|
d->ireqHandler = nullptr;
|
|
d->irespHandler = nullptr;
|
|
|
|
// if url is a local file (UNIX socket) the host could be empty!
|
|
if ( !url.isValid() || url.isEmpty() /*|| url.host().isEmpty()*/ )
|
|
return false;
|
|
|
|
// process handlers
|
|
if ( resHandler ) {
|
|
d->irespHandler = resHandler;
|
|
|
|
if ( reqHandler )
|
|
d->ireqHandler = reqHandler;
|
|
else
|
|
d->ireqHandler = [](QHttpRequest* req) ->void {
|
|
req->addHeader("connection", "close");
|
|
req->end();
|
|
};
|
|
}
|
|
|
|
auto requestCreator = [this, method, url]() {
|
|
// create request object
|
|
if ( d_ptr->ilastRequest )
|
|
d_ptr->ilastRequest->deleteLater();
|
|
|
|
d_ptr->ilastRequest = new QHttpRequest(this);
|
|
QObject::connect(d_ptr->ilastRequest, &QHttpRequest::done, [this](bool wasTheLastPacket){
|
|
d_ptr->ikeepAlive = !wasTheLastPacket;
|
|
});
|
|
|
|
d_ptr->ilastRequest->d_ptr->imethod = method;
|
|
d_ptr->ilastRequest->d_ptr->iurl = url;
|
|
};
|
|
|
|
// connecting to host/server must be the last thing. (after all function handlers and ...)
|
|
// check for type
|
|
if ( url.scheme().toLower() == QLatin1String("file") ) {
|
|
d->isocket.ibackendType = ELocalSocket;
|
|
d->initializeSocket();
|
|
|
|
requestCreator();
|
|
|
|
if ( d->isocket.isOpen() )
|
|
d->onConnected();
|
|
else
|
|
d->isocket.connectTo(url);
|
|
|
|
} else {
|
|
bool ssl = url.scheme() == "https";
|
|
|
|
d->isocket.ibackendType = ssl ? ESslSocket : ETcpSocket;
|
|
d->initializeSocket();
|
|
|
|
requestCreator();
|
|
|
|
if ( d->isocket.isOpen() )
|
|
d->onConnected();
|
|
else
|
|
d->isocket.connectTo(url.host(), url.port(ssl ? 443 : 80));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
QHttpClient::timerEvent(QTimerEvent *e) {
|
|
Q_D(QHttpClient);
|
|
|
|
if ( e->timerId() == d->itimer.timerId() ) {
|
|
killConnection();
|
|
|
|
} else if ( e->timerId() == d->iconnectingTimer.timerId() ) {
|
|
d->iconnectingTimer.stop();
|
|
emit connectingTimeOut();
|
|
}
|
|
}
|
|
|
|
void
|
|
QHttpClient::onRequestReady(QHttpRequest *req) {
|
|
emit httpConnected(req);
|
|
}
|
|
|
|
void
|
|
QHttpClient::onResponseReady(QHttpResponse *res) {
|
|
emit newResponse(res);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// if server closes the connection, ends the response or by any other reason
|
|
// the socket disconnects, then the irequest and iresponse instances may have
|
|
// been deleted. In these situations reading more http body or emitting end()
|
|
// for incoming request are not possible:
|
|
// if ( ilastRequest == nullptr )
|
|
// return 0;
|
|
|
|
|
|
|
|
int
|
|
QHttpClientPrivate::messageBegin(http_parser*) {
|
|
itempHeaderField.clear();
|
|
itempHeaderValue.clear();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
QHttpClientPrivate::status(http_parser* parser, const char* at, size_t length) {
|
|
if ( ilastResponse )
|
|
ilastResponse->deleteLater();
|
|
|
|
ilastResponse = new QHttpResponse(q_func());
|
|
ilastResponse->d_func()->istatus = static_cast<TStatusCode>(parser->status_code);
|
|
ilastResponse->d_func()->iversion = QString("%1.%2")
|
|
.arg(parser->http_major)
|
|
.arg(parser->http_minor);
|
|
ilastResponse->d_func()->icustomStatusMessage = QString::fromUtf8(at, length);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
QHttpClientPrivate::headerField(http_parser*, const char* at, size_t length) {
|
|
if ( ilastResponse == nullptr )
|
|
return 0;
|
|
|
|
// insert the header we parsed previously
|
|
// into the header map
|
|
if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) {
|
|
// header names are always lower-cased
|
|
ilastResponse->d_func()->iheaders.insert(
|
|
itempHeaderField.toLower(),
|
|
itempHeaderValue.toLower()
|
|
);
|
|
// clear header value. this sets up a nice
|
|
// feedback loop where the next time
|
|
// HeaderValue is called, it can simply append
|
|
itempHeaderField.clear();
|
|
itempHeaderValue.clear();
|
|
}
|
|
|
|
itempHeaderField.append(at, length);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
QHttpClientPrivate::headerValue(http_parser*, const char* at, size_t length) {
|
|
|
|
itempHeaderValue.append(at, length);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
QHttpClientPrivate::headersComplete(http_parser*) {
|
|
if ( ilastResponse == nullptr )
|
|
return 0;
|
|
|
|
// Insert last remaining header
|
|
ilastResponse->d_func()->iheaders.insert(
|
|
itempHeaderField.toLower(),
|
|
itempHeaderValue.toLower()
|
|
);
|
|
|
|
if ( irespHandler )
|
|
irespHandler(ilastResponse);
|
|
else
|
|
q_func()->onResponseReady(ilastResponse);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
QHttpClientPrivate::body(http_parser*, const char* at, size_t length) {
|
|
if ( ilastResponse == nullptr )
|
|
return 0;
|
|
|
|
ilastResponse->d_func()->ireadState = QHttpResponsePrivate::EPartial;
|
|
|
|
if ( ilastResponse->d_func()->icollectRequired ) {
|
|
if ( !ilastResponse->d_func()->append(at, length) ) {
|
|
// forcefully dispatch the ilastResponse
|
|
finalizeConnection();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
emit ilastResponse->data(QByteArray(at, length));
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
QHttpClientPrivate::messageComplete(http_parser*) {
|
|
if ( ilastResponse == nullptr )
|
|
return 0;
|
|
|
|
// response is done
|
|
finalizeConnection();
|
|
return 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
} // namespace client
|
|
} // namespace qhttp
|
|
///////////////////////////////////////////////////////////////////////////////
|