<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 template

import (
	"fmt"
	"io"
	"io/ioutil"
	"path/filepath"
	"sync"
	"text/template"
	"text/template/parse"
)

// Template is a specialized template.Template that produces a safe HTML
// document fragment.
type Template struct {
	escaped bool
	// We could embed the text/template field, but it's safer not to because
	// we need to keep our version of the name space and the underlying
	// template's in sync.
	text       *template.Template
	*nameSpace // common to all associated templates
}

// nameSpace is the data structure shared by all templates in an association.
type nameSpace struct {
	mu  sync.Mutex
	set map[string]*Template
}

// Execute applies a parsed template to the specified data object,
// writing the output to wr.
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
	t.nameSpace.mu.Lock()
	if !t.escaped {
		if err = escapeTemplates(t, t.Name()); err != nil {
			t.escaped = true
		}
	}
	t.nameSpace.mu.Unlock()
	if err != nil {
		return
	}
	return t.text.Execute(wr, data)
}

// ExecuteTemplate applies the template associated with t that has the given name
// to the specified data object and writes the output to wr.
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) (err error) {
	t.nameSpace.mu.Lock()
	tmpl := t.set[name]
	if tmpl == nil {
		t.nameSpace.mu.Unlock()
		return fmt.Errorf("template: no template %q associated with template %q", name, t.Name())
	}
	if !tmpl.escaped {
		err = escapeTemplates(tmpl, name)
	}
	t.nameSpace.mu.Unlock()
	if err != nil {
		return
	}
	return tmpl.text.ExecuteTemplate(wr, name, data)
}

// Parse parses a string into a template. Nested template definitions
// will be associated with the top-level template t. Parse may be
// called multiple times to parse definitions of templates to associate
// with t. It is an error if a resulting template is non-empty (contains
// content other than template definitions) and would replace a
// non-empty template with the same name.  (In multiple calls to Parse
// with the same receiver template, only one call can contain text
// other than space, comments, and template definitions.)
func (t *Template) Parse(src string) (*Template, error) {
	t.nameSpace.mu.Lock()
	t.escaped = false
	t.nameSpace.mu.Unlock()
	ret, err := t.text.Parse(src)
	if err != nil {
		return nil, err
	}
	// In general, all the named templates might have changed underfoot.
	// Regardless, some new ones may have been defined.
	// The template.Template set has been updated; update ours.
	t.nameSpace.mu.Lock()
	defer t.nameSpace.mu.Unlock()
	for _, v := range ret.Templates() {
		name := v.Name()
		tmpl := t.set[name]
		if tmpl == nil {
			tmpl = t.new(name)
		}
		tmpl.escaped = false
		tmpl.text = v
	}
	return t, nil
}

// AddParseTree is unimplemented.
func (t *Template) AddParseTree(name string, tree *parse.Tree) error {
	return fmt.Errorf("html/template: AddParseTree unimplemented")
}

// Clone is unimplemented.
func (t *Template) Clone(name string) error {
	return fmt.Errorf("html/template: Add unimplemented")
}

// New allocates a new HTML template with the given name.
func New(name string) *Template {
	tmpl := &Template{
		false,
		template.New(name),
		&nameSpace{
			set: make(map[string]*Template),
		},
	}
	tmpl.set[name] = tmpl
	return tmpl
}

// New allocates a new HTML template associated with the given one
// and with the same delimiters. The association, which is transitive,
// allows one template to invoke another with a {{template}} action.
func (t *Template) New(name string) *Template {
	t.nameSpace.mu.Lock()
	defer t.nameSpace.mu.Unlock()
	return t.new(name)
}

// new is the implementation of New, without the lock.
func (t *Template) new(name string) *Template {
	tmpl := &Template{
		false,
		t.text.New(name),
		t.nameSpace,
	}
	tmpl.set[name] = tmpl
	return tmpl
}

// Name returns the name of the template.
func (t *Template) Name() string {
	return t.text.Name()
}

// Funcs adds the elements of the argument map to the template's function map.
// It panics if a value in the map is not a function with appropriate return
// type. However, it is legal to overwrite elements of the map. The return
// value is the template, so calls can be chained.
func (t *Template) Funcs(funcMap template.FuncMap) *Template {
	t.text.Funcs(funcMap)
	return t
}

// Delims sets the action delimiters to the specified strings, to be used in
// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
// definitions will inherit the settings. An empty delimiter stands for the
// corresponding default: {{ or }}.
// The return value is the template, so calls can be chained.
func (t *Template) Delims(left, right string) *Template {
	t.text.Delims(left, right)
	return t
}

// Lookup returns the template with the given name that is associated with t,
// or nil if there is no such template.
func (t *Template) Lookup(name string) *Template {
	t.nameSpace.mu.Lock()
	defer t.nameSpace.mu.Unlock()
	return t.set[name]
}

// Must panics if err is non-nil in the same way as template.Must.
func Must(t *Template, err error) *Template {
	t.text = template.Must(t.text, err)
	return t
}

// ParseFiles creates a new Template and parses the template definitions from
// the named files. The returned template's name will have the (base) name and
// (parsed) contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
func ParseFiles(filenames ...string) (*Template, error) {
	return parseFiles(nil, filenames...)
}

// ParseFiles parses the named files and associates the resulting templates with
// t. If an error occurs, parsing stops and the returned template is nil;
// otherwise it is t. There must be at least one file.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
	return parseFiles(t, filenames...)
}

// parseFiles is the helper for the method and function. If the argument
// template is nil, it is created from the first file.
func parseFiles(t *Template, filenames ...string) (*Template, error) {
	if len(filenames) == 0 {
		// Not really a problem, but be consistent.
		return nil, fmt.Errorf("template: no files named in call to ParseFiles")
	}
	for _, filename := range filenames {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return nil, err
		}
		s := string(b)
		name := filepath.Base(filename)
		// First template becomes return value if not already defined,
		// and we use that one for subsequent New calls to associate
		// all the templates together. Also, if this file has the same name
		// as t, this file becomes the contents of t, so
		//  t, err := New(name).Funcs(xxx).ParseFiles(name)
		// works. Otherwise we create a new template associated with t.
		var tmpl *Template
		if t == nil {
			t = New(name)
		}
		if name == t.Name() {
			tmpl = t
		} else {
			tmpl = t.New(name)
		}
		_, err = tmpl.Parse(s)
		if err != nil {
			return nil, err
		}
	}
	return t, nil
}

// ParseGlob creates a new Template and parses the template definitions from the
// files identified by the pattern, which must match at least one file. The
// returned template will have the (base) name and (parsed) contents of the
// first file matched by the pattern. ParseGlob is equivalent to calling
// ParseFiles with the list of files matched by the pattern.
func ParseGlob(pattern string) (*Template, error) {
	return parseGlob(nil, pattern)
}

// ParseGlob parses the template definitions in the files identified by the
// pattern and associates the resulting templates with t. The pattern is
// processed by filepath.Glob and must match at least one file. ParseGlob is
// equivalent to calling t.ParseFiles with the list of files matched by the
// pattern.
func (t *Template) ParseGlob(pattern string) (*Template, error) {
	return parseGlob(t, pattern)
}

// parseGlob is the implementation of the function and method ParseGlob.
func parseGlob(t *Template, pattern string) (*Template, error) {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return nil, err
	}
	if len(filenames) == 0 {
		return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
	}
	return parseFiles(t, filenames...)
}
<commit_msg>html/template: simplify ExecuteTemplate a little Allow the text template to handle the error case of no template with the given name. Simplification suggested by Mike Samuel.<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 template

import (
	"fmt"
	"io"
	"io/ioutil"
	"path/filepath"
	"sync"
	"text/template"
	"text/template/parse"
)

// Template is a specialized template.Template that produces a safe HTML
// document fragment.
type Template struct {
	escaped bool
	// We could embed the text/template field, but it's safer not to because
	// we need to keep our version of the name space and the underlying
	// template's in sync.
	text       *template.Template
	*nameSpace // common to all associated templates
}

// nameSpace is the data structure shared by all templates in an association.
type nameSpace struct {
	mu  sync.Mutex
	set map[string]*Template
}

// Execute applies a parsed template to the specified data object,
// writing the output to wr.
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
	t.nameSpace.mu.Lock()
	if !t.escaped {
		if err = escapeTemplates(t, t.Name()); err != nil {
			t.escaped = true
		}
	}
	t.nameSpace.mu.Unlock()
	if err != nil {
		return
	}
	return t.text.Execute(wr, data)
}

// ExecuteTemplate applies the template associated with t that has the given
// name to the specified data object and writes the output to wr.
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) (err error) {
	t.nameSpace.mu.Lock()
	tmpl := t.set[name]
	if (tmpl == nil) != (t.text.Lookup(name) == nil) {
		panic("html/template internal error: template escaping out of sync")
	}
	if tmpl != nil && !tmpl.escaped {
		err = escapeTemplates(tmpl, name)
	}
	t.nameSpace.mu.Unlock()
	if err != nil {
		return
	}
	return t.text.ExecuteTemplate(wr, name, data)
}

// Parse parses a string into a template. Nested template definitions
// will be associated with the top-level template t. Parse may be
// called multiple times to parse definitions of templates to associate
// with t. It is an error if a resulting template is non-empty (contains
// content other than template definitions) and would replace a
// non-empty template with the same name.  (In multiple calls to Parse
// with the same receiver template, only one call can contain text
// other than space, comments, and template definitions.)
func (t *Template) Parse(src string) (*Template, error) {
	t.nameSpace.mu.Lock()
	t.escaped = false
	t.nameSpace.mu.Unlock()
	ret, err := t.text.Parse(src)
	if err != nil {
		return nil, err
	}
	// In general, all the named templates might have changed underfoot.
	// Regardless, some new ones may have been defined.
	// The template.Template set has been updated; update ours.
	t.nameSpace.mu.Lock()
	defer t.nameSpace.mu.Unlock()
	for _, v := range ret.Templates() {
		name := v.Name()
		tmpl := t.set[name]
		if tmpl == nil {
			tmpl = t.new(name)
		}
		tmpl.escaped = false
		tmpl.text = v
	}
	return t, nil
}

// AddParseTree is unimplemented.
func (t *Template) AddParseTree(name string, tree *parse.Tree) error {
	return fmt.Errorf("html/template: AddParseTree unimplemented")
}

// Clone is unimplemented.
func (t *Template) Clone(name string) error {
	return fmt.Errorf("html/template: Clone unimplemented")
}

// New allocates a new HTML template with the given name.
func New(name string) *Template {
	tmpl := &Template{
		false,
		template.New(name),
		&nameSpace{
			set: make(map[string]*Template),
		},
	}
	tmpl.set[name] = tmpl
	return tmpl
}

// New allocates a new HTML template associated with the given one
// and with the same delimiters. The association, which is transitive,
// allows one template to invoke another with a {{template}} action.
func (t *Template) New(name string) *Template {
	t.nameSpace.mu.Lock()
	defer t.nameSpace.mu.Unlock()
	return t.new(name)
}

// new is the implementation of New, without the lock.
func (t *Template) new(name string) *Template {
	tmpl := &Template{
		false,
		t.text.New(name),
		t.nameSpace,
	}
	tmpl.set[name] = tmpl
	return tmpl
}

// Name returns the name of the template.
func (t *Template) Name() string {
	return t.text.Name()
}

// Funcs adds the elements of the argument map to the template's function map.
// It panics if a value in the map is not a function with appropriate return
// type. However, it is legal to overwrite elements of the map. The return
// value is the template, so calls can be chained.
func (t *Template) Funcs(funcMap template.FuncMap) *Template {
	t.text.Funcs(funcMap)
	return t
}

// Delims sets the action delimiters to the specified strings, to be used in
// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
// definitions will inherit the settings. An empty delimiter stands for the
// corresponding default: {{ or }}.
// The return value is the template, so calls can be chained.
func (t *Template) Delims(left, right string) *Template {
	t.text.Delims(left, right)
	return t
}

// Lookup returns the template with the given name that is associated with t,
// or nil if there is no such template.
func (t *Template) Lookup(name string) *Template {
	t.nameSpace.mu.Lock()
	defer t.nameSpace.mu.Unlock()
	return t.set[name]
}

// Must panics if err is non-nil in the same way as template.Must.
func Must(t *Template, err error) *Template {
	t.text = template.Must(t.text, err)
	return t
}

