|
21 | 21 | #include <winsock2.h> |
22 | 22 | #else |
23 | 23 | #include <unistd.h> |
| 24 | +#include <poll.h> |
| 25 | +#include <errno.h> |
24 | 26 | #endif |
25 | 27 |
|
26 | 28 | static php_io php_io_instance = { |
@@ -75,7 +77,81 @@ ssize_t php_io_generic_copy_fallback(int src_fd, int dest_fd, size_t maxlen) |
75 | 77 | return total_copied; |
76 | 78 | } |
77 | 79 |
|
| 80 | +/* For a blocking socket source, wait until data is available (or the configured |
| 81 | + * timeout elapses) before reading. Mirrors the per-platform wait_for_data |
| 82 | + * helpers so the generic copy path honours stream timeouts on systems without a |
| 83 | + * kernel offload (e.g. Haiku). Returns >0 ready, 0 timeout, <0 error. */ |
| 84 | +#ifndef PHP_WIN32 |
| 85 | +static int php_io_generic_wait_for_data(php_io_fd *fd) |
| 86 | +{ |
| 87 | + if (fd->fd_type != PHP_IO_FD_SOCKET || !fd->is_blocked) { |
| 88 | + return 1; |
| 89 | + } |
| 90 | + |
| 91 | + int timeout_ms = (fd->timeout.tv_sec == -1) |
| 92 | + ? -1 |
| 93 | + : (int) (fd->timeout.tv_sec * 1000 + fd->timeout.tv_usec / 1000); |
| 94 | + |
| 95 | + struct pollfd pfd; |
| 96 | + pfd.fd = fd->fd; |
| 97 | + pfd.events = POLLIN; |
| 98 | + |
| 99 | + int ret; |
| 100 | + do { |
| 101 | + ret = poll(&pfd, 1, timeout_ms); |
| 102 | + } while (ret == -1 && errno == EINTR); |
| 103 | + |
| 104 | + return ret; |
| 105 | +} |
| 106 | +#else |
| 107 | +static int php_io_generic_wait_for_data(php_io_fd *fd) |
| 108 | +{ |
| 109 | + (void) fd; |
| 110 | + return 1; |
| 111 | +} |
| 112 | +#endif |
| 113 | + |
78 | 114 | ssize_t php_io_generic_copy(php_io_fd *src, php_io_fd *dest, size_t maxlen) |
79 | 115 | { |
80 | | - return php_io_generic_copy_fallback(src->fd, dest->fd, maxlen); |
| 116 | + char buf[PHP_IO_COPY_BUFSIZE]; |
| 117 | + ssize_t total_copied = 0; |
| 118 | + size_t remaining = (maxlen == PHP_IO_COPY_ALL) ? SIZE_MAX : maxlen; |
| 119 | + |
| 120 | + while (remaining > 0) { |
| 121 | + int ready = php_io_generic_wait_for_data(src); |
| 122 | + if (ready == 0) { |
| 123 | + /* timeout */ |
| 124 | + break; |
| 125 | + } else if (ready < 0) { |
| 126 | + return total_copied > 0 ? total_copied : -1; |
| 127 | + } |
| 128 | + |
| 129 | + size_t to_read = (remaining < sizeof(buf)) ? remaining : sizeof(buf); |
| 130 | + ssize_t bytes_read = read(src->fd, buf, to_read); |
| 131 | + |
| 132 | + if (bytes_read < 0) { |
| 133 | + return total_copied > 0 ? total_copied : -1; |
| 134 | + } else if (bytes_read == 0) { |
| 135 | + return total_copied; |
| 136 | + } |
| 137 | + |
| 138 | + char *writeptr = buf; |
| 139 | + size_t to_write = (size_t) bytes_read; |
| 140 | + |
| 141 | + while (to_write > 0) { |
| 142 | + ssize_t bytes_written = write(dest->fd, writeptr, to_write); |
| 143 | + if (bytes_written <= 0) { |
| 144 | + return total_copied > 0 ? total_copied : -1; |
| 145 | + } |
| 146 | + total_copied += bytes_written; |
| 147 | + writeptr += bytes_written; |
| 148 | + to_write -= bytes_written; |
| 149 | + } |
| 150 | + |
| 151 | + if (maxlen != PHP_IO_COPY_ALL) { |
| 152 | + remaining -= bytes_read; |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + return total_copied; |
81 | 157 | } |
0 commit comments