<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"os/user"
	"strings"
	"text/tabwriter"

	"github.com/atsaki/golang-cloudstack-library"
	"github.com/codegangsta/cli"
	"github.com/vaughan0/go-ini"
)

const (
	sep = '\t'
)

var (
	client *cloudstack.Client
)

func expandPath(path string) string {
	usr, _ := user.Current()
	home := usr.HomeDir

	if strings.HasPrefix(path, "~/") {
		path = strings.Replace(path, "~/", home+"/", 1)
	}
	return path
}

func setup(c *cli.Context) {
	if !c.GlobalBool("debug") {
		log.SetOutput(ioutil.Discard)
	}
	configfile := expandPath(c.GlobalString("config-file"))
	log.Println("configfile:", configfile)
	cfg, err := ini.LoadFile(configfile)
	if err != nil {
		log.Fatal(err)
	}
	var ok bool

	profile, ok := cfg.Get("core", "profile")
	if !ok {
		profile = "local"
	}
	log.Println("profile:", profile)

	endpointUrl, ok := cfg.Get(profile, "url")
	if !ok {
		log.Fatalf("URL is not specified")
	}
	log.Println("url:", endpointUrl)

	apikey, ok := cfg.Get(profile, "apikey")
	if !ok {
		apikey = ""
	}
	log.Println("apikey:", apikey)

	secretkey, ok := cfg.Get(profile, "secretkey")
	if !ok {
		secretkey = ""
	}
	log.Println("secretkey:", secretkey)

	username, ok := cfg.Get(profile, "username")
	if !ok {
		username = ""
	}
	log.Println("username:", username)

	password, ok := cfg.Get(profile, "password")
	if !ok {
		password = ""
	}
	log.Println("password:", password)

	endpoint, err := url.Parse(endpointUrl)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("endpoint:", endpoint)

	client, err = cloudstack.NewClient(*endpoint, apikey, secretkey,
		username, password)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {

	tabw := new(tabwriter.Writer)
	tabw.Init(os.Stdout, 0, 8, 0, byte(sep), 0)

	app := cli.NewApp()
	app.Name = "lg"
	app.Usage = "lg comand"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config-file, c",
			Value: "~/.cloudmonkey/config",
			Usage: "Config file path",
		},
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Show debug messages",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:      "virtualmachines",
			ShortName: "vms",
			Usage:     "List virtualmachines",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListVirtualMachinesParameter{}
				vms, err := client.ListVirtualMachines(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, vm := range vms {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								vm.Id.String,
								vm.Name.String,
								vm.Displayname.String,
								vm.State.String,
								vm.Zonename.String,
								vm.Templatename.String,
								vm.Serviceofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:  "deploy",
			Usage: "Deploy virtualmachine",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "zone, z",
					Value: "",
					Usage: "The zoneid or zonename of the virtualmachine",
				},
				cli.StringFlag{
					Name:  "serviceoffering, s",
					Value: "",
					Usage: "The serviceofferingid or serviceofferingname of the virtualmachine",
				},
				cli.StringFlag{
					Name:  "template, t",
					Value: "",
					Usage: "The templateid or templatename of the virtualmachine",
				},
				cli.StringFlag{
					Name:  "displayname",
					Value: "",
					Usage: "The displayname of the virtualmachine",
				},
			},
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.DeployVirtualMachineParameter{}
				if c.String("zone") != "" {
					params.SetZoneid(c.String("zone"))
				}
				if c.String("serviceoffering") != "" {
					params.SetServiceofferingid(c.String("serviceoffering"))
				}
				if c.String("template") != "" {
					params.SetTemplateid(c.String("template"))
				}
				if c.String("displayname") != "" {
					params.SetDisplayname(c.String("displayname"))
				}
				vm, err := client.DeployVirtualMachine(params)
				if err != nil {
					log.Fatal(err)
				}
				fmt.Fprintln(
					tabw,
					strings.Join(
						[]string{
							vm.Id.String,
							vm.Name.String,
							vm.Displayname.String,
							vm.State.String,
							vm.Zonename.String,
							vm.Templatename.String,
							vm.Serviceofferingname.String,
						}, string(sep)))
				tabw.Flush()
			},
		},
		{
			Name:  "start",
			Usage: "Start virtualmachine",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.StartVirtualMachineParameter{}
				var ids []string
				if len(c.Args()) == 0 {
					scanner := bufio.NewScanner(os.Stdin)
					for scanner.Scan() {
						ids = append(ids, strings.Split(scanner.Text(), string(sep))[0])
					}
				} else if len(c.Args()) == 1 {
					ids = append(ids, c.Args()[0])
				}
				log.Println("ids:", ids)
				for _, id := range ids {
					params.SetId(id)
					vm, err := client.StartVirtualMachine(params)
					if err != nil {
						log.Fatal(err)
					}
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								vm.Id.String,
								vm.Name.String,
								vm.Displayname.String,
								vm.State.String,
								vm.Zonename.String,
								vm.Templatename.String,
								vm.Serviceofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:  "stop",
			Usage: "Stop virtualmachine",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.StopVirtualMachineParameter{}
				var ids []string
				if len(c.Args()) == 0 {
					scanner := bufio.NewScanner(os.Stdin)
					for scanner.Scan() {
						ids = append(ids, strings.Split(scanner.Text(), string(sep))[0])
					}
				} else if len(c.Args()) == 1 {
					ids = append(ids, c.Args()[0])
				}
				log.Println("ids:", ids)
				for _, id := range ids {
					params.SetId(id)
					vm, err := client.StopVirtualMachine(params)
					if err != nil {
						log.Fatal(err)
					}
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								vm.Id.String,
								vm.Name.String,
								vm.Displayname.String,
								vm.State.String,
								vm.Zonename.String,
								vm.Templatename.String,
								vm.Serviceofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:  "destroy",
			Usage: "Destroy virtualmachine",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.DestroyVirtualMachineParameter{}
				var ids []string
				if len(c.Args()) == 0 {
					scanner := bufio.NewScanner(os.Stdin)
					for scanner.Scan() {
						ids = append(ids, strings.Split(scanner.Text(), string(sep))[0])
					}
				} else if len(c.Args()) == 1 {
					ids = append(ids, c.Args()[0])
				}
				log.Println("ids:", ids)
				for _, id := range ids {
					params.SetId(id)
					vm, err := client.DestroyVirtualMachine(params)
					if err != nil {
						log.Fatal(err)
					}
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								vm.Id.String,
								vm.Name.String,
								vm.Displayname.String,
								vm.State.String,
								vm.Zonename.String,
								vm.Templatename.String,
								vm.Serviceofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:  "zones",
			Usage: "List zones",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListZonesParameter{}
				zones, err := client.ListZones(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, zone := range zones {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								zone.Id.String,
								zone.Name.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:      "serviceofferings",
			ShortName: "sizes",
			Usage:     "List serviceofferings",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListServiceOfferingsParameter{}
				serviceofferings, err := client.ListServiceOfferings(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, serviceoffering := range serviceofferings {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								serviceoffering.Id.String,
								serviceoffering.Name.String,
								fmt.Sprint(serviceoffering.Cpunumber.Int64),
								fmt.Sprint(serviceoffering.Cpuspeed.Int64),
								fmt.Sprint(serviceoffering.Memory.Int64),
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:      "templates",
			ShortName: "images",
			Usage:     "list templates",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListTemplatesParameter{}
				params.SetTemplatefilter("featured")
				templates, err := client.ListTemplates(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, template := range templates {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								template.Id.String,
								template.Name.String,
								template.Displaytext.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:      "networks",
			ShortName: "nws",
			Usage:     "list network",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListNetworksParameter{}
				networks, err := client.ListNetworks(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, network := range networks {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								network.Id.String,
								network.Name.String,
								network.Networkofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:      "publicipaddresses",
			ShortName: "ips",
			Usage:     "list ipaddresses",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListPublicIpAddressesParameter{}
				ips, err := client.ListPublicIpAddresses(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, ip := range ips {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								ip.Id.String,
								ip.Zonename.String,
								ip.Associatednetworkname.String,
								fmt.Sprint(ip.Issourcenat.Bool),
								ip.Ipaddress.String,
								ip.Virtualmachinedisplayname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>add profile option<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"os/user"
	"strings"
	"text/tabwriter"

	"github.com/atsaki/golang-cloudstack-library"
	"github.com/codegangsta/cli"
	"github.com/vaughan0/go-ini"
)

const (
	sep = '\t'
)

var (
	client *cloudstack.Client
)

func expandPath(path string) string {
	usr, _ := user.Current()
	home := usr.HomeDir

	if strings.HasPrefix(path, "~/") {
		path = strings.Replace(path, "~/", home+"/", 1)
	}
	return path
}

func setup(c *cli.Context) {

	if !c.GlobalBool("debug") {
		log.SetOutput(ioutil.Discard)
	}

	configfile := expandPath(c.GlobalString("config-file"))
	log.Println("configfile:", configfile)
	cfg, err := ini.LoadFile(configfile)
	if err != nil {
		log.Fatal(err)
	}
	var ok bool

	profile, ok := cfg.Get("core", "profile")
	if !ok {
		profile = "local"
	}
	if c.GlobalString("profile") != "" {
		profile = c.GlobalString("profile")
	}
	log.Println("profile:", profile)

	endpointUrl, ok := cfg.Get(profile, "url")
	if !ok {
		log.Fatalf("URL is not specified")
	}
	log.Println("url:", endpointUrl)

	apikey, ok := cfg.Get(profile, "apikey")
	if !ok {
		apikey = ""
	}
	log.Println("apikey:", apikey)

	secretkey, ok := cfg.Get(profile, "secretkey")
	if !ok {
		secretkey = ""
	}
	log.Println("secretkey:", secretkey)

	username, ok := cfg.Get(profile, "username")
	if !ok {
		username = ""
	}
	log.Println("username:", username)

	password, ok := cfg.Get(profile, "password")
	if !ok {
		password = ""
	}
	log.Println("password:", password)

	endpoint, err := url.Parse(endpointUrl)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("endpoint:", endpoint)

	client, err = cloudstack.NewClient(*endpoint, apikey, secretkey,
		username, password)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {

	tabw := new(tabwriter.Writer)
	tabw.Init(os.Stdout, 0, 8, 0, byte(sep), 0)

	app := cli.NewApp()
	app.Name = "lg"
	app.Usage = "lg comand"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config-file, c",
			Value: "~/.cloudmonkey/config",
			Usage: "Config file path",
		},
		cli.StringFlag{
			Name:  "profile, P",
			Value: "",
			Usage: "Server profile",
		},
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Show debug messages",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:      "virtualmachines",
			ShortName: "vms",
			Usage:     "List virtualmachines",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListVirtualMachinesParameter{}
				vms, err := client.ListVirtualMachines(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, vm := range vms {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								vm.Id.String,
								vm.Name.String,
								vm.Displayname.String,
								vm.State.String,
								vm.Zonename.String,
								vm.Templatename.String,
								vm.Serviceofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:  "deploy",
			Usage: "Deploy virtualmachine",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "zone, z",
					Value: "",
					Usage: "The zoneid or zonename of the virtualmachine",
				},
				cli.StringFlag{
					Name:  "serviceoffering, s",
					Value: "",
					Usage: "The serviceofferingid or serviceofferingname of the virtualmachine",
				},
				cli.StringFlag{
					Name:  "template, t",
					Value: "",
					Usage: "The templateid or templatename of the virtualmachine",
				},
				cli.StringFlag{
					Name:  "displayname",
					Value: "",
					Usage: "The displayname of the virtualmachine",
				},
			},
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.DeployVirtualMachineParameter{}
				if c.String("zone") != "" {
					params.SetZoneid(c.String("zone"))
				}
				if c.String("serviceoffering") != "" {
					params.SetServiceofferingid(c.String("serviceoffering"))
				}
				if c.String("template") != "" {
					params.SetTemplateid(c.String("template"))
				}
				if c.String("displayname") != "" {
					params.SetDisplayname(c.String("displayname"))
				}
				vm, err := client.DeployVirtualMachine(params)
				if err != nil {
					log.Fatal(err)
				}
				fmt.Fprintln(
					tabw,
					strings.Join(
						[]string{
							vm.Id.String,
							vm.Name.String,
							vm.Displayname.String,
							vm.State.String,
							vm.Zonename.String,
							vm.Templatename.String,
							vm.Serviceofferingname.String,
						}, string(sep)))
				tabw.Flush()
			},
		},
		{
			Name:  "start",
			Usage: "Start virtualmachine",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.StartVirtualMachineParameter{}
				var ids []string
				if len(c.Args()) == 0 {
					scanner := bufio.NewScanner(os.Stdin)
					for scanner.Scan() {
						ids = append(ids, strings.Split(scanner.Text(), string(sep))[0])
					}
				} else if len(c.Args()) == 1 {
					ids = append(ids, c.Args()[0])
				}
				log.Println("ids:", ids)
				for _, id := range ids {
					params.SetId(id)
					vm, err := client.StartVirtualMachine(params)
					if err != nil {
						log.Fatal(err)
					}
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								vm.Id.String,
								vm.Name.String,
								vm.Displayname.String,
								vm.State.String,
								vm.Zonename.String,
								vm.Templatename.String,
								vm.Serviceofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:  "stop",
			Usage: "Stop virtualmachine",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.StopVirtualMachineParameter{}
				var ids []string
				if len(c.Args()) == 0 {
					scanner := bufio.NewScanner(os.Stdin)
					for scanner.Scan() {
						ids = append(ids, strings.Split(scanner.Text(), string(sep))[0])
					}
				} else if len(c.Args()) == 1 {
					ids = append(ids, c.Args()[0])
				}
				log.Println("ids:", ids)
				for _, id := range ids {
					params.SetId(id)
					vm, err := client.StopVirtualMachine(params)
					if err != nil {
						log.Fatal(err)
					}
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								vm.Id.String,
								vm.Name.String,
								vm.Displayname.String,
								vm.State.String,
								vm.Zonename.String,
								vm.Templatename.String,
								vm.Serviceofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:  "destroy",
			Usage: "Destroy virtualmachine",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.DestroyVirtualMachineParameter{}
				var ids []string
				if len(c.Args()) == 0 {
					scanner := bufio.NewScanner(os.Stdin)
					for scanner.Scan() {
						ids = append(ids, strings.Split(scanner.Text(), string(sep))[0])
					}
				} else if len(c.Args()) == 1 {
					ids = append(ids, c.Args()[0])
				}
				log.Println("ids:", ids)
				for _, id := range ids {
					params.SetId(id)
					vm, err := client.DestroyVirtualMachine(params)
					if err != nil {
						log.Fatal(err)
					}
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								vm.Id.String,
								vm.Name.String,
								vm.Displayname.String,
								vm.State.String,
								vm.Zonename.String,
								vm.Templatename.String,
								vm.Serviceofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:  "zones",
			Usage: "List zones",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListZonesParameter{}
				zones, err := client.ListZones(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, zone := range zones {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								zone.Id.String,
								zone.Name.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:      "serviceofferings",
			ShortName: "sizes",
			Usage:     "List serviceofferings",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListServiceOfferingsParameter{}
				serviceofferings, err := client.ListServiceOfferings(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, serviceoffering := range serviceofferings {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								serviceoffering.Id.String,
								serviceoffering.Name.String,
								fmt.Sprint(serviceoffering.Cpunumber.Int64),
								fmt.Sprint(serviceoffering.Cpuspeed.Int64),
								fmt.Sprint(serviceoffering.Memory.Int64),
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:      "templates",
			ShortName: "images",
			Usage:     "list templates",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListTemplatesParameter{}
				params.SetTemplatefilter("featured")
				templates, err := client.ListTemplates(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, template := range templates {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								template.Id.String,
								template.Name.String,
								template.Displaytext.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:      "networks",
			ShortName: "nws",
			Usage:     "list network",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListNetworksParameter{}
				networks, err := client.ListNetworks(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, network := range networks {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								network.Id.String,
								network.Name.String,
								network.Networkofferingname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
		{
			Name:      "publicipaddresses",
			ShortName: "ips",
			Usage:     "list ipaddresses",
			Action: func(c *cli.Context) {
				setup(c)
				params := cloudstack.ListPublicIpAddressesParameter{}
				ips, err := client.ListPublicIpAddresses(params)
				if err != nil {
					log.Fatal(err)
				}
				for _, ip := range ips {
					fmt.Fprintln(
						tabw,
						strings.Join(
							[]string{
								ip.Id.String,
								ip.Zonename.String,
								ip.Associatednetworkname.String,
								fmt.Sprint(ip.Issourcenat.Bool),
								ip.Ipaddress.String,
								ip.Virtualmachinedisplayname.String,
							}, string(sep)))
				}
				tabw.Flush()
			},
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fukata/golang-stats-api-handler"
	"github.com/jessevdk/go-flags"
	"github.com/kazeburo/chocon/proxy"
	"github.com/lestrrat/go-apache-logformat"
	"github.com/lestrrat/go-file-rotatelogs"
	"github.com/lestrrat/go-server-starter-listener"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

var (
	Version string
)

type cmdOpts struct {
	Listen           string `short:"l" long:"listen" default:"0.0.0.0" description:"address to bind"`
	Port             string `short:"p" long:"port" default:"3000" description:"Port number to bind"`
	LogDir           string `long:"access-log-dir" default:"" description:"directory to store logfiles"`
	LogRotate        int64  `long:"access-log-rotate" default:"30" description:"Number of day before remove logs"`
	Version          bool   `short:"v" long:"version" description:"Show version"`
	KeepaliveConns   int    `short:"c" default:"2" long:"keepalive-conns" description:"maximun keepalive connections for upstream"`
	ReadTimeout      int    `long:"read-timeout" default:"30" description:"timeout of reading request"`
	WriteTimeout     int    `long:"write-timeout" default:"90" description:"timeout of writing response"`
	ProxyReadTimeout int    `long:"proxy-read-timeout" default:"60" description:"timeout of reading response from upstream"`
}

func addStatsHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Index(r.URL.Path, "/.api/stats") == 0 {
			stats_api.Handler(w, r)
		} else {
			h.ServeHTTP(w, r)
		}
	})
}

func addLogHandler(h http.Handler, log_dir string, log_rotate int64) http.Server {
	apache_log, err := apachelog.New(`%h %l %u %t "%r" %>s %b "%v" %T.%{msec_frac}t %{X-Chocon-Req}i`)
	if err != nil {
		panic(fmt.Sprintf("could not create logger: %v", err))
	}

	if log_dir == "stdout" {
		return http.Server{
			Handler: apache_log.Wrap(h, os.Stdout),
		}
	} else if log_dir == "" {
		return http.Server{
			Handler: apache_log.Wrap(h, os.Stderr),
		}
	} else if log_dir == "none" {
		return http.Server{
			Handler: h,
		}
	}

	log_file := log_dir
	link_name := log_dir
	if !strings.HasSuffix(log_dir, "/") {
		log_file += "/"
		link_name += "/"

	}
	log_file += "access_log.%Y%m%d%H%M"
	link_name += "current"

	rl := rotatelogs.New(
		log_file,
		rotatelogs.WithLinkName(link_name),
		rotatelogs.WithMaxAge(time.Duration(log_rotate)*86400*time.Second),
		rotatelogs.WithRotationTime(time.Second*86400),
	)

	return http.Server{
		Handler: apache_log.Wrap(h, rl),
	}
}

func main() {
	opts := cmdOpts{}
	psr := flags.NewParser(&opts, flags.Default)
	_, err := psr.Parse()
	if err != nil {
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf(`chocon %s
Compiler: %s %s
`,
			Version,
			runtime.Compiler,
			runtime.Version())
		return

	}

	requestConverter := func(r *http.Request, pr *http.Request, ps *proxy_handler.ProxyStatus) {
		if r.Host == "" {
			ps.Status = http.StatusBadRequest
			return
		}
		originalHost := r.Host
		hostSplited := strings.Split(originalHost, ".")
		lastPartIndex := 0
		for i, hostPart := range hostSplited {
			if hostPart == "ccnproxy-ssl" || hostPart == "ccnproxy-secure" || hostPart == "ccnproxy-https" || hostPart == "ccnproxy" {
				lastPartIndex = i
			}
		}
		if lastPartIndex == 0 {
			ps.Status = http.StatusBadRequest
			return
		}

		pr.URL.Host = strings.Join(hostSplited[0:lastPartIndex], ".")
		if hostSplited[lastPartIndex] == "ccnproxy-https" || hostSplited[lastPartIndex] == "ccnproxy-secure" || hostSplited[lastPartIndex] == "ccnproxy-ssl" {
			pr.URL.Scheme = "https"
		}
	}

	var transport http.RoundTripper = &http.Transport{
		// inherited http.DefaultTransport
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
		// self-customized values
		MaxIdleConnsPerHost:   opts.KeepaliveConns,
		ResponseHeaderTimeout: time.Duration(opts.ProxyReadTimeout) * time.Second,
	}

	proxyHandler := addStatsHandler(proxy_handler.NewProxyWithRequestConverter(requestConverter, &transport))

	l, err := ss.NewListener()
	if l == nil || err != nil {
		// Fallback if not running under Server::Starter
		l, err = net.Listen("tcp", fmt.Sprintf("%s:%s", opts.Listen, opts.Port))
		if err != nil {
			panic(fmt.Sprintf("Failed to listen to port %s:%s", opts.Listen, opts.Port))
		}
	}

	server := addLogHandler(proxyHandler, opts.LogDir, opts.LogRotate)
	server.ReadTimeout = time.Duration(opts.ReadTimeout) * time.Second
	server.WriteTimeout = time.Duration(opts.WriteTimeout) * time.Second
	server.Serve(l)
}
<commit_msg>bugfix. rewrite host header<commit_after>package main

import (
	"fmt"
	"github.com/fukata/golang-stats-api-handler"
	"github.com/jessevdk/go-flags"
	"github.com/kazeburo/chocon/proxy"
	"github.com/lestrrat/go-apache-logformat"
	"github.com/lestrrat/go-file-rotatelogs"
	"github.com/lestrrat/go-server-starter-listener"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

var (
	Version string
)

type cmdOpts struct {
	Listen           string `short:"l" long:"listen" default:"0.0.0.0" description:"address to bind"`
	Port             string `short:"p" long:"port" default:"3000" description:"Port number to bind"`
	LogDir           string `long:"access-log-dir" default:"" description:"directory to store logfiles"`
	LogRotate        int64  `long:"access-log-rotate" default:"30" description:"Number of day before remove logs"`
	Version          bool   `short:"v" long:"version" description:"Show version"`
	KeepaliveConns   int    `short:"c" default:"2" long:"keepalive-conns" description:"maximun keepalive connections for upstream"`
	ReadTimeout      int    `long:"read-timeout" default:"30" description:"timeout of reading request"`
	WriteTimeout     int    `long:"write-timeout" default:"90" description:"timeout of writing response"`
	ProxyReadTimeout int    `long:"proxy-read-timeout" default:"60" description:"timeout of reading response from upstream"`
}

func addStatsHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Index(r.URL.Path, "/.api/stats") == 0 {
			stats_api.Handler(w, r)
		} else {
			h.ServeHTTP(w, r)
		}
	})
}

func addLogHandler(h http.Handler, log_dir string, log_rotate int64) http.Server {
	apache_log, err := apachelog.New(`%h %l %u %t "%r" %>s %b "%v" %T.%{msec_frac}t %{X-Chocon-Req}i`)
	if err != nil {
		panic(fmt.Sprintf("could not create logger: %v", err))
	}

	if log_dir == "stdout" {
		return http.Server{
			Handler: apache_log.Wrap(h, os.Stdout),
		}
	} else if log_dir == "" {
		return http.Server{
			Handler: apache_log.Wrap(h, os.Stderr),
		}
	} else if log_dir == "none" {
		return http.Server{
			Handler: h,
		}
	}

	log_file := log_dir
	link_name := log_dir
	if !strings.HasSuffix(log_dir, "/") {
		log_file += "/"
		link_name += "/"

	}
	log_file += "access_log.%Y%m%d%H%M"
	link_name += "current"

	rl := rotatelogs.New(
		log_file,
		rotatelogs.WithLinkName(link_name),
		rotatelogs.WithMaxAge(time.Duration(log_rotate)*86400*time.Second),
		rotatelogs.WithRotationTime(time.Second*86400),
	)

	return http.Server{
		Handler: apache_log.Wrap(h, rl),
	}
}

func main() {
	opts := cmdOpts{}
	psr := flags.NewParser(&opts, flags.Default)
	_, err := psr.Parse()
	if err != nil {
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf(`chocon %s
Compiler: %s %s
`,
			Version,
			runtime.Compiler,
			runtime.Version())
		return

	}

	requestConverter := func(r *http.Request, pr *http.Request, ps *proxy_handler.ProxyStatus) {
		if r.Host == "" {
			ps.Status = http.StatusBadRequest
			return
		}
		originalHost := r.Host
		hostSplited := strings.Split(originalHost, ".")
		lastPartIndex := 0
		for i, hostPart := range hostSplited {
			if hostPart == "ccnproxy-ssl" || hostPart == "ccnproxy-secure" || hostPart == "ccnproxy-https" || hostPart == "ccnproxy" {
				lastPartIndex = i
			}
		}
		if lastPartIndex == 0 {
			ps.Status = http.StatusBadRequest
			return
		}

		pr.URL.Host = strings.Join(hostSplited[0:lastPartIndex], ".")
		pr.Host = pr.URL.Host
		if hostSplited[lastPartIndex] == "ccnproxy-https" || hostSplited[lastPartIndex] == "ccnproxy-secure" || hostSplited[lastPartIndex] == "ccnproxy-ssl" {
			pr.URL.Scheme = "https"
		}
	}

	var transport http.RoundTripper = &http.Transport{
		// inherited http.DefaultTransport
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
		// self-customized values
		MaxIdleConnsPerHost:   opts.KeepaliveConns,
		ResponseHeaderTimeout: time.Duration(opts.ProxyReadTimeout) * time.Second,
	}

	proxyHandler := addStatsHandler(proxy_handler.NewProxyWithRequestConverter(requestConverter, &transport))

	l, err := ss.NewListener()
	if l == nil || err != nil {
		// Fallback if not running under Server::Starter
		l, err = net.Listen("tcp", fmt.Sprintf("%s:%s", opts.Listen, opts.Port))
		if err != nil {
			panic(fmt.Sprintf("Failed to listen to port %s:%s", opts.Listen, opts.Port))
		}
	}

	server := addLogHandler(proxyHandler, opts.LogDir, opts.LogRotate)
	server.ReadTimeout = time.Duration(opts.ReadTimeout) * time.Second
	server.WriteTimeout = time.Duration(opts.WriteTimeout) * time.Second
	server.Serve(l)
}
<|endoftext|>


<commit_before>package haproxy

import (
	"github.com/QubitProducts/bamboo/Godeps/_workspace/src/github.com/samuel/go-zookeeper/zk"
	conf "github.com/QubitProducts/bamboo/configuration"
	"github.com/QubitProducts/bamboo/services/marathon"
	"github.com/QubitProducts/bamboo/services/service"
)

type templateData struct {
	Apps     marathon.AppList
	Services map[string]service.Service
}

func GetTemplateData(config *conf.Configuration, conn *zk.Conn) (*templateData, error) {

	apps, err := marathon.FetchApps(config.Marathon, config)

	if err != nil {
		return nil, err
	}

	services, err := service.All(conn, config.Bamboo.Zookeeper)

	if err != nil {
		return nil, err
	}

	return &templateData{apps, services}, nil
}
<commit_msg>Update services/haproxy for service.Storage change<commit_after>package haproxy

import (
	conf "github.com/QubitProducts/bamboo/configuration"
	"github.com/QubitProducts/bamboo/services/marathon"
	"github.com/QubitProducts/bamboo/services/service"
)

type templateData struct {
	Apps     marathon.AppList
	Services map[string]service.Service
}

func GetTemplateData(config *conf.Configuration, storage service.Storage) (*templateData, error) {

	apps, err := marathon.FetchApps(config.Marathon, config)

	if err != nil {
		return nil, err
	}

	services, err := storage.All()
	if err != nil {
		return nil, err
	}

	byName := make(map[string]service.Service)
	for _, service := range services {
		byName[service.Id] = service
	}

	return &templateData{apps, byName}, nil
}
<|endoftext|>


<commit_before>package jwthelper

import (
	"sync"
	"time"

	"github.com/dgrijalva/jwt-go"
)

// claims stores JWT claims.
// it contains a jwt.MapClaims.
type claims struct {
	m      sync.Mutex
	claims jwt.MapClaims
}

// Claim represents JWT claim.
// Use claim helper functions to get a Claim:
// StringClaim(), IntClaim(), UintClaim(), TimeClaim()...
type Claim struct {
	f func(ops *claims)
}

// newClaims news a Claims and intializes the internal mutext and map.
func newClaims() claims {
	return claims{
		sync.Mutex{},
		map[string]interface{}{},
	}
}

// newClaim news a Claim and set the internal map with given name -> value pair.
func newClaim(name string, value interface{}) Claim {
	return Claim{func(c *claims) {
		c.m.Lock()
		defer c.m.Unlock()
		c.claims[name] = value
	}}
}

// StringClaim returns a Claim with string value.
func StringClaim(name, value string) Claim {
	return newClaim(name, value)
}

// IntClaim returns a Claim with int value.
func IntClaim(name string, value int) Claim {
	return newClaim(name, value)
}

// Int32Claim returns a Claim with int32 value.
func Int32Claim(name string, value int32) Claim {
	return newClaim(name, value)
}

// Int64Claim returns a Claim with int64 value.
func Int64Claim(name string, value int64) Claim {
	return newClaim(name, value)
}

// UintClaim returns a Claim with uint value.
func UintClaim(name string, value uint) Claim {
	return newClaim(name, value)
}

// Uint32Claim returns a Claim with uint32 value.
func Uint32Claim(name string, value uint32) Claim {
	return newClaim(name, value)
}

// Uint64Claim returns a Claim with uint64 value.
func Uint64Claim(name string, value uint64) Claim {
	return newClaim(name, value)
}

// Float32Claim returns a Claim with float32 value.
func Float32Claim(name string, value float32) Claim {
	return newClaim(name, value)
}

// Float64Claim returns a Claim with float64 value.
func Float64Claim(name string, value float64) Claim {
	return newClaim(name, value)
}

// TimeClaim returns a Claim with time.Time value.
// It'll convert the time.Time to a Unix timestamp.
func TimeClaim(name string, value time.Time) Claim {
	return Int64Claim(name, value.Unix())
}
<commit_msg>Export NewClaim() and remove other type specific functions<commit_after>package jwthelper

import (
	"sync"
	"time"

	"github.com/dgrijalva/jwt-go"
)

// claims stores JWT claims.
// it contains a jwt.MapClaims.
type claims struct {
	m      sync.Mutex
	claims jwt.MapClaims
}

// Claim represents JWT claim.
// Use claim helper functions to get a Claim:
// StringClaim(), IntClaim(), UintClaim(), TimeClaim()...
type Claim struct {
	f func(ops *claims)
}

// newClaims news a Claims and intializes the internal mutext and map.
func newClaims() claims {
	return claims{
		sync.Mutex{},
		map[string]interface{}{},
	}
}

// NewClaim news a Claim with given name -> value pair.
func NewClaim(name string, value interface{}) Claim {
	return Claim{func(c *claims) {
		c.m.Lock()
		defer c.m.Unlock()
		c.claims[name] = value
	}}
}

// TimeClaim returns a Claim with time.Time value.
// It'll convert the time.Time to a Unix timestamp.
func TimeClaim(name string, value time.Time) Claim {
	return NewClaim(name, value.Unix())
}
<|endoftext|>


<commit_before>package libp2pquic

import (
	"context"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"sync"
	"time"

	"github.com/minio/sha256-simd"
	"golang.org/x/crypto/hkdf"

	logging "github.com/ipfs/go-log"
	"github.com/libp2p/go-libp2p-core/connmgr"
	ic "github.com/libp2p/go-libp2p-core/crypto"
	n "github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/pnet"
	tpt "github.com/libp2p/go-libp2p-core/transport"
	p2ptls "github.com/libp2p/go-libp2p-tls"
	"github.com/lucas-clemente/quic-go"
	ma "github.com/multiformats/go-multiaddr"
	mafmt "github.com/multiformats/go-multiaddr-fmt"
	manet "github.com/multiformats/go-multiaddr/net"
)

var log = logging.Logger("quic-transport")

var ErrHolePunching = errors.New("hole punching attempted; no active dial")

var quicDialContext = quic.DialContext // so we can mock it in tests

var HolePunchTimeout = 5 * time.Second

var quicConfig = &quic.Config{
	MaxIncomingStreams:         1000,
	MaxIncomingUniStreams:      -1,             // disable unidirectional streams
	MaxStreamReceiveWindow:     10 * (1 << 20), // 10 MB
	MaxConnectionReceiveWindow: 15 * (1 << 20), // 15 MB
	AcceptToken: func(clientAddr net.Addr, _ *quic.Token) bool {
		// TODO(#6): require source address validation when under load
		return true
	},
	KeepAlive: true,
	Versions:  []quic.VersionNumber{quic.VersionDraft29, quic.Version1},
}

const statelessResetKeyInfo = "libp2p quic stateless reset key"
const errorCodeConnectionGating = 0x47415445 // GATE in ASCII

type connManager struct {
	reuseUDP4 *reuse
	reuseUDP6 *reuse
}

func newConnManager() (*connManager, error) {
	reuseUDP4 := newReuse()
	reuseUDP6 := newReuse()

	return &connManager{
		reuseUDP4: reuseUDP4,
		reuseUDP6: reuseUDP6,
	}, nil
}

func (c *connManager) getReuse(network string) (*reuse, error) {
	switch network {
	case "udp4":
		return c.reuseUDP4, nil
	case "udp6":
		return c.reuseUDP6, nil
	default:
		return nil, errors.New("invalid network: must be either udp4 or udp6")
	}
}

func (c *connManager) Listen(network string, laddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Listen(network, laddr)
}

func (c *connManager) Dial(network string, raddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Dial(network, raddr)
}

func (c *connManager) Close() error {
	if err := c.reuseUDP6.Close(); err != nil {
		return err
	}
	return c.reuseUDP4.Close()
}

// The Transport implements the tpt.Transport interface for QUIC connections.
type transport struct {
	privKey      ic.PrivKey
	localPeer    peer.ID
	identity     *p2ptls.Identity
	connManager  *connManager
	serverConfig *quic.Config
	clientConfig *quic.Config
	gater        connmgr.ConnectionGater

	holePunchingMx sync.Mutex
	holePunching   map[holePunchKey]*activeHolePunch
}

var _ tpt.Transport = &transport{}

type holePunchKey struct {
	addr string
	peer peer.ID
}

type activeHolePunch struct {
	connCh    chan tpt.CapableConn
	fulfilled bool
}

// NewTransport creates a new QUIC transport
func NewTransport(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater) (tpt.Transport, error) {
	if len(psk) > 0 {
		log.Error("QUIC doesn't support private networks yet.")
		return nil, errors.New("QUIC doesn't support private networks yet")
	}
	localPeer, err := peer.IDFromPrivateKey(key)
	if err != nil {
		return nil, err
	}
	identity, err := p2ptls.NewIdentity(key)
	if err != nil {
		return nil, err
	}
	connManager, err := newConnManager()
	if err != nil {
		return nil, err
	}
	config := quicConfig.Clone()
	keyBytes, err := key.Raw()
	if err != nil {
		return nil, err
	}
	keyReader := hkdf.New(sha256.New, keyBytes, nil, []byte(statelessResetKeyInfo))
	config.StatelessResetKey = make([]byte, 32)
	if _, err := io.ReadFull(keyReader, config.StatelessResetKey); err != nil {
		return nil, err
	}
	config.Tracer = tracer

	return &transport{
		privKey:      key,
		localPeer:    localPeer,
		identity:     identity,
		connManager:  connManager,
		serverConfig: config,
		clientConfig: config.Clone(),
		gater:        gater,
		holePunching: make(map[holePunchKey]*activeHolePunch),
	}, nil
}

// Dial dials a new QUIC connection
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) {
	network, host, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
	}
	addr, err := net.ResolveUDPAddr(network, host)
	if err != nil {
		return nil, err
	}
	remoteMultiaddr, err := toQuicMultiaddr(addr)
	if err != nil {
		return nil, err
	}
	tlsConf, keyCh := t.identity.ConfigForPeer(p)

	if ok, isClient, _ := n.GetSimultaneousConnect(ctx); ok && !isClient {
		return t.holePunch(ctx, network, addr, p)
	}

	pconn, err := t.connManager.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	sess, err := quicDialContext(ctx, pconn, addr, host, tlsConf, t.clientConfig)
	if err != nil {
		pconn.DecreaseCount()
		return nil, err
	}
	// Should be ready by this point, don't block.
	var remotePubKey ic.PubKey
	select {
	case remotePubKey = <-keyCh:
	default:
	}
	if remotePubKey == nil {
		pconn.DecreaseCount()
		return nil, errors.New("go-libp2p-quic-transport BUG: expected remote pub key to be set")
	}
	go func() {
		<-sess.Context().Done()
		pconn.DecreaseCount()
	}()

	localMultiaddr, err := toQuicMultiaddr(pconn.LocalAddr())
	if err != nil {
		sess.CloseWithError(0, "")
		return nil, err
	}
	conn := &conn{
		sess:            sess,
		transport:       t,
		privKey:         t.privKey,
		localPeer:       t.localPeer,
		localMultiaddr:  localMultiaddr,
		remotePubKey:    remotePubKey,
		remotePeerID:    p,
		remoteMultiaddr: remoteMultiaddr,
	}
	if t.gater != nil && !t.gater.InterceptSecured(n.DirOutbound, p, conn) {
		sess.CloseWithError(errorCodeConnectionGating, "connection gated")
		return nil, fmt.Errorf("secured connection gated")
	}
	return conn, nil
}

func (t *transport) holePunch(ctx context.Context, network string, addr *net.UDPAddr, p peer.ID) (tpt.CapableConn, error) {
	pconn, err := t.connManager.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	defer pconn.DecreaseCount()

	ctx, cancel := context.WithTimeout(ctx, HolePunchTimeout)
	defer cancel()

	key := holePunchKey{addr: addr.String(), peer: p}
	t.holePunchingMx.Lock()
	if _, ok := t.holePunching[key]; ok {
		t.holePunchingMx.Unlock()
		return nil, fmt.Errorf("already punching hole for %s", addr)
	}
	connCh := make(chan tpt.CapableConn, 1)
	t.holePunching[key] = &activeHolePunch{connCh: connCh}
	t.holePunchingMx.Unlock()

	var timer *time.Timer
	defer func() {
		if timer != nil {
			timer.Stop()
		}
	}()

	payload := make([]byte, 64)
	var punchErr error
loop:
	for i := 0; ; i++ {
		if _, err := rand.Read(payload); err != nil {
			punchErr = err
			break
		}
		if _, err := pconn.UDPConn.WriteToUDP(payload, addr); err != nil {
			punchErr = err
			break
		}

		maxSleep := 10 * (i + 1) * (i + 1) // in ms
		if maxSleep > 200 {
			maxSleep = 200
		}
		d := 10*time.Millisecond + time.Duration(rand.Intn(maxSleep))*time.Millisecond
		if timer == nil {
			timer = time.NewTimer(d)
		} else {
			timer.Reset(d)
		}
		select {
		case c := <-connCh:
			t.holePunchingMx.Lock()
			delete(t.holePunching, key)
			t.holePunchingMx.Unlock()
			return c, nil
		case <-timer.C:
		case <-ctx.Done():
			punchErr = ErrHolePunching
			break loop
		}
	}
	// we only arrive here if punchErr != nil
	t.holePunchingMx.Lock()
	defer func() {
		delete(t.holePunching, key)
		t.holePunchingMx.Unlock()
	}()
	select {
	case c := <-t.holePunching[key].connCh:
		return c, nil
	default:
		return nil, punchErr
	}
}

// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC))

// CanDial determines if we can dial to an address
func (t *transport) CanDial(addr ma.Multiaddr) bool {
	return dialMatcher.Matches(addr)
}

// Listen listens for new QUIC connections on the passed multiaddr.
func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
	lnet, host, err := manet.DialArgs(addr)
	if err != nil {
		return nil, err
	}
	laddr, err := net.ResolveUDPAddr(lnet, host)
	if err != nil {
		return nil, err
	}
	conn, err := t.connManager.Listen(lnet, laddr)
	if err != nil {
		return nil, err
	}
	ln, err := newListener(conn, t, t.localPeer, t.privKey, t.identity)
	if err != nil {
		conn.DecreaseCount()
		return nil, err
	}
	return ln, nil
}

// Proxy returns true if this transport proxies.
func (t *transport) Proxy() bool {
	return false
}

// Protocols returns the set of protocols handled by this transport.
func (t *transport) Protocols() []int {
	return []int{ma.P_QUIC}
}

func (t *transport) String() string {
	return "QUIC"
}

func (t *transport) Close() error {
	return t.connManager.Close()
}
<commit_msg>chore: update go-log to v2 (#242)<commit_after>package libp2pquic

import (
	"context"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"sync"
	"time"

	"github.com/minio/sha256-simd"
	"golang.org/x/crypto/hkdf"

	logging "github.com/ipfs/go-log/v2"
	"github.com/libp2p/go-libp2p-core/connmgr"
	ic "github.com/libp2p/go-libp2p-core/crypto"
	n "github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/pnet"
	tpt "github.com/libp2p/go-libp2p-core/transport"
	p2ptls "github.com/libp2p/go-libp2p-tls"
	"github.com/lucas-clemente/quic-go"
	ma "github.com/multiformats/go-multiaddr"
	mafmt "github.com/multiformats/go-multiaddr-fmt"
	manet "github.com/multiformats/go-multiaddr/net"
)

var log = logging.Logger("quic-transport")

var ErrHolePunching = errors.New("hole punching attempted; no active dial")

var quicDialContext = quic.DialContext // so we can mock it in tests

var HolePunchTimeout = 5 * time.Second

var quicConfig = &quic.Config{
	MaxIncomingStreams:         1000,
	MaxIncomingUniStreams:      -1,             // disable unidirectional streams
	MaxStreamReceiveWindow:     10 * (1 << 20), // 10 MB
	MaxConnectionReceiveWindow: 15 * (1 << 20), // 15 MB
	AcceptToken: func(clientAddr net.Addr, _ *quic.Token) bool {
		// TODO(#6): require source address validation when under load
		return true
	},
	KeepAlive: true,
	Versions:  []quic.VersionNumber{quic.VersionDraft29, quic.Version1},
}

const statelessResetKeyInfo = "libp2p quic stateless reset key"
const errorCodeConnectionGating = 0x47415445 // GATE in ASCII

type connManager struct {
	reuseUDP4 *reuse
	reuseUDP6 *reuse
}

func newConnManager() (*connManager, error) {
	reuseUDP4 := newReuse()
	reuseUDP6 := newReuse()

	return &connManager{
		reuseUDP4: reuseUDP4,
		reuseUDP6: reuseUDP6,
	}, nil
}

func (c *connManager) getReuse(network string) (*reuse, error) {
	switch network {
	case "udp4":
		return c.reuseUDP4, nil
	case "udp6":
		return c.reuseUDP6, nil
	default:
		return nil, errors.New("invalid network: must be either udp4 or udp6")
	}
}

func (c *connManager) Listen(network string, laddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Listen(network, laddr)
}

func (c *connManager) Dial(network string, raddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Dial(network, raddr)
}

func (c *connManager) Close() error {
	if err := c.reuseUDP6.Close(); err != nil {
		return err
	}
	return c.reuseUDP4.Close()
}

// The Transport implements the tpt.Transport interface for QUIC connections.
type transport struct {
	privKey      ic.PrivKey
	localPeer    peer.ID
	identity     *p2ptls.Identity
	connManager  *connManager
	serverConfig *quic.Config
	clientConfig *quic.Config
	gater        connmgr.ConnectionGater

	holePunchingMx sync.Mutex
	holePunching   map[holePunchKey]*activeHolePunch
}

var _ tpt.Transport = &transport{}

type holePunchKey struct {
	addr string
	peer peer.ID
}

type activeHolePunch struct {
	connCh    chan tpt.CapableConn
	fulfilled bool
}

// NewTransport creates a new QUIC transport
func NewTransport(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater) (tpt.Transport, error) {
	if len(psk) > 0 {
		log.Error("QUIC doesn't support private networks yet.")
		return nil, errors.New("QUIC doesn't support private networks yet")
	}
	localPeer, err := peer.IDFromPrivateKey(key)
	if err != nil {
		return nil, err
	}
	identity, err := p2ptls.NewIdentity(key)
	if err != nil {
		return nil, err
	}
	connManager, err := newConnManager()
	if err != nil {
		return nil, err
	}
	config := quicConfig.Clone()
	keyBytes, err := key.Raw()
	if err != nil {
		return nil, err
	}
	keyReader := hkdf.New(sha256.New, keyBytes, nil, []byte(statelessResetKeyInfo))
	config.StatelessResetKey = make([]byte, 32)
	if _, err := io.ReadFull(keyReader, config.StatelessResetKey); err != nil {
		return nil, err
	}
	config.Tracer = tracer

	return &transport{
		privKey:      key,
		localPeer:    localPeer,
		identity:     identity,
		connManager:  connManager,
		serverConfig: config,
		clientConfig: config.Clone(),
		gater:        gater,
		holePunching: make(map[holePunchKey]*activeHolePunch),
	}, nil
}

// Dial dials a new QUIC connection
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) {
	network, host, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
	}
	addr, err := net.ResolveUDPAddr(network, host)
	if err != nil {
		return nil, err
	}
	remoteMultiaddr, err := toQuicMultiaddr(addr)
	if err != nil {
		return nil, err
	}
	tlsConf, keyCh := t.identity.ConfigForPeer(p)

	if ok, isClient, _ := n.GetSimultaneousConnect(ctx); ok && !isClient {
		return t.holePunch(ctx, network, addr, p)
	}

	pconn, err := t.connManager.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	sess, err := quicDialContext(ctx, pconn, addr, host, tlsConf, t.clientConfig)
	if err != nil {
		pconn.DecreaseCount()
		return nil, err
	}
	// Should be ready by this point, don't block.
	var remotePubKey ic.PubKey
	select {
	case remotePubKey = <-keyCh:
	default:
	}
	if remotePubKey == nil {
		pconn.DecreaseCount()
		return nil, errors.New("go-libp2p-quic-transport BUG: expected remote pub key to be set")
	}
	go func() {
		<-sess.Context().Done()
		pconn.DecreaseCount()
	}()

	localMultiaddr, err := toQuicMultiaddr(pconn.LocalAddr())
	if err != nil {
		sess.CloseWithError(0, "")
		return nil, err
	}
	conn := &conn{
		sess:            sess,
		transport:       t,
		privKey:         t.privKey,
		localPeer:       t.localPeer,
		localMultiaddr:  localMultiaddr,
		remotePubKey:    remotePubKey,
		remotePeerID:    p,
		remoteMultiaddr: remoteMultiaddr,
	}
	if t.gater != nil && !t.gater.InterceptSecured(n.DirOutbound, p, conn) {
		sess.CloseWithError(errorCodeConnectionGating, "connection gated")
		return nil, fmt.Errorf("secured connection gated")
	}
	return conn, nil
}

func (t *transport) holePunch(ctx context.Context, network string, addr *net.UDPAddr, p peer.ID) (tpt.CapableConn, error) {
	pconn, err := t.connManager.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	defer pconn.DecreaseCount()

	ctx, cancel := context.WithTimeout(ctx, HolePunchTimeout)
	defer cancel()

	key := holePunchKey{addr: addr.String(), peer: p}
	t.holePunchingMx.Lock()
	if _, ok := t.holePunching[key]; ok {
		t.holePunchingMx.Unlock()
		return nil, fmt.Errorf("already punching hole for %s", addr)
	}
	connCh := make(chan tpt.CapableConn, 1)
	t.holePunching[key] = &activeHolePunch{connCh: connCh}
	t.holePunchingMx.Unlock()

	var timer *time.Timer
	defer func() {
		if timer != nil {
			timer.Stop()
		}
	}()

	payload := make([]byte, 64)
	var punchErr error
loop:
	for i := 0; ; i++ {
		if _, err := rand.Read(payload); err != nil {
			punchErr = err
			break
		}
		if _, err := pconn.UDPConn.WriteToUDP(payload, addr); err != nil {
			punchErr = err
			break
		}

		maxSleep := 10 * (i + 1) * (i + 1) // in ms
		if maxSleep > 200 {
			maxSleep = 200
		}
		d := 10*time.Millisecond + time.Duration(rand.Intn(maxSleep))*time.Millisecond
		if timer == nil {
			timer = time.NewTimer(d)
		} else {
			timer.Reset(d)
		}
		select {
		case c := <-connCh:
			t.holePunchingMx.Lock()
			delete(t.holePunching, key)
			t.holePunchingMx.Unlock()
			return c, nil
		case <-timer.C:
		case <-ctx.Done():
			punchErr = ErrHolePunching
			break loop
		}
	}
	// we only arrive here if punchErr != nil
	t.holePunchingMx.Lock()
	defer func() {
		delete(t.holePunching, key)
		t.holePunchingMx.Unlock()
	}()
	select {
	case c := <-t.holePunching[key].connCh:
		return c, nil
	default:
		return nil, punchErr
	}
}

// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC))

// CanDial determines if we can dial to an address
func (t *transport) CanDial(addr ma.Multiaddr) bool {
	return dialMatcher.Matches(addr)
}

// Listen listens for new QUIC connections on the passed multiaddr.
func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
	lnet, host, err := manet.DialArgs(addr)
	if err != nil {
		return nil, err
	}
	laddr, err := net.ResolveUDPAddr(lnet, host)
	if err != nil {
		return nil, err
	}
	conn, err := t.connManager.Listen(lnet, laddr)
	if err != nil {
		return nil, err
	}
	ln, err := newListener(conn, t, t.localPeer, t.privKey, t.identity)
	if err != nil {
		conn.DecreaseCount()
		return nil, err
	}
	return ln, nil
}

// Proxy returns true if this transport proxies.
func (t *transport) Proxy() bool {
	return false
}

// Protocols returns the set of protocols handled by this transport.
func (t *transport) Protocols() []int {
	return []int{ma.P_QUIC}
}

func (t *transport) String() string {
	return "QUIC"
}

func (t *transport) Close() error {
	return t.connManager.Close()
}
<|endoftext|>


<commit_before>package websocket

import (
	"io"
	"net"
	"sync"
	"time"

	ws "github.com/gorilla/websocket"
)

// GracefulCloseTimeout is the time to wait trying to gracefully close a
// connection before simply cutting it.
var GracefulCloseTimeout = 100 * time.Millisecond

var _ net.Conn = (*Conn)(nil)

// Conn implements net.Conn interface for gorilla/websocket.
type Conn struct {
	*ws.Conn
	DefaultMessageType int
	done               func()
	reader             io.Reader
	closeOnce          sync.Once
}

func (c *Conn) Read(b []byte) (int, error) {
	if c.reader == nil {
		if err := c.prepNextReader(); err != nil {
			return 0, err
		}
	}

	for {
		n, err := c.reader.Read(b)
		switch err {
		case io.EOF:
			c.reader = nil

			if n > 0 {
				return n, nil
			}

			if err := c.prepNextReader(); err != nil {
				return 0, err
			}

			// explicitly looping
		default:
			return n, err
		}
	}
}

func (c *Conn) prepNextReader() error {
	t, r, err := c.Conn.NextReader()
	if err != nil {
		if wserr, ok := err.(*ws.CloseError); ok {
			if wserr.Code == 1000 || wserr.Code == 1005 {
				return io.EOF
			}
		}
		return err
	}

	if t == ws.CloseMessage {
		return io.EOF
	}

	c.reader = r
	return nil
}

func (c *Conn) Write(b []byte) (n int, err error) {
	if err := c.Conn.WriteMessage(c.DefaultMessageType, b); err != nil {
		return 0, err
	}

	return len(b), nil
}

// Close closes the connection. Only the first call to Close will receive the
// close error, subsequent and concurrent calls will return nil.
// This method is thread-safe.
func (c *Conn) Close() error {
	var err error = nil
	c.closeOnce.Do(func() {
		if c.done != nil {
			c.done()
			// Be nice to GC
			c.done = nil
		}

		c.Conn.WriteControl(ws.CloseMessage, nil, time.Now().Add(GracefulCloseTimeout))
		err = c.Conn.Close()
	})
	return err
}

func (c *Conn) LocalAddr() net.Addr {
	return NewAddr(c.Conn.LocalAddr().String())
}

func (c *Conn) RemoteAddr() net.Addr {
	return NewAddr(c.Conn.RemoteAddr().String())
}

func (c *Conn) SetDeadline(t time.Time) error {
	if err := c.SetReadDeadline(t); err != nil {
		return err
	}

	return c.SetWriteDeadline(t)
}

func (c *Conn) SetReadDeadline(t time.Time) error {
	return c.Conn.SetReadDeadline(t)
}

func (c *Conn) SetWriteDeadline(t time.Time) error {
	return c.Conn.SetWriteDeadline(t)
}

// NewConn creates a Conn given a regular gorilla/websocket Conn.
func NewConn(raw *ws.Conn, done func()) *Conn {
	return &Conn{
		Conn:               raw,
		DefaultMessageType: ws.BinaryMessage,
		done:               done,
	}
}
<commit_msg>don't explicitly nil err<commit_after>package websocket

import (
	"io"
	"net"
	"sync"
	"time"

	ws "github.com/gorilla/websocket"
)

// GracefulCloseTimeout is the time to wait trying to gracefully close a
// connection before simply cutting it.
var GracefulCloseTimeout = 100 * time.Millisecond

var _ net.Conn = (*Conn)(nil)

// Conn implements net.Conn interface for gorilla/websocket.
type Conn struct {
	*ws.Conn
	DefaultMessageType int
	done               func()
	reader             io.Reader
	closeOnce          sync.Once
}

func (c *Conn) Read(b []byte) (int, error) {
	if c.reader == nil {
		if err := c.prepNextReader(); err != nil {
			return 0, err
		}
	}

	for {
		n, err := c.reader.Read(b)
		switch err {
		case io.EOF:
			c.reader = nil

			if n > 0 {
				return n, nil
			}

			if err := c.prepNextReader(); err != nil {
				return 0, err
			}

			// explicitly looping
		default:
			return n, err
		}
	}
}

func (c *Conn) prepNextReader() error {
	t, r, err := c.Conn.NextReader()
	if err != nil {
		if wserr, ok := err.(*ws.CloseError); ok {
			if wserr.Code == 1000 || wserr.Code == 1005 {
				return io.EOF
			}
		}
		return err
	}

	if t == ws.CloseMessage {
		return io.EOF
	}

	c.reader = r
	return nil
}

func (c *Conn) Write(b []byte) (n int, err error) {
	if err := c.Conn.WriteMessage(c.DefaultMessageType, b); err != nil {
		return 0, err
	}

	return len(b), nil
}

// Close closes the connection. Only the first call to Close will receive the
// close error, subsequent and concurrent calls will return nil.
// This method is thread-safe.
func (c *Conn) Close() error {
	var err error
	c.closeOnce.Do(func() {
		if c.done != nil {
			c.done()
			// Be nice to GC
			c.done = nil
		}

		c.Conn.WriteControl(ws.CloseMessage, nil, time.Now().Add(GracefulCloseTimeout))
		err = c.Conn.Close()
	})
	return err
}

func (c *Conn) LocalAddr() net.Addr {
	return NewAddr(c.Conn.LocalAddr().String())
}

func (c *Conn) RemoteAddr() net.Addr {
	return NewAddr(c.Conn.RemoteAddr().String())
}

func (c *Conn) SetDeadline(t time.Time) error {
	if err := c.SetReadDeadline(t); err != nil {
		return err
	}

	return c.SetWriteDeadline(t)
}

func (c *Conn) SetReadDeadline(t time.Time) error {
	return c.Conn.SetReadDeadline(t)
}

func (c *Conn) SetWriteDeadline(t time.Time) error {
	return c.Conn.SetWriteDeadline(t)
}

// NewConn creates a Conn given a regular gorilla/websocket Conn.
func NewConn(raw *ws.Conn, done func()) *Conn {
	return &Conn{
		Conn:               raw,
		DefaultMessageType: ws.BinaryMessage,
		done:               done,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"reflect"

	"github.com/jmoiron/sqlx"
)

type Client struct {
	db      *sqlx.DB
	history []string
}

type Row []interface{}

type Result struct {
	Columns []string `json:"columns"`
	Rows    []Row    `json:"rows"`
}

func NewClient() (*Client, error) {
	db, err := sqlx.Open("postgres", getConnectionString())

	if err != nil {
		return nil, err
	}

	return &Client{db: db}, nil
}

func NewClientFromUrl(url string) (*Client, error) {
	db, err := sqlx.Open("postgres", url)

	if err != nil {
		return nil, err
	}

	return &Client{db: db}, nil
}

func (client *Client) Test() error {
	return client.db.Ping()
}

func (client *Client) recordQuery(query string) {
	client.history = append(client.history, query)
}

func (client *Client) Info() (*Result, error) {
	return client.query(PG_INFO)
}

func (client *Client) Databases() ([]string, error) {
	res, err := client.query(PG_DATABASES)

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

func (client *Client) Tables() ([]string, error) {
	res, err := client.query(PG_TABLES)

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

func (client *Client) Table(table string) (*Result, error) {
	return client.query(PG_TABLE_SCHEMA, table)
}

func (client *Client) TableInfo(table string) (*Result, error) {
	return client.query(PG_TABLE_INFO, table)
}

func (client *Client) TableIndexes(table string) (*Result, error) {
	res, err := client.query(PG_TABLE_INDEXES, table)

	if err != nil {
		return nil, err
	}

	return res, err
}

func (client *Client) Query(query string) (*Result, error) {
	client.recordQuery(query)
	return client.query(query)
}

func (client *Client) query(query string, args ...interface{}) (*Result, error) {
	rows, err := client.db.Queryx(query, args...)

	if err != nil {
		return nil, err
	}

	defer rows.Close()

	cols, err := rows.Columns()

	if err != nil {
		return nil, err
	}

	result := Result{
		Columns: cols,
	}

	for rows.Next() {
		obj, err := rows.SliceScan()

		for i, item := range obj {
			if item == nil {
				obj[i] = nil
			} else {
				t := reflect.TypeOf(item).Kind().String()

				if t == "slice" {
					obj[i] = string(item.([]byte))
				}
			}
		}

		if err == nil {
			result.Rows = append(result.Rows, obj)
		}
	}

	return &result, nil
}

func (res *Result) Format() []map[string]interface{} {
	var items []map[string]interface{}

	for _, row := range res.Rows {
		item := make(map[string]interface{})

		for i, c := range res.Columns {
			item[c] = row[i]
		}

		items = append(items, item)
	}

	return items
}

func (res *Result) CSV() []byte {
	buff := &bytes.Buffer{}
	writer := csv.NewWriter(buff)

	writer.Write(res.Columns)

	for _, row := range res.Rows {
		record := make([]string, len(res.Columns))

		for i, item := range row {
			if item != nil {
				record[i] = fmt.Sprintf("%v", item)
			} else {
				record[i] = ""
			}
		}

		err := writer.Write(record)

		if err != nil {
			fmt.Println(err)
			break
		}
	}

	writer.Flush()
	return buff.Bytes()
}
<commit_msg>Propertly initialize string slice for Tables() method<commit_after>package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"reflect"

	"github.com/jmoiron/sqlx"
)

type Client struct {
	db      *sqlx.DB
	history []string
}

type Row []interface{}

type Result struct {
	Columns []string `json:"columns"`
	Rows    []Row    `json:"rows"`
}

func NewClient() (*Client, error) {
	db, err := sqlx.Open("postgres", getConnectionString())

	if err != nil {
		return nil, err
	}

	return &Client{db: db}, nil
}

func NewClientFromUrl(url string) (*Client, error) {
	db, err := sqlx.Open("postgres", url)

	if err != nil {
		return nil, err
	}

	return &Client{db: db}, nil
}

func (client *Client) Test() error {
	return client.db.Ping()
}

func (client *Client) recordQuery(query string) {
	client.history = append(client.history, query)
}

func (client *Client) Info() (*Result, error) {
	return client.query(PG_INFO)
}

func (client *Client) Databases() ([]string, error) {
	res, err := client.query(PG_DATABASES)

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

func (client *Client) Tables() ([]string, error) {
	res, err := client.query(PG_TABLES)

	if err != nil {
		return nil, err
	}

	results := make([]string, 0)

	for _, row := range res.Rows {
		results = append(results, row[0].(string))
	}

	return results, nil
}

func (client *Client) Table(table string) (*Result, error) {
	return client.query(PG_TABLE_SCHEMA, table)
}

func (client *Client) TableInfo(table string) (*Result, error) {
	return client.query(PG_TABLE_INFO, table)
}

func (client *Client) TableIndexes(table string) (*Result, error) {
	res, err := client.query(PG_TABLE_INDEXES, table)

	if err != nil {
		return nil, err
	}

	return res, err
}

func (client *Client) Query(query string) (*Result, error) {
	client.recordQuery(query)
	return client.query(query)
}

func (client *Client) query(query string, args ...interface{}) (*Result, error) {
	rows, err := client.db.Queryx(query, args...)

	if err != nil {
		return nil, err
	}

	defer rows.Close()

	cols, err := rows.Columns()

	if err != nil {
		return nil, err
	}

	result := Result{
		Columns: cols,
	}

	for rows.Next() {
		obj, err := rows.SliceScan()

		for i, item := range obj {
			if item == nil {
				obj[i] = nil
			} else {
				t := reflect.TypeOf(item).Kind().String()

				if t == "slice" {
					obj[i] = string(item.([]byte))
				}
			}
		}

		if err == nil {
			result.Rows = append(result.Rows, obj)
		}
	}

	return &result, nil
}

func (res *Result) Format() []map[string]interface{} {
	var items []map[string]interface{}

	for _, row := range res.Rows {
		item := make(map[string]interface{})

		for i, c := range res.Columns {
			item[c] = row[i]
		}

		items = append(items, item)
	}

	return items
}

func (res *Result) CSV() []byte {
	buff := &bytes.Buffer{}
	writer := csv.NewWriter(buff)

	writer.Write(res.Columns)

	for _, row := range res.Rows {
		record := make([]string, len(res.Columns))

		for i, item := range row {
			if item != nil {
				record[i] = fmt.Sprintf("%v", item)
			} else {
				record[i] = ""
			}
		}

		err := writer.Write(record)

		if err != nil {
			fmt.Println(err)
			break
		}
	}

	writer.Flush()
	return buff.Bytes()
}
<|endoftext|>


<commit_before>package prisclient

import (
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/priscillachat/prislog"
	"io"
	"net"
	"time"
)

type Client struct {
	raw      net.Conn
	decoder  *json.Decoder
	encoder  *json.Encoder
	SourceId string
	Logger   *prislog.PrisLogger
}

type CommandBlock struct {
	Id      string   `json:"id,omitempty"`
	Action  string   `json:"action,omitempty"`
	Type    string   `json:"type,omitempty"`
	Time    int64    `json:"time,omitempty"`
	Data    string   `json:"data,omitempty"`
	Array   []string `json:"array,omitempty"`
	Options []string `json:"options,omitempty"`
}

type MessageBlock struct {
	Message   string `json:"message,omitempty"`
	From      string `json:"from,omitempty"`
	Room      string `json:"room,omitempty"`
	Mentioned bool   `json:"mentioned,omitempty"`
}

type Query struct {
	Type    string        `json:"type,omitempty"`
	Source  string        `json:"source,omitempty"`
	To      string        `json:"to,omitempty"`
	Command *CommandBlock `json:"command,omitempty"`
	Message *MessageBlock `json:"message,omitempty"`
}

func RandomId() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)
}

func NewClient(host, port string,
	logger *prislog.PrisLog) (*Client, error) {

	pris := new(Client)

	pris.Logger = logger

	conn, err := net.Dial("tcp", host+":"+port)

	if err != nil {
		return pris, err
	}

	pris.raw = conn
	pris.decoder = json.NewDecoder(pris.raw)
	pris.encoder = json.NewEncoder(pris.raw)

	return pris, nil
}

func (pris *Client) Engage(clienttype, sourceid string) error {
	if clienttype != "adapter" && clienttype != "responder" {
		return errors.New("client type has to be adapter or responder")
	}

	q := Query{
		Type:   "command",
		Source: sourceid,
		Command: &CommandBlock{
			Id:     RandomId(),
			Action: "engage",
			Type:   clienttype,
			Time:   time.Now().Unix(),
		},
	}

	pris.SourceId = sourceid

	return pris.encoder.Encode(&q)
}

func (pris *Client) listen(out chan<- *Query) {
	var q *Query
	for {
		q = new(Query)
		err := pris.decoder.Decode(q)

		if err != nil {
			fmt.Println(err)
			if err.Error() == "EOF" {
				out <- &Query{
					Type:   "command",
					Source: "pris",
					Command: &CommandBlock{
						Action: "disengage",
					},
				}
				break
			}
		}

		if pris.ValidateQuery(q) {
			pris.Logger.Debug.Println("Query received:", *q)
			if q.Type == "message" {
				pris.Logger.Debug.Println("Message:", q.Message.Message)
				pris.Logger.Debug.Println("From:", q.Message.From)
				pris.Logger.Debug.Println("Room:", q.Message.Room)
			}
			out <- q
		} else {
			pris.Logger.Error.Println("Invalid query from server(?!!):", q)
		}
	}
}

func (pris *Client) Run(toPris <-chan *Query, fromPris chan<- *Query) {

	go pris.listen(fromPris)
	var q *Query
	for {
		q = <-toPris
		if pris.ValidateQuery(q) {
			pris.encoder.Encode(q)
		} else {
			pris.Logger.Error.Println("Invalid query:", q)
		}
	}
}

func (pris *Client) ValidateQuery(q *Query) bool {
	switch {
	case q.Type == "command" && q.Command != nil:
		return true
	case q.Type == "message" && q.Message != nil && q.Message.Room != "":
		return true
	}
	return false
}
<commit_msg>typo on the logger ref<commit_after>package prisclient

import (
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/priscillachat/prislog"
	"io"
	"net"
	"time"
)

type Client struct {
	raw      net.Conn
	decoder  *json.Decoder
	encoder  *json.Encoder
	SourceId string
	Logger   *prislog.PrisLog
}

type CommandBlock struct {
	Id      string   `json:"id,omitempty"`
	Action  string   `json:"action,omitempty"`
	Type    string   `json:"type,omitempty"`
	Time    int64    `json:"time,omitempty"`
	Data    string   `json:"data,omitempty"`
	Array   []string `json:"array,omitempty"`
	Options []string `json:"options,omitempty"`
}

type MessageBlock struct {
	Message   string `json:"message,omitempty"`
	From      string `json:"from,omitempty"`
	Room      string `json:"room,omitempty"`
	Mentioned bool   `json:"mentioned,omitempty"`
}

type Query struct {
	Type    string        `json:"type,omitempty"`
	Source  string        `json:"source,omitempty"`
	To      string        `json:"to,omitempty"`
	Command *CommandBlock `json:"command,omitempty"`
	Message *MessageBlock `json:"message,omitempty"`
}

func RandomId() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)
}

func NewClient(host, port string,
	logger *prislog.PrisLog) (*Client, error) {

	pris := new(Client)

	pris.Logger = logger

	conn, err := net.Dial("tcp", host+":"+port)

	if err != nil {
		return pris, err
	}

	pris.raw = conn
	pris.decoder = json.NewDecoder(pris.raw)
	pris.encoder = json.NewEncoder(pris.raw)

	return pris, nil
}

func (pris *Client) Engage(clienttype, sourceid string) error {
	if clienttype != "adapter" && clienttype != "responder" {
		return errors.New("client type has to be adapter or responder")
	}

	q := Query{
		Type:   "command",
		Source: sourceid,
		Command: &CommandBlock{
			Id:     RandomId(),
			Action: "engage",
			Type:   clienttype,
			Time:   time.Now().Unix(),
		},
	}

	pris.SourceId = sourceid

	return pris.encoder.Encode(&q)
}

func (pris *Client) listen(out chan<- *Query) {
	var q *Query
	for {
		q = new(Query)
		err := pris.decoder.Decode(q)

		if err != nil {
			fmt.Println(err)
			if err.Error() == "EOF" {
				out <- &Query{
					Type:   "command",
					Source: "pris",
					Command: &CommandBlock{
						Action: "disengage",
					},
				}
				break
			}
		}

		if pris.ValidateQuery(q) {
			pris.Logger.Debug.Println("Query received:", *q)
			if q.Type == "message" {
				pris.Logger.Debug.Println("Message:", q.Message.Message)
				pris.Logger.Debug.Println("From:", q.Message.From)
				pris.Logger.Debug.Println("Room:", q.Message.Room)
			}
			out <- q
		} else {
			pris.Logger.Error.Println("Invalid query from server(?!!):", q)
		}
	}
}

func (pris *Client) Run(toPris <-chan *Query, fromPris chan<- *Query) {

	go pris.listen(fromPris)
	var q *Query
	for {
		q = <-toPris
		if pris.ValidateQuery(q) {
			pris.encoder.Encode(q)
		} else {
			pris.Logger.Error.Println("Invalid query:", q)
		}
	}
}

func (pris *Client) ValidateQuery(q *Query) bool {
	switch {
	case q.Type == "command" && q.Command != nil:
		return true
	case q.Type == "message" && q.Message != nil && q.Message.Room != "":
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package pusher

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

// ErrEvent error on event
const ErrEvent = "ErrEvent"

// Client is a pusher client
type Client struct {
	ws                 *websocket.Conn
	Events             chan *Event
	Stop               chan bool
	subscribedChannels *subscribedChannels
	binders            map[string]chan *Event
	m                  sync.Mutex
}

// heartbeat send a ping frame to server each - TODO reconnect on disconnect
func (c *Client) heartbeat() {
	for !c.Stopped() {
		websocket.Message.Send(c.ws, `{"event":"pusher:ping","data":"{}"}`)
		time.Sleep(HEARTBEAT_RATE * time.Second)
	}
}

// listen to Pusher server and process/dispatch recieved events
func (c *Client) listen() {
	for !c.Stopped() {
		var event Event
		err := websocket.JSON.Receive(c.ws, &event)
		if err != nil {
			if c.Stopped() {
				// Normal termination (ws Receive returns error when ws is
				// closed by other goroutine)
				log.Println("Listen error and c stopped :", err)
				return
			}
			c.m.Lock()
			ch, ok := c.binders[ErrEvent]
			c.m.Unlock()
			if ok {
				ch <- &Event{
					Event: ErrEvent,
					Data:  err.Error()}
			}
			// no matter what error happened, will log again and again
			// so return
			// err eg:
			// 1. use of closed network connection
			// 2. EOF (network error)
			// 3. ...
			log.Println("Listen error : ", err)
			return
		}
		switch event.Event {
		case "pusher:ping":
			websocket.Message.Send(c.ws, `{"event":"pusher:pong","data":"{}"}`)
		case "pusher:pong":
		case "pusher:error":
			log.Println("Event error received: ", event.Data)
		default:
			c.m.Lock()
			ch, ok := c.binders[event.Event]
			c.m.Unlock()
			if ok {
				ch <- &event
			}
		}

	}
}

// Subscribe ..to a channel
func (c *Client) Subscribe(channel string) (err error) {
	// Already subscribed ?
	if c.subscribedChannels.contains(channel) {
		err = fmt.Errorf("Channel %s already subscribed", channel)
		return
	}
	err = websocket.Message.Send(c.ws, fmt.Sprintf(`{"event":"pusher:subscribe","data":{"channel":"%s"}}`, channel))
	if err != nil {
		return
	}
	err = c.subscribedChannels.add(channel)
	return
}

// Unsubscribe from a channel
func (c *Client) Unsubscribe(channel string) (err error) {
	// subscribed ?
	if !c.subscribedChannels.contains(channel) {
		err = fmt.Errorf("Client isn't subscrived to %s", channel)
		return
	}
	err = websocket.Message.Send(c.ws, fmt.Sprintf(`{"event":"pusher:unsubscribe","data":{"channel":"%s"}}`, channel))
	if err != nil {
		return
	}
	// Remove channel from subscribedChannels slice
	c.subscribedChannels.remove(channel)
	return
}

// Bind an event
func (c *Client) Bind(evt string) (dataChannel chan *Event, err error) {
	// Already binded
	c.m.Lock()
	_, ok := c.binders[evt]
	c.m.Unlock()
	if ok {
		err = fmt.Errorf("Event %s already binded", evt)
		return
	}
	// New data channel
	dataChannel = make(chan *Event, EVENT_CHANNEL_BUFF_SIZE)
	c.m.Lock()
	c.binders[evt] = dataChannel
	c.m.Unlock()
	return
}

// Unbind a event
func (c *Client) Unbind(evt string) {
	c.m.Lock()
	delete(c.binders, evt)
	c.m.Unlock()
}

// Stopped checks, in a non-blocking way, if client has been closed.
func (c *Client) Stopped() bool {
	select {
	case <-c.Stop:
		return true
	default:
		return false
	}
}

// Close the underlying Pusher connection (websocket)
func (c *Client) Close() error {
	// Closing the Stop channel "broadcasts" the stop signal.
	close(c.Stop)
	return c.ws.Close()
}

// NewCustomClient return a custom client
func NewCustomClient(appKey, host, scheme string) (*Client, error) {
	ws, err := NewWSS(appKey, host, scheme)
	if err != nil {
		return nil, err
	}
	sChannels := new(subscribedChannels)
	sChannels.channels = make([]string, 0)
	pClient := Client{ws, make(chan *Event, EVENT_CHANNEL_BUFF_SIZE), make(chan bool), sChannels, make(map[string]chan *Event), sync.Mutex{}}
	go pClient.heartbeat()
	go pClient.listen()
	return &pClient, nil
}

// NewWSS return a websocket connexion
func NewWSS(appKey, host, scheme string) (ws *websocket.Conn, err error) {
	origin := "http://localhost/"
	url := scheme + "://" + host + "/app/" + appKey + "?protocol=" + PROTOCOL_VERSION
	ws, err = websocket.Dial(url, "", origin)
	if err != nil {
		return nil, err
	}
	var resp = make([]byte, 11000) // Pusher max message size is 10KB
	n, err := ws.Read(resp)
	if err != nil {
		return nil, err
	}
	var eventStub EventStub
	err = json.Unmarshal(resp[0:n], &eventStub)
	if err != nil {
		return nil, err
	}
	switch eventStub.Event {
	case "pusher:error":
		var ewe EventError
		err = json.Unmarshal(resp[0:n], &ewe)
		if err != nil {
			return nil, err
		}
		return nil, ewe
	case "pusher:connection_established":
		return ws, nil
	}
	return nil, errors.New("Ooooops something wrong happen")
}

// NewClient initialize & return a Pusher client
func NewClient(appKey string) (*Client, error) {
	return NewCustomClient(appKey, "ws.pusherapp.com:443", "wss")
}
<commit_msg>Adding error channel.<commit_after>package pusher

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

// ErrEvent error on event
const ErrEvent = "ErrEvent"

// Client is a pusher client
type Client struct {
	ws                 *websocket.Conn
	Events             chan *Event
	Stop               chan bool
	Errors             chan error
	subscribedChannels *subscribedChannels
	binders            map[string]chan *Event
	m                  sync.Mutex
}

func (c *Client) sendError(err error) {
	select {
	case c.Errors <- err:
	default:
		log.Println(err)
	}
}

// heartbeat send a ping frame to server each - TODO reconnect on disconnect
func (c *Client) heartbeat() {
	for !c.Stopped() {
		if err := websocket.Message.Send(c.ws, `{"event":"pusher:ping","data":"{}"}`); err != nil {
			c.sendError(err)
		}
		time.Sleep(HEARTBEAT_RATE * time.Second)
	}
}

// listen to Pusher server and process/dispatch recieved events
func (c *Client) listen() {
	for !c.Stopped() {
		var event Event
		err := websocket.JSON.Receive(c.ws, &event)
		if err != nil {
			if c.Stopped() {
				// Normal termination (ws Receive returns error when ws is
				// closed by other goroutine)
				c.sendError(fmt.Errorf("Listen error and c stopped : %s", err))
				return
			}
			c.m.Lock()
			ch, ok := c.binders[ErrEvent]
			c.m.Unlock()
			if ok {
				ch <- &Event{
					Event: ErrEvent,
					Data:  err.Error()}
			}
			// no matter what error happened, will log again and again
			// so return
			// err eg:
			// 1. use of closed network connection
			// 2. EOF (network error)
			// 3. ...
			c.sendError(fmt.Errorf("Listen error : %s", err))
			return
		}
		switch event.Event {
		case "pusher:ping":
			websocket.Message.Send(c.ws, `{"event":"pusher:pong","data":"{}"}`)
		case "pusher:pong":
		case "pusher:error":
			c.sendError(fmt.Errorf("Event error received : %s", event.Data))
		default:
			c.m.Lock()
			ch, ok := c.binders[event.Event]
			c.m.Unlock()
			if ok {
				ch <- &event
			}
		}

	}
}

// Subscribe ..to a channel
func (c *Client) Subscribe(channel string) (err error) {
	// Already subscribed ?
	if c.subscribedChannels.contains(channel) {
		err = fmt.Errorf("Channel %s already subscribed", channel)
		return
	}
	err = websocket.Message.Send(c.ws, fmt.Sprintf(`{"event":"pusher:subscribe","data":{"channel":"%s"}}`, channel))
	if err != nil {
		return
	}
	err = c.subscribedChannels.add(channel)
	return
}

// Unsubscribe from a channel
func (c *Client) Unsubscribe(channel string) (err error) {
	// subscribed ?
	if !c.subscribedChannels.contains(channel) {
		err = fmt.Errorf("Client isn't subscrived to %s", channel)
		return
	}
	err = websocket.Message.Send(c.ws, fmt.Sprintf(`{"event":"pusher:unsubscribe","data":{"channel":"%s"}}`, channel))
	if err != nil {
		return
	}
	// Remove channel from subscribedChannels slice
	c.subscribedChannels.remove(channel)
	return
}

// Bind an event
func (c *Client) Bind(evt string) (dataChannel chan *Event, err error) {
	// Already binded
	c.m.Lock()
	_, ok := c.binders[evt]
	c.m.Unlock()
	if ok {
		err = fmt.Errorf("Event %s already binded", evt)
		return
	}
	// New data channel
	dataChannel = make(chan *Event, EVENT_CHANNEL_BUFF_SIZE)
	c.m.Lock()
	c.binders[evt] = dataChannel
	c.m.Unlock()
	return
}

// Unbind a event
func (c *Client) Unbind(evt string) {
	c.m.Lock()
	delete(c.binders, evt)
	c.m.Unlock()
}

// Stopped checks, in a non-blocking way, if client has been closed.
func (c *Client) Stopped() bool {
	select {
	case <-c.Stop:
		return true
	default:
		return false
	}
}

// Close the underlying Pusher connection (websocket)
func (c *Client) Close() error {
	// Closing the Stop channel "broadcasts" the stop signal.
	close(c.Stop)
	return c.ws.Close()
}

// NewCustomClient return a custom client
func NewCustomClient(appKey, host, scheme string) (*Client, error) {
	ws, err := NewWSS(appKey, host, scheme)
	if err != nil {
		return nil, err
	}
	sChannels := new(subscribedChannels)
	sChannels.channels = make([]string, 0)
	pClient := Client{
		ws:                 ws,
		Events:             make(chan *Event, EVENT_CHANNEL_BUFF_SIZE),
		Stop:               make(chan bool),
		Errors:             make(chan error),
		subscribedChannels: sChannels,
		binders:            make(map[string]chan *Event)}
	go pClient.heartbeat()
	go pClient.listen()
	return &pClient, nil
}

// NewWSS return a websocket connexion
func NewWSS(appKey, host, scheme string) (ws *websocket.Conn, err error) {
	origin := "http://localhost/"
	url := scheme + "://" + host + "/app/" + appKey + "?protocol=" + PROTOCOL_VERSION
	ws, err = websocket.Dial(url, "", origin)
	if err != nil {
		return nil, err
	}
	var resp = make([]byte, 11000) // Pusher max message size is 10KB
	n, err := ws.Read(resp)
	if err != nil {
		return nil, err
	}
	var eventStub EventStub
	err = json.Unmarshal(resp[0:n], &eventStub)
	if err != nil {
		return nil, err
	}
	switch eventStub.Event {
	case "pusher:error":
		var ewe EventError
		err = json.Unmarshal(resp[0:n], &ewe)
		if err != nil {
			return nil, err
		}
		return nil, ewe
	case "pusher:connection_established":
		return ws, nil
	}
	return nil, errors.New("Ooooops something wrong happen")
}

// NewClient initialize & return a Pusher client
func NewClient(appKey string) (*Client, error) {
	return NewCustomClient(appKey, "ws.pusherapp.com:443", "wss")
}
<|endoftext|>


<commit_before>package flowdock

import (
	"github.com/njern/httpstream"
	"log"
	"sync"
)

const (
	flowdockAPIURL = "https://api.flowdock.com"
)

// A Client is a Flowdock API client. It should be created
// using NewClient() and provided with a valid API key.
type Client struct {
	apiKey         string
	streamClient   *httpstream.Client
	organizations  []Organization
	AvailableFlows []Flow // TODO: Change to map[ID]Flow
	Users          map[string]User
}

// NewClient creates a new Client and automatically fetches
// information about joined flows, known users, etc.
func NewClient(apiKey string) *Client {
	client := &Client{apiKey: apiKey}

	var wg sync.WaitGroup
	wg.Add(3)

	go func() {
		var err error
		client.Users, err = getUsers(apiKey)
		if err != nil {
			log.Printf("Failed to get users: %v", err)
		}
		wg.Done()
	}()

	go func() {
		var err error
		client.AvailableFlows, err = getFlows(apiKey)
		if err != nil {
			log.Printf("Failed to get flows: %v", err)
		}
		wg.Done()
	}()

	go func() {
		var err error
		client.organizations, err = getOrganizations(apiKey)
		if err != nil {
			log.Printf("Failed to get organizations: %v", err)
		}
		wg.Done()
	}()

	wg.Wait()

	return client
}

// Connect connects the client to the streaming API. Flowdock events will
// be sent back to the caller over the events channel, as will errors.
// If flows is nil, the stream will contain events from all the
// flows the user has joined.
// TODO: pass online state (active / idle / offline)
// TODO: Flag for whether to receive priv messages or not (now defaulting to not)
func (c *Client) Connect(flows []Flow, events chan Event) error {
	stream := make(chan []byte, 1024) // Incoming (raw) data channel
	done := make(chan error)          // Error channel
	flowURL := flowStreamURL(c, flows)

	// Set up the stream. Note we need to set a random password string ("BATMAN") or things will break.
	c.streamClient = httpstream.NewBasicAuthClient(c.apiKey, "BATMAN", func(line []byte) {
		stream <- line
	})

	// Initialize the connection
	err := c.streamClient.Connect(flowURL, done)
	if err != nil {
		return err
	}

	// Fire up a goroutine that will listen to the stream
	// and pass events back to the client.
	go func() {
		for {
			select {
			case event := <-stream:
				parsedEvent, err := unmarshalFlowdockJSONEvent(event)
				if err != nil {
					events <- err
				} else {
					events <- parsedEvent
				}

			case err := <-done:
				if err != nil {
					// TODO: Actually handle errors instead of just closing the channel
					events <- err
					close(events)
				}
			}
		}
	}()

	return nil
}

// DetailsForUser returns a User object for the given user ID.
func (c *Client) DetailsForUser(id string) User {
	return c.Users[id]
}

// DetailsForFlow returns a Flow object for the given Flow ID
// or nil if the client can't access details for that flow.
func (c *Client) DetailsForFlow(id string) *Flow {
	for _, flow := range c.AvailableFlows {
		if flow.ID == id {
			return &flow
		}
	}

	return nil
}

// SendMessage starts a new thread in the specified Flow
// TODO: Implement this
func SendMessage(flow Flow, message string) error {
	return nil
}

// SendReply replies to an existing thread
// TODO: Implement this
func SendReply(flow Flow, reply string, threadID int64) error {
	return nil
}

// flowStreamURL creates the complete URL used to connect
// to the streaming API endpoint, including the flows filter.
func flowStreamURL(c *Client, flows []Flow) string {
	if flows == nil {
		// Add all the flows!
		flows = c.AvailableFlows
	}

	flowURL := "https://stream.flowdock.com/flows?filter="
	for i, flow := range flows {
		if i == len(flows)-1 {
			// Special case; Last item - no comma.
			flowURL = flowURL + flow.Organization.APIName + "/" + flow.APIName
		} else {
			flowURL = flowURL + flow.Organization.APIName + "/" + flow.APIName + ","
		}
	}
	return flowURL
}
<commit_msg>Handle keep alive more nicely<commit_after>package flowdock

import (
	"github.com/njern/httpstream"
	"log"
	"sync"
)

const (
	flowdockAPIURL = "https://api.flowdock.com"
)

// A Client is a Flowdock API client. It should be created
// using NewClient() and provided with a valid API key.
type Client struct {
	apiKey         string
	streamClient   *httpstream.Client
	organizations  []Organization
	AvailableFlows []Flow // TODO: Change to map[ID]Flow
	Users          map[string]User
}

// NewClient creates a new Client and automatically fetches
// information about joined flows, known users, etc.
func NewClient(apiKey string) *Client {
	client := &Client{apiKey: apiKey}

	var wg sync.WaitGroup
	wg.Add(3)

	go func() {
		var err error
		client.Users, err = getUsers(apiKey)
		if err != nil {
			log.Printf("Failed to get users: %v", err)
		}
		wg.Done()
	}()

	go func() {
		var err error
		client.AvailableFlows, err = getFlows(apiKey)
		if err != nil {
			log.Printf("Failed to get flows: %v", err)
		}
		wg.Done()
	}()

	go func() {
		var err error
		client.organizations, err = getOrganizations(apiKey)
		if err != nil {
			log.Printf("Failed to get organizations: %v", err)
		}
		wg.Done()
	}()

	wg.Wait()

	return client
}

// Connect connects the client to the streaming API. Flowdock events will
// be sent back to the caller over the events channel, as will errors.
// If flows is nil, the stream will contain events from all the
// flows the user has joined.
// TODO: pass online state (active / idle / offline)
// TODO: Flag for whether to receive priv messages or not (now defaulting to not)
func (c *Client) Connect(flows []Flow, events chan Event) error {
	stream := make(chan []byte, 1024) // Incoming (raw) data channel
	done := make(chan error)          // Error channel
	flowURL := flowStreamURL(c, flows)

	// Set up the stream. Note we need to set a random password string ("BATMAN") or things will break.
	c.streamClient = httpstream.NewBasicAuthClient(c.apiKey, "BATMAN", func(line []byte) {
		stream <- line
	})

	// Initialize the connection
	err := c.streamClient.Connect(flowURL, done)
	if err != nil {
		return err
	}

	// Fire up a goroutine that will listen to the stream
	// and pass events back to the client.
	go func() {
		for {
			select {
			case event := <-stream:
				if bytes.Equal(event, []byte{10}) {
					log.Printf("Keepalive, ignore...")
					continue
				}
				parsedEvent, err := unmarshalFlowdockJSONEvent(event)
				if err != nil {
					events <- err
				} else {
					events <- parsedEvent
				}

			case err := <-done:
				if err != nil {
					// TODO: Actually handle errors instead of just closing the channel
					events <- err
					close(events)
				}
			}
		}
	}()

	return nil
}

// DetailsForUser returns a User object for the given user ID.
func (c *Client) DetailsForUser(id string) User {
	return c.Users[id]
}

// DetailsForFlow returns a Flow object for the given Flow ID
// or nil if the client can't access details for that flow.
func (c *Client) DetailsForFlow(id string) *Flow {
	for _, flow := range c.AvailableFlows {
		if flow.ID == id {
			return &flow
		}
	}

	return nil
}

// SendMessage starts a new thread in the specified Flow
// TODO: Implement this
func SendMessage(flow Flow, message string) error {
	return nil
}

// SendReply replies to an existing thread
// TODO: Implement this
func SendReply(flow Flow, reply string, threadID int64) error {
	return nil
}

// flowStreamURL creates the complete URL used to connect
// to the streaming API endpoint, including the flows filter.
func flowStreamURL(c *Client, flows []Flow) string {
	if flows == nil {
		// Add all the flows!
		flows = c.AvailableFlows
	}

	flowURL := "https://stream.flowdock.com/flows?filter="
	for i, flow := range flows {
		if i == len(flows)-1 {
			// Special case; Last item - no comma.
			flowURL = flowURL + flow.Organization.APIName + "/" + flow.APIName
		} else {
			flowURL = flowURL + flow.Organization.APIName + "/" + flow.APIName + ","
		}
	}
	return flowURL
}
<|endoftext|>