// ParseFiles creates a new Template and parses the template definitions from
// the named files. The returned template's name will have the (base) name and
// (parsed) contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
func ParseFiles(filenames ...string) (*Template, error) {
	return parseFiles(nil, filenames...)
}

// ParseFiles parses the named files and associates the resulting templates with
// t. If an error occurs, parsing stops and the returned template is nil;
// otherwise it is t. There must be at least one file.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
	return parseFiles(t, filenames...)
}

// parseFiles is the helper for the method and function. If the argument
// template is nil, it is created from the first file.
func parseFiles(t *Template, filenames ...string) (*Template, error) {
	if len(filenames) == 0 {
		// Not really a problem, but be consistent.
		return nil, fmt.Errorf("template: no files named in call to ParseFiles")
	}
	for _, filename := range filenames {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return nil, err
		}
		s := string(b)
		name := filepath.Base(filename)
		// First template becomes return value if not already defined,
		// and we use that one for subsequent New calls to associate
		// all the templates together. Also, if this file has the same name
		// as t, this file becomes the contents of t, so
		//  t, err := New(name).Funcs(xxx).ParseFiles(name)
		// works. Otherwise we create a new template associated with t.
		var tmpl *Template
		if t == nil {
			t = New(name)
		}
		if name == t.Name() {
			tmpl = t
		} else {
			tmpl = t.New(name)
		}
		_, err = tmpl.Parse(s)
		if err != nil {
			return nil, err
		}
	}
	return t, nil
}

// ParseGlob creates a new Template and parses the template definitions from the
// files identified by the pattern, which must match at least one file. The
// returned template will have the (base) name and (parsed) contents of the
// first file matched by the pattern. ParseGlob is equivalent to calling
// ParseFiles with the list of files matched by the pattern.
func ParseGlob(pattern string) (*Template, error) {
	return parseGlob(nil, pattern)
}

// ParseGlob parses the template definitions in the files identified by the
// pattern and associates the resulting templates with t. The pattern is
// processed by filepath.Glob and must match at least one file. ParseGlob is
// equivalent to calling t.ParseFiles with the list of files matched by the
// pattern.
func (t *Template) ParseGlob(pattern string) (*Template, error) {
	return parseGlob(t, pattern)
}

// parseGlob is the implementation of the function and method ParseGlob.
func parseGlob(t *Template, pattern string) (*Template, error) {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return nil, err
	}
	if len(filenames) == 0 {
		return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
	}
	return parseFiles(t, filenames...)
}
<|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 httputil

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"
)

// One of the copies, say from b to r2, could be avoided by using a more
// elaborate trick where the other copy is made during Request/Response.Write.
// This would complicate things too much, given that these functions are for
// debugging only.
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
	var buf bytes.Buffer
	if _, err = buf.ReadFrom(b); err != nil {
		return nil, nil, err
	}
	if err = b.Close(); err != nil {
		return nil, nil, err
	}
	return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewBuffer(buf.Bytes())), nil
}

// dumpConn is a net.Conn which writes to Writer and reads from Reader
type dumpConn struct {
	io.Writer
	io.Reader
}

func (c *dumpConn) Close() error                       { return nil }
func (c *dumpConn) LocalAddr() net.Addr                { return nil }
func (c *dumpConn) RemoteAddr() net.Addr               { return nil }
func (c *dumpConn) SetDeadline(t time.Time) error      { return nil }
func (c *dumpConn) SetReadDeadline(t time.Time) error  { return nil }
func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }

// DumpRequestOut is like DumpRequest but includes
// headers that the standard http.Transport adds,
// such as User-Agent.
func DumpRequestOut(req *http.Request, body bool) (dump []byte, err error) {
	save := req.Body
	if !body || req.Body == nil {
		req.Body = nil
	} else {
		save, req.Body, err = drainBody(req.Body)
		if err != nil {
			return
		}
	}

	var b bytes.Buffer
	dialed := false
	t := &http.Transport{
		Dial: func(net, addr string) (c net.Conn, err error) {
			if dialed {
				return nil, errors.New("unexpected second dial")
			}
			c = &dumpConn{
				Writer: &b,
				Reader: strings.NewReader("HTTP/1.1 500 Fake Error\r\n\r\n"),
			}
			return
		},
	}

	_, err = t.RoundTrip(req)

	req.Body = save
	if err != nil {
		return
	}
	dump = b.Bytes()
	return
}

// Return value if nonempty, def otherwise.
func valueOrDefault(value, def string) string {
	if value != "" {
		return value
	}
	return def
}

var reqWriteExcludeHeaderDump = map[string]bool{
	"Host":              true, // not in Header map anyway
	"Content-Length":    true,
	"Transfer-Encoding": true,
	"Trailer":           true,
}

// dumpAsReceived writes req to w in the form as it was received, or
// at least as accurately as possible from the information retained in
// the request.
func dumpAsReceived(req *http.Request, w io.Writer) error {
	return nil
}

// DumpRequest returns the as-received wire representation of req,
// optionally including the request body, for debugging.
// DumpRequest is semantically a no-op, but in order to
// dump the body, it reads the body data into memory and
// changes req.Body to refer to the in-memory copy.
// The documentation for http.Request.Write details which fields
// of req are used.
func DumpRequest(req *http.Request, body bool) (dump []byte, err error) {
	save := req.Body
	if !body || req.Body == nil {
		req.Body = nil
	} else {
		save, req.Body, err = drainBody(req.Body)
		if err != nil {
			return
		}
	}

	var b bytes.Buffer

	fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
		req.URL.RequestURI(), req.ProtoMajor, req.ProtoMinor)

	host := req.Host
	if host == "" && req.URL != nil {
		host = req.URL.Host
	}
	if host != "" {
		fmt.Fprintf(&b, "Host: %s\r\n", host)
	}

	chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked"
	if len(req.TransferEncoding) > 0 {
		fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ","))
	}
	if req.Close {
		fmt.Fprintf(&b, "Connection: close\r\n")
	}

	err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
	if err != nil {
		return
	}

	io.WriteString(&b, "\r\n")

	if req.Body != nil {
		var dest io.Writer = &b
		if chunked {
			dest = NewChunkedWriter(dest)
		}
		_, err = io.Copy(dest, req.Body)
		if chunked {
			dest.(io.Closer).Close()
			io.WriteString(&b, "\r\n")
		}
	}

	req.Body = save
	if err != nil {
		return
	}
	dump = b.Bytes()
	return
}

// DumpResponse is like DumpRequest but dumps a response.
func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) {
	var b bytes.Buffer
	save := resp.Body
	savecl := resp.ContentLength
	if !body || resp.Body == nil {
		resp.Body = nil
		resp.ContentLength = 0
	} else {
		save, resp.Body, err = drainBody(resp.Body)
		if err != nil {
			return
		}
	}
	err = resp.Write(&b)
	resp.Body = save
	resp.ContentLength = savecl
	if err != nil {
		return
	}
	dump = b.Bytes()
	return
}
<commit_msg>net/http/httputil: fix race in DumpRequestOut<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 httputil

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"
)

// One of the copies, say from b to r2, could be avoided by using a more
// elaborate trick where the other copy is made during Request/Response.Write.
// This would complicate things too much, given that these functions are for
// debugging only.
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
	var buf bytes.Buffer
	if _, err = buf.ReadFrom(b); err != nil {
		return nil, nil, err
	}
	if err = b.Close(); err != nil {
		return nil, nil, err
	}
	return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewBuffer(buf.Bytes())), nil
}

// dumpConn is a net.Conn which writes to Writer and reads from Reader
type dumpConn struct {
	io.Writer
	io.Reader
}

func (c *dumpConn) Close() error                       { return nil }
func (c *dumpConn) LocalAddr() net.Addr                { return nil }
func (c *dumpConn) RemoteAddr() net.Addr               { return nil }
func (c *dumpConn) SetDeadline(t time.Time) error      { return nil }
func (c *dumpConn) SetReadDeadline(t time.Time) error  { return nil }
func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }

// DumpRequestOut is like DumpRequest but includes
// headers that the standard http.Transport adds,
// such as User-Agent.
func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
	save := req.Body
	if !body || req.Body == nil {
		req.Body = nil
	} else {
		var err error
		save, req.Body, err = drainBody(req.Body)
		if err != nil {
			return nil, err
		}
	}

	// Use the actual Transport code to record what we would send
	// on the wire, but not using TCP.  Use a Transport with a
	// customer dialer that returns a fake net.Conn that waits
	// for the full input (and recording it), and then responds
	// with a dummy response.
	var buf bytes.Buffer // records the output
	pr, pw := io.Pipe()
	dr := &delegateReader{c: make(chan io.Reader)}
	// Wait for the request before replying with a dummy response:
	go func() {
		http.ReadRequest(bufio.NewReader(pr))
		dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\n\r\n")
	}()

	t := &http.Transport{
		Dial: func(net, addr string) (net.Conn, error) {
			return &dumpConn{io.MultiWriter(pw, &buf), dr}, nil
		},
	}

	_, err := t.RoundTrip(req)

	req.Body = save
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// delegateReader is a reader that delegates to another reader,
// once it arrives on a channel.
type delegateReader struct {
	c chan io.Reader
	r io.Reader // nil until received from c
}

func (r *delegateReader) Read(p []byte) (int, error) {
	if r.r == nil {
		r.r = <-r.c
	}
	return r.r.Read(p)
}

// Return value if nonempty, def otherwise.
func valueOrDefault(value, def string) string {
	if value != "" {
		return value
	}
	return def
}

var reqWriteExcludeHeaderDump = map[string]bool{
	"Host":              true, // not in Header map anyway
	"Content-Length":    true,
	"Transfer-Encoding": true,
	"Trailer":           true,
}

// dumpAsReceived writes req to w in the form as it was received, or
// at least as accurately as possible from the information retained in
// the request.
func dumpAsReceived(req *http.Request, w io.Writer) error {
	return nil
}

// DumpRequest returns the as-received wire representation of req,
// optionally including the request body, for debugging.
// DumpRequest is semantically a no-op, but in order to
// dump the body, it reads the body data into memory and
// changes req.Body to refer to the in-memory copy.
// The documentation for http.Request.Write details which fields
// of req are used.
func DumpRequest(req *http.Request, body bool) (dump []byte, err error) {
	save := req.Body
	if !body || req.Body == nil {
		req.Body = nil
	} else {
		save, req.Body, err = drainBody(req.Body)
		if err != nil {
			return
		}
	}

	var b bytes.Buffer

	fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
		req.URL.RequestURI(), req.ProtoMajor, req.ProtoMinor)

	host := req.Host
	if host == "" && req.URL != nil {
		host = req.URL.Host
	}
	if host != "" {
		fmt.Fprintf(&b, "Host: %s\r\n", host)
	}

	chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked"
	if len(req.TransferEncoding) > 0 {
		fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ","))
	}
	if req.Close {
		fmt.Fprintf(&b, "Connection: close\r\n")
	}

	err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
	if err != nil {
		return
	}

	io.WriteString(&b, "\r\n")

	if req.Body != nil {
		var dest io.Writer = &b
		if chunked {
			dest = NewChunkedWriter(dest)
		}
		_, err = io.Copy(dest, req.Body)
		if chunked {
			dest.(io.Closer).Close()
			io.WriteString(&b, "\r\n")
		}
	}

	req.Body = save
	if err != nil {
		return
	}
	dump = b.Bytes()
	return
}

// DumpResponse is like DumpRequest but dumps a response.
func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) {
	var b bytes.Buffer
	save := resp.Body
	savecl := resp.ContentLength
	if !body || resp.Body == nil {
		resp.Body = nil
		resp.ContentLength = 0
	} else {
		save, resp.Body, err = drainBody(resp.Body)
		if err != nil {
			return
		}
	}
	err = resp.Write(&b)
	resp.Body = save
	resp.ContentLength = savecl
	if err != nil {
		return
	}
	dump = b.Bytes()
	return
}
<|endoftext|>


<commit_before>// Copyright 2019 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package hugofs provides the file systems used by Hugo.
package hugofs

import (
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/gohugoio/hugo/hugofs/files"
	"golang.org/x/text/unicode/norm"

	"github.com/pkg/errors"

	"github.com/spf13/cast"

	"github.com/gohugoio/hugo/common/hreflect"

	"github.com/spf13/afero"
)

