cutelyst  5.0.1
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 
21 namespace {
22 // Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED
23 inline 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 
59 using namespace Cutelyst;
60 using namespace Qt::Literals::StringLiterals;
61 
62 LocalServer::LocalServer(Server *server, QObject *parent)
63  : QLocalServer(parent)
64  , m_server(server)
65 {
66 }
67 
68 void LocalServer::setProtocol(Protocol *protocol)
69 {
70  m_protocol = protocol;
71 }
72 
73 LocalServer *LocalServer::createServer(ServerEngine *engine) const
74 {
75  auto server = new LocalServer(m_server, 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 
101 void LocalServer::pauseAccepting()
102 {
103  auto notifier = socketDescriptorNotifier();
104  if (notifier) {
105  notifier->setEnabled(false);
106  }
107 }
108 
109 void 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 
121 void 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 
149 qintptr LocalServer::socket() const
150 {
151  QSocketNotifier *notifier = socketDescriptorNotifier();
152  if (notifier) {
153  return notifier->socket();
154  }
155 
156  return 0;
157 }
158 
159 void 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 sock = qobject_cast<LocalSocket *>(child);
169  if (sock) {
170  connect(sock, &LocalSocket::finished, this, [this]() {
171  if (!m_processing) {
172  m_engine->serverShutdown();
173  }
174  });
175  m_engine->handleSocketShutdown(sock);
176  }
177  }
178  }
179 }
180 
181 void LocalServer::timeoutConnections()
182 {
183  if (m_processing) {
184  const auto childrenL = children();
185  for (auto child : childrenL) {
186  auto sock = qobject_cast<LocalSocket *>(child);
187  if (sock && !sock->processing && sock->state() == QLocalSocket::ConnectedState) {
188  if (sock->timeout) {
189  sock->connectionClose();
190  } else {
191  sock->timeout = true;
192  }
193  }
194  }
195  }
196 }
197 
198 Protocol *LocalServer::protocol() const
199 {
200  return m_protocol;
201 }
202 
203 QSocketNotifier *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
223 void 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"
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