30#include <fcgi_stdio.h>
32using namespace Qt::StringLiterals;
34#if defined( Q_OS_UNIX ) && !defined( Q_OS_ANDROID )
37#include <sys/socket.h>
43typedef struct QgsFCGXStreamData
48 unsigned char *buffStop;
58 int isAnythingWritten;
60 FCGX_Request *reqDataPtr;
65using namespace std::chrono_literals;
70 : mFeedback( std::move( feedback ) )
72 Q_ASSERT( mFeedback );
74 mShouldStop.store(
false );
76#if defined( Q_OS_UNIX ) && !defined( Q_OS_ANDROID )
77 if ( FCGI_stdout && FCGI_stdout->fcgx_stream && FCGI_stdout->fcgx_stream->data )
79 QgsFCGXStreamData *stream =
static_cast<QgsFCGXStreamData *
>( FCGI_stdout->fcgx_stream->data );
80 if ( stream && stream->reqDataPtr )
82 mIpcFd = stream->reqDataPtr->ipcFd;
87 u
"FCGI_stdout stream data is null! Socket monitoring disabled."_s,
96 u
"FCGI_stdout is null! Socket monitoring disabled."_s,
107 mShouldStop.store(
true );
124#if defined( Q_OS_UNIX ) && !defined( Q_OS_ANDROID )
125 quint64 threadId =
reinterpret_cast<quint64
>( QThread::currentThreadId() );
130 FD_ZERO( &setOptions );
131 FD_SET( mIpcFd, &setOptions );
133 struct timeval timeout;
135 timeout.tv_usec = 10000;
137 while ( !mShouldStop.load() )
141 int rv = select( mIpcFd + 1, &setOptions,
nullptr,
nullptr, &timeout );
146 u
"FCGIServer %1: remote socket has been closed (select)! errno: %2"_s
159 const ssize_t x = recv( mIpcFd, &
c, 1, MSG_PEEK | MSG_DONTWAIT );
164 u
"FCGIServer %1: remote socket still connected. errno: %2, x: %3"_s
175 u
"FCGIServer %1: remote socket has been closed (recv)! errno: %2, x: %3"_s
189 if ( mMutex.try_lock_for( 333ms ) )
193 if ( mShouldStop.load() )
200 u
"FCGIServer::run %1: socket monitoring quits: no more socket."_s
217 mBuffer.open( QIODevice::ReadWrite );
220 mSocketMonitoringThread = std::make_unique<QgsSocketMonitoringThread>( mFeedback );
231 mSocketMonitoringThread->stop();
239 mHeaders.remove( key );
246 if ( !mHeaders.contains( key ) )
248 mHeaders[key] = QList<QString>();
250 mHeaders[key].append( value );
257 mHeaders[key] = QList<QString>() << value;
262 const QList<QString> values = mHeaders.value( key );
263 return values.isEmpty() ? QString() : values.last();
268 QMap<QString, QString> singleHeaders;
269 for (
auto it = mHeaders.keyBegin(); it != mHeaders.keyEnd(); ++it )
271 singleHeaders.insert( *it,
header( *it ) );
273 return singleHeaders;
278 return mHeaders.value( key );
290 setHeader( u
"Status"_s, u
" %1"_s.arg( code ) );
305 setHeader( u
"Content-Type"_s, u
"text/html;charset=utf-8"_s );
306 write( u
"<html><body>%1</body></html>"_s.arg( message ) );
323 if ( mFeedback->isCanceled() )
326 FCGI_stdout->fcgx_stream->wasFCloseCalled =
true;
333 if ( !mHeaders.contains(
"Content-Length" ) )
335 setHeader( u
"Content-Length"_s, QString::number( mBuffer.pos() ) );
347 QMap<QString, QList<QString>>::const_iterator it;
348 for ( it = mHeaders.constBegin(); it != mHeaders.constEnd(); ++it )
350 for (
const QString &headerValue : std::as_const( it.value() ) )
352 fputs( it.key().toUtf8(), FCGI_stdout );
353 fputs(
": ", FCGI_stdout );
354 fputs( headerValue.toUtf8(), FCGI_stdout );
355 fputs(
"\n", FCGI_stdout );
358 fputs(
"\n", FCGI_stdout );
367 mBuffer.buffer().clear();
369 else if ( mBuffer.bytesAvailable() > 0 )
371 QByteArray &ba = mBuffer.buffer();
372 const size_t count = fwrite( (
void * ) ba.data(), ba.size(), 1, FCGI_stdout );
374 qDebug() << u
"Sent %1 blocks of %2 bytes"_s.arg( count ).arg( ba.size() );
388 mBuffer.buffer().clear();
394 return mBuffer.data();
401 mBuffer.buffer().clear();
static QString version()
Version string.
@ Warning
Warning message.
@ Info
Information message.
void setDefaultHeaders()
Set the default headers.
void clear() override
Reset all headers and content for this response.
void setHeader(const QString &key, const QString &value) override
Set a single header value replacing any existing value(s) for the same key.
void flush() override
Flushes the current output buffer to the network.
~QgsFcgiServerResponse() override
QList< QString > fullHeader(const QString &key) const override
Returns a (possibly empty) list of all the header values for the given key.
void removeHeader(const QString &key) override
Clear all header values for the given key Undo a previous 'setHeader' call.
QMap< QString, QString > headers() const override
Returns the header values as a map: only the last value is returned if multiple values are set for th...
QByteArray data() const override
Gets the data written so far.
QIODevice * io() override
Returns the underlying QIODevice.
bool headersSent() const override
Returns true if the headers have already been sent.
void setStatusCode(int code) override
Set the http status code.
QgsFcgiServerResponse(QgsServerRequest::Method method=QgsServerRequest::GetMethod)
Constructor for QgsFcgiServerResponse.
void addHeader(const QString &key, const QString &value) override
Add a header value for the given key, without replacing any existing value for the same key Add Heade...
void sendError(int code, const QString &message) override
Send error This method delegates error handling at the server level.
void truncate() override
Truncate data.
void finish() override
Finish the response, ending the transaction.
QString header(const QString &key) const override
Returns a single header value for a given key.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Method
HTTP Method (or equivalent) used for the request.
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
void run()
main thread function
void stop()
Stop the thread.
QgsSocketMonitoringThread(std::shared_ptr< QgsFeedback > feedback)
Constructor for QgsSocketMonitoringThread.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define QgsDebugMsgLevel(str, level)