mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-15 00:07:25 -05:00
Server standalone: reimplement without signals
Because of WMS internal event loop that will crash on cascading.
This commit is contained in:
parent
5d678a57a9
commit
14b0c24d87
@ -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
@ -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
@ -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 );
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user