const (
	metaKeyFilename = "filename"

	metaKeySourceRoot                 = "sourceRoot"
	metaKeyBaseDir                    = "baseDir" // Abs base directory of source file.
	metaKeyMountRoot                  = "mountRoot"
	metaKeyModule                     = "module"
	metaKeyOriginalFilename           = "originalFilename"
	metaKeyName                       = "name"
	metaKeyPath                       = "path"
	metaKeyPathWalk                   = "pathWalk"
	metaKeyLang                       = "lang"
	metaKeyWeight                     = "weight"
	metaKeyOrdinal                    = "ordinal"
	metaKeyFs                         = "fs"
	metaKeyOpener                     = "opener"
	metaKeyIsOrdered                  = "isOrdered"
	metaKeyIsSymlink                  = "isSymlink"
	metaKeyJoinStat                   = "joinStat"
	metaKeySkipDir                    = "skipDir"
	metaKeyClassifier                 = "classifier"
	metaKeyTranslationBaseName        = "translationBaseName"
	metaKeyTranslationBaseNameWithExt = "translationBaseNameWithExt"
	metaKeyTranslations               = "translations"
	metaKeyDecoraterPath              = "decoratorPath"
)

type FileMeta map[string]interface{}

func (f FileMeta) GetInt(key string) int {
	return cast.ToInt(f[key])
}

func (f FileMeta) GetString(key string) string {
	return cast.ToString(f[key])
}

func (f FileMeta) GetBool(key string) bool {
	return cast.ToBool(f[key])
}

func (f FileMeta) Filename() string {
	return f.stringV(metaKeyFilename)
}

func (f FileMeta) OriginalFilename() string {
	return f.stringV(metaKeyOriginalFilename)
}

func (f FileMeta) SkipDir() bool {
	return f.GetBool(metaKeySkipDir)
}

func (f FileMeta) TranslationBaseName() string {
	return f.stringV(metaKeyTranslationBaseName)
}

func (f FileMeta) TranslationBaseNameWithExt() string {
	return f.stringV(metaKeyTranslationBaseNameWithExt)
}

func (f FileMeta) Translations() []string {
	return cast.ToStringSlice(f[metaKeyTranslations])
}

func (f FileMeta) Name() string {
	return f.stringV(metaKeyName)
}

func (f FileMeta) Classifier() files.ContentClass {
	c, found := f[metaKeyClassifier]
	if found {
		return c.(files.ContentClass)
	}

	return files.ContentClassFile // For sorting
}

func (f FileMeta) Lang() string {
	return f.stringV(metaKeyLang)
}

// Path returns the relative file path to where this file is mounted.
func (f FileMeta) Path() string {
	return f.stringV(metaKeyPath)
}

// PathFile returns the relative file path for the file source.
func (f FileMeta) PathFile() string {
	base := f.stringV(metaKeyBaseDir)
	if base == "" {
		return ""
	}
	return strings.TrimPrefix(strings.TrimPrefix(f.Filename(), base), filepathSeparator)
}

func (f FileMeta) SourceRoot() string {
	return f.stringV(metaKeySourceRoot)
}

func (f FileMeta) MountRoot() string {
	return f.stringV(metaKeyMountRoot)
}

func (f FileMeta) Module() string {
	return f.stringV(metaKeyModule)
}

func (f FileMeta) Weight() int {
	return f.GetInt(metaKeyWeight)
}

func (f FileMeta) Ordinal() int {
	return f.GetInt(metaKeyOrdinal)
}

func (f FileMeta) IsOrdered() bool {
	return f.GetBool(metaKeyIsOrdered)
}

// IsSymlink returns whether this comes from a symlinked file or directory.
func (f FileMeta) IsSymlink() bool {
	return f.GetBool(metaKeyIsSymlink)
}

func (f FileMeta) Watch() bool {
	if v, found := f["watch"]; found {
		return v.(bool)
	}
	return false
}

func (f FileMeta) Fs() afero.Fs {
	if v, found := f[metaKeyFs]; found {
		return v.(afero.Fs)
	}
	return nil
}

func (f FileMeta) GetOpener() func() (afero.File, error) {
	o, found := f[metaKeyOpener]
	if !found {
		return nil
	}
	return o.(func() (afero.File, error))
}

func (f FileMeta) Open() (afero.File, error) {
	v, found := f[metaKeyOpener]
	if !found {
		return nil, errors.New("file opener not found")
	}
	return v.(func() (afero.File, error))()
}

func (f FileMeta) JoinStat(name string) (FileMetaInfo, error) {
	v, found := f[metaKeyJoinStat]
	if !found {
		return nil, os.ErrNotExist
	}
	return v.(func(name string) (FileMetaInfo, error))(name)
}

func (f FileMeta) stringV(key string) string {
	if v, found := f[key]; found {
		return v.(string)
	}
	return ""
}

func (f FileMeta) setIfNotZero(key string, val interface{}) {
	if !hreflect.IsTruthful(val) {
		return
	}
	f[key] = val
}

type FileMetaInfo interface {
	os.FileInfo
	Meta() FileMeta
}

type fileInfoMeta struct {
	os.FileInfo

	m FileMeta
}

// Name returns the file's name. Note that we follow symlinks,
// if supported by the file system, and the Name given here will be the
// name of the symlink, which is what Hugo needs in all situations.
func (fi *fileInfoMeta) Name() string {
	if name := fi.m.Name(); name != "" {
		return name
	}
	return fi.FileInfo.Name()
}

func (fi *fileInfoMeta) Meta() FileMeta {
	return fi.m
}

func NewFileMetaInfo(fi os.FileInfo, m FileMeta) FileMetaInfo {
	if fim, ok := fi.(FileMetaInfo); ok {
		mergeFileMeta(fim.Meta(), m)
	}
	return &fileInfoMeta{FileInfo: fi, m: m}
}

func copyFileMeta(m FileMeta) FileMeta {
	c := make(FileMeta)
	for k, v := range m {
		c[k] = v
	}
	return c
}

// Merge metadata, last entry wins.
func mergeFileMeta(from, to FileMeta) {
	if from == nil {
		return
	}
	for k, v := range from {
		if _, found := to[k]; !found {
			to[k] = v
		}
	}
}

type dirNameOnlyFileInfo struct {
	name string
}

func (fi *dirNameOnlyFileInfo) Name() string {
	return fi.name
}

func (fi *dirNameOnlyFileInfo) Size() int64 {
	panic("not implemented")
}

func (fi *dirNameOnlyFileInfo) Mode() os.FileMode {
	return os.ModeDir
}

func (fi *dirNameOnlyFileInfo) ModTime() time.Time {
	return time.Time{}
}

func (fi *dirNameOnlyFileInfo) IsDir() bool {
	return true
}

func (fi *dirNameOnlyFileInfo) Sys() interface{} {
	return nil
}

func newDirNameOnlyFileInfo(name string, meta FileMeta, fileOpener func() (afero.File, error)) FileMetaInfo {
	name = normalizeFilename(name)
	_, base := filepath.Split(name)

	m := copyFileMeta(meta)
	if _, found := m[metaKeyFilename]; !found {
		m.setIfNotZero(metaKeyFilename, name)
	}
	m[metaKeyOpener] = fileOpener
	m[metaKeyIsOrdered] = false

	return NewFileMetaInfo(
		&dirNameOnlyFileInfo{name: base},
		m,
	)
}

func decorateFileInfo(
	fi os.FileInfo,
	fs afero.Fs, opener func() (afero.File, error),
	filename, filepath string, inMeta FileMeta) FileMetaInfo {
	var meta FileMeta
	var fim FileMetaInfo

	filepath = strings.TrimPrefix(filepath, filepathSeparator)

	var ok bool
	if fim, ok = fi.(FileMetaInfo); ok {
		meta = fim.Meta()
	} else {
		meta = make(FileMeta)
		fim = NewFileMetaInfo(fi, meta)
	}

	meta.setIfNotZero(metaKeyOpener, opener)
	meta.setIfNotZero(metaKeyFs, fs)
	meta.setIfNotZero(metaKeyPath, normalizeFilename(filepath))
	meta.setIfNotZero(metaKeyFilename, normalizeFilename(filename))

	mergeFileMeta(inMeta, meta)

	return fim
}

func isSymlink(fi os.FileInfo) bool {
	return fi != nil && fi.Mode()&os.ModeSymlink == os.ModeSymlink
}

func fileInfosToFileMetaInfos(fis []os.FileInfo) []FileMetaInfo {
	fims := make([]FileMetaInfo, len(fis))
	for i, v := range fis {
		fims[i] = v.(FileMetaInfo)
	}
	return fims
}

func normalizeFilename(filename string) string {
	if filename == "" {
		return ""
	}
	if runtime.GOOS == "darwin" {
		// When a file system is HFS+, its filepath is in NFD form.
		return norm.NFC.String(filename)
	}
	return filename
}

func fileInfosToNames(fis []os.FileInfo) []string {
	names := make([]string, len(fis))
	for i, d := range fis {
		names[i] = d.Name()
	}
	return names
}

func fromSlash(filenames []string) []string {
	for i, name := range filenames {
		filenames[i] = filepath.FromSlash(name)
	}
	return filenames
}

func sortFileInfos(fis []os.FileInfo) {
	sort.Slice(fis, func(i, j int) bool {
		fimi, fimj := fis[i].(FileMetaInfo), fis[j].(FileMetaInfo)
		return fimi.Meta().Filename() < fimj.Meta().Filename()
	})
}
<commit_msg>Fix invalid timestamp of the "public" folder<commit_after>// Copyright 2019 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package hugofs provides the file systems used by Hugo.
package hugofs

import (
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/gohugoio/hugo/hugofs/files"
	"golang.org/x/text/unicode/norm"

	"github.com/pkg/errors"

	"github.com/spf13/cast"

	"github.com/gohugoio/hugo/common/hreflect"

	"github.com/spf13/afero"
)

const (
	metaKeyFilename = "filename"

	metaKeySourceRoot                 = "sourceRoot"
	metaKeyBaseDir                    = "baseDir" // Abs base directory of source file.
	metaKeyMountRoot                  = "mountRoot"
	metaKeyModule                     = "module"
	metaKeyOriginalFilename           = "originalFilename"
	metaKeyName                       = "name"
	metaKeyPath                       = "path"
	metaKeyPathWalk                   = "pathWalk"
	metaKeyLang                       = "lang"
	metaKeyWeight                     = "weight"
	metaKeyOrdinal                    = "ordinal"
	metaKeyFs                         = "fs"
	metaKeyOpener                     = "opener"
	metaKeyIsOrdered                  = "isOrdered"
	metaKeyIsSymlink                  = "isSymlink"
	metaKeyJoinStat                   = "joinStat"
	metaKeySkipDir                    = "skipDir"
	metaKeyClassifier                 = "classifier"
	metaKeyTranslationBaseName        = "translationBaseName"
	metaKeyTranslationBaseNameWithExt = "translationBaseNameWithExt"
	metaKeyTranslations               = "translations"
	metaKeyDecoraterPath              = "decoratorPath"
)

type FileMeta map[string]interface{}

func (f FileMeta) GetInt(key string) int {
	return cast.ToInt(f[key])
}

func (f FileMeta) GetString(key string) string {
	return cast.ToString(f[key])
}

func (f FileMeta) GetBool(key string) bool {
	return cast.ToBool(f[key])
}

func (f FileMeta) Filename() string {
	return f.stringV(metaKeyFilename)
}

func (f FileMeta) OriginalFilename() string {
	return f.stringV(metaKeyOriginalFilename)
}

func (f FileMeta) SkipDir() bool {
	return f.GetBool(metaKeySkipDir)
}

func (f FileMeta) TranslationBaseName() string {
	return f.stringV(metaKeyTranslationBaseName)
}

func (f FileMeta) TranslationBaseNameWithExt() string {
	return f.stringV(metaKeyTranslationBaseNameWithExt)
}

func (f FileMeta) Translations() []string {
	return cast.ToStringSlice(f[metaKeyTranslations])
}

func (f FileMeta) Name() string {
	return f.stringV(metaKeyName)
}

func (f FileMeta) Classifier() files.ContentClass {
	c, found := f[metaKeyClassifier]
	if found {
		return c.(files.ContentClass)
	}

	return files.ContentClassFile // For sorting
}

func (f FileMeta) Lang() string {
	return f.stringV(metaKeyLang)
}

// Path returns the relative file path to where this file is mounted.
func (f FileMeta) Path() string {
	return f.stringV(metaKeyPath)
}

