From a365cd2033ea4447126b052e70d75239b7ab6abc Mon Sep 17 00:00:00 2001 From: xfy Date: Wed, 29 Apr 2026 18:24:15 +0800 Subject: [PATCH] refactor(handler): separate sendfile common code Create sendfile_common.go for shared constants and functions: - MinSendfileSize constant - getNetConn helper - copyFile fallback function Platform-specific files now only contain platform implementations. Eliminates ~50 lines of duplicate code between Linux and non-Linux. Co-Authored-By: Claude Opus 4.7 --- internal/handler/sendfile.go | 53 +------------------------- internal/handler/sendfile_common.go | 59 +++++++++++++++++++++++++++++ internal/handler/sendfile_linux.go | 50 +----------------------- 3 files changed, 63 insertions(+), 99 deletions(-) create mode 100644 internal/handler/sendfile_common.go diff --git a/internal/handler/sendfile.go b/internal/handler/sendfile.go index cbf0adc..22e7914 100644 --- a/internal/handler/sendfile.go +++ b/internal/handler/sendfile.go @@ -8,20 +8,12 @@ package handler import ( - "io" - "net" "os" "syscall" "github.com/valyala/fasthttp" ) -const ( - // MinSendfileSize 使用 sendfile 的最小文件大小(8KB)。 - // 小于该值的文件使用普通 io.Copy,避免系统调用开销。 - MinSendfileSize = 8 * 1024 -) - // SendFile 零拷贝文件传输。 // // 大文件使用系统调用直接从文件传输到 socket,避免用户空间拷贝, @@ -68,47 +60,6 @@ func SendFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) err return nil } -// getNetConn 从 fasthttp.RequestCtx 获取底层 net.Conn。 -// -// 参数: -// - ctx: fasthttp 请求上下文 -// -// 返回值: -// - net.Conn: 底层网络连接,如果无法获取则返回 nil -func getNetConn(ctx *fasthttp.RequestCtx) net.Conn { - // fasthttp 内部使用 net.Conn,通过接口获取 - return ctx.Conn() -} - -// copyFile 普通文件拷贝(fallback)。 -// -// 使用 io.Copy 进行文件传输,适用于不支持 sendfile 的平台或小文件。 -// -// 参数: -// - ctx: fasthttp 请求上下文,作为写入目标 -// - file: 源文件对象 -// - offset: 文件起始偏移量 -// - length: 传输长度,0 表示拷贝到文件末尾 -// -// 返回值: -// - error: 拷贝过程中的错误 -func copyFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) error { - if offset > 0 { - if _, err := file.Seek(offset, io.SeekStart); err != nil { - return err - } - } - - // 使用 io.CopyN 或 io.Copy - if length > 0 { - _, err := io.CopyN(ctx, file, length) - return err - } - - _, err := io.Copy(ctx, file) - return err -} - // platformSendfile 非 Linux 平台的 sendfile 实现。 // // macOS 和 Windows 不支持 sendfile 系统调用,返回 ENOTSUP 触发 fallback。 @@ -121,8 +72,8 @@ func copyFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) err // // 返回值: // - error: 始终返回 ENOTSUP,表示不支持 -func platformSendfile(conn net.Conn, file *os.File, offset, length int64) error { +func platformSendfile(conn any, file *os.File, offset, length int64) error { // macOS sendfile 签名复杂,简化使用 fallback // Windows TransmitFile 需要特殊 API return syscall.ENOTSUP -} +} \ No newline at end of file diff --git a/internal/handler/sendfile_common.go b/internal/handler/sendfile_common.go new file mode 100644 index 0000000..2f02327 --- /dev/null +++ b/internal/handler/sendfile_common.go @@ -0,0 +1,59 @@ +// Package handler 提供 HTTP 请求处理器,包括路由、静态文件服务和零拷贝传输。 +// +// 该文件包含 sendfile 的公共代码,供所有平台使用。 +// +// 作者:xfy +package handler + +import ( + "io" + "net" + "os" + + "github.com/valyala/fasthttp" +) + +const ( + // MinSendfileSize 使用 sendfile 的最小文件大小(8KB)。 + // 小于该值的文件使用普通 io.Copy,避免系统调用开销。 + MinSendfileSize = 8 * 1024 +) + +// getNetConn 从 fasthttp.RequestCtx 获取底层 net.Conn。 +// +// 参数: +// - ctx: fasthttp 请求上下文 +// +// 返回值: +// - net.Conn: 底层网络连接,如果无法获取则返回 nil +func getNetConn(ctx *fasthttp.RequestCtx) net.Conn { + return ctx.Conn() +} + +// copyFile 普通文件拷贝(fallback)。 +// +// 使用 io.Copy 进行文件传输,适用于不支持 sendfile 的平台或小文件。 +// +// 参数: +// - ctx: fasthttp 请求上下文,作为写入目标 +// - file: 源文件对象 +// - offset: 文件起始偏移量 +// - length: 传输长度,0 表示拷贝到文件末尾 +// +// 返回值: +// - error: 拷贝过程中的错误 +func copyFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) error { + if offset > 0 { + if _, err := file.Seek(offset, io.SeekStart); err != nil { + return err + } + } + + if length > 0 { + _, err := io.CopyN(ctx, file, length) + return err + } + + _, err := io.Copy(ctx, file) + return err +} \ No newline at end of file diff --git a/internal/handler/sendfile_linux.go b/internal/handler/sendfile_linux.go index 45a0e00..1b792a7 100644 --- a/internal/handler/sendfile_linux.go +++ b/internal/handler/sendfile_linux.go @@ -2,13 +2,12 @@ // Package handler 提供 HTTP 请求处理器,包括路由、静态文件服务和零拷贝传输。 // -// 该文件包含 Linux 平台完整的 sendfile 实现(零拷贝 + 公共函数)。 +// 该文件包含 Linux 平台完整的 sendfile 实现(零拷贝)。 // // 作者:xfy package handler import ( - "io" "net" "os" "syscall" @@ -18,10 +17,6 @@ import ( ) const ( - // MinSendfileSize 使用 sendfile 的最小文件大小(8KB)。 - // 小于该值的文件使用普通 io.Copy,避免系统调用开销。 - MinSendfileSize = 8 * 1024 - // sendfileMaxRetries sendfile 系统调用最大重试次数。 // 用于处理 EAGAIN/EWOULDBLOCK 等临时性错误。 sendfileMaxRetries = 100 @@ -71,47 +66,6 @@ func SendFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) err return nil } -// getNetConn 从 fasthttp.RequestCtx 获取底层 net.Conn。 -// -// 用于获取网络连接以便提取 socket 文件描述符。 -// -// 参数: -// - ctx: fasthttp 请求上下文 -// -// 返回值: -// - net.Conn: 底层网络连接,如果无法获取则返回 nil -func getNetConn(ctx *fasthttp.RequestCtx) net.Conn { - return ctx.Conn() -} - -// copyFile 普通文件拷贝(fallback)。 -// -// 使用 io.Copy 进行文件传输,适用于不支持 sendfile 的场景或小文件。 -// -// 参数: -// - ctx: fasthttp 请求上下文,作为写入目标 -// - file: 源文件对象 -// - offset: 文件起始偏移量 -// - length: 传输长度,0 表示拷贝到文件末尾 -// -// 返回值: -// - error: 拷贝过程中的错误 -func copyFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) error { - if offset > 0 { - if _, err := file.Seek(offset, io.SeekStart); err != nil { - return err - } - } - - if length > 0 { - _, err := io.CopyN(ctx, file, length) - return err - } - - _, err := io.Copy(ctx, file) - return err -} - // linuxSendfile Linux sendfile 系统调用。 // // 使用 Linux 特有的 sendfile 系统调用实现零拷贝传输。 @@ -213,4 +167,4 @@ func getSocketFd(conn net.Conn) (uintptr, error) { default: return 0, syscall.ENOTSUP } -} +} \ No newline at end of file