cutelyst  4.6.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 
23 static inline int cutelyst_safe_accept(int s, struct sockaddr *addr, uint *addrlen, int flags = 0);
24 #endif
25 
26 using namespace Cutelyst;
27 
28 LocalServer::LocalServer(Server *wsgi, QObject *parent)
29  : QLocalServer(parent)
30  , m_wsgi(wsgi)
31 {
32 }
33 
34 void LocalServer::setProtocol(Protocol *protocol)
35 {
36  m_protocol = protocol;
37 }
38 
39 LocalServer *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 
67 void LocalServer::pauseAccepting()
68 {
69  auto notifier = socketDescriptorNotifier();
70  if (notifier) {
71  notifier->setEnabled(false);
72  }
73 }
74 
75 void 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 
87 void 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 
115 qintptr LocalServer::socket() const
116 {
117  QSocketNotifier *notifier = socketDescriptorNotifier();
118  if (notifier) {
119  return notifier->socket();
120  }
121 
122  return 0;
123 }
124 
125 void 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 
147 void 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 
164 Protocol *LocalServer::protocol() const
165 {
166  return m_protocol;
167 }
168 
169 QSocketNotifier *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
189 void 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
204 static 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"
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
const QObjectList & children() const const
Implements a web server.
Definition: server.h:59
The Cutelyst namespace holds all public Cutelyst API.
void setEnabled(bool enable)
void readyRead()
T qobject_cast(QObject *object)
qintptr socket() const const