1
0
mirror of https://github.com/golang/go synced 2024-11-25 00:07:56 -07:00

net: change SetTimeout to SetDeadline

Previously, a timeout (in int64 nanoseconds) applied to a granularity
even smaller than one operation:  a 100 byte read with a 1 second timeout
could take 100 seconds, if the bytes all arrived on the network 1 second
apart.  This was confusing.

Rather than making the timeout granularity be per-Read/Write,
this CL makes callers set an absolute deadline (in time.Time)
after which operations will fail.  This makes it possible to
set deadlines at higher levels, without knowing exactly how
many read/write operations will happen in e.g. reading an HTTP
request.

Fixes #2723

R=r, rsc, dave
CC=golang-dev
https://golang.org/cl/5555048
This commit is contained in:
Brad Fitzpatrick 2012-01-18 16:24:06 -08:00
parent 5e77b009d0
commit b71883e9b0
23 changed files with 250 additions and 231 deletions

View File

@ -750,6 +750,17 @@ Gofix will update the few programs that are affected except for
uses of <code>RawURL</code>, which must be fixed by hand. uses of <code>RawURL</code>, which must be fixed by hand.
</p> </p>
<h3 id="net">The net package</h3>
<p>In Go 1, the various <code>SetTimeout</code>,
<code>SetReadTimeout</code>, and <code>SetWriteTimeout</code> methods
have been replaced with <code>SetDeadline</code>,
<code>SetReadDeadline</code>, and <code>SetWriteDeadline</code>,
respectively. Rather than taking a timeout value in nanoseconds that
apply to any activity on the connection, the new methods set an
absolute deadline (as a <code>time.Time</code> value) after which
reads and writes will time out and no longer block.</p>
<h3 id="strconv">The strconv package</h3> <h3 id="strconv">The strconv package</h3>
<p> <p>
@ -957,8 +968,8 @@ documentation for a package is created with:
</pre> </pre>
<p> <p>
where the new <code>mode</mode> parameter specifies the operation mode: where the new <code>mode</code> parameter specifies the operation mode:
if set to <a href="go/doc/#AllDecls"><code>AllDecls</a>, all declarations if set to <a href="go/doc/#AllDecls"><code>AllDecls</code></a>, all declarations
(not just exported ones) are considered. (not just exported ones) are considered.
The function <code>NewFileDoc</code> was removed, and the function The function <code>NewFileDoc</code> was removed, and the function
<code>CommentText</code> has become the method <code>CommentText</code> has become the method

View File

@ -654,6 +654,17 @@ Gofix will update the few programs that are affected except for
uses of <code>RawURL</code>, which must be fixed by hand. uses of <code>RawURL</code>, which must be fixed by hand.
</p> </p>
<h3 id="net">The net package</h3>
<p>In Go 1, the various <code>SetTimeout</code>,
<code>SetReadTimeout</code>, and <code>SetWriteTimeout</code> methods
have been replaced with <code>SetDeadline</code>,
<code>SetReadDeadline</code>, and <code>SetWriteDeadline</code>,
respectively. Rather than taking a timeout value in nanoseconds that
apply to any activity on the connection, the new methods set an
absolute deadline (as a <code>time.Time</code> value) after which
reads and writes will time out and no longer block.</p>
<h3 id="strconv">The strconv package</h3> <h3 id="strconv">The strconv package</h3>
<p> <p>
@ -861,8 +872,8 @@ documentation for a package is created with:
</pre> </pre>
<p> <p>
where the new <code>mode</mode> parameter specifies the operation mode: where the new <code>mode</code> parameter specifies the operation mode:
if set to <a href="go/doc/#AllDecls"><code>AllDecls</a>, all declarations if set to <a href="go/doc/#AllDecls"><code>AllDecls</code></a>, all declarations
(not just exported ones) are considered. (not just exported ones) are considered.
The function <code>NewFileDoc</code> was removed, and the function The function <code>NewFileDoc</code> was removed, and the function
<code>CommentText</code> has become the method <code>CommentText</code> has become the method

View File

