5 #include "protocolfastcgi.h"
10 #include <Cutelyst/Context>
13 #include <QCoreApplication>
14 #include <QLoggingCategory>
15 #include <QTemporaryFile>
17 Q_LOGGING_CATEGORY(C_SERVER_FCGI,
"cutelyst.server.fcgi", QtWarningMsg)
22 #define FCGI_LISTENSOCK_FILENO 0
28 #define FCGI_HEADER_LEN 8
33 #define FCGI_VERSION_1 1
38 #define FCGI_BEGIN_REQUEST 1
39 #define FCGI_ABORT_REQUEST 2
40 #define FCGI_END_REQUEST 3
46 #define FCGI_GET_VALUES 9
47 #define FCGI_GET_VALUES_RESULT 10
48 #define FCGI_UNKNOWN_TYPE 11
49 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
54 #define FCGI_NULL_REQUEST_ID 0
59 #define FCGI_KEEP_CONN 1
64 #define FCGI_RESPONDER 1
65 #define FCGI_AUTHORIZER 2
71 #define FCGI_REQUEST_COMPLETE 0
72 #define FCGI_CANT_MPX_CONN 1
73 #define FCGI_OVERLOADED 2
74 #define FCGI_UNKNOWN_ROLE 3
79 #define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
80 #define FCGI_MAX_REQS "FCGI_MAX_REQS"
81 #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
88 #define FCGI_ALIGNMENT 8
89 #define FCGI_ALIGN(n) (((n) + (FCGI_ALIGNMENT - 1)) & ~(FCGI_ALIGNMENT - 1))
117 __attribute__((__packed__));
120 ProtocolFastCGI::ProtocolFastCGI(
Server *wsgi)
125 ProtocolFastCGI::~ProtocolFastCGI()
129 Protocol::Type ProtocolFastCGI::type()
const
131 return Protocol::Type::FastCGI1;
138 quint16 vallen)
const
140 char *buffer = request->buffer + request->pktsize;
141 char *watermark = request->buffer + m_bufferSize;
143 if (buffer + keylen + vallen + 2 + 2 >= watermark) {
144 qCWarning(C_SERVER_FCGI,
145 "unable to add %.*s=%.*s to wsgi packet, consider increasing buffer size",
153 if (keylen > 5 && memcmp(key,
"HTTP_", 5) == 0) {
155 if (!request->headerHost && memcmp(key + 5,
"HOST", 4) == 0) {
157 request->headerHost =
true;
163 }
else if (memcmp(key,
"REQUEST_METHOD", 14) == 0) {
165 }
else if (memcmp(key,
"REQUEST_URI", 11) == 0) {
166 const char *pch =
static_cast<const char *
>(memchr(val,
'?', vallen));
168 int pos = int(pch - val);
169 request->
setPath(
const_cast<char *
>(val), pos);
172 request->
setPath(
const_cast<char *
>(val), vallen);
175 }
else if (memcmp(key,
"SERVER_PROTOCOL", 15) == 0) {
177 }
else if (memcmp(key,
"REMOTE_ADDR", 11) == 0) {
179 }
else if (memcmp(key,
"REMOTE_PORT", 11) == 0) {
181 }
else if (memcmp(key,
"CONTENT_TYPE", 12) == 0) {
185 }
else if (memcmp(key,
"CONTENT_LENGTH", 14) == 0) {
187 }
else if (memcmp(key,
"REQUEST_SCHEME", 14) == 0) {
195 return keylen + vallen + 2 + 2;
198 int ProtocolFastCGI::parseHeaders(
ProtoRequestFastCGI *request,
const char *buf, quint16 len)
const
202 quint32 keylen, vallen;
203 quint8 octet =
static_cast<quint8
>(buf[j]);
209 keylen = net_be32(&buf[j]) ^ 0x80000000;
217 octet =
static_cast<quint8
>(buf[j]);
223 vallen = net_be32(&buf[j]) ^ 0x80000000;
231 if (j + (keylen + vallen) > len || keylen > 0xffff || vallen > 0xffff) {
236 addHeader(request, buf + j, quint16(keylen), buf + j + keylen, quint16(vallen));
239 request->pktsize += pktsize;
241 j += keylen + vallen;
251 if (request->buf_size >=
int(
sizeof(
struct fcgi_record))) {
252 auto fr =
reinterpret_cast<struct
fcgi_record *
>(request->buffer);
254 quint8 fcgi_type = fr->type;
255 quint16 fcgi_len = quint16(fr->cl0 | (fr->cl1 << 8));
256 qint32 fcgi_all_len =
sizeof(
struct fcgi_record) + fcgi_len + fr->pad;
257 request->stream_id = quint16(fr->req0 | (fr->req1 << 8));
260 if (fcgi_type == FCGI_STDIN) {
262 memmove(request->buffer,
263 request->buffer + fcgi_all_len,
264 size_t(request->buf_size - fcgi_all_len));
265 request->buf_size -= fcgi_all_len;
269 int content_size = request->buf_size - int(
sizeof(
struct fcgi_record));
270 if (!writeBody(request,
272 qMin(content_size,
int(fcgi_len)))) {
276 if (content_size < fcgi_len) {
278 request->connState = ProtoRequestFastCGI::ContentBody;
279 request->pktsize = quint16(fcgi_len - content_size);
280 request->buf_size = fr->pad;
284 memmove(request->buffer,
285 request->buffer + fcgi_all_len,
286 size_t(request->buf_size - fcgi_all_len));
287 request->buf_size -= fcgi_all_len;
288 }
else if (request->buf_size >= fcgi_all_len) {
290 if (fcgi_type == FCGI_PARAMS) {
292 request, request->buffer +
sizeof(
struct fcgi_record), fcgi_len)) {
295 }
else if (fcgi_type == FCGI_BEGIN_REQUEST) {
298 request->headerConnection = (brb->flags & FCGI_KEEP_CONN)
299 ? ProtoRequestFastCGI::HeaderConnection::Keep
300 : ProtoRequestFastCGI::HeaderConnection::Close;
301 request->contentLength = -1;
303 request->connState = ProtoRequestFastCGI::MethodLine;
306 memmove(request->buffer,
307 request->buffer + fcgi_all_len,
308 size_t(request->buf_size - fcgi_all_len));
309 request->buf_size -= fcgi_all_len;
322 if (!request->
body) {
323 request->
body = createBody(request->contentLength);
324 if (!request->
body) {
329 return request->
body->
write(buf, len) == len;
332 qint64 ProtocolFastCGI::readBody(
Socket *sock,
QIODevice *io, qint64 bytesAvailable)
const
336 int &pad = request->buf_size;
337 while (bytesAvailable && request->pktsize + pad) {
339 qint64 len = io->
read(m_postBuffer,
340 qMin(m_postBufferSize,
static_cast<qint64
>(request->pktsize + pad)));
342 sock->connectionClose();
345 bytesAvailable -= len;
347 if (len > request->pktsize) {
349 pad -= len - request->pktsize;
350 len = request->pktsize;
351 request->pktsize = 0;
353 request->pktsize -= len;
356 body->
write(m_postBuffer, len);
359 if (request->pktsize + pad == 0) {
360 request->connState = ProtoRequestFastCGI::MethodLine;
363 return bytesAvailable;
370 if (request->
status & Cutelyst::EngineRequest::Async) {
375 if (request->connState == ProtoRequestFastCGI::ContentBody) {
376 bytesAvailable = readBody(sock, io, bytesAvailable);
377 if (bytesAvailable == -1) {
384 io->
read(request->buffer + request->buf_size, m_bufferSize - request->buf_size);
385 bytesAvailable -= len;
388 request->buf_size += len;
394 if (request->buf_size <
int(
sizeof(
struct fcgi_record))) {
399 int ret = processPacket(request);
400 if (ret == WSGI_AGAIN) {
402 }
else if (ret == WSGI_OK) {
408 if (request->
status & Cutelyst::EngineRequest::Async) {
411 }
else if (ret == WSGI_BODY) {
412 bytesAvailable = readBody(sock, io, bytesAvailable);
413 if (bytesAvailable == -1) {
417 qCWarning(C_SERVER_FCGI) <<
"Failed to parse packet from"
418 << sock->remoteAddress.
toString() << sock->remotePort;
423 qCWarning(C_SERVER_FCGI) <<
"Failed to read from socket" << io->
errorString();
426 }
while (bytesAvailable);
434 ProtoRequestFastCGI::ProtoRequestFastCGI(
Socket *sock,
int bufferSize)
439 ProtoRequestFastCGI::~ProtoRequestFastCGI()
443 void ProtoRequestFastCGI::setupNewConnection(
Socket *sock)
463 bool hasDate =
false;
464 auto it = headersData.begin();
465 while (it != headersData.end()) {
470 headerBuffer.append(
"\r\n");
471 headerBuffer.append(it->key);
472 headerBuffer.append(
": ");
473 headerBuffer.append(it->value);
479 headerBuffer.append(
static_cast<ServerEngine *
>(sock->engine)->lastDate());
481 headerBuffer.append(
"\r\n\r\n", 4);
483 return doWrite(headerBuffer.constData(), headerBuffer.size()) != -1;
489 qint64 write_pos = 0;
490 quint32 proto_parser_status = 0;
497 if (proto_parser_status == 0) {
499 if (len - write_pos < 0xffff) {
500 fcgi_len = quint16(len - write_pos);
504 proto_parser_status = fcgi_len;
507 fr.version = FCGI_VERSION_1;
508 fr.type = FCGI_STDOUT;
510 fr.req1 = quint8(stream_id >> 8);
511 fr.req0 = quint8(stream_id);
513 quint16 padded_len = FCGI_ALIGN(fcgi_len);
514 if (padded_len > fcgi_len) {
515 padding = quint8(padded_len - fcgi_len);
520 fr.cl1 = quint8(fcgi_len >> 8);
521 fr.cl0 = quint8(fcgi_len);
522 if (io->
write(
reinterpret_cast<const char *
>(&fr),
sizeof(
struct fcgi_record)) !=
528 qint64 wlen = io->
write(data + write_pos, proto_parser_status);
530 io->
write(
"\0\0\0\0\0\0\0\0\0", padding);
535 proto_parser_status -= wlen;
536 if (write_pos == len) {
542 qCWarning(C_SERVER_FCGI) <<
"Writing socket error" << io->
errorString();
548 #define FCGI_END_REQUEST_DATA "\1\x06\0\1\0\0\0\0\1\3\0\1\0\x08\0\0\0\0\0\0\0\0\0\0"
552 char end_request[] = FCGI_END_REQUEST_DATA;
553 char *sid =
reinterpret_cast<char *
>(&stream_id);
555 end_request[2] = sid[1];
556 end_request[3] = sid[0];
557 end_request[10] = sid[1];
558 end_request[11] = sid[0];
559 io->
write(end_request, 24);
561 if (!sock->requestFinished()) {
566 if (headerConnection == ProtoRequestFastCGI::HeaderConnection::Close) {
568 sock->connectionClose();
572 const auto size = buf_size;
576 if (
status & EngineRequest::Async && buf_size) {
577 sock->proto->parse(sock, io);
581 #include "moc_protocolfastcgi.cpp"
void setPath(char *rawPath, const int len)
QHostAddress remoteAddress
TimePointSteady startOfRequest
void processRequest(EngineRequest *request)
bool writeHeaders(quint16 status, const Cutelyst::Headers &headers) override final
qint64 doWrite(const char *data, qint64 len) override final
void processingFinished() override final
The Cutelyst namespace holds all public Cutelyst API.
QByteArray number(double n, char format, int precision)
QByteArray & replace(QByteArrayView before, QByteArrayView after)
void reserve(qsizetype size)
void resize(qsizetype newSize, char c)
int toInt(bool *ok, int base) const const
bool setAddress(const QString &address)
QString toString() const const
virtual qint64 bytesAvailable() const const
QString errorString() const const
QByteArray read(qint64 maxSize)
virtual bool seek(qint64 pos)
qint64 write(const QByteArray &data)
QString fromLatin1(QByteArrayView str)