1
0
mirror of https://github.com/golang/go synced 2024-11-11 23:10:23 -07:00

add network listening & tests

R=r,presotto
OCL=15410
CL=15440
This commit is contained in:
Russ Cox 2008-09-17 13:49:23 -07:00
parent a456463891
commit 9350ef4eea
10 changed files with 381 additions and 43 deletions

View File

@ -4,17 +4,7 @@
package io
import os "os"
export func StringToBytes(b *[]byte, s string) bool {
if len(s) >= len(b) {
return false
}
for i := 0; i < len(s); i++ {
b[i] = s[i]
}
b[len(s)] = '\000'; // not necessary - memory is zeroed - but be explicit
return true
}
import syscall "syscall"
export type Read interface {
Read(p *[]byte) (n int, err *os.Error);
@ -24,13 +14,17 @@ export type Write interface {
Write(p *[]byte) (n int, err *os.Error);
}
export type ReadWrite interface {
Read(p *[]byte) (n int, err *os.Error);
Write(p *[]byte) (n int, err *os.Error);
}
export func WriteString(w Write, s string) (n int, err *os.Error) {
b := new([]byte, len(s)+1)
if !StringToBytes(b, s) {
if !syscall.StringToBytes(b, s) {
return -1, os.EINVAL
}
// BUG return w.Write(b[0:len(s)])
r, e := w.Write(b[0:len(s)])
return r, e
}

View File

@ -22,3 +22,10 @@ do
6g -o $GOROOT/pkg/$base.6 $i
done
for i in net
do
echo; echo; echo %%%% making lib/$i %%%%; echo
cd $i
make install
cd ..
done

View File

@ -336,6 +336,10 @@ func ParseIPv6(s string) *[]byte {
if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
ellipsis = 0;
i = 2
// Might be only ellipsis
if i == len(s) {
return p
}
}
// Loop, parsing hex numbers followed by colon.
@ -343,12 +347,12 @@ func ParseIPv6(s string) *[]byte {
L: for j < IPv6len {
// Hex number.
n, i1, ok := xtoi(s, i)
if !ok || n >= 0xFFFF {
if !ok || n > 0xFFFF {
return nil
}
// If followed by dot, might be in trailing IPv4.
if s[i1] == '.' {
if i1 < len(s) && s[i1] == '.' {
if ellipsis < 0 && j != IPv6len - IPv4len {
// Not the right place.
return nil
@ -389,7 +393,7 @@ L: for j < IPv6len {
i++
// Look for ellipsis.
if s[i+1] == ':' {
if s[i] == ':' {
if ellipsis >= 0 { // already have one
return nil
}
@ -411,7 +415,7 @@ L: for j < IPv6len {
return nil
}
n := IPv6len - j
for k := j; k >= ellipsis; k-- {
for k := j-1; k >= ellipsis; k-- {
p[k+n] = p[k]
}
for k := ellipsis+n-1; k>=ellipsis; k-- {

View File

@ -19,7 +19,8 @@ func NewError(s string) *os.Error {
}
export var (
BadAddress = NewError("malformed addres");
BadAddress = NewError("malformed address");
MissingAddress = NewError("missing address");
UnknownNetwork = NewError("unknown network");
UnknownHost = NewError("unknown host");
UnknownPort = NewError("unknown port");
@ -39,10 +40,10 @@ func SplitHostPort(hostport string) (host, port string, err *os.Error) {
if i < 0 {
return "", "", BadAddress
}
host = hostport[0:i];
port = hostport[i+1:len(hostport)];
// Can put brackets around host ...
if host[0] == '[' && host[len(host)-1] == ']' {
host = host[1:len(host)-1]
@ -69,6 +70,20 @@ func JoinHostPort(host, port string) string {
return host + ":" + port
}
func dtoi(s string) (n int, ok bool) {
if s == "" || s[0] < '0' || s[0] > '9' {
return 0, false
}
n = 0;
for i := 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
n = n*10 + int(s[i] - '0')
if n >= 1000000 { // bigger than we need
return 0, false
}
}
return n, true
}
// Convert "host:port" into IP address and port.
// For now, host and port must be numeric literals.
// Eventually, we'll have name resolution.
@ -78,22 +93,21 @@ func HostPortToIP(net string, hostport string) (ip *[]byte, iport int, err *os.E
if err != nil {
return nil, 0, err
}
// TODO: Resolve host.
addr := ip.ParseIP(host);
if addr == nil {
print("Failed to parse: ", host, "\n");
return nil, 0, UnknownHost
}
// TODO: Resolve port.
p, ok := strings.atoi(port);
p, ok := dtoi(port);
if !ok || p < 0 || p > 0xFFFF {
return nil, 0, UnknownPort
}
return addr, p, nil
}
@ -117,7 +131,7 @@ func SockaddrToHostPort(sa *socket.Sockaddr) (hostport string, err *os.Error) {
func boolint(b bool) int {
if b {
return 1
}
}
return 0
}
@ -127,7 +141,10 @@ func Socket(f, p, t int64, la, ra *socket.Sockaddr) (fd int64, err *os.Error) {
if e != nil {
return -1, e
}
// Allow reuse of recently-used addresses.
socket.setsockopt_int(s, socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
var r int64
if la != nil {
r, e = socket.bind(s, la)
@ -136,7 +153,7 @@ func Socket(f, p, t int64, la, ra *socket.Sockaddr) (fd int64, err *os.Error) {
return -1, e
}
}
if ra != nil {
r, e = socket.connect(s, ra)
if e != nil {
@ -144,7 +161,7 @@ func Socket(f, p, t int64, la, ra *socket.Sockaddr) (fd int64, err *os.Error) {
return -1, e
}
}
return s, nil
}
@ -256,11 +273,18 @@ func (c *ConnBase) SetLinger(sec int) *os.Error {
// PreferIPv4 here should fall back to the IPv4 socket interface when possible.
const PreferIPv4 = false
func DialInternet(net, laddr, raddr string, proto int64) (fd int64, err *os.Error) {
func InternetSocket(net, laddr, raddr string, proto int64) (fd int64, err *os.Error) {
// Parse addresses (unless they are empty).
var lip, rip *[]byte
var lport, rport int
var lerr, rerr *os.Error
// BUG 6g doesn't zero var lists
lip = nil;
rip = nil;
lport = 0;
rport = 0;
lerr = nil;
rerr = nil
if laddr != "" {
lip, lport, lerr = HostPortToIP(net, laddr)
if lerr != nil {
@ -274,7 +298,7 @@ func DialInternet(net, laddr, raddr string, proto int64) (fd int64, err *os.Erro
}
}
// Figure out IP version.
// Figure out IP version.
// If network has a suffix like "tcp4", obey it.
vers := 0;
switch net[len(net)-1] {
@ -303,8 +327,11 @@ func DialInternet(net, laddr, raddr string, proto int64) (fd int64, err *os.Erro
cvt = &socket.IPv6ToSockaddr;
family = socket.AF_INET6
}
var la, ra *socket.Sockaddr;
// BUG
la = nil;
ra = nil
if lip != nil {
la, lerr = cvt(lip, lport);
if lerr != nil {
@ -388,15 +415,23 @@ func (c *ConnTCP) SetKeepAlive(keepalive bool) *os.Error {
return (&c.base).SetKeepAlive(keepalive)
}
func NewConnTCP(fd int64, raddr string) *ConnTCP {
c := new(ConnTCP);
c.base.fd = os.NewFD(fd);
c.base.raddr = raddr;
c.SetNoDelay(true);
return c
}
export func DialTCP(net, laddr, raddr string) (c *ConnTCP, err *os.Error) {
fd, e := DialInternet(net, laddr, raddr, socket.SOCK_STREAM)
if raddr == "" {
return nil, MissingAddress
}
fd, e := InternetSocket(net, laddr, raddr, socket.SOCK_STREAM)
if e != nil {
return nil, e
}
c = new(ConnTCP);
c.base.fd = os.NewFD(fd);
c.SetNoDelay(true)
return c, nil
return NewConnTCP(fd, raddr), nil
}
@ -481,3 +516,83 @@ export func Dial(net, laddr, raddr string) (c Conn, err *os.Error) {
return nil, UnknownNetwork
}
export type Listener interface {
Accept() (c Conn, raddr string, err *os.Error);
Close() *os.Error;
}
type NoListener struct { unused int }
func (l *NoListener) Accept() (c Conn, raddr string, err *os.Error) {
return &noconn, "", os.EINVAL
}
func (l *NoListener) Close() *os.Error { return os.EINVAL }
var nolistener NoListener
export type ListenerTCP struct {
fd *os.FD;
laddr string
}
export func ListenTCP(net, laddr string) (l *ListenerTCP, err *os.Error) {
fd, e := InternetSocket(net, laddr, "", socket.SOCK_STREAM)
if e != nil {
return nil, e
}
r, e1 := socket.listen(fd, socket.ListenBacklog())
if e1 != nil {
syscall.close(fd)
return nil, e1
}
l = new(ListenerTCP);
l.fd = os.NewFD(fd);
return l, nil
}
func (l *ListenerTCP) AcceptTCP() (c *ConnTCP, raddr string, err *os.Error) {
if l == nil || l.fd == nil || l.fd.fd < 0 {
return nil, "", os.EINVAL
}
var sa socket.Sockaddr;
fd, e := socket.accept(l.fd.fd, &sa)
if e != nil {
return nil, "", e
}
raddr, e = SockaddrToHostPort(&sa)
if e != nil {
syscall.close(fd)
return nil, "", e
}
return NewConnTCP(fd, raddr), raddr, nil
}
func (l *ListenerTCP) Accept() (c Conn, raddr string, err *os.Error) {
c1, r1, e1 := l.AcceptTCP()
if e1 != nil {
return &noconn, "", e1
}
return c1, r1, nil
}
func (l *ListenerTCP) Close() *os.Error {
if l == nil || l.fd == nil {
return os.EINVAL
}
return l.fd.Close()
}
export func Listen(net, laddr string) (l Listener, err *os.Error) {
switch net {
case "tcp", "tcp4", "tcp6":
l, err := ListenTCP(net, laddr)
if err != nil {
return &nolistener, err
}
return l, nil
/*
more here
*/
}
return nil, UnknownNetwork
}

View File

@ -53,6 +53,8 @@ export const (
IPPROTO_UDP = 17;
TCP_NODELAY = 0x01;
SOMAXCONN = 128;
)
export type SockaddrUnix struct {
@ -127,7 +129,7 @@ export func listen(fd, n int64) (ret int64, err *os.Error) {
}
export func accept(fd int64, sa *Sockaddr) (ret int64, err *os.Error) {
n := int32(sa.len);
n := SizeofSockaddr;
r1, r2, e := syscall.Syscall(ACCEPT, fd, SockaddrPtr(sa), Int32Ptr(&n));
return r1, os.ErrnoToError(e)
}
@ -229,3 +231,6 @@ export func SockaddrToIP(sa1 *Sockaddr) (p *[]byte, port int, err *os.Error) {
return nil, 0, nil // not reached
}
export func ListenBacklog() int64 {
return SOMAXCONN
}

View File

@ -63,6 +63,8 @@ export const (
IPPROTO_UDP = 17;
TCP_NODELAY = 0x01;
SOMAXCONN = 128;
)
export type SockaddrUnix struct {
@ -145,7 +147,7 @@ export func listen(fd, n int64) (ret int64, err *os.Error) {
}
export func accept(fd int64, sa *Sockaddr) (ret int64, err *os.Error) {
n := int32(sa.Len());
n := SizeofSockaddr;
r1, r2, e := syscall.Syscall(ACCEPT, fd, SockaddrPtr(sa), Int32Ptr(&n));
return r1, os.ErrnoToError(e)
}
@ -208,11 +210,21 @@ export func IPv4ToSockaddr(p *[]byte, port int) (sa1 *Sockaddr, err *os.Error) {
return SockaddrInet4ToSockaddr(sa), nil
}
var IPv6zero [ip.IPv6len]byte;
export func IPv6ToSockaddr(p *[]byte, port int) (sa1 *Sockaddr, err *os.Error) {
p = ip.ToIPv6(p)
if p == nil || port < 0 || port > 0xFFFF {
return nil, os.EINVAL
}
// IPv4 callers use 0.0.0.0 to mean "announce on any available address".
// In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
// which it refuses to do. Rewrite to the IPv6 all zeros.
if p4 := ip.ToIPv4(p); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 {
p = &IPv6zero;
}
sa := new(SockaddrInet6);
sa.family = AF_INET6;
sa.port[0] = byte(port>>8);
@ -245,3 +257,10 @@ export func SockaddrToIP(sa1 *Sockaddr) (p *[]byte, port int, err *os.Error) {
return nil, 0, nil // not reached
}
export func ListenBacklog() int64 {
// TODO: maybe /proc/sys/net/core/somaxconn
// and read the limit out of there, to take advantage of kernels
// that have increased the limit
return SOMAXCONN
}

View File

@ -78,7 +78,7 @@ schedinit(void)
byte *p;
sched.mmax = 1;
p = getenv("gomaxprocs");
p = getenv("GOMAXPROCS");
if(p != nil && (n = atoi(p)) != 0)
sched.mmax = n;
sched.mcount = 1;

View File

@ -37,7 +37,6 @@ TEXT syscall·Syscall6(SB),7,$-8
MOVQ 48(SP), R8
MOVQ 56(SP), R9
MOVQ 8(SP), AX // syscall entry
ADDQ $0x2000000, AX
SYSCALL
JLS 6(PC)
MOVQ $-1, 64(SP) // r1

96
test/dialgoogle.go Normal file
View File

@ -0,0 +1,96 @@
// 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.
// $G $F.go && $L $F.$A && ./$A.out
package main
import (
"net";
"flag";
"os";
"syscall"
)
// If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address.
var ipv6 = false
var ipv6_flag = flag.Bool("ipv6", false, &ipv6, "assume ipv6 tunnel is present")
func StringToBuf(s string) *[]byte
{
l := len(s);
b := new([]byte, l);
for i := 0; i < l; i++ {
b[i] = s[i];
}
return b;
}
// fd is already connected to www.google.com port 80.
// Run an HTTP request to fetch the main page.
func FetchGoogle(fd net.Conn) {
req := StringToBuf("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
n, errno := fd.Write(req);
buf := new([1000]byte);
n, errno = fd.Read(buf);
fd.Close();
if n < 1000 {
panic("short http read");
}
}
func TestDial(network, addr string) {
fd, err := net.Dial(network, "", addr)
if err != nil {
panic("net.Dial ", network, " ", addr, ": ", err.String())
}
FetchGoogle(fd)
}
func TestDialTCP(network, addr string) {
fd, err := net.DialTCP(network, "", addr)
if err != nil {
panic("net.DialTCP ", network, " ", addr, ": ", err.String())
}
FetchGoogle(fd)
}
var addrs = []string {
"74.125.19.99:80",
"074.125.019.099:0080",
"[::ffff:74.125.19.99]:80",
"[::ffff:4a7d:1363]:80",
"[0:0:0:0:0000:ffff:74.125.19.99]:80",
"[0:0:0:0:000000:ffff:74.125.19.99]:80",
"[0:0:0:0:0:ffff::74.125.19.99]:80",
"[2001:4860:0:2001::68]:80" // ipv6.google.com; removed if ipv6 flag not set
}
func main()
{
flag.Parse()
// If no ipv6 tunnel, don't try the last address.
if !ipv6 {
addrs[len(addrs)-1] = ""
}
for i := 0; i < len(addrs); i++ {
addr := addrs[i]
if addr == "" {
continue
}
// print(addr, "\n");
TestDial("tcp", addr);
TestDialTCP("tcp", addr)
if addr[0] != '[' {
TestDial("tcp4", addr);
TestDialTCP("tcp4", addr)
}
TestDial("tcp6", addr);
TestDialTCP("tcp6", addr)
}
}

99
test/tcpserver.go Normal file
View File

@ -0,0 +1,99 @@
// 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.
// $G $F.go && $L $F.$A && GOMAXPROCS=3 ./$A.out
// # TODO(rsc): GOMAXPROCS will go away eventually.
// # 3 is one for Echo, one for Serve, one for Connect.
package main
import (
"os";
"io";
"net";
"syscall"
)
func StringToBuf(s string) *[]byte {
l := len(s);
b := new([]byte, l);
for i := 0; i < l; i++ {
b[i] = s[i];
}
return b;
}
func Echo(fd io.ReadWrite, done *chan<- int) {
var buf [1024]byte;
for {
n, err := fd.Read(&buf);
if err != nil || n == 0 {
break;
}
fd.Write((&buf)[0:n])
}
done <- 1
}
func Serve(network, addr string, listening, done *chan<- int) {
l, err := net.Listen(network, addr);
if err != nil {
panic("listen: "+err.String());
}
listening <- 1;
for {
fd, addr, err := l.Accept();
if err != nil {
break;
}
echodone := new(chan int)
go Echo(fd, echodone);
<-echodone; // make sure Echo stops
l.Close();
}
done <- 1
}
func Connect(network, addr string) {
fd, err := net.Dial(network, "", addr);
if err != nil {
panic("connect: "+err.String());
}
b := StringToBuf("hello, world\n");
var b1 [100]byte;
n, errno := fd.Write(b);
if n != len(b) {
panic("syscall.write in connect");
}
n, errno = fd.Read(&b1);
if n != len(b) {
panic("syscall.read in connect");
}
// os.Stdout.Write((&b1)[0:n]);
fd.Close();
}
func Test(network, listenaddr, dialaddr string) {
// print("Test ", network, " ", listenaddr, " ", dialaddr, "\n");
listening := new(chan int);
done := new(chan int);
go Serve(network, listenaddr, listening, done);
<-listening; // wait for server to start
Connect(network, dialaddr);
<-done; // make sure server stopped
}
func main() {
Test("tcp", "0.0.0.0:9999", "127.0.0.1:9999");
Test("tcp", "[::]:9999", "[::ffff:127.0.0.1]:9999");
Test("tcp", "[::]:9999", "127.0.0.1:9999");
Test("tcp", "0.0.0.0:9999", "[::ffff:127.0.0.1]:9999");
sys.exit(0); // supposed to happen on return, doesn't
}