// PathFile returns the relative file path for the file source.
func (f FileMeta) PathFile() string {
	base := f.stringV(metaKeyBaseDir)
	if base == "" {
		return ""
	}
	return strings.TrimPrefix(strings.TrimPrefix(f.Filename(), base), filepathSeparator)
}

func (f FileMeta) SourceRoot() string {
	return f.stringV(metaKeySourceRoot)
}

func (f FileMeta) MountRoot() string {
	return f.stringV(metaKeyMountRoot)
}

func (f FileMeta) Module() string {
	return f.stringV(metaKeyModule)
}

func (f FileMeta) Weight() int {
	return f.GetInt(metaKeyWeight)
}

func (f FileMeta) Ordinal() int {
	return f.GetInt(metaKeyOrdinal)
}

func (f FileMeta) IsOrdered() bool {
	return f.GetBool(metaKeyIsOrdered)
}

// IsSymlink returns whether this comes from a symlinked file or directory.
func (f FileMeta) IsSymlink() bool {
	return f.GetBool(metaKeyIsSymlink)
}

func (f FileMeta) Watch() bool {
	if v, found := f["watch"]; found {
		return v.(bool)
	}
	return false
}

func (f FileMeta) Fs() afero.Fs {
	if v, found := f[metaKeyFs]; found {
		return v.(afero.Fs)
	}
	return nil
}

func (f FileMeta) GetOpener() func() (afero.File, error) {
	o, found := f[metaKeyOpener]
	if !found {
		return nil
	}
	return o.(func() (afero.File, error))
}

func (f FileMeta) Open() (afero.File, error) {
	v, found := f[metaKeyOpener]
	if !found {
		return nil, errors.New("file opener not found")
	}
	return v.(func() (afero.File, error))()
}

func (f FileMeta) JoinStat(name string) (FileMetaInfo, error) {
	v, found := f[metaKeyJoinStat]
	if !found {
		return nil, os.ErrNotExist
	}
	return v.(func(name string) (FileMetaInfo, error))(name)
}

func (f FileMeta) stringV(key string) string {
	if v, found := f[key]; found {
		return v.(string)
	}
	return ""
}

func (f FileMeta) setIfNotZero(key string, val interface{}) {
	if !hreflect.IsTruthful(val) {
		return
	}
	f[key] = val
}

type FileMetaInfo interface {
	os.FileInfo
	Meta() FileMeta
}

type fileInfoMeta struct {
	os.FileInfo

	m FileMeta
}

// Name returns the file's name. Note that we follow symlinks,
// if supported by the file system, and the Name given here will be the
// name of the symlink, which is what Hugo needs in all situations.
func (fi *fileInfoMeta) Name() string {
	if name := fi.m.Name(); name != "" {
		return name
	}
	return fi.FileInfo.Name()
}

func (fi *fileInfoMeta) Meta() FileMeta {
	return fi.m
}

func NewFileMetaInfo(fi os.FileInfo, m FileMeta) FileMetaInfo {
	if fim, ok := fi.(FileMetaInfo); ok {
		mergeFileMeta(fim.Meta(), m)
	}
	return &fileInfoMeta{FileInfo: fi, m: m}
}

func copyFileMeta(m FileMeta) FileMeta {
	c := make(FileMeta)
	for k, v := range m {
		c[k] = v
	}
	return c
}

// Merge metadata, last entry wins.
func mergeFileMeta(from, to FileMeta) {
	if from == nil {
		return
	}
	for k, v := range from {
		if _, found := to[k]; !found {
			to[k] = v
		}
	}
}

type dirNameOnlyFileInfo struct {
	name string
}

func (fi *dirNameOnlyFileInfo) Name() string {
	return fi.name
}

func (fi *dirNameOnlyFileInfo) Size() int64 {
	panic("not implemented")
}

func (fi *dirNameOnlyFileInfo) Mode() os.FileMode {
	return os.ModeDir
}

func (fi *dirNameOnlyFileInfo) ModTime() time.Time {
	return time.Now()
}

func (fi *dirNameOnlyFileInfo) IsDir() bool {
	return true
}

func (fi *dirNameOnlyFileInfo) Sys() interface{} {
	return nil
}

func newDirNameOnlyFileInfo(name string, meta FileMeta, fileOpener func() (afero.File, error)) FileMetaInfo {
	name = normalizeFilename(name)
	_, base := filepath.Split(name)

	m := copyFileMeta(meta)
	if _, found := m[metaKeyFilename]; !found {
		m.setIfNotZero(metaKeyFilename, name)
	}
	m[metaKeyOpener] = fileOpener
	m[metaKeyIsOrdered] = false

	return NewFileMetaInfo(
		&dirNameOnlyFileInfo{name: base},
		m,
	)
}

func decorateFileInfo(
	fi os.FileInfo,
	fs afero.Fs, opener func() (afero.File, error),
	filename, filepath string, inMeta FileMeta) FileMetaInfo {
	var meta FileMeta
	var fim FileMetaInfo

	filepath = strings.TrimPrefix(filepath, filepathSeparator)

	var ok bool
	if fim, ok = fi.(FileMetaInfo); ok {
		meta = fim.Meta()
	} else {
		meta = make(FileMeta)
		fim = NewFileMetaInfo(fi, meta)
	}

	meta.setIfNotZero(metaKeyOpener, opener)
	meta.setIfNotZero(metaKeyFs, fs)
	meta.setIfNotZero(metaKeyPath, normalizeFilename(filepath))
	meta.setIfNotZero(metaKeyFilename, normalizeFilename(filename))

	mergeFileMeta(inMeta, meta)

	return fim
}

func isSymlink(fi os.FileInfo) bool {
	return fi != nil && fi.Mode()&os.ModeSymlink == os.ModeSymlink
}

func fileInfosToFileMetaInfos(fis []os.FileInfo) []FileMetaInfo {
	fims := make([]FileMetaInfo, len(fis))
	for i, v := range fis {
		fims[i] = v.(FileMetaInfo)
	}
	return fims
}

func normalizeFilename(filename string) string {
	if filename == "" {
		return ""
	}
	if runtime.GOOS == "darwin" {
		// When a file system is HFS+, its filepath is in NFD form.
		return norm.NFC.String(filename)
	}
	return filename
}

func fileInfosToNames(fis []os.FileInfo) []string {
	names := make([]string, len(fis))
	for i, d := range fis {
		names[i] = d.Name()
	}
	return names
}

func fromSlash(filenames []string) []string {
	for i, name := range filenames {
		filenames[i] = filepath.FromSlash(name)
	}
	return filenames
}

func sortFileInfos(fis []os.FileInfo) {
	sort.Slice(fis, func(i, j int) bool {
		fimi, fimj := fis[i].(FileMetaInfo), fis[j].(FileMetaInfo)
		return fimi.Meta().Filename() < fimj.Meta().Filename()
	})
}
<|endoftext|>


<commit_before>package hystrix

import (
	"sync"
	"time"

	"github.com/afex/hystrix-go/hystrix/metric_collector"
	"github.com/afex/hystrix-go/hystrix/rolling"
)

type commandExecution struct {
	Type        string        `json:"type"`
	Start       time.Time     `json:"start_time"`
	RunDuration time.Duration `json:"run_duration"`
}

type metricExchange struct {
	Name    string
	Updates chan *commandExecution
	Mutex   *sync.RWMutex

	metricCollectors []metricCollector.MetricCollector
}

func newMetricExchange(name string) *metricExchange {
	m := &metricExchange{}
	m.Name = name

	m.Updates = make(chan *commandExecution)
	m.Mutex = &sync.RWMutex{}
	m.metricCollectors = metricCollector.Registry.InitializeMetricCollectors(name)
	m.Reset()

	go m.Monitor()

	return m
}

// The Default Collector function will panic if collectors are not setup to specification.
func (m *metricExchange) DefaultCollector() *metricCollector.DefaultMetricCollector {
	if len(m.metricCollectors) < 1 {
		panic("No Metric Collectors Registered.")
	}
	collection, ok := m.metricCollectors[0].(*metricCollector.DefaultMetricCollector)
	if !ok {
		panic("Default metric collector is not registered correctly. The default metric collector must be registered first.")
	}
	return collection
}

func (m *metricExchange) Monitor() {
	for update := range m.Updates {
		// we only grab a read lock to make sure Reset() isn't changing the numbers.
		m.Mutex.RLock()

		totalDuration := time.Now().Sub(update.Start)
		for _, collector := range m.metricCollectors {
			collector.IncrementAttempts()
			if update.Type != "success" {
				collector.IncrementErrors()
			}

			// granular metrics
			if update.Type == "success" {
				collector.IncrementSuccesses()
			}
			if update.Type == "failure" {
				collector.IncrementFailures()
			}
			if update.Type == "rejected" {
				collector.IncrementRejects()
			}
			if update.Type == "short-circuit" {
				collector.IncrementShortCircuits()
			}
			if update.Type == "timeout" {
				collector.IncrementTimeouts()
			}

			// fallback metrics
			if update.Type == "fallback-success" {
				collector.IncrementFallbackSuccesses()
			}
			if update.Type == "fallback-failure" {
				collector.IncrementFallbackFailures()
			}

			collector.UpdateTotalDuration(totalDuration)
			collector.UpdateRunDuration(update.RunDuration)
		}

		m.Mutex.RUnlock()
	}
}

func (m *metricExchange) Reset() {
	m.Mutex.Lock()
	defer m.Mutex.Unlock()

	for _, collector := range m.metricCollectors {
		collector.Reset()
	}
}

func (m *metricExchange) Requests() *rolling.Number {
	m.Mutex.RLock()
	defer m.Mutex.RUnlock()

	return m.DefaultCollector().NumRequests
}

func (m *metricExchange) ErrorPercent(now time.Time) int {
	m.Mutex.RLock()
	defer m.Mutex.RUnlock()

	var errPct float64
	reqs := m.Requests().Sum(now)
	errs := m.DefaultCollector().Errors.Sum(now)

	if reqs > 0 {
		errPct = (float64(errs) / float64(reqs)) * 100
	}

	return int(errPct + 0.5)
}

func (m *metricExchange) IsHealthy(now time.Time) bool {
	return m.ErrorPercent(now) < getSettings(m.Name).ErrorPercentThreshold
}
<commit_msg>Fix deadlock in ErrorPercent method where mutex is RLocked twice, and in between another goroutine Locks on same mutex.<commit_after>package hystrix

import (
	"sync"
	"time"

	"github.com/afex/hystrix-go/hystrix/metric_collector"
	"github.com/afex/hystrix-go/hystrix/rolling"
)

type commandExecution struct {
	Type        string        `json:"type"`
	Start       time.Time     `json:"start_time"`
	RunDuration time.Duration `json:"run_duration"`
}

type metricExchange struct {
	Name    string
	Updates chan *commandExecution
	Mutex   *sync.RWMutex

	metricCollectors []metricCollector.MetricCollector
}

func newMetricExchange(name string) *metricExchange {
	m := &metricExchange{}
	m.Name = name

	m.Updates = make(chan *commandExecution)
	m.Mutex = &sync.RWMutex{}
	m.metricCollectors = metricCollector.Registry.InitializeMetricCollectors(name)
	m.Reset()

	go m.Monitor()

	return m
}

// The Default Collector function will panic if collectors are not setup to specification.
func (m *metricExchange) DefaultCollector() *metricCollector.DefaultMetricCollector {
	if len(m.metricCollectors) < 1 {
		panic("No Metric Collectors Registered.")
	}
	collection, ok := m.metricCollectors[0].(*metricCollector.DefaultMetricCollector)
	if !ok {
		panic("Default metric collector is not registered correctly. The default metric collector must be registered first.")
	}
	return collection
}

func (m *metricExchange) Monitor() {
	for update := range m.Updates {
		// we only grab a read lock to make sure Reset() isn't changing the numbers.
		m.Mutex.RLock()

		totalDuration := time.Now().Sub(update.Start)
		for _, collector := range m.metricCollectors {
			collector.IncrementAttempts()
			if update.Type != "success" {
				collector.IncrementErrors()
			}

			// granular metrics
			if update.Type == "success" {
				collector.IncrementSuccesses()
			}
			if update.Type == "failure" {
				collector.IncrementFailures()
			}
			if update.Type == "rejected" {
				collector.IncrementRejects()
			}
			if update.Type == "short-circuit" {
				collector.IncrementShortCircuits()
			}
			if update.Type == "timeout" {
				collector.IncrementTimeouts()
			}

			// fallback metrics
			if update.Type == "fallback-success" {
				collector.IncrementFallbackSuccesses()
			}
			if update.Type == "fallback-failure" {
				collector.IncrementFallbackFailures()
			}

			collector.UpdateTotalDuration(totalDuration)
			collector.UpdateRunDuration(update.RunDuration)
		}

		m.Mutex.RUnlock()
	}
}

