<commit_before>// Package eval handles evaluation of parsed Elvish code and provides runtime
// facilities.
package eval

import (
	"fmt"
	"io"
	"os"
	"strconv"

	"github.com/elves/elvish/pkg/daemon"
	"github.com/elves/elvish/pkg/eval/mods/bundled"
	"github.com/elves/elvish/pkg/eval/vals"
	"github.com/elves/elvish/pkg/eval/vars"
	"github.com/elves/elvish/pkg/logutil"
	"github.com/elves/elvish/pkg/parse"
	"github.com/xiaq/persistent/vector"
)

var logger = logutil.GetLogger("[eval] ")

const (
	// FnSuffix is the suffix for the variable names of functions. Defining a
	// function "foo" is equivalent to setting a variable named "foo~", and vice
	// versa.
	FnSuffix = "~"
	// NsSuffix is the suffix for the variable names of namespaces. Defining a
	// namespace foo is equivalent to setting a variable named "foo:", and vice
	// versa.
	NsSuffix = ":"
)

const (
	defaultValuePrefix        = "▶ "
	defaultNotifyBgJobSuccess = true
	initIndent                = vals.NoPretty
)

// Evaler is used to evaluate elvish sources. It maintains runtime context
// shared among all evalCtx instances.
type Evaler struct {
	evalerScopes

	state state

	// Chdir hooks.
	beforeChdir []func(string)
	afterChdir  []func(string)

	// State of the module system.
	libDir  string
	bundled map[string]string
	// Internal modules are indexed by use specs. External modules are indexed by
	// absolute paths.
	modules map[string]*Ns

	deprecations deprecationRegistry

	// Dependencies.
	//
	// TODO: Remove these dependency by providing more general extension points.
	DaemonClient daemon.Client
	Editor       Editor
}

// EvalCfg keeps configuration for the (*Evaler).Eval method.
type EvalCfg struct {
	// Ports used in evaluation. Must contain at least 3 elements; the Eval
	// method panics otherwise.
	Ports []*Port
	// Callback to get a channel of interrupt signals and a function to call
	// when the channel is no longer needed.
	Interrupt func() (<-chan struct{}, func())
	// Whether the Eval method should try to put the Elvish in the foreground
	// after evaluating the Op.
	PutInFg bool
}

type evalerScopes struct {
	Global  *Ns
	Builtin *Ns
}

//elvdoc:var after-chdir
//
// A list of functions to run after changing directory. These functions are always
// called with directory to change it, which might be a relative path. The
// following example also shows `$before-chdir`:
//
// ```elvish-transcript
// ~> before-chdir = [[dir]{ echo "Going to change to "$dir", pwd is "$pwd }]
// ~> after-chdir = [[dir]{ echo "Changed to "$dir", pwd is "$pwd }]
// ~> cd /usr
// Going to change to /usr, pwd is /Users/xiaq
// Changed to /usr, pwd is /usr
// /usr> cd local
// Going to change to local, pwd is /usr
// Changed to local, pwd is /usr/local
// /usr/local>
// ```
//
// @cf before-chdir

//elvdoc:var before-chdir
//
// A list of functions to run before changing directory. These functions are always
// called with the new working directory.
//
// @cf after-chdir

//elvdoc:var num-bg-jobs
//
// Number of background jobs.

//elvdoc:var notify-bg-job-success
//
// Whether to notify success of background jobs, defaulting to `$true`.
//
// Failures of background jobs are always notified.

//elvdoc:var value-out-indicator
//
// A string put before value outputs (such as those of of `put`). Defaults to
// `'▶ '`. Example:
//
// ```elvish-transcript
// ~> put lorem ipsum
// ▶ lorem
// ▶ ipsum
// ~> value-out-indicator = 'val> '
// ~> put lorem ipsum
// val> lorem
// val> ipsum
// ```
//
// Note that you almost always want some trailing whitespace for readability.

// NewEvaler creates a new Evaler.
func NewEvaler() *Evaler {
	builtin := builtinNs.Ns()

	ev := &Evaler{
		state: state{
			valuePrefix:        defaultValuePrefix,
			notifyBgJobSuccess: defaultNotifyBgJobSuccess,
			numBgJobs:          0,
		},
		evalerScopes: evalerScopes{
			Global:  new(Ns),
			Builtin: builtin,
		},
		modules: map[string]*Ns{
			"builtin": builtin,
		},
		bundled: bundled.Get(),

		deprecations: newDeprecationRegistry(),
	}

	beforeChdirElvish, afterChdirElvish := vector.Empty, vector.Empty
	ev.beforeChdir = append(ev.beforeChdir,
		adaptChdirHook("before-chdir", ev, &beforeChdirElvish))
	ev.afterChdir = append(ev.afterChdir,
		adaptChdirHook("after-chdir", ev, &afterChdirElvish))

	moreBuiltinsBuilder := make(NsBuilder)
	moreBuiltinsBuilder["before-chdir"] = vars.FromPtr(&beforeChdirElvish)
	moreBuiltinsBuilder["after-chdir"] = vars.FromPtr(&afterChdirElvish)

	moreBuiltinsBuilder["value-out-indicator"] = vars.FromPtrWithMutex(
		&ev.state.valuePrefix, &ev.state.mutex)
	moreBuiltinsBuilder["notify-bg-job-success"] = vars.FromPtrWithMutex(
		&ev.state.notifyBgJobSuccess, &ev.state.mutex)
	moreBuiltinsBuilder["num-bg-jobs"] = vars.FromGet(func() interface{} {
		return strconv.Itoa(ev.state.getNumBgJobs())
	})
	moreBuiltinsBuilder["pwd"] = NewPwdVar(ev)

	moreBuiltins := moreBuiltinsBuilder.Ns()
	builtin.slots = append(builtin.slots, moreBuiltins.slots...)
	builtin.names = append(builtin.names, moreBuiltins.names...)

	return ev
}

func adaptChdirHook(name string, ev *Evaler, pfns *vector.Vector) func(string) {
	return func(path string) {
		ports, cleanup := portsFromFiles(
			[3]*os.File{os.Stdin, os.Stdout, os.Stderr}, ev.state.getValuePrefix())
		defer cleanup()
		for it := (*pfns).Iterator(); it.HasElem(); it.Next() {
			fn, ok := it.Elem().(Callable)
			if !ok {
				fmt.Fprintln(os.Stderr, name, "hook must be callable")
				continue
			}
			fm := NewTopFrame(ev, parse.Source{Name: "[hook " + name + "]"}, ports[:])
			err := fn.Call(fm, []interface{}{path}, NoOpts)
			if err != nil {
				// TODO: Stack trace
				fmt.Fprintln(os.Stderr, err)
			}
		}
	}
}

// Close releases resources allocated when creating this Evaler. Currently this
// does nothing and always returns a nil error.
func (ev *Evaler) Close() error {
	return nil
}

// AddBeforeChdir adds a function to run before changing directory.
func (ev *Evaler) AddBeforeChdir(f func(string)) {
	ev.beforeChdir = append(ev.beforeChdir, f)
}

// AddAfterChdir adds a function to run after changing directory.
func (ev *Evaler) AddAfterChdir(f func(string)) {
	ev.afterChdir = append(ev.afterChdir, f)
}

// InstallDaemonClient installs a daemon client to the Evaler.
func (ev *Evaler) InstallDaemonClient(client daemon.Client) {
	ev.DaemonClient = client
}

// InstallModule installs a module to the Evaler so that it can be used with
// "use $name" from script.
func (ev *Evaler) InstallModule(name string, mod *Ns) {
	ev.modules[name] = mod
}

// InstallBundled installs a bundled module to the Evaler.
func (ev *Evaler) InstallBundled(name, src string) {
	ev.bundled[name] = src
}

// SetArgs replaces the $args builtin variable with a vector built from the
// argument.
func (ev *Evaler) SetArgs(args []string) {
	v := vector.Empty
	for _, arg := range args {
		v = v.Cons(arg)
	}
	// TODO: Avoid creating the variable dynamically; instead, always create the
	// variable, and set its value here.
	ev.Builtin.slots = append(ev.Builtin.slots, vars.NewReadOnly(v))
	ev.Builtin.names = append(ev.Builtin.names, "args")
}

// SetLibDir sets the library directory, in which external modules are to be
// found.
func (ev *Evaler) SetLibDir(libDir string) {
	ev.libDir = libDir
}

// growPorts makes the size of ec.ports at least n, adding nil's if necessary.
func (fm *Frame) growPorts(n int) {
	if len(fm.ports) >= n {
		return
	}
	ports := fm.ports
	fm.ports = make([]*Port, n)
	copy(fm.ports, ports)
}

// Eval evaluates an Op with the specified configuration.
func (ev *Evaler) Eval(op Op, cfg EvalCfg) error {
	if cfg.Ports == nil {
		cfg.Ports = DevNullPorts[:]
	}
	fm := NewTopFrame(ev, op.Src, cfg.Ports)
	if cfg.Interrupt != nil {
		intCh, cleanup := cfg.Interrupt()
		defer cleanup()
		fm.intCh = intCh
	}
	if cfg.PutInFg {
		defer func() {
			err := putSelfInFg()
			if err != nil {
				fmt.Fprintln(cfg.Ports[2].File,
					"failed to put myself in foreground:", err)
			}
		}()
	}
	return op.Inner.exec(fm)
}

// ParseAndCompile parses and compiles a Source.
func (ev *Evaler) ParseAndCompile(src parse.Source, w io.Writer) (Op, error) {
	tree, err := parse.ParseWithDeprecation(src, w)
	if err != nil {
		return Op{}, err
	}
	return ev.Compile(tree, w)
}

// Compile compiles Elvish code in the global scope. If the error is not nil, it
// can be passed to GetCompilationError to retrieve more details.
func (ev *Evaler) Compile(tree parse.Tree, w io.Writer) (Op, error) {
	return ev.CompileWithGlobal(tree, ev.Global, w)
}

// CompileWithGlobal compiles Elvish code in an alternative global scope. If the
// error is not nil, it can be passed to GetCompilationError to retrieve more
// details.
//
// TODO(xiaq): To use the Op created, the caller must create a Frame and mutate
// its local scope manually. Consider restructuring the API to make that
// unnecessary.
func (ev *Evaler) CompileWithGlobal(tree parse.Tree, g *Ns, w io.Writer) (Op, error) {
	return compile(ev.Builtin.static(), g.static(), tree, w)
}
<commit_msg>pkg/eval: Remove unused (*Evaler).InstallBundled.<commit_after>// Package eval handles evaluation of parsed Elvish code and provides runtime
// facilities.
package eval

import (
	"fmt"
	"io"
	"os"
	"strconv"

	"github.com/elves/elvish/pkg/daemon"
	"github.com/elves/elvish/pkg/eval/mods/bundled"
	"github.com/elves/elvish/pkg/eval/vals"
	"github.com/elves/elvish/pkg/eval/vars"
	"github.com/elves/elvish/pkg/logutil"
	"github.com/elves/elvish/pkg/parse"
	"github.com/xiaq/persistent/vector"
)

var logger = logutil.GetLogger("[eval] ")

const (
	// FnSuffix is the suffix for the variable names of functions. Defining a
	// function "foo" is equivalent to setting a variable named "foo~", and vice
	// versa.
	FnSuffix = "~"
	// NsSuffix is the suffix for the variable names of namespaces. Defining a
	// namespace foo is equivalent to setting a variable named "foo:", and vice
	// versa.
	NsSuffix = ":"
)

const (
	defaultValuePrefix        = "▶ "
	defaultNotifyBgJobSuccess = true
	initIndent                = vals.NoPretty
)

// Evaler is used to evaluate elvish sources. It maintains runtime context
// shared among all evalCtx instances.
type Evaler struct {
	evalerScopes

	state state

	// Chdir hooks.
	beforeChdir []func(string)
	afterChdir  []func(string)

	// State of the module system.
	libDir  string
	bundled map[string]string
	// Internal modules are indexed by use specs. External modules are indexed by
	// absolute paths.
	modules map[string]*Ns

	deprecations deprecationRegistry

	// Dependencies.
	//
	// TODO: Remove these dependency by providing more general extension points.
	DaemonClient daemon.Client
	Editor       Editor
}

// EvalCfg keeps configuration for the (*Evaler).Eval method.
type EvalCfg struct {
	// Ports used in evaluation. Must contain at least 3 elements; the Eval
	// method panics otherwise.
	Ports []*Port
	// Callback to get a channel of interrupt signals and a function to call
	// when the channel is no longer needed.
	Interrupt func() (<-chan struct{}, func())
	// Whether the Eval method should try to put the Elvish in the foreground
	// after evaluating the Op.
	PutInFg bool
}

type evalerScopes struct {
	Global  *Ns
	Builtin *Ns
}

//elvdoc:var after-chdir
//
// A list of functions to run after changing directory. These functions are always
// called with directory to change it, which might be a relative path. The
// following example also shows `$before-chdir`:
//
// ```elvish-transcript
// ~> before-chdir = [[dir]{ echo "Going to change to "$dir", pwd is "$pwd }]
// ~> after-chdir = [[dir]{ echo "Changed to "$dir", pwd is "$pwd }]
// ~> cd /usr
// Going to change to /usr, pwd is /Users/xiaq
// Changed to /usr, pwd is /usr
// /usr> cd local
// Going to change to local, pwd is /usr
// Changed to local, pwd is /usr/local
// /usr/local>
// ```
//
// @cf before-chdir

