sendfile_unix_alt.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  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. //go:build (darwin && !ios) || dragonfly || freebsd || solaris
  5. package net
  6. import (
  7. "internal/poll"
  8. "io"
  9. "os"
  10. )
  11. // sendFile copies the contents of r to c using the sendfile
  12. // system call to minimize copies.
  13. //
  14. // if handled == true, sendFile returns the number of bytes copied and any
  15. // non-EOF error.
  16. //
  17. // if handled == false, sendFile performed no work.
  18. func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
  19. // FreeBSD, DragonFly and Solaris use 0 as the "until EOF" value.
  20. // If you pass in more bytes than the file contains, it will
  21. // loop back to the beginning ad nauseam until it's sent
  22. // exactly the number of bytes told to. As such, we need to
  23. // know exactly how many bytes to send.
  24. var remain int64 = 0
  25. lr, ok := r.(*io.LimitedReader)
  26. if ok {
  27. remain, r = lr.N, lr.R
  28. if remain <= 0 {
  29. return 0, nil, true
  30. }
  31. }
  32. f, ok := r.(*os.File)
  33. if !ok {
  34. return 0, nil, false
  35. }
  36. if remain == 0 {
  37. fi, err := f.Stat()
  38. if err != nil {
  39. return 0, err, false
  40. }
  41. remain = fi.Size()
  42. }
  43. // The other quirk with FreeBSD/DragonFly/Solaris's sendfile
  44. // implementation is that it doesn't use the current position
  45. // of the file -- if you pass it offset 0, it starts from
  46. // offset 0. There's no way to tell it "start from current
  47. // position", so we have to manage that explicitly.
  48. pos, err := f.Seek(0, io.SeekCurrent)
  49. if err != nil {
  50. return 0, err, false
  51. }
  52. sc, err := f.SyscallConn()
  53. if err != nil {
  54. return 0, nil, false
  55. }
  56. var werr error
  57. err = sc.Read(func(fd uintptr) bool {
  58. written, werr = poll.SendFile(&c.pfd, int(fd), pos, remain)
  59. return true
  60. })
  61. if err == nil {
  62. err = werr
  63. }
  64. if lr != nil {
  65. lr.N = remain - written
  66. }
  67. _, err1 := f.Seek(written, io.SeekCurrent)
  68. if err1 != nil && err == nil {
  69. return written, err1, written > 0
  70. }
  71. return written, wrapSyscallError("sendfile", err), written > 0
  72. }