@ -15,6 +15,7 @@ import (
"io" "io"
"net" "net"
"sync" "sync"
"time"
) )
// A Conn represents a secured connection. // A Conn represents a secured connection.
@ -86,24 +87,23 @@ func (c *Conn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr() return c.conn.RemoteAddr()
} }
// SetTimeout sets the read deadline associated with the connection. // SetDeadline sets the read deadline associated with the connection.
// There is no write deadline. // There is no write deadline.
func (c *Conn) SetTimeout(nsec int64) error { // A zero value for t means Read will not time out.
return c.conn.SetTimeout(nsec) func (c *Conn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
} }
// SetReadTimeout sets the time (in nanoseconds) that // SetReadDeadline sets the read deadline on the underlying connection.
// Read will wait for data before returning a net.Error // A zero value for t means Read will not time out.
// with Timeout() == true. func (c *Conn) SetReadDeadline(t time.Time) error {
// Setting nsec == 0 (the default) disables the deadline. return c.conn.SetReadDeadline(t)
func (c *Conn) SetReadTimeout(nsec int64) error {
return c.conn.SetReadTimeout(nsec)
} }
// SetWriteTimeout exists to satisfy the net.Conn interface // SetWriteDeadline exists to satisfy the net.Conn interface
// but is not implemented by TLS. It always returns an error. // but is not implemented by TLS. It always returns an error.
func (c *Conn) SetWriteTimeout(nsec int64) error { func (c *Conn) SetWriteDeadline(t time.Time) error {
return errors.New("TLS does not support SetWriteTimeout") return errors.New("TLS does not support SetWriteDeadline")
} }
// A halfConn represents one direction of the record layer // A halfConn represents one direction of the record layer
@ -744,7 +744,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
} }
// Read can be made to time out and return a net.Error with Timeout() == true // Read can be made to time out and return a net.Error with Timeout() == true
// after a fixed time limit; see SetTimeout and SetReadTimeout. // after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *Conn) Read(b []byte) (n int, err error) { func (c *Conn) Read(b []byte) (n int, err error) {
if err = c.Handshake(); err != nil { if err = c.Handshake(); err != nil {
return return

View File

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"time"
) )
// Dial initiates a connection to the addr from the remote host. // Dial initiates a connection to the addr from the remote host.
@ -107,27 +108,25 @@ func (t *tcpchanconn) RemoteAddr() net.Addr {
return t.raddr return t.raddr
} }
// SetTimeout sets the read and write deadlines associated // SetDeadline sets the read and write deadlines associated
// with the connection. // with the connection.
func (t *tcpchanconn) SetTimeout(nsec int64) error { func (t *tcpchanconn) SetDeadline(deadline time.Time) error {
if err := t.SetReadTimeout(nsec); err != nil { if err := t.SetReadDeadline(deadline); err != nil {
return err return err
} }
return t.SetWriteTimeout(nsec) return t.SetWriteDeadline(deadline)
} }
// SetReadTimeout sets the time (in nanoseconds) that // SetReadDeadline sets the read deadline.
// Read will wait for data before returning an error with Timeout() == true. // A zero value for t means Read will not time out.
// Setting nsec == 0 (the default) disables the deadline. // After the deadline, the error from Read will implement net.Error
func (t *tcpchanconn) SetReadTimeout(nsec int64) error { // with Timeout() == true.
return errors.New("ssh: tcpchan: timeout not supported") func (t *tcpchanconn) SetReadDeadline(deadline time.Time) error {
return errors.New("ssh: tcpchan: deadline not supported")
} }
// SetWriteTimeout sets the time (in nanoseconds) that // SetWriteDeadline exists to satisfy the net.Conn interface
// Write will wait to send its data before returning an error with Timeout() == true. // but is not implemented by this type. It always returns an error.
// Setting nsec == 0 (the default) disables the deadline. func (t *tcpchanconn) SetWriteDeadline(deadline time.Time) error {
// Even if write times out, it may return n > 0, indicating that return errors.New("ssh: tcpchan: deadline not supported")
// some of the data was successfully written.
func (t *tcpchanconn) SetWriteTimeout(nsec int64) error {
return errors.New("ssh: tcpchan: timeout not supported")
} }

View File

@ -8,6 +8,7 @@ import (
"log" "log"
"net" "net"
"testing" "testing"
"time"
) )
var serverAddr string var serverAddr string
@ -31,7 +32,7 @@ func startServer(done chan<- string) {
log.Fatalf("net.ListenPacket failed udp :0 %v", e) log.Fatalf("net.ListenPacket failed udp :0 %v", e)
} }
serverAddr = c.LocalAddr().String() serverAddr = c.LocalAddr().String()
c.SetReadTimeout(100e6) // 100ms c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
go runSyslog(c, done) go runSyslog(c, done)
} }

View File

@ -45,7 +45,11 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
return nil, err return nil, err
} }
c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds if cfg.timeout == 0 {
c.SetReadDeadline(time.Time{})
} else {
c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second))
}
buf := make([]byte, 2000) // More than enough. buf := make([]byte, 2000) // More than enough.
n, err = c.Read(buf) n, err = c.Read(buf)

View File