//elvdoc:var before-chdir
//
// A list of functions to run before changing directory. These functions are always
// called with the new working directory.
//
// @cf after-chdir

//elvdoc:var num-bg-jobs
//
// Number of background jobs.

//elvdoc:var notify-bg-job-success
//
// Whether to notify success of background jobs, defaulting to `$true`.
//
// Failures of background jobs are always notified.

//elvdoc:var value-out-indicator
//
// A string put before value outputs (such as those of of `put`). Defaults to
// `'▶ '`. Example:
//
// ```elvish-transcript
// ~> put lorem ipsum
// ▶ lorem
// ▶ ipsum
// ~> value-out-indicator = 'val> '
// ~> put lorem ipsum
// val> lorem
// val> ipsum
// ```
//
// Note that you almost always want some trailing whitespace for readability.

// NewEvaler creates a new Evaler.
func NewEvaler() *Evaler {
	builtin := builtinNs.Ns()

	ev := &Evaler{
		state: state{
			valuePrefix:        defaultValuePrefix,
			notifyBgJobSuccess: defaultNotifyBgJobSuccess,
			numBgJobs:          0,
		},
		evalerScopes: evalerScopes{
			Global:  new(Ns),
			Builtin: builtin,
		},
		modules: map[string]*Ns{
			"builtin": builtin,
		},
		bundled: bundled.Get(),

		deprecations: newDeprecationRegistry(),
	}

	beforeChdirElvish, afterChdirElvish := vector.Empty, vector.Empty
	ev.beforeChdir = append(ev.beforeChdir,
		adaptChdirHook("before-chdir", ev, &beforeChdirElvish))
	ev.afterChdir = append(ev.afterChdir,
		adaptChdirHook("after-chdir", ev, &afterChdirElvish))

	moreBuiltinsBuilder := make(NsBuilder)
	moreBuiltinsBuilder["before-chdir"] = vars.FromPtr(&beforeChdirElvish)
	moreBuiltinsBuilder["after-chdir"] = vars.FromPtr(&afterChdirElvish)

	moreBuiltinsBuilder["value-out-indicator"] = vars.FromPtrWithMutex(
		&ev.state.valuePrefix, &ev.state.mutex)
	moreBuiltinsBuilder["notify-bg-job-success"] = vars.FromPtrWithMutex(
		&ev.state.notifyBgJobSuccess, &ev.state.mutex)
	moreBuiltinsBuilder["num-bg-jobs"] = vars.FromGet(func() interface{} {
		return strconv.Itoa(ev.state.getNumBgJobs())
	})
	moreBuiltinsBuilder["pwd"] = NewPwdVar(ev)

	moreBuiltins := moreBuiltinsBuilder.Ns()
	builtin.slots = append(builtin.slots, moreBuiltins.slots...)
	builtin.names = append(builtin.names, moreBuiltins.names...)

	return ev
}

func adaptChdirHook(name string, ev *Evaler, pfns *vector.Vector) func(string) {
	return func(path string) {
		ports, cleanup := portsFromFiles(
			[3]*os.File{os.Stdin, os.Stdout, os.Stderr}, ev.state.getValuePrefix())
		defer cleanup()
		for it := (*pfns).Iterator(); it.HasElem(); it.Next() {
			fn, ok := it.Elem().(Callable)
			if !ok {
				fmt.Fprintln(os.Stderr, name, "hook must be callable")
				continue
			}
			fm := NewTopFrame(ev, parse.Source{Name: "[hook " + name + "]"}, ports[:])
			err := fn.Call(fm, []interface{}{path}, NoOpts)
			if err != nil {
				// TODO: Stack trace
				fmt.Fprintln(os.Stderr, err)
			}
		}
	}
}

// Close releases resources allocated when creating this Evaler. Currently this
// does nothing and always returns a nil error.
func (ev *Evaler) Close() error {
	return nil
}

// AddBeforeChdir adds a function to run before changing directory.
func (ev *Evaler) AddBeforeChdir(f func(string)) {
	ev.beforeChdir = append(ev.beforeChdir, f)
}

// AddAfterChdir adds a function to run after changing directory.
func (ev *Evaler) AddAfterChdir(f func(string)) {
	ev.afterChdir = append(ev.afterChdir, f)
}

// InstallDaemonClient installs a daemon client to the Evaler.
func (ev *Evaler) InstallDaemonClient(client daemon.Client) {
	ev.DaemonClient = client
}

// InstallModule installs a module to the Evaler so that it can be used with
// "use $name" from script.
func (ev *Evaler) InstallModule(name string, mod *Ns) {
	ev.modules[name] = mod
}

// SetArgs replaces the $args builtin variable with a vector built from the
// argument.
func (ev *Evaler) SetArgs(args []string) {
	v := vector.Empty
	for _, arg := range args {
		v = v.Cons(arg)
	}
	// TODO: Avoid creating the variable dynamically; instead, always create the
	// variable, and set its value here.
	ev.Builtin.slots = append(ev.Builtin.slots, vars.NewReadOnly(v))
	ev.Builtin.names = append(ev.Builtin.names, "args")
}

// SetLibDir sets the library directory, in which external modules are to be
// found.
func (ev *Evaler) SetLibDir(libDir string) {
	ev.libDir = libDir
}

// growPorts makes the size of ec.ports at least n, adding nil's if necessary.
func (fm *Frame) growPorts(n int) {
	if len(fm.ports) >= n {
		return
	}
	ports := fm.ports
	fm.ports = make([]*Port, n)
	copy(fm.ports, ports)
}

// Eval evaluates an Op with the specified configuration.
func (ev *Evaler) Eval(op Op, cfg EvalCfg) error {
	if cfg.Ports == nil {
		cfg.Ports = DevNullPorts[:]
	}
	fm := NewTopFrame(ev, op.Src, cfg.Ports)
	if cfg.Interrupt != nil {
		intCh, cleanup := cfg.Interrupt()
		defer cleanup()
		fm.intCh = intCh
	}
	if cfg.PutInFg {
		defer func() {
			err := putSelfInFg()
			if err != nil {
				fmt.Fprintln(cfg.Ports[2].File,
					"failed to put myself in foreground:", err)
			}
		}()
	}
	return op.Inner.exec(fm)
}

// ParseAndCompile parses and compiles a Source.
func (ev *Evaler) ParseAndCompile(src parse.Source, w io.Writer) (Op, error) {
	tree, err := parse.ParseWithDeprecation(src, w)
	if err != nil {
		return Op{}, err
	}
	return ev.Compile(tree, w)
}

// Compile compiles Elvish code in the global scope. If the error is not nil, it
// can be passed to GetCompilationError to retrieve more details.
func (ev *Evaler) Compile(tree parse.Tree, w io.Writer) (Op, error) {
	return ev.CompileWithGlobal(tree, ev.Global, w)
}

// CompileWithGlobal compiles Elvish code in an alternative global scope. If the
// error is not nil, it can be passed to GetCompilationError to retrieve more
// details.
//
// TODO(xiaq): To use the Op created, the caller must create a Frame and mutate
// its local scope manually. Consider restructuring the API to make that
// unnecessary.
func (ev *Evaler) CompileWithGlobal(tree parse.Tree, g *Ns, w io.Writer) (Op, error) {
	return compile(ev.Builtin.static(), g.static(), tree, w)
}
<|endoftext|>


<commit_before>// Copyright 2020, 2021 Google LLC
//
// 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
//
//     https://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.

// Binary wrap is a test helper program for //elisp:binary_test, which see.
package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"reflect"

	"github.com/google/go-cmp/cmp"
	"github.com/phst/runfiles"
)

func main() {
	log.Println("Args:", os.Args)
	log.Println("Environment:", os.Environ())
	var manifestFile string
	flag.StringVar(&manifestFile, "manifest", "", "")
	flag.Parse()
	if manifestFile == "" {
		log.Fatal("--manifest is empty")
	}
	runfilesLib, err := runfiles.Path("phst_rules_elisp/elisp/runfiles/runfiles.elc")
	if err != nil {
		log.Fatal(err)
	}
	// The load path setup depends on whether we use manifest-based or
	// directory-based runfiles.
	var loadPathArgs []string
	if dir, err := runfiles.Path("phst_rules_elisp"); err == nil {
		// Directory-based runfiles.
		loadPathArgs = []string{"--directory=" + dir}
	} else {
		// Manifest-based runfiles.
		loadPathArgs = []string{
			"--load=" + runfilesLib,
			"--funcall=elisp/runfiles/install-handler",
			"--directory=/bazel-runfile:phst_rules_elisp",
		}
	}
	gotArgs := flag.Args()
	wantArgs := append(
		append([]string{"--quick", "--batch"}, loadPathArgs...),
		"--option",
		"elisp/binary.cc",
		" \t\n\r\f äα𝐴🐈'\\\"",
		"/:/tmp/output.dat",
	)
	if diff := cmp.Diff(gotArgs, wantArgs); diff != "" {
		log.Fatalf("positional arguments: -got +want:\n%s", diff)
	}
	jsonData, err := ioutil.ReadFile(manifestFile)
	if err != nil {
		log.Fatalf("can’t read manifest: %s", err)
	}
	var gotManifest map[string]interface{}
	if err := json.Unmarshal(jsonData, &gotManifest); err != nil {
		log.Fatalf("can’t decode manifest: %s", err)
	}
	var outputFile string
	if os.PathSeparator == '/' {
		outputFile = "/tmp/output.dat"
	} else {
		outputFile = `C:\Temp\output.dat`
	}
	wantManifest := map[string]interface{}{
		"root":        "RUNFILES_ROOT",
		"tags":        []interface{}{"local", "mytag"},
		"loadPath":    []interface{}{"phst_rules_elisp"},
		"inputFiles":  []interface{}{"phst_rules_elisp/elisp/binary.cc", "phst_rules_elisp/elisp/binary.h"},
		"outputFiles": []interface{}{outputFile},
	}
	if diff := cmp.Diff(
		gotManifest, wantManifest,
		cmp.FilterPath(isInputFile, cmp.Transformer("", resolveRunfile)),
	); diff != "" {
		log.Fatalf("manifest: -got +want:\n%s", diff)
	}
}

func isInputFile(p cmp.Path) bool {
	if len(p) < 2 {
		return false
	}
	m, ok := p[1].(cmp.MapIndex)
	if !ok {
		return false
	}
	k := m.Key()
	return k.Kind() == reflect.String && k.String() == "inputFiles"
}

func resolveRunfile(s string) string {
	r, err := runfiles.Path(s)
	if err != nil {
		log.Fatal(err)
	}
	return r
}
<commit_msg>Fix expected output filename argument on Windows<commit_after>// Copyright 2020, 2021 Google LLC
//
// 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
//
//     https://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.

// Binary wrap is a test helper program for //elisp:binary_test, which see.
package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"reflect"

	"github.com/google/go-cmp/cmp"
	"github.com/phst/runfiles"
)

func main() {
	log.Println("Args:", os.Args)
	log.Println("Environment:", os.Environ())
	var manifestFile string
	flag.StringVar(&manifestFile, "manifest", "", "")
	flag.Parse()
	if manifestFile == "" {
		log.Fatal("--manifest is empty")
	}
	runfilesLib, err := runfiles.Path("phst_rules_elisp/elisp/runfiles/runfiles.elc")
	if err != nil {
		log.Fatal(err)
	}
	// The load path setup depends on whether we use manifest-based or
	// directory-based runfiles.
	var loadPathArgs []string
	if dir, err := runfiles.Path("phst_rules_elisp"); err == nil {
		// Directory-based runfiles.
		loadPathArgs = []string{"--directory=" + dir}
	} else {
		// Manifest-based runfiles.
		loadPathArgs = []string{
			"--load=" + runfilesLib,
			"--funcall=elisp/runfiles/install-handler",
			"--directory=/bazel-runfile:phst_rules_elisp",
		}
	}
	var outputFile string
	if os.PathSeparator == '/' {
		outputFile = "/tmp/output.dat"
	} else {
		outputFile = `C:\Temp\output.dat`
	}
	gotArgs := flag.Args()
	wantArgs := append(
		append([]string{"--quick", "--batch"}, loadPathArgs...),
		"--option",
		"elisp/binary.cc",
		" \t\n\r\f äα𝐴🐈'\\\"",
		"/:"+outputFile,
	)
	if diff := cmp.Diff(gotArgs, wantArgs); diff != "" {
		log.Fatalf("positional arguments: -got +want:\n%s", diff)
	}
	jsonData, err := ioutil.ReadFile(manifestFile)
	if err != nil {
		log.Fatalf("can’t read manifest: %s", err)
	}
	var gotManifest map[string]interface{}
	if err := json.Unmarshal(jsonData, &gotManifest); err != nil {
		log.Fatalf("can’t decode manifest: %s", err)
	}
	wantManifest := map[string]interface{}{
		"root":        "RUNFILES_ROOT",
		"tags":        []interface{}{"local", "mytag"},
		"loadPath":    []interface{}{"phst_rules_elisp"},
		"inputFiles":  []interface{}{"phst_rules_elisp/elisp/binary.cc", "phst_rules_elisp/elisp/binary.h"},
		"outputFiles": []interface{}{outputFile},
	}
	if diff := cmp.Diff(
		gotManifest, wantManifest,
		cmp.FilterPath(isInputFile, cmp.Transformer("", resolveRunfile)),
	); diff != "" {
		log.Fatalf("manifest: -got +want:\n%s", diff)
	}
}

