<commit_before>package jd

import (
	"testing"
)

func TestDiffAndPatch(t *testing.T) {
	checkDiffAndPatchSuccess(t,
		`{"a":1}`,
		`{"a":2}`,
		`{"a":1,"c":3}`,
		`{"a":2,"c":3}`)
	checkDiffAndPatchSuccess(t,
		`[[]]`,
		`[[1]]`,
		`[[],[2]]`,
		`[[1],[2]]`)
	checkDiffAndPatchSuccess(t,
		`[{"a":1},{"a":1}]`,
		`[{"a":2},{"a":3}]`,
		`[{"a":1},{"a":1,"b":4},{"c":5}]`,
		`[{"a":2},{"a":3,"b":4},{"c":5}]`)
}

func TestDiffAndPatchSet(t *testing.T) {
	checkDiffAndPatchSuccessSet(t,
		`{"a":{"b" : ["3", "4" ],"c" : ["2", "1"]}}`,
		`{"a":{"b" : ["3", "4", "5", "6"],"c" : ["2", "1"]}}`,
		`{"a":{"b" : ["3", "4" ],"c" : ["2", "1"]}}`,
		`{"a":{"b" : ["3", "4", "5", "6"],"c" : ["2", "1"]}}`)
}

func TestDiffAndPatchError(t *testing.T) {
	checkDiffAndPatchError(t,
		`{"a":1}`,
		`{"a":2}`,
		`{"a":3}`)
	checkDiffAndPatchError(t,
		`{"a":1}`,
		`{"a":2}`,
		`{}`)
	checkDiffAndPatchError(t,
		`1`,
		`2`,
		``)
	checkDiffAndPatchError(t,
		`1`,
		``,
		`2`)
}

func checkDiffAndPatchSuccessSet(t *testing.T, a, b, c, expect string) {
	err := checkDiffAndPatch(t, a, b, c, expect, SET)
	if err != nil {
		t.Errorf(err.Error())
	}
}

func checkDiffAndPatchSuccess(t *testing.T, a, b, c, expect string) {
	err := checkDiffAndPatch(t, a, b, c, expect)
	if err != nil {
		t.Errorf(err.Error())
	}
}

func checkDiffAndPatchError(t *testing.T, a, b, c string) {
	err := checkDiffAndPatch(t, a, b, c, "")
	if err == nil {
		t.Errorf("Expected error.")
	}
}

func checkDiffAndPatch(t *testing.T, a, b, c, expect string, metadata ...Metadata) error {
	nodeA, err := ReadJsonString(a)
	if err != nil {
		return err
	}
	nodeB, err := ReadJsonString(b)
	if err != nil {
		return err
	}
	nodeC, err := ReadJsonString(c)
	if err != nil {
		return err
	}
	expectNode, err := ReadJsonString(expect)
	if err != nil {
		return err
	}
	diffString := nodeA.Diff(nodeB).Render()
	diff, err := ReadDiffString(diffString)
	if err != nil {
		return err
	}
	actualNode, err := nodeC.Patch(diff)
	if err != nil {
		return err
	}
	if !actualNode.Equals(expectNode) {
		t.Errorf("actual = %v. Want %v.", actualNode, expectNode)
	}
	return nil
}
<commit_msg>Add JSON Patch format to round-tripping E2E tests.<commit_after>package jd

import (
	"testing"
)

func TestDiffAndPatch(t *testing.T) {
	checkDiffAndPatchSuccess(t,
		`{"a":1}`,
		`{"a":2}`,
		`{"a":1,"c":3}`,
		`{"a":2,"c":3}`)
	checkDiffAndPatchSuccess(t,
		`[[]]`,
		`[[1]]`,
		`[[],[2]]`,
		`[[1],[2]]`)
	checkDiffAndPatchSuccess(t,
		`[{"a":1},{"a":1}]`,
		`[{"a":2},{"a":3}]`,
		`[{"a":1},{"a":1,"b":4},{"c":5}]`,
		`[{"a":2},{"a":3,"b":4},{"c":5}]`)
}

func TestDiffAndPatchSet(t *testing.T) {
	checkDiffAndPatchSuccessSet(t,
		`{"a":{"b" : ["3", "4" ],"c" : ["2", "1"]}}`,
		`{"a":{"b" : ["3", "4", "5", "6"],"c" : ["2", "1"]}}`,
		`{"a":{"b" : ["3", "4" ],"c" : ["2", "1"]}}`,
		`{"a":{"b" : ["3", "4", "5", "6"],"c" : ["2", "1"]}}`)
}

func TestDiffAndPatchError(t *testing.T) {
	checkDiffAndPatchError(t,
		`{"a":1}`,
		`{"a":2}`,
		`{"a":3}`)
	checkDiffAndPatchError(t,
		`{"a":1}`,
		`{"a":2}`,
		`{}`)
	checkDiffAndPatchError(t,
		`1`,
		`2`,
		``)
	checkDiffAndPatchError(t,
		`1`,
		``,
		`2`)
}

type format string

const (
	formatJd    format = "jd"
	formatPatch format = "patch"
	formatMerge format = "merge"
)

func checkDiffAndPatchSuccessSet(t *testing.T, a, b, c, expect string) {
	err := checkDiffAndPatch(t, formatJd, a, b, c, expect, SET)
	if err != nil {
		t.Errorf("Error round-tripping jd format: %v", err)
	}
	// JSON Patch format does not support sets.
}

func checkDiffAndPatchSuccess(t *testing.T, a, b, c, expect string) {
	err := checkDiffAndPatch(t, formatJd, a, b, c, expect)
	if err != nil {
		t.Errorf("Error round-tripping jd format: %v", err)
	}
	err = checkDiffAndPatch(t, formatPatch, a, b, c, expect)
	if err != nil {
		t.Errorf("Error round-tripping patch format: %v", err)
	}
}

func checkDiffAndPatchError(t *testing.T, a, b, c string) {
	err := checkDiffAndPatch(t, formatJd, a, b, c, "")
	if err == nil {
		t.Errorf("Expected error round-tripping jd format.")
	}
	err = checkDiffAndPatch(t, formatPatch, a, b, c, "")
	if err == nil {
		t.Errorf("Expected error rount-tripping patch format.")
	}
}

func checkDiffAndPatch(t *testing.T, f format, a, b, c, expect string, metadata ...Metadata) error {
	nodeA, err := ReadJsonString(a)
	if err != nil {
		return err
	}
	nodeB, err := ReadJsonString(b)
	if err != nil {
		return err
	}
	nodeC, err := ReadJsonString(c)
	if err != nil {
		return err
	}
	expectNode, err := ReadJsonString(expect)
	if err != nil {
		return err
	}
	var diff Diff
	switch f {
	case formatJd:
		diffString := nodeA.Diff(nodeB).Render()
		diff, err = ReadDiffString(diffString)
	case formatPatch:
		patchString, err := nodeA.Diff(nodeB).RenderPatch()
		if err != nil {
			return nil
		}
		diff, err = ReadPatchString(patchString)
	case formatMerge:
		// not yet implemented
	}
	if err != nil {
		return err
	}
	actualNode, err := nodeC.Patch(diff)
	if err != nil {
		return err
	}
	if !actualNode.Equals(expectNode) {
		t.Errorf("actual = %v. Want %v.", actualNode, expectNode)
	}
	return nil
}
<|endoftext|>


<commit_before>// 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"
	"log"
	"os"
	"path/filepath"
	"runtime"
)

// 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()
	// TODO(adg): check object version is consistent
}

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 = path[len(tpath):]
			return
		}
		err = fmt.Errorf("path %q not inside a GOPATH", path)
		return
	}
	tree = defaultTree
	pkg = 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 {
		log.Printf("invalid GOROOT %q: %v", root, err)
	} else {
		t.Goroot = true
		Path = []*Tree{t}
	}

Loop:
	for _, p := range filepath.SplitList(os.Getenv("GOPATH")) {
		if p == "" {
			continue
		}
		t, err := newTree(p)
		if err != nil {
			log.Printf("invalid GOPATH %q: %v", p, err)
			continue
		}

		// Check for dupes.
		// TODO(alexbrainman): make this correct under windows (case insensitive).
		for _, t2 := range Path {
			if t2.Path != t.Path {
				continue
			}
			if t2.Goroot {
				log.Printf("GOPATH is the same as GOROOT: %q", t.Path)
			} else {
				log.Printf("duplicate GOPATH entry: %q", t.Path)
			}
			continue Loop
		}

		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]
	}
}
<commit_msg>go/build: no back slash in FindTree returned pkg name<commit_after>// 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"
	"log"
	"os"
	"path/filepath"
	"runtime"
)

// 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()
	// TODO(adg): check object version is consistent
}

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 {
		log.Printf("invalid GOROOT %q: %v", root, err)
	} else {
		t.Goroot = true
		Path = []*Tree{t}
	}

Loop:
	for _, p := range filepath.SplitList(os.Getenv("GOPATH")) {
		if p == "" {
			continue
		}
		t, err := newTree(p)
		if err != nil {
			log.Printf("invalid GOPATH %q: %v", p, err)
			continue
		}

		// Check for dupes.
		// TODO(alexbrainman): make this correct under windows (case insensitive).
		for _, t2 := range Path {
			if t2.Path != t.Path {
				continue
			}
			if t2.Goroot {
				log.Printf("GOPATH is the same as GOROOT: %q", t.Path)
			} else {
				log.Printf("duplicate GOPATH entry: %q", t.Path)
			}
			continue Loop
		}

		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]
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http

import (
	"bufio"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

// transferWriter inspects the fields of a user-supplied Request or Response,
// sanitizes them without changing the user object and provides methods for
// writing the respective header, body and trailer in wire format.
type transferWriter struct {
	Body             io.ReadCloser
	ResponseToHEAD   bool
	ContentLength    int64
	Close            bool
	TransferEncoding []string
	Trailer          Header
}

func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) {
	t = &transferWriter{}

	// Extract relevant fields
	atLeastHTTP11 := false
	switch rr := r.(type) {
	case *Request:
		t.Body = rr.Body
		t.ContentLength = rr.ContentLength
		t.Close = rr.Close
		t.TransferEncoding = rr.TransferEncoding
		t.Trailer = rr.Trailer
		atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
	case *Response:
		t.Body = rr.Body
		t.ContentLength = rr.ContentLength
		t.Close = rr.Close
		t.TransferEncoding = rr.TransferEncoding
		t.Trailer = rr.Trailer
		atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
		t.ResponseToHEAD = noBodyExpected(rr.RequestMethod)
	}

	// Sanitize Body,ContentLength,TransferEncoding
	if t.ResponseToHEAD {
		t.Body = nil
		t.TransferEncoding = nil
		// ContentLength is expected to hold Content-Length
		if t.ContentLength < 0 {
			return nil, ErrMissingContentLength
		}
	} else {
		if !atLeastHTTP11 || t.Body == nil {
			t.TransferEncoding = nil
		}
		if chunked(t.TransferEncoding) {
			t.ContentLength = -1
		} else if t.Body == nil { // no chunking, no body
			t.ContentLength = 0
		}
	}

	// Sanitize Trailer
	if !chunked(t.TransferEncoding) {
		t.Trailer = nil
	}

	return t, nil
}

