net: test and fix support for 0-length datagram packets.

Fixes #274.

R=r
CC=jonathan.r.hudson
https://golang.org/cl/163072
diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go
index e1592eb..733f957 100644
--- a/src/pkg/net/fd.go
+++ b/src/pkg/net/fd.go
@@ -386,6 +386,10 @@
 		}
 		break;
 	}
+	if fd.proto == syscall.SOCK_DGRAM && err == os.EOF {
+		// 0 in datagram protocol just means 0-length packet
+		err = nil
+	}
 	return;
 }
 
@@ -433,7 +437,9 @@
 	}
 	err = nil;
 	nn := 0;
-	for nn < len(p) {
+	first := true;	// force at least one Write, to send 0-length datagram packets
+	for nn < len(p) || first {
+		first = false;
 		n, err = fd.sysfile.Write(p[nn:]);
 		if n > 0 {
 			nn += n
diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go
index d5c2f18..454f1d7 100644
--- a/src/pkg/net/server_test.go
+++ b/src/pkg/net/server_test.go
@@ -45,7 +45,7 @@
 	done <- 1;
 }
 
-func connect(t *testing.T, network, addr string) {
+func connect(t *testing.T, network, addr string, isEmpty bool) {
 	var laddr string;
 	if network == "unixgram" {
 		laddr = addr + ".local"
@@ -54,18 +54,22 @@
 	if err != nil {
 		t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, laddr, addr, err)
 	}
+	fd.SetReadTimeout(10e6);	// 10ms
 
-	b := strings.Bytes("hello, world\n");
+	var b []byte;
+	if !isEmpty {
+		b = strings.Bytes("hello, world\n")
+	}
 	var b1 [100]byte;
 
-	n, errno := fd.Write(b);
+	n, err := fd.Write(b);
 	if n != len(b) {
-		t.Fatalf("fd.Write(%q) = %d, %v", b, n, errno)
+		t.Fatalf("fd.Write(%q) = %d, %v", b, n, err)
 	}
 
-	n, errno = fd.Read(&b1);
-	if n != len(b) {
-		t.Fatalf("fd.Read() = %d, %v", n, errno)
+	n, err = fd.Read(&b1);
+	if n != len(b) || err != nil {
+		t.Fatalf("fd.Read() = %d, %v (want %d, nil)", n, err, len(b))
 	}
 	fd.Close();
 }
@@ -82,7 +86,7 @@
 	if network == "tcp" {
 		dialaddr += addr[strings.LastIndex(addr, ":"):]
 	}
-	connect(t, network, dialaddr);
+	connect(t, network, dialaddr, false);
 	<-done;	// make sure server stopped
 }
 
@@ -133,7 +137,7 @@
 	done <- 1;
 }
 
-func doTestPacket(t *testing.T, network, listenaddr, dialaddr string) {
+func doTestPacket(t *testing.T, network, listenaddr, dialaddr string, isEmpty bool) {
 	t.Logf("TestPacket %s %s %s\n", network, listenaddr, dialaddr);
 	listening := make(chan string);
 	done := make(chan int);
@@ -145,29 +149,33 @@
 	if network == "udp" {
 		dialaddr += addr[strings.LastIndex(addr, ":"):]
 	}
-	connect(t, network, dialaddr);
+	connect(t, network, dialaddr, isEmpty);
 	<-done;	// tell server to stop
 	<-done;	// wait for stop
 }
 
 func TestUDPServer(t *testing.T) {
-	doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1");
-	doTestPacket(t, "udp", "", "127.0.0.1");
-	if kernelSupportsIPv6() {
-		doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]");
-		doTestPacket(t, "udp", "[::]", "127.0.0.1");
-		doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]");
+	for _, isEmpty := range []bool{false, true} {
+		doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1", isEmpty);
+		doTestPacket(t, "udp", "", "127.0.0.1", isEmpty);
+		if kernelSupportsIPv6() {
+			doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]", isEmpty);
+			doTestPacket(t, "udp", "[::]", "127.0.0.1", isEmpty);
+			doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]", isEmpty);
+		}
 	}
 }
 
 func TestUnixDatagramServer(t *testing.T) {
-	os.Remove("/tmp/gotest1.net");
-	os.Remove("/tmp/gotest1.net.local");
-	doTestPacket(t, "unixgram", "/tmp/gotest1.net", "/tmp/gotest1.net");
-	os.Remove("/tmp/gotest1.net");
-	os.Remove("/tmp/gotest1.net.local");
-	if syscall.OS == "linux" {
-		// Test abstract unix domain socket, a Linux-ism
-		doTestPacket(t, "unixgram", "@gotest1/net", "@gotest1/net")
+	for _, isEmpty := range []bool{false, true} {
+		os.Remove("/tmp/gotest1.net");
+		os.Remove("/tmp/gotest1.net.local");
+		doTestPacket(t, "unixgram", "/tmp/gotest1.net", "/tmp/gotest1.net", isEmpty);
+		os.Remove("/tmp/gotest1.net");
+		os.Remove("/tmp/gotest1.net.local");
+		if syscall.OS == "linux" {
+			// Test abstract unix domain socket, a Linux-ism
+			doTestPacket(t, "unixgram", "@gotest1/net", "@gotest1/net", isEmpty)
+		}
 	}
 }