func isInputFile(p cmp.Path) bool {
	if len(p) < 2 {
		return false
	}
	m, ok := p[1].(cmp.MapIndex)
	if !ok {
		return false
	}
	k := m.Key()
	return k.Kind() == reflect.String && k.String() == "inputFiles"
}

func resolveRunfile(s string) string {
	r, err := runfiles.Path(s)
	if err != nil {
		log.Fatal(err)
	}
	return r
}
<|endoftext|>


<commit_before>package net

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
)

var (
	BindLocal   = false // TODO(geoah) refactor to remove global
	BindPrivate = false // TODO(geoah) refactor to remove global
	bindIpv6    = false // TODO(geoah) refactor to remove global
)

func init() {
	BindLocal, _ = strconv.ParseBool(os.Getenv("BIND_LOCAL"))
	BindPrivate, _ = strconv.ParseBool(os.Getenv("BIND_PRIVATE"))
	bindIpv6, _ = strconv.ParseBool(os.Getenv("BIND_IPV6"))
}

// GetAddresses returns the addresses the transport is listening to
func GetAddresses(protocol string, l net.Listener) []string {
	port := l.Addr().(*net.TCPAddr).Port
	// TODO log errors
	addrs, _ := GetLocalPeerAddresses(port)
	for i, addr := range addrs {
		addrs[i] = fmt.Sprintf("%s:%s", protocol, addr)
	}
	return addrs
}

func fmtAddress(protocol, address string, port int) string {
	return fmt.Sprintf("%s:%s:%d", protocol, address, port)
}

// GetLocalPeerAddresses returns the addresses TCP can listen to on the local machine
func GetLocalPeerAddresses(port int) ([]string, error) {
	// go through all ifs
	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	// gather addresses of all ifs
	ips := []net.Addr{}
	for _, iface := range ifaces {
		ifIPs, err := iface.Addrs()
		if err != nil {
			continue
		}
		ips = append(ips, ifIPs...)
	}

	// gather valid addresses
	addrs := []string{}
	for _, ip := range ips {
		cleanIP, valid := isValidIP(ip)
		if valid {
			hostPort := fmt.Sprintf("%s:%d", cleanIP, port)
			addrs = append(addrs, hostPort)
		}
	}
	return addrs, nil
}

func isValidIP(addr net.Addr) (string, bool) {
	var ip net.IP
	switch v := addr.(type) {
	case *net.IPNet:
		ip = v.IP
	case *net.IPAddr:
		ip = v.IP
	}
	if ip == nil {
		return "", false
	}
	if !BindLocal && ip.IsLoopback() {
		return "", false
	}
	if !BindPrivate && isPrivate(ip) {
		return "", false
	}
	if !bindIpv6 && isIPv6(ip.String()) {
		return "", false
	}
	return ip.String(), true
}

func isIPv6(address string) bool {
	return strings.Count(address, ":") >= 2
}

func isPrivate(ip net.IP) bool {
	_, object24, _ := net.ParseCIDR("10.0.0.0/8")
	_, object20, _ := net.ParseCIDR("172.16.0.0/12")
	_, object16, _ := net.ParseCIDR("192.168.0.0/16")
	return object16.Contains(ip) || object20.Contains(ip) || object24.Contains(ip)
}
<commit_msg>fix(range-check): add link local range in private check<commit_after>package net

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
)

var (
	BindLocal   = false // TODO(geoah) refactor to remove global
	BindPrivate = false // TODO(geoah) refactor to remove global
	bindIpv6    = false // TODO(geoah) refactor to remove global
)

func init() {
	BindLocal, _ = strconv.ParseBool(os.Getenv("BIND_LOCAL"))
	BindPrivate, _ = strconv.ParseBool(os.Getenv("BIND_PRIVATE"))
	bindIpv6, _ = strconv.ParseBool(os.Getenv("BIND_IPV6"))
}

// GetAddresses returns the addresses the transport is listening to
func GetAddresses(protocol string, l net.Listener) []string {
	port := l.Addr().(*net.TCPAddr).Port
	// TODO log errors
	addrs, _ := GetLocalPeerAddresses(port)
	for i, addr := range addrs {
		addrs[i] = fmt.Sprintf("%s:%s", protocol, addr)
	}
	return addrs
}

func fmtAddress(protocol, address string, port int) string {
	return fmt.Sprintf("%s:%s:%d", protocol, address, port)
}

// GetLocalPeerAddresses returns the addresses TCP can listen to on the local machine
func GetLocalPeerAddresses(port int) ([]string, error) {
	// go through all ifs
	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	// gather addresses of all ifs
	ips := []net.Addr{}
	for _, iface := range ifaces {
		ifIPs, err := iface.Addrs()
		if err != nil {
			continue
		}
		ips = append(ips, ifIPs...)
	}

	// gather valid addresses
	addrs := []string{}
	for _, ip := range ips {
		cleanIP, valid := isValidIP(ip)
		if valid {
			hostPort := fmt.Sprintf("%s:%d", cleanIP, port)
			addrs = append(addrs, hostPort)
		}
	}
	return addrs, nil
}

func isValidIP(addr net.Addr) (string, bool) {
	var ip net.IP
	switch v := addr.(type) {
	case *net.IPNet:
		ip = v.IP
	case *net.IPAddr:
		ip = v.IP
	}
	if ip == nil {
		return "", false
	}
	if !BindLocal && ip.IsLoopback() {
		return "", false
	}
	if !BindPrivate && isPrivate(ip) {
		return "", false
	}
	if !bindIpv6 && isIPv6(ip.String()) {
		return "", false
	}
	return ip.String(), true
}

func isIPv6(address string) bool {
	return strings.Count(address, ":") >= 2
}

func isPrivate(ip net.IP) bool {
	_, block24, _ := net.ParseCIDR("10.0.0.0/8")
	_, block20, _ := net.ParseCIDR("172.16.0.0/12")
	_, block16, _ := net.ParseCIDR("192.168.0.0/16")
	_, blockLinkLocal, _ := net.ParseCIDR("169.254.0.0/16")
	return block16.Contains(ip) || block20.Contains(ip) || block24.Contains(ip) ||
		blockLinkLocal.Contains(ip)
}
<|endoftext|>


<commit_before>package ec2

import (
	"errors"

	goaws "github.com/aws/aws-sdk-go/aws"
	awsec2 "github.com/aws/aws-sdk-go/service/ec2"
)

type AvailabilityZoneRetriever struct {
	ec2ClientProvider ec2ClientProvider
}

func NewAvailabilityZoneRetriever(ec2ClientProvider ec2ClientProvider) AvailabilityZoneRetriever {
	return AvailabilityZoneRetriever{
		ec2ClientProvider: ec2ClientProvider,
	}
}

func (r AvailabilityZoneRetriever) Retrieve(region string) ([]string, error) {
	output, err := r.ec2ClientProvider.GetEC2Client().DescribeAvailabilityZones(&awsec2.DescribeAvailabilityZonesInput{
		Filters: []*awsec2.Filter{{
			Name:   goaws.String("region-name"),
			Values: []*string{goaws.String(region)},
		}},
	})
	if err != nil {
		return []string{}, err
	}

	azList := []string{}
	for _, az := range output.AvailabilityZones {
		if az == nil {
			return []string{}, errors.New("aws returned nil availability zone")
		}
		if az.ZoneName == nil {
			return []string{}, errors.New("aws returned availability zone with nil zone name")
		}

		azList = append(azList, *az.ZoneName)
	}

	return azList, nil
}
<commit_msg>Fix bbl up for AWS<commit_after>package ec2

import (
	"errors"

	goaws "github.com/aws/aws-sdk-go/aws"
	awsec2 "github.com/aws/aws-sdk-go/service/ec2"
)

type AvailabilityZoneRetriever struct {
	ec2ClientProvider ec2ClientProvider
}

func NewAvailabilityZoneRetriever(ec2ClientProvider ec2ClientProvider) AvailabilityZoneRetriever {
	return AvailabilityZoneRetriever{
		ec2ClientProvider: ec2ClientProvider,
	}
}

func (r AvailabilityZoneRetriever) Retrieve(region string) ([]string, error) {
	output, err := r.ec2ClientProvider.GetEC2Client().DescribeAvailabilityZones(&awsec2.DescribeAvailabilityZonesInput{
		Filters: []*awsec2.Filter{{
			Name:   goaws.String("region-name"),
			Values: []*string{goaws.String(region)},
		}},
	})
	if err != nil {
		return []string{}, err
	}

	azList := []string{}
	for _, az := range output.AvailabilityZones {
		if az == nil {
			return []string{}, errors.New("aws returned nil availability zone")
		}
		if az.ZoneName == nil {
			return []string{}, errors.New("aws returned availability zone with nil zone name")
		}

		if *az.ZoneName != "us-east-1d" {
			azList = append(azList, *az.ZoneName)
		}
	}

	return azList, nil
}
<|endoftext|>


<commit_before>package plugins

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/koffeinsource/go-URLextract/webpage"
	"github.com/koffeinsource/go-imgur"
	"github.com/koffeinsource/go-klogger"
)

func createIMGTag(link string, webm string, mp4 string, height int, width int) string {
	var ret string
	if webm != "" && mp4 != "" {
		ret = "<video preload=\"auto\" autoplay=\"autoplay\" muted=\"muted\" loop=\"loop\""
		ret += " style=\"width: " + strconv.Itoa(width) + "px; height: " + strconv.Itoa(height) + "px;\">"
		ret += "<source src=\"" + strings.Replace(webm, "http://", "https://", 1) + "type=\"video/webm\">"
		ret += "<source src=\"" + strings.Replace(mp4, "http://", "https://", 1) + "type=\"video/mp4\">"
		ret += "</video>
"
		return ret
	}
	ret = "
"
	return ret
}

func image(i *webpage.Info, v interface{}) {
	i.URL = strings.Replace(i.URL, "http://", "https://", 1)
	i.ImageURL = ""

	switch s := v.(type) {
	case *imgur.ImageInfo:
		i.Caption = s.Title
		i.Description = createIMGTag(s.Link, s.Webm, s.Mp4, s.Height, s.Width)
		if s.Description != "" {
			i.Description += s.Description
		}

	case *imgur.GalleryImageInfo:
		i.Caption = s.Title
		i.Description = createIMGTag(s.Link, s.Webm, s.Mp4, s.Height, s.Width)
		if s.Description != "" {
			i.Description += s.Description
		}
	default:
		panic("Passed invalid type to image function in go-URLextract imgur plugin")
	}
}

func album(i *webpage.Info, v interface{}) {
	i.ImageURL = ""

	switch s := v.(type) {
	case *imgur.AlbumInfo:
		i.URL = strings.Replace(s.Link, "http://", "https://", 1)
		i.Caption = "[ALBUM] " + s.Title

		i.Description = ""
		fmt.Println(s.ImagesCount)
		if s.Description != "" {
			i.Description += s.Description
		}
		if s.ImagesCount > 0 {
			i.Description += createIMGTag(s.Images[0].Link, s.Images[0].Webm, s.Images[0].Mp4, s.Images[0].Height, s.Images[0].Width)
		}
		if s.ImagesCount > 1 {
			i.Description += "Click the link below for the rest of the album!
"
		}

	case *imgur.GalleryAlbumInfo:
		i.URL = strings.Replace(s.Link, "http://", "https://", 1)
		i.Caption = "[ALBUM] " + s.Title

		i.Description = ""
		if s.Description != "" {
			i.Description += s.Description
		}
		if s.ImagesCount > 0 {
			i.Description += createIMGTag(s.Images[0].Link, s.Images[0].Webm, s.Images[0].Mp4, s.Images[0].Height, s.Images[0].Width)
		}
		if s.ImagesCount > 1 {
			i.Description += "Click the link below for the rest of the album!
"
		}

	default:
		panic("Passed invalid type to album function in go-URLextract imgur plugin")
	}
}

// Imgurl extract all images from an imgurl album
func Imgurl(i *webpage.Info, sourceURL string, httpClient *http.Client, log klogger.KLogger, imgurClientID string) {
	if !strings.Contains(sourceURL, "imgur.com/") {
		return
	}

	log.Infof("Running imgurl plugin.")

	client := new(imgur.Client)
	client.HTTPClient = httpClient
	client.Log = log
	client.ImgurClientID = imgurClientID

	gi, status, err := client.GetInfoFromURL(sourceURL)

	if err != nil {
		log.Errorf("Error using go-imgur. Status: %v. Error: %v", status, err)
		return
	}
	if status > 399 {
		log.Errorf("Error using go-imgur. Status: %v. Error: nil", status)
		return
	}

	if gi.Image != nil {
		image(i, gi.Image)
		return
	}
	if gi.GImage != nil {
		image(i, gi.GImage)
		return
	}

	if gi.Album != nil {
		log.Debugf("1\n")
		album(i, gi.Album)
		return
	}
	if gi.GAlbum != nil {
		log.Debugf("2\n")
		album(i, gi.GAlbum)
		return
	}

	panic("Unknown type used in go-URLextract imgur plugin")
}
<commit_msg>imgur plugin: handel empty titles.<commit_after>package plugins

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/koffeinsource/go-URLextract/webpage"
	"github.com/koffeinsource/go-imgur"
	"github.com/koffeinsource/go-klogger"
)

