Update dependencies

This commit is contained in:
Jakob Borg 2014-03-22 21:58:10 +01:00
parent 145c8e4063
commit 75cfa4c33e
69 changed files with 2605 additions and 917 deletions

11
Godeps/Godeps.json generated
View File

@ -7,8 +7,8 @@
"Deps": [
{
"ImportPath": "code.google.com/p/go.net/ipv6",
"Comment": "null-88",
"Rev": "55437409069bb181ad562b1ad1e5f361c44aff17"
"Comment": "null-117",
"Rev": "c17ad62118ea511e1051721b429779fa40bddc74"
},
{
"ImportPath": "code.google.com/p/go.text/transform",
@ -22,15 +22,16 @@
},
{
"ImportPath": "github.com/calmh/ini",
"Rev": "1020b6d8618a7dc6031cda4a7782324eac346875"
"Rev": "386c4240a9684d91d9ec4d93651909b49c7269e1"
},
{
"ImportPath": "github.com/codegangsta/inject",
"Rev": "37512bbe41b4cc579cdd706742efc7bf34f94b06"
"Rev": "9aea7a2fa5b79ef7fc00f63a575e72df33b4e886"
},
{
"ImportPath": "github.com/codegangsta/martini",
"Rev": "f86ef0561cc5aa2801d41a7277fd480272ad1556"
"Comment": "v0.1-142-g8659df7",
"Rev": "8659df7a51aebe6c6120268cd5a8b4c34fa8441a"
}
]
}

View File

@ -12,7 +12,6 @@ import (
)
var (
errNotSupported = errors.New("not supported")
errMissingAddress = errors.New("missing address")
errInvalidConnType = errors.New("invalid conn type")
errNoSuchInterface = errors.New("no such interface")

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin
package ipv6
import (
@ -47,23 +49,23 @@ func newControlMessage(opt *rawOpt) (oob []byte) {
l += syscall.CmsgSpace(4)
}
if opt.isset(pktinfo) {
l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
l += syscall.CmsgSpace(sysSizeofPacketInfo)
}
if l > 0 {
oob = make([]byte, l)
if opt.isset(FlagHopLimit) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_2292HOPLIMIT
m.Type = sysSockopt2292HopLimit
m.SetLen(syscall.CmsgLen(4))
off += syscall.CmsgSpace(4)
}
if opt.isset(pktinfo) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_2292PKTINFO
m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
m.Type = sysSockopt2292PacketInfo
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
off += syscall.CmsgSpace(sysSizeofPacketInfo)
}
}
return
@ -83,12 +85,12 @@ func parseControlMessage(b []byte) (*ControlMessage, error) {
continue
}
switch m.Header.Type {
case syscall.IPV6_2292HOPLIMIT:
case sysSockopt2292HopLimit:
cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IPV6_2292PKTINFO:
pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(pi.Ifindex)
cm.Dst = pi.Addr[:]
case sysSockopt2292PacketInfo:
pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(pi.IfIndex)
cm.Dst = pi.IP[:]
}
}
return cm, nil
@ -105,7 +107,7 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
pion := false
if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 {
pion = true
l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
l += syscall.CmsgSpace(sysSizeofPacketInfo)
}
if len(cm.NextHop) == net.IPv6len {
l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
@ -115,7 +117,7 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if cm.HopLimit > 0 {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_2292HOPLIMIT
m.Type = sysSockopt2292HopLimit
m.SetLen(syscall.CmsgLen(4))
data := oob[off+syscall.CmsgLen(0):]
*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
@ -124,26 +126,24 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if pion {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_2292PKTINFO
m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
m.Type = sysSockopt2292PacketInfo
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
copy(pi.Addr[:], ip)
copy(pi.IP[:], ip)
}
if cm.IfIndex != 0 {
pi.Ifindex = uint32(cm.IfIndex)
pi.IfIndex = uint32(cm.IfIndex)
}
off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
off += syscall.CmsgSpace(sysSizeofPacketInfo)
}
if len(cm.NextHop) == net.IPv6len {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_2292NEXTHOP
m.Type = sysSockopt2292NextHop
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
sa.Len = syscall.SizeofSockaddrInet6
sa.Family = syscall.AF_INET6
copy(sa.Addr[:], cm.NextHop)
setSockaddr(sa, cm.NextHop, cm.IfIndex)
off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
}
}

View File

@ -1,217 +0,0 @@
// Copyright 2013 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 ipv6
import (
"net"
"os"
"syscall"
"unsafe"
)
const (
// See /usr/include/linux/in6.h.
syscall_IPV6_RECVPATHMTU = syscall.IPV6_DSTOPTS + 1 + iota
syscall_IPV6_PATHMTU
syscall_IPV6_DONTFRAG
)
const pktinfo = FlagDst | FlagInterface
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.Lock()
defer opt.Unlock()
if cf&FlagTrafficClass != 0 {
if err := setIPv6ReceiveTrafficClass(fd, on); err != nil {
return err
}
if on {
opt.set(FlagTrafficClass)
} else {
opt.clear(FlagTrafficClass)
}
}
if cf&FlagHopLimit != 0 {
if err := setIPv6ReceiveHopLimit(fd, on); err != nil {
return err
}
if on {
opt.set(FlagHopLimit)
} else {
opt.clear(FlagHopLimit)
}
}
if cf&pktinfo != 0 {
if err := setIPv6ReceivePacketInfo(fd, on); err != nil {
return err
}
if on {
opt.set(cf & pktinfo)
} else {
opt.clear(cf & pktinfo)
}
}
if cf&FlagPathMTU != 0 {
if err := setIPv6ReceivePathMTU(fd, on); err != nil {
return err
}
if on {
opt.set(FlagPathMTU)
} else {
opt.clear(FlagPathMTU)
}
}
return nil
}
func newControlMessage(opt *rawOpt) (oob []byte) {
opt.Lock()
defer opt.Unlock()
l, off := 0, 0
if opt.isset(FlagTrafficClass) {
l += syscall.CmsgSpace(4)
}
if opt.isset(FlagHopLimit) {
l += syscall.CmsgSpace(4)
}
if opt.isset(pktinfo) {
l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
}
if opt.isset(FlagPathMTU) {
l += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo)
}
if l > 0 {
oob = make([]byte, l)
if opt.isset(FlagTrafficClass) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_RECVTCLASS
m.SetLen(syscall.CmsgLen(4))
off += syscall.CmsgSpace(4)
}
if opt.isset(FlagHopLimit) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_RECVHOPLIMIT
m.SetLen(syscall.CmsgLen(4))
off += syscall.CmsgSpace(4)
}
if opt.isset(pktinfo) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_RECVPKTINFO
m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
}
if opt.isset(FlagPathMTU) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall_IPV6_RECVPATHMTU
m.SetLen(syscall.CmsgLen(syscall.SizeofIPv6MTUInfo))
off += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo)
}
}
return
}
func parseControlMessage(b []byte) (*ControlMessage, error) {
if len(b) == 0 {
return nil, nil
}
cmsgs, err := syscall.ParseSocketControlMessage(b)
if err != nil {
return nil, os.NewSyscallError("parse socket control message", err)
}
cm := &ControlMessage{}
for _, m := range cmsgs {
if m.Header.Level != ianaProtocolIPv6 {
continue
}
switch m.Header.Type {
case syscall.IPV6_TCLASS:
cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IPV6_HOPLIMIT:
cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IPV6_PKTINFO:
pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0]))
cm.Dst = pi.Addr[:]
cm.IfIndex = int(pi.Ifindex)
case syscall_IPV6_PATHMTU:
mi := (*syscall.IPv6MTUInfo)(unsafe.Pointer(&m.Data[0]))
cm.Dst = mi.Addr.Addr[:]
cm.IfIndex = int(mi.Addr.Scope_id)
cm.MTU = int(mi.Mtu)
}
}
return cm, nil
}
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if cm == nil {
return
}
l, off := 0, 0
if cm.TrafficClass > 0 {
l += syscall.CmsgSpace(4)
}
if cm.HopLimit > 0 {
l += syscall.CmsgSpace(4)
}
pion := false
if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 {
pion = true
l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
}
if len(cm.NextHop) == net.IPv6len {
l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
}
if l > 0 {
oob = make([]byte, l)
if cm.TrafficClass > 0 {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_TCLASS
m.SetLen(syscall.CmsgLen(4))
data := oob[off+syscall.CmsgLen(0):]
*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass)
off += syscall.CmsgSpace(4)
}
if cm.HopLimit > 0 {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_HOPLIMIT
m.SetLen(syscall.CmsgLen(4))
data := oob[off+syscall.CmsgLen(0):]
*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
off += syscall.CmsgSpace(4)
}
if pion {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_PKTINFO
m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
copy(pi.Addr[:], ip)
}
if cm.IfIndex != 0 {
pi.Ifindex = uint32(cm.IfIndex)
}
off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
}
if len(cm.NextHop) == net.IPv6len {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_NEXTHOP
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
sa.Family = syscall.AF_INET6
copy(sa.Addr[:], cm.NextHop)
sa.Scope_id = uint32(cm.IfIndex)
off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
}
}
return
}

View File

@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ipv6
// +build dragonfly plan9 solaris
import "syscall"
package ipv6
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}
func newControlMessage(opt *rawOpt) (oob []byte) {
@ -18,7 +18,7 @@ func newControlMessage(opt *rawOpt) (oob []byte) {
func parseControlMessage(b []byte) (*ControlMessage, error) {
// TODO(mikio): Implement this
return nil, syscall.EPLAN9
return nil, errOpNoSupport
}
func marshalControlMessage(cm *ControlMessage) (oob []byte) {

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build freebsd netbsd openbsd
// +build freebsd linux netbsd openbsd
package ipv6
@ -72,40 +72,40 @@ func newControlMessage(opt *rawOpt) (oob []byte) {
l += syscall.CmsgSpace(4)
}
if opt.isset(pktinfo) {
l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
l += syscall.CmsgSpace(sysSizeofPacketInfo)
}
if opt.isset(FlagPathMTU) {
l += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo)
l += syscall.CmsgSpace(sysSizeofMTUInfo)
}
if l > 0 {
oob = make([]byte, l)
if opt.isset(FlagTrafficClass) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_RECVTCLASS
m.Type = sysSockoptReceiveTrafficClass
m.SetLen(syscall.CmsgLen(4))
off += syscall.CmsgSpace(4)
}
if opt.isset(FlagHopLimit) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_RECVHOPLIMIT
m.Type = sysSockoptReceiveHopLimit
m.SetLen(syscall.CmsgLen(4))
off += syscall.CmsgSpace(4)
}
if opt.isset(pktinfo) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_RECVPKTINFO
m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
m.Type = sysSockoptReceivePacketInfo
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
off += syscall.CmsgSpace(sysSizeofPacketInfo)
}
if opt.isset(FlagPathMTU) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_RECVPATHMTU
m.SetLen(syscall.CmsgLen(syscall.SizeofIPv6MTUInfo))
off += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo)
m.Type = sysSockoptReceivePathMTU
m.SetLen(syscall.CmsgLen(sysSizeofMTUInfo))
off += syscall.CmsgSpace(sysSizeofMTUInfo)
}
}
return
@ -125,19 +125,19 @@ func parseControlMessage(b []byte) (*ControlMessage, error) {
continue
}
switch m.Header.Type {
case syscall.IPV6_TCLASS:
case sysSockoptTrafficClass:
cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IPV6_HOPLIMIT:
case sysSockoptHopLimit:
cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IPV6_PKTINFO:
pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0]))
cm.Dst = pi.Addr[:]
cm.IfIndex = int(pi.Ifindex)
case syscall.IPV6_PATHMTU:
mi := (*syscall.IPv6MTUInfo)(unsafe.Pointer(&m.Data[0]))
case sysSockoptPacketInfo:
pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
cm.Dst = pi.IP[:]
cm.IfIndex = int(pi.IfIndex)
case sysSockoptPathMTU:
mi := (*sysMTUInfo)(unsafe.Pointer(&m.Data[0]))
cm.Dst = mi.Addr.Addr[:]
cm.IfIndex = int(mi.Addr.Scope_id)
cm.MTU = int(mi.Mtu)
cm.MTU = int(mi.MTU)
}
}
return cm, nil
@ -157,7 +157,7 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
pion := false
if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 {
pion = true
l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
l += syscall.CmsgSpace(sysSizeofPacketInfo)
}
if len(cm.NextHop) == net.IPv6len {
l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
@ -167,7 +167,7 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if cm.TrafficClass > 0 {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_TCLASS
m.Type = sysSockoptTrafficClass
m.SetLen(syscall.CmsgLen(4))
data := oob[off+syscall.CmsgLen(0):]
*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass)
@ -176,7 +176,7 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if cm.HopLimit > 0 {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_HOPLIMIT
m.Type = sysSockoptHopLimit
m.SetLen(syscall.CmsgLen(4))
data := oob[off+syscall.CmsgLen(0):]
*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
@ -185,27 +185,24 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if pion {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_PKTINFO
m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
m.Type = sysSockoptPacketInfo
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
copy(pi.Addr[:], ip)
copy(pi.IP[:], ip)
}
if cm.IfIndex != 0 {
pi.Ifindex = uint32(cm.IfIndex)
pi.IfIndex = uint32(cm.IfIndex)
}
off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
off += syscall.CmsgSpace(sysSizeofPacketInfo)
}
if len(cm.NextHop) == net.IPv6len {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIPv6
m.Type = syscall.IPV6_NEXTHOP
m.Type = sysSockoptNextHop
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
sa.Len = syscall.SizeofSockaddrInet6
sa.Family = syscall.AF_INET6
copy(sa.Addr[:], cm.NextHop)
sa.Scope_id = uint32(cm.IfIndex)
setSockaddr(sa, cm.NextHop, cm.IfIndex)
off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
}
}

View File

@ -1,42 +0,0 @@
// Copyright 2013 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 ipv6
import (
"sync"
"testing"
)
func TestControlFlags(t *testing.T) {
tf := FlagInterface | FlagPathMTU
opt := rawOpt{cflags: tf | FlagHopLimit}
// This loop runs methods of raw.Opt concurrently for testing
// concurrent access to the rawOpt. The first entry shold be
// opt.set and the last entry should be opt.clear.
tfns := []func(ControlFlags){opt.set, opt.clear, opt.clear}
ch := make(chan bool)
var wg sync.WaitGroup
for i, fn := range tfns {
wg.Add(1)
go func(i int, fn func(ControlFlags)) {
defer wg.Done()
switch i {
case 0:
close(ch)
case len(tfns) - 1:
<-ch
}
opt.Lock()
defer opt.Unlock()
fn(tf)
}(i, fn)
}
wg.Wait()
if opt.isset(tf) {
t.Fatalf("got %#x; expected %#x", opt.cflags, FlagHopLimit)
}
}

View File

@ -2,53 +2,52 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build dragonfly plan9 solaris
package ipv6
import (
"net"
"syscall"
)
import "net"
// MulticastHopLimit returns the hop limit field value for outgoing
// multicast packets.
func (c *dgramOpt) MulticastHopLimit() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
return 0, errOpNoSupport
}
// SetMulticastHopLimit sets the hop limit field value for future
// outgoing multicast packets.
func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}
// MulticastInterface returns the default interface for multicast
// packet transmissions.
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
// TODO(mikio): Implement this
return nil, syscall.EPLAN9
return nil, errOpNoSupport
}
// SetMulticastInterface sets the default interface for future
// multicast packet transmissions.
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}
// MulticastLoopback reports whether transmitted multicast packets
// should be copied and send back to the originator.
func (c *dgramOpt) MulticastLoopback() (bool, error) {
// TODO(mikio): Implement this
return false, syscall.EPLAN9
return false, errOpNoSupport
}
// SetMulticastLoopback sets whether transmitted multicast packets
// should be copied and send back to the originator.
func (c *dgramOpt) SetMulticastLoopback(on bool) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}
// JoinGroup joins the group address group on the interface ifi.
@ -57,13 +56,13 @@ func (c *dgramOpt) SetMulticastLoopback(on bool) error {
// platforms and sometimes it might require routing configuration.
func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}
// LeaveGroup leaves the group address group on the interface ifi.
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}
// Checksum reports whether the kernel will compute, store or verify a
@ -72,7 +71,7 @@ func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
// field is located.
func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
// TODO(mikio): Implement this
return false, 0, syscall.EPLAN9
return false, 0, errOpNoSupport
}
// SetChecksum enables the kernel checksum processing. If on is ture,
@ -80,17 +79,17 @@ func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
// checksum field is located.
func (c *dgramOpt) SetChecksum(on bool, offset int) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}
// ICMPFilter returns an ICMP filter.
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
// TODO(mikio): Implement this
return nil, syscall.EPLAN9
return nil, errOpNoSupport
}
// SetICMPFilter deploys the ICMP filter.
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}