func noBodyExpected(requestMethod string) bool {
	return requestMethod == "HEAD"
}

func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) {
	if t.Close {
		_, err = io.WriteString(w, "Connection: close\r\n")
		if err != nil {
			return
		}
	}

	// Write Content-Length and/or Transfer-Encoding whose values are a
	// function of the sanitized field triple (Body, ContentLength,
	// TransferEncoding)
	if chunked(t.TransferEncoding) {
		_, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")
		if err != nil {
			return
		}
	} else if t.ContentLength > 0 || t.ResponseToHEAD {
		io.WriteString(w, "Content-Length: ")
		_, err = io.WriteString(w, strconv.Itoa64(t.ContentLength)+"\r\n")
		if err != nil {
			return
		}
	}

	// Write Trailer header
	if t.Trailer != nil {
		// TODO: At some point, there should be a generic mechanism for
		// writing long headers, using HTTP line splitting
		io.WriteString(w, "Trailer: ")
		needComma := false
		for k := range t.Trailer {
			k = CanonicalHeaderKey(k)
			switch k {
			case "Transfer-Encoding", "Trailer", "Content-Length":
				return &badStringError{"invalid Trailer key", k}
			}
			if needComma {
				io.WriteString(w, ",")
			}
			io.WriteString(w, k)
			needComma = true
		}
		_, err = io.WriteString(w, "\r\n")
	}

	return
}

func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) {
	// Write body
	if t.Body != nil {
		if chunked(t.TransferEncoding) {
			cw := NewChunkedWriter(w)
			_, err = io.Copy(cw, t.Body)
			if err == nil {
				err = cw.Close()
			}
		} else if t.ContentLength == -1 {
			_, err = io.Copy(w, t.Body)
		} else {
			_, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength))
		}
		if err != nil {
			return err
		}
		if err = t.Body.Close(); err != nil {
			return err
		}
	}

	// TODO(petar): Place trailer writer code here.
	if chunked(t.TransferEncoding) {
		// Last chunk, empty trailer
		_, err = io.WriteString(w, "\r\n")
	}

	return
}

type transferReader struct {
	// Input
	Header        Header
	StatusCode    int
	RequestMethod string
	ProtoMajor    int
	ProtoMinor    int
	// Output
	Body             io.ReadCloser
	ContentLength    int64
	TransferEncoding []string
	Close            bool
	Trailer          Header
}

// bodyAllowedForStatus returns whether a given response status code
// permits a body.  See RFC2616, section 4.4.
func bodyAllowedForStatus(status int) bool {
	switch {
	case status >= 100 && status <= 199:
		return false
	case status == 204:
		return false
	case status == 304:
		return false
	}
	return true
}

// msg is *Request or *Response.
func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
	t := &transferReader{}

	// Unify input
	switch rr := msg.(type) {
	case *Response:
		t.Header = rr.Header
		t.StatusCode = rr.StatusCode
		t.RequestMethod = rr.RequestMethod
		t.ProtoMajor = rr.ProtoMajor
		t.ProtoMinor = rr.ProtoMinor
		t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
	case *Request:
		t.Header = rr.Header
		t.ProtoMajor = rr.ProtoMajor
		t.ProtoMinor = rr.ProtoMinor
		// Transfer semantics for Requests are exactly like those for
		// Responses with status code 200, responding to a GET method
		t.StatusCode = 200
		t.RequestMethod = "GET"
	}

	// Default to HTTP/1.1
	if t.ProtoMajor == 0 && t.ProtoMinor == 0 {
		t.ProtoMajor, t.ProtoMinor = 1, 1
	}

	// Transfer encoding, content length
	t.TransferEncoding, err = fixTransferEncoding(t.RequestMethod, t.Header)
	if err != nil {
		return err
	}

	t.ContentLength, err = fixLength(t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
	if err != nil {
		return err
	}

	// Trailer
	t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding)
	if err != nil {
		return err
	}

	// If there is no Content-Length or chunked Transfer-Encoding on a *Response
	// and the status is not 1xx, 204 or 304, then the body is unbounded.
	// See RFC2616, section 4.4.
	switch msg.(type) {
	case *Response:
		if t.ContentLength == -1 &&
			!chunked(t.TransferEncoding) &&
			bodyAllowedForStatus(t.StatusCode) {
			// Unbounded body.
			t.Close = true
		}
	}

	// Prepare body reader.  ContentLength < 0 means chunked encoding
	// or close connection when finished, since multipart is not supported yet
	switch {
	case chunked(t.TransferEncoding):
		t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
	case t.ContentLength >= 0:
		// TODO: limit the Content-Length. This is an easy DoS vector.
		t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close}
	default:
		// t.ContentLength < 0, i.e. "Content-Length" not mentioned in header
		if t.Close {
			// Close semantics (i.e. HTTP/1.0)
			t.Body = &body{Reader: r, closing: t.Close}
		} else {
			// Persistent connection (i.e. HTTP/1.1)
			t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
		}
		// TODO(petar): It may be a good idea, for extra robustness, to
		// assume ContentLength=0 for GET requests (and other special
		// cases?). This logic should be in fixLength().
	}

	// Unify output
	switch rr := msg.(type) {
	case *Request:
		rr.Body = t.Body
		rr.ContentLength = t.ContentLength
		rr.TransferEncoding = t.TransferEncoding
		rr.Close = t.Close
		rr.Trailer = t.Trailer
	case *Response:
		rr.Body = t.Body
		rr.ContentLength = t.ContentLength
		rr.TransferEncoding = t.TransferEncoding
		rr.Close = t.Close
		rr.Trailer = t.Trailer
	}

	return nil
}

// Checks whether chunked is part of the encodings stack
func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }

// Sanitize transfer encoding
func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Error) {
	raw, present := header["Transfer-Encoding"]
	if !present {
		return nil, nil
	}

	header["Transfer-Encoding"] = nil, false

	// Head responses have no bodies, so the transfer encoding
	// should be ignored.
	if requestMethod == "HEAD" {
		return nil, nil
	}

	encodings := strings.Split(raw[0], ",", -1)
	te := make([]string, 0, len(encodings))
	// TODO: Even though we only support "identity" and "chunked"
	// encodings, the loop below is designed with foresight. One
	// invariant that must be maintained is that, if present,
	// chunked encoding must always come first.
	for _, encoding := range encodings {
		encoding = strings.ToLower(strings.TrimSpace(encoding))
		// "identity" encoding is not recored
		if encoding == "identity" {
			break
		}
		if encoding != "chunked" {
			return nil, &badStringError{"unsupported transfer encoding", encoding}
		}
		te = te[0 : len(te)+1]
		te[len(te)-1] = encoding
	}
	if len(te) > 1 {
		return nil, &badStringError{"too many transfer encodings", strings.Join(te, ",")}
	}
	if len(te) > 0 {
		// Chunked encoding trumps Content-Length. See RFC 2616
		// Section 4.4. Currently len(te) > 0 implies chunked
		// encoding.
		header["Content-Length"] = nil, false
		return te, nil
	}

	return nil, nil
}

// Determine the expected body length, using RFC 2616 Section 4.4. This
// function is not a method, because ultimately it should be shared by
// ReadResponse and ReadRequest.
func fixLength(status int, requestMethod string, header Header, te []string) (int64, os.Error) {

	// Logic based on response type or status
	if noBodyExpected(requestMethod) {
		return 0, nil
	}
	if status/100 == 1 {
		return 0, nil
	}
	switch status {
	case 204, 304:
		return 0, nil
	}

	// Logic based on Transfer-Encoding
	if chunked(te) {
		return -1, nil
	}

	// Logic based on Content-Length
	cl := strings.TrimSpace(header.Get("Content-Length"))
	if cl != "" {
		n, err := strconv.Atoi64(cl)
		if err != nil || n < 0 {
			return -1, &badStringError{"bad Content-Length", cl}
		}
		return n, nil
	} else {
		header.Del("Content-Length")
	}

	// Logic based on media type. The purpose of the following code is just
	// to detect whether the unsupported "multipart/byteranges" is being
	// used. A proper Content-Type parser is needed in the future.
	if strings.Contains(strings.ToLower(header.Get("Content-Type")), "multipart/byteranges") {
		return -1, ErrNotSupported
	}

	// Body-EOF logic based on other methods (like closing, or chunked coding)
	return -1, nil
}

// Determine whether to hang up after sending a request and body, or
// receiving a response and body
// 'header' is the request headers
func shouldClose(major, minor int, header Header) bool {
	if major < 1 {
		return true
	} else if major == 1 && minor == 0 {
		if !strings.Contains(strings.ToLower(header.Get("Connection")), "keep-alive") {
			return true
		}
		return false
	} else {
		// TODO: Should split on commas, toss surrounding white space,
		// and check each field.
		if strings.ToLower(header.Get("Connection")) == "close" {
			header.Del("Connection")
			return true
		}
	}
	return false
}

// Parse the trailer header
func fixTrailer(header Header, te []string) (Header, os.Error) {
	raw := header.Get("Trailer")
	if raw == "" {
		return nil, nil
	}

	header.Del("Trailer")
	trailer := make(Header)
	keys := strings.Split(raw, ",", -1)
	for _, key := range keys {
		key = CanonicalHeaderKey(strings.TrimSpace(key))
		switch key {
		case "Transfer-Encoding", "Trailer", "Content-Length":
			return nil, &badStringError{"bad trailer key", key}
		}
		trailer.Del(key)
	}
	if len(trailer) == 0 {
		return nil, nil
	}
	if !chunked(te) {
		// Trailer and no chunking
		return nil, ErrUnexpectedTrailer
	}
	return trailer, nil
}

// body turns a Reader into a ReadCloser.
// Close ensures that the body has been fully read
// and then reads the trailer if necessary.
type body struct {
	io.Reader
	hdr     interface{}   // non-nil (Response or Request) value means read trailer
	r       *bufio.Reader // underlying wire-format reader for the trailer
	closing bool          // is the connection to be closed after reading body?
}

