Commit 593f74bb by Ian Lance Taylor

libgo: Update to weekly.2012-03-04 release.

From-SVN: r185010
parent 46402cbe
96bd78e7d35e f4470a54e6db
The first line of this file holds the Mercurial revision number of the The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources. last merge done from the master library sources.
...@@ -658,10 +658,17 @@ go_net_sock_file = go/net/sock_linux.go ...@@ -658,10 +658,17 @@ go_net_sock_file = go/net/sock_linux.go
go_net_sockopt_file = go/net/sockopt_linux.go go_net_sockopt_file = go/net/sockopt_linux.go
go_net_sockoptip_file = go/net/sockoptip_linux.go go_net_sockoptip_file = go/net/sockoptip_linux.go
else else
if LIBGO_IS_FREEBSD
go_net_cgo_file = go/net/cgo_bsd.go go_net_cgo_file = go/net/cgo_bsd.go
go_net_sock_file = go/net/sock_bsd.go go_net_sock_file = go/net/sock_bsd.go
go_net_sockopt_file = go/net/sockopt_bsd.go go_net_sockopt_file = go/net/sockopt_bsd.go
go_net_sockoptip_file = go/net/sockoptip_bsd.go go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_freebsd.go
else
go_net_cgo_file = go/net/cgo_bsd.go
go_net_sock_file = go/net/sock_bsd.go
go_net_sockopt_file = go/net/sockopt_bsd.go
go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_netbsd.go
endif
endif endif
endif endif
endif endif
...@@ -704,6 +711,7 @@ go_net_files = \ ...@@ -704,6 +711,7 @@ go_net_files = \
go/net/ipsock.go \ go/net/ipsock.go \
go/net/ipsock_posix.go \ go/net/ipsock_posix.go \
go/net/lookup_unix.go \ go/net/lookup_unix.go \
go/net/mac.go \
go/net/net.go \ go/net/net.go \
go/net/parse.go \ go/net/parse.go \
go/net/pipe.go \ go/net/pipe.go \
...@@ -1126,8 +1134,7 @@ go_go_ast_files = \ ...@@ -1126,8 +1134,7 @@ go_go_ast_files = \
go/go/ast/walk.go go/go/ast/walk.go
go_go_build_files = \ go_go_build_files = \
go/go/build/build.go \ go/go/build/build.go \
go/go/build/dir.go \ go/go/build/doc.go \
go/go/build/path.go \
syslist.go syslist.go
go_go_doc_files = \ go_go_doc_files = \
go/go/doc/comment.go \ go/go/doc/comment.go \
......
...@@ -1012,19 +1012,23 @@ go_mime_files = \ ...@@ -1012,19 +1012,23 @@ go_mime_files = \
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
@LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go @LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_cgo_file = go/net/cgo_linux.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_cgo_file = go/net/cgo_linux.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_cgo_file = go/net/cgo_linux.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_cgo_file = go/net/cgo_linux.go
@LIBGO_IS_LINUX_TRUE@go_net_cgo_file = go/net/cgo_linux.go @LIBGO_IS_LINUX_TRUE@go_net_cgo_file = go/net/cgo_linux.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sock_file = go/net/sock_linux.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sock_file = go/net/sock_linux.go
@LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockopt_file = go/net/sockopt_linux.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockopt_file = go/net/sockopt_linux.go
@LIBGO_IS_LINUX_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_netbsd.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_freebsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.go
@LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go
...@@ -1055,6 +1059,7 @@ go_net_files = \ ...@@ -1055,6 +1059,7 @@ go_net_files = \
go/net/ipsock.go \ go/net/ipsock.go \
go/net/ipsock_posix.go \ go/net/ipsock_posix.go \
go/net/lookup_unix.go \ go/net/lookup_unix.go \
go/net/mac.go \
go/net/net.go \ go/net/net.go \
go/net/parse.go \ go/net/parse.go \
go/net/pipe.go \ go/net/pipe.go \
...@@ -1467,8 +1472,7 @@ go_go_ast_files = \ ...@@ -1467,8 +1472,7 @@ go_go_ast_files = \
go_go_build_files = \ go_go_build_files = \
go/go/build/build.go \ go/go/build/build.go \
go/go/build/dir.go \ go/go/build/doc.go \
go/go/build/path.go \
syslist.go syslist.go
go_go_doc_files = \ go_go_doc_files = \
......
...@@ -169,48 +169,21 @@ func (r *checksumReader) Read(b []byte) (n int, err error) { ...@@ -169,48 +169,21 @@ func (r *checksumReader) Read(b []byte) (n int, err error) {
func (r *checksumReader) Close() error { return r.rc.Close() } func (r *checksumReader) Close() error { return r.rc.Close() }
func readFileHeader(f *File, r io.Reader) error {
var b [fileHeaderLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil {
return err
}
c := binary.LittleEndian
if sig := c.Uint32(b[:4]); sig != fileHeaderSignature {
return ErrFormat
}
f.ReaderVersion = c.Uint16(b[4:6])
f.Flags = c.Uint16(b[6:8])
f.Method = c.Uint16(b[8:10])
f.ModifiedTime = c.Uint16(b[10:12])
f.ModifiedDate = c.Uint16(b[12:14])
f.CRC32 = c.Uint32(b[14:18])
f.CompressedSize = c.Uint32(b[18:22])
f.UncompressedSize = c.Uint32(b[22:26])
filenameLen := int(c.Uint16(b[26:28]))
extraLen := int(c.Uint16(b[28:30]))
d := make([]byte, filenameLen+extraLen)
if _, err := io.ReadFull(r, d); err != nil {
return err
}
f.Name = string(d[:filenameLen])
f.Extra = d[filenameLen:]
return nil
}
// findBodyOffset does the minimum work to verify the file has a header // findBodyOffset does the minimum work to verify the file has a header
// and returns the file body offset. // and returns the file body offset.
func (f *File) findBodyOffset() (int64, error) { func (f *File) findBodyOffset() (int64, error) {
r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset) r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset)
var b [fileHeaderLen]byte var buf [fileHeaderLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, buf[:]); err != nil {
return 0, err return 0, err
} }
c := binary.LittleEndian b := readBuf(buf[:])
if sig := c.Uint32(b[:4]); sig != fileHeaderSignature { if sig := b.uint32(); sig != fileHeaderSignature {
return 0, ErrFormat return 0, ErrFormat
} }
filenameLen := int(c.Uint16(b[26:28])) b = b[22:] // skip over most of the header
extraLen := int(c.Uint16(b[28:30])) filenameLen := int(b.uint16())
extraLen := int(b.uint16())
return int64(fileHeaderLen + filenameLen + extraLen), nil return int64(fileHeaderLen + filenameLen + extraLen), nil
} }
...@@ -218,30 +191,29 @@ func (f *File) findBodyOffset() (int64, error) { ...@@ -218,30 +191,29 @@ func (f *File) findBodyOffset() (int64, error) {
// It returns io.ErrUnexpectedEOF if it cannot read a complete header, // It returns io.ErrUnexpectedEOF if it cannot read a complete header,
// and ErrFormat if it doesn't find a valid header signature. // and ErrFormat if it doesn't find a valid header signature.
func readDirectoryHeader(f *File, r io.Reader) error { func readDirectoryHeader(f *File, r io.Reader) error {
var b [directoryHeaderLen]byte var buf [directoryHeaderLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, buf[:]); err != nil {
return err return err
} }
c := binary.LittleEndian b := readBuf(buf[:])
if sig := c.Uint32(b[:4]); sig != directoryHeaderSignature { if sig := b.uint32(); sig != directoryHeaderSignature {
return ErrFormat return ErrFormat
} }
f.CreatorVersion = c.Uint16(b[4:6]) f.CreatorVersion = b.uint16()
f.ReaderVersion = c.Uint16(b[6:8]) f.ReaderVersion = b.uint16()
f.Flags = c.Uint16(b[8:10]) f.Flags = b.uint16()
f.Method = c.Uint16(b[10:12]) f.Method = b.uint16()
f.ModifiedTime = c.Uint16(b[12:14]) f.ModifiedTime = b.uint16()
f.ModifiedDate = c.Uint16(b[14:16]) f.ModifiedDate = b.uint16()
f.CRC32 = c.Uint32(b[16:20]) f.CRC32 = b.uint32()
f.CompressedSize = c.Uint32(b[20:24]) f.CompressedSize = b.uint32()
f.UncompressedSize = c.Uint32(b[24:28]) f.UncompressedSize = b.uint32()
filenameLen := int(c.Uint16(b[28:30])) filenameLen := int(b.uint16())
extraLen := int(c.Uint16(b[30:32])) extraLen := int(b.uint16())
commentLen := int(c.Uint16(b[32:34])) commentLen := int(b.uint16())
// startDiskNumber := c.Uint16(b[34:36]) // Unused b = b[4:] // skipped start disk number and internal attributes (2x uint16)
// internalAttributes := c.Uint16(b[36:38]) // Unused f.ExternalAttrs = b.uint32()
f.ExternalAttrs = c.Uint32(b[38:42]) f.headerOffset = int64(b.uint32())
f.headerOffset = int64(c.Uint32(b[42:46]))
d := make([]byte, filenameLen+extraLen+commentLen) d := make([]byte, filenameLen+extraLen+commentLen)
if _, err := io.ReadFull(r, d); err != nil { if _, err := io.ReadFull(r, d); err != nil {
return err return err
...@@ -253,30 +225,30 @@ func readDirectoryHeader(f *File, r io.Reader) error { ...@@ -253,30 +225,30 @@ func readDirectoryHeader(f *File, r io.Reader) error {
} }
func readDataDescriptor(r io.Reader, f *File) error { func readDataDescriptor(r io.Reader, f *File) error {
var b [dataDescriptorLen]byte var buf [dataDescriptorLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, buf[:]); err != nil {
return err return err
} }
c := binary.LittleEndian b := readBuf(buf[:])
f.CRC32 = c.Uint32(b[:4]) f.CRC32 = b.uint32()
f.CompressedSize = c.Uint32(b[4:8]) f.CompressedSize = b.uint32()
f.UncompressedSize = c.Uint32(b[8:12]) f.UncompressedSize = b.uint32()
return nil return nil
} }
func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
// look for directoryEndSignature in the last 1k, then in the last 65k // look for directoryEndSignature in the last 1k, then in the last 65k
var b []byte var buf []byte
for i, bLen := range []int64{1024, 65 * 1024} { for i, bLen := range []int64{1024, 65 * 1024} {
if bLen > size { if bLen > size {
bLen = size bLen = size
} }
b = make([]byte, int(bLen)) buf = make([]byte, int(bLen))
if _, err := r.ReadAt(b, size-bLen); err != nil && err != io.EOF { if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF {
return nil, err return nil, err
} }
if p := findSignatureInBlock(b); p >= 0 { if p := findSignatureInBlock(buf); p >= 0 {
b = b[p:] buf = buf[p:]
break break
} }
if i == 1 || bLen == size { if i == 1 || bLen == size {
...@@ -285,16 +257,21 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) ...@@ -285,16 +257,21 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
} }
// read header into struct // read header into struct
c := binary.LittleEndian b := readBuf(buf[4:]) // skip signature
d := new(directoryEnd) d := &directoryEnd{
d.diskNbr = c.Uint16(b[4:6]) diskNbr: b.uint16(),
d.dirDiskNbr = c.Uint16(b[6:8]) dirDiskNbr: b.uint16(),
d.dirRecordsThisDisk = c.Uint16(b[8:10]) dirRecordsThisDisk: b.uint16(),
d.directoryRecords = c.Uint16(b[10:12]) directoryRecords: b.uint16(),
d.directorySize = c.Uint32(b[12:16]) directorySize: b.uint32(),
d.directoryOffset = c.Uint32(b[16:20]) directoryOffset: b.uint32(),
d.commentLen = c.Uint16(b[20:22]) commentLen: b.uint16(),
d.comment = string(b[22 : 22+int(d.commentLen)]) }
l := int(d.commentLen)
if l > len(b) {
return nil, errors.New("zip: invalid comment length")
}
d.comment = string(b[:l])
return d, nil return d, nil
} }
...@@ -311,3 +288,17 @@ func findSignatureInBlock(b []byte) int { ...@@ -311,3 +288,17 @@ func findSignatureInBlock(b []byte) int {
} }
return -1 return -1
} }
type readBuf []byte
func (b *readBuf) uint16() uint16 {
v := binary.LittleEndian.Uint16(*b)
*b = (*b)[2:]
return v
}
func (b *readBuf) uint32() uint32 {
v := binary.LittleEndian.Uint32(*b)
*b = (*b)[4:]
return v
}
...@@ -165,7 +165,7 @@ func readTestZip(t *testing.T, zt ZipTest) { ...@@ -165,7 +165,7 @@ func readTestZip(t *testing.T, zt ZipTest) {
t.Errorf("%s: comment=%q, want %q", zt.Name, z.Comment, zt.Comment) t.Errorf("%s: comment=%q, want %q", zt.Name, z.Comment, zt.Comment)
} }
if len(z.File) != len(zt.File) { if len(z.File) != len(zt.File) {
t.Errorf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File)) t.Fatalf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File))
} }
// test read of each file // test read of each file
......
...@@ -100,16 +100,6 @@ type directoryEnd struct { ...@@ -100,16 +100,6 @@ type directoryEnd struct {
comment string comment string
} }
func recoverError(errp *error) {
if e := recover(); e != nil {
if err, ok := e.(error); ok {
*errp = err
return
}
panic(e)
}
}
// msDosTimeToTime converts an MS-DOS date and time into a time.Time. // msDosTimeToTime converts an MS-DOS date and time into a time.Time.
// The resolution is 2s. // The resolution is 2s.
// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
......
...@@ -37,10 +37,10 @@ func NewWriter(w io.Writer) *Writer { ...@@ -37,10 +37,10 @@ func NewWriter(w io.Writer) *Writer {
// Close finishes writing the zip file by writing the central directory. // Close finishes writing the zip file by writing the central directory.
// It does not (and can not) close the underlying writer. // It does not (and can not) close the underlying writer.
func (w *Writer) Close() (err error) { func (w *Writer) Close() error {
if w.last != nil && !w.last.closed { if w.last != nil && !w.last.closed {
if err = w.last.close(); err != nil { if err := w.last.close(); err != nil {
return return err
} }
w.last = nil w.last = nil
} }
...@@ -49,43 +49,55 @@ func (w *Writer) Close() (err error) { ...@@ -49,43 +49,55 @@ func (w *Writer) Close() (err error) {
} }
w.closed = true w.closed = true
defer recoverError(&err)
// write central directory // write central directory
start := w.cw.count start := w.cw.count
for _, h := range w.dir { for _, h := range w.dir {
write(w.cw, uint32(directoryHeaderSignature)) var buf [directoryHeaderLen]byte
write(w.cw, h.CreatorVersion) b := writeBuf(buf[:])
write(w.cw, h.ReaderVersion) b.uint32(uint32(directoryHeaderSignature))
write(w.cw, h.Flags) b.uint16(h.CreatorVersion)
write(w.cw, h.Method) b.uint16(h.ReaderVersion)
write(w.cw, h.ModifiedTime) b.uint16(h.Flags)
write(w.cw, h.ModifiedDate) b.uint16(h.Method)
write(w.cw, h.CRC32) b.uint16(h.ModifiedTime)
write(w.cw, h.CompressedSize) b.uint16(h.ModifiedDate)
write(w.cw, h.UncompressedSize) b.uint32(h.CRC32)
write(w.cw, uint16(len(h.Name))) b.uint32(h.CompressedSize)
write(w.cw, uint16(len(h.Extra))) b.uint32(h.UncompressedSize)
write(w.cw, uint16(len(h.Comment))) b.uint16(uint16(len(h.Name)))
write(w.cw, uint16(0)) // disk number start b.uint16(uint16(len(h.Extra)))
write(w.cw, uint16(0)) // internal file attributes b.uint16(uint16(len(h.Comment)))
write(w.cw, h.ExternalAttrs) b = b[4:] // skip disk number start and internal file attr (2x uint16)
write(w.cw, h.offset) b.uint32(h.ExternalAttrs)
writeBytes(w.cw, []byte(h.Name)) b.uint32(h.offset)
writeBytes(w.cw, h.Extra) if _, err := w.cw.Write(buf[:]); err != nil {
writeBytes(w.cw, []byte(h.Comment)) return err
}
if _, err := io.WriteString(w.cw, h.Name); err != nil {
return err
}
if _, err := w.cw.Write(h.Extra); err != nil {
return err
}
if _, err := io.WriteString(w.cw, h.Comment); err != nil {
return err
}
} }
end := w.cw.count end := w.cw.count
// write end record // write end record
write(w.cw, uint32(directoryEndSignature)) var buf [directoryEndLen]byte
write(w.cw, uint16(0)) // disk number b := writeBuf(buf[:])
write(w.cw, uint16(0)) // disk number where directory starts b.uint32(uint32(directoryEndSignature))
write(w.cw, uint16(len(w.dir))) // number of entries this disk b = b[4:] // skip over disk number and first disk number (2x uint16)
write(w.cw, uint16(len(w.dir))) // number of entries total b.uint16(uint16(len(w.dir))) // number of entries this disk
write(w.cw, uint32(end-start)) // size of directory b.uint16(uint16(len(w.dir))) // number of entries total
write(w.cw, uint32(start)) // start of directory b.uint32(uint32(end - start)) // size of directory
write(w.cw, uint16(0)) // size of comment b.uint32(uint32(start)) // start of directory
// skipped size of comment (always zero)
if _, err := w.cw.Write(buf[:]); err != nil {
return err
}
return w.cw.w.(*bufio.Writer).Flush() return w.cw.w.(*bufio.Writer).Flush()
} }
...@@ -152,22 +164,28 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { ...@@ -152,22 +164,28 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
return fw, nil return fw, nil
} }
func writeHeader(w io.Writer, h *FileHeader) (err error) { func writeHeader(w io.Writer, h *FileHeader) error {
defer recoverError(&err) var buf [fileHeaderLen]byte
write(w, uint32(fileHeaderSignature)) b := writeBuf(buf[:])
write(w, h.ReaderVersion) b.uint32(uint32(fileHeaderSignature))
write(w, h.Flags) b.uint16(h.ReaderVersion)
write(w, h.Method) b.uint16(h.Flags)
write(w, h.ModifiedTime) b.uint16(h.Method)
write(w, h.ModifiedDate) b.uint16(h.ModifiedTime)
write(w, h.CRC32) b.uint16(h.ModifiedDate)
write(w, h.CompressedSize) b.uint32(h.CRC32)
write(w, h.UncompressedSize) b.uint32(h.CompressedSize)
write(w, uint16(len(h.Name))) b.uint32(h.UncompressedSize)
write(w, uint16(len(h.Extra))) b.uint16(uint16(len(h.Name)))
writeBytes(w, []byte(h.Name)) b.uint16(uint16(len(h.Extra)))
writeBytes(w, h.Extra) if _, err := w.Write(buf[:]); err != nil {
return nil return err
}
if _, err := io.WriteString(w, h.Name); err != nil {
return err
}
_, err := w.Write(h.Extra)
return err
} }
type fileWriter struct { type fileWriter struct {
...@@ -188,13 +206,13 @@ func (w *fileWriter) Write(p []byte) (int, error) { ...@@ -188,13 +206,13 @@ func (w *fileWriter) Write(p []byte) (int, error) {
return w.rawCount.Write(p) return w.rawCount.Write(p)
} }
func (w *fileWriter) close() (err error) { func (w *fileWriter) close() error {
if w.closed { if w.closed {
return errors.New("zip: file closed twice") return errors.New("zip: file closed twice")
} }
w.closed = true w.closed = true
if err = w.comp.Close(); err != nil { if err := w.comp.Close(); err != nil {
return return err
} }
// update FileHeader // update FileHeader
...@@ -204,12 +222,13 @@ func (w *fileWriter) close() (err error) { ...@@ -204,12 +222,13 @@ func (w *fileWriter) close() (err error) {
fh.UncompressedSize = uint32(w.rawCount.count) fh.UncompressedSize = uint32(w.rawCount.count)
// write data descriptor // write data descriptor
defer recoverError(&err) var buf [dataDescriptorLen]byte
write(w.zipw, fh.CRC32) b := writeBuf(buf[:])
write(w.zipw, fh.CompressedSize) b.uint32(fh.CRC32)
write(w.zipw, fh.UncompressedSize) b.uint32(fh.CompressedSize)
b.uint32(fh.UncompressedSize)
return nil _, err := w.zipw.Write(buf[:])
return err
} }
type countWriter struct { type countWriter struct {
...@@ -231,18 +250,14 @@ func (w nopCloser) Close() error { ...@@ -231,18 +250,14 @@ func (w nopCloser) Close() error {
return nil return nil
} }
func write(w io.Writer, data interface{}) { type writeBuf []byte
if err := binary.Write(w, binary.LittleEndian, data); err != nil {
panic(err) func (b *writeBuf) uint16(v uint16) {
} binary.LittleEndian.PutUint16(*b, v)
*b = (*b)[2:]
} }
func writeBytes(w io.Writer, b []byte) { func (b *writeBuf) uint32(v uint32) {
n, err := w.Write(b) binary.LittleEndian.PutUint32(*b, v)
if err != nil { *b = (*b)[4:]
panic(err)
}
if n != len(b) {
panic(io.ErrShortWrite)
}
} }
...@@ -23,7 +23,6 @@ var ( ...@@ -23,7 +23,6 @@ var (
ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune") ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
ErrBufferFull = errors.New("bufio: buffer full") ErrBufferFull = errors.New("bufio: buffer full")
ErrNegativeCount = errors.New("bufio: negative count") ErrNegativeCount = errors.New("bufio: negative count")
errInternal = errors.New("bufio: internal error")
) )
// Buffered input. // Buffered input.
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
package ecdsa package ecdsa
// References: // References:
// [NSA]: Suite B implementor's guide to FIPS 186-3, // [NSA]: Suite B implementer's guide to FIPS 186-3,
// http://www.nsa.gov/ia/_files/ecdsa.pdf // http://www.nsa.gov/ia/_files/ecdsa.pdf
// [SECG]: SECG, SEC1 // [SECG]: SECG, SEC1
// http://www.secg.org/download/aid-780/sec1-v2.pdf // http://www.secg.org/download/aid-780/sec1-v2.pdf
......
...@@ -5,11 +5,9 @@ ...@@ -5,11 +5,9 @@
package tls package tls
/* /*
// Note: We disable -Werror here because the code in this file uses a deprecated API to stay #cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060
// compatible with both Mac OS X 10.6 and 10.7. Using a deprecated function on Darwin generates
// a warning.
#cgo CFLAGS: -Wno-error -Wno-deprecated-declarations
#cgo LDFLAGS: -framework CoreFoundation -framework Security #cgo LDFLAGS: -framework CoreFoundation -framework Security
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h> #include <Security/Security.h>
...@@ -40,26 +38,12 @@ int FetchPEMRoots(CFDataRef *pemRoots) { ...@@ -40,26 +38,12 @@ int FetchPEMRoots(CFDataRef *pemRoots) {
continue; continue;
} }
// SecKeychainImportExport is deprecated in >= OS X 10.7, and has been replaced by // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
// SecItemExport. If we're built on a host with a Lion SDK, this code gets conditionally // Once we support weak imports via cgo we should prefer that, and fall back to this
// included in the output, also for binaries meant for 10.6. // for older systems.
// err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
// To make sure that we run on both Mac OS X 10.6 and 10.7 we use weak linking if (err != noErr) {
// and check whether SecItemExport is available before we attempt to call it. On continue;
// 10.6, this won't be the case, and we'll fall back to calling SecKeychainItemExport.
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
if (SecItemExport) {
err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
if (err != noErr) {
continue;
}
} else
#endif
if (data == NULL) {
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
if (err != noErr) {
continue;
}
} }
if (data != NULL) { if (data != NULL) {
......
...@@ -135,8 +135,8 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) error { ...@@ -135,8 +135,8 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
// Verify attempts to verify c by building one or more chains from c to a // Verify attempts to verify c by building one or more chains from c to a
// certificate in opts.roots, using certificates in opts.Intermediates if // certificate in opts.roots, using certificates in opts.Intermediates if
// needed. If successful, it returns one or chains where the first element of // needed. If successful, it returns one or more chains where the first
// the chain is c and the last element is from opts.Roots. // element of the chain is c and the last element is from opts.Roots.
// //
// WARNING: this doesn't do any revocation checking. // WARNING: this doesn't do any revocation checking.
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
......
...@@ -153,7 +153,7 @@ const ( ...@@ -153,7 +153,7 @@ const (
// //
// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } // md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
// //
// md5WithRSAEncryption OBJECT IDENTIFER ::= { pkcs-1 4 } // md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
// //
// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } // sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
// //
...@@ -172,9 +172,9 @@ const ( ...@@ -172,9 +172,9 @@ const (
// //
// RFC 5758 3.1 DSA Signature Algorithms // RFC 5758 3.1 DSA Signature Algorithms
// //
// dsaWithSha356 OBJECT IDENTIFER ::= { // dsaWithSha256 OBJECT IDENTIFIER ::= {
// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) // joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101)
// algorithms(4) id-dsa-with-sha2(3) 2} // csor(3) algorithms(4) id-dsa-with-sha2(3) 2}
// //
var ( var (
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
......
...@@ -29,17 +29,13 @@ type ByteOrder interface { ...@@ -29,17 +29,13 @@ type ByteOrder interface {
String() string String() string
} }
// This is byte instead of struct{} so that it can be compared,
// allowing, e.g., order == binary.LittleEndian.
type unused byte
// LittleEndian is the little-endian implementation of ByteOrder. // LittleEndian is the little-endian implementation of ByteOrder.
var LittleEndian littleEndian var LittleEndian littleEndian
// BigEndian is the big-endian implementation of ByteOrder. // BigEndian is the big-endian implementation of ByteOrder.
var BigEndian bigEndian var BigEndian bigEndian
type littleEndian unused type littleEndian struct{}
func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 }
...@@ -79,7 +75,7 @@ func (littleEndian) String() string { return "LittleEndian" } ...@@ -79,7 +75,7 @@ func (littleEndian) String() string { return "LittleEndian" }
func (littleEndian) GoString() string { return "binary.LittleEndian" } func (littleEndian) GoString() string { return "binary.LittleEndian" }
type bigEndian unused type bigEndian struct{}
func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 } func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 }
......
...@@ -1455,11 +1455,14 @@ func TestFuzz(t *testing.T) { ...@@ -1455,11 +1455,14 @@ func TestFuzz(t *testing.T) {
func TestFuzzRegressions(t *testing.T) { func TestFuzzRegressions(t *testing.T) {
// An instance triggering a type name of length ~102 GB. // An instance triggering a type name of length ~102 GB.
testFuzz(t, 1328492090837718000, 100, new(float32)) testFuzz(t, 1328492090837718000, 100, new(float32))
// An instance triggering a type name of 1.6 GB.
// Commented out because it takes 5m to run.
//testFuzz(t, 1330522872628565000, 100, new(int))
} }
func testFuzz(t *testing.T, seed int64, n int, input ...interface{}) { func testFuzz(t *testing.T, seed int64, n int, input ...interface{}) {
t.Logf("seed=%d n=%d\n", seed, n)
for _, e := range input { for _, e := range input {
t.Logf("seed=%d n=%d e=%T", seed, n, e)
rng := rand.New(rand.NewSource(seed)) rng := rand.New(rand.NewSource(seed))
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
encFuzzDec(rng, e) encFuzzDec(rng, e)
......
...@@ -3,14 +3,15 @@ ...@@ -3,14 +3,15 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Delete the next line to include in the gob package. // Delete the next line to include in the gob package.
// +build gob-debug // +build ignore
package gob package gob
// This file is not normally included in the gob package. Used only for debugging the package itself. // This file is not normally included in the gob package. Used only for debugging the package itself.
// Add debug.go to the files listed in the Makefile to add Debug to the gob package.
// Except for reading uints, it is an implementation of a reader that is independent of // Except for reading uints, it is an implementation of a reader that is independent of
// the one implemented by Decoder. // the one implemented by Decoder.
// To enable the Debug function, delete the +build ignore line above and do
// go install
import ( import (
"bytes" "bytes"
......
...@@ -392,12 +392,12 @@ func decUint8Slice(i *decInstr, state *decoderState, p unsafe.Pointer) { ...@@ -392,12 +392,12 @@ func decUint8Slice(i *decInstr, state *decoderState, p unsafe.Pointer) {
} }
p = *(*unsafe.Pointer)(p) p = *(*unsafe.Pointer)(p)
} }
n := int(state.decodeUint()) n := state.decodeUint()
if n < 0 { if n > uint64(state.b.Len()) {
errorf("negative length decoding []byte") errorf("length of []byte exceeds input size (%d bytes)", n)
} }
slice := (*[]uint8)(p) slice := (*[]uint8)(p)
if cap(*slice) < n { if uint64(cap(*slice)) < n {
*slice = make([]uint8, n) *slice = make([]uint8, n)
} else { } else {
*slice = (*slice)[0:n] *slice = (*slice)[0:n]
...@@ -417,7 +417,11 @@ func decString(i *decInstr, state *decoderState, p unsafe.Pointer) { ...@@ -417,7 +417,11 @@ func decString(i *decInstr, state *decoderState, p unsafe.Pointer) {
} }
p = *(*unsafe.Pointer)(p) p = *(*unsafe.Pointer)(p)
} }
b := make([]byte, state.decodeUint()) n := state.decodeUint()
if n > uint64(state.b.Len()) {
errorf("string length exceeds input size (%d bytes)", n)
}
b := make([]byte, n)
state.b.Read(b) state.b.Read(b)
// It would be a shame to do the obvious thing here, // It would be a shame to do the obvious thing here,
// *(*string)(p) = string(b) // *(*string)(p) = string(b)
...@@ -647,7 +651,11 @@ func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) { ...@@ -647,7 +651,11 @@ func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) {
// decodeSlice decodes a slice and stores the slice header through p. // decodeSlice decodes a slice and stores the slice header through p.
// Slices are encoded as an unsigned length followed by the elements. // Slices are encoded as an unsigned length followed by the elements.
func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl error) { func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl error) {
n := int(uintptr(state.decodeUint())) nr := state.decodeUint()
if nr > uint64(state.b.Len()) {
errorf("length of slice exceeds input size (%d elements)", nr)
}
n := int(nr)
if indir > 0 { if indir > 0 {
up := unsafe.Pointer(p) up := unsafe.Pointer(p)
if *(*unsafe.Pointer)(up) == nil { if *(*unsafe.Pointer)(up) == nil {
...@@ -702,6 +710,9 @@ func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p ui ...@@ -702,6 +710,9 @@ func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p ui
*(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData() *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData()
return return
} }
if len(name) > 1024 {
errorf("name too long (%d bytes): %.20q...", len(name), name)
}
// The concrete type must be registered. // The concrete type must be registered.
typ, ok := nameToConcreteType[name] typ, ok := nameToConcreteType[name]
if !ok { if !ok {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package main package main
// Need to compile package gob with debug.go to build this program. // Need to compile package gob with debug.go to build this program.
// See comments in debug.go for how to do this.
import ( import (
"encoding/gob" "encoding/gob"
......
...@@ -709,7 +709,7 @@ func TestGobPtrSlices(t *testing.T) { ...@@ -709,7 +709,7 @@ func TestGobPtrSlices(t *testing.T) {
t.Fatal("decode:", err) t.Fatal("decode:", err)
} }
if !reflect.DeepEqual(in, out) { if !reflect.DeepEqual(in, out) {
t.Fatal("got %v; wanted %v", out, in) t.Fatalf("got %v; wanted %v", out, in)
} }
} }
......
...@@ -239,16 +239,6 @@ func TestEscape(t *testing.T) { ...@@ -239,16 +239,6 @@ func TestEscape(t *testing.T) {
} }
} }
func TestHTMLEscape(t *testing.T) {
b, err := MarshalForHTML("foobarbaz<>&quux")
if err != nil {
t.Fatalf("MarshalForHTML error: %v", err)
}
if !bytes.Equal(b, []byte(`"foobarbaz\u003c\u003e\u0026quux"`)) {
t.Fatalf("Unexpected encoding of \"<>&\": %s", b)
}
}
// WrongString is a struct that's misusing the ,string modifier. // WrongString is a struct that's misusing the ,string modifier.
type WrongString struct { type WrongString struct {
Message string `json:"result,string"` Message string `json:"result,string"`
......
...@@ -123,17 +123,6 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { ...@@ -123,17 +123,6 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
// MarshalForHTML is like Marshal but applies HTMLEscape to the output.
func MarshalForHTML(v interface{}) ([]byte, error) {
b, err := Marshal(v)
if err != nil {
return nil, err
}
var buf bytes.Buffer
HTMLEscape(&buf, b)
return buf.Bytes(), nil
}
// HTMLEscape appends to dst the JSON-encoded src with <, >, and & // HTMLEscape appends to dst the JSON-encoded src with <, >, and &
// characters inside string literals changed to \u003c, \u003e, \u0026 // characters inside string literals changed to \u003c, \u003e, \u0026
// so that the JSON will be safe to embed inside HTML <script> tags. // so that the JSON will be safe to embed inside HTML <script> tags.
...@@ -200,11 +189,6 @@ func (e *MarshalerError) Error() string { ...@@ -200,11 +189,6 @@ func (e *MarshalerError) Error() string {
return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Err.Error() return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Err.Error()
} }
type interfaceOrPtrValue interface {
IsNil() bool
Elem() reflect.Value
}
var hex = "0123456789abcdef" var hex = "0123456789abcdef"
// An encodeState encodes JSON into a bytes.Buffer. // An encodeState encodes JSON into a bytes.Buffer.
...@@ -276,7 +260,7 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { ...@@ -276,7 +260,7 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
b, err := m.MarshalJSON() b, err := m.MarshalJSON()
if err == nil { if err == nil {
// copy JSON into buffer, checking validity. // copy JSON into buffer, checking validity.
err = Compact(&e.Buffer, b) err = compact(&e.Buffer, b, true)
} }
if err != nil { if err != nil {
e.error(&MarshalerError{v.Type(), err}) e.error(&MarshalerError{v.Type(), err})
......
...@@ -167,3 +167,22 @@ func TestRefValMarshal(t *testing.T) { ...@@ -167,3 +167,22 @@ func TestRefValMarshal(t *testing.T) {
t.Errorf("got %q, want %q", got, want) t.Errorf("got %q, want %q", got, want)
} }
} }
// C implements Marshaler and returns unescaped JSON.
type C int
func (C) MarshalJSON() ([]byte, error) {
return []byte(`"<&>"`), nil
}
func TestMarshalerEscaping(t *testing.T) {
var c C
const want = `"\u003c\u0026\u003e"`
b, err := Marshal(c)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
if got := string(b); got != want {
t.Errorf("got %q, want %q", got, want)
}
}
...@@ -9,11 +9,24 @@ import "bytes" ...@@ -9,11 +9,24 @@ import "bytes"
// Compact appends to dst the JSON-encoded src with // Compact appends to dst the JSON-encoded src with
// insignificant space characters elided. // insignificant space characters elided.
func Compact(dst *bytes.Buffer, src []byte) error { func Compact(dst *bytes.Buffer, src []byte) error {
return compact(dst, src, false)
}
func compact(dst *bytes.Buffer, src []byte, escape bool) error {
origLen := dst.Len() origLen := dst.Len()
var scan scanner var scan scanner
scan.reset() scan.reset()
start := 0 start := 0
for i, c := range src { for i, c := range src {
if escape && (c == '<' || c == '>' || c == '&') {
if start < i {
dst.Write(src[start:i])
}
dst.WriteString(`\u00`)
dst.WriteByte(hex[c>>4])
dst.WriteByte(hex[c&0xF])
start = i + 1
}
v := scan.step(&scan, int(c)) v := scan.step(&scan, int(c))
if v >= scanSkipSpace { if v >= scanSkipSpace {
if v == scanError { if v == scanError {
......
...@@ -136,12 +136,12 @@ type NamePrecedence struct { ...@@ -136,12 +136,12 @@ type NamePrecedence struct {
type XMLNameWithTag struct { type XMLNameWithTag struct {
XMLName Name `xml:"InXMLNameTag"` XMLName Name `xml:"InXMLNameTag"`
Value string ",chardata" Value string `xml:",chardata"`
} }
type XMLNameWithoutTag struct { type XMLNameWithoutTag struct {
XMLName Name XMLName Name
Value string ",chardata" Value string `xml:",chardata"`
} }
type NameInField struct { type NameInField struct {
...@@ -532,9 +532,9 @@ var marshalTests = []struct { ...@@ -532,9 +532,9 @@ var marshalTests = []struct {
InFieldName: "D", InFieldName: "D",
}, },
ExpectXML: `<Parent>` + ExpectXML: `<Parent>` +
`<InTag><Value>A</Value></InTag>` + `<InTag>A</InTag>` +
`<InXMLName><Value>B</Value></InXMLName>` + `<InXMLName>B</InXMLName>` +
`<InXMLNameTag><Value>C</Value></InXMLNameTag>` + `<InXMLNameTag>C</InXMLNameTag>` +
`<InFieldName>D</InFieldName>` + `<InFieldName>D</InFieldName>` +
`</Parent>`, `</Parent>`,
MarshalOnly: true, MarshalOnly: true,
...@@ -548,9 +548,9 @@ var marshalTests = []struct { ...@@ -548,9 +548,9 @@ var marshalTests = []struct {
InFieldName: "D", InFieldName: "D",
}, },
ExpectXML: `<Parent>` + ExpectXML: `<Parent>` +
`<InTag><Value>A</Value></InTag>` + `<InTag>A</InTag>` +
`<FromNameVal><Value>B</Value></FromNameVal>` + `<FromNameVal>B</FromNameVal>` +
`<InXMLNameTag><Value>C</Value></InXMLNameTag>` + `<InXMLNameTag>C</InXMLNameTag>` +
`<InFieldName>D</InFieldName>` + `<InFieldName>D</InFieldName>` +
`</Parent>`, `</Parent>`,
UnmarshalOnly: true, UnmarshalOnly: true,
......
...@@ -34,6 +34,8 @@ The flags are: ...@@ -34,6 +34,8 @@ The flags are:
Verbose mode. Verbose mode.
Debugging flags: Debugging flags:
-comments
Parse comments (ignored if -ast not set).
-ast -ast
Print AST (disables concurrent parsing). Print AST (disables concurrent parsing).
-trace -trace
......
...@@ -27,8 +27,9 @@ var ( ...@@ -27,8 +27,9 @@ var (
allErrors = flag.Bool("e", false, "print all (including spurious) errors") allErrors = flag.Bool("e", false, "print all (including spurious) errors")
// debugging support // debugging support
printTrace = flag.Bool("trace", false, "print parse trace") parseComments = flag.Bool("comments", false, "parse comments (ignored if -ast not set)")
printAST = flag.Bool("ast", false, "print AST") printTrace = flag.Bool("trace", false, "print parse trace")
printAST = flag.Bool("ast", false, "print AST")
) )
var exitCode = 0 var exitCode = 0
...@@ -73,6 +74,9 @@ func parse(fset *token.FileSet, filename string, src []byte) *ast.File { ...@@ -73,6 +74,9 @@ func parse(fset *token.FileSet, filename string, src []byte) *ast.File {
if *allErrors { if *allErrors {
mode |= parser.SpuriousErrors mode |= parser.SpuriousErrors
} }
if *parseComments && *printAST {
mode |= parser.ParseComments
}
if *printTrace { if *printTrace {
mode |= parser.Trace mode |= parser.Trace
} }
......
...@@ -110,7 +110,7 @@ func (s *nodeStack) top() *Node { ...@@ -110,7 +110,7 @@ func (s *nodeStack) top() *Node {
return nil return nil
} }
// index returns the index of the top-most occurence of n in the stack, or -1 // index returns the index of the top-most occurrence of n in the stack, or -1
// if n is not present. // if n is not present.
func (s *nodeStack) index(n *Node) int { func (s *nodeStack) index(n *Node) int {
for i := len(*s) - 1; i >= 0; i-- { for i := len(*s) - 1; i >= 0; i-- {
......
...@@ -18,17 +18,17 @@ package norm ...@@ -18,17 +18,17 @@ package norm
// has the form: // has the form:
// <header> <decomp_byte>* [<tccc> [<lccc>]] // <header> <decomp_byte>* [<tccc> [<lccc>]]
// The header contains the number of bytes in the decomposition (excluding this // The header contains the number of bytes in the decomposition (excluding this
// length byte). The two most significant bits of this lenght byte correspond // length byte). The two most significant bits of this length byte correspond
// to bit 2 and 3 of qcIfo (see below). The byte sequence itself starts at v+1. // to bit 2 and 3 of qcIfo (see below). The byte sequence itself starts at v+1.
// The byte sequence is followed by a trailing and leading CCC if the values // The byte sequence is followed by a trailing and leading CCC if the values
// for these are not zero. The value of v determines which ccc are appended // for these are not zero. The value of v determines which ccc are appended
// to the sequences. For v < firstCCC, there are none, for v >= firstCCC, // to the sequences. For v < firstCCC, there are none, for v >= firstCCC,
// the seqence is followed by a trailing ccc, and for v >= firstLeadingCC // the sequence is followed by a trailing ccc, and for v >= firstLeadingCC
// there is an additional leading ccc. // there is an additional leading ccc.
const ( const (
qcInfoMask = 0xF // to clear all but the relevant bits in a qcInfo qcInfoMask = 0xF // to clear all but the relevant bits in a qcInfo
headerLenMask = 0x3F // extract the lenght value from the header byte headerLenMask = 0x3F // extract the length value from the header byte
headerFlagsMask = 0xC0 // extract the qcInfo bits from the header byte headerFlagsMask = 0xC0 // extract the qcInfo bits from the header byte
) )
......
...@@ -75,7 +75,7 @@ func (p *PerHost) dialerForRequest(host string) Dialer { ...@@ -75,7 +75,7 @@ func (p *PerHost) dialerForRequest(host string) Dialer {
} }
// AddFromString parses a string that contains comma-separated values // AddFromString parses a string that contains comma-separated values
// specifing hosts that should use the bypass proxy. Each value is either an // specifying hosts that should use the bypass proxy. Each value is either an
// IP address, a CIDR range, a zone (*.example.com) or a hostname // IP address, a CIDR range, a zone (*.example.com) or a hostname
// (localhost). A best effort is made to parse the string and errors are // (localhost). A best effort is made to parse the string and errors are
// ignored. // ignored.
......
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings"
"text/scanner" "text/scanner"
) )
...@@ -39,11 +40,14 @@ func findPkg(path string) (filename, id string) { ...@@ -39,11 +40,14 @@ func findPkg(path string) (filename, id string) {
switch path[0] { switch path[0] {
default: default:
// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
tree, pkg, err := build.FindTree(path) bp, _ := build.Import(path, "", build.FindOnly)
if err != nil { if bp.PkgObj == "" {
return return
} }
noext = filepath.Join(tree.PkgDir(), pkg) noext = bp.PkgObj
if strings.HasSuffix(noext, ".a") {
noext = noext[:len(noext)-2]
}
case '.': case '.':
// "./x" -> "/this/directory/x.ext", "/this/directory/x" // "./x" -> "/this/directory/x.ext", "/this/directory/x"
...@@ -742,7 +746,7 @@ func (p *gcParser) parseVarDecl() { ...@@ -742,7 +746,7 @@ func (p *gcParser) parseVarDecl() {
} }
// FuncBody = "{" ... "}" . // FuncBody = "{" ... "}" .
// //
func (p *gcParser) parseFuncBody() { func (p *gcParser) parseFuncBody() {
p.expect('{') p.expect('{')
for i := 1; i > 0; p.next() { for i := 1; i > 0; p.next() {
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// PACKAGE UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE. // Package types declares the types used to represent Go types
// Package types declares the types used to represent Go types. // (UNDER CONSTRUCTION). ANY AND ALL PARTS MAY CHANGE.
// //
package types package types
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This file contains printing suppport for ASTs. // This file contains printing support for ASTs.
package ast package ast
......
...@@ -5,83 +5,14 @@ ...@@ -5,83 +5,14 @@
package build package build
import ( import (
"os"
"path/filepath" "path/filepath"
"reflect"
"runtime" "runtime"
"sort"
"testing" "testing"
) )
func sortstr(x []string) []string {
sort.Strings(x)
return x
}
var buildPkgs = []struct {
dir string
info *DirInfo
}{
{
"go/build/pkgtest",
&DirInfo{
GoFiles: []string{"pkgtest.go"},
SFiles: []string{"sqrt_" + runtime.GOARCH + ".s"},
Package: "pkgtest",
Imports: []string{"bytes"},
TestImports: []string{"fmt", "pkgtest"},
TestGoFiles: sortstr([]string{"sqrt_test.go", "sqrt_" + runtime.GOARCH + "_test.go"}),
XTestGoFiles: []string{"xsqrt_test.go"},
},
},
{
"go/build/cmdtest",
&DirInfo{
GoFiles: []string{"main.go"},
Package: "main",
Imports: []string{"go/build/pkgtest"},
TestImports: []string{},
},
},
{
"go/build/cgotest",
&DirInfo{
CgoFiles: ifCgo([]string{"cgotest.go"}),
CFiles: []string{"cgotest.c"},
HFiles: []string{"cgotest.h"},
Imports: []string{"C", "unsafe"},
TestImports: []string{},
Package: "cgotest",
},
},
}
func ifCgo(x []string) []string {
if DefaultContext.CgoEnabled {
return x
}
return nil
}
func TestBuild(t *testing.T) {
for _, tt := range buildPkgs {
tree := Path[0] // Goroot
dir := filepath.Join(tree.SrcDir(), tt.dir)
info, err := ScanDir(dir)
if err != nil {
t.Errorf("ScanDir(%#q): %v", tt.dir, err)
continue
}
// Don't bother testing import positions.
tt.info.ImportPos, tt.info.TestImportPos = info.ImportPos, info.TestImportPos
if !reflect.DeepEqual(info, tt.info) {
t.Errorf("ScanDir(%#q) = %#v, want %#v\n", tt.dir, info, tt.info)
continue
}
}
}
func TestMatch(t *testing.T) { func TestMatch(t *testing.T) {
ctxt := DefaultContext ctxt := Default
what := "default" what := "default"
match := func(tag string) { match := func(tag string) {
if !ctxt.match(tag) { if !ctxt.match(tag) {
...@@ -106,3 +37,40 @@ func TestMatch(t *testing.T) { ...@@ -106,3 +37,40 @@ func TestMatch(t *testing.T) {
match(runtime.GOOS + "," + runtime.GOARCH + ",!bar") match(runtime.GOOS + "," + runtime.GOARCH + ",!bar")
nomatch(runtime.GOOS + "," + runtime.GOARCH + ",bar") nomatch(runtime.GOOS + "," + runtime.GOARCH + ",bar")
} }
func TestDotSlashImport(t *testing.T) {
p, err := ImportDir("testdata/other", 0)
if err != nil {
t.Fatal(err)
}
if len(p.Imports) != 1 || p.Imports[0] != "./file" {
t.Fatalf("testdata/other: Imports=%v, want [./file]", p.Imports)
}
p1, err := Import("./file", "testdata/other", 0)
if err != nil {
t.Fatal(err)
}
if p1.Name != "file" {
t.Fatalf("./file: Name=%q, want %q", p1.Name, "file")
}
dir := filepath.Clean("testdata/other/file") // Clean to use \ on Windows
if p1.Dir != dir {
t.Fatalf("./file: Dir=%q, want %q", p1.Name, dir)
}
}
func TestLocalDirectory(t *testing.T) {
cwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
p, err := ImportDir(cwd, 0)
if err != nil {
t.Fatal(err)
}
if p.ImportPath != "go/build" {
t.Fatalf("ImportPath=%q, want %q", p.ImportPath, "go/build")
}
}
// Copyright 2011 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 cgotest
/*
char* greeting = "hello, world";
*/
// #include "cgotest.h"
import "C"
import "unsafe"
var Greeting = C.GoString(C.greeting)
func DoAdd(x, y int) (sum int) {
C.Add(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&sum)))
return
}
// Copyright 2011 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 main
import "go/build/pkgtest"
func main() {
pkgtest.Foo()
print(int(pkgtest.Sqrt(9)))
}
// Copyright 2011 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 build gathers information about Go packages.
//
// Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
// Go tree. The default path is the value of the GOPATH environment
// variable, interpreted as a path list appropriate to the operating system
// (on Unix, the variable is a colon-separated string;
// on Windows, a semicolon-separated string;
// on Plan 9, a list).
//
// Each directory listed in the Go path must have a prescribed structure:
//
// The src/ directory holds source code. The path below 'src' determines
// the import path or executable name.
//
// The pkg/ directory holds installed package objects.
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path. That is, the
// command with source in DIR/src/foo/quux is installed into
// DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
//
// Here's an example directory layout:
//
// GOPATH=/home/user/gocode
//
// /home/user/gocode/
// src/
// foo/
// bar/ (go code in package bar)
// x.go
// quux/ (go code in package main)
// y.go
// bin/
// quux (installed command)
// pkg/
// linux_amd64/
// foo/
// bar.a (installed package object)
//
// Build Constraints
//
// A build constraint is a line comment beginning with the directive +build
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must be appear near the top of the file, preceded
// only by blank lines and other line comments.
//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
// That is, the build constraint:
//
// // +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
// (linux AND 386) OR (darwin AND (NOT cgo))
//
// During a particular build, the following words are satisfied:
//
// - the target operating system, as spelled by runtime.GOOS
// - the target architecture, as spelled by runtime.GOARCH
// - "cgo", if ctxt.CgoEnabled is true
// - any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches *_GOOS, *_GOARCH, or *_GOOS_GOARCH for any known operating
// system and architecture values, then the file is considered to have an implicit
// build constraint requiring those terms.
//
// To keep a file from being considered for the build:
//
// // +build ignore
//
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
// // +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
// // +build !linux !darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
package build
// Copyright 2011 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 build
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
)
// ToolDir is the directory containing build tools.
var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
// Path is a validated list of Trees derived from $GOROOT and $GOPATH at init.
var Path []*Tree
// Tree describes a Go source tree, either $GOROOT or one from $GOPATH.
type Tree struct {
Path string
Goroot bool
}
func newTree(p string) (*Tree, error) {
if !filepath.IsAbs(p) {
return nil, errors.New("must be absolute")
}
ep, err := filepath.EvalSymlinks(p)
if err != nil {
return nil, err
}
return &Tree{Path: ep}, nil
}
// SrcDir returns the tree's package source directory.
func (t *Tree) SrcDir() string {
if t.Goroot {
return filepath.Join(t.Path, "src", "pkg")
}
return filepath.Join(t.Path, "src")
}
// PkgDir returns the tree's package object directory.
func (t *Tree) PkgDir() string {
goos, goarch := runtime.GOOS, runtime.GOARCH
if e := os.Getenv("GOOS"); e != "" {
goos = e
}
if e := os.Getenv("GOARCH"); e != "" {
goarch = e
}
return filepath.Join(t.Path, "pkg", goos+"_"+goarch)
}
// BinDir returns the tree's binary executable directory.
func (t *Tree) BinDir() string {
if t.Goroot {
if gobin := os.Getenv("GOBIN"); gobin != "" {
return filepath.Clean(gobin)
}
}
return filepath.Join(t.Path, "bin")
}
// HasSrc returns whether the given package's
// source can be found inside this Tree.
func (t *Tree) HasSrc(pkg string) bool {
fi, err := os.Stat(filepath.Join(t.SrcDir(), pkg))
if err != nil {
return false
}
return fi.IsDir()
}
// HasPkg returns whether the given package's
// object file can be found inside this Tree.
func (t *Tree) HasPkg(pkg string) bool {
fi, err := os.Stat(filepath.Join(t.PkgDir(), pkg+".a"))
if err != nil {
return false
}
return !fi.IsDir()
}
var (
ErrNotFound = errors.New("package could not be found locally")
ErrTreeNotFound = errors.New("no valid GOROOT or GOPATH could be found")
)
// FindTree takes an import or filesystem path and returns the
// tree where the package source should be and the package import path.
func FindTree(path string) (tree *Tree, pkg string, err error) {
if isLocalPath(path) {
if path, err = filepath.Abs(path); err != nil {
return
}
if path, err = filepath.EvalSymlinks(path); err != nil {
return
}
for _, t := range Path {
tpath := t.SrcDir() + string(filepath.Separator)
if !filepath.HasPrefix(path, tpath) {
continue
}
tree = t
pkg = filepath.ToSlash(path[len(tpath):])
return
}
err = fmt.Errorf("path %q not inside a GOPATH", path)
return
}
tree = defaultTree
pkg = filepath.ToSlash(path)
for _, t := range Path {
if t.HasSrc(pkg) {
tree = t
return
}
}
if tree == nil {
err = ErrTreeNotFound
} else {
err = ErrNotFound
}
return
}
// isLocalPath returns whether the given path is local (/foo ./foo ../foo . ..)
// Windows paths that starts with drive letter (c:\foo c:foo) are considered local.
func isLocalPath(s string) bool {
const sep = string(filepath.Separator)
return s == "." || s == ".." ||
filepath.HasPrefix(s, sep) ||
filepath.HasPrefix(s, "."+sep) || filepath.HasPrefix(s, ".."+sep) ||
filepath.VolumeName(s) != ""
}
var (
// argument lists used by the build's gc and ld methods
gcImportArgs []string
ldImportArgs []string
// default tree for remote packages
defaultTree *Tree
)
// set up Path: parse and validate GOROOT and GOPATH variables
func init() {
root := runtime.GOROOT()
t, err := newTree(root)
if err == nil {
t.Goroot = true
Path = []*Tree{t}
}
for _, p := range filepath.SplitList(os.Getenv("GOPATH")) {
if p == "" {
continue
}
t, err := newTree(p)
if err != nil {
continue
}
Path = append(Path, t)
gcImportArgs = append(gcImportArgs, "-I", t.PkgDir())
ldImportArgs = append(ldImportArgs, "-L", t.PkgDir())
// select first GOPATH entry as default
if defaultTree == nil {
defaultTree = t
}
}
// use GOROOT if no valid GOPATH specified
if defaultTree == nil && len(Path) > 0 {
defaultTree = Path[0]
}
}
// Copyright 2011 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 pkgtest
import "bytes"
func Foo() *bytes.Buffer {
return nil
}
func Sqrt(x float64) float64
// Copyright 2011 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 pkgtest
import "fmt"
var _ = fmt.Printf
// Copyright 2011 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 pkgtest_test
import "pkgtest"
var _ = pkgtest.Foo
...@@ -55,7 +55,7 @@ var tests = []GoodFileTest{ ...@@ -55,7 +55,7 @@ var tests = []GoodFileTest{
func TestGoodOSArch(t *testing.T) { func TestGoodOSArch(t *testing.T) {
for _, test := range tests { for _, test := range tests {
if DefaultContext.goodOSArchFile(test.name) != test.result { if Default.goodOSArchFile(test.name) != test.result {
t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result) t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result)
} }
} }
......
// Test data - not compiled.
package file
func F() {}
// Test data - not compiled.
package main
import (
"./file"
)
func main() {
file.F()
}
...@@ -432,6 +432,17 @@ func (r *reader) readFile(src *ast.File) { ...@@ -432,6 +432,17 @@ func (r *reader) readFile(src *ast.File) {
r.readValue(d) r.readValue(d)
case token.TYPE: case token.TYPE:
// types are handled individually // types are handled individually
if len(d.Specs) == 1 && !d.Lparen.IsValid() {
// common case: single declaration w/o parentheses
// (if a single declaration is parenthesized,
// create a new fake declaration below, so that
// go/doc type declarations always appear w/o
// parentheses)
if s, ok := d.Specs[0].(*ast.TypeSpec); ok {
r.readType(d, s)
}
break
}
for _, spec := range d.Specs { for _, spec := range d.Specs {
if s, ok := spec.(*ast.TypeSpec); ok { if s, ok := spec.(*ast.TypeSpec); ok {
// use an individual (possibly fake) declaration // use an individual (possibly fake) declaration
...@@ -439,8 +450,13 @@ func (r *reader) readFile(src *ast.File) { ...@@ -439,8 +450,13 @@ func (r *reader) readFile(src *ast.File) {
// gets to (re-)use the declaration documentation // gets to (re-)use the declaration documentation
// if there's none associated with the spec itself // if there's none associated with the spec itself
fake := &ast.GenDecl{ fake := &ast.GenDecl{
Doc: d.Doc, Doc: d.Doc,
TokPos: d.Pos(), // don't use the existing TokPos because it
// will lead to the wrong selection range for
// the fake declaration if there are more
// than one type in the group (this affects
// src/cmd/godoc/godoc.go's posLink_urlFunc)
TokPos: s.Pos(),
Tok: token.TYPE, Tok: token.TYPE,
Specs: []ast.Spec{s}, Specs: []ast.Spec{s},
} }
......
...@@ -14,6 +14,9 @@ import ( ...@@ -14,6 +14,9 @@ import (
"go/ast" "go/ast"
"go/scanner" "go/scanner"
"go/token" "go/token"
"strconv"
"strings"
"unicode"
) )
// The parser structure holds the parser's internal state. // The parser structure holds the parser's internal state.
...@@ -1913,6 +1916,17 @@ func (p *parser) parseStmt() (s ast.Stmt) { ...@@ -1913,6 +1916,17 @@ func (p *parser) parseStmt() (s ast.Stmt) {
type parseSpecFunction func(p *parser, doc *ast.CommentGroup, iota int) ast.Spec type parseSpecFunction func(p *parser, doc *ast.CommentGroup, iota int) ast.Spec
func isValidImport(lit string) bool {
const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
s, _ := strconv.Unquote(lit) // go/scanner returns a legal string literal
for _, r := range s {
if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
return false
}
}
return s != ""
}
func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
if p.trace { if p.trace {
defer un(trace(p, "ImportSpec")) defer un(trace(p, "ImportSpec"))
...@@ -1929,6 +1943,9 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { ...@@ -1929,6 +1943,9 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
var path *ast.BasicLit var path *ast.BasicLit
if p.tok == token.STRING { if p.tok == token.STRING {
if !isValidImport(p.lit) {
p.error(p.pos, "invalid import path: "+p.lit)
}
path = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit} path = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next() p.next()
} else { } else {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package parser package parser
import ( import (
"fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"os" "os"
...@@ -204,3 +205,48 @@ func TestVarScope(t *testing.T) { ...@@ -204,3 +205,48 @@ func TestVarScope(t *testing.T) {
} }
} }
} }
var imports = map[string]bool{
`"a"`: true,
"`a`": true,
`"a/b"`: true,
`"a.b"`: true,
`"m\x61th"`: true,
`"greek/αβ"`: true,
`""`: false,
// Each of these pairs tests both `` vs "" strings
// and also use of invalid characters spelled out as
// escape sequences and written directly.
// For example `"\x00"` tests import "\x00"
// while "`\x00`" tests import `<actual-NUL-byte>`.
`"\x00"`: false,
"`\x00`": false,
`"\x7f"`: false,
"`\x7f`": false,
`"a!"`: false,
"`a!`": false,
`"a b"`: false,
"`a b`": false,
`"a\\b"`: false,
"`a\\b`": false,
"\"`a`\"": false,
"`\"a\"`": false,
`"\x80\x80"`: false,
"`\x80\x80`": false,
`"\xFFFD"`: false,
"`\xFFFD`": false,
}
func TestImports(t *testing.T) {
for path, isValid := range imports {
src := fmt.Sprintf("package p; import %s", path)
_, err := ParseFile(fset, "", src, 0)
switch {
case err != nil && isValid:
t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
case err == nil && !isValid:
t.Errorf("ParseFile(%s): got no error; expected one", src)
}
}
}
...@@ -34,9 +34,6 @@ const ( ...@@ -34,9 +34,6 @@ const (
unindent = whiteSpace('<') unindent = whiteSpace('<')
) )
// Use ignoreMultiLine if the multiLine information is not important.
var ignoreMultiLine = new(bool)
// A pmode value represents the current printer mode. // A pmode value represents the current printer mode.
type pmode int type pmode int
...@@ -280,10 +277,9 @@ func (p *printer) writeString(pos token.Position, s string, isLit bool) { ...@@ -280,10 +277,9 @@ func (p *printer) writeString(pos token.Position, s string, isLit bool) {
// it as is likely to help position the comment nicely. // it as is likely to help position the comment nicely.
// pos is the comment position, next the position of the item // pos is the comment position, next the position of the item
// after all pending comments, prev is the previous comment in // after all pending comments, prev is the previous comment in
// a group of comments (or nil), and isKeyword indicates if the // a group of comments (or nil), and tok is the next token.
// next item is a keyword.
// //
func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, isKeyword bool) { func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, tok token.Token) {
if len(p.output) == 0 { if len(p.output) == 0 {
// the comment is the first item to be printed - don't write any whitespace // the comment is the first item to be printed - don't write any whitespace
return return
...@@ -338,38 +334,41 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as ...@@ -338,38 +334,41 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
// comment on a different line: // comment on a different line:
// separate with at least one line break // separate with at least one line break
droppedLinebreak := false droppedLinebreak := false
if prev == nil { j := 0
// first comment of a comment group for i, ch := range p.wsbuf {
j := 0 switch ch {
for i, ch := range p.wsbuf { case blank, vtab:
switch ch { // ignore any horizontal whitespace before line breaks
case blank, vtab: p.wsbuf[i] = ignore
// ignore any horizontal whitespace before line breaks continue
p.wsbuf[i] = ignore case indent:
// apply pending indentation
continue
case unindent:
// if this is not the last unindent, apply it
// as it is (likely) belonging to the last
// construct (e.g., a multi-line expression list)
// and is not part of closing a block
if i+1 < len(p.wsbuf) && p.wsbuf[i+1] == unindent {
continue continue
case indent: }
// apply pending indentation // if the next token is not a closing }, apply the unindent
// if it appears that the comment is aligned with the
// token; otherwise assume the unindent is part of a
// closing block and stop (this scenario appears with
// comments before a case label where the comments
// apply to the next case instead of the current one)
if tok != token.RBRACE && pos.Column == next.Column {
continue continue
case unindent:
// if the next token is a keyword, apply the outdent
// if it appears that the comment is aligned with the
// keyword; otherwise assume the outdent is part of a
// closing block and stop (this scenario appears with
// comments before a case label where the comments
// apply to the next case instead of the current one)
if isKeyword && pos.Column == next.Column {
continue
}
case newline, formfeed:
// TODO(gri): may want to keep formfeed info in some cases
p.wsbuf[i] = ignore
droppedLinebreak = true
} }
j = i case newline, formfeed:
break p.wsbuf[i] = ignore
droppedLinebreak = prev == nil // record only if first comment of a group
} }
p.writeWhitespace(j) j = i
break
} }
p.writeWhitespace(j)
// determine number of linebreaks before the comment // determine number of linebreaks before the comment
n := 0 n := 0
...@@ -678,7 +677,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro ...@@ -678,7 +677,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
var last *ast.Comment var last *ast.Comment
for p.commentBefore(next) { for p.commentBefore(next) {
for _, c := range p.comment.List { for _, c := range p.comment.List {
p.writeCommentPrefix(p.posFor(c.Pos()), next, last, c, tok.IsKeyword()) p.writeCommentPrefix(p.posFor(c.Pos()), next, last, c, tok)
p.writeComment(c) p.writeComment(c)
last = c last = c
} }
...@@ -1011,18 +1010,18 @@ func (p *printer) printNode(node interface{}) error { ...@@ -1011,18 +1010,18 @@ func (p *printer) printNode(node interface{}) error {
// format node // format node
switch n := node.(type) { switch n := node.(type) {
case ast.Expr: case ast.Expr:
p.expr(n, ignoreMultiLine) p.expr(n)
case ast.Stmt: case ast.Stmt:
// A labeled statement will un-indent to position the // A labeled statement will un-indent to position the
// label. Set indent to 1 so we don't get indent "underflow". // label. Set indent to 1 so we don't get indent "underflow".
if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt { if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt {
p.indent = 1 p.indent = 1
} }
p.stmt(n, false, ignoreMultiLine) p.stmt(n, false)
case ast.Decl: case ast.Decl:
p.decl(n, ignoreMultiLine) p.decl(n)
case ast.Spec: case ast.Spec:
p.spec(n, 1, false, ignoreMultiLine) p.spec(n, 1, false)
case *ast.File: case *ast.File:
p.file(n) p.file(n)
default: default:
......
...@@ -154,15 +154,12 @@ var data = []entry{ ...@@ -154,15 +154,12 @@ var data = []entry{
} }
func TestFiles(t *testing.T) { func TestFiles(t *testing.T) {
for i, e := range data { for _, e := range data {
source := filepath.Join(dataDir, e.source) source := filepath.Join(dataDir, e.source)
golden := filepath.Join(dataDir, e.golden) golden := filepath.Join(dataDir, e.golden)
check(t, source, golden, e.mode) check(t, source, golden, e.mode)
// TODO(gri) check that golden is idempotent // TODO(gri) check that golden is idempotent
//check(t, golden, golden, e.mode) //check(t, golden, golden, e.mode)
if testing.Short() && i >= 3 {
break
}
} }
} }
......
...@@ -168,6 +168,91 @@ func typeswitch(x interface{}) { ...@@ -168,6 +168,91 @@ func typeswitch(x interface{}) {
// this comment should not be indented // this comment should not be indented
} }
//
// Indentation of comments after possibly indented multi-line constructs
// (test cases for issue 3147).
//
func _() {
s := 1 +
2
// should be indented like s
}
func _() {
s := 1 +
2 // comment
// should be indented like s
}
func _() {
s := 1 +
2 // comment
// should be indented like s
_ = 0
}
func _() {
s := 1 +
2
// should be indented like s
_ = 0
}
func _() {
s := 1 +
2
// should be indented like s
}
func _() {
s := 1 +
2 // comment
// should be indented like s
}
func _() {
s := 1 +
2 // comment
// should be indented like s
_ = 0
}
func _() {
s := 1 +
2
// should be indented like s
_ = 0
}
// Test case from issue 3147.
func f() {
templateText := "a" + // A
"b" + // B
"c" // C
// should be aligned with f()
f()
}
// Modified test case from issue 3147.
func f() {
templateText := "a" + // A
"b" + // B
"c" // C
// may not be aligned with f() (source is not aligned)
f()
}
//
// Test cases for alignment of lines in general comments.
//
func _() { func _() {
/* freestanding comment /* freestanding comment
aligned line aligned line
......
...@@ -171,6 +171,91 @@ func typeswitch(x interface{}) { ...@@ -171,6 +171,91 @@ func typeswitch(x interface{}) {
// this comment should not be indented // this comment should not be indented
} }
//
// Indentation of comments after possibly indented multi-line constructs
// (test cases for issue 3147).
//
func _() {
s := 1 +
2
// should be indented like s
}
func _() {
s := 1 +
2 // comment
// should be indented like s
}
func _() {
s := 1 +
2 // comment
// should be indented like s
_ = 0
}
func _() {
s := 1 +
2
// should be indented like s
_ = 0
}
func _() {
s := 1 +
2
// should be indented like s
}
func _() {
s := 1 +
2 // comment
// should be indented like s
}
func _() {
s := 1 +
2 // comment
// should be indented like s
_ = 0
}
func _() {
s := 1 +
2
// should be indented like s
_ = 0
}
// Test case from issue 3147.
func f() {
templateText := "a" + // A
"b" + // B
"c" // C
// should be aligned with f()
f()
}
// Modified test case from issue 3147.
func f() {
templateText := "a" + // A
"b" + // B
"c" // C
// may not be aligned with f() (source is not aligned)
f()
}
//
// Test cases for alignment of lines in general comments.
//
func _() { func _() {
/* freestanding comment /* freestanding comment
aligned line aligned line
......
...@@ -83,13 +83,13 @@ import ( ...@@ -83,13 +83,13 @@ import (
// more import examples // more import examples
import ( import (
"xxx" "xxx"
"much longer name" // comment "much_longer_name" // comment
"short name" // comment "short_name" // comment
) )
import ( import (
_ "xxx" _ "xxx"
"much longer name" // comment "much_longer_name" // comment
) )
import ( import (
...@@ -500,7 +500,7 @@ type _ struct { ...@@ -500,7 +500,7 @@ type _ struct {
type _ struct { type _ struct {
a, b, a, b,
c, d int // this line should be indented c, d int // this line should be indented
u, v, w, x float // this line should be indented u, v, w, x float // this line should be indented
p, q, p, q,
r, s float // this line should be indented r, s float // this line should be indented
...@@ -562,7 +562,7 @@ var a2, b2, ...@@ -562,7 +562,7 @@ var a2, b2,
var ( var (
a3, b3, a3, b3,
c3, d3 int // this line should be indented c3, d3 int // this line should be indented
a4, b4, c4 int // this line should be indented a4, b4, c4 int // this line should be indented
) )
......
...@@ -84,13 +84,13 @@ import ( ...@@ -84,13 +84,13 @@ import (
// more import examples // more import examples
import ( import (
"xxx" "xxx"
"much longer name" // comment "much_longer_name" // comment
"short name" // comment "short_name" // comment
) )
import ( import (
_ "xxx" _ "xxx"
"much longer name" // comment "much_longer_name" // comment
) )
import ( import (
......
...@@ -625,3 +625,25 @@ func f() { ...@@ -625,3 +625,25 @@ func f() {
log.Fatal(err) log.Fatal(err)
} }
} }
// Handle multi-line argument lists ending in ... correctly.
// Was issue 3130.
func _() {
_ = append(s, a...)
_ = append(
s, a...)
_ = append(s,
a...)
_ = append(
s,
a...)
_ = append(s, a...,
)
_ = append(s,
a...,
)
_ = append(
s,
a...,
)
}
...@@ -654,3 +654,25 @@ func f() { ...@@ -654,3 +654,25 @@ func f() {
log.Fatal(err) log.Fatal(err)
} }
} }
// Handle multi-line argument lists ending in ... correctly.
// Was issue 3130.
func _() {
_ = append(s, a...)
_ = append(
s, a...)
_ = append(s,
a...)
_ = append(
s,
a...)
_ = append(s, a...,
)
_ = append(s,
a...,
)
_ = append(
s,
a...,
)
}
...@@ -625,3 +625,25 @@ func f() { ...@@ -625,3 +625,25 @@ func f() {
log.Fatal(err) log.Fatal(err)
} }
} }
// Handle multi-line argument lists ending in ... correctly.
// Was issue 3130.
func _() {
_ = append(s, a...)
_ = append(
s, a...)
_ = append(s,
a...)
_ = append(
s,
a...)
_ = append(s, a...,
)
_ = append(s,
a...,
)
_ = append(
s,
a...,
)
}
...@@ -52,7 +52,7 @@ type parser struct { ...@@ -52,7 +52,7 @@ type parser struct {
// Non-syntactic parser control // Non-syntactic parser control
exprLev int // < 0: in control clause, >= 0: in expression exprLev int // < 0: in control clause, >= 0: in expression
// Ordinary identifer scopes // Ordinary identifier scopes
pkgScope *ast.Scope // pkgScope.Outer == nil pkgScope *ast.Scope // pkgScope.Outer == nil
topScope *ast.Scope // top-most scope; may be pkgScope topScope *ast.Scope // top-most scope; may be pkgScope
unresolved []*ast.Ident // unresolved identifiers unresolved []*ast.Ident // unresolved identifiers
......
...@@ -8,6 +8,82 @@ var expr bool ...@@ -8,6 +8,82 @@ var expr bool
func use(x interface{}) {} func use(x interface{}) {}
// Formatting of multi-line return statements.
func _f() {
return
return x, y, z
return T{}
return T{1, 2, 3},
x, y, z
return T{1, 2, 3},
x, y,
z
return T{1,
2,
3}
return T{1,
2,
3,
}
return T{
1,
2,
3}
return T{
1,
2,
3,
}
return T{
1,
T{1, 2, 3},
3,
}
return T{
1,
T{1,
2, 3},
3,
}
return T{
1,
T{1,
2,
3},
3,
}
return T{
1,
2,
},
nil
return T{
1,
2,
},
T{
x: 3,
y: 4,
},
nil
return x + y +
z
return func() {}
return func() {
_ = 0
}, T{
1, 2,
}
return func() {
_ = 0
}
return func() T {
return T{
1, 2,
}
}
}
// Formatting of if-statement headers. // Formatting of if-statement headers.
func _() { func _() {
if true { if true {
......
...@@ -8,6 +8,82 @@ var expr bool ...@@ -8,6 +8,82 @@ var expr bool
func use(x interface{}) {} func use(x interface{}) {}
// Formatting of multi-line return statements.
func _f() {
return
return x, y, z
return T{}
return T{1, 2, 3},
x, y, z
return T{1, 2, 3},
x, y,
z
return T{1,
2,
3}
return T{1,
2,
3,
}
return T{
1,
2,
3}
return T{
1,
2,
3,
}
return T{
1,
T{1, 2, 3},
3,
}
return T{
1,
T{1,
2, 3},
3,
}
return T{
1,
T{1,
2,
3},
3,
}
return T{
1,
2,
},
nil
return T{
1,
2,
},
T{
x: 3,
y: 4,
},
nil
return x + y +
z
return func() {}
return func() {
_ = 0
}, T{
1, 2,
}
return func() {
_ = 0
}
return func() T {
return T {
1, 2,
}
}
}
// Formatting of if-statement headers. // Formatting of if-statement headers.
func _() { func _() {
if true {} if true {}
......
...@@ -19,7 +19,7 @@ to parse and execute HTML templates safely. ...@@ -19,7 +19,7 @@ to parse and execute HTML templates safely.
tmpl, err := template.New("name").Parse(...) tmpl, err := template.New("name").Parse(...)
// Error checking elided // Error checking elided
err = tmpl.Execute(out, "Foo", data) err = tmpl.Execute(out, data)
If successful, tmpl will now be injection-safe. Otherwise, err is an error If successful, tmpl will now be injection-safe. Otherwise, err is an error
defined in the docs for ErrorCode. defined in the docs for ErrorCode.
......
...@@ -593,7 +593,7 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context { ...@@ -593,7 +593,7 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context {
} }
} }
for j := i; j < end; j++ { for j := i; j < end; j++ {
if s[j] == '<' && !bytes.HasPrefix(s[j:], doctypeBytes) { if s[j] == '<' && !bytes.HasPrefix(bytes.ToUpper(s[j:]), doctypeBytes) {
b.Write(s[written:j]) b.Write(s[written:j])
b.WriteString("&lt;") b.WriteString("&lt;")
written = j + 1 written = j + 1
......
...@@ -223,14 +223,14 @@ func TestEscape(t *testing.T) { ...@@ -223,14 +223,14 @@ func TestEscape(t *testing.T) {
`<button onclick='alert(&quot;\x3cHello\x3e&quot;)'>`, `<button onclick='alert(&quot;\x3cHello\x3e&quot;)'>`,
}, },
{ {
"badMarshaller", "badMarshaler",
`<button onclick='alert(1/{{.B}}in numbers)'>`, `<button onclick='alert(1/{{.B}}in numbers)'>`,
`<button onclick='alert(1/ /* json: error calling MarshalJSON for type *template.badMarshaler: invalid character &#39;f&#39; looking for beginning of object key string */null in numbers)'>`, `<button onclick='alert(1/ /* json: error calling MarshalJSON for type *template.badMarshaler: invalid character &#39;f&#39; looking for beginning of object key string */null in numbers)'>`,
}, },
{ {
"jsMarshaller", "jsMarshaler",
`<button onclick='alert({{.M}})'>`, `<button onclick='alert({{.M}})'>`,
`<button onclick='alert({&#34;&lt;foo&gt;&#34;:&#34;O&#39;Reilly&#34;})'>`, `<button onclick='alert({&#34;\u003cfoo\u003e&#34;:&#34;O&#39;Reilly&#34;})'>`,
}, },
{ {
"jsStrNotUnderEscaped", "jsStrNotUnderEscaped",
...@@ -432,6 +432,11 @@ func TestEscape(t *testing.T) { ...@@ -432,6 +432,11 @@ func TestEscape(t *testing.T) {
"<!DOCTYPE html>Hello, World!", "<!DOCTYPE html>Hello, World!",
}, },
{ {
"HTML doctype not case-insensitive",
"<!doCtYPE htMl>Hello, World!",
"<!doCtYPE htMl>Hello, World!",
},
{
"No doctype injection", "No doctype injection",
`<!{{"DOCTYPE"}}`, `<!{{"DOCTYPE"}}`,
"&lt;!DOCTYPE", "&lt;!DOCTYPE",
......
...@@ -134,7 +134,7 @@ var htmlNospaceNormReplacementTable = []string{ ...@@ -134,7 +134,7 @@ var htmlNospaceNormReplacementTable = []string{
'`': "&#96;", '`': "&#96;",
} }
// htmlReplacer returns s with runes replaced acccording to replacementTable // htmlReplacer returns s with runes replaced according to replacementTable
// and when badRunes is true, certain bad runes are allowed through unescaped. // and when badRunes is true, certain bad runes are allowed through unescaped.
func htmlReplacer(s string, replacementTable []string, badRunes bool) string { func htmlReplacer(s string, replacementTable []string, badRunes bool) string {
written, b := 0, new(bytes.Buffer) written, b := 0, new(bytes.Buffer)
......
...@@ -6,6 +6,10 @@ ...@@ -6,6 +6,10 @@
// Its primary job is to wrap existing implementations of such primitives, // Its primary job is to wrap existing implementations of such primitives,
// such as those in package os, into shared public interfaces that // such as those in package os, into shared public interfaces that
// abstract the functionality, plus some other related primitives. // abstract the functionality, plus some other related primitives.
//
// Because these interfaces and primitives wrap lower-level operations with
// various implementations, unless otherwise informed clients should not
// assume they are safe for parallel execution.
package io package io
import ( import (
...@@ -156,6 +160,9 @@ type WriterTo interface { ...@@ -156,6 +160,9 @@ type WriterTo interface {
// If ReadAt is reading from an input source with a seek offset, // If ReadAt is reading from an input source with a seek offset,
// ReadAt should not affect nor be affected by the underlying // ReadAt should not affect nor be affected by the underlying
// seek offset. // seek offset.
//
// Clients of ReadAt can execute parallel ReadAt calls on the
// same input source.
type ReaderAt interface { type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error) ReadAt(p []byte, off int64) (n int, err error)
} }
......
...@@ -175,6 +175,10 @@ func (w *PipeWriter) CloseWithError(err error) error { ...@@ -175,6 +175,10 @@ func (w *PipeWriter) CloseWithError(err error) error {
// with code expecting an io.Writer. // with code expecting an io.Writer.
// Reads on one end are matched with writes on the other, // Reads on one end are matched with writes on the other,
// copying data directly between the two; there is no internal buffering. // copying data directly between the two; there is no internal buffering.
// It is safe to call Read and Write in parallel with each other or with
// Close. Close will complete once pending I/O is done. Parallel calls to
// Read, and parallel calls to Write, are also safe:
// the individual calls will be gated sequentially.
func Pipe() (*PipeReader, *PipeWriter) { func Pipe() (*PipeReader, *PipeWriter) {
p := new(pipe) p := new(pipe)
p.rwait.L = &p.l p.rwait.L = &p.l
......
...@@ -27,11 +27,11 @@ const ( ...@@ -27,11 +27,11 @@ const (
// Max is the largest finite value representable by the type. // Max is the largest finite value representable by the type.
// SmallestNonzero is the smallest positive, non-zero value representable by the type. // SmallestNonzero is the smallest positive, non-zero value representable by the type.
const ( const (
MaxFloat32 = 3.40282346638528859811704183484516925440e+38 /* 2**127 * (2**24 - 1) / 2**23 */ MaxFloat32 = 3.40282346638528859811704183484516925440e+38 // 2**127 * (2**24 - 1) / 2**23
SmallestNonzeroFloat32 = 1.401298464324817070923729583289916131280e-45 /* 1 / 2**(127 - 1 + 23) */ SmallestNonzeroFloat32 = 1.401298464324817070923729583289916131280e-45 // 1 / 2**(127 - 1 + 23)
MaxFloat64 = 1.797693134862315708145274237317043567981e+308 /* 2**1023 * (2**53 - 1) / 2**52 */ MaxFloat64 = 1.797693134862315708145274237317043567981e+308 // 2**1023 * (2**53 - 1) / 2**52
SmallestNonzeroFloat64 = 4.940656458412465441765687928682213723651e-324 /* 1 / 2**(1023 - 1 + 52) */ SmallestNonzeroFloat64 = 4.940656458412465441765687928682213723651e-324 // 1 / 2**(1023 - 1 + 52)
) )
// Integer limit values. // Integer limit values.
......
...@@ -69,7 +69,7 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) { ...@@ -69,7 +69,7 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
// //
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" // "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and "unixpacket". // (IPv4-only), "ip6" (IPv6-only), "unix" and "unixpacket".
// //
// For TCP and UDP networks, addresses have the form host:port. // For TCP and UDP networks, addresses have the form host:port.
// If host is a literal IPv6 address, it must be enclosed // If host is a literal IPv6 address, it must be enclosed
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
package net package net
import ( import (
"flag"
"regexp"
"runtime" "runtime"
"testing" "testing"
"time" "time"
...@@ -128,3 +130,82 @@ func TestSelfConnect(t *testing.T) { ...@@ -128,3 +130,82 @@ func TestSelfConnect(t *testing.T) {
} }
} }
} }
var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check for dns errors")
type DialErrorTest struct {
Net string
Raddr string
Pattern string
}
var dialErrorTests = []DialErrorTest{
{
"datakit", "mh/astro/r70",
"dial datakit mh/astro/r70: unknown network datakit",
},
{
"tcp", "127.0.0.1:☺",
"dial tcp 127.0.0.1:☺: unknown port tcp/☺",
},
{
"tcp", "no-such-name.google.com.:80",
"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
},
{
"tcp", "no-such-name.no-such-top-level-domain.:80",
"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
},
{
"tcp", "no-such-name:80",
`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
},
{
"tcp", "mh/astro/r70:http",
"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
},
{
"unix", "/etc/file-not-found",
"dial unix /etc/file-not-found: no such file or directory",
},
{
"unix", "/etc/",
"dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)",
},
{
"unixpacket", "/etc/file-not-found",
"dial unixpacket /etc/file-not-found: no such file or directory",
},
{
"unixpacket", "/etc/",
"dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
},
}
var duplicateErrorPattern = `dial (.*) dial (.*)`
func TestDialError(t *testing.T) {
if !*runErrorTest {
t.Logf("test disabled; use -run_error_test to enable")
return
}
for i, tt := range dialErrorTests {
c, err := Dial(tt.Net, tt.Raddr)
if c != nil {
c.Close()
}
if err == nil {
t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern)
continue
}
s := err.Error()
match, _ := regexp.MatchString(tt.Pattern, s)
if !match {
t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern)
}
match, _ = regexp.MatchString(duplicateErrorPattern, s)
if match {
t.Errorf("#%d: %q, duplicate error return from Dial", i, s)
}
}
}
...@@ -42,9 +42,8 @@ func doDial(t *testing.T, network, addr string) { ...@@ -42,9 +42,8 @@ func doDial(t *testing.T, network, addr string) {
} }
func TestLookupCNAME(t *testing.T) { func TestLookupCNAME(t *testing.T) {
if testing.Short() { if testing.Short() || !*testExternal {
// Don't use external network. t.Logf("skipping test to avoid external network")
t.Logf("skipping external network test during -short")
return return
} }
cname, err := LookupCNAME("www.google.com") cname, err := LookupCNAME("www.google.com")
...@@ -67,9 +66,8 @@ var googleaddrsipv4 = []string{ ...@@ -67,9 +66,8 @@ var googleaddrsipv4 = []string{
} }
func TestDialGoogleIPv4(t *testing.T) { func TestDialGoogleIPv4(t *testing.T) {
if testing.Short() { if testing.Short() || !*testExternal {
// Don't use external network. t.Logf("skipping test to avoid external network")
t.Logf("skipping external network test during -short")
return return
} }
...@@ -124,9 +122,8 @@ var googleaddrsipv6 = []string{ ...@@ -124,9 +122,8 @@ var googleaddrsipv6 = []string{
} }
func TestDialGoogleIPv6(t *testing.T) { func TestDialGoogleIPv6(t *testing.T) {
if testing.Short() { if testing.Short() || !*testExternal {
// Don't use external network. t.Logf("skipping test to avoid external network")
t.Logf("skipping external network test during -short")
return return
} }
// Only run tcp6 if the kernel will take it. // Only run tcp6 if the kernel will take it.
......
...@@ -144,6 +144,7 @@ func Serve(handler http.Handler) error { ...@@ -144,6 +144,7 @@ func Serve(handler http.Handler) error {
bufw: bufio.NewWriter(os.Stdout), bufw: bufio.NewWriter(os.Stdout),
} }
handler.ServeHTTP(rw, req) handler.ServeHTTP(rw, req)
rw.Write(nil) // make sure a response is sent
if err = rw.bufw.Flush(); err != nil { if err = rw.bufw.Flush(); err != nil {
return err return err
} }
......
...@@ -41,6 +41,7 @@ func runCgiTest(t *testing.T, h *Handler, httpreq string, expectedMap map[string ...@@ -41,6 +41,7 @@ func runCgiTest(t *testing.T, h *Handler, httpreq string, expectedMap map[string
// Make a map to hold the test map that the CGI returns. // Make a map to hold the test map that the CGI returns.
m := make(map[string]string) m := make(map[string]string)
m["_body"] = rw.Body.String()
linesRead := 0 linesRead := 0
readlines: readlines:
for { for {
......
...@@ -51,6 +51,22 @@ func TestHostingOurselves(t *testing.T) { ...@@ -51,6 +51,22 @@ func TestHostingOurselves(t *testing.T) {
} }
} }
// Test that a child handler only writing headers works.
func TestChildOnlyHeaders(t *testing.T) {
h := &Handler{
Path: os.Args[0],
Root: "/test.go",
Args: []string{"-test.run=TestBeChildCGIProcess"},
}
expectedMap := map[string]string{
"_body": "",
}
replay := runCgiTest(t, h, "GET /test.go?no-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
}
}
// Note: not actually a test. // Note: not actually a test.
func TestBeChildCGIProcess(t *testing.T) { func TestBeChildCGIProcess(t *testing.T) {
if os.Getenv("REQUEST_METHOD") == "" { if os.Getenv("REQUEST_METHOD") == "" {
...@@ -59,8 +75,11 @@ func TestBeChildCGIProcess(t *testing.T) { ...@@ -59,8 +75,11 @@ func TestBeChildCGIProcess(t *testing.T) {
} }
Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("X-Test-Header", "X-Test-Value") rw.Header().Set("X-Test-Header", "X-Test-Value")
fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
req.ParseForm() req.ParseForm()
if req.FormValue("no-body") == "1" {
return
}
fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
for k, vv := range req.Form { for k, vv := range req.Form {
for _, v := range vv { for _, v := range vv {
fmt.Fprintf(rw, "param-%s=%s\n", k, v) fmt.Fprintf(rw, "param-%s=%s\n", k, v)
......
...@@ -152,12 +152,19 @@ func TestFileServerCleans(t *testing.T) { ...@@ -152,12 +152,19 @@ func TestFileServerCleans(t *testing.T) {
} }
} }
func mustRemoveAll(dir string) {
err := os.RemoveAll(dir)
if err != nil {
panic(err)
}
}
func TestFileServerImplicitLeadingSlash(t *testing.T) { func TestFileServerImplicitLeadingSlash(t *testing.T) {
tempDir, err := ioutil.TempDir("", "") tempDir, err := ioutil.TempDir("", "")
if err != nil { if err != nil {
t.Fatalf("TempDir: %v", err) t.Fatalf("TempDir: %v", err)
} }
defer os.RemoveAll(tempDir) defer mustRemoveAll(tempDir)
if err := ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("Hello world"), 0644); err != nil { if err := ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("Hello world"), 0644); err != nil {
t.Fatalf("WriteFile: %v", err) t.Fatalf("WriteFile: %v", err)
} }
...@@ -172,6 +179,7 @@ func TestFileServerImplicitLeadingSlash(t *testing.T) { ...@@ -172,6 +179,7 @@ func TestFileServerImplicitLeadingSlash(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("ReadAll %s: %v", suffix, err) t.Fatalf("ReadAll %s: %v", suffix, err)
} }
res.Body.Close()
return string(b) return string(b)
} }
if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") { if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") {
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"sync"
) )
// A Server is an HTTP server listening on a system-chosen port on the // A Server is an HTTP server listening on a system-chosen port on the
...@@ -25,6 +26,10 @@ type Server struct { ...@@ -25,6 +26,10 @@ type Server struct {
// Config may be changed after calling NewUnstartedServer and // Config may be changed after calling NewUnstartedServer and
// before Start or StartTLS. // before Start or StartTLS.
Config *http.Server Config *http.Server
// wg counts the number of outstanding HTTP requests on this server.
// Close blocks until all requests are finished.
wg sync.WaitGroup
} }
// historyListener keeps track of all connections that it's ever // historyListener keeps track of all connections that it's ever
...@@ -93,6 +98,7 @@ func (s *Server) Start() { ...@@ -93,6 +98,7 @@ func (s *Server) Start() {
} }
s.Listener = &historyListener{s.Listener, make([]net.Conn, 0)} s.Listener = &historyListener{s.Listener, make([]net.Conn, 0)}
s.URL = "http://" + s.Listener.Addr().String() s.URL = "http://" + s.Listener.Addr().String()
s.wrapHandler()
go s.Config.Serve(s.Listener) go s.Config.Serve(s.Listener)
if *serve != "" { if *serve != "" {
fmt.Fprintln(os.Stderr, "httptest: serving on", s.URL) fmt.Fprintln(os.Stderr, "httptest: serving on", s.URL)
...@@ -118,9 +124,21 @@ func (s *Server) StartTLS() { ...@@ -118,9 +124,21 @@ func (s *Server) StartTLS() {
s.Listener = &historyListener{tlsListener, make([]net.Conn, 0)} s.Listener = &historyListener{tlsListener, make([]net.Conn, 0)}
s.URL = "https://" + s.Listener.Addr().String() s.URL = "https://" + s.Listener.Addr().String()
s.wrapHandler()
go s.Config.Serve(s.Listener) go s.Config.Serve(s.Listener)
} }
func (s *Server) wrapHandler() {
h := s.Config.Handler
if h == nil {
h = http.DefaultServeMux
}
s.Config.Handler = &waitGroupHandler{
s: s,
h: h,
}
}
// NewTLSServer starts and returns a new Server using TLS. // NewTLSServer starts and returns a new Server using TLS.
// The caller should call Close when finished, to shut it down. // The caller should call Close when finished, to shut it down.
func NewTLSServer(handler http.Handler) *Server { func NewTLSServer(handler http.Handler) *Server {
...@@ -129,9 +147,11 @@ func NewTLSServer(handler http.Handler) *Server { ...@@ -129,9 +147,11 @@ func NewTLSServer(handler http.Handler) *Server {
return ts return ts
} }
// Close shuts down the server. // Close shuts down the server and blocks until all outstanding
// requests on this server have completed.
func (s *Server) Close() { func (s *Server) Close() {
s.Listener.Close() s.Listener.Close()
s.wg.Wait()
} }
// CloseClientConnections closes any currently open HTTP connections // CloseClientConnections closes any currently open HTTP connections
...@@ -146,6 +166,20 @@ func (s *Server) CloseClientConnections() { ...@@ -146,6 +166,20 @@ func (s *Server) CloseClientConnections() {
} }
} }
// waitGroupHandler wraps a handler, incrementing and decrementing a
// sync.WaitGroup on each request, to enable Server.Close to block
// until outstanding requests are finished.
type waitGroupHandler struct {
s *Server
h http.Handler // non-nil
}
func (h *waitGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.s.wg.Add(1)
defer h.s.wg.Done() // a defer, in case ServeHTTP below panics
h.h.ServeHTTP(w, r)
}
// localhostCert is a PEM-encoded TLS cert with SAN DNS names // localhostCert is a PEM-encoded TLS cert with SAN DNS names
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time). // of ASN.1 time).
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
"net/url"
"strings" "strings"
"time" "time"
) )
...@@ -59,6 +60,19 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) { ...@@ -59,6 +60,19 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
} }
} }
// Since we're using the actual Transport code to write the request,
// switch to http so the Transport doesn't try to do an SSL
// negotiation with our dumpConn and its bytes.Buffer & pipe.
// The wire format for https and http are the same, anyway.
reqSend := req
if req.URL.Scheme == "https" {
reqSend = new(http.Request)
*reqSend = *req
reqSend.URL = new(url.URL)
*reqSend.URL = *req.URL
reqSend.URL.Scheme = "http"
}
// Use the actual Transport code to record what we would send // Use the actual Transport code to record what we would send
// on the wire, but not using TCP. Use a Transport with a // on the wire, but not using TCP. Use a Transport with a
// customer dialer that returns a fake net.Conn that waits // customer dialer that returns a fake net.Conn that waits
...@@ -79,7 +93,7 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) { ...@@ -79,7 +93,7 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
}, },
} }
_, err := t.RoundTrip(req) _, err := t.RoundTrip(reqSend)
req.Body = save req.Body = save
if err != nil { if err != nil {
......
...@@ -71,6 +71,18 @@ var dumpTests = []dumpTest{ ...@@ -71,6 +71,18 @@ var dumpTests = []dumpTest{
"User-Agent: Go http package\r\n" + "User-Agent: Go http package\r\n" +
"Accept-Encoding: gzip\r\n\r\n", "Accept-Encoding: gzip\r\n\r\n",
}, },
// Test that an https URL doesn't try to do an SSL negotiation
// with a bytes.Buffer and hang with all goroutines not
// runnable.
{
Req: *mustNewRequest("GET", "https://example.com/foo", nil),
WantDumpOut: "GET /foo HTTP/1.1\r\n" +
"Host: example.com\r\n" +
"User-Agent: Go http package\r\n" +
"Accept-Encoding: gzip\r\n\r\n",
},
} }
func TestDumpRequest(t *testing.T) { func TestDumpRequest(t *testing.T) {
......
...@@ -383,7 +383,7 @@ func (cc *ClientConn) Read(req *http.Request) (resp *http.Response, err error) { ...@@ -383,7 +383,7 @@ func (cc *ClientConn) Read(req *http.Request) (resp *http.Response, err error) {
// Make sure body is fully consumed, even if user does not call body.Close // Make sure body is fully consumed, even if user does not call body.Close
if lastbody != nil { if lastbody != nil {
// body.Close is assumed to be idempotent and multiple calls to // body.Close is assumed to be idempotent and multiple calls to
// it should return the error that its first invokation // it should return the error that its first invocation
// returned. // returned.
err = lastbody.Close() err = lastbody.Close()
if err != nil { if err != nil {
......
...@@ -14,14 +14,6 @@ func isSeparator(c byte) bool { ...@@ -14,14 +14,6 @@ func isSeparator(c byte) bool {
return false return false
} }
func isSpace(c byte) bool {
switch c {
case ' ', '\t', '\r', '\n':
return true
}
return false
}
func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 } func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 }
func isChar(c byte) bool { return 0 <= c && c <= 127 } func isChar(c byte) bool { return 0 <= c && c <= 127 }
......
...@@ -129,9 +129,10 @@ func TestSniffWriteSize(t *testing.T) { ...@@ -129,9 +129,10 @@ func TestSniffWriteSize(t *testing.T) {
})) }))
defer ts.Close() defer ts.Close()
for _, size := range []int{0, 1, 200, 600, 999, 1000, 1023, 1024, 512 << 10, 1 << 20} { for _, size := range []int{0, 1, 200, 600, 999, 1000, 1023, 1024, 512 << 10, 1 << 20} {
_, err := Get(fmt.Sprintf("%s/?size=%d", ts.URL, size)) res, err := Get(fmt.Sprintf("%s/?size=%d", ts.URL, size))
if err != nil { if err != nil {
t.Fatalf("size %d: %v", size, err) t.Fatalf("size %d: %v", size, err)
} }
res.Body.Close()
} }
} }
...@@ -43,6 +43,7 @@ const ( ...@@ -43,6 +43,7 @@ const (
StatusUnsupportedMediaType = 415 StatusUnsupportedMediaType = 415
StatusRequestedRangeNotSatisfiable = 416 StatusRequestedRangeNotSatisfiable = 416
StatusExpectationFailed = 417 StatusExpectationFailed = 417
StatusTeapot = 418
StatusInternalServerError = 500 StatusInternalServerError = 500
StatusNotImplemented = 501 StatusNotImplemented = 501
...@@ -90,6 +91,7 @@ var statusText = map[int]string{ ...@@ -90,6 +91,7 @@ var statusText = map[int]string{
StatusUnsupportedMediaType: "Unsupported Media Type", StatusUnsupportedMediaType: "Unsupported Media Type",
StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable", StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable",
StatusExpectationFailed: "Expectation Failed", StatusExpectationFailed: "Expectation Failed",
StatusTeapot: "I'm a teapot",
StatusInternalServerError: "Internal Server Error", StatusInternalServerError: "Internal Server Error",
StatusNotImplemented: "Not Implemented", StatusNotImplemented: "Not Implemented",
......
...@@ -383,7 +383,7 @@ func fixTransferEncoding(requestMethod string, header Header) ([]string, error) ...@@ -383,7 +383,7 @@ func fixTransferEncoding(requestMethod string, header Header) ([]string, error)
// chunked encoding must always come first. // chunked encoding must always come first.
for _, encoding := range encodings { for _, encoding := range encodings {
encoding = strings.ToLower(strings.TrimSpace(encoding)) encoding = strings.ToLower(strings.TrimSpace(encoding))
// "identity" encoding is not recored // "identity" encoding is not recorded
if encoding == "identity" { if encoding == "identity" {
break break
} }
......
...@@ -76,7 +76,9 @@ type Transport struct { ...@@ -76,7 +76,9 @@ type Transport struct {
// ProxyFromEnvironment returns the URL of the proxy to use for a // ProxyFromEnvironment returns the URL of the proxy to use for a
// given request, as indicated by the environment variables // given request, as indicated by the environment variables
// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy). // $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy).
// Either URL or an error is returned. // An error is returned if the proxy environment is invalid.
// A nil URL and nil error are returned if no proxy is defined in the
// environment, or a proxy should not be used for the given request.
func ProxyFromEnvironment(req *Request) (*url.URL, error) { func ProxyFromEnvironment(req *Request) (*url.URL, error) {
proxy := getenvEitherCase("HTTP_PROXY") proxy := getenvEitherCase("HTTP_PROXY")
if proxy == "" { if proxy == "" {
...@@ -86,7 +88,7 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) { ...@@ -86,7 +88,7 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) {
return nil, nil return nil, nil
} }
proxyURL, err := url.Parse(proxy) proxyURL, err := url.Parse(proxy)
if err != nil { if err != nil || proxyURL.Scheme == "" {
if u, err := url.Parse("http://" + proxy); err == nil { if u, err := url.Parse("http://" + proxy); err == nil {
proxyURL = u proxyURL = u
err = nil err = nil
......
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
. "net/http" . "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"os"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
...@@ -727,6 +728,36 @@ func TestTransportAltProto(t *testing.T) { ...@@ -727,6 +728,36 @@ func TestTransportAltProto(t *testing.T) {
} }
} }
var proxyFromEnvTests = []struct {
env string
wanturl string
wanterr error
}{
{"127.0.0.1:8080", "http://127.0.0.1:8080", nil},
{"http://127.0.0.1:8080", "http://127.0.0.1:8080", nil},
{"https://127.0.0.1:8080", "https://127.0.0.1:8080", nil},
{"", "<nil>", nil},
}
func TestProxyFromEnvironment(t *testing.T) {
os.Setenv("HTTP_PROXY", "")
os.Setenv("http_proxy", "")
os.Setenv("NO_PROXY", "")
os.Setenv("no_proxy", "")
for i, tt := range proxyFromEnvTests {
os.Setenv("HTTP_PROXY", tt.env)
req, _ := NewRequest("GET", "http://example.com", nil)
url, err := ProxyFromEnvironment(req)
if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e {
t.Errorf("%d. got error = %q, want %q", i, g, e)
continue
}
if got := fmt.Sprintf("%s", url); got != tt.wanturl {
t.Errorf("%d. got URL = %q, want %q", i, url, tt.wanturl)
}
}
}
// rgz is a gzip quine that uncompresses to itself. // rgz is a gzip quine that uncompresses to itself.
var rgz = []byte{ var rgz = []byte{
0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
......
...@@ -108,7 +108,6 @@ func DateServer(rw http.ResponseWriter, req *http.Request) { ...@@ -108,7 +108,6 @@ func DateServer(rw http.ResponseWriter, req *http.Request) {
fmt.Fprintf(rw, "fork/exec: %s\n", err) fmt.Fprintf(rw, "fork/exec: %s\n", err)
return return
} }
defer p.Release()
io.Copy(rw, r) io.Copy(rw, r)
wait, err := p.Wait(0) wait, err := p.Wait(0)
if err != nil { if err != nil {
......
...@@ -6,11 +6,7 @@ ...@@ -6,11 +6,7 @@
package net package net
import ( import "errors"
"bytes"
"errors"
"fmt"
)
var ( var (
errInvalidInterface = errors.New("net: invalid interface") errInvalidInterface = errors.New("net: invalid interface")
...@@ -20,77 +16,6 @@ var ( ...@@ -20,77 +16,6 @@ var (
errNoSuchMulticastInterface = errors.New("net: no such multicast interface") errNoSuchMulticastInterface = errors.New("net: no such multicast interface")
) )
// A HardwareAddr represents a physical hardware address.
type HardwareAddr []byte
func (a HardwareAddr) String() string {
var buf bytes.Buffer
for i, b := range a {
if i > 0 {
buf.WriteByte(':')
}
fmt.Fprintf(&buf, "%02x", b)
}
return buf.String()
}
// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
// following formats:
// 01:23:45:67:89:ab
// 01:23:45:67:89:ab:cd:ef
// 01-23-45-67-89-ab
// 01-23-45-67-89-ab-cd-ef
// 0123.4567.89ab
// 0123.4567.89ab.cdef
func ParseMAC(s string) (hw HardwareAddr, err error) {
if len(s) < 14 {
goto error
}
if s[2] == ':' || s[2] == '-' {
if (len(s)+1)%3 != 0 {
goto error
}
n := (len(s) + 1) / 3
if n != 6 && n != 8 {
goto error
}
hw = make(HardwareAddr, n)
for x, i := 0, 0; i < n; i++ {
var ok bool
if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
goto error
}
x += 3
}
} else if s[4] == '.' {
if (len(s)+1)%5 != 0 {
goto error
}
n := 2 * (len(s) + 1) / 5
if n != 6 && n != 8 {
goto error
}
hw = make(HardwareAddr, n)
for x, i := 0, 0; i < n; i += 2 {
var ok bool
if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
goto error
}
if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
goto error
}
x += 5
}
} else {
goto error
}
return hw, nil
error:
return nil, errors.New("invalid MAC address: " + s)
}
// Interface represents a mapping between network interface name // Interface represents a mapping between network interface name
// and index. It also represents network interface facility // and index. It also represents network interface facility
// information. // information.
......
...@@ -6,8 +6,6 @@ package net ...@@ -6,8 +6,6 @@ package net
import ( import (
"bytes" "bytes"
"reflect"
"strings"
"testing" "testing"
) )
...@@ -96,46 +94,3 @@ func testMulticastAddrs(t *testing.T, ifmat []Addr) { ...@@ -96,46 +94,3 @@ func testMulticastAddrs(t *testing.T, ifmat []Addr) {
} }
} }
} }
var mactests = []struct {
in string
out HardwareAddr
err string
}{
{"01:23:45:67:89:AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
{"01-23-45-67-89-AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
{"0123.4567.89AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
{"ab:cd:ef:AB:CD:EF", HardwareAddr{0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef}, ""},
{"01.02.03.04.05.06", nil, "invalid MAC address"},
{"01:02:03:04:05:06:", nil, "invalid MAC address"},
{"x1:02:03:04:05:06", nil, "invalid MAC address"},
{"01002:03:04:05:06", nil, "invalid MAC address"},
{"01:02003:04:05:06", nil, "invalid MAC address"},
{"01:02:03004:05:06", nil, "invalid MAC address"},
{"01:02:03:04005:06", nil, "invalid MAC address"},
{"01:02:03:04:05006", nil, "invalid MAC address"},
{"01-02:03:04:05:06", nil, "invalid MAC address"},
{"01:02-03-04-05-06", nil, "invalid MAC address"},
{"0123:4567:89AF", nil, "invalid MAC address"},
{"0123-4567-89AF", nil, "invalid MAC address"},
{"01:23:45:67:89:AB:CD:EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
{"01-23-45-67-89-AB-CD-EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
{"0123.4567.89AB.CDEF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
}
func match(err error, s string) bool {
if s == "" {
return err == nil
}
return err != nil && strings.Contains(err.Error(), s)
}
func TestParseMAC(t *testing.T) {
for _, tt := range mactests {
out, err := ParseMAC(tt.in)
if !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) {
t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out,
tt.err)
}
}
}
...@@ -76,7 +76,7 @@ func lookupProtocol(name string) (proto int, err error) { ...@@ -76,7 +76,7 @@ func lookupProtocol(name string) (proto int, err error) {
} }
func lookupHost(host string) (addrs []string, err error) { func lookupHost(host string) (addrs []string, err error) {
// Use /net/cs insead of /net/dns because cs knows about // Use /net/cs instead of /net/dns because cs knows about
// host names in local network (e.g. from /lib/ndb/local) // host names in local network (e.g. from /lib/ndb/local)
lines, err := queryCS("tcp", host, "1") lines, err := queryCS("tcp", host, "1")
if err != nil { if err != nil {
......
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
"testing" "testing"
) )
var testExternal = flag.Bool("external", false, "allow use of external networks during test") var testExternal = flag.Bool("external", true, "allow use of external networks during long test")
func TestGoogleSRV(t *testing.T) { func TestGoogleSRV(t *testing.T) {
if testing.Short() || !*testExternal { if testing.Short() || !*testExternal {
...@@ -78,3 +78,40 @@ func TestGoogleDNSAddr(t *testing.T) { ...@@ -78,3 +78,40 @@ func TestGoogleDNSAddr(t *testing.T) {
t.Errorf("no results") t.Errorf("no results")
} }
} }
var revAddrTests = []struct {
Addr string
Reverse string
ErrPrefix string
}{
{"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""},
{"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""},
{"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""},
{"::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", ""},
{"1::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.ip6.arpa.", ""},
{"1234:567::89a:bcde", "e.d.c.b.a.9.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
{"1234:567:fefe:bcbc:adad:9e4a:89a:bcde", "e.d.c.b.a.9.8.0.a.4.e.9.d.a.d.a.c.b.c.b.e.f.e.f.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
{"1.2.3", "", "unrecognized address"},
{"1.2.3.4.5", "", "unrecognized address"},
{"1234:567:bcbca::89a:bcde", "", "unrecognized address"},
{"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"},
}
func TestReverseAddress(t *testing.T) {
for i, tt := range revAddrTests {
a, err := reverseaddr(tt.Addr)
if len(tt.ErrPrefix) > 0 && err == nil {
t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix)
continue
}
if len(tt.ErrPrefix) == 0 && err != nil {
t.Errorf("#%d: expected <nil>, got %q (error)", i, err)
}
if err != nil && err.(*DNSError).Err != tt.ErrPrefix {
t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, err.(*DNSError).Err)
}
if a != tt.Reverse {
t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a)
}
}
}
// Copyright 2011 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.
// MAC address manipulations
package net
import (
"bytes"
"errors"
"fmt"
)
// A HardwareAddr represents a physical hardware address.
type HardwareAddr []byte
func (a HardwareAddr) String() string {
var buf bytes.Buffer
for i, b := range a {
if i > 0 {
buf.WriteByte(':')
}
fmt.Fprintf(&buf, "%02x", b)
}
return buf.String()
}
// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
// following formats:
// 01:23:45:67:89:ab
// 01:23:45:67:89:ab:cd:ef
// 01-23-45-67-89-ab
// 01-23-45-67-89-ab-cd-ef
// 0123.4567.89ab
// 0123.4567.89ab.cdef
func ParseMAC(s string) (hw HardwareAddr, err error) {
if len(s) < 14 {
goto error
}
if s[2] == ':' || s[2] == '-' {
if (len(s)+1)%3 != 0 {
goto error
}
n := (len(s) + 1) / 3
if n != 6 && n != 8 {
goto error
}
hw = make(HardwareAddr, n)
for x, i := 0, 0; i < n; i++ {
var ok bool
if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
goto error
}
x += 3
}
} else if s[4] == '.' {
if (len(s)+1)%5 != 0 {
goto error
}
n := 2 * (len(s) + 1) / 5
if n != 6 && n != 8 {
goto error
}
hw = make(HardwareAddr, n)
for x, i := 0, 0; i < n; i += 2 {
var ok bool
if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
goto error
}
if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
goto error
}
x += 5
}
} else {
goto error
}
return hw, nil
error:
return nil, errors.New("invalid MAC address: " + s)
}
// Copyright 2011 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 (
"reflect"
"strings"
"testing"
)
var mactests = []struct {
in string
out HardwareAddr
err string
}{
{"01:23:45:67:89:AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
{"01-23-45-67-89-AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
{"0123.4567.89AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
{"ab:cd:ef:AB:CD:EF", HardwareAddr{0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef}, ""},
{"01.02.03.04.05.06", nil, "invalid MAC address"},
{"01:02:03:04:05:06:", nil, "invalid MAC address"},
{"x1:02:03:04:05:06", nil, "invalid MAC address"},
{"01002:03:04:05:06", nil, "invalid MAC address"},
{"01:02003:04:05:06", nil, "invalid MAC address"},
{"01:02:03004:05:06", nil, "invalid MAC address"},
{"01:02:03:04005:06", nil, "invalid MAC address"},
{"01:02:03:04:05006", nil, "invalid MAC address"},
{"01-02:03:04:05:06", nil, "invalid MAC address"},
{"01:02-03-04-05-06", nil, "invalid MAC address"},
{"0123:4567:89AF", nil, "invalid MAC address"},
{"0123-4567-89AF", nil, "invalid MAC address"},
{"01:23:45:67:89:AB:CD:EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
{"01-23-45-67-89-AB-CD-EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
{"0123.4567.89AB.CDEF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
}
func match(err error, s string) bool {
if s == "" {
return err == nil
}
return err != nil && strings.Contains(err.Error(), s)
}
func TestParseMAC(t *testing.T) {
for _, tt := range mactests {
out, err := ParseMAC(tt.in)
if !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) {
t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out,
tt.err)
}
}
}
...@@ -5,130 +5,12 @@ ...@@ -5,130 +5,12 @@
package net package net
import ( import (
"flag"
"io" "io"
"regexp"
"runtime" "runtime"
"testing" "testing"
"time" "time"
) )
var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check for dns errors")
type DialErrorTest struct {
Net string
Raddr string
Pattern string
}
var dialErrorTests = []DialErrorTest{
{
"datakit", "mh/astro/r70",
"dial datakit mh/astro/r70: unknown network datakit",
},
{
"tcp", "127.0.0.1:☺",
"dial tcp 127.0.0.1:☺: unknown port tcp/☺",
},
{
"tcp", "no-such-name.google.com.:80",
"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
},
{
"tcp", "no-such-name.no-such-top-level-domain.:80",
"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
},
{
"tcp", "no-such-name:80",
`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
},
{
"tcp", "mh/astro/r70:http",
"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
},
{
"unix", "/etc/file-not-found",
"dial unix /etc/file-not-found: [nN]o such file or directory",
},
{
"unix", "/etc/",
"dial unix /etc/: ([pP]ermission denied|socket operation on non-socket|connection refused)",
},
{
"unixpacket", "/etc/file-not-found",
"dial unixpacket /etc/file-not-found: no such file or directory",
},
{
"unixpacket", "/etc/",
"dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
},
}
var duplicateErrorPattern = `dial (.*) dial (.*)`
func TestDialError(t *testing.T) {
if !*runErrorTest {
t.Logf("test disabled; use --run_error_test to enable")
return
}
for i, tt := range dialErrorTests {
c, err := Dial(tt.Net, tt.Raddr)
if c != nil {
c.Close()
}
if err == nil {
t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern)
continue
}
s := err.Error()
match, _ := regexp.MatchString(tt.Pattern, s)
if !match {
t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern)
}
match, _ = regexp.MatchString(duplicateErrorPattern, s)
if match {
t.Errorf("#%d: %q, duplicate error return from Dial", i, s)
}
}
}
var revAddrTests = []struct {
Addr string
Reverse string
ErrPrefix string
}{
{"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""},
{"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""},
{"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""},
{"::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", ""},
{"1::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.ip6.arpa.", ""},
{"1234:567::89a:bcde", "e.d.c.b.a.9.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
{"1234:567:fefe:bcbc:adad:9e4a:89a:bcde", "e.d.c.b.a.9.8.0.a.4.e.9.d.a.d.a.c.b.c.b.e.f.e.f.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
{"1.2.3", "", "unrecognized address"},
{"1.2.3.4.5", "", "unrecognized address"},
{"1234:567:bcbca::89a:bcde", "", "unrecognized address"},
{"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"},
}
func TestReverseAddress(t *testing.T) {
for i, tt := range revAddrTests {
a, err := reverseaddr(tt.Addr)
if len(tt.ErrPrefix) > 0 && err == nil {
t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix)
continue
}
if len(tt.ErrPrefix) == 0 && err != nil {
t.Errorf("#%d: expected <nil>, got %q (error)", i, err)
}
if err != nil && err.(*DNSError).Err != tt.ErrPrefix {
t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, err.(*DNSError).Err)
}
if a != tt.Reverse {
t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a)
}
}
}
func TestShutdown(t *testing.T) { func TestShutdown(t *testing.T) {
if runtime.GOOS == "plan9" { if runtime.GOOS == "plan9" {
return return
......
...@@ -140,7 +140,7 @@ func (client *Client) input() { ...@@ -140,7 +140,7 @@ func (client *Client) input() {
} }
client.mutex.Unlock() client.mutex.Unlock()
client.sending.Unlock() client.sending.Unlock()
if err != io.EOF || !closing { if err != io.EOF && !closing {
log.Println("rpc: client protocol error:", err) log.Println("rpc: client protocol error:", err)
} }
} }
......
...@@ -13,13 +13,19 @@ ...@@ -13,13 +13,19 @@
Only methods that satisfy these criteria will be made available for remote access; Only methods that satisfy these criteria will be made available for remote access;
other methods will be ignored: other methods will be ignored:
- the method name is exported, that is, begins with an upper case letter. - the method is exported.
- the method receiver is exported or local (defined in the package - the method has two arguments, both exported (or builtin) types.
registering the service).
- the method has two arguments, both exported or local types.
- the method's second argument is a pointer. - the method's second argument is a pointer.
- the method has return type error. - the method has return type error.
In effect, the method must look schematically like
func (t *T) MethodName(argType T1, replyType *T2) error
where T, T1 and T2 can be marshaled by encoding/gob.
These requirements apply even if a different codec is used.
(In future, these requirements may soften for custom codecs.)
The method's first argument represents the arguments provided by the caller; the The method's first argument represents the arguments provided by the caller; the
second argument represents the result parameters to be returned to the caller. second argument represents the result parameters to be returned to the caller.
The method's return value, if non-nil, is passed back as a string that the client The method's return value, if non-nil, is passed back as a string that the client
...@@ -36,10 +42,12 @@ ...@@ -36,10 +42,12 @@
call, a pointer containing the arguments, and a pointer to receive the result call, a pointer containing the arguments, and a pointer to receive the result
parameters. parameters.
Call waits for the remote call to complete; Go launches the call asynchronously The Call method waits for the remote call to complete while the Go method
and returns a channel that will signal completion. launches the call asynchronously and signals completion using the Call
structure's Done channel.
Package "gob" is used to transport the data. Unless an explicit codec is set up, package encoding/gob is used to
transport the data.
Here is a simple example. A server wishes to export an object of type Arith: Here is a simple example. A server wishes to export an object of type Arith:
...@@ -256,6 +264,7 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro ...@@ -256,6 +264,7 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
method := s.typ.Method(m) method := s.typ.Method(m)
mtype := method.Type mtype := method.Type
mname := method.Name mname := method.Name
// Method must be exported.
if method.PkgPath != "" { if method.PkgPath != "" {
continue continue
} }
...@@ -267,7 +276,7 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro ...@@ -267,7 +276,7 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
// First arg need not be a pointer. // First arg need not be a pointer.
argType := mtype.In(1) argType := mtype.In(1)
if !isExportedOrBuiltinType(argType) { if !isExportedOrBuiltinType(argType) {
log.Println(mname, "argument type not exported or local:", argType) log.Println(mname, "argument type not exported:", argType)
continue continue
} }
// Second arg must be a pointer. // Second arg must be a pointer.
...@@ -276,15 +285,17 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro ...@@ -276,15 +285,17 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
log.Println("method", mname, "reply type not a pointer:", replyType) log.Println("method", mname, "reply type not a pointer:", replyType)
continue continue
} }
// Reply type must be exported.
if !isExportedOrBuiltinType(replyType) { if !isExportedOrBuiltinType(replyType) {
log.Println("method", mname, "reply type not exported or local:", replyType) log.Println("method", mname, "reply type not exported:", replyType)
continue continue
} }
// Method needs one out: error. // Method needs one out.
if mtype.NumOut() != 1 { if mtype.NumOut() != 1 {
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut()) log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
continue continue
} }
// The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError { if returnType := mtype.Out(0); returnType != typeOfError {
log.Println("method", mname, "returns", returnType.String(), "not error") log.Println("method", mname, "returns", returnType.String(), "not error")
continue continue
...@@ -301,10 +312,10 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro ...@@ -301,10 +312,10 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
return nil return nil
} }
// A value sent as a placeholder for the response when the server receives an invalid request. // A value sent as a placeholder for the server's response value when the server
type InvalidRequest struct{} // receives an invalid request. It is never decoded by the client since the Response
// contains an error when it is used.
var invalidRequest = InvalidRequest{} var invalidRequest = struct{}{}
func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec ServerCodec, errmsg string) { func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec ServerCodec, errmsg string) {
resp := server.getResponse() resp := server.getResponse()
......
// 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.
// IP-level socket options for NetBSD
package net
import "syscall"
func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
// TODO: Implement this
return nil, syscall.EAFNOSUPPORT
}
func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
// TODO: Implement this
return syscall.EAFNOSUPPORT
}
func ipv4MulticastLoopback(fd *netFD) (bool, error) {
// TODO: Implement this
return false, syscall.EAFNOSUPPORT
}
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
// TODO: Implement this
return syscall.EAFNOSUPPORT
}
func ipv4ReceiveInterface(fd *netFD) (bool, error) {
// TODO: Implement this
return false, syscall.EAFNOSUPPORT
}
func setIPv4ReceiveInterface(fd *netFD, v bool) error {
// TODO: Implement this
return syscall.EAFNOSUPPORT
}
...@@ -40,7 +40,7 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) { ...@@ -40,7 +40,7 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
errc <- fmt.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1) errc <- fmt.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1)
return return
} }
if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 250*time.Millisecond { if dt := t1.Sub(t0); dt < 50*time.Millisecond || !testing.Short() && dt > 250*time.Millisecond {
errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt) errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt)
return return
} }
......
...@@ -38,18 +38,18 @@ func testWriteToConn(t *testing.T, raddr string) { ...@@ -38,18 +38,18 @@ func testWriteToConn(t *testing.T, raddr string) {
_, err = c.(*UDPConn).WriteToUDP([]byte("Connection-oriented mode socket"), ra) _, err = c.(*UDPConn).WriteToUDP([]byte("Connection-oriented mode socket"), ra)
if err == nil { if err == nil {
t.Fatal("WriteToUDP should be failed") t.Fatal("WriteToUDP should fail")
} }
if err != nil && err.(*OpError).Err != ErrWriteToConnected { if err != nil && err.(*OpError).Err != ErrWriteToConnected {
t.Fatalf("WriteToUDP should be failed as ErrWriteToConnected: %v", err) t.Fatalf("WriteToUDP should fail as ErrWriteToConnected: %v", err)
} }
_, err = c.(*UDPConn).WriteTo([]byte("Connection-oriented mode socket"), ra) _, err = c.(*UDPConn).WriteTo([]byte("Connection-oriented mode socket"), ra)
if err == nil { if err == nil {
t.Fatal("WriteTo should be failed") t.Fatal("WriteTo should fail")
} }
if err != nil && err.(*OpError).Err != ErrWriteToConnected { if err != nil && err.(*OpError).Err != ErrWriteToConnected {
t.Fatalf("WriteTo should be failed as ErrWriteToConnected: %v", err) t.Fatalf("WriteTo should fail as ErrWriteToConnected: %v", err)
} }
_, err = c.Write([]byte("Connection-oriented mode socket")) _, err = c.Write([]byte("Connection-oriented mode socket"))
...@@ -82,6 +82,6 @@ func testWriteToPacketConn(t *testing.T, raddr string) { ...@@ -82,6 +82,6 @@ func testWriteToPacketConn(t *testing.T, raddr string) {
_, err = c.(*UDPConn).Write([]byte("Connection-less mode socket")) _, err = c.(*UDPConn).Write([]byte("Connection-less mode socket"))
if err == nil { if err == nil {
t.Fatal("Write should be failed") t.Fatal("Write should fail")
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment