blob: 18cbb27b5330212aef3f010125722f0596924f80 [file] [log] [blame]
Brad Fitzpatrick51947442016-03-01 22:57:46 +00001// Copyright 2011 The Go Authors. All rights reserved.
L Campbella9a8d7b2012-06-25 20:26:19 -04002// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package net
6
7import (
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.
15const 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.
24func 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 Griesemerf3913622014-05-02 13:17:55 -070026 // file contains, it will loop back to the beginning ad nauseam until it's sent
L Campbella9a8d7b2012-06-25 20:26:19 -040027 // 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 Fitzpatrick381e5ee2016-04-13 04:35:37 +000056 pos, err := f.Seek(0, io.SeekCurrent)
L Campbella9a8d7b2012-06-25 20:26:19 -040057 if err != nil {
58 return 0, err, false
59 }
60
Dmitriy Vyukov23e15f72013-08-09 21:43:00 +040061 if err := c.writeLock(); err != nil {
L Campbella9a8d7b2012-06-25 20:26:19 -040062 return 0, err, true
63 }
Dmitriy Vyukov23e15f72013-08-09 21:43:00 +040064 defer c.writeUnlock()
L Campbella9a8d7b2012-06-25 20:26:19 -040065
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 Coxf009fef2012-06-27 17:02:39 -040073 pos1 := pos
74 n, err1 := syscall.Sendfile(dst, src, &pos1, n)
L Campbella9a8d7b2012-06-25 20:26:19 -040075 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 Cheney9fb96992012-12-05 15:59:01 +110083 if err1 == syscall.EAGAIN {
Mikio Haraac2f84d2016-03-17 05:33:13 +090084 if err1 = c.pd.waitWrite(); err1 == nil {
L Campbella9a8d7b2012-06-25 20:26:19 -040085 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ărneanua77fcb32015-03-23 21:33:08 +010094 // don't implement sendfile)
Mikio Haraec114442015-04-16 23:10:56 +090095 err = err1
L Campbella9a8d7b2012-06-25 20:26:19 -040096 break
97 }
98 }
99 if lr != nil {
100 lr.N = remain
101 }
Mikio Hara055ecb72015-04-21 21:20:15 +0900102 if err != nil {
103 err = os.NewSyscallError("sendfile", err)
104 }
L Campbella9a8d7b2012-06-25 20:26:19 -0400105 return written, err, written > 0
106}