func (b *body) Close() os.Error {
	if b.hdr == nil && b.closing {
		// no trailer and closing the connection next.
		// no point in reading to EOF.
		return nil
	}

	if _, err := io.Copy(ioutil.Discard, b); err != nil {
		return err
	}

	if b.hdr == nil { // not reading trailer
		return nil
	}

	// TODO(petar): Put trailer reader code here

	return nil
}
<commit_msg>http: new error for reading a body after it's been closed<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http

import (
	"bufio"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

// transferWriter inspects the fields of a user-supplied Request or Response,
// sanitizes them without changing the user object and provides methods for
// writing the respective header, body and trailer in wire format.
type transferWriter struct {
	Body             io.ReadCloser
	ResponseToHEAD   bool
	ContentLength    int64
	Close            bool
	TransferEncoding []string
	Trailer          Header
}

func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) {
	t = &transferWriter{}

	// Extract relevant fields
	atLeastHTTP11 := false
	switch rr := r.(type) {
	case *Request:
		t.Body = rr.Body
		t.ContentLength = rr.ContentLength
		t.Close = rr.Close
		t.TransferEncoding = rr.TransferEncoding
		t.Trailer = rr.Trailer
		atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
	case *Response:
		t.Body = rr.Body
		t.ContentLength = rr.ContentLength
		t.Close = rr.Close
		t.TransferEncoding = rr.TransferEncoding
		t.Trailer = rr.Trailer
		atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
		t.ResponseToHEAD = noBodyExpected(rr.RequestMethod)
	}

	// Sanitize Body,ContentLength,TransferEncoding
	if t.ResponseToHEAD {
		t.Body = nil
		t.TransferEncoding = nil
		// ContentLength is expected to hold Content-Length
		if t.ContentLength < 0 {
			return nil, ErrMissingContentLength
		}
	} else {
		if !atLeastHTTP11 || t.Body == nil {
			t.TransferEncoding = nil
		}
		if chunked(t.TransferEncoding) {
			t.ContentLength = -1
		} else if t.Body == nil { // no chunking, no body
			t.ContentLength = 0
		}
	}

	// Sanitize Trailer
	if !chunked(t.TransferEncoding) {
		t.Trailer = nil
	}

	return t, nil
}

func noBodyExpected(requestMethod string) bool {
	return requestMethod == "HEAD"
}

func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) {
	if t.Close {
		_, err = io.WriteString(w, "Connection: close\r\n")
		if err != nil {
			return
		}
	}

	// Write Content-Length and/or Transfer-Encoding whose values are a
	// function of the sanitized field triple (Body, ContentLength,
	// TransferEncoding)
	if chunked(t.TransferEncoding) {
		_, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")
		if err != nil {
			return
		}
	} else if t.ContentLength > 0 || t.ResponseToHEAD {
		io.WriteString(w, "Content-Length: ")
		_, err = io.WriteString(w, strconv.Itoa64(t.ContentLength)+"\r\n")
		if err != nil {
			return
		}
	}

	// Write Trailer header
	if t.Trailer != nil {
		// TODO: At some point, there should be a generic mechanism for
		// writing long headers, using HTTP line splitting
		io.WriteString(w, "Trailer: ")
		needComma := false
		for k := range t.Trailer {
			k = CanonicalHeaderKey(k)
			switch k {
			case "Transfer-Encoding", "Trailer", "Content-Length":
				return &badStringError{"invalid Trailer key", k}
			}
			if needComma {
				io.WriteString(w, ",")
			}
			io.WriteString(w, k)
			needComma = true
		}
		_, err = io.WriteString(w, "\r\n")
	}

	return
}

func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) {
	// Write body
	if t.Body != nil {
		if chunked(t.TransferEncoding) {
			cw := NewChunkedWriter(w)
			_, err = io.Copy(cw, t.Body)
			if err == nil {
				err = cw.Close()
			}
		} else if t.ContentLength == -1 {
			_, err = io.Copy(w, t.Body)
		} else {
			_, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength))
		}
		if err != nil {
			return err
		}
		if err = t.Body.Close(); err != nil {
			return err
		}
	}

	// TODO(petar): Place trailer writer code here.
	if chunked(t.TransferEncoding) {
		// Last chunk, empty trailer
		_, err = io.WriteString(w, "\r\n")
	}

	return
}

type transferReader struct {
	// Input
	Header        Header
	StatusCode    int
	RequestMethod string
	ProtoMajor    int
	ProtoMinor    int
	// Output
	Body             io.ReadCloser
	ContentLength    int64
	TransferEncoding []string
	Close            bool
	Trailer          Header
}

// bodyAllowedForStatus returns whether a given response status code
// permits a body.  See RFC2616, section 4.4.
func bodyAllowedForStatus(status int) bool {
	switch {
	case status >= 100 && status <= 199:
		return false
	case status == 204:
		return false
	case status == 304:
		return false
	}
	return true
}

// msg is *Request or *Response.
func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
	t := &transferReader{}

	// Unify input
	switch rr := msg.(type) {
	case *Response:
		t.Header = rr.Header
		t.StatusCode = rr.StatusCode
		t.RequestMethod = rr.RequestMethod
		t.ProtoMajor = rr.ProtoMajor
		t.ProtoMinor = rr.ProtoMinor
		t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
	case *Request:
		t.Header = rr.Header
		t.ProtoMajor = rr.ProtoMajor
		t.ProtoMinor = rr.ProtoMinor
		// Transfer semantics for Requests are exactly like those for
		// Responses with status code 200, responding to a GET method
		t.StatusCode = 200
		t.RequestMethod = "GET"
	}

	// Default to HTTP/1.1
	if t.ProtoMajor == 0 && t.ProtoMinor == 0 {
		t.ProtoMajor, t.ProtoMinor = 1, 1
	}

	// Transfer encoding, content length
	t.TransferEncoding, err = fixTransferEncoding(t.RequestMethod, t.Header)
	if err != nil {
		return err
	}

	t.ContentLength, err = fixLength(t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
	if err != nil {
		return err
	}

	// Trailer
	t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding)
	if err != nil {
		return err
	}

	// If there is no Content-Length or chunked Transfer-Encoding on a *Response
	// and the status is not 1xx, 204 or 304, then the body is unbounded.
	// See RFC2616, section 4.4.
	switch msg.(type) {
	case *Response:
		if t.ContentLength == -1 &&
			!chunked(t.TransferEncoding) &&
			bodyAllowedForStatus(t.StatusCode) {
			// Unbounded body.
			t.Close = true
		}
	}

	// Prepare body reader.  ContentLength < 0 means chunked encoding
	// or close connection when finished, since multipart is not supported yet
	switch {
	case chunked(t.TransferEncoding):
		t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
	case t.ContentLength >= 0:
		// TODO: limit the Content-Length. This is an easy DoS vector.
		t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close}
	default:
		// t.ContentLength < 0, i.e. "Content-Length" not mentioned in header
		if t.Close {
			// Close semantics (i.e. HTTP/1.0)
			t.Body = &body{Reader: r, closing: t.Close}
		} else {
			// Persistent connection (i.e. HTTP/1.1)
			t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
		}
		// TODO(petar): It may be a good idea, for extra robustness, to
		// assume ContentLength=0 for GET requests (and other special
		// cases?). This logic should be in fixLength().
	}

	// Unify output
	switch rr := msg.(type) {
	case *Request:
		rr.Body = t.Body
		rr.ContentLength = t.ContentLength
		rr.TransferEncoding = t.TransferEncoding
		rr.Close = t.Close
		rr.Trailer = t.Trailer
	case *Response:
		rr.Body = t.Body
		rr.ContentLength = t.ContentLength
		rr.TransferEncoding = t.TransferEncoding
		rr.Close = t.Close
		rr.Trailer = t.Trailer
	}

	return nil
}

// Checks whether chunked is part of the encodings stack
func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }

// Sanitize transfer encoding
func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Error) {
	raw, present := header["Transfer-Encoding"]
	if !present {
		return nil, nil
	}

	header["Transfer-Encoding"] = nil, false

	// Head responses have no bodies, so the transfer encoding
	// should be ignored.
	if requestMethod == "HEAD" {
		return nil, nil
	}

	encodings := strings.Split(raw[0], ",", -1)
	te := make([]string, 0, len(encodings))
	// TODO: Even though we only support "identity" and "chunked"
	// encodings, the loop below is designed with foresight. One
	// invariant that must be maintained is that, if present,
	// chunked encoding must always come first.
	for _, encoding := range encodings {
		encoding = strings.ToLower(strings.TrimSpace(encoding))
		// "identity" encoding is not recored
		if encoding == "identity" {
			break
		}
		if encoding != "chunked" {
			return nil, &badStringError{"unsupported transfer encoding", encoding}
		}
		te = te[0 : len(te)+1]
		te[len(te)-1] = encoding
	}
	if len(te) > 1 {
		return nil, &badStringError{"too many transfer encodings", strings.Join(te, ",")}
	}
	if len(te) > 0 {
		// Chunked encoding trumps Content-Length. See RFC 2616
		// Section 4.4. Currently len(te) > 0 implies chunked
		// encoding.
		header["Content-Length"] = nil, false
		return te, nil
	}

	return nil, nil
}

// Determine the expected body length, using RFC 2616 Section 4.4. This
// function is not a method, because ultimately it should be shared by
// ReadResponse and ReadRequest.
func fixLength(status int, requestMethod string, header Header, te []string) (int64, os.Error) {

	// Logic based on response type or status
	if noBodyExpected(requestMethod) {
		return 0, nil
	}
	if status/100 == 1 {
		return 0, nil
	}
	switch status {
	case 204, 304:
		return 0, nil
	}

	// Logic based on Transfer-Encoding
	if chunked(te) {
		return -1, nil
	}

	// Logic based on Content-Length
	cl := strings.TrimSpace(header.Get("Content-Length"))
	if cl != "" {
		n, err := strconv.Atoi64(cl)
		if err != nil || n < 0 {
			return -1, &badStringError{"bad Content-Length", cl}
		}
		return n, nil
	} else {
		header.Del("Content-Length")
	}

	// Logic based on media type. The purpose of the following code is just
	// to detect whether the unsupported "multipart/byteranges" is being
	// used. A proper Content-Type parser is needed in the future.
	if strings.Contains(strings.ToLower(header.Get("Content-Type")), "multipart/byteranges") {
		return -1, ErrNotSupported
	}

	// Body-EOF logic based on other methods (like closing, or chunked coding)
	return -1, nil
}