View File

@ -2,33 +2,33 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ipv6
// +build dragonfly plan9 solaris
import "syscall"
package ipv6
// TrafficClass returns the traffic class field value for outgoing
// packets.
func (c *genericOpt) TrafficClass() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
return 0, errOpNoSupport
}
// SetTrafficClass sets the traffic class field value for future
// outgoing packets.
func (c *genericOpt) SetTrafficClass(tclass int) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}
// HopLimit returns the hop limit field value for outgoing packets.
func (c *genericOpt) HopLimit() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
return 0, errOpNoSupport
}
// SetHopLimit sets the hop limit field value for future outgoing
// packets.
func (c *genericOpt) SetHopLimit(hoplim int) error {
// TODO(mikio): Implement this
return syscall.EPLAN9
return errOpNoSupport
}

View File

@ -4,7 +4,12 @@
package ipv6
import "net"
import (
"errors"
"net"
)
var errOpNoSupport = errors.New("operation not supported")
func boolint(b bool) int {
if b {

View File

@ -2,21 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ipv6
// +build dragonfly plan9 solaris
import "syscall"
package ipv6
func (c *genericOpt) sysfd() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
return 0, errOpNoSupport
}
func (c *dgramOpt) sysfd() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
return 0, errOpNoSupport
}
func (c *payloadHandler) sysfd() (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
return 0, errOpNoSupport
}

View File

@ -21,26 +21,27 @@ func (typ ICMPType) String() string {
// packets.
type ICMPFilter struct {
mu sync.RWMutex
rawICMPFilter
sysICMPFilter
}
// Set sets the ICMP type and filter action to the filter.
func (f *ICMPFilter) Set(typ ICMPType, block bool) {
f.mu.Lock()
defer f.mu.Unlock()
f.set(typ, block)
f.mu.Unlock()
}
// SetAll sets the filter action to the filter.
func (f *ICMPFilter) SetAll(block bool) {
f.mu.Lock()
defer f.mu.Unlock()
f.setAll(block)
f.mu.Unlock()
}
// WillBlock reports whether the ICMP type will be blocked.
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
f.mu.RLock()
defer f.mu.RUnlock()
return f.willBlock(typ)
ok := f.willBlock(typ)
f.mu.RUnlock()
return ok
}

View File

@ -6,13 +6,11 @@
package ipv6
import "syscall"
type rawICMPFilter struct {
syscall.ICMPv6Filter
type sysICMPFilter struct {
Filt [8]uint32
}
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
if block {
f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
} else {
@ -20,7 +18,7 @@ func (f *rawICMPFilter) set(typ ICMPType, block bool) {
}
}
func (f *rawICMPFilter) setAll(block bool) {
func (f *sysICMPFilter) setAll(block bool) {
for i := range f.Filt {
if block {
f.Filt[i] = 0
@ -30,6 +28,6 @@ func (f *rawICMPFilter) setAll(block bool) {
}
}
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
}

View File

@ -4,13 +4,11 @@
package ipv6
import "syscall"
type rawICMPFilter struct {
syscall.ICMPv6Filter
type sysICMPFilter struct {
Data [8]uint32
}
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
if block {
f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
} else {
@ -18,7 +16,7 @@ func (f *rawICMPFilter) set(typ ICMPType, block bool) {
}
}
func (f *rawICMPFilter) setAll(block bool) {
func (f *sysICMPFilter) setAll(block bool) {
for i := range f.Data {
if block {
f.Data[i] = 1<<32 - 1
@ -28,6 +26,6 @@ func (f *rawICMPFilter) setAll(block bool) {
}
}
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
}

View File

@ -2,21 +2,23 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build dragonfly plan9 solaris
package ipv6
type rawICMPFilter struct {
type sysICMPFilter struct {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) setAll(block bool) {
func (f *sysICMPFilter) setAll(block bool) {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
// TODO(mikio): Implement this
return false
}

View File

@ -14,9 +14,27 @@ import (
"testing"
)
var icmpStringTests = []struct {
in ipv6.ICMPType
out string
}{
{ipv6.ICMPTypeDestinationUnreachable, "destination unreachable"},
{256, "<nil>"},
}
func TestICMPString(t *testing.T) {
for _, tt := range icmpStringTests {
s := tt.in.String()
if s != tt.out {
t.Errorf("got %s; expected %s", s, tt.out)
}
}
}
func TestICMPFilter(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
@ -49,7 +67,7 @@ func TestICMPFilter(t *testing.T) {
func TestSetICMPFilter(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {

View File

@ -4,19 +4,19 @@
package ipv6
type rawICMPFilter struct {
type sysICMPFilter struct {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) set(typ ICMPType, block bool) {
func (f *sysICMPFilter) set(typ ICMPType, block bool) {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) setAll(block bool) {
func (f *sysICMPFilter) setAll(block bool) {
// TODO(mikio): Implement this
}
func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
// TODO(mikio): Implement this
return false
}

View File

@ -7,8 +7,22 @@ package ipv6_test
import (
"code.google.com/p/go.net/ipv6"
"errors"
"net"
)
const (
ipv6PseudoHeaderLen = 2*net.IPv6len + 8
ianaProtocolIPv6ICMP = 58
)
func ipv6PseudoHeader(src, dst net.IP, nextHeader int) []byte {
b := make([]byte, ipv6PseudoHeaderLen)
copy(b[:net.IPv6len], src)
copy(b[net.IPv6len:], dst)
b[len(b)-1] = byte(nextHeader)
return b
}
// icmpMessage represents an ICMP message.
type icmpMessage struct {
Type ipv6.ICMPType // type
@ -25,8 +39,11 @@ type icmpMessageBody interface {
// Marshal returns the binary enconding of the ICMP echo request or
// reply message m.
func (m *icmpMessage) Marshal() ([]byte, error) {
func (m *icmpMessage) Marshal(psh []byte) ([]byte, error) {
b := []byte{byte(m.Type), byte(m.Code), 0, 0}
if psh != nil {
b = append(psh, b...)
}
if m.Body != nil && m.Body.Len() != 0 {
mb, err := m.Body.Marshal()
if err != nil {
@ -34,10 +51,11 @@ func (m *icmpMessage) Marshal() ([]byte, error) {
}
b = append(b, mb...)
}
switch m.Type {
case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
if psh == nil {
return b, nil
}
off, l := 2*net.IPv6len, len(b)-len(psh)
b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
csumcv := len(b) - 1 // checksum coverage
s := uint32(0)
for i := 0; i < csumcv; i += 2 {
@ -50,9 +68,9 @@ func (m *icmpMessage) Marshal() ([]byte, error) {
s = s + s>>16
// Place checksum back in header; using ^= avoids the
// assumption the checksum bytes are zero.
b[2] ^= byte(^s)
b[3] ^= byte(^s >> 8)
return b, nil
b[len(psh)+2] ^= byte(^s)
b[len(psh)+3] ^= byte(^s >> 8)
return b[len(psh):], nil
}
// parseICMPMessage parses b as an ICMP message.

View File

@ -86,25 +86,3 @@ func acceptor(t *testing.T, ln net.Listener, done chan<- bool) {
}
c.Close()
}
func transponder(t *testing.T, ln net.Listener, done chan<- bool) {
defer func() { done <- true }()
c, err := ln.Accept()
if err != nil {
t.Errorf("net.Listener.Accept failed: %v", err)
return
}
defer c.Close()
b := make([]byte, 128)
n, err := c.Read(b)
if err != nil {
t.Errorf("net.Conn.Read failed: %v", err)
return
}
if _, err := c.Write(b[:n]); err != nil {
t.Errorf("net.Conn.Write failed: %v", err)
return
}
}

View File

@ -5,11 +5,13 @@
package ipv6_test
import (
"bytes"
"code.google.com/p/go.net/ipv6"
"net"
"os"
"runtime"
"testing"
"time"
)
func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
@ -17,7 +19,7 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
case "freebsd": // due to a bug on loopback marking
// See http://www.freebsd.org/cgi/query-pr.cgi?pr=180065.
t.Skipf("not supported on %q", runtime.GOOS)
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -44,33 +46,49 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
}
p := ipv6.NewPacketConn(c)
defer p.Close()
if err := p.JoinGroup(ifi, dst); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
if err := p.SetMulticastInterface(ifi); err != nil {
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
}
if _, err := p.MulticastInterface(); err != nil {
t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err)
}
if err := p.SetMulticastLoopback(true); err != nil {
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
}
if _, err := p.MulticastLoopback(); err != nil {
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
}
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
Src: net.IPv6loopback,
IfIndex: ifi.Index,
}
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
wb := []byte("HELLO-R-U-THERE")
for i, toggle := range []bool{true, false, true} {
if err := p.SetControlMessage(cf, toggle); err != nil {
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
cm.HopLimit = i + 1
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err)
}
b := make([]byte, 128)
if _, cm, _, err := p.ReadFrom(b); err != nil {
cm.HopLimit = i + 1
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
} else if n != len(wb) {
t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
}
rb := make([]byte, 128)
if n, cm, _, err := p.ReadFrom(rb); err != nil {
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
} else if !bytes.Equal(rb[:n], wb) {
t.Fatalf("got %v; expected %v", rb[:n], wb)
} else {
t.Logf("rcvd cmsg: %v", cm)
}
@ -79,7 +97,7 @@ func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -104,22 +122,31 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
t.Fatalf("net.ResolveIPAddr failed: %v", err)
}
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
p := ipv6.NewPacketConn(c)
defer p.Close()
if err := p.JoinGroup(ifi, dst); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
if err := p.SetMulticastInterface(ifi); err != nil {
t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
}
if _, err := p.MulticastInterface(); err != nil {
t.Fatalf("ipv6.PacketConn.MulticastInterface failed: %v", err)
}
if err := p.SetMulticastLoopback(true); err != nil {
t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
}
if _, err := p.MulticastLoopback(); err != nil {
t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
}
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
Src: net.IPv6loopback,
IfIndex: ifi.Index,
}
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
var f ipv6.ICMPFilter
f.SetAll(true)
@ -128,30 +155,47 @@ func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
}
var psh []byte
for i, toggle := range []bool{true, false, true} {
if toggle {
psh = nil
if err := p.SetChecksum(true, 2); err != nil {
t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err)
}
} else {
psh = pshicmp
// Some platforms never allow to disable the
// kernel checksum processing.
p.SetChecksum(false, -1)
}
wb, err := (&icmpMessage{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmpEcho{
ID: os.Getpid() & 0xffff, Seq: i + 1,
Data: []byte("HELLO-R-U-THERE"),
},
}).Marshal()
}).Marshal(psh)
if err != nil {
t.Fatalf("icmpMessage.Marshal failed: %v", err)
}
if err := p.SetControlMessage(cf, toggle); err != nil {
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
cm.HopLimit = i + 1
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
t.Fatalf("ipv6.PacketConn.SetDeadline failed: %v", err)
}
b := make([]byte, 128)
if n, cm, _, err := p.ReadFrom(b); err != nil {
cm.HopLimit = i + 1
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
} else if n != len(wb) {
t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
}
rb := make([]byte, 128)
if n, cm, _, err := p.ReadFrom(rb); err != nil {
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
} else {
t.Logf("rcvd cmsg: %v", cm)
if m, err := parseICMPMessage(b[:n]); err != nil {
if m, err := parseICMPMessage(rb[:n]); err != nil {
t.Fatalf("parseICMPMessage failed: %v", err)
} else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 {
t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0)

View File

@ -21,7 +21,7 @@ var udpMultipleGroupListenerTests = []net.Addr{
func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -59,9 +59,9 @@ func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
}
}
func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -113,14 +113,14 @@ func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
t.Skip("ipv6 is not supported")
}
gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
type ml struct {
c *ipv6.PacketConn
ifi *net.Interface
@ -142,13 +142,13 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
}
defer c.Close()
p := ipv6.NewPacketConn(c)
if err := p.JoinGroup(&ifi, gaddr); err != nil {
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
mlt = append(mlt, &ml{p, &ift[i]})
}
for _, m := range mlt {
if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
}
}
@ -156,7 +156,7 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -166,14 +166,14 @@ func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
t.Skip("must be root")
}
c, err := net.ListenPacket("ip6:ipv6-icmp", "::")
c, err := net.ListenPacket("ip6:ipv6-icmp", "::") // wildcard address
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
var mift []*net.Interface
ift, err := net.Interfaces()
@ -184,14 +184,60 @@ func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
if _, ok := isMulticastAvailable(&ifi); !ok {
continue
}
if err := p.JoinGroup(&ifi, gaddr); err != nil {
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
mift = append(mift, &ift[i])
}
for _, ifi := range mift {
if err := p.LeaveGroup(ifi, gaddr); err != nil {
if err := p.LeaveGroup(ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", ifi, err)
}
}
}
func TestIPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "darwin", "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
t.Skip("ipv6 is not supported")
}
if os.Getuid() != 0 {
t.Skip("must be root")
}
gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
type ml struct {
c *ipv6.PacketConn
ifi *net.Interface
}
var mlt []*ml
ift, err := net.Interfaces()
if err != nil {
t.Fatalf("net.Interfaces failed: %v", err)
}
for i, ifi := range ift {
ip, ok := isMulticastAvailable(&ifi)
if !ok {
continue
}
c, err := net.ListenPacket("ip6:ipv6-icmp", fmt.Sprintf("%s%%%s", ip.String(), ifi.Name)) // unicast address
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
if err := p.JoinGroup(&ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
}
mlt = append(mlt, &ml{p, &ift[i]})
}
for _, m := range mlt {
if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
}
}
}

View File

@ -22,7 +22,7 @@ var packetConnMulticastSocketOptionTests = []struct {
func TestPacketConnMulticastSocketOptions(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {

View File

@ -0,0 +1,168 @@
// Copyright 2013 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 ipv6_test
import (
"bytes"
"code.google.com/p/go.net/ipv6"
"net"
"runtime"
"sync"
"testing"
)
func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
c, err := net.ListenPacket("udp6", "[::1]:0")
if err != nil {
return nil, nil, err
}
dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
if err != nil {
c.Close()
return nil, nil, err
}
return c, dst, nil
}
func BenchmarkReadWriteNetUDP(b *testing.B) {
c, dst, err := benchmarkUDPListener()
if err != nil {
b.Fatalf("benchmarkUDPListener failed: %v", err)
}
defer c.Close()
wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchmarkReadWriteNetUDP(b, c, wb, rb, dst)
}
}
func benchmarkReadWriteNetUDP(b *testing.B, c net.PacketConn, wb, rb []byte, dst net.Addr) {
if _, err := c.WriteTo(wb, dst); err != nil {
b.Fatalf("net.PacketConn.WriteTo failed: %v", err)
}
if _, _, err := c.ReadFrom(rb); err != nil {
b.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
}
}
func BenchmarkReadWriteIPv6UDP(b *testing.B) {
c, dst, err := benchmarkUDPListener()
if err != nil {
b.Fatalf("benchmarkUDPListener failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
if err := p.SetControlMessage(cf, true); err != nil {
b.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
ifi := loopbackInterface()
wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchmarkReadWriteIPv6UDP(b, p, wb, rb, dst, ifi)
}
}
func benchmarkReadWriteIPv6UDP(b *testing.B, p *ipv6.PacketConn, wb, rb []byte, dst net.Addr, ifi *net.Interface) {
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
HopLimit: 1,
}
if ifi != nil {
cm.IfIndex = ifi.Index
}
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
b.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
} else if n != len(wb) {
b.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
}
if _, _, _, err := p.ReadFrom(rb); err != nil {
b.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
}
}
func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
switch runtime.GOOS {
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
t.Skip("ipv6 is not supported")
}
c, err := net.ListenPacket("udp6", "[::1]:0")
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
defer p.Close()
dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
if err != nil {
t.Fatalf("net.ResolveUDPAddr failed: %v", err)
}
ifi := loopbackInterface()
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
wb := []byte("HELLO-R-U-THERE")
var wg sync.WaitGroup
reader := func() {
defer wg.Done()
rb := make([]byte, 128)
if n, cm, _, err := p.ReadFrom(rb); err != nil {
t.Errorf("ipv6.PacketConn.ReadFrom failed: %v", err)
return
} else if !bytes.Equal(rb[:n], wb) {
t.Errorf("got %v; expected %v", rb[:n], wb)
return
} else {
t.Logf("rcvd cmsg: %v", cm)
}
}
writer := func(toggle bool) {
defer wg.Done()
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
Src: net.IPv6loopback,
Dst: net.IPv6loopback,
}
if ifi != nil {
cm.IfIndex = ifi.Index
}
if err := p.SetControlMessage(cf, toggle); err != nil {
t.Errorf("ipv6.PacketConn.SetControlMessage failed: %v", err)
return
}
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Errorf("ipv6.PacketConn.WriteTo failed: %v", err)
return
} else if n != len(wb) {
t.Errorf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
return
}
}
const N = 10
wg.Add(N)
for i := 0; i < N; i++ {
go reader()
}
wg.Add(2 * N)
for i := 0; i < 2*N; i++ {
go writer(i%2 != 0)
}
wg.Add(N)
for i := 0; i < N; i++ {
go reader()
}
wg.Wait()
}

View File

@ -1,66 +0,0 @@
// Copyright 2013 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 ipv6
import (
"os"
"syscall"
)
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
return false, errNotSupported
}
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
return errNotSupported
}
func ipv6ReceiveHopLimit(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292HOPLIMIT)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceiveHopLimit(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292HOPLIMIT, boolint(v)))
}
func ipv6ReceivePacketInfo(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292PKTINFO)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceivePacketInfo(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292PKTINFO, boolint(v)))
}
func ipv6PathMTU(fd int) (int, error) {
return 0, errNotSupported
}
func ipv6ReceivePathMTU(fd int) (bool, error) {
return false, errNotSupported
}
func setIPv6ReceivePathMTU(fd int, v bool) error {
return errNotSupported
}
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER)
if err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil
}
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER, &f.rawICMPFilter.ICMPv6Filter))
}

