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

package main

import (
	"os"

	"github.com/minio-io/cli"
	"github.com/minio-io/minio/pkg/server"
	"github.com/minio-io/minio/pkg/utils/log"
)

// commitID is automatically set by git. Settings are controlled
// through .gitattributes
const commitID = "$Id$"

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "domain,d",
		Value: "",
		Usage: "domain used for routing incoming API requests",
	},
	cli.StringFlag{
		Name:  "api-address,a",
		Value: ":9000",
		Usage: "address for incoming API requests",
	},
	cli.StringFlag{
		Name:  "web-address,w",
		Value: ":9001",
		Usage: "address for incoming Management UI requests",
	},
	cli.StringFlag{
		Name:  "cert,c",
		Hide:  true,
		Value: "",
		Usage: "cert.pem",
	},
	cli.StringFlag{
		Name:  "key,k",
		Hide:  true,
		Value: "",
		Usage: "key.pem",
	},
	cli.StringFlag{
		Name:  "driver-type,t",
		Value: "donut",
		Usage: "valid entries: file,inmemory,donut",
	},
}

func getDriverType(input string) server.DriverType {
	switch {
	case input == "file":
		return server.File
	case input == "memory":
		return server.Memory
	case input == "donut":
		return server.Donut
	default:
		{
			log.Println("Unknown driver type:", input)
			log.Println("Choosing default driver type as 'file'..")
			return server.File
		}
	}
}

func runCmd(c *cli.Context) {
	driverTypeStr := c.String("driver-type")
	domain := c.String("domain")
	apiaddress := c.String("api-address")
	webaddress := c.String("web-address")
	certFile := c.String("cert")
	keyFile := c.String("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		log.Fatal("Both certificate and key must be provided to enable https")
	}
	tls := (certFile != "" && keyFile != "")
	driverType := getDriverType(driverTypeStr)
	var serverConfigs []server.Config
	apiServerConfig := server.Config{
		Domain:   domain,
		Address:  apiaddress,
		TLS:      tls,
		CertFile: certFile,
		KeyFile:  keyFile,
		APIType: server.MinioAPI{
			DriverType: driverType,
		},
	}
	webUIServerConfig := server.Config{
		Domain:   domain,
		Address:  webaddress,
		TLS:      false,
		CertFile: "",
		KeyFile:  "",
		APIType: server.Web{
			Websocket: false,
		},
	}
	serverConfigs = append(serverConfigs, apiServerConfig)
	serverConfigs = append(serverConfigs, webUIServerConfig)
	server.Start(serverConfigs)
}

