Server standalone: reimplement without signals

Because of WMS internal event loop that will
crash on cascading.
This commit is contained in:
Alessandro Pasotti 2020-10-20 11:54:07 +02:00
parent 5d678a57a9
commit 14b0c24d87
10 changed files with 59 additions and 67 deletions

View File

@ -1 +0,0 @@
dt[data-v-2c58573b]{font-weight:700}.leaflet-container[data-v-3c793242]{height:20rem}.card-footer .btn[data-v-3c793242]{margin-right:.5em}h4.loading[data-v-3c793242]{margin-top:.35em}.metadata[data-v-3c793242]{z-index:1001}.v-dialog{border-radius:4px;margin:24px;overflow-y:auto;pointer-events:auto;transition:.3s cubic-bezier(.25,.8,.25,1);width:100%;z-index:inherit;box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12)}.v-dialog:not(.v-dialog--fullscreen){max-height:90%}.v-dialog>*{width:100%}.v-dialog>.v-card>.v-card__title{font-size:1.25rem;font-weight:500;letter-spacing:.0125em;padding:16px 24px 10px}.v-dialog>.v-card>.v-card__subtitle,.v-dialog>.v-card>.v-card__text{padding:0 24px 20px}.v-dialog__content{align-items:center;display:flex;height:100%;justify-content:center;left:0;pointer-events:none;position:fixed;top:0;transition:.2s cubic-bezier(.25,.8,.25,1),z-index 1ms;width:100%;z-index:6;outline:none}.v-dialog__container{display:none}.v-dialog__container--attached{display:inline}.v-dialog--animated{-webkit-animation-duration:.15s;animation-duration:.15s;-webkit-animation-name:animate-dialog;animation-name:animate-dialog;-webkit-animation-timing-function:cubic-bezier(.25,.8,.25,1);animation-timing-function:cubic-bezier(.25,.8,.25,1)}.v-dialog--fullscreen{border-radius:0;margin:0;height:100%;position:fixed;overflow-y:auto;top:0;left:0}.v-dialog--fullscreen>.v-card{min-height:100%;min-width:100%;margin:0!important;padding:0!important}.v-dialog--scrollable,.v-dialog--scrollable>form{display:flex}.v-dialog--scrollable>.v-card,.v-dialog--scrollable>form>.v-card{display:flex;flex:1 1 100%;flex-direction:column;max-height:100%;max-width:100%}.v-dialog--scrollable>.v-card>.v-card__actions,.v-dialog--scrollable>.v-card>.v-card__title,.v-dialog--scrollable>form>.v-card>.v-card__actions,.v-dialog--scrollable>form>.v-card>.v-card__title{flex:0 0 auto}.v-dialog--scrollable>.v-card>.v-card__text,.v-dialog--scrollable>form>.v-card>.v-card__text{-webkit-backface-visibility:hidden;backface-visibility:hidden;flex:1 1 auto;overflow-y:auto}@-webkit-keyframes animate-dialog{0%{transform:scale(1)}50%{transform:scale(1.03)}to{transform:scale(1)}}@keyframes animate-dialog{0%{transform:scale(1)}50%{transform:scale(1.03)}to{transform:scale(1)}}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel=icon href=/favicon.ico><title>app</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link href=/css/chunk-123bc409.8679d8ba.css rel=prefetch><link href=/css/chunk-144cd10c.61ac68ab.css rel=prefetch><link href=/css/chunk-fd085c1c.a76e3223.css rel=prefetch><link href=/js/chunk-123bc409.ef3c4d35.js rel=prefetch><link href=/js/chunk-144cd10c.517e8bc2.js rel=prefetch><link href=/js/chunk-fd085c1c.83a1da86.js rel=prefetch><link href=/css/app.ca3f5643.css rel=preload as=style><link href=/css/chunk-vendors.a728f495.css rel=preload as=style><link href=/js/app.e63a21da.js rel=preload as=script><link href=/js/chunk-vendors.ebebb286.js rel=preload as=script><link href=/css/chunk-vendors.a728f495.css rel=stylesheet><link href=/css/app.ca3f5643.css rel=stylesheet></head><body><noscript><strong>We're sorry but app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.ebebb286.js></script><script src=/js/app.e63a21da.js></script></body></html>
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel=icon href=/favicon.ico><title>app</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link href=/css/chunk-123bc409.8679d8ba.css rel=prefetch><link href=/css/chunk-78e1f0b8.8e24811f.css rel=prefetch><link href=/css/chunk-db289a14.336091d0.css rel=prefetch><link href=/js/chunk-123bc409.ef3c4d35.js rel=prefetch><link href=/js/chunk-78e1f0b8.041cba9a.js rel=prefetch><link href=/js/chunk-db289a14.4c4313be.js rel=prefetch><link href=/css/app.ca3f5643.css rel=preload as=style><link href=/css/chunk-vendors.a728f495.css rel=preload as=style><link href=/js/app.fbc6732c.js rel=preload as=script><link href=/js/chunk-vendors.ebebb286.js rel=preload as=script><link href=/css/chunk-vendors.a728f495.css rel=stylesheet><link href=/css/app.ca3f5643.css rel=stylesheet></head><body><noscript><strong>We're sorry but app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.ebebb286.js></script><script src=/js/app.fbc6732c.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -50,6 +50,10 @@ while QGIS server internal logging is printed to stderr.
///@cond PRIVATE
// For the signal exit handler
QAtomicInt IS_RUNNING = 1;
/**
* The HttpException class represents an HTTP parsing exception.
*/
@ -239,51 +243,45 @@ int main( int argc, char *argv[] )
std::cout << QObject::tr( "CTRL+C to exit" ).toStdString() << std::endl;
#endif
// Starts HTTP loop with a poor man's HTTP parser
tcpServer.connect( &tcpServer, &QTcpServer::newConnection, [ & ]
// Poor man's synchronous HTTP handler
// The reason why this cannnot be implemented using signals is that
// WMS provider (and probably others) run its own event loop and this
// crashes in case the project contains a WMS layer (aka: cascading)
auto httpHandler = [ & ]( QTcpSocket * clientConnection )
{
QTcpSocket *clientConnection = tcpServer.nextPendingConnection();
connCounter++;
QString *incomingData = new QString();
//qDebug() << clientConnection << "Active connection" << connCounter;
//qDebug() << "Active connections: " << connCounter;
// Lambda disconnect context
QObject *context { new QObject };
// Deletes the connection later
auto connectionDeleter = [ =, &connCounter ]()
{
clientConnection->deleteLater();
connCounter--;
delete incomingData;
};
// This will delete the connection when disconnected before ready read is called
clientConnection->connect( clientConnection, &QAbstractSocket::disconnected, context, connectionDeleter, Qt::QueuedConnection );
QString incomingData;
// Incoming connection parser
clientConnection->connect( clientConnection, &QIODevice::readyRead, context, [ =, &server, &connCounter ] {
while ( IS_RUNNING && clientConnection->state() == QAbstractSocket::SocketState::ConnectedState )
{
if ( ! clientConnection->bytesAvailable() )
{
qApp->processEvents();
continue;
}
// Read all incoming data
while ( clientConnection->bytesAvailable() > 0 )
while ( IS_RUNNING && clientConnection->bytesAvailable() > 0 )
{
incomingData->append( clientConnection->readAll() );
incomingData.append( clientConnection->readAll() );
}
try
{
// Parse protocol and URL GET /path HTTP/1.1
int firstLinePos { incomingData->indexOf( "\r\n" ) };
int firstLinePos { incomingData.indexOf( "\r\n" ) };
if ( firstLinePos == -1 )
{
throw HttpException( QStringLiteral( "HTTP error finding protocol header" ) );
}
const QString firstLine { incomingData->left( firstLinePos ) };
const QString firstLine { incomingData.left( firstLinePos ) };
const QStringList firstLinePieces { firstLine.split( ' ' ) };
if ( firstLinePieces.size() != 3 )
{
@ -330,14 +328,14 @@ int main( int argc, char *argv[] )
// Headers
QgsBufferServerRequest::Headers headers;
int endHeadersPos { incomingData->indexOf( "\r\n\r\n" ) };
int endHeadersPos { incomingData.indexOf( "\r\n\r\n" ) };
if ( endHeadersPos == -1 )
{
throw HttpException( QStringLiteral( "HTTP error finding headers" ) );
}
const QStringList httpHeaders { incomingData->mid( firstLinePos + 2, endHeadersPos - firstLinePos ).split( "\r\n" ) };
const QStringList httpHeaders { incomingData.mid( firstLinePos + 2, endHeadersPos - firstLinePos ).split( "\r\n" ) };
for ( const auto &headerLine : httpHeaders )
{
@ -355,15 +353,13 @@ int main( int argc, char *argv[] )
{
bool ok;
const int contentLength { headers.value( QStringLiteral( "Content-Length" ) ).toInt( &ok ) };
if ( ok && contentLength > incomingData->length() - headersSize )
if ( ok && contentLength > incomingData.length() - headersSize )
{
return;
break;
}
}
// At this point we should have read all data:
// disconnect the lambdas
delete context;
// Build URL from env ...
QString url { qgetenv( "REQUEST_URI" ) };
@ -383,7 +379,7 @@ int main( int argc, char *argv[] )
}
// Inefficient copy :(
QByteArray data { incomingData->mid( headersSize ).toUtf8() };
QByteArray data { incomingData.mid( headersSize ).toUtf8() };
auto start = std::chrono::steady_clock::now();
@ -394,17 +390,9 @@ int main( int argc, char *argv[] )
// The QGIS server machinery calls processEvents and has internal loop events
// that might change the connection state
if ( clientConnection->state() == QAbstractSocket::SocketState::ConnectedState )
if ( clientConnection->state() != QAbstractSocket::SocketState::ConnectedState )
{
clientConnection->connect( clientConnection, &QAbstractSocket::disconnected,
clientConnection, connectionDeleter, Qt::QueuedConnection );
}
else
{
connCounter --;
clientConnection->deleteLater();
delete incomingData;
return;
break;
}
auto elapsedTime { std::chrono::steady_clock::now() - start };
@ -442,17 +430,9 @@ int main( int argc, char *argv[] )
catch ( HttpException &ex )
{
if ( clientConnection->state() == QAbstractSocket::SocketState::ConnectedState )
if ( clientConnection->state() != QAbstractSocket::SocketState::ConnectedState )
{
clientConnection->connect( clientConnection, &QAbstractSocket::disconnected,
clientConnection, connectionDeleter );
}
else
{
connCounter --;
clientConnection->deleteLater();
delete incomingData;
return;
break;
}
// Output stream: send error
@ -467,12 +447,34 @@ int main( int argc, char *argv[] )
.arg( ex.message() ).toStdString() << std::endl;
clientConnection->disconnectFromHost();
}
};
}, Qt::QueuedConnection );
clientConnection->deleteLater();
connCounter--;
};
// Starts HTTP handler loop
QTimer::singleShot( 0, [ & ]
{
while ( IS_RUNNING )
{
if ( tcpServer.hasPendingConnections() )
{
QTcpSocket *clientConnection = tcpServer.nextPendingConnection();
if ( clientConnection )
{
httpHandler( clientConnection );
}
}
else
{
qApp->processEvents( );
}
}
} );
}
// Exit handlers
@ -481,7 +483,8 @@ int main( int argc, char *argv[] )
auto exitHandler = [ ]( int signal )
{
std::cout << QStringLiteral( "Signal %1 received: quitting" ).arg( signal ).toStdString() << std::endl;
qApp->quit();
IS_RUNNING = 0;
qApp->quit( );
};
signal( SIGTERM, exitHandler );