cutelyst 4.8.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
localserver.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2018 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "localserver.h"
6
7#include "protocol.h"
8#include "server.h"
9#include "socket.h"
10
11#include <Cutelyst/Engine>
12
13#include <QDateTime>
14#include <QSocketNotifier>
15
16#ifdef Q_OS_UNIX
17// #include <sys/types.h>
18# include <sys/socket.h>
19# include <sys/un.h>
20// #include <netinet/in.h>
21# include <fcntl.h>
22
23static inline int cutelyst_safe_accept(int s, struct sockaddr *addr, uint *addrlen, int flags = 0);
24#endif
25
26using namespace Cutelyst;
27using namespace Qt::Literals::StringLiterals;
28
29LocalServer::LocalServer(Server *wsgi, QObject *parent)
30 : QLocalServer(parent)
31 , m_wsgi(wsgi)
32{
33}
34
35void LocalServer::setProtocol(Protocol *protocol)
36{
37 m_protocol = protocol;
38}
39
40LocalServer *LocalServer::createServer(ServerEngine *engine) const
41{
42 auto server = new LocalServer(m_wsgi, engine);
43 server->setProtocol(m_protocol);
44 server->m_engine = engine;
45
46#ifdef Q_OS_UNIX
47 server->m_socket = socket();
48 server->m_socketNotifier = new QSocketNotifier(server->m_socket, QSocketNotifier::Read, server);
49 server->m_socketNotifier->setEnabled(false);
50 connect(server->m_socketNotifier,
52 server,
53 &LocalServer::socketNotifierActivated);
54#else
55 if (server->listen(socket())) {
56 server->pauseAccepting();
57 } else {
58 qFatal("Failed to set server socket descriptor");
59 }
60#endif
61
62 connect(engine, &ServerEngine::started, server, &LocalServer::resumeAccepting);
63 connect(engine, &ServerEngine::shutdown, server, &LocalServer::shutdown);
64
65 return server;
66}
67
68void LocalServer::pauseAccepting()
69{
70 auto notifier = socketDescriptorNotifier();
71 if (notifier) {
72 notifier->setEnabled(false);
73 }
74}
75
76void LocalServer::resumeAccepting()
77{
78#ifdef Q_OS_UNIX
79 m_socketNotifier->setEnabled(true);
80#else
81 auto notifier = socketDescriptorNotifier();
82 if (notifier) {
83 notifier->setEnabled(true);
84 }
85#endif
86}
87
88void LocalServer::incomingConnection(quintptr handle)
89{
90 auto sock = new LocalSocket(m_engine, this);
91 sock->protoData = m_protocol->createData(sock);
92
93 connect(sock, &QIODevice::readyRead, [sock]() {
94 sock->timeout = false;
95 sock->proto->parse(sock, sock);
96 });
97 connect(sock, &LocalSocket::finished, this, [this, sock]() {
98 sock->deleteLater();
99 if (--m_processing == 0) {
100 m_engine->stopSocketTimeout();
101 }
102 });
103
104 if (Q_LIKELY(sock->setSocketDescriptor(qintptr(handle)))) {
105 sock->proto = m_protocol;
106
107 sock->serverAddress = "localhost"_ba;
108 if (++m_processing) {
109 m_engine->startSocketTimeout();
110 }
111 } else {
112 delete sock;
113 }
114}
115
116qintptr LocalServer::socket() const
117{
118 QSocketNotifier *notifier = socketDescriptorNotifier();
119 if (notifier) {
120 return notifier->socket();
121 }
122
123 return 0;
124}
125
126void LocalServer::shutdown()
127{
128 close();
129
130 if (m_processing == 0) {
131 m_engine->serverShutdown();
132 } else {
133 const auto childrenL = children();
134 for (auto child : childrenL) {
135 auto socket = qobject_cast<LocalSocket *>(child);
136 if (socket) {
137 connect(socket, &LocalSocket::finished, this, [this]() {
138 if (!m_processing) {
139 m_engine->serverShutdown();
140 }
141 });
142 m_engine->handleSocketShutdown(socket);
143 }
144 }
145 }
146}
147
148void LocalServer::timeoutConnections()
149{
150 if (m_processing) {
151 const auto childrenL = children();
152 for (auto child : childrenL) {
153 auto socket = qobject_cast<LocalSocket *>(child);
154 if (socket && !socket->processing && socket->state() == QLocalSocket::ConnectedState) {
155 if (socket->timeout) {
156 socket->connectionClose();
157 } else {
158 socket->timeout = true;
159 }
160 }
161 }
162 }
163}
164
165Protocol *LocalServer::protocol() const
166{
167 return m_protocol;
168}
169
170QSocketNotifier *LocalServer::socketDescriptorNotifier() const
171{
172 QSocketNotifier *ret = nullptr;
173 // THIS IS A HACK
174 // QLocalServer does not expose the socket
175 // descriptor, so we get it from it's QSocketNotifier child
176 // if this breaks it we fail with an error.
177 const auto childrenL = children();
178 for (auto child : childrenL) {
179 auto notifier = qobject_cast<QSocketNotifier *>(child);
180 if (notifier) {
181 ret = notifier;
182 break;
183 }
184 }
185
186 return ret;
187}
188
189#ifdef Q_OS_UNIX
190void LocalServer::socketNotifierActivated()
191{
192 if (-1 == m_socket)
193 return;
194
195 ::sockaddr_un addr;
196 uint length = sizeof(sockaddr_un);
197 int connectedSocket =
198 cutelyst_safe_accept(int(m_socket), reinterpret_cast<sockaddr *>(&addr), &length);
199 if (-1 != connectedSocket) {
200 incomingConnection(quintptr(connectedSocket));
201 }
202}
203
204// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED
205static inline int cutelyst_safe_accept(int s, struct sockaddr *addr, uint *addrlen, int flags)
206{
207 Q_ASSERT((flags & ~O_NONBLOCK) == 0);
208
209 int fd;
210# ifdef QT_THREADSAFE_CLOEXEC
211 // use accept4
212 int sockflags = SOCK_CLOEXEC;
213 if (flags & O_NONBLOCK)
214 sockflags |= SOCK_NONBLOCK;
215# if defined(Q_OS_NETBSD)
216 fd = ::paccept(s, addr, static_cast<socklen_t *>(addrlen), NULL, sockflags);
217# else
218 fd = ::accept4(s, addr, static_cast<socklen_t *>(addrlen), sockflags);
219# endif
220 return fd;
221# else
222 fd = ::accept(s, addr, static_cast<socklen_t *>(addrlen));
223 if (fd == -1)
224 return -1;
225
226 ::fcntl(fd, F_SETFD, FD_CLOEXEC);
227
228 // set non-block too?
229 if (flags & O_NONBLOCK)
230 ::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK);
231
232 return fd;
233# endif
234}
235#endif // Q_OS_UNIX
236
237#include "moc_localserver.cpp"
Implements a web server.
Definition server.h:60
The Cutelyst namespace holds all public Cutelyst API.
void readyRead()
const QObjectList & children() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
qintptr socket() const const