func main() {
	app := cli.NewApp()
	app.Name = "minio"
	app.Version = "0.1.0"
	app.Author = "Minio.io"
	app.Usage = "Minimalist Object Storage"
	app.EnableBashCompletion = true
	app.Flags = flags
	app.Action = runCmd
	app.Run(os.Args)
}
<commit_msg>Use commitID as version<commit_after>/*
 * Minimalist Object Storage, (C) 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"os"

	"github.com/minio-io/cli"
	"github.com/minio-io/minio/pkg/server"
	"github.com/minio-io/minio/pkg/utils/log"
)

// commitID is automatically set by git. Settings are controlled
// through .gitattributes
const commitID = "$Id$"

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "domain,d",
		Value: "",
		Usage: "domain used for routing incoming API requests",
	},
	cli.StringFlag{
		Name:  "api-address,a",
		Value: ":9000",
		Usage: "address for incoming API requests",
	},
	cli.StringFlag{
		Name:  "web-address,w",
		Value: ":9001",
		Usage: "address for incoming Management UI requests",
	},
	cli.StringFlag{
		Name:  "cert,c",
		Hide:  true,
		Value: "",
		Usage: "cert.pem",
	},
	cli.StringFlag{
		Name:  "key,k",
		Hide:  true,
		Value: "",
		Usage: "key.pem",
	},
	cli.StringFlag{
		Name:  "driver-type,t",
		Value: "donut",
		Usage: "valid entries: file,inmemory,donut",
	},
}

func getDriverType(input string) server.DriverType {
	switch {
	case input == "file":
		return server.File
	case input == "memory":
		return server.Memory
	case input == "donut":
		return server.Donut
	default:
		{
			log.Println("Unknown driver type:", input)
			log.Println("Choosing default driver type as 'file'..")
			return server.File
		}
	}
}

func runCmd(c *cli.Context) {
	driverTypeStr := c.String("driver-type")
	domain := c.String("domain")
	apiaddress := c.String("api-address")
	webaddress := c.String("web-address")
	certFile := c.String("cert")
	keyFile := c.String("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		log.Fatal("Both certificate and key must be provided to enable https")
	}
	tls := (certFile != "" && keyFile != "")
	driverType := getDriverType(driverTypeStr)
	var serverConfigs []server.Config
	apiServerConfig := server.Config{
		Domain:   domain,
		Address:  apiaddress,
		TLS:      tls,
		CertFile: certFile,
		KeyFile:  keyFile,
		APIType: server.MinioAPI{
			DriverType: driverType,
		},
	}
	webUIServerConfig := server.Config{
		Domain:   domain,
		Address:  webaddress,
		TLS:      false,
		CertFile: "",
		KeyFile:  "",
		APIType: server.Web{
			Websocket: false,
		},
	}
	serverConfigs = append(serverConfigs, apiServerConfig)
	serverConfigs = append(serverConfigs, webUIServerConfig)
	server.Start(serverConfigs)
}

func main() {
	app := cli.NewApp()
	app.Name = "minio"
	app.Version = commitID
	app.Author = "Minio.io"
	app.Usage = "Minimalist Object Storage"
	app.EnableBashCompletion = true
	app.Flags = flags
	app.Action = runCmd
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"net/http"
	"os"

	"github.com/guregu/kami"
	"github.com/kyokomi/slackbot"
	"github.com/kyokomi/slackbot/plugins"
	"golang.org/x/net/context"

	// init insert bot.plugins
	//	_ "github.com/kyokomi/slackbot/plugins/echo"
	"github.com/kyokomi/nepu-bot/plugins/nepubot"
	_ "github.com/kyokomi/slackbot/plugins/tiqav"
	_ "github.com/kyokomi/slackbot/plugins/lgtm"
)

func main() {
	var apikey string
	flag.StringVar(&apikey, "d", os.Getenv("DOCOMO_APIKEY"), "ドコモのAPIKEY")
	var token string
	flag.StringVar(&token, "token", os.Getenv("SLACK_BOT_TOKEN"), "SlackのBotToken")
	flag.Parse()

	ctx := plugins.Context()
	ctx = nepubot.NewContext(ctx, apikey)

	c := slackbot.DefaultConfig()
	c.Name = "いーすん"
	c.SlackToken = token

	slackbot.WebSocketRTM(ctx, c)

	kami.Get("/", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	})
	kami.Get("/ping", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("PONG"))
	})
	kami.Serve()
}
<commit_msg>added suddendeath<commit_after>package main

import (
	"flag"
	"net/http"
	"os"

	"github.com/guregu/kami"
	"github.com/kyokomi/slackbot"
	"github.com/kyokomi/slackbot/plugins"
	"golang.org/x/net/context"

	// init insert bot.plugins
	//	_ "github.com/kyokomi/slackbot/plugins/echo"
	"github.com/kyokomi/nepu-bot/plugins/nepubot"
	_ "github.com/kyokomi/slackbot/plugins/tiqav"
	_ "github.com/kyokomi/slackbot/plugins/lgtm"
	_ "github.com/kyokomi/slackbot/plugins/suddendeath"
)

func main() {
	var apikey string
	flag.StringVar(&apikey, "d", os.Getenv("DOCOMO_APIKEY"), "ドコモのAPIKEY")
	var token string
	flag.StringVar(&token, "token", os.Getenv("SLACK_BOT_TOKEN"), "SlackのBotToken")
	flag.Parse()

	ctx := plugins.Context()
	ctx = nepubot.NewContext(ctx, apikey)

	c := slackbot.DefaultConfig()
	c.Name = "いーすん"
	c.SlackToken = token

	slackbot.WebSocketRTM(ctx, c)

	kami.Get("/", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("OK"))
	})
	kami.Get("/ping", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("PONG"))
	})
	kami.Serve()
}
<|endoftext|>


<commit_before>// Version 1.82
// Supports Windows, Linux, Mac, and Raspberry Pi, Beagle Bone Black

package main

import (
	"flag"
	"os"
	"os/user"
	"path/filepath"
	"runtime/debug"
	"strconv"
	"text/template"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/carlescere/scheduler"
	"github.com/gin-gonic/gin"
	"github.com/itsjamie/gin-cors"
	"github.com/kardianos/osext"
	"github.com/vharitonsky/iniflags"
	//"github.com/sanbornm/go-selfupdate/selfupdate" #included in update.go to change heavily
)

var (
	version              = "x.x.x-dev" //don't modify it, Jenkins will take care
	git_revision         = "xxxxxxxx"  //don't modify it, Jenkins will take care
	embedded_autoupdate  = true
	embedded_autoextract = false
	hibernate            = flag.Bool("hibernate", false, "start hibernated")
	verbose              = flag.Bool("v", true, "show debug logging")
	//verbose = flag.Bool("v", false, "show debug logging")
	isLaunchSelf = flag.Bool("ls", false, "launch self 5 seconds later")
	configIni    = flag.String("configFile", "config.ini", "config file path")
	regExpFilter = flag.String("regex", "usb|acm|com", "Regular expression to filter serial port list")
	gcType       = flag.String("gc", "std", "Type of garbage collection. std = Normal garbage collection allowing system to decide (this has been known to cause a stop the world in the middle of a CNC job which can cause lost responses from the CNC controller and thus stalled jobs. use max instead to solve.), off = let memory grow unbounded (you have to send in the gc command manually to garbage collect or you will run out of RAM eventually), max = Force garbage collection on each recv or send on a serial port (this minimizes stop the world events and thus lost serial responses, but increases CPU usage)")
	logDump      = flag.String("log", "off", "off = (default)")
	// hostname. allow user to override, otherwise we look it up
	hostname       = flag.String("hostname", "unknown-hostname", "Override the hostname we get from the OS")
	updateUrl      = flag.String("updateUrl", "", "")
	appName        = flag.String("appName", "", "")
	genCert        = flag.Bool("generateCert", false, "")
	globalToolsMap = make(map[string]string)
	tempToolsPath  = createToolsDir()
	port           string
	portSSL        string
	origins        = flag.String("origins", "", "Allowed origin list for CORS")
)

type NullWriter int

func (NullWriter) Write([]byte) (int, error) { return 0, nil }

type logWriter struct{}

func (u *logWriter) Write(p []byte) (n int, err error) {
	h.broadcastSys <- p
	return len(p), nil
}

var logger_ws logWriter

func createToolsDir() string {
	usr, _ := user.Current()
	return usr.HomeDir + "/.arduino-create"
}

func homeHandler(c *gin.Context) {
	homeTemplate.Execute(c.Writer, c.Request.Host)
}

func launchSelfLater() {
	log.Println("Going to launch myself 2 seconds later.")
	time.Sleep(2 * 1000 * time.Millisecond)
	log.Println("Done waiting 2 secs. Now launching...")
}

func main() {

	flag.Parse()

	if *genCert == true {
		generateCertificates()
		os.Exit(0)
	}

	if *hibernate == false {

		go func() {

			// autoextract self
			src, _ := osext.Executable()
			dest := filepath.Dir(src)

			os.Mkdir(tempToolsPath, 0777)
			hideFile(tempToolsPath)

			if embedded_autoextract {
				// save the config.ini (if it exists)
				if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
					log.Println("First run, unzipping self")
					err := Unzip(src, dest)
					log.Println("Self extraction, err:", err)
				}

				if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
					flag.Parse()
					log.Println("No config.ini at", *configIni)
				} else {
					flag.Parse()
					flag.Set("config", dest+"/"+*configIni)
					iniflags.Parse()
				}
			} else {
				flag.Set("config", dest+"/"+*configIni)
				iniflags.Parse()
			}

			// move CORS to config file compatibility, Vagrant version
			if *origins == "" {
				log.Println("Patching config.ini for compatibility")
				f, err := os.OpenFile(dest+"/"+*configIni, os.O_APPEND|os.O_WRONLY, 0666)
				if err != nil {
					panic(err)
				}
				_, err = f.WriteString("\norigins = http://webide.arduino.cc:8080\n")
				if err != nil {
					panic(err)
				}
				f.Close()
				restart("")
			}
			//log.SetFormatter(&log.JSONFormatter{})

			log.SetLevel(log.InfoLevel)

			log.SetOutput(os.Stderr)

			// see if we are supposed to wait 5 seconds
			if *isLaunchSelf {
				launchSelfLater()
			}

			if embedded_autoupdate {

				var updater = &Updater{
					CurrentVersion: version,
					ApiURL:         *updateUrl,
					BinURL:         *updateUrl,
					DiffURL:        "",
					Dir:            "update/",
					CmdName:        *appName,
				}

				if updater != nil {
					updater_job := func() {
						go updater.BackgroundRun()
					}
					scheduler.Every(5).Minutes().Run(updater_job)
				}
			}

			log.Println("Version:" + version)

			// hostname
			hn, _ := os.Hostname()
			if *hostname == "unknown-hostname" {
				*hostname = hn
			}
			log.Println("Hostname:", *hostname)

			// turn off garbage collection
			// this is dangerous, as u could overflow memory
			//if *isGC {
			if *gcType == "std" {
				log.Println("Garbage collection is on using Standard mode, meaning we just let Golang determine when to garbage collect.")
			} else if *gcType == "max" {
				log.Println("Garbage collection is on for MAXIMUM real-time collecting on each send/recv from serial port. Higher CPU, but less stopping of the world to garbage collect since it is being done on a constant basis.")
			} else {
				log.Println("Garbage collection is off. Memory use will grow unbounded. You WILL RUN OUT OF RAM unless you send in the gc command to manually force garbage collection. Lower CPU, but progressive memory footprint.")
				debug.SetGCPercent(-1)
			}

			// see if they provided a regex filter
			if len(*regExpFilter) > 0 {
				log.Printf("You specified a serial port regular expression filter: %v\n", *regExpFilter)
			}

			// list serial ports
			portList, _ := GetList(false)
			log.Println("Your serial ports:")
			if len(portList) == 0 {
				log.Println("\tThere are no serial ports to list.")
			}
			for _, element := range portList {
				log.Printf("\t%v\n", element)

			}

			if !*verbose {
				log.Println("You can enter verbose mode to see all logging by starting with the -v command line switch.")
				log.SetOutput(new(NullWriter)) //route all logging to nullwriter
			}

			// launch the hub routine which is the singleton for the websocket server
			go h.run()
			// launch our serial port routine
			go sh.run()
			// launch our dummy data routine
			//go d.run()

			go discoverLoop()

			r := gin.New()

			socketHandler := wsHandler().ServeHTTP

			extraOriginStr := "https://create.arduino.cc, http://create.arduino.cc, https://create-dev.arduino.cc, http://create-dev.arduino.cc, http://create-staging.arduino.cc, https://create-staging.arduino.cc"

			for i := 8990; i < 9001; i++ {
				extraOriginStr = extraOriginStr + ", http://localhost:" + strconv.Itoa(i) + ", https://localhost:" + strconv.Itoa(i)
			}

			r.Use(cors.Middleware(cors.Config{
				Origins:         *origins + ", " + extraOriginStr,
				Methods:         "GET, PUT, POST, DELETE",
				RequestHeaders:  "Origin, Authorization, Content-Type",
				ExposedHeaders:  "",
				MaxAge:          50 * time.Second,
				Credentials:     true,
				ValidateHeaders: false,
			}))

			r.LoadHTMLFiles("templates/nofirefox.html")

			r.GET("/", homeHandler)
			r.GET("/certificate.crt", certHandler)
			r.DELETE("/certificate.crt", deleteCertHandler)
			r.POST("/upload", uploadHandler)
			r.GET("/socket.io/", socketHandler)
			r.POST("/socket.io/", socketHandler)
			r.Handle("WS", "/socket.io/", socketHandler)
			r.Handle("WSS", "/socket.io/", socketHandler)
			r.GET("/info", infoHandler)
			r.POST("/killbrowser", killBrowserHandler)
			r.POST("/pause", pauseHandler)

			go func() {
				// check if certificates exist; if not, use plain http
				if _, err := os.Stat(filepath.Join(dest, "cert.pem")); os.IsNotExist(err) {
					return
				}

				start := 8990
				end := 9000
				i := start
				for i < end {
					i = i + 1
					portSSL = ":" + strconv.Itoa(i)
					if err := r.RunTLS(portSSL, filepath.Join(dest, "cert.pem"), filepath.Join(dest, "key.pem")); err != nil {
						log.Printf("Error trying to bind to port: %v, so exiting...", err)
						continue
					} else {
						ip := "0.0.0.0"
						log.Print("Starting server and websocket (SSL) on " + ip + "" + port)
						break
					}
				}
			}()

			go func() {
				start := 8990
				end := 9000
				i := start
				for i < end {
					i = i + 1
					port = ":" + strconv.Itoa(i)
					if err := r.Run(port); err != nil {
						log.Printf("Error trying to bind to port: %v, so exiting...", err)
						continue
					} else {
						ip := "0.0.0.0"
						log.Print("Starting server and websocket on " + ip + "" + port)
						break
					}
				}
			}()

		}()
	}
	setupSysTray()
}

var homeTemplate = template.Must(template.New("home").Parse(homeTemplateHtml))

// If you navigate to this server's homepage, you'll get this HTML
// so you can directly interact with the serial port server
const homeTemplateHtml = `<!DOCTYPE html>

<title>Serial Port Example</title>

<form id="form">
    <input type="submit" value="Send" />
    <input type="text" id="msg" size="64"/>
    <input name="pause" type="checkbox" value="pause" id="myCheck"/> Pause

</form>
</form>

`
<commit_msg>Only listen to localhost by default<commit_after>// Version 1.82
// Supports Windows, Linux, Mac, and Raspberry Pi, Beagle Bone Black

package main

import (
	"flag"
	"os"
	"os/user"
	"path/filepath"
	"runtime/debug"
	"strconv"
	"text/template"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/carlescere/scheduler"
	"github.com/gin-gonic/gin"
	"github.com/itsjamie/gin-cors"
	"github.com/kardianos/osext"
	"github.com/vharitonsky/iniflags"
	//"github.com/sanbornm/go-selfupdate/selfupdate" #included in update.go to change heavily
)

var (
	version              = "x.x.x-dev" //don't modify it, Jenkins will take care
	git_revision         = "xxxxxxxx"  //don't modify it, Jenkins will take care
	embedded_autoupdate  = true
	embedded_autoextract = false
	hibernate            = flag.Bool("hibernate", false, "start hibernated")
	verbose              = flag.Bool("v", true, "show debug logging")
	//verbose = flag.Bool("v", false, "show debug logging")
	isLaunchSelf = flag.Bool("ls", false, "launch self 5 seconds later")
	configIni    = flag.String("configFile", "config.ini", "config file path")
	regExpFilter = flag.String("regex", "usb|acm|com", "Regular expression to filter serial port list")
	gcType       = flag.String("gc", "std", "Type of garbage collection. std = Normal garbage collection allowing system to decide (this has been known to cause a stop the world in the middle of a CNC job which can cause lost responses from the CNC controller and thus stalled jobs. use max instead to solve.), off = let memory grow unbounded (you have to send in the gc command manually to garbage collect or you will run out of RAM eventually), max = Force garbage collection on each recv or send on a serial port (this minimizes stop the world events and thus lost serial responses, but increases CPU usage)")
	logDump      = flag.String("log", "off", "off = (default)")
	// hostname. allow user to override, otherwise we look it up
	hostname       = flag.String("hostname", "unknown-hostname", "Override the hostname we get from the OS")
	updateUrl      = flag.String("updateUrl", "", "")
	appName        = flag.String("appName", "", "")
	genCert        = flag.Bool("generateCert", false, "")
	globalToolsMap = make(map[string]string)
	tempToolsPath  = createToolsDir()
	port           string
	portSSL        string
	origins        = flag.String("origins", "", "Allowed origin list for CORS")
	address        = flag.String("address", "127.0.0.1", "The address where to listen. Defaults to localhost")
)

type NullWriter int

func (NullWriter) Write([]byte) (int, error) { return 0, nil }

type logWriter struct{}

func (u *logWriter) Write(p []byte) (n int, err error) {
	h.broadcastSys <- p
	return len(p), nil
}

var logger_ws logWriter

func createToolsDir() string {
	usr, _ := user.Current()
	return usr.HomeDir + "/.arduino-create"
}

func homeHandler(c *gin.Context) {
	homeTemplate.Execute(c.Writer, c.Request.Host)
}

func launchSelfLater() {
	log.Println("Going to launch myself 2 seconds later.")
	time.Sleep(2 * 1000 * time.Millisecond)
	log.Println("Done waiting 2 secs. Now launching...")
}

func main() {

	flag.Parse()

	if *genCert == true {
		generateCertificates()
		os.Exit(0)
	}

	if *hibernate == false {

		go func() {

			// autoextract self
			src, _ := osext.Executable()
			dest := filepath.Dir(src)

			os.Mkdir(tempToolsPath, 0777)
			hideFile(tempToolsPath)

			if embedded_autoextract {
				// save the config.ini (if it exists)
				if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
					log.Println("First run, unzipping self")
					err := Unzip(src, dest)
					log.Println("Self extraction, err:", err)
				}

				if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
					flag.Parse()
					log.Println("No config.ini at", *configIni)
				} else {
					flag.Parse()
					flag.Set("config", dest+"/"+*configIni)
					iniflags.Parse()
				}
			} else {
				flag.Set("config", dest+"/"+*configIni)
				iniflags.Parse()
			}

			// move CORS to config file compatibility, Vagrant version
			if *origins == "" {
				log.Println("Patching config.ini for compatibility")
				f, err := os.OpenFile(dest+"/"+*configIni, os.O_APPEND|os.O_WRONLY, 0666)
				if err != nil {
					panic(err)
				}
				_, err = f.WriteString("\norigins = http://webide.arduino.cc:8080\n")
				if err != nil {
					panic(err)
				}
				f.Close()
				restart("")
			}
			//log.SetFormatter(&log.JSONFormatter{})

			log.SetLevel(log.InfoLevel)

			log.SetOutput(os.Stderr)

			// see if we are supposed to wait 5 seconds
			if *isLaunchSelf {
				launchSelfLater()
			}

			if embedded_autoupdate {

				var updater = &Updater{
					CurrentVersion: version,
					ApiURL:         *updateUrl,
					BinURL:         *updateUrl,
					DiffURL:        "",
					Dir:            "update/",
					CmdName:        *appName,
				}

				if updater != nil {
					updater_job := func() {
						go updater.BackgroundRun()
					}
					scheduler.Every(5).Minutes().Run(updater_job)
				}
			}

			log.Println("Version:" + version)

			// hostname
			hn, _ := os.Hostname()
			if *hostname == "unknown-hostname" {
				*hostname = hn
			}
			log.Println("Hostname:", *hostname)

			// turn off garbage collection
			// this is dangerous, as u could overflow memory
			//if *isGC {
			if *gcType == "std" {
				log.Println("Garbage collection is on using Standard mode, meaning we just let Golang determine when to garbage collect.")
			} else if *gcType == "max" {
				log.Println("Garbage collection is on for MAXIMUM real-time collecting on each send/recv from serial port. Higher CPU, but less stopping of the world to garbage collect since it is being done on a constant basis.")
			} else {
				log.Println("Garbage collection is off. Memory use will grow unbounded. You WILL RUN OUT OF RAM unless you send in the gc command to manually force garbage collection. Lower CPU, but progressive memory footprint.")
				debug.SetGCPercent(-1)
			}

			// see if they provided a regex filter
			if len(*regExpFilter) > 0 {
				log.Printf("You specified a serial port regular expression filter: %v\n", *regExpFilter)
			}

			// list serial ports
			portList, _ := GetList(false)
			log.Println("Your serial ports:")
			if len(portList) == 0 {
				log.Println("\tThere are no serial ports to list.")
			}
			for _, element := range portList {
				log.Printf("\t%v\n", element)

			}

			if !*verbose {
				log.Println("You can enter verbose mode to see all logging by starting with the -v command line switch.")
				log.SetOutput(new(NullWriter)) //route all logging to nullwriter
			}

			// launch the hub routine which is the singleton for the websocket server
			go h.run()
			// launch our serial port routine
			go sh.run()
			// launch our dummy data routine
			//go d.run()

			go discoverLoop()

			r := gin.New()

			socketHandler := wsHandler().ServeHTTP

			extraOriginStr := "https://create.arduino.cc, http://create.arduino.cc, https://create-dev.arduino.cc, http://create-dev.arduino.cc, http://create-staging.arduino.cc, https://create-staging.arduino.cc"

			for i := 8990; i < 9001; i++ {
				extraOriginStr = extraOriginStr + ", http://localhost:" + strconv.Itoa(i) + ", https://localhost:" + strconv.Itoa(i)
			}

			r.Use(cors.Middleware(cors.Config{
				Origins:         *origins + ", " + extraOriginStr,
				Methods:         "GET, PUT, POST, DELETE",
				RequestHeaders:  "Origin, Authorization, Content-Type",
				ExposedHeaders:  "",
				MaxAge:          50 * time.Second,
				Credentials:     true,
				ValidateHeaders: false,
			}))

			r.LoadHTMLFiles("templates/nofirefox.html")

			r.GET("/", homeHandler)
			r.GET("/certificate.crt", certHandler)
			r.DELETE("/certificate.crt", deleteCertHandler)
			r.POST("/upload", uploadHandler)
			r.GET("/socket.io/", socketHandler)
			r.POST("/socket.io/", socketHandler)
			r.Handle("WS", "/socket.io/", socketHandler)
			r.Handle("WSS", "/socket.io/", socketHandler)
			r.GET("/info", infoHandler)
			r.POST("/killbrowser", killBrowserHandler)
			r.POST("/pause", pauseHandler)

			go func() {
				// check if certificates exist; if not, use plain http
				if _, err := os.Stat(filepath.Join(dest, "cert.pem")); os.IsNotExist(err) {
					return
				}

				start := 8990
				end := 9000
				i := start
				for i < end {
					i = i + 1
					portSSL = ":" + strconv.Itoa(i)
					if err := r.RunTLS(*address+portSSL, filepath.Join(dest, "cert.pem"), filepath.Join(dest, "key.pem")); err != nil {
						log.Printf("Error trying to bind to port: %v, so exiting...", err)
						continue
					} else {
						ip := "0.0.0.0"
						log.Print("Starting server and websocket (SSL) on " + ip + "" + port)
						break
					}
				}
			}()

			go func() {
				start := 8990
				end := 9000
				i := start
				for i < end {
					i = i + 1
					port = ":" + strconv.Itoa(i)
					if err := r.Run(*address + port); err != nil {
						log.Printf("Error trying to bind to port: %v, so exiting...", err)
						continue
					} else {
						ip := "0.0.0.0"
						log.Print("Starting server and websocket on " + ip + "" + port)
						break
					}
				}
			}()

		}()
	}
	setupSysTray()
}

var homeTemplate = template.Must(template.New("home").Parse(homeTemplateHtml))

// If you navigate to this server's homepage, you'll get this HTML
// so you can directly interact with the serial port server
const homeTemplateHtml = `<!DOCTYPE html>

<title>Serial Port Example</title>

<form id="form">
    <input type="submit" value="Send" />
    <input type="text" id="msg" size="64"/>
    <input name="pause" type="checkbox" value="pause" id="myCheck"/> Pause

</form>
</form>

`
<|endoftext|>


<commit_before>package zipkinproxy

import (
	"github.com/flachnetz/dd-zipkin-proxy/datadog"
	"github.com/flachnetz/dd-zipkin-proxy/proxy"
	"github.com/flachnetz/go-admin"
	"github.com/flachnetz/startup"
	"github.com/flachnetz/startup/startup_base"
	"github.com/flachnetz/startup/startup_http"
	"github.com/flachnetz/startup/startup_metrics"
	"github.com/jessevdk/go-flags"
	"github.com/julienschmidt/httprouter"
	"github.com/pkg/profile"
	"github.com/sirupsen/logrus"
	"net/http"

	"regexp"
	"strings"

	_ "github.com/apache/thrift/lib/go/thrift"
)

var log = logrus.WithField("prefix", "main")

type SpanConverter func(span proxy.Span) (proxy.Span, error)

func Main(spanConverter SpanConverter) {
	var opts struct {
		Base    startup_base.BaseOptions       `group:"Base options"`
		HTTP    startup_http.HTTPOptions       `group:"HTTP server options"`
		Metrics startup_metrics.MetricsOptions `group:"Metrics configuration"`

		DisableSpanCorrection bool `long:"disable-span-correction" description:"Do not to correct the spans."`

		TraceAgent struct {
			Host string `long:"trace-host" value:"localhost" description:"Hostname of the trace agent."`
			Port string `long:"trace-port" value:"8126" description:"Port of the trace agent."`
		}
	}

	defer profile.Start().Stop()

	opts.Metrics.Inputs.MetricsPrefix = "zipkin.proxy"

	startup.MustParseCommandLine(&opts)

	var channels []chan<- proxy.Span

	{
		log.Info("Enable forwarding of spans to datadog trace-agent")
		datadog.Initialize(opts.TraceAgent.Host + ":" + opts.TraceAgent.Port)

		// accept zipkin spans
		spans := make(chan proxy.Span, 1024)
		channels = append(channels, spans)

		go datadog.Sink(spans)
	}

	// a channel to store the last spans that were received
	var buffer *SpansBuffer
	{
		zipkinSpans := make(chan proxy.Span, 1024)
		channels = append(channels, zipkinSpans)

		// just keep references to previous spans.
		buffer = NewSpansBuffer(2048)
		go buffer.ReadFrom(zipkinSpans)
	}

	// multiplex input channel to all the target channels
	proxySpans := make(chan proxy.Span, 16)
	go forwardSpansToChannels(proxySpans, channels, spanConverter)

	originalZipkinSpans := make(chan proxy.Span, 1024)
	if opts.DisableSpanCorrection {
		go PipeThroughSpans(originalZipkinSpans, proxySpans)
	} else {
		// do error correction for spans
		go ErrorCorrectSpans(originalZipkinSpans, proxySpans)
	}

	opts.HTTP.Serve(startup_http.Config{
		Name: "dd-zipkin-proxy",

		AdminHandlers: []admin.RouteConfig{
			admin.Describe("A buffer of the previous traces (in openzipkin-format) in the order they were received.",
				admin.WithGenericValue("/spans", buffer.ToSlice)),
		},

		Routing: func(router *httprouter.Router) http.Handler {
			// we emulate the zipkin api
			router.POST("/api/v1/spans", handleSpans(originalZipkinSpans, 1))
			router.POST("/api/v2/spans", handleSpans(originalZipkinSpans, 2))
			return router
		},
	})
}

func configureEnvironmentVariables(group *flags.Group) {
	for _, gr := range group.Groups() {
		configureEnvironmentVariables(gr)
	}

	expr := regexp.MustCompile("[^A-Z0-9]+")
	for _, op := range group.Options() {

		name := strings.ToUpper(op.LongNameWithNamespace())
		op.EnvDefaultKey = "DDZK_" + expr.ReplaceAllString(name, "_")
	}
}

func forwardSpansToChannels(source <-chan proxy.Span, targets []chan<- proxy.Span, converter SpanConverter) {
	for span := range source {
		converted, err := converter(span)
		if err == nil {
			for _, target := range targets {
				target <- converted
			}
		}
	}
}
<commit_msg>Added ability to provide external routing config for extra custom routes<commit_after>package zipkinproxy

import (
	"github.com/flachnetz/dd-zipkin-proxy/datadog"
	"github.com/flachnetz/dd-zipkin-proxy/proxy"
	"github.com/flachnetz/go-admin"
	"github.com/flachnetz/startup"
	"github.com/flachnetz/startup/startup_base"
	"github.com/flachnetz/startup/startup_http"
	"github.com/flachnetz/startup/startup_metrics"
	"github.com/jessevdk/go-flags"
	"github.com/julienschmidt/httprouter"
	"github.com/pkg/profile"
	"github.com/sirupsen/logrus"
	"net/http"

	"regexp"
	"strings"

	_ "github.com/apache/thrift/lib/go/thrift"
)

var log = logrus.WithField("prefix", "main")

type SpanConverter func(span proxy.Span) (proxy.Span, error)
type Routing func(*httprouter.Router) http.Handler

func Main(spanConverter SpanConverter) {
	routing := func(router *httprouter.Router) http.Handler {
		return router
	}

	MainWithRouting(routing, spanConverter)
}

func MainWithRouting(routing Routing,  spanConverter SpanConverter) {
	var opts struct {
		Base    startup_base.BaseOptions       `group:"Base options"`
		HTTP    startup_http.HTTPOptions       `group:"HTTP server options"`
		Metrics startup_metrics.MetricsOptions `group:"Metrics configuration"`

		DisableSpanCorrection bool `long:"disable-span-correction" description:"Do not to correct the spans."`

		TraceAgent struct {
			Host string `long:"trace-host" value:"localhost" description:"Hostname of the trace agent."`
			Port string `long:"trace-port" value:"8126" description:"Port of the trace agent."`
		}
	}

	defer profile.Start().Stop()

	opts.Metrics.Inputs.MetricsPrefix = "zipkin.proxy"

	startup.MustParseCommandLine(&opts)

	var channels []chan<- proxy.Span

	{
		log.Info("Enable forwarding of spans to datadog trace-agent")
		datadog.Initialize(opts.TraceAgent.Host + ":" + opts.TraceAgent.Port)

		// accept zipkin spans
		spans := make(chan proxy.Span, 1024)
		channels = append(channels, spans)

		go datadog.Sink(spans)
	}

	// a channel to store the last spans that were received
	var buffer *SpansBuffer
	{
		zipkinSpans := make(chan proxy.Span, 1024)
		channels = append(channels, zipkinSpans)

		// just keep references to previous spans.
		buffer = NewSpansBuffer(2048)
		go buffer.ReadFrom(zipkinSpans)
	}

	// multiplex input channel to all the target channels
	proxySpans := make(chan proxy.Span, 16)
	go forwardSpansToChannels(proxySpans, channels, spanConverter)

	originalZipkinSpans := make(chan proxy.Span, 1024)
	if opts.DisableSpanCorrection {
		go PipeThroughSpans(originalZipkinSpans, proxySpans)
	} else {
		// do error correction for spans
		go ErrorCorrectSpans(originalZipkinSpans, proxySpans)
	}

	opts.HTTP.Serve(startup_http.Config{
		Name: "dd-zipkin-proxy",

		AdminHandlers: []admin.RouteConfig{
			admin.Describe("A buffer of the previous traces (in openzipkin-format) in the order they were received.",
				admin.WithGenericValue("/spans", buffer.ToSlice)),
		},

		Routing: func(router *httprouter.Router) http.Handler {
			// we emulate the zipkin api
			router.POST("/api/v1/spans", handleSpans(originalZipkinSpans, 1))
			router.POST("/api/v2/spans", handleSpans(originalZipkinSpans, 2))

			return routing(router)
		},
	})
}

func configureEnvironmentVariables(group *flags.Group) {
	for _, gr := range group.Groups() {
		configureEnvironmentVariables(gr)
	}

	expr := regexp.MustCompile("[^A-Z0-9]+")
	for _, op := range group.Options() {

		name := strings.ToUpper(op.LongNameWithNamespace())
		op.EnvDefaultKey = "DDZK_" + expr.ReplaceAllString(name, "_")
	}
}

func forwardSpansToChannels(source <-chan proxy.Span, targets []chan<- proxy.Span, converter SpanConverter) {
	for span := range source {
		converted, err := converter(span)
		if err == nil {
			for _, target := range targets {
				target <- converted
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/levenlabs/go-llog"
	"github.com/mediocregopher/lever"
)

type subInfo map[string]uint64

func (c subInfo) sub(c2 subInfo) subInfo {
	if c == nil {
		return subInfo{}
	} else if c2 == nil {
		c2 = subInfo{}
	}
	out := subInfo{}
	for k := range c {
		// If the new value is less than the old it means we've overflowed the
		// unsigned int and are starting over. In this case we just skip this
		// diff entirely, and will print the next time around
		if c[k] < c2[k] {
			return nil
		}
		out[k] = c[k] - c2[k]
	}
	return out
}

func (c subInfo) toKV() llog.KV {
	kv := llog.KV{}
	for k, v := range c {
		kv[k] = v
	}
	return kv
}

type subInfoSet map[string]subInfo

func (s subInfoSet) sub(s2 subInfoSet) subInfoSet {
	diff := subInfoSet{}
	for k, si := range s {
		si2, ok := s2[k]
		if !ok {
			continue
		}
		siDiff := si.sub(si2)
		// If any of the contained diffs should be skipped, we just skip the
		// whole lot of them, for consistency
		if siDiff == nil {
			return nil
		}
		diff[k] = siDiff
	}
	return diff
}

// a count of zero or less can be given for an unlimited ticker, which is
// effectively like calling time.Tick
func limitedTicker(c int, interval time.Duration) <-chan time.Time {
	t := time.Tick(interval)
	ch := make(chan time.Time)
	go func() {
		ch <- time.Now()
		c-- // subtract one because we just wrote one
		for i := 0; c < 0 || i < c; i++ {
			ch <- <-t
		}
		close(ch)
	}()
	return ch
}

func main() {
	l := lever.New("cricket", nil)
	l.Add(lever.Param{
		Name:        "--limit",
		Description: "Number of times to print each metric. Less than 0 means no limit",
		Default:     "-1",
	})
	l.Add(lever.Param{
		Name:        "--cpu-interval",
		Description: "Interval at which to display cpu stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--mem-interval",
		Description: "Interval at which to display memory stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--disk-interval",
		Description: "Interval at which to display disk stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--disk-io-interval",
		Description: "Interval at which to display disk io stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--net-interval",
		Description: "Interval at which to display net stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--ping-interval",
		Description: "Interval at which to ping hosts. Blank to not ping",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--ping-hosts",
		Description: "Comma-delimited set of hostnames or ips to ping. Blank to not ping",
	})
	l.Add(lever.Param{
		Name:        "--ping-count",
		Description: "Number of pings to send per interval",
		Default:     "3",
	})
	l.Parse()

	var wg sync.WaitGroup
	goTick := func(param string, extraTick bool, fn func(tick <-chan time.Time)) {
		s, _ := l.ParamStr(param)
		if s == "" {
			return
		}
		d, err := time.ParseDuration(s)
		if err != nil {
			llog.Fatal("could not parse duration", llog.KV{"param": param}, llog.ErrKV(err))
		} else if d == 0 {
			return
		}

		// extraTick is used in cases like diskIOLoop and cpuLoop because those
		// are actually printing out a diff every loop, so the first time they
		// run they can't print out anything useful because they don't have
		// anything to diff with.
		limit, _ := l.ParamInt("--limit")
		if extraTick && limit > 0 {
			limit++
		}

		wg.Add(1)
		go func() {
			fn(limitedTicker(limit, d))
			wg.Done()
		}()
	}

	goTick("--cpu-interval", true, cpuLoop)
	goTick("--mem-interval", false, memLoop)
	goTick("--disk-interval", false, diskLoop)
	goTick("--disk-io-interval", true, diskIOLoop)
	goTick("--net-interval", true, netLoop)

	hostsRaw, _ := l.ParamStr("--ping-hosts")
	if hostsRaw != "" {
		hosts := strings.Split(hostsRaw, ",")
		count, _ := l.ParamInt("--ping-count")
		goTick("--ping-interval", false, func(tick <-chan time.Time) {
			pingLoop(tick, hosts, count)
		})
	}

	wg.Wait()
	llog.Flush()
}

func cpuLoop(ch <-chan time.Time) {
	var full, diff subInfo
	var err error
	var doPrint bool
	for range ch {
		if full, diff, err = cpuDiff(full); err != nil {
			llog.Fatal("err calling cpuDiff", llog.ErrKV(err))
		}

		if doPrint && diff != nil {
			llog.Info("cpu stats (diff)", diff.toKV())
		}
		doPrint = true
	}
}

func memLoop(ch <-chan time.Time) {
	for range ch {
		m, err := mem()
		if err != nil {
			llog.Fatal("err calling mem", llog.ErrKV(err))
		}
		llog.Info("mem stats", llog.KV(m))
	}
}

func diskLoop(ch <-chan time.Time) {
	for range ch {
		dd, err := disk()
		if err != nil {
			llog.Fatal("err calling disk", llog.ErrKV(err))
		}
		for _, d := range dd {
			llog.Info("disk usage stats", llog.KV(d))
		}
	}
}

func diskIOLoop(ch <-chan time.Time) {
	// get the device list
	devList := func() []string {
		dd, err := disk()
		if err != nil {
			llog.Fatal("err calling disk", llog.ErrKV(err))
		}

		devs := make([]string, 0, len(dd))
		for _, d := range dd {
			devs = append(devs, d["fs"].(string))
		}
		return devs
	}

	var full, diff subInfoSet
	var err error
	var doPrint bool
	for range ch {
		if full, diff, err = diskDiff(full, devList()); err != nil {
			llog.Fatal("err calling diskDiff", llog.ErrKV(err))
		}

		if doPrint {
			for dev, d := range diff {
				kv := d.toKV()
				kv["fs"] = dev
				llog.Info("disk io stats (diff)", kv)
			}
		}
		doPrint = true
	}
}

func netLoop(ch <-chan time.Time) {
	var full, diff subInfoSet
	var err error
	for range ch {
		if full, diff, err = netDiff(full); err != nil {
			llog.Fatal("err calling netDiff", llog.ErrKV(err))
		}

		for dev, d := range diff {
			kv := d.toKV()
			kv["dev"] = dev
			llog.Info("net stats (diff)", kv)
		}

	}
}

type pingRes struct {
	d   time.Duration
	err error
}

func pingPromise(count int, addr string) chan pingRes {
	ch := make(chan pingRes)
	go func() {
		start := time.Now()
		err := exec.Command("ping", "-c"+strconv.Itoa(count), "-w5", addr).Run()
		if err != nil {
			ch <- pingRes{err: err}
			return
		}
		ch <- pingRes{
			d: time.Since(start) / time.Duration(count),
		}
	}()
	return ch
}

func pingLoop(ch <-chan time.Time, hosts []string, count int) {
	for range ch {
		proms := make([]chan pingRes, len(hosts))
		for i, host := range hosts {
			proms[i] = pingPromise(count, host)
		}

		for i := range proms {
			pr := <-proms[i]
			kv := llog.KV{"host": hosts[i]}
			if pr.err != nil {
				llog.Warn("ping failed", kv, llog.ErrKV(pr.err))
			} else {
				llog.Info("ping result", kv.Set("tookMS", pr.d.Seconds()*1e3))
			}
		}
	}
}
<commit_msg>make ping loop print out the average ping time, in addition to the total<commit_after>package main

import (
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/levenlabs/go-llog"
	"github.com/mediocregopher/lever"
)

type subInfo map[string]uint64

func (c subInfo) sub(c2 subInfo) subInfo {
	if c == nil {
		return subInfo{}
	} else if c2 == nil {
		c2 = subInfo{}
	}
	out := subInfo{}
	for k := range c {
		// If the new value is less than the old it means we've overflowed the
		// unsigned int and are starting over. In this case we just skip this
		// diff entirely, and will print the next time around
		if c[k] < c2[k] {
			return nil
		}
		out[k] = c[k] - c2[k]
	}
	return out
}

func (c subInfo) toKV() llog.KV {
	kv := llog.KV{}
	for k, v := range c {
		kv[k] = v
	}
	return kv
}

type subInfoSet map[string]subInfo

func (s subInfoSet) sub(s2 subInfoSet) subInfoSet {
	diff := subInfoSet{}
	for k, si := range s {
		si2, ok := s2[k]
		if !ok {
			continue
		}
		siDiff := si.sub(si2)
		// If any of the contained diffs should be skipped, we just skip the
		// whole lot of them, for consistency
		if siDiff == nil {
			return nil
		}
		diff[k] = siDiff
	}
	return diff
}

// a count of zero or less can be given for an unlimited ticker, which is
// effectively like calling time.Tick
func limitedTicker(c int, interval time.Duration) <-chan time.Time {
	t := time.Tick(interval)
	ch := make(chan time.Time)
	go func() {
		ch <- time.Now()
		c-- // subtract one because we just wrote one
		for i := 0; c < 0 || i < c; i++ {
			ch <- <-t
		}
		close(ch)
	}()
	return ch
}

func main() {
	l := lever.New("cricket", nil)
	l.Add(lever.Param{
		Name:        "--limit",
		Description: "Number of times to print each metric. Less than 0 means no limit",
		Default:     "-1",
	})
	l.Add(lever.Param{
		Name:        "--cpu-interval",
		Description: "Interval at which to display cpu stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--mem-interval",
		Description: "Interval at which to display memory stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--disk-interval",
		Description: "Interval at which to display disk stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--disk-io-interval",
		Description: "Interval at which to display disk io stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--net-interval",
		Description: "Interval at which to display net stats. Blank to not show",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--ping-interval",
		Description: "Interval at which to ping hosts. Blank to not ping",
		Default:     "1s",
	})
	l.Add(lever.Param{
		Name:        "--ping-hosts",
		Description: "Comma-delimited set of hostnames or ips to ping. Blank to not ping",
	})
	l.Add(lever.Param{
		Name:        "--ping-count",
		Description: "Number of pings to send per interval",
		Default:     "3",
	})
	l.Parse()

	var wg sync.WaitGroup
	goTick := func(param string, extraTick bool, fn func(tick <-chan time.Time)) {
		s, _ := l.ParamStr(param)
		if s == "" {
			return
		}
		d, err := time.ParseDuration(s)
		if err != nil {
			llog.Fatal("could not parse duration", llog.KV{"param": param}, llog.ErrKV(err))
		} else if d == 0 {
			return
		}

		// extraTick is used in cases like diskIOLoop and cpuLoop because those
		// are actually printing out a diff every loop, so the first time they
		// run they can't print out anything useful because they don't have
		// anything to diff with.
		limit, _ := l.ParamInt("--limit")
		if extraTick && limit > 0 {
			limit++
		}

		wg.Add(1)
		go func() {
			fn(limitedTicker(limit, d))
			wg.Done()
		}()
	}

	goTick("--cpu-interval", true, cpuLoop)
	goTick("--mem-interval", false, memLoop)
	goTick("--disk-interval", false, diskLoop)
	goTick("--disk-io-interval", true, diskIOLoop)
	goTick("--net-interval", true, netLoop)

	hostsRaw, _ := l.ParamStr("--ping-hosts")
	if hostsRaw != "" {
		hosts := strings.Split(hostsRaw, ",")
		count, _ := l.ParamInt("--ping-count")
		goTick("--ping-interval", false, func(tick <-chan time.Time) {
			pingLoop(tick, hosts, count)
		})
	}

	wg.Wait()
	llog.Flush()
}

func cpuLoop(ch <-chan time.Time) {
	var full, diff subInfo
	var err error
	var doPrint bool
	for range ch {
		if full, diff, err = cpuDiff(full); err != nil {
			llog.Fatal("err calling cpuDiff", llog.ErrKV(err))
		}

		if doPrint && diff != nil {
			llog.Info("cpu stats (diff)", diff.toKV())
		}
		doPrint = true
	}
}

func memLoop(ch <-chan time.Time) {
	for range ch {
		m, err := mem()
		if err != nil {
			llog.Fatal("err calling mem", llog.ErrKV(err))
		}
		llog.Info("mem stats", llog.KV(m))
	}
}

func diskLoop(ch <-chan time.Time) {
	for range ch {
		dd, err := disk()
		if err != nil {
			llog.Fatal("err calling disk", llog.ErrKV(err))
		}
		for _, d := range dd {
			llog.Info("disk usage stats", llog.KV(d))
		}
	}
}

func diskIOLoop(ch <-chan time.Time) {
	// get the device list
	devList := func() []string {
		dd, err := disk()
		if err != nil {
			llog.Fatal("err calling disk", llog.ErrKV(err))
		}

		devs := make([]string, 0, len(dd))
		for _, d := range dd {
			devs = append(devs, d["fs"].(string))
		}
		return devs
	}

	var full, diff subInfoSet
	var err error
	var doPrint bool
	for range ch {
		if full, diff, err = diskDiff(full, devList()); err != nil {
			llog.Fatal("err calling diskDiff", llog.ErrKV(err))
		}

		if doPrint {
			for dev, d := range diff {
				kv := d.toKV()
				kv["fs"] = dev
				llog.Info("disk io stats (diff)", kv)
			}
		}
		doPrint = true
	}
}

func netLoop(ch <-chan time.Time) {
	var full, diff subInfoSet
	var err error
	for range ch {
		if full, diff, err = netDiff(full); err != nil {
			llog.Fatal("err calling netDiff", llog.ErrKV(err))
		}

		for dev, d := range diff {
			kv := d.toKV()
			kv["dev"] = dev
			llog.Info("net stats (diff)", kv)
		}

	}
}

type pingRes struct {
	d   time.Duration
	err error
}

func pingPromise(count int, addr string) chan pingRes {
	ch := make(chan pingRes, 1)
	go func() {
		start := time.Now()
		err := exec.Command("ping", "-c"+strconv.Itoa(count), "-w5", addr).Run()
		if err != nil {
			ch <- pingRes{err: err}
			return
		}
		ch <- pingRes{
			d: time.Since(start) / time.Duration(count),
		}
	}()
	return ch
}

func pingLoop(ch <-chan time.Time, hosts []string, count int) {
	for range ch {
		proms := make([]chan pingRes, len(hosts))
		for i, host := range hosts {
			proms[i] = pingPromise(count, host)
		}

		for i := range proms {
			pr := <-proms[i]
			kv := llog.KV{"host": hosts[i]}
			if pr.err != nil {
				llog.Warn("ping failed", kv, llog.ErrKV(pr.err))
			} else {
				took := int64(pr.d / time.Millisecond)
				tkv := llog.KV{
					"tookMS":    took,
					"tookMSAvg": took / int64(count),
				}
				llog.Info("ping result", kv, tkv)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The Glamor authors.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"net/mail"
	"net/smtp"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/jessevdk/go-flags"
)

const version = "1.0"

const (
	returnOk = iota
	returnHelp
	returnSignal
)

const (
	statusUp = iota
	statusDown
)

type host struct {
	name   string
	status byte
	down   uint64
	up     uint64
}

var opts struct {
	Host                      []string `long:"host" description:"A host to ping" required:"true"`
	Interval                  uint64   `long:"interval" default:"60" description:"Ping interval in seconds"`
	MaxDown                   uint64   `long:"max-down" default:"5" description:"How many pings must fail (in a row) before the host status is down"`
	MaxUp                     uint64   `long:"max-up" default:"20" description:"How many pings must succeed (in a row) before the host status is up again"`
	SMTP                      string   `long:"smtp" description:"The SMTP server + port for sending report mails"`
	SMTPFrom                  string   `long:"smtp-from" description:"From-mail address"`
	SMTPSkipCertificateVerify bool     `long:"smtp-skip-certificate-verify" description:"Do not verify the SMTP certificate"`
	SMTPTLS                   bool     `long:"smtp-tls" description:"Use TLS for the SMTP connection"`
	SMTPTo                    []string `long:"smtp-to" description:"A To-mail address"`
	Verbose                   bool     `long:"verbose" description:"Do verbose output"`
	Version                   bool     `long:"version" description:"Print the version of this program"`
}

func checkArguments() {
	p := flags.NewNamedParser("glamor", flags.HelpFlag)
	p.ShortDescription = "A daemon for monitoring hosts via ICMP echo request (ping)"
	p.AddGroup("Glamor arguments", "", &opts)

	if _, err := p.ParseArgs(os.Args); err != nil {
		if opts.Version {
			fmt.Printf("Glamor v%s\n", version)

			os.Exit(returnOk)
		}

		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			panic(err)
		} else {
			p.WriteHelp(os.Stdout)

			os.Exit(returnHelp)
		}
	}

	if opts.Interval < 1 {
		panic("interval must be at least 1")
	} else if opts.MaxDown < 1 {
		panic("max-down must be at least 1")
	} else if opts.MaxUp < 1 {
		panic("max-up must be at least 1")
	}

	if _, err := mail.ParseAddress(opts.SMTPFrom); opts.SMTPFrom != "" && err != nil {
		panic("smtp-from is not a valid mail address")
	}

	for _, m := range opts.SMTPTo {
		if _, err := mail.ParseAddress(m); m == "" || err != nil {
			panic(fmt.Sprintf("smtp-to \"%s\" is not a valid mail address", m))
		}
	}
}

func checkHost(wg *sync.WaitGroup, host *host) {
	defer wg.Done()

	var cmd = exec.Command("ping", "-w", "1", "-c", "1", host.name)

	out, err := cmd.CombinedOutput()
	if err != nil {
		v("ping to %s failed: %v", host.name, err)
	}

	if strings.Contains(string(out), "1 received") {
		if host.status == statusDown {
			host.up++

			if host.up >= opts.MaxUp {
				host.down = 0
				host.up = 0

				host.status = statusUp

				subject := fmt.Sprintf("host %s is up", host.name)

				v(subject)

				if opts.SMTP != "" {
					if err := sendMail(subject, host.name+" is reachable again via ping"); err != nil {
						v("Cannot send mail: %v", err)
					}
				}
			}
		} else {
			host.down = 0
		}
	} else {
		host.down++

		if host.status == statusUp {
			if host.down >= opts.MaxDown {
				host.down = 0
				host.up = 0

				host.status = statusDown

				subject := fmt.Sprintf("host %s is down", host.name)

				v(subject)

				if opts.SMTP != "" {
					if err := sendMail(subject, host.name+" is not reachable via ping"); err != nil {
						v("Cannot send mail: %v", err)
					}
				}
			}
		} else {
			host.up = 0
		}
	}
}

func sendMail(subject string, message string) error {
	if opts.SMTP == "" {
		return fmt.Errorf("no SMTP server defined")
	}

	c, err := smtp.Dial(opts.SMTP)
	if err != nil {
		return fmt.Errorf("cannot open SMTP connection: %v", err)
	}

	if opts.SMTPTLS {
		if err := c.StartTLS(&tls.Config{InsecureSkipVerify: opts.SMTPSkipCertificateVerify}); err != nil {
			return fmt.Errorf("cannot start SMTP TLS: %v", err)
		}
	}

	c.Mail(opts.SMTPFrom)
	for _, m := range opts.SMTPTo {
		c.Rcpt(m)
	}

	wc, err := c.Data()
	if err != nil {
		return fmt.Errorf("cannot open Data writer: %v", err)
	}

	defer wc.Close()

	buf := bytes.NewBufferString(`Return-path: <` + opts.SMTPFrom + `>
From: ` + opts.SMTPFrom + `
To: ` + strings.Join(opts.SMTPTo, ", ") + `
Subject: ` + subject + `
Content-Transfer-Encoding: 7Bit
Content-Type: text/plain; charset="us-ascii"

` + message + `
`)

	if _, err = buf.WriteTo(wc); err != nil {
		return fmt.Errorf("cannot write mail body: %v", err)
	}

	return nil
}

func v(format string, a ...interface{}) {
	if opts.Verbose {
		fmt.Fprintf(os.Stderr, format+"\n", a...)
	}
}

func main() {
	checkArguments()

	sig := make(chan os.Signal)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	go func() {
		s := <-sig
		v("caught signal \"%v\", will exit now", s)

		os.Exit(returnSignal)
	}()

	hosts := make([]host, len(opts.Host))

	for i, name := range opts.Host {
		hosts[i].name = name
		hosts[i].status = statusUp
	}

	var wg sync.WaitGroup

	for {
		for i := range hosts {
			wg.Add(1)

			go checkHost(&wg, &hosts[i])
		}

		wg.Wait()

		time.Sleep(time.Duration(opts.Interval) * time.Second)
	}
}
<commit_msg>proclaim version 1.1<commit_after>// Copyright 2013-2014 The Glamor authors.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"net/mail"
	"net/smtp"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/jessevdk/go-flags"
)

const version = "1.1"

const (
	returnOk = iota
	returnHelp
	returnSignal
)

const (
	statusUp = iota
	statusDown
)

type host struct {
	name   string
	status byte
	down   uint64
	up     uint64
}

var opts struct {
	Host                      []string `long:"host" description:"A host to ping" required:"true"`
	Interval                  uint64   `long:"interval" default:"60" description:"Ping interval in seconds"`
	MaxDown                   uint64   `long:"max-down" default:"5" description:"How many pings must fail (in a row) before the host status is down"`
	MaxUp                     uint64   `long:"max-up" default:"20" description:"How many pings must succeed (in a row) before the host status is up again"`
	SMTP                      string   `long:"smtp" description:"The SMTP server + port for sending report mails"`
	SMTPFrom                  string   `long:"smtp-from" description:"From-mail address"`
	SMTPSkipCertificateVerify bool     `long:"smtp-skip-certificate-verify" description:"Do not verify the SMTP certificate"`
	SMTPTLS                   bool     `long:"smtp-tls" description:"Use TLS for the SMTP connection"`
	SMTPTo                    []string `long:"smtp-to" description:"A To-mail address"`
	Verbose                   bool     `long:"verbose" description:"Do verbose output"`
	Version                   bool     `long:"version" description:"Print the version of this program"`
}

func checkArguments() {
	p := flags.NewNamedParser("glamor", flags.HelpFlag)
	p.ShortDescription = "A daemon for monitoring hosts via ICMP echo request (ping)"
	p.AddGroup("Glamor arguments", "", &opts)

	if _, err := p.ParseArgs(os.Args); err != nil {
		if opts.Version {
			fmt.Printf("Glamor v%s\n", version)

			os.Exit(returnOk)
		}

		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			panic(err)
		} else {
			p.WriteHelp(os.Stdout)

			os.Exit(returnHelp)
		}
	}

	if opts.Interval < 1 {
		panic("interval must be at least 1")
	} else if opts.MaxDown < 1 {
		panic("max-down must be at least 1")
	} else if opts.MaxUp < 1 {
		panic("max-up must be at least 1")
	}

	if _, err := mail.ParseAddress(opts.SMTPFrom); opts.SMTPFrom != "" && err != nil {
		panic("smtp-from is not a valid mail address")
	}

	for _, m := range opts.SMTPTo {
		if _, err := mail.ParseAddress(m); m == "" || err != nil {
			panic(fmt.Sprintf("smtp-to \"%s\" is not a valid mail address", m))
		}
	}
}

func checkHost(wg *sync.WaitGroup, host *host) {
	defer wg.Done()

	var cmd = exec.Command("ping", "-w", "1", "-c", "1", host.name)

	out, err := cmd.CombinedOutput()
	if err != nil {
		v("ping to %s failed: %v", host.name, err)
	}

	if strings.Contains(string(out), "1 received") {
		if host.status == statusDown {
			host.up++

			if host.up >= opts.MaxUp {
				host.down = 0
				host.up = 0

				host.status = statusUp

				subject := fmt.Sprintf("host %s is up", host.name)

				v(subject)

				if opts.SMTP != "" {
					if err := sendMail(subject, host.name+" is reachable again via ping"); err != nil {
						v("Cannot send mail: %v", err)
					}
				}
			}
		} else {
			host.down = 0
		}
	} else {
		host.down++

		if host.status == statusUp {
			if host.down >= opts.MaxDown {
				host.down = 0
				host.up = 0

				host.status = statusDown

				subject := fmt.Sprintf("host %s is down", host.name)

				v(subject)

				if opts.SMTP != "" {
					if err := sendMail(subject, host.name+" is not reachable via ping"); err != nil {
						v("Cannot send mail: %v", err)
					}
				}
			}
		} else {
			host.up = 0
		}
	}
}

func sendMail(subject string, message string) error {
	if opts.SMTP == "" {
		return fmt.Errorf("no SMTP server defined")
	}

	c, err := smtp.Dial(opts.SMTP)
	if err != nil {
		return fmt.Errorf("cannot open SMTP connection: %v", err)
	}

	if opts.SMTPTLS {
		if err := c.StartTLS(&tls.Config{InsecureSkipVerify: opts.SMTPSkipCertificateVerify}); err != nil {
			return fmt.Errorf("cannot start SMTP TLS: %v", err)
		}
	}

	c.Mail(opts.SMTPFrom)
	for _, m := range opts.SMTPTo {
		c.Rcpt(m)
	}

	wc, err := c.Data()
	if err != nil {
		return fmt.Errorf("cannot open Data writer: %v", err)
	}

	defer wc.Close()

	buf := bytes.NewBufferString(`Return-path: <` + opts.SMTPFrom + `>
From: ` + opts.SMTPFrom + `
To: ` + strings.Join(opts.SMTPTo, ", ") + `
Subject: ` + subject + `
Content-Transfer-Encoding: 7Bit
Content-Type: text/plain; charset="us-ascii"

` + message + `
`)

	if _, err = buf.WriteTo(wc); err != nil {
		return fmt.Errorf("cannot write mail body: %v", err)
	}

	return nil
}

func v(format string, a ...interface{}) {
	if opts.Verbose {
		fmt.Fprintf(os.Stderr, format+"\n", a...)
	}
}

func main() {
	checkArguments()

	sig := make(chan os.Signal)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	go func() {
		s := <-sig
		v("caught signal \"%v\", will exit now", s)

		os.Exit(returnSignal)
	}()

	hosts := make([]host, len(opts.Host))

	for i, name := range opts.Host {
		hosts[i].name = name
		hosts[i].status = statusUp
	}

	var wg sync.WaitGroup

	for {
		for i := range hosts {
			wg.Add(1)

			go checkHost(&wg, &hosts[i])
		}

		wg.Wait()

		time.Sleep(time.Duration(opts.Interval) * time.Second)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"github.com/gorilla/websocket"
	"github.com/op/go-logging"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

type slackEvent struct {
	Type      string `json:"type"`
	User      string `json:"user"`
	Text      string `json:"text"`
	Timestamp string `json:"ts"`
}

type restart struct {
	Reason string
}

type client struct {
	conn net.Conn
	name string
}

type connToUserMsg struct {
	UserID  string
	Message string
}

type connToAllUsersMsg struct {
	Message string
}

type connReplyMsg struct {
	Message string
}

type user struct {
	ID   string
	Recv chan slackEvent
	Send chan connReplyMsg
}

type connRegister struct {
	Name string
	Conn net.Conn
}

type connUnregister struct {
	Name string
}

type connMessage struct {
	Type   string
	Msg    string
	UserID string
}

type msg struct {
	Type string `json:"type"`
	Data string `json:"data"`
	User string `json:"user,omitempty"`
}

type config struct {
	Token string   `json:"token"`
	Users []string `json:"users"`
	Port  int      `json:"port"`
	Host  string   `json:"host"`
}

type rtmResponse struct {
	URL string `json:"url"`
}

type ctx struct {
	Restarts    chan restart
	SlackEvents chan string
	UserEvents  chan interface{}
	Connections chan interface{}
}

var log = logging.MustGetLogger("main-logger")

func readConfig() config {
	config := config{
		Token: "",
		Users: []string{},
		Port:  9191,
		Host:  "localhost",
	}
	file, errReadFile := ioutil.ReadFile("config.json")
	if errReadFile != nil {
		log.Warningf("Config file error: %s; using default values.", errReadFile)
		return config
	}
	errUnmarshal := json.Unmarshal(file, &config)
	if errUnmarshal != nil {
		log.Warningf("Config file error: %s; using default values.", errUnmarshal)
		return config
	}
	return config
}

func startRtm(origin string) rtmResponse {
	resp, err := http.Get(origin)
	if err != nil {
		return rtmResponse{}
	}
	defer resp.Body.Close()
	var data rtmResponse
	json.NewDecoder(resp.Body).Decode(&data)
	return data
}

func connectWs(url string, origin string) (*websocket.Conn, error) {
	c, _, err := websocket.DefaultDialer.Dial(url, nil)
	return c, err
}

func eventsProducer(ws *websocket.Conn, ctx ctx) {
	ticker := time.NewTicker(10 * time.Second)
	stopPinger := make(chan struct{})
	ws.SetReadDeadline(time.Now().Add(15 * time.Second))
	ws.SetPongHandler(func(string) error {
		ws.SetReadDeadline(time.Now().Add(15 * time.Second))
		return nil
	})
	go func() {
		for {
			select {
			case <-ticker.C:
				if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
					log.Errorf("Error while writing ping: %v", err)
				}
			case <-stopPinger:
				ticker.Stop()
				return
			}
		}
	}()
	for {
		_, msg, err := ws.ReadMessage()
		if err != nil {
			log.Errorf("Error while reading message: %v", err)
			close(stopPinger)
			ws.Close()
			ctx.Restarts <- restart{Reason: "slack read message error"}
			go func() {
				ctx.Connections <- connMessage{Type: "restart", Msg: "slack read message error"}
			}()
			break
		}
		ctx.SlackEvents <- string(msg[:])
	}
}

func parseSlackEvent(jsonEvent string) (slackEvent, error) {
	var event slackEvent
	err := json.Unmarshal([]byte(jsonEvent), &event)
	if err != nil {
		return event, errors.New("Invalid event")
	}
	return event, nil
}

func dispatchSlackEvents(ctx ctx) {
	for {
		jsonEvent := <-ctx.SlackEvents
		event, _ := parseSlackEvent(jsonEvent)
		go func() {
			ctx.UserEvents <- event
		}()
	}
}

func runUsers(ctx ctx, userIds []string) {
	users := make(map[string]user)
	for _, userID := range userIds {
		u := user{
			ID:   userID,
			Recv: make(chan slackEvent),
			Send: make(chan connReplyMsg),
		}
		users[userID] = u
		go func() {
			for {
				m := <-u.Recv
				if m.Type == "message" {
					go func() {
						ctx.Connections <- connMessage{
							Type:   "msg",
							Msg:    m.Text,
							UserID: u.ID,
						}
					}()
				}
				log.Infof("Dispatching event: %#v, for user: %#v", m, u)
			}
		}()
		go func() {
			for {
				m := <-u.Send
				log.Infof("Message for the user: %#v, for user: %s", m, u.ID)
			}
		}()
	}
	for {
		m := <-ctx.UserEvents
		switch v := m.(type) {
		case slackEvent:
			u := users[v.User]
			if u != (user{}) {
				go func() {
					u.Recv <- v
				}()
			}
		case connToUserMsg:
			u := users[v.UserID]
			if u != (user{}) {
				go func() {
					u.Send <- connReplyMsg{Message: v.Message}
				}()
			}
		case connToAllUsersMsg:
			for _, u := range users {
				go func(u user) {
					u.Send <- connReplyMsg{Message: v.Message}
				}(u)
			}
		default:
			log.Warningf("User event unknown: %#v", v)
		}
	}
}

func slackHandler(ctx ctx, token string) {
	origin := "https://slack.com/api/rtm.start?token=" + token
	go func() {
		ctx.Restarts <- restart{Reason: "initial start"}
	}()
	for {
		msg := <-ctx.Restarts
		log.Infof("Restarting: %#v; no of goroutines: %d", msg, runtime.NumGoroutine())
		rtm := startRtm(origin)
		log.Debugf("RTM url: %s", rtm.URL)
		ws, err := connectWs(rtm.URL, origin)
		if err != nil {
			errorMsg := "Error trying to connect to WS"
			log.Errorf("%s: %v", errorMsg, err)
			go func() {
				ctx.Restarts <- restart{Reason: errorMsg}
			}()
			go func() {
				ctx.Connections <- connMessage{Type: "restart", Msg: errorMsg}
			}()
		} else {
			go eventsProducer(ws, ctx)
		}
		time.Sleep(10 * time.Second)
	}
}

func msgToJSONMsg(msg msg) string {
	json, _ := json.Marshal(msg)
	return (string(json) + "\n")
}

func jsonToMsg(jsonString string) (msg, error) {
	var msg msg
	decodeErr := json.NewDecoder(strings.NewReader(jsonString)).Decode(&msg)
	return msg, decodeErr
}

func runClient(ctx ctx, conn net.Conn) {
	defer func() {
		log.Infof("No of goroutines: %d", runtime.NumGoroutine())
		log.Info("Closing client")
	}()
	reader := bufio.NewReader(conn)
	registered := connRegister{}
	for {
		rawMsg, err := reader.ReadString('\n')
		if err != nil {
			conn.Close()
			if registered != (connRegister{}) {
				go func() {
					ctx.Connections <- connUnregister{Name: registered.Name}
				}()
			}
			return
		}
		reply := msg{Type: "ok", Data: "ok"}
		message := strings.TrimSpace(rawMsg)
		decodedMsg, decodeErr := jsonToMsg(message)
		if decodeErr != nil {
			log.Warningf("Error while decoding message: %s; invalid message format: %s", decodeErr, message)
			reply = msg{Type: "error", Data: "invalid format"}
		} else {
			switch decodedMsg.Type {
			case "register":
				registered = connRegister{
					Name: decodedMsg.Data,
					Conn: conn,
				}
				go func() {
					ctx.Connections <- registered
				}()
			case "msg":
				go func() {
					if decodedMsg.User == "" {
						ctx.UserEvents <- connToAllUsersMsg{Message: decodedMsg.Data}
					} else {
						ctx.UserEvents <- connToUserMsg{
							UserID:  decodedMsg.User,
							Message: decodedMsg.Data,
						}
					}
				}()
			default:
				reply = msg{Type: "error", Data: "message unknown"}
			}
		}
		conn.Write([]byte(msgToJSONMsg(reply)))
		log.Debugf("message %#v", decodedMsg)
	}
}

func tcpHandler(host string, port int, ctx ctx) {
	listener, err := net.Listen("tcp", host+":"+strconv.Itoa(port))
	if err != nil {
		log.Fatalf("Error starting TCP server on host %s, port %d", host, port)
	}
	log.Infof("Started TCP listener on %s:%d", host, port)
	defer listener.Close()
	for {
		conn, _ := listener.Accept()
		go runClient(ctx, conn)
	}
}

func connectionsRegistrar(ctx ctx) {
	connections := make(map[string]net.Conn)
	for {
		connCmd := <-ctx.Connections
		switch v := connCmd.(type) {
		case connRegister:
			connections[v.Name] = v.Conn
		case connUnregister:
			delete(connections, v.Name)
		case connMessage:
			for key, conn := range connections {
				log.Infof("Sent message to connection: %s", key)
				txt := msgToJSONMsg(msg{
					Type: v.Type,
					Data: v.Msg,
					User: v.UserID,
				})
				go func() {
					conn.Write([]byte(txt))
				}()
			}
		default:
			log.Infof("Unknown connection message type: %v", v)
		}
	}
}

func main() {
	config := readConfig()
	token := config.Token
	users := config.Users
	tcpport := config.Port
	tcphost := config.Host
	var wg sync.WaitGroup
	var format = logging.MustStringFormatter(
		`%{color}%{time:2006-01-02T15:04:05.000} %{shortfunc} >>> %{level} %{id:03x} %{message}%{color:reset}`,
	)
	loggingBackend := logging.NewLogBackend(os.Stderr, "", 0)
	backend2Formatter := logging.NewBackendFormatter(loggingBackend, format)
	loggingBackendLeveled := logging.AddModuleLevel(loggingBackend)
	loggingBackendLeveled.SetLevel(logging.DEBUG, "")
	logging.SetBackend(backend2Formatter)
	ctx := ctx{
		Restarts:    make(chan restart),
		SlackEvents: make(chan string),
		UserEvents:  make(chan interface{}),
		Connections: make(chan interface{}),
	}
	wg.Add(5)
	go func() {
		defer wg.Done()
		dispatchSlackEvents(ctx)
	}()
	go func() {
		defer wg.Done()
		runUsers(ctx, users)
	}()
	go func() {
		defer wg.Done()
		slackHandler(ctx, token)
	}()
	go func() {
		defer wg.Done()
		tcpHandler(tcphost, tcpport, ctx)
	}()
	go func() {
		defer wg.Done()
		connectionsRegistrar(ctx)
	}()
	wg.Wait()
}
<commit_msg>Fix possible race issue.<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"github.com/gorilla/websocket"
	"github.com/op/go-logging"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

type slackEvent struct {
	Type      string `json:"type"`
	User      string `json:"user"`
	Text      string `json:"text"`
	Timestamp string `json:"ts"`
}

type restart struct {
	Reason string
}

type client struct {
	conn net.Conn
	name string
}

type connToUserMsg struct {
	UserID  string
	Message string
}

type connToAllUsersMsg struct {
	Message string
}

type connReplyMsg struct {
	Message string
}

type user struct {
	ID   string
	Recv chan slackEvent
	Send chan connReplyMsg
}

type connRegister struct {
	Name string
	Conn net.Conn
}

type connUnregister struct {
	Name string
}

type connMessage struct {
	Type   string
	Msg    string
	UserID string
}

type msg struct {
	Type string `json:"type"`
	Data string `json:"data"`
	User string `json:"user,omitempty"`
}

type config struct {
	Token string   `json:"token"`
	Users []string `json:"users"`
	Port  int      `json:"port"`
	Host  string   `json:"host"`
}

type rtmResponse struct {
	URL string `json:"url"`
}

type ctx struct {
	Restarts    chan restart
	SlackEvents chan string
	UserEvents  chan interface{}
	Connections chan interface{}
}

var log = logging.MustGetLogger("main-logger")

func readConfig() config {
	config := config{
		Token: "",
		Users: []string{},
		Port:  9191,
		Host:  "localhost",
	}
	file, errReadFile := ioutil.ReadFile("config.json")
	if errReadFile != nil {
		log.Warningf("Config file error: %s; using default values.", errReadFile)
		return config
	}
	errUnmarshal := json.Unmarshal(file, &config)
	if errUnmarshal != nil {
		log.Warningf("Config file error: %s; using default values.", errUnmarshal)
		return config
	}
	return config
}

func startRtm(origin string) rtmResponse {
	resp, err := http.Get(origin)
	if err != nil {
		return rtmResponse{}
	}
	defer resp.Body.Close()
	var data rtmResponse
	json.NewDecoder(resp.Body).Decode(&data)
	return data
}

func connectWs(url string, origin string) (*websocket.Conn, error) {
	c, _, err := websocket.DefaultDialer.Dial(url, nil)
	return c, err
}

func eventsProducer(ws *websocket.Conn, ctx ctx) {
	ticker := time.NewTicker(10 * time.Second)
	stopPinger := make(chan struct{})
	ws.SetReadDeadline(time.Now().Add(15 * time.Second))
	ws.SetPongHandler(func(string) error {
		ws.SetReadDeadline(time.Now().Add(15 * time.Second))
		return nil
	})
	go func() {
		for {
			select {
			case <-ticker.C:
				if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
					log.Errorf("Error while writing ping: %v", err)
				}
			case <-stopPinger:
				ticker.Stop()
				return
			}
		}
	}()
	for {
		_, msg, err := ws.ReadMessage()
		if err != nil {
			log.Errorf("Error while reading message: %v", err)
			close(stopPinger)
			ws.Close()
			ctx.Restarts <- restart{Reason: "slack read message error"}
			go func() {
				ctx.Connections <- connMessage{Type: "restart", Msg: "slack read message error"}
			}()
			break
		}
		ctx.SlackEvents <- string(msg[:])
	}
}

func parseSlackEvent(jsonEvent string) (slackEvent, error) {
	var event slackEvent
	err := json.Unmarshal([]byte(jsonEvent), &event)
	if err != nil {
		return event, errors.New("Invalid event")
	}
	return event, nil
}

func dispatchSlackEvents(ctx ctx) {
	for {
		jsonEvent := <-ctx.SlackEvents
		event, _ := parseSlackEvent(jsonEvent)
		go func() {
			ctx.UserEvents <- event
		}()
	}
}

func userReceiveMessages(ctx ctx, u user) {
	for {
		m := <-u.Recv
		if m.Type == "message" {
			go func() {
				ctx.Connections <- connMessage{
					Type:   "msg",
					Msg:    m.Text,
					UserID: u.ID,
				}
			}()
		}
		log.Infof("Dispatching event: %#v, for user: %#v", m, u)
	}
}

func userSendMessages(ctx ctx, u user) {
	for {
		m := <-u.Send
		log.Infof("Message for the user: %#v, for user: %s", m, u.ID)
	}
}

func usersRegistrar(ctx ctx, userIds []string) {
	users := make(map[string]user)
	for _, userID := range userIds {
		u := user{
			ID:   userID,
			Recv: make(chan slackEvent),
			Send: make(chan connReplyMsg),
		}
		users[userID] = u
		go userReceiveMessages(ctx, u)
		go userSendMessages(ctx, u)
	}
	for {
		m := <-ctx.UserEvents
		switch v := m.(type) {
		case slackEvent:
			u := users[v.User]
			if u != (user{}) {
				go func() {
					u.Recv <- v
				}()
			}
		case connToUserMsg:
			u := users[v.UserID]
			if u != (user{}) {
				go func() {
					u.Send <- connReplyMsg{Message: v.Message}
				}()
			}
		case connToAllUsersMsg:
			for _, u := range users {
				go func(u user) {
					u.Send <- connReplyMsg{Message: v.Message}
				}(u)
			}
		default:
			log.Warningf("User event unknown: %#v", v)
		}
	}
}

func slackHandler(ctx ctx, token string) {
	origin := "https://slack.com/api/rtm.start?token=" + token
	go func() {
		ctx.Restarts <- restart{Reason: "initial start"}
	}()
	for {
		msg := <-ctx.Restarts
		log.Infof("Restarting: %#v; no of goroutines: %d", msg, runtime.NumGoroutine())
		rtm := startRtm(origin)
		log.Debugf("RTM url: %s", rtm.URL)
		ws, err := connectWs(rtm.URL, origin)
		if err != nil {
			errorMsg := "Error trying to connect to WS"
			log.Errorf("%s: %v", errorMsg, err)
			go func() {
				ctx.Restarts <- restart{Reason: errorMsg}
			}()
			go func() {
				ctx.Connections <- connMessage{Type: "restart", Msg: errorMsg}
			}()
		} else {
			go eventsProducer(ws, ctx)
		}
		time.Sleep(10 * time.Second)
	}
}

func msgToJSONMsg(msg msg) string {
	json, _ := json.Marshal(msg)
	return (string(json) + "\n")
}

func jsonToMsg(jsonString string) (msg, error) {
	var msg msg
	decodeErr := json.NewDecoder(strings.NewReader(jsonString)).Decode(&msg)
	return msg, decodeErr
}

func runClient(ctx ctx, conn net.Conn) {
	defer func() {
		log.Infof("No of goroutines: %d", runtime.NumGoroutine())
		log.Info("Closing client")
	}()
	reader := bufio.NewReader(conn)
	registered := connRegister{}
	for {
		rawMsg, err := reader.ReadString('\n')
		if err != nil {
			conn.Close()
			if registered != (connRegister{}) {
				go func() {
					ctx.Connections <- connUnregister{Name: registered.Name}
				}()
			}
			return
		}
		reply := msg{Type: "ok", Data: "ok"}
		message := strings.TrimSpace(rawMsg)
		decodedMsg, decodeErr := jsonToMsg(message)
		if decodeErr != nil {
			log.Warningf("Error while decoding message: %s; invalid message format: %s", decodeErr, message)
			reply = msg{Type: "error", Data: "invalid format"}
		} else {
			switch decodedMsg.Type {
			case "register":
				registered = connRegister{
					Name: decodedMsg.Data,
					Conn: conn,
				}
				go func() {
					ctx.Connections <- registered
				}()
			case "msg":
				go func() {
					if decodedMsg.User == "" {
						ctx.UserEvents <- connToAllUsersMsg{Message: decodedMsg.Data}
					} else {
						ctx.UserEvents <- connToUserMsg{
							UserID:  decodedMsg.User,
							Message: decodedMsg.Data,
						}
					}
				}()
			default:
				reply = msg{Type: "error", Data: "message unknown"}
			}
		}
		conn.Write([]byte(msgToJSONMsg(reply)))
		log.Debugf("message %#v", decodedMsg)
	}
}

func tcpHandler(host string, port int, ctx ctx) {
	listener, err := net.Listen("tcp", host+":"+strconv.Itoa(port))
	if err != nil {
		log.Fatalf("Error starting TCP server on host %s, port %d", host, port)
	}
	log.Infof("Started TCP listener on %s:%d", host, port)
	defer listener.Close()
	for {
		conn, _ := listener.Accept()
		go runClient(ctx, conn)
	}
}

func connectionsRegistrar(ctx ctx) {
	connections := make(map[string]net.Conn)
	for {
		connCmd := <-ctx.Connections
		switch v := connCmd.(type) {
		case connRegister:
			connections[v.Name] = v.Conn
		case connUnregister:
			delete(connections, v.Name)
		case connMessage:
			for key, conn := range connections {
				log.Infof("Sent message to connection: %s", key)
				txt := msgToJSONMsg(msg{
					Type: v.Type,
					Data: v.Msg,
					User: v.UserID,
				})
				go func(conn net.Conn) {
					conn.Write([]byte(txt))
				}(conn)
			}
		default:
			log.Infof("Unknown connection message type: %v", v)
		}
	}
}

func main() {
	config := readConfig()
	token := config.Token
	users := config.Users
	tcpport := config.Port
	tcphost := config.Host
	var wg sync.WaitGroup
	var format = logging.MustStringFormatter(
		`%{color}%{time:2006-01-02T15:04:05.000} %{shortfunc} >>> %{level} %{id:03x} %{message}%{color:reset}`,
	)
	loggingBackend := logging.NewLogBackend(os.Stderr, "", 0)
	backend2Formatter := logging.NewBackendFormatter(loggingBackend, format)
	loggingBackendLeveled := logging.AddModuleLevel(loggingBackend)
	loggingBackendLeveled.SetLevel(logging.DEBUG, "")
	logging.SetBackend(backend2Formatter)
	ctx := ctx{
		Restarts:    make(chan restart),
		SlackEvents: make(chan string),
		UserEvents:  make(chan interface{}),
		Connections: make(chan interface{}),
	}
	wg.Add(5)
	go func() {
		defer wg.Done()
		usersRegistrar(ctx, users)
	}()
	go func() {
		defer wg.Done()
		slackHandler(ctx, token)
	}()
	go func() {
		defer wg.Done()
		dispatchSlackEvents(ctx)
	}()
	go func() {
		defer wg.Done()
		tcpHandler(tcphost, tcpport, ctx)
	}()
	go func() {
		defer wg.Done()
		connectionsRegistrar(ctx)
	}()
	wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"os"
	"regexp"
	"strconv"
	"time"
)

const (
	DefaultNumOfResultToShow = 3
)

type Time struct {
	hour, minute int
}

func (time *Time) toString() string {
	if time.minute < 10 {
		return fmt.Sprintf("%d:0%d ", time.hour, time.minute)
	} else {
		return fmt.Sprintf("%d:%d ", time.hour, time.minute)
	}
}

func departureTime(departure string) (int, int) {
	ret, _ := regexp.MatchString("^[0-9]{1,2}:[0-9]{1,2}$", departure)
	var hour int
	var minute int
	if ret {
		re := regexp.MustCompile("^([0-9]{1,2}):([0-9]{1,2})$")
		bs := []byte(departure)
		group := re.FindSubmatch(bs)
		h, _ := strconv.Atoi(string(group[1]))
		m, _ := strconv.Atoi(string(group[2]))
		if h >= 0 && h < 24 && m >= 0 && m < 60 {
			hour = h
			minute = m
		} else {
			now := time.Now()
			hour = now.Hour()
			minute = now.Minute()
		}
	} else {
		now := time.Now()
		hour = now.Hour()
		minute = now.Minute()
	}
	return hour, minute
}

func createTimetable() []Time {
	var timetable = make([]Time, 0)
	doc, _ := goquery.NewDocument("http://www.jreast-timetable.jp/1509/timetable/tt0413/0413020.html")
	doc.Find(".timetable .result_03").Each(func(_ int, s *goquery.Selection) {
		s.Find("tbody tr").Each(func(_ int, s *goquery.Selection) {
			key, err := strconv.Atoi(s.Find("td:nth-child(1)").Text())
			if err == nil {
				s.Find("td:nth-child(2) div.timetable_time[data-dest='府']").Each(func(_ int, s *goquery.Selection) {
					value, _ := strconv.Atoi(s.Find(".minute").Text())
					timetable = append(timetable, Time{key, value})
				})
			}
		})
	})
	return timetable
}

func printTimes(times []string) {
	for _, v := range times {
		fmt.Println(v)
	}
}

func main() {
	var (
		departure   string
		numOfResult int
		isLast      bool
	)
	flag.StringVar(&departure, "t", "", "specify departure time.")
	flag.BoolVar(&isLast, "l", false, "show last bus of the day.")
	flag.IntVar(&numOfResult, "n", DefaultNumOfResultToShow, "specify amount of result.")
	flag.Parse()

	if numOfResult < 0 {
		fmt.Fprintf(os.Stderr, "parameter for -n must be greater than 0.\n")
		os.Exit(2)
	}

	hour, minute := departureTime(departure)
	timetable := createTimetable()

	if isLast {
		result := []string{timetable[len(timetable)-1].toString()}
		printTimes(result)
		return
	}

	result := make([]string, 0, numOfResult)
	for i := 0; i < len(timetable); i++ {
		v := timetable[i]
		if v.minute > minute && v.hour >= hour {
			timeStr := v.toString()
			result = append(result, timeStr)
			if len(result) >= numOfResult {
				break
			}
		}
	}
	printTimes(result)
}
<commit_msg>support Saturday and Sunday.<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"os"
	"regexp"
	"strconv"
	"time"
)

const (
	DefaultNumOfResultToShow = 3
)

type Time struct {
	hour, minute int
}

func (time *Time) toString() string {
	if time.minute < 10 {
		return fmt.Sprintf("%d:0%d ", time.hour, time.minute)
	} else {
		return fmt.Sprintf("%d:%d ", time.hour, time.minute)
	}
}

func departureTime(departure string) (int, int) {
	ret, _ := regexp.MatchString("^[0-9]{1,2}:[0-9]{1,2}$", departure)
	var hour int
	var minute int
	if ret {
		re := regexp.MustCompile("^([0-9]{1,2}):([0-9]{1,2})$")
		bs := []byte(departure)
		group := re.FindSubmatch(bs)
		h, _ := strconv.Atoi(string(group[1]))
		m, _ := strconv.Atoi(string(group[2]))
		if h >= 0 && h < 24 && m >= 0 && m < 60 {
			hour = h
			minute = m
		} else {
			now := time.Now()
			hour = now.Hour()
			minute = now.Minute()
		}
	} else {
		now := time.Now()
		hour = now.Hour()
		minute = now.Minute()
	}
	return hour, minute
}

func getTimeTableUrl() string {
	weekday := time.Now().Weekday().String()
	if weekday == "Saturday" || weekday == "Sunday" {
		return "http://www.jreast-timetable.jp/1509/timetable/tt0413/0413021.html"
	} else {
		return "http://www.jreast-timetable.jp/1509/timetable/tt0413/0413020.html"
	}
}

func createTimetable() []Time {
	var timetable = make([]Time, 0)
	url := getTimeTableUrl()
	doc, _ := goquery.NewDocument(url)
	doc.Find(".timetable .result_03").Each(func(_ int, s *goquery.Selection) {
		s.Find("tbody tr").Each(func(_ int, s *goquery.Selection) {
			key, err := strconv.Atoi(s.Find("td:nth-child(1)").Text())
			if err == nil {
				s.Find("td:nth-child(2) div.timetable_time[data-dest='府']").Each(func(_ int, s *goquery.Selection) {
					value, _ := strconv.Atoi(s.Find(".minute").Text())
					timetable = append(timetable, Time{key, value})
				})
			}
		})
	})
	return timetable
}

func printTimes(times []string) {
	for _, v := range times {
		fmt.Println(v)
	}
}

func main() {
	var (
		departure   string
		numOfResult int
		isLast      bool
	)
	flag.StringVar(&departure, "t", "", "specify departure time.")
	flag.BoolVar(&isLast, "l", false, "show last bus of the day.")
	flag.IntVar(&numOfResult, "n", DefaultNumOfResultToShow, "specify amount of result.")
	flag.Parse()

	if numOfResult < 0 {
		fmt.Fprintf(os.Stderr, "parameter for -n must be greater than 0.\n")
		os.Exit(2)
	}

	hour, minute := departureTime(departure)
	timetable := createTimetable()

	if isLast {
		result := []string{timetable[len(timetable)-1].toString()}
		printTimes(result)
		return
	}

	result := make([]string, 0, numOfResult)
	for i := 0; i < len(timetable); i++ {
		v := timetable[i]
		if v.minute > minute && v.hour >= hour {
			timeStr := v.toString()
			result = append(result, timeStr)
			if len(result) >= numOfResult {
				break
			}
		}
	}
	printTimes(result)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/xml"
	"errors"
	"net"
	"net/http"
	"os"

	"github.com/jasiek/ciscowx/ciscoxml"
	"github.com/jasiek/ciscowx/funds"
	"github.com/jasiek/ciscowx/weather"

	"github.com/abh/geoip"
	"github.com/aws/aws-lambda-go/events"
)

const ctxGeoipKey = "geoip"

var (
	// ErrNameNotProvided is thrown when a name is not provided
	ErrNameNotProvided = errors.New("no name was provided in the HTTP body")
)

func rootHandler(w http.ResponseWriter, r *http.Request) {
	x := ciscoxml.CiscoIPPhoneMenu{
		Title: "Exciting services",
		MenuItem: []ciscoxml.MenuItem{
			ciscoxml.MenuItem{
				Name: "Weather",
				URL:  "/wx",
			},
			ciscoxml.MenuItem{
				Name: "Available funds",
				URL:  "/funds",
			},
		},
	}
	bytes, _ := xml.Marshal(x)
	w.Write(bytes)
}

func contentTypeXml(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/xml")
		next.ServeHTTP(w, r)
	})
}

func configurationMiddleware(next http.Handler) http.Handler {
	testing := len(os.Getenv("TESTING")) > 0
	forecastioKey := os.Getenv("FORECASTIO_KEY")
	if len(forecastioKey) == 0 {
		panic("FORECASTIO_KEY not set")
	}
	ecUsername := os.Getenv("EC_USERNAME")
	if len(ecUsername) == 0 {
		panic("EC_USERNAME not set")
	}
	ecPassword := os.Getenv("EC_PASSWORD")
	if len(ecPassword) == 0 {
		panic("EC_PASSWORD is not set")
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithValue(r.Context(), "TESTING", testing)
		ctx = context.WithValue(ctx, "FORECASTIO_KEY", forecastioKey)
		ctx = context.WithValue(ctx, "EC_USERNAME", ecUsername)
		ctx = context.WithValue(ctx, "EC_PASSWORD", ecPassword)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func geoIpMiddleware(next http.Handler) http.Handler {
	gi, _ := geoip.Open("vendor/GeoIPCity.dat")

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		testing := r.Context().Value("TESTING").(bool)
		ip, _, _ := net.SplitHostPort(r.RemoteAddr)

		if testing {
			ip = "172.217.20.206"
		}
		record := gi.GetRecord(ip)
		ctx := context.WithValue(r.Context(), ctxGeoipKey, record)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func wrap(next http.Handler) http.Handler {
	return configurationMiddleware(geoIpMiddleware(contentTypeXml(next)))
}

func awsHandler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	if len(request.Body) < 1 {
		return events.APIGatewayProxyResponse{}, ErrNameNotProvided
	}
	return events.APIGatewayProxyResponse{}, nil
}

func main() {
	http.Handle("/funds", wrap(funds.MakeFundsHandler()))
	http.Handle("/wx", wrap(weather.MakeWxRootHandler()))
	http.Handle("/wx/", wrap(weather.MakeWxHandler()))
	http.Handle("/", wrap(http.HandlerFunc(rootHandler)))
	http.ListenAndServe("0.0.0.0:8080", nil)
}
<commit_msg>If running inside of a lambda, execute a different path.<commit_after>package main

import (
	"context"
	"encoding/xml"
	"errors"
	"net"
	"net/http"
	"os"

	"github.com/jasiek/ciscowx/ciscoxml"
	"github.com/jasiek/ciscowx/funds"
	"github.com/jasiek/ciscowx/weather"

	"github.com/abh/geoip"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

const ctxGeoipKey = "geoip"

var (
	// ErrNameNotProvided is thrown when a name is not provided
	ErrNameNotProvided = errors.New("no name was provided in the HTTP body")
)

func rootHandler(w http.ResponseWriter, r *http.Request) {
	x := ciscoxml.CiscoIPPhoneMenu{
		Title: "Exciting services",
		MenuItem: []ciscoxml.MenuItem{
			ciscoxml.MenuItem{
				Name: "Weather",
				URL:  "/wx",
			},
			ciscoxml.MenuItem{
				Name: "Available funds",
				URL:  "/funds",
			},
		},
	}
	bytes, _ := xml.Marshal(x)
	w.Write(bytes)
}

func contentTypeXml(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/xml")
		next.ServeHTTP(w, r)
	})
}

func configurationMiddleware(next http.Handler) http.Handler {
	testing := len(os.Getenv("TESTING")) > 0
	forecastioKey := os.Getenv("FORECASTIO_KEY")
	if len(forecastioKey) == 0 {
		panic("FORECASTIO_KEY not set")
	}
	ecUsername := os.Getenv("EC_USERNAME")
	if len(ecUsername) == 0 {
		panic("EC_USERNAME not set")
	}
	ecPassword := os.Getenv("EC_PASSWORD")
	if len(ecPassword) == 0 {
		panic("EC_PASSWORD is not set")
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithValue(r.Context(), "TESTING", testing)
		ctx = context.WithValue(ctx, "FORECASTIO_KEY", forecastioKey)
		ctx = context.WithValue(ctx, "EC_USERNAME", ecUsername)
		ctx = context.WithValue(ctx, "EC_PASSWORD", ecPassword)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func geoIpMiddleware(next http.Handler) http.Handler {
	gi, _ := geoip.Open("vendor/GeoIPCity.dat")

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		testing := r.Context().Value("TESTING").(bool)
		ip, _, _ := net.SplitHostPort(r.RemoteAddr)

		if testing {
			ip = "172.217.20.206"
		}
		record := gi.GetRecord(ip)
		ctx := context.WithValue(r.Context(), ctxGeoipKey, record)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func wrap(next http.Handler) http.Handler {
	return configurationMiddleware(geoIpMiddleware(contentTypeXml(next)))
}

func awsHandler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	if len(request.Body) < 1 {
		return events.APIGatewayProxyResponse{}, ErrNameNotProvided
	}
	return events.APIGatewayProxyResponse{}, nil
}

func main() {
	// Are we running inside of a lambda?
	if os.Getenv("AWS_EXECUTION_ENV") != "" {
		lambda.Start(awsHandler)
	} else {
		http.Handle("/funds", wrap(funds.MakeFundsHandler()))
		http.Handle("/wx", wrap(weather.MakeWxRootHandler()))
		http.Handle("/wx/", wrap(weather.MakeWxHandler()))
		http.Handle("/", wrap(http.HandlerFunc(rootHandler)))
		http.ListenAndServe("0.0.0.0:8080", nil)
	}
}
<|endoftext|>


<commit_before>package wordmaker

import (
	"fmt"
	"unicode"
	"unicode/utf8"
)

func Lex(name, input string) (*lexer, chan item) {
	l := &lexer{
		name:  name,
		input: input,
		items: make(chan item),
	}
	go l.run()
	return l, l.items
}

var eof rune // intentionally nil

type stateFn func(*lexer) stateFn

type lexer struct {
	name       string
	input      string
	start      int
	pos        int
	width      int
	parenDepth int
	items      chan item
}

type item struct {
	typ itemType
	val string
}

type itemType int

const (
	itemError itemType = iota

	itemClass
	itemColon
	itemSlash
	itemChoice
	itemLeftParen
	itemRightParen
	itemPattern
	itemEOL
	itemEOF
)

func (i item) String() string {
	switch i.typ {
	case itemError:
		return i.val
	case itemEOF:
		return "EOF"
	}
	return fmt.Sprintf("%q", i.val)
}

func (l *lexer) run() {
	for state := lexLine; state != nil; {
		state = state(l)
	}
	defer close(l.items)
}

func (l *lexer) emit(t itemType) {
	// fmt.Printf("Emit %q %q@%v\n", t, l.input[l.start:l.pos], l.start)
	l.items <- item{t, l.input[l.start:l.pos]}
	l.start = l.pos
}

func (l *lexer) next() (r rune) {
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}
	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	l.pos += l.width
	return r
}

func (l *lexer) backup() {
	l.pos -= l.width
}

func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

func lexLine(l *lexer) stateFn {
	r := l.peek()
	if isClass(r) {
		return lexClass
	} else if r == 'r' {
		return lexPattern
	} else if r == 'n' {
		return lexNumber
	}
	l.emit(itemError)
	return nil
}

func lexClass(l *lexer) stateFn {
	l.next()
	if l.peek() == ':' {
		l.emit(itemClass)
		l.next()
		l.emit(itemColon)
		return lexInChoices
	}
	l.emit(itemError)
	return nil
}

func lexPattern(l *lexer) stateFn {
	l.next()
	if l.peek() == ':' {
		l.emit(itemPattern)
		l.next()
		l.emit(itemColon)
		return lexInPattern
	}
	l.emit(itemError)
	return nil
}

func lexInChoices(l *lexer) (next stateFn) {
	return choiceLexer(lexChoice)(l)
}

func choiceLexer(choiceFn stateFn) stateFn {
	return func(l *lexer) stateFn {
		switch r := l.next(); {
		case r == eof || isEOL(r):
			return nil
		case r == '/':
			l.emit(itemSlash)
			return choiceFn
		case r == '(':
			l.emit(itemLeftParen)
			l.parenDepth++
			return choiceFn
		case r == ')':
			l.emit(itemRightParen)
			l.parenDepth--
			if l.parenDepth < 0 {
				l.emit(itemError)
				return nil
			}
			// blanks are not allowed after closing parens
			return choiceLexer(choiceFn)
		default:
			l.backup()
			return choiceFn
		}
		return nil
	}
}

func lexChoice(l *lexer) stateFn {
Loop:
	for {
		switch r := l.next(); {
		case isAlpha(r):
			// absorb
			// fmt.Print(" absorb\n")
		default:
			// fmt.Print("Emit due to %v\n", r)
			l.backup()
			l.emit(itemChoice)
			break Loop
		}
	}
	return lexInChoices
}

func lexInPattern(l *lexer) stateFn {
	return choiceLexer(lexPatternItem)(l)
}

func lexPatternItem(l *lexer) stateFn {
	emit := true
Loop:
	for {
		switch r := l.next(); {
		case isDelim(r):
			if r == '(' {
				// don't emit a blank item between ((
				emit = false
			}
			l.backup()
			break Loop
		case r == eof:
			break Loop
		}
	}
	if emit || l.start < l.pos {
		l.emit(itemChoice)
	}
	return lexInPattern
}

func lexNumber(l *lexer) stateFn {
	return nil
}

func isEOL(r rune) bool {
	return r == '\r' || r == '\n'
}

func isAlpha(r rune) bool {
	return unicode.IsLetter(r)
}

func isClass(r rune) bool {
	return isAlpha(r) && unicode.IsUpper(r)
}

func isDelim(r rune) bool {
	return (r == '/' || r == '(' || r == ')')
}
<commit_msg>Remove unreachable code<commit_after>package wordmaker

import (
	"fmt"
	"unicode"
	"unicode/utf8"
)

func Lex(name, input string) (*lexer, chan item) {
	l := &lexer{
		name:  name,
		input: input,
		items: make(chan item),
	}
	go l.run()
	return l, l.items
}

var eof rune // intentionally nil

type stateFn func(*lexer) stateFn

type lexer struct {
	name       string
	input      string
	start      int
	pos        int
	width      int
	parenDepth int
	items      chan item
}

type item struct {
	typ itemType
	val string
}

type itemType int

const (
	itemError itemType = iota

	itemClass
	itemColon
	itemSlash
	itemChoice
	itemLeftParen
	itemRightParen
	itemPattern
	itemEOL
	itemEOF
)

func (i item) String() string {
	switch i.typ {
	case itemError:
		return i.val
	case itemEOF:
		return "EOF"
	}
	return fmt.Sprintf("%q", i.val)
}

func (l *lexer) run() {
	for state := lexLine; state != nil; {
		state = state(l)
	}
	defer close(l.items)
}

func (l *lexer) emit(t itemType) {
	// fmt.Printf("Emit %q %q@%v\n", t, l.input[l.start:l.pos], l.start)
	l.items <- item{t, l.input[l.start:l.pos]}
	l.start = l.pos
}

func (l *lexer) next() (r rune) {
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}
	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	l.pos += l.width
	return r
}

func (l *lexer) backup() {
	l.pos -= l.width
}

func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

func lexLine(l *lexer) stateFn {
	r := l.peek()
	if isClass(r) {
		return lexClass
	} else if r == 'r' {
		return lexPattern
	} else if r == 'n' {
		return lexNumber
	}
	l.emit(itemError)
	return nil
}

func lexClass(l *lexer) stateFn {
	l.next()
	if l.peek() == ':' {
		l.emit(itemClass)
		l.next()
		l.emit(itemColon)
		return lexInChoices
	}
	l.emit(itemError)
	return nil
}

func lexPattern(l *lexer) stateFn {
	l.next()
	if l.peek() == ':' {
		l.emit(itemPattern)
		l.next()
		l.emit(itemColon)
		return lexInPattern
	}
	l.emit(itemError)
	return nil
}

func lexInChoices(l *lexer) (next stateFn) {
	return choiceLexer(lexChoice)(l)
}

func choiceLexer(choiceFn stateFn) stateFn {
	return func(l *lexer) stateFn {
		switch r := l.next(); {
		case r == eof || isEOL(r):
			return nil
		case r == '/':
			l.emit(itemSlash)
			return choiceFn
		case r == '(':
			l.emit(itemLeftParen)
			l.parenDepth++
			return choiceFn
		case r == ')':
			l.emit(itemRightParen)
			l.parenDepth--
			if l.parenDepth < 0 {
				l.emit(itemError)
				return nil
			}
			// blanks are not allowed after closing parens
			return choiceLexer(choiceFn)
		default:
			l.backup()
			return choiceFn
		}
	}
}

func lexChoice(l *lexer) stateFn {
Loop:
	for {
		switch r := l.next(); {
		case isAlpha(r):
			// absorb
			// fmt.Print(" absorb\n")
		default:
			// fmt.Print("Emit due to %v\n", r)
			l.backup()
			l.emit(itemChoice)
			break Loop
		}
	}
	return lexInChoices
}

func lexInPattern(l *lexer) stateFn {
	return choiceLexer(lexPatternItem)(l)
}

func lexPatternItem(l *lexer) stateFn {
	emit := true
Loop:
	for {
		switch r := l.next(); {
		case isDelim(r):
			if r == '(' {
				// don't emit a blank item between ((
				emit = false
			}
			l.backup()
			break Loop
		case r == eof:
			break Loop
		}
	}
	if emit || l.start < l.pos {
		l.emit(itemChoice)
	}
	return lexInPattern
}

func lexNumber(l *lexer) stateFn {
	return nil
}

func isEOL(r rune) bool {
	return r == '\r' || r == '\n'
}

func isAlpha(r rune) bool {
	return unicode.IsLetter(r)
}

func isClass(r rune) bool {
	return isAlpha(r) && unicode.IsUpper(r)
}

func isDelim(r rune) bool {
	return (r == '/' || r == '(' || r == ')')
}
<|endoftext|>