<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * rpc/handler.go                                         *
 *                                                        *
 * hprose handler manager for Go.                         *
 *                                                        *
 * LastModified: Oct 2, 2016                              *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package rpc

import "reflect"

// NextInvokeHandler is the next invoke handler function
type NextInvokeHandler func(
	name string,
	args []reflect.Value,
	context Context) (results []reflect.Value, err error)

// InvokeHandler is the invoke handler function
type InvokeHandler func(
	name string,
	args []reflect.Value,
	context Context,
	next NextInvokeHandler) (results []reflect.Value, err error)

// NextFilterHandler is the next filter handler function
type NextFilterHandler func(
	request []byte,
	context Context) (response []byte, err error)

// FilterHandler is the filter handler function
type FilterHandler func(
	request []byte,
	context Context,
	next NextFilterHandler) (response []byte, err error)

// handlerManager is the hprose handler manager
type handlerManager struct {
	invokeHandlers             []InvokeHandler
	beforeFilterHandlers       []FilterHandler
	afterFilterHandlers        []FilterHandler
	defaultInvokeHandler       NextInvokeHandler
	defaultBeforeFilterHandler NextFilterHandler
	defaultAfterFilterHandler  NextFilterHandler
	invokeHandler              NextInvokeHandler
	beforeFilterHandler        NextFilterHandler
	afterFilterHandler         NextFilterHandler
	override                   struct {
		invokeHandler       NextInvokeHandler
		beforeFilterHandler NextFilterHandler
		afterFilterHandler  NextFilterHandler
	}
}

func (hm *handlerManager) initHandlerManager() {
	hm.defaultInvokeHandler = func(
		name string,
		args []reflect.Value,
		context Context) (results []reflect.Value, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return hm.override.invokeHandler(name, args, context)
	}
	hm.defaultBeforeFilterHandler = func(
		request []byte,
		context Context) (response []byte, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return hm.override.beforeFilterHandler(request, context)
	}
	hm.defaultAfterFilterHandler = func(
		request []byte,
		context Context) (response []byte, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return hm.override.afterFilterHandler(request, context)
	}
	hm.invokeHandler = hm.defaultInvokeHandler
	hm.beforeFilterHandler = hm.defaultBeforeFilterHandler
	hm.afterFilterHandler = hm.defaultAfterFilterHandler
	return
}

func getNextInvokeHandler(
	next NextInvokeHandler, handler InvokeHandler) NextInvokeHandler {
	return func(name string,
		args []reflect.Value,
		context Context) (results []reflect.Value, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return handler(name, args, context, next)
	}
}

func getNextFilterHandler(
	next NextFilterHandler, handler FilterHandler) NextFilterHandler {
	return func(request []byte, context Context) (response []byte, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return handler(request, context, next)
	}
}

// AddInvokeHandler add the invoke handler
func (hm *handlerManager) AddInvokeHandler(handler ...InvokeHandler) {
	if len(handler) == 0 {
		return
	}
	hm.invokeHandlers = append(hm.invokeHandlers, handler...)
	next := hm.defaultInvokeHandler
	for i := len(hm.invokeHandlers) - 1; i >= 0; i-- {
		next = getNextInvokeHandler(next, hm.invokeHandlers[i])
	}
	hm.invokeHandler = next
}

// AddBeforeFilterHandler add the filter handler before filters
func (hm *handlerManager) AddBeforeFilterHandler(handler ...FilterHandler) {
	if len(handler) == 0 {
		return
	}
	hm.beforeFilterHandlers = append(hm.beforeFilterHandlers, handler...)
	next := hm.defaultBeforeFilterHandler
	for i := len(hm.beforeFilterHandlers) - 1; i >= 0; i-- {
		next = getNextFilterHandler(next, hm.beforeFilterHandlers[i])
	}
	hm.beforeFilterHandler = next
}

// AddAfterFilterHandler add the filter handler after filters
func (hm *handlerManager) AddAfterFilterHandler(handler ...FilterHandler) {
	if len(handler) == 0 {
		return
	}
	hm.afterFilterHandlers = append(hm.afterFilterHandlers, handler...)
	next := hm.defaultAfterFilterHandler
	for i := len(hm.afterFilterHandlers) - 1; i >= 0; i-- {
		next = getNextFilterHandler(next, hm.afterFilterHandlers[i])
	}
	hm.afterFilterHandler = next
}
<commit_msg>Added Use method<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * rpc/handler.go                                         *
 *                                                        *
 * hprose handler manager for Go.                         *
 *                                                        *
 * LastModified: Oct 18, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package rpc

import "reflect"

// NextInvokeHandler is the next invoke handler function
type NextInvokeHandler func(
	name string,
	args []reflect.Value,
	context Context) (results []reflect.Value, err error)

// InvokeHandler is the invoke handler function
type InvokeHandler func(
	name string,
	args []reflect.Value,
	context Context,
	next NextInvokeHandler) (results []reflect.Value, err error)

// NextFilterHandler is the next filter handler function
type NextFilterHandler func(
	request []byte,
	context Context) (response []byte, err error)

// FilterHandler is the filter handler function
type FilterHandler func(
	request []byte,
	context Context,
	next NextFilterHandler) (response []byte, err error)

type addFilterHandler struct {
	Use func(handler ...FilterHandler)
}

// handlerManager is the hprose handler manager
type handlerManager struct {
	BeforeFilter               addFilterHandler
	AfterFilter                addFilterHandler
	invokeHandlers             []InvokeHandler
	beforeFilterHandlers       []FilterHandler
	afterFilterHandlers        []FilterHandler
	defaultInvokeHandler       NextInvokeHandler
	defaultBeforeFilterHandler NextFilterHandler
	defaultAfterFilterHandler  NextFilterHandler
	invokeHandler              NextInvokeHandler
	beforeFilterHandler        NextFilterHandler
	afterFilterHandler         NextFilterHandler
	override                   struct {
		invokeHandler       NextInvokeHandler
		beforeFilterHandler NextFilterHandler
		afterFilterHandler  NextFilterHandler
	}
}

func (hm *handlerManager) initHandlerManager() {
	hm.BeforeFilter.Use = hm.AddBeforeFilterHandler
	hm.AfterFilter.Use = hm.AddAfterFilterHandler
	hm.defaultInvokeHandler = func(
		name string,
		args []reflect.Value,
		context Context) (results []reflect.Value, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return hm.override.invokeHandler(name, args, context)
	}
	hm.defaultBeforeFilterHandler = func(
		request []byte,
		context Context) (response []byte, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return hm.override.beforeFilterHandler(request, context)
	}
	hm.defaultAfterFilterHandler = func(
		request []byte,
		context Context) (response []byte, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return hm.override.afterFilterHandler(request, context)
	}
	hm.invokeHandler = hm.defaultInvokeHandler
	hm.beforeFilterHandler = hm.defaultBeforeFilterHandler
	hm.afterFilterHandler = hm.defaultAfterFilterHandler
	return
}

func getNextInvokeHandler(
	next NextInvokeHandler, handler InvokeHandler) NextInvokeHandler {
	return func(name string,
		args []reflect.Value,
		context Context) (results []reflect.Value, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return handler(name, args, context, next)
	}
}

func getNextFilterHandler(
	next NextFilterHandler, handler FilterHandler) NextFilterHandler {
	return func(request []byte, context Context) (response []byte, err error) {
		defer func() {
			if e := recover(); e != nil {
				err = NewPanicError(e)
			}
		}()
		return handler(request, context, next)
	}
}

// AddInvokeHandler add the invoke handler
func (hm *handlerManager) AddInvokeHandler(handler ...InvokeHandler) {
	if len(handler) == 0 {
		return
	}
	hm.invokeHandlers = append(hm.invokeHandlers, handler...)
	next := hm.defaultInvokeHandler
	for i := len(hm.invokeHandlers) - 1; i >= 0; i-- {
		next = getNextInvokeHandler(next, hm.invokeHandlers[i])
	}
	hm.invokeHandler = next
}

// AddBeforeFilterHandler add the filter handler before filters
func (hm *handlerManager) AddBeforeFilterHandler(handler ...FilterHandler) {
	if len(handler) == 0 {
		return
	}
	hm.beforeFilterHandlers = append(hm.beforeFilterHandlers, handler...)
	next := hm.defaultBeforeFilterHandler
	for i := len(hm.beforeFilterHandlers) - 1; i >= 0; i-- {
		next = getNextFilterHandler(next, hm.beforeFilterHandlers[i])
	}
	hm.beforeFilterHandler = next
}

// AddAfterFilterHandler add the filter handler after filters
func (hm *handlerManager) AddAfterFilterHandler(handler ...FilterHandler) {
	if len(handler) == 0 {
		return
	}
	hm.afterFilterHandlers = append(hm.afterFilterHandlers, handler...)
	next := hm.defaultAfterFilterHandler
	for i := len(hm.afterFilterHandlers) - 1; i >= 0; i-- {
		next = getNextFilterHandler(next, hm.afterFilterHandlers[i])
	}
	hm.afterFilterHandler = next
}

// Use is a method alias of AddInvokeHandler
func (hm *handlerManager) Use(handler ...InvokeHandler) {
	hm.AddInvokeHandler(handler...)
}
<|endoftext|>


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

// root-ls lists the content of a ROOT file.
//
// Usage: root-ls [options] file1.root [file2.root [...]]
//
// ex:
//
//  $> root-ls -t ./testdata/graphs.root ./testdata/small-flat-tree.root
//  === [./testdata/graphs.root] ===
//  version: 60806
//  TGraph            tg      graph without errors         (cycle=1)
//  TGraphErrors      tge     graph with errors            (cycle=1)
//  TGraphAsymmErrors tgae    graph with asymmetric errors (cycle=1)
//
//  === [./testdata/small-flat-tree.root] ===
//  version: 60804
//  TTree          tree                 my tree title (entries=100)
//    Int32        "Int32/I"            TBranch
//    Int64        "Int64/L"            TBranch
//    UInt32       "UInt32/i"           TBranch
//    UInt64       "UInt64/l"           TBranch
//    Float32      "Float32/F"          TBranch
//    Float64      "Float64/D"          TBranch
//    ArrayInt32   "ArrayInt32[10]/I"   TBranch
//    ArrayInt64   "ArrayInt64[10]/L"   TBranch
//    ArrayUInt32  "ArrayInt32[10]/i"   TBranch
//    ArrayUInt64  "ArrayInt64[10]/l"   TBranch
//    ArrayFloat32 "ArrayFloat32[10]/F" TBranch
//    ArrayFloat64 "ArrayFloat64[10]/D" TBranch
//    N            "N/I"                TBranch
//    SliceInt32   "SliceInt32[N]/I"    TBranch
//    SliceInt64   "SliceInt64[N]/L"    TBranch
//    SliceUInt32  "SliceInt32[N]/i"    TBranch
//    SliceUInt64  "SliceInt64[N]/l"    TBranch
//    SliceFloat32 "SliceFloat32[N]/F"  TBranch
//    SliceFloat64 "SliceFloat64[N]/D"  TBranch
//
package main // import "go-hep.org/x/hep/groot/cmd/root-ls"

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime/pprof"
	"text/tabwriter"

	"github.com/pkg/errors"
	"go-hep.org/x/hep/groot"
	"go-hep.org/x/hep/groot/riofs"
	"go-hep.org/x/hep/groot/rtree"
	_ "go-hep.org/x/hep/groot/ztypes"
)

var (
	doProf = flag.String("profile", "", "filename of cpuprofile")
	doSI   = flag.Bool("sinfos", false, "print StreamerInfos")
	doTree = flag.Bool("t", false, "print Tree(s) (recursively)")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(
			os.Stderr,
			`Usage: root-ls [options] file1.root [file2.root [...]]

ex:
 $> root-ls ./testdata/graphs.root

options:
`,
		)
		flag.PrintDefaults()
	}

	flag.Parse()

	if *doProf != "" {
		f, err := os.Create(*doProf)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if flag.NArg() <= 0 {
		fmt.Fprintf(os.Stderr, "error: you need to give a ROOT file\n\n")
		flag.Usage()
		os.Exit(1)
	}

	cmd := rootls{
		stdout:    os.Stdout,
		streamers: *doSI,
		trees:     *doTree,
	}

	for ii, fname := range flag.Args() {

		if ii > 0 {
			fmt.Fprintf(cmd.stdout, "\n")
		}
		err := cmd.ls(fname)
		if err != nil {
			log.Printf("%+v", err)
			os.Exit(1)
		}
	}
}