// Determine whether to hang up after sending a request and body, or
// receiving a response and body
// 'header' is the request headers
func shouldClose(major, minor int, header Header) bool {
	if major < 1 {
		return true
	} else if major == 1 && minor == 0 {
		if !strings.Contains(strings.ToLower(header.Get("Connection")), "keep-alive") {
			return true
		}
		return false
	} else {
		// TODO: Should split on commas, toss surrounding white space,
		// and check each field.
		if strings.ToLower(header.Get("Connection")) == "close" {
			header.Del("Connection")
			return true
		}
	}
	return false
}

// Parse the trailer header
func fixTrailer(header Header, te []string) (Header, os.Error) {
	raw := header.Get("Trailer")
	if raw == "" {
		return nil, nil
	}

	header.Del("Trailer")
	trailer := make(Header)
	keys := strings.Split(raw, ",", -1)
	for _, key := range keys {
		key = CanonicalHeaderKey(strings.TrimSpace(key))
		switch key {
		case "Transfer-Encoding", "Trailer", "Content-Length":
			return nil, &badStringError{"bad trailer key", key}
		}
		trailer.Del(key)
	}
	if len(trailer) == 0 {
		return nil, nil
	}
	if !chunked(te) {
		// Trailer and no chunking
		return nil, ErrUnexpectedTrailer
	}
	return trailer, nil
}

// body turns a Reader into a ReadCloser.
// Close ensures that the body has been fully read
// and then reads the trailer if necessary.
type body struct {
	io.Reader
	hdr     interface{}   // non-nil (Response or Request) value means read trailer
	r       *bufio.Reader // underlying wire-format reader for the trailer
	closing bool          // is the connection to be closed after reading body?
	closed  bool
}

// ErrBodyReadAferClose is returned when reading a Request Body after
// the body has been closed. This typically happens when the body is
// read after an HTTP Handler calls WriteHeader or Write on its
// ResponseWriter.
var ErrBodyReadAferClose = os.NewError("http: invalid Read on closed request Body")

func (b *body) Read(p []byte) (n int, err os.Error) {
	if b.closed {
		return 0, ErrBodyReadAferClose
	}
	return b.Reader.Read(p)
}

func (b *body) Close() os.Error {
	if b.closed {
		return nil
	}
	defer func() {
		b.closed = true
	}()
	if b.hdr == nil && b.closing {
		// no trailer and closing the connection next.
		// no point in reading to EOF.
		return nil
	}

	if _, err := io.Copy(ioutil.Discard, b); err != nil {
		return err
	}

	if b.hdr == nil { // not reading trailer
		return nil
	}

	// TODO(petar): Put trailer reader code here

	return nil
}
<|endoftext|>


<commit_before>// 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 (
	"runtime"
	"testing"
	"time"
)

func newLocalListener(t *testing.T) Listener {
	ln, err := Listen("tcp", "127.0.0.1:0")
	if err != nil {
		ln, err = Listen("tcp6", "[::1]:0")
	}
	if err != nil {
		t.Fatal(err)
	}
	return ln
}

func TestDialTimeout(t *testing.T) {
	ln := newLocalListener(t)
	defer ln.Close()

	errc := make(chan error)

	const SOMAXCONN = 0x80 // copied from syscall, but not always available
	const numConns = SOMAXCONN + 10

	// TODO(bradfitz): It's hard to test this in a portable
	// way. This is unforunate, but works for now.
	switch runtime.GOOS {
	case "linux":
		// The kernel will start accepting TCP connections before userspace
		// gets a chance to not accept them, so fire off a bunch to fill up
		// the kernel's backlog.  Then we test we get a failure after that.
		for i := 0; i < numConns; i++ {
			go func() {
				_, err := DialTimeout("tcp", ln.Addr().String(), 200*time.Millisecond)
				errc <- err
			}()
		}
	case "darwin", "windows":
		// At least OS X 10.7 seems to accept any number of
		// connections, ignoring listen's backlog, so resort
		// to connecting to a hopefully-dead 127/8 address.
		// Same for windows.
		go func() {
			_, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond)
			errc <- err
		}()
	default:
		// TODO(bradfitz):
		// OpenBSD may have a reject route to 10/8.
		// FreeBSD likely works, but is untested.
		t.Logf("skipping test on %q; untested.", runtime.GOOS)
		return
	}

	connected := 0
	for {
		select {
		case <-time.After(15 * time.Second):
			t.Fatal("too slow")
		case err := <-errc:
			if err == nil {
				connected++
				if connected == numConns {
					t.Fatal("all connections connected; expected some to time out")
				}
			} else {
				terr, ok := err.(timeout)
				if !ok {
					t.Fatalf("got error %q; want error with timeout interface", err)
				}
				if !terr.Timeout() {
					t.Fatalf("got error %q; not a timeout", err)
				}
				// Pass. We saw a timeout error.
				return
			}
		}
	}
}
<commit_msg>net: fix comment, make use of listenerBacklog<commit_after>// 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 (
	"runtime"
	"testing"
	"time"
)

func newLocalListener(t *testing.T) Listener {
	ln, err := Listen("tcp", "127.0.0.1:0")
	if err != nil {
		ln, err = Listen("tcp6", "[::1]:0")
	}
	if err != nil {
		t.Fatal(err)
	}
	return ln
}