func createIMGTag(link string, webm string, mp4 string, height int, width int) string {
	var ret string
	if webm != "" && mp4 != "" {
		ret = "<video preload=\"auto\" autoplay=\"autoplay\" muted=\"muted\" loop=\"loop\""
		ret += " style=\"width: " + strconv.Itoa(width) + "px; height: " + strconv.Itoa(height) + "px;\">"
		ret += "<source src=\"" + strings.Replace(webm, "http://", "https://", 1) + "type=\"video/webm\">"
		ret += "<source src=\"" + strings.Replace(mp4, "http://", "https://", 1) + "type=\"video/mp4\">"
		ret += "</video>
"
		return ret
	}
	ret = "
"
	return ret
}

func image(i *webpage.Info, v interface{}) {
	i.URL = strings.Replace(i.URL, "http://", "https://", 1)
	i.ImageURL = ""

	switch s := v.(type) {
	case *imgur.ImageInfo:
		if s.Title != "" {
			i.Caption = s.Title
		}

		i.Description = createIMGTag(s.Link, s.Webm, s.Mp4, s.Height, s.Width)
		if s.Description != "" {
			i.Description += s.Description
		}

	case *imgur.GalleryImageInfo:
		if s.Title != "" {
			i.Caption = s.Title
		}

		i.Description = createIMGTag(s.Link, s.Webm, s.Mp4, s.Height, s.Width)
		if s.Description != "" {
			i.Description += s.Description
		}
	default:
		panic("Passed invalid type to image function in go-URLextract imgur plugin")
	}
}

func album(i *webpage.Info, v interface{}) {
	i.ImageURL = ""

	switch s := v.(type) {
	case *imgur.AlbumInfo:
		i.URL = strings.Replace(s.Link, "http://", "https://", 1)
		if s.Title != "" {
			i.Caption = "[ALBUM] " + s.Title
		}

		i.Description = ""
		fmt.Println(s.ImagesCount)
		if s.Description != "" {
			i.Description += s.Description
		}
		if s.ImagesCount > 0 {
			i.Description += createIMGTag(s.Images[0].Link, s.Images[0].Webm, s.Images[0].Mp4, s.Images[0].Height, s.Images[0].Width)
		}
		if s.ImagesCount > 1 {
			i.Description += "Click the link below for the rest of the album!
"
		}

	case *imgur.GalleryAlbumInfo:
		i.URL = strings.Replace(s.Link, "http://", "https://", 1)
		if s.Title != "" {
			i.Caption = "[ALBUM] " + s.Title
		}

		i.Description = ""
		if s.Description != "" {
			i.Description += s.Description
		}
		if s.ImagesCount > 0 {
			i.Description += createIMGTag(s.Images[0].Link, s.Images[0].Webm, s.Images[0].Mp4, s.Images[0].Height, s.Images[0].Width)
		}
		if s.ImagesCount > 1 {
			i.Description += "Click the link below for the rest of the album!
"
		}

	default:
		panic("Passed invalid type to album function in go-URLextract imgur plugin")
	}
}

// Imgurl extract all images from an imgurl album
func Imgurl(i *webpage.Info, sourceURL string, httpClient *http.Client, log klogger.KLogger, imgurClientID string) {
	if !strings.Contains(sourceURL, "imgur.com/") {
		return
	}

	log.Infof("Running imgurl plugin.")

	client := new(imgur.Client)
	client.HTTPClient = httpClient
	client.Log = log
	client.ImgurClientID = imgurClientID

	gi, status, err := client.GetInfoFromURL(sourceURL)

	if err != nil {
		log.Errorf("Error using go-imgur. Status: %v. Error: %v", status, err)
		return
	}
	if status > 399 {
		log.Errorf("Error using go-imgur. Status: %v. Error: nil", status)
		return
	}

	if gi.Image != nil {
		image(i, gi.Image)
		return
	}
	if gi.GImage != nil {
		image(i, gi.GImage)
		return
	}

	if gi.Album != nil {
		log.Debugf("1\n")
		album(i, gi.Album)
		return
	}
	if gi.GAlbum != nil {
		log.Debugf("2\n")
		album(i, gi.GAlbum)
		return
	}

	panic("Unknown type used in go-URLextract imgur plugin")
}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"fmt"
	"sync"

	"github.com/decred/bitset"
	"github.com/decred/dcrutil/hdkeychain"
	"github.com/decred/dcrwallet/chain"
	"github.com/decred/dcrwallet/wallet/udb"
	"github.com/decred/dcrwallet/walletdb"
)

func (w *Wallet) findLastUsedAccount(client *chain.RPCClient, coinTypeXpriv *hdkeychain.ExtendedKey) (uint32, error) {
	const scanLen = 100
	var (
		lastUsed uint32
		lo, hi   uint32 = 0, hdkeychain.HardenedKeyStart / scanLen
	)
Bsearch:
	for lo <= hi {
		mid := (hi + lo) / 2
		type result struct {
			used    bool
			account uint32
			err     error
		}
		var results [scanLen]result
		var wg sync.WaitGroup
		for i := scanLen - 1; i >= 0; i-- {
			i := i
			account := mid*scanLen + uint32(i)
			if account >= hdkeychain.HardenedKeyStart {
				continue
			}
			xpriv, err := coinTypeXpriv.Child(hdkeychain.HardenedKeyStart + account)
			if err != nil {
				return 0, err
			}
			xpub, err := xpriv.Neuter()
			if err != nil {
				xpriv.Zero()
				return 0, err
			}
			wg.Add(1)
			go func() {
				used, err := w.accountUsed(client, xpub)
				xpriv.Zero()
				results[i] = result{used, account, err}
				wg.Done()
			}()
		}
		wg.Wait()
		for i := scanLen - 1; i >= 0; i-- {
			if results[i].err != nil {
				return 0, results[i].err
			}
			if results[i].used {
				lastUsed = results[i].account
				lo = mid + 1
				continue Bsearch
			}
		}
		if mid == 0 {
			break
		}
		hi = mid - 1
	}
	return lastUsed, nil
}

func (w *Wallet) accountUsed(client *chain.RPCClient, xpub *hdkeychain.ExtendedKey) (bool, error) {
	extKey, intKey, err := deriveBranches(xpub)
	if err != nil {
		return false, err
	}
	type result struct {
		used bool
		err  error
	}
	results := make(chan result, 2)
	merge := func(used bool, err error) {
		results <- result{used, err}
	}
	go func() { merge(w.branchUsed(client, extKey)) }()
	go func() { merge(w.branchUsed(client, intKey)) }()
	for i := 0; i < 2; i++ {
		r := <-results
		if r.err != nil {
			return false, err
		}
		if r.used {
			return true, nil
		}
	}
	return false, nil
}

func (w *Wallet) branchUsed(client *chain.RPCClient, branchXpub *hdkeychain.ExtendedKey) (bool, error) {
	addrs, err := deriveChildAddresses(branchXpub, 0, uint32(w.gapLimit), w.chainParams)
	if err != nil {
		return false, err
	}
	existsBitsHex, err := client.ExistsAddresses(addrs)
	if err != nil {
		return false, err
	}
	for _, r := range existsBitsHex {
		if r != '0' {
			return true, nil
		}
	}
	return false, nil
}

// findLastUsedAddress returns the child index of the last used child address
// derived from a branch key.  If no addresses are found, ^uint32(0) is
// returned.
func (w *Wallet) findLastUsedAddress(client *chain.RPCClient, xpub *hdkeychain.ExtendedKey) (uint32, error) {
	var (
		lastUsed        = ^uint32(0)
		scanLen         = uint32(w.gapLimit)
		segments        = hdkeychain.HardenedKeyStart / scanLen
		lo, hi   uint32 = 0, segments - 1
	)
Bsearch:
	for lo <= hi {
		mid := (hi + lo) / 2
		addrs, err := deriveChildAddresses(xpub, mid*scanLen, scanLen, w.chainParams)
		if err != nil {
			return 0, err
		}
		existsBitsHex, err := client.ExistsAddresses(addrs)
		if err != nil {
			return 0, err
		}
		existsBits, err := hex.DecodeString(existsBitsHex)
		if err != nil {
			return 0, err
		}
		for i := len(addrs) - 1; i >= 0; i-- {
			if bitset.Bytes(existsBits).Get(i) {
				lastUsed = mid*scanLen + uint32(i)
				lo = mid + 1
				continue Bsearch
			}
		}
		if mid == 0 {
			break
		}
		hi = mid - 1
	}
	return lastUsed, nil
}

// DiscoverActiveAddresses accesses the consensus RPC server to discover all the
// addresses that have been used by an HD keychain stemming from this wallet. If
// discoverAccts is true, used accounts will be discovered as well.  This
// feature requires the wallet to be unlocked in order to derive hardened
// account extended pubkeys.
//
// A transaction filter (re)load and rescan should be performed after discovery.
func (w *Wallet) DiscoverActiveAddresses(chainClient *chain.RPCClient, discoverAccts bool) error {
	// Start by rescanning the accounts and determining what the
	// current account index is. This scan should only ever be
	// performed if we're restoring our wallet from seed.
	if discoverAccts {
		log.Infof("Discovering used accounts")
		var coinTypePrivKey *hdkeychain.ExtendedKey
		defer func() {
			if coinTypePrivKey != nil {
				coinTypePrivKey.Zero()
			}
		}()
		err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
			var err error
			coinTypePrivKey, err = w.Manager.CoinTypePrivKey(tx)
			return err
		})
		if err != nil {
			return err
		}
		lastUsed, err := w.findLastUsedAccount(chainClient, coinTypePrivKey)
		if err != nil {
			return err
		}
		if lastUsed != 0 {
			var lastRecorded uint32
			acctXpubs := make(map[uint32]*hdkeychain.ExtendedKey)
			w.addressBuffersMu.Lock()
			err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
				var err error
				lastRecorded, err = w.Manager.LastAccount(ns)
				if err != nil {
					return err
				}
				for acct := lastRecorded + 1; acct <= lastUsed; acct++ {
					acct, err := w.Manager.NewAccount(ns, fmt.Sprintf("account-%d", acct))
					if err != nil {
						return err
					}
					xpub, err := w.Manager.AccountExtendedPubKey(tx, acct)
					if err != nil {
						return err
					}
					acctXpubs[acct] = xpub
				}
				return nil
			})
			if err != nil {
				w.addressBuffersMu.Unlock()
				return err
			}
			for acct := lastRecorded + 1; acct <= lastUsed; acct++ {
				_, ok := w.addressBuffers[acct]
				if !ok {
					extKey, intKey, err := deriveBranches(acctXpubs[acct])
					if err != nil {
						w.addressBuffersMu.Unlock()
						return err
					}
					w.addressBuffers[acct] = &bip0044AccountData{
						albExternal: addressBuffer{branchXpub: extKey},
						albInternal: addressBuffer{branchXpub: intKey},
					}
				}
			}
			w.addressBuffersMu.Unlock()
		}
	}

	var lastAcct uint32
	err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
		ns := tx.ReadBucket(waddrmgrNamespaceKey)
		var err error
		lastAcct, err = w.Manager.LastAccount(ns)
		return err
	})
	if err != nil {
		return err
	}

	log.Infof("Discovering used addresses for %d account(s)", lastAcct+1)

	// Rescan addresses for the both the internal and external
	// branches of the account.
	errs := make(chan error, lastAcct+1)
	var wg sync.WaitGroup
	wg.Add(int(lastAcct + 1))
	for acct := uint32(0); acct <= lastAcct; acct++ {
		// Address usage discovery for each account can be performed
		// concurrently.
		acct := acct
		go func() {
			defer wg.Done()
			// Do this for both external (0) and internal (1) branches.
			for branch := uint32(0); branch < 2; branch++ {
				var branchXpub *hdkeychain.ExtendedKey
				err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
					var err error
					branchXpub, err = w.Manager.AccountBranchExtendedPubKey(tx, acct, branch)
					return err
				})
				if err != nil {
					errs <- err
					return
				}

				lastUsed, err := w.findLastUsedAddress(chainClient, branchXpub)
				if err != nil {
					errs <- err
					return
				}

				// Save discovered addresses for the account plus additional
				// addresses that may be used by other wallets sharing the same
				// seed.
				err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
					ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)

					// SyncAccountToAddrIndex never removes derived addresses
					// from an account, and can be called with just the
					// discovered last used child index, plus the gap limit.
					// Cap it to the highest child index.
					//
					// If no addresses were used for this branch, lastUsed is
					// ^uint32(0) and adding the gap limit it will sync exactly
					// gapLimit number of addresses (e.g. 0-19 when the gap
					// limit is 20).
					gapLimit := uint32(w.gapLimit)
					err := w.Manager.SyncAccountToAddrIndex(ns, acct,
						minUint32(lastUsed+gapLimit, hdkeychain.HardenedKeyStart-1),
						branch)
					if err != nil {
						return err
					}
					if lastUsed < hdkeychain.HardenedKeyStart {
						err = w.Manager.MarkUsedChildIndex(tx, acct, branch, lastUsed)
						if err != nil {
							return err
						}
					}

					props, err := w.Manager.AccountProperties(ns, acct)
					if err != nil {
						return err
					}
					lastReturned := props.LastReturnedExternalIndex

					w.addressBuffersMu.Lock()
					acctData := w.addressBuffers[acct]
					buf := &acctData.albExternal
					if branch == udb.InternalBranch {
						buf = &acctData.albInternal
						lastReturned = props.LastReturnedInternalIndex
					}
					buf.lastUsed = lastUsed
					buf.cursor = lastReturned - lastUsed
					w.addressBuffersMu.Unlock()

					log.Infof("Synchronized account %d branch %d to next child index %v",
						acct, branch, lastUsed+1)
					return nil
				})
				if err != nil {
					errs <- err
					return
				}
			}
		}()
	}
	wg.Wait()
	select {
	case err := <-errs:
		// Drain remaining
		go func() {
			for {
				select {
				case <-errs:
				default:
					return
				}
			}
		}()
		return err
	default:
		log.Infof("Finished address discovery")
		return nil
	}
}
<commit_msg>Log the correct next child index.<commit_after>// Copyright (c) 2015-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"fmt"
	"sync"

	"github.com/decred/bitset"
	"github.com/decred/dcrutil/hdkeychain"
	"github.com/decred/dcrwallet/chain"
	"github.com/decred/dcrwallet/wallet/udb"
	"github.com/decred/dcrwallet/walletdb"
)