func (m *metricExchange) Reset() {
	m.Mutex.Lock()
	defer m.Mutex.Unlock()

	for _, collector := range m.metricCollectors {
		collector.Reset()
	}
}

func (m *metricExchange) Requests() *rolling.Number {
	m.Mutex.RLock()
	defer m.Mutex.RUnlock()

	return m.DefaultCollector().NumRequests
}

func (m *metricExchange) ErrorPercent(now time.Time) int {
	m.Mutex.RLock()
	defer m.Mutex.RUnlock()

	var errPct float64
	reqs := m.DefaultCollector().NumRequests.Sum(now)
	errs := m.DefaultCollector().Errors.Sum(now)

	if reqs > 0 {
		errPct = (float64(errs) / float64(reqs)) * 100
	}

	return int(errPct + 0.5)
}

func (m *metricExchange) IsHealthy(now time.Time) bool {
	return m.ErrorPercent(now) < getSettings(m.Name).ErrorPercentThreshold
}
<|endoftext|>


<commit_before>package srpc

import (
	"bufio"
	"crypto/tls"
	"errors"
	"io"
	"net"
	"net/http"
	"time"
)

func dialHTTP(network, address string, tlsConfig *tls.Config,
	timeout time.Duration) (*Client, error) {
	unsecuredConn, err := net.DialTimeout(network, address, timeout)
	if err != nil {
		return nil, err
	}
	path := rpcPath
	if tlsConfig != nil {
		path = tlsRpcPath
	}
	io.WriteString(unsecuredConn, "CONNECT "+path+" HTTP/1.0\n\n")
	// Require successful HTTP response before switching to SRPC protocol.
	resp, err := http.ReadResponse(bufio.NewReader(unsecuredConn),
		&http.Request{Method: "CONNECT"})
	if err != nil {
		return nil, err
	}
	if resp.Status != connectString {
		return nil, errors.New("unexpected HTTP response: " + resp.Status)
	}
	if tlsConfig == nil {
		return newClient(unsecuredConn), nil
	}
	tlsConn := tls.Client(unsecuredConn, tlsConfig)
	if err := tlsConn.Handshake(); err != nil {
		return nil, err
	}
	return newClient(tlsConn), nil
}

func newClient(conn net.Conn) *Client {
	return &Client{
		conn: conn,
		bufrw: bufio.NewReadWriter(bufio.NewReader(conn),
			bufio.NewWriter(conn))}
}

func (client *Client) call(serviceMethod string) (*Conn, error) {
	client.callLock.Lock()
	conn, err := client.callWithLock(serviceMethod)
	if err != nil {
		client.callLock.Unlock()
	}
	return conn, err
}

func (client *Client) callWithLock(serviceMethod string) (*Conn, error) {
	_, err := client.bufrw.WriteString(serviceMethod + "\n")
	if err != nil {
		return nil, err
	}
	if err = client.bufrw.Flush(); err != nil {
		return nil, err
	}
	resp, err := client.bufrw.ReadString('\n')
	if err != nil {
		return nil, err
	}
	if resp != "\n" {
		return nil, errors.New(resp[:len(resp)-1])
	}
	conn := new(Conn)
	conn.parent = client
	conn.ReadWriter = client.bufrw
	return conn, nil
}

func (client *Client) close() error {
	client.bufrw.Flush()
	return client.conn.Close()
}

func (client *Client) ping() error {
	conn, err := client.call("\n")
	if err != nil {
		return err
	}
	conn.Close()
	return nil
}
<commit_msg>Change srpc.dialHTTP() to fallback to insecure connection if appropriate.<commit_after>package srpc

import (
	"bufio"
	"crypto/tls"
	"errors"
	"io"
	"net"
	"net/http"
	"time"
)

func dialHTTP(network, address string, tlsConfig *tls.Config,
	timeout time.Duration) (*Client, error) {
	unsecuredConn, err := net.DialTimeout(network, address, timeout)
	if err != nil {
		return nil, err
	}
	path := rpcPath
	if tlsConfig != nil {
		path = tlsRpcPath
	}
	io.WriteString(unsecuredConn, "CONNECT "+path+" HTTP/1.0\n\n")
	// Require successful HTTP response before switching to SRPC protocol.
	resp, err := http.ReadResponse(bufio.NewReader(unsecuredConn),
		&http.Request{Method: "CONNECT"})
	if err != nil {
		return nil, err
	}
	if resp.StatusCode == http.StatusNotFound &&
		tlsConfig != nil &&
		tlsConfig.InsecureSkipVerify {
		// Fall back to insecure connection.
		return dialHTTP(network, address, nil, timeout)
	}
	if resp.Status != connectString {
		return nil, errors.New("unexpected HTTP response: " + resp.Status)
	}
	if tlsConfig == nil {
		return newClient(unsecuredConn), nil
	}
	tlsConn := tls.Client(unsecuredConn, tlsConfig)
	if err := tlsConn.Handshake(); err != nil {
		return nil, err
	}
	return newClient(tlsConn), nil
}

func newClient(conn net.Conn) *Client {
	return &Client{
		conn: conn,
		bufrw: bufio.NewReadWriter(bufio.NewReader(conn),
			bufio.NewWriter(conn))}
}

func (client *Client) call(serviceMethod string) (*Conn, error) {
	client.callLock.Lock()
	conn, err := client.callWithLock(serviceMethod)
	if err != nil {
		client.callLock.Unlock()
	}
	return conn, err
}

func (client *Client) callWithLock(serviceMethod string) (*Conn, error) {
	_, err := client.bufrw.WriteString(serviceMethod + "\n")
	if err != nil {
		return nil, err
	}
	if err = client.bufrw.Flush(); err != nil {
		return nil, err
	}
	resp, err := client.bufrw.ReadString('\n')
	if err != nil {
		return nil, err
	}
	if resp != "\n" {
		return nil, errors.New(resp[:len(resp)-1])
	}
	conn := new(Conn)
	conn.parent = client
	conn.ReadWriter = client.bufrw
	return conn, nil
}

func (client *Client) close() error {
	client.bufrw.Flush()
	return client.conn.Close()
}

func (client *Client) ping() error {
	conn, err := client.call("\n")
	if err != nil {
		return err
	}
	conn.Close()
	return nil
}
<|endoftext|>


<commit_before>package standard

import (
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net"
	"net/http"
	"strings"

	"github.com/labstack/echo"
	"github.com/labstack/echo/engine"
	"github.com/labstack/echo/log"
)

type (
	// Request implements `engine.Request`.
	Request struct {
		*http.Request
		header engine.Header
		url    engine.URL
		logger log.Logger
	}
)

const (
	defaultMemory = 32 << 20 // 32 MB
)

// NewRequest returns `Request` instance.
func NewRequest(r *http.Request, l log.Logger) *Request {
	return &Request{
		Request: r,
		url:     &URL{URL: r.URL},
		header:  &Header{Header: r.Header},
		logger:  l,
	}
}

// IsTLS implements `engine.Request#TLS` function.
func (r *Request) IsTLS() bool {
	return r.Request.TLS != nil
}

// Scheme implements `engine.Request#Scheme` function.
func (r *Request) Scheme() string {
	// Can't use `r.Request.URL.Scheme`
	// See: https://groups.google.com/forum/#!topic/golang-nuts/pMUkBlQBDF0
	if r.IsTLS() {
		return "https"
	}
	return "http"
}

// Host implements `engine.Request#Host` function.
func (r *Request) Host() string {
	return r.Request.Host
}

// URL implements `engine.Request#URL` function.
func (r *Request) URL() engine.URL {
	return r.url
}

// Header implements `engine.Request#URL` function.
func (r *Request) Header() engine.Header {
	return r.header
}

// Referer implements `engine.Request#Referer` function.
func (r *Request) Referer() string {
	return r.Request.Referer()
}

// func Proto() string {
// 	return r.request.Proto()
// }
//
// func ProtoMajor() int {
// 	return r.request.ProtoMajor()
// }
//
// func ProtoMinor() int {
// 	return r.request.ProtoMinor()
// }

// ContentLength implements `engine.Request#ContentLength` function.
func (r *Request) ContentLength() int64 {
	return r.Request.ContentLength
}

// UserAgent implements `engine.Request#UserAgent` function.
func (r *Request) UserAgent() string {
	return r.Request.UserAgent()
}

// RemoteAddress implements `engine.Request#RemoteAddress` function.
func (r *Request) RemoteAddress() string {
	return r.RemoteAddr
}

// RealIP implements `engine.Request#RealIP` function.
func (r *Request) RealIP() string {
	ra := r.RemoteAddress()
	if ip := r.Header().Get(echo.HeaderXForwardedFor); ip != "" {
		ra = ip
	} else if ip := r.Header().Get(echo.HeaderXRealIP); ip != "" {
		ra = ip
	} else {
		ra, _, _ = net.SplitHostPort(ra)
	}
	return ra
}

// Method implements `engine.Request#Method` function.
func (r *Request) Method() string {
	return r.Request.Method
}

// SetMethod implements `engine.Request#SetMethod` function.
func (r *Request) SetMethod(method string) {
	r.Request.Method = method
}

// URI implements `engine.Request#URI` function.
func (r *Request) URI() string {
	return r.RequestURI
}

// SetURI implements `engine.Request#SetURI` function.
func (r *Request) SetURI(uri string) {
	r.RequestURI = uri
}

// Body implements `engine.Request#Body` function.
func (r *Request) Body() io.Reader {
	return r.Request.Body
}

// SetBody implements `engine.Request#SetBody` function.
func (r *Request) SetBody(reader io.Reader) {
	r.Request.Body = ioutil.NopCloser(reader)
}

// FormValue implements `engine.Request#FormValue` function.
func (r *Request) FormValue(name string) string {
	return r.Request.FormValue(name)
}

// FormParams implements `engine.Request#FormParams` function.
func (r *Request) FormParams() map[string][]string {
	if strings.HasPrefix(r.header.Get(echo.HeaderContentType), echo.MIMEMultipartForm) {
		if err := r.ParseMultipartForm(defaultMemory); err != nil {
			panic(fmt.Sprintf("echo: %v", err))
		}
	} else {
		if err := r.ParseForm(); err != nil {
			panic(fmt.Sprintf("echo: %v", err))
		}
	}
	return map[string][]string(r.Request.Form)
}

// FormFile implements `engine.Request#FormFile` function.
func (r *Request) FormFile(name string) (*multipart.FileHeader, error) {
	_, fh, err := r.Request.FormFile(name)
	return fh, err
}

// MultipartForm implements `engine.Request#MultipartForm` function.
func (r *Request) MultipartForm() (*multipart.Form, error) {
	err := r.ParseMultipartForm(defaultMemory)
	return r.Request.MultipartForm, err
}

// Cookie implements `engine.Request#Cookie` function.
func (r *Request) Cookie(name string) (engine.Cookie, error) {
	c, err := r.Request.Cookie(name)
	if err != nil {
		return nil, echo.ErrCookieNotFound
	}
	return &Cookie{c}, nil
}

// Cookies implements `engine.Request#Cookies` function.
func (r *Request) Cookies() []engine.Cookie {
	cs := r.Request.Cookies()
	cookies := make([]engine.Cookie, len(cs))
	for i, c := range cs {
		cookies[i] = &Cookie{c}
	}
	return cookies
}

