summaryrefslogtreecommitdiff
path: root/src/pkg/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/net')
-rw-r--r--src/pkg/net/dial.go28
-rw-r--r--src/pkg/net/fd_windows.go655
-rw-r--r--src/pkg/net/iprawsock.go6
-rw-r--r--src/pkg/net/ipsock.go29
-rw-r--r--src/pkg/net/multicast_test.go62
-rw-r--r--src/pkg/net/net.go3
-rw-r--r--src/pkg/net/parse.go10
-rw-r--r--src/pkg/net/textproto/Makefile1
-rw-r--r--src/pkg/net/textproto/header.go43
-rw-r--r--src/pkg/net/textproto/reader.go16
-rw-r--r--src/pkg/net/textproto/reader_test.go8
-rw-r--r--src/pkg/net/udpsock.go41
12 files changed, 509 insertions, 393 deletions
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go
index 03b9d87be..1cf8e7915 100644
--- a/src/pkg/net/dial.go
+++ b/src/pkg/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/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index d9c83831d..63a8fbc44 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/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/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go
index 241be1509..81a918ce5 100644
--- a/src/pkg/net/iprawsock.go
+++ b/src/pkg/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/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go
index 4ba6a55b9..ae4204b48 100644
--- a/src/pkg/net/ipsock.go
+++ b/src/pkg/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/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go
new file mode 100644
index 000000000..32fdec85b
--- /dev/null
+++ b/src/pkg/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/src/pkg/net/net.go b/src/pkg/net/net.go
index c0c1c3b8a..04a898a9a 100644
--- a/src/pkg/net/net.go
+++ b/src/pkg/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/src/pkg/net/parse.go b/src/pkg/net/parse.go
index 605f3110b..2bc0db465 100644
--- a/src/pkg/net/parse.go
+++ b/src/pkg/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/src/pkg/net/textproto/Makefile b/src/pkg/net/textproto/Makefile
index 7897fa711..cadf3ab69 100644
--- a/src/pkg/net/textproto/Makefile
+++ b/src/pkg/net/textproto/Makefile
@@ -6,6 +6,7 @@ include ../../../Make.inc
TARG=net/textproto
GOFILES=\
+ header.go\
pipeline.go\
reader.go\
textproto.go\
diff --git a/src/pkg/net/textproto/header.go b/src/pkg/net/textproto/header.go
new file mode 100644
index 000000000..288deb2ce
--- /dev/null
+++ b/src/pkg/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/src/pkg/net/textproto/reader.go b/src/pkg/net/textproto/reader.go
index c8e34b758..ac1278689 100644
--- a/src/pkg/net/textproto/reader.go
+++ b/src/pkg/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/src/pkg/net/textproto/reader_test.go b/src/pkg/net/textproto/reader_test.go
index 2cecbc75f..0658e58b8 100644
--- a/src/pkg/net/textproto/reader_test.go
+++ b/src/pkg/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/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go
index 0270954c1..f9274493e 100644
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/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
+}