cutelyst 5.1.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
tcpserverbalancer.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2017-2018 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#if defined(_WIN32)
6# ifndef _WIN32_WINNT
7# define _WIN32_WINNT 0x0601
8# endif
9# ifndef WIN32_LEAN_AND_MEAN
10# define WIN32_LEAN_AND_MEAN
11# endif
12# include <winsock2.h>
13# include <ws2tcpip.h>
14#endif
15
16#include "server.h"
17#include "serverengine.h"
18#include "tcpserver.h"
19#include "tcpserverbalancer.h"
20#include "tcpsslserver.h"
21
22#include <iostream>
23#include <mutex>
24
25#include <QFile>
26#include <QLoggingCategory>
27#include <QSslKey>
28
29#ifdef Q_OS_LINUX
30# include <arpa/inet.h>
31# include <fcntl.h>
32# include <sys/socket.h>
33# include <sys/types.h>
34# include <unistd.h>
35#endif
36
37Q_LOGGING_CATEGORY(C_SERVER_BALANCER, "cutelyst.server.tcpbalancer", QtWarningMsg)
38
39using namespace Cutelyst;
40
41#ifdef Q_OS_LINUX
42namespace {
43int listenReuse(const QHostAddress &address,
44 int listenQueue,
45 quint16 port,
46 bool reusePort,
47 bool startListening);
48}
49#endif
50
51#ifdef Q_OS_WIN
52namespace {
53bool ensureWinsockInitialized(QString *errorOut)
54{
55 static std::once_flag once;
56 static int wsaInitError = 0;
57 std::call_once(once, [] {
58 WSADATA wsaData;
59 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
60 wsaInitError = WSAGetLastError();
61 }
62 });
63 if (wsaInitError != 0) {
64 if (errorOut) {
65 *errorOut =
66 QStringLiteral("WSAStartup failed (Windows socket error %1)").arg(wsaInitError);
67 }
68 return false;
69 }
70 return true;
71}
72
73QString windowsSocketErrorString(int error)
74{
75 switch (error) {
76 case WSAEADDRINUSE:
77 return QStringLiteral("The bound address is already in use");
78 case WSAEACCES:
79 return QStringLiteral("The requested address is a protected address and requires "
80 "appropriate privileges");
81 case WSAEADDRNOTAVAIL:
82 return QStringLiteral("The requested address is not valid in this context");
83 case WSANOTINITIALISED:
84 return QStringLiteral("Winsock has not been initialized");
85 default:
86 return QStringLiteral("Windows socket error %1").arg(error);
87 }
88}
89
90int listenExclusive(const QHostAddress &address, int listenQueue, quint16 port, QString *errorOut)
91{
92 if (!ensureWinsockInitialized(errorOut)) {
93 return -1;
94 }
95
96 const bool dualStackAny =
97 address == QHostAddress::Any || address.protocol() == QHostAddress::AnyIPProtocol;
98 const bool ipv6 = address.protocol() == QHostAddress::IPv6Protocol || dualStackAny;
99
100 SOCKET socket = WSASocketW(ipv6 ? AF_INET6 : AF_INET,
101 SOCK_STREAM,
102 IPPROTO_TCP,
103 nullptr,
104 0,
105 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
106 if (socket == INVALID_SOCKET) {
107 if (errorOut) {
108 *errorOut = windowsSocketErrorString(WSAGetLastError());
109 }
110 return -1;
111 }
112
113 BOOL exclusive = TRUE;
114 if (setsockopt(socket,
115 SOL_SOCKET,
116 SO_EXCLUSIVEADDRUSE,
117 reinterpret_cast<const char *>(&exclusive),
118 sizeof(exclusive)) != 0) {
119 if (errorOut) {
120 *errorOut = windowsSocketErrorString(WSAGetLastError());
121 }
122 closesocket(socket);
123 return -1;
124 }
125
126 if (ipv6) {
127 sockaddr_in6 sa{};
128 sa.sin6_family = AF_INET6;
129 sa.sin6_port = htons(port);
130 if (dualStackAny) {
131 sa.sin6_addr = in6addr_any;
132 const int v6only = 0;
133 setsockopt(socket,
134 IPPROTO_IPV6,
135 IPV6_V6ONLY,
136 reinterpret_cast<const char *>(&v6only),
137 sizeof(v6only));
138 } else {
139 const Q_IPV6ADDR tmp = address.toIPv6Address();
140 memcpy(&sa.sin6_addr, &tmp, sizeof(tmp));
141 }
142 if (bind(socket, reinterpret_cast<sockaddr *>(&sa), sizeof(sa)) != 0) {
143 if (errorOut) {
144 *errorOut = windowsSocketErrorString(WSAGetLastError());
145 }
146 closesocket(socket);
147 return -1;
148 }
149 } else {
150 sockaddr_in sa{};
151 sa.sin_family = AF_INET;
152 sa.sin_port = htons(port);
153 if (address.protocol() == QHostAddress::Any) {
154 sa.sin_addr.s_addr = INADDR_ANY;
155 } else {
156 sa.sin_addr.s_addr = htonl(address.toIPv4Address());
157 }
158 if (bind(socket, reinterpret_cast<sockaddr *>(&sa), sizeof(sa)) != 0) {
159 if (errorOut) {
160 *errorOut = windowsSocketErrorString(WSAGetLastError());
161 }
162 closesocket(socket);
163 return -1;
164 }
165 }
166
167 if (::listen(socket, listenQueue) != 0) {
168 if (errorOut) {
169 *errorOut = windowsSocketErrorString(WSAGetLastError());
170 }
171 closesocket(socket);
172 return -1;
173 }
174
175 return static_cast<int>(socket);
176}
177} // namespace
178#endif
179
180TcpServerBalancer::TcpServerBalancer(Server *server)
181 : QTcpServer(server)
182 , m_server(server)
183{
184}
185
186TcpServerBalancer::~TcpServerBalancer()
187{
188#ifndef QT_NO_SSL
189 delete m_sslConfiguration;
190#endif // QT_NO_SSL
191}
192
193bool TcpServerBalancer::listen(const QString &line, Protocol *protocol, bool secure)
194{
195 m_protocol = protocol;
196
197 int commaPos = line.indexOf(u',');
198 const QString addressPortString = line.mid(0, commaPos);
199
200 QString addressString;
201 int closeBracketPos = addressPortString.indexOf(u']');
202 if (closeBracketPos != -1) {
203 if (!line.startsWith(u'[')) {
204 std::cerr << "Failed to parse address: " << qPrintable(addressPortString) << '\n';
205 return false;
206 }
207 addressString = addressPortString.mid(1, closeBracketPos - 1);
208 } else {
209 addressString = addressPortString.section(u':', 0, -2);
210 }
211 const QString portString = addressPortString.section(u':', -1);
212
213 QHostAddress address;
214 if (addressString.isEmpty()) {
215 address = QHostAddress(QHostAddress::Any);
216 } else {
217 address.setAddress(addressString);
218 }
219
220 bool ok;
221 quint16 port = portString.toUInt(&ok);
222 if (!ok || (port < 1 || port > 35554)) {
223 port = 80;
224 }
225
226#ifndef QT_NO_SSL
227 if (secure) {
228 if (commaPos == -1) {
229 std::cerr << "No SSL certificate specified" << '\n';
230 return false;
231 }
232
233 const QString sslString = line.mid(commaPos + 1);
234 const QString certPath = sslString.section(u',', 0, 0);
235 QFile certFile(certPath);
236 if (!certFile.open(QFile::ReadOnly)) {
237 std::cerr << "Failed to open SSL certificate" << qPrintable(certPath)
238 << qPrintable(certFile.errorString()) << '\n';
239 return false;
240 }
241 QSslCertificate cert(&certFile);
242 if (cert.isNull()) {
243 std::cerr << "Failed to parse SSL certificate" << '\n';
244 return false;
245 }
246
247 const QString keyPath = sslString.section(u',', 1, 1);
248 QFile keyFile(keyPath);
249 if (!keyFile.open(QFile::ReadOnly)) {
250 std::cerr << "Failed to open SSL private key" << qPrintable(keyPath)
251 << qPrintable(keyFile.errorString()) << '\n';
252 return false;
253 }
254
255 QSsl::KeyAlgorithm algorithm = QSsl::Rsa;
256 const QString keyAlgorithm = sslString.section(u',', 2, 2);
257 if (!keyAlgorithm.isEmpty()) {
258 if (keyAlgorithm.compare(u"rsa", Qt::CaseInsensitive) == 0) {
259 algorithm = QSsl::Rsa;
260 } else if (keyAlgorithm.compare(u"ec", Qt::CaseInsensitive) == 0) {
261 algorithm = QSsl::Ec;
262 } else {
263 std::cerr << "Failed to select SSL Key Algorithm" << qPrintable(keyAlgorithm)
264 << '\n';
265 return false;
266 }
267 }
268
269 QSslKey key(&keyFile, algorithm);
270 if (key.isNull()) {
271 std::cerr << "Failed to parse SSL private key" << '\n';
272 return false;
273 }
274
275 m_sslConfiguration = new QSslConfiguration;
276 m_sslConfiguration->setLocalCertificate(cert);
277 m_sslConfiguration->setPrivateKey(key);
278 m_sslConfiguration->setPeerVerifyMode(
279 QSslSocket::VerifyNone); // prevent asking for client certificate
280 if (m_server->httpsH2()) {
281 m_sslConfiguration->setAllowedNextProtocols(
282 {QByteArrayLiteral("h2"), QSslConfiguration::NextProtocolHttp1_1});
283 }
284 }
285#endif // QT_NO_SSL
286
287 m_address = address;
288 m_port = port;
289 m_bindError.clear();
290
291#ifdef Q_OS_LINUX
292 int socket = listenReuse(
293 address, m_server->listenQueue(), port, m_server->reusePort(), !m_server->reusePort());
294 if (socket > 0) {
295 if (setSocketDescriptor(socket)) {
297 } else {
298 m_bindError = errorString();
299 ::close(socket);
300 qCWarning(C_SERVER_BALANCER) << "Failed to listen on TCP:" << line << m_bindError;
301 return false;
302 }
303 } else {
304 std::cerr << "Failed to listen on TCP: " << qPrintable(line) << " : "
305 << qPrintable(errorString()) << '\n';
306 return false;
307 }
308#elif defined(Q_OS_WIN)
309 int socket = listenExclusive(address, m_server->listenQueue(), port, &m_bindError);
310 if (socket > 0) {
311 if (setSocketDescriptor(socket)) {
313 } else {
314 if (m_bindError.isEmpty()) {
315 m_bindError = errorString();
316 }
317 closesocket(socket);
318 qCWarning(C_SERVER_BALANCER) << "Failed to listen on TCP:" << line << m_bindError;
319 return false;
320 }
321 } else {
322 qCWarning(C_SERVER_BALANCER) << "Failed to listen on TCP:" << line << m_bindError;
323 return false;
324 }
325#else
326 setListenBacklogSize(m_server->listenQueue());
327 bool ret = QTcpServer::listen(address, port);
328 if (ret) {
330 } else {
331 m_bindError = errorString();
332 std::cerr << "Failed to listen on TCP: " << qPrintable(line) << " : "
333 << qPrintable(m_bindError) << '\n';
334 return false;
335 }
336#endif
337
338 m_serverName = serverAddress().toString().toLatin1() + ':' + QByteArray::number(port);
339 return true;
340}
341
342namespace {
343#ifdef Q_OS_LINUX
344// UnixWare 7 redefines socket -> _socket
345inline int qt_safe_socket(int domain, int type, int protocol, int flags = 0)
346{
347 Q_ASSERT((flags & ~O_NONBLOCK) == 0);
348
349 int fd;
350# ifdef QT_THREADSAFE_CLOEXEC
351 int newtype = type | SOCK_CLOEXEC;
352 if (flags & O_NONBLOCK) {
353 newtype |= SOCK_NONBLOCK;
354 }
355 fd = ::socket(domain, newtype, protocol);
356 return fd;
357# else
358 fd = ::socket(domain, type, protocol);
359 if (fd == -1) {
360 return -1;
361 }
362
363 ::fcntl(fd, F_SETFD, FD_CLOEXEC);
364
365 // set non-block too?
366 if (flags & O_NONBLOCK) {
367 ::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK);
368 }
369
370 return fd;
371# endif
372}
373
374int createNewSocket(QAbstractSocket::NetworkLayerProtocol &socketProtocol)
375{
376 int protocol = 0;
377
378 int domain = (socketProtocol == QAbstractSocket::IPv6Protocol ||
379 socketProtocol == QAbstractSocket::AnyIPProtocol)
380 ? AF_INET6
381 : AF_INET;
382 int type = SOCK_STREAM;
383
384 int socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK);
385 if (socket < 0 && socketProtocol == QAbstractSocket::AnyIPProtocol && errno == EAFNOSUPPORT) {
386 domain = AF_INET;
387 socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK);
388 socketProtocol = QAbstractSocket::IPv4Protocol;
389 }
390
391 if (socket < 0) {
392 int ecopy = errno;
393 switch (ecopy) {
394 case EPROTONOSUPPORT:
395 case EAFNOSUPPORT:
396 case EINVAL:
397 qCDebug(C_SERVER_BALANCER)
398 << "setError(QAbstractSocket::UnsupportedSocketOperationError, "
399 "ProtocolUnsupportedErrorString)";
400 break;
401 case ENFILE:
402 case EMFILE:
403 case ENOBUFS:
404 case ENOMEM:
405 qCDebug(C_SERVER_BALANCER)
406 << "setError(QAbstractSocket::SocketResourceError, ResourceErrorString)";
407 break;
408 case EACCES:
409 qCDebug(C_SERVER_BALANCER)
410 << "setError(QAbstractSocket::SocketAccessError, AccessErrorString)";
411 break;
412 default:
413 break;
414 }
415
416# if defined(QNATIVESOCKETENGINE_DEBUG)
417 qCDebug(C_SERVER_BALANCER,
418 "QNativeSocketEnginePrivate::createNewSocket(%d, %d) == false (%s)",
419 socketType,
420 socketProtocol,
421 strerror(ecopy));
422# endif
423
424 return false;
425 }
426
427# if defined(QNATIVESOCKETENGINE_DEBUG)
428 qCDebug(C_SERVER_BALANCER,
429 "QNativeSocketEnginePrivate::createNewSocket(%d, %d) == true",
430 socketType,
431 socketProtocol);
432# endif
433
434 return socket;
435}
436
437union qt_sockaddr {
438 sockaddr a;
439 sockaddr_in a4;
440 sockaddr_in6 a6;
441};
442
443# define QT_SOCKLEN_T int
444# define QT_SOCKET_BIND ::bind
445
446namespace SetSALen {
447template <typename T>
448void set(T *sa, typename std::enable_if<(&T::sa_len, true), QT_SOCKLEN_T>::type len)
449{
450 sa->sa_len = len;
451}
452template <typename T>
453void set(T *sin6, typename std::enable_if<(&T::sin6_len, true), QT_SOCKLEN_T>::type len)
454{
455 sin6->sin6_len = len;
456}
457template <typename T>
458void set(T *, ...)
459{
460}
461} // namespace SetSALen
462
463void setPortAndAddress(quint16 port,
464 const QHostAddress &address,
466 qt_sockaddr *aa,
467 int *sockAddrSize)
468{
469 if (address.protocol() == QAbstractSocket::IPv6Protocol ||
471 socketProtocol == QAbstractSocket::IPv6Protocol ||
472 socketProtocol == QAbstractSocket::AnyIPProtocol) {
473 memset(&aa->a6, 0, sizeof(sockaddr_in6));
474 aa->a6.sin6_family = AF_INET6;
475 // #if QT_CONFIG(networkinterface)
476 // aa->a6.sin6_scope_id = scopeIdFromString(address.scopeId());
477 // #endif
478 aa->a6.sin6_port = htons(port);
479 Q_IPV6ADDR tmp = address.toIPv6Address();
480 memcpy(&aa->a6.sin6_addr, &tmp, sizeof(tmp));
481 *sockAddrSize = sizeof(sockaddr_in6);
482 SetSALen::set(&aa->a, sizeof(sockaddr_in6));
483 } else {
484 memset(&aa->a, 0, sizeof(sockaddr_in));
485 aa->a4.sin_family = AF_INET;
486 aa->a4.sin_port = htons(port);
487 aa->a4.sin_addr.s_addr = htonl(address.toIPv4Address());
488 *sockAddrSize = sizeof(sockaddr_in);
489 SetSALen::set(&aa->a, sizeof(sockaddr_in));
490 }
491}
492
493bool nativeBind(int socketDescriptor, const QHostAddress &address, quint16 port)
494{
495 qt_sockaddr aa;
496 int sockAddrSize;
497 setPortAndAddress(port, address, address.protocol(), &aa, &sockAddrSize);
498
499# ifdef IPV6_V6ONLY
500 if (aa.a.sa_family == AF_INET6) {
501 int ipv6only = 0;
502 if (address.protocol() == QAbstractSocket::IPv6Protocol) {
503 ipv6only = 1;
504 }
505 // default value of this socket option varies depending on unix variant (or system
506 // configuration on BSD), so always set it explicitly
507 ::setsockopt(
508 socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &ipv6only, sizeof(ipv6only));
509 }
510# endif
511
512 int bindResult = ::bind(socketDescriptor, &aa.a, sockAddrSize);
513 if (bindResult < 0 && errno == EAFNOSUPPORT &&
515 // retry with v4
516 aa.a4.sin_family = AF_INET;
517 aa.a4.sin_port = htons(port);
518 aa.a4.sin_addr.s_addr = htonl(address.toIPv4Address());
519 sockAddrSize = sizeof(aa.a4);
520 bindResult = QT_SOCKET_BIND(socketDescriptor, &aa.a, sockAddrSize);
521 }
522
523 if (bindResult < 0) {
524# if defined(QNATIVESOCKETENGINE_DEBUG)
525 int ecopy = errno;
526# endif
527 // switch(errno) {
528 // case EADDRINUSE:
529 // setError(QAbstractSocket::AddressInUseError, AddressInuseErrorString);
530 // break;
531 // case EACCES:
532 // setError(QAbstractSocket::SocketAccessError, AddressProtectedErrorString);
533 // break;
534 // case EINVAL:
535 // setError(QAbstractSocket::UnsupportedSocketOperationError,
536 // OperationUnsupportedErrorString); break;
537 // case EADDRNOTAVAIL:
538 // setError(QAbstractSocket::SocketAddressNotAvailableError,
539 // AddressNotAvailableErrorString); break;
540 // default:
541 // break;
542 // }
543
544# if defined(QNATIVESOCKETENGINE_DEBUG)
545 qCDebug(C_SERVER_BALANCER,
546 "QNativeSocketEnginePrivate::nativeBind(%s, %i) == false (%s)",
547 address.toString().toLatin1().constData(),
548 port,
549 strerror(ecopy));
550# endif
551
552 return false;
553 }
554
555# if defined(QNATIVESOCKETENGINE_DEBUG)
556 qCDebug(C_SERVER_BALANCER,
557 "QNativeSocketEnginePrivate::nativeBind(%s, %i) == true",
558 address.toString().toLatin1().constData(),
559 port);
560# endif
561 // socketState = QAbstractSocket::BoundState;
562 return true;
563}
564
565int listenReuse(const QHostAddress &address,
566 int listenQueue,
567 quint16 port,
568 bool reusePort,
569 bool startListening)
570{
572
573 int socket = createNewSocket(proto);
574 if (socket < 0) {
575 qCCritical(C_SERVER_BALANCER) << "Failed to create new socket";
576 return -1;
577 }
578
579 int optval = 1;
580 // SO_REUSEADDR is set by default on QTcpServer and allows to bind again
581 // without having to wait all previous connections to close
582 if (::setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) {
583 qCCritical(C_SERVER_BALANCER) << "Failed to set SO_REUSEADDR on socket" << socket;
584 return -1;
585 }
586
587 if (reusePort) {
588 if (::setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval))) {
589 qCCritical(C_SERVER_BALANCER) << "Failed to set SO_REUSEPORT on socket" << socket;
590 return -1;
591 }
592 }
593
594 if (!nativeBind(socket, address, port)) {
595 qCCritical(C_SERVER_BALANCER) << "Failed to bind to socket" << socket;
596 return -1;
597 }
598
599 if (startListening && ::listen(socket, listenQueue) < 0) {
600 qCCritical(C_SERVER_BALANCER) << "Failed to listen to socket" << socket;
601 return -1;
602 }
603
604 return socket;
605}
606#endif // Q_OS_LINUX
607} // namespace
608
609void TcpServerBalancer::setBalancer(bool enable)
610{
611 m_balancer = enable;
612}
613
614void TcpServerBalancer::incomingConnection(qintptr handle)
615{
616 TcpServer *serverIdle = m_servers.at(m_currentServer++ % m_servers.size());
617
618 Q_EMIT serverIdle->createConnection(handle);
619}
620
621TcpServer *TcpServerBalancer::createServer(ServerEngine *engine)
622{
623 TcpServer *server;
624 if (m_sslConfiguration) {
625#ifndef QT_NO_SSL
626 auto sslServer = new TcpSslServer(m_serverName, m_protocol, m_server, engine);
627 sslServer->setSslConfiguration(*m_sslConfiguration);
628 server = sslServer;
629#endif // QT_NO_SSL
630 } else {
631 server = new TcpServer(m_serverName, m_protocol, m_server, engine);
632 }
633 connect(engine, &ServerEngine::shutdown, server, &TcpServer::shutdown);
634
635 if (m_balancer) {
636 connect(engine, &ServerEngine::started, this, [this, server]() {
637 m_servers.push_back(server);
640 connect(server,
641 &TcpServer::createConnection,
642 server,
643 &TcpServer::incomingConnection,
645 } else {
646
647#ifdef Q_OS_LINUX
648 if (m_server->reusePort()) {
649 connect(engine, &ServerEngine::started, this, [this, server]() {
650 int socket = listenReuse(
651 m_address, m_server->listenQueue(), m_port, m_server->reusePort(), true);
652 if (!server->setSocketDescriptor(socket)) {
653 qFatal("Failed to set server socket descriptor, reuse-port");
654 }
656 return server;
657 }
658#endif
659
660 if (server->setSocketDescriptor(socketDescriptor())) {
661 server->pauseAccepting();
662 connect(engine,
663 &ServerEngine::started,
664 server,
667 } else {
668 qFatal("Failed to set server socket descriptor");
669 }
670 }
671
672 return server;
673}
674
675#include "moc_tcpserverbalancer.cpp"
Implements a web server.
Definition server.h:60
The Cutelyst namespace holds all public Cutelyst API.
const char * constData() const const
QByteArray number(double n, char format, int precision)
int protocol() const const
bool setAddress(const QString &address)
quint32 toIPv4Address(bool *ok) const const
Q_IPV6ADDR toIPv6Address() const const
QString toString() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
KeyAlgorithm
QString arg(Args &&... args) const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) &&
QString section(QChar sep, qsizetype start, qsizetype end, QString::SectionFlags flags) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
uint toUInt(bool *ok, int base) const const
CaseInsensitive
QueuedConnection
void close()
QString errorString() const const
bool listen(const QHostAddress &address, quint16 port)
void pauseAccepting()
void resumeAccepting()
QHostAddress serverAddress() const const
void setListenBacklogSize(int size)
bool setSocketDescriptor(qintptr socketDescriptor)
qintptr socketDescriptor() const const