2012-08-20 11:27:52 -06:00
|
|
|
// Copyright 2012 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package net
|
|
|
|
|
|
|
|
import (
|
net: support IPv6 scoped addressing zone
This CL provides IPv6 scoped addressing zone support as defined
in RFC 4007 for internet protocol family connection setups.
Follwoing types and functions allow a literal IPv6 address with
zone identifer as theirs parameter.
pkg net, func Dial(string, string) (Conn, error)
pkg net, func DialOpt(string, ...DialOption) (Conn, error)
pkg net, func DialTimeout(string, string, time.Duration) (Conn, error)
pkg net, func Listen(string, string) (Listener, error)
pkg net, func ListenPacket(string, string) (PacketConn, error)
pkg net, func ResolveIPAddr(string, string) (*IPAddr, error)
pkg net, func ResolveTCPAddr(string, string) (*TCPAddr, error)
pkg net, func ResolveUDPAddr(string, string) (*UDPAddr, error)
pkg net, type IPAddr struct, Zone string
pkg net, type TCPAddr struct, Zone string
pkg net, type UDPAddr struct, Zone string
Also follwoing methods return a literal IPv6 address with zone
identifier string if possible.
pkg net, method (*IPAddr) String() string
pkg net, method (*TCPAddr) String() string
pkg net, method (*UDPAddr) String() string
Fixes #4234.
Fixes #4501.
Update #5081.
R=rsc, iant
CC=golang-dev
https://golang.org/cl/6816116
2013-03-22 18:57:40 -06:00
|
|
|
"fmt"
|
2013-08-06 04:40:10 -06:00
|
|
|
"io"
|
2012-11-27 14:36:05 -07:00
|
|
|
"reflect"
|
2012-08-20 11:27:52 -06:00
|
|
|
"runtime"
|
2013-08-04 13:31:23 -06:00
|
|
|
"sync"
|
2012-08-20 11:27:52 -06:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2013-03-02 19:25:49 -07:00
|
|
|
func BenchmarkTCP4OneShot(b *testing.B) {
|
|
|
|
benchmarkTCP(b, false, false, "127.0.0.1:0")
|
2012-08-20 11:27:52 -06:00
|
|
|
}
|
|
|
|
|
2013-03-02 19:25:49 -07:00
|
|
|
func BenchmarkTCP4OneShotTimeout(b *testing.B) {
|
|
|
|
benchmarkTCP(b, false, true, "127.0.0.1:0")
|
2012-08-20 11:27:52 -06:00
|
|
|
}
|
|
|
|
|
2013-03-02 19:25:49 -07:00
|
|
|
func BenchmarkTCP4Persistent(b *testing.B) {
|
|
|
|
benchmarkTCP(b, true, false, "127.0.0.1:0")
|
2012-08-20 11:27:52 -06:00
|
|
|
}
|
|
|
|
|
2013-03-02 19:25:49 -07:00
|
|
|
func BenchmarkTCP4PersistentTimeout(b *testing.B) {
|
|
|
|
benchmarkTCP(b, true, true, "127.0.0.1:0")
|
2012-08-20 11:27:52 -06:00
|
|
|
}
|
|
|
|
|
2013-03-02 19:25:49 -07:00
|
|
|
func BenchmarkTCP6OneShot(b *testing.B) {
|
|
|
|
if !supportsIPv6 {
|
|
|
|
b.Skip("ipv6 is not supported")
|
|
|
|
}
|
|
|
|
benchmarkTCP(b, false, false, "[::1]:0")
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkTCP6OneShotTimeout(b *testing.B) {
|
|
|
|
if !supportsIPv6 {
|
|
|
|
b.Skip("ipv6 is not supported")
|
|
|
|
}
|
|
|
|
benchmarkTCP(b, false, true, "[::1]:0")
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkTCP6Persistent(b *testing.B) {
|
|
|
|
if !supportsIPv6 {
|
|
|
|
b.Skip("ipv6 is not supported")
|
|
|
|
}
|
|
|
|
benchmarkTCP(b, true, false, "[::1]:0")
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkTCP6PersistentTimeout(b *testing.B) {
|
|
|
|
if !supportsIPv6 {
|
|
|
|
b.Skip("ipv6 is not supported")
|
|
|
|
}
|
|
|
|
benchmarkTCP(b, true, true, "[::1]:0")
|
|
|
|
}
|
|
|
|
|
|
|
|
func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
|
2012-08-20 11:27:52 -06:00
|
|
|
const msgLen = 512
|
|
|
|
conns := b.N
|
|
|
|
numConcurrent := runtime.GOMAXPROCS(-1) * 16
|
|
|
|
msgs := 1
|
|
|
|
if persistent {
|
|
|
|
conns = numConcurrent
|
|
|
|
msgs = b.N / conns
|
|
|
|
if msgs == 0 {
|
|
|
|
msgs = 1
|
|
|
|
}
|
|
|
|
if conns > b.N {
|
|
|
|
conns = b.N
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sendMsg := func(c Conn, buf []byte) bool {
|
|
|
|
n, err := c.Write(buf)
|
|
|
|
if n != len(buf) || err != nil {
|
|
|
|
b.Logf("Write failed: %v", err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
recvMsg := func(c Conn, buf []byte) bool {
|
|
|
|
for read := 0; read != len(buf); {
|
|
|
|
n, err := c.Read(buf)
|
|
|
|
read += n
|
|
|
|
if err != nil {
|
|
|
|
b.Logf("Read failed: %v", err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
2013-03-02 19:25:49 -07:00
|
|
|
ln, err := Listen("tcp", laddr)
|
2012-08-20 11:27:52 -06:00
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("Listen failed: %v", err)
|
|
|
|
}
|
|
|
|
defer ln.Close()
|
|
|
|
// Acceptor.
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Server connection.
|
|
|
|
go func(c Conn) {
|
|
|
|
defer c.Close()
|
|
|
|
if timeout {
|
|
|
|
c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire.
|
|
|
|
}
|
|
|
|
var buf [msgLen]byte
|
|
|
|
for m := 0; m < msgs; m++ {
|
|
|
|
if !recvMsg(c, buf[:]) || !sendMsg(c, buf[:]) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(c)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
sem := make(chan bool, numConcurrent)
|
|
|
|
for i := 0; i < conns; i++ {
|
|
|
|
sem <- true
|
|
|
|
// Client connection.
|
|
|
|
go func() {
|
|
|
|
defer func() {
|
|
|
|
<-sem
|
|
|
|
}()
|
|
|
|
c, err := Dial("tcp", ln.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
b.Logf("Dial failed: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer c.Close()
|
|
|
|
if timeout {
|
|
|
|
c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire.
|
|
|
|
}
|
|
|
|
var buf [msgLen]byte
|
|
|
|
for m := 0; m < msgs; m++ {
|
|
|
|
if !sendMsg(c, buf[:]) || !recvMsg(c, buf[:]) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
for i := 0; i < cap(sem); i++ {
|
|
|
|
sem <- true
|
|
|
|
}
|
|
|
|
}
|
2012-11-12 20:56:28 -07:00
|
|
|
|
2013-03-26 10:06:48 -06:00
|
|
|
type resolveTCPAddrTest struct {
|
2012-11-27 14:36:05 -07:00
|
|
|
net string
|
|
|
|
litAddr string
|
|
|
|
addr *TCPAddr
|
|
|
|
err error
|
2013-03-26 10:06:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
var resolveTCPAddrTests = []resolveTCPAddrTest{
|
2012-11-27 14:36:05 -07:00
|
|
|
{"tcp", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
|
|
|
|
{"tcp4", "127.0.0.1:65535", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
|
|
|
|
|
|
|
|
{"tcp", "[::1]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1}, nil},
|
|
|
|
{"tcp6", "[::1]:65534", &TCPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
|
|
|
|
|
net: support IPv6 scoped addressing zone
This CL provides IPv6 scoped addressing zone support as defined
in RFC 4007 for internet protocol family connection setups.
Follwoing types and functions allow a literal IPv6 address with
zone identifer as theirs parameter.
pkg net, func Dial(string, string) (Conn, error)
pkg net, func DialOpt(string, ...DialOption) (Conn, error)
pkg net, func DialTimeout(string, string, time.Duration) (Conn, error)
pkg net, func Listen(string, string) (Listener, error)
pkg net, func ListenPacket(string, string) (PacketConn, error)
pkg net, func ResolveIPAddr(string, string) (*IPAddr, error)
pkg net, func ResolveTCPAddr(string, string) (*TCPAddr, error)
pkg net, func ResolveUDPAddr(string, string) (*UDPAddr, error)
pkg net, type IPAddr struct, Zone string
pkg net, type TCPAddr struct, Zone string
pkg net, type UDPAddr struct, Zone string
Also follwoing methods return a literal IPv6 address with zone
identifier string if possible.
pkg net, method (*IPAddr) String() string
pkg net, method (*TCPAddr) String() string
pkg net, method (*UDPAddr) String() string
Fixes #4234.
Fixes #4501.
Update #5081.
R=rsc, iant
CC=golang-dev
https://golang.org/cl/6816116
2013-03-22 18:57:40 -06:00
|
|
|
{"tcp", "[::1%en0]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1, Zone: "en0"}, nil},
|
|
|
|
{"tcp6", "[::1%911]:2", &TCPAddr{IP: ParseIP("::1"), Port: 2, Zone: "911"}, nil},
|
|
|
|
|
2012-11-30 22:49:54 -07:00
|
|
|
{"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
|
|
|
|
{"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
|
|
|
|
|
2012-11-27 14:36:05 -07:00
|
|
|
{"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
|
|
|
|
}
|
|
|
|
|
2013-03-26 10:06:48 -06:00
|
|
|
func init() {
|
|
|
|
if ifi := loopbackInterface(); ifi != nil {
|
|
|
|
index := fmt.Sprintf("%v", ifi.Index)
|
|
|
|
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
|
|
|
|
{"tcp6", "[fe80::1%" + ifi.Name + "]:3", &TCPAddr{IP: ParseIP("fe80::1"), Port: 3, Zone: zoneToString(ifi.Index)}, nil},
|
|
|
|
{"tcp6", "[fe80::1%" + index + "]:4", &TCPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: index}, nil},
|
|
|
|
}...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-27 14:36:05 -07:00
|
|
|
func TestResolveTCPAddr(t *testing.T) {
|
|
|
|
for _, tt := range resolveTCPAddrTests {
|
|
|
|
addr, err := ResolveTCPAddr(tt.net, tt.litAddr)
|
|
|
|
if err != tt.err {
|
|
|
|
t.Fatalf("ResolveTCPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(addr, tt.addr) {
|
|
|
|
t.Fatalf("got %#v; expected %#v", addr, tt.addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-12 20:56:28 -07:00
|
|
|
var tcpListenerNameTests = []struct {
|
|
|
|
net string
|
|
|
|
laddr *TCPAddr
|
|
|
|
}{
|
|
|
|
{"tcp4", &TCPAddr{IP: IPv4(127, 0, 0, 1)}},
|
|
|
|
{"tcp4", &TCPAddr{}},
|
|
|
|
{"tcp4", nil},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTCPListenerName(t *testing.T) {
|
|
|
|
if testing.Short() || !*testExternal {
|
2013-01-23 23:32:10 -07:00
|
|
|
t.Skip("skipping test to avoid external network")
|
2012-11-12 20:56:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tcpListenerNameTests {
|
|
|
|
ln, err := ListenTCP(tt.net, tt.laddr)
|
|
|
|
if err != nil {
|
2013-03-23 07:32:19 -06:00
|
|
|
t.Fatalf("ListenTCP failed: %v", err)
|
2012-11-12 20:56:28 -07:00
|
|
|
}
|
|
|
|
defer ln.Close()
|
|
|
|
la := ln.Addr()
|
|
|
|
if a, ok := la.(*TCPAddr); !ok || a.Port == 0 {
|
2013-03-23 07:32:19 -06:00
|
|
|
t.Fatalf("got %v; expected a proper address with non-zero port number", la)
|
2012-11-12 20:56:28 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
net: support IPv6 scoped addressing zone
This CL provides IPv6 scoped addressing zone support as defined
in RFC 4007 for internet protocol family connection setups.
Follwoing types and functions allow a literal IPv6 address with
zone identifer as theirs parameter.
pkg net, func Dial(string, string) (Conn, error)
pkg net, func DialOpt(string, ...DialOption) (Conn, error)
pkg net, func DialTimeout(string, string, time.Duration) (Conn, error)
pkg net, func Listen(string, string) (Listener, error)
pkg net, func ListenPacket(string, string) (PacketConn, error)
pkg net, func ResolveIPAddr(string, string) (*IPAddr, error)
pkg net, func ResolveTCPAddr(string, string) (*TCPAddr, error)
pkg net, func ResolveUDPAddr(string, string) (*UDPAddr, error)
pkg net, type IPAddr struct, Zone string
pkg net, type TCPAddr struct, Zone string
pkg net, type UDPAddr struct, Zone string
Also follwoing methods return a literal IPv6 address with zone
identifier string if possible.
pkg net, method (*IPAddr) String() string
pkg net, method (*TCPAddr) String() string
pkg net, method (*UDPAddr) String() string
Fixes #4234.
Fixes #4501.
Update #5081.
R=rsc, iant
CC=golang-dev
https://golang.org/cl/6816116
2013-03-22 18:57:40 -06:00
|
|
|
|
|
|
|
func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
|
|
|
|
if testing.Short() || !*testExternal {
|
|
|
|
t.Skip("skipping test to avoid external network")
|
|
|
|
}
|
|
|
|
if !supportsIPv6 {
|
|
|
|
t.Skip("ipv6 is not supported")
|
|
|
|
}
|
|
|
|
ifi := loopbackInterface()
|
|
|
|
if ifi == nil {
|
|
|
|
t.Skip("loopback interface not found")
|
|
|
|
}
|
|
|
|
laddr := ipv6LinkLocalUnicastAddr(ifi)
|
|
|
|
if laddr == "" {
|
|
|
|
t.Skip("ipv6 unicast address on loopback not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
type test struct {
|
|
|
|
net, addr string
|
|
|
|
nameLookup bool
|
|
|
|
}
|
|
|
|
var tests = []test{
|
|
|
|
{"tcp", "[" + laddr + "%" + ifi.Name + "]:0", false},
|
|
|
|
{"tcp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
|
|
|
|
}
|
|
|
|
switch runtime.GOOS {
|
|
|
|
case "darwin", "freebsd", "opensbd", "netbsd":
|
|
|
|
tests = append(tests, []test{
|
|
|
|
{"tcp", "[localhost%" + ifi.Name + "]:0", true},
|
|
|
|
{"tcp6", "[localhost%" + ifi.Name + "]:0", true},
|
|
|
|
}...)
|
|
|
|
case "linux":
|
|
|
|
tests = append(tests, []test{
|
|
|
|
{"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
|
|
|
|
{"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
|
|
|
|
}...)
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
ln, err := Listen(tt.net, tt.addr)
|
|
|
|
if err != nil {
|
|
|
|
// It might return "LookupHost returned no
|
|
|
|
// suitable address" error on some platforms.
|
|
|
|
t.Logf("Listen failed: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
defer ln.Close()
|
|
|
|
if la, ok := ln.Addr().(*TCPAddr); !ok || !tt.nameLookup && la.Zone == "" {
|
|
|
|
t.Fatalf("got %v; expected a proper address with zone identifier", la)
|
|
|
|
}
|
|
|
|
|
|
|
|
done := make(chan int)
|
|
|
|
go transponder(t, ln, done)
|
|
|
|
|
|
|
|
c, err := Dial(tt.net, ln.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Dial failed: %v", err)
|
|
|
|
}
|
|
|
|
defer c.Close()
|
|
|
|
if la, ok := c.LocalAddr().(*TCPAddr); !ok || !tt.nameLookup && la.Zone == "" {
|
|
|
|
t.Fatalf("got %v; expected a proper address with zone identifier", la)
|
|
|
|
}
|
|
|
|
if ra, ok := c.RemoteAddr().(*TCPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
|
|
|
|
t.Fatalf("got %v; expected a proper address with zone identifier", ra)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := c.Write([]byte("TCP OVER IPV6 LINKLOCAL TEST")); err != nil {
|
|
|
|
t.Fatalf("Conn.Write failed: %v", err)
|
|
|
|
}
|
|
|
|
b := make([]byte, 32)
|
|
|
|
if _, err := c.Read(b); err != nil {
|
|
|
|
t.Fatalf("Conn.Read failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
<-done
|
|
|
|
}
|
|
|
|
}
|
2013-08-04 13:31:23 -06:00
|
|
|
|
|
|
|
func TestTCPConcurrentAccept(t *testing.T) {
|
|
|
|
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
|
|
|
|
ln, err := Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Listen failed: %v", err)
|
|
|
|
}
|
|
|
|
const N = 10
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(N)
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
for i := 0; i < 10*N; i++ {
|
|
|
|
c, err := Dial("tcp", ln.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Dial failed: %v", err)
|
|
|
|
}
|
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
ln.Close()
|
|
|
|
wg.Wait()
|
|
|
|
}
|
2013-08-06 04:40:10 -06:00
|
|
|
|
|
|
|
func TestTCPReadWriteMallocs(t *testing.T) {
|
|
|
|
maxMallocs := 0
|
|
|
|
switch runtime.GOOS {
|
|
|
|
// Add other OSes if you know how many mallocs they do.
|
|
|
|
case "windows":
|
|
|
|
maxMallocs = 0
|
|
|
|
}
|
|
|
|
ln, err := Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Listen failed: %v", err)
|
|
|
|
}
|
|
|
|
defer ln.Close()
|
|
|
|
var server Conn
|
|
|
|
errc := make(chan error)
|
|
|
|
go func() {
|
|
|
|
var err error
|
|
|
|
server, err = ln.Accept()
|
|
|
|
errc <- err
|
|
|
|
}()
|
|
|
|
client, err := Dial("tcp", ln.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Dial failed: %v", err)
|
|
|
|
}
|
|
|
|
if err := <-errc; err != nil {
|
|
|
|
t.Fatalf("Accept failed: %v", err)
|
|
|
|
}
|
|
|
|
defer server.Close()
|
|
|
|
var buf [128]byte
|
|
|
|
mallocs := testing.AllocsPerRun(1000, func() {
|
|
|
|
_, err := server.Write(buf[:])
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Write failed: %v", err)
|
|
|
|
}
|
|
|
|
_, err = io.ReadFull(client, buf[:])
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Read failed: %v", err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if int(mallocs) > maxMallocs {
|
|
|
|
t.Fatalf("Got %v allocs, want %v", mallocs, maxMallocs)
|
|
|
|
}
|
|
|
|
}
|