diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-03-16 23:05:44 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-03-16 23:05:44 +0000 |
commit | 5133f00ef8baab894d92de1e8b8baae59815a8b6 (patch) | |
tree | 44176975832a3faf1626836e70c97d5edd674122 /libgo/go/net | |
parent | f617201f55938fc89b532f2240bdf77bea946471 (diff) | |
download | gcc-5133f00ef8baab894d92de1e8b8baae59815a8b6.zip gcc-5133f00ef8baab894d92de1e8b8baae59815a8b6.tar.gz gcc-5133f00ef8baab894d92de1e8b8baae59815a8b6.tar.bz2 |
Update to current version of Go library (revision 94d654be2064).
From-SVN: r171076
Diffstat (limited to 'libgo/go/net')
-rw-r--r-- | libgo/go/net/dial.go | 28 | ||||
-rw-r--r-- | libgo/go/net/dnsclient.go | 27 | ||||
-rw-r--r-- | libgo/go/net/fd.go | 15 | ||||
-rw-r--r-- | libgo/go/net/fd_windows.go | 655 | ||||
-rw-r--r-- | libgo/go/net/iprawsock.go | 6 | ||||
-rw-r--r-- | libgo/go/net/ipsock.go | 29 | ||||
-rw-r--r-- | libgo/go/net/multicast_test.go | 62 | ||||
-rw-r--r-- | libgo/go/net/net.go | 3 | ||||
-rw-r--r-- | libgo/go/net/parse.go | 10 | ||||
-rw-r--r-- | libgo/go/net/server_test.go | 18 | ||||
-rw-r--r-- | libgo/go/net/textproto/header.go | 43 | ||||
-rw-r--r-- | libgo/go/net/textproto/reader.go | 16 | ||||
-rw-r--r-- | libgo/go/net/textproto/reader_test.go | 8 | ||||
-rw-r--r-- | libgo/go/net/udpsock.go | 41 |
14 files changed, 544 insertions, 417 deletions
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go index 03b9d87..1cf8e79 100644 --- a/libgo/go/net/dial.go +++ b/libgo/go/net/dial.go @@ -24,7 +24,7 @@ import "os" // Dial("tcp", "127.0.0.1:123", "127.0.0.1:88") // func Dial(net, laddr, raddr string) (c Conn, err os.Error) { - switch prefixBefore(net, ':') { + switch net { case "tcp", "tcp4", "tcp6": var la, ra *TCPAddr if laddr != "" { @@ -137,7 +137,7 @@ func Listen(net, laddr string) (l Listener, err os.Error) { // The network string net must be a packet-oriented network: // "udp", "udp4", "udp6", or "unixgram". func ListenPacket(net, laddr string) (c PacketConn, err os.Error) { - switch prefixBefore(net, ':') { + switch net { case "udp", "udp4", "udp6": var la *UDPAddr if laddr != "" { @@ -162,18 +162,24 @@ func ListenPacket(net, laddr string) (c PacketConn, err os.Error) { return nil, err } return c, nil - case "ip", "ip4", "ip6": - var la *IPAddr - if laddr != "" { - if la, err = ResolveIPAddr(laddr); err != nil { + } + + if i := last(net, ':'); i > 0 { + switch net[0:i] { + case "ip", "ip4", "ip6": + var la *IPAddr + if laddr != "" { + if la, err = ResolveIPAddr(laddr); err != nil { + return nil, err + } + } + c, err := ListenIP(net, la) + if err != nil { return nil, err } + return c, nil } - c, err := ListenIP(net, la) - if err != nil { - return nil, err - } - return c, nil } + return nil, UnknownNetworkError(net) } diff --git a/libgo/go/net/dnsclient.go b/libgo/go/net/dnsclient.go index 87d7626..3252dd4 100644 --- a/libgo/go/net/dnsclient.go +++ b/libgo/go/net/dnsclient.go @@ -98,18 +98,18 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, os.Er // Find answer for name in dns message. // On return, if err == nil, addrs != nil. -func answer(name, server string, dns *dnsMsg, qtype uint16) (addrs []dnsRR, err os.Error) { +func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { addrs = make([]dnsRR, 0, len(dns.answer)) if dns.rcode == dnsRcodeNameError && dns.recursion_available { - return nil, &DNSError{Error: noSuchHost, Name: name} + return "", nil, &DNSError{Error: noSuchHost, Name: name} } if dns.rcode != dnsRcodeSuccess { // None of the error codes make sense // for the query we sent. If we didn't get // a name error and we didn't get success, // the server is behaving incorrectly. - return nil, &DNSError{Error: "server misbehaving", Name: name, Server: server} + return "", nil, &DNSError{Error: "server misbehaving", Name: name, Server: server} } // Look for the name. @@ -137,19 +137,19 @@ Cname: } } if len(addrs) == 0 { - return nil, &DNSError{Error: noSuchHost, Name: name, Server: server} + return "", nil, &DNSError{Error: noSuchHost, Name: name, Server: server} } - return addrs, nil + return name, addrs, nil } - return nil, &DNSError{Error: "too many redirects", Name: name, Server: server} + return "", nil, &DNSError{Error: "too many redirects", Name: name, Server: server} } // Do a lookup for a single name, which must be rooted // (otherwise answer will not find the answers). -func tryOneName(cfg *dnsConfig, name string, qtype uint16) (addrs []dnsRR, err os.Error) { +func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { if len(cfg.servers) == 0 { - return nil, &DNSError{Error: "no DNS servers", Name: name} + return "", nil, &DNSError{Error: "no DNS servers", Name: name} } for i := 0; i < len(cfg.servers); i++ { // Calling Dial here is scary -- we have to be sure @@ -170,7 +170,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (addrs []dnsRR, err o err = merr continue } - addrs, err = answer(name, server, msg, qtype) + cname, addrs, err = answer(name, server, msg, qtype) if err == nil || err.(*DNSError).Error == noSuchHost { break } @@ -261,9 +261,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro rname += "." } // Can try as ordinary name. - addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg, rname, qtype) if err == nil { - cname = rname return } } @@ -277,9 +276,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro if rname[len(rname)-1] != '.' { rname += "." } - addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg, rname, qtype) if err == nil { - cname = rname return } } @@ -289,9 +287,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro if !rooted { rname += "." } - addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg, rname, qtype) if err == nil { - cname = rname return } return diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go index 26d17d4..d48aefe 100644 --- a/libgo/go/net/fd.go +++ b/libgo/go/net/fd.go @@ -220,11 +220,16 @@ func (s *pollServer) Run() { nn, _ = s.pr.Read(scratch[0:]) } // Read from channels - for fd, ok := <-s.cr; ok; fd, ok = <-s.cr { - s.AddFD(fd, 'r') - } - for fd, ok := <-s.cw; ok; fd, ok = <-s.cw { - s.AddFD(fd, 'w') + Update: + for { + select { + case fd := <-s.cr: + s.AddFD(fd, 'r') + case fd := <-s.cw: + s.AddFD(fd, 'w') + default: + break Update + } } } else { netfd := s.LookupFD(fd, mode) diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index 9b91eb3..63a8fbc 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -13,147 +13,241 @@ import ( "unsafe" ) +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 } + +var initErr os.Error + +func init() { + var d syscall.WSAData + e := syscall.WSAStartup(uint32(0x101), &d) + if e != 0 { + initErr = os.NewSyscallError("WSAStartup", e) + } +} + +func closesocket(s int) (errno int) { + return syscall.Closesocket(int32(s)) +} + +// Interface for all io operations. +type anOpIface interface { + Op() *anOp + Name() string + Submit() (errno int) +} + // IO completion result parameters. type ioResult struct { - key uint32 - qty uint32 - errno int + qty uint32 + err int } -// Network file descriptor. -type netFD struct { - // locking/lifetime of sysfd - sysmu sync.Mutex - sysref int - closing bool +// anOp implements functionality common to all io operations. +type anOp struct { + // Used by IOCP interface, it must be first field + // of the struct, as our code rely on it. + o syscall.Overlapped - // immutable until Close - sysfd int - family int - proto int - cr chan *ioResult - cw chan *ioResult - net string - laddr Addr - raddr Addr + resultc chan ioResult // io completion results + errnoc chan int // io submit / cancel operation errors + fd *netFD +} - // owned by client - rdeadline_delta int64 - rdeadline int64 - rio sync.Mutex - wdeadline_delta int64 - wdeadline int64 - wio sync.Mutex +func (o *anOp) Init(fd *netFD) { + o.fd = fd + o.resultc = make(chan ioResult, 1) + o.errnoc = make(chan int) } -type InvalidConnError struct{} +func (o *anOp) Op() *anOp { + return o +} -func (e *InvalidConnError) String() string { return "invalid net.Conn" } -func (e *InvalidConnError) Temporary() bool { return false } -func (e *InvalidConnError) Timeout() bool { return false } +// bufOp is used by io operations that read / write +// data from / to client buffer. +type bufOp struct { + anOp + buf syscall.WSABuf +} -// pollServer will run around waiting for io completion request -// to arrive. Every request received will contain channel to signal -// io owner about the completion. +func (o *bufOp) Init(fd *netFD, buf []byte) { + o.anOp.Init(fd) + o.buf.Len = uint32(len(buf)) + if len(buf) == 0 { + o.buf.Buf = nil + } else { + o.buf.Buf = (*byte)(unsafe.Pointer(&buf[0])) + } +} -type pollServer struct { +// resultSrv will retreive all io completion results from +// iocp and send them to the correspondent waiting client +// goroutine via channel supplied in the request. +type resultSrv struct { iocp int32 } -func newPollServer() (s *pollServer, err os.Error) { - s = new(pollServer) - var e int - if s.iocp, e = syscall.CreateIoCompletionPort(-1, 0, 0, 1); e != 0 { - return nil, os.NewSyscallError("CreateIoCompletionPort", e) +func (s *resultSrv) Run() { + var o *syscall.Overlapped + var key uint32 + var r ioResult + for { + r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE) + switch { + case r.err == 0: + // Dequeued successfully completed io packet. + case r.err == syscall.WAIT_TIMEOUT && o == nil: + // Wait has timed out (should not happen now, but might be used in the future). + panic("GetQueuedCompletionStatus timed out") + case o == nil: + // Failed to dequeue anything -> report the error. + panic("GetQueuedCompletionStatus failed " + syscall.Errstr(r.err)) + default: + // Dequeued failed io packet. + } + (*anOp)(unsafe.Pointer(o)).resultc <- r } - go s.Run() - return s, nil } -type ioPacket struct { - // Used by IOCP interface, - // it must be first field of the struct, - // as our code rely on it. - o syscall.Overlapped - // Link to the io owner. - c chan *ioResult - - w *syscall.WSABuf +// ioSrv executes net io requests. +type ioSrv struct { + submchan chan anOpIface // submit io requests + canchan chan anOpIface // cancel io requests } -func (s *pollServer) getCompletedIO() (ov *syscall.Overlapped, result *ioResult, err os.Error) { - var r ioResult - var o *syscall.Overlapped - _, e := syscall.GetQueuedCompletionStatus(s.iocp, &r.qty, &r.key, &o, syscall.INFINITE) - switch { - case e == 0: - // Dequeued successfully completed io packet. - return o, &r, nil - case e == syscall.WAIT_TIMEOUT && o == nil: - // Wait has timed out (should not happen now, but might be used in the future). - return nil, &r, os.NewSyscallError("GetQueuedCompletionStatus", e) - case o == nil: - // Failed to dequeue anything -> report the error. - return nil, &r, os.NewSyscallError("GetQueuedCompletionStatus", e) - default: - // Dequeued failed io packet. - r.errno = e - return o, &r, nil +// ProcessRemoteIO will execute submit io requests on behalf +// of other goroutines, all on a single os thread, so it can +// cancel them later. Results of all operations will be sent +// back to their requesters via channel supplied in request. +func (s *ioSrv) ProcessRemoteIO() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + for { + select { + case o := <-s.submchan: + o.Op().errnoc <- o.Submit() + case o := <-s.canchan: + o.Op().errnoc <- syscall.CancelIo(uint32(o.Op().fd.sysfd)) + } } - return } -func (s *pollServer) Run() { - for { - o, r, err := s.getCompletedIO() - if err != nil { - panic("Run pollServer: " + err.String() + "\n") +// ExecIO executes a single io operation. It either executes it +// inline, or, if timeouts are employed, passes the request onto +// a special goroutine and waits for completion or cancels request. +func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err os.Error) { + var e int + o := oi.Op() + if deadline_delta > 0 { + // Send request to a special dedicated thread, + // so it can stop the io with CancelIO later. + s.submchan <- oi + e = <-o.errnoc + } else { + e = oi.Submit() + } + switch e { + case 0: + // IO completed immediately, but we need to get our completion message anyway. + case syscall.ERROR_IO_PENDING: + // IO started, and we have to wait for it's completion. + default: + return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(e)} + } + // Wait for our request to complete. + var r ioResult + if deadline_delta > 0 { + select { + case r = <-o.resultc: + case <-time.After(deadline_delta): + s.canchan <- oi + <-o.errnoc + r = <-o.resultc + if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled + r.err = syscall.EWOULDBLOCK + } } - p := (*ioPacket)(unsafe.Pointer(o)) - p.c <- r + } else { + r = <-o.resultc } + if r.err != 0 { + err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(r.err)} + } + return int(r.qty), err } -// Network FD methods. -// All the network FDs use a single pollServer. - -var pollserver *pollServer +// Start helper goroutines. +var resultsrv *resultSrv +var iosrv *ioSrv var onceStartServer sync.Once func startServer() { - p, err := newPollServer() - if err != nil { - panic("Start pollServer: " + err.String() + "\n") - } - pollserver = p - - go timeoutIO() + resultsrv = new(resultSrv) + var errno int + resultsrv.iocp, errno = syscall.CreateIoCompletionPort(-1, 0, 0, 1) + if errno != 0 { + panic("CreateIoCompletionPort failed " + syscall.Errstr(errno)) + } + go resultsrv.Run() + + iosrv = new(ioSrv) + iosrv.submchan = make(chan anOpIface) + iosrv.canchan = make(chan anOpIface) + go iosrv.ProcessRemoteIO() } -var initErr os.Error +// Network file descriptor. +type netFD struct { + // locking/lifetime of sysfd + sysmu sync.Mutex + sysref int + closing bool -func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) { - if initErr != nil { - return nil, initErr - } - onceStartServer.Do(startServer) - // Associate our socket with pollserver.iocp. - if _, e := syscall.CreateIoCompletionPort(int32(fd), pollserver.iocp, 0, 0); e != 0 { - return nil, &OpError{"CreateIoCompletionPort", net, laddr, os.Errno(e)} - } + // immutable until Close + sysfd int + family int + proto int + net string + laddr Addr + raddr Addr + + // owned by client + rdeadline_delta int64 + rdeadline int64 + rio sync.Mutex + wdeadline_delta int64 + wdeadline int64 + wio sync.Mutex +} + +func allocFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD) { f = &netFD{ sysfd: fd, family: family, proto: proto, - cr: make(chan *ioResult, 1), - cw: make(chan *ioResult, 1), net: net, laddr: laddr, raddr: raddr, } runtime.SetFinalizer(f, (*netFD).Close) - return f, nil + return f +} + +func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) { + if initErr != nil { + return nil, initErr + } + onceStartServer.Do(startServer) + // Associate our socket with resultsrv.iocp. + if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 { + return nil, &OpError{"CreateIoCompletionPort", net, laddr, os.Errno(e)} + } + return allocFD(fd, family, proto, net, laddr, raddr), nil } // Add a reference to this fd. @@ -172,7 +266,7 @@ func (fd *netFD) decref() { // In case the user has set linger, switch to blocking mode so // the close blocks. As long as this doesn't happen often, we // can handle the extra OS processes. Otherwise we'll need to - // use the pollserver for Close too. Sigh. + // use the resultsrv for Close too. Sigh. syscall.SetNonblock(fd.sysfd, false) closesocket(fd.sysfd) fd.sysfd = -1 @@ -194,89 +288,22 @@ func (fd *netFD) Close() os.Error { return nil } -func newWSABuf(p []byte) *syscall.WSABuf { - var p0 *byte - if len(p) > 0 { - p0 = (*byte)(unsafe.Pointer(&p[0])) - } - return &syscall.WSABuf{uint32(len(p)), p0} -} - -func waitPacket(fd *netFD, pckt *ioPacket, mode int) (r *ioResult) { - var delta int64 - if mode == 'r' { - delta = fd.rdeadline_delta - } - if mode == 'w' { - delta = fd.wdeadline_delta - } - if delta <= 0 { - return <-pckt.c - } +// Read from network. - select { - case r = <-pckt.c: - case <-time.After(delta): - a := &arg{f: cancel, fd: fd, pckt: pckt, c: make(chan int)} - ioChan <- a - <-a.c - r = <-pckt.c - if r.errno == 995 { // IO Canceled - r.errno = syscall.EWOULDBLOCK - } - } - return r +type readOp struct { + bufOp } -const ( - read = iota - readfrom - write - writeto - cancel -) +func (o *readOp) Submit() (errno int) { + var d, f uint32 + return syscall.WSARecv(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil) +} -type arg struct { - f int - fd *netFD - pckt *ioPacket - done *uint32 - flags *uint32 - rsa *syscall.RawSockaddrAny - size *int32 - sa *syscall.Sockaddr - c chan int -} - -var ioChan chan *arg = make(chan *arg) - -func timeoutIO() { - // CancelIO only cancels all pending input and output (I/O) operations that are - // issued by the calling thread for the specified file, does not cancel I/O - // operations that other threads issue for a file handle. So we need do all timeout - // I/O in single OS thread. - runtime.LockOSThread() - defer runtime.UnlockOSThread() - for { - o := <-ioChan - var e int - switch o.f { - case read: - e = syscall.WSARecv(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, o.flags, &o.pckt.o, nil) - case readfrom: - e = syscall.WSARecvFrom(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, o.flags, o.rsa, o.size, &o.pckt.o, nil) - case write: - e = syscall.WSASend(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, uint32(0), &o.pckt.o, nil) - case writeto: - e = syscall.WSASendto(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, 0, *o.sa, &o.pckt.o, nil) - case cancel: - _, e = syscall.CancelIo(uint32(o.fd.sysfd)) - } - o.c <- e - } +func (o *readOp) Name() string { + return "WSARecv" } -func (fd *netFD) Read(p []byte) (n int, err os.Error) { +func (fd *netFD) Read(buf []byte) (n int, err os.Error) { if fd == nil { return 0, os.EINVAL } @@ -287,45 +314,37 @@ func (fd *netFD) Read(p []byte) (n int, err os.Error) { if fd.sysfd == -1 { return 0, os.EINVAL } - // Submit receive request. - var pckt ioPacket - pckt.c = fd.cr - pckt.w = newWSABuf(p) - var done uint32 - flags := uint32(0) - var e int - if fd.rdeadline_delta > 0 { - a := &arg{f: read, fd: fd, pckt: &pckt, done: &done, flags: &flags, c: make(chan int)} - ioChan <- a - e = <-a.c - } else { - e = syscall.WSARecv(uint32(fd.sysfd), pckt.w, 1, &done, &flags, &pckt.o, nil) - } - switch e { - case 0: - // IO completed immediately, but we need to get our completion message anyway. - case syscall.ERROR_IO_PENDING: - // IO started, and we have to wait for it's completion. - default: - return 0, &OpError{"WSARecv", fd.net, fd.laddr, os.Errno(e)} - } - // Wait for our request to complete. - r := waitPacket(fd, &pckt, 'r') - if r.errno != 0 { - err = &OpError{"WSARecv", fd.net, fd.laddr, os.Errno(r.errno)} - } - n = int(r.qty) + var o readOp + o.Init(fd, buf) + n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) if err == nil && n == 0 { err = os.EOF } return } -func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) { +// ReadFrom from network. + +type readFromOp struct { + bufOp + rsa syscall.RawSockaddrAny +} + +func (o *readFromOp) Submit() (errno int) { + var d, f uint32 + l := int32(unsafe.Sizeof(o.rsa)) + return syscall.WSARecvFrom(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.rsa, &l, &o.o, nil) +} + +func (o *readFromOp) Name() string { + return "WSARecvFrom" +} + +func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err os.Error) { if fd == nil { return 0, nil, os.EINVAL } - if len(p) == 0 { + if len(buf) == 0 { return 0, nil, nil } fd.rio.Lock() @@ -335,41 +354,29 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) { if fd.sysfd == -1 { return 0, nil, os.EINVAL } - // Submit receive request. - var pckt ioPacket - pckt.c = fd.cr - pckt.w = newWSABuf(p) - var done uint32 - flags := uint32(0) - var rsa syscall.RawSockaddrAny - l := int32(unsafe.Sizeof(rsa)) - var e int - if fd.rdeadline_delta > 0 { - a := &arg{f: readfrom, fd: fd, pckt: &pckt, done: &done, flags: &flags, rsa: &rsa, size: &l, c: make(chan int)} - ioChan <- a - e = <-a.c - } else { - e = syscall.WSARecvFrom(uint32(fd.sysfd), pckt.w, 1, &done, &flags, &rsa, &l, &pckt.o, nil) - } - switch e { - case 0: - // IO completed immediately, but we need to get our completion message anyway. - case syscall.ERROR_IO_PENDING: - // IO started, and we have to wait for it's completion. - default: - return 0, nil, &OpError{"WSARecvFrom", fd.net, fd.laddr, os.Errno(e)} - } - // Wait for our request to complete. - r := waitPacket(fd, &pckt, 'r') - if r.errno != 0 { - err = &OpError{"WSARecvFrom", fd.net, fd.laddr, os.Errno(r.errno)} - } - n = int(r.qty) - sa, _ = rsa.Sockaddr() + var o readFromOp + o.Init(fd, buf) + n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) + sa, _ = o.rsa.Sockaddr() return } -func (fd *netFD) Write(p []byte) (n int, err os.Error) { +// Write to network. + +type writeOp struct { + bufOp +} + +func (o *writeOp) Submit() (errno int) { + var d uint32 + return syscall.WSASend(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, &o.o, nil) +} + +func (o *writeOp) Name() string { + return "WSASend" +} + +func (fd *netFD) Write(buf []byte) (n int, err os.Error) { if fd == nil { return 0, os.EINVAL } @@ -380,41 +387,32 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) { if fd.sysfd == -1 { return 0, os.EINVAL } - // Submit send request. - var pckt ioPacket - pckt.c = fd.cw - pckt.w = newWSABuf(p) - var done uint32 - var e int - if fd.wdeadline_delta > 0 { - a := &arg{f: write, fd: fd, pckt: &pckt, done: &done, c: make(chan int)} - ioChan <- a - e = <-a.c - } else { - e = syscall.WSASend(uint32(fd.sysfd), pckt.w, 1, &done, uint32(0), &pckt.o, nil) - } - switch e { - case 0: - // IO completed immediately, but we need to get our completion message anyway. - case syscall.ERROR_IO_PENDING: - // IO started, and we have to wait for it's completion. - default: - return 0, &OpError{"WSASend", fd.net, fd.laddr, os.Errno(e)} - } - // Wait for our request to complete. - r := waitPacket(fd, &pckt, 'w') - if r.errno != 0 { - err = &OpError{"WSASend", fd.net, fd.laddr, os.Errno(r.errno)} - } - n = int(r.qty) - return + var o writeOp + o.Init(fd, buf) + return iosrv.ExecIO(&o, fd.wdeadline_delta) +} + +// WriteTo to network. + +type writeToOp struct { + bufOp + sa syscall.Sockaddr +} + +func (o *writeToOp) Submit() (errno int) { + var d uint32 + return syscall.WSASendto(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, o.sa, &o.o, nil) +} + +func (o *writeToOp) Name() string { + return "WSASendto" } -func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) { +func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error) { if fd == nil { return 0, os.EINVAL } - if len(p) == 0 { + if len(buf) == 0 { return 0, nil } fd.wio.Lock() @@ -424,34 +422,29 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) { if fd.sysfd == -1 { return 0, os.EINVAL } - // Submit send request. - var pckt ioPacket - pckt.c = fd.cw - pckt.w = newWSABuf(p) - var done uint32 - var e int - if fd.wdeadline_delta > 0 { - a := &arg{f: writeto, fd: fd, pckt: &pckt, done: &done, sa: &sa, c: make(chan int)} - ioChan <- a - e = <-a.c - } else { - e = syscall.WSASendto(uint32(fd.sysfd), pckt.w, 1, &done, 0, sa, &pckt.o, nil) - } - switch e { - case 0: - // IO completed immediately, but we need to get our completion message anyway. - case syscall.ERROR_IO_PENDING: - // IO started, and we have to wait for it's completion. - default: - return 0, &OpError{"WSASendTo", fd.net, fd.laddr, os.Errno(e)} - } - // Wait for our request to complete. - r := waitPacket(fd, &pckt, 'w') - if r.errno != 0 { - err = &OpError{"WSASendTo", fd.net, fd.laddr, os.Errno(r.errno)} - } - n = int(r.qty) - return + var o writeToOp + o.Init(fd, buf) + o.sa = sa + return iosrv.ExecIO(&o, fd.wdeadline_delta) +} + +// Accept new network connections. + +type acceptOp struct { + anOp + newsock int + attrs [2]syscall.RawSockaddrAny // space for local and remote address only +} + +func (o *acceptOp) Submit() (errno int) { + var d uint32 + l := uint32(unsafe.Sizeof(o.attrs[0])) + return syscall.AcceptEx(uint32(o.fd.sysfd), uint32(o.newsock), + (*byte)(unsafe.Pointer(&o.attrs[0])), 0, l, l, &d, &o.o) +} + +func (o *acceptOp) Name() string { + return "AcceptEx" } func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.Error) { @@ -474,72 +467,40 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. // Associate our new socket with IOCP. onceStartServer.Do(startServer) - if _, e = syscall.CreateIoCompletionPort(int32(s), pollserver.iocp, 0, 0); e != 0 { + if _, e = syscall.CreateIoCompletionPort(int32(s), resultsrv.iocp, 0, 0); e != 0 { return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, os.Errno(e)} } // Submit accept request. - // Will use new unique channel here, because, unlike Read or Write, - // Accept is expected to be executed by many goroutines simultaniously. - var pckt ioPacket - pckt.c = make(chan *ioResult) - attrs, e := syscall.AcceptIOCP(fd.sysfd, s, &pckt.o) - switch e { - case 0: - // IO completed immediately, but we need to get our completion message anyway. - case syscall.ERROR_IO_PENDING: - // IO started, and we have to wait for it's completion. - default: - closesocket(s) - return nil, &OpError{"AcceptEx", fd.net, fd.laddr, os.Errno(e)} - } - - // Wait for peer connection. - r := <-pckt.c - if r.errno != 0 { + var o acceptOp + o.Init(fd) + o.newsock = s + _, err = iosrv.ExecIO(&o, 0) + if err != nil { closesocket(s) - return nil, &OpError{"AcceptEx", fd.net, fd.laddr, os.Errno(r.errno)} + return nil, err } // Inherit properties of the listening socket. e = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, fd.sysfd) if e != 0 { closesocket(s) - return nil, &OpError{"Setsockopt", fd.net, fd.laddr, os.Errno(r.errno)} + return nil, err } // Get local and peer addr out of AcceptEx buffer. - lsa, rsa := syscall.GetAcceptIOCPSockaddrs(attrs) - - // Create our netFD and return it for further use. - laddr := toAddr(lsa) - raddr := toAddr(rsa) - - f := &netFD{ - sysfd: s, - family: fd.family, - proto: fd.proto, - cr: make(chan *ioResult, 1), - cw: make(chan *ioResult, 1), - net: fd.net, - laddr: laddr, - raddr: raddr, - } - runtime.SetFinalizer(f, (*netFD).Close) - return f, nil -} - -func closesocket(s int) (errno int) { - return syscall.Closesocket(int32(s)) + var lrsa, rrsa *syscall.RawSockaddrAny + var llen, rlen int32 + l := uint32(unsafe.Sizeof(*lrsa)) + syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&o.attrs[0])), + 0, l, l, &lrsa, &llen, &rrsa, &rlen) + lsa, _ := lrsa.Sockaddr() + rsa, _ := rrsa.Sockaddr() + + return allocFD(s, fd.family, fd.proto, fd.net, toAddr(lsa), toAddr(rsa)), nil } -func init() { - var d syscall.WSAData - e := syscall.WSAStartup(uint32(0x101), &d) - if e != 0 { - initErr = os.NewSyscallError("WSAStartup", e) - } -} +// Not implemeted functions. func (fd *netFD) dup() (f *os.File, err os.Error) { // TODO: Implement this diff --git a/libgo/go/net/iprawsock.go b/libgo/go/net/iprawsock.go index 241be15..81a918c 100644 --- a/libgo/go/net/iprawsock.go +++ b/libgo/go/net/iprawsock.go @@ -245,7 +245,7 @@ func hostToIP(host string) (ip IP, err os.Error) { err = err1 goto Error } - addr = ParseIP(addrs[0]) + addr = firstSupportedAddr(addrs) if addr == nil { // should not happen err = &AddrError{"LookupHost returned invalid address", addrs[0]} @@ -311,7 +311,7 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) { if err != nil { return } - switch prefixBefore(net, ':') { + switch net { case "ip", "ip4", "ip6": default: return nil, UnknownNetworkError(net) @@ -335,7 +335,7 @@ func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) { if err != nil { return } - switch prefixBefore(net, ':') { + switch net { case "ip", "ip4", "ip6": default: return nil, UnknownNetworkError(net) diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index 4ba6a55..ae4204b 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.go @@ -18,19 +18,34 @@ import ( // Unfortunately, we need to run on kernels built without IPv6 support too. // So probe the kernel to figure it out. func kernelSupportsIPv6() bool { - // FreeBSD does not support this sort of interface. - if syscall.OS == "freebsd" { + s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if err != 0 { return false } - fd, e := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) - if fd >= 0 { - closesocket(fd) + defer closesocket(s) + + la := &TCPAddr{IP: IPv4(127, 0, 0, 1)} + sa, oserr := la.toAddr().sockaddr(syscall.AF_INET6) + if oserr != nil { + return false } - return e == 0 + + return syscall.Bind(s, sa) == 0 } var preferIPv4 = !kernelSupportsIPv6() +func firstSupportedAddr(addrs []string) (addr IP) { + for _, s := range addrs { + addr = ParseIP(s) + if !preferIPv4 || addr.To4() != nil { + break + } + addr = nil + } + return addr +} + // TODO(rsc): if syscall.OS == "linux", we're supposd to read // /proc/sys/net/core/somaxconn, // to take advantage of kernels that have raised the limit. @@ -208,7 +223,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { err = err1 goto Error } - addr = ParseIP(addrs[0]) + addr = firstSupportedAddr(addrs) if addr == nil { // should not happen err = &AddrError{"LookupHost returned invalid address", addrs[0]} diff --git a/libgo/go/net/multicast_test.go b/libgo/go/net/multicast_test.go new file mode 100644 index 0000000..32fdec8 --- /dev/null +++ b/libgo/go/net/multicast_test.go @@ -0,0 +1,62 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "runtime" + "testing" +) + +func TestMulticastJoinAndLeave(t *testing.T) { + if runtime.GOOS == "windows" { + return + } + + addr := &UDPAddr{ + IP: IPv4zero, + Port: 0, + } + // open a UDPConn + conn, err := ListenUDP("udp4", addr) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + // try to join group + mcast := IPv4(224, 0, 0, 254) + err = conn.JoinGroup(mcast) + if err != nil { + t.Fatal(err) + } + + // try to leave group + err = conn.LeaveGroup(mcast) + if err != nil { + t.Fatal(err) + } +} + +func TestJoinFailureWithIPv6Address(t *testing.T) { + addr := &UDPAddr{ + IP: IPv4zero, + Port: 0, + } + + // open a UDPConn + conn, err := ListenUDP("udp4", addr) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + // try to join group + mcast := ParseIP("ff02::1") + err = conn.JoinGroup(mcast) + if err == nil { + t.Fatal("JoinGroup succeeded, should fail") + } + t.Logf("%s", err) +} diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index c0c1c3b..04a898a 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/net/net.go @@ -31,7 +31,6 @@ type Conn interface { Write(b []byte) (n int, err os.Error) // Close closes the connection. - // The error returned is an os.Error to satisfy io.Closer; Close() os.Error // LocalAddr returns the local network address. @@ -83,7 +82,6 @@ type PacketConn interface { WriteTo(b []byte, addr Addr) (n int, err os.Error) // Close closes the connection. - // The error returned is an os.Error to satisfy io.Closer; Close() os.Error // LocalAddr returns the local network address. @@ -112,7 +110,6 @@ type Listener interface { Accept() (c Conn, err os.Error) // Close closes the listener. - // The error returned is an os.Error to satisfy io.Closer; Close() os.Error // Addr returns the listener's network address. diff --git a/libgo/go/net/parse.go b/libgo/go/net/parse.go index 605f311..2bc0db4 100644 --- a/libgo/go/net/parse.go +++ b/libgo/go/net/parse.go @@ -192,16 +192,6 @@ func count(s string, b byte) int { return n } -// Returns the prefix of s up to but not including the character c -func prefixBefore(s string, c byte) string { - for i, v := range s { - if v == int(c) { - return s[0:i] - } - } - return s -} - // Index of rightmost occurrence of b in s. func last(s string, b byte) int { i := len(s) diff --git a/libgo/go/net/server_test.go b/libgo/go/net/server_test.go index 3f2442a..3dda500 100644 --- a/libgo/go/net/server_test.go +++ b/libgo/go/net/server_test.go @@ -25,7 +25,7 @@ func runEcho(fd io.ReadWriter, done chan<- int) { for { n, err := fd.Read(buf[0:]) - if err != nil || n == 0 { + if err != nil || n == 0 || string(buf[:n]) == "END" { break } fd.Write(buf[0:n]) @@ -79,6 +79,13 @@ func connect(t *testing.T, network, addr string, isEmpty bool) { if n != len(b) || err1 != nil { t.Fatalf("fd.Read() = %d, %v (want %d, nil)", n, err1, len(b)) } + + // Send explicit ending for unixpacket. + // Older Linux kernels do stop reads on close. + if network == "unixpacket" { + fd.Write([]byte("END")) + } + fd.Close() } @@ -133,13 +140,16 @@ func runPacket(t *testing.T, network, addr string, listening chan<- string, done listening <- c.LocalAddr().String() c.SetReadTimeout(10e6) // 10ms var buf [1000]byte +Run: for { n, addr, err := c.ReadFrom(buf[0:]) if e, ok := err.(Error); ok && e.Timeout() { - if done <- 1 { - break + select { + case done <- 1: + break Run + default: + continue Run } - continue } if err != nil { break diff --git a/libgo/go/net/textproto/header.go b/libgo/go/net/textproto/header.go new file mode 100644 index 0000000..288deb2 --- /dev/null +++ b/libgo/go/net/textproto/header.go @@ -0,0 +1,43 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package textproto + +// A MIMEHeader represents a MIME-style header mapping +// keys to sets of values. +type MIMEHeader map[string][]string + +// Add adds the key, value pair to the header. +// It appends to any existing values associated with key. +func (h MIMEHeader) Add(key, value string) { + key = CanonicalMIMEHeaderKey(key) + h[key] = append(h[key], value) +} + +// Set sets the header entries associated with key to +// the single element value. It replaces any existing +// values associated with key. +func (h MIMEHeader) Set(key, value string) { + h[CanonicalMIMEHeaderKey(key)] = []string{value} +} + +// Get gets the first value associated with the given key. +// If there are no values associated with the key, Get returns "". +// Get is a convenience method. For more complex queries, +// access the map directly. +func (h MIMEHeader) Get(key string) string { + if h == nil { + return "" + } + v := h[CanonicalMIMEHeaderKey(key)] + if len(v) == 0 { + return "" + } + return v[0] +} + +// Del deletes the values associated with key. +func (h MIMEHeader) Del(key string) { + h[CanonicalMIMEHeaderKey(key)] = nil, false +} diff --git a/libgo/go/net/textproto/reader.go b/libgo/go/net/textproto/reader.go index c8e34b7..ac12786 100644 --- a/libgo/go/net/textproto/reader.go +++ b/libgo/go/net/textproto/reader.go @@ -402,7 +402,7 @@ func (r *Reader) ReadDotLines() ([]string, os.Error) { // ReadMIMEHeader reads a MIME-style header from r. // The header is a sequence of possibly continued Key: Value lines // ending in a blank line. -// The returned map m maps CanonicalHeaderKey(key) to a +// The returned map m maps CanonicalMIMEHeaderKey(key) to a // sequence of values in the same order encountered in the input. // // For example, consider this input: @@ -415,12 +415,12 @@ func (r *Reader) ReadDotLines() ([]string, os.Error) { // Given that input, ReadMIMEHeader returns the map: // // map[string][]string{ -// "My-Key": []string{"Value 1", "Value 2"}, -// "Long-Key": []string{"Even Longer Value"}, +// "My-Key": {"Value 1", "Value 2"}, +// "Long-Key": {"Even Longer Value"}, // } // -func (r *Reader) ReadMIMEHeader() (map[string][]string, os.Error) { - m := make(map[string][]string) +func (r *Reader) ReadMIMEHeader() (MIMEHeader, os.Error) { + m := make(MIMEHeader) for { kv, err := r.ReadContinuedLineBytes() if len(kv) == 0 { @@ -432,7 +432,7 @@ func (r *Reader) ReadMIMEHeader() (map[string][]string, os.Error) { if i < 0 || bytes.IndexByte(kv[0:i], ' ') >= 0 { return m, ProtocolError("malformed MIME header line: " + string(kv)) } - key := CanonicalHeaderKey(string(kv[0:i])) + key := CanonicalMIMEHeaderKey(string(kv[0:i])) // Skip initial spaces in value. i++ // skip colon @@ -452,12 +452,12 @@ func (r *Reader) ReadMIMEHeader() (map[string][]string, os.Error) { panic("unreachable") } -// CanonicalHeaderKey returns the canonical format of the +// CanonicalMIMEHeaderKey returns the canonical format of the // MIME header key s. The canonicalization converts the first // letter and any letter following a hyphen to upper case; // the rest are converted to lowercase. For example, the // canonical key for "accept-encoding" is "Accept-Encoding". -func CanonicalHeaderKey(s string) string { +func CanonicalMIMEHeaderKey(s string) string { // Quick check for canonical encoding. needUpper := true for i := 0; i < len(s); i++ { diff --git a/libgo/go/net/textproto/reader_test.go b/libgo/go/net/textproto/reader_test.go index 2cecbc7..0658e58 100644 --- a/libgo/go/net/textproto/reader_test.go +++ b/libgo/go/net/textproto/reader_test.go @@ -26,10 +26,10 @@ var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{ {"USER-AGENT", "User-Agent"}, } -func TestCanonicalHeaderKey(t *testing.T) { +func TestCanonicalMIMEHeaderKey(t *testing.T) { for _, tt := range canonicalHeaderKeyTests { - if s := CanonicalHeaderKey(tt.in); s != tt.out { - t.Errorf("CanonicalHeaderKey(%q) = %q, want %q", tt.in, s, tt.out) + if s := CanonicalMIMEHeaderKey(tt.in); s != tt.out { + t.Errorf("CanonicalMIMEHeaderKey(%q) = %q, want %q", tt.in, s, tt.out) } } } @@ -130,7 +130,7 @@ func TestReadDotBytes(t *testing.T) { func TestReadMIMEHeader(t *testing.T) { r := reader("my-key: Value 1 \r\nLong-key: Even \n Longer Value\r\nmy-Key: Value 2\r\n\n") m, err := r.ReadMIMEHeader() - want := map[string][]string{ + want := MIMEHeader{ "My-Key": {"Value 1", "Value 2"}, "Long-Key": {"Even Longer Value"}, } diff --git a/libgo/go/net/udpsock.go b/libgo/go/net/udpsock.go index 0270954..f927449 100644 --- a/libgo/go/net/udpsock.go +++ b/libgo/go/net/udpsock.go @@ -279,3 +279,44 @@ func (c *UDPConn) BindToDevice(device string) os.Error { // It is the caller's responsibility to close f when finished. // Closing c does not affect f, and closing f does not affect c. func (c *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } + +var errInvalidMulticast = os.ErrorString("invalid IPv4 multicast address") + +// JoinGroup joins the IPv4 multicast group named by addr. +// The UDPConn must use the "udp4" network. +func (c *UDPConn) JoinGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + ip := addr.To4() + if ip == nil { + return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast} + } + mreq := &syscall.IpMreq{ + Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + } + err := os.NewSyscallError("setsockopt", syscall.SetsockoptIpMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) + if err != nil { + return &OpError{"joingroup", "udp", &IPAddr{ip}, err} + } + return nil +} + +// LeaveGroup exits the IPv4 multicast group named by addr. +func (c *UDPConn) LeaveGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + ip := addr.To4() + if ip == nil { + return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast} + } + mreq := &syscall.IpMreq{ + Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + } + err := os.NewSyscallError("setsockopt", syscall.SetsockoptIpMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)) + if err != nil { + return &OpError{"leavegroup", "udp", &IPAddr{ip}, err} + } + return nil +} |