|  | // Copyright 2016 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // +build !js | 
|  |  | 
|  | package net | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "crypto/sha256" | 
|  | "encoding/hex" | 
|  | "fmt" | 
|  | "io" | 
|  | "os" | 
|  | "testing" | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | twain       = "testdata/Mark.Twain-Tom.Sawyer.txt" | 
|  | twainLen    = 387851 | 
|  | twainSHA256 = "461eb7cb2d57d293fc680c836464c9125e4382be3596f7d415093ae9db8fcb0e" | 
|  | ) | 
|  |  | 
|  | func TestSendfile(t *testing.T) { | 
|  | ln, err := newLocalListener("tcp") | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer ln.Close() | 
|  |  | 
|  | errc := make(chan error, 1) | 
|  | go func(ln Listener) { | 
|  | // Wait for a connection. | 
|  | conn, err := ln.Accept() | 
|  | if err != nil { | 
|  | errc <- err | 
|  | close(errc) | 
|  | return | 
|  | } | 
|  |  | 
|  | go func() { | 
|  | defer close(errc) | 
|  | defer conn.Close() | 
|  |  | 
|  | f, err := os.Open(twain) | 
|  | if err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  | defer f.Close() | 
|  |  | 
|  | // Return file data using io.Copy, which should use | 
|  | // sendFile if available. | 
|  | sbytes, err := io.Copy(conn, f) | 
|  | if err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  |  | 
|  | if sbytes != twainLen { | 
|  | errc <- fmt.Errorf("sent %d bytes; expected %d", sbytes, twainLen) | 
|  | return | 
|  | } | 
|  | }() | 
|  | }(ln) | 
|  |  | 
|  | // Connect to listener to retrieve file and verify digest matches | 
|  | // expected. | 
|  | c, err := Dial("tcp", ln.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer c.Close() | 
|  |  | 
|  | h := sha256.New() | 
|  | rbytes, err := io.Copy(h, c) | 
|  | if err != nil { | 
|  | t.Error(err) | 
|  | } | 
|  |  | 
|  | if rbytes != twainLen { | 
|  | t.Errorf("received %d bytes; expected %d", rbytes, twainLen) | 
|  | } | 
|  |  | 
|  | if res := hex.EncodeToString(h.Sum(nil)); res != twainSHA256 { | 
|  | t.Error("retrieved data hash did not match") | 
|  | } | 
|  |  | 
|  | for err := range errc { | 
|  | t.Error(err) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestSendfileParts(t *testing.T) { | 
|  | ln, err := newLocalListener("tcp") | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer ln.Close() | 
|  |  | 
|  | errc := make(chan error, 1) | 
|  | go func(ln Listener) { | 
|  | // Wait for a connection. | 
|  | conn, err := ln.Accept() | 
|  | if err != nil { | 
|  | errc <- err | 
|  | close(errc) | 
|  | return | 
|  | } | 
|  |  | 
|  | go func() { | 
|  | defer close(errc) | 
|  | defer conn.Close() | 
|  |  | 
|  | f, err := os.Open(twain) | 
|  | if err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  | defer f.Close() | 
|  |  | 
|  | for i := 0; i < 3; i++ { | 
|  | // Return file data using io.CopyN, which should use | 
|  | // sendFile if available. | 
|  | _, err = io.CopyN(conn, f, 3) | 
|  | if err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  | } | 
|  | }() | 
|  | }(ln) | 
|  |  | 
|  | c, err := Dial("tcp", ln.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer c.Close() | 
|  |  | 
|  | buf := new(bytes.Buffer) | 
|  | buf.ReadFrom(c) | 
|  |  | 
|  | if want, have := "Produced ", buf.String(); have != want { | 
|  | t.Errorf("unexpected server reply %q, want %q", have, want) | 
|  | } | 
|  |  | 
|  | for err := range errc { | 
|  | t.Error(err) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestSendfileSeeked(t *testing.T) { | 
|  | ln, err := newLocalListener("tcp") | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer ln.Close() | 
|  |  | 
|  | const seekTo = 65 << 10 | 
|  | const sendSize = 10 << 10 | 
|  |  | 
|  | errc := make(chan error, 1) | 
|  | go func(ln Listener) { | 
|  | // Wait for a connection. | 
|  | conn, err := ln.Accept() | 
|  | if err != nil { | 
|  | errc <- err | 
|  | close(errc) | 
|  | return | 
|  | } | 
|  |  | 
|  | go func() { | 
|  | defer close(errc) | 
|  | defer conn.Close() | 
|  |  | 
|  | f, err := os.Open(twain) | 
|  | if err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  | defer f.Close() | 
|  | if _, err := f.Seek(seekTo, os.SEEK_SET); err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  |  | 
|  | _, err = io.CopyN(conn, f, sendSize) | 
|  | if err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  | }() | 
|  | }(ln) | 
|  |  | 
|  | c, err := Dial("tcp", ln.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer c.Close() | 
|  |  | 
|  | buf := new(bytes.Buffer) | 
|  | buf.ReadFrom(c) | 
|  |  | 
|  | if buf.Len() != sendSize { | 
|  | t.Errorf("Got %d bytes; want %d", buf.Len(), sendSize) | 
|  | } | 
|  |  | 
|  | for err := range errc { | 
|  | t.Error(err) | 
|  | } | 
|  | } |