@ -33,12 +33,10 @@ type netFD struct {
raddr Addr raddr Addr
// owned by client // owned by client
rdeadline_delta int64 rdeadline int64
rdeadline int64 rio sync.Mutex
rio sync.Mutex wdeadline int64
wdeadline_delta int64 wio sync.Mutex
wdeadline int64
wio sync.Mutex
// owned by fd wait server // owned by fd wait server
ncr, ncw int ncr, ncw int
@ -388,11 +386,6 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
if fd.sysfile == nil { if fd.sysfile == nil {
return 0, os.EINVAL return 0, os.EINVAL
} }
if fd.rdeadline_delta > 0 {
fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
} else {
fd.rdeadline = 0
}
for { for {
n, err = syscall.Read(fd.sysfile.Fd(), p) n, err = syscall.Read(fd.sysfile.Fd(), p)
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
@ -423,11 +416,6 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
defer fd.rio.Unlock() defer fd.rio.Unlock()
fd.incref() fd.incref()
defer fd.decref() defer fd.decref()
if fd.rdeadline_delta > 0 {
fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
} else {
fd.rdeadline = 0
}
for { for {
n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
@ -456,11 +444,6 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
defer fd.rio.Unlock() defer fd.rio.Unlock()
fd.incref() fd.incref()
defer fd.decref() defer fd.decref()
if fd.rdeadline_delta > 0 {
fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
} else {
fd.rdeadline = 0
}
for { for {
n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
@ -493,11 +476,6 @@ func (fd *netFD) Write(p []byte) (n int, err error) {
if fd.sysfile == nil { if fd.sysfile == nil {
return 0, os.EINVAL return 0, os.EINVAL
} }
if fd.wdeadline_delta > 0 {
fd.wdeadline = pollserver.Now() + fd.wdeadline_delta
} else {
fd.wdeadline = 0
}
nn := 0 nn := 0
for { for {
@ -539,11 +517,6 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
defer fd.wio.Unlock() defer fd.wio.Unlock()
fd.incref() fd.incref()
defer fd.decref() defer fd.decref()
if fd.wdeadline_delta > 0 {
fd.wdeadline = pollserver.Now() + fd.wdeadline_delta
} else {
fd.wdeadline = 0
}
for { for {
err = syscall.Sendto(fd.sysfd, p, 0, sa) err = syscall.Sendto(fd.sysfd, p, 0, sa)
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
@ -571,11 +544,6 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
defer fd.wio.Unlock() defer fd.wio.Unlock()
fd.incref() fd.incref()
defer fd.decref() defer fd.decref()
if fd.wdeadline_delta > 0 {
fd.wdeadline = pollserver.Now() + fd.wdeadline_delta
} else {
fd.wdeadline = 0
}
for { for {
err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0)
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
@ -603,11 +571,6 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
fd.incref() fd.incref()
defer fd.decref() defer fd.decref()
if fd.rdeadline_delta > 0 {
fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
} else {
fd.rdeadline = 0
}
// See ../syscall/exec.go for description of ForkLock. // See ../syscall/exec.go for description of ForkLock.
// It is okay to hold the lock across syscall.Accept // It is okay to hold the lock across syscall.Accept

View File

@ -150,12 +150,13 @@ func (s *ioSrv) ProcessRemoteIO() {
} }
// ExecIO executes a single io operation. It either executes it // ExecIO executes a single io operation. It either executes it
// inline, or, if timeouts are employed, passes the request onto // inline, or, if a deadline is employed, passes the request onto
// a special goroutine and waits for completion or cancels request. // a special goroutine and waits for completion or cancels request.
func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { // deadline is unix nanos.
func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (n int, err error) {
var e error var e error
o := oi.Op() o := oi.Op()
if deadline_delta > 0 { if deadline != 0 {
// Send request to a special dedicated thread, // Send request to a special dedicated thread,
// so it can stop the io with CancelIO later. // so it can stop the io with CancelIO later.
s.submchan <- oi s.submchan <- oi
@ -172,12 +173,17 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) {
return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, e} return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, e}
} }
// Wait for our request to complete. // Wait for our request to complete.
// TODO(rsc): This should stop the timer.
var r ioResult var r ioResult
if deadline_delta > 0 { if deadline != 0 {
dt := deadline - time.Now().UnixNano()
if dt < 1 {
dt = 1
}
ticker := time.NewTicker(time.Duration(dt) * time.Nanosecond)
defer ticker.Stop()
select { select {
case r = <-o.resultc: case r = <-o.resultc:
case <-time.After(time.Duration(deadline_delta) * time.Nanosecond): case <-ticker.C:
s.canchan <- oi s.canchan <- oi
<-o.errnoc <-o.errnoc
r = <-o.resultc r = <-o.resultc
@ -232,12 +238,10 @@ type netFD struct {
errnoc [2]chan error // read/write submit or cancel operation errors errnoc [2]chan error // read/write submit or cancel operation errors
// owned by client // owned by client
rdeadline_delta int64 rdeadline int64
rdeadline int64 rio sync.Mutex
rio sync.Mutex wdeadline int64
wdeadline_delta int64 wio sync.Mutex
wdeadline int64
wio sync.Mutex
} }
func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) { func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) {
@ -357,7 +361,7 @@ func (fd *netFD) Read(buf []byte) (n int, err error) {
} }
var o readOp var o readOp
o.Init(fd, buf, 'r') o.Init(fd, buf, 'r')
n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) n, err = iosrv.ExecIO(&o, fd.rdeadline)
if err == nil && n == 0 { if err == nil && n == 0 {
err = io.EOF err = io.EOF
} }
@ -398,7 +402,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
var o readFromOp var o readFromOp
o.Init(fd, buf, 'r') o.Init(fd, buf, 'r')
o.rsan = int32(unsafe.Sizeof(o.rsa)) o.rsan = int32(unsafe.Sizeof(o.rsa))
n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) n, err = iosrv.ExecIO(&o, fd.rdeadline)
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
@ -434,7 +438,7 @@ func (fd *netFD) Write(buf []byte) (n int, err error) {
} }
var o writeOp var o writeOp
o.Init(fd, buf, 'w') o.Init(fd, buf, 'w')
return iosrv.ExecIO(&o, fd.wdeadline_delta) return iosrv.ExecIO(&o, fd.wdeadline)
} }
// WriteTo to network. // WriteTo to network.
@ -470,7 +474,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err error) {
var o writeToOp var o writeToOp
o.Init(fd, buf, 'w') o.Init(fd, buf, 'w')
o.sa = sa o.sa = sa
return iosrv.ExecIO(&o, fd.wdeadline_delta) return iosrv.ExecIO(&o, fd.wdeadline)
} }
// Accept new network connections. // Accept new network connections.

View File

@ -13,6 +13,7 @@ import (
"net" "net"
"net/http" "net/http"
"strings" "strings"
"time"
) )
// One of the copies, say from b to r2, could be avoided by using a more // One of the copies, say from b to r2, could be avoided by using a more
@ -36,12 +37,12 @@ type dumpConn struct {
io.Reader io.Reader
} }
func (c *dumpConn) Close() error { return nil } func (c *dumpConn) Close() error { return nil }
func (c *dumpConn) LocalAddr() net.Addr { return nil } func (c *dumpConn) LocalAddr() net.Addr { return nil }
func (c *dumpConn) RemoteAddr() net.Addr { return nil } func (c *dumpConn) RemoteAddr() net.Addr { return nil }
func (c *dumpConn) SetTimeout(nsec int64) error { return nil } func (c *dumpConn) SetDeadline(t time.Time) error { return nil }
func (c *dumpConn) SetReadTimeout(nsec int64) error { return nil } func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil }
func (c *dumpConn) SetWriteTimeout(nsec int64) error { return nil } func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
// DumpRequestOut is like DumpRequest but includes // DumpRequestOut is like DumpRequest but includes
// headers that the standard http.Transport adds, // headers that the standard http.Transport adds,

View File

@ -84,15 +84,15 @@ func (c *testConn) RemoteAddr() net.Addr {
return dummyAddr("remote-addr") return dummyAddr("remote-addr")
} }
func (c *testConn) SetTimeout(nsec int64) error { func (c *testConn) SetDeadline(t time.Time) error {
return nil return nil
} }
func (c *testConn) SetReadTimeout(nsec int64) error { func (c *testConn) SetReadDeadline(t time.Time) error {
return nil return nil
} }
func (c *testConn) SetWriteTimeout(nsec int64) error { func (c *testConn) SetWriteDeadline(t time.Time) error {
return nil return nil
} }

View File

@ -954,8 +954,8 @@ func Serve(l net.Listener, handler Handler) error {
type Server struct { type Server struct {
Addr string // TCP address to listen on, ":http" if empty Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke, http.DefaultServeMux if nil Handler Handler // handler to invoke, http.DefaultServeMux if nil
ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections ReadTimeout time.Duration // maximum duration before timing out read of the request
WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections WriteTimeout time.Duration // maximum duration before timing out write of the response
MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0 MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0
} }
@ -989,10 +989,10 @@ func (srv *Server) Serve(l net.Listener) error {
return e return e
} }
if srv.ReadTimeout != 0 { if srv.ReadTimeout != 0 {
rw.SetReadTimeout(srv.ReadTimeout.Nanoseconds()) rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout))
} }
if srv.WriteTimeout != 0 { if srv.WriteTimeout != 0 {
rw.SetWriteTimeout(srv.WriteTimeout.Nanoseconds()) rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout))
} }
c, err := srv.newConn(rw) c, err := srv.newConn(rw)
if err != nil { if err != nil {

View File

@ -12,6 +12,7 @@ import (
"flag" "flag"
"os" "os"
"testing" "testing"
"time"
) )
const ICMP_ECHO_REQUEST = 8 const ICMP_ECHO_REQUEST = 8
@ -101,7 +102,7 @@ func TestICMP(t *testing.T) {
t.Fatalf(`net.WriteToIP(..., %v) = %v, %v`, raddr, n, err) t.Fatalf(`net.WriteToIP(..., %v) = %v, %v`, raddr, n, err)
} }
c.SetTimeout(100e6) c.SetDeadline(time.Now().Add(100 * time.Millisecond))
resp := make([]byte, 1024) resp := make([]byte, 1024)
for { for {
n, from, err := c.ReadFrom(resp) n, from, err := c.ReadFrom(resp)

View File

@ -12,6 +12,7 @@ import (
"errors" "errors"
"os" "os"
"syscall" "syscall"
"time"
) )
func sockaddrToIP(sa syscall.Sockaddr) Addr { func sockaddrToIP(sa syscall.Sockaddr) Addr {
@ -97,28 +98,28 @@ func (c *IPConn) RemoteAddr() Addr {
return c.fd.raddr return c.fd.raddr
} }
// SetTimeout implements the net.Conn SetTimeout method. // SetDeadline implements the net.Conn SetDeadline method.
func (c *IPConn) SetTimeout(nsec int64) error { func (c *IPConn) SetDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setTimeout(c.fd, nsec) return setDeadline(c.fd, t)
} }
// SetReadTimeout implements the net.Conn SetReadTimeout method. // SetReadDeadline implements the net.Conn SetReadDeadline method.
func (c *IPConn) SetReadTimeout(nsec int64) error { func (c *IPConn) SetReadDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setReadTimeout(c.fd, nsec) return setReadDeadline(c.fd, t)
} }
// SetWriteTimeout implements the net.Conn SetWriteTimeout method. // SetWriteDeadline implements the net.Conn SetWriteDeadline method.
func (c *IPConn) SetWriteTimeout(nsec int64) error { func (c *IPConn) SetWriteDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setWriteTimeout(c.fd, nsec) return setWriteDeadline(c.fd, t)
} }
// SetReadBuffer sets the size of the operating system's // SetReadBuffer sets the size of the operating system's
@ -146,8 +147,8 @@ func (c *IPConn) SetWriteBuffer(bytes int) error {
// that was on the packet. // that was on the packet.
// //
// ReadFromIP can be made to time out and return an error with // ReadFromIP can be made to time out and return an error with
// Timeout() == true after a fixed time limit; see SetTimeout and // Timeout() == true after a fixed time limit; see SetDeadline and
// SetReadTimeout. // SetReadDeadline.
func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err error) { func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err error) {
if !c.ok() { if !c.ok() {
return 0, nil, os.EINVAL return 0, nil, os.EINVAL
@ -182,7 +183,7 @@ func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
// //
// WriteToIP can be made to time out and return // WriteToIP can be made to time out and return
// an error with Timeout() == true after a fixed time limit; // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetWriteTimeout. // see SetDeadline and SetWriteDeadline.
// On packet-oriented connections, write timeouts are rare. // On packet-oriented connections, write timeouts are rare.
func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err error) { func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err error) {
if !c.ok() { if !c.ok() {

View File

@ -9,7 +9,10 @@ package net
// TODO(rsc): // TODO(rsc):
// support for raw ethernet sockets // support for raw ethernet sockets
import "errors" import (
"errors"
"time"
)
// Addr represents a network end point address. // Addr represents a network end point address.
type Addr interface { type Addr interface {
@ -38,21 +41,23 @@ type Conn interface {
// RemoteAddr returns the remote network address. // RemoteAddr returns the remote network address.
RemoteAddr() Addr RemoteAddr() Addr
// SetTimeout sets the read and write deadlines associated // SetDeadline sets the read and write deadlines associated
// with the connection. // with the connection.
SetTimeout(nsec int64) error SetDeadline(t time.Time) error
// SetReadTimeout sets the time (in nanoseconds) that // SetReadDeadline sets the deadline for all Read calls to return.
// Read will wait for data before returning an error with Timeout() == true. // If the deadline is reached, Read will fail with a timeout
// Setting nsec == 0 (the default) disables the deadline. // (see type Error) instead of blocking.
SetReadTimeout(nsec int64) error // A zero value for t means Read will not time out.
SetReadDeadline(t time.Time) error
// SetWriteTimeout sets the time (in nanoseconds) that // SetWriteDeadline sets the deadline for all Write calls to return.
// Write will wait to send its data before returning an error with Timeout() == true. // If the deadline is reached, Write will fail with a timeout
// Setting nsec == 0 (the default) disables the deadline. // (see type Error) instead of blocking.
// A zero value for t means Write will not time out.
// Even if write times out, it may return n > 0, indicating that // Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written. // some of the data was successfully written.
SetWriteTimeout(nsec int64) error SetWriteDeadline(t time.Time) error
} }
// An Error represents a network error. // An Error represents a network error.
@ -70,13 +75,13 @@ type PacketConn interface {
// was on the packet. // was on the packet.
// ReadFrom can be made to time out and return // ReadFrom can be made to time out and return
// an error with Timeout() == true after a fixed time limit; // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetReadTimeout. // see SetDeadline and SetReadDeadline.
ReadFrom(b []byte) (n int, addr Addr, err error) ReadFrom(b []byte) (n int, addr Addr, err error)
// WriteTo writes a packet with payload b to addr. // WriteTo writes a packet with payload b to addr.
// WriteTo can be made to time out and return // WriteTo can be made to time out and return
// an error with Timeout() == true after a fixed time limit; // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetWriteTimeout. // see SetDeadline and SetWriteDeadline.
// On packet-oriented connections, write timeouts are rare. // On packet-oriented connections, write timeouts are rare.
WriteTo(b []byte, addr Addr) (n int, err error) WriteTo(b []byte, addr Addr) (n int, err error)
@ -86,21 +91,23 @@ type PacketConn interface {
// LocalAddr returns the local network address. // LocalAddr returns the local network address.
LocalAddr() Addr LocalAddr() Addr
// SetTimeout sets the read and write deadlines associated // SetDeadline sets the read and write deadlines associated
// with the connection. // with the connection.
SetTimeout(nsec int64) error SetDeadline(t time.Time) error
// SetReadTimeout sets the time (in nanoseconds) that // SetReadDeadline sets the deadline for all Read calls to return.
// Read will wait for data before returning an error with Timeout() == true. // If the deadline is reached, Read will fail with a timeout
// Setting nsec == 0 (the default) disables the deadline. // (see type Error) instead of blocking.
SetReadTimeout(nsec int64) error // A zero value for t means Read will not time out.
SetReadDeadline(t time.Time) error
// SetWriteTimeout sets the time (in nanoseconds) that // SetWriteDeadline sets the deadline for all Write calls to return.
// Write will wait to send its data before returning an error with Timeout() == true. // If the deadline is reached, Write will fail with a timeout
// Setting nsec == 0 (the default) disables the deadline. // (see type Error) instead of blocking.
// A zero value for t means Write will not time out.
// Even if write times out, it may return n > 0, indicating that // Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written. // some of the data was successfully written.
SetWriteTimeout(nsec int64) error SetWriteDeadline(t time.Time) error
} }
// A Listener is a generic network listener for stream-oriented protocols. // A Listener is a generic network listener for stream-oriented protocols.

View File

@ -7,6 +7,7 @@ package net
import ( import (
"errors" "errors"
"io" "io"
"time"
) )
// Pipe creates a synchronous, in-memory, full duplex // Pipe creates a synchronous, in-memory, full duplex
@ -53,14 +54,14 @@ func (p *pipe) RemoteAddr() Addr {
return pipeAddr(0) return pipeAddr(0)
} }
func (p *pipe) SetTimeout(nsec int64) error { func (p *pipe) SetDeadline(t time.Time) error {
return errors.New("net.Pipe does not support timeouts") return errors.New("net.Pipe does not support deadlines")
} }
func (p *pipe) SetReadTimeout(nsec int64) error { func (p *pipe) SetReadDeadline(t time.Time) error {
return errors.New("net.Pipe does not support timeouts") return errors.New("net.Pipe does not support deadlines")
} }
func (p *pipe) SetWriteTimeout(nsec int64) error { func (p *pipe) SetWriteDeadline(t time.Time) error {
return errors.New("net.Pipe does not support timeouts") return errors.New("net.Pipe does not support deadlines")
} }

View File

@ -40,15 +40,6 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
defer c.wio.Unlock() defer c.wio.Unlock()
c.incref() c.incref()
defer c.decref() defer c.decref()
if c.wdeadline_delta > 0 {
// This is a little odd that we're setting the timeout
// for the entire file but Write has the same issue
// (if one slurps the whole file into memory and
// do one large Write). At least they're consistent.
c.wdeadline = pollserver.Now() + c.wdeadline_delta
} else {
c.wdeadline = 0
}
dst := c.sysfd dst := c.sysfd
src := f.Fd() src := f.Fd()

View File

@ -11,6 +11,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
"time"
) )
// Do not test empty datagrams by default. // Do not test empty datagrams by default.
@ -63,7 +64,7 @@ func connect(t *testing.T, network, addr string, isEmpty bool) {
if err != nil { if err != nil {
t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err) t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err)
} }
fd.SetReadTimeout(1e9) // 1s fd.SetReadDeadline(time.Now().Add(1 * time.Second))
var b []byte var b []byte
if !isEmpty { if !isEmpty {
@ -169,10 +170,10 @@ func runPacket(t *testing.T, network, addr string, listening chan<- string, done
t.Fatalf("net.ListenPacket(%q, %q) = _, %v", network, addr, err) t.Fatalf("net.ListenPacket(%q, %q) = _, %v", network, addr, err)
} }
listening <- c.LocalAddr().String() listening <- c.LocalAddr().String()
c.SetReadTimeout(10e6) // 10ms
var buf [1000]byte var buf [1000]byte
Run: Run:
for { for {
c.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
n, addr, err := c.ReadFrom(buf[0:]) n, addr, err := c.ReadFrom(buf[0:])
if e, ok := err.(Error); ok && e.Timeout() { if e, ok := err.(Error); ok && e.Timeout() {
select { select {

View File

@ -12,6 +12,7 @@ import (
"bytes" "bytes"
"os" "os"
"syscall" "syscall"
"time"
) )
// Boolean to int. // Boolean to int.
@ -115,21 +116,21 @@ func setWriteBuffer(fd *netFD, bytes int) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)) return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes))
} }
func setReadTimeout(fd *netFD, nsec int64) error { func setReadDeadline(fd *netFD, t time.Time) error {
fd.rdeadline_delta = nsec fd.rdeadline = t.UnixNano()
return nil return nil
} }
func setWriteTimeout(fd *netFD, nsec int64) error { func setWriteDeadline(fd *netFD, t time.Time) error {
fd.wdeadline_delta = nsec fd.wdeadline = t.UnixNano()
return nil return nil
} }
func setTimeout(fd *netFD, nsec int64) error { func setDeadline(fd *netFD, t time.Time) error {
if e := setReadTimeout(fd, nsec); e != nil { if e := setReadDeadline(fd, t); e != nil {
return e return e
} }
return setWriteTimeout(fd, nsec) return setWriteDeadline(fd, t)
} }
func setReuseAddr(fd *netFD, reuse bool) error { func setReuseAddr(fd *netFD, reuse bool) error {

View File

@ -12,6 +12,7 @@ import (
"io" "io"
"os" "os"
"syscall" "syscall"
"time"
) )
// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for // BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
@ -134,28 +135,28 @@ func (c *TCPConn) RemoteAddr() Addr {
return c.fd.raddr return c.fd.raddr
} }
// SetTimeout implements the net.Conn SetTimeout method. // SetDeadline implements the net.Conn SetDeadline method.
func (c *TCPConn) SetTimeout(nsec int64) error { func (c *TCPConn) SetDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setTimeout(c.fd, nsec) return setDeadline(c.fd, t)
} }
// SetReadTimeout implements the net.Conn SetReadTimeout method. // SetReadDeadline implements the net.Conn SetReadDeadline method.
func (c *TCPConn) SetReadTimeout(nsec int64) error { func (c *TCPConn) SetReadDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setReadTimeout(c.fd, nsec) return setReadDeadline(c.fd, t)
} }
// SetWriteTimeout implements the net.Conn SetWriteTimeout method. // SetWriteDeadline implements the net.Conn SetWriteDeadline method.
func (c *TCPConn) SetWriteTimeout(nsec int64) error { func (c *TCPConn) SetWriteDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setWriteTimeout(c.fd, nsec) return setWriteDeadline(c.fd, t)
} }
// SetReadBuffer sets the size of the operating system's // SetReadBuffer sets the size of the operating system's
@ -294,12 +295,13 @@ func (l *TCPListener) Close() error {
// Addr returns the listener's network address, a *TCPAddr. // Addr returns the listener's network address, a *TCPAddr.
func (l *TCPListener) Addr() Addr { return l.fd.laddr } func (l *TCPListener) Addr() Addr { return l.fd.laddr }
// SetTimeout sets the deadline associated with the listener // SetDeadline sets the deadline associated with the listener.
func (l *TCPListener) SetTimeout(nsec int64) error { // A zero time value disables the deadline.
func (l *TCPListener) SetDeadline(t time.Time) error {
if l == nil || l.fd == nil { if l == nil || l.fd == nil {
return os.EINVAL return os.EINVAL
} }
return setTimeout(l.fd, nsec) return setDeadline(l.fd, t)
} }
// File returns a copy of the underlying os.File, set to blocking mode. // File returns a copy of the underlying os.File, set to blocking mode.

View File

@ -5,6 +5,7 @@
package net package net
import ( import (
"fmt"
"runtime" "runtime"
"testing" "testing"
"time" "time"
@ -17,26 +18,41 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
return return
} }
defer fd.Close() defer fd.Close()
t0 := time.Now()
fd.SetReadTimeout(1e8) // 100ms
var b [100]byte
var n int
var err1 error
if readFrom {
n, _, err1 = fd.(PacketConn).ReadFrom(b[0:])
} else {
n, err1 = fd.Read(b[0:])
}
t1 := time.Now()
what := "Read" what := "Read"
if readFrom { if readFrom {
what = "ReadFrom" what = "ReadFrom"
} }
if n != 0 || err1 == nil || !err1.(Error).Timeout() {
t.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1) errc := make(chan error, 1)
} go func() {
if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 150*time.Millisecond { t0 := time.Now()
t.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt) fd.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
var b [100]byte
var n int
var err1 error
if readFrom {
n, _, err1 = fd.(PacketConn).ReadFrom(b[0:])
} else {
n, err1 = fd.Read(b[0:])
}
t1 := time.Now()
if n != 0 || err1 == nil || !err1.(Error).Timeout() {
errc <- fmt.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1)
return
}
if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 250*time.Millisecond {
errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt)
return
}
errc <- nil
}()
select {
case err := <-errc:
if err != nil {
t.Error(err)
}
case <-time.After(1 * time.Second):
t.Errorf("%s on %s %s took over 1 second, expected 0.1s", what, network, addr)
} }
} }

