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