type rootls struct {
	stdout    io.Writer
	streamers bool
	trees     bool
}

func (ls rootls) ls(fname string) error {
	fmt.Fprintf(ls.stdout, "=== [%s] ===\n", fname)
	f, err := groot.Open(fname)
	if err != nil {
		return errors.Wrapf(err, "could not open file")
	}
	defer f.Close()
	fmt.Fprintf(ls.stdout, "version: %v\n", f.Version())
	if ls.streamers {
		fmt.Fprintf(ls.stdout, "streamer-infos:\n")
		sinfos := f.StreamerInfos()
		for _, v := range sinfos {
			name := v.Name()
			fmt.Fprintf(ls.stdout, " StreamerInfo for %q version=%d title=%q\n", name, v.ClassVersion(), v.Title())
			w := tabwriter.NewWriter(ls.stdout, 8, 4, 1, ' ', 0)
			for _, elm := range v.Elements() {
				fmt.Fprintf(w, "  %s\t%s\toffset=%3d\ttype=%3d\tsize=%3d\t %s\n", elm.TypeName(), elm.Name(), elm.Offset(), elm.Type(), elm.Size(), elm.Title())
			}
			w.Flush()
		}
		fmt.Fprintf(ls.stdout, "---\n")
	}

	w := tabwriter.NewWriter(ls.stdout, 8, 4, 1, ' ', 0)
	for _, k := range f.Keys() {
		ls.walk(w, k)
	}
	w.Flush()

	return nil
}

func (ls rootls) walk(w io.Writer, k riofs.Key) {
	obj := k.Value()
	if ls.trees {
		tree, ok := obj.(rtree.Tree)
		if ok {
			w := newWindent(2, w)
			fmt.Fprintf(w, "%s\t%s\t%s\t(entries=%d)\n", k.ClassName(), k.Name(), k.Title(), tree.Entries())
			displayBranches(w, tree, 2)
			w.Flush()
			return
		}
	}
	fmt.Fprintf(w, "%s\t%s\t%s\t(cycle=%d)\n", k.ClassName(), k.Name(), k.Title(), k.Cycle())
	if dir, ok := obj.(riofs.Directory); ok {
		w := newWindent(2, w)
		for _, k := range dir.Keys() {
			ls.walk(w, k)
		}
		w.Flush()
	}
}

type windent struct {
	hdr []byte
	w   io.Writer
}

func newWindent(n int, w io.Writer) *windent {
	return &windent{
		hdr: bytes.Repeat([]byte(" "), n),
		w:   w,
	}
}

func (w *windent) Write(data []byte) (int, error) {
	return w.w.Write(append(w.hdr, data...))
}

func (w *windent) Flush() error {
	ww, ok := w.w.(flusher)
	if !ok {
		return nil
	}
	return ww.Flush()
}

type flusher interface {
	Flush() error
}

type brancher interface {
	Branches() []rtree.Branch
}

func displayBranches(w io.Writer, bres brancher, indent int) {
	branches := bres.Branches()
	if len(branches) <= 0 {
		return
	}
	ww := newWindent(indent, w)
	for _, b := range branches {
		fmt.Fprintf(ww, "%s\t%q\t%v\n", b.Name(), b.Title(), b.Class())
		displayBranches(ww, b, 2)
	}
	ww.Flush()
}
<commit_msg>groot/cmd/root-ls: lazily load data off disk<commit_after>// Copyright 2017 The go-hep Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// root-ls lists the content of a ROOT file.
//
// Usage: root-ls [options] file1.root [file2.root [...]]
//
// ex:
//
//  $> root-ls -t ./testdata/graphs.root ./testdata/small-flat-tree.root
//  === [./testdata/graphs.root] ===
//  version: 60806
//  TGraph            tg      graph without errors         (cycle=1)
//  TGraphErrors      tge     graph with errors            (cycle=1)
//  TGraphAsymmErrors tgae    graph with asymmetric errors (cycle=1)
//
//  === [./testdata/small-flat-tree.root] ===
//  version: 60804
//  TTree          tree                 my tree title (entries=100)
//    Int32        "Int32/I"            TBranch
//    Int64        "Int64/L"            TBranch
//    UInt32       "UInt32/i"           TBranch
//    UInt64       "UInt64/l"           TBranch
//    Float32      "Float32/F"          TBranch
//    Float64      "Float64/D"          TBranch
//    ArrayInt32   "ArrayInt32[10]/I"   TBranch
//    ArrayInt64   "ArrayInt64[10]/L"   TBranch
//    ArrayUInt32  "ArrayInt32[10]/i"   TBranch
//    ArrayUInt64  "ArrayInt64[10]/l"   TBranch
//    ArrayFloat32 "ArrayFloat32[10]/F" TBranch
//    ArrayFloat64 "ArrayFloat64[10]/D" TBranch
//    N            "N/I"                TBranch
//    SliceInt32   "SliceInt32[N]/I"    TBranch
//    SliceInt64   "SliceInt64[N]/L"    TBranch
//    SliceUInt32  "SliceInt32[N]/i"    TBranch
//    SliceUInt64  "SliceInt64[N]/l"    TBranch
//    SliceFloat32 "SliceFloat32[N]/F"  TBranch
//    SliceFloat64 "SliceFloat64[N]/D"  TBranch
//
package main // import "go-hep.org/x/hep/groot/cmd/root-ls"

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime/pprof"
	"text/tabwriter"

	"github.com/pkg/errors"
	"go-hep.org/x/hep/groot"
	"go-hep.org/x/hep/groot/riofs"
	"go-hep.org/x/hep/groot/rtree"
	_ "go-hep.org/x/hep/groot/ztypes"
)

var (
	doProf = flag.String("profile", "", "filename of cpuprofile")
	doSI   = flag.Bool("sinfos", false, "print StreamerInfos")
	doTree = flag.Bool("t", false, "print Tree(s) (recursively)")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(
			os.Stderr,
			`Usage: root-ls [options] file1.root [file2.root [...]]

ex:
 $> root-ls ./testdata/graphs.root

options:
`,
		)
		flag.PrintDefaults()
	}

	flag.Parse()

	if *doProf != "" {
		f, err := os.Create(*doProf)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if flag.NArg() <= 0 {
		fmt.Fprintf(os.Stderr, "error: you need to give a ROOT file\n\n")
		flag.Usage()
		os.Exit(1)
	}

	cmd := rootls{
		stdout:    os.Stdout,
		streamers: *doSI,
		trees:     *doTree,
	}

	for ii, fname := range flag.Args() {

		if ii > 0 {
			fmt.Fprintf(cmd.stdout, "\n")
		}
		err := cmd.ls(fname)
		if err != nil {
			log.Printf("%+v", err)
			os.Exit(1)
		}
	}
}

type rootls struct {
	stdout    io.Writer
	streamers bool
	trees     bool
}

func (ls rootls) ls(fname string) error {
	fmt.Fprintf(ls.stdout, "=== [%s] ===\n", fname)
	f, err := groot.Open(fname)
	if err != nil {
		return errors.Wrapf(err, "could not open file")
	}
	defer f.Close()
	fmt.Fprintf(ls.stdout, "version: %v\n", f.Version())
	if ls.streamers {
		fmt.Fprintf(ls.stdout, "streamer-infos:\n")
		sinfos := f.StreamerInfos()
		for _, v := range sinfos {
			name := v.Name()
			fmt.Fprintf(ls.stdout, " StreamerInfo for %q version=%d title=%q\n", name, v.ClassVersion(), v.Title())
			w := tabwriter.NewWriter(ls.stdout, 8, 4, 1, ' ', 0)
			for _, elm := range v.Elements() {
				fmt.Fprintf(w, "  %s\t%s\toffset=%3d\ttype=%3d\tsize=%3d\t %s\n", elm.TypeName(), elm.Name(), elm.Offset(), elm.Type(), elm.Size(), elm.Title())
			}
			w.Flush()
		}
		fmt.Fprintf(ls.stdout, "---\n")
	}

	w := tabwriter.NewWriter(ls.stdout, 8, 4, 1, ' ', 0)
	for _, k := range f.Keys() {
		ls.walk(w, k)
	}
	w.Flush()

	return nil
}

func (ls rootls) walk(w io.Writer, k riofs.Key) {
	if ls.trees && isTreelike(k.ClassName()) {
		obj := k.Value()
		tree, ok := obj.(rtree.Tree)
		if ok {
			w := newWindent(2, w)
			fmt.Fprintf(w, "%s\t%s\t%s\t(entries=%d)\n", k.ClassName(), k.Name(), k.Title(), tree.Entries())
			displayBranches(w, tree, 2)
			w.Flush()
			return
		}
	}
	fmt.Fprintf(w, "%s\t%s\t%s\t(cycle=%d)\n", k.ClassName(), k.Name(), k.Title(), k.Cycle())
	if isDirlike(k.ClassName()) {
		obj := k.Value()
		if dir, ok := obj.(riofs.Directory); ok {
			w := newWindent(2, w)
			for _, k := range dir.Keys() {
				ls.walk(w, k)
			}
			w.Flush()
		}
	}
}

func isDirlike(class string) bool {
	switch class {
	case "TDirectory", "TDirectoryFile":
		return true
	}
	return false
}

func isTreelike(class string) bool {
	switch class {
	case "TTree", "TTreeSQL", "TChain", "TNtuple", "TNtupleD":
		return true
	}
	return false
}

type windent struct {
	hdr []byte
	w   io.Writer
}

func newWindent(n int, w io.Writer) *windent {
	return &windent{
		hdr: bytes.Repeat([]byte(" "), n),
		w:   w,
	}
}

func (w *windent) Write(data []byte) (int, error) {
	return w.w.Write(append(w.hdr, data...))
}

func (w *windent) Flush() error {
	ww, ok := w.w.(flusher)
	if !ok {
		return nil
	}
	return ww.Flush()
}

type flusher interface {
	Flush() error
}

type brancher interface {
	Branches() []rtree.Branch
}

func displayBranches(w io.Writer, bres brancher, indent int) {
	branches := bres.Branches()
	if len(branches) <= 0 {
		return
	}
	ww := newWindent(indent, w)
	for _, b := range branches {
		fmt.Fprintf(ww, "%s\t%q\t%v\n", b.Name(), b.Title(), b.Class())
		displayBranches(ww, b, 2)
	}
	ww.Flush()
}
<|endoftext|>


<commit_before>package concurrent

import (
	"fmt"
	//"reflect"
	"runtime"
	"strconv"
	"sync"
	"testing"
)

var (
	listN  int
	number int
	list   [][]interface{}
	readCM *ConcurrentMap
	readLM *lockMap
	readM  map[interface{}]interface{}
)

func init() {
	MAXPROCS := runtime.NumCPU()
	runtime.GOMAXPROCS(MAXPROCS)
	listN = MAXPROCS + 1
	number = 1000
	fmt.Println("MAXPROCS is ", MAXPROCS, ", listN is", listN, ", n is ", number, "\n")

	list = make([][]interface{}, listN, listN)
	for i := 0; i < listN; i++ {
		list1 := make([]interface{}, 0, number)
		for j := 0; j < number; j++ {
			list1 = append(list1, j+(i)*number/10)
		}
		list[i] = list1
	}

	readCM = NewConcurrentMap()
	readM = make(map[interface{}]interface{})
	readLM = newLockMap()
	for i := range list[0] {
		readCM.Put(i, i)
		readLM.put(i, i)
		readM[i] = i
	}
}

type lockMap struct {
	m  map[interface{}]interface{}
	rw *sync.RWMutex
}

func (t *lockMap) put(k interface{}, v interface{}) {
	t.rw.Lock()
	defer t.rw.Unlock()
	t.m[k] = v
}

func (t *lockMap) putIfNotExist(k interface{}, v interface{}) (ok bool) {
	t.rw.Lock()
	defer t.rw.Unlock()
	if _, ok = t.m[k]; !ok {
		t.m[k] = v
	}
	return
}

