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