func (r *Request) reset(req *http.Request, h engine.Header, u engine.URL) {
	r.Request = req
	r.header = h
	r.url = u
}
<commit_msg>fix godoc comment (#645)<commit_after>package standard

import (
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net"
	"net/http"
	"strings"

	"github.com/labstack/echo"
	"github.com/labstack/echo/engine"
	"github.com/labstack/echo/log"
)

type (
	// Request implements `engine.Request`.
	Request struct {
		*http.Request
		header engine.Header
		url    engine.URL
		logger log.Logger
	}
)

const (
	defaultMemory = 32 << 20 // 32 MB
)

// NewRequest returns `Request` instance.
func NewRequest(r *http.Request, l log.Logger) *Request {
	return &Request{
		Request: r,
		url:     &URL{URL: r.URL},
		header:  &Header{Header: r.Header},
		logger:  l,
	}
}

// IsTLS implements `engine.Request#TLS` function.
func (r *Request) IsTLS() bool {
	return r.Request.TLS != nil
}

// Scheme implements `engine.Request#Scheme` function.
func (r *Request) Scheme() string {
	// Can't use `r.Request.URL.Scheme`
	// See: https://groups.google.com/forum/#!topic/golang-nuts/pMUkBlQBDF0
	if r.IsTLS() {
		return "https"
	}
	return "http"
}

// Host implements `engine.Request#Host` function.
func (r *Request) Host() string {
	return r.Request.Host
}

// URL implements `engine.Request#URL` function.
func (r *Request) URL() engine.URL {
	return r.url
}

// Header implements `engine.Request#Header` function.
func (r *Request) Header() engine.Header {
	return r.header
}

// Referer implements `engine.Request#Referer` function.
func (r *Request) Referer() string {
	return r.Request.Referer()
}

// func Proto() string {
// 	return r.request.Proto()
// }
//
// func ProtoMajor() int {
// 	return r.request.ProtoMajor()
// }
//
// func ProtoMinor() int {
// 	return r.request.ProtoMinor()
// }

// ContentLength implements `engine.Request#ContentLength` function.
func (r *Request) ContentLength() int64 {
	return r.Request.ContentLength
}

// UserAgent implements `engine.Request#UserAgent` function.
func (r *Request) UserAgent() string {
	return r.Request.UserAgent()
}

// RemoteAddress implements `engine.Request#RemoteAddress` function.
func (r *Request) RemoteAddress() string {
	return r.RemoteAddr
}

// RealIP implements `engine.Request#RealIP` function.
func (r *Request) RealIP() string {
	ra := r.RemoteAddress()
	if ip := r.Header().Get(echo.HeaderXForwardedFor); ip != "" {
		ra = ip
	} else if ip := r.Header().Get(echo.HeaderXRealIP); ip != "" {
		ra = ip
	} else {
		ra, _, _ = net.SplitHostPort(ra)
	}
	return ra
}

// Method implements `engine.Request#Method` function.
func (r *Request) Method() string {
	return r.Request.Method
}

// SetMethod implements `engine.Request#SetMethod` function.
func (r *Request) SetMethod(method string) {
	r.Request.Method = method
}

// URI implements `engine.Request#URI` function.
func (r *Request) URI() string {
	return r.RequestURI
}

// SetURI implements `engine.Request#SetURI` function.
func (r *Request) SetURI(uri string) {
	r.RequestURI = uri
}

// Body implements `engine.Request#Body` function.
func (r *Request) Body() io.Reader {
	return r.Request.Body
}

// SetBody implements `engine.Request#SetBody` function.
func (r *Request) SetBody(reader io.Reader) {
	r.Request.Body = ioutil.NopCloser(reader)
}

// FormValue implements `engine.Request#FormValue` function.
func (r *Request) FormValue(name string) string {
	return r.Request.FormValue(name)
}

// FormParams implements `engine.Request#FormParams` function.
func (r *Request) FormParams() map[string][]string {
	if strings.HasPrefix(r.header.Get(echo.HeaderContentType), echo.MIMEMultipartForm) {
		if err := r.ParseMultipartForm(defaultMemory); err != nil {
			panic(fmt.Sprintf("echo: %v", err))
		}
	} else {
		if err := r.ParseForm(); err != nil {
			panic(fmt.Sprintf("echo: %v", err))
		}
	}
	return map[string][]string(r.Request.Form)
}

// FormFile implements `engine.Request#FormFile` function.
func (r *Request) FormFile(name string) (*multipart.FileHeader, error) {
	_, fh, err := r.Request.FormFile(name)
	return fh, err
}

// MultipartForm implements `engine.Request#MultipartForm` function.
func (r *Request) MultipartForm() (*multipart.Form, error) {
	err := r.ParseMultipartForm(defaultMemory)
	return r.Request.MultipartForm, err
}

// Cookie implements `engine.Request#Cookie` function.
func (r *Request) Cookie(name string) (engine.Cookie, error) {
	c, err := r.Request.Cookie(name)
	if err != nil {
		return nil, echo.ErrCookieNotFound
	}
	return &Cookie{c}, nil
}

// Cookies implements `engine.Request#Cookies` function.
func (r *Request) Cookies() []engine.Cookie {
	cs := r.Request.Cookies()
	cookies := make([]engine.Cookie, len(cs))
	for i, c := range cs {
		cookies[i] = &Cookie{c}
	}
	return cookies
}

func (r *Request) reset(req *http.Request, h engine.Header, u engine.URL) {
	r.Request = req
	r.header = h
	r.url = u
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Ștefan Talpalaru <stefantalpalaru@yahoo.com>
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:

// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package main

import (
	// "fmt"
	"reflect"
	"runtime"
	"time"
	"unsafe"
)

// keep the channels around to avoid garbage collection while they are used in C
var channels = map[chan *int]bool{}

func Golib_init() {
}

func Chan_make(size int) chan *int {
	c := make(chan *int, size)
	channels[c] = true
	return c
}

func Chan_send(c chan *int, v *int) {
	c <- v
}

func Chan_recv(c chan *int) (v *int) {
	v = <-c
	return
}

func Chan_recv2(c chan *int) (v *int, ok bool) {
	v, ok = <-c
	return
}

func Chan_close(c chan *int) {
	close(c)
}

func Chan_dispose(c chan *int) {
	delete(channels, c)
}

type Chan_select_case struct {
	Dir  int
	Chan chan *int
	Send *int
}

func Chan_select(cases *Chan_select_case, num_cases int) (chosen int, recv *int, recv_ok bool) {
	select_cases := make([]reflect.SelectCase, num_cases)
	cases2 := (*[1 << 30]Chan_select_case)(unsafe.Pointer(cases))
	for i := 0; i < num_cases; i++ {
		dir := reflect.SelectDir(cases2[i].Dir)
		// somehow the Value of a typed nil is not the zero Value
		var c reflect.Value
		if cases2[i].Chan == (chan *int)(nil) {
			c = reflect.ValueOf(nil)
		} else {
			c = reflect.ValueOf(cases2[i].Chan)
		}
		var send reflect.Value
		if cases2[i].Send == (*int)(nil) {
			send = reflect.ValueOf(nil)
		} else {
			send = reflect.ValueOf(cases2[i].Send)
		}
		select_cases[i] = reflect.SelectCase{dir, c, send}
	}
	var recv_val reflect.Value
	chosen, recv_val, recv_ok = reflect.Select(select_cases)
	if recv_val.IsValid() {
		recv = recv_val.Interface().(*int)
	} else {
		recv = (*int)(nil)
	}
	return
}

func Sleep_ms(n int64) {
	time.Sleep((time.Duration)(n) * time.Millisecond)
}

func Set_finalizer(obj []byte, finalizer func([]byte)) {
	runtime.SetFinalizer(obj, finalizer)
}
<commit_msg>try a struct<commit_after>// Copyright (c) 2015, Ștefan Talpalaru <stefantalpalaru@yahoo.com>
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:

// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package main

import (
	// "fmt"
	"reflect"
	"runtime"
	"time"
	"unsafe"
)

// keep the channels around to avoid garbage collection while they are used in C
var channels = map[chan *int]bool{}

func Golib_init() {
}

func Chan_make(size int) chan *int {
	c := make(chan *int, size)
	channels[c] = true
	return c
}

func Chan_send(c chan *int, v *int) {
	c <- v
}

func Chan_recv(c chan *int) (v *int) {
	v = <-c
	return
}

func Chan_recv2(c chan *int) (v *int, ok bool) {
	v, ok = <-c
	return
}

func Chan_close(c chan *int) {
	close(c)
}

func Chan_dispose(c chan *int) {
	delete(channels, c)
}

type Chan_select_case struct {
	Dir  int
	Chan chan *int
	Send *int
}

func Chan_select(cases *Chan_select_case, num_cases int) (chosen int, recv *int, recv_ok bool) {
	select_cases := make([]reflect.SelectCase, num_cases)
	cases2 := (*[1 << 30]Chan_select_case)(unsafe.Pointer(cases))
	for i := 0; i < num_cases; i++ {
		dir := reflect.SelectDir(cases2[i].Dir)
		// somehow the Value of a typed nil is not the zero Value
		var c reflect.Value
		if cases2[i].Chan == (chan *int)(nil) {
			c = reflect.ValueOf(nil)
		} else {
			c = reflect.ValueOf(cases2[i].Chan)
		}
		var send reflect.Value
		if cases2[i].Send == (*int)(nil) {
			send = reflect.ValueOf(nil)
		} else {
			send = reflect.ValueOf(cases2[i].Send)
		}
		select_cases[i] = reflect.SelectCase{dir, c, send}
	}
	var recv_val reflect.Value
	chosen, recv_val, recv_ok = reflect.Select(select_cases)
	if recv_val.IsValid() {
		recv = recv_val.Interface().(*int)
	} else {
		recv = (*int)(nil)
	}
	return
}

func Sleep_ms(n int64) {
	time.Sleep((time.Duration)(n) * time.Millisecond)
}

type bogus struct {
	s string
}

func Set_finalizer(obj *bogus, finalizer func(*bogus)) {
	runtime.SetFinalizer(obj, finalizer)
}
<|endoftext|>


<commit_before>package jujutest

import (
	"bytes"
	"io"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/trivial"
	"net/http"
)

// Tests is a gocheck suite containing tests verifying juju functionality
// against the environment with the given configuration. The
// tests are not designed to be run against a live server - the Environ
// is opened once for each test, and some potentially expensive operations
// may be executed.
type Tests struct {
	coretesting.LoggingSuite
	Config map[string]interface{}
	Env    environs.Environ
}

// Open opens an instance of the testing environment.
func (t *Tests) Open(c *C) environs.Environ {
	e, err := environs.NewFromAttrs(t.Config)
	c.Assert(err, IsNil, Commentf("opening environ %#v", t.Config))
	c.Assert(e, NotNil)
	return e
}

func (t *Tests) SetUpTest(c *C) {
	t.LoggingSuite.SetUpTest(c)
	t.Env = t.Open(c)
}

func (t *Tests) TearDownTest(c *C) {
	if t.Env != nil {
		err := t.Env.Destroy(nil)
		c.Check(err, IsNil)
		t.Env = nil
	}
	t.LoggingSuite.TearDownTest(c)
}

func (t *Tests) TestBootstrapWithoutAdminSecret(c *C) {
	m := t.Env.Config().AllAttrs()
	delete(m, "admin-secret")
	env, err := environs.NewFromAttrs(m)
	c.Assert(err, IsNil)
	err = environs.Bootstrap(env, false, panicWrite)
	c.Assert(err, ErrorMatches, ".*admin-secret is required for bootstrap")
}

func (t *Tests) TestProviderAssignmentPolicy(c *C) {
	e := t.Open(c)
	policy := e.AssignmentPolicy()
	c.Assert(policy, FitsTypeOf, state.AssignUnused)
}

func (t *Tests) TestStartStop(c *C) {
	e := t.Open(c)

	insts, err := e.Instances(nil)
	c.Assert(err, IsNil)
	c.Assert(insts, HasLen, 0)

	inst0, err := e.StartInstance("0", testing.InvalidStateInfo("0"), testing.InvalidAPIInfo("0"), nil)
	c.Assert(err, IsNil)
	c.Assert(inst0, NotNil)
	id0 := inst0.Id()

	inst1, err := e.StartInstance("1", testing.InvalidStateInfo("1"), testing.InvalidAPIInfo("1"), nil)
	c.Assert(err, IsNil)
	c.Assert(inst1, NotNil)
	id1 := inst1.Id()

	insts, err = e.Instances([]state.InstanceId{id0, id1})
	c.Assert(err, IsNil)
	c.Assert(insts, HasLen, 2)
	c.Assert(insts[0].Id(), Equals, id0)
	c.Assert(insts[1].Id(), Equals, id1)

	// order of results is not specified
	insts, err = e.AllInstances()
	c.Assert(err, IsNil)
	c.Assert(insts, HasLen, 2)
	c.Assert(insts[0].Id(), Not(Equals), insts[1].Id())

	err = e.StopInstances([]environs.Instance{inst0})
	c.Assert(err, IsNil)

	insts, err = e.Instances([]state.InstanceId{id0, id1})
	c.Assert(err, Equals, environs.ErrPartialInstances)
	c.Assert(insts[0], IsNil)
	c.Assert(insts[1].Id(), Equals, id1)

	insts, err = e.AllInstances()
	c.Assert(err, IsNil)
	c.Assert(insts[0].Id(), Equals, id1)
}

func (t *Tests) TestBootstrap(c *C) {
	// TODO tests for Bootstrap(true)
	e := t.Open(c)
	err := environs.Bootstrap(e, false, panicWrite)
	c.Assert(err, IsNil)

	info, apiInfo, err := e.StateInfo()
	c.Check(info.Addrs, Not(HasLen), 0)
	c.Check(apiInfo.Addrs, Not(HasLen), 0)

	err = environs.Bootstrap(e, false, panicWrite)
	c.Assert(err, ErrorMatches, "environment is already bootstrapped")

	e2 := t.Open(c)
	err = environs.Bootstrap(e2, false, panicWrite)
	c.Assert(err, ErrorMatches, "environment is already bootstrapped")

	info2, apiInfo2, err := e2.StateInfo()
	c.Check(info2, DeepEquals, info)
	c.Check(apiInfo2, DeepEquals, apiInfo)

	err = e2.Destroy(nil)
	c.Assert(err, IsNil)

	// Open again because Destroy invalidates old environments.
	e3 := t.Open(c)

	err = environs.Bootstrap(e3, false, panicWrite)
	c.Assert(err, IsNil)

	err = environs.Bootstrap(e3, false, panicWrite)
	c.Assert(err, NotNil)
}

var noRetry = trivial.AttemptStrategy{}

func (t *Tests) TestPersistence(c *C) {
	storage := t.Open(c).Storage()

	names := []string{
		"aa",
		"zzz/aa",
		"zzz/bb",
	}
	for _, name := range names {
		checkFileDoesNotExist(c, storage, name, noRetry)
		checkPutFile(c, storage, name, []byte(name))
	}
	checkList(c, storage, "", names)
	checkList(c, storage, "a", []string{"aa"})
	checkList(c, storage, "zzz/", []string{"zzz/aa", "zzz/bb"})

	storage2 := t.Open(c).Storage()
	for _, name := range names {
		checkFileHasContents(c, storage2, name, []byte(name), noRetry)
	}

	// remove the first file and check that the others remain.
	err := storage2.Remove(names[0])
	c.Check(err, IsNil)

	// check that it's ok to remove a file twice.
	err = storage2.Remove(names[0])
	c.Check(err, IsNil)

	// ... and check it's been removed in the other environment
	checkFileDoesNotExist(c, storage, names[0], noRetry)

	// ... and that the rest of the files are still around
	checkList(c, storage2, "", names[1:])

	for _, name := range names[1:] {
		err := storage2.Remove(name)
		c.Assert(err, IsNil)
	}

	// check they've all gone
	checkList(c, storage2, "", nil)
}

func checkList(c *C, storage environs.StorageReader, prefix string, names []string) {
	lnames, err := storage.List(prefix)
	c.Assert(err, IsNil)
	c.Assert(lnames, DeepEquals, names)
}

func checkPutFile(c *C, storage environs.StorageWriter, name string, contents []byte) {
	err := storage.Put(name, bytes.NewBuffer(contents), int64(len(contents)))
	c.Assert(err, IsNil)
}

func checkFileDoesNotExist(c *C, storage environs.StorageReader, name string, attempt trivial.AttemptStrategy) {
	var r io.ReadCloser
	var err error
	for a := attempt.Start(); a.Next(); {
		r, err = storage.Get(name)
		if err != nil {
			break
		}
	}
	c.Assert(r, IsNil)
	var notFoundError *environs.NotFoundError
	c.Assert(err, FitsTypeOf, notFoundError)
}

func checkFileHasContents(c *C, storage environs.StorageReader, name string, contents []byte, attempt trivial.AttemptStrategy) {
	r, err := storage.Get(name)
	c.Assert(err, IsNil)
	c.Check(r, NotNil)
	defer r.Close()

	data, err := ioutil.ReadAll(r)
	c.Check(err, IsNil)
	c.Check(data, DeepEquals, contents)

	url, err := storage.URL(name)
	c.Assert(err, IsNil)

	var resp *http.Response
	for a := attempt.Start(); a.Next(); {
		resp, err = http.Get(url)
		c.Assert(err, IsNil)
		if resp.StatusCode != 404 {
			break
		}
		c.Logf("get retrying after earlier get succeeded. *sigh*.")
	}
	c.Assert(err, IsNil)
	data, err = ioutil.ReadAll(resp.Body)
	c.Assert(err, IsNil)
	defer resp.Body.Close()
	c.Assert(resp.StatusCode, Equals, 200, Commentf("error response: %s", data))
	c.Check(data, DeepEquals, contents)
}
<commit_msg>environs/jujutest: always sort during checkList<commit_after>package jujutest

import (
	"bytes"
	"io"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/trivial"
	"net/http"
	"sort"
)

// Tests is a gocheck suite containing tests verifying juju functionality
// against the environment with the given configuration. The
// tests are not designed to be run against a live server - the Environ
// is opened once for each test, and some potentially expensive operations
// may be executed.
type Tests struct {
	coretesting.LoggingSuite
	Config map[string]interface{}
	Env    environs.Environ
}

// Open opens an instance of the testing environment.
func (t *Tests) Open(c *C) environs.Environ {
	e, err := environs.NewFromAttrs(t.Config)
	c.Assert(err, IsNil, Commentf("opening environ %#v", t.Config))
	c.Assert(e, NotNil)
	return e
}

func (t *Tests) SetUpTest(c *C) {
	t.LoggingSuite.SetUpTest(c)
	t.Env = t.Open(c)
}

func (t *Tests) TearDownTest(c *C) {
	if t.Env != nil {
		err := t.Env.Destroy(nil)
		c.Check(err, IsNil)
		t.Env = nil
	}
	t.LoggingSuite.TearDownTest(c)
}

func (t *Tests) TestBootstrapWithoutAdminSecret(c *C) {
	m := t.Env.Config().AllAttrs()
	delete(m, "admin-secret")
	env, err := environs.NewFromAttrs(m)
	c.Assert(err, IsNil)
	err = environs.Bootstrap(env, false, panicWrite)
	c.Assert(err, ErrorMatches, ".*admin-secret is required for bootstrap")
}

func (t *Tests) TestProviderAssignmentPolicy(c *C) {
	e := t.Open(c)
	policy := e.AssignmentPolicy()
	c.Assert(policy, FitsTypeOf, state.AssignUnused)
}

func (t *Tests) TestStartStop(c *C) {
	e := t.Open(c)

	insts, err := e.Instances(nil)
	c.Assert(err, IsNil)
	c.Assert(insts, HasLen, 0)

	inst0, err := e.StartInstance("0", testing.InvalidStateInfo("0"), testing.InvalidAPIInfo("0"), nil)
	c.Assert(err, IsNil)
	c.Assert(inst0, NotNil)
	id0 := inst0.Id()

	inst1, err := e.StartInstance("1", testing.InvalidStateInfo("1"), testing.InvalidAPIInfo("1"), nil)
	c.Assert(err, IsNil)
	c.Assert(inst1, NotNil)
	id1 := inst1.Id()

	insts, err = e.Instances([]state.InstanceId{id0, id1})
	c.Assert(err, IsNil)
	c.Assert(insts, HasLen, 2)
	c.Assert(insts[0].Id(), Equals, id0)
	c.Assert(insts[1].Id(), Equals, id1)

	// order of results is not specified
	insts, err = e.AllInstances()
	c.Assert(err, IsNil)
	c.Assert(insts, HasLen, 2)
	c.Assert(insts[0].Id(), Not(Equals), insts[1].Id())

	err = e.StopInstances([]environs.Instance{inst0})
	c.Assert(err, IsNil)

	insts, err = e.Instances([]state.InstanceId{id0, id1})
	c.Assert(err, Equals, environs.ErrPartialInstances)
	c.Assert(insts[0], IsNil)
	c.Assert(insts[1].Id(), Equals, id1)

	insts, err = e.AllInstances()
	c.Assert(err, IsNil)
	c.Assert(insts[0].Id(), Equals, id1)
}

func (t *Tests) TestBootstrap(c *C) {
	// TODO tests for Bootstrap(true)
	e := t.Open(c)
	err := environs.Bootstrap(e, false, panicWrite)
	c.Assert(err, IsNil)

	info, apiInfo, err := e.StateInfo()
	c.Check(info.Addrs, Not(HasLen), 0)
	c.Check(apiInfo.Addrs, Not(HasLen), 0)

	err = environs.Bootstrap(e, false, panicWrite)
	c.Assert(err, ErrorMatches, "environment is already bootstrapped")

	e2 := t.Open(c)
	err = environs.Bootstrap(e2, false, panicWrite)
	c.Assert(err, ErrorMatches, "environment is already bootstrapped")

	info2, apiInfo2, err := e2.StateInfo()
	c.Check(info2, DeepEquals, info)
	c.Check(apiInfo2, DeepEquals, apiInfo)

	err = e2.Destroy(nil)
	c.Assert(err, IsNil)

	// Open again because Destroy invalidates old environments.
	e3 := t.Open(c)

	err = environs.Bootstrap(e3, false, panicWrite)
	c.Assert(err, IsNil)

	err = environs.Bootstrap(e3, false, panicWrite)
	c.Assert(err, NotNil)
}

var noRetry = trivial.AttemptStrategy{}

func (t *Tests) TestPersistence(c *C) {
	storage := t.Open(c).Storage()

	names := []string{
		"aa",
		"zzz/aa",
		"zzz/bb",
	}
	for _, name := range names {
		checkFileDoesNotExist(c, storage, name, noRetry)
		checkPutFile(c, storage, name, []byte(name))
	}
	checkList(c, storage, "", names)
	checkList(c, storage, "a", []string{"aa"})
	checkList(c, storage, "zzz/", []string{"zzz/aa", "zzz/bb"})

	storage2 := t.Open(c).Storage()
	for _, name := range names {
		checkFileHasContents(c, storage2, name, []byte(name), noRetry)
	}

	// remove the first file and check that the others remain.
	err := storage2.Remove(names[0])
	c.Check(err, IsNil)

	// check that it's ok to remove a file twice.
	err = storage2.Remove(names[0])
	c.Check(err, IsNil)

	// ... and check it's been removed in the other environment
	checkFileDoesNotExist(c, storage, names[0], noRetry)

	// ... and that the rest of the files are still around
	checkList(c, storage2, "", names[1:])

	for _, name := range names[1:] {
		err := storage2.Remove(name)
		c.Assert(err, IsNil)
	}

	// check they've all gone
	checkList(c, storage2, "", nil)
}

func checkList(c *C, storage environs.StorageReader, prefix string, names []string) {
	lnames, err := storage.List(prefix)
	c.Assert(err, IsNil)
	// TODO(dfc) gocheck should grow an SliceEquals checker.
	expected := copyslice(lnames)
	sort.Strings(expected)
	actual := copyslice(names)
	sort.Strings(actual)
	c.Assert(expected, DeepEquals, actual)
}

// copyslice returns a copy of the slice
func copyslice(s []string) []string {
	r := make([]string, len(s))
	copy(r, s)
	return r
}

func checkPutFile(c *C, storage environs.StorageWriter, name string, contents []byte) {
	err := storage.Put(name, bytes.NewBuffer(contents), int64(len(contents)))
	c.Assert(err, IsNil)
}

func checkFileDoesNotExist(c *C, storage environs.StorageReader, name string, attempt trivial.AttemptStrategy) {
	var r io.ReadCloser
	var err error
	for a := attempt.Start(); a.Next(); {
		r, err = storage.Get(name)
		if err != nil {
			break
		}
	}
	c.Assert(r, IsNil)
	var notFoundError *environs.NotFoundError
	c.Assert(err, FitsTypeOf, notFoundError)
}

func checkFileHasContents(c *C, storage environs.StorageReader, name string, contents []byte, attempt trivial.AttemptStrategy) {
	r, err := storage.Get(name)
	c.Assert(err, IsNil)
	c.Check(r, NotNil)
	defer r.Close()

	data, err := ioutil.ReadAll(r)
	c.Check(err, IsNil)
	c.Check(data, DeepEquals, contents)

	url, err := storage.URL(name)
	c.Assert(err, IsNil)

	var resp *http.Response
	for a := attempt.Start(); a.Next(); {
		resp, err = http.Get(url)
		c.Assert(err, IsNil)
		if resp.StatusCode != 404 {
			break
		}
		c.Logf("get retrying after earlier get succeeded. *sigh*.")
	}
	c.Assert(err, IsNil)
	data, err = ioutil.ReadAll(resp.Body)
	c.Assert(err, IsNil)
	defer resp.Body.Close()
	c.Assert(resp.StatusCode, Equals, 200, Commentf("error response: %s", data))
	c.Check(data, DeepEquals, contents)
}
<|endoftext|>


<commit_before>package veneur

import (
	"net"
	"testing"

	"github.com/stretchr/testify/assert"
)

func writeReadUDP(t *testing.T, sock net.PacketConn, addr string) {
	client, err := net.Dial("udp", addr)
	assert.NoError(t, err, "should have connected to socket")
	_, err = client.Write([]byte("hello world"))
	assert.NoError(t, err, "should have written to socket")

	b := make([]byte, 15)
	n, laddr, err := sock.ReadFrom(b)
	assert.NoError(t, err, "should have read from socket")
	assert.Equal(t, n, 11, "should have read 11 bytes from socket")
	assert.Equal(t, "hello world", string(b[:n]), "should have gotten message from socket")
	assert.Equal(t, client.LocalAddr().String(), laddr.String(), "should have gotten message from client's address")
	err = client.Close()
	assert.NoError(t, err, "client.Close should not fail")
}

func TestSocket(t *testing.T) {
	const portString = "8200"
	const v4Localhost = "127.0.0.1:" + portString
	const v6Localhost = "[::1]:" + portString

	tests := []struct {
		addr         string
		supportsIPv4 bool
		supportsIPv6 bool
	}{
		{v4Localhost, true, false},
		{v6Localhost, false, true},
		{":" + portString, true, true},
	}

	for _, test := range tests {
		addr, err := net.ResolveUDPAddr("udp", test.addr)
		assert.NoError(t, err, "should have resolved udp address %s correctly", test.addr)

		sock, err := NewSocket(addr, 2*1024*1024, false)
		assert.NoError(t, err, "should have constructed socket correctly")

		if test.supportsIPv4 {
			writeReadUDP(t, sock, v4Localhost)
		}
		if test.supportsIPv6 {
			writeReadUDP(t, sock, v6Localhost)
		}

		err = sock.Close()
		assert.NoError(t, err, "close should not fail")
	}
}
<commit_msg>TravisCI doesn't support IPv6? Checking ...<commit_after>package veneur

import (
	"net"
	"testing"

	"github.com/stretchr/testify/assert"
)

func writeReadUDP(t *testing.T, sock net.PacketConn, addr string) {
	client, err := net.Dial("udp", addr)
	assert.NoError(t, err, "should have connected to socket")
	_, err = client.Write([]byte("hello world"))
	assert.NoError(t, err, "should have written to socket")

	b := make([]byte, 15)
	n, laddr, err := sock.ReadFrom(b)
	assert.NoError(t, err, "should have read from socket")
	assert.Equal(t, n, 11, "should have read 11 bytes from socket")
	assert.Equal(t, "hello world", string(b[:n]), "should have gotten message from socket")
	assert.Equal(t, client.LocalAddr().String(), laddr.String(), "should have gotten message from client's address")
	err = client.Close()
	assert.NoError(t, err, "client.Close should not fail")
}

func TestSocket(t *testing.T) {
	const portString = "8200"
	const v4Localhost = "127.0.0.1:" + portString
	const v6Localhost = "[::1]:" + portString

	// see if the system supports ipv6 by listening to a port
	systemSupportsV6 := true
	conn, err := net.ListenPacket("udp", "[::1]:0")
	if err != nil {
		t.Error("IPv6 not supported?", err)
		systemSupportsV6 = false
	}
	conn.Close()

	tests := []struct {
		addr         string
		supportsIPv4 bool
		supportsIPv6 bool
	}{
		{v4Localhost, true, false},
		{v6Localhost, false, true},
		{":" + portString, true, true},
	}

	for _, test := range tests {
		if test.addr == v6Localhost && !systemSupportsV6 {
			t.Error("skipping v6Localhost because the system does not support it")
			continue
		}

		addr, err := net.ResolveUDPAddr("udp", test.addr)
		assert.NoError(t, err, "should have resolved udp address %s correctly", test.addr)

		sock, err := NewSocket(addr, 2*1024*1024, false)
		assert.NoError(t, err, "should have constructed socket correctly")

		if test.supportsIPv4 {
			writeReadUDP(t, sock, v4Localhost)
		}
		if test.supportsIPv6 && systemSupportsV6 {
			writeReadUDP(t, sock, v6Localhost)
		}

		err = sock.Close()
		assert.NoError(t, err, "close should not fail")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// 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 watchmanager

import (
	"context"
	"fmt"
	"time"

	"github.com/m3db/m3x/log"

	"github.com/coreos/etcd/clientv3"
	"github.com/uber-go/tally"
)

// NewWatchManager creates a new watch manager
func NewWatchManager(opts Options) (WatchManager, error) {
	if err := opts.Validate(); err != nil {
		return nil, err
	}

	scope := opts.InstrumentsOptions().MetricsScope()
	return &manager{
		opts:   opts,
		logger: opts.InstrumentsOptions().Logger(),
		m: metrics{
			etcdWatchCreate: scope.Counter("etcd-watch-create"),
			etcdWatchError:  scope.Counter("etcd-watch-error"),
			etcdWatchReset:  scope.Counter("etcd-watch-reset"),
		},
		updateFn:      opts.UpdateFn(),
		tickAndStopFn: opts.TickAndStopFn(),
	}, nil
}

type manager struct {
	opts   Options
	logger xlog.Logger
	m      metrics

	updateFn      UpdateFn
	tickAndStopFn TickAndStopFn
}

type metrics struct {
	etcdWatchCreate tally.Counter
	etcdWatchError  tally.Counter
	etcdWatchReset  tally.Counter
}

func (w *manager) watchChanWithTimeout(key string) (clientv3.WatchChan, error) {
	doneCh := make(chan struct{})

	var watchChan clientv3.WatchChan
	go func() {
		watchChan = w.opts.Watcher().Watch(
			clientv3.WithRequireLeader(context.Background()),
			key,
			w.opts.WatchOptions()...,
		)
		close(doneCh)
	}()

	timeout := w.opts.WatchChanInitTimeout()
	select {
	case <-doneCh:
		return watchChan, nil
	case <-time.After(timeout):
		return nil, fmt.Errorf("etcd watch create timed out after %s for key: %s", timeout.String(), key)
	}
}

func (w *manager) Watch(key string) {
	ticker := time.Tick(w.opts.WatchChanCheckInterval())

	var (
		watchChan clientv3.WatchChan
		err       error
	)
	for {
		if watchChan == nil {
			w.m.etcdWatchCreate.Inc(1)
			watchChan, err = w.watchChanWithTimeout(key)
			if err != nil {
				w.logger.Errorf("could not create etcd watch: %v", err)

				// NB(cw) when we failed to create a etcd watch channel
				// we do a get for now and will try to recreate the watch chan later
				if err = w.updateFn(key); err != nil {
					w.logger.Errorf("failed to get value for key %s: %v", key, err)
				}
				// avoid recreating watch channel too frequently
				time.Sleep(w.opts.WatchChanResetInterval())
				continue
			}
		}

		select {
		case r, ok := <-watchChan:
			if !ok {
				// the watch chan is closed, set it to nil so it will be recreated
				// this is unlikely to happen but just to be defensive
				watchChan = nil
				w.logger.Warnf("etcd watch channel closed on key %s, recreating a watch channel", key)

				// avoid recreating watch channel too frequently
				time.Sleep(w.opts.WatchChanResetInterval())
				w.m.etcdWatchReset.Inc(1)

				continue
			}

			// handle the update
			if err = r.Err(); err != nil {
				w.logger.Errorf("received error on watch channel: %v", err)
				w.m.etcdWatchError.Inc(1)
				// do not stop here, even though the update contains an error
				// we still take this chance to attemp a Get() for the latest value
			}

			if err = w.updateFn(key); err != nil {
				w.logger.Errorf("received notification for key %s, but failed to get value: %v", key, err)
			}
		case <-ticker:
			if w.tickAndStopFn(key) {
				w.logger.Infof("watch on key %s ended", key)
				return
			}
		}
	}
}
<commit_msg>Cancel context in etcd.Watch() call when there is a timeout (#98)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// 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 watchmanager

import (
	"context"
	"fmt"
	"time"

	"github.com/m3db/m3x/log"

	"github.com/coreos/etcd/clientv3"
	"github.com/uber-go/tally"
)

// NewWatchManager creates a new watch manager
func NewWatchManager(opts Options) (WatchManager, error) {
	if err := opts.Validate(); err != nil {
		return nil, err
	}

	scope := opts.InstrumentsOptions().MetricsScope()
	return &manager{
		opts:   opts,
		logger: opts.InstrumentsOptions().Logger(),
		m: metrics{
			etcdWatchCreate: scope.Counter("etcd-watch-create"),
			etcdWatchError:  scope.Counter("etcd-watch-error"),
			etcdWatchReset:  scope.Counter("etcd-watch-reset"),
		},
		updateFn:      opts.UpdateFn(),
		tickAndStopFn: opts.TickAndStopFn(),
	}, nil
}

type manager struct {
	opts   Options
	logger xlog.Logger
	m      metrics

	updateFn      UpdateFn
	tickAndStopFn TickAndStopFn
}

type metrics struct {
	etcdWatchCreate tally.Counter
	etcdWatchError  tally.Counter
	etcdWatchReset  tally.Counter
}

func (w *manager) watchChanWithTimeout(key string) (clientv3.WatchChan, error) {
	doneCh := make(chan struct{})

	ctx, cancelFn := context.WithCancel(clientv3.WithRequireLeader(context.Background()))

	var watchChan clientv3.WatchChan
	go func() {
		watchChan = w.opts.Watcher().Watch(
			ctx,
			key,
			w.opts.WatchOptions()...,
		)
		close(doneCh)
	}()

	timeout := w.opts.WatchChanInitTimeout()
	select {
	case <-doneCh:
		return watchChan, nil
	case <-time.After(timeout):
		cancelFn()
		return nil, fmt.Errorf("etcd watch create timed out after %s for key: %s", timeout.String(), key)
	}
}

func (w *manager) Watch(key string) {
	ticker := time.Tick(w.opts.WatchChanCheckInterval())

	var (
		watchChan clientv3.WatchChan
		err       error
	)
	for {
		if watchChan == nil {
			w.m.etcdWatchCreate.Inc(1)
			watchChan, err = w.watchChanWithTimeout(key)
			if err != nil {
				w.logger.Errorf("could not create etcd watch: %v", err)

				// NB(cw) when we failed to create a etcd watch channel
				// we do a get for now and will try to recreate the watch chan later
				if err = w.updateFn(key); err != nil {
					w.logger.Errorf("failed to get value for key %s: %v", key, err)
				}
				// avoid recreating watch channel too frequently
				time.Sleep(w.opts.WatchChanResetInterval())
				continue
			}
		}

		select {
		case r, ok := <-watchChan:
			if !ok {
				// the watch chan is closed, set it to nil so it will be recreated
				// this is unlikely to happen but just to be defensive
				watchChan = nil
				w.logger.Warnf("etcd watch channel closed on key %s, recreating a watch channel", key)

				// avoid recreating watch channel too frequently
				time.Sleep(w.opts.WatchChanResetInterval())
				w.m.etcdWatchReset.Inc(1)

				continue
			}

			// handle the update
			if err = r.Err(); err != nil {
				w.logger.Errorf("received error on watch channel: %v", err)
				w.m.etcdWatchError.Inc(1)
				// do not stop here, even though the update contains an error
				// we still take this chance to attemp a Get() for the latest value
			}

			if err = w.updateFn(key); err != nil {
				w.logger.Errorf("received notification for key %s, but failed to get value: %v", key, err)
			}
		case <-ticker:
			if w.tickAndStopFn(key) {
				w.logger.Infof("watch on key %s ended", key)
				return
			}
		}
	}
}
<|endoftext|>