func TestDialTimeout(t *testing.T) {
	ln := newLocalListener(t)
	defer ln.Close()

	errc := make(chan error)

	numConns := listenerBacklog + 10

	// TODO(bradfitz): It's hard to test this in a portable
	// way. This is unforunate, but works for now.
	switch runtime.GOOS {
	case "linux":
		// The kernel will start accepting TCP connections before userspace
		// gets a chance to not accept them, so fire off a bunch to fill up
		// the kernel's backlog.  Then we test we get a failure after that.
		for i := 0; i < numConns; i++ {
			go func() {
				_, err := DialTimeout("tcp", ln.Addr().String(), 200*time.Millisecond)
				errc <- err
			}()
		}
	case "darwin", "windows":
		// At least OS X 10.7 seems to accept any number of
		// connections, ignoring listen's backlog, so resort
		// to connecting to a hopefully-dead 127/8 address.
		// Same for windows.
		go func() {
			_, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond)
			errc <- err
		}()
	default:
		// TODO(bradfitz):
		// OpenBSD may have a reject route to 127/8 except 127.0.0.1/32
		// by default. FreeBSD likely works, but is untested.
		t.Logf("skipping test on %q; untested.", runtime.GOOS)
		return
	}

	connected := 0
	for {
		select {
		case <-time.After(15 * time.Second):
			t.Fatal("too slow")
		case err := <-errc:
			if err == nil {
				connected++
				if connected == numConns {
					t.Fatal("all connections connected; expected some to time out")
				}
			} else {
				terr, ok := err.(timeout)
				if !ok {
					t.Fatalf("got error %q; want error with timeout interface", err)
				}
				if !terr.Timeout() {
					t.Fatalf("got error %q; not a timeout", err)
				}
				// Pass. We saw a timeout error.
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// DNS client: see RFC 1035.
// Has to be linked into package net for Dial.

// TODO(rsc):
//	Check periodically whether /etc/resolv.conf has changed.
//	Could potentially handle many outstanding lookups faster.
//	Could have a small cache.
//	Random UDP source port (net.Dial should do that for us).
//	Random request IDs.

package net

import (
	"once"
	"os"
)

// DNSError represents a DNS lookup error.
type DNSError struct {
	Error  string // description of the error
	Name   string // name looked for
	Server string // server used
}

func (e *DNSError) String() string {
	s := "lookup " + e.Name
	if e.Server != "" {
		s += " on " + e.Server
	}
	s += ": " + e.Error
	return s
}

const noSuchHost = "no such host"

// Send a request on the connection and hope for a reply.
// Up to cfg.attempts attempts.
func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error) {
	if len(name) >= 256 {
		return nil, &DNSError{"name too long", name, ""}
	}
	out := new(_DNS_Msg)
	out.id = 0x1234
	out.question = []_DNS_Question{
		_DNS_Question{name, _DNS_TypeA, _DNS_ClassINET},
	}
	out.recursion_desired = true
	msg, ok := out.Pack()
	if !ok {
		return nil, &DNSError{"internal error - cannot pack message", name, ""}
	}

	for attempt := 0; attempt < cfg.attempts; attempt++ {
		n, err := c.Write(msg)
		if err != nil {
			return nil, err
		}

		c.SetReadTimeout(1e9) // nanoseconds

		buf := make([]byte, 2000) // More than enough.
		n, err = c.Read(buf)
		if isEAGAIN(err) {
			err = nil
			continue
		}
		if err != nil {
			return nil, err
		}
		buf = buf[0:n]
		in := new(_DNS_Msg)
		if !in.Unpack(buf) || in.id != out.id {
			continue
		}
		return in, nil
	}
	var server string
	if a := c.RemoteAddr(); a != nil {
		server = a.String()
	}
	return nil, &DNSError{"no answer from server", name, server}
}

// Find answer for name in dns message.
// On return, if err == nil, addrs != nil.
func answer(name, server string, dns *_DNS_Msg) (addrs []string, err *DNSError) {
	addrs = make([]string, 0, len(dns.answer))

	if dns.rcode == _DNS_RcodeNameError && dns.recursion_available {
		return nil, &DNSError{noSuchHost, name, ""}
	}
	if dns.rcode != _DNS_RcodeSuccess {
		// None of the error codes make sense
		// for the query we sent.  If we didn't get
		// a name error and we didn't get success,
		// the server is behaving incorrectly.
		return nil, &DNSError{"server misbehaving", name, server}
	}

	// Look for the name.
	// Presotto says it's okay to assume that servers listed in
	// /etc/resolv.conf are recursive resolvers.
	// We asked for recursion, so it should have included
	// all the answers we need in this one packet.
Cname:
	for cnameloop := 0; cnameloop < 10; cnameloop++ {
		addrs = addrs[0:0]
		for i := 0; i < len(dns.answer); i++ {
			rr := dns.answer[i]
			h := rr.Header()
			if h.Class == _DNS_ClassINET && h.Name == name {
				switch h.Rrtype {
				case _DNS_TypeA:
					n := len(addrs)
					a := rr.(*_DNS_RR_A).A
					addrs = addrs[0 : n+1]
					addrs[n] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String()
				case _DNS_TypeCNAME:
					// redirect to cname
					name = rr.(*_DNS_RR_CNAME).Cname
					continue Cname
				}
			}
		}
		if len(addrs) == 0 {
			return nil, &DNSError{noSuchHost, name, server}
		}
		return addrs, nil
	}

	return nil, &DNSError{"too many redirects", name, server}
}

// Do a lookup for a single name, which must be rooted
// (otherwise answer will not find the answers).
func tryOneName(cfg *_DNS_Config, name string) (addrs []string, err os.Error) {
	if len(cfg.servers) == 0 {
		return nil, &DNSError{"no DNS servers", name, ""}
	}
	for i := 0; i < len(cfg.servers); i++ {
		// Calling Dial here is scary -- we have to be sure
		// not to dial a name that will require a DNS lookup,
		// or Dial will call back here to translate it.
		// The DNS config parser has already checked that
		// all the cfg.servers[i] are IP addresses, which
		// Dial will use without a DNS lookup.
		server := cfg.servers[i] + ":53"
		c, cerr := Dial("udp", "", server)
		if cerr != nil {
			err = cerr
			continue
		}
		msg, merr := _Exchange(cfg, c, name)
		c.Close()
		if merr != nil {
			err = merr
			continue
		}
		var dnserr *DNSError
		addrs, dnserr = answer(name, server, msg)
		if dnserr != nil {
			err = dnserr
		} else {
			err = nil // nil os.Error, not nil *DNSError
		}
		if dnserr == nil || dnserr.Error == noSuchHost {
			break
		}
	}
	return
}

var cfg *_DNS_Config
var dnserr os.Error

func loadConfig() { cfg, dnserr = _DNS_ReadConfig() }

func isDomainName(s string) bool {
	// Requirements on DNS name:
	//	* must not be empty.
	//	* must be alphanumeric plus - and .
	//	* each of the dot-separated elements must begin
	//	  and end with a letter or digit.
	//	  RFC 1035 required the element to begin with a letter,
	//	  but RFC 3696 says this has been relaxed to allow digits too.
	//	  still, there must be a letter somewhere in the entire name.
	if len(s) == 0 {
		return false
	}
	if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot
		s += "."
	}

	last := byte('.')
	ok := false // ok once we've seen a letter
	for i := 0; i < len(s); i++ {
		c := s[i]
		switch {
		default:
			return false
		case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
			ok = true
		case '0' <= c && c <= '9':
			// fine
		case c == '-':
			// byte before dash cannot be dot
			if last == '.' {
				return false
			}
		case c == '.':
			// byte before dot cannot be dot, dash
			if last == '.' || last == '-' {
				return false
			}
		}
		last = c
	}

	return ok
}

// LookupHost looks for name using the local hosts file and DNS resolver.
// It returns the canonical name for the host and an array of that
// host's addresses.
func LookupHost(name string) (cname string, addrs []string, err os.Error) {
	if !isDomainName(name) {
		return name, nil, &DNSError{"invalid domain name", name, ""}
	}
	once.Do(loadConfig)
	if dnserr != nil || cfg == nil {
		err = dnserr
		return
	}
	// Use entries from /etc/hosts if they match.
	addrs = lookupStaticHost(name)
	if len(addrs) > 0 {
		cname = name
		return
	}
	// If name is rooted (trailing dot) or has enough dots,
	// try it by itself first.
	rooted := len(name) > 0 && name[len(name)-1] == '.'
	if rooted || count(name, '.') >= cfg.ndots {
		rname := name
		if !rooted {
			rname += "."
		}
		// Can try as ordinary name.
		addrs, err = tryOneName(cfg, rname)
		if err == nil {
			cname = rname
			return
		}
	}
	if rooted {
		return
	}

	// Otherwise, try suffixes.
	for i := 0; i < len(cfg.search); i++ {
		rname := name + "." + cfg.search[i]
		if rname[len(rname)-1] != '.' {
			rname += "."
		}
		addrs, err = tryOneName(cfg, rname)
		if err == nil {
			cname = rname
			return
		}
	}

	// Last ditch effort: try unsuffixed.
	rname := name
	if !rooted {
		rname += "."
	}
	addrs, err = tryOneName(cfg, rname)
	if err == nil {
		cname = rname
		return
	}
	return
}
<commit_msg>net: use slightly less predictable dns request id<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// DNS client: see RFC 1035.
// Has to be linked into package net for Dial.

// TODO(rsc):
//	Check periodically whether /etc/resolv.conf has changed.
//	Could potentially handle many outstanding lookups faster.
//	Could have a small cache.
//	Random UDP source port (net.Dial should do that for us).
//	Random request IDs.

package net

import (
	"once"
	"os"
	"rand"
	"time"
)

// DNSError represents a DNS lookup error.
type DNSError struct {
	Error  string // description of the error
	Name   string // name looked for
	Server string // server used
}

func (e *DNSError) String() string {
	s := "lookup " + e.Name
	if e.Server != "" {
		s += " on " + e.Server
	}
	s += ": " + e.Error
	return s
}

const noSuchHost = "no such host"

// Send a request on the connection and hope for a reply.
// Up to cfg.attempts attempts.
func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error) {
	if len(name) >= 256 {
		return nil, &DNSError{"name too long", name, ""}
	}
	out := new(_DNS_Msg)
	out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds())
	out.question = []_DNS_Question{
		_DNS_Question{name, _DNS_TypeA, _DNS_ClassINET},
	}
	out.recursion_desired = true
	msg, ok := out.Pack()
	if !ok {
		return nil, &DNSError{"internal error - cannot pack message", name, ""}
	}

	for attempt := 0; attempt < cfg.attempts; attempt++ {
		n, err := c.Write(msg)
		if err != nil {
			return nil, err
		}

		c.SetReadTimeout(1e9) // nanoseconds

		buf := make([]byte, 2000) // More than enough.
		n, err = c.Read(buf)
		if isEAGAIN(err) {
			err = nil
			continue
		}
		if err != nil {
			return nil, err
		}
		buf = buf[0:n]
		in := new(_DNS_Msg)
		if !in.Unpack(buf) || in.id != out.id {
			continue
		}
		return in, nil
	}
	var server string
	if a := c.RemoteAddr(); a != nil {
		server = a.String()
	}
	return nil, &DNSError{"no answer from server", name, server}
}

// Find answer for name in dns message.
// On return, if err == nil, addrs != nil.
func answer(name, server string, dns *_DNS_Msg) (addrs []string, err *DNSError) {
	addrs = make([]string, 0, len(dns.answer))

	if dns.rcode == _DNS_RcodeNameError && dns.recursion_available {
		return nil, &DNSError{noSuchHost, name, ""}
	}
	if dns.rcode != _DNS_RcodeSuccess {
		// None of the error codes make sense
		// for the query we sent.  If we didn't get
		// a name error and we didn't get success,
		// the server is behaving incorrectly.
		return nil, &DNSError{"server misbehaving", name, server}
	}

	// Look for the name.
	// Presotto says it's okay to assume that servers listed in
	// /etc/resolv.conf are recursive resolvers.
	// We asked for recursion, so it should have included
	// all the answers we need in this one packet.
Cname:
	for cnameloop := 0; cnameloop < 10; cnameloop++ {
		addrs = addrs[0:0]
		for i := 0; i < len(dns.answer); i++ {
			rr := dns.answer[i]
			h := rr.Header()
			if h.Class == _DNS_ClassINET && h.Name == name {
				switch h.Rrtype {
				case _DNS_TypeA:
					n := len(addrs)
					a := rr.(*_DNS_RR_A).A
					addrs = addrs[0 : n+1]
					addrs[n] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String()
				case _DNS_TypeCNAME:
					// redirect to cname
					name = rr.(*_DNS_RR_CNAME).Cname
					continue Cname
				}
			}
		}
		if len(addrs) == 0 {
			return nil, &DNSError{noSuchHost, name, server}
		}
		return addrs, nil
	}

	return nil, &DNSError{"too many redirects", name, server}
}

// Do a lookup for a single name, which must be rooted
// (otherwise answer will not find the answers).
func tryOneName(cfg *_DNS_Config, name string) (addrs []string, err os.Error) {
	if len(cfg.servers) == 0 {
		return nil, &DNSError{"no DNS servers", name, ""}
	}
	for i := 0; i < len(cfg.servers); i++ {
		// Calling Dial here is scary -- we have to be sure
		// not to dial a name that will require a DNS lookup,
		// or Dial will call back here to translate it.
		// The DNS config parser has already checked that
		// all the cfg.servers[i] are IP addresses, which
		// Dial will use without a DNS lookup.
		server := cfg.servers[i] + ":53"
		c, cerr := Dial("udp", "", server)
		if cerr != nil {
			err = cerr
			continue
		}
		msg, merr := _Exchange(cfg, c, name)
		c.Close()
		if merr != nil {
			err = merr
			continue
		}
		var dnserr *DNSError
		addrs, dnserr = answer(name, server, msg)
		if dnserr != nil {
			err = dnserr
		} else {
			err = nil // nil os.Error, not nil *DNSError
		}
		if dnserr == nil || dnserr.Error == noSuchHost {
			break
		}
	}
	return
}

var cfg *_DNS_Config
var dnserr os.Error

func loadConfig() { cfg, dnserr = _DNS_ReadConfig() }

func isDomainName(s string) bool {
	// Requirements on DNS name:
	//	* must not be empty.
	//	* must be alphanumeric plus - and .
	//	* each of the dot-separated elements must begin
	//	  and end with a letter or digit.
	//	  RFC 1035 required the element to begin with a letter,
	//	  but RFC 3696 says this has been relaxed to allow digits too.
	//	  still, there must be a letter somewhere in the entire name.
	if len(s) == 0 {
		return false
	}
	if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot
		s += "."
	}

	last := byte('.')
	ok := false // ok once we've seen a letter
	for i := 0; i < len(s); i++ {
		c := s[i]
		switch {
		default:
			return false
		case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
			ok = true
		case '0' <= c && c <= '9':
			// fine
		case c == '-':
			// byte before dash cannot be dot
			if last == '.' {
				return false
			}
		case c == '.':
			// byte before dot cannot be dot, dash
			if last == '.' || last == '-' {
				return false
			}
		}
		last = c
	}

	return ok
}