func (t *lockMap) get(k interface{}) (v interface{}, ok bool) {
	t.rw.RLock()
	defer t.rw.RUnlock()
	v, ok = t.m[k]
	return
}

func (t *lockMap) len() int {
	t.rw.RLock()
	defer t.rw.RUnlock()
	return len(t.m)

}

func newLockMap() *lockMap {
	return &lockMap{make(map[interface{}]interface{}), new(sync.RWMutex)}
}

func newLockMap1(initCap int) *lockMap {
	return &lockMap{make(map[interface{}]interface{}, initCap), new(sync.RWMutex)}
}

func BenchmarkLockMapPut(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := newLockMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.put(j, j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapPut(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := make(map[interface{}]interface{})

		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for _, j := range list[i] {
				cm[j] = j
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapPut(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := NewConcurrentMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.Put(j, j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkLockMapPutNoGrow(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := newLockMap1(listN * number)

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.put(j, j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapPutNoGrow(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := make(map[interface{}]interface{}, listN*number)

		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for _, j := range list[i] {
				cm[j] = j
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapPutNoGrow(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := NewConcurrentMap(listN * number)

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.Put(j, j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkLockMapPut2(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := newLockMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.put(strconv.Itoa(j.(int)), j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapPut2(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := make(map[interface{}]interface{})

		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for _, j := range list[i] {
				cm[strconv.Itoa(j.(int))] = j
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapPut2(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := NewConcurrentMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.Put(strconv.Itoa(j.(int)), j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkLockMapGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			go func() {
				//itr := NewMapIterator(cm)
				//for itr.HasNext() {
				//	entry := itr.NextEntry()
				//	k := entry.key.(string)
				//	v := entry.value.(int)
				for k := range list[0] {
					_, _ = readLM.get(k)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for k := range list[0] {
				_, _ = readM[k]
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			go func() {
				for k := range list[0] {
					_, _ = readCM.Get(k)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkLockMapPutAndGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := newLockMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.put(j, j)
					_, _ = cm.get(j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapPutAndGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := make(map[interface{}]interface{})

		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for _, j := range list[i] {
				cm[j] = j
				_ = cm[j]
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapPutAndGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := NewConcurrentMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.Put(j, j)
					_, _ = cm.Get(j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}
<commit_msg>update test case<commit_after>package concurrent

import (
	"fmt"
	//"reflect"
	"runtime"
	"strconv"
	"sync"
	"testing"
)

var (
	listN  int
	number int
	list   [][]interface{}
	readCM *ConcurrentMap
	readLM *lockMap
	readM  map[interface{}]interface{}
)

func init() {
	MAXPROCS := runtime.NumCPU()
	runtime.GOMAXPROCS(MAXPROCS)
	listN = MAXPROCS + 1
	number = 100000
	fmt.Println("MAXPROCS is ", MAXPROCS, ", listN is", listN, ", n is ", number, "\n")

	list = make([][]interface{}, listN, listN)
	for i := 0; i < listN; i++ {
		list1 := make([]interface{}, 0, number)
		for j := 0; j < number; j++ {
			list1 = append(list1, j+(i)*number/10)
		}
		list[i] = list1
	}

	readCM = NewConcurrentMap()
	readM = make(map[interface{}]interface{})
	readLM = newLockMap()
	for i := range list[0] {
		readCM.Put(i, i)
		readLM.put(i, i)
		readM[i] = i
	}
}

type lockMap struct {
	m  map[interface{}]interface{}
	rw *sync.RWMutex
}

func (t *lockMap) put(k interface{}, v interface{}) {
	t.rw.Lock()
	defer t.rw.Unlock()
	t.m[k] = v
}

func (t *lockMap) putIfNotExist(k interface{}, v interface{}) (ok bool) {
	t.rw.Lock()
	defer t.rw.Unlock()
	if _, ok = t.m[k]; !ok {
		t.m[k] = v
	}
	return
}

func (t *lockMap) get(k interface{}) (v interface{}, ok bool) {
	t.rw.RLock()
	defer t.rw.RUnlock()
	v, ok = t.m[k]
	return
}

func (t *lockMap) len() int {
	t.rw.RLock()
	defer t.rw.RUnlock()
	return len(t.m)

}

func newLockMap() *lockMap {
	return &lockMap{make(map[interface{}]interface{}), new(sync.RWMutex)}
}

func newLockMap1(initCap int) *lockMap {
	return &lockMap{make(map[interface{}]interface{}, initCap), new(sync.RWMutex)}
}

func BenchmarkLockMapPut(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := newLockMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.put(j, j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapPut(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := make(map[interface{}]interface{})

		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for _, j := range list[i] {
				cm[j] = j
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapPut(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := NewConcurrentMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.Put(j, j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkLockMapPutNoGrow(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := newLockMap1(listN * number)

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.put(j, j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapPutNoGrow(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := make(map[interface{}]interface{}, listN*number)

		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for _, j := range list[i] {
				cm[j] = j
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapPutNoGrow(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := NewConcurrentMap(listN * number)

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.Put(j, j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkLockMapPut2(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := newLockMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.put(strconv.Itoa(j.(int)), j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapPut2(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := make(map[interface{}]interface{})

		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for _, j := range list[i] {
				cm[strconv.Itoa(j.(int))] = j
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapPut2(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := NewConcurrentMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.Put(strconv.Itoa(j.(int)), j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkLockMapGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			go func() {
				//itr := NewMapIterator(cm)
				//for itr.HasNext() {
				//	entry := itr.NextEntry()
				//	k := entry.key.(string)
				//	v := entry.value.(int)
				for k := range list[0] {
					_, _ = readLM.get(k)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for k := range list[0] {
				_, _ = readM[k]
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			go func() {
				for k := range list[0] {
					_, _ = readCM.Get(k)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkLockMapPutAndGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := newLockMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.put(j, j)
					_, _ = cm.get(j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}

func BenchmarkMapPutAndGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := make(map[interface{}]interface{})

		//wg := new(sync.WaitGroup)
		//wg.Add(listN)
		for i := 0; i < listN; i++ {
			for _, j := range list[i] {
				cm[j] = j
				_ = cm[j]
			}
			//wg.Done()
		}
	}
}

func BenchmarkConcurrentMapPutAndGet(b *testing.B) {
	for n := 0; n < b.N; n++ {
		cm := NewConcurrentMap()

		wg := new(sync.WaitGroup)
		wg.Add(listN)
		for i := 0; i < listN; i++ {
			k := i
			go func() {
				for _, j := range list[k] {
					cm.Put(j, j)
					_, _ = cm.Get(j)
				}
				wg.Done()
			}()
		}
		wg.Wait()
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 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.

// +build js,wasm

package http

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"syscall/js"
)

// RoundTrip implements the RoundTripper interface using the WHATWG Fetch API.
func (t *Transport) RoundTrip(req *Request) (*Response, error) {
	if useFakeNetwork() {
		return t.roundTrip(req)
	}
	headers := js.Global().Get("Headers").New()
	for key, values := range req.Header {
		for _, value := range values {
			headers.Call("append", key, value)
		}
	}

	ac := js.Global().Get("AbortController")
	if ac != js.Undefined() {
		// Some browsers that support WASM don't necessarily support
		// the AbortController. See
		// https://developer.mozilla.org/en-US/docs/Web/API/AbortController#Browser_compatibility.
		ac = ac.New()
	}

	opt := js.Global().Get("Object").New()
	// See https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
	// for options available.
	opt.Set("headers", headers)
	opt.Set("method", req.Method)
	opt.Set("credentials", "same-origin")
	if ac != js.Undefined() {
		opt.Set("signal", ac.Get("signal"))
	}

	if req.Body != nil {
		// TODO(johanbrandhorst): Stream request body when possible.
		// See https://bugs.chromium.org/p/chromium/issues/detail?id=688906 for Blink issue.
		// See https://bugzilla.mozilla.org/show_bug.cgi?id=1387483 for Firefox issue.
		// See https://github.com/web-platform-tests/wpt/issues/7693 for WHATWG tests issue.
		// See https://developer.mozilla.org/en-US/docs/Web/API/Streams_API for more details on the Streams API
		// and browser support.
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			req.Body.Close() // RoundTrip must always close the body, including on errors.
			return nil, err
		}
		req.Body.Close()
		a := js.TypedArrayOf(body)
		defer a.Release()
		opt.Set("body", a)
	}
	respPromise := js.Global().Call("fetch", req.URL.String(), opt)
	var (
		respCh = make(chan *Response, 1)
		errCh  = make(chan error, 1)
	)
	success := js.NewCallback(func(args []js.Value) {
		result := args[0]
		header := Header{}
		// https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries
		headersIt := result.Get("headers").Call("entries")
		for {
			n := headersIt.Call("next")
			if n.Get("done").Bool() {
				break
			}
			pair := n.Get("value")
			key, value := pair.Index(0).String(), pair.Index(1).String()
			ck := CanonicalHeaderKey(key)
			header[ck] = append(header[ck], value)
		}

		contentLength := int64(0)
		if cl, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil {
			contentLength = cl
		}

		b := result.Get("body")
		var body io.ReadCloser
		if b != js.Undefined() {
			body = &streamReader{stream: b.Call("getReader")}
		} else {
			// Fall back to using ArrayBuffer
			// https://developer.mozilla.org/en-US/docs/Web/API/Body/arrayBuffer
			body = &arrayReader{arrayPromise: result.Call("arrayBuffer")}
		}

		select {
		case respCh <- &Response{
			Status:        result.Get("status").String() + " " + StatusText(result.Get("status").Int()),
			StatusCode:    result.Get("status").Int(),
			Header:        header,
			ContentLength: contentLength,
			Body:          body,
			Request:       req,
		}:
		case <-req.Context().Done():
		}
	})
	defer success.Release()
	failure := js.NewCallback(func(args []js.Value) {
		err := fmt.Errorf("net/http: fetch() failed: %s", args[0].String())
		select {
		case errCh <- err:
		case <-req.Context().Done():
		}
	})
	defer failure.Release()
	respPromise.Call("then", success, failure)
	select {
	case <-req.Context().Done():
		if ac != js.Undefined() {
			// Abort the Fetch request
			ac.Call("abort")
		}
		return nil, req.Context().Err()
	case resp := <-respCh:
		return resp, nil
	case err := <-errCh:
		return nil, err
	}
}

var errClosed = errors.New("net/http: reader is closed")

// useFakeNetwork is used to determine whether the request is made
// by a test and should be made to use the fake in-memory network.
func useFakeNetwork() bool {
	return len(os.Args) > 0 && strings.HasSuffix(os.Args[0], ".test")
}

// streamReader implements an io.ReadCloser wrapper for ReadableStream.
// See https://fetch.spec.whatwg.org/#readablestream for more information.
type streamReader struct {
	pending []byte
	stream  js.Value
	err     error // sticky read error
}

func (r *streamReader) Read(p []byte) (n int, err error) {
	if r.err != nil {
		return 0, r.err
	}
	if len(r.pending) == 0 {
		var (
			bCh   = make(chan []byte, 1)
			errCh = make(chan error, 1)
		)
		success := js.NewCallback(func(args []js.Value) {
			result := args[0]
			if result.Get("done").Bool() {
				errCh <- io.EOF
				return
			}
			value := make([]byte, result.Get("value").Get("byteLength").Int())
			a := js.TypedArrayOf(value)
			a.Call("set", result.Get("value"))
			a.Release()
			bCh <- value
		})
		defer success.Release()
		failure := js.NewCallback(func(args []js.Value) {
			// Assumes it's a TypeError. See
			// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
			// for more information on this type. See
			// https://streams.spec.whatwg.org/#byob-reader-read for the spec on
			// the read method.
			errCh <- errors.New(args[0].Get("message").String())
		})
		defer failure.Release()
		r.stream.Call("read").Call("then", success, failure)
		select {
		case b := <-bCh:
			r.pending = b
		case err := <-errCh:
			r.err = err
			return 0, err
		}
	}
	n = copy(p, r.pending)
	r.pending = r.pending[n:]
	return n, nil
}

func (r *streamReader) Close() error {
	// This ignores any error returned from cancel method. So far, I did not encounter any concrete
	// situation where reporting the error is meaningful. Most users ignore error from resp.Body.Close().
	// If there's a need to report error here, it can be implemented and tested when that need comes up.
	r.stream.Call("cancel")
	if r.err == nil {
		r.err = errClosed
	}
	return nil
}

// arrayReader implements an io.ReadCloser wrapper for ArrayBuffer.
// https://developer.mozilla.org/en-US/docs/Web/API/Body/arrayBuffer.
type arrayReader struct {
	arrayPromise js.Value
	pending      []byte
	read         bool
	err          error // sticky read error
}

func (r *arrayReader) Read(p []byte) (n int, err error) {
	if r.err != nil {
		return 0, r.err
	}
	if !r.read {
		r.read = true
		var (
			bCh   = make(chan []byte, 1)
			errCh = make(chan error, 1)
		)
		success := js.NewCallback(func(args []js.Value) {
			// Wrap the input ArrayBuffer with a Uint8Array
			uint8arrayWrapper := js.Global().Get("Uint8Array").New(args[0])
			value := make([]byte, uint8arrayWrapper.Get("byteLength").Int())
			a := js.TypedArrayOf(value)
			a.Call("set", uint8arrayWrapper)
			a.Release()
			bCh <- value
		})
		defer success.Release()
		failure := js.NewCallback(func(args []js.Value) {
			// Assumes it's a TypeError. See
			// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
			// for more information on this type.
			// See https://fetch.spec.whatwg.org/#concept-body-consume-body for reasons this might error.
			errCh <- errors.New(args[0].Get("message").String())
		})
		defer failure.Release()
		r.arrayPromise.Call("then", success, failure)
		select {
		case b := <-bCh:
			r.pending = b
		case err := <-errCh:
			return 0, err
		}
	}
	if len(r.pending) == 0 {
		return 0, io.EOF
	}
	n = copy(p, r.pending)
	r.pending = r.pending[n:]
	return n, nil
}

func (r *arrayReader) Close() error {
	if r.err == nil {
		r.err = errClosed
	}
	return nil
}
<commit_msg>net/http: support configuring fetch options<commit_after>// Copyright 2018 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.

// +build js,wasm

package http

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"syscall/js"
)

// jsFetchMode is a Request.Header map key that, if present,
// signals that the map entry is actually an option to the Fetch API mode setting.
// Valid values are: "cors", "no-cors", "same-origin", "navigate"
// The default is "same-origin".
//
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters
const jsFetchMode = "js.fetch:mode"

// jsFetchCreds is a Request.Header map key that, if present,
// signals that the map entry is actually an option to the Fetch API credentials setting.
// Valid values are: "omit", "same-origin", "include"
// The default is "same-origin".
//
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters
const jsFetchCreds = "js.fetch:credentials"

// RoundTrip implements the RoundTripper interface using the WHATWG Fetch API.
func (t *Transport) RoundTrip(req *Request) (*Response, error) {
	if useFakeNetwork() {
		return t.roundTrip(req)
	}

	ac := js.Global().Get("AbortController")
	if ac != js.Undefined() {
		// Some browsers that support WASM don't necessarily support
		// the AbortController. See
		// https://developer.mozilla.org/en-US/docs/Web/API/AbortController#Browser_compatibility.
		ac = ac.New()
	}

	opt := js.Global().Get("Object").New()
	// See https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
	// for options available.
	opt.Set("method", req.Method)
	opt.Set("credentials", "same-origin")
	if h := req.Header.Get(jsFetchCreds); h != "" {
		opt.Set("credentials", h)
		req.Header.Del(jsFetchCreds)
	}
	if h := req.Header.Get(jsFetchMode); h != "" {
		opt.Set("mode", h)
		req.Header.Del(jsFetchMode)
	}
	if ac != js.Undefined() {
		opt.Set("signal", ac.Get("signal"))
	}
	headers := js.Global().Get("Headers").New()
	for key, values := range req.Header {
		for _, value := range values {
			headers.Call("append", key, value)
		}
	}
	opt.Set("headers", headers)

	if req.Body != nil {
		// TODO(johanbrandhorst): Stream request body when possible.
		// See https://bugs.chromium.org/p/chromium/issues/detail?id=688906 for Blink issue.
		// See https://bugzilla.mozilla.org/show_bug.cgi?id=1387483 for Firefox issue.
		// See https://github.com/web-platform-tests/wpt/issues/7693 for WHATWG tests issue.
		// See https://developer.mozilla.org/en-US/docs/Web/API/Streams_API for more details on the Streams API
		// and browser support.
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			req.Body.Close() // RoundTrip must always close the body, including on errors.
			return nil, err
		}
		req.Body.Close()
		a := js.TypedArrayOf(body)
		defer a.Release()
		opt.Set("body", a)
	}
	respPromise := js.Global().Call("fetch", req.URL.String(), opt)
	var (
		respCh = make(chan *Response, 1)
		errCh  = make(chan error, 1)
	)
	success := js.NewCallback(func(args []js.Value) {
		result := args[0]
		header := Header{}
		// https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries
		headersIt := result.Get("headers").Call("entries")
		for {
			n := headersIt.Call("next")
			if n.Get("done").Bool() {
				break
			}
			pair := n.Get("value")
			key, value := pair.Index(0).String(), pair.Index(1).String()
			ck := CanonicalHeaderKey(key)
			header[ck] = append(header[ck], value)
		}

		contentLength := int64(0)
		if cl, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil {
			contentLength = cl
		}

		b := result.Get("body")
		var body io.ReadCloser
		if b != js.Undefined() {
			body = &streamReader{stream: b.Call("getReader")}
		} else {
			// Fall back to using ArrayBuffer
			// https://developer.mozilla.org/en-US/docs/Web/API/Body/arrayBuffer
			body = &arrayReader{arrayPromise: result.Call("arrayBuffer")}
		}

		select {
		case respCh <- &Response{
			Status:        result.Get("status").String() + " " + StatusText(result.Get("status").Int()),
			StatusCode:    result.Get("status").Int(),
			Header:        header,
			ContentLength: contentLength,
			Body:          body,
			Request:       req,
		}:
		case <-req.Context().Done():
		}
	})
	defer success.Release()
	failure := js.NewCallback(func(args []js.Value) {
		err := fmt.Errorf("net/http: fetch() failed: %s", args[0].String())
		select {
		case errCh <- err:
		case <-req.Context().Done():
		}
	})
	defer failure.Release()
	respPromise.Call("then", success, failure)
	select {
	case <-req.Context().Done():
		if ac != js.Undefined() {
			// Abort the Fetch request
			ac.Call("abort")
		}
		return nil, req.Context().Err()
	case resp := <-respCh:
		return resp, nil
	case err := <-errCh:
		return nil, err
	}
}

var errClosed = errors.New("net/http: reader is closed")

// useFakeNetwork is used to determine whether the request is made
// by a test and should be made to use the fake in-memory network.
func useFakeNetwork() bool {
	return len(os.Args) > 0 && strings.HasSuffix(os.Args[0], ".test")
}

// streamReader implements an io.ReadCloser wrapper for ReadableStream.
// See https://fetch.spec.whatwg.org/#readablestream for more information.
type streamReader struct {
	pending []byte
	stream  js.Value
	err     error // sticky read error
}

func (r *streamReader) Read(p []byte) (n int, err error) {
	if r.err != nil {
		return 0, r.err
	}
	if len(r.pending) == 0 {
		var (
			bCh   = make(chan []byte, 1)
			errCh = make(chan error, 1)
		)
		success := js.NewCallback(func(args []js.Value) {
			result := args[0]
			if result.Get("done").Bool() {
				errCh <- io.EOF
				return
			}
			value := make([]byte, result.Get("value").Get("byteLength").Int())
			a := js.TypedArrayOf(value)
			a.Call("set", result.Get("value"))
			a.Release()
			bCh <- value
		})
		defer success.Release()
		failure := js.NewCallback(func(args []js.Value) {
			// Assumes it's a TypeError. See
			// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
			// for more information on this type. See
			// https://streams.spec.whatwg.org/#byob-reader-read for the spec on
			// the read method.
			errCh <- errors.New(args[0].Get("message").String())
		})
		defer failure.Release()
		r.stream.Call("read").Call("then", success, failure)
		select {
		case b := <-bCh:
			r.pending = b
		case err := <-errCh:
			r.err = err
			return 0, err
		}
	}
	n = copy(p, r.pending)
	r.pending = r.pending[n:]
	return n, nil
}

func (r *streamReader) Close() error {
	// This ignores any error returned from cancel method. So far, I did not encounter any concrete
	// situation where reporting the error is meaningful. Most users ignore error from resp.Body.Close().
	// If there's a need to report error here, it can be implemented and tested when that need comes up.
	r.stream.Call("cancel")
	if r.err == nil {
		r.err = errClosed
	}
	return nil
}

// arrayReader implements an io.ReadCloser wrapper for ArrayBuffer.
// https://developer.mozilla.org/en-US/docs/Web/API/Body/arrayBuffer.
type arrayReader struct {
	arrayPromise js.Value
	pending      []byte
	read         bool
	err          error // sticky read error
}

func (r *arrayReader) Read(p []byte) (n int, err error) {
	if r.err != nil {
		return 0, r.err
	}
	if !r.read {
		r.read = true
		var (
			bCh   = make(chan []byte, 1)
			errCh = make(chan error, 1)
		)
		success := js.NewCallback(func(args []js.Value) {
			// Wrap the input ArrayBuffer with a Uint8Array
			uint8arrayWrapper := js.Global().Get("Uint8Array").New(args[0])
			value := make([]byte, uint8arrayWrapper.Get("byteLength").Int())
			a := js.TypedArrayOf(value)
			a.Call("set", uint8arrayWrapper)
			a.Release()
			bCh <- value
		})
		defer success.Release()
		failure := js.NewCallback(func(args []js.Value) {
			// Assumes it's a TypeError. See
			// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
			// for more information on this type.
			// See https://fetch.spec.whatwg.org/#concept-body-consume-body for reasons this might error.
			errCh <- errors.New(args[0].Get("message").String())
		})
		defer failure.Release()
		r.arrayPromise.Call("then", success, failure)
		select {
		case b := <-bCh:
			r.pending = b
		case err := <-errCh:
			return 0, err
		}
	}
	if len(r.pending) == 0 {
		return 0, io.EOF
	}
	n = copy(p, r.pending)
	r.pending = r.pending[n:]
	return n, nil
}

func (r *arrayReader) Close() error {
	if r.err == nil {
		r.err = errClosed
	}
	return nil
}
<|endoftext|>


<commit_before>// +build !windows,!plan9

package log

import "log/syslog"

// SyslogHandler sends the logging output to syslog.
type SyslogHandler struct {
	*BaseHandler
	w *syslog.Writer
}

func NewSyslogHandler(tag string) (*SyslogHandler, error) {
	// Priority in New constructor is not important here because we
	// do not use w.Write() directly.
	w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_USER, tag)
	if err != nil {
		return nil, err
	}
	return &SyslogHandler{
		BaseHandler: NewBaseHandler(),
		w:           w,
	}, nil
}

func (b *SyslogHandler) Handle(rec *Record) {
	message := b.BaseHandler.FilterAndFormat(rec)
	if message == "" {
		return
	}

	var fn func(string) error
	switch rec.Level {
	case CRITICAL:
		fn = b.w.Crit
	case ERROR:
		fn = b.w.Err
	case WARNING:
		fn = b.w.Warning
	case NOTICE:
		fn = b.w.Notice
	case INFO:
		fn = b.w.Info
	case DEBUG:
		fn = b.w.Debug
	}
	fn(message)
}

func (b *SyslogHandler) Close() error {
	return b.w.Close()
}
<commit_msg>add NewSyslogHandlerDial func<commit_after>// +build !windows,!plan9

package log

import "log/syslog"

// SyslogHandler sends the logging output to syslog.
type SyslogHandler struct {
	*BaseHandler
	w *syslog.Writer
}

func NewSyslogHandler(tag string) (*SyslogHandler, error) {
	// Priority in New constructor is not important here because we
	// do not use w.Write() directly.
	w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_USER, tag)
	if err != nil {
		return nil, err
	}
	return &SyslogHandler{
		BaseHandler: NewBaseHandler(),
		w:           w,
	}, nil
}

func NewSyslogHandlerDial(network, raddr, tag string) (*SyslogHandler, error) {
	// Priority in New constructor is not important here because we
	// do not use w.Write() directly.
	w, err := syslog.Dial(network, raddr, syslog.LOG_INFO|syslog.LOG_USER, tag)
	if err != nil {
		return nil, err
	}
	return &SyslogHandler{
		BaseHandler: NewBaseHandler(),
		w:           w,
	}, nil
}

func (b *SyslogHandler) Handle(rec *Record) {
	message := b.BaseHandler.FilterAndFormat(rec)
	if message == "" {
		return
	}

	var fn func(string) error
	switch rec.Level {
	case CRITICAL:
		fn = b.w.Crit
	case ERROR:
		fn = b.w.Err
	case WARNING:
		fn = b.w.Warning
	case NOTICE:
		fn = b.w.Notice
	case INFO:
		fn = b.w.Info
	case DEBUG:
		fn = b.w.Debug
	}
	fn(message)
}

func (b *SyslogHandler) Close() error {
	return b.w.Close()
}
<|endoftext|>


<commit_before>// flashlight is a lightweight chained proxy that can run in client or server mode.
package main

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/getlantern/fronted"
	"github.com/getlantern/golog"
	"github.com/getlantern/i18n"
	"github.com/getlantern/profiling"

	"github.com/getlantern/flashlight/analytics"
	"github.com/getlantern/flashlight/autoupdate"
	"github.com/getlantern/flashlight/client"
	"github.com/getlantern/flashlight/config"
	"github.com/getlantern/flashlight/geolookup"
	"github.com/getlantern/flashlight/logging"
	"github.com/getlantern/flashlight/proxiedsites"
	"github.com/getlantern/flashlight/server"
	"github.com/getlantern/flashlight/settings"
	"github.com/getlantern/flashlight/statreporter"
	"github.com/getlantern/flashlight/statserver"
	"github.com/getlantern/flashlight/ui"
	"github.com/getlantern/flashlight/util"
)

var (
	version   string
	buildDate string
	log       = golog.LoggerFor("flashlight")

	// Command-line Flags
	help      = flag.Bool("help", false, "Get usage help")
	parentPID = flag.Int("parentpid", 0, "the parent process's PID, used on Windows for killing flashlight when the parent disappears")
	headless  = flag.Bool("headless", false, "if true, lantern will run with no ui")

	configUpdates = make(chan *config.Config)
	exitCh        = make(chan error, 1)

	showui = true
)

func init() {

	if packageVersion != defaultPackageVersion {
		// packageVersion has precedence over GIT revision. This will happen when
		// packing a version intended for release.
		version = packageVersion
	}

	if version == "" {
		version = "development"
	}

	if buildDate == "" {
		buildDate = "now"
	}

	// Passing public key and version to the autoupdate service.
	autoupdate.PublicKey = []byte(packagePublicKey)
	autoupdate.Version = packageVersion

	rand.Seed(time.Now().UnixNano())
}

func main() {
	parseFlags()
	showui = !*headless

	if showui {
		runOnSystrayReady(_main)
	} else {
		log.Debug("Running headless")
		_main()
	}
}

func _main() {
	err := doMain()
	if err != nil {
		log.Fatal(err)
	}
	log.Debug("Lantern stopped")
	os.Exit(0)
}

func doMain() error {
	err := logging.Init()
	if err != nil {
		return err
	}

	// Schedule cleanup actions
	defer logging.Close()
	defer pacOff()
	defer quitSystray()

	i18nInit()
	if showui {
		err = configureSystemTray()
		if err != nil {
			return err
		}
	}
	displayVersion()

	parseFlags()

	cfg, err := config.Init()
	if err != nil {
		return fmt.Errorf("Unable to initialize configuration: %v", err)
	}
	go func() {
		err := config.Run(func(updated *config.Config) {
			configUpdates <- updated
		})
		if err != nil {
			exit(err)
		}
	}()
	if *help || cfg.Addr == "" || (cfg.Role != "server" && cfg.Role != "client") {
		flag.Usage()
		return fmt.Errorf("Wrong arguments")
	}

	finishProfiling := profiling.Start(cfg.CpuProfile, cfg.MemProfile)
	defer finishProfiling()

	// Configure stats initially
	err = statreporter.Configure(cfg.Stats)
	if err != nil {
		return err
	}

	log.Debug("Running proxy")
	if cfg.IsDownstream() {
		// This will open a proxy on the address and port given by -addr
		runClientProxy(cfg)
	} else {
		runServerProxy(cfg)
	}

	return waitForExit()
}

func i18nInit() {
	i18n.SetMessagesFunc(func(filename string) ([]byte, error) {
		return ui.Translations.Get(filename)
	})
	err := i18n.UseOSLocale()
	if err != nil {
		log.Debugf("i18n.UseOSLocale: %q", err)
	}
}

func displayVersion() {
	log.Debugf("---- flashlight version: %s, release: %s, build date: %s ----", version, packageVersion, buildDate)
}

func parseFlags() {
	args := os.Args[1:]
	// On OS X, the first time that the program is run after download it is
	// quarantined.  OS X will ask the user whether or not it's okay to run the
	// program.  If the user says that it's okay, OS X will run the program but
	// pass an extra flag like -psn_0_1122578.  flag.Parse() fails if it sees
	// any flags that haven't been declared, so we remove the extra flag.
	if len(os.Args) == 2 && strings.HasPrefix(os.Args[1], "-psn") {
		log.Debugf("Ignoring extra flag %v", os.Args[1])
		args = []string{}
	}
	// Note - we can ignore the returned error because CommandLine.Parse() will
	// exit if it fails.
	flag.CommandLine.Parse(args)
}

// runClientProxy runs the client-side (get mode) proxy.
func runClientProxy(cfg *config.Config) {
	var err error

	// Set Lantern as system proxy by creating and using a PAC file.
	setProxyAddr(cfg.Addr)

	if err = setUpPacTool(); err != nil {
		exit(err)
	}

	// Create the client-side proxy.
	client := &client.Client{
		Addr:         cfg.Addr,
		ReadTimeout:  0, // don't timeout
		WriteTimeout: 0,
	}

	// Update client configuration and get the highest QOS dialer available.
	hqfd := client.Configure(cfg.Client)

	// Start user interface.
	if cfg.UIAddr != "" {
		if err = ui.Start(cfg.UIAddr); err != nil {
			exit(fmt.Errorf("Unable to start UI: %v", err))
			return
		}
		if showui {
			// Launch a browser window with Lantern.
			ui.Show()
		}
	}

	autoupdate.Configure(cfg)
	logging.Configure(cfg, version, buildDate)
	settings.Configure(cfg, version, buildDate)
	proxiedsites.Configure(cfg.ProxiedSites)

	httpClient, er := util.HTTPClient(cfg.CloudConfigCA, cfg.Addr)
	if er != nil {
		log.Errorf("Could not create HTTP client %v", er)
	} else {
		analytics.Configure(cfg, false, httpClient)
	}

	if hqfd == nil {
		log.Errorf("No fronted dialer available, not enabling geolocation, stats or analytics")
	} else {
		// An *http.Client that uses the highest QOS dialer.
		hqfdClient := hqfd.NewDirectDomainFronter()

		config.Configure(hqfdClient)
		geolookup.Configure(hqfdClient)
		statserver.Configure(hqfdClient)
	}

	// Continually poll for config updates and update client accordingly
	go func() {
		for {
			cfg := <-configUpdates

			proxiedsites.Configure(cfg.ProxiedSites)
			// Note - we deliberately ignore the error from statreporter.Configure here
			statreporter.Configure(cfg.Stats)

			hqfd = client.Configure(cfg.Client)

			if hqfd != nil {
				// Create and pass the *http.Client that uses the highest QOS dialer to
				// critical modules that require continual comunication with external
				// services.
				hqfdClient := hqfd.NewDirectDomainFronter()

				config.Configure(hqfdClient)
				geolookup.Configure(hqfdClient)
				statserver.Configure(hqfdClient)
				settings.Configure(cfg, version, buildDate)
				logging.Configure(cfg, version, buildDate)
				autoupdate.Configure(cfg)
				// Note we don't call Configure on analytics here, as that would
				// result in an extra analytics call and double counting.
			}
		}
	}()

	// watchDirectAddrs will spawn a goroutine that will add any site that is
	// directly accesible to the PAC file.
	watchDirectAddrs()

	go func() {
		exit(client.ListenAndServe(pacOn))
	}()
}

// Runs the server-side proxy
func runServerProxy(cfg *config.Config) {
	useAllCores()

	pkFile, err := config.InConfigDir("proxypk.pem")
	if err != nil {
		log.Fatal(err)
	}
	certFile, err := config.InConfigDir("servercert.pem")
	if err != nil {
		log.Fatal(err)
	}

	updateServerSideConfigClient(cfg)

	srv := &server.Server{
		Addr:         cfg.Addr,
		ReadTimeout:  0, // don't timeout
		WriteTimeout: 0,
		CertContext: &fronted.CertContext{
			PKFile:         pkFile,
			ServerCertFile: certFile,
		},
		AllowedPorts: []int{80, 443, 8080, 8443, 5222, 5223, 5228},

		// We allow all censored countries plus us, es, mx, and gb because we do work
		// and testing from those countries.
		AllowedCountries: []string{"US", "ES", "MX", "GB", "CN", "VN", "IN", "IQ", "IR", "CU", "SY", "SA", "BH", "ET", "ER", "UZ", "TM", "PK", "TR", "VE"},
	}

	srv.Configure(cfg.Server)
	analytics.Configure(cfg, true, nil)

	// Continually poll for config updates and update server accordingly
	go func() {
		for {
			cfg := <-configUpdates
			updateServerSideConfigClient(cfg)
			statreporter.Configure(cfg.Stats)
			srv.Configure(cfg.Server)
		}
	}()

	err = srv.ListenAndServe(func(update func(*server.ServerConfig) error) {
		err := config.Update(func(cfg *config.Config) error {
			return update(cfg.Server)
		})
		if err != nil {
			log.Errorf("Error while trying to update: %v", err)
		}
	})
	if err != nil {
		log.Fatalf("Unable to run server proxy: %s", err)
	}
}

func updateServerSideConfigClient(cfg *config.Config) {
	client, err := util.HTTPClient(cfg.CloudConfigCA, "")
	if err != nil {
		log.Errorf("Couldn't create http.Client for fetching the config")
		return
	}
	config.Configure(client)
}

func useAllCores() {
	numcores := runtime.NumCPU()
	log.Debugf("Using all %d cores on machine", numcores)
	runtime.GOMAXPROCS(numcores)
}

// exit tells the application to exit, optionally supplying an error that caused
// the exit.
func exit(err error) {
	exitCh <- err
}

// WaitForExit waits for a request to exit the application.
func waitForExit() error {
	return <-exitCh
}
<commit_msg>don't ue CloudConfigCA<commit_after>// flashlight is a lightweight chained proxy that can run in client or server mode.
package main

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/getlantern/fronted"
	"github.com/getlantern/golog"
	"github.com/getlantern/i18n"
	"github.com/getlantern/profiling"

	"github.com/getlantern/flashlight/analytics"
	"github.com/getlantern/flashlight/autoupdate"
	"github.com/getlantern/flashlight/client"
	"github.com/getlantern/flashlight/config"
	"github.com/getlantern/flashlight/geolookup"
	"github.com/getlantern/flashlight/logging"
	"github.com/getlantern/flashlight/proxiedsites"
	"github.com/getlantern/flashlight/server"
	"github.com/getlantern/flashlight/settings"
	"github.com/getlantern/flashlight/statreporter"
	"github.com/getlantern/flashlight/statserver"
	"github.com/getlantern/flashlight/ui"
	"github.com/getlantern/flashlight/util"
)

var (
	version   string
	buildDate string
	log       = golog.LoggerFor("flashlight")

	// Command-line Flags
	help      = flag.Bool("help", false, "Get usage help")
	parentPID = flag.Int("parentpid", 0, "the parent process's PID, used on Windows for killing flashlight when the parent disappears")
	headless  = flag.Bool("headless", false, "if true, lantern will run with no ui")

	configUpdates = make(chan *config.Config)
	exitCh        = make(chan error, 1)

	showui = true
)

func init() {

	if packageVersion != defaultPackageVersion {
		// packageVersion has precedence over GIT revision. This will happen when
		// packing a version intended for release.
		version = packageVersion
	}

	if version == "" {
		version = "development"
	}

	if buildDate == "" {
		buildDate = "now"
	}

	// Passing public key and version to the autoupdate service.
	autoupdate.PublicKey = []byte(packagePublicKey)
	autoupdate.Version = packageVersion

	rand.Seed(time.Now().UnixNano())
}

func main() {
	parseFlags()
	showui = !*headless

	if showui {
		runOnSystrayReady(_main)
	} else {
		log.Debug("Running headless")
		_main()
	}
}

func _main() {
	err := doMain()
	if err != nil {
		log.Fatal(err)
	}
	log.Debug("Lantern stopped")
	os.Exit(0)
}

func doMain() error {
	err := logging.Init()
	if err != nil {
		return err
	}

	// Schedule cleanup actions
	defer logging.Close()
	defer pacOff()
	defer quitSystray()

	i18nInit()
	if showui {
		err = configureSystemTray()
		if err != nil {
			return err
		}
	}
	displayVersion()

	parseFlags()

	cfg, err := config.Init()
	if err != nil {
		return fmt.Errorf("Unable to initialize configuration: %v", err)
	}
	go func() {
		err := config.Run(func(updated *config.Config) {
			configUpdates <- updated
		})
		if err != nil {
			exit(err)
		}
	}()
	if *help || cfg.Addr == "" || (cfg.Role != "server" && cfg.Role != "client") {
		flag.Usage()
		return fmt.Errorf("Wrong arguments")
	}

	finishProfiling := profiling.Start(cfg.CpuProfile, cfg.MemProfile)
	defer finishProfiling()

	// Configure stats initially
	err = statreporter.Configure(cfg.Stats)
	if err != nil {
		return err
	}

	log.Debug("Running proxy")
	if cfg.IsDownstream() {
		// This will open a proxy on the address and port given by -addr
		runClientProxy(cfg)
	} else {
		runServerProxy(cfg)
	}

	return waitForExit()
}

func i18nInit() {
	i18n.SetMessagesFunc(func(filename string) ([]byte, error) {
		return ui.Translations.Get(filename)
	})
	err := i18n.UseOSLocale()
	if err != nil {
		log.Debugf("i18n.UseOSLocale: %q", err)
	}
}

func displayVersion() {
	log.Debugf("---- flashlight version: %s, release: %s, build date: %s ----", version, packageVersion, buildDate)
}

func parseFlags() {
	args := os.Args[1:]
	// On OS X, the first time that the program is run after download it is
	// quarantined.  OS X will ask the user whether or not it's okay to run the
	// program.  If the user says that it's okay, OS X will run the program but
	// pass an extra flag like -psn_0_1122578.  flag.Parse() fails if it sees
	// any flags that haven't been declared, so we remove the extra flag.
	if len(os.Args) == 2 && strings.HasPrefix(os.Args[1], "-psn") {
		log.Debugf("Ignoring extra flag %v", os.Args[1])
		args = []string{}
	}
	// Note - we can ignore the returned error because CommandLine.Parse() will
	// exit if it fails.
	flag.CommandLine.Parse(args)
}

// runClientProxy runs the client-side (get mode) proxy.
func runClientProxy(cfg *config.Config) {
	var err error

	// Set Lantern as system proxy by creating and using a PAC file.
	setProxyAddr(cfg.Addr)

	if err = setUpPacTool(); err != nil {
		exit(err)
	}

	// Create the client-side proxy.
	client := &client.Client{
		Addr:         cfg.Addr,
		ReadTimeout:  0, // don't timeout
		WriteTimeout: 0,
	}

	// Update client configuration and get the highest QOS dialer available.
	hqfd := client.Configure(cfg.Client)

	// Start user interface.
	if cfg.UIAddr != "" {
		if err = ui.Start(cfg.UIAddr); err != nil {
			exit(fmt.Errorf("Unable to start UI: %v", err))
			return
		}
		if showui {
			// Launch a browser window with Lantern.
			ui.Show()
		}
	}

	autoupdate.Configure(cfg)
	logging.Configure(cfg, version, buildDate)
	settings.Configure(cfg, version, buildDate)
	proxiedsites.Configure(cfg.ProxiedSites)

	httpClient, er := util.HTTPClient("", cfg.Addr)
	if er != nil {
		log.Errorf("Could not create HTTP client %v", er)
	} else {
		analytics.Configure(cfg, false, httpClient)
	}

	if hqfd == nil {
		log.Errorf("No fronted dialer available, not enabling geolocation, stats or analytics")
	} else {
		// An *http.Client that uses the highest QOS dialer.
		hqfdClient := hqfd.NewDirectDomainFronter()

		config.Configure(hqfdClient)
		geolookup.Configure(hqfdClient)
		statserver.Configure(hqfdClient)
	}

	// Continually poll for config updates and update client accordingly
	go func() {
		for {
			cfg := <-configUpdates

			proxiedsites.Configure(cfg.ProxiedSites)
			// Note - we deliberately ignore the error from statreporter.Configure here
			statreporter.Configure(cfg.Stats)

			hqfd = client.Configure(cfg.Client)

			if hqfd != nil {
				// Create and pass the *http.Client that uses the highest QOS dialer to
				// critical modules that require continual comunication with external
				// services.
				hqfdClient := hqfd.NewDirectDomainFronter()

				config.Configure(hqfdClient)
				geolookup.Configure(hqfdClient)
				statserver.Configure(hqfdClient)
				settings.Configure(cfg, version, buildDate)
				logging.Configure(cfg, version, buildDate)
				autoupdate.Configure(cfg)
				// Note we don't call Configure on analytics here, as that would
				// result in an extra analytics call and double counting.
			}
		}
	}()

	// watchDirectAddrs will spawn a goroutine that will add any site that is
	// directly accesible to the PAC file.
	watchDirectAddrs()

	go func() {
		exit(client.ListenAndServe(pacOn))
	}()
}

// Runs the server-side proxy
func runServerProxy(cfg *config.Config) {
	useAllCores()

	pkFile, err := config.InConfigDir("proxypk.pem")
	if err != nil {
		log.Fatal(err)
	}
	certFile, err := config.InConfigDir("servercert.pem")
	if err != nil {
		log.Fatal(err)
	}

	updateServerSideConfigClient(cfg)

	srv := &server.Server{
		Addr:         cfg.Addr,
		ReadTimeout:  0, // don't timeout
		WriteTimeout: 0,
		CertContext: &fronted.CertContext{
			PKFile:         pkFile,
			ServerCertFile: certFile,
		},
		AllowedPorts: []int{80, 443, 8080, 8443, 5222, 5223, 5228},

		// We allow all censored countries plus us, es, mx, and gb because we do work
		// and testing from those countries.
		AllowedCountries: []string{"US", "ES", "MX", "GB", "CN", "VN", "IN", "IQ", "IR", "CU", "SY", "SA", "BH", "ET", "ER", "UZ", "TM", "PK", "TR", "VE"},
	}

	srv.Configure(cfg.Server)
	analytics.Configure(cfg, true, nil)

	// Continually poll for config updates and update server accordingly
	go func() {
		for {
			cfg := <-configUpdates
			updateServerSideConfigClient(cfg)
			statreporter.Configure(cfg.Stats)
			srv.Configure(cfg.Server)
		}
	}()

	err = srv.ListenAndServe(func(update func(*server.ServerConfig) error) {
		err := config.Update(func(cfg *config.Config) error {
			return update(cfg.Server)
		})
		if err != nil {
			log.Errorf("Error while trying to update: %v", err)
		}
	})
	if err != nil {
		log.Fatalf("Unable to run server proxy: %s", err)
	}
}

func updateServerSideConfigClient(cfg *config.Config) {
	client, err := util.HTTPClient(cfg.CloudConfigCA, "")
	if err != nil {
		log.Errorf("Couldn't create http.Client for fetching the config")
		return
	}
	config.Configure(client)
}

func useAllCores() {
	numcores := runtime.NumCPU()
	log.Debugf("Using all %d cores on machine", numcores)
	runtime.GOMAXPROCS(numcores)
}

// exit tells the application to exit, optionally supplying an error that caused
// the exit.
func exit(err error) {
	exitCh <- err
}

// WaitForExit waits for a request to exit the application.
func waitForExit() error {
	return <-exitCh
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"github.com/simonz05/godis"
)

const HELP string = `
<!DOCTYPE html>

        <title>cdrv.ws</title>

cdrvws(1)                          CDRV.WS                          cdrvws(1)

NAME
    cdrvws: command line url shortener:

SYNOPSIS
    <command> | curl -F 'rvw=<-' http://cdrv.ws

`

const CHARS string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const BASE uint64 = uint64(len(CHARS))

var redis *godis.Client

func main() {
	connectToRedis()
	http.HandleFunc("/", route)
	startServer()
}

func encode(id uint64) string {
	encoded := ""
	for id > 0 {
		encoded += string(CHARS[id%BASE])
		id = id / BASE
	}
	return encoded
}

func createShortUrl(longurl string) (error, string) {
	shortid, err := redis.Incr("urlId")
	if err != nil {
		return err, ""
	}
	shorturl := encode(uint64(shortid))
	if err := redis.Set(shorturl, longurl); err != nil {
		return err, ""
	}
	return nil, shorturl
}

func expand(shorturl string) (error, string) {
	longurl, err := redis.Get(shorturl)
	if err != nil && err.Error() == "Nonexisting key" {
		return nil, ""
	} else if err != nil {
		return err, ""
	}
	return nil, longurl.String()
}

func connectToRedis() {
	rawurl := os.Getenv("REDISTOGO_URL")
	log.Printf("Redis to go url: %s\n", rawurl)
	redisurl := url.URL{
		User: url.UserPassword("", ""),
	}
	parsedurl := &redisurl
	if rawurl != "" {
		var err error
		parsedurl, err = parsedurl.Parse(rawurl)
		if err != nil {
			log.Fatal("Could not parse redis url", err)
		}
	}
	password, _ := parsedurl.User.Password()
	log.Printf("Connecting to redis: %s\n", parsedurl.String())
	redis = godis.New(parsedurl.Host, 0, password)
}

func startServer() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("Starting on %s\n", port)
	err := http.ListenAndServe(":" + port, nil)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

func route(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" {
		if req.URL.String() == "/" {
			handleHome(w, req)
		} else {
			handleExpand(w, req)
		}
	} else if req.Method == "POST" {
		handleShorten(w, req)
	}
}

func handleHome(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, HELP)
}

func handleShorten(w http.ResponseWriter, req *http.Request) {
	err, shorturl := createShortUrl(req.FormValue("rvw"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fullurl := url.URL{
		Scheme: "http",
		Host:   req.Host,
		Path:   "/" + shorturl,
	}
	fmt.Fprintln(w, fullurl.String())
}

func handleExpand(w http.ResponseWriter, req *http.Request) {
	shorturl := strings.Trim(req.URL.String(), "/")
	err, longurl := expand(shorturl)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else if longurl == "" {
		http.NotFound(w, req)
	} else {
		http.Redirect(w, req, longurl, http.StatusMovedPermanently)
	}
}
<commit_msg>try to glean more info<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"github.com/simonz05/godis"
)

const HELP string = `
<!DOCTYPE html>

        <title>cdrv.ws</title>

cdrvws(1)                          CDRV.WS                          cdrvws(1)

NAME
    cdrvws: command line url shortener:

SYNOPSIS
    <command> | curl -F 'rvw=<-' http://cdrv.ws

`

const CHARS string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const BASE uint64 = uint64(len(CHARS))

var redis *godis.Client

func main() {
	connectToRedis()
	http.HandleFunc("/", route)
	startServer()
}

func encode(id uint64) string {
	encoded := ""
	for id > 0 {
		encoded += string(CHARS[id%BASE])
		id = id / BASE
	}
	return encoded
}

func createShortUrl(longurl string) (error, string) {
	shortid, err := redis.Incr("urlId")
	if err != nil {
		return err, ""
	}
	shorturl := encode(uint64(shortid))
	if err := redis.Set(shorturl, longurl); err != nil {
		return err, ""
	}
	return nil, shorturl
}

func expand(shorturl string) (error, string) {
	longurl, err := redis.Get(shorturl)
	if err != nil && err.Error() == "Nonexisting key" {
		return nil, ""
	} else if err != nil {
		return err, ""
	}
	return nil, longurl.String()
}

func connectToRedis() {
	rawurl := os.Getenv("REDISTOGO_URL")
	log.Printf("Redis to go url: %s\n", rawurl)
	redisurl := url.URL{
		User: url.UserPassword("", ""),
	}
	parsedurl := &redisurl
	if rawurl != "" {
		var err error
		parsedurl, err = parsedurl.Parse(rawurl)
		if err != nil {
			log.Fatal("Could not parse redis url", err)
		}
	}
	password, _ := parsedurl.User.Password()
	log.Printf("Connecting to redis: '%s' with password '%s'\n", parsedurl.Host, password)
	redis = godis.New(parsedurl.Host, 0, password)
}

func startServer() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("Starting on %s\n", port)
	err := http.ListenAndServe(":" + port, nil)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

func route(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" {
		if req.URL.String() == "/" {
			handleHome(w, req)
		} else {
			handleExpand(w, req)
		}
	} else if req.Method == "POST" {
		handleShorten(w, req)
	}
}

func handleHome(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, HELP)
}

func handleShorten(w http.ResponseWriter, req *http.Request) {
	err, shorturl := createShortUrl(req.FormValue("rvw"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fullurl := url.URL{
		Scheme: "http",
		Host:   req.Host,
		Path:   "/" + shorturl,
	}
	fmt.Fprintln(w, fullurl.String())
}

func handleExpand(w http.ResponseWriter, req *http.Request) {
	shorturl := strings.Trim(req.URL.String(), "/")
	err, longurl := expand(shorturl)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else if longurl == "" {
		http.NotFound(w, req)
	} else {
		http.Redirect(w, req, longurl, http.StatusMovedPermanently)
	}
}
<|endoftext|>


<commit_before>package awspurge

import (
	"errors"
	"fmt"
	"net/http"
	"sync"
	"time"

	awsclient "github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/go-multierror"
)

type Config struct {
	Regions        []string      `toml:"regions" json:"regions"`
	RegionsExclude []string      `toml:"regions_exclude" json:"regions_exclude"`
	AccessKey      string        `toml:"access_key" json:"access_key"`
	SecretKey      string        `toml:"secret_key" json:"secret_key"`
	Timeout        time.Duration `toml:"timeout" json:"timeout"`
}

type resources struct {
	instances []*ec2.Instance
}

type Purge struct {
	services *multiRegion

	// resources represents the current available resources per region. It's
	// populated by the Fetch() method.
	resources map[string]*resources
}

func New(conf *Config) (*Purge, error) {
	checkCfg := "Please check your configuration"

	if len(conf.Regions) == 0 {
		return nil, errors.New("AWS Regions are not set. " + checkCfg)
	}

	if conf.AccessKey == "" {
		return nil, errors.New("AWS Access Key is not set. " + checkCfg)
	}

	if conf.SecretKey == "" {
		return nil, errors.New("AWS Secret Key is not set. " + checkCfg)
	}

	if conf.Timeout == 0 {
		conf.Timeout = time.Second * 30
	}

	client := &http.Client{
		Transport: &http.Transport{TLSHandshakeTimeout: conf.Timeout},
		Timeout:   conf.Timeout,
	}

	creds := credentials.NewStaticCredentials(conf.AccessKey, conf.SecretKey, "")
	awsCfg := &awsclient.Config{
		Credentials: creds,
		HTTPClient:  client,
		Logger:      awsclient.NewDefaultLogger(),
	}

	m := newMultiRegion(awsCfg, filterRegions(conf.Regions, conf.RegionsExclude))
	return &Purge{
		services:  m,
		resources: make(map[string]*resources, 0),
	}, nil
}

func (p *Purge) Do() error {
	if err := p.Fetch(); err != nil {
		return err
	}

	if err := p.Print(); err != nil {
		return err
	}

	return nil
}

// Print prints all fetched resources
func (p *Purge) Print() error {
	for region, resources := range p.resources {
		fmt.Printf("[%s] found '%d' instances\n", region, len(resources.instances))
	}
	return nil
}

// Fetch fetches all given resources and stores them internally. To print them
// use the Print() method
func (p *Purge) Fetch() error {
	allInstances, err := p.DescribeInstances()
	if err != nil {
		return err
	}

	for region, instances := range allInstances {
		p.resources[region] = &resources{
			instances: instances,
		}
	}

	return nil
}

func (p *Purge) DescribeInstances() (map[string][]*ec2.Instance, error) {
	var (
		wg sync.WaitGroup
		mu sync.Mutex

		multiErrors error
	)

	output := make(map[string][]*ec2.Instance)

	for r, s := range p.services.regions {
		wg.Add(1)

		go func(region string, svc *ec2.EC2) {
			input := &ec2.DescribeInstancesInput{}
			resp, err := svc.DescribeInstances(input)
			if err != nil {
				mu.Lock()
				multiErrors = multierror.Append(multiErrors, err)
				mu.Unlock()
				wg.Done()
				return
			}

			if resp.Reservations != nil {
				instances := make([]*ec2.Instance, 0)
				for _, reserv := range resp.Reservations {
					instances = append(instances, reserv.Instances...)
				}

				mu.Lock()
				output[region] = instances
				mu.Unlock()
			}

			wg.Done()
		}(r, s)
	}

	wg.Wait()

	return output, multiErrors
}
<commit_msg>awspurge: add volumes<commit_after>package awspurge

import (
	"errors"
	"fmt"
	"net/http"
	"sync"
	"time"

	awsclient "github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/go-multierror"
)

type Config struct {
	Regions        []string      `toml:"regions" json:"regions"`
	RegionsExclude []string      `toml:"regions_exclude" json:"regions_exclude"`
	AccessKey      string        `toml:"access_key" json:"access_key"`
	SecretKey      string        `toml:"secret_key" json:"secret_key"`
	Timeout        time.Duration `toml:"timeout" json:"timeout"`
}

type resources struct {
	instances []*ec2.Instance
	volumes   []*ec2.Volume
}

type Purge struct {
	services *multiRegion

	// resources represents the current available resources per region. It's
	// populated by the Fetch() method.
	resources map[string]*resources
}

func New(conf *Config) (*Purge, error) {
	checkCfg := "Please check your configuration"

	if len(conf.Regions) == 0 {
		return nil, errors.New("AWS Regions are not set. " + checkCfg)
	}

	if conf.AccessKey == "" {
		return nil, errors.New("AWS Access Key is not set. " + checkCfg)
	}

	if conf.SecretKey == "" {
		return nil, errors.New("AWS Secret Key is not set. " + checkCfg)
	}

	if conf.Timeout == 0 {
		conf.Timeout = time.Second * 30
	}

	client := &http.Client{
		Transport: &http.Transport{TLSHandshakeTimeout: conf.Timeout},
		Timeout:   conf.Timeout,
	}

	creds := credentials.NewStaticCredentials(conf.AccessKey, conf.SecretKey, "")
	awsCfg := &awsclient.Config{
		Credentials: creds,
		HTTPClient:  client,
		Logger:      awsclient.NewDefaultLogger(),
	}

	m := newMultiRegion(awsCfg, filterRegions(conf.Regions, conf.RegionsExclude))
	return &Purge{
		services:  m,
		resources: make(map[string]*resources, 0),
	}, nil
}

func (p *Purge) Do() error {
	if err := p.Fetch(); err != nil {
		return err
	}

	if err := p.Print(); err != nil {
		return err
	}

	return nil
}

// Print prints all fetched resources
func (p *Purge) Print() error {
	for region, resources := range p.resources {
		fmt.Printf("[%s] found '%d' instances\n", region, len(resources.instances))
		fmt.Printf("[%s] found '%d' volumes\n", region, len(resources.volumes))
	}
	return nil
}

// Fetch fetches all given resources and stores them internally. To print them
// use the Print() method
func (p *Purge) Fetch() error {
	allInstances, err := p.DescribeInstances()
	if err != nil {
		return err
	}

	allVolumes, err := p.DescribeVolumes()
	if err != nil {
		return err
	}

	for _, region := range allRegions {
		p.resources[region] = &resources{
			instances: allInstances[region],
			volumes:   allVolumes[region],
		}
	}

	return nil
}

// DescribeVolumes returns all volumes per region.
func (p *Purge) DescribeVolumes() (map[string][]*ec2.Volume, error) {
	var (
		wg sync.WaitGroup
		mu sync.Mutex

		multiErrors error
	)

	output := make(map[string][]*ec2.Volume)

	for r, s := range p.services.regions {
		wg.Add(1)

		go func(region string, svc *ec2.EC2) {
			resp, err := svc.DescribeVolumes(nil)
			if err != nil {
				mu.Lock()
				multiErrors = multierror.Append(multiErrors, err)
				mu.Unlock()
				wg.Done()
				return
			}

			if resp.Volumes != nil && len(resp.Volumes) != 0 {
				mu.Lock()
				output[region] = resp.Volumes
				mu.Unlock()
			}

			wg.Done()
		}(r, s)
	}

	wg.Wait()

	return output, multiErrors
}

// DescribeInstances returns all instances per region.
func (p *Purge) DescribeInstances() (map[string][]*ec2.Instance, error) {
	var (
		wg sync.WaitGroup
		mu sync.Mutex

		multiErrors error
	)

	output := make(map[string][]*ec2.Instance)

	for r, s := range p.services.regions {
		wg.Add(1)

		go func(region string, svc *ec2.EC2) {
			resp, err := svc.DescribeInstances(nil)
			if err != nil {
				mu.Lock()
				multiErrors = multierror.Append(multiErrors, err)
				mu.Unlock()
				wg.Done()
				return
			}

			if resp.Reservations != nil {
				instances := make([]*ec2.Instance, 0)

				for _, reserv := range resp.Reservations {
					if len(reserv.Instances) != 0 {
						instances = append(instances, reserv.Instances...)
					}
				}

				mu.Lock()
				output[region] = instances
				mu.Unlock()
			}

			wg.Done()
		}(r, s)
	}

	wg.Wait()

	return output, multiErrors
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	kodingmodels "koding/db/models"
	"koding/db/mongodb/modelhelper"
	"socialapi/models"
	"socialapi/workers/common/sem"
	"sync"
	"time"

	"github.com/nlopes/slack"
	"gopkg.in/mgo.v2/bson"
)

type SlackUser struct {
	slack.User
	LastActivity *time.Time `json:"lastActivity,omitempty"`
}

type SlackChannelsResponse struct {
	Groups   []slack.Group   `json:"groups,omitempty"`
	Channels []slack.Channel `json:"channels,omitempty"`
}

// getChannels send a request to the slack with user's token & gets the channels
func getChannels(token string) (*SlackChannelsResponse, error) {
	api := slack.New(token)
	var groups []slack.Group
	var channels []slack.Channel
	var gerr, cerr error
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		groups, gerr = api.GetGroups(true)
		wg.Done()
	}()

	go func() {
		channels, cerr = api.GetChannels(true)
		wg.Done()
	}()

	wg.Wait()

	if gerr != nil {
		return nil, gerr
	}

	if cerr != nil {
		return nil, cerr
	}

	return &SlackChannelsResponse{
		Groups:   groups,
		Channels: channels,
	}, nil
}

func getUsers(token string) ([]SlackUser, error) {
	api := slack.New(token)
	allusers, err := api.GetUsers()
	if err != nil {
		return nil, err
	}

	activeUsersChan := make(chan SlackUser) // idk the total valid number

	sem := sem.New(20) // run 20 goroutines concurrently
	var wg sync.WaitGroup
	wg.Add(len(allusers))

	var activeUsers []SlackUser
	go func() {
		for t := range activeUsersChan {
			activeUsers = append(activeUsers, t)
		}
	}()

	for _, u := range allusers {
		sem.Lock()
		go func(u slack.User) {
			defer sem.Unlock()
			defer wg.Done()

			// no need to add deleted users
			if u.Deleted {
				return
			}

			// filter bots
			if u.IsBot {
				return
			}

			// even if we get error from slack api set it to nil
			var lastActive *time.Time

			// presence information is optional, so we can skip the errored ones
			p, err := api.GetUserPresence(u.ID)
			if err != nil || p == nil {
				activeUsersChan <- SlackUser{u, lastActive}
				return
			}

			// set presence info
			u.Presence = p.Presence

			// LastActivity works inconsistently
			// if u.Presence != slackAway {
			//  LastActiveTime := p.LastActivity.Time()
			//  lastActive = &LastActiveTime
			// }

			activeUsersChan <- SlackUser{u, lastActive}
		}(u)
	}

	wg.Wait()
	close(activeUsersChan)

	return activeUsers, nil
}

func postMessage(req *SlackMessageRequest) (string, error) {
	api := slack.New(req.Token)
	_, id, err := api.PostMessage(req.Channel, req.Text, req.Params)
	return id, err
}

func getTeamInfo(token string) (*slack.TeamInfo, error) {
	api := slack.New(token)
	info, err := api.GetTeamInfo()
	if err != nil {
		return nil, err
	}

	return info, nil
}

func updateUserSlackToken(user *kodingmodels.User, groupName string, m string) error {
	selector := bson.M{"username": user.Name}
	key := fmt.Sprintf("foreignAuth.slack.%s.token", groupName)
	update := bson.M{key: m}

	return modelhelper.UpdateUser(selector, update)
}

// getSlackToken fetches the user's slack token with user's accountID
func getSlackToken(context *models.Context) (string, error) {
	var token string

	user, err := modelhelper.GetUser(context.Client.Account.Nick)
	if err != nil {
		return token, err
	}

	groupName := context.GroupName

	if user.ForeignAuth.Slack != nil {
		if gName, ok := user.ForeignAuth.Slack[groupName]; ok {
			if gName.Token != "" {
				return gName.Token, nil
			}
		}
	}

	return token, models.ErrTokenIsNotFound

}
<commit_msg>socialapi: instead of passing token in request, direclty pass the oauth token<commit_after>package api

import (
	"fmt"
	kodingmodels "koding/db/models"
	"koding/db/mongodb/modelhelper"
	"socialapi/models"
	"socialapi/workers/common/sem"
	"sync"
	"time"

	"github.com/nlopes/slack"
	"gopkg.in/mgo.v2/bson"
)

type SlackUser struct {
	slack.User
	LastActivity *time.Time `json:"lastActivity,omitempty"`
}

type SlackChannelsResponse struct {
	Groups   []slack.Group   `json:"groups,omitempty"`
	Channels []slack.Channel `json:"channels,omitempty"`
}

// getChannels send a request to the slack with user's token & gets the channels
func getChannels(token string) (*SlackChannelsResponse, error) {
	api := slack.New(token)
	var groups []slack.Group
	var channels []slack.Channel
	var gerr, cerr error
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		groups, gerr = api.GetGroups(true)
		wg.Done()
	}()

	go func() {
		channels, cerr = api.GetChannels(true)
		wg.Done()
	}()

	wg.Wait()

	if gerr != nil {
		return nil, gerr
	}

	if cerr != nil {
		return nil, cerr
	}

	return &SlackChannelsResponse{
		Groups:   groups,
		Channels: channels,
	}, nil
}

func getUsers(token string) ([]SlackUser, error) {
	api := slack.New(token)
	allusers, err := api.GetUsers()
	if err != nil {
		return nil, err
	}

	activeUsersChan := make(chan SlackUser) // idk the total valid number

	sem := sem.New(20) // run 20 goroutines concurrently
	var wg sync.WaitGroup
	wg.Add(len(allusers))

	var activeUsers []SlackUser
	go func() {
		for t := range activeUsersChan {
			activeUsers = append(activeUsers, t)
		}
	}()

	for _, u := range allusers {
		sem.Lock()
		go func(u slack.User) {
			defer sem.Unlock()
			defer wg.Done()

			// no need to add deleted users
			if u.Deleted {
				return
			}

			// filter bots
			if u.IsBot {
				return
			}

			// even if we get error from slack api set it to nil
			var lastActive *time.Time

			// presence information is optional, so we can skip the errored ones
			p, err := api.GetUserPresence(u.ID)
			if err != nil || p == nil {
				activeUsersChan <- SlackUser{u, lastActive}
				return
			}

			// set presence info
			u.Presence = p.Presence

			// LastActivity works inconsistently
			// if u.Presence != slackAway {
			//  LastActiveTime := p.LastActivity.Time()
			//  lastActive = &LastActiveTime
			// }

			activeUsersChan <- SlackUser{u, lastActive}
		}(u)
	}

	wg.Wait()
	close(activeUsersChan)

	return activeUsers, nil
}

func postMessage(token string, req *SlackMessageRequest) (string, error) {
	api := slack.New(token)
	_, id, err := api.PostMessage(req.Channel, req.Text, req.Params)
	return id, err
}

func getTeamInfo(token string) (*slack.TeamInfo, error) {
	api := slack.New(token)
	info, err := api.GetTeamInfo()
	if err != nil {
		return nil, err
	}

	return info, nil
}

func updateUserSlackToken(user *kodingmodels.User, groupName string, m string) error {
	selector := bson.M{"username": user.Name}
	key := fmt.Sprintf("foreignAuth.slack.%s.token", groupName)
	update := bson.M{key: m}

	return modelhelper.UpdateUser(selector, update)
}

// getSlackToken fetches the user's slack token with user's accountID
func getSlackToken(context *models.Context) (string, error) {
	var token string

	user, err := modelhelper.GetUser(context.Client.Account.Nick)
	if err != nil {
		return token, err
	}

	groupName := context.GroupName

	if user.ForeignAuth.Slack != nil {
		if gName, ok := user.ForeignAuth.Slack[groupName]; ok {
			if gName.Token != "" {
				return gName.Token, nil
			}
		}
	}

	return token, models.ErrTokenIsNotFound

}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"testing"
)

func TestRequestAdminSigning(t *testing.T) {
	req, err := http.NewRequest("GET", "http://example.org/repositories", nil)
	if err != nil {
		t.Fatal(err)
	}
	key := []byte("test")

	SignAsAdmin(req, key)
	if req.Header.Get("Authorization") == "" {
		t.Fatal("no authorization header")
	}
	if !ValidateAdminSigned(req, key, time.Minute) {
		buf := bytes.Buffer{}
		concatenateTo(req, &buf)
		t.Log(buf.String())
		t.Fatal("validation failed")
	}

	req.Header.Set("Authorization", "")
	if ValidateAdminSigned(req, key, time.Minute) {
		t.Fatal("validation succeeded even without Authorization header")
	}

	req.Header.Set("Authorization", "basic foo")
	if ValidateAdminSigned(req, key, time.Minute) {
		t.Fatal("validation succeeded even non-lara Authorization header")
	}

	req.Header.Set("Authorization", "lara foo")
	if ValidateAdminSigned(req, key, time.Minute) {
		t.Fatal("validation succeeded even non-admin Authorization header")
	}

	req.Header.Set("Authorization", "lara admin ")
	if ValidateAdminSigned(req, key, time.Minute) {
		t.Fatal("validation succeeded even without hash in Authorization header")
	}

	SignAsAdmin(req, key)
	req.Method = "POST"
	if ValidateAdminSigned(req, key, time.Minute) {
		t.Fatal("validation succeeded even after method change")
	}

	SignAsAdmin(req, key)
	req.Header.Set("Test", "1")
	if ValidateAdminSigned(req, key, time.Minute) {
		t.Fatal("validation succeeded even after header addition")
	}

	SignAsAdmin(req, key)
	newURL, err := url.Parse("http://example.org/repositories?x=3")
	if err != nil {
		t.Fatal(err)
	}
	req.URL = newURL
	if ValidateAdminSigned(req, key, time.Minute) {
		t.Fatal("validation succeeded even after URL changing addition")
	}

	SignAsAdmin(req, key)
	req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("changed body")))
	if ValidateAdminSigned(req, key, time.Minute) {
		t.Fatal("validation succeeded even after body change")
	}
	// ensure that req.Body is still readable as it would be without signing
	buf := make([]byte, 100)
	read, _ := req.Body.Read(buf)
	if string(buf[:read]) != "changed body" {
		t.Fatal("body no longer readable after signing")
	}

	tenSecsAgo := time.Now().Add(-10 * time.Second)
	// this will most likely send non-GMT time which is against the HTTP RFC;
	// as we should handle this as well, it's ok for testing:
	req.Header.Set("Date", tenSecsAgo.Format(time.RFC1123))
	SignAsAdmin(req, key)
	if ValidateAdminSigned(req, key, 9*time.Second) {
		t.Fatal("validation succeeded even after reaching max signature age")
	}
}
<commit_msg>test: admin signing tests refactored to have individual tests for all test cases.<commit_after>package api

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"testing"
)

func TestAuthorizationHeader(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	if req.Header.Get("Authorization") == "" {
		t.Fatal("no authorization header")
	}
}

func runSigningTestsWithMaxAge(req *http.Request, maxAge time.Duration) bool {
	key := adminSecret()
	return ValidateAdminSigned(req, key, maxAge)
}

func runSigningTest(req *http.Request) bool {
	return runSigningTestsWithMaxAge(req, time.Minute)
}

func TestAdminSigningCorrectSignature(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	if !runSigningTest(req) {
		buf := bytes.Buffer{}
		concatenateTo(req, &buf)
		t.Log(buf.String())
		t.Fatal("validation failed")
	}
}

func TestAdminSigningEmptyAuthorizationHeader(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	req.Header.Set("Authorization", "")

	if runSigningTest(req) {
		t.Fatal("validation succeeded even without Authorization header")
	}
}

func TestAdminSigningNonLaraAuthorizationHeader(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	req.Header.Set("Authorization", "basic foo")

	if runSigningTest(req) {
		t.Fatal("validation succeeded even non-lara Authorization header")
	}
}

func TestAdminSigningNonAdminAuthorizationHeader(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	req.Header.Set("Authorization", "lara foo")
	if runSigningTest(req) {
		t.Fatal("validation succeeded even non-admin Authorization header")
	}
}

func TestAdminSigningNonGivenAuthorizationHeader(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	req.Header.Set("Authorization", "lara admin ")
	if runSigningTest(req) {
		t.Fatal("validation succeeded even without hash in Authorization header")
	}
}

func TestAdminSigningChangedMethodAuthorizationHeader(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	req.Method = "POST"
	if runSigningTest(req) {
		t.Fatal("validation succeeded even after method change")
	}
}

func TestAdminSigningAddedHeader(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	req.Header.Set("Test", "1")
	if runSigningTest(req) {
		t.Fatal("validation succeeded even after header addition")
	}
}

func TestAdminSigningChangedUrl(t *testing.T) {
	req := getRepositoriesAdminRequest(t)
	newURL, err := url.Parse("http://example.org/repositories?x=3")
	if err != nil {
		t.Fatal(err)
	}
	req.URL = newURL
	if runSigningTest(req) {
		t.Fatal("validation succeeded even after URL changing addition")
	}
}

func TestAdminSigningOutdatedSignature(t *testing.T) {
	req := getRepositoriesRequest(t)
	tenSecsAgo := time.Now().Add(-10 * time.Second)
	// this will most likely send non-GMT time which is against the HTTP RFC;
	// as we should handle this as well, it's ok for testing:
	req.Header.Set("Date", tenSecsAgo.Format(time.RFC1123))
	SignAsAdmin(req, adminSecret())
	if runSigningTestsWithMaxAge(req, 9*time.Second) {
		t.Fatal("validation succeeded even after reaching max signature age")
	}
}

func changedBodyAdminRequestForValidation(t *testing.T) *http.Request {
	req := getRepositoriesAdminRequest(t)
	req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("changed body")))
	return req
}

func TestAdminSigningBodyChange(t *testing.T) {
	req := changedBodyAdminRequestForValidation(t)
	if runSigningTest(req) {
		t.Fatal("validation succeeded even after body change")
	}
}

func TestAdminSigningBodyTextRead(t *testing.T) {
	req := changedBodyAdminRequestForValidation(t)

	runSigningTest(req)
	buf := make([]byte, 100)
	read, _ := req.Body.Read(buf)
	if string(buf[:read]) != "changed body" {
		t.Fatal("body no longer readable after signing")
	}
}
<|endoftext|>