github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/net/sendfile_unix_alt.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build dragonfly || freebsd || solaris 6 // +build dragonfly freebsd solaris 7 8 package net 9 10 import ( 11 "internal/poll" 12 "io" 13 "os" 14 ) 15 16 // sendFile copies the contents of r to c using the sendfile 17 // system call to minimize copies. 18 // 19 // if handled == true, sendFile returns the number of bytes copied and any 20 // non-EOF error. 21 // 22 // if handled == false, sendFile performed no work. 23 func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { 24 // FreeBSD, DragonFly and Solaris use 0 as the "until EOF" value. 25 // If you pass in more bytes than the file contains, it will 26 // loop back to the beginning ad nauseam until it's sent 27 // exactly the number of bytes told to. As such, we need to 28 // know exactly how many bytes to send. 29 var remain int64 = 0 30 31 lr, ok := r.(*io.LimitedReader) 32 if ok { 33 remain, r = lr.N, lr.R 34 if remain <= 0 { 35 return 0, nil, true 36 } 37 } 38 f, ok := r.(*os.File) 39 if !ok { 40 return 0, nil, false 41 } 42 43 if remain == 0 { 44 fi, err := f.Stat() 45 if err != nil { 46 return 0, err, false 47 } 48 49 remain = fi.Size() 50 } 51 52 // The other quirk with FreeBSD/DragonFly/Solaris's sendfile 53 // implementation is that it doesn't use the current position 54 // of the file -- if you pass it offset 0, it starts from 55 // offset 0. There's no way to tell it "start from current 56 // position", so we have to manage that explicitly. 57 pos, err := f.Seek(0, io.SeekCurrent) 58 if err != nil { 59 return 0, err, false 60 } 61 62 sc, err := f.SyscallConn() 63 if err != nil { 64 return 0, nil, false 65 } 66 67 var werr error 68 err = sc.Read(func(fd uintptr) bool { 69 written, werr = poll.SendFile(&c.pfd, int(fd), pos, remain) 70 return true 71 }) 72 if err == nil { 73 err = werr 74 } 75 76 if lr != nil { 77 lr.N = remain - written 78 } 79 80 _, err1 := f.Seek(written, io.SeekCurrent) 81 if err1 != nil && err == nil { 82 return written, err1, written > 0 83 } 84 85 return written, wrapSyscallError("sendfile", err), written > 0 86 }