42 # include <type_traits>
75 update_overlapped_offset(WSAOVERLAPPED & overlapped, DWORD n)
78 ++overlapped.OffsetHigh;
83 const string & context_)
84 : fdin(fdin_), fdout(fdout_), context(context_)
87 memset(&overlapped, 0,
sizeof(overlapped));
88 overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
89 if (!overlapped.hEvent)
93 #elif defined USE_SO_NOSIGPIPE
110 if (setsockopt(
fdout, SOL_SOCKET, SO_NOSIGPIPE,
111 reinterpret_cast<char*
>(&on),
sizeof(on)) < 0) {
118 if (errno != ENOTSOCK && errno != EBADF) {
123 #elif defined USE_MSG_NOSIGNAL
135 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
142 RemoteConnection::~RemoteConnection()
144 if (overlapped.hEvent)
145 CloseHandle(overlapped.hEvent);
152 LOGCALL(REMOTE,
bool,
"RemoteConnection::read_at_least", min_len |
end_time);
157 HANDLE hin = fd_to_handle(
fdin);
161 BOOL ok = ReadFile(hin, buf,
sizeof(buf), &received, &overlapped);
163 int errcode = GetLastError();
164 if (errcode != ERROR_IO_PENDING)
168 waitrc = WaitForSingleObject(overlapped.hEvent, calc_read_wait_msecs(
end_time));
169 if (waitrc != WAIT_OBJECT_0) {
170 LOGLINE(REMOTE,
"read: timeout has expired");
174 if (!GetOverlappedResult(hin, &overlapped, &received, FALSE))
176 context, -
int(GetLastError()));
183 buffer.append(buf, received);
186 update_overlapped_offset(overlapped, received);
187 }
while (
buffer.length() < min_len);
190 if (fcntl(
fdin, F_SETFL, (
end_time != 0.0) ? O_NONBLOCK : 0) < 0) {
197 ssize_t received = read(
fdin, buf,
sizeof(buf));
200 buffer.append(buf, received);
209 LOGLINE(REMOTE,
"read gave errno = " << errno);
210 if (errno == EINTR)
continue;
222 LOGLINE(REMOTE,
"read: timeout has expired");
231 int poll_result = poll(&fds, 1,
int(time_diff * 1000));
232 if (poll_result > 0)
break;
234 if (poll_result == 0)
239 if (errno != EINTR && errno != EAGAIN)
243 if (
fdin >= FD_SETSIZE) {
250 FD_SET(
fdin, &fdset);
254 int select_result = select(
fdin + 1, &fdset, 0, 0, &tv);
255 if (select_result > 0)
break;
257 if (select_result == 0)
264 if (errno != EINTR && errno != EAGAIN)
278 # ifdef USE_MSG_NOSIGNAL
280 ssize_t n = send(
fdout,
p, len, send_flags);
281 if (
usual(n >= 0 || errno != ENOTSOCK))
return n;
288 return write(
fdout,
p, len);
302 string_view header_view = header;
305 HANDLE hout = fd_to_handle(
fdout);
306 const string_view*
str = &header_view;
311 BOOL ok = WriteFile(hout,
str->data() + count,
str->size() - count, &n, &overlapped);
313 int errcode = GetLastError();
314 if (errcode != ERROR_IO_PENDING)
318 waitrc = WaitForSingleObject(overlapped.hEvent, calc_read_wait_msecs(
end_time));
319 if (waitrc != WAIT_OBJECT_0) {
320 LOGLINE(REMOTE,
"write: timeout has expired");
324 if (!GetOverlappedResult(hout, &overlapped, &n, FALSE))
326 context, -
int(GetLastError()));
332 update_overlapped_offset(overlapped, n);
334 if (count ==
str->size()) {
335 if (
str == &message || message.empty())
return;
342 if (fcntl(
fdout, F_SETFL, (
end_time != 0.0) ? O_NONBLOCK : 0) < 0) {
347 const string_view*
str = &header_view;
357 if (count ==
str->size()) {
358 if (
str == &message || message.empty())
return;
365 LOGLINE(REMOTE,
"write gave errno = " << errno);
366 if (errno == EINTR)
continue;
374 LOGLINE(REMOTE,
"write: timeout has expired");
382 fds.events = POLLOUT;
383 int result = poll(&fds, 1,
int(time_diff * 1000));
384 # define POLLSELECT "poll"
386 if (
fdout >= FD_SETSIZE) {
394 FD_SET(
fdout, &fdset);
398 int result = select(
fdout + 1, 0, &fdset, 0, &tv);
399 # define POLLSELECT "select"
403 if (errno == EINTR || errno == EAGAIN) {
438 c += enc_size.size();
441 memcpy(buf + 1, enc_size.data(), enc_size.size());
445 HANDLE hout = fd_to_handle(
fdout);
449 BOOL ok = WriteFile(hout, buf + count, c - count, &n, &overlapped);
451 int errcode = GetLastError();
452 if (errcode != ERROR_IO_PENDING)
456 waitrc = WaitForSingleObject(overlapped.hEvent, calc_read_wait_msecs(
end_time));
457 if (waitrc != WAIT_OBJECT_0) {
458 LOGLINE(REMOTE,
"write: timeout has expired");
462 if (!GetOverlappedResult(hout, &overlapped, &n, FALSE))
464 context, -
int(GetLastError()));
470 update_overlapped_offset(overlapped, n);
473 if (size == 0)
return;
477 res = read(fd, buf,
sizeof(buf));
478 }
while (res < 0 && errno == EINTR);
488 if (fcntl(
fdout, F_SETFL, (
end_time != 0.0) ? O_NONBLOCK : 0) < 0) {
502 if (size == 0)
return;
506 res = read(fd, buf,
sizeof(buf));
507 }
while (res < 0 && errno == EINTR);
517 LOGLINE(REMOTE,
"write gave errno = " << errno);
518 if (errno == EINTR)
continue;
526 LOGLINE(REMOTE,
"write: timeout has expired");
534 fds.events = POLLOUT;
535 int result = poll(&fds, 1,
int(time_diff * 1000));
536 # define POLLSELECT "poll"
538 if (
fdout >= FD_SETSIZE) {
546 FD_SET(
fdout, &fdset);
550 int result = select(
fdout + 1, 0, &fdset, 0, &tv);
551 # define POLLSELECT "select"
555 if (errno == EINTR || errno == EAGAIN) {
575 LOGCALL(REMOTE,
int,
"RemoteConnection::sniff_next_message_type",
end_time);
581 unsigned char type =
buffer[0];
588 LOGCALL(REMOTE,
int,
"RemoteConnection::get_message", result |
end_time);
596 size_t len =
static_cast<unsigned char>(
buffer[1]);
600 result.assign(
buffer.data() + 2, len);
601 unsigned char type =
buffer[0];
611 const char* p_end =
p +
buffer.size();
616 size_t header_len = (
p -
buffer.data());
619 result.assign(
buffer.data() + header_len, len);
620 unsigned char type =
buffer[0];
621 buffer.erase(0, header_len + len);
628 LOGCALL(REMOTE,
int,
"RemoteConnection::get_message_chunked",
end_time);
637 uint_least64_t len =
static_cast<unsigned char>(
buffer[1]);
650 const char* p_end =
p +
buffer.size();
656 size_t header_len = (
p -
buffer.data());
657 unsigned char type =
buffer[0];
658 buffer.erase(0, header_len);
666 LOGCALL(REMOTE,
int,
"RemoteConnection::get_message_chunk", result | at_least |
end_time);
670 if (at_least <= result.size())
RETURN(
true);
671 at_least -= result.size();
680 result.append(
buffer, 0, retlen);
692 ssize_t c = write(fd,
p, n);
694 if (errno == EINTR)
continue;
705 LOGCALL(REMOTE,
int,
"RemoteConnection::receive_file", file |
end_time);
721 buffer.erase(0, min_read);
729 LOGCALL_VOID(REMOTE,
"RemoteConnection::shutdown", NO_ARGS);
731 if (
fdin < 0)
return;
737 HANDLE hin = fd_to_handle(
fdin);
740 BOOL ok = ReadFile(hin, &
dummy, 1, &received, &overlapped);
741 if (!ok && GetLastError() == ERROR_IO_PENDING) {
743 (void)WaitForSingleObject(overlapped.hEvent, INFINITE);
754 res = poll(&fds, 1, -1);
755 }
while (res < 0 && (errno == EINTR || errno == EAGAIN));
757 if (
fdin < FD_SETSIZE) {
760 FD_SET(
fdin, &fdset);
763 res = select(
fdin + 1, &fdset, 0, 0, NULL);
764 }
while (res < 0 && (errno == EINTR || errno == EAGAIN));
775 LOGCALL_VOID(REMOTE,
"RemoteConnection::do_close", NO_ARGS);
794 RemoteConnection::calc_read_wait_msecs(
double end_time)
803 if (time_diff < 0.0) {
806 return static_cast<DWORD
>(time_diff * 1000.0);
void send_message(char type, std::string_view s, double end_time)
Send a message.
int fdin
The file descriptor used for reading.
std::string buffer
Buffer to hold unprocessed input.
RemoteConnection(const RemoteConnection &)
Don't allow copying.
int get_message_chunk(std::string &result, size_t at_least, double end_time)
Read a chunk of a message from fdin.
bool read_at_least(size_t min_len, double end_time)
Read until there are at least min_len bytes in buffer.
int fdout
The file descriptor used for writing.
int receive_file(const std::string &file, double end_time)
Save the contents of a message as a file.
void do_close()
Close the connection.
int get_message(std::string &result, double end_time)
Read one message from fdin.
size_t chunked_data_left
Remaining bytes of message data still to come over fdin for a chunked read.
void shutdown()
Shutdown the connection.
int sniff_next_message_type(double end_time)
Check what the next message type is.
int get_message_chunked(double end_time)
Prepare to read one message from fdin in chunks.
std::string context
The context to report with errors.
ssize_t send_or_write(const void *p, size_t n)
Helper which calls send() or write().
void send_file(char type, int fd, double end_time)
Send the contents of a file as a message.
Indicates an attempt to access a closed database.
Indicates a problem communicating with a remote database.
Indicates a timeout expired while communicating with a remote database.
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
#define LOGCALL_VOID(CATEGORY, FUNC, PARAMS)
Hierarchy of classes which Xapian can throw as exceptions.
Wrapper class around a file descriptor to avoid leaks.
Utility functions for testing files.
file_size_type file_size(const char *path)
Returns the size of a file.
double end_time(double timeout)
Return the end time for a timeout in timeout seconds.
double now()
Return the current time.
void to_timeval(double t, struct timeval *tv)
Fill in struct timeval from number of seconds in a double.
void sleep(double t)
Sleep until the time represented by this object.
string str(int value)
Convert int to std::string.
Various assertion macros.
#define AssertRel(A, REL, B)
Arithmetic operations with overflow checks.
std::enable_if_t< std::is_unsigned_v< T1 > &&std::is_unsigned_v< T2 > &&std::is_unsigned_v< R >, bool > add_overflows(T1 a, T2 b, R &res)
Addition with overflow checking.
Pack types into strings and unpack them again.
bool unpack_uint(const char **p, const char *end, U *result)
Decode an unsigned integer from a string.
void pack_uint(std::string &s, U value)
Append an encoded unsigned integer to a string.
Provides wrappers with POSIXy semantics.
Functions for handling a time or time interval in a double.
static void throw_database_closed()
static void throw_timeout(const char *msg, const string &context)
static void write_all(int fd, const char *p, size_t n)
Write n bytes from block pointed to by p to file descriptor fd.
static constexpr size_t CHUNKSIZE
RemoteConnection class used by the remote backend.
include <fcntl.h>, but working around broken platforms.
include <sys/select.h> with portability workarounds.
<unistd.h>, but with compat.
Socket handling utilities.
void close_fd_or_socket(int fd)