Skip to content

Commit a5b8ca0

Browse files
authored
fasthttp: fix sendfile (#26402)
1 parent a965a39 commit a5b8ca0

File tree

1 file changed

+78
-16
lines changed

1 file changed

+78
-16
lines changed

vlib/fasthttp/fasthttp_linux.v

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ fn C.epoll_ctl(__epfd int, __op int, __fd int, __event &C.epoll_event) int
1515

1616
fn C.epoll_wait(__epfd int, __events &C.epoll_event, __maxevents int, __timeout int) int
1717

18-
fn C.sendfile(out_fd int, in_fd int, offset &int, count usize) int
18+
fn C.sendfile(out_fd int, in_fd int, offset &i64, count usize) int
1919

2020
fn C.fstat(fd int, buf &C.stat) int
2121

22-
union C.epoll_data {
22+
@[typedef]
23+
union C.epoll_data_t {
2324
ptr voidptr
2425
fd int
2526
u32 u32
@@ -28,7 +29,7 @@ union C.epoll_data {
2829

2930
struct C.epoll_event {
3031
events u32
31-
data C.epoll_data
32+
data C.epoll_data_t
3233
}
3334

3435
struct Server {
@@ -201,14 +202,14 @@ fn process_events(mut server Server, epoll_fd int, listen_fd int) {
201202
for {
202203
num_events := C.epoll_wait(epoll_fd, &events[0], max_connection_size, -1)
203204
for i := 0; i < num_events; i++ {
205+
client_fd := unsafe { events[i].data.fd }
204206
// Accept new connections when the listening socket is readable
205-
if unsafe { events[i].data.fd } == listen_fd {
207+
if client_fd == listen_fd {
206208
handle_accept_loop(epoll_fd, listen_fd)
207209
continue
208210
}
209211

210212
if events[i].events & u32((C.EPOLLHUP | C.EPOLLERR)) != 0 {
211-
client_fd := unsafe { events[i].data.fd }
212213
if client_fd == listen_fd {
213214
eprintln('ERROR: listen fd had HUP/ERR')
214215
continue
@@ -224,7 +225,6 @@ fn process_events(mut server Server, epoll_fd int, listen_fd int) {
224225
continue
225226
}
226227
if events[i].events & u32(C.EPOLLIN) != 0 {
227-
client_fd := unsafe { events[i].data.fd }
228228
bytes_read := C.recv(client_fd, unsafe { &request_buffer[0] }, server.max_request_buffer_size - 1,
229229
0)
230230
if bytes_read > 0 {
@@ -257,10 +257,19 @@ fn process_events(mut server Server, epoll_fd int, listen_fd int) {
257257
}
258258
// Send response content (headers/body)
259259
if response.content.len > 0 {
260-
sent := C.send(client_fd, response.content.data, response.content.len,
261-
C.MSG_NOSIGNAL | C.MSG_DONTWAIT)
262-
if sent < 0 && C.errno != C.EAGAIN && C.errno != C.EWOULDBLOCK {
263-
eprintln('ERROR: send() failed with errno=${C.errno}')
260+
mut send_error := false
261+
mut pos := 0
262+
for pos < response.content.len {
263+
sent := C.send(client_fd, unsafe { &response.content[pos] },
264+
response.content.len - pos, C.MSG_NOSIGNAL)
265+
if sent <= 0 {
266+
eprintln('ERROR: send() failed with errno=${C.errno}')
267+
send_error = true
268+
break
269+
}
270+
pos += sent
271+
}
272+
if send_error {
264273
handle_client_closure(epoll_fd, client_fd)
265274
continue
266275
}
@@ -269,13 +278,66 @@ fn process_events(mut server Server, epoll_fd int, listen_fd int) {
269278
// Send file if present
270279
if response.file_path != '' {
271280
fd := C.open(response.file_path.str, C.O_RDONLY)
272-
if fd != -1 {
273-
mut st := C.stat{}
274-
C.fstat(fd, &st)
275-
offset := 0
276-
C.sendfile(client_fd, fd, &offset, usize(st.st_size))
277-
C.close(fd)
281+
if fd == -1 {
282+
eprintln('ERROR: open file failed')
283+
handle_client_closure(epoll_fd, client_fd)
284+
continue
278285
}
286+
mut st := C.stat{}
287+
if C.fstat(fd, &st) != 0 {
288+
eprintln('ERROR: fstat failed')
289+
handle_client_closure(epoll_fd, client_fd)
290+
continue
291+
}
292+
mut offset := i64(0)
293+
mut remaining := i64(st.st_size)
294+
mut sf_retries := 0
295+
for remaining > 0 {
296+
ssize := C.sendfile(client_fd, fd, &offset, usize(remaining))
297+
if ssize > 0 {
298+
remaining -= i64(ssize)
299+
sf_retries = 0
300+
continue
301+
}
302+
errno_val := C.errno
303+
match errno_val {
304+
C.EAGAIN, C.EWOULDBLOCK, C.EINTR {
305+
if sf_retries < 3 {
306+
sf_retries++
307+
continue
308+
}
309+
eprintln('ERROR: sendfile() transient failure after ${sf_retries} retries (errno=${errno_val})')
310+
}
311+
C.EBADF {
312+
eprintln('ERROR: sendfile() EBADF: input fd or socket not open for required access (errno=${errno_val})')
313+
}
314+
C.EFAULT {
315+
eprintln('ERROR: sendfile() EFAULT: bad address for offset (errno=${errno_val})')
316+
}
317+
C.EINVAL {
318+
eprintln('ERROR: sendfile() EINVAL: invalid descriptor state or non-seekable input (errno=${errno_val})')
319+
}
320+
C.EIO {
321+
eprintln('ERROR: sendfile() EIO: I/O error while reading input file (errno=${errno_val})')
322+
}
323+
C.ENOMEM {
324+
eprintln('ERROR: sendfile() ENOMEM: insufficient kernel memory (errno=${errno_val})')
325+
}
326+
C.EOVERFLOW {
327+
eprintln('ERROR: sendfile() EOVERFLOW: count exceeds file/socket limits (errno=${errno_val})')
328+
}
329+
C.ESPIPE {
330+
eprintln('ERROR: sendfile() ESPIPE: input file not seekable with offset (errno=${errno_val})')
331+
}
332+
else {
333+
eprintln('ERROR: sendfile() failed with errno=${errno_val}')
334+
}
335+
}
336+
handle_client_closure(epoll_fd, client_fd)
337+
break
338+
}
339+
340+
C.close(fd)
279341
}
280342
// Leave the connection open; closure is driven by client FIN or errors
281343
} else if bytes_read == 0 {

0 commit comments

Comments
 (0)