// LookupHost looks for name using the local hosts file and DNS resolver.
// It returns the canonical name for the host and an array of that
// host's addresses.
func LookupHost(name string) (cname string, addrs []string, err os.Error) {
	if !isDomainName(name) {
		return name, nil, &DNSError{"invalid domain name", name, ""}
	}
	once.Do(loadConfig)
	if dnserr != nil || cfg == nil {
		err = dnserr
		return
	}
	// Use entries from /etc/hosts if they match.
	addrs = lookupStaticHost(name)
	if len(addrs) > 0 {
		cname = name
		return
	}
	// If name is rooted (trailing dot) or has enough dots,
	// try it by itself first.
	rooted := len(name) > 0 && name[len(name)-1] == '.'
	if rooted || count(name, '.') >= cfg.ndots {
		rname := name
		if !rooted {
			rname += "."
		}
		// Can try as ordinary name.
		addrs, err = tryOneName(cfg, rname)
		if err == nil {
			cname = rname
			return
		}
	}
	if rooted {
		return
	}

	// Otherwise, try suffixes.
	for i := 0; i < len(cfg.search); i++ {
		rname := name + "." + cfg.search[i]
		if rname[len(rname)-1] != '.' {
			rname += "."
		}
		addrs, err = tryOneName(cfg, rname)
		if err == nil {
			cname = rname
			return
		}
	}

	// Last ditch effort: try unsuffixed.
	rname := name
	if !rooted {
		rname += "."
	}
	addrs, err = tryOneName(cfg, rname)
	if err == nil {
		cname = rname
		return
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Parse "zoneinfo" time zone file.
// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
// and ftp://munnari.oz.au/pub/oldtz/

package time

import (
	"io/ioutil"
	"os"
)

const (
	headerSize = 4 + 16 + 4*7
	zoneDir    = "/usr/share/zoneinfo/"
)

// Simple I/O interface to binary blob of data.
type data struct {
	p     []byte
	error bool
}

func (d *data) read(n int) []byte {
	if len(d.p) < n {
		d.p = nil
		d.error = true
		return nil
	}
	p := d.p[0:n]
	d.p = d.p[n:]
	return p
}

func (d *data) big4() (n uint32, ok bool) {
	p := d.read(4)
	if len(p) < 4 {
		d.error = true
		return 0, false
	}
	return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
}

func (d *data) byte() (n byte, ok bool) {
	p := d.read(1)
	if len(p) < 1 {
		d.error = true
		return 0, false
	}
	return p[0], true
}

// Make a string by stopping at the first NUL
func byteString(p []byte) string {
	for i := 0; i < len(p); i++ {
		if p[i] == 0 {
			return string(p[0:i])
		}
	}
	return string(p)
}

// Parsed representation
type zone struct {
	utcoff int
	isdst  bool
	name   string
}

type zonetime struct {
	time         int32 // transition time, in seconds since 1970 GMT
	zone         *zone // the zone that goes into effect at that time
	isstd, isutc bool  // ignored - no idea what these mean
}

func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
	d := data{bytes, false}

	// 4-byte magic "TZif"
	if magic := d.read(4); string(magic) != "TZif" {
		return nil, false
	}

	// 1-byte version, then 15 bytes of padding
	var p []byte
	if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
		return nil, false
	}

	// six big-endian 32-bit integers:
	//	number of UTC/local indicators
	//	number of standard/wall indicators
	//	number of leap seconds
	//	number of transition times
	//	number of local time zones
	//	number of characters of time zone abbrev strings
	const (
		NUTCLocal = iota
		NStdWall
		NLeap
		NTime
		NZone
		NChar
	)
	var n [6]int
	for i := 0; i < 6; i++ {
		nn, ok := d.big4()
		if !ok {
			return nil, false
		}
		n[i] = int(nn)
	}

	// Transition times.
	txtimes := data{d.read(n[NTime] * 4), false}

	// Time zone indices for transition times.
	txzones := d.read(n[NTime])

	// Zone info structures
	zonedata := data{d.read(n[NZone] * 6), false}

	// Time zone abbreviations.
	abbrev := d.read(n[NChar])

	// Leap-second time pairs
	d.read(n[NLeap] * 8)

	// Whether tx times associated with local time types
	// are specified as standard time or wall time.
	isstd := d.read(n[NStdWall])

	// Whether tx times associated with local time types
	// are specified as UTC or local time.
	isutc := d.read(n[NUTCLocal])

	if d.error { // ran out of data
		return nil, false
	}

	// If version == 2, the entire file repeats, this time using
	// 8-byte ints for txtimes and leap seconds.
	// We won't need those until 2106.

	// Now we can build up a useful data structure.
	// First the zone information.
	//	utcoff[4] isdst[1] nameindex[1]
	z := make([]zone, n[NZone])
	for i := 0; i < len(z); i++ {
		var ok bool
		var n uint32
		if n, ok = zonedata.big4(); !ok {
			return nil, false
		}
		z[i].utcoff = int(n)
		var b byte
		if b, ok = zonedata.byte(); !ok {
			return nil, false
		}
		z[i].isdst = b != 0
		if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
			return nil, false
		}
		z[i].name = byteString(abbrev[b:])
	}

	// Now the transition time info.
	zt = make([]zonetime, n[NTime])
	for i := 0; i < len(zt); i++ {
		var ok bool
		var n uint32
		if n, ok = txtimes.big4(); !ok {
			return nil, false
		}
		zt[i].time = int32(n)
		if int(txzones[i]) >= len(z) {
			return nil, false
		}
		zt[i].zone = &z[txzones[i]]
		if i < len(isstd) {
			zt[i].isstd = isstd[i] != 0
		}
		if i < len(isutc) {
			zt[i].isutc = isutc[i] != 0
		}
	}
	return zt, true
}

func readinfofile(name string) ([]zonetime, bool) {
	buf, err := ioutil.ReadFile(name)
	if err != nil {
		return nil, false
	}
	return parseinfo(buf)
}

var zones []zonetime
var onceSetupZone sync.Once

func setupZone() {
	// consult $TZ to find the time zone to use.
	// no $TZ means use the system default /etc/localtime.
	// $TZ="" means use UTC.
	// $TZ="foo" means use /usr/share/zoneinfo/foo.

	tz, err := os.Getenverror("TZ")
	switch {
	case err == os.ENOENV:
		zones, _ = readinfofile("/etc/localtime")
	case len(tz) > 0:
		zones, _ = readinfofile(zoneDir + tz)
	case len(tz) == 0:
		// do nothing: use UTC
	}
}

// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
func lookupTimezone(sec int64) (zone string, offset int) {
	onceSetupZone.Do(setupZone)
	if len(zones) == 0 {
		return "UTC", 0
	}

	// Binary search for entry with largest time <= sec
	tz := zones
	for len(tz) > 1 {
		m := len(tz) / 2
		if sec < int64(tz[m].time) {
			tz = tz[0:m]
		} else {
			tz = tz[m:]
		}
	}
	z := tz[0].zone
	return z.name, z.utcoff
}
<commit_msg>time: Remove unused zoneinfo.go (replaced by zoneinfo_unix.go).<commit_after><|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package xml

import (
	"reflect"
	"testing"
)

// Stripped down Atom feed data structures.

func TestUnmarshalFeed(t *testing.T) {
	var f Feed
	if err := Unmarshal(StringReader(rssFeedString), &f); err != nil {
		t.Fatalf("Unmarshal: %s", err)
	}
	if !reflect.DeepEqual(f, rssFeed) {
		t.Fatalf("have %#v\nwant %#v", f, rssFeed)
	}
}

// hget http://codereview.appspot.com/rss/mine/rsc
const rssFeedString = `
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><li-nk href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></li-nk><id>http://codereview.appspot.com/</id><updated>2009-10-04T01:35:58+00:00</updated><author><name>rietveld<></name></author><entry><title>rietveld: an attempt at pubsubhubbub
</title><link hre-f="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
  An attempt at adding pubsubhubbub support to Rietveld.
http://code.google.com/p/pubsubhubbub
http://code.google.com/p/rietveld/issues/detail?id=155

The server side of the protocol is trivial:
  1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
     feeds that will be pubsubhubbubbed.
  2. every time one of those feeds changes, tell the hub
     with a simple POST request.

I have tested this by adding debug prints to a local hub
server and checking that the server got the right publish
requests.

I can&#39;t quite get the server to work, but I think the bug
is not in my code.  I think that the server expects to be
able to grab the feed and see the feed&#39;s actual URL in
the link rel=&quot;self&quot;, but the default value for that drops
the :port from the URL, and I cannot for the life of me
figure out how to get the Atom generator deep inside
django not to do that, or even where it is doing that,
or even what code is running to generate the Atom feed.
(I thought I knew but I added some assert False statements
and it kept running!)

Ignoring that particular problem, I would appreciate
feedback on the right way to get the two values at
the top of feeds.py marked NOTE(rsc).

</summary></entry><entry><title>rietveld: correct tab handling
</title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
  This fixes the buggy tab rendering that can be seen at
http://codereview.appspot.com/116075/diff/1/2

The fundamental problem was that the tab code was
not being told what column the text began in, so it
didn&#39;t know where to put the tab stops.  Another problem
was that some of the code assumed that string byte
offsets were the same as column offsets, which is only
true if there are no tabs.

In the process of fixing this, I cleaned up the arguments
to Fold and ExpandTabs and renamed them Break and
_ExpandTabs so that I could be sure that I found all the
call sites.  I also wanted to verify that ExpandTabs was
not being used from outside intra_region_diff.py.

</summary></entry></feed> 	   `

type Feed struct {
	XMLName Name "http://www.w3.org/2005/Atom feed"
	Title   string
	Id      string
	Link    []Link
	Updated Time
	Author  Person
	Entry   []Entry
}

type Entry struct {
	Title   string
	Id      string
	Link    []Link
	Updated Time
	Author  Person
	Summary Text
}

type Link struct {
	Rel  string "attr"
	Href string "attr"
}

type Person struct {
	Name     string
	URI      string
	Email    string
	InnerXML string "innerxml"
}

type Text struct {
	Type string "attr"
	Body string "chardata"
}

type Time string

