// Package socktest provides utilities for socket testing.
package socktest
-import "sync"
+import (
+ "fmt"
+ "sync"
+)
func switchInit(sw *Switch) {
sw.fltab = make(map[FilterType]Filter)
type Status struct {
Cookie Cookie
Err error // error status of socket system call
- SocketErr int // error status of socket by SO_ERROR
+ SocketErr error // error status of socket by SO_ERROR
+}
+
+func (so Status) String() string {
+ return fmt.Sprintf("(%s, %s, %s): syscallerr=%v, socketerr=%v", familyString(so.Cookie.Family()), typeString(so.Cookie.Type()), protocolString(so.Cookie.Protocol()), so.Err, so.SocketErr)
}
// A Stat represents a per-cookie socket statistics.
Protocol int // protocol number
Opened uint64 // number of sockets opened
- Accepted uint64 // number of sockets accepted
Connected uint64 // number of sockets connected
+ Listened uint64 // number of sockets listened
+ Accepted uint64 // number of sockets accepted
Closed uint64 // number of sockets closed
+
+ OpenFailed uint64 // number of sockets open failed
+ ConnectFailed uint64 // number of sockets connect failed
+ ListenFailed uint64 // number of sockets listen failed
+ AcceptFailed uint64 // number of sockets accept failed
+ CloseFailed uint64 // number of sockets close failed
+}
+
+func (st Stat) String() string {
+ return fmt.Sprintf("(%s, %s, %s): opened=%d, connected=%d, listened=%d, accepted=%d, closed=%d, openfailed=%d, connectfailed=%d, listenfailed=%d, acceptfailed=%d, closefailed=%d", familyString(st.Family), typeString(st.Type), protocolString(st.Protocol), st.Opened, st.Connected, st.Listened, st.Accepted, st.Closed, st.OpenFailed, st.ConnectFailed, st.ListenFailed, st.AcceptFailed, st.CloseFailed)
}
type stats map[Cookie]*Stat
const (
FilterSocket FilterType = iota // for Socket
- FilterAccept // for Accept or Accept4
FilterConnect // for Connect or ConnectEx
+ FilterListen // for Listen
+ FilterAccept // for Accept or Accept4
FilterGetsockoptInt // for GetsockoptInt
FilterClose // for Close or Closesocket
)
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !plan9
+
+package socktest
+
+import (
+ "fmt"
+ "syscall"
+)
+
+func familyString(family int) string {
+ switch family {
+ case syscall.AF_INET:
+ return "inet4"
+ case syscall.AF_INET6:
+ return "inet6"
+ case syscall.AF_UNIX:
+ return "local"
+ default:
+ return fmt.Sprintf("%d", family)
+ }
+}
+
+func typeString(sotype int) string {
+ var s string
+ switch sotype & 0xff {
+ case syscall.SOCK_STREAM:
+ s = "stream"
+ case syscall.SOCK_DGRAM:
+ s = "datagram"
+ case syscall.SOCK_RAW:
+ s = "raw"
+ case syscall.SOCK_SEQPACKET:
+ s = "seqpacket"
+ default:
+ s = fmt.Sprintf("%d", sotype&0xff)
+ }
+ if flags := uint(sotype) & ^uint(0xff); flags != 0 {
+ s += fmt.Sprintf("|%#x", flags)
+ }
+ return s
+}
+
+func protocolString(proto int) string {
+ switch proto {
+ case 0:
+ return "default"
+ case syscall.IPPROTO_TCP:
+ return "tcp"
+ case syscall.IPPROTO_UDP:
+ return "udp"
+ default:
+ return fmt.Sprintf("%d", proto)
+ }
+}
// Sockets maps a socket descriptor to the status of socket.
type Sockets map[int]Status
+
+func familyString(family int) string { return "<nil>" }
+
+func typeString(sotype int) string { return "<nil>" }
+
+func protocolString(proto int) string { return "<nil>" }
return -1, nil, err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).AcceptFailed++
return -1, nil, so.Err
}
- sw.smu.Lock()
nso := sw.addLocked(ns, so.Cookie.Family(), so.Cookie.Type(), so.Cookie.Protocol())
sw.stats.getLocked(nso.Cookie).Accepted++
- sw.smu.Unlock()
return ns, sa, nil
}
return -1, err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).OpenFailed++
return -1, so.Err
}
- sw.smu.Lock()
nso := sw.addLocked(s, family, sotype, proto)
sw.stats.getLocked(nso.Cookie).Opened++
- sw.smu.Unlock()
return s, nil
}
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).CloseFailed++
return so.Err
}
- sw.smu.Lock()
delete(sw.sotab, s)
sw.stats.getLocked(so.Cookie).Closed++
- sw.smu.Unlock()
return nil
}
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ConnectFailed++
return so.Err
}
- sw.smu.Lock()
sw.stats.getLocked(so.Cookie).Connected++
- sw.smu.Unlock()
+ return nil
+}
+
+// Listen wraps syscall.Listen.
+func (sw *Switch) Listen(s, backlog int) (err error) {
+ so := sw.sockso(s)
+ if so == nil {
+ return syscall.Listen(s, backlog)
+ }
+ sw.fmu.RLock()
+ f, _ := sw.fltab[FilterListen]
+ sw.fmu.RUnlock()
+
+ af, err := f.apply(so)
+ if err != nil {
+ return err
+ }
+ so.Err = syscall.Listen(s, backlog)
+ if err = af.apply(so); err != nil {
+ return err
+ }
+
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
+ if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ListenFailed++
+ return so.Err
+ }
+ sw.stats.getLocked(so.Cookie).Listened++
return nil
}
return -1, nil, err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).AcceptFailed++
return -1, nil, so.Err
}
- sw.smu.Lock()
nso := sw.addLocked(ns, so.Cookie.Family(), so.Cookie.Type(), so.Cookie.Protocol())
sw.stats.getLocked(nso.Cookie).Accepted++
- sw.smu.Unlock()
return ns, sa, nil
}
if err != nil {
return -1, err
}
- so.SocketErr, so.Err = syscall.GetsockoptInt(s, level, opt)
+ soerr, so.Err = syscall.GetsockoptInt(s, level, opt)
+ so.SocketErr = syscall.Errno(soerr)
if err = af.apply(so); err != nil {
return -1, err
}
if so.Err != nil {
return -1, so.Err
}
- if opt == syscall.SO_ERROR && (so.SocketErr == 0 || syscall.Errno(so.SocketErr) == syscall.EISCONN) {
+ if opt == syscall.SO_ERROR && (so.SocketErr == syscall.Errno(0) || so.SocketErr == syscall.EISCONN) {
sw.smu.Lock()
sw.stats.getLocked(so.Cookie).Connected++
sw.smu.Unlock()
}
- return so.SocketErr, nil
+ return soerr, nil
}
return syscall.InvalidHandle, err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).OpenFailed++
return syscall.InvalidHandle, so.Err
}
- sw.smu.Lock()
nso := sw.addLocked(s, family, sotype, proto)
sw.stats.getLocked(nso.Cookie).Opened++
- sw.smu.Unlock()
return s, nil
}
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).CloseFailed++
return so.Err
}
- sw.smu.Lock()
delete(sw.sotab, s)
sw.stats.getLocked(so.Cookie).Closed++
- sw.smu.Unlock()
return nil
}
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ConnectFailed++
return so.Err
}
- sw.smu.Lock()
sw.stats.getLocked(so.Cookie).Connected++
- sw.smu.Unlock()
return nil
}
return err
}
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ConnectFailed++
return so.Err
}
- sw.smu.Lock()
sw.stats.getLocked(so.Cookie).Connected++
- sw.smu.Unlock()
+ return nil
+}
+
+// Listen wraps syscall.Listen.
+func (sw *Switch) Listen(s syscall.Handle, backlog int) (err error) {
+ so := sw.sockso(s)
+ if so == nil {
+ return syscall.Listen(s, backlog)
+ }
+ sw.fmu.RLock()
+ f, _ := sw.fltab[FilterListen]
+ sw.fmu.RUnlock()
+
+ af, err := f.apply(so)
+ if err != nil {
+ return err
+ }
+ so.Err = syscall.Listen(s, backlog)
+ if err = af.apply(so); err != nil {
+ return err
+ }
+
+ sw.smu.Lock()
+ defer sw.smu.Unlock()
+ if so.Err != nil {
+ sw.stats.getLocked(so.Cookie).ListenFailed++
+ return so.Err
+ }
+ sw.stats.getLocked(so.Cookie).Listened++
return nil
}