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 package net 6 7 import ( 8 "io" 9 "os" 10 "syscall" 11 ) 12 13 // maxSendfileSize is the largest chunk size we ask the kernel to copy 14 // at a time. 15 const maxSendfileSize int = 4 << 20 16 17 // sendFile copies the contents of r to c using the sendfile 18 // system call to minimize copies. 19 // 20 // if handled == true, sendFile returns the number of bytes copied and any 21 // non-EOF error. 22 // 23 // if handled == false, sendFile performed no work. 24 func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { 25 // FreeBSD uses 0 as the "until EOF" value. If you pass in more bytes than the 26 // file contains, it will loop back to the beginning ad nauseam until it's sent 27 // exactly the number of bytes told to. As such, we need to know exactly how many 28 // 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's sendfile implementation is that it doesn't 53 // use the current position of the file -- if you pass it offset 0, it starts 54 // from offset 0. There's no way to tell it "start from current position", so 55 // we have to manage that explicitly. 56 pos, err := f.Seek(0, os.SEEK_CUR) 57 if err != nil { 58 return 0, err, false 59 } 60 61 if err := c.writeLock(); err != nil { 62 return 0, err, true 63 } 64 defer c.writeUnlock() 65 66 dst := c.sysfd 67 src := int(f.Fd()) 68 for remain > 0 { 69 n := maxSendfileSize 70 if int64(n) > remain { 71 n = int(remain) 72 } 73 pos1 := pos 74 n, err1 := syscall.Sendfile(dst, src, &pos1, n) 75 if n > 0 { 76 pos += int64(n) 77 written += int64(n) 78 remain -= int64(n) 79 } 80 if n == 0 && err1 == nil { 81 break 82 } 83 if err1 == syscall.EAGAIN { 84 if err1 = c.pd.WaitWrite(); err1 == nil { 85 continue 86 } 87 } 88 if err1 == syscall.EINTR { 89 continue 90 } 91 if err1 != nil { 92 // This includes syscall.ENOSYS (no kernel 93 // support) and syscall.EINVAL (fd types which 94 // don't implement sendfile) 95 err = err1 96 break 97 } 98 } 99 if lr != nil { 100 lr.N = remain 101 } 102 if err != nil { 103 err = os.NewSyscallError("sendfile", err) 104 } 105 return written, err, written > 0 106 } 107