QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgsfcgiserverresponse.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsfcgiserverresponse.cpp
3
4 Define response wrapper for fcgi response
5 -------------------
6 begin : 2017-01-03
7 copyright : (C) 2017 by David Marteau
8 email : david dot marteau at 3liz dot com
9 ***************************************************************************/
10
11/***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
20#include "qgis.h"
22#include "moc_qgsfcgiserverresponse.cpp"
23#include "qgsmessagelog.h"
24#include <fcgi_stdio.h>
25#include <QDebug>
26
27#include "qgslogger.h"
28
29#if defined( Q_OS_UNIX ) && !defined( Q_OS_ANDROID )
30#include <sys/types.h>
31#include <sys/socket.h>
32
33//
34// QgsFCGXStreamData copied from libfcgi FCGX_Stream_Data
35//
36typedef struct QgsFCGXStreamData
37{
38 unsigned char *buff; /* buffer after alignment */
39 int bufflen; /* number of bytes buff can store */
40 unsigned char *mBuff; /* buffer as returned by Malloc */
41 unsigned char *buffStop; /* reader: last valid byte + 1 of entire buffer.
42 * stop generally differs from buffStop for
43 * readers because of record structure.
44 * writer: buff + bufflen */
45 int type; /* reader: FCGI_PARAMS or FCGI_STDIN
46 * writer: FCGI_STDOUT or FCGI_STDERR */
47 int eorStop; /* reader: stop stream at end-of-record */
48 int skip; /* reader: don't deliver content bytes */
49 int contentLen; /* reader: bytes of unread content */
50 int paddingLen; /* reader: bytes of unread padding */
51 int isAnythingWritten; /* writer: data has been written to ipcFd */
52 int rawWrite; /* writer: write data without stream headers */
53 FCGX_Request *reqDataPtr; /* request data not specific to one stream */
54} QgsFCGXStreamData;
55#endif
56
58 : mIsResponseFinished( isResponseFinished )
59 , mFeedback( feedback )
60 , mIpcFd( -1 )
61{
62 setObjectName( "FCGI socket monitor" );
63 Q_ASSERT( mIsResponseFinished );
64 Q_ASSERT( mFeedback );
65
66#if defined( Q_OS_UNIX ) && !defined( Q_OS_ANDROID )
67 if ( FCGI_stdout && FCGI_stdout->fcgx_stream && FCGI_stdout->fcgx_stream->data )
68 {
69 QgsFCGXStreamData *stream = static_cast<QgsFCGXStreamData *>( FCGI_stdin->fcgx_stream->data );
70 if ( stream && stream->reqDataPtr )
71 {
72 mIpcFd = stream->reqDataPtr->ipcFd;
73 }
74 else
75 {
76 QgsMessageLog::logMessage( QStringLiteral( "FCGI_stdin stream data is null! Socket monitoring disable." ), QStringLiteral( "FCGIServer" ), Qgis::MessageLevel::Warning );
77 }
78 }
79 else
80 {
81 QgsMessageLog::logMessage( QStringLiteral( "FCGI_stdin is null! Socket monitoring disable." ), QStringLiteral( "FCGIServer" ), Qgis::MessageLevel::Warning );
82 }
83#endif
84}
85
87{
88 if ( mIpcFd < 0 )
89 {
90 QgsMessageLog::logMessage( QStringLiteral( "Socket monitoring disabled: no socket fd!" ), QStringLiteral( "FCGIServer" ), Qgis::MessageLevel::Warning );
91 return;
92 }
93
94#if defined( Q_OS_UNIX ) && !defined( Q_OS_ANDROID )
95 char c;
96 while ( !*mIsResponseFinished )
97 {
98 const ssize_t x = recv( mIpcFd, &c, 1, MSG_PEEK | MSG_DONTWAIT ); // see https://stackoverflow.com/a/12402596
99 if ( x < 0 )
100 {
101 // Ie. we are still connected but we have an 'error' as there is nothing to read
102 QgsDebugMsgLevel( QStringLiteral( "FCGIServer: remote socket still connected. errno: %1" ).arg( errno ), 5 );
103 }
104 else
105 {
106 // socket closed, nothing can be read
107 QgsDebugMsgLevel( QStringLiteral( "FCGIServer: remote socket has been closed! errno: %1" ).arg( errno ), 2 );
108 mFeedback->cancel();
109 break;
110 }
111
112 QThread::msleep( 333L );
113 }
114
115 if ( *mIsResponseFinished )
116 {
117 QgsDebugMsgLevel( QStringLiteral( "FCGIServer: socket monitoring quits normally." ), 2 );
118 }
119 else
120 {
121 QgsDebugMsgLevel( QStringLiteral( "FCGIServer: socket monitoring quits: no more socket." ), 2 );
122 }
123#endif
124}
125
126
127//
128// QgsFcgiServerResponse
129//
131 : mMethod( method )
132 , mFeedback( new QgsFeedback )
133{
134 mBuffer.open( QIODevice::ReadWrite );
136
137 mSocketMonitoringThread = std::make_unique<QgsSocketMonitoringThread>( &mFinished, mFeedback.get() );
138 mSocketMonitoringThread->start();
139}
140
142{
143 mFinished = true;
144 mSocketMonitoringThread->exit();
145 mSocketMonitoringThread->wait();
146}
147
148void QgsFcgiServerResponse::removeHeader( const QString &key )
149{
150 mHeaders.remove( key );
151}
152
153void QgsFcgiServerResponse::setHeader( const QString &key, const QString &value )
154{
155 mHeaders.insert( key, value );
156}
157
158QString QgsFcgiServerResponse::header( const QString &key ) const
159{
160 return mHeaders.value( key );
161}
162
164{
165 return mHeadersSent;
166}
167
169{
170 // fcgi applications must return HTTP status in header
171 mHeaders.insert( QStringLiteral( "Status" ), QStringLiteral( " %1" ).arg( code ) );
172 // Store the code to make it available for plugins
173 mStatusCode = code;
174}
175
176void QgsFcgiServerResponse::sendError( int code, const QString &message )
177{
178 if ( mHeadersSent )
179 {
180 QgsMessageLog::logMessage( "Cannot send error after headers written", QStringLiteral( "FCGIServer" ), Qgis::MessageLevel::Warning );
181 return;
182 }
183
184 clear();
185 setStatusCode( code );
186 setHeader( QStringLiteral( "Content-Type" ), QStringLiteral( "text/html;charset=utf-8" ) );
187 write( QStringLiteral( "<html><body>%1</body></html>" ).arg( message ) );
188 finish();
189}
190
192{
193 return &mBuffer;
194}
195
197{
198 if ( mFinished )
199 {
200 QgsMessageLog::logMessage( "finish() called twice", QStringLiteral( "FCGIServer" ), Qgis::MessageLevel::Warning );
201 return;
202 }
203
204 if ( mFeedback->isCanceled() )
205 {
206 clear(); // we clear all buffers as the socket is dead
207 FCGI_stdout->fcgx_stream->wasFCloseCalled = true; // avoid sending FCGI end protocol as the socket is dead
208 mFinished = true;
209 return;
210 }
211
212 if ( !mHeadersSent )
213 {
214 if ( !mHeaders.contains( "Content-Length" ) )
215 {
216 mHeaders.insert( QStringLiteral( "Content-Length" ), QString::number( mBuffer.pos() ) );
217 }
218 }
219 flush();
220 mFinished = true;
221}
222
224{
225 if ( !mHeadersSent )
226 {
227 // Send all headers
228 QMap<QString, QString>::const_iterator it;
229 for ( it = mHeaders.constBegin(); it != mHeaders.constEnd(); ++it )
230 {
231 fputs( it.key().toUtf8(), FCGI_stdout );
232 fputs( ": ", FCGI_stdout );
233 fputs( it.value().toUtf8(), FCGI_stdout );
234 fputs( "\n", FCGI_stdout );
235 }
236 fputs( "\n", FCGI_stdout );
237 mHeadersSent = true;
238 }
239
240 mBuffer.seek( 0 );
241 if ( mMethod == QgsServerRequest::HeadMethod )
242 {
243 // Ignore data for head method as we only
244 // write headers for HEAD requests
245 mBuffer.buffer().clear();
246 }
247 else if ( mBuffer.bytesAvailable() > 0 )
248 {
249 QByteArray &ba = mBuffer.buffer();
250 const size_t count = fwrite( ( void * ) ba.data(), ba.size(), 1, FCGI_stdout );
251#ifdef QGISDEBUG
252 qDebug() << QStringLiteral( "Sent %1 blocks of %2 bytes" ).arg( count ).arg( ba.size() );
253#else
254 Q_UNUSED( count )
255#endif
256 // Reset the internal buffer
257 ba.clear();
258 }
259}
260
261
263{
264 mHeaders.clear();
265 mBuffer.seek( 0 );
266 mBuffer.buffer().clear();
267
268 // Restore default headers
270}
271
272
274{
275 return mBuffer.data();
276}
277
278
280{
281 mBuffer.seek( 0 );
282 mBuffer.buffer().clear();
283}
284
285
287{
288 setHeader( QStringLiteral( "Server" ), QStringLiteral( " QGIS FCGI server - QGIS version %1" ).arg( Qgis::version() ) );
289}
static QString version()
Version string.
Definition qgis.cpp:259
@ Warning
Warning message.
Definition qgis.h:156
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 Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
void flush() override
Flushes the current output buffer to the network.
void removeHeader(const QString &key) override
Clear header Undo a previous 'setHeader' call.
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 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 the header value.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
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.
QgsSocketMonitoringThread(bool *isResponseFinished, QgsFeedback *feedback)
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)
Definition qgslogger.h:39