Brad Fitzpatrick | 5194744 | 2016-03-01 22:57:46 +0000 | [diff] [blame] | 1 | // Copyright 2011 The Go Authors. All rights reserved. |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 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 |
Robert Griesemer | f391362 | 2014-05-02 13:17:55 -0700 | [diff] [blame] | 26 | // file contains, it will loop back to the beginning ad nauseam until it's sent |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 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. |
Brad Fitzpatrick | 381e5ee | 2016-04-13 04:35:37 +0000 | [diff] [blame] | 56 | pos, err := f.Seek(0, io.SeekCurrent) |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 57 | if err != nil { |
| 58 | return 0, err, false |
| 59 | } |
| 60 | |
Dmitriy Vyukov | 23e15f7 | 2013-08-09 21:43:00 +0400 | [diff] [blame] | 61 | if err := c.writeLock(); err != nil { |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 62 | return 0, err, true |
| 63 | } |
Dmitriy Vyukov | 23e15f7 | 2013-08-09 21:43:00 +0400 | [diff] [blame] | 64 | defer c.writeUnlock() |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 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 | } |
Russ Cox | f009fef | 2012-06-27 17:02:39 -0400 | [diff] [blame] | 73 | pos1 := pos |
| 74 | n, err1 := syscall.Sendfile(dst, src, &pos1, n) |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 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 | } |
Dave Cheney | 9fb9699 | 2012-12-05 15:59:01 +1100 | [diff] [blame] | 83 | if err1 == syscall.EAGAIN { |
Mikio Hara | ac2f84d | 2016-03-17 05:33:13 +0900 | [diff] [blame] | 84 | if err1 = c.pd.waitWrite(); err1 == nil { |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 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 |
Aram Hăvărneanu | a77fcb3 | 2015-03-23 21:33:08 +0100 | [diff] [blame] | 94 | // don't implement sendfile) |
Mikio Hara | ec11444 | 2015-04-16 23:10:56 +0900 | [diff] [blame] | 95 | err = err1 |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 96 | break |
| 97 | } |
| 98 | } |
| 99 | if lr != nil { |
| 100 | lr.N = remain |
| 101 | } |
Mikio Hara | 055ecb7 | 2015-04-21 21:20:15 +0900 | [diff] [blame] | 102 | if err != nil { |
| 103 | err = os.NewSyscallError("sendfile", err) |
| 104 | } |
L Campbell | a9a8d7b | 2012-06-25 20:26:19 -0400 | [diff] [blame] | 105 | return written, err, written > 0 |
| 106 | } |