View File

@ -0,0 +1,73 @@
// Copyright 2013 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 darwin
package ipv6
import (
"os"
"unsafe"
)
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
return false, errOpNoSupport
}
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
return errOpNoSupport
}
func ipv6ReceiveHopLimit(fd int) (bool, error) {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockopt2292HopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceiveHopLimit(fd int, v bool) error {
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockopt2292HopLimit, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6ReceivePacketInfo(fd int) (bool, error) {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockopt2292PacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceivePacketInfo(fd int, v bool) error {
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockopt2292PacketInfo, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6PathMTU(fd int) (int, error) {
return 0, errOpNoSupport
}
func ipv6ReceivePathMTU(fd int) (bool, error) {
return false, errOpNoSupport
}
func setIPv6ReceivePathMTU(fd int, v bool) error {
return errOpNoSupport
}
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
var v ICMPFilter
l := sysSockoptLen(sysSizeofICMPFilter)
if err := getsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&v.sysICMPFilter)), &l); err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return &v, nil
}
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&f.sysICMPFilter)), sysSizeofICMPFilter))
}

View File

@ -8,12 +8,13 @@ package ipv6
import (
"os"
"syscall"
"unsafe"
)
func setIPv6Checksum(fd int, on bool, offset int) error {
if !on {
offset = -1
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM, offset))
v := int32(offset)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4))
}

View File

@ -6,12 +6,13 @@ package ipv6
import (
"os"
"syscall"
"unsafe"
)
func setIPv6Checksum(fd int, on bool, offset int) error {
if !on {
offset = -1
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolReserved, syscall.IPV6_CHECKSUM, offset))
v := int32(offset)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolReserved, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4))
}

View File

@ -9,66 +9,74 @@ package ipv6
import (
"net"
"os"
"syscall"
"unsafe"
)
func ipv6TrafficClass(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
return int(v), nil
}
func setIPv6TrafficClass(fd, v int) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS, v))
vv := int32(v)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6HopLimit(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
return int(v), nil
}
func setIPv6HopLimit(fd, v int) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, v))
vv := int32(v)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6Checksum(fd int) (bool, int, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, 0, os.NewSyscallError("getsockopt", err)
}
on := true
if v == -1 {
on = false
}
return on, v, nil
return on, int(v), nil
}
func ipv6MulticastHopLimit(fd int) (int, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return v, nil
return int(v), nil
}
func setIPv6MulticastHopLimit(fd, v int) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, v))
vv := int32(v)
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6MulticastInterface(fd int) (*net.Interface, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
if v == 0 {
return nil, nil
}
ifi, err := net.InterfaceByIndex(v)
ifi, err := net.InterfaceByIndex(int(v))
if err != nil {
return nil, err
}
@ -76,39 +84,41 @@ func ipv6MulticastInterface(fd int) (*net.Interface, error) {
}
func setIPv6MulticastInterface(fd int, ifi *net.Interface) error {
var v int
var v int32
if ifi != nil {
v = ifi.Index
v = int32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), 4))
}
func ipv6MulticastLoopback(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6MulticastLoopback(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, boolint(v)))
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, uintptr(unsafe.Pointer(&vv)), 4))
}
func joinIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
mreq := syscall.IPv6Mreq{}
copy(mreq.Multiaddr[:], grp)
mreq := sysMulticastReq{}
copy(mreq.IP[:], grp)
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
mreq.IfIndex = uint32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, &mreq))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq))
}
func leaveIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
mreq := syscall.IPv6Mreq{}
copy(mreq.Multiaddr[:], grp)
mreq := sysMulticastReq{}
copy(mreq.IP[:], grp)
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
mreq.IfIndex = uint32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, &mreq))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq))
}

View File

@ -24,7 +24,7 @@ func setIPv6TrafficClass(fd syscall.Handle, v int) error {
func ipv6HopLimit(fd syscall.Handle) (int, error) {
var v int32
l := int32(4)
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v), nil
@ -32,7 +32,7 @@ func ipv6HopLimit(fd syscall.Handle) (int, error) {
func setIPv6HopLimit(fd syscall.Handle, v int) error {
vv := int32(v)
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, (*byte)(unsafe.Pointer(&vv)), 4))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4))
}
func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
@ -43,7 +43,7 @@ func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
var v int32
l := int32(4)
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v), nil
@ -51,13 +51,13 @@ func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error {
vv := int32(v)
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, (*byte)(unsafe.Pointer(&vv)), 4))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4))
}
func ipv6MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
var v int32
l := int32(4)
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
if v == 0 {
@ -75,13 +75,13 @@ func setIPv6MulticastInterface(fd syscall.Handle, ifi *net.Interface) error {
if ifi != nil {
v = int32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, (*byte)(unsafe.Pointer(&v)), 4))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), 4))
}
func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
var v int32
l := int32(4)
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
@ -89,25 +89,25 @@ func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error {
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&vv)), 4))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&vv)), 4))
}
func joinIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
mreq := syscall.IPv6Mreq{}
copy(mreq.Multiaddr[:], grp)
mreq := sysMulticastReq{}
copy(mreq.IP[:], grp)
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
mreq.IfIndex = uint32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptJoinGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq)))
}
func leaveIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
mreq := syscall.IPv6Mreq{}
copy(mreq.Multiaddr[:], grp)
mreq := sysMulticastReq{}
copy(mreq.IP[:], grp)
if ifi != nil {
mreq.Interface = uint32(ifi.Index)
mreq.IfIndex = uint32(ifi.Index)
}
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofMulticastReq)))
}
func setIPv6Checksum(fd syscall.Handle, on bool, offset int) error {

View File

@ -1,44 +0,0 @@
// Copyright 2013 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 freebsd netbsd openbsd
package ipv6
import (
"os"
"syscall"
)
func ipv6PathMTU(fd int) (int, error) {
v, err := syscall.GetsockoptIPv6MTUInfo(fd, ianaProtocolIPv6, syscall.IPV6_PATHMTU)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v.Mtu), nil
}
func ipv6ReceivePathMTU(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPATHMTU)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceivePathMTU(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPATHMTU, boolint(v)))
}
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER)
if err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil
}
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER, &f.rawICMPFilter.ICMPv6Filter))
}

View File

@ -1,42 +0,0 @@
// Copyright 2013 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 ipv6
import (
"os"
"syscall"
)
func ipv6PathMTU(fd int) (int, error) {
v, err := syscall.GetsockoptIPv6MTUInfo(fd, ianaProtocolIPv6, syscall_IPV6_PATHMTU)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v.Mtu), nil
}
func ipv6ReceivePathMTU(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall_IPV6_RECVPATHMTU)
if err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceivePathMTU(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall_IPV6_RECVPATHMTU, boolint(v)))
}
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMPV6_FILTER)
if err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil
}
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMPV6_FILTER, &f.rawICMPFilter.ICMPv6Filter))
}

View File

@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ipv6
// +build dragonfly plan9 solaris
import "syscall"
package ipv6
func ipv6PathMTU(fd int) (int, error) {
// TODO(mikio): Implement this
return 0, syscall.EPLAN9
return 0, errOpNoSupport
}

View File

@ -8,41 +8,83 @@ package ipv6
import (
"os"
"syscall"
"unsafe"
)
func ipv6ReceiveTrafficClass(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceiveTrafficClass(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS, boolint(v)))
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6ReceiveHopLimit(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceiveHopLimit(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT, boolint(v)))
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6ReceivePacketInfo(fd int) (bool, error) {
v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO)
if err != nil {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceivePacketInfo(fd int, v bool) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO, boolint(v)))
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePacketInfo, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6PathMTU(fd int) (int, error) {
var v sysMTUInfo
l := sysSockoptLen(sysSizeofMTUInfo)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptPathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return int(v.MTU), nil
}
func ipv6ReceivePathMTU(fd int) (bool, error) {
var v int32
l := sysSockoptLen(4)
if err := getsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&v)), &l); err != nil {
return false, os.NewSyscallError("getsockopt", err)
}
return v == 1, nil
}
func setIPv6ReceivePathMTU(fd int, v bool) error {
vv := int32(boolint(v))
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceivePathMTU, uintptr(unsafe.Pointer(&vv)), 4))
}
func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
var v ICMPFilter
l := sysSockoptLen(sysSizeofICMPFilter)
if err := getsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&v.sysICMPFilter)), &l); err != nil {
return nil, os.NewSyscallError("getsockopt", err)
}
return &v, nil
}
func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6ICMP, sysSockoptICMPFilter, uintptr(unsafe.Pointer(&f.sysICMPFilter)), sysSizeofICMPFilter))
}

View File