func (w *Wallet) findLastUsedAccount(client *chain.RPCClient, coinTypeXpriv *hdkeychain.ExtendedKey) (uint32, error) {
	const scanLen = 100
	var (
		lastUsed uint32
		lo, hi   uint32 = 0, hdkeychain.HardenedKeyStart / scanLen
	)
Bsearch:
	for lo <= hi {
		mid := (hi + lo) / 2
		type result struct {
			used    bool
			account uint32
			err     error
		}
		var results [scanLen]result
		var wg sync.WaitGroup
		for i := scanLen - 1; i >= 0; i-- {
			i := i
			account := mid*scanLen + uint32(i)
			if account >= hdkeychain.HardenedKeyStart {
				continue
			}
			xpriv, err := coinTypeXpriv.Child(hdkeychain.HardenedKeyStart + account)
			if err != nil {
				return 0, err
			}
			xpub, err := xpriv.Neuter()
			if err != nil {
				xpriv.Zero()
				return 0, err
			}
			wg.Add(1)
			go func() {
				used, err := w.accountUsed(client, xpub)
				xpriv.Zero()
				results[i] = result{used, account, err}
				wg.Done()
			}()
		}
		wg.Wait()
		for i := scanLen - 1; i >= 0; i-- {
			if results[i].err != nil {
				return 0, results[i].err
			}
			if results[i].used {
				lastUsed = results[i].account
				lo = mid + 1
				continue Bsearch
			}
		}
		if mid == 0 {
			break
		}
		hi = mid - 1
	}
	return lastUsed, nil
}

func (w *Wallet) accountUsed(client *chain.RPCClient, xpub *hdkeychain.ExtendedKey) (bool, error) {
	extKey, intKey, err := deriveBranches(xpub)
	if err != nil {
		return false, err
	}
	type result struct {
		used bool
		err  error
	}
	results := make(chan result, 2)
	merge := func(used bool, err error) {
		results <- result{used, err}
	}
	go func() { merge(w.branchUsed(client, extKey)) }()
	go func() { merge(w.branchUsed(client, intKey)) }()
	for i := 0; i < 2; i++ {
		r := <-results
		if r.err != nil {
			return false, err
		}
		if r.used {
			return true, nil
		}
	}
	return false, nil
}

func (w *Wallet) branchUsed(client *chain.RPCClient, branchXpub *hdkeychain.ExtendedKey) (bool, error) {
	addrs, err := deriveChildAddresses(branchXpub, 0, uint32(w.gapLimit), w.chainParams)
	if err != nil {
		return false, err
	}
	existsBitsHex, err := client.ExistsAddresses(addrs)
	if err != nil {
		return false, err
	}
	for _, r := range existsBitsHex {
		if r != '0' {
			return true, nil
		}
	}
	return false, nil
}

// findLastUsedAddress returns the child index of the last used child address
// derived from a branch key.  If no addresses are found, ^uint32(0) is
// returned.
func (w *Wallet) findLastUsedAddress(client *chain.RPCClient, xpub *hdkeychain.ExtendedKey) (uint32, error) {
	var (
		lastUsed        = ^uint32(0)
		scanLen         = uint32(w.gapLimit)
		segments        = hdkeychain.HardenedKeyStart / scanLen
		lo, hi   uint32 = 0, segments - 1
	)
Bsearch:
	for lo <= hi {
		mid := (hi + lo) / 2
		addrs, err := deriveChildAddresses(xpub, mid*scanLen, scanLen, w.chainParams)
		if err != nil {
			return 0, err
		}
		existsBitsHex, err := client.ExistsAddresses(addrs)
		if err != nil {
			return 0, err
		}
		existsBits, err := hex.DecodeString(existsBitsHex)
		if err != nil {
			return 0, err
		}
		for i := len(addrs) - 1; i >= 0; i-- {
			if bitset.Bytes(existsBits).Get(i) {
				lastUsed = mid*scanLen + uint32(i)
				lo = mid + 1
				continue Bsearch
			}
		}
		if mid == 0 {
			break
		}
		hi = mid - 1
	}
	return lastUsed, nil
}

// DiscoverActiveAddresses accesses the consensus RPC server to discover all the
// addresses that have been used by an HD keychain stemming from this wallet. If
// discoverAccts is true, used accounts will be discovered as well.  This
// feature requires the wallet to be unlocked in order to derive hardened
// account extended pubkeys.
//
// A transaction filter (re)load and rescan should be performed after discovery.
func (w *Wallet) DiscoverActiveAddresses(chainClient *chain.RPCClient, discoverAccts bool) error {
	// Start by rescanning the accounts and determining what the
	// current account index is. This scan should only ever be
	// performed if we're restoring our wallet from seed.
	if discoverAccts {
		log.Infof("Discovering used accounts")
		var coinTypePrivKey *hdkeychain.ExtendedKey
		defer func() {
			if coinTypePrivKey != nil {
				coinTypePrivKey.Zero()
			}
		}()
		err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
			var err error
			coinTypePrivKey, err = w.Manager.CoinTypePrivKey(tx)
			return err
		})
		if err != nil {
			return err
		}
		lastUsed, err := w.findLastUsedAccount(chainClient, coinTypePrivKey)
		if err != nil {
			return err
		}
		if lastUsed != 0 {
			var lastRecorded uint32
			acctXpubs := make(map[uint32]*hdkeychain.ExtendedKey)
			w.addressBuffersMu.Lock()
			err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
				var err error
				lastRecorded, err = w.Manager.LastAccount(ns)
				if err != nil {
					return err
				}
				for acct := lastRecorded + 1; acct <= lastUsed; acct++ {
					acct, err := w.Manager.NewAccount(ns, fmt.Sprintf("account-%d", acct))
					if err != nil {
						return err
					}
					xpub, err := w.Manager.AccountExtendedPubKey(tx, acct)
					if err != nil {
						return err
					}
					acctXpubs[acct] = xpub
				}
				return nil
			})
			if err != nil {
				w.addressBuffersMu.Unlock()
				return err
			}
			for acct := lastRecorded + 1; acct <= lastUsed; acct++ {
				_, ok := w.addressBuffers[acct]
				if !ok {
					extKey, intKey, err := deriveBranches(acctXpubs[acct])
					if err != nil {
						w.addressBuffersMu.Unlock()
						return err
					}
					w.addressBuffers[acct] = &bip0044AccountData{
						albExternal: addressBuffer{branchXpub: extKey},
						albInternal: addressBuffer{branchXpub: intKey},
					}
				}
			}
			w.addressBuffersMu.Unlock()
		}
	}

	var lastAcct uint32
	err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
		ns := tx.ReadBucket(waddrmgrNamespaceKey)
		var err error
		lastAcct, err = w.Manager.LastAccount(ns)
		return err
	})
	if err != nil {
		return err
	}

	log.Infof("Discovering used addresses for %d account(s)", lastAcct+1)

	// Rescan addresses for the both the internal and external
	// branches of the account.
	errs := make(chan error, lastAcct+1)
	var wg sync.WaitGroup
	wg.Add(int(lastAcct + 1))
	for acct := uint32(0); acct <= lastAcct; acct++ {
		// Address usage discovery for each account can be performed
		// concurrently.
		acct := acct
		go func() {
			defer wg.Done()
			// Do this for both external (0) and internal (1) branches.
			for branch := uint32(0); branch < 2; branch++ {
				var branchXpub *hdkeychain.ExtendedKey
				err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
					var err error
					branchXpub, err = w.Manager.AccountBranchExtendedPubKey(tx, acct, branch)
					return err
				})
				if err != nil {
					errs <- err
					return
				}

				lastUsed, err := w.findLastUsedAddress(chainClient, branchXpub)
				if err != nil {
					errs <- err
					return
				}

				// Save discovered addresses for the account plus additional
				// addresses that may be used by other wallets sharing the same
				// seed.
				err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
					ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)

					// SyncAccountToAddrIndex never removes derived addresses
					// from an account, and can be called with just the
					// discovered last used child index, plus the gap limit.
					// Cap it to the highest child index.
					//
					// If no addresses were used for this branch, lastUsed is
					// ^uint32(0) and adding the gap limit it will sync exactly
					// gapLimit number of addresses (e.g. 0-19 when the gap
					// limit is 20).
					gapLimit := uint32(w.gapLimit)
					err := w.Manager.SyncAccountToAddrIndex(ns, acct,
						minUint32(lastUsed+gapLimit, hdkeychain.HardenedKeyStart-1),
						branch)
					if err != nil {
						return err
					}
					if lastUsed < hdkeychain.HardenedKeyStart {
						err = w.Manager.MarkUsedChildIndex(tx, acct, branch, lastUsed)
						if err != nil {
							return err
						}
					}

					props, err := w.Manager.AccountProperties(ns, acct)
					if err != nil {
						return err
					}
					lastReturned := props.LastReturnedExternalIndex

					w.addressBuffersMu.Lock()
					acctData := w.addressBuffers[acct]
					buf := &acctData.albExternal
					if branch == udb.InternalBranch {
						buf = &acctData.albInternal
						lastReturned = props.LastReturnedInternalIndex
					}
					buf.lastUsed = lastUsed
					buf.cursor = lastReturned - lastUsed
					w.addressBuffersMu.Unlock()

					log.Infof("Synchronized account %d branch %d to next child index %v",
						acct, branch, lastReturned+1)
					return nil
				})
				if err != nil {
					errs <- err
					return
				}
			}
		}()
	}
	wg.Wait()
	select {
	case err := <-errs:
		// Drain remaining
		go func() {
			for {
				select {
				case <-errs:
				default:
					return
				}
			}
		}()
		return err
	default:
		log.Infof("Finished address discovery")
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"io"
	"log"
	"os"
	"os/exec"
	"path"
)

const VERSION = "0.1"

func main() {
	log.Printf("Starting watchgopher %s ...\n", VERSION)
	flag.Parse()
	configPath := flag.Arg(0)
	_, err := os.Stat(configPath)
	if err != nil && os.IsNotExist(err) {
		log.Fatal("ERROR: Could not find configuration file")
	}

	rules, err := ParseConfig(configPath)
	if err != nil {
		log.Fatalf("ERROR: Could not parse config file: %s", err)
	}
	log.Printf("Successfully loaded configuration file. Number of rules: %d", len(rules))

	dirs := make([]string, 0)
	for _, rule := range rules {
		dirs = append(dirs, rule.Path)
	}

	watcher, err := WatchDirs(dirs)
	if err != nil {
		log.Println("ERROR: Could not start watching directories")
		log.Fatal(err)
	}

	defer func() {
		err = watcher.Stop()
		if err != nil {
			log.Fatal("ERROR: Did not shut down cleanly")
		}
	}()

	queue := Manage(watcher.Events, rules)

	log.Println("Watchgopher is now ready process file events")

	for payload := range queue {
		workOff(payload)
	}
}

func workOff(pl CmdPayload) {
	if pl.LogOutput {
		outp, err := pl.Cmd.StdoutPipe()
		if err != nil {
			logCmdErr(pl.Cmd, err)
		}

		errp, err := pl.Cmd.StderrPipe()
		if err != nil {
			logCmdErr(pl.Cmd, err)
		}

		_, filename := path.Split(pl.Cmd.Path)

		if outp != nil {
			go pipeToLog(filename, "STDOUT", outp)
		}

		if errp != nil {
			go pipeToLog(filename, "STDERR", errp)
		}
	}

	log.Printf("%s, ARGS: %s -- START\n", pl.Cmd.Path, pl.Cmd.Args[1:])

	if err := pl.Cmd.Start(); err != nil {
		logCmdErr(pl.Cmd, err)
		return
	}

	err := pl.Cmd.Wait()
	if err != nil {
		logCmdErr(pl.Cmd, err)
		return
	}
	log.Printf("%s, ARGS: %s -- SUCCESS\n", pl.Cmd.Path, pl.Cmd.Args[1:])
}

func pipeToLog(filename, prefix string, pipe io.ReadCloser) {
	reader := bufio.NewReader(pipe)
	for {
		line, err := reader.ReadBytes('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("[%s %s] Reading Error: %s", filename, prefix, err)
			break
		}
		log.Printf("[%s %s] %s", filename, prefix, line)
	}
}

func logCmdErr(cmd *exec.Cmd, err error) {
	log.Printf("%s, ARGS: %s -- ERROR: %s\n", cmd.Path, cmd.Args[1:], err)
}
<commit_msg>Support `--help` command line argument<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path"
)

const VERSION = "0.1"