View File

@ -11,6 +11,7 @@ package net
import ( import (
"os" "os"
"syscall" "syscall"
"time"
) )
func sockaddrToUDP(sa syscall.Sockaddr) Addr { func sockaddrToUDP(sa syscall.Sockaddr) Addr {
@ -98,28 +99,28 @@ func (c *UDPConn) RemoteAddr() Addr {
return c.fd.raddr return c.fd.raddr
} }
// SetTimeout implements the net.Conn SetTimeout method. // SetDeadline implements the net.Conn SetDeadline method.
func (c *UDPConn) SetTimeout(nsec int64) error { func (c *UDPConn) SetDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setTimeout(c.fd, nsec) return setDeadline(c.fd, t)
} }
// SetReadTimeout implements the net.Conn SetReadTimeout method. // SetReadDeadline implements the net.Conn SetReadDeadline method.
func (c *UDPConn) SetReadTimeout(nsec int64) error { func (c *UDPConn) SetReadDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setReadTimeout(c.fd, nsec) return setReadDeadline(c.fd, t)
} }
// SetWriteTimeout implements the net.Conn SetWriteTimeout method. // SetWriteDeadline implements the net.Conn SetWriteDeadline method.
func (c *UDPConn) SetWriteTimeout(nsec int64) error { func (c *UDPConn) SetWriteDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setWriteTimeout(c.fd, nsec) return setWriteDeadline(c.fd, t)
} }
// SetReadBuffer sets the size of the operating system's // SetReadBuffer sets the size of the operating system's
@ -147,7 +148,7 @@ func (c *UDPConn) SetWriteBuffer(bytes int) error {
// that was on the packet. // that was on the packet.
// //
// ReadFromUDP can be made to time out and return an error with Timeout() == true // ReadFromUDP can be made to time out and return an error with Timeout() == true
// after a fixed time limit; see SetTimeout and SetReadTimeout. // after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
if !c.ok() { if !c.ok() {
return 0, nil, os.EINVAL return 0, nil, os.EINVAL
@ -175,7 +176,7 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
// //
// WriteToUDP can be made to time out and return // WriteToUDP can be made to time out and return
// an error with Timeout() == true after a fixed time limit; // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetWriteTimeout. // see SetDeadline and SetWriteDeadline.
// On packet-oriented connections, write timeouts are rare. // On packet-oriented connections, write timeouts are rare.
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) { func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
if !c.ok() { if !c.ok() {

View File

@ -11,6 +11,7 @@ package net
import ( import (
"os" "os"
"syscall" "syscall"
"time"
) )
func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err error) { func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err error) {
@ -164,28 +165,28 @@ func (c *UnixConn) RemoteAddr() Addr {
return c.fd.raddr return c.fd.raddr
} }
// SetTimeout implements the net.Conn SetTimeout method. // SetDeadline implements the net.Conn SetDeadline method.
func (c *UnixConn) SetTimeout(nsec int64) error { func (c *UnixConn) SetDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setTimeout(c.fd, nsec) return setDeadline(c.fd, t)
} }
// SetReadTimeout implements the net.Conn SetReadTimeout method. // SetReadDeadline implements the net.Conn SetReadDeadline method.
func (c *UnixConn) SetReadTimeout(nsec int64) error { func (c *UnixConn) SetReadDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setReadTimeout(c.fd, nsec) return setReadDeadline(c.fd, t)
} }
// SetWriteTimeout implements the net.Conn SetWriteTimeout method. // SetWriteDeadline implements the net.Conn SetWriteDeadline method.
func (c *UnixConn) SetWriteTimeout(nsec int64) error { func (c *UnixConn) SetWriteDeadline(t time.Time) error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
} }
return setWriteTimeout(c.fd, nsec) return setWriteDeadline(c.fd, t)
} }
// SetReadBuffer sets the size of the operating system's // SetReadBuffer sets the size of the operating system's
@ -212,7 +213,7 @@ func (c *UnixConn) SetWriteBuffer(bytes int) error {
// //
// ReadFromUnix can be made to time out and return // ReadFromUnix can be made to time out and return
// an error with Timeout() == true after a fixed time limit; // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetReadTimeout. // see SetDeadline and SetReadDeadline.
func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) { func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
if !c.ok() { if !c.ok() {
return 0, nil, os.EINVAL return 0, nil, os.EINVAL
@ -238,7 +239,7 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
// //
// WriteToUnix can be made to time out and return // WriteToUnix can be made to time out and return
// an error with Timeout() == true after a fixed time limit; // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetWriteTimeout. // see SetDeadline and SetWriteDeadline.
// On packet-oriented connections, write timeouts are rare. // On packet-oriented connections, write timeouts are rare.
func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) { func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
if !c.ok() { if !c.ok() {
@ -386,12 +387,13 @@ func (l *UnixListener) Close() error {
// Addr returns the listener's network address. // Addr returns the listener's network address.
func (l *UnixListener) Addr() Addr { return l.fd.laddr } func (l *UnixListener) Addr() Addr { return l.fd.laddr }
// SetTimeout sets the deadline associated wuth the listener // SetTimeout sets the deadline associated with the listener.
func (l *UnixListener) SetTimeout(nsec int64) (err error) { // A zero time value disables the deadline.
func (l *UnixListener) SetDeadline(t time.Time) (err error) {
if l == nil || l.fd == nil { if l == nil || l.fd == nil {
return os.EINVAL return os.EINVAL
} }
return setTimeout(l.fd, nsec) return setDeadline(l.fd, t)
} }
// File returns a copy of the underlying os.File, set to blocking mode. // File returns a copy of the underlying os.File, set to blocking mode.

View File

@ -17,6 +17,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"sync" "sync"
"time"
) )
const ( const (
@ -243,30 +244,30 @@ func (ws *Conn) RemoteAddr() net.Addr {
return &Addr{ws.config.Origin} return &Addr{ws.config.Origin}
} }
var errSetTimeout = errors.New("websocket: cannot set timeout: not using a net.Conn") var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
// SetTimeout sets the connection's network timeout in nanoseconds. // SetDeadline sets the connection's network read & write deadlines.
func (ws *Conn) SetTimeout(nsec int64) error { func (ws *Conn) SetDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok { if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetTimeout(nsec) return conn.SetDeadline(t)
} }
return errSetTimeout return errSetDeadline
} }
// SetReadTimeout sets the connection's network read timeout in nanoseconds. // SetReadDeadline sets the connection's network read deadline.
func (ws *Conn) SetReadTimeout(nsec int64) error { func (ws *Conn) SetReadDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok { if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetReadTimeout(nsec) return conn.SetReadDeadline(t)
} }
return errSetTimeout return errSetDeadline
} }
// SetWriteTimeout sets the connection's network write timeout in nanoseconds. // SetWriteDeadline sets the connection's network write deadline.
func (ws *Conn) SetWriteTimeout(nsec int64) error { func (ws *Conn) SetWriteDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok { if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetWriteTimeout(nsec) return conn.SetWriteDeadline(t)
} }
return errSetTimeout return errSetDeadline
} }
// Config returns the WebSocket config. // Config returns the WebSocket config.