@@ -15,11 +15,12 @@ fn C.epoll_ctl(__epfd int, __op int, __fd int, __event &C.epoll_event) int
1515
1616fn 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
2020fn 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
2930struct C.epoll_event {
3031 events u32
31- data C.epoll_data
32+ data C.epoll_data_t
3233}
3334
3435struct 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