func usage() {
	fmt.Fprintf(os.Stderr, "Watchgopher %s - Listen to file changes and react to them\n", VERSION)
	fmt.Fprintf(os.Stderr, "\nUsage:\n")
	fmt.Fprintf(os.Stderr, "\t watchgopher [configuration file]\n")
	fmt.Fprintf(os.Stderr, "\nArguments:\n")
	fmt.Fprintf(os.Stderr, "\t -h or --help\t\tPrint help (this message) and exit\n")
}

func main() {
	flag.Usage = usage
	flag.Parse()

	log.Printf("Starting watchgopher %s ...\n", VERSION)

	configPath := flag.Arg(0)
	_, err := os.Stat(configPath)
	if err != nil && os.IsNotExist(err) {
		log.Fatal("ERROR: Could not find configuration file")
	}

	rules, err := ParseConfig(configPath)
	if err != nil {
		log.Fatalf("ERROR: Could not parse config file: %s", err)
	}
	log.Printf("Successfully loaded configuration file. Number of rules: %d", len(rules))

	dirs := make([]string, 0)
	for _, rule := range rules {
		dirs = append(dirs, rule.Path)
	}

	watcher, err := WatchDirs(dirs)
	if err != nil {
		log.Println("ERROR: Could not start watching directories")
		log.Fatal(err)
	}

	defer func() {
		err = watcher.Stop()
		if err != nil {
			log.Fatal("ERROR: Did not shut down cleanly")
		}
	}()

	queue := Manage(watcher.Events, rules)

	log.Println("Watchgopher is now ready process file events")

	for payload := range queue {
		workOff(payload)
	}
}

func workOff(pl CmdPayload) {
	if pl.LogOutput {
		outp, err := pl.Cmd.StdoutPipe()
		if err != nil {
			logCmdErr(pl.Cmd, err)
		}

		errp, err := pl.Cmd.StderrPipe()
		if err != nil {
			logCmdErr(pl.Cmd, err)
		}

		_, filename := path.Split(pl.Cmd.Path)

		if outp != nil {
			go pipeToLog(filename, "STDOUT", outp)
		}

		if errp != nil {
			go pipeToLog(filename, "STDERR", errp)
		}
	}

	log.Printf("%s, ARGS: %s -- START\n", pl.Cmd.Path, pl.Cmd.Args[1:])

	if err := pl.Cmd.Start(); err != nil {
		logCmdErr(pl.Cmd, err)
		return
	}

	err := pl.Cmd.Wait()
	if err != nil {
		logCmdErr(pl.Cmd, err)
		return
	}
	log.Printf("%s, ARGS: %s -- SUCCESS\n", pl.Cmd.Path, pl.Cmd.Args[1:])
}

func pipeToLog(filename, prefix string, pipe io.ReadCloser) {
	reader := bufio.NewReader(pipe)
	for {
		line, err := reader.ReadBytes('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("[%s %s] Reading Error: %s", filename, prefix, err)
			break
		}
		log.Printf("[%s %s] %s", filename, prefix, line)
	}
}

func logCmdErr(cmd *exec.Cmd, err error) {
	log.Printf("%s, ARGS: %s -- ERROR: %s\n", cmd.Path, cmd.Args[1:], err)
}
<|endoftext|>


<commit_before>package web

import (
	"html/template"
	"io"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/kdada/tinygo/connector"
	"github.com/kdada/tinygo/router"
	"github.com/kdada/tinygo/session"
	"github.com/kdada/tinygo/util"
)

// 视图数据类型
type ViewData map[string]interface{}

// 表单文件
type FormFile struct {
	file   multipart.File        //表单文件
	header *multipart.FileHeader //文件头信息
}

// FileName 返回文件名
func (this *FormFile) FileName() string {
	return this.header.Filename
}

// Header 返回文件头信息
func (this *FormFile) Header() textproto.MIMEHeader {
	return this.header.Header
}

// File 返回表单文件
func (this *FormFile) File() multipart.File {
	return this.file
}

// Close 关闭表单文件
func (this *FormFile) Close() {
	this.file.Close()
}

// SaveTo 将文件保存到指定路径,保存完毕后自动关闭表单文件
func (this *FormFile) SaveTo(path string) error {
	var lf, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0660)
	if err == nil {
		defer lf.Close()
		_, err = io.Copy(lf, this.file)
		if err == nil {
			this.Close()

		}
	}
	return err
}

type Context struct {
	router.BaseContext
	HttpContext *connector.HttpContext //http上下文
	Session     session.Session        //http会话
	CSRF        session.Session        //csrf会话
	End         router.Router          //处理当前上下文的路由
	Processor   *HttpProcessor         //生成当前上下文的处理器
}

// NewContext 创建上下文信息
func NewContext(segments []string, context *connector.HttpContext) (*Context, error) {
	var err = context.Request.ParseForm()
	if err != nil {
		return nil, err
	}
	var method = context.Request.Method
	var c = new(Context)
	c.Segs = append(segments, method)
	c.HttpContext = context
	return c, nil
}

// Value 返回值
func (this *Context) Value(name string) (string, bool) {
	return this.HttpContext.Request.Form.Get(name), true
}

// Value 返回值数组
func (this *Context) Values(name string) ([]string, bool) {
	var result, ok = this.HttpContext.Request.Form[name]
	return result, ok
}

// SetValue 设置值
func (this *Context) SetValue(name string, value string) {
	var _, ok = this.HttpContext.Request.Form[name]
	if ok {
		this.HttpContext.Request.Form.Add(name, value)
	} else {
		this.HttpContext.Request.Form.Set(name, value)
	}
}

// ParamString 获取http参数字符串
func (this *Context) ParamString(key string) (string, error) {
	var routerResult, ok = this.Value(key)
	if ok {
		return routerResult, nil
	}
	return "", ErrorParamNotExist.Error()
}

// ParamString 获取http参数字符串数组
func (this *Context) ParamStringArray(key string) ([]string, error) {
	var result, ok = this.Values(key)
	if ok {
		return result, nil
	}
	return nil, ErrorParamNotExist.Error()
}

// ParamString 获取http参数字符串
func (this *Context) ParamBool(key string) (bool, error) {
	var result, err = this.ParamString(key)
	if err == nil {
		return strconv.ParseBool(result)
	}
	return false, err
}

// ParamString 获取http参数字符串
func (this *Context) ParamInt(key string) (int, error) {
	var result, err = this.ParamString(key)
	if err == nil {
		var v, err2 = strconv.ParseInt(result, 10, 64)
		return int(v), err2
	}
	return 0, err

}

// ParamString 获取http参数字符串
func (this *Context) ParamFloat(key string) (float64, error) {
	var result, err = this.ParamString(key)
	if err == nil {
		return strconv.ParseFloat(result, 64)
	}
	return 0, err
}

// ParamString 获取http参数文件
func (this *Context) ParamFile(key string) (*FormFile, error) {
	var file, header, err = this.HttpContext.Request.FormFile(key)
	if err == nil {
		return &FormFile{file, header}, nil
	}
	return nil, err
}

// ParamString 获取http参数文件数组
func (this *Context) ParamFiles(key string) ([]*FormFile, error) {
	var r = this.HttpContext.Request
	if r.MultipartForm == nil {
		err := r.ParseMultipartForm(int64(this.Processor.Config.MaxRequestMemory))
		if err != nil {
			return nil, err
		}
	}
	if r.MultipartForm != nil && r.MultipartForm.File != nil {
		if fhs := r.MultipartForm.File[key]; len(fhs) > 0 {
			var files = make([]*FormFile, 0)
			for _, fh := range fhs {
				f, err := fh.Open()
				if err == nil {
					files = append(files, &FormFile{f, fh})
				} else {
					return nil, err
				}
			}
			return files, nil
		}
	}
	return nil, http.ErrMissingFile
}

// WriteString 将字符串写入http response流
func (this *Context) WriteString(value string) error {
	var _, err = this.HttpContext.ResponseWriter.Write([]byte(value))
	return err
}

// WriteResult 将Result写入http response流
func (this *Context) WriteResult(result Result) error {
	return result.WriteTo(this.HttpContext.ResponseWriter)
}

// 返回文件类型结果
func (this *Context) File(path string) *FileResult {
	var result = new(FileResult)
	result.Status = 200
	result.context = this
	result.filePath = path
	return result
}

// 返回Json类型结果
func (this *Context) Json(data interface{}) *JsonResult {
	var result = new(JsonResult)
	result.Status = 200
	result.obj = data
	return result
}

// 返回Xml类型结果
func (this *Context) Xml(data interface{}) *XmlResult {
	var result = new(XmlResult)
	result.Status = 200
	result.obj = data
	return result
}

// 返回Api类型结果
func (this *Context) Api(data interface{}) HttpResult {
	switch this.Processor.Config.Api {
	case "json":
		{
			return this.Json(data)
		}
	case "xml":
		{
			return this.Xml(data)
		}
	default:
		{
			//检测请求头中是否包含指定的api格式
			//优先检测json格式,如果存在指定格式则返回指定格式
			//如果均不存在则返回json格式
			var accept = this.HttpContext.Request.Header.Get("Accept")
			if strings.Index(accept, "application/json") >= 0 {
				return this.Json(data)
			} else if strings.Index(accept, "application/xml") >= 0 {
				return this.Json(data)
			}
		}
	}
	return this.Json(data)
}

// 返回NotFound类型结果
func (this *Context) NotFound() *NotFoundResult {
	var result = new(NotFoundResult)
	result.Status = 404
	result.context = this
	return result
}

// 返回临时重定向类型结果
func (this *Context) Redirect(url string) *RedirectResult {
	var result = new(RedirectResult)
	result.Status = 302
	result.context = this
	result.url = url
	return result
}

// 返回永久重定向类型结果
func (this *Context) RedirectPermanently(url string) *RedirectResult {
	var result = new(RedirectResult)
	result.Status = 301
	result.context = this
	result.url = url
	return result
}

// 返回数据类型结果
func (this *Context) Data(data []byte) *DataResult {
	var result = new(DataResult)
	result.Status = 200
	result.data = data
	return result
}

// 返回视图类型结果
func (this *Context) View(path string, data ...interface{}) *ViewResult {
	var result = new(ViewResult)
	result.Status = 200
	result.templates = this.Processor.Templates
	result.path = path
	data = append(data, this.commonViewData())
	result.data = this.integrate(data...)
	return result
}

// 返回部分视图类型结果
func (this *Context) PartialView(path string, data ...interface{}) *PartialViewResult {
	var result = new(PartialViewResult)
	result.Status = 200
	result.templates = this.Processor.Templates
	result.path = path
	data = append(data, this.commonViewData())
	result.data = this.integrate(data...)
	return result
}

// commonViewData 生成公共视图数据
func (this *Context) commonViewData() ViewData {
	var vd = ViewData{}
	if this.Session != nil {
		vd["SESSION"] = NewTemplateSession(this.Session)
	}
	if this.CSRF != nil {
		vd["CSRF"] = NewTemplateCSRF(this.CSRF, this.Processor.Config.CSRFTokenName)
	}
	return vd
}

// integrate 将data的数据整合为一个ViewData
func (this *Context) integrate(data ...interface{}) ViewData {
	var result = make(ViewData)
	for _, output := range data {
		var outputType = reflect.TypeOf(output)
		if outputType.Kind() == reflect.Map {
			//添加Map
			var dataMap, ok = output.(ViewData)
			if ok {
				for k, v := range dataMap {
					result[k] = v
				}
			}
		} else if IsStructPtrType(outputType) {
			this.mapTo(reflect.ValueOf(output).Elem(), result)
		}
	}
	return result
}

// mapTo 将value结构体转换到一个ViewData中,value必须是结构体
func (this *Context) mapTo(value reflect.Value, data ViewData) {
	if value.Kind() == reflect.Struct {
		for i := 0; i < value.NumField(); i++ {
			var fieldValue = value.Field(i)
			if fieldValue.CanInterface() {
				var fieldType = value.Type().Field(i)
				if fieldType.Anonymous {
					//匿名组合字段,进行递归解析
					this.mapTo(fieldValue, data)
				} else {
					//非匿名字段
					data[fieldType.Name] = fieldValue.Interface()
				}
			}
		}
	}
}

// 重新分发,将当前请求交给另一个path处理
func (this *Context) Redispatch(path string) *UserDefinedResult {
	var result = new(UserDefinedResult)
	result.Status = StatusCodeRedispatch
	result.Msg = path
	return result
}

// ValidateCSRF 验证表单请求中是否存在csrf的token并且该token有效,验证后token立即失效
func (this *Context) ValidateCSRF() bool {
	if this.CSRF != nil {
		var token, err = this.ParamString(this.Processor.Config.CSRFTokenName)
		if err == nil {
			var t, ok = this.CSRF.Int(token)
			if ok {
				this.CSRF.Delete(token)
				return int(time.Now().Unix())-t <= this.Processor.Config.CSRFExpire
			}
		}
	}
	return false
}

// 模板会话信息
type TemplateSession struct {
	sess session.Session //当前会话
}

// NewTemplateSession 创建一个模板Session
func NewTemplateSession(sess session.Session) *TemplateSession {
	return &TemplateSession{sess}
}

// String 获取字符串
func (this *TemplateSession) String(key string) (string, error) {
	var v, ok = this.sess.String(key)
	if ok {
		return v, nil
	}
	return "", ErrorInvalidKey.Format(key).Error()
}

