[ci] Fix clang-santisers job for GHA change
[xapian.git] / xapian-core / net / remoteconnection.h
blob9983115aa7c1ca3a7386ca43cefe00f1d0449c30
1 /** @file
2 * @brief RemoteConnection class used by the remote backend.
3 */
4 /* Copyright (C) 2006,2007,2008,2010,2011,2014,2015,2019,2024 Olly Betts
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
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 General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #ifndef XAPIAN_INCLUDED_REMOTECONNECTION_H
22 #define XAPIAN_INCLUDED_REMOTECONNECTION_H
24 #include <string>
26 #include "remoteprotocol.h"
28 #ifdef __WIN32__
29 # include "safewinsock2.h"
30 #else
31 # include "safesyssocket.h"
32 #endif
34 /** A RemoteConnection object provides a bidirectional connection to another
35 * RemoteConnection object on a remote machine.
37 * The connection is implemented using a pair of file descriptors. Messages
38 * with a single byte type code and arbitrary data as the contents can be
39 * sent and received.
41 class RemoteConnection {
42 /// Don't allow assignment.
43 void operator=(const RemoteConnection &);
45 /// Don't allow copying.
46 RemoteConnection(const RemoteConnection &);
48 /** The file descriptor used for reading.
50 * If this is -1, the connection is unidirectional and write-only.
51 * If both fdin and fdout are -1, then the connection has been closed.
53 int fdin;
55 /** The file descriptor used for writing.
57 * If this is -1, the connection is unidirectional and read-only.
58 * If both fdin and fdout are -1, then the connection has been closed.
59 * It is valid for fdout to be the same as fdin.
61 int fdout;
63 #ifndef __WIN32__
64 // On Unix-like platforms we want to avoid generating SIGPIPE when writing
65 // to a socket when the other end has been closed since signals break the
66 // encapsulation of what we're doing inside the library - either user code
67 // would need to handle the SIGPIPE, or we set a signal handler for SIGPIPE
68 // but that would handle *any* SIGPIPE in the process, not just those we
69 // might trigger, and that could break user code which expects to trigger
70 // and handle SIGPIPE.
72 // We don't need SIGPIPE since we can check errno==EPIPE instead (which is
73 // actually simpler to do).
75 // We support using SO_NOSIGPIPE (not standardised) or MSG_NOSIGNAL
76 // (specified by POSIX but more awkward to use) which seems to cover all
77 // modern Unix-like platforms. For platforms without either we currently
78 // just set the SIGPIPE signal handler to SIG_IGN.
79 # if defined(SO_NOSIGPIPE) && !defined(__NetBSD__)
80 // Prefer using SO_NOSIGPIPE and write(), except on NetBSD where we seem to
81 // still get SIGPIPE despite using it.
82 # define USE_SO_NOSIGPIPE
83 # elif defined MSG_NOSIGNAL
84 // Use send(..., MSG_NOSIGNAL).
85 int send_flags = MSG_NOSIGNAL;
86 # define USE_MSG_NOSIGNAL
87 # endif
88 #endif
90 /// Buffer to hold unprocessed input.
91 std::string buffer;
93 /// Remaining bytes of message data still to come over fdin for a chunked read.
94 off_t chunked_data_left;
96 /** Read until there are at least min_len bytes in buffer.
98 * If for some reason this isn't possible, returns false upon EOF and
99 * otherwise throws NetworkError.
101 * @param min_len Minimum number of bytes required in buffer.
102 * @param end_time If this time is reached, then a timeout
103 * exception will be thrown. If (end_time == 0.0),
104 * then keep trying indefinitely.
106 * @return false on EOF, otherwise true.
108 bool read_at_least(size_t min_len, double end_time);
110 #ifdef __WIN32__
111 /** On Windows we use overlapped IO. We share an overlapped structure
112 * for both reading and writing, as we know that we always wait for
113 * one to finish before starting another (ie, we don't *really* use
114 * overlapped IO - no IO is overlapped - it's used only to manage
115 * timeouts)
117 WSAOVERLAPPED overlapped;
119 /** Calculate how many milliseconds a read should wait.
121 * This will raise a timeout exception if end_time has already passed.
123 DWORD calc_read_wait_msecs(double end_time);
124 #else
125 /** Helper which calls send() or write(). */
126 ssize_t send_or_write(const void* p, size_t n);
127 #endif
129 protected:
130 /** The context to report with errors.
132 * Subclasses are allowed to manage this.
134 std::string context;
136 public:
137 /// Constructor.
138 RemoteConnection(int fdin_, int fdout_,
139 const std::string & context_ = std::string());
141 #ifdef __WIN32__
142 /// Destructor
143 ~RemoteConnection();
144 #endif
146 /** Return the underlying fd this remote connection reads from. */
147 int get_read_fd() const { return fdin; }
149 /** Check what the next message type is.
151 * This must not be called after a call to get_message_chunked() until
152 * get_message_chunk() has returned 0 to indicate the whole message
153 * has been received.
155 * Other than that restriction, this may be called at any time to
156 * determine what the next message waiting to be processed is: it will not
157 * affect subsequent calls which read messages.
159 * @param end_time If this time is reached, then a timeout
160 * exception will be thrown. If
161 * (end_time == 0.0) then the operation will
162 * never timeout.
164 * @return Message type code or -1 for EOF.
166 int sniff_next_message_type(double end_time);
168 /** Read one message from fdin.
170 * @param[out] result Message data.
171 * @param end_time If this time is reached, then a timeout
172 * exception will be thrown. If
173 * (end_time == 0.0) then the operation will
174 * never timeout.
176 * @return Message type code or -1 for EOF.
178 int get_message(std::string &result, double end_time);
180 /** Prepare to read one message from fdin in chunks.
182 * Sometimes a message can be sufficiently large that you don't want to
183 * read it all into memory before processing it. Also, it may be more
184 * efficient to process it as you go.
186 * This method doesn't actually return any message data - call
187 * get_message_chunk() to do that.
189 * @param end_time If this time is reached, then a timeout
190 * exception will be thrown. If
191 * (end_time == 0.0) then the operation will
192 * never timeout.
194 * @return Message type code or -1 for EOF.
196 int get_message_chunked(double end_time);
198 /** Read a chunk of a message from fdin.
200 * You must call get_message_chunked() before calling this method.
202 * @param[in,out] result Message data. This is appended to, so if you
203 * read more than needed the previous time, leave
204 * the excess in result.
205 * @param at_least Return at least this many bytes in result,
206 * unless there isn't enough data left in the
207 * message (in which case all remaining data is
208 * read and 0 is returned).
209 * @param end_time If this time is reached, then a timeout
210 * exception will be thrown. If
211 * (end_time == 0.0) then the operation will
212 * never timeout.
214 * @return 1 if at least at_least bytes are now in result;
215 * -1 on EOF on the connection; 0 for having read
216 * < at_least bytes, but finished the message.
218 int get_message_chunk(std::string &result, size_t at_least,
219 double end_time);
221 /** Save the contents of a message as a file.
223 * @param file Path to file to save the message data into. If
224 * the file exists it will be overwritten.
225 * @param end_time If this time is reached, then a timeout
226 * exception will be thrown. If
227 * (end_time == 0.0) then the operation will
228 * never timeout.
230 * @return Message type code or -1 for EOF.
232 int receive_file(const std::string &file, double end_time);
234 /** Send a message.
236 * @param type Message type code.
237 * @param s Message data.
238 * @param end_time If this time is reached, then a timeout
239 * exception will be thrown. If
240 * (end_time == 0.0) then the operation will
241 * never timeout.
243 void send_message(char type, std::string_view s, double end_time);
245 /** Send the contents of a file as a message.
247 * @param type Message type code.
248 * @param fd File containing the message data.
249 * @param end_time If this time is reached, then a timeout
250 * exception will be thrown. If
251 * (end_time == 0.0) then the operation will
252 * never timeout.
254 void send_file(char type, int fd, double end_time);
256 /** Shutdown the connection.
258 * Sends a shutdown message to the server and waits for it to close its
259 * end of the connection.
261 void shutdown();
263 /** Close the connection. */
264 void do_close();
266 /** Return the context to report with errors. */
267 const std::string& get_context() const { return context; }
270 /** RemoteConnection which owns its own fd(s).
272 * The object takes ownership of the fd(s) for the connection and will close
273 * them when it is destroyed.
275 class OwnedRemoteConnection : public RemoteConnection {
276 public:
277 /// Constructor.
278 OwnedRemoteConnection(int fdin_, int fdout_,
279 const std::string& context_ = std::string())
280 : RemoteConnection(fdin_, fdout_, context_) { }
282 /// Destructor.
283 ~OwnedRemoteConnection() {
284 do_close();
288 #endif // XAPIAN_INCLUDED_REMOTECONNECTION_H