@ -24,7 +24,7 @@ func init() {
var condFatalf = func() func(*testing.T, string, ...interface{}) {
// A few APIs are not implemented yet on some platforms.
switch runtime.GOOS {
case "darwin", "plan9", "windows":
case "darwin", "dragonfly", "plan9", "solaris", "windows":
return (*testing.T).Logf
}
return (*testing.T).Fatalf
@ -32,7 +32,7 @@ var condFatalf = func() func(*testing.T, string, ...interface{}) {
func TestConnInitiatorPathMTU(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -65,7 +65,7 @@ func TestConnInitiatorPathMTU(t *testing.T) {
func TestConnResponderPathMTU(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -98,7 +98,7 @@ func TestConnResponderPathMTU(t *testing.T) {
func TestPacketConnChecksum(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {

View File

@ -0,0 +1,23 @@
// Copyright 2013 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 ipv6
type sysSockoptLen uint32
const (
sysSizeofPacketInfo = 0x14
sysSizeofMulticastReq = 0x14
sysSizeofICMPFilter = 0x20
)
type sysPacketInfo struct {
IP [16]byte
IfIndex uint32
}
type sysMulticastReq struct {
IP [16]byte
IfIndex uint32
}

View File

@ -0,0 +1,48 @@
// Copyright 2013 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 freebsd netbsd openbsd
package ipv6
import (
"net"
"syscall"
)
// RFC 3493 options
const (
// See /usr/include/netinet6/in6.h.
sysSockoptUnicastHopLimit = 0x4
sysSockoptMulticastHopLimit = 0xa
sysSockoptMulticastInterface = 0x9
sysSockoptMulticastLoopback = 0xb
sysSockoptJoinGroup = 0xc
sysSockoptLeaveGroup = 0xd
)
// RFC 3542 options
const (
// See /usr/include/netinet6/in6.h.
sysSockoptReceiveTrafficClass = 0x39
sysSockoptTrafficClass = 0x3d
sysSockoptReceiveHopLimit = 0x25
sysSockoptHopLimit = 0x2f
sysSockoptReceivePacketInfo = 0x24
sysSockoptPacketInfo = 0x2e
sysSockoptReceivePathMTU = 0x2b
sysSockoptPathMTU = 0x2c
sysSockoptNextHop = 0x30
sysSockoptChecksum = 0x1a
// See /usr/include/netinet6/in6.h.
sysSockoptICMPFilter = 0x12
)
func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
sa.Len = syscall.SizeofSockaddrInet6
sa.Family = syscall.AF_INET6
copy(sa.Addr[:], ip)
sa.Scope_id = uint32(ifindex)
}

View File

@ -0,0 +1,54 @@
// Copyright 2013 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 ipv6
import (
"net"
"syscall"
)
// RFC 2292 options
const (
// See /usr/include/netinet6/in6.h.
sysSockopt2292HopLimit = 0x14
sysSockopt2292PacketInfo = 0x13
sysSockopt2292NextHop = 0x15
)
// RFC 3493 options
const (
// See /usr/include/netinet6/in6.h.
sysSockoptUnicastHopLimit = 0x4
sysSockoptMulticastHopLimit = 0xa
sysSockoptMulticastInterface = 0x9
sysSockoptMulticastLoopback = 0xb
sysSockoptJoinGroup = 0xc
sysSockoptLeaveGroup = 0xd
)
// RFC 3542 options
const (
// See /usr/include/netinet6/in6.h.
sysSockoptReceiveTrafficClass = 0x23
sysSockoptTrafficClass = 0x24
sysSockoptReceiveHopLimit = 0x25
sysSockoptHopLimit = 0x2f
sysSockoptReceivePacketInfo = 0x3d
sysSockoptPacketInfo = 0x2e
sysSockoptReceivePathMTU = 0x2b
sysSockoptPathMTU = 0x2c
sysSockoptNextHop = 0x30
sysSockoptChecksum = 0x1a
// See /usr/include/netinet6/in6.h.
sysSockoptICMPFilter = 0x12
)
func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
sa.Len = syscall.SizeofSockaddrInet6
sa.Family = syscall.AF_INET6
copy(sa.Addr[:], ip)
sa.Scope_id = uint32(ifindex)
}

View File

@ -0,0 +1,45 @@
// Copyright 2013 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 ipv6
import (
"net"
"syscall"
)
// RFC 3493 options
const (
// See /usr/include/linux/in6.h.
sysSockoptUnicastHopLimit = 0x10
sysSockoptMulticastHopLimit = 0x12
sysSockoptMulticastInterface = 0x11
sysSockoptMulticastLoopback = 0x13
sysSockoptJoinGroup = 0x14
sysSockoptLeaveGroup = 0x15
)
// RFC 3542 options
const (
// See /usr/include/linux/ipv6.h,in6.h.
sysSockoptReceiveTrafficClass = 0x42
sysSockoptTrafficClass = 0x43
sysSockoptReceiveHopLimit = 0x33
sysSockoptHopLimit = 0x34
sysSockoptReceivePacketInfo = 0x31
sysSockoptPacketInfo = 0x32
sysSockoptReceivePathMTU = 0x3c
sysSockoptPathMTU = 0x3d
sysSockoptNextHop = 0x9
sysSockoptChecksum = 0x7
// See /usr/include/linux/icmpv6.h.
sysSockoptICMPFilter = 0x1
)
func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
sa.Family = syscall.AF_INET6
copy(sa.Addr[:], ip)
sa.Scope_id = uint32(ifindex)
}

View File

@ -0,0 +1,16 @@
// Copyright 2013 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 darwin freebsd linux netbsd openbsd
package ipv6
import "syscall"
const sysSizeofMTUInfo = 0x20
type sysMTUInfo struct {
Addr syscall.RawSockaddrInet6
MTU uint32
}

View File

@ -0,0 +1,33 @@
// Copyright 2013 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 ipv6
import (
"net"
"syscall"
)
// RFC 3493 options
const (
// See ws2tcpip.h.
sysSockoptUnicastHopLimit = 0x4
sysSockoptMulticastHopLimit = 0xa
sysSockoptMulticastInterface = 0x9
sysSockoptMulticastLoopback = 0xb
sysSockoptJoinGroup = 0xc
sysSockoptLeaveGroup = 0xd
)
// RFC 3542 options
const (
// See ws2tcpip.h.
sysSockoptPacketInfo = 0x13
)
func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
sa.Family = syscall.AF_INET6
copy(sa.Addr[:], ip)
sa.Scope_id = uint32(ifindex)
}

View File

@ -0,0 +1,42 @@
// Copyright 2009 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.
// This code is a duplicate of syscall/syscall_linux_386.go with small
// modifications.
package ipv6
import (
"syscall"
"unsafe"
)
// On x86 Linux, all the socket calls go through an extra indirection,
// I think because the 5-register system call interface can't handle
// the 6-argument calls like sendto and recvfrom. Instead the
// arguments to the underlying system call are the number below and a
// pointer to an array of uintptr. We hide the pointer in the
// socketcall assembly to avoid allocation on every system call.
const (
// See /usr/include/linux/net.h.
_SETSOCKOPT = 14
_GETSOCKOPT = 15
)
var socketcall func(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
func getsockopt(fd int, level int, name int, v uintptr, l *sysSockoptLen) error {
if _, errno := socketcall(_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
return error(errno)
}
return nil
}
func setsockopt(fd int, level int, name int, v uintptr, l uintptr) error {
if _, errno := socketcall(_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), v, l, 0); errno != 0 {
return error(errno)
}
return nil
}

View File

@ -0,0 +1,56 @@
// Copyright 2009 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.
// This code is a duplicate of syscall/syscall_linux_386.s with small
// modifications.
#define SYS_SOCKETCALL 102 // from zsysnum_linux_386.go
// func socketcallnosplit7(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
// Kernel interface gets call sub-number and pointer to a0 for Go 1.1.
TEXT ·socketcallnosplit7(SB),7,$0
CALL runtime·entersyscall(SB)
MOVL $SYS_SOCKETCALL, AX // syscall entry
MOVL 4(SP), BX // socket call number
LEAL 8(SP), CX // pointer to call arguments
MOVL $0, DX
MOVL $0, SI
MOVL $0, DI
CALL *runtime·_vdso(SB)
CMPL AX, $0xfffff001
JLS ok1
MOVL $-1, 32(SP) // n
NEGL AX
MOVL AX, 36(SP) // errno
CALL runtime·exitsyscall(SB)
RET
ok1:
MOVL AX, 32(SP) // n
MOVL $0, 36(SP) // errno
CALL runtime·exitsyscall(SB)
RET
// func socketcallnosplit4(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
// Kernel interface gets call sub-number and pointer to a0 for Go 1.2.
TEXT ·socketcallnosplit4(SB),4,$0-40
CALL runtime·entersyscall(SB)
MOVL $SYS_SOCKETCALL, AX // syscall entry
MOVL 4(SP), BX // socket call number
LEAL 8(SP), CX // pointer to call arguments
MOVL $0, DX
MOVL $0, SI
MOVL $0, DI
CALL *runtime·_vdso(SB)
CMPL AX, $0xfffff001
JLS ok2
MOVL $-1, 32(SP) // n
NEGL AX
MOVL AX, 36(SP) // errno
CALL runtime·exitsyscall(SB)
RET
ok2:
MOVL AX, 32(SP) // n
MOVL $0, 36(SP) // errno
CALL runtime·exitsyscall(SB)
RET

View File

@ -0,0 +1,15 @@
// Copyright 2013 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 go1.2
package ipv6
import "syscall"
func socketcallnosplit4(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
func init() {
socketcall = socketcallnosplit4
}

View File

@ -0,0 +1,15 @@
// Copyright 2013 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 go1.1,!go1.2
package ipv6
import "syscall"
func socketcallnosplit7(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
func init() {
socketcall = socketcallnosplit7
}

View File

@ -0,0 +1,26 @@
// Copyright 2013 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 darwin freebsd linux,amd64 linux,arm netbsd openbsd
package ipv6
import (
"syscall"
"unsafe"
)
func getsockopt(fd int, level, name int, v uintptr, l *sysSockoptLen) error {
if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
return error(errno)
}
return nil
}
func setsockopt(fd int, level int, name int, v uintptr, l uintptr) error {
if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
return error(errno)
}
return nil
}

View File

@ -5,89 +5,18 @@
package ipv6_test
import (
"bytes"
"code.google.com/p/go.net/ipv6"
"net"
"os"
"runtime"
"testing"
"time"
)
func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
c, err := net.ListenPacket("udp6", "[::1]:0")
if err != nil {
return nil, nil, err
}
dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
if err != nil {
c.Close()
return nil, nil, err
}
return c, dst, nil
}
func BenchmarkReadWriteNetUDP(b *testing.B) {
c, dst, err := benchmarkUDPListener()
if err != nil {
b.Fatalf("benchmarkUDPListener failed: %v", err)
}
defer c.Close()
wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchmarkReadWriteNetUDP(b, c, wb, rb, dst)
}
}
func benchmarkReadWriteNetUDP(b *testing.B, c net.PacketConn, wb, rb []byte, dst net.Addr) {
if _, err := c.WriteTo(wb, dst); err != nil {
b.Fatalf("net.PacketConn.WriteTo failed: %v", err)
}
if _, _, err := c.ReadFrom(rb); err != nil {
b.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
}
}
func BenchmarkReadWriteIPv6UDP(b *testing.B) {
c, dst, err := benchmarkUDPListener()
if err != nil {
b.Fatalf("benchmarkUDPListener failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
if err := p.SetControlMessage(cf, true); err != nil {
b.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
ifi := loopbackInterface()
wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchmarkReadWriteIPv6UDP(b, p, wb, rb, dst, ifi)
}
}
func benchmarkReadWriteIPv6UDP(b *testing.B, p *ipv6.PacketConn, wb, rb []byte, dst net.Addr, ifi *net.Interface) {
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
HopLimit: 1,
}
if ifi != nil {
cm.IfIndex = ifi.Index
}
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
b.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
}
if _, _, _, err := p.ReadFrom(rb); err != nil {
b.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
}
}
func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -99,33 +28,47 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
defer p.Close()
dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
if err != nil {
t.Fatalf("net.ResolveUDPAddr failed: %v", err)
}
p := ipv6.NewPacketConn(c)
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
Src: net.IPv6loopback,
Dst: net.IPv6loopback,
}
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
ifi := loopbackInterface()
if ifi != nil {
cm.IfIndex = ifi.Index
}
wb := []byte("HELLO-R-U-THERE")
for i, toggle := range []bool{true, false, true} {
if err := p.SetControlMessage(cf, toggle); err != nil {
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
cm.HopLimit = i + 1
if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err)
}
b := make([]byte, 128)
if _, cm, _, err := p.ReadFrom(b); err != nil {
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
} else if n != len(wb) {
t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
}
rb := make([]byte, 128)
if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err)
}
if n, cm, _, err := p.ReadFrom(rb); err != nil {
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
} else if !bytes.Equal(rb[:n], wb) {
t.Fatalf("got %v; expected %v", rb[:n], wb)
} else {
t.Logf("rcvd cmsg: %v", cm)
}
@ -134,7 +77,7 @@ func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -149,15 +92,21 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
t.Fatalf("net.ListenPacket failed: %v", err)
}
defer c.Close()
p := ipv6.NewPacketConn(c)
defer p.Close()
dst, err := net.ResolveIPAddr("ip6", "::1")
if err != nil {
t.Fatalf("net.ResolveIPAddr failed: %v", err)
}
p := ipv6.NewPacketConn(c)
cm := ipv6.ControlMessage{TrafficClass: DiffServAF11 | CongestionExperienced}
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
pshicmp := ipv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP, ianaProtocolIPv6ICMP)
cm := ipv6.ControlMessage{
TrafficClass: DiffServAF11 | CongestionExperienced,
Src: net.IPv6loopback,
Dst: net.IPv6loopback,
}
cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
ifi := loopbackInterface()
if ifi != nil {
cm.IfIndex = ifi.Index
@ -170,14 +119,26 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
}
var psh []byte
for i, toggle := range []bool{true, false, true} {
if toggle {
psh = nil
if err := p.SetChecksum(true, 2); err != nil {
t.Fatalf("ipv6.PacketConn.SetChecksum failed: %v", err)
}
} else {
psh = pshicmp
// Some platforms never allow to disable the
// kernel checksum processing.
p.SetChecksum(false, -1)
}
wb, err := (&icmpMessage{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmpEcho{
ID: os.Getpid() & 0xffff, Seq: i + 1,
Data: []byte("HELLO-R-U-THERE"),
},
}).Marshal()
}).Marshal(psh)
if err != nil {
t.Fatalf("icmpMessage.Marshal failed: %v", err)
}
@ -185,15 +146,23 @@ func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
}
cm.HopLimit = i + 1
if _, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
t.Fatalf("ipv6.PacketConn.SetWriteDeadline failed: %v", err)
}
b := make([]byte, 128)
if n, cm, _, err := p.ReadFrom(b); err != nil {
if n, err := p.WriteTo(wb, &cm, dst); err != nil {
t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
} else if n != len(wb) {
t.Fatalf("ipv6.PacketConn.WriteTo failed: short write: %v", n)
}
rb := make([]byte, 128)
if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
t.Fatalf("ipv6.PacketConn.SetReadDeadline failed: %v", err)
}
if n, cm, _, err := p.ReadFrom(rb); err != nil {
t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
} else {
t.Logf("rcvd cmsg: %v", cm)
if m, err := parseICMPMessage(b[:n]); err != nil {
if m, err := parseICMPMessage(rb[:n]); err != nil {
t.Fatalf("parseICMPMessage failed: %v", err)
} else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 {
t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0)

View File

@ -14,7 +14,7 @@ import (
func TestConnUnicastSocketOptions(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {
@ -50,7 +50,7 @@ var packetConnUnicastSocketOptionTests = []struct {
func TestPacketConnUnicastSocketOptions(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
case "dragonfly", "plan9", "solaris", "windows":
t.Skipf("not supported on %q", runtime.GOOS)
}
if !supportsIPv6 {

View File

@ -218,3 +218,18 @@ func (c *Config) Set(sectionName, key, value string) {
options: []option{{key, value}},
})
}
// Delete removes the option from the specified section.
func (c *Config) Delete(section, key string) {
for sn, sect := range c.sections {
if sect.name == section {
for i, opt := range sect.options {
if opt.name == key {
c.sections[sn].options = append(sect.options[:i], sect.options[i+1:]...)
return
}
}
return
}
}
}

View File

@ -2,9 +2,10 @@ package ini_test
import (
"bytes"
"github.com/calmh/ini"
"strings"
"testing"
"github.com/calmh/ini"
)
func TestParseValues(t *testing.T) {
@ -136,6 +137,41 @@ baz2=quux2
}
}
func TestDelete(t *testing.T) {
buf := bytes.NewBufferString("[general]\nfoo=bar\nfoo2=bar2\nfoo3=baz\n")
cfg := ini.Parse(buf)
cfg.Delete("general", "foo")
out := new(bytes.Buffer)
cfg.Write(out)
correct := "[general]\nfoo2=bar2\nfoo3=baz\n\n"
if s := out.String(); s != correct {
t.Errorf("Incorrect INI after delete:\n%s", s)
}
buf = bytes.NewBufferString("[general]\nfoo=bar\nfoo2=bar2\nfoo3=baz\n")
cfg = ini.Parse(buf)
cfg.Delete("general", "foo2")
out = new(bytes.Buffer)
cfg.Write(out)
correct = "[general]\nfoo=bar\nfoo3=baz\n\n"
if s := out.String(); s != correct {
t.Errorf("Incorrect INI after delete:\n%s", s)
}
buf = bytes.NewBufferString("[general]\nfoo=bar\nfoo2=bar2\nfoo3=baz\n")
cfg = ini.Parse(buf)
cfg.Delete("general", "foo3")
out = new(bytes.Buffer)
cfg.Write(out)
correct = "[general]\nfoo=bar\nfoo2=bar2\n\n"
if s := out.String(); s != correct {
t.Errorf("Incorrect INI after delete:\n%s", s)
}
}
func TestSetManyEquals(t *testing.T) {
buf := bytes.NewBufferString("[general]\nfoo=bar==\nfoo2=bar2==\n")
cfg := ini.Parse(buf)

View File

@ -1,3 +1,4 @@
// Package inject provides utilities for mapping and injecting dependencies in various ways.
package inject
import (
@ -5,24 +6,49 @@ import (
"reflect"
)
// Injector represents an interface for mapping and injecting dependencies into structs
// and function arguments.
type Injector interface {
Applicator
Invoker
TypeMapper
// SetParent sets the parent of the injector. If the injector cannot find a
// dependency in its Type map it will check its parent before returning an
// error.
SetParent(Injector)
}
// Applicator represents an interface for mapping dependencies to a struct.
type Applicator interface {
// Maps dependencies in the Type map to each field in the struct
// that is tagged with 'inject'. Returns an error if the injection
// fails.
Apply(interface{}) error
}
// Invoker represents an interface for calling functions via reflection.
type Invoker interface {
// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type. Returns
// a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
Invoke(interface{}) ([]reflect.Value, error)
}
// TypeMapper represents an interface for mapping interface{} values based on type.
type TypeMapper interface {
// Maps the interface{} value based on its immediate type from reflect.TypeOf.
Map(interface{}) TypeMapper
// Maps the interface{} value based on the pointer of an Interface provided.
// This is really only useful for mapping a value as an interface, as interfaces
// cannot at this time be referenced directly without a pointer.
MapTo(interface{}, interface{}) TypeMapper
// Provides a possibility to directly insert a mapping based on type and value.
// This makes it possible to directly map type arguments not possible to instantiate
// with reflect like unidirectional channels.
Set(reflect.Type, reflect.Value) TypeMapper
// Returns the Value that is mapped to the current type. Returns a zeroed Value if
// the Type has not been mapped.
Get(reflect.Type) reflect.Value
}
@ -31,6 +57,8 @@ type injector struct {
parent Injector
}
// InterfaceOf dereferences a pointer to an Interface type.
// It panics if value is not an pointer to an interface.
func InterfaceOf(value interface{}) reflect.Type {
t := reflect.TypeOf(value)
@ -45,16 +73,22 @@ func InterfaceOf(value interface{}) reflect.Type {
return t
}
// New returns a new Injector.
func New() Injector {
return &injector{
values: make(map[reflect.Type]reflect.Value),
}
}
// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type.
// Returns a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
// It panics if f is not a function
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
t := reflect.TypeOf(f)
var in = make([]reflect.Value, t.NumIn())
var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func
for i := 0; i < t.NumIn(); i++ {
argType := t.In(i)
val := inj.Get(argType)
@ -68,6 +102,9 @@ func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
return reflect.ValueOf(f).Call(in), nil
}
// Maps dependencies in the Type map to each field in the struct
// that is tagged with 'inject'.
// Returns an error if the injection fails.
func (inj *injector) Apply(val interface{}) error {
v := reflect.ValueOf(val)
@ -76,7 +113,7 @@ func (inj *injector) Apply(val interface{}) error {
}
if v.Kind() != reflect.Struct {
return nil
return nil // Should not panic here ?
}
t := v.Type()
@ -99,6 +136,8 @@ func (inj *injector) Apply(val interface{}) error {
return nil
}
// Maps the concrete value of val to its dynamic type using reflect.TypeOf,
// It returns the TypeMapper registered in.
func (i *injector) Map(val interface{}) TypeMapper {
i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
return i
@ -109,6 +148,13 @@ func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
return i
}
// Maps the given reflect.Type to the given reflect.Value and returns
// the Typemapper the mapping has been registered in.
func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
i.values[typ] = val
return i
}
func (i *injector) Get(t reflect.Type) reflect.Value {
val := i.values[t]
if !val.IsValid() && i.parent != nil {

View File

@ -36,10 +36,20 @@ func Test_InjectorInvoke(t *testing.T) {
injector.Map(dep)
dep2 := "another dep"
injector.MapTo(dep2, (*SpecialString)(nil))
_, err := injector.Invoke(func(d1 string, d2 SpecialString) {
dep3 := make(chan *SpecialString)
dep4 := make(chan *SpecialString)
typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem())
typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem())
injector.Set(typRecv, reflect.ValueOf(dep3))
injector.Set(typSend, reflect.ValueOf(dep4))
_, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) {
expect(t, d1, dep)
expect(t, d2, dep2)
expect(t, reflect.TypeOf(d3).Elem(), reflect.TypeOf(dep3).Elem())
expect(t, reflect.TypeOf(d4).Elem(), reflect.TypeOf(dep4).Elem())
expect(t, reflect.TypeOf(d3).ChanDir(), reflect.RecvDir)
expect(t, reflect.TypeOf(d4).ChanDir(), reflect.SendDir)
})
expect(t, err, nil)
@ -92,6 +102,26 @@ func Test_InterfaceOf(t *testing.T) {
iType = inject.InterfaceOf((*testing.T)(nil))
}
func Test_InjectorSet(t *testing.T) {
injector := inject.New()
typ := reflect.TypeOf("string")
typSend := reflect.ChanOf(reflect.SendDir, typ)
typRecv := reflect.ChanOf(reflect.RecvDir, typ)
// instantiating unidirectional channels is not possible using reflect
// http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064
chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
injector.Set(typSend, chanSend)
injector.Set(typRecv, chanRecv)
expect(t, injector.Get(typSend).IsValid(), true)
expect(t, injector.Get(typRecv).IsValid(), true)
expect(t, injector.Get(chanSend.Type()).IsValid(), false)
}
func Test_InjectorGet(t *testing.T) {
injector := inject.New()

View File

@ -1,7 +1,10 @@
# Martini [![Build Status](https://drone.io/github.com/codegangsta/martini/status.png)](https://drone.io/github.com/codegangsta/martini/latest) [![GoDoc](https://godoc.org/github.com/codegangsta/martini?status.png)](http://godoc.org/github.com/codegangsta/martini)
# Martini [![wercker status](https://app.wercker.com/status/174bef7e3c999e103cacfe2770102266 "wercker status")](https://app.wercker.com/project/bykey/174bef7e3c999e103cacfe2770102266) [![GoDoc](https://godoc.org/github.com/codegangsta/martini?status.png)](http://godoc.org/github.com/codegangsta/martini)
Martini is a powerful package for quickly writing modular web applications/services in Golang.
Language Translations: [Simplified Chinese (zh_CN)](translations/README_zh_cn.md)
## Getting Started
After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`.
@ -41,7 +44,7 @@ Watch the [Demo Video](http://martini.codegangsta.io/#demo)
## Features
* Extremely simple to use.
* Non-intrusive design.
* Play nice with other Golang packages.
* Plays nice with other Golang packages.
* Awesome path matching and routing.
* Modular design - Easy to add functionality, easy to rip stuff out.
* Lots of good handlers/middlewares to use.
@ -49,7 +52,7 @@ Watch the [Demo Video](http://martini.codegangsta.io/#demo)
* **Fully compatible with the [http.HandlerFunc](http://godoc.org/net/http#HandlerFunc) interface.**
## More Middleware
For more middleware and functionality, check out the [martini-contrib](http://github.com/codegangsta/martini-contrib) repository.
For more middleware and functionality, check out the repositories in the [martini-contrib](https://github.com/martini-contrib) organization.
## Table of Contents
* [Classic Martini](#classic-martini)
@ -59,6 +62,7 @@ For more middleware and functionality, check out the [martini-contrib](http://gi
* [Serving Static Files](#serving-static-files)
* [Middleware Handlers](#middleware-handlers)
* [Next()](#next)
* [Martini Env](#martini-env)
* [FAQ](#faq)
## Classic Martini
@ -101,7 +105,7 @@ m.Get("/", func() (int, string) {
#### Service Injection
Handlers are invoked via reflection. Martini makes use of *Dependency Injection* to resolve dependencies in a Handlers argument list. **This makes Martini completely compatible with golang's `http.HandlerFunc` interface.**
If you add an argument to your Handler, Martini will search it's list of services and attempt to resolve the dependency via type assertion:
If you add an argument to your Handler, Martini will search its list of services and attempt to resolve the dependency via type assertion:
~~~ go
m.Get("/", func(res http.ResponseWriter, req *http.Request) { // res and req are injected by Martini
res.WriteHeader(200) // HTTP 200
@ -173,6 +177,26 @@ m.Get("/secret", authorize, func() {
})
~~~
Route groups can be added too using the Group method.
~~~ go
m.Group("/books", func(r Router) {
r.Get("/:id", GetBooks)
r.Post("/new", NewBook)
r.Put("/update/:id", UpdateBook)
r.Delete("/delete/:id", DeleteBook)
})
~~~
Just like you can pass middlewares to a handler you can pass middlewares to groups.
~~~ go
m.Group("/books", func(r Router) {
r.Get("/:id", GetBooks)
r.Post("/new", NewBook)
r.Put("/update/:id", UpdateBook)
r.Delete("/delete/:id", DeleteBook)
}, MyMiddleware1, MyMiddleware2)
~~~
### Services
Services are objects that are available to be injected into a Handler's argument list. You can map a service on a *Global* or *Request* level.
@ -219,7 +243,7 @@ m.Use(func() {
})
~~~
You can have full control over the middleware stack with the `Handlers` function:
You can have full control over the middleware stack with the `Handlers` function. This will replace any handlers that have been previously set:
~~~ go
m.Handlers(
Middleware1,
@ -251,17 +275,28 @@ m.Use(func(c martini.Context, log *log.Logger){
})
~~~
## Martini Env
Some Martini handlers make use of the `martini.Env` global variable to provide special functionality for development environments vs production environments. It is reccomended that the `MARTINI_ENV=production` environment variable to be set when deploying a Martini server into a production environment.
## FAQ
### Where do I find middleware X?
Start by looking in the [martini-contrib](http://github.com/codegangsta/martini-contrib) package. If it is not there feel free to put up a Pull Request for one.
Start by looking in the [martini-contrib](https://github.com/martini-contrib) projects. If it is not there feel free to contact a martini-contrib team member about adding a new repo to the organization.
* [auth](https://github.com/codegangsta/martini-contrib/tree/master/auth) - Handlers for authentication.
* [form](https://github.com/codegangsta/martini-contrib/tree/master/form) - Handler for parsing and mapping form fields.
* [gzip](https://github.com/codegangsta/martini-contrib/tree/master/gzip) - Handler for adding gzip compress to requests
* [render](https://github.com/codegangsta/martini-contrib/tree/master/render) - Handler that provides a service for easily rendering JSON and HTML templates.
* [acceptlang](https://github.com/codegangsta/martini-contrib/tree/master/acceptlang) - Handler for parsing the `Accept-Language` HTTP header.
* [auth](https://github.com/martini-contrib/auth) - Handlers for authentication.
* [binding](https://github.com/martini-contrib/binding) - Handler for mapping/validating a raw request into a structure.
* [gzip](https://github.com/martini-contrib/gzip) - Handler for adding gzip compress to requests
* [render](https://github.com/martini-contrib/render) - Handler that provides a service for easily rendering JSON and HTML templates.
* [acceptlang](https://github.com/martini-contrib/acceptlang) - Handler for parsing the `Accept-Language` HTTP header.
* [sessions](https://github.com/martini-contrib/sessions) - Handler that provides a Session service.
* [strip](https://github.com/martini-contrib/strip) - URL Prefix stripping.
* [method](https://github.com/martini-contrib/method) - HTTP method overriding via Header or form fields.
* [secure](https://github.com/martini-contrib/secure) - Implements a few quick security wins.
* [encoder](https://github.com/martini-contrib/encoder) - Encoder service for rendering data in several formats and content negotiation.
* [cors](https://github.com/martini-contrib/cors) - Handler that enables CORS support.
* [oauth2](https://github.com/martini-contrib/oauth2) - Handler that provides OAuth 2.0 login for Martini apps. Google Sign-in, Facebook Connect and Github login is supported.
### How do I integrate with existing servers?
@ -287,17 +322,24 @@ func init() {
### How do I change the port/host?
Martini's `Run` function looks for the PORT environment variable and uses that. Otherwise Martini will default to port 3000.
Martini's `Run` function looks for the PORT and HOST environment variables and uses those. Otherwise Martini will default to localhost:3000.
To have more flexibility over port and host, use the `http.ListenAndServe` function instead.
~~~ go
m := martini.Classic()
// ...
http.ListenAndServe(":8080", m)
log.Fatal(http.ListenAndServe(":8080", m))
~~~
### Live code reload?
[gin](https://github.com/codegangsta/gin) and [fresh](https://github.com/pilu/fresh) both live reload martini apps.
## Contributing
Martini is meant to be kept tiny and clean. Most contributions should end up in the [martini-contrib](http://github.com/codegangsta/martini-contrib) repository. If you do have a contribution for the core of Martini feel free to put up a Pull Request.
Martini is meant to be kept tiny and clean. Most contributions should end up in a repository in the [martini-contrib](https://github.com/martini-contrib) organization. If you do have a contribution for the core of Martini feel free to put up a Pull Request.
## About
Inspired by [express](https://github.com/visionmedia/express) and [sinatra](https://github.com/sinatra/sinatra)
Martini is obsessively designed by none other than the [Code Gangsta](http://codegangsta.io/)

View File

@ -4,6 +4,7 @@ import (
"os"
)
// Envs
const (
Dev string = "development"
Prod string = "production"
@ -11,11 +12,14 @@ const (
)
// Env is the environment that Martini is executing in. The MARTINI_ENV is read on initialization to set this variable.
var Env string = Dev
var Env = Dev
func init() {
e := os.Getenv("MARTINI_ENV")
func setENV(e string) {
if len(e) > 0 {
Env = e
}
}
func init() {
setENV(os.Getenv("MARTINI_ENV"))
}

View File

@ -0,0 +1,22 @@
package martini
import (
"testing"
)
func Test_SetENV(t *testing.T) {
tests := []struct {
in string
out string
}{
{"", "development"},
{"not_development", "not_development"},
}
for _, test := range tests {
setENV(test.in)
if Env != test.out {
expect(t, Env, test.out)
}
}
}

View File

@ -1,3 +1,7 @@
// +build !go1.1
"martini requires go 1.1 or greater to build"
package martini
func MartiniDoesNotSupportGo1Point0() {
"Martini requires Go 1.1 or greater."
}

View File

@ -12,9 +12,7 @@ func Logger() Handler {
start := time.Now()
log.Printf("Started %s %s", req.Method, req.URL.Path)
rw := NewResponseWriter(res)
c.MapTo(rw, (*http.ResponseWriter)(nil))
rw := res.(ResponseWriter)
c.Next()
log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start))

View File

@ -18,11 +18,12 @@
package martini
import (
"github.com/codegangsta/inject"
"log"
"net/http"
"os"
"reflect"
"github.com/codegangsta/inject"
)
// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level.
@ -35,11 +36,27 @@ type Martini struct {
// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used.
func New() *Martini {
m := &Martini{inject.New(), []Handler{}, func() {}, log.New(os.Stdout, "[martini] ", 0)}
m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}
m.Map(m.logger)
m.Map(defaultReturnHandler())
return m
}
// Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers.
// Will panic if any of the handlers is not a callable function
func (m *Martini) Handlers(handlers ...Handler) {
m.handlers = make([]Handler, 0)
for _, handler := range handlers {
m.Use(handler)
}
}
// Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic().
func (m *Martini) Action(handler Handler) {
validateHandler(handler)
m.action = handler
}
// Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added.
func (m *Martini) Use(handler Handler) {
validateHandler(handler)
@ -52,34 +69,21 @@ func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {
m.createContext(res, req).run()
}
// Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic().
func (m *Martini) Action(handler Handler) {
validateHandler(handler)
m.action = handler
}
// Run the http server. Listening on os.GetEnv("PORT") or 3000 by default.
func (m *Martini) Run() {
port := os.Getenv("PORT")
if len(port) == 0 {
if port == "" {
port = "3000"
}
m.logger.Println("listening on port " + port)
m.logger.Fatalln(http.ListenAndServe(":"+port, m))
}
host := os.Getenv("HOST")
// Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers.
// Will panic if any of the handlers is not a callable function
func (m *Martini) Handlers(handlers ...Handler) {
m.handlers = make([]Handler, 0)
for _, handler := range handlers {
m.Use(handler)
}
m.logger.Println("listening on " + host + ":" + port)
m.logger.Fatalln(http.ListenAndServe(host+":"+port, m))
}
func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
c := &context{inject.New(), append(m.handlers, m.action), NewResponseWriter(res), 0}
c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0}
c.SetParent(m)
c.MapTo(c, (*Context)(nil))
c.MapTo(c.rw, (*http.ResponseWriter)(nil))
@ -93,13 +97,15 @@ type ClassicMartini struct {
Router
}
// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery, and martini.Static.
// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery and martini.Static.
// Classic also maps martini.Routes as a service.
func Classic() *ClassicMartini {
r := NewRouter()
m := New()
m.Use(Logger())
m.Use(Recovery())
m.Use(Static("public"))
m.MapTo(r, (*Routes)(nil))
m.Action(r.Handle)
return &ClassicMartini{m, r}
}
@ -121,34 +127,46 @@ type Context interface {
// the other Handlers have been executed. This works really well for any operations that must
// happen after an http request
Next()
written() bool
// Written returns whether or not the response for this context has been written.
Written() bool
}
type context struct {
inject.Injector
handlers []Handler
action Handler
rw ResponseWriter
index int
}
func (c *context) handler() Handler {
if c.index < len(c.handlers) {
return c.handlers[c.index]
}
if c.index == len(c.handlers) {
return c.action
}
panic("invalid index for context handler")
}
func (c *context) Next() {
c.index += 1
c.run()
}
func (c *context) written() bool {
func (c *context) Written() bool {
return c.rw.Written()
}
func (c *context) run() {
for c.index < len(c.handlers) {
_, err := c.Invoke(c.handlers[c.index])
for c.index <= len(c.handlers) {
_, err := c.Invoke(c.handler())
if err != nil {
panic(err)
}
c.index += 1
if c.rw.Written() {
if c.Written() {
return
}
}

View File

@ -22,7 +22,14 @@ func refute(t *testing.T, a interface{}, b interface{}) {
func Test_New(t *testing.T) {
m := New()
refute(t, m, nil)
if m == nil {
t.Error("martini.New() cannot return nil")
}
}
func Test_Martini_Run(t *testing.T) {
// just test that Run doesn't bomb
go New().Run()
}
func Test_Martini_ServeHTTP(t *testing.T) {
@ -103,3 +110,32 @@ func Test_Martini_EarlyWrite(t *testing.T) {
expect(t, result, "foobar")
expect(t, response.Code, http.StatusOK)
}
func Test_Martini_Written(t *testing.T) {
response := httptest.NewRecorder()
m := New()
m.Handlers(func(res http.ResponseWriter) {
res.WriteHeader(http.StatusOK)
})
ctx := m.createContext(response, (*http.Request)(nil))
expect(t, ctx.Written(), false)
ctx.run()
expect(t, ctx.Written(), true)
}
func Test_Martini_Basic_NoRace(t *testing.T) {
m := New()
handlers := []Handler{func() {}, func() {}}
// Ensure append will not realloc to trigger the race condition
m.handlers = handlers[:1]
req, _ := http.NewRequest("GET", "/", nil)
for i := 0; i < 2; i++ {
go func() {
response := httptest.NewRecorder()
m.ServeHTTP(response, req)
}()
}
}

View File

@ -1,18 +1,139 @@
package martini
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"runtime/debug"
"runtime"
"github.com/codegangsta/inject"
)
const (
panicHtml = `<html>
<head><title>PANIC: %s</title>
<style type="text/css">
html, body {
font-family: "Roboto", sans-serif;
color: #333333;
background-color: #ea5343;
margin: 0px;
}
h1 {
color: #d04526;
background-color: #ffffff;
padding: 20px;
border-bottom: 1px dashed #2b3848;
}
pre {
margin: 20px;
padding: 20px;
border: 2px solid #2b3848;
background-color: #ffffff;
}
</style>
</head><body>
<h1>PANIC</h1>
<pre style="font-weight: bold;">%s</pre>
<pre>%s</pre>
</body>
</html>`
)
var (
dunno = []byte("???")
centerDot = []byte("·")
dot = []byte(".")
slash = []byte("/")
)
// stack returns a nicely formated stack frame, skipping skip frames
func stack(skip int) []byte {
buf := new(bytes.Buffer) // the returned data
// As we loop, we open files and read them. These variables record the currently
// loaded file.
var lines [][]byte
var lastFile string
for i := skip; ; i++ { // Skip the expected number of frames
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
// Print this much at least. If we can't find the source, it won't show.
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
if file != lastFile {
data, err := ioutil.ReadFile(file)
if err != nil {
continue
}
lines = bytes.Split(data, []byte{'\n'})
lastFile = file
}
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
}
return buf.Bytes()
}
// source returns a space-trimmed slice of the n'th line.
func source(lines [][]byte, n int) []byte {
n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
if n < 0 || n >= len(lines) {
return dunno
}
return bytes.TrimSpace(lines[n])
}
// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
fn := runtime.FuncForPC(pc)
if fn == nil {
return dunno
}
name := []byte(fn.Name())
// The name includes the path name to the package, which is unnecessary
// since the file name is already included. Plus, it has center dots.
// That is, we see
// runtime/debug.*T·ptrmethod
// and want
// *T.ptrmethod
// Also the package path might contains dot (e.g. code.google.com/...),
// so first eliminate the path prefix
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
name = name[lastslash+1:]
}
if period := bytes.Index(name, dot); period >= 0 {
name = name[period+1:]
}
name = bytes.Replace(name, centerDot, dot, -1)
return name
}
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
// While Martini is in development mode, Recovery will also output the panic as HTML.
func Recovery() Handler {
return func(res http.ResponseWriter, c Context, logger *log.Logger) {
return func(c Context, log *log.Logger) {
defer func() {
if err := recover(); err != nil {
stack := stack(3)
log.Printf("PANIC: %s\n%s", err, stack)
// Lookup the current responsewriter
val := c.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
res := val.Interface().(http.ResponseWriter)
// respond with panic message while in development mode
var body []byte
if Env == Dev {
res.Header().Set("Content-Type", "text/html")
body = []byte(fmt.Sprintf(panicHtml, err, err, stack))
}
res.WriteHeader(http.StatusInternalServerError)
logger.Printf("PANIC: %s\n%s", err, debug.Stack())
if nil != body {
res.Write(body)
}
}
}()

View File

@ -12,15 +12,38 @@ func Test_Recovery(t *testing.T) {
buff := bytes.NewBufferString("")
recorder := httptest.NewRecorder()
setENV(Dev)
m := New()
// replace log for testing
m.Map(log.New(buff, "[martini] ", 0))
m.Use(func(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "unpredictable")
})
m.Use(Recovery())
m.Use(func(res http.ResponseWriter, req *http.Request) {
panic("here is a panic!")
})
m.ServeHTTP(recorder, (*http.Request)(nil))
expect(t, recorder.Code, http.StatusInternalServerError)
expect(t, recorder.HeaderMap.Get("Content-Type"), "text/html")
refute(t, recorder.Body.Len(), 0)
refute(t, len(buff.String()), 0)
}
func Test_Recovery_ResponseWriter(t *testing.T) {
recorder := httptest.NewRecorder()
recorder2 := httptest.NewRecorder()
setENV(Dev)
m := New()
m.Use(Recovery())
m.Use(func(c Context) {
c.MapTo(recorder2, (*http.ResponseWriter)(nil))
panic("here is a panic!")
})
m.ServeHTTP(recorder, (*http.Request)(nil))
expect(t, recorder2.Code, http.StatusInternalServerError)
expect(t, recorder2.HeaderMap.Get("Content-Type"), "text/html")
refute(t, recorder2.Body.Len(), 0)
}

View File

@ -12,26 +12,35 @@ import (
// if the functionality calls for it.
type ResponseWriter interface {
http.ResponseWriter
http.Flusher
// Status returns the status code of the response or 0 if the response has not been written.
Status() int
// Written returns whether or not the ResponseWriter has been written.
Written() bool
// Size returns the size of the response body.
Size() int
// Before allows for a function to be called before the ResponseWriter has been written to. This is
// useful for setting headers or any other operations that must happen before a response has been written.
Before(BeforeFunc)
}
// BeforeFunc is a function that is called before the ResponseWriter has been written to.
type BeforeFunc func(ResponseWriter)
// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter
func NewResponseWriter(rw http.ResponseWriter) ResponseWriter {
return &responseWriter{rw, 0, 0}
return &responseWriter{rw, 0, 0, nil}
}
type responseWriter struct {
http.ResponseWriter
status int
size int
status int
size int
beforeFuncs []BeforeFunc
}
func (rw *responseWriter) WriteHeader(s int) {
rw.callBefore()
rw.ResponseWriter.WriteHeader(s)
rw.status = s
}
@ -39,7 +48,7 @@ func (rw *responseWriter) WriteHeader(s int) {
func (rw *responseWriter) Write(b []byte) (int, error) {
if !rw.Written() {
// The status will be StatusOK if WriteHeader has not been called yet
rw.status = http.StatusOK
rw.WriteHeader(http.StatusOK)
}
size, err := rw.ResponseWriter.Write(b)
rw.size += size
@ -58,10 +67,31 @@ func (rw *responseWriter) Written() bool {
return rw.status != 0
}
func (rw *responseWriter) Before(before BeforeFunc) {
rw.beforeFuncs = append(rw.beforeFuncs, before)
}
func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hijacker, ok := rw.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("ResponseWriter doesn't support Hijacker interface")
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
}
return hijacker.Hijack()
}
func (rw *responseWriter) CloseNotify() <-chan bool {
return rw.ResponseWriter.(http.CloseNotifier).CloseNotify()
}
func (rw *responseWriter) callBefore() {
for i := len(rw.beforeFuncs) - 1; i >= 0; i-- {
rw.beforeFuncs[i](rw)
}
}
func (rw *responseWriter) Flush() {
flusher, ok := rw.ResponseWriter.(http.Flusher)
if ok {
flusher.Flush()
}
}

View File

@ -2,12 +2,34 @@ package martini
import (
"bufio"
"io"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"
)
type closeNotifyingRecorder struct {
*httptest.ResponseRecorder
closed chan bool
}
func newCloseNotifyingRecorder() *closeNotifyingRecorder {
return &closeNotifyingRecorder{
httptest.NewRecorder(),
make(chan bool, 1),
}
}
func (c *closeNotifyingRecorder) close() {
c.closed <- true
}
func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
return c.closed
}
type hijackableResponse struct {
Hijacked bool
}
@ -63,6 +85,27 @@ func Test_ResponseWriter_WritingHeader(t *testing.T) {
expect(t, rw.Size(), 0)
}
func Test_ResponseWriter_Before(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
result := ""
rw.Before(func(ResponseWriter) {
result += "foo"
})
rw.Before(func(ResponseWriter) {
result += "bar"
})
rw.WriteHeader(http.StatusNotFound)
expect(t, rec.Code, rw.Status())
expect(t, rec.Body.String(), "")
expect(t, rw.Status(), http.StatusNotFound)
expect(t, rw.Size(), 0)
expect(t, result, "barfoo")
}
func Test_ResponseWriter_Hijack(t *testing.T) {
hijackable := newHijackableResponse()
rw := NewResponseWriter(hijackable)
@ -74,3 +117,72 @@ func Test_ResponseWriter_Hijack(t *testing.T) {
}
expect(t, hijackable.Hijacked, true)
}
func Test_ResponseWrite_Hijack_NotOK(t *testing.T) {
hijackable := new(http.ResponseWriter)
rw := NewResponseWriter(*hijackable)
hijacker, ok := rw.(http.Hijacker)
expect(t, ok, true)
_, _, err := hijacker.Hijack()
refute(t, err, nil)
}
func Test_ResponseWriter_CloseNotify(t *testing.T) {
rec := newCloseNotifyingRecorder()
rw := NewResponseWriter(rec)
closed := false
notifier := rw.(http.CloseNotifier).CloseNotify()
rec.close()
select {
case <-notifier:
closed = true
case <-time.After(time.Second):
}
expect(t, closed, true)
}
func Test_ResponseWriter_Flusher(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
_, ok := rw.(http.Flusher)
expect(t, ok, true)
}
func Test_ResponseWriter_FlusherHandler(t *testing.T) {
// New martini instance
m := Classic()
m.Get("/events", func(w http.ResponseWriter, r *http.Request) {
f, ok := w.(http.Flusher)
expect(t, ok, true)
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
for i := 0; i < 2; i++ {
time.Sleep(10 * time.Millisecond)
io.WriteString(w, "data: Hello\n\n")
f.Flush()
}
})
recorder := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/events", nil)
m.ServeHTTP(recorder, r)
if recorder.Code != 200 {
t.Error("Response not 200")
}
if recorder.Body.String() != "data: Hello\n\ndata: Hello\n\n" {
t.Error("Didn't receive correct body, got:", recorder.Body.String())
}
}

View File

@ -0,0 +1,43 @@
package martini
import (
"github.com/codegangsta/inject"
"net/http"
"reflect"
)
// ReturnHandler is a service that Martini provides that is called
// when a route handler returns something. The ReturnHandler is
// responsible for writing to the ResponseWriter based on the values
// that are passed into this function.
type ReturnHandler func(Context, []reflect.Value)
func defaultReturnHandler() ReturnHandler {
return func(ctx Context, vals []reflect.Value) {
rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
res := rv.Interface().(http.ResponseWriter)
var responseVal reflect.Value
if len(vals) > 1 && vals[0].Kind() == reflect.Int {
res.WriteHeader(int(vals[0].Int()))
responseVal = vals[1]
} else if len(vals) > 0 {
responseVal = vals[0]
}
if canDeref(responseVal) {
responseVal = responseVal.Elem()
}
if isByteSlice(responseVal) {
res.Write(responseVal.Bytes())
} else {
res.Write([]byte(responseVal.String()))
}
}
}
func isByteSlice(val reflect.Value) bool {
return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8
}
func canDeref(val reflect.Value) bool {
return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr
}

View File

@ -2,7 +2,6 @@ package martini
import (
"fmt"
"github.com/codegangsta/inject"
"net/http"
"reflect"
"regexp"
@ -14,6 +13,10 @@ type Params map[string]string
// Router is Martini's de-facto routing interface. Supports HTTP verbs, stacked handlers, and dependency injection.
type Router interface {
Routes
// Group adds a group where related routes can be added.
Group(string, func(Router), ...Handler)
// Get adds a route for a HTTP GET request to the specified matching pattern.
Get(string, ...Handler) Route
// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
@ -26,24 +29,46 @@ type Router interface {
Delete(string, ...Handler) Route
// Options adds a route for a HTTP OPTIONS request to the specified matching pattern.
Options(string, ...Handler) Route
// Head adds a route for a HTTP HEAD request to the specified matching pattern.
Head(string, ...Handler) Route
// Any adds a route for any HTTP method request to the specified matching pattern.
Any(string, ...Handler) Route
// NotFound sets the handler that is called when a no route matches a request. Throws a basic 404 by default.
NotFound(Handler)
// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
NotFound(...Handler)
// Handle is the entry point for routing. This is used as a martini.Handler
Handle(http.ResponseWriter, *http.Request, Context)
}
type router struct {
routes []*route
notFound Handler
routes []*route
notFounds []Handler
groups []group
}
type group struct {
pattern string
handlers []Handler
}
// NewRouter creates a new Router instance.
// If you aren't using ClassicMartini, then you can add Routes as a
// service with:
//
// m := martini.New()
// r := martini.NewRouter()
// m.MapTo(r, (*martini.Routes)(nil))
//
// If you are using ClassicMartini, then this is done for you.
func NewRouter() Router {
return &router{notFound: http.NotFound}
return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}
func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
r.groups = append(r.groups, group{pattern, h})
fn(r)
r.groups = r.groups[:len(r.groups)-1]
}
func (r *router) Get(pattern string, h ...Handler) Route {
@ -70,6 +95,10 @@ func (r *router) Options(pattern string, h ...Handler) Route {
return r.addRoute("OPTIONS", pattern, h)
}
func (r *router) Head(pattern string, h ...Handler) Route {
return r.addRoute("HEAD", pattern, h)
}
func (r *router) Any(pattern string, h ...Handler) Route {
return r.addRoute("*", pattern, h)
}
@ -80,38 +109,52 @@ func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Cont
if ok {
params := Params(vals)
context.Map(params)
r := routes{}
context.MapTo(r, (*Routes)(nil))
_, err := context.Invoke(route.Handle)
if err != nil {
panic(err)
}
route.Handle(context, res)
return
}
}
// no routes exist, 404
_, err := context.Invoke(r.notFound)
if err != nil {
panic(err)
}
c := &routeContext{context, 0, r.notFounds}
context.MapTo(c, (*Context)(nil))
c.run()
}
func (r *router) NotFound(handler Handler) {
r.notFound = handler
func (r *router) NotFound(handler ...Handler) {
r.notFounds = handler
}
func (r *router) addRoute(method string, pattern string, handlers []Handler) *route {
if len(r.groups) > 0 {
group := r.groups[len(r.groups)-1]
pattern = group.pattern + pattern
h := make([]Handler, len(group.handlers)+len(handlers))
copy(h, group.handlers)
copy(h[len(group.handlers):], handlers)
handlers = h
}
route := newRoute(method, pattern, handlers)
route.Validate()
r.routes = append(r.routes, route)
return route
}
func (r *router) findRoute(name string) *route {
for _, route := range r.routes {
if route.name == name {
return route
}
}
return nil
}
// Route is an interface representing a Route in Martini's routing layer.
type Route interface {
// URLWith returns a rendering of the Route's url with the given string params.
URLWith([]string) string
Name(string)
}
type route struct {
@ -119,10 +162,11 @@ type route struct {
regex *regexp.Regexp
handlers []Handler
pattern string
name string
}
func newRoute(method string, pattern string, handlers []Handler) *route {
route := route{method, nil, handlers, pattern}
route := route{method, nil, handlers, pattern, ""}
r := regexp.MustCompile(`:[^/#?()\.\\]+`)
pattern = r.ReplaceAllStringFunc(pattern, func(m string) string {
return fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:])
@ -138,9 +182,13 @@ func newRoute(method string, pattern string, handlers []Handler) *route {
return &route
}
func (r route) MatchMethod(method string) bool {
return r.method == "*" || method == r.method || (method == "HEAD" && r.method == "GET")
}
func (r route) Match(method string, path string) (bool, map[string]string) {
// add Any method matching support
if r.method != "*" && method != r.method {
if !r.MatchMethod(method) {
return false, nil
}
@ -191,16 +239,26 @@ func (r *route) URLWith(args []string) string {
return r.pattern
}
func (r *route) Name(name string) {
r.name = name
}
// Routes is a helper service for Martini's routing layer.
type Routes interface {
// URLFor returns a rendered URL for the given route. Optional params can be passed to fulfill named parameters in the route.
URLFor(route Route, params ...interface{}) string
URLFor(name string, params ...interface{}) string
// MethodsFor returns an array of methods available for the path
MethodsFor(path string) []string
}
type routes struct{}
// URLFor returns the url for the given route name.
func (r routes) URLFor(route Route, params ...interface{}) string {
func (r *router) URLFor(name string, params ...interface{}) string {
route := r.findRoute(name)
if route == nil {
panic("route not found")
}
var args []string
for _, param := range params {
switch v := param.(type) {
@ -210,7 +268,7 @@ func (r routes) URLFor(route Route, params ...interface{}) string {
args = append(args, v)
default:
if v != nil {
panic("Arguments passed to UrlFor must be integers or strings")
panic("Arguments passed to URLFor must be integers or strings")
}
}
}
@ -218,6 +276,27 @@ func (r routes) URLFor(route Route, params ...interface{}) string {
return route.URLWith(args)
}
func hasMethod(methods []string, method string) bool {
for _, v := range methods {
if v == method {
return true
}
}
return false
}
// MethodsFor returns all methods available for path
func (r *router) MethodsFor(path string) []string {
methods := []string{}
for _, route := range r.routes {
matches := route.regex.FindStringSubmatch(path)
if len(matches) > 0 && matches[0] == path && !hasMethod(methods, route.method) {
methods = append(methods, route.method)
}
}
return methods
}
type routeContext struct {
Context
index int
@ -238,17 +317,14 @@ func (r *routeContext) run() {
}
r.index += 1
// if the handler returned something, write it to
// the http response
rv := r.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
res := rv.Interface().(http.ResponseWriter)
if len(vals) > 1 && vals[0].Kind() == reflect.Int {
res.WriteHeader(int(vals[0].Int()))
res.Write([]byte(vals[1].String()))
} else if len(vals) > 0 {
res.Write([]byte(vals[0].String()))
// if the handler returned something, write it to the http response
if len(vals) > 0 {
ev := r.Get(reflect.TypeOf(ReturnHandler(nil)))
handleReturn := ev.Interface().(ReturnHandler)
handleReturn(r, vals)
}
if r.written() {
if r.Written() {
return
}
}

View File

@ -3,6 +3,7 @@ package martini
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
@ -10,47 +11,41 @@ func Test_Routing(t *testing.T) {
router := NewRouter()
recorder := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:3000/foo", nil)
if err != nil {
t.Error(err)
}
req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
context := New().createContext(recorder, req)
req2, err := http.NewRequest("POST", "http://localhost:3000/bar/bat", nil)
if err != nil {
t.Error(err)
}
req2, _ := http.NewRequest("POST", "http://localhost:3000/bar/bat", nil)
context2 := New().createContext(recorder, req2)
req3, err := http.NewRequest("DELETE", "http://localhost:3000/baz", nil)
if err != nil {
t.Error(err)
}
req3, _ := http.NewRequest("DELETE", "http://localhost:3000/baz", nil)
context3 := New().createContext(recorder, req3)
req4, err := http.NewRequest("PATCH", "http://localhost:3000/bar/foo", nil)
if err != nil {
t.Error(err)
}
req4, _ := http.NewRequest("PATCH", "http://localhost:3000/bar/foo", nil)
context4 := New().createContext(recorder, req4)
req5, err := http.NewRequest("GET", "http://localhost:3000/fez/this/should/match", nil)
if err != nil {
t.Error(err)
}
req5, _ := http.NewRequest("GET", "http://localhost:3000/fez/this/should/match", nil)
context5 := New().createContext(recorder, req5)
req6, err := http.NewRequest("PUT", "http://localhost:3000/pop/blah/blah/blah/bap/foo/", nil)
if err != nil {
t.Error(err)
}
req6, _ := http.NewRequest("PUT", "http://localhost:3000/pop/blah/blah/blah/bap/foo/", nil)
context6 := New().createContext(recorder, req6)
req7, err := http.NewRequest("DELETE", "http://localhost:3000/wap//pow", nil)
if err != nil {
t.Error(err)
}
context7 := New().createContext(recorder, req6)
req7, _ := http.NewRequest("DELETE", "http://localhost:3000/wap//pow", nil)
context7 := New().createContext(recorder, req7)
req8, _ := http.NewRequest("HEAD", "http://localhost:3000/wap//pow", nil)
context8 := New().createContext(recorder, req8)
req9, _ := http.NewRequest("OPTIONS", "http://localhost:3000/opts", nil)
context9 := New().createContext(recorder, req9)
req10, _ := http.NewRequest("HEAD", "http://localhost:3000/foo", nil)
context10 := New().createContext(recorder, req10)
req11, _ := http.NewRequest("GET", "http://localhost:3000/bazz/inga", nil)
context11 := New().createContext(recorder, req11)
req12, _ := http.NewRequest("POST", "http://localhost:3000/bazz/inga", nil)
context12 := New().createContext(recorder, req12)
result := ""
router.Get("/foo", func(req *http.Request) {
@ -84,6 +79,26 @@ func Test_Routing(t *testing.T) {
expect(t, params["_1"], "")
result += "wappow"
})
router.Options("/opts", func() {
result += "opts"
})
router.Head("/wap/**/pow", func(params Params) {
expect(t, params["_1"], "")
result += "wappow"
})
router.Group("/bazz", func(r Router) {
r.Get("/inga", func() {
result += "get"
})
r.Post("/inga", func() {
result += "post"
})
}, func() {
result += "bazz"
}, func() {
result += "inga"
})
router.Handle(recorder, req, context)
router.Handle(recorder, req2, context2)
@ -92,7 +107,12 @@ func Test_Routing(t *testing.T) {
router.Handle(recorder, req5, context5)
router.Handle(recorder, req6, context6)
router.Handle(recorder, req7, context7)
expect(t, result, "foobarbatbarfoofezpopbapwappow")
router.Handle(recorder, req8, context8)
router.Handle(recorder, req9, context9)
router.Handle(recorder, req10, context10)
router.Handle(recorder, req11, context11)
router.Handle(recorder, req12, context12)
expect(t, result, "foobarbatbarfoofezpopbapwappowwappowoptsfoobazzingagetbazzingapost")
expect(t, recorder.Code, http.StatusNotFound)
expect(t, recorder.Body.String(), "404 page not found\n")
}
@ -108,13 +128,16 @@ func Test_RouterHandlerStatusCode(t *testing.T) {
router.Get("/baz", func() (string, string) {
return "baz", "BAZ!"
})
router.Get("/bytes", func() []byte {
return []byte("Bytes!")
})
router.Get("/interface", func() interface{} {
return "Interface!"
})
// code should be 200 if none is returned from the handler
recorder := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:3000/foo", nil)
if err != nil {
t.Error(err)
}
req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
context := New().createContext(recorder, req)
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusOK)
@ -122,10 +145,7 @@ func Test_RouterHandlerStatusCode(t *testing.T) {
// if a status code is returned, it should be used
recorder = httptest.NewRecorder()
req, err = http.NewRequest("GET", "http://localhost:3000/bar", nil)
if err != nil {
t.Error(err)
}
req, _ = http.NewRequest("GET", "http://localhost:3000/bar", nil)
context = New().createContext(recorder, req)
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusForbidden)
@ -133,24 +153,34 @@ func Test_RouterHandlerStatusCode(t *testing.T) {
// shouldn't use the first returned value as a status code if not an integer
recorder = httptest.NewRecorder()
req, err = http.NewRequest("GET", "http://localhost:3000/baz", nil)
if err != nil {
t.Error(err)
}
req, _ = http.NewRequest("GET", "http://localhost:3000/baz", nil)
context = New().createContext(recorder, req)
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusOK)
expect(t, recorder.Body.String(), "baz")
// Should render bytes as a return value as well.
recorder = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "http://localhost:3000/bytes", nil)
context = New().createContext(recorder, req)
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusOK)
expect(t, recorder.Body.String(), "Bytes!")
// Should render interface{} values.
recorder = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "http://localhost:3000/interface", nil)
context = New().createContext(recorder, req)
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusOK)
expect(t, recorder.Body.String(), "Interface!")
}
func Test_RouterHandlerStacking(t *testing.T) {
router := NewRouter()
recorder := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:3000/foo", nil)
if err != nil {
t.Error(err)
}
req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
context := New().createContext(recorder, req)
result := ""
@ -209,6 +239,37 @@ func Test_RouteMatching(t *testing.T) {
}
}
func Test_MethodsFor(t *testing.T) {
router := NewRouter()
recorder := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "http://localhost:3000/foo", nil)
context := New().createContext(recorder, req)
context.MapTo(router, (*Routes)(nil))
router.Post("/foo/bar", func() {
})
router.Post("/fo", func() {
})
router.Get("/foo", func() {
})
router.Put("/foo", func() {
})
router.NotFound(func(routes Routes, w http.ResponseWriter, r *http.Request) {
methods := routes.MethodsFor(r.URL.Path)
if len(methods) != 0 {
w.Header().Set("Allow", strings.Join(methods, ","))
w.WriteHeader(http.StatusMethodNotAllowed)
}
})
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusMethodNotAllowed)
expect(t, recorder.Header().Get("Allow"), "GET,PUT")
}
func Test_NotFound(t *testing.T) {
router := NewRouter()
recorder := httptest.NewRecorder()
@ -225,6 +286,81 @@ func Test_NotFound(t *testing.T) {
expect(t, recorder.Body.String(), "Nope\n")
}
func Test_NotFoundAsHandler(t *testing.T) {
router := NewRouter()
recorder := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
context := New().createContext(recorder, req)
router.NotFound(func() string {
return "not found"
})
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusOK)
expect(t, recorder.Body.String(), "not found")
recorder = httptest.NewRecorder()
context = New().createContext(recorder, req)
router.NotFound(func() (int, string) {
return 404, "not found"
})
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusNotFound)
expect(t, recorder.Body.String(), "not found")
recorder = httptest.NewRecorder()
context = New().createContext(recorder, req)
router.NotFound(func() (int, string) {
return 200, ""
})
router.Handle(recorder, req, context)
expect(t, recorder.Code, http.StatusOK)
expect(t, recorder.Body.String(), "")
}
func Test_NotFoundStacking(t *testing.T) {
router := NewRouter()
recorder := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
context := New().createContext(recorder, req)
result := ""
f1 := func() {
result += "foo"
}
f2 := func(c Context) {
result += "bar"
c.Next()
result += "bing"
}
f3 := func() string {
result += "bat"
return "Not Found"
}
f4 := func() {
result += "baz"
}
router.NotFound(f1, f2, f3, f4)
router.Handle(recorder, req, context)
expect(t, result, "foobarbatbing")
expect(t, recorder.Body.String(), "Not Found")
}
func Test_Any(t *testing.T) {
router := NewRouter()
router.Any("/foo", func(res http.ResponseWriter) {
@ -250,28 +386,25 @@ func Test_Any(t *testing.T) {
func Test_URLFor(t *testing.T) {
router := NewRouter()
var barIDNameRoute, fooRoute, barRoute Route
fooRoute = router.Get("/foo", func() {
router.Get("/foo", func() {
// Nothing
})
}).Name("foo")
barRoute = router.Post("/bar/:id", func(params Params) {
router.Post("/bar/:id", func(params Params) {
// Nothing
})
}).Name("bar")
barIDNameRoute = router.Get("/bar/:id/:name", func(params Params, routes Routes) {
expect(t, routes.URLFor(fooRoute, nil), "/foo")
expect(t, routes.URLFor(barRoute, 5), "/bar/5")
expect(t, routes.URLFor(barIDNameRoute, 5, "john"), "/bar/5/john")
})
router.Get("/bar/:id/:name", func(params Params, routes Routes) {
expect(t, routes.URLFor("foo", nil), "/foo")
expect(t, routes.URLFor("bar", 5), "/bar/5")
expect(t, routes.URLFor("bar_id", 5, "john"), "/bar/5/john")
}).Name("bar_id")
// code should be 200 if none is returned from the handler
recorder := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:3000/bar/foo/bar", nil)
if err != nil {
t.Error(err)
}
req, _ := http.NewRequest("GET", "http://localhost:3000/bar/foo/bar", nil)
context := New().createContext(recorder, req)
context.MapTo(router, (*Routes)(nil))
router.Handle(recorder, req, context)
}

View File

@ -7,11 +7,61 @@ import (
"strings"
)
// StaticOptions is a struct for specifying configuration options for the martini.Static middleware.
type StaticOptions struct {
// Prefix is the optional prefix used to serve the static directory content
Prefix string
// SkipLogging will disable [Static] log messages when a static file is served.
SkipLogging bool
// IndexFile defines which file to serve as index if it exists.
IndexFile string
// Expires defines which user-defined function to use for producing a HTTP Expires Header
// https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
Expires func() string
}
func prepareStaticOptions(options []StaticOptions) StaticOptions {
var opt StaticOptions
if len(options) > 0 {
opt = options[0]
}
// Defaults
if len(opt.IndexFile) == 0 {
opt.IndexFile = "index.html"
}
// Normalize the prefix if provided
if opt.Prefix != "" {
// Ensure we have a leading '/'
if opt.Prefix[0] != '/' {
opt.Prefix = "/" + opt.Prefix
}
// Remove any trailing '/'
opt.Prefix = strings.TrimRight(opt.Prefix, "/")
}
return opt
}
// Static returns a middleware handler that serves static files in the given directory.
func Static(directory string) Handler {
func Static(directory string, staticOpt ...StaticOptions) Handler {
dir := http.Dir(directory)
opt := prepareStaticOptions(staticOpt)
return func(res http.ResponseWriter, req *http.Request, log *log.Logger) {
if req.Method != "GET" && req.Method != "HEAD" {
return
}
file := req.URL.Path
// if we have a prefix, filter requests by stripping the prefix
if opt.Prefix != "" {
if !strings.HasPrefix(file, opt.Prefix) {
return
}
file = file[len(opt.Prefix):]
if file != "" && file[0] != '/' {
return
}
}
f, err := dir.Open(file)
if err != nil {
// discard the error?
@ -24,16 +74,15 @@ func Static(directory string) Handler {
return
}
// Try to serve index.html
// try to serve index file
if fi.IsDir() {
// redirect if missing trailing slash
if !strings.HasSuffix(file, "/") {
http.Redirect(res, req, file+"/", http.StatusFound)
if !strings.HasSuffix(req.URL.Path, "/") {
http.Redirect(res, req, req.URL.Path+"/", http.StatusFound)
return
}
file = path.Join(file, "index.html")
file = path.Join(file, opt.IndexFile)
f, err = dir.Open(file)
if err != nil {
return
@ -46,7 +95,15 @@ func Static(directory string) Handler {
}
}
log.Println("[Static] Serving " + file)
if !opt.SkipLogging {
log.Println("[Static] Serving " + file)
}
// Add an Expires header to the static content
if opt.Expires != nil {
res.Header().Set("Expires", opt.Expires())
}
http.ServeContent(res, req, file, fi.ModTime(), f)
}
}

View File

@ -0,0 +1,200 @@
package martini
import (
"bytes"
"log"
"net/http"
"net/http/httptest"
"testing"
"github.com/codegangsta/inject"
)
func Test_Static(t *testing.T) {
response := httptest.NewRecorder()
response.Body = new(bytes.Buffer)
m := New()
r := NewRouter()
m.Use(Static("."))
m.Action(r.Handle)
req, err := http.NewRequest("GET", "http://localhost:3000/martini.go", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
expect(t, response.Code, http.StatusOK)
expect(t, response.Header().Get("Expires"), "")
if response.Body.Len() == 0 {
t.Errorf("Got empty body for GET request")
}
}
func Test_Static_Head(t *testing.T) {
response := httptest.NewRecorder()
response.Body = new(bytes.Buffer)
m := New()
r := NewRouter()
m.Use(Static("."))
m.Action(r.Handle)
req, err := http.NewRequest("HEAD", "http://localhost:3000/martini.go", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
expect(t, response.Code, http.StatusOK)
if response.Body.Len() != 0 {
t.Errorf("Got non-empty body for HEAD request")
}
}
func Test_Static_As_Post(t *testing.T) {
response := httptest.NewRecorder()
m := New()
r := NewRouter()
m.Use(Static("."))
m.Action(r.Handle)
req, err := http.NewRequest("POST", "http://localhost:3000/martini.go", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
expect(t, response.Code, http.StatusNotFound)
}
func Test_Static_BadDir(t *testing.T) {
response := httptest.NewRecorder()
m := Classic()
req, err := http.NewRequest("GET", "http://localhost:3000/martini.go", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
refute(t, response.Code, http.StatusOK)
}
func Test_Static_Options_Logging(t *testing.T) {
response := httptest.NewRecorder()
var buffer bytes.Buffer
m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(&buffer, "[martini] ", 0)}
m.Map(m.logger)
m.Map(defaultReturnHandler())
opt := StaticOptions{}
m.Use(Static(".", opt))
req, err := http.NewRequest("GET", "http://localhost:3000/martini.go", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
expect(t, response.Code, http.StatusOK)
expect(t, buffer.String(), "[martini] [Static] Serving /martini.go\n")
// Now without logging
m.Handlers()
buffer.Reset()
// This should disable logging
opt.SkipLogging = true
m.Use(Static(".", opt))
m.ServeHTTP(response, req)
expect(t, response.Code, http.StatusOK)
expect(t, buffer.String(), "")
}
func Test_Static_Options_ServeIndex(t *testing.T) {
response := httptest.NewRecorder()
var buffer bytes.Buffer
m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(&buffer, "[martini] ", 0)}
m.Map(m.logger)
m.Map(defaultReturnHandler())
opt := StaticOptions{IndexFile: "martini.go"} // Define martini.go as index file
m.Use(Static(".", opt))
req, err := http.NewRequest("GET", "http://localhost:3000/", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
expect(t, response.Code, http.StatusOK)
expect(t, buffer.String(), "[martini] [Static] Serving /martini.go\n")
}
func Test_Static_Options_Prefix(t *testing.T) {
response := httptest.NewRecorder()
var buffer bytes.Buffer
m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(&buffer, "[martini] ", 0)}
m.Map(m.logger)
m.Map(defaultReturnHandler())
// Serve current directory under /public
m.Use(Static(".", StaticOptions{Prefix: "/public"}))
// Check file content behaviour
req, err := http.NewRequest("GET", "http://localhost:3000/public/martini.go", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
expect(t, response.Code, http.StatusOK)
expect(t, buffer.String(), "[martini] [Static] Serving /martini.go\n")
}
func Test_Static_Options_Expires(t *testing.T) {
response := httptest.NewRecorder()
var buffer bytes.Buffer
m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(&buffer, "[martini] ", 0)}
m.Map(m.logger)
m.Map(defaultReturnHandler())
// Serve current directory under /public
m.Use(Static(".", StaticOptions{Expires: func() string { return "46" }}))
// Check file content behaviour
req, err := http.NewRequest("GET", "http://localhost:3000/martini.go", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
expect(t, response.Header().Get("Expires"), "46")
}
func Test_Static_Redirect(t *testing.T) {
response := httptest.NewRecorder()
m := New()
m.Use(Static(".", StaticOptions{Prefix: "/public"}))
req, err := http.NewRequest("GET", "http://localhost:3000/public", nil)
if err != nil {
t.Error(err)
}
m.ServeHTTP(response, req)
expect(t, response.Code, http.StatusFound)
expect(t, response.Header().Get("Location"), "/public/")
}

View File

@ -0,0 +1,311 @@
# Martini [![wercker status](https://app.wercker.com/status/174bef7e3c999e103cacfe2770102266 "wercker status")](https://app.wercker.com/project/bykey/174bef7e3c999e103cacfe2770102266) [![GoDoc](https://godoc.org/github.com/codegangsta/martini?status.png)](http://godoc.org/github.com/codegangsta/martini)
Martini是一个强大为了编写模块化Web应用而生的GO语言框架.
## 第一个应用
在你安装了GO语言和设置了你的[GOPATH](http://golang.org/doc/code.html#GOPATH)之后, 创建你的自己的`.go`文件, 这里我们假设它的名字叫做 `server.go`.
~~~ go
package main
import "github.com/codegangsta/martini"
func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world!"
})
m.Run()
}
~~~
然后安装Martini的包. (注意Martini需要Go语言1.1或者以上的版本支持):
~~~
go get github.com/codegangsta/martini
~~~
最后运行你的服务:
~~~
go run server.go
~~~
这时你将会有一个Martini的服务监听了, 地址是: `localhost:3000`.
## 获得帮助
请加入: [邮件列表](https://groups.google.com/forum/#!forum/martini-go)
或者可以查看在线演示地址: [演示视频](http://martini.codegangsta.io/#demo)
## 功能列表
* 使用极其简单.
* 无侵入式的设计.
* 很好的与其他的Go语言包协同使用.
* 超赞的路径匹配和路由.
* 模块化的设计 - 容易插入功能件,也容易将其拔出来.
* 已有很多的中间件可以直接使用.
* 框架内已拥有很好的开箱即用的功能支持.
* **完全兼容[http.HandlerFunc](http://godoc.org/net/http#HandlerFunc)接口.**
## 更多中间件
更多的中间件和功能组件, 请查看代码仓库: [martini-contrib](https://github.com/martini-contrib).
## 目录
* [核心 Martini](#classic-martini)
* [处理器](#handlers)
* [路由](#routing)
* [服务](#services)
* [服务静态文件](#serving-static-files)
* [中间件处理器](#middleware-handlers)
* [Next()](#next)
* [常见问答](#faq)
## 核心 Martini
为了更快速的启用Martini, [martini.Classic()](http://godoc.org/github.com/codegangsta/martini#Classic) 提供了一些默认的方便Web开发的工具:
~~~ go
m := martini.Classic()
// ... middleware and routing goes here
m.Run()
~~~
下面是Martini核心已经包含的功能 [martini.Classic()](http://godoc.org/github.com/codegangsta/martini#Classic):
* Request/Response Logging (请求/相应日志) - [martini.Logger](http://godoc.org/github.com/codegangsta/martini#Logger)
* Panic Recovery (容错) - [martini.Recovery](http://godoc.org/github.com/codegangsta/martini#Recovery)
* Static File serving (静态文件服务) - [martini.Static](http://godoc.org/github.com/codegangsta/martini#Static)
* Routing (路由) - [martini.Router](http://godoc.org/github.com/codegangsta/martini#Router)
### 处理器
处理器是Martini的灵魂和核心所在. 一个处理器基本上可以是任何的函数:
~~~ go
m.Get("/", func() {
println("hello world")
})
~~~
#### 返回值
当一个处理器返回结果的时候, Martini将会把返回值作为字符串写入到当前的[http.ResponseWriter](http://godoc.org/net/http#ResponseWriter)里面:
~~~ go
m.Get("/", func() string {
return "hello world" // HTTP 200 : "hello world"
})
~~~
另外你也可以选择性的返回多一个状态码:
~~~ go
m.Get("/", func() (int, string) {
return 418, "i'm a teapot" // HTTP 418 : "i'm a teapot"
})
~~~
#### 服务的注入
处理器是通过反射来调用的. Martini 通过*Dependency Injection* *(依赖注入)* 来为处理器注入参数列表. **这样使得Martini与Go语言的`http.HandlerFunc`接口完全兼容.**
如果你加入一个参数到你的处理器, Martini将会搜索它参数列表中的服务并且通过类型判断来解决依赖关系:
~~~ go
m.Get("/", func(res http.ResponseWriter, req *http.Request) { // res 和 req 是通过Martini注入的
res.WriteHeader(200) // HTTP 200
})
~~~
下面的这些服务已经被包含在核心Martini中: [martini.Classic()](http://godoc.org/github.com/codegangsta/martini#Classic):
* [*log.Logger](http://godoc.org/log#Logger) - Martini的全局日志.
* [martini.Context](http://godoc.org/github.com/codegangsta/martini#Context) - http request context (请求上下文).
* [martini.Params](http://godoc.org/github.com/codegangsta/martini#Params) - `map[string]string` of named params found by route matching. (名字和参数键值对的参数列表)
* [martini.Routes](http://godoc.org/github.com/codegangsta/martini#Routes) - Route helper service. (路由协助处理)
* [http.ResponseWriter](http://godoc.org/net/http/#ResponseWriter) - http Response writer interface. (响应结果的流接口)
* [*http.Request](http://godoc.org/net/http/#Request) - http Request. http请求)
### 路由
在Martini中, 路由是一个HTTP方法配对一个URL匹配模型. 每一个路由可以对应一个或多个处理器方法:
~~~ go
m.Get("/", func() {
// 显示
})
m.Patch("/", func() {
// 更新
})
m.Post("/", func() {
// 创建
})
m.Put("/", func() {
// 替换
})
m.Delete("/", func() {
// 删除
})
m.Options("/", func() {
// http 选项
})
m.NotFound(func() {
// 处理 404
})
~~~
路由匹配的顺序是按照他们被定义的顺序执行的. 最先被定义的路由将会首先被用户请求匹配并调用.
路由模型可能包含参数列表, 可以通过[martini.Params](http://godoc.org/github.com/codegangsta/martini#Params)服务来获取:
~~~ go
m.Get("/hello/:name", func(params martini.Params) string {
return "Hello " + params["name"]
})
~~~
路由匹配可以通过正则表达式或者glob的形式:
~~~ go
m.Get("/hello/**", func(params martini.Params) string {
return "Hello " + params["_1"]
})
~~~
路由处理器可以被相互叠加使用, 例如很有用的地方可以是在验证和授权的时候:
~~~ go
m.Get("/secret", authorize, func() {
// 该方法将会在authorize方法没有输出结果的时候执行.
})
~~~
### 服务
服务即是被注入到处理器中的参数. 你可以映射一个服务到 *全局* 或者 *请求* 的级别.
#### 全局映射
如果一个Martini实现了inject.Injector的接口, 那么映射成为一个服务就非常简单:
~~~ go
db := &MyDatabase{}
m := martini.Classic()
m.Map(db) // *MyDatabase 这个服务将可以在所有的处理器中被使用到.
// ...
m.Run()
~~~
#### 请求级别的映射
映射在请求级别的服务可以用[martini.Context](http://godoc.org/github.com/codegangsta/martini#Context)来完成:
~~~ go
func MyCustomLoggerHandler(c martini.Context, req *http.Request) {
logger := &MyCustomLogger{req}
c.Map(logger) // 映射成为了 *MyCustomLogger
}
~~~
#### 映射值到接口
关于服务最强悍的地方之一就是它能够映射服务到接口. 例如说, 假设你想要覆盖[http.ResponseWriter](http://godoc.org/net/http#ResponseWriter)成为一个对象, 那么你可以封装它并包含你自己的额外操作, 你可以如下这样来编写你的处理器:
~~~ go
func WrapResponseWriter(res http.ResponseWriter, c martini.Context) {
rw := NewSpecialResponseWriter(res)
c.MapTo(rw, (*http.ResponseWriter)(nil)) // 覆盖 ResponseWriter 成为我们封装过的 ResponseWriter
}
~~~
### 服务静态文件
[martini.Classic()](http://godoc.org/github.com/codegangsta/martini#Classic) 默认会服务位于你服务器环境根目录下的"public"文件夹.
你可以通过加入[martini.Static](http://godoc.org/github.com/codegangsta/martini#Static)的处理器来加入更多的静态文件服务的文件夹.
~~~ go
m.Use(martini.Static("assets")) // 也会服务静态文件于"assets"的文件夹
~~~
## 中间件处理器
中间件处理器是工作于请求和路由之间的. 本质上来说和Martini其他的处理器没有分别. 你可以像如下这样添加一个中间件处理器到它的堆中:
~~~ go
m.Use(func() {
// 做一些中间件该做的事情
})
~~~
你可以通过`Handlers`函数对中间件堆有完全的控制. 它将会替换掉之前的任何设置过的处理器:
~~~ go
m.Handlers(
Middleware1,
Middleware2,
Middleware3,
)
~~~
中间件处理器可以非常好处理一些功能像logging(日志), authorization(授权), authentication(认证), sessions(会话), error pages(错误页面), 以及任何其他的操作需要在http请求发生之前或者之后的:
~~~ go
// 验证api密匙
m.Use(func(res http.ResponseWriter, req *http.Request) {
if req.Header.Get("X-API-KEY") != "secret123" {
res.WriteHeader(http.StatusUnauthorized)
}
})
~~~
### Next()
[Context.Next()](http://godoc.org/github.com/codegangsta/martini#Context)是一个可选的函数用于中间件处理器暂时放弃执行直到其他的处理器都执行完毕. 这样就可以很好的处理在http请求完成后需要做的操作.
~~~ go
// log 记录请求完成前后 (*译者注: 很巧妙,掌声鼓励.)
m.Use(func(c martini.Context, log *log.Logger){
log.Println("before a request")
c.Next()
log.Println("after a request")
})
~~~
## 常见问答
### 我在哪里可以找到中间件资源?
可以查看 [martini-contrib](https://github.com/martini-contrib) 项目. 如果看了觉得没有什么好货色, 可以联系martini-contrib的团队成员为你创建一个新的代码资源库.
* [auth](https://github.com/martini-contrib/auth) - 认证处理器.
* [binding](https://github.com/martini-contrib/binding) - 映射/验证raw请求到结构体(structure)里的处理器
* [gzip](https://github.com/martini-contrib/gzip) - 加入giz支持的处理器
* [render](https://github.com/martini-contrib/render) - 渲染JSON和HTML模板的处理器.
* [acceptlang](https://github.com/martini-contrib/acceptlang) - 解析`Accept-Language` HTTP报头的处理器.
* [sessions](https://github.com/martini-contrib/sessions) - 提供会话服务支持的处理器.
* [strip](https://github.com/martini-contrib/strip) - URL Prefix stripping.
* [method](https://github.com/martini-contrib/method) - HTTP method overriding via Header or form fields.
* [secure](https://github.com/martini-contrib/secure) - Implements a few quick security wins.
* [encoder](https://github.com/martini-contrib/encoder) - Encoder service for rendering data in several formats and content negotiation.
### 我如何整合到我现有的服务器中?
由于Martini实现了 `http.Handler`, 所以它可以很简单的应用到现有Go服务器的子集中. 例如说这是一段在Google App Engine中的示例:
~~~ go
package hello
import (
"net/http"
"github.com/codegangsta/martini"
)
func init() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world!"
})
http.Handle("/", m)
}
~~~
### 我如何修改port/host?
Martini的`Run`函数会检查PORT和HOST的环境变量并使用它们. 否则Martini将会默认使用localhost:3000
如果想要自定义PORT和HOST, 使用`http.ListenAndServe`函数来代替.
~~~ go
m := martini.Classic()
// ...
http.ListenAndServe(":8080", m)
~~~
## 贡献
Martini项目想要保持简单且干净的代码. 大部分的代码应该贡献到[martini-contrib](https://github.com/martini-contrib)组织中作为一个项目. 如果你想要贡献Martini的核心代码也可以发起一个Pull Request.
## 关于
灵感来自于 [express](https://github.com/visionmedia/express) 和 [sinatra](https://github.com/sinatra/sinatra)
Martini作者 [Code Gangsta](http://codegangsta.io/)
译者: [Leon](http://github.com/leonli)

View File

@ -0,0 +1 @@
box: wercker/golang@1.1.1