cutelyst  4.9.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 using namespace Qt::Literals::StringLiterals;
28 
29 LocalServer::LocalServer(Server *wsgi, QObject *parent)
30  : QLocalServer(parent)
31  , m_wsgi(wsgi)
32 {
33 }
34 
35 void LocalServer::setProtocol(Protocol *protocol)
36 {
37  m_protocol = protocol;
38 }
39 
40 LocalServer *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 
68 void LocalServer::pauseAccepting()
69 {
70  auto notifier = socketDescriptorNotifier();
71  if (notifier) {
72  notifier->setEnabled(false);
73  }
74 }
75 
76 void 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 
88 void 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 
116 qintptr LocalServer::socket() const
117 {
118  QSocketNotifier *notifier = socketDescriptorNotifier();
119  if (notifier) {
120  return notifier->socket();
121  }
122 
123  return 0;
124 }
125 
126 void 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 
148 void 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 
165 Protocol *LocalServer::protocol() const
166 {
167  return m_protocol;
168 }
169 
170 QSocketNotifier *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
190 void 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
205 static 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"
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