//  Int 获取整数值
func (this *TemplateSession) Int(key string) (int, error) {
	var v, ok = this.sess.Int(key)
	if ok {
		return v, nil
	}
	return 0, ErrorInvalidKey.Format(key).Error()
}

// Bool 获取bool值
func (this *TemplateSession) Bool(key string) (bool, error) {
	var v, ok = this.sess.Bool(key)
	if ok {
		return v, nil
	}
	return false, ErrorInvalidKey.Format(key).Error()
}

// Float 获取浮点值
func (this *TemplateSession) Float(key string) (float64, error) {
	var v, ok = this.sess.Float(key)
	if ok {
		return v, nil
	}
	return 0.0, ErrorInvalidKey.Format(key).Error()
}

// 模板CSRF信息
type TemplateCSRF struct {
	sess session.Session //当前CSRF会话
	name string          //token字段名称
}

// NewTemplateCSRF 创建一个模板CSRF
func NewTemplateCSRF(sess session.Session, fieldName string) *TemplateCSRF {
	return &TemplateCSRF{sess, fieldName}
}

// Token 生成一个CSRF认证字符串
func (this *TemplateCSRF) Token() template.HTML {
	var token = util.NewUUID().Hex()
	this.sess.SetInt(token, int(time.Now().Unix())) //记录生成时间(秒)
	return template.HTML(token)
}

// Field 生成一个包含CSRF的隐藏域
func (this *TemplateCSRF) Field() template.HTML {
	return template.HTML(`<input type="hidden" name="` + this.name + `" value="` + string(this.Token()) + `" >`)
}
<commit_msg>改变TemplateSession方法结构,提高可用性<commit_after>package web

import (
	"html/template"
	"io"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/kdada/tinygo/connector"
	"github.com/kdada/tinygo/router"
	"github.com/kdada/tinygo/session"
	"github.com/kdada/tinygo/util"
)

// 视图数据类型
type ViewData map[string]interface{}

// 表单文件
type FormFile struct {
	file   multipart.File        //表单文件
	header *multipart.FileHeader //文件头信息
}

// FileName 返回文件名
func (this *FormFile) FileName() string {
	return this.header.Filename
}

// Header 返回文件头信息
func (this *FormFile) Header() textproto.MIMEHeader {
	return this.header.Header
}

// File 返回表单文件
func (this *FormFile) File() multipart.File {
	return this.file
}

// Close 关闭表单文件
func (this *FormFile) Close() {
	this.file.Close()
}

// SaveTo 将文件保存到指定路径,保存完毕后自动关闭表单文件
func (this *FormFile) SaveTo(path string) error {
	var lf, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0660)
	if err == nil {
		defer lf.Close()
		_, err = io.Copy(lf, this.file)
		if err == nil {
			this.Close()

		}
	}
	return err
}

type Context struct {
	router.BaseContext
	HttpContext *connector.HttpContext //http上下文
	Session     session.Session        //http会话
	CSRF        session.Session        //csrf会话
	End         router.Router          //处理当前上下文的路由
	Processor   *HttpProcessor         //生成当前上下文的处理器
}

// NewContext 创建上下文信息
func NewContext(segments []string, context *connector.HttpContext) (*Context, error) {
	var err = context.Request.ParseForm()
	if err != nil {
		return nil, err
	}
	var method = context.Request.Method
	var c = new(Context)
	c.Segs = append(segments, method)
	c.HttpContext = context
	return c, nil
}

// Value 返回值
func (this *Context) Value(name string) (string, bool) {
	return this.HttpContext.Request.Form.Get(name), true
}

// Value 返回值数组
func (this *Context) Values(name string) ([]string, bool) {
	var result, ok = this.HttpContext.Request.Form[name]
	return result, ok
}

// SetValue 设置值
func (this *Context) SetValue(name string, value string) {
	var _, ok = this.HttpContext.Request.Form[name]
	if ok {
		this.HttpContext.Request.Form.Add(name, value)
	} else {
		this.HttpContext.Request.Form.Set(name, value)
	}
}

// ParamString 获取http参数字符串
func (this *Context) ParamString(key string) (string, error) {
	var routerResult, ok = this.Value(key)
	if ok {
		return routerResult, nil
	}
	return "", ErrorParamNotExist.Error()
}

// ParamString 获取http参数字符串数组
func (this *Context) ParamStringArray(key string) ([]string, error) {
	var result, ok = this.Values(key)
	if ok {
		return result, nil
	}
	return nil, ErrorParamNotExist.Error()
}

// ParamString 获取http参数字符串
func (this *Context) ParamBool(key string) (bool, error) {
	var result, err = this.ParamString(key)
	if err == nil {
		return strconv.ParseBool(result)
	}
	return false, err
}

// ParamString 获取http参数字符串
func (this *Context) ParamInt(key string) (int, error) {
	var result, err = this.ParamString(key)
	if err == nil {
		var v, err2 = strconv.ParseInt(result, 10, 64)
		return int(v), err2
	}
	return 0, err

}

// ParamString 获取http参数字符串
func (this *Context) ParamFloat(key string) (float64, error) {
	var result, err = this.ParamString(key)
	if err == nil {
		return strconv.ParseFloat(result, 64)
	}
	return 0, err
}

// ParamString 获取http参数文件
func (this *Context) ParamFile(key string) (*FormFile, error) {
	var file, header, err = this.HttpContext.Request.FormFile(key)
	if err == nil {
		return &FormFile{file, header}, nil
	}
	return nil, err
}

// ParamString 获取http参数文件数组
func (this *Context) ParamFiles(key string) ([]*FormFile, error) {
	var r = this.HttpContext.Request
	if r.MultipartForm == nil {
		err := r.ParseMultipartForm(int64(this.Processor.Config.MaxRequestMemory))
		if err != nil {
			return nil, err
		}
	}
	if r.MultipartForm != nil && r.MultipartForm.File != nil {
		if fhs := r.MultipartForm.File[key]; len(fhs) > 0 {
			var files = make([]*FormFile, 0)
			for _, fh := range fhs {
				f, err := fh.Open()
				if err == nil {
					files = append(files, &FormFile{f, fh})
				} else {
					return nil, err
				}
			}
			return files, nil
		}
	}
	return nil, http.ErrMissingFile
}

// WriteString 将字符串写入http response流
func (this *Context) WriteString(value string) error {
	var _, err = this.HttpContext.ResponseWriter.Write([]byte(value))
	return err
}

// WriteResult 将Result写入http response流
func (this *Context) WriteResult(result Result) error {
	return result.WriteTo(this.HttpContext.ResponseWriter)
}

// 返回文件类型结果
func (this *Context) File(path string) *FileResult {
	var result = new(FileResult)
	result.Status = 200
	result.context = this
	result.filePath = path
	return result
}

// 返回Json类型结果
func (this *Context) Json(data interface{}) *JsonResult {
	var result = new(JsonResult)
	result.Status = 200
	result.obj = data
	return result
}

// 返回Xml类型结果
func (this *Context) Xml(data interface{}) *XmlResult {
	var result = new(XmlResult)
	result.Status = 200
	result.obj = data
	return result
}

// 返回Api类型结果
func (this *Context) Api(data interface{}) HttpResult {
	switch this.Processor.Config.Api {
	case "json":
		{
			return this.Json(data)
		}
	case "xml":
		{
			return this.Xml(data)
		}
	default:
		{
			//检测请求头中是否包含指定的api格式
			//优先检测json格式,如果存在指定格式则返回指定格式
			//如果均不存在则返回json格式
			var accept = this.HttpContext.Request.Header.Get("Accept")
			if strings.Index(accept, "application/json") >= 0 {
				return this.Json(data)
			} else if strings.Index(accept, "application/xml") >= 0 {
				return this.Json(data)
			}
		}
	}
	return this.Json(data)
}

// 返回NotFound类型结果
func (this *Context) NotFound() *NotFoundResult {
	var result = new(NotFoundResult)
	result.Status = 404
	result.context = this
	return result
}

// 返回临时重定向类型结果
func (this *Context) Redirect(url string) *RedirectResult {
	var result = new(RedirectResult)
	result.Status = 302
	result.context = this
	result.url = url
	return result
}

// 返回永久重定向类型结果
func (this *Context) RedirectPermanently(url string) *RedirectResult {
	var result = new(RedirectResult)
	result.Status = 301
	result.context = this
	result.url = url
	return result
}

// 返回数据类型结果
func (this *Context) Data(data []byte) *DataResult {
	var result = new(DataResult)
	result.Status = 200
	result.data = data
	return result
}

// 返回视图类型结果
func (this *Context) View(path string, data ...interface{}) *ViewResult {
	var result = new(ViewResult)
	result.Status = 200
	result.templates = this.Processor.Templates
	result.path = path
	data = append(data, this.commonViewData())
	result.data = this.integrate(data...)
	return result
}

// 返回部分视图类型结果
func (this *Context) PartialView(path string, data ...interface{}) *PartialViewResult {
	var result = new(PartialViewResult)
	result.Status = 200
	result.templates = this.Processor.Templates
	result.path = path
	data = append(data, this.commonViewData())
	result.data = this.integrate(data...)
	return result
}

// commonViewData 生成公共视图数据
func (this *Context) commonViewData() ViewData {
	var vd = ViewData{}
	if this.Session != nil {
		vd["SESSION"] = NewTemplateSession(this.Session)
	}
	if this.CSRF != nil {
		vd["CSRF"] = NewTemplateCSRF(this.CSRF, this.Processor.Config.CSRFTokenName)
	}
	return vd
}

// integrate 将data的数据整合为一个ViewData
func (this *Context) integrate(data ...interface{}) ViewData {
	var result = make(ViewData)
	for _, output := range data {
		var outputType = reflect.TypeOf(output)
		if outputType.Kind() == reflect.Map {
			//添加Map
			var dataMap, ok = output.(ViewData)
			if ok {
				for k, v := range dataMap {
					result[k] = v
				}
			}
		} else if IsStructPtrType(outputType) {
			this.mapTo(reflect.ValueOf(output).Elem(), result)
		}
	}
	return result
}

// mapTo 将value结构体转换到一个ViewData中,value必须是结构体
func (this *Context) mapTo(value reflect.Value, data ViewData) {
	if value.Kind() == reflect.Struct {
		for i := 0; i < value.NumField(); i++ {
			var fieldValue = value.Field(i)
			if fieldValue.CanInterface() {
				var fieldType = value.Type().Field(i)
				if fieldType.Anonymous {
					//匿名组合字段,进行递归解析
					this.mapTo(fieldValue, data)
				} else {
					//非匿名字段
					data[fieldType.Name] = fieldValue.Interface()
				}
			}
		}
	}
}

// 重新分发,将当前请求交给另一个path处理
func (this *Context) Redispatch(path string) *UserDefinedResult {
	var result = new(UserDefinedResult)
	result.Status = StatusCodeRedispatch
	result.Msg = path
	return result
}

// ValidateCSRF 验证表单请求中是否存在csrf的token并且该token有效,验证后token立即失效
func (this *Context) ValidateCSRF() bool {
	if this.CSRF != nil {
		var token, err = this.ParamString(this.Processor.Config.CSRFTokenName)
		if err == nil {
			var t, ok = this.CSRF.Int(token)
			if ok {
				this.CSRF.Delete(token)
				return int(time.Now().Unix())-t <= this.Processor.Config.CSRFExpire
			}
		}
	}
	return false
}

// 模板会话信息
type TemplateSession struct {
	sess session.Session //当前会话
}

// NewTemplateSession 创建一个模板Session
func NewTemplateSession(sess session.Session) *TemplateSession {
	return &TemplateSession{sess}
}

// Contains 确认Session中是否包含该key
func (this *TemplateSession) Contains(key string) bool {
	var _, ok = this.sess.Value(key)
	return ok
}

// SetDefault 设置默认值,当且仅当key不存在时设置成功
func (this *TemplateSession) SetDefault(key string, value interface{}) string {
	if !this.Contains(key) {
		this.sess.SetValue(key, value)
	}
	return ""
}

// Value 获取值
func (this *TemplateSession) Value(key string) interface{} {
	var v, ok = this.sess.Value(key)
	if ok {
		return v
	}
	return nil
}

// String 获取字符串
func (this *TemplateSession) String(key string) string {
	var v, ok = this.sess.String(key)
	if ok {
		return v
	}
	return ""
}

//  Int 获取整数值
func (this *TemplateSession) Int(key string) int {
	var v, ok = this.sess.Int(key)
	if ok {
		return v
	}
	return 0
}

// Bool 获取bool值
func (this *TemplateSession) Bool(key string) bool {
	var v, ok = this.sess.Bool(key)
	if ok {
		return v
	}
	return false
}

// Float 获取浮点值
func (this *TemplateSession) Float(key string) float64 {
	var v, ok = this.sess.Float(key)
	if ok {
		return v
	}
	return 0.0
}

// 模板CSRF信息
type TemplateCSRF struct {
	sess session.Session //当前CSRF会话
	name string          //token字段名称
}

// NewTemplateCSRF 创建一个模板CSRF
func NewTemplateCSRF(sess session.Session, fieldName string) *TemplateCSRF {
	return &TemplateCSRF{sess, fieldName}
}

// Token 生成一个CSRF认证字符串
func (this *TemplateCSRF) Token() template.HTML {
	var token = util.NewUUID().Hex()
	this.sess.SetInt(token, int(time.Now().Unix())) //记录生成时间(秒)
	return template.HTML(token)
}

