-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add hio_sendfile() API for zero-copy file transfer using sendfile(2) #821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,15 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "herr.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "hthread.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #ifdef OS_LINUX | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <sys/sendfile.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #if defined(OS_DARWIN) || defined(OS_FREEBSD) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <sys/types.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <sys/socket.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <sys/uio.h> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static void __connect_timeout_cb(htimer_t* timer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hio_t* io = (hio_t*)timer->privdata; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (io) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -304,6 +313,60 @@ static int __nio_write(hio_t* io, const void* buf, int len, struct sockaddr* add | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nwrite; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Platform-abstracted sendfile: returns bytes sent, -1 on error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static ssize_t __nio_sendfile_sys(int out_fd, int in_fd, off_t* offset, size_t count) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #ifdef OS_LINUX | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return sendfile(out_fd, in_fd, offset, count); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #elif defined(OS_DARWIN) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| off_t len = count; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int ret = sendfile(in_fd, out_fd, *offset, &len, NULL, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (ret == 0 || (ret == -1 && errno == EAGAIN && len > 0)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *offset += len; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (ssize_t)len; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #elif defined(OS_FREEBSD) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| off_t sbytes = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int ret = sendfile(in_fd, out_fd, *offset, count, NULL, &sbytes, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (ret == 0 || (ret == -1 && errno == EAGAIN && sbytes > 0)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *offset += sbytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (ssize_t)sbytes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fallback: pread + write (sends one chunk per call to integrate with event loop) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| char buf[65536]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size_t to_read = count < sizeof(buf) ? count : sizeof(buf); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ssize_t nread = pread(in_fd, buf, to_read, *offset); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (nread <= 0) return nread; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ssize_t total_written = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (total_written < nread) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ssize_t nwrite = write(out_fd, buf + total_written, nread - total_written); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (nwrite < 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (total_written > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *offset += total_written; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return total_written; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return -1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (nwrite == 0) break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| total_written += nwrite; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| *offset += total_written; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return total_written; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Try sendfile for the current io, called with write_mutex held | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Returns: > 0 bytes sent, 0 if nothing sent, < 0 on error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static ssize_t __nio_sendfile(hio_t* io) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ssize_t nsent = __nio_sendfile_sys(io->fd, io->sendfile_fd, &io->sendfile_offset, io->sendfile_remain); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (nsent > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| io->sendfile_remain -= nsent; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nsent; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static void nio_read(hio_t* io) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // printd("nio_read fd=%d\n", io->fd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void* buf; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -361,6 +424,10 @@ static void nio_write(hio_t* io) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hrecursive_mutex_lock(&io->write_mutex); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| write: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (write_queue_empty(&io->write_queue)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check for pending sendfile after write queue is drained | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (io->sendfile_fd >= 0 && io->sendfile_remain > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| goto do_sendfile; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hrecursive_mutex_unlock(&io->write_mutex); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (io->close) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| io->close = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -407,6 +474,33 @@ static void nio_write(hio_t* io) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hrecursive_mutex_unlock(&io->write_mutex); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| do_sendfile: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ssize_t nsent = __nio_sendfile(io); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (nsent < 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = socket_errno(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (err == EAGAIN || err == EINTR) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hrecursive_mutex_unlock(&io->write_mutex); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| io->error = err; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| goto write_error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (nsent == 0 && (io->io_type & HIO_TYPE_SOCK_STREAM)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| goto disconnect; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+490
to
+491
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (nsent == 0 && (io->io_type & HIO_TYPE_SOCK_STREAM)) { | |
| goto disconnect; | |
| /* Treat nsent == 0 as sendfile completion (EOF on file), not as a socket disconnect. */ | |
| if (nsent == 0) { | |
| io->sendfile_remain = 0; |
Copilot
AI
Mar 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SSL fallback reads the entire [offset, offset+length) synchronously in a tight loop and calls hio_write repeatedly. This can block the event-loop thread on disk I/O and can enqueue up to length bytes into the write queue (triggering max_write_bufsize overflow / unexpected connection close) which defeats the large-file streaming goal. Consider implementing the SSL path as incremental chunking driven by HV_WRITE (similar to the non-SSL sendfile state machine), or at least stop reading once the write queue reaches a threshold and resume on write-complete events.
| // SSL: fall back to read + hio_write (sendfile cannot bypass SSL encryption) | |
| // NOTE: hio_write is non-blocking and queues data internally, | |
| // so this won't block the event loop even for large files. | |
| if (io->io_type == HIO_TYPE_SSL) { | |
| char buf[65536]; | |
| off_t cur_offset = offset; | |
| size_t remaining = length; | |
| while (remaining > 0) { | |
| size_t to_read = remaining < sizeof(buf) ? remaining : sizeof(buf); | |
| ssize_t nread = pread(in_fd, buf, to_read, cur_offset); | |
| if (nread < 0) { | |
| hloge("hio_sendfile pread error: %s", strerror(errno)); | |
| return -1; | |
| } | |
| if (nread == 0) { | |
| hlogw("hio_sendfile: unexpected EOF at offset %lld", (long long)cur_offset); | |
| break; | |
| } | |
| int nwrite = hio_write(io, buf, nread); | |
| if (nwrite < 0) return nwrite; | |
| cur_offset += nread; | |
| remaining -= nread; | |
| } | |
| // SSL: fall back to read + hio_write (sendfile cannot bypass SSL encryption). | |
| // To avoid blocking the event-loop thread and overfilling the write queue, | |
| // limit how much data we read and enqueue in a single call. | |
| if (io->io_type == HIO_TYPE_SSL) { | |
| char buf[65536]; | |
| off_t cur_offset = offset; | |
| size_t remaining = length; | |
| /* Cap the amount of data we enqueue in one call to hio_sendfile over SSL. | |
| * This bounds both disk I/O time and queued bytes, even if 'length' is large. */ | |
| const size_t MAX_SSL_SENDFILE_ENQUEUE = 1024 * 1024; /* 1 MiB per call */ | |
| size_t total_enqueued = 0; | |
| while (remaining > 0 && total_enqueued < MAX_SSL_SENDFILE_ENQUEUE) { | |
| size_t to_read = remaining < sizeof(buf) ? remaining : sizeof(buf); | |
| if (to_read > (MAX_SSL_SENDFILE_ENQUEUE - total_enqueued)) { | |
| to_read = MAX_SSL_SENDFILE_ENQUEUE - total_enqueued; | |
| } | |
| ssize_t nread; | |
| do { | |
| nread = pread(in_fd, buf, to_read, cur_offset); | |
| } while (nread < 0 && errno == EINTR); | |
| if (nread < 0) { | |
| if (errno == EAGAIN) { | |
| /* Caller can retry later; don't treat as fatal here. */ | |
| break; | |
| } | |
| hloge("hio_sendfile pread error: %s", strerror(errno)); | |
| return -1; | |
| } | |
| if (nread == 0) { | |
| /* Reached EOF before sending the requested length. */ | |
| hlogw("hio_sendfile: unexpected EOF at offset %lld", (long long)cur_offset); | |
| break; | |
| } | |
| int nwrite = hio_write(io, buf, (size_t)nread); | |
| if (nwrite < 0) { | |
| return nwrite; | |
| } | |
| cur_offset += nread; | |
| remaining -= (size_t)nread; | |
| total_enqueued += (size_t)nread; | |
| } | |
| /* If 'remaining' is still > 0, the caller may invoke hio_sendfile again | |
| * with an updated offset/length to continue sending. */ |
Copilot
AI
Mar 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hio_sendfile overwrites io->sendfile_fd/offset/remain unconditionally. If a caller invokes hio_sendfile again while a previous sendfile is still active, the in-flight transfer state will be corrupted. Please add a guard that returns an error (e.g., busy) when sendfile_fd >= 0 && sendfile_remain > 0, or explicitly cancel/finish the previous sendfile before starting a new one.
Copilot
AI
Mar 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
io->sendfile_fd = -1; is written after releasing write_mutex (both in nio_write completion handling and in the immediate-send path here). Since hio_sendfile/write code is otherwise synchronized via write_mutex, updating sendfile state outside the lock introduces a data race with other threads calling hio_sendfile/hio_write_bufsize/event logic. Consider setting/clearing sendfile_fd (and any related fields) while holding write_mutex, or making these fields atomic if they must be touched lock-free.
| hrecursive_mutex_unlock(&io->write_mutex); | |
| __write_cb(io, NULL, nsent); | |
| if (complete) { | |
| io->sendfile_fd = -1; | |
| if (complete) { | |
| io->sendfile_fd = -1; | |
| } | |
| hrecursive_mutex_unlock(&io->write_mutex); | |
| __write_cb(io, NULL, nsent); | |
| if (complete) { |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -387,6 +387,29 @@ int hio_sendto (hio_t* io, const void* buf, size_t len, struct sockaddr* addr) { | |
| return hio_write4(io, buf, len, addr ? addr : io->peeraddr); | ||
| } | ||
|
|
||
| int hio_sendfile (hio_t* io, int in_fd, off_t offset, size_t length) { | ||
| if (io->closed) return -1; | ||
| if (in_fd < 0) return -1; | ||
| if (length == 0) return 0; | ||
| // NOTE: Windows fallback uses read + hio_write. | ||
| // hio_write is non-blocking (queues via IOCP), so this won't block. | ||
| char buf[65536]; | ||
| off_t cur_offset = offset; | ||
| size_t remaining = length; | ||
| while (remaining > 0) { | ||
| size_t to_read = remaining < sizeof(buf) ? remaining : sizeof(buf); | ||
| if (_lseeki64(in_fd, cur_offset, SEEK_SET) < 0) return -1; | ||
| ssize_t nread = _read(in_fd, buf, (unsigned int)to_read); | ||
| if (nread < 0) return -1; | ||
| if (nread == 0) break; // EOF | ||
| int nwrite = hio_write(io, buf, nread); | ||
| if (nwrite < 0) return nwrite; | ||
| cur_offset += nread; | ||
| remaining -= nread; | ||
| } | ||
| return 0; | ||
|
Comment on lines
+390
to
+410
|
||
| } | ||
|
|
||
| int hio_close (hio_t* io) { | ||
| if (io->closed) return 0; | ||
| io->closed = 1; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -659,6 +659,20 @@ int HttpHandler::defaultLargeFileHandler(const std::string &filepath) { | |||||||||||||||||||||
| // forbidden to send large file | ||||||||||||||||||||||
| resp->content_length = 0; | ||||||||||||||||||||||
| resp->status_code = HTTP_STATUS_FORBIDDEN; | ||||||||||||||||||||||
| } else if (service->limit_rate < 0 && file->fp && fileno(file->fp) >= 0) { | ||||||||||||||||||||||
| // unlimited: use zero-copy sendfile | ||||||||||||||||||||||
| int filefd = fileno(file->fp); | ||||||||||||||||||||||
| size_t length = resp->content_length; | ||||||||||||||||||||||
| writer->EndHeaders(); | ||||||||||||||||||||||
| writer->onwrite = [this](HBuf* buf) { | ||||||||||||||||||||||
| if (writer->isWriteComplete()) { | ||||||||||||||||||||||
| resp->content_length = 0; | ||||||||||||||||||||||
| writer->End(); | ||||||||||||||||||||||
| closeFile(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
| hio_sendfile(io, filefd, 0, length); | ||||||||||||||||||||||
|
||||||||||||||||||||||
| hio_sendfile(io, filefd, 0, length); | |
| int rv = hio_sendfile(io, filefd, 0, length); | |
| if (rv != 0) { | |
| // sendfile failed synchronously: clean up and report error | |
| resp->status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR; | |
| resp->content_length = 0; | |
| closeFile(); | |
| writer->End(); | |
| return resp->status_code; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
hio_sendfiledoc comment is misleading/incomplete for consumers ofhwrite_cb: sendfile bytes are not part of the write queue, so completion is not just “write_queue is empty”; it also requiressendfile_remain == 0. Also, thebufpointer passed tohwrite_cbcan beNULLfor sendfile writes (__write_cb(io, NULL, nsent)), so callbacks must not assumebuf != NULL. Please clarify these semantics in the API comment.