var rssFeed = Feed{
	XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
	Title:   "Code Review - My issues",
	Link: []Link{
		{Rel: "alternate", Href: "http://codereview.appspot.com/"},
		{Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"},
	},
	Id:      "http://codereview.appspot.com/",
	Updated: "2009-10-04T01:35:58+00:00",
	Author: Person{
		Name:     "rietveld<>",
		InnerXML: "<name>rietveld<></name>",
	},
	Entry: []Entry{
		{
			Title: "rietveld: an attempt at pubsubhubbub\n",
			Link: []Link{
				{Rel: "alternate", Href: "http://codereview.appspot.com/126085"},
			},
			Updated: "2009-10-04T01:35:58+00:00",
			Author: Person{
				Name:     "email-address-removed",
				InnerXML: "<name>email-address-removed</name>",
			},
			Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
			Summary: Text{
				Type: "html",
				Body: `
  An attempt at adding pubsubhubbub support to Rietveld.
http://code.google.com/p/pubsubhubbub
http://code.google.com/p/rietveld/issues/detail?id=155

The server side of the protocol is trivial:
  1. add a <link rel="hub" href="hub-server"> tag to all
     feeds that will be pubsubhubbubbed.
  2. every time one of those feeds changes, tell the hub
     with a simple POST request.

I have tested this by adding debug prints to a local hub
server and checking that the server got the right publish
requests.

I can't quite get the server to work, but I think the bug
is not in my code.  I think that the server expects to be
able to grab the feed and see the feed's actual URL in
the link rel="self", but the default value for that drops
the :port from the URL, and I cannot for the life of me
figure out how to get the Atom generator deep inside
django not to do that, or even where it is doing that,
or even what code is running to generate the Atom feed.
(I thought I knew but I added some assert False statements
and it kept running!)

Ignoring that particular problem, I would appreciate
feedback on the right way to get the two values at
the top of feeds.py marked NOTE(rsc).

`,
			},
		},
		{
			Title: "rietveld: correct tab handling\n",
			Link: []Link{
				{Rel: "alternate", Href: "http://codereview.appspot.com/124106"},
			},
			Updated: "2009-10-03T23:02:17+00:00",
			Author: Person{
				Name:     "email-address-removed",
				InnerXML: "<name>email-address-removed</name>",
			},
			Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
			Summary: Text{
				Type: "html",
				Body: `
  This fixes the buggy tab rendering that can be seen at
http://codereview.appspot.com/116075/diff/1/2

The fundamental problem was that the tab code was
not being told what column the text began in, so it
didn't know where to put the tab stops.  Another problem
was that some of the code assumed that string byte
offsets were the same as column offsets, which is only
true if there are no tabs.

In the process of fixing this, I cleaned up the arguments
to Fold and ExpandTabs and renamed them Break and
_ExpandTabs so that I could be sure that I found all the
call sites.  I also wanted to verify that ExpandTabs was
not being used from outside intra_region_diff.py.

`,
			},
		},
	},
}

type FieldNameTest struct {
	in, out string
}

var FieldNameTests = []FieldNameTest{
	{"Profile-Image", "profileimage"},
	{"_score", "score"},
}

func TestFieldName(t *testing.T) {
	for _, tt := range FieldNameTests {
		a := fieldName(tt.in)
		if a != tt.out {
			t.Fatalf("have %#v\nwant %#v\n\n", a, tt.out)
		}
	}
}

const pathTestString = `
<result>
    <before>1</before>
    <items>
        <item1>
            <value>A</value>
        </item1>
        <item2>
            <value>B</value>
        </item2>
        <Item1>
            <Value>C</Value>
            <Value>D</Value>
        </Item1>
    </items>
    <after>2</after>
</result>
`

type PathTestItem struct {
	Value string
}

type PathTestA struct {
	Items         []PathTestItem ">item1"
	Before, After string
}

type PathTestB struct {
	Other         []PathTestItem "items>Item1"
	Before, After string
}

type PathTestC struct {
	Values1       []string "items>item1>value"
	Values2       []string "items>item2>value"
	Before, After string
}

type PathTestSet struct {
	Item1 []PathTestItem
}

type PathTestD struct {
	Other         PathTestSet "items>"
	Before, After string
}

var pathTests = []interface{}{
	&PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
	&PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
	&PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"},
	&PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"},
}

func TestUnmarshalPaths(t *testing.T) {
	for _, pt := range pathTests {
		p := reflect.MakeZero(reflect.NewValue(pt).Type()).(*reflect.PtrValue)
		p.PointTo(reflect.MakeZero(p.Type().(*reflect.PtrType).Elem()))
		v := p.Interface()
		if err := Unmarshal(StringReader(pathTestString), v); err != nil {
			t.Fatalf("Unmarshal: %s", err)
		}
		if !reflect.DeepEqual(v, pt) {
			t.Fatalf("have %#v\nwant %#v", v, pt)
		}
	}
}

type BadPathTestA struct {
	First  string "items>item1"
	Other  string "items>item2"
	Second string "items>"
}

type BadPathTestB struct {
	Other  string "items>item2>value"
	First  string "items>item1"
	Second string "items>item1>value"
}

var badPathTests = []struct {
	v, e interface{}
}{
	{&BadPathTestA{}, &TagPathError{reflect.Typeof(BadPathTestA{}), "First", "items>item1", "Second", "items>"}},
	{&BadPathTestB{}, &TagPathError{reflect.Typeof(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
}

func TestUnmarshalBadPaths(t *testing.T) {
	for _, tt := range badPathTests {
		err := Unmarshal(StringReader(pathTestString), tt.v)
		if !reflect.DeepEqual(err, tt.e) {
			t.Fatalf("Unmarshal with %#v didn't fail properly: %#v", tt.v, err)
		}
	}
}
<commit_msg>xml: fix typo in test.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package xml

import (
	"reflect"
	"testing"
)

// Stripped down Atom feed data structures.

func TestUnmarshalFeed(t *testing.T) {
	var f Feed
	if err := Unmarshal(StringReader(atomFeedString), &f); err != nil {
		t.Fatalf("Unmarshal: %s", err)
	}
	if !reflect.DeepEqual(f, atomFeed) {
		t.Fatalf("have %#v\nwant %#v", f, atomFeed)
	}
}

// hget http://codereview.appspot.com/rss/mine/rsc
const atomFeedString = `
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><li-nk href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></li-nk><id>http://codereview.appspot.com/</id><updated>2009-10-04T01:35:58+00:00</updated><author><name>rietveld<></name></author><entry><title>rietveld: an attempt at pubsubhubbub
</title><link hre-f="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
  An attempt at adding pubsubhubbub support to Rietveld.
http://code.google.com/p/pubsubhubbub
http://code.google.com/p/rietveld/issues/detail?id=155

The server side of the protocol is trivial:
  1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
     feeds that will be pubsubhubbubbed.
  2. every time one of those feeds changes, tell the hub
     with a simple POST request.

I have tested this by adding debug prints to a local hub
server and checking that the server got the right publish
requests.

I can&#39;t quite get the server to work, but I think the bug
is not in my code.  I think that the server expects to be
able to grab the feed and see the feed&#39;s actual URL in
the link rel=&quot;self&quot;, but the default value for that drops
the :port from the URL, and I cannot for the life of me
figure out how to get the Atom generator deep inside
django not to do that, or even where it is doing that,
or even what code is running to generate the Atom feed.
(I thought I knew but I added some assert False statements
and it kept running!)

Ignoring that particular problem, I would appreciate
feedback on the right way to get the two values at
the top of feeds.py marked NOTE(rsc).

</summary></entry><entry><title>rietveld: correct tab handling
</title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
  This fixes the buggy tab rendering that can be seen at
http://codereview.appspot.com/116075/diff/1/2

The fundamental problem was that the tab code was
not being told what column the text began in, so it
didn&#39;t know where to put the tab stops.  Another problem
was that some of the code assumed that string byte
offsets were the same as column offsets, which is only
true if there are no tabs.

In the process of fixing this, I cleaned up the arguments
to Fold and ExpandTabs and renamed them Break and
_ExpandTabs so that I could be sure that I found all the
call sites.  I also wanted to verify that ExpandTabs was
not being used from outside intra_region_diff.py.

</summary></entry></feed> 	   `

type Feed struct {
	XMLName Name "http://www.w3.org/2005/Atom feed"
	Title   string
	Id      string
	Link    []Link
	Updated Time
	Author  Person
	Entry   []Entry
}

type Entry struct {
	Title   string
	Id      string
	Link    []Link
	Updated Time
	Author  Person
	Summary Text
}

type Link struct {
	Rel  string "attr"
	Href string "attr"
}

type Person struct {
	Name     string
	URI      string
	Email    string
	InnerXML string "innerxml"
}

type Text struct {
	Type string "attr"
	Body string "chardata"
}

type Time string

var atomFeed = Feed{
	XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
	Title:   "Code Review - My issues",
	Link: []Link{
		{Rel: "alternate", Href: "http://codereview.appspot.com/"},
		{Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"},
	},
	Id:      "http://codereview.appspot.com/",
	Updated: "2009-10-04T01:35:58+00:00",
	Author: Person{
		Name:     "rietveld<>",
		InnerXML: "<name>rietveld<></name>",
	},
	Entry: []Entry{
		{
			Title: "rietveld: an attempt at pubsubhubbub\n",
			Link: []Link{
				{Rel: "alternate", Href: "http://codereview.appspot.com/126085"},
			},
			Updated: "2009-10-04T01:35:58+00:00",
			Author: Person{
				Name:     "email-address-removed",
				InnerXML: "<name>email-address-removed</name>",
			},
			Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
			Summary: Text{
				Type: "html",
				Body: `
  An attempt at adding pubsubhubbub support to Rietveld.
http://code.google.com/p/pubsubhubbub
http://code.google.com/p/rietveld/issues/detail?id=155

The server side of the protocol is trivial:
  1. add a <link rel="hub" href="hub-server"> tag to all
     feeds that will be pubsubhubbubbed.
  2. every time one of those feeds changes, tell the hub
     with a simple POST request.

I have tested this by adding debug prints to a local hub
server and checking that the server got the right publish
requests.

I can't quite get the server to work, but I think the bug
is not in my code.  I think that the server expects to be
able to grab the feed and see the feed's actual URL in
the link rel="self", but the default value for that drops
the :port from the URL, and I cannot for the life of me
figure out how to get the Atom generator deep inside
django not to do that, or even where it is doing that,
or even what code is running to generate the Atom feed.
(I thought I knew but I added some assert False statements
and it kept running!)

Ignoring that particular problem, I would appreciate
feedback on the right way to get the two values at
the top of feeds.py marked NOTE(rsc).

`,
			},
		},
		{
			Title: "rietveld: correct tab handling\n",
			Link: []Link{
				{Rel: "alternate", Href: "http://codereview.appspot.com/124106"},
			},
			Updated: "2009-10-03T23:02:17+00:00",
			Author: Person{
				Name:     "email-address-removed",
				InnerXML: "<name>email-address-removed</name>",
			},
			Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
			Summary: Text{
				Type: "html",
				Body: `
  This fixes the buggy tab rendering that can be seen at
http://codereview.appspot.com/116075/diff/1/2

The fundamental problem was that the tab code was
not being told what column the text began in, so it
didn't know where to put the tab stops.  Another problem
was that some of the code assumed that string byte
offsets were the same as column offsets, which is only
true if there are no tabs.

In the process of fixing this, I cleaned up the arguments
to Fold and ExpandTabs and renamed them Break and
_ExpandTabs so that I could be sure that I found all the
call sites.  I also wanted to verify that ExpandTabs was
not being used from outside intra_region_diff.py.

`,
			},
		},
	},
}

type FieldNameTest struct {
	in, out string
}

var FieldNameTests = []FieldNameTest{
	{"Profile-Image", "profileimage"},
	{"_score", "score"},
}

func TestFieldName(t *testing.T) {
	for _, tt := range FieldNameTests {
		a := fieldName(tt.in)
		if a != tt.out {
			t.Fatalf("have %#v\nwant %#v\n\n", a, tt.out)
		}
	}
}

const pathTestString = `
<result>
    <before>1</before>
    <items>
        <item1>
            <value>A</value>
        </item1>
        <item2>
            <value>B</value>
        </item2>
        <Item1>
            <Value>C</Value>
            <Value>D</Value>
        </Item1>
    </items>
    <after>2</after>
</result>
`

type PathTestItem struct {
	Value string
}

type PathTestA struct {
	Items         []PathTestItem ">item1"
	Before, After string
}

type PathTestB struct {
	Other         []PathTestItem "items>Item1"
	Before, After string
}

type PathTestC struct {
	Values1       []string "items>item1>value"
	Values2       []string "items>item2>value"
	Before, After string
}

type PathTestSet struct {
	Item1 []PathTestItem
}

type PathTestD struct {
	Other         PathTestSet "items>"
	Before, After string
}

var pathTests = []interface{}{
	&PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
	&PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
	&PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"},
	&PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"},
}

func TestUnmarshalPaths(t *testing.T) {
	for _, pt := range pathTests {
		p := reflect.MakeZero(reflect.NewValue(pt).Type()).(*reflect.PtrValue)
		p.PointTo(reflect.MakeZero(p.Type().(*reflect.PtrType).Elem()))
		v := p.Interface()
		if err := Unmarshal(StringReader(pathTestString), v); err != nil {
			t.Fatalf("Unmarshal: %s", err)
		}
		if !reflect.DeepEqual(v, pt) {
			t.Fatalf("have %#v\nwant %#v", v, pt)
		}
	}
}

type BadPathTestA struct {
	First  string "items>item1"
	Other  string "items>item2"
	Second string "items>"
}

type BadPathTestB struct {
	Other  string "items>item2>value"
	First  string "items>item1"
	Second string "items>item1>value"
}

var badPathTests = []struct {
	v, e interface{}
}{
	{&BadPathTestA{}, &TagPathError{reflect.Typeof(BadPathTestA{}), "First", "items>item1", "Second", "items>"}},
	{&BadPathTestB{}, &TagPathError{reflect.Typeof(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
}

func TestUnmarshalBadPaths(t *testing.T) {
	for _, tt := range badPathTests {
		err := Unmarshal(StringReader(pathTestString), tt.v)
		if !reflect.DeepEqual(err, tt.e) {
			t.Fatalf("Unmarshal with %#v didn't fail properly: %#v", tt.v, err)
		}
	}
}
<|endoftext|>


<commit_before>/*
	Copyright (C) 2013 Ondrej Kupka
	Copyright (C) 2013 Contributors as noted in the AUTHORS file

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"),
	to deal in the Software without restriction, including without limitation
	the rights to use, copy, modify, merge, publish, distribute, sublicense,
	and/or sell copies of the Software, and to permit persons to whom
	the Software is furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included
	in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
	THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
	IN THE SOFTWARE.
*/

package monitor

import (
	"errors"
	"fmt"
	"testing"
	"time"
)

import zmq "github.com/alecthomas/gozmq"

const (
	TestMonitor_Sink   = "tcp://127.0.0.1:23456"
	TestMonitor_Events = "inproc://TestMonitor_Events"
)

func TestMonitor(test *testing.T) {
	factory, err := NewSocketFactory()
	if err != nil {
		test.Fatal(err)
	}
	defer factory.Close()

	out, err := factory.ctx.NewSocket(zmq.PULL)
	if err != nil {
		test.Fatal(err)
	}
	err = out.Bind(TestMonitor_Sink)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	in, err := factory.NewSocket(zmq.PUSH)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	mon, err := New(factory.ctx, in, TestMonitor_Events,
		zmq.EVENT_CONNECTED|zmq.EVENT_DISCONNECTED)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}
	defer func() {
		ex := mon.CloseWait()
		if ex != nil {
			test.Error(ex)
		}
		return
	}()

	eventChan, err := mon.Start()
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	err = in.Connect(TestMonitor_Sink)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	err = assertNextEvent(test, eventChan, zmq.EVENT_CONNECTED)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	err = out.Close()
	if err != nil {
		test.Fatal(err)
	}

	err = assertNextEvent(test, eventChan, zmq.EVENT_DISCONNECTED)
	if err != nil {
		test.Fatal(err)
	}
}

func assertNextEvent(test *testing.T,
	eventChan <-chan *SocketEvent, event zmq.Event) error {

	select {
	case e := <-eventChan:
		test.Logf("%#v\n", e)
		if e.Error != nil {
			return e.Error
		}
		if zmq.Event(e.Event) != event {
			return errors.New(
				fmt.Sprintf("Unexpected event encountered: %d", e.Event))
		}
		if e.Addr != TestMonitor_Sink {
			return errors.New("Unexpected event address encountered.")
		}
	case <-time.After(time.Second):
		return errors.New("Test timed out.")
	}

	return nil
}

/**
 * SocketFactory to easily create sockets and close 0MQ
 */

type SocketFactory struct {
	ctx         *zmq.Context
	ss          []*zmq.Socket
	pipeCounter int
}

func NewSocketFactory() (sf *SocketFactory, err error) {
	ctx, err := zmq.NewContext()
	if err == nil {
		sf = &SocketFactory{ctx, make([]*zmq.Socket, 0), 0}
	}
	return
}

func (self *SocketFactory) NewSocket(t zmq.SocketType) (*zmq.Socket, error) {
	if self.ctx == nil {
		return nil, errors.New("SocketFactory has been closed.")
	}
	s, err := self.ctx.NewSocket(t)
	if err != nil {
		return nil, err
	}
	self.ss = append(self.ss, s)
	return s, nil
}

func (self *SocketFactory) NewPipe() (in *zmq.Socket, out *zmq.Socket, err error) {
	in, err = self.NewSocket(zmq.PAIR)
	if err != nil {
		return
	}
	out, err = self.NewSocket(zmq.PAIR)
	if err != nil {
		return
	}

	endpoint := fmt.Sprintf("inproc://pipe%d", self.pipeCounter)
	self.pipeCounter++

	// Leave the sockets to be collected by factory.Close().
	err = out.Bind(endpoint)
	if err != nil {
		return
	}
	err = in.Connect(endpoint)
	if err != nil {
		return
	}

	return
}

func (self *SocketFactory) Close() {
	for _, s := range self.ss {
		s.Close()
	}
	self.ctx.Close()
	self.ctx = nil
}
<commit_msg>monitor_test: Remove forgotten debug printfs<commit_after>/*
	Copyright (C) 2013 Ondrej Kupka
	Copyright (C) 2013 Contributors as noted in the AUTHORS file

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"),
	to deal in the Software without restriction, including without limitation
	the rights to use, copy, modify, merge, publish, distribute, sublicense,
	and/or sell copies of the Software, and to permit persons to whom
	the Software is furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included
	in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
	THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
	IN THE SOFTWARE.
*/

package monitor

import (
	"errors"
	"fmt"
	"testing"
	"time"
)

import zmq "github.com/alecthomas/gozmq"

const (
	TestMonitor_Sink   = "tcp://127.0.0.1:23456"
	TestMonitor_Events = "inproc://TestMonitor_Events"
)

func TestMonitor(test *testing.T) {
	factory, err := NewSocketFactory()
	if err != nil {
		test.Fatal(err)
	}
	defer factory.Close()

	out, err := factory.ctx.NewSocket(zmq.PULL)
	if err != nil {
		test.Fatal(err)
	}
	err = out.Bind(TestMonitor_Sink)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	in, err := factory.NewSocket(zmq.PUSH)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	mon, err := New(factory.ctx, in, TestMonitor_Events,
		zmq.EVENT_CONNECTED|zmq.EVENT_DISCONNECTED)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}
	defer func() {
		ex := mon.CloseWait()
		if ex != nil {
			test.Error(ex)
		}
		return
	}()

	eventChan, err := mon.Start()
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	err = in.Connect(TestMonitor_Sink)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	err = assertNextEvent(test, eventChan, zmq.EVENT_CONNECTED)
	if err != nil {
		out.Close()
		test.Fatal(err)
	}

	err = out.Close()
	if err != nil {
		test.Fatal(err)
	}

	err = assertNextEvent(test, eventChan, zmq.EVENT_DISCONNECTED)
	if err != nil {
		test.Fatal(err)
	}
}

func assertNextEvent(test *testing.T,
	eventChan <-chan *SocketEvent, event zmq.Event) error {

	select {
	case e := <-eventChan:
		if e.Error != nil {
			return e.Error
		}
		if zmq.Event(e.Event) != event {
			return errors.New(
				fmt.Sprintf("Unexpected event encountered: %d", e.Event))
		}
		if e.Addr != TestMonitor_Sink {
			return errors.New("Unexpected event address encountered.")
		}
	case <-time.After(time.Second):
		return errors.New("Test timed out.")
	}

	return nil
}

/**
 * SocketFactory to easily create sockets and close 0MQ
 */

type SocketFactory struct {
	ctx         *zmq.Context
	ss          []*zmq.Socket
	pipeCounter int
}

func NewSocketFactory() (sf *SocketFactory, err error) {
	ctx, err := zmq.NewContext()
	if err == nil {
		sf = &SocketFactory{ctx, make([]*zmq.Socket, 0), 0}
	}
	return
}

func (self *SocketFactory) NewSocket(t zmq.SocketType) (*zmq.Socket, error) {
	if self.ctx == nil {
		return nil, errors.New("SocketFactory has been closed.")
	}
	s, err := self.ctx.NewSocket(t)
	if err != nil {
		return nil, err
	}
	self.ss = append(self.ss, s)
	return s, nil
}

func (self *SocketFactory) NewPipe() (in *zmq.Socket, out *zmq.Socket, err error) {
	in, err = self.NewSocket(zmq.PAIR)
	if err != nil {
		return
	}
	out, err = self.NewSocket(zmq.PAIR)
	if err != nil {
		return
	}

	endpoint := fmt.Sprintf("inproc://pipe%d", self.pipeCounter)
	self.pipeCounter++

	// Leave the sockets to be collected by factory.Close().
	err = out.Bind(endpoint)
	if err != nil {
		return
	}
	err = in.Connect(endpoint)
	if err != nil {
		return
	}

	return
}

func (self *SocketFactory) Close() {
	for _, s := range self.ss {
		s.Close()
	}
	self.ctx.Close()
	self.ctx = nil
}
<|endoftext|>