// Field 生成一个包含CSRF的隐藏域
func (this *TemplateCSRF) Field() template.HTML {
	return template.HTML(`<input type="hidden" name="` + this.name + `" value="` + string(this.Token()) + `" >`)
}
<|endoftext|>


<commit_before>// Copyright 2010 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 jsonrpc

import (
	"code.google.com/p/vitess/go/rpcplus"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"testing"
)

type Args struct {
	A, B int
}

type Reply struct {
	C int
}

type Arith int

func (t *Arith) Add(args *Args, reply *Reply) error {
	reply.C = args.A + args.B
	return nil
}

func (t *Arith) Mul(args *Args, reply *Reply) error {
	reply.C = args.A * args.B
	return nil
}

func (t *Arith) Div(args *Args, reply *Reply) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	reply.C = args.A / args.B
	return nil
}

func (t *Arith) Error(args *Args, reply *Reply) error {
	panic("ERROR")
}

func (t *Arith) Thrive(args *Args, sendReply func(reply interface{}) error) error {
	for i := 0; i < args.A; i++ {
		r := &Reply{C: i}
		err := sendReply(r)
		if err != nil {
			return err
		}
	}

	return nil
}

func init() {
	rpcplus.Register(new(Arith))
}

func TestServer(t *testing.T) {
	type addResp struct {
		Id     interface{} `json:"id"`
		Result Reply       `json:"result"`
		Error  interface{} `json:"error"`
	}

	cli, srv := net.Pipe()
	defer cli.Close()
	go ServeConn(srv)
	dec := json.NewDecoder(cli)

	// Send hand-coded requests to server, parse responses.
	for i := 0; i < 10; i++ {
		fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "\u%04d", "params": [{"A": %d, "B": %d}]}`, i, i, i+1)
		var resp addResp
		err := dec.Decode(&resp)
		if err != nil {
			t.Fatalf("Decode: %s", err)
		}
		if resp.Error != nil {
			t.Fatalf("resp.Error: %s", resp.Error)
		}
		if resp.Id.(string) != string(i) {
			t.Fatalf("resp: bad id %q want %q", resp.Id.(string), string(i))
		}
		if resp.Result.C != 2*i+1 {
			t.Fatalf("resp: bad result: %d+%d=%d", i, i+1, resp.Result.C)
		}
	}

	fmt.Fprintf(cli, "{}\n")
	var resp addResp
	if err := dec.Decode(&resp); err != nil {
		t.Fatalf("Decode after empty: %s", err)
	}
	if resp.Error == nil {
		t.Fatalf("Expected error, got nil")
	}
}

func TestClient(t *testing.T) {
	// Assume server is okay (TestServer is above).
	// Test client against server.
	cli, srv := net.Pipe()
	go ServeConn(srv)

	client := NewClient(cli)
	defer client.Close()

	// Synchronous calls
	args := &Args{7, 8}
	reply := new(Reply)
	err := client.Call("Arith.Add", args, reply)
	if err != nil {
		t.Errorf("Add: expected no error but got string %q", err.Error())
	}
	if reply.C != args.A+args.B {
		t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
	}

	args = &Args{7, 8}
	reply = new(Reply)
	err = client.Call("Arith.Mul", args, reply)
	if err != nil {
		t.Errorf("Mul: expected no error but got string %q", err.Error())
	}
	if reply.C != args.A*args.B {
		t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B)
	}

	// Out of order.
	args = &Args{7, 8}
	mulReply := new(Reply)
	mulCall := client.Go("Arith.Mul", args, mulReply, nil)
	addReply := new(Reply)
	addCall := client.Go("Arith.Add", args, addReply, nil)

	addCall = <-addCall.Done
	if addCall.Error != nil {
		t.Errorf("Add: expected no error but got string %q", addCall.Error.Error())
	}
	if addReply.C != args.A+args.B {
		t.Errorf("Add: expected %d got %d", addReply.C, args.A+args.B)
	}

	mulCall = <-mulCall.Done
	if mulCall.Error != nil {
		t.Errorf("Mul: expected no error but got string %q", mulCall.Error.Error())
	}
	if mulReply.C != args.A*args.B {
		t.Errorf("Mul: expected %d got %d", mulReply.C, args.A*args.B)
	}

	// Error test
	args = &Args{7, 0}
	reply = new(Reply)
	err = client.Call("Arith.Div", args, reply)
	// expect an error: zero divide
	if err == nil {
		t.Error("Div: expected error")
	} else if err.Error() != "divide by zero" {
		t.Error("Div: expected divide by zero error; got", err)
	}
}

func TestMalformedInput(t *testing.T) {
	cli, srv := net.Pipe()
	go cli.Write([]byte(`{id:1}`)) // invalid json
	ServeConn(srv)                 // must return, not loop
}

func TestUnexpectedError(t *testing.T) {
	cli, srv := myPipe()
	go cli.PipeWriter.CloseWithError(errors.New("unexpected error!")) // reader will get this error
	ServeConn(srv)                                                    // must return, not loop
}

func TestStreamingCall(t *testing.T) {
	// Assume server is okay (TestServer is above).
	// Test client against server.
	cli, srv := net.Pipe()
	go ServeConn(srv)

	client := NewClient(cli)
	defer client.Close()

	args := &Args{7, 0}
	rowChan := make(chan *Reply, 10)
	c := client.StreamGo("Arith.Thrive", args, rowChan)

	// fetch all the rows
	count := 0
	for row := range rowChan {
		if row.C != count {
			t.Fatal("unexpected value:", row.C)
		}
		count += 1

		// log.Println("Values: ", row)
	}

	if c.Error != nil {
		t.Fatal("unexpected error:", c.Error.Error())
	}

	if count != 7 {
		t.Fatal("Didn't receive the right number of packets back:", count)
	}
}

// Copied from package net.
func myPipe() (*pipe, *pipe) {
	r1, w1 := io.Pipe()
	r2, w2 := io.Pipe()

	return &pipe{r1, w2}, &pipe{r2, w1}
}

type pipe struct {
	*io.PipeReader
	*io.PipeWriter
}

type pipeAddr int

func (pipeAddr) Network() string {
	return "pipe"
}

func (pipeAddr) String() string {
	return "pipe"
}

func (p *pipe) Close() error {
	err := p.PipeReader.Close()
	err1 := p.PipeWriter.Close()
	if err == nil {
		err = err1
	}
	return err
}

func (p *pipe) LocalAddr() net.Addr {
	return pipeAddr(0)
}

func (p *pipe) RemoteAddr() net.Addr {
	return pipeAddr(0)
}

func (p *pipe) SetTimeout(nsec int64) error {
	return errors.New("net.Pipe does not support timeouts")
}

func (p *pipe) SetReadTimeout(nsec int64) error {
	return errors.New("net.Pipe does not support timeouts")
}

func (p *pipe) SetWriteTimeout(nsec int64) error {
	return errors.New("net.Pipe does not support timeouts")
}
<commit_msg>Fix jsonrpc tests<commit_after>// Copyright 2010 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 jsonrpc

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"testing"

	"github.com/flynn/rpcplus"
)

type Args struct {
	A, B int
}

type Reply struct {
	C int
}

type Arith int

func (t *Arith) Add(args *Args, reply *Reply) error {
	reply.C = args.A + args.B
	return nil
}

func (t *Arith) Mul(args *Args, reply *Reply) error {
	reply.C = args.A * args.B
	return nil
}

func (t *Arith) Div(args *Args, reply *Reply) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	reply.C = args.A / args.B
	return nil
}

func (t *Arith) Error(args *Args, reply *Reply) error {
	panic("ERROR")
}

func (t *Arith) Thrive(args *Args, stream rpcplus.Stream) error {
	for i := 0; i < args.A; i++ {
		stream.Send <- &Reply{C: i}
	}
	return nil
}

func init() {
	rpcplus.Register(new(Arith))
}

func TestServer(t *testing.T) {
	type addResp struct {
		Id     interface{} `json:"id"`
		Result Reply       `json:"result"`
		Error  interface{} `json:"error"`
	}

	cli, srv := net.Pipe()
	defer cli.Close()
	go ServeConn(srv)
	dec := json.NewDecoder(cli)

	// Send hand-coded requests to server, parse responses.
	for i := 0; i < 10; i++ {
		fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "\u%04d", "params": [{"A": %d, "B": %d}]}`, i, i, i+1)
		var resp addResp
		err := dec.Decode(&resp)
		if err != nil {
			t.Fatalf("Decode: %s", err)
		}
		if resp.Error != nil {
			t.Fatalf("resp.Error: %s", resp.Error)
		}
		if resp.Id.(string) != string(i) {
			t.Fatalf("resp: bad id %q want %q", resp.Id.(string), string(i))
		}
		if resp.Result.C != 2*i+1 {
			t.Fatalf("resp: bad result: %d+%d=%d", i, i+1, resp.Result.C)
		}
	}

	fmt.Fprintf(cli, "{}\n")
	var resp addResp
	if err := dec.Decode(&resp); err != nil {
		t.Fatalf("Decode after empty: %s", err)
	}
	if resp.Error == nil {
		t.Fatalf("Expected error, got nil")
	}
}

func TestClient(t *testing.T) {
	// Assume server is okay (TestServer is above).
	// Test client against server.
	cli, srv := net.Pipe()
	go ServeConn(srv)

	client := NewClient(cli)
	defer client.Close()

	// Synchronous calls
	args := &Args{7, 8}
	reply := new(Reply)
	err := client.Call("Arith.Add", args, reply)
	if err != nil {
		t.Errorf("Add: expected no error but got string %q", err.Error())
	}
	if reply.C != args.A+args.B {
		t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
	}

	args = &Args{7, 8}
	reply = new(Reply)
	err = client.Call("Arith.Mul", args, reply)
	if err != nil {
		t.Errorf("Mul: expected no error but got string %q", err.Error())
	}
	if reply.C != args.A*args.B {
		t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B)
	}

	// Out of order.
	args = &Args{7, 8}
	mulReply := new(Reply)
	mulCall := client.Go("Arith.Mul", args, mulReply, nil)
	addReply := new(Reply)
	addCall := client.Go("Arith.Add", args, addReply, nil)

	addCall = <-addCall.Done
	if addCall.Error != nil {
		t.Errorf("Add: expected no error but got string %q", addCall.Error.Error())
	}
	if addReply.C != args.A+args.B {
		t.Errorf("Add: expected %d got %d", addReply.C, args.A+args.B)
	}

	mulCall = <-mulCall.Done
	if mulCall.Error != nil {
		t.Errorf("Mul: expected no error but got string %q", mulCall.Error.Error())
	}
	if mulReply.C != args.A*args.B {
		t.Errorf("Mul: expected %d got %d", mulReply.C, args.A*args.B)
	}

	// Error test
	args = &Args{7, 0}
	reply = new(Reply)
	err = client.Call("Arith.Div", args, reply)
	// expect an error: zero divide
	if err == nil {
		t.Error("Div: expected error")
	} else if err.Error() != "divide by zero" {
		t.Error("Div: expected divide by zero error; got", err)
	}
}

func TestMalformedInput(t *testing.T) {
	cli, srv := net.Pipe()
	go cli.Write([]byte(`{id:1}`)) // invalid json
	ServeConn(srv)                 // must return, not loop
}

func TestUnexpectedError(t *testing.T) {
	cli, srv := myPipe()
	go cli.PipeWriter.CloseWithError(errors.New("unexpected error!")) // reader will get this error
	ServeConn(srv)                                                    // must return, not loop
}

func TestStreamingCall(t *testing.T) {
	// Assume server is okay (TestServer is above).
	// Test client against server.
	cli, srv := net.Pipe()
	go ServeConn(srv)

	client := NewClient(cli)
	defer client.Close()

	args := &Args{7, 0}
	rowChan := make(chan *Reply, 10)
	c := client.StreamGo("Arith.Thrive", args, rowChan)

	// fetch all the rows
	count := 0
	for row := range rowChan {
		if row.C != count {
			t.Fatal("unexpected value:", row.C)
		}
		count += 1

		// log.Println("Values: ", row)
	}

	if c.Error != nil {
		t.Fatal("unexpected error:", c.Error.Error())
	}

	if count != 7 {
		t.Fatal("Didn't receive the right number of packets back:", count)
	}
}

// Copied from package net.
func myPipe() (*pipe, *pipe) {
	r1, w1 := io.Pipe()
	r2, w2 := io.Pipe()

	return &pipe{r1, w2}, &pipe{r2, w1}
}

type pipe struct {
	*io.PipeReader
	*io.PipeWriter
}

type pipeAddr int

func (pipeAddr) Network() string {
	return "pipe"
}

func (pipeAddr) String() string {
	return "pipe"
}

func (p *pipe) Close() error {
	err := p.PipeReader.Close()
	err1 := p.PipeWriter.Close()
	if err == nil {
		err = err1
	}
	return err
}

func (p *pipe) LocalAddr() net.Addr {
	return pipeAddr(0)
}

func (p *pipe) RemoteAddr() net.Addr {
	return pipeAddr(0)
}

func (p *pipe) SetTimeout(nsec int64) error {
	return errors.New("net.Pipe does not support timeouts")
}

func (p *pipe) SetReadTimeout(nsec int64) error {
	return errors.New("net.Pipe does not support timeouts")
}

func (p *pipe) SetWriteTimeout(nsec int64) error {
	return errors.New("net.Pipe does not support timeouts")
}
<|endoftext|>