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
|
2013-08-06 11:29:35 -06:00
|
|
|
numConcurrent := runtime.GOMAXPROCS(-1) * 2
|
2012-08-20 11:27:52 -06:00
|
|
|
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()
|
2014-04-07 01:00:07 -06:00
|
|
|
serverSem := make(chan bool, numConcurrent)
|
2012-08-20 11:27:52 -06:00
|
|
|
// Acceptor.
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
2014-04-07 01:00:07 -06:00
|
|
|
serverSem <- true
|
2012-08-20 11:27:52 -06:00
|
|
|
// Server connection.
|
|
|
|
go func(c Conn) {
|
2014-04-07 01:00:07 -06:00
|
|
|
defer func() {
|
|
|
|
c.Close()
|
|
|
|
<-serverSem
|
|
|
|
}()
|
2012-08-20 11:27:52 -06:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}()
|
2014-04-07 01:00:07 -06:00
|
|
|
clientSem := make(chan bool, numConcurrent)
|
2012-08-20 11:27:52 -06:00
|
|
|
for i := 0; i < conns; i++ {
|
2014-04-07 01:00:07 -06:00
|
|
|
clientSem <- true
|
2012-08-20 11:27:52 -06:00
|
|
|
// Client connection.
|
|
|
|
go func() {
|
|
|
|
defer func() {
|
2014-04-07 01:00:07 -06:00
|
|
|
<-clientSem
|
2012-08-20 11:27:52 -06:00
|
|
|
}()
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2014-04-07 01:00:07 -06:00
|
|
|
for i := 0; i < numConcurrent; i++ {
|
|
|
|
clientSem <- true
|
|
|
|
serverSem <- true
|
2012-08-20 11:27:52 -06:00
|
|
|
}
|
|
|
|
}
|
2012-11-12 20:56:28 -07:00
|
|
|
|
2013-08-06 11:29:35 -06:00
|
|
|
func BenchmarkTCP4ConcurrentReadWrite(b *testing.B) {
|
|
|
|
benchmarkTCPConcurrentReadWrite(b, "127.0.0.1:0")
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkTCP6ConcurrentReadWrite(b *testing.B) {
|
|
|
|
if !supportsIPv6 {
|
|
|
|
b.Skip("ipv6 is not supported")
|
|
|
|
}
|
|
|
|
benchmarkTCPConcurrentReadWrite(b, "[::1]:0")
|
|
|
|
}
|
|
|
|
|
|
|
|
func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
|
|
|
|
// The benchmark creates GOMAXPROCS client/server pairs.
|
|
|
|
// Each pair creates 4 goroutines: client reader/writer and server reader/writer.
|
|
|
|
// The benchmark stresses concurrent reading and writing to the same connection.
|
|
|
|
// Such pattern is used in net/http and net/rpc.
|
|
|
|
|
|
|
|
b.StopTimer()
|
|
|
|
|
|
|
|
P := runtime.GOMAXPROCS(0)
|
|
|
|
N := b.N / P
|
|
|
|
W := 1000
|
|
|
|
|
|
|
|
// Setup P client/server connections.
|
|
|
|
clients := make([]Conn, P)
|
|
|
|
servers := make([]Conn, P)
|
|
|
|
ln, err := Listen("tcp", laddr)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("Listen failed: %v", err)
|
|
|
|
}
|
|
|
|
defer ln.Close()
|
|
|
|
done := make(chan bool)
|
|
|
|
go func() {
|
|
|
|
for p := 0; p < P; p++ {
|
|
|
|
s, err := ln.Accept()
|
|
|
|
if err != nil {
|
2014-03-14 22:43:02 -06:00
|
|
|
b.Errorf("Accept failed: %v", err)
|
|
|
|
return
|
2013-08-06 11:29:35 -06:00
|
|
|
}
|
|
|
|
servers[p] = s
|
|
|
|
}
|
|
|
|
done <- true
|
|
|
|
}()
|
|
|
|
for p := 0; p < P; p++ {
|
|
|
|
c, err := Dial("tcp", ln.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("Dial failed: %v", err)
|
|
|
|
}
|
|
|
|
clients[p] = c
|
|
|
|
}
|
|
|
|
<-done
|
|
|
|
|
|
|
|
b.StartTimer()
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(4 * P)
|
|
|
|
for p := 0; p < P; p++ {
|
|
|
|
// Client writer.
|
|
|
|
go func(c Conn) {
|
|
|
|
defer wg.Done()
|
|
|
|
var buf [1]byte
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
v := byte(i)
|
|
|
|
for w := 0; w < W; w++ {
|
|
|
|
v *= v
|
|
|
|
}
|
|
|
|
buf[0] = v
|
|
|
|
_, err := c.Write(buf[:])
|
|
|
|
if err != nil {
|
2014-03-14 22:43:02 -06:00
|
|
|
b.Errorf("Write failed: %v", err)
|
|
|
|
return
|
2013-08-06 11:29:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}(clients[p])
|
|
|
|
|
|
|
|
// Pipe between server reader and server writer.
|
|
|
|
pipe := make(chan byte, 128)
|
|
|
|
|
|
|
|
// Server reader.
|
|
|
|
go func(s Conn) {
|
|
|
|
defer wg.Done()
|
|
|
|
var buf [1]byte
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
_, err := s.Read(buf[:])
|
|
|
|
if err != nil {
|
2014-03-14 22:43:02 -06:00
|
|
|
b.Errorf("Read failed: %v", err)
|
|
|
|
return
|
2013-08-06 11:29:35 -06:00
|
|
|
}
|
|
|
|
pipe <- buf[0]
|
|
|
|
}
|
|
|
|
}(servers[p])
|
|
|
|
|
|
|
|
// Server writer.
|
|
|
|
go func(s Conn) {
|
|
|
|
defer wg.Done()
|
|
|
|
var buf [1]byte
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
v := <-pipe
|
|
|
|
for w := 0; w < W; w++ {
|
|
|
|
v *= v
|
|
|
|
}
|
|
|
|
buf[0] = v
|
|
|
|
_, err := s.Write(buf[:])
|
|
|
|
if err != nil {
|
2014-03-14 22:43:02 -06:00
|
|
|
b.Errorf("Write failed: %v", err)
|
|
|
|
return
|
2013-08-06 11:29:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
s.Close()
|
|
|
|
}(servers[p])
|
|
|
|
|
|
|
|
// Client reader.
|
|
|
|
go func(c Conn) {
|
|
|
|
defer wg.Done()
|
|
|
|
var buf [1]byte
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
_, err := c.Read(buf[:])
|
|
|
|
if err != nil {
|
2014-03-14 22:43:02 -06:00
|
|
|
b.Errorf("Read failed: %v", err)
|
|
|
|
return
|
2013-08-06 11:29:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
c.Close()
|
|
|
|
}(clients[p])
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
2013-03-26 10:06:48 -06:00
|
|
|
type resolveTCPAddrTest struct {
|
2013-08-30 19:28:49 -06:00
|
|
|
net string
|
|
|
|
litAddrOrName 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
|
|
|
|
|
2013-09-23 20:40:24 -06:00
|
|
|
{"tcp", ":12345", &TCPAddr{Port: 12345}, nil},
|
|
|
|
|
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},
|
|
|
|
}...)
|
|
|
|
}
|
2013-08-30 19:28:49 -06:00
|
|
|
if ips, err := LookupIP("localhost"); err == nil && len(ips) > 1 && supportsIPv4 && supportsIPv6 {
|
|
|
|
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
|
2013-09-23 20:40:24 -06:00
|
|
|
{"tcp", "localhost:5", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5}, nil},
|
|
|
|
{"tcp4", "localhost:6", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 6}, nil},
|
2013-08-30 19:28:49 -06:00
|
|
|
{"tcp6", "localhost:7", &TCPAddr{IP: IPv6loopback, Port: 7}, nil},
|
|
|
|
}...)
|
|
|
|
}
|
2013-03-26 10:06:48 -06:00
|
|
|
}
|
|
|
|
|
2012-11-27 14:36:05 -07:00
|
|
|
func TestResolveTCPAddr(t *testing.T) {
|
|
|
|
for _, tt := range resolveTCPAddrTests {
|
2013-08-30 19:28:49 -06:00
|
|
|
addr, err := ResolveTCPAddr(tt.net, tt.litAddrOrName)
|
2012-11-27 14:36:05 -07:00
|
|
|
if err != tt.err {
|
2013-08-30 19:28:49 -06:00
|
|
|
t.Fatalf("ResolveTCPAddr(%q, %q) failed: %v", tt.net, tt.litAddrOrName, err)
|
2012-11-27 14:36:05 -07:00
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(addr, tt.addr) {
|
2013-09-23 20:40:24 -06:00
|
|
|
t.Fatalf("ResolveTCPAddr(%q, %q) = %#v, want %#v", tt.net, tt.litAddrOrName, addr, tt.addr)
|
|
|
|
}
|
|
|
|
if err == nil {
|
|
|
|
str := addr.String()
|
|
|
|
addr1, err := ResolveTCPAddr(tt.net, str)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("ResolveTCPAddr(%q, %q) [from %q]: %v", tt.net, str, tt.litAddrOrName, err)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(addr1, addr) {
|
|
|
|
t.Fatalf("ResolveTCPAddr(%q, %q) [from %q] = %#v, want %#v", tt.net, str, tt.litAddrOrName, addr1, addr)
|
|
|
|
}
|
2012-11-27 14:36:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2014-04-27 18:39:13 -06:00
|
|
|
case "darwin", "freebsd", "openbsd", "netbsd":
|
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
|
|
|
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()
|
|
|
|
}()
|
|
|
|
}
|
2014-03-24 11:56:37 -06:00
|
|
|
attempts := 10 * N
|
|
|
|
fails := 0
|
|
|
|
d := &Dialer{Timeout: 200 * time.Millisecond}
|
|
|
|
for i := 0; i < attempts; i++ {
|
|
|
|
c, err := d.Dial("tcp", ln.Addr().String())
|
2013-08-04 13:31:23 -06:00
|
|
|
if err != nil {
|
2014-03-24 11:56:37 -06:00
|
|
|
fails++
|
|
|
|
} else {
|
|
|
|
c.Close()
|
2013-08-04 13:31:23 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ln.Close()
|
|
|
|
wg.Wait()
|
2014-03-24 11:56:37 -06:00
|
|
|
if fails > attempts/9 { // see issues 7400 and 7541
|
|
|
|
t.Fatalf("too many Dial failed: %v", fails)
|
|
|
|
}
|
|
|
|
if fails > 0 {
|
|
|
|
t.Logf("# of failed Dials: %v", fails)
|
|
|
|
}
|
2013-08-04 13:31:23 -06:00
|
|
|
}
|
2013-08-06 04:40:10 -06:00
|
|
|
|
2015-02-23 22:13:47 -07:00
|
|
|
func TestTCPReadWriteAllocs(t *testing.T) {
|
|
|
|
switch runtime.GOOS {
|
2015-03-01 10:30:15 -07:00
|
|
|
case "nacl", "windows", "darwin", "dragonfly":
|
2015-02-23 22:13:47 -07:00
|
|
|
// NaCl needs to allocate pseudo file descriptor
|
|
|
|
// stuff. See syscall/fd_nacl.go.
|
|
|
|
// Windows uses closures and channels for IO
|
|
|
|
// completion port-based netpoll. See fd_windows.go.
|
2015-02-28 13:34:53 -07:00
|
|
|
// Darwin is unreliable for unknown reasons (issue 8859).
|
2015-03-01 10:30:15 -07:00
|
|
|
// Dragonfly also unreliable (lumped into issue 8859).
|
2015-02-23 22:13:47 -07:00
|
|
|
t.Skipf("not supported on %s", runtime.GOOS)
|
2013-08-20 22:00:45 -06:00
|
|
|
}
|
2015-02-23 22:13:47 -07:00
|
|
|
|
2013-08-06 04:40:10 -06:00
|
|
|
ln, err := Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
2015-02-23 22:13:47 -07:00
|
|
|
t.Fatal(err)
|
2013-08-06 04:40:10 -06:00
|
|
|
}
|
|
|
|
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 {
|
2015-02-23 22:13:47 -07:00
|
|
|
t.Fatal(err)
|
2013-08-06 04:40:10 -06:00
|
|
|
}
|
2015-02-23 22:13:47 -07:00
|
|
|
defer client.Close()
|
2013-08-06 04:40:10 -06:00
|
|
|
if err := <-errc; err != nil {
|
2015-02-23 22:13:47 -07:00
|
|
|
t.Fatal(err)
|
2013-08-06 04:40:10 -06:00
|
|
|
}
|
|
|
|
defer server.Close()
|
|
|
|
var buf [128]byte
|
2015-02-23 22:13:47 -07:00
|
|
|
allocs := testing.AllocsPerRun(1000, func() {
|
2013-08-06 04:40:10 -06:00
|
|
|
_, err := server.Write(buf[:])
|
|
|
|
if err != nil {
|
2015-02-23 22:13:47 -07:00
|
|
|
t.Fatal(err)
|
2013-08-06 04:40:10 -06:00
|
|
|
}
|
|
|
|
_, err = io.ReadFull(client, buf[:])
|
|
|
|
if err != nil {
|
2015-02-23 22:13:47 -07:00
|
|
|
t.Fatal(err)
|
2013-08-06 04:40:10 -06:00
|
|
|
}
|
|
|
|
})
|
2015-02-23 22:13:47 -07:00
|
|
|
if allocs > 0 {
|
|
|
|
t.Fatalf("got %v; want 0", allocs)
|
2013-08-06 04:40:10 -06:00
|
|
|
}
|
|
|
|
}
|
2013-08-13 12:07:42 -06:00
|
|
|
|
|
|
|
func TestTCPStress(t *testing.T) {
|
|
|
|
const conns = 2
|
|
|
|
const msgLen = 512
|
2013-08-14 11:53:27 -06:00
|
|
|
msgs := int(1e4)
|
|
|
|
if testing.Short() {
|
|
|
|
msgs = 1e2
|
|
|
|
}
|
2013-08-13 12:07:42 -06:00
|
|
|
|
|
|
|
sendMsg := func(c Conn, buf []byte) bool {
|
|
|
|
n, err := c.Write(buf)
|
|
|
|
if n != len(buf) || err != nil {
|
|
|
|
t.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 {
|
|
|
|
t.Logf("Read failed: %v", err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
ln, err := Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
t.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()
|
|
|
|
var buf [msgLen]byte
|
|
|
|
for m := 0; m < msgs; m++ {
|
|
|
|
if !recvMsg(c, buf[:]) || !sendMsg(c, buf[:]) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(c)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
done := make(chan bool)
|
|
|
|
for i := 0; i < conns; i++ {
|
|
|
|
// Client connection.
|
|
|
|
go func() {
|
|
|
|
defer func() {
|
|
|
|
done <- true
|
|
|
|
}()
|
|
|
|
c, err := Dial("tcp", ln.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("Dial failed: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer c.Close()
|
|
|
|
var buf [msgLen]byte
|
|
|
|
for m := 0; m < msgs; m++ {
|
|
|
|
if !sendMsg(c, buf[:]) || !recvMsg(c, buf[:]) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
for i := 0; i < conns; i++ {
|
|
|
|
<-done
|
|
|
|
}
|
|
|
|
}
|