5#include "protocolfastcgi.h"
10#include <Cutelyst/Context>
13#include <QCoreApplication>
14#include <QLoggingCategory>
15#include <QTemporaryFile>
17Q_LOGGING_CATEGORY(C_SERVER_FCGI,
"cutelyst.server.fcgi", QtWarningMsg)
23constexpr auto FCGI_VERSION_1 = 1;
28constexpr auto FCGI_BEGIN_REQUEST = 1;
29constexpr auto FCGI_PARAMS = 4;
30constexpr auto FCGI_STDIN = 5;
31constexpr auto FCGI_STDOUT = 6;
36constexpr auto FCGI_KEEP_CONN = 1;
38constexpr auto WSGI_OK = 0;
39constexpr auto WSGI_AGAIN = 1;
40constexpr auto WSGI_BODY = 2;
41constexpr auto WSGI_ERROR = -1;
43#define FCGI_ALIGNMENT 8
44#define FCGI_ALIGN(n) (((n) + (FCGI_ALIGNMENT - 1)) & ~(FCGI_ALIGNMENT - 1))
61struct fcgi_begin_request_body {
70__attribute__((__packed__));
76ProtocolFastCGI::ProtocolFastCGI(
Server *wsgi)
81ProtocolFastCGI::~ProtocolFastCGI()
85Protocol::Type ProtocolFastCGI::type()
const
87 return Protocol::Type::FastCGI1;
96 char *buffer = request->buffer + request->pktsize;
97 char *watermark = request->buffer + m_bufferSize;
99 if (buffer + keylen + vallen + 2 + 2 >= watermark) {
100 qCWarning(C_SERVER_FCGI,
101 "unable to add %.*s=%.*s to wsgi packet, consider increasing buffer size",
109 if (keylen > 5 && memcmp(key,
"HTTP_", 5) == 0) {
111 if (!request->headerHost && memcmp(key + 5,
"HOST", 4) == 0) {
113 request->headerHost =
true;
119 }
else if (memcmp(key,
"REQUEST_METHOD", 14) == 0) {
121 }
else if (memcmp(key,
"REQUEST_URI", 11) == 0) {
122 const char *pch =
static_cast<const char *
>(memchr(val,
'?', vallen));
124 int pos = int(pch - val);
125 request->
setPath(
const_cast<char *
>(val), pos);
128 request->
setPath(
const_cast<char *
>(val), vallen);
131 }
else if (memcmp(key,
"SERVER_PROTOCOL", 15) == 0) {
133 }
else if (memcmp(key,
"REMOTE_ADDR", 11) == 0) {
135 }
else if (memcmp(key,
"REMOTE_PORT", 11) == 0) {
137 }
else if (memcmp(key,
"CONTENT_TYPE", 12) == 0) {
141 }
else if (memcmp(key,
"CONTENT_LENGTH", 14) == 0) {
143 }
else if (memcmp(key,
"REQUEST_SCHEME", 14) == 0) {
151 return keylen + vallen + 2 + 2;
154int ProtocolFastCGI::parseHeaders(
ProtoRequestFastCGI *request,
const char *buf, quint16 len)
const
159 auto octet =
static_cast<quint8
>(buf[j]);
166 keylen = net_be32(&buf[j]) ^ 0x80000000;
176 octet =
static_cast<quint8
>(buf[j]);
183 vallen = net_be32(&buf[j]) ^ 0x80000000;
192 if (j + (keylen + vallen) > len || keylen > 0xffff || vallen > 0xffff) {
197 addHeader(request, buf + j, quint16(keylen), buf + j + keylen, quint16(vallen));
201 request->pktsize += pktsize;
203 j += keylen + vallen;
213 if (request->buf_size >=
int(
sizeof(
struct fcgi_record))) {
214 auto fr =
reinterpret_cast<struct fcgi_record *
>(request->buffer);
216 quint8 fcgi_type = fr->type;
217 auto fcgi_len = quint16(fr->cl0 | (fr->cl1 << 8));
218 qint32 fcgi_all_len =
sizeof(
struct fcgi_record) + fcgi_len + fr->pad;
219 request->stream_id = quint16(fr->req0 | (fr->req1 << 8));
222 if (fcgi_type == FCGI_STDIN) {
224 memmove(request->buffer,
225 request->buffer + fcgi_all_len,
226 size_t(request->buf_size - fcgi_all_len));
227 request->buf_size -= fcgi_all_len;
231 int content_size = request->buf_size - int(
sizeof(
struct fcgi_record));
232 if (!writeBody(request,
233 request->buffer +
sizeof(
struct fcgi_record),
234 qMin(content_size,
int(fcgi_len)))) {
238 if (content_size < fcgi_len) {
240 request->connState = ProtoRequestFastCGI::ContentBody;
241 request->pktsize = quint16(fcgi_len - content_size);
242 request->buf_size = fr->pad;
246 memmove(request->buffer,
247 request->buffer + fcgi_all_len,
248 size_t(request->buf_size - fcgi_all_len));
249 request->buf_size -= fcgi_all_len;
250 }
else if (request->buf_size >= fcgi_all_len) {
252 if (fcgi_type == FCGI_PARAMS) {
254 request, request->buffer +
sizeof(
struct fcgi_record), fcgi_len)) {
257 }
else if (fcgi_type == FCGI_BEGIN_REQUEST) {
258 auto brb =
reinterpret_cast<struct fcgi_begin_request_body *
>(
259 request->buffer +
sizeof(
struct fcgi_begin_request_body));
260 request->headerConnection = (brb->flags & FCGI_KEEP_CONN)
261 ? ProtoRequestFastCGI::HeaderConnection::Keep
263 request->contentLength = -1;
265 request->connState = ProtoRequestFastCGI::MethodLine;
268 memmove(request->buffer,
269 request->buffer + fcgi_all_len,
270 size_t(request->buf_size - fcgi_all_len));
271 request->buf_size -= fcgi_all_len;
284 if (!request->
body) {
285 request->
body = createBody(request->contentLength);
286 if (!request->
body) {
291 return request->
body->
write(buf, len) == len;
294qint64 ProtocolFastCGI::readBody(
Socket *sock,
QIODevice *io, qint64 bytesAvailable)
const
298 int &pad = request->buf_size;
299 while (bytesAvailable && request->pktsize + pad) {
301 qint64 len = io->
read(m_postBuffer,
302 qMin(m_postBufferSize,
static_cast<qint64
>(request->pktsize + pad)));
304 sock->connectionClose();
307 bytesAvailable -= len;
309 if (len > request->pktsize) {
311 pad -= len - request->pktsize;
312 len = request->pktsize;
313 request->pktsize = 0;
315 request->pktsize -= len;
318 body->
write(m_postBuffer, len);
321 if (request->pktsize + pad == 0) {
322 request->connState = ProtoRequestFastCGI::MethodLine;
325 return bytesAvailable;
332 if (request->
status & Cutelyst::EngineRequest::Async) {
337 if (request->connState == ProtoRequestFastCGI::ContentBody) {
338 bytesAvailable = readBody(sock, io, bytesAvailable);
339 if (bytesAvailable == -1) {
346 io->
read(request->buffer + request->buf_size, m_bufferSize - request->buf_size);
347 bytesAvailable -= len;
350 request->buf_size += len;
356 if (request->buf_size <
int(
sizeof(
struct fcgi_record))) {
361 int ret = processPacket(request);
362 if (ret == WSGI_AGAIN) {
364 }
else if (ret == WSGI_OK) {
370 if (request->
status & Cutelyst::EngineRequest::Async) {
373 }
else if (ret == WSGI_BODY) {
374 bytesAvailable = readBody(sock, io, bytesAvailable);
375 if (bytesAvailable == -1) {
379 qCWarning(C_SERVER_FCGI) <<
"Failed to parse packet from"
380 << sock->remoteAddress.
toString() << sock->remotePort;
385 qCWarning(C_SERVER_FCGI) <<
"Failed to read from socket" << io->
errorString();
388 }
while (bytesAvailable);
396ProtoRequestFastCGI::ProtoRequestFastCGI(
Socket *sock,
int bufferSize)
401ProtoRequestFastCGI::~ProtoRequestFastCGI()
405void ProtoRequestFastCGI::setupNewConnection(
Socket *sock)
425 bool hasDate =
false;
426 for (
const auto &[key, value] : headersData) {
431 headerBuffer.
append(
"\r\n");
433 headerBuffer.
append(
": ");
434 headerBuffer.
append(value);
440 headerBuffer.
append(
"\r\n\r\n", 4);
448 qint64 write_pos = 0;
449 quint32 proto_parser_status = 0;
456 if (proto_parser_status == 0) {
458 if (len - write_pos < 0xffff) {
459 fcgi_len = quint16(len - write_pos);
463 proto_parser_status = fcgi_len;
465 struct fcgi_record fr;
466 fr.version = FCGI_VERSION_1;
467 fr.type = FCGI_STDOUT;
469 fr.req1 = quint8(stream_id >> 8);
470 fr.req0 = quint8(stream_id);
472 quint16 padded_len = FCGI_ALIGN(fcgi_len);
473 if (padded_len > fcgi_len) {
474 padding = quint8(padded_len - fcgi_len);
479 fr.cl1 = quint8(fcgi_len >> 8);
480 fr.cl0 = quint8(fcgi_len);
481 if (io->
write(
reinterpret_cast<const char *
>(&fr),
sizeof(
struct fcgi_record)) !=
482 sizeof(
struct fcgi_record)) {
487 qint64 wlen = io->
write(data + write_pos, proto_parser_status);
489 io->
write(
"\0\0\0\0\0\0\0\0\0", padding);
494 proto_parser_status -= wlen;
495 if (write_pos == len) {
501 qCWarning(C_SERVER_FCGI) <<
"Writing socket error" << io->
errorString();
507#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"
511 char end_request[] = FCGI_END_REQUEST_DATA;
512 char *sid =
reinterpret_cast<char *
>(&stream_id);
514 end_request[2] = sid[1];
515 end_request[3] = sid[0];
516 end_request[10] = sid[1];
517 end_request[11] = sid[0];
518 io->
write(end_request, 24);
520 if (!sock->requestFinished()) {
525 if (headerConnection == ProtoRequestFastCGI::HeaderConnection::Close) {
527 sock->connectionClose();
531 const auto size = buf_size;
535 if (
status & EngineRequest::Async && buf_size) {
536 sock->proto->parse(sock, io);
540#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 & append(QByteArrayView data)
const char * constData() const const
QByteArray number(double n, char format, int precision)
QByteArray & replace(QByteArrayView before, QByteArrayView after)
void reserve(qsizetype size)
void resize(qsizetype newSize, char c)
qsizetype size() const const
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)