diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index ce88b2470..a8e9902b6 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -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" } ] } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control.go index be06dd674..e2101903b 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control.go @@ -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") diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc2292_darwin.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go similarity index 75% rename from Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc2292_darwin.go rename to Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go index f09ad7b6f..47196c58f 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc2292_darwin.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go @@ -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) } } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_linux.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_linux.go deleted file mode 100644 index 3e73f3b16..000000000 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_linux.go +++ /dev/null @@ -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 -} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_plan9.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go similarity index 87% rename from Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_plan9.go rename to Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go index 052b229f6..4419bf518 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_plan9.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go @@ -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) { diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_bsd.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go similarity index 74% rename from Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_bsd.go rename to Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go index 0b42c0b71..882a77be3 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_bsd.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go @@ -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) } } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_test.go deleted file mode 100644 index 0f993210b..000000000 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/control_test.go +++ /dev/null @@ -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) - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_plan9.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go similarity index 88% rename from Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_plan9.go rename to Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go index 4c26be259..9c760dfcd 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_plan9.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_plan9.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_stub.go similarity index 86% rename from Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_plan9.go rename to Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_stub.go index 6108443f5..1b4219590 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_plan9.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/genericopt_stub.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper.go index ec87a0f73..649348481 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper.go @@ -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 { diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_plan9.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_stub.go similarity index 78% rename from Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_plan9.go rename to Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_stub.go index 9f12a3a10..0b5b35a3d 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_plan9.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/helper_stub.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp.go index 9fb6a483c..b45486fd8 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_bsd.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_bsd.go index fd5f83ef9..b92e0d9e0 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_bsd.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_bsd.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_linux.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_linux.go index 0a70f5f7c..7b3e2bd49 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_linux.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_linux.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_plan9.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_stub.go similarity index 59% rename from Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_plan9.go rename to Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_stub.go index b97c8280b..312269cf3 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_plan9.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_stub.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_test.go index a7833dd16..091451341 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_test.go @@ -14,9 +14,27 @@ import ( "testing" ) +var icmpStringTests = []struct { + in ipv6.ICMPType + out string +}{ + {ipv6.ICMPTypeDestinationUnreachable, "destination unreachable"}, + + {256, ""}, +} + +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 { diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_windows.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_windows.go index b97c8280b..cf4ea4c09 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_windows.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/icmp_windows.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mockicmp_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mockicmp_test.go index e453a9c54..c28935513 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mockicmp_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mockicmp_test.go @@ -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. diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go index 2da546412..708670395 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go @@ -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 - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicast_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicast_test.go index 7267ad4a5..89c37a8a0 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicast_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicast_test.go @@ -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) diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go index 836455f4d..3881e5db4 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go @@ -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) + } + } +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go index 578c04a02..650dadd06 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go @@ -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 { diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/readwrite_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/readwrite_test.go new file mode 100644 index 000000000..ff4e67b88 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/readwrite_test.go @@ -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() +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_darwin.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_darwin.go deleted file mode 100644 index f21802c8c..000000000 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_darwin.go +++ /dev/null @@ -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)) -} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go new file mode 100644 index 000000000..f9fde2581 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go @@ -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)) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go index aa5e6e3ab..352a1617a 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go @@ -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)) } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go index b2557e86b..f13a28f82 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go @@ -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)) } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go index b055cf043..a9f5e1134 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go @@ -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)) } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go index 04d4526a1..aeac8b6a8 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go @@ -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 { diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_bsd.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_bsd.go deleted file mode 100644 index 61073b126..000000000 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_bsd.go +++ /dev/null @@ -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)) -} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_linux.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_linux.go deleted file mode 100644 index 9cea683f2..000000000 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_linux.go +++ /dev/null @@ -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)) -} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_plan9.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go similarity index 80% rename from Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_plan9.go rename to Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go index 53a59f446..2b383a26a 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_plan9.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go @@ -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 } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go index 463f426b9..da9111cd9 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go @@ -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)) } diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_test.go index e489ca83c..9c899e1b3 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sockopt_test.go @@ -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 { diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys.go new file mode 100644 index 000000000..18b1acacd --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys.go @@ -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 +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_bsd.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_bsd.go new file mode 100644 index 000000000..4a08217fe --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_bsd.go @@ -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) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_darwin.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_darwin.go new file mode 100644 index 000000000..3d07dff57 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_darwin.go @@ -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) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_linux.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_linux.go new file mode 100644 index 000000000..d90c8cb8a --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_linux.go @@ -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) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_unix.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_unix.go new file mode 100644 index 000000000..40cdfc4d8 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_unix.go @@ -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 +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_windows.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_windows.go new file mode 100644 index 000000000..c09672f28 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/sys_windows.go @@ -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) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go new file mode 100644 index 000000000..a3866362e --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go @@ -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 +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s new file mode 100644 index 000000000..34c0457fd --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s @@ -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 diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go new file mode 100644 index 000000000..6d4ac0960 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go @@ -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 +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go new file mode 100644 index 000000000..2e4da7b53 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go @@ -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 +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_unix.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_unix.go new file mode 100644 index 000000000..d88dab52c --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/syscall_unix.go @@ -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 +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicast_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicast_test.go index c79847dcd..6ad93ed60 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicast_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicast_test.go @@ -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) diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go index c4a79ea39..e3967ca67 100644 --- a/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go +++ b/Godeps/_workspace/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go @@ -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 { diff --git a/Godeps/_workspace/src/github.com/calmh/ini/ini.go b/Godeps/_workspace/src/github.com/calmh/ini/ini.go index 808450c5e..280b6d935 100644 --- a/Godeps/_workspace/src/github.com/calmh/ini/ini.go +++ b/Godeps/_workspace/src/github.com/calmh/ini/ini.go @@ -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 + } + } +} diff --git a/Godeps/_workspace/src/github.com/calmh/ini/ini_test.go b/Godeps/_workspace/src/github.com/calmh/ini/ini_test.go index 4a313fe5a..8cc0e5a0a 100644 --- a/Godeps/_workspace/src/github.com/calmh/ini/ini_test.go +++ b/Godeps/_workspace/src/github.com/calmh/ini/ini_test.go @@ -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) diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/inject.go b/Godeps/_workspace/src/github.com/codegangsta/inject/inject.go index 17d87081e..dc7342fbb 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/inject/inject.go +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/inject.go @@ -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 { diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/inject_test.go b/Godeps/_workspace/src/github.com/codegangsta/inject/inject_test.go index ffcc174cd..acca573db 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/inject/inject_test.go +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/inject_test.go @@ -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() diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/README.md b/Godeps/_workspace/src/github.com/codegangsta/martini/README.md index bdece746e..bb51d2d56 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/README.md +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/README.md @@ -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/) diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/env.go b/Godeps/_workspace/src/github.com/codegangsta/martini/env.go index 250a99161..9bf15b5f6 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/env.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/env.go @@ -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")) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/env_test.go b/Godeps/_workspace/src/github.com/codegangsta/martini/env_test.go new file mode 100644 index 000000000..739e03e9a --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/env_test.go @@ -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) + } + } +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/go_version.go b/Godeps/_workspace/src/github.com/codegangsta/martini/go_version.go index e76fe162e..bd271a8cf 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/go_version.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/go_version.go @@ -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." +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/logger.go b/Godeps/_workspace/src/github.com/codegangsta/martini/logger.go index ff9d91a1f..3105dc167 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/logger.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/logger.go @@ -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)) diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/martini.go b/Godeps/_workspace/src/github.com/codegangsta/martini/martini.go index 5a71c84dc..7a479b03b 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/martini.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/martini.go @@ -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 } } diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/martini_test.go b/Godeps/_workspace/src/github.com/codegangsta/martini/martini_test.go index ba5a65c13..6789d9e01 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/martini_test.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/martini_test.go @@ -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) + }() + } +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/recovery.go b/Godeps/_workspace/src/github.com/codegangsta/martini/recovery.go index f93a6cee3..722622acd 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/recovery.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/recovery.go @@ -1,18 +1,139 @@ package martini import ( + "bytes" + "fmt" + "io/ioutil" "log" "net/http" - "runtime/debug" + "runtime" + + "github.com/codegangsta/inject" ) +const ( + panicHtml = ` +PANIC: %s + + +

PANIC

+
%s
+
%s
+ +` +) + +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) + } } }() diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/recovery_test.go b/Godeps/_workspace/src/github.com/codegangsta/martini/recovery_test.go index 0c31d32ca..17e2e0113 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/recovery_test.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/recovery_test.go @@ -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) } diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/response_writer.go b/Godeps/_workspace/src/github.com/codegangsta/martini/response_writer.go index 645e2e381..fc1615348 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/response_writer.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/response_writer.go @@ -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() + } +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/response_writer_test.go b/Godeps/_workspace/src/github.com/codegangsta/martini/response_writer_test.go index 96f3af859..6ccb9e09e 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/response_writer_test.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/response_writer_test.go @@ -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()) + } + +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/return_handler.go b/Godeps/_workspace/src/github.com/codegangsta/martini/return_handler.go new file mode 100644 index 000000000..4ea8f34b5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/return_handler.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/router.go b/Godeps/_workspace/src/github.com/codegangsta/martini/router.go index 19bd79fe8..8da1331a4 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/router.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/router.go @@ -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 } } diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/router_test.go b/Godeps/_workspace/src/github.com/codegangsta/martini/router_test.go index 2ad9c8912..cd5255499 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/router_test.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/router_test.go @@ -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) } diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/static.go b/Godeps/_workspace/src/github.com/codegangsta/martini/static.go index 747036817..db5277451 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/martini/static.go +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/static.go @@ -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) } } diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/static_test.go b/Godeps/_workspace/src/github.com/codegangsta/martini/static_test.go new file mode 100644 index 000000000..dad88a856 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/static_test.go @@ -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/") +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/translations/README_zh_cn.md b/Godeps/_workspace/src/github.com/codegangsta/martini/translations/README_zh_cn.md new file mode 100644 index 000000000..f8e1e87e9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/translations/README_zh_cn.md @@ -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) diff --git a/Godeps/_workspace/src/github.com/codegangsta/martini/wercker.yml b/Godeps/_workspace/src/github.com/codegangsta/martini/wercker.yml new file mode 100644 index 000000000..f8bf918a8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/martini/wercker.yml @@ -0,0 +1 @@ +box: wercker/golang@1.1.1 \ No newline at end of file