Skip to content

Commit 8b0bc14

Browse files
committed
respect socket timeouts in generic copy path
poll() before each read in php_io_generic_copy so a blocking socket source with a stream timeout no longer blocks forever on platforms without a kernel offload (e.g. Haiku).
1 parent e214146 commit 8b0bc14

1 file changed

Lines changed: 77 additions & 1 deletion

File tree

main/io/php_io.c

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <winsock2.h>
2222
#else
2323
#include <unistd.h>
24+
#include <poll.h>
25+
#include <errno.h>
2426
#endif
2527

2628
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)
7577
return total_copied;
7678
}
7779

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+
78114
ssize_t php_io_generic_copy(php_io_fd *src, php_io_fd *dest, size_t maxlen)
79115
{
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;
81157
}

0 commit comments

Comments
 (0)