SNode.C
Loading...
Searching...
No Matches
SocketConnection.hpp
Go to the documentation of this file.
1/*
2 * SNode.C - A Slim Toolkit for Network Communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "core/socket/stream/SocketConnection.h"
43#include "core/socket/stream/SocketContext.h"
44
45#ifndef DOXYGEN_SHOULD_SKIP_THIS
46
47#include "log/Logger.h"
48#include "utils/PreserveErrno.h"
49#include "utils/system/signal.h"
50
51#include <cstddef>
52#include <string>
53#include <utility>
54
55#endif /* DOXYGEN_SHOULD_SKIP_THIS */
56
57namespace core::socket::stream {
58 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
59 SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::SocketConnectionT(const std::string& instanceName,
60 PhysicalSocket&& physicalSocket,
61 const std::function<void()>& onDisconnect,
62 const std::string& configuredServer,
63 const SocketAddress& localAddress,
64 const SocketAddress& remoteAddress,
65 const utils::Timeval& readTimeout,
66 const utils::Timeval& writeTimeout,
67 std::size_t readBlockSize,
68 std::size_t writeBlockSize,
69 const utils::Timeval& terminateTimeout)
70 : SocketConnection(instanceName, physicalSocket.getFd(), configuredServer)
71 , SocketReader(
72 instanceName + " [" + std::to_string(physicalSocket.getFd()) + "]",
73 [this](int errnum) {
74 {
75 const utils::PreserveErrno pe(errnum);
76 if (errno == 0) {
77 LOG(TRACE) << connectionName << " OnReadError: EOF received";
78 } else {
79 PLOG(TRACE) << connectionName << " OnReadError";
80 }
81 }
82 SocketReader::disable();
83
84 onReadError(errnum);
85 },
86 readTimeout,
87 readBlockSize,
88 terminateTimeout)
89 , SocketWriter(
90 instanceName + " [" + std::to_string(physicalSocket.getFd()) + "]",
91 [this](int errnum) {
92 {
93 const utils::PreserveErrno pe(errnum);
94 PLOG(TRACE) << connectionName << " OnWriteError";
95 }
96 SocketWriter::disable();
97
98 onWriteError(errnum);
99 },
100 writeTimeout,
101 writeBlockSize,
102 terminateTimeout)
103 , physicalSocket(std::move(physicalSocket))
104 , onDisconnect(onDisconnect)
105 , localAddress(localAddress)
106 , remoteAddress(remoteAddress) {
107 if (!SocketReader::enable(this->physicalSocket.getFd())) {
108 delete this;
109 } else if (!SocketWriter::enable(this->physicalSocket.getFd())) {
110 delete this;
111 } else {
112 SocketWriter::suspend();
113 }
114 }
115
116 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
117 SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::~SocketConnectionT() {
118 }
119
120 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
121 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::setTimeout(const utils::Timeval& timeout) {
122 SocketReader::setTimeout(timeout);
123 SocketWriter::setTimeout(timeout);
124 }
125
126 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
127 int SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::getFd() const {
128 return physicalSocket.getFd();
129 }
130
131 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
132 const typename SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::SocketAddress&
133 SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::getLocalAddress() const {
134 return localAddress;
135 }
136
137 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
138 const typename SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::SocketAddress&
139 SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::getRemoteAddress() const {
140 return remoteAddress;
141 }
142
143 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
144 std::size_t SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::readFromPeer(char* chunk, std::size_t chunkLen) {
145 std::size_t ret = 0;
146
147 if (newSocketContext == nullptr) {
148 ret = SocketReader::readFromPeer(chunk, chunkLen);
149 } else {
150 LOG(TRACE) << connectionName << " ReadFromPeer: New SocketContext != nullptr: SocketContextSwitch still in progress";
151 }
152
153 return ret;
154 }
155
156 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
157 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::sendToPeer(const char* chunk, std::size_t chunkLen) {
158 SocketWriter::sendToPeer(chunk, chunkLen);
159 }
160
161 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
162 bool SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::streamToPeer(core::pipe::Source* source) {
163 return SocketWriter::streamToPeer(source);
164 }
165
166 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
167 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::streamEof() {
168 SocketWriter::streamEof();
169 }
170
171 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
172 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::shutdownRead() {
173 LOG(TRACE) << connectionName << ": Shutdown (RD)";
174
175 SocketReader::shutdownRead();
176
177 if (physicalSocket.shutdown(PhysicalSocket::SHUT::RD) == 0) {
178 LOG(DEBUG) << connectionName << " Shutdown (RD): success";
179 } else {
180 PLOG(ERROR) << connectionName << " Shutdown (RD)";
181 }
182 }
183
184 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
185 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::shutdownWrite(bool forceClose) {
186 if (!SocketWriter::shutdownInProgress) {
187 LOG(TRACE) << connectionName << ": Stop writing";
188
189 SocketWriter::shutdownWrite([forceClose, this]() {
190 if (SocketWriter::isEnabled()) {
191 SocketWriter::disable();
192 }
193 if (forceClose && SocketReader::isEnabled()) {
194 close();
195 }
196 });
197 }
198 }
199
200 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
201 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::close() {
202 if (SocketWriter::isEnabled()) {
203 SocketWriter::disable();
204 }
205 if (SocketReader::isEnabled()) {
206 SocketReader::disable();
207 }
208 }
209
210 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
211 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::doWriteShutdown(const std::function<void()>& onShutdown) {
212 errno = 0;
213
214 setTimeout(SocketWriter::terminateTimeout);
215
216 LOG(TRACE) << connectionName << ": Shutdown (WR)";
217
218 if (physicalSocket.shutdown(PhysicalSocket::SHUT::WR) == 0) {
219 LOG(DEBUG) << connectionName << " Shutdown (WR): success";
220 } else {
221 PLOG(ERROR) << connectionName << " Shutdown (WR)";
222 }
223
224 onShutdown();
225 }
226
227 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
228 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::onReceivedFromPeer(std::size_t available) {
229 std::size_t consumed = socketContext->onReceivedFromPeer();
230
231 if (available != 0 && consumed == 0) {
232 LOG(TRACE) << connectionName << ": Data available: " << available << " but nothing read";
233
234 close();
235
236 delete newSocketContext; // delete of nullptr is valid since C++14!
237 newSocketContext = nullptr;
238 } else if (newSocketContext != nullptr) { // Perform a pending SocketContextSwitch
241 newSocketContext = nullptr;
242 }
243 }
244
245 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
246 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::onWriteError(int errnum) {
248 }
249
250 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
251 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::onReadError(int errnum) {
253 }
254
255 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
256 bool SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::onSignal(int signum) {
257 switch (signum) {
258 case SIGINT:
259 [[fallthrough]];
260 case SIGTERM:
261 [[fallthrough]];
262 case SIGABRT:
263 [[fallthrough]];
264 case SIGHUP:
265 LOG(DEBUG) << connectionName << ": Shutting down due to signal '" << strsignal(signum) << "' (SIG"
266 << utils::system::sigabbrev_np(signum) << " [" << signum << "])";
267 break;
268 case SIGALRM:
269 break;
270 }
271
272 return socketContext != nullptr ? socketContext->onSignal(signum) : true;
273 }
274
275 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
276 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::readTimeout() {
277 LOG(WARNING) << connectionName << ": Read timeout";
278 close();
279 }
280
281 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
282 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::writeTimeout() {
283 LOG(WARNING) << connectionName << ": Write timeout";
284 close();
285 }
286
287 template <typename PhysicalSocket, typename SocketReader, typename SocketWriter>
288 void SocketConnectionT<PhysicalSocket, SocketReader, SocketWriter>::unobservedEvent() {
290
291 onDisconnect();
292
293 delete this;
294 }
295
296} // namespace core::socket::stream
virtual std::size_t onReceivedFromPeer()=0
virtual bool onSignal(int sig)=0
virtual ~Socket()
Definition Socket.hpp:56
Config & getConfig() const
Definition Socket.hpp:60
std::shared_ptr< Config > config
Definition Socket.h:72
Socket(const std::string &name)
Definition Socket.hpp:51
void onReceivedFromPeer(std::size_t available) final
void setTimeout(const utils::Timeval &timeout) final
std::size_t readFromPeer(char *chunk, std::size_t chunkLen) final
const SocketAddress & getRemoteAddress() const final
SocketConnectionT(const std::string &instanceName, PhysicalSocket &&physicalSocket, const std::function< void()> &onDisconnect, const std::string &configuredServer, const SocketAddress &localAddress, const SocketAddress &remoteAddress, const utils::Timeval &readTimeout, const utils::Timeval &writeTimeout, std::size_t readBlockSize, std::size_t writeBlockSize, const utils::Timeval &terminateTimeout)
void doWriteShutdown(const std::function< void()> &onShutdown) override
bool streamToPeer(core::pipe::Source *source) final
void shutdownWrite(bool forceClose) final
void sendToPeer(const char *chunk, std::size_t chunkLen) final
const SocketAddress & getLocalAddress() const final
core::socket::stream::SocketContext * newSocketContext
core::socket::stream::SocketContext * socketContext
void setSocketContext(SocketContext *socketContext)
void onReadError(int errnum) override
void onWriteError(int errnum) override
SocketConnection(const std::string &instanceName, PhysicalSocket &&physicalSocket, const std::function< void(SocketConnection *)> &onDisconnect, const std::string &configuredServer, const SocketAddress &localAddress, const SocketAddress &remoteAddress, const utils::Timeval &readTimeout, const utils::Timeval &writeTimeout, std::size_t readBlockSize, std::size_t writeBlockSize, const utils::Timeval &terminateTimeout)
PreserveErrno(int newErrno=errno)
std::string sigabbrev_np(int sig)
Definition signal.cpp:59