Commit 47a05334 authored by Russ Cox's avatar Russ Cox

net: introduce net.Error interface

Adds two more methods, Timeout and Temporary.
Implemented by os.Errno too.  The intent is to make
the checks for os.EAGAIN a little less clunky.
It should also let us clean up a bug that Mike Solomon
pointed out: if a network server gets an "out of file descriptors"
error from Accept, the listener should not stop.
It will be able to check this because that error would
have Temporary() == true.

Also clean up some underscore names.

Fixes #442.

R=r
CC=golang-dev, msolo
https://golang.org/cl/957045
parent cd5191fd
...@@ -23,9 +23,10 @@ import ( ...@@ -23,9 +23,10 @@ import (
// DNSError represents a DNS lookup error. // DNSError represents a DNS lookup error.
type DNSError struct { type DNSError struct {
Error string // description of the error Error string // description of the error
Name string // name looked for Name string // name looked for
Server string // server used Server string // server used
IsTimeout bool
} }
func (e *DNSError) String() string { func (e *DNSError) String() string {
...@@ -37,23 +38,26 @@ func (e *DNSError) String() string { ...@@ -37,23 +38,26 @@ func (e *DNSError) String() string {
return s return s
} }
func (e *DNSError) Timeout() bool { return e.IsTimeout }
func (e *DNSError) Temporary() bool { return e.IsTimeout }
const noSuchHost = "no such host" const noSuchHost = "no such host"
// Send a request on the connection and hope for a reply. // Send a request on the connection and hope for a reply.
// Up to cfg.attempts attempts. // Up to cfg.attempts attempts.
func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error) { func exchange(cfg *dnsConfig, c Conn, name string) (*dnsMsg, os.Error) {
if len(name) >= 256 { if len(name) >= 256 {
return nil, &DNSError{"name too long", name, ""} return nil, &DNSError{Error: "name too long", Name: name}
} }
out := new(_DNS_Msg) out := new(dnsMsg)
out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds())
out.question = []_DNS_Question{ out.question = []dnsQuestion{
_DNS_Question{name, _DNS_TypeA, _DNS_ClassINET}, dnsQuestion{name, dnsTypeA, dnsClassINET},
} }
out.recursion_desired = true out.recursion_desired = true
msg, ok := out.Pack() msg, ok := out.Pack()
if !ok { if !ok {
return nil, &DNSError{"internal error - cannot pack message", name, ""} return nil, &DNSError{Error: "internal error - cannot pack message", Name: name}
} }
for attempt := 0; attempt < cfg.attempts; attempt++ { for attempt := 0; attempt < cfg.attempts; attempt++ {
...@@ -66,15 +70,14 @@ func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error ...@@ -66,15 +70,14 @@ func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error
buf := make([]byte, 2000) // More than enough. buf := make([]byte, 2000) // More than enough.
n, err = c.Read(buf) n, err = c.Read(buf)
if isEAGAIN(err) {
err = nil
continue
}
if err != nil { if err != nil {
if e, ok := err.(Error); ok && e.Timeout() {
continue
}
return nil, err return nil, err
} }
buf = buf[0:n] buf = buf[0:n]
in := new(_DNS_Msg) in := new(dnsMsg)
if !in.Unpack(buf) || in.id != out.id { if !in.Unpack(buf) || in.id != out.id {
continue continue
} }
...@@ -84,24 +87,24 @@ func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error ...@@ -84,24 +87,24 @@ func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error
if a := c.RemoteAddr(); a != nil { if a := c.RemoteAddr(); a != nil {
server = a.String() server = a.String()
} }
return nil, &DNSError{"no answer from server", name, server} return nil, &DNSError{Error: "no answer from server", Name: name, Server: server, IsTimeout: true}
} }
// Find answer for name in dns message. // Find answer for name in dns message.
// On return, if err == nil, addrs != nil. // On return, if err == nil, addrs != nil.
func answer(name, server string, dns *_DNS_Msg) (addrs []string, err *DNSError) { func answer(name, server string, dns *dnsMsg) (addrs []string, err os.Error) {
addrs = make([]string, 0, len(dns.answer)) addrs = make([]string, 0, len(dns.answer))
if dns.rcode == _DNS_RcodeNameError && dns.recursion_available { if dns.rcode == dnsRcodeNameError && dns.recursion_available {
return nil, &DNSError{noSuchHost, name, ""} return nil, &DNSError{Error: noSuchHost, Name: name}
} }
if dns.rcode != _DNS_RcodeSuccess { if dns.rcode != dnsRcodeSuccess {
// None of the error codes make sense // None of the error codes make sense
// for the query we sent. If we didn't get // for the query we sent. If we didn't get
// a name error and we didn't get success, // a name error and we didn't get success,
// the server is behaving incorrectly. // the server is behaving incorrectly.
return nil, &DNSError{"server misbehaving", name, server} return nil, &DNSError{Error: "server misbehaving", Name: name, Server: server}
} }
// Look for the name. // Look for the name.
...@@ -115,34 +118,34 @@ Cname: ...@@ -115,34 +118,34 @@ Cname:
for i := 0; i < len(dns.answer); i++ { for i := 0; i < len(dns.answer); i++ {
rr := dns.answer[i] rr := dns.answer[i]
h := rr.Header() h := rr.Header()
if h.Class == _DNS_ClassINET && h.Name == name { if h.Class == dnsClassINET && h.Name == name {
switch h.Rrtype { switch h.Rrtype {
case _DNS_TypeA: case dnsTypeA:
n := len(addrs) n := len(addrs)
a := rr.(*_DNS_RR_A).A a := rr.(*dnsRR_A).A
addrs = addrs[0 : n+1] addrs = addrs[0 : n+1]
addrs[n] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String() addrs[n] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String()
case _DNS_TypeCNAME: case dnsTypeCNAME:
// redirect to cname // redirect to cname
name = rr.(*_DNS_RR_CNAME).Cname name = rr.(*dnsRR_CNAME).Cname
continue Cname continue Cname
} }
} }
} }
if len(addrs) == 0 { if len(addrs) == 0 {
return nil, &DNSError{noSuchHost, name, server} return nil, &DNSError{Error: noSuchHost, Name: name, Server: server}
} }
return addrs, nil return addrs, nil
} }
return nil, &DNSError{"too many redirects", name, server} return nil, &DNSError{Error: "too many redirects", Name: name, Server: server}
} }
// Do a lookup for a single name, which must be rooted // Do a lookup for a single name, which must be rooted
// (otherwise answer will not find the answers). // (otherwise answer will not find the answers).
func tryOneName(cfg *_DNS_Config, name string) (addrs []string, err os.Error) { func tryOneName(cfg *dnsConfig, name string) (addrs []string, err os.Error) {
if len(cfg.servers) == 0 { if len(cfg.servers) == 0 {
return nil, &DNSError{"no DNS servers", name, ""} return nil, &DNSError{Error: "no DNS servers", Name: name}
} }
for i := 0; i < len(cfg.servers); i++ { for i := 0; i < len(cfg.servers); i++ {
// Calling Dial here is scary -- we have to be sure // Calling Dial here is scary -- we have to be sure
...@@ -157,30 +160,24 @@ func tryOneName(cfg *_DNS_Config, name string) (addrs []string, err os.Error) { ...@@ -157,30 +160,24 @@ func tryOneName(cfg *_DNS_Config, name string) (addrs []string, err os.Error) {
err = cerr err = cerr
continue continue
} }
msg, merr := _Exchange(cfg, c, name) msg, merr := exchange(cfg, c, name)
c.Close() c.Close()
if merr != nil { if merr != nil {
err = merr err = merr
continue continue
} }
var dnserr *DNSError addrs, err = answer(name, server, msg)
addrs, dnserr = answer(name, server, msg) if err == nil || err.(*DNSError).Error == noSuchHost {
if dnserr != nil {
err = dnserr
} else {
err = nil // nil os.Error, not nil *DNSError
}
if dnserr == nil || dnserr.Error == noSuchHost {
break break
} }
} }
return return
} }
var cfg *_DNS_Config var cfg *dnsConfig
var dnserr os.Error var dnserr os.Error
func loadConfig() { cfg, dnserr = _DNS_ReadConfig() } func loadConfig() { cfg, dnserr = dnsReadConfig() }
func isDomainName(s string) bool { func isDomainName(s string) bool {
// Requirements on DNS name: // Requirements on DNS name:
...@@ -231,7 +228,7 @@ func isDomainName(s string) bool { ...@@ -231,7 +228,7 @@ func isDomainName(s string) bool {
// host's addresses. // host's addresses.
func LookupHost(name string) (cname string, addrs []string, err os.Error) { func LookupHost(name string) (cname string, addrs []string, err os.Error) {
if !isDomainName(name) { if !isDomainName(name) {
return name, nil, &DNSError{"invalid domain name", name, ""} return name, nil, &DNSError{Error: "invalid domain name", Name: name}
} }
once.Do(loadConfig) once.Do(loadConfig)
if dnserr != nil || cfg == nil { if dnserr != nil || cfg == nil {
......
...@@ -8,7 +8,7 @@ package net ...@@ -8,7 +8,7 @@ package net
import "os" import "os"
type _DNS_Config struct { type dnsConfig struct {
servers []string // servers to use servers []string // servers to use
search []string // suffixes to append to local name search []string // suffixes to append to local name
ndots int // number of dots in name to trigger absolute lookup ndots int // number of dots in name to trigger absolute lookup
...@@ -17,18 +17,30 @@ type _DNS_Config struct { ...@@ -17,18 +17,30 @@ type _DNS_Config struct {
rotate bool // round robin among servers rotate bool // round robin among servers
} }
var _DNS_configError os.Error var dnsconfigError os.Error
type DNSConfigError struct {
Error os.Error
}
func (e *DNSConfigError) String() string {
return "error reading DNS config: " + e.Error.String()
}
func (e *DNSConfigError) Timeout() bool { return false }
func (e *DNSConfigError) Temporary() bool { return false }
// See resolv.conf(5) on a Linux machine. // See resolv.conf(5) on a Linux machine.
// TODO(rsc): Supposed to call uname() and chop the beginning // TODO(rsc): Supposed to call uname() and chop the beginning
// of the host name to get the default search domain. // of the host name to get the default search domain.
// We assume it's in resolv.conf anyway. // We assume it's in resolv.conf anyway.
func _DNS_ReadConfig() (*_DNS_Config, os.Error) { func dnsReadConfig() (*dnsConfig, os.Error) {
file, err := open("/etc/resolv.conf") file, err := open("/etc/resolv.conf")
if err != nil { if err != nil {
return nil, err return nil, &DNSConfigError{err}
} }
conf := new(_DNS_Config) conf := new(dnsConfig)
conf.servers = make([]string, 3)[0:0] // small, but the standard limit conf.servers = make([]string, 3)[0:0] // small, but the standard limit
conf.search = make([]string, 0) conf.search = make([]string, 0)
conf.ndots = 1 conf.ndots = 1
......
This diff is collapsed.
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package net package net
import ( import (
"io"
"once" "once"
"os" "os"
"sync" "sync"
...@@ -44,6 +45,12 @@ type netFD struct { ...@@ -44,6 +45,12 @@ type netFD struct {
ncr, ncw int ncr, ncw int
} }
type InvalidConnError struct{}
func (e *InvalidConnError) String() string { return "invalid net.Conn" }
func (e *InvalidConnError) Temporary() bool { return false }
func (e *InvalidConnError) Timeout() bool { return false }
// A pollServer helps FDs determine when to retry a non-blocking // A pollServer helps FDs determine when to retry a non-blocking
// read or write after they get EAGAIN. When an FD needs to wait, // read or write after they get EAGAIN. When an FD needs to wait,
// send the fd on s.cr (for a read) or s.cw (for a write) to pass the // send the fd on s.cr (for a read) or s.cw (for a write) to pass the
...@@ -342,13 +349,6 @@ func (fd *netFD) decref() { ...@@ -342,13 +349,6 @@ func (fd *netFD) decref() {
fd.sysmu.Unlock() fd.sysmu.Unlock()
} }
func isEAGAIN(e os.Error) bool {
if e1, ok := e.(*os.PathError); ok {
return e1.Error == os.EAGAIN
}
return e == os.EAGAIN
}
func (fd *netFD) Close() os.Error { func (fd *netFD) Close() os.Error {
if fd == nil || fd.sysfile == nil { if fd == nil || fd.sysfile == nil {
return os.EINVAL return os.EINVAL
...@@ -374,17 +374,24 @@ func (fd *netFD) Read(p []byte) (n int, err os.Error) { ...@@ -374,17 +374,24 @@ func (fd *netFD) Read(p []byte) (n int, err os.Error) {
} else { } else {
fd.rdeadline = 0 fd.rdeadline = 0
} }
var oserr os.Error
for { for {
n, err = fd.sysfile.Read(p) var errno int
if isEAGAIN(err) && fd.rdeadline >= 0 { n, errno = syscall.Read(fd.sysfile.Fd(), p)
if errno == syscall.EAGAIN && fd.rdeadline >= 0 {
pollserver.WaitRead(fd) pollserver.WaitRead(fd)
continue continue
} }
if errno != 0 {
n = 0
oserr = os.Errno(errno)
} else if n == 0 && errno == 0 && fd.proto != syscall.SOCK_DGRAM {
err = os.EOF
}
break break
} }
if fd.proto == syscall.SOCK_DGRAM && err == os.EOF { if oserr != nil {
// 0 in datagram protocol just means 0-length packet err = &OpError{"read", fd.net, fd.raddr, oserr}
err = nil
} }
return return
} }
...@@ -402,6 +409,7 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) { ...@@ -402,6 +409,7 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) {
} else { } else {
fd.rdeadline = 0 fd.rdeadline = 0
} }
var oserr os.Error
for { for {
var errno int var errno int
n, sa, errno = syscall.Recvfrom(fd.sysfd, p, 0) n, sa, errno = syscall.Recvfrom(fd.sysfd, p, 0)
...@@ -411,10 +419,13 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) { ...@@ -411,10 +419,13 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) {
} }
if errno != 0 { if errno != 0 {
n = 0 n = 0
err = &os.PathError{"recvfrom", fd.sysfile.Name(), os.Errno(errno)} oserr = os.Errno(errno)
} }
break break
} }
if oserr != nil {
err = &OpError{"read", fd.net, fd.laddr, oserr}
}
return return
} }
...@@ -431,26 +442,33 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) { ...@@ -431,26 +442,33 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) {
} else { } else {
fd.wdeadline = 0 fd.wdeadline = 0
} }
err = nil
nn := 0 nn := 0
first := true // force at least one Write, to send 0-length datagram packets var oserr os.Error
for nn < len(p) || first { for {
first = false n, errno := syscall.Write(fd.sysfile.Fd(), p[nn:])
n, err = fd.sysfile.Write(p[nn:])
if n > 0 { if n > 0 {
nn += n nn += n
} }
if nn == len(p) { if nn == len(p) {
break break
} }
if isEAGAIN(err) && fd.wdeadline >= 0 { if errno == syscall.EAGAIN && fd.wdeadline >= 0 {
pollserver.WaitWrite(fd) pollserver.WaitWrite(fd)
continue continue
} }
if n == 0 || err != nil { if errno != 0 {
n = 0
oserr = os.Errno(errno)
break
}
if n == 0 {
oserr = io.ErrUnexpectedEOF
break break
} }
} }
if oserr != nil {
err = &OpError{"write", fd.net, fd.raddr, oserr}
}
return nn, err return nn, err
} }
...@@ -467,7 +485,7 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) { ...@@ -467,7 +485,7 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) {
} else { } else {
fd.wdeadline = 0 fd.wdeadline = 0
} }
err = nil var oserr os.Error
for { for {
errno := syscall.Sendto(fd.sysfd, p, 0, sa) errno := syscall.Sendto(fd.sysfd, p, 0, sa)
if errno == syscall.EAGAIN && fd.wdeadline >= 0 { if errno == syscall.EAGAIN && fd.wdeadline >= 0 {
...@@ -475,12 +493,14 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) { ...@@ -475,12 +493,14 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) {
continue continue
} }
if errno != 0 { if errno != 0 {
err = &os.PathError{"sendto", fd.sysfile.Name(), os.Errno(errno)} oserr = os.Errno(errno)
} }
break break
} }
if err == nil { if oserr == nil {
n = len(p) n = len(p)
} else {
err = &OpError{"write", fd.net, fd.raddr, oserr}
} }
return return
} }
......
...@@ -49,6 +49,7 @@ type sockaddr interface { ...@@ -49,6 +49,7 @@ type sockaddr interface {
func internetSocket(net string, laddr, raddr sockaddr, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) { func internetSocket(net string, laddr, raddr sockaddr, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) {
// Figure out IP version. // Figure out IP version.
// If network has a suffix like "tcp4", obey it. // If network has a suffix like "tcp4", obey it.
var oserr os.Error
family := syscall.AF_INET6 family := syscall.AF_INET6
switch net[len(net)-1] { switch net[len(net)-1] {
case '4': case '4':
...@@ -67,16 +68,16 @@ func internetSocket(net string, laddr, raddr sockaddr, proto int, mode string, t ...@@ -67,16 +68,16 @@ func internetSocket(net string, laddr, raddr sockaddr, proto int, mode string, t
var la, ra syscall.Sockaddr var la, ra syscall.Sockaddr
if laddr != nil { if laddr != nil {
if la, err = laddr.sockaddr(family); err != nil { if la, oserr = laddr.sockaddr(family); err != nil {
goto Error goto Error
} }
} }
if raddr != nil { if raddr != nil {
if ra, err = raddr.sockaddr(family); err != nil { if ra, oserr = raddr.sockaddr(family); err != nil {
goto Error goto Error
} }
} }
fd, err = socket(net, family, proto, 0, la, ra, toAddr) fd, oserr = socket(net, family, proto, 0, la, ra, toAddr)
if err != nil { if err != nil {
goto Error goto Error
} }
...@@ -87,7 +88,7 @@ Error: ...@@ -87,7 +88,7 @@ Error:
if mode == "listen" { if mode == "listen" {
addr = laddr addr = laddr
} }
return nil, &OpError{mode, net, addr, err} return nil, &OpError{mode, net, addr, oserr}
} }
func getip(fd int, remote bool) (ip []byte, port int, ok bool) { func getip(fd int, remote bool) (ip []byte, port int, ok bool) {
...@@ -109,6 +110,13 @@ func getip(fd int, remote bool) (ip []byte, port int, ok bool) { ...@@ -109,6 +110,13 @@ func getip(fd int, remote bool) (ip []byte, port int, ok bool) {
return return
} }
type InvalidAddrError string
func (e InvalidAddrError) String() string { return string(e) }
func (e InvalidAddrError) Timeout() bool { return false }
func (e InvalidAddrError) Temporary() bool { return false }
func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
switch family { switch family {
case syscall.AF_INET: case syscall.AF_INET:
...@@ -116,7 +124,7 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { ...@@ -116,7 +124,7 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
ip = IPv4zero ip = IPv4zero
} }
if ip = ip.To4(); ip == nil { if ip = ip.To4(); ip == nil {
return nil, os.EINVAL return nil, InvalidAddrError("non-IPv4 address")
} }
s := new(syscall.SockaddrInet4) s := new(syscall.SockaddrInet4)
for i := 0; i < IPv4len; i++ { for i := 0; i < IPv4len; i++ {
...@@ -135,7 +143,7 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { ...@@ -135,7 +143,7 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
ip = IPzero ip = IPzero
} }
if ip = ip.To16(); ip == nil { if ip = ip.To16(); ip == nil {
return nil, os.EINVAL return nil, InvalidAddrError("non-IPv6 address")
} }
s := new(syscall.SockaddrInet6) s := new(syscall.SockaddrInet6)
for i := 0; i < IPv6len; i++ { for i := 0; i < IPv6len; i++ {
...@@ -144,7 +152,7 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { ...@@ -144,7 +152,7 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
s.Port = port s.Port = port
return s, nil return s, nil
} }
return nil, os.EINVAL return nil, InvalidAddrError("unexpected socket family")
} }
// Split "host:port" into "host" and "port". // Split "host:port" into "host" and "port".
......
...@@ -22,16 +22,17 @@ type Addr interface { ...@@ -22,16 +22,17 @@ type Addr interface {
// Conn is a generic stream-oriented network connection. // Conn is a generic stream-oriented network connection.
type Conn interface { type Conn interface {
// Read reads data from the connection. // Read reads data from the connection.
// Read can be made to time out and return err == os.EAGAIN // 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 SetTimeout and SetReadTimeout.
Read(b []byte) (n int, err os.Error) Read(b []byte) (n int, err os.Error)
// Write writes data to the connection. // Write writes data to the connection.
// Write can be made to time out and return err == os.EAGAIN // Write 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 SetTimeout and SetWriteTimeout.
Write(b []byte) (n int, err os.Error) Write(b []byte) (n int, err os.Error)
// Close closes the connection. // Close closes the connection.
// The error returned is an os.Error to satisfy io.Closer;
Close() os.Error Close() os.Error
// LocalAddr returns the local network address. // LocalAddr returns the local network address.
...@@ -45,35 +46,45 @@ type Conn interface { ...@@ -45,35 +46,45 @@ type Conn interface {
SetTimeout(nsec int64) os.Error SetTimeout(nsec int64) os.Error
// SetReadTimeout sets the time (in nanoseconds) that // SetReadTimeout sets the time (in nanoseconds) that
// Read will wait for data before returning os.EAGAIN. // Read will wait for data before returning an error with Timeout() == true.
// Setting nsec == 0 (the default) disables the deadline. // Setting nsec == 0 (the default) disables the deadline.
SetReadTimeout(nsec int64) os.Error SetReadTimeout(nsec int64) os.Error
// SetWriteTimeout sets the time (in nanoseconds) that // SetWriteTimeout sets the time (in nanoseconds) that
// Write will wait to send its data before returning os.EAGAIN. // Write will wait to send its data before returning an error with Timeout() == true.
// Setting nsec == 0 (the default) disables the deadline. // Setting nsec == 0 (the default) disables the deadline.
// 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) os.Error SetWriteTimeout(nsec int64) os.Error
} }
// An Error represents a network error.
type Error interface {
os.Error
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}
// PacketConn is a generic packet-oriented network connection. // PacketConn is a generic packet-oriented network connection.
type PacketConn interface { type PacketConn interface {
// ReadFrom reads a packet from the connection, // ReadFrom reads a packet from the connection,
// copying the payload into b. It returns the number of // copying the payload into b. It returns the number of
// bytes copied into b and the return address that // bytes copied into b and the return address that
// was on the packet. // was on the packet.
// ReadFrom can be made to time out and return err == os.EAGAIN // ReadFrom can be made to time out and return
// after a fixed time limit; see SetTimeout and SetReadTimeout. // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetReadTimeout.
ReadFrom(b []byte) (n int, addr Addr, err os.Error) ReadFrom(b []byte) (n int, addr Addr, err os.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 err == os.EAGAIN // WriteTo can be made to time out and return
// after a fixed time limit; see SetTimeout and SetWriteTimeout. // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetWriteTimeout.
// On packet-oriented connections, write timeouts are rare. // On packet-oriented connections, write timeouts are rare.
WriteTo(b []byte, addr Addr) (n int, err os.Error) WriteTo(b []byte, addr Addr) (n int, err os.Error)
// Close closes the connection. // Close closes the connection.
// The error returned is an os.Error to satisfy io.Closer;
Close() os.Error Close() os.Error
// LocalAddr returns the local network address. // LocalAddr returns the local network address.
...@@ -84,12 +95,12 @@ type PacketConn interface { ...@@ -84,12 +95,12 @@ type PacketConn interface {
SetTimeout(nsec int64) os.Error SetTimeout(nsec int64) os.Error
// SetReadTimeout sets the time (in nanoseconds) that // SetReadTimeout sets the time (in nanoseconds) that
// Read will wait for data before returning os.EAGAIN. // Read will wait for data before returning an error with Timeout() == true.
// Setting nsec == 0 (the default) disables the deadline. // Setting nsec == 0 (the default) disables the deadline.
SetReadTimeout(nsec int64) os.Error SetReadTimeout(nsec int64) os.Error
// SetWriteTimeout sets the time (in nanoseconds) that // SetWriteTimeout sets the time (in nanoseconds) that
// Write will wait to send its data before returning os.EAGAIN. // Write will wait to send its data before returning an error with Timeout() == true.
// Setting nsec == 0 (the default) disables the deadline. // Setting nsec == 0 (the default) disables the deadline.
// 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.
...@@ -97,11 +108,16 @@ type PacketConn interface { ...@@ -97,11 +108,16 @@ type PacketConn interface {
} }
// A Listener is a generic network listener for stream-oriented protocols. // A Listener is a generic network listener for stream-oriented protocols.
// Accept waits for the next connection and Close closes the connection.
type Listener interface { type Listener interface {
// Accept waits for and returns the next connection to the listener.
Accept() (c Conn, err os.Error) Accept() (c Conn, err os.Error)
// Close closes the listener.
// The error returned is an os.Error to satisfy io.Closer;
Close() os.Error Close() os.Error
Addr() Addr // Listener's network address
// Addr returns the listener's network address.
Addr() Addr
} }
// Dial connects to the remote address raddr on the network net. // Dial connects to the remote address raddr on the network net.
...@@ -266,6 +282,24 @@ func (e *OpError) String() string { ...@@ -266,6 +282,24 @@ func (e *OpError) String() string {
return s return s
} }
type temporary interface {
Temporary() bool
}
func (e *OpError) Temporary() bool {
t, ok := e.Error.(temporary)
return ok && t.Temporary()
}
type timeout interface {
Timeout() bool
}
func (e *OpError) Timeout() bool {
t, ok := e.Error.(timeout)
return ok && t.Timeout()
}
type AddrError struct { type AddrError struct {
Error string Error string
Addr string Addr string
...@@ -279,6 +313,16 @@ func (e *AddrError) String() string { ...@@ -279,6 +313,16 @@ func (e *AddrError) String() string {
return s return s
} }
func (e *AddrError) Temporary() bool {
return false
}
func (e *AddrError) Timeout() bool {
return false
}
type UnknownNetworkError string type UnknownNetworkError string
func (e UnknownNetworkError) String() string { return "unknown network " + string(e) } func (e UnknownNetworkError) String() string { return "unknown network " + string(e) }
func (e UnknownNetworkError) Temporary() bool { return false }
func (e UnknownNetworkError) Timeout() bool { return false }
...@@ -69,14 +69,14 @@ func connect(t *testing.T, network, addr string, isEmpty bool) { ...@@ -69,14 +69,14 @@ func connect(t *testing.T, network, addr string, isEmpty bool) {
} }
var b1 [100]byte var b1 [100]byte
n, err := fd.Write(b) n, err1 := fd.Write(b)
if n != len(b) { if n != len(b) {
t.Fatalf("fd.Write(%q) = %d, %v", b, n, err) t.Fatalf("fd.Write(%q) = %d, %v", b, n, err1)
} }
n, err = fd.Read(&b1) n, err1 = fd.Read(&b1)
if n != len(b) || err != nil { if n != len(b) || err1 != nil {
t.Fatalf("fd.Read() = %d, %v (want %d, nil)", n, err, len(b)) t.Fatalf("fd.Read() = %d, %v (want %d, nil)", n, err1, len(b))
} }
fd.Close() fd.Close()
} }
...@@ -127,7 +127,7 @@ func runPacket(t *testing.T, network, addr string, listening chan<- string, done ...@@ -127,7 +127,7 @@ func runPacket(t *testing.T, network, addr string, listening chan<- string, done
var buf [1000]byte var buf [1000]byte
for { for {
n, addr, err := c.ReadFrom(&buf) n, addr, err := c.ReadFrom(&buf)
if isEAGAIN(err) { if e, ok := err.(Error); ok && e.Timeout() {
if done <- 1 { if done <- 1 {
break break
} }
......
...@@ -81,10 +81,7 @@ func (c *TCPConn) ok() bool { return c != nil && c.fd != nil } ...@@ -81,10 +81,7 @@ func (c *TCPConn) ok() bool { return c != nil && c.fd != nil }
// Implementation of the Conn interface - see Conn for documentation. // Implementation of the Conn interface - see Conn for documentation.
// Read reads data from the TCP connection. // Read implements the net.Conn Read method.
//
// Read can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *TCPConn) Read(b []byte) (n int, err os.Error) { func (c *TCPConn) Read(b []byte) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
...@@ -92,10 +89,7 @@ func (c *TCPConn) Read(b []byte) (n int, err os.Error) { ...@@ -92,10 +89,7 @@ func (c *TCPConn) Read(b []byte) (n int, err os.Error) {
return c.fd.Read(b) return c.fd.Read(b)
} }
// Write writes data to the TCP connection. // Write implements the net.Conn Write method.
//
// Write can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *TCPConn) Write(b []byte) (n int, err os.Error) { func (c *TCPConn) Write(b []byte) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
...@@ -129,8 +123,7 @@ func (c *TCPConn) RemoteAddr() Addr { ...@@ -129,8 +123,7 @@ func (c *TCPConn) RemoteAddr() Addr {
return c.fd.raddr return c.fd.raddr
} }
// SetTimeout sets the read and write deadlines associated // SetTimeout implements the net.Conn SetTimeout method.
// with the connection.
func (c *TCPConn) SetTimeout(nsec int64) os.Error { func (c *TCPConn) SetTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
...@@ -138,9 +131,7 @@ func (c *TCPConn) SetTimeout(nsec int64) os.Error { ...@@ -138,9 +131,7 @@ func (c *TCPConn) SetTimeout(nsec int64) os.Error {
return setTimeout(c.fd, nsec) return setTimeout(c.fd, nsec)
} }
// SetReadTimeout sets the time (in nanoseconds) that // SetReadTimeout implements the net.Conn SetReadTimeout method.
// Read will wait for data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
func (c *TCPConn) SetReadTimeout(nsec int64) os.Error { func (c *TCPConn) SetReadTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
...@@ -148,11 +139,7 @@ func (c *TCPConn) SetReadTimeout(nsec int64) os.Error { ...@@ -148,11 +139,7 @@ func (c *TCPConn) SetReadTimeout(nsec int64) os.Error {
return setReadTimeout(c.fd, nsec) return setReadTimeout(c.fd, nsec)
} }
// SetWriteTimeout sets the time (in nanoseconds) that // SetWriteTimeout implements the net.Conn SetWriteTimeout method.
// Write will wait to send its data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
func (c *TCPConn) SetWriteTimeout(nsec int64) os.Error { func (c *TCPConn) SetWriteTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
......
...@@ -32,8 +32,8 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) { ...@@ -32,8 +32,8 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
if readFrom { if readFrom {
what = "ReadFrom" what = "ReadFrom"
} }
if n != 0 || !isEAGAIN(err1) { if n != 0 || err1 == nil || !err1.(Error).Timeout() {
t.Errorf("fd.%s on %s %s did not return 0, EAGAIN: %v, %v", what, network, addr, n, err1) t.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1)
} }
if t1-t0 < 0.5e8 || t1-t0 > 1.5e8 { if t1-t0 < 0.5e8 || t1-t0 > 1.5e8 {
t.Errorf("fd.%s on %s %s took %f seconds, expected 0.1", what, network, addr, float64(t1-t0)/1e9) t.Errorf("fd.%s on %s %s took %f seconds, expected 0.1", what, network, addr, float64(t1-t0)/1e9)
......
...@@ -77,12 +77,7 @@ func (c *UDPConn) ok() bool { return c != nil && c.fd != nil } ...@@ -77,12 +77,7 @@ func (c *UDPConn) ok() bool { return c != nil && c.fd != nil }
// Implementation of the Conn interface - see Conn for documentation. // Implementation of the Conn interface - see Conn for documentation.
// Read reads data from a single UDP packet on the connection. // Read implements the net.Conn Read method.
// If the slice b is smaller than the arriving packet,
// the excess packet data may be discarded.
//
// Read can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UDPConn) Read(b []byte) (n int, err os.Error) { func (c *UDPConn) Read(b []byte) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
...@@ -90,10 +85,7 @@ func (c *UDPConn) Read(b []byte) (n int, err os.Error) { ...@@ -90,10 +85,7 @@ func (c *UDPConn) Read(b []byte) (n int, err os.Error) {
return c.fd.Read(b) return c.fd.Read(b)
} }
// Write writes data to the connection as a single UDP packet. // Write implements the net.Conn Write method.
//
// Write can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UDPConn) Write(b []byte) (n int, err os.Error) { func (c *UDPConn) Write(b []byte) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
...@@ -127,8 +119,7 @@ func (c *UDPConn) RemoteAddr() Addr { ...@@ -127,8 +119,7 @@ func (c *UDPConn) RemoteAddr() Addr {
return c.fd.raddr return c.fd.raddr
} }
// SetTimeout sets the read and write deadlines associated // SetTimeout implements the net.Conn SetTimeout method.
// with the connection.
func (c *UDPConn) SetTimeout(nsec int64) os.Error { func (c *UDPConn) SetTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
...@@ -136,9 +127,7 @@ func (c *UDPConn) SetTimeout(nsec int64) os.Error { ...@@ -136,9 +127,7 @@ func (c *UDPConn) SetTimeout(nsec int64) os.Error {
return setTimeout(c.fd, nsec) return setTimeout(c.fd, nsec)
} }
// SetReadTimeout sets the time (in nanoseconds) that // SetReadTimeout implements the net.Conn SetReadTimeout method.
// Read will wait for data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
func (c *UDPConn) SetReadTimeout(nsec int64) os.Error { func (c *UDPConn) SetReadTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
...@@ -146,11 +135,7 @@ func (c *UDPConn) SetReadTimeout(nsec int64) os.Error { ...@@ -146,11 +135,7 @@ func (c *UDPConn) SetReadTimeout(nsec int64) os.Error {
return setReadTimeout(c.fd, nsec) return setReadTimeout(c.fd, nsec)
} }
// SetWriteTimeout sets the time (in nanoseconds) that // SetWriteTimeout implements the net.Conn SetWriteTimeout method.
// Write will wait to send its data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error { func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
...@@ -182,7 +167,7 @@ func (c *UDPConn) SetWriteBuffer(bytes int) os.Error { ...@@ -182,7 +167,7 @@ func (c *UDPConn) SetWriteBuffer(bytes int) os.Error {
// It returns the number of bytes copied into b and the return address // It returns the number of bytes copied into b and the return address
// that was on the packet. // that was on the packet.
// //
// ReadFromUDP can be made to time out and return err == os.EAGAIN // 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 SetTimeout and SetReadTimeout.
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) {
if !c.ok() { if !c.ok() {
...@@ -198,12 +183,7 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { ...@@ -198,12 +183,7 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) {
return return
} }
// ReadFrom reads a UDP packet from c, copying the payload into b. // ReadFrom implements the net.PacketConn ReadFrom method.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
// ReadFrom can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, nil, os.EINVAL return 0, nil, os.EINVAL
...@@ -214,25 +194,22 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { ...@@ -214,25 +194,22 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) {
// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. // WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
// //
// WriteToUDP can be made to time out and return err == os.EAGAIN // WriteToUDP can be made to time out and return
// after a fixed time limit; see SetTimeout and SetWriteTimeout. // an error with Timeout() == true after a fixed time limit;
// On packet-oriented connections such as UDP, write timeouts are rare. // see SetTimeout and SetWriteTimeout.
// On packet-oriented connections, write timeouts are rare.
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
} }
sa, err := addr.sockaddr(c.fd.family) sa, err1 := addr.sockaddr(c.fd.family)
if err != nil { if err1 != nil {
return 0, err return 0, &OpError{Op: "write", Net: "udp", Addr: addr, Error: err1}
} }
return c.fd.WriteTo(b, sa) return c.fd.WriteTo(b, sa)
} }
// WriteTo writes a UDP packet with payload b to addr via c. // WriteTo implements the net.PacketConn WriteTo method.
//
// WriteTo can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetWriteTimeout.
// On packet-oriented connections such as UDP, write timeouts are rare.
func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
......
...@@ -34,7 +34,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err ...@@ -34,7 +34,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
if raddr != nil { if raddr != nil {
ra = &syscall.SockaddrUnix{Name: raddr.Name} ra = &syscall.SockaddrUnix{Name: raddr.Name}
} else if proto != syscall.SOCK_DGRAM || laddr == nil { } else if proto != syscall.SOCK_DGRAM || laddr == nil {
return nil, &OpError{mode, net, nil, errMissingAddress} return nil, &OpError{Op: mode, Net: net, Error: errMissingAddress}
} }
case "listen": case "listen":
...@@ -43,7 +43,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err ...@@ -43,7 +43,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
} }
la = &syscall.SockaddrUnix{Name: laddr.Name} la = &syscall.SockaddrUnix{Name: laddr.Name}
if raddr != nil { if raddr != nil {
return nil, &OpError{mode, net, raddr, &AddrError{"unexpected remote address", raddr.String()}} return nil, &OpError{Op: mode, Net: net, Addr: raddr, Error: &AddrError{Error: "unexpected remote address", Addr: raddr.String()}}
} }
} }
...@@ -51,8 +51,8 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err ...@@ -51,8 +51,8 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
if proto != syscall.SOCK_STREAM { if proto != syscall.SOCK_STREAM {
f = sockaddrToUnixgram f = sockaddrToUnixgram
} }
fd, err = socket(net, syscall.AF_UNIX, proto, 0, la, ra, f) fd, oserr := socket(net, syscall.AF_UNIX, proto, 0, la, ra, f)
if err != nil { if oserr != nil {
goto Error goto Error
} }
return fd, nil return fd, nil
...@@ -62,7 +62,7 @@ Error: ...@@ -62,7 +62,7 @@ Error:
if mode == "listen" { if mode == "listen" {
addr = laddr addr = laddr
} }
return nil, &OpError{mode, net, addr, err} return nil, &OpError{Op: mode, Net: net, Addr: addr, Error: oserr}
} }
// UnixAddr represents the address of a Unix domain socket end point. // UnixAddr represents the address of a Unix domain socket end point.
...@@ -133,10 +133,7 @@ func (c *UnixConn) ok() bool { return c != nil && c.fd != nil } ...@@ -133,10 +133,7 @@ func (c *UnixConn) ok() bool { return c != nil && c.fd != nil }
// Implementation of the Conn interface - see Conn for documentation. // Implementation of the Conn interface - see Conn for documentation.
// Read reads data from the Unix domain connection. // Read implements the net.Conn Read method.
//
// Read can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UnixConn) Read(b []byte) (n int, err os.Error) { func (c *UnixConn) Read(b []byte) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
...@@ -144,10 +141,7 @@ func (c *UnixConn) Read(b []byte) (n int, err os.Error) { ...@@ -144,10 +141,7 @@ func (c *UnixConn) Read(b []byte) (n int, err os.Error) {
return c.fd.Read(b) return c.fd.Read(b)
} }
// Write writes data to the Unix domain connection. // Write implements the net.Conn Write method.
//
// Write can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UnixConn) Write(b []byte) (n int, err os.Error) { func (c *UnixConn) Write(b []byte) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
...@@ -184,8 +178,7 @@ func (c *UnixConn) RemoteAddr() Addr { ...@@ -184,8 +178,7 @@ func (c *UnixConn) RemoteAddr() Addr {
return c.fd.raddr return c.fd.raddr
} }
// SetTimeout sets the read and write deadlines associated // SetTimeout implements the net.Conn SetTimeout method.
// with the connection.
func (c *UnixConn) SetTimeout(nsec int64) os.Error { func (c *UnixConn) SetTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
...@@ -193,9 +186,7 @@ func (c *UnixConn) SetTimeout(nsec int64) os.Error { ...@@ -193,9 +186,7 @@ func (c *UnixConn) SetTimeout(nsec int64) os.Error {
return setTimeout(c.fd, nsec) return setTimeout(c.fd, nsec)
} }
// SetReadTimeout sets the time (in nanoseconds) that // SetReadTimeout implements the net.Conn SetReadTimeout method.
// Read will wait for data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { func (c *UnixConn) SetReadTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
...@@ -203,11 +194,7 @@ func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { ...@@ -203,11 +194,7 @@ func (c *UnixConn) SetReadTimeout(nsec int64) os.Error {
return setReadTimeout(c.fd, nsec) return setReadTimeout(c.fd, nsec)
} }
// SetWriteTimeout sets the time (in nanoseconds) that // SetWriteTimeout implements the net.Conn SetWriteTimeout method.
// Write will wait to send its data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error { func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error {
if !c.ok() { if !c.ok() {
return os.EINVAL return os.EINVAL
...@@ -237,8 +224,9 @@ func (c *UnixConn) SetWriteBuffer(bytes int) os.Error { ...@@ -237,8 +224,9 @@ func (c *UnixConn) SetWriteBuffer(bytes int) os.Error {
// It returns the number of bytes copied into b and the return address // It returns the number of bytes copied into b and the return address
// that was on the packet. // that was on the packet.
// //
// ReadFromUnix can be made to time out and return err == os.EAGAIN // ReadFromUnix can be made to time out and return
// after a fixed time limit; see SetTimeout and SetReadTimeout. // an error with Timeout() == true after a fixed time limit;
// see SetTimeout and SetReadTimeout.
func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) { func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, nil, os.EINVAL return 0, nil, os.EINVAL
...@@ -251,12 +239,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) ...@@ -251,12 +239,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error)
return return
} }
// ReadFrom reads a packet from c, copying the payload into b. // ReadFrom implements the net.PacketConn ReadFrom method.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
// ReadFrom can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, nil, os.EINVAL return 0, nil, os.EINVAL
...@@ -267,9 +250,10 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { ...@@ -267,9 +250,10 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) {
// WriteToUnix writes a packet to addr via c, copying the payload from b. // WriteToUnix writes a packet to addr via c, copying the payload from b.
// //
// WriteToUnix can be made to time out and return err == os.EAGAIN // WriteToUnix can be made to time out and return
// after a fixed time limit; see SetTimeout and SetWriteTimeout. // an error with Timeout() == true after a fixed time limit;
// On packet-oriented connections such as UDP, write timeouts are rare. // see SetTimeout and SetWriteTimeout.
// On packet-oriented connections, write timeouts are rare.
func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) { func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
...@@ -281,11 +265,7 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) { ...@@ -281,11 +265,7 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) {
return c.fd.WriteTo(b, sa) return c.fd.WriteTo(b, sa)
} }
// WriteTo writes a packet to addr via c, copying the payload from b. // WriteTo implements the net.PacketConn WriteTo method.
//
// WriteTo can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetWriteTimeout.
// On packet-oriented connections such as UDP, write timeouts are rare.
func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) {
if !c.ok() { if !c.ok() {
return 0, os.EINVAL return 0, os.EINVAL
...@@ -325,17 +305,14 @@ func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { ...@@ -325,17 +305,14 @@ func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) {
if laddr != nil { if laddr != nil {
laddr = &UnixAddr{laddr.Name, net == "unixgram"} // make our own copy laddr = &UnixAddr{laddr.Name, net == "unixgram"} // make our own copy
} }
fd, e := unixSocket(net, laddr, nil, "listen") fd, err := unixSocket(net, laddr, nil, "listen")
if e != nil { if err != nil {
if pe, ok := e.(*os.PathError); ok { return nil, err
e = pe.Error
}
return nil, e
} }
e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog()); e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog());
if e1 != 0 { if e1 != 0 {
syscall.Close(fd.sysfd) syscall.Close(fd.sysfd)
return nil, &OpError{"listen", "unix", laddr, os.Errno(e1)} return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Error: os.Errno(e1)}
} }
return &UnixListener{fd, laddr.Name}, nil return &UnixListener{fd, laddr.Name}, nil
} }
......
...@@ -15,7 +15,9 @@ type Error interface { ...@@ -15,7 +15,9 @@ type Error interface {
// Error. // Error.
type ErrorString string type ErrorString string
func (e ErrorString) String() string { return string(e) } func (e ErrorString) String() string { return string(e) }
func (e ErrorString) Temporary() bool { return false }
func (e ErrorString) Timeout() bool { return false }
// Note: If the name of the function NewError changes, // Note: If the name of the function NewError changes,
// pkg/go/doc/doc.go should be adjusted since it hardwires // pkg/go/doc/doc.go should be adjusted since it hardwires
...@@ -30,6 +32,14 @@ type Errno int64 ...@@ -30,6 +32,14 @@ type Errno int64
func (e Errno) String() string { return syscall.Errstr(int(e)) } func (e Errno) String() string { return syscall.Errstr(int(e)) }
func (e Errno) Temporary() bool {
return e == Errno(syscall.EINTR) || e.Timeout()
}
func (e Errno) Timeout() bool {
return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK)
}
// Commonly known Unix errors. // Commonly known Unix errors.
var ( var (
EPERM Error = Errno(syscall.EPERM) EPERM Error = Errno(syscall.EPERM)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment