<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"
	"strconv"
	"sync"

	"github.com/Snapbug/gomemcache/memcache"
	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	namespace = "memcache"
)

var (
	cacheOperations  = []string{"get", "delete", "incr", "decr", "cas", "touch"}
	cacheStatuses    = []string{"hits", "misses"}
	usageTimes       = []string{"curr", "total"}
	usageResources   = []string{"items", "connections"}
	bytesDirections  = []string{"read", "written"}
	removalsStatuses = []string{"expired", "evicted"}
)

// Exporter collects metrics from a set of memcache servers.
type Exporter struct {
	mutex    sync.RWMutex
	mc       *memcache.Client
	up       prometheus.Gauge
	uptime   prometheus.Counter
	cache    *prometheus.CounterVec
	usage    *prometheus.GaugeVec
	bytes    *prometheus.CounterVec
	removals *prometheus.CounterVec
}

// NewExporter returns an initialized exporter
func NewExporter(server string) *Exporter {
	return &Exporter{
		mc: memcache.New(server),
		up: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Name:        "up",
				Namespace:   namespace,
				Help:        "Are the servers up.",
				ConstLabels: prometheus.Labels{"server": server},
			},
		),
		uptime: prometheus.NewCounter(
			prometheus.CounterOpts{
				Name:        "uptime",
				Namespace:   namespace,
				Help:        "The uptime of the server.",
				ConstLabels: prometheus.Labels{"server": server},
			},
		),
		cache: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name:        "cache",
				Namespace:   namespace,
				Help:        "The cache hits/misses broken down by command (get, set, etc.).",
				ConstLabels: prometheus.Labels{"server": server},
			},
			[]string{"command", "status"},
		),
		usage: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Name:        "usage",
				Namespace:   namespace,
				Help:        "Details the resource usage (items/connections) of the server, by time (current/total).",
				ConstLabels: prometheus.Labels{"server": server},
			},
			[]string{"time", "resource"},
		),
		bytes: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name:        "bytes",
				Namespace:   namespace,
				Help:        "The bytes sent/received by the server.",
				ConstLabels: prometheus.Labels{"server": server},
			},
			[]string{"direction"},
		),
		removals: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name:        "removal",
				Namespace:   namespace,
				Help:        "Number of items that have been evicted/expired (status), and if the were fetched ever or not.",
				ConstLabels: prometheus.Labels{"server": server},
			},
			[]string{"status", "fetched"},
		),
	}
}

// Describe describes all the metrics exported by the memcache exporter. It
// implements prometheus.Collector.
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- e.up.Desc()
	ch <- e.uptime.Desc()

	e.cache.Describe(ch)
	e.usage.Describe(ch)
	e.bytes.Describe(ch)
	e.removals.Describe(ch)
}

// Collect fetches the statistics from the configured memcache servers, and
// delivers them as prometheus metrics. It implements prometheus.Collector.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	// prevent concurrent metric collections
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.cache.Reset()
	e.usage.Reset()
	e.bytes.Reset()
	e.removals.Reset()

	stats, err := e.mc.Stats()

	if err != nil {
		glog.Infof("Failed to collect stats from memcache: %s", err)
		return
	}

	e.up.Set(1)
	for server, _ := range stats {
		m, err := strconv.ParseUint(stats[server]["uptime"], 10, 64)
		if err != nil {
			e.uptime.Set(0)
		} else {
			e.uptime.Set(float64(m))
		}

		for _, op := range cacheOperations {
			for _, st := range cacheStatuses {
				m, err := strconv.ParseUint(stats[server][fmt.Sprintf("%s_%s", op, st)], 10, 64)
				if err != nil {
					e.cache.WithLabelValues(op, st).Set(0)
				} else {
					e.cache.WithLabelValues(op, st).Set(float64(m))
				}
			}
		}

		for _, t := range usageTimes {
			for _, r := range usageResources {
				m, err := strconv.ParseUint(stats[server][fmt.Sprintf("%s_%s", t, r)], 10, 64)
				if err != nil {
					e.usage.WithLabelValues(t, r).Set(0)
				} else {
					e.usage.WithLabelValues(t, r).Set(float64(m))
				}
			}
		}

		for _, dir := range bytesDirections {
			m, err := strconv.ParseUint(stats[server][fmt.Sprintf("bytes_%s", dir)], 10, 64)
			if err != nil {
				e.bytes.WithLabelValues(dir).Set(0)
			} else {
				e.bytes.WithLabelValues(dir).Set(float64(m))
			}
		}

		for _, st := range removalsStatuses {
			m, err := strconv.ParseUint(stats[server][fmt.Sprintf("%s_unfetched", st)], 10, 64)
			if err != nil {
				e.removals.WithLabelValues(st, "unfetched").Set(0)
			} else {
				e.removals.WithLabelValues(st, "unfetched").Set(float64(m))
			}
		}
		m, err = strconv.ParseUint(stats[server]["evicted"], 10, 64)
		if err != nil {
			e.removals.WithLabelValues("evicted", "fetched").Set(0)
		} else {
			e.removals.WithLabelValues("evicted", "fetched").Set(float64(m))
		}
	}

	ch <- e.up
	ch <- e.uptime
	e.cache.Collect(ch)
	e.usage.Collect(ch)
	e.bytes.Collect(ch)
	e.removals.Collect(ch)
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9106", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
	)

	flag.Parse()

	for _, server := range flag.Args() {
		exporter := NewExporter(server)
		prometheus.MustRegister(exporter)
	}

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Memcached Exporter</title>

Memcached Exporter

<a href='` + *metricsPath + `'>Metrics

             `))
	})
	glog.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<commit_msg>Set memcache_up to 0 on failure to scrape<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"
	"strconv"
	"sync"

	"github.com/Snapbug/gomemcache/memcache"
	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	namespace = "memcache"
)

var (
	cacheOperations  = []string{"get", "delete", "incr", "decr", "cas", "touch"}
	cacheStatuses    = []string{"hits", "misses"}
	usageTimes       = []string{"curr", "total"}
	usageResources   = []string{"items", "connections"}
	bytesDirections  = []string{"read", "written"}
	removalsStatuses = []string{"expired", "evicted"}
)

// Exporter collects metrics from a set of memcache servers.
type Exporter struct {
	mutex    sync.RWMutex
	mc       *memcache.Client
	up       prometheus.Gauge
	uptime   prometheus.Counter
	cache    *prometheus.CounterVec
	usage    *prometheus.GaugeVec
	bytes    *prometheus.CounterVec
	removals *prometheus.CounterVec
}

// NewExporter returns an initialized exporter
func NewExporter(server string) *Exporter {
	return &Exporter{
		mc: memcache.New(server),
		up: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Name:        "up",
				Namespace:   namespace,
				Help:        "Are the servers up.",
				ConstLabels: prometheus.Labels{"server": server},
			},
		),
		uptime: prometheus.NewCounter(
			prometheus.CounterOpts{
				Name:        "uptime",
				Namespace:   namespace,
				Help:        "The uptime of the server.",
				ConstLabels: prometheus.Labels{"server": server},
			},
		),
		cache: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name:        "cache",
				Namespace:   namespace,
				Help:        "The cache hits/misses broken down by command (get, set, etc.).",
				ConstLabels: prometheus.Labels{"server": server},
			},
			[]string{"command", "status"},
		),
		usage: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Name:        "usage",
				Namespace:   namespace,
				Help:        "Details the resource usage (items/connections) of the server, by time (current/total).",
				ConstLabels: prometheus.Labels{"server": server},
			},
			[]string{"time", "resource"},
		),
		bytes: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name:        "bytes",
				Namespace:   namespace,
				Help:        "The bytes sent/received by the server.",
				ConstLabels: prometheus.Labels{"server": server},
			},
			[]string{"direction"},
		),
		removals: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Name:        "removal",
				Namespace:   namespace,
				Help:        "Number of items that have been evicted/expired (status), and if the were fetched ever or not.",
				ConstLabels: prometheus.Labels{"server": server},
			},
			[]string{"status", "fetched"},
		),
	}
}

// Describe describes all the metrics exported by the memcache exporter. It
// implements prometheus.Collector.
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- e.up.Desc()
	ch <- e.uptime.Desc()

	e.cache.Describe(ch)
	e.usage.Describe(ch)
	e.bytes.Describe(ch)
	e.removals.Describe(ch)
}

// Collect fetches the statistics from the configured memcache servers, and
// delivers them as prometheus metrics. It implements prometheus.Collector.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	// prevent concurrent metric collections
	e.mutex.Lock()
	defer e.mutex.Unlock()

	e.cache.Reset()
	e.usage.Reset()
	e.bytes.Reset()
	e.removals.Reset()

	stats, err := e.mc.Stats()

	if err != nil {
		e.up.Set(0)
		ch <- e.up
		glog.Infof("Failed to collect stats from memcache: %s", err)
		return
	}

	e.up.Set(1)
	for server, _ := range stats {
		m, err := strconv.ParseUint(stats[server]["uptime"], 10, 64)
		if err != nil {
			e.uptime.Set(0)
		} else {
			e.uptime.Set(float64(m))
		}

		for _, op := range cacheOperations {
			for _, st := range cacheStatuses {
				m, err := strconv.ParseUint(stats[server][fmt.Sprintf("%s_%s", op, st)], 10, 64)
				if err != nil {
					e.cache.WithLabelValues(op, st).Set(0)
				} else {
					e.cache.WithLabelValues(op, st).Set(float64(m))
				}
			}
		}

		for _, t := range usageTimes {
			for _, r := range usageResources {
				m, err := strconv.ParseUint(stats[server][fmt.Sprintf("%s_%s", t, r)], 10, 64)
				if err != nil {
					e.usage.WithLabelValues(t, r).Set(0)
				} else {
					e.usage.WithLabelValues(t, r).Set(float64(m))
				}
			}
		}

		for _, dir := range bytesDirections {
			m, err := strconv.ParseUint(stats[server][fmt.Sprintf("bytes_%s", dir)], 10, 64)
			if err != nil {
				e.bytes.WithLabelValues(dir).Set(0)
			} else {
				e.bytes.WithLabelValues(dir).Set(float64(m))
			}
		}

		for _, st := range removalsStatuses {
			m, err := strconv.ParseUint(stats[server][fmt.Sprintf("%s_unfetched", st)], 10, 64)
			if err != nil {
				e.removals.WithLabelValues(st, "unfetched").Set(0)
			} else {
				e.removals.WithLabelValues(st, "unfetched").Set(float64(m))
			}
		}
		m, err = strconv.ParseUint(stats[server]["evicted"], 10, 64)
		if err != nil {
			e.removals.WithLabelValues("evicted", "fetched").Set(0)
		} else {
			e.removals.WithLabelValues("evicted", "fetched").Set(float64(m))
		}
	}

	ch <- e.up
	ch <- e.uptime
	e.cache.Collect(ch)
	e.usage.Collect(ch)
	e.bytes.Collect(ch)
	e.removals.Collect(ch)
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9106", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
	)

	flag.Parse()

	for _, server := range flag.Args() {
		exporter := NewExporter(server)
		prometheus.MustRegister(exporter)
	}

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Memcached Exporter</title>

Memcached Exporter

<a href='` + *metricsPath + `'>Metrics

             `))
	})
	glog.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"dmas/models"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/asaskevich/govalidator"
	"github.com/gin-gonic/gin"
	"github.com/googollee/go-socket.io"
	"gopkg.in/mgo.v2/bson"
)

var soIO *socketio.Server

func main() {
	port := ":4555" //on this port our web server will be liste for incoming requests

	r := gin.Default() //default instance of gin framework used as router as well
	//CORSMiddlware will inject needed headers for our angular client
	r.Use(dmas.CORSMiddleware())

	//routes for socketIO
	r.GET("/socket.io/", socketHandler)
	r.POST("/socket.io/", socketHandler)
	r.Handle("WS", "/socket.io/", socketHandler)
	r.Handle("WSS", "/socket.io/", socketHandler)

	//listen for certain paths - Home
	r.GET("/", HomeHandler)

	//now we will do some routes grouping and there we are going to have basic post/get functions/handlers
	userRoutes := r.Group("/user")
	{
		userRoutes.POST("/register", registerHandler)
		userRoutes.GET("/login", loginHandler)
	}

	//and testing some api routes now
	apiRoutes := r.Group("api")
	{
		apiRoutes.GET("/fetch/:link", apiFetch)
		apiRoutes.GET("/json", apiTournaments)

		// API routes for questions
		apiRoutes.GET("/questions", apiQuestions)
		apiRoutes.GET("/question/:id", apiOneQuestion)
		apiRoutes.POST("/question/create", apiCreateQuestion)
		apiRoutes.POST("/question/update", apiUpdateQuestion)
		apiRoutes.POST("/question/delete/:id", apiDeleteQuestion)
	}

	//now listen on defined port
	r.Run(port)
}

//func socketHandler will handle socketIO related stuff
func socketHandler(c *gin.Context) {
	//create socketIO server as well
	soIO, err := socketio.NewServer(nil)
	if err != nil {
		panic(err)
	}

	soIO.On("connection", func(so socketio.Socket) {
		fmt.Println("on connection")

		so.Join("chat")
		//fmt.Println("URL:", so.Request().URL)

		so.BroadcastTo("chat", "event", "Hello my dear client")

		//so.On("event", func(msg string) {
		//fmt.Println("emit:", so.Emit("chat message", msg))
		//so.BroadcastTo("chat", "event", msg)
		//})
		so.On("disconnection", func() {
			fmt.Println("on disconnect")
		})
	})

	soIO.On("error", func(so socketio.Socket, err error) {
		fmt.Printf("[ WebSocket ] Error : %v", err.Error())
	})

	soIO.ServeHTTP(c.Writer, c.Request)
}

//HomeHandler this function is using ginContext
func HomeHandler(c *gin.Context) {
	//and sand some response do
	c.String(200, "Hello from gin")
}

//handle simple route for login - return template or something
func loginHandler(c *gin.Context) {
	c.String(200, "Login route here")
}

//for now just return submited params
func registerHandler(c *gin.Context) {
	username := c.PostForm("username")
	email := c.PostForm("email")

	//we are using external library to validate email and other things if needed
	if govalidator.IsEmail(email) {
		//c.String(200, "Username is: %s and e-mail %s", username, email)
		//or we can return JSON as well
		c.JSON(200, gin.H{"username": username, "email": email})
	} else {
		//email is not valid we can't register this user
		c.String(400, "We are sorry but email: %s is not valid e-mail adress", email)
	}
}

//let's for learning purposes see how to interact for example with rest api
func apiTournaments(c *gin.Context) {
	response, err := http.Get("http://jsonplaceholder.typicode.com/posts")

	if err != nil {
		c.JSON(400, gin.H{"error": err})
	} else {
		contents, e := ioutil.ReadAll(response.Body) //read complete body if any with utils

		if e != nil {
			c.JSON(400, gin.H{"We have an error": e})
		} else {
			c.String(200, string(contents))
		}
	}
}

func apiQuestions(c *gin.Context) {
	fmt.Println("Into questions function...")
	result := dmas.GetAllQuestions()

	c.JSON(200, result)
}

func apiOneQuestion(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 0, 64)

	if err != nil {
		c.JSON(500, gin.H{"There was some problem...": err})
	}

	result := dmas.GetQuestionByID(id)
	c.JSON(200, result)
}

func apiCreateQuestion(c *gin.Context) {
	title := c.PostForm("title")
	answer := c.PostForm("answer")
	a1 := c.PostForm("a1")
	a2 := c.PostForm("a2")
	a3 := c.PostForm("a3")
	a4 := c.PostForm("a4")

	status, err := dmas.CreateQuestion(title, answer, a1, a2, a3, a4)

	if status {
		c.JSON(200, gin.H{"status": "All good"})
	} else {
		c.JSON(500, gin.H{"There was some problem...": err})
	}
}

func apiUpdateQuestion(c *gin.Context) {
	id, err := strconv.ParseInt(c.PostForm("id"), 0, 64)

	if err != nil {
		c.JSON(500, gin.H{"There was some problem with id...": err})
	}

	updateParams := bson.M{}

	if c.PostForm("title") != "" {
		updateParams["title"] = c.PostForm("title")
	}

	if c.PostForm("answer") != "" {
		updateParams["answer"] = c.PostForm("answer")
	}

	if c.PostForm("a1") != "" {
		updateParams["a1"] = c.PostForm("a1")
	}

	if c.PostForm("a2") != "" {
		updateParams["a2"] = c.PostForm("a2")
	}

	if c.PostForm("a3") != "" {
		updateParams["a3"] = c.PostForm("a3")
	}

	if c.PostForm("a4") != "" {
		updateParams["a4"] = c.PostForm("a4")
	}

	status, err := dmas.UpdateQuestion(id, updateParams)

	if status {
		c.JSON(200, gin.H{"status": "All good"})
	} else {
		c.JSON(500, gin.H{"There was some problem...": err})
	}
}

func apiDeleteQuestion(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 0, 64)

	if err != nil {
		c.JSON(500, gin.H{"There was some problem...": err})
	}

	deleted := dmas.DeleteQuestion(id)
	if deleted {
		c.JSON(200, gin.H{"status": "Document is deleted"})
	} else {
		c.JSON(500, gin.H{"status": "There was some problem"})
	}

}

//function to handle get of any url - content will be returned
func apiFetch(c *gin.Context) {
	response, err := http.Get("http://" + c.Param("link"))
	if err != nil {
		c.String(400, "Your input is not valid, link should look like: www.google.com or google.com")
	} else {
		defer response.Body.Close() //close it on the end
		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			c.String(500, "Specific error happend: %s", err)
			//os.Exit(1) if we want to kill the server
		} else {
			//fmt.Printf("%s\n", string(contents))
			c.String(200, string(contents))
		}
	}
}
<commit_msg>Socket.io removed gorilla added<commit_after>package main

import (
	"dmas/models"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/asaskevich/govalidator"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"gopkg.in/mgo.v2/bson"
)

func main() {
	port := ":4555" //on this port our web server will be liste for incoming requests

	r := gin.Default() //default instance of gin framework used as router as well
	//CORSMiddlware will inject needed headers for our angular client
	r.Use(dmas.CORSMiddleware())

	//for sockets
	r.GET("/ws", func(c *gin.Context) {
		wshandler(c.Writer, c.Request)
	})

	//listen for certain paths - Home
	r.GET("/", HomeHandler)

	//now we will do some routes grouping and there we are going to have basic post/get functions/handlers
	userRoutes := r.Group("/user")
	{
		userRoutes.POST("/register", registerHandler)
		userRoutes.GET("/login", loginHandler)
	}

	//and testing some api routes now
	apiRoutes := r.Group("api")
	{
		apiRoutes.GET("/fetch/:link", apiFetch)
		apiRoutes.GET("/json", apiTournaments)

		// API routes for questions
		apiRoutes.GET("/questions", apiQuestions)
		apiRoutes.GET("/question/:id", apiOneQuestion)
		apiRoutes.POST("/question/create", apiCreateQuestion)
		apiRoutes.POST("/question/update", apiUpdateQuestion)
		apiRoutes.POST("/question/delete/:id", apiDeleteQuestion)
	}

	//now listen on defined port
	r.Run(port)
}

//socket structure
var wsupgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

//socket handler
func wshandler(w http.ResponseWriter, r *http.Request) {
	conn, err := wsupgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println("Failed to set websocket upgrade: %+v", err)
		return
	}

	for {
		t, msg, err := conn.ReadMessage()
		if err != nil {
			break
		}
		conn.WriteMessage(t, msg)
	}
}

//HomeHandler this function is using ginContext
func HomeHandler(c *gin.Context) {
	//and sand some response do
	c.String(200, "Hello from gin")
}

//handle simple route for login - return template or something
func loginHandler(c *gin.Context) {
	c.String(200, "Login route here")
}

//for now just return submited params
func registerHandler(c *gin.Context) {
	username := c.PostForm("username")
	email := c.PostForm("email")

	//we are using external library to validate email and other things if needed
	if govalidator.IsEmail(email) {
		//c.String(200, "Username is: %s and e-mail %s", username, email)
		//or we can return JSON as well
		c.JSON(200, gin.H{"username": username, "email": email})
	} else {
		//email is not valid we can't register this user
		c.String(400, "We are sorry but email: %s is not valid e-mail adress", email)
	}
}

//let's for learning purposes see how to interact for example with rest api
func apiTournaments(c *gin.Context) {
	response, err := http.Get("http://jsonplaceholder.typicode.com/posts")

	if err != nil {
		c.JSON(400, gin.H{"error": err})
	} else {
		contents, e := ioutil.ReadAll(response.Body) //read complete body if any with utils

		if e != nil {
			c.JSON(400, gin.H{"We have an error": e})
		} else {
			c.String(200, string(contents))
		}
	}
}

func apiQuestions(c *gin.Context) {
	fmt.Println("Into questions function...")
	result := dmas.GetAllQuestions()

	c.JSON(200, result)
}

func apiOneQuestion(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 0, 64)

	if err != nil {
		c.JSON(500, gin.H{"There was some problem...": err})
	}

	result := dmas.GetQuestionByID(id)
	c.JSON(200, result)
}

func apiCreateQuestion(c *gin.Context) {
	title := c.PostForm("title")
	answer := c.PostForm("answer")
	a1 := c.PostForm("a1")
	a2 := c.PostForm("a2")
	a3 := c.PostForm("a3")
	a4 := c.PostForm("a4")

	status, err := dmas.CreateQuestion(title, answer, a1, a2, a3, a4)

	if status {
		c.JSON(200, gin.H{"status": "All good"})
	} else {
		c.JSON(500, gin.H{"There was some problem...": err})
	}
}

func apiUpdateQuestion(c *gin.Context) {
	id, err := strconv.ParseInt(c.PostForm("id"), 0, 64)

	if err != nil {
		c.JSON(500, gin.H{"There was some problem with id...": err})
	}

	updateParams := bson.M{}

	if c.PostForm("title") != "" {
		updateParams["title"] = c.PostForm("title")
	}

	if c.PostForm("answer") != "" {
		updateParams["answer"] = c.PostForm("answer")
	}

	if c.PostForm("a1") != "" {
		updateParams["a1"] = c.PostForm("a1")
	}

	if c.PostForm("a2") != "" {
		updateParams["a2"] = c.PostForm("a2")
	}

	if c.PostForm("a3") != "" {
		updateParams["a3"] = c.PostForm("a3")
	}

	if c.PostForm("a4") != "" {
		updateParams["a4"] = c.PostForm("a4")
	}

	status, err := dmas.UpdateQuestion(id, updateParams)

	if status {
		c.JSON(200, gin.H{"status": "All good"})
	} else {
		c.JSON(500, gin.H{"There was some problem...": err})
	}
}

func apiDeleteQuestion(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 0, 64)

	if err != nil {
		c.JSON(500, gin.H{"There was some problem...": err})
	}

	deleted := dmas.DeleteQuestion(id)
	if deleted {
		c.JSON(200, gin.H{"status": "Document is deleted"})
	} else {
		c.JSON(500, gin.H{"status": "There was some problem"})
	}

}

//function to handle get of any url - content will be returned
func apiFetch(c *gin.Context) {
	response, err := http.Get("http://" + c.Param("link"))
	if err != nil {
		c.String(400, "Your input is not valid, link should look like: www.google.com or google.com")
	} else {
		defer response.Body.Close() //close it on the end
		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			c.String(500, "Specific error happend: %s", err)
			//os.Exit(1) if we want to kill the server
		} else {
			//fmt.Printf("%s\n", string(contents))
			c.String(200, string(contents))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	stdlog "log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	_ "expvar"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/bepress/camo/logging"
	"github.com/bepress/camo/proxy"
	"github.com/reedobrien/cowl"
	"github.com/rs/zerolog"
)

const (
	app       = "camo"
	drainTime = 10 * time.Second
)

var (
	// BuildDate is populated on build for version info.
	BuildDate string

	// GitBranch is populated on build for version info.
	GitBranch string

	// GitHash is populated on build for version info.
	GitHash string

	// BuildVersion is populated on build for version info.
	BuildVersion string
)

func main() {
	var (
		addr    = flag.String("addr", ":443", "The address and port to listen on")
		maxsize = flag.Int64("maxsize", 5, "Maximum size to proxy in whole MB (no decimal)")
		secret  = flag.String("secret", "", "The 'shared secret' hmac key")
		tlscert = flag.String("cert", "cert.pem", "The TLS certificate to use")
		tlskey  = flag.String("key", "key.pem", "The TLS key to use")
		verbose = flag.Bool("verbose", false, "If verbose logging should take place (No-op at this time as there's no debug log statements)")
		version = flag.Bool("version", false, "Display version and build info, then exit")

		// TODO(ro) 2017-10-10 Add flags for other proxy set-ables.
		logger zerolog.Logger
	)
	flag.Parse()

	if *version {
		versionInfo()
	}

	// Create a context so we can cancel goroutines.
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Make sure they go away when main exits.
	hostname, err := os.Hostname()
	if err != nil {
		panic("failed to get hostname")
	}
	// Start an aws session, and make the cloudwatchlogs service.
	session := session.Must(session.NewSession())
	svc := cloudwatchlogs.New(session)

	// Create the cloudwatchlog writer.
	cwl := cowl.MustNewWriterWithContext(ctx, svc, app, hostname+"-app")
	// Write to both stdout and cwl.
	w := io.MultiWriter(cwl, os.Stdout)
	// Set up the logger to use it.
	logger = logging.NewLogger(app, *verbose, w).With().Str(
		"handler", "proxy").Logger()
	if BuildVersion != "" {
		logger = logger.With().
			Str("app_version", BuildVersion+"-"+GitHash).
			Logger()
	}

	stdlog.SetFlags(0)
	stdlog.SetOutput(logger)

	// Stop channel to block until we get a signal. This is used for graceful
	// shutdown.
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)

	// Set up options.
	// TODO(ro) 2017-10-11 Add more options here and as flags as necessary.
	options := []func(*proxy.Proxy){}
	if *maxsize > 0 {
		options = append(options, func(p *proxy.Proxy) { p.MaxSize = *maxsize * 1024 * 1024 })
	}

	// Create proxy handler.
	p := proxy.MustNew([]byte(*secret), logger, options...)
	// Wrap proxy handler with logger.
	proxyHandler := logging.NewAccessLogger(p, logger)
	s := http.Server{Addr: *addr, Handler: proxyHandler}

	// Start TLS server.
	go func() {
		if err := s.ListenAndServeTLS(*tlscert, *tlskey); err != http.ErrServerClosed {
			logger.Fatal().Err(err).Msg("failed to start server")
		}
	}()
	// Start a server on localhost:9000 for expvar.
	go func() {
		if err := http.ListenAndServe("127.0.0.1:9000", nil); err != nil {
			logger.Fatal().Err(err).Msg("failed to start expvar server")
		}
	}()

	<-stop

	// Create a context with timeout to limit shutdown time in the event of
	// slow clients.
	ctx, cancel = context.WithTimeout(ctx, drainTime)
	defer cancel()

	if err := s.Shutdown(ctx); err != nil {
		logger.Error().Err(err).Msg("caught on proxy.Shutdown")
	}
}

func versionInfo() {
	fmt.Printf(`
build date: %s
branch    : %s
hash      : %s
version   : %s
built with: %s
`[1:], BuildDate, GitBranch, GitHash, BuildVersion, runtime.Version())
	os.Exit(0)
}
<commit_msg>make app the log stream prefix so we can aggregate<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	stdlog "log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	_ "expvar"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/bepress/camo/logging"
	"github.com/bepress/camo/proxy"
	"github.com/reedobrien/cowl"
	"github.com/rs/zerolog"
)

const (
	app       = "camo"
	drainTime = 10 * time.Second
)

var (
	// BuildDate is populated on build for version info.
	BuildDate string

	// GitBranch is populated on build for version info.
	GitBranch string

	// GitHash is populated on build for version info.
	GitHash string

	// BuildVersion is populated on build for version info.
	BuildVersion string
)

func main() {
	var (
		addr    = flag.String("addr", ":443", "The address and port to listen on")
		maxsize = flag.Int64("maxsize", 5, "Maximum size to proxy in whole MB (no decimal)")
		secret  = flag.String("secret", "", "The 'shared secret' hmac key")
		tlscert = flag.String("cert", "cert.pem", "The TLS certificate to use")
		tlskey  = flag.String("key", "key.pem", "The TLS key to use")
		verbose = flag.Bool("verbose", false, "If verbose logging should take place (No-op at this time as there's no debug log statements)")
		version = flag.Bool("version", false, "Display version and build info, then exit")

		// TODO(ro) 2017-10-10 Add flags for other proxy set-ables.
		logger zerolog.Logger
	)
	flag.Parse()

	if *version {
		versionInfo()
	}

	// Create a context so we can cancel goroutines.
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Make sure they go away when main exits.
	hostname, err := os.Hostname()
	if err != nil {
		panic("failed to get hostname")
	}
	// Start an aws session, and make the cloudwatchlogs service.
	session := session.Must(session.NewSession())
	svc := cloudwatchlogs.New(session)

	// Create the cloudwatchlog writer.
	cwl := cowl.MustNewWriterWithContext(ctx, svc, app, "app-"+hostname)
	// Write to both stdout and cwl.
	w := io.MultiWriter(cwl, os.Stdout)
	// Set up the logger to use it.
	logger = logging.NewLogger(app, *verbose, w).With().Str(
		"handler", "proxy").Logger()
	if BuildVersion != "" {
		logger = logger.With().
			Str("app_version", BuildVersion+"-"+GitHash).
			Logger()
	}

	stdlog.SetFlags(0)
	stdlog.SetOutput(logger)

	// Stop channel to block until we get a signal. This is used for graceful
	// shutdown.
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)

	// Set up options.
	// TODO(ro) 2017-10-11 Add more options here and as flags as necessary.
	options := []func(*proxy.Proxy){}
	if *maxsize > 0 {
		options = append(options, func(p *proxy.Proxy) { p.MaxSize = *maxsize * 1024 * 1024 })
	}

	// Create proxy handler.
	p := proxy.MustNew([]byte(*secret), logger, options...)
	// Wrap proxy handler with logger.
	proxyHandler := logging.NewAccessLogger(p, logger)
	s := http.Server{Addr: *addr, Handler: proxyHandler}

	// Start TLS server.
	go func() {
		if err := s.ListenAndServeTLS(*tlscert, *tlskey); err != http.ErrServerClosed {
			logger.Fatal().Err(err).Msg("failed to start server")
		}
	}()
	// Start a server on localhost:9000 for expvar.
	go func() {
		if err := http.ListenAndServe("127.0.0.1:9000", nil); err != nil {
			logger.Fatal().Err(err).Msg("failed to start expvar server")
		}
	}()

	<-stop

	// Create a context with timeout to limit shutdown time in the event of
	// slow clients.
	ctx, cancel = context.WithTimeout(ctx, drainTime)
	defer cancel()

	if err := s.Shutdown(ctx); err != nil {
		logger.Error().Err(err).Msg("caught on proxy.Shutdown")
	}
}

func versionInfo() {
	fmt.Printf(`
build date: %s
branch    : %s
hash      : %s
version   : %s
built with: %s
`[1:], BuildDate, GitBranch, GitHash, BuildVersion, runtime.Version())
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path/filepath"

	cli "gopkg.in/urfave/cli.v1"
)

const version = "1.0.2"

func main() {
	var (
		conf hivemindConfig
		err  error
	)

	app := cli.NewApp()

	app.Name = "Hivemind"
	app.HelpName = "hivemind"
	app.Usage = "The mind to rule processes of your development environment"
	app.Description = "Hivemind is a process manager for Procfile-based applications"
	app.Author = "Sergey \"DarthSim\" Alexandrovich"
	app.Email = "darthsim@gmail.com"
	app.Version = version
	app.ArgsUsage = "[procfile]"
	app.HideHelp = true

	app.Flags = []cli.Flag{
		cli.StringFlag{Name: "title, w", Usage: "Specify a title of the application", Destination: &conf.Title},
		cli.StringFlag{Name: "processes, l", Usage: "Specify process names to launch. Divide names with comma", Destination: &conf.ProcNames},
		cli.IntFlag{Name: "port, p", Usage: "specify a port to use as the base", Value: 5000, Destination: &conf.PortBase},
		cli.IntFlag{Name: "port-step, P", Usage: "specify a step to increase port number", Value: 100, Destination: &conf.PortStep},
		cli.StringFlag{Name: "root, d", Usage: "specify a working directory of application. Default: directory containing the Procfile", Destination: &conf.Root},
		cli.IntFlag{Name: "timeout, t", Usage: "specify the amount of time (in seconds) processes have to shut down gracefully before being brutally killed", Value: 5, Destination: &conf.Timeout},
	}

	app.Action = func(c *cli.Context) error {
		switch c.NArg() {
		case 0:
			conf.Procfile = "./Procfile"
		case 1:
			conf.Procfile = c.Args().First()
		default:
			fatal("Specify a single procfile")
		}

		if conf.Timeout < 1 {
			fatal("Timeout should be greater than 0")
		}

		if len(conf.Root) == 0 {
			conf.Root = filepath.Dir(conf.Procfile)
		}

		conf.Root, err = filepath.Abs(conf.Root)
		fatalOnErr(err)

		newHivemind(conf).Run()

		return nil
	}

	app.Run(os.Args)
}
<commit_msg>Bump version<commit_after>package main

import (
	"os"
	"path/filepath"

	cli "gopkg.in/urfave/cli.v1"
)

const version = "1.0.3"

func main() {
	var (
		conf hivemindConfig
		err  error
	)

	app := cli.NewApp()

	app.Name = "Hivemind"
	app.HelpName = "hivemind"
	app.Usage = "The mind to rule processes of your development environment"
	app.Description = "Hivemind is a process manager for Procfile-based applications"
	app.Author = "Sergey \"DarthSim\" Alexandrovich"
	app.Email = "darthsim@gmail.com"
	app.Version = version
	app.ArgsUsage = "[procfile]"
	app.HideHelp = true

	app.Flags = []cli.Flag{
		cli.StringFlag{Name: "title, w", Usage: "Specify a title of the application", Destination: &conf.Title},
		cli.StringFlag{Name: "processes, l", Usage: "Specify process names to launch. Divide names with comma", Destination: &conf.ProcNames},
		cli.IntFlag{Name: "port, p", Usage: "specify a port to use as the base", Value: 5000, Destination: &conf.PortBase},
		cli.IntFlag{Name: "port-step, P", Usage: "specify a step to increase port number", Value: 100, Destination: &conf.PortStep},
		cli.StringFlag{Name: "root, d", Usage: "specify a working directory of application. Default: directory containing the Procfile", Destination: &conf.Root},
		cli.IntFlag{Name: "timeout, t", Usage: "specify the amount of time (in seconds) processes have to shut down gracefully before being brutally killed", Value: 5, Destination: &conf.Timeout},
	}

	app.Action = func(c *cli.Context) error {
		switch c.NArg() {
		case 0:
			conf.Procfile = "./Procfile"
		case 1:
			conf.Procfile = c.Args().First()
		default:
			fatal("Specify a single procfile")
		}

		if conf.Timeout < 1 {
			fatal("Timeout should be greater than 0")
		}

		if len(conf.Root) == 0 {
			conf.Root = filepath.Dir(conf.Procfile)
		}

		conf.Root, err = filepath.Abs(conf.Root)
		fatalOnErr(err)

		newHivemind(conf).Run()

		return nil
	}

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


<commit_before>// Copyright (c) 2014 Ludovic Fauvet
// Licensed under the MIT license

package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"syscall"
	"time"
)

var (
	// Compile time variables
	defaultPidFile string
)

var (
	daemon     bool
	debug      bool
	monitor    bool
	cpuProfile string
	configFile string
	pidFile    string
	runLog     string
)

func init() {
	// Improves perf in 1.1.2 linux/amd64
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.BoolVar(&daemon, "D", false, "Daemon mode")
	flag.BoolVar(&debug, "debug", false, "Debug mode")
	flag.BoolVar(&monitor, "monitor", true, "Enable the background mirrors monitor")
	flag.StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file")
	flag.StringVar(&configFile, "config", "", "Path to the config file")
	flag.StringVar(&pidFile, "p", "", "Path to pid file")
	flag.StringVar(&runLog, "log", "", "File to output logs (default: stderr)")
	flag.Parse()

	LoadConfig()
	ReloadLogs()
}

func main() {

	if debug {
		os.Setenv("DEBUG", "1")
	}

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

	if daemon {
		writePidFile()

		/* Connect to the database */
		r := NewRedis()
		c := NewCache(r)
		h := HTTPServer(r, c)

		/* Handle SIGNALS */
		k := make(chan os.Signal, 1)
		signal.Notify(k,
			syscall.SIGINT,  // Terminate
			syscall.SIGTERM, // Terminate
			syscall.SIGQUIT, // Stop gracefully
			syscall.SIGHUP,  // Reload config
			syscall.SIGUSR1, // Reopen log files
			syscall.SIGUSR2, // Seamless binary upgrade
		)
		go func() {
			for {
				sig := <-k
				switch sig {
				case syscall.SIGINT:
					fallthrough
				case syscall.SIGTERM:
					removePidFile()
					os.Exit(0)
				case syscall.SIGQUIT:
					if h.SListener != nil {
						log.Notice("Waiting for running tasks for finish...")
						h.SListener.Stop <- true
					} else {
						removePidFile()
						os.Exit(0)
					}
				case syscall.SIGHUP:
					listenAddress := GetConfig().ListenAddress
					if err := ReloadConfig(); err != nil {
						log.Warning("SIGHUP Received: %s\n", err)
					} else {
						log.Notice("SIGHUP Received: Reloading configuration...")
					}
					if GetConfig().ListenAddress != listenAddress {
						h.Restarting = true
						h.SListener.Stop <- true
					}
					h.Reload()
				case syscall.SIGUSR1:
					log.Notice("SIGUSR1 Received: Re-opening logs...")
					ReloadLogs()
				case syscall.SIGUSR2:
					log.Notice("SIGUSR2 Received: Seamless binary upgrade...")
					err := Relaunch(h.SListener.Listener)
					if err != nil {
						log.Error("%s", err)
					} else {
						h.SListener.Stop <- true
					}
				}
			}
		}()

		// Show our nice welcome logo
		fmt.Println(welcome)

		/* Start the background monitor */
		m := NewMonitor(r, c)
		if monitor {
			go m.monitorLoop()
		}

		// Recover an existing listener (see process.go)
		if l, ppid, err := Recover(); err == nil {
			h.SetListener(l)
			KillParent(ppid)
		}

		/* Finally start the HTTP server */
		var err error
		for {
			err = h.RunServer()
			if h.SListener.Stopped && h.Restarting {
				h.Restarting = false
				continue
			}
			break
		}

		/* Check why the Serve loop exited */
		if h.SListener.Stopped {
			var alive int32 = h.SListener.ConnCount.Get()
			if alive > 0 {
				log.Info("%d client(s) still connected…\n", alive)
			}

			m.Terminate()

			/* Wait at most 5 seconds for the clients to disconnect */
			for i := 0; i < 5; i++ {
				/* Get the number of clients still connected */
				alive = h.SListener.ConnCount.Get()
				if alive == 0 {
					break
				}
				time.Sleep(1 * time.Second)
			}

			alive = h.SListener.ConnCount.Get()
			h.Terminate()
			if alive > 0 {
				log.Warning("Server stopped after 5 seconds with %d client(s) still connected.", alive)
			} else {
				log.Notice("Server stopped gracefully.")
			}
			removePidFile()
		} else if err != nil {
			removePidFile()
			m.Terminate()
			h.Terminate()
			log.Fatal(err)
		}
	} else {
		if err := ParseCommands(flag.Args()...); err != nil {
			log.Fatal(err)
		}
	}
}
<commit_msg>Fix type error introduced by a local, non-commited change<commit_after>// Copyright (c) 2014 Ludovic Fauvet
// Licensed under the MIT license

package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"syscall"
	"time"
)

var (
	// Compile time variables
	defaultPidFile string
)

var (
	daemon     bool
	debug      bool
	monitor    bool
	cpuProfile string
	configFile string
	pidFile    string
	runLog     string
)

func init() {
	// Improves perf in 1.1.2 linux/amd64
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.BoolVar(&daemon, "D", false, "Daemon mode")
	flag.BoolVar(&debug, "debug", false, "Debug mode")
	flag.BoolVar(&monitor, "monitor", true, "Enable the background mirrors monitor")
	flag.StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file")
	flag.StringVar(&configFile, "config", "", "Path to the config file")
	flag.StringVar(&pidFile, "p", "", "Path to pid file")
	flag.StringVar(&runLog, "log", "", "File to output logs (default: stderr)")
	flag.Parse()

	LoadConfig()
	ReloadLogs()
}

func main() {

	if debug {
		os.Setenv("DEBUG", "1")
	}

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

	if daemon {
		writePidFile()

		/* Connect to the database */
		r := NewRedis()
		c := NewCache(r)
		h := HTTPServer(r, c)

		/* Handle SIGNALS */
		k := make(chan os.Signal, 1)
		signal.Notify(k,
			syscall.SIGINT,  // Terminate
			syscall.SIGTERM, // Terminate
			syscall.SIGQUIT, // Stop gracefully
			syscall.SIGHUP,  // Reload config
			syscall.SIGUSR1, // Reopen log files
			syscall.SIGUSR2, // Seamless binary upgrade
		)
		go func() {
			for {
				sig := <-k
				switch sig {
				case syscall.SIGINT:
					fallthrough
				case syscall.SIGTERM:
					removePidFile()
					os.Exit(0)
				case syscall.SIGQUIT:
					if h.SListener != nil {
						log.Notice("Waiting for running tasks for finish...")
						h.SListener.Stop <- true
					} else {
						removePidFile()
						os.Exit(0)
					}
				case syscall.SIGHUP:
					listenAddress := GetConfig().ListenAddress
					if err := ReloadConfig(); err != nil {
						log.Warning("SIGHUP Received: %s\n", err)
					} else {
						log.Notice("SIGHUP Received: Reloading configuration...")
					}
					if GetConfig().ListenAddress != listenAddress {
						h.Restarting = true
						h.SListener.Stop <- true
					}
					h.Reload()
				case syscall.SIGUSR1:
					log.Notice("SIGUSR1 Received: Re-opening logs...")
					ReloadLogs()
				case syscall.SIGUSR2:
					log.Notice("SIGUSR2 Received: Seamless binary upgrade...")
					err := Relaunch(h.SListener.Listener)
					if err != nil {
						log.Error("%s", err)
					} else {
						h.SListener.Stop <- true
					}
				}
			}
		}()

		// Show our nice welcome logo
		fmt.Println(welcome)

		/* Start the background monitor */
		m := NewMonitor(r, c)
		if monitor {
			go m.monitorLoop()
		}

		// Recover an existing listener (see process.go)
		if l, ppid, err := Recover(); err == nil {
			h.SetListener(l)
			KillParent(ppid)
		}

		/* Finally start the HTTP server */
		var err error
		for {
			err = h.RunServer()
			if h.SListener.Stopped && h.Restarting {
				h.Restarting = false
				continue
			}
			break
		}

		/* Check why the Serve loop exited */
		if h.SListener.Stopped {
			alive := h.SListener.ConnCount.Get()
			if alive > 0 {
				log.Info("%d client(s) still connected…\n", alive)
			}

			m.Terminate()

			/* Wait at most 5 seconds for the clients to disconnect */
			for i := 0; i < 5; i++ {
				/* Get the number of clients still connected */
				alive = h.SListener.ConnCount.Get()
				if alive == 0 {
					break
				}
				time.Sleep(1 * time.Second)
			}

			alive = h.SListener.ConnCount.Get()
			h.Terminate()
			if alive > 0 {
				log.Warning("Server stopped after 5 seconds with %d client(s) still connected.", alive)
			} else {
				log.Notice("Server stopped gracefully.")
			}
			removePidFile()
		} else if err != nil {
			removePidFile()
			m.Terminate()
			h.Terminate()
			log.Fatal(err)
		}
	} else {
		if err := ParseCommands(flag.Args()...); err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

//----------------------------------------------------
// Gobuster -- by OJ Reeves
//
// A crap attempt at building something that resembles
// dirbuster or dirb using Go. The goal was to build
// a tool that would help learn Go and to actually do
// something useful. The idea of having this compile
// to native code is also appealing.
//
// Run: gobuster -h
//----------------------------------------------------

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"
)

// A single result which comes from an individual web
// request.
type Result struct {
	Entity string
	Status int
	Extra  string
}

type PrintResultFunc func(s *State, r *Result)
type ProcessorFunc func(s *State, entity string, resultChan chan<- Result)

// Shim type for "set"
type IntSet struct {
	set map[int]bool
}

// Contains State that are read in from the command
// line when the program is invoked.
type State struct {
	Threads        int
	Wordlist       string
	Url            string
	Cookies        string
	Extensions     []string
	StatusCodes    IntSet
	Verbose        bool
	UseSlash       bool
	FollowRedirect bool
	Quiet          bool
	NoStatus       bool
	Expanded       bool
	Mode           string
	Printer        PrintResultFunc
	Processor      ProcessorFunc
	Client         *http.Client
}

type RedirectHandler struct {
	Transport http.RoundTripper
	State     *State
}

type RedirectError struct {
	StatusCode int
}

// Add an element to a set
func (set *IntSet) Add(i int) bool {
	_, found := set.set[i]
	set.set[i] = true
	return !found
}

// Test if an element is in a set
func (set *IntSet) Contains(i int) bool {
	_, found := set.set[i]
	return found
}

// Stringify the set
func (set *IntSet) Stringify() string {
	values := []string{}
	for s, _ := range set.set {
		values = append(values, strconv.Itoa(s))
	}
	return strings.Join(values, ",")
}

// Make a request to the given URL.
func MakeRequest(client *http.Client, fullUrl, cookie string) *int {
	req, err := http.NewRequest("GET", fullUrl, nil)

	if err != nil {
		return nil
	}

	if cookie != "" {
		req.Header.Set("Cookie", cookie)
	}

	resp, err := client.Do(req)

	if err != nil {
		if ue, ok := err.(*url.Error); ok {
			if re, ok := ue.Err.(*RedirectError); ok {
				return &re.StatusCode
			}
		}
		return nil
	}

	defer resp.Body.Close()

	return &resp.StatusCode
}

// Small helper to combine URL with URI then make a
// request to the generated location.
func GoGet(client *http.Client, url, uri, cookie string) *int {
	return MakeRequest(client, url+uri, cookie)
}

// Parse all the command line options into a settings
// instance for future use.
func ParseCmdLine() *State {
	var extensions string
	var codes string
	valid := true

	s := State{StatusCodes: IntSet{set: map[int]bool{}}}

	// Set up the variables we're interested in parsing.
	flag.IntVar(&s.Threads, "t", 10, "Number of concurrent threads")
	flag.StringVar(&s.Mode, "m", "dir", "Directory/File mode (dir) or DNS mode (dns)")
	flag.StringVar(&s.Wordlist, "w", "", "Path to the wordlist")
	flag.StringVar(&codes, "s", "200,204,301,302,307", "Positive status codes (dir mode only)")
	flag.StringVar(&s.Url, "u", "", "The target URL or Domain")
	flag.StringVar(&s.Cookies, "c", "", "Cookies to use for the requests (dir mode only)")
	flag.StringVar(&extensions, "x", "", "File extension(s) to search for (dir mode only)")
	flag.BoolVar(&s.Verbose, "v", false, "Verbose output (errors and IP addresses")
	flag.BoolVar(&s.FollowRedirect, "r", false, "Follow redirects")
	flag.BoolVar(&s.Quiet, "q", false, "Don't print the banner")
	flag.BoolVar(&s.Expanded, "e", false, "Expanded mode, print full URLs")
	flag.BoolVar(&s.NoStatus, "n", false, "Don't print status codes")
	flag.BoolVar(&s.UseSlash, "f", false, "Append a forward-slash to each directory request (dir mode only)")

	flag.Parse()

	switch strings.ToLower(s.Mode) {
	case "dir":
		s.Printer = PrintDirResult
		s.Processor = ProcessDirEntry
	case "dns":
		s.Printer = PrintDnsResult
		s.Processor = ProcessDnsEntry
	default:
		fmt.Println("Mode (-m): Invalid value:", s.Mode)
		valid = false
	}

	if s.Threads < 0 {
		fmt.Println("Threads (-t): Invalid value:", s.Threads)
		valid = false
	}

	if s.Wordlist == "" {
		fmt.Println("WordList (-w): Must be specified")
		valid = false
	} else if _, err := os.Stat(s.Wordlist); os.IsNotExist(err) {
		fmt.Println("Wordlist (-w): File does not exist:", s.Wordlist)
		valid = false
	}

	if s.Url == "" {
		fmt.Println("Url/Domain (-u): Must be specified")
		valid = false
	}

	if s.Mode == "dir" {
		if strings.HasSuffix(s.Url, "/") == false {
			s.Url = s.Url + "/"
		}

		// extensions are comma seaprated
		if extensions != "" {
			s.Extensions = strings.Split(extensions, ",")
		}

		// status codes are comma seaprated
		if codes != "" {
			for _, c := range strings.Split(codes, ",") {
				i, err := strconv.Atoi(c)
				if err != nil {
					panic("Invalid status code given")
				}
				s.StatusCodes.Add(i)
			}
		}

		if valid {
			s.Client = &http.Client{
				Transport: &RedirectHandler{
					State: &s,
					Transport: &http.Transport{
						TLSClientConfig: &tls.Config{
							InsecureSkipVerify: true,
						},
					},
				}}

			if GoGet(s.Client, s.Url, "", s.Cookies) == nil {
				fmt.Println("[-] Unable to connect:", s.Url)
				valid = false
			}
		}
	}

	if valid {
		return &s
	}

	return nil
}

// Process the busting of the website with the given
// set of settings from the command line.
func Process(s *State) {
	wordlist, err := os.Open(s.Wordlist)
	if err != nil {
		panic("Failed to open wordlist")
	}

	Banner(s)

	// channels used for comms
	wordChan := make(chan string, s.Threads)
	resultChan := make(chan Result)

	// Use a wait group for waiting for all threads
	// to finish
	processorGroup := new(sync.WaitGroup)
	processorGroup.Add(s.Threads)
	printerGroup := new(sync.WaitGroup)
	printerGroup.Add(1)

	// Create goroutines for each of the number of threads
	// specified.
	for i := 0; i < s.Threads; i++ {
		go func() {
			for {
				word := <-wordChan

				// Did we reach the end? If so break.
				if word == "" {
					break
				}

				// Mode-specific processing
				s.Processor(s, word, resultChan)
			}

			// Indicate to the wait group that the thread
			// has finished.
			processorGroup.Done()
		}()
	}

	// Single goroutine which handles the results as they
	// appear from the worker threads.
	go func() {
		for r := range resultChan {
			s.Printer(s, &r)
		}
		printerGroup.Done()
	}()

	defer wordlist.Close()

	// Lazy reading of the wordlist line by line
	scanner := bufio.NewScanner(wordlist)
	for scanner.Scan() {
		word := scanner.Text()

		// Skip "comment" lines
		if strings.HasPrefix(word, "#") == false {
			wordChan <- word
		}
	}

	close(wordChan)
	processorGroup.Wait()
	close(resultChan)
	printerGroup.Wait()
	Ruler(s)
}

func ProcessDnsEntry(s *State, word string, resultChan chan<- Result) {
	subdomain := word + "." + s.Url
	ips, err := net.LookupHost(subdomain)

	if err == nil {
		result := Result{
			Entity: subdomain,
		}
		if s.Verbose {
			result.Extra = strings.Join(ips, ", ")
		}
		resultChan <- result
	}
}

func ProcessDirEntry(s *State, word string, resultChan chan<- Result) {
	suffix := ""
	if s.UseSlash {
		suffix = "/"
	}

	// Try the DIR first
	dirResp := GoGet(s.Client, s.Url, word+suffix, s.Cookies)
	if dirResp != nil {
		resultChan <- Result{
			Entity: word + suffix,
			Status: *dirResp,
		}
	}

	// Follow up with files using each ext.
	for ext := range s.Extensions {
		file := word + s.Extensions[ext]
		fileResp := GoGet(s.Client, s.Url, file, s.Cookies)
		if fileResp != nil {
			resultChan <- Result{
				Entity: file,
				Status: *fileResp,
			}
		}
	}
}

func PrintDnsResult(s *State, r *Result) {
	if s.Verbose {
		fmt.Printf("Found: %s [%s]\n", r.Entity, r.Extra)
	} else {
		fmt.Printf("Found: %s\n", r.Entity)
	}
}

func PrintDirResult(s *State, r *Result) {
	output := ""

	// Prefix if we're in verbose mode
	if s.Verbose {
		if s.StatusCodes.Contains(r.Status) {
			output += "Found : "
		} else {
			output += "Missed: "
		}
	}

	if s.StatusCodes.Contains(r.Status) || s.Verbose {
		if s.Expanded {
			output += s.Url
		} else {
			output += "/"
		}
		output += r.Entity

		if !s.NoStatus {
			output += fmt.Sprintf(" (%d)", r.Status)
		}

		fmt.Println(output)
	}
}

func (e *RedirectError) Error() string {
	return fmt.Sprintf("Redirect code: %d", e.StatusCode)
}

func (rh *RedirectHandler) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	if rh.State.FollowRedirect {
		return rh.Transport.RoundTrip(req)
	}

	resp, err = rh.Transport.RoundTrip(req)
	if err != nil {
		return resp, err
	}

	switch resp.StatusCode {
	case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther,
		http.StatusNotModified, http.StatusUseProxy, http.StatusTemporaryRedirect:
		return nil, &RedirectError{StatusCode: resp.StatusCode}
	}

	return resp, err
}

func Ruler(s *State) {
	if !s.Quiet {
		fmt.Println("=====================================================")
	}
}

func Banner(state *State) {
	if state.Quiet {
		return
	}

	fmt.Println("")
	Ruler(state)
	fmt.Println("Gobuster v0.8 (DIR support by OJ Reeves @TheColonial)")
	fmt.Println("              (DNS support by Peleus     @0x42424242)")
	Ruler(state)

	if state != nil {
		fmt.Printf("[+] Mode         : %s\n", state.Mode)
		fmt.Printf("[+] Url/Domain   : %s\n", state.Url)
		fmt.Printf("[+] Threads      : %d\n", state.Threads)
		fmt.Printf("[+] Wordlist     : %s\n", state.Wordlist)

		if state.Mode == "dir" {
			fmt.Printf("[+] Status codes : %s\n", state.StatusCodes.Stringify())

			if state.Cookies != "" {
				fmt.Printf("[+] Cookies      : %s\n", state.Cookies)
			}

			if len(state.Extensions) > 0 {
				fmt.Printf("[+] Extensions   : %s\n", strings.Join(state.Extensions, ","))
			}

			if state.UseSlash {
				fmt.Printf("[+] Add Slash    : true\n")
			}

			if state.FollowRedirect {
				fmt.Printf("[+] Follow Redir : true\n")
			}

			if state.Expanded {
				fmt.Printf("[+] Expanded     : true\n")
			}

			if state.NoStatus {
				fmt.Printf("[+] No status    : true\n")
			}

			if state.Verbose {
				fmt.Printf("[+] Verbose      : true\n")
			}
		}
		Ruler(state)
	}
}

func main() {
	state := ParseCmdLine()
	if state != nil {
		Process(state)
	}
}
<commit_msg>Reimplemented wildcard check<commit_after>package main

//----------------------------------------------------
// Gobuster -- by OJ Reeves
//
// A crap attempt at building something that resembles
// dirbuster or dirb using Go. The goal was to build
// a tool that would help learn Go and to actually do
// something useful. The idea of having this compile
// to native code is also appealing.
//
// Run: gobuster -h
//----------------------------------------------------

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"
)

// A single result which comes from an individual web
// request.
type Result struct {
	Entity string
	Status int
	Extra  string
}

type PrintResultFunc func(s *State, r *Result)
type ProcessorFunc func(s *State, entity string, resultChan chan<- Result)
type SetupFunc func(s *State) bool

// Shim type for "set"
type IntSet struct {
	set map[int]bool
}

// Contains State that are read in from the command
// line when the program is invoked.
type State struct {
	Threads        int
	Wordlist       string
	Url            string
	Cookies        string
	Extensions     []string
	StatusCodes    IntSet
	Verbose        bool
	UseSlash       bool
	FollowRedirect bool
	Quiet          bool
	NoStatus       bool
	Expanded       bool
	Mode           string
	Setup          SetupFunc
	Printer        PrintResultFunc
	Processor      ProcessorFunc
	Client         *http.Client
}

type RedirectHandler struct {
	Transport http.RoundTripper
	State     *State
}

type RedirectError struct {
	StatusCode int
}

// Add an element to a set
func (set *IntSet) Add(i int) bool {
	_, found := set.set[i]
	set.set[i] = true
	return !found
}

// Test if an element is in a set
func (set *IntSet) Contains(i int) bool {
	_, found := set.set[i]
	return found
}

// Stringify the set
func (set *IntSet) Stringify() string {
	values := []string{}
	for s, _ := range set.set {
		values = append(values, strconv.Itoa(s))
	}
	return strings.Join(values, ",")
}

// Make a request to the given URL.
func MakeRequest(client *http.Client, fullUrl, cookie string) *int {
	req, err := http.NewRequest("GET", fullUrl, nil)

	if err != nil {
		return nil
	}

	if cookie != "" {
		req.Header.Set("Cookie", cookie)
	}

	resp, err := client.Do(req)

	if err != nil {
		if ue, ok := err.(*url.Error); ok {
			if re, ok := ue.Err.(*RedirectError); ok {
				return &re.StatusCode
			}
		}
		return nil
	}

	defer resp.Body.Close()

	return &resp.StatusCode
}

// Small helper to combine URL with URI then make a
// request to the generated location.
func GoGet(client *http.Client, url, uri, cookie string) *int {
	return MakeRequest(client, url+uri, cookie)
}

// Parse all the command line options into a settings
// instance for future use.
func ParseCmdLine() *State {
	var extensions string
	var codes string
	valid := true

	s := State{StatusCodes: IntSet{set: map[int]bool{}}}

	// Set up the variables we're interested in parsing.
	flag.IntVar(&s.Threads, "t", 10, "Number of concurrent threads")
	flag.StringVar(&s.Mode, "m", "dir", "Directory/File mode (dir) or DNS mode (dns)")
	flag.StringVar(&s.Wordlist, "w", "", "Path to the wordlist")
	flag.StringVar(&codes, "s", "200,204,301,302,307", "Positive status codes (dir mode only)")
	flag.StringVar(&s.Url, "u", "", "The target URL or Domain")
	flag.StringVar(&s.Cookies, "c", "", "Cookies to use for the requests (dir mode only)")
	flag.StringVar(&extensions, "x", "", "File extension(s) to search for (dir mode only)")
	flag.BoolVar(&s.Verbose, "v", false, "Verbose output (errors and IP addresses")
	flag.BoolVar(&s.FollowRedirect, "r", false, "Follow redirects")
	flag.BoolVar(&s.Quiet, "q", false, "Don't print the banner")
	flag.BoolVar(&s.Expanded, "e", false, "Expanded mode, print full URLs")
	flag.BoolVar(&s.NoStatus, "n", false, "Don't print status codes")
	flag.BoolVar(&s.UseSlash, "f", false, "Append a forward-slash to each directory request (dir mode only)")

	flag.Parse()

	switch strings.ToLower(s.Mode) {
	case "dir":
		s.Printer = PrintDirResult
		s.Processor = ProcessDirEntry
		s.Setup = SetupDir
	case "dns":
		s.Printer = PrintDnsResult
		s.Processor = ProcessDnsEntry
		s.Setup = SetupDns
	default:
		fmt.Println("Mode (-m): Invalid value:", s.Mode)
		valid = false
	}

	if s.Threads < 0 {
		fmt.Println("Threads (-t): Invalid value:", s.Threads)
		valid = false
	}

	if s.Wordlist == "" {
		fmt.Println("WordList (-w): Must be specified")
		valid = false
	} else if _, err := os.Stat(s.Wordlist); os.IsNotExist(err) {
		fmt.Println("Wordlist (-w): File does not exist:", s.Wordlist)
		valid = false
	}

	if s.Url == "" {
		fmt.Println("Url/Domain (-u): Must be specified")
		valid = false
	}

	if s.Mode == "dir" {
		if strings.HasSuffix(s.Url, "/") == false {
			s.Url = s.Url + "/"
		}

		// extensions are comma seaprated
		if extensions != "" {
			s.Extensions = strings.Split(extensions, ",")
		}

		// status codes are comma seaprated
		if codes != "" {
			for _, c := range strings.Split(codes, ",") {
				i, err := strconv.Atoi(c)
				if err != nil {
					panic("Invalid status code given")
				}
				s.StatusCodes.Add(i)
			}
		}

		if valid {
			s.Client = &http.Client{
				Transport: &RedirectHandler{
					State: &s,
					Transport: &http.Transport{
						TLSClientConfig: &tls.Config{
							InsecureSkipVerify: true,
						},
					},
				}}

			if GoGet(s.Client, s.Url, "", s.Cookies) == nil {
				fmt.Println("[-] Unable to connect:", s.Url)
				valid = false
			}
		}
	}

	if valid {
		return &s
	}

	return nil
}

// Process the busting of the website with the given
// set of settings from the command line.
func Process(s *State) {
	wordlist, err := os.Open(s.Wordlist)
	if err != nil {
		panic("Failed to open wordlist")
	}

	Banner(s)

	if s.Setup(s) == false {
		Ruler(s)
		return
	}

	// channels used for comms
	wordChan := make(chan string, s.Threads)
	resultChan := make(chan Result)

	// Use a wait group for waiting for all threads
	// to finish
	processorGroup := new(sync.WaitGroup)
	processorGroup.Add(s.Threads)
	printerGroup := new(sync.WaitGroup)
	printerGroup.Add(1)

	// Create goroutines for each of the number of threads
	// specified.
	for i := 0; i < s.Threads; i++ {
		go func() {
			for {
				word := <-wordChan

				// Did we reach the end? If so break.
				if word == "" {
					break
				}

				// Mode-specific processing
				s.Processor(s, word, resultChan)
			}

			// Indicate to the wait group that the thread
			// has finished.
			processorGroup.Done()
		}()
	}

	// Single goroutine which handles the results as they
	// appear from the worker threads.
	go func() {
		for r := range resultChan {
			s.Printer(s, &r)
		}
		printerGroup.Done()
	}()

	defer wordlist.Close()

	// Lazy reading of the wordlist line by line
	scanner := bufio.NewScanner(wordlist)
	for scanner.Scan() {
		word := scanner.Text()

		// Skip "comment" lines
		if strings.HasPrefix(word, "#") == false {
			wordChan <- word
		}
	}

	close(wordChan)
	processorGroup.Wait()
	close(resultChan)
	printerGroup.Wait()
	Ruler(s)
}

func SetupDns(s *State) bool {
	// Resolve a subdomain that probably shouldn't exist
	_, err := net.LookupHost("bba1b18d-50f8-4f1d-8295-c861445ed7f5." + s.Url)
	if err == nil {
		fmt.Println("Wildcard DNS found.")
		return false
	}
	return true
}

func SetupDir(s *State) bool {
	return true
}

func ProcessDnsEntry(s *State, word string, resultChan chan<- Result) {
	subdomain := word + "." + s.Url
	ips, err := net.LookupHost(subdomain)

	if err == nil {
		result := Result{
			Entity: subdomain,
		}
		if s.Verbose {
			result.Extra = strings.Join(ips, ", ")
		}
		resultChan <- result
	}
}

func ProcessDirEntry(s *State, word string, resultChan chan<- Result) {
	suffix := ""
	if s.UseSlash {
		suffix = "/"
	}

	// Try the DIR first
	dirResp := GoGet(s.Client, s.Url, word+suffix, s.Cookies)
	if dirResp != nil {
		resultChan <- Result{
			Entity: word + suffix,
			Status: *dirResp,
		}
	}

	// Follow up with files using each ext.
	for ext := range s.Extensions {
		file := word + s.Extensions[ext]
		fileResp := GoGet(s.Client, s.Url, file, s.Cookies)
		if fileResp != nil {
			resultChan <- Result{
				Entity: file,
				Status: *fileResp,
			}
		}
	}
}

func PrintDnsResult(s *State, r *Result) {
	if s.Verbose {
		fmt.Printf("Found: %s [%s]\n", r.Entity, r.Extra)
	} else {
		fmt.Printf("Found: %s\n", r.Entity)
	}
}

func PrintDirResult(s *State, r *Result) {
	output := ""

	// Prefix if we're in verbose mode
	if s.Verbose {
		if s.StatusCodes.Contains(r.Status) {
			output += "Found : "
		} else {
			output += "Missed: "
		}
	}

	if s.StatusCodes.Contains(r.Status) || s.Verbose {
		if s.Expanded {
			output += s.Url
		} else {
			output += "/"
		}
		output += r.Entity

		if !s.NoStatus {
			output += fmt.Sprintf(" (%d)", r.Status)
		}

		fmt.Println(output)
	}
}

func (e *RedirectError) Error() string {
	return fmt.Sprintf("Redirect code: %d", e.StatusCode)
}

func (rh *RedirectHandler) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	if rh.State.FollowRedirect {
		return rh.Transport.RoundTrip(req)
	}

	resp, err = rh.Transport.RoundTrip(req)
	if err != nil {
		return resp, err
	}

	switch resp.StatusCode {
	case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther,
		http.StatusNotModified, http.StatusUseProxy, http.StatusTemporaryRedirect:
		return nil, &RedirectError{StatusCode: resp.StatusCode}
	}

	return resp, err
}

func Ruler(s *State) {
	if !s.Quiet {
		fmt.Println("=====================================================")
	}
}

func Banner(state *State) {
	if state.Quiet {
		return
	}

	fmt.Println("")
	Ruler(state)
	fmt.Println("Gobuster v0.8 (DIR support by OJ Reeves @TheColonial)")
	fmt.Println("              (DNS support by Peleus     @0x42424242)")
	Ruler(state)

	if state != nil {
		fmt.Printf("[+] Mode         : %s\n", state.Mode)
		fmt.Printf("[+] Url/Domain   : %s\n", state.Url)
		fmt.Printf("[+] Threads      : %d\n", state.Threads)
		fmt.Printf("[+] Wordlist     : %s\n", state.Wordlist)

		if state.Mode == "dir" {
			fmt.Printf("[+] Status codes : %s\n", state.StatusCodes.Stringify())

			if state.Cookies != "" {
				fmt.Printf("[+] Cookies      : %s\n", state.Cookies)
			}

			if len(state.Extensions) > 0 {
				fmt.Printf("[+] Extensions   : %s\n", strings.Join(state.Extensions, ","))
			}

			if state.UseSlash {
				fmt.Printf("[+] Add Slash    : true\n")
			}

			if state.FollowRedirect {
				fmt.Printf("[+] Follow Redir : true\n")
			}

			if state.Expanded {
				fmt.Printf("[+] Expanded     : true\n")
			}

			if state.NoStatus {
				fmt.Printf("[+] No status    : true\n")
			}

			if state.Verbose {
				fmt.Printf("[+] Verbose      : true\n")
			}
		}

		Ruler(state)
	}
}

func main() {
	state := ParseCmdLine()
	if state != nil {
		Process(state)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"regexp"
)

func main() {
	resp, _ := http.Get("http://hichannel.hinet.net/radio/mobile/index.do?id=232")
	defer resp.Body.Close()
	html := new(bytes.Buffer)
	html.ReadFrom(resp.Body)
	//fmt.Println(html.String())
	reg := regexp.MustCompile(`var url = '([\S]+)'`)
	url_string := reg.FindAllStringSubmatch(html.String(), -1)
	//fmt.Println(url_string[0][1])

	replace := regexp.MustCompile(`\\\/`)
	replace_string := replace.ReplaceAllString(url_string[0][1], `/`)
	fmt.Println(replace_string)

	//fmt.Println("----- test open -----\r\n")
	//exec.Command("open", "-a", "firefox").Run()
}
<commit_msg>Add `GetUrl`<commit_after>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"regexp"
)

func GetUrl(url_no string) (replace_string string) {
	url := fmt.Sprintf("http://hichannel.hinet.net/radio/mobile/index.do?id=%s", url_no)
	//resp, _ := http.Get("http://hichannel.hinet.net/radio/mobile/index.do?id=232")
	//resp, _ := http.Get("http://hichannel.hinet.net/radio/mobile/index.do?id=207")
	resp, _ := http.Get(url)
	defer resp.Body.Close()
	html := new(bytes.Buffer)
	html.ReadFrom(resp.Body)
	//fmt.Println(html.String())
	reg := regexp.MustCompile(`var url = '([\S]+)'`)
	url_string := reg.FindAllStringSubmatch(html.String(), -1)
	//fmt.Println(url_string[0][1])

	replace := regexp.MustCompile(`\\\/`)
	replace_string = replace.ReplaceAllString(url_string[0][1], `/`)
	//fmt.Println(replace_string)
	return
}

func main() {
	//fmt.Println("----- test open -----\r\n")
	//exec.Command("open", "-a", "firefox").Run()

	fmt.Println(GetUrl("207"))

	//in := bufio.NewReader(os.Stdin)
	//line, _ := in.ReadString('\n')
	//fmt.Println(line)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/a10y/classy/classfile"
	"github.com/fatih/color"
)

var (
	HeaderColorizer  *color.Color = color.New(color.FgYellow, color.Bold)
	SuccessColorizer              = color.New(color.BgWhite, color.FgBlack)
	ErrorColorizer                = color.New(color.FgWhite, color.BgRed)
	AuxColorizer                  = color.New(color.FgBlue, color.Bold)
	FieldTypeColor                = color.New(color.FgHiMagenta)
	FieldNameColor                = color.New(color.FgCyan, color.Bold)
)

func main() {
	data, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		panic(err)
	}
	classFile := classfile.ReadClassFile(data)

	validMsg := SuccessColorizer.Sprint("valid")
	if classFile.Magic != 0xCAFEBABE {
		validMsg = ErrorColorizer.Sprintf("INVALID")
	}

	AuxColorizer.Printf("Magic:")
	fmt.Printf(" 0x%X (%v)\n", classFile.Magic, validMsg)
	AuxColorizer.Printf("Major:")
	fmt.Printf(" %v\n", classFile.MajorVersion)
	AuxColorizer.Printf("Minor:")
	fmt.Printf(" %v\n", classFile.MinorVersion)

	HeaderColorizer.Printf("\nConstantPool:")
	fmt.Printf(" (%v entries)\n", classFile.ConstantPoolCount)
	printCP(classFile)

	HeaderColorizer.Printf("\nMethods:")
	fmt.Printf(" (%v entries)\n", classFile.MethodsCount)
	printMethods(classFile)

	HeaderColorizer.Printf("\nFields:")
	fmt.Printf(" (%v entries)\n", classFile.FieldsCount)
	printFields(classFile)

	HeaderColorizer.Printf("\nAttrs:")
	fmt.Printf(" (%v entries)\n", classFile.AttrsCount)
	printAttrs(classFile)
}

func printCP(cf *classfile.ClassFile) {
	for i, cpEntry := range cf.ConstantPool {
		// Skip over empty continuation slots for 8-byte constants
		if cpEntry == nil {
			continue
		}
		branch := "├──"
		if i == int(cf.ConstantPoolCount)-2 {
			branch = "└──"
		}
		fmt.Printf("  %v %02d: %v\n", branch, i+1, cpEntry.StringTag())
		fmt.Printf("  │\t\t%v\n", cpEntry.Display(cf.ConstantPool))
	}
}

func printMethods(cf *classfile.ClassFile) {
	for i, meth := range cf.Methods {
		branch := "├──"
		if i == int(cf.MethodsCount)-1 {
			branch = "└──"
		}
		name := meth.Name(cf.ConstantPool)
		params, ret := classfile.ParseMethodDescriptor(meth.Descriptor(cf.ConstantPool))
		repr := fmt.Sprintf("%v %v(%v)\n", ret, name, strings.Join(params, ", "))
		fmt.Printf("  %v %v", branch, repr)
	}
}

func printFields(cf *classfile.ClassFile) {
	for i, field := range cf.Fields {
		branch := "├──"
		if i == int(cf.FieldsCount)-1 {
			branch = "└──"
		}
		desc := FieldTypeColor.Sprint(classfile.ParseFieldDescriptor(field.Descriptor(cf.ConstantPool)))
		name := FieldNameColor.Sprint(field.Name(cf.ConstantPool))
		fmt.Printf("  %v %v %v\n", branch, desc, name)
	}
}

func printAttrs(cf *classfile.ClassFile) {
	for i, attr := range cf.Attrs {
		branch := "├──"
		if i == int(cf.AttrsCount)-1 {
			branch = "└──"
		}
		fmt.Printf("  %v %v\n", branch, attr.Name(cf.ConstantPool))
	}
}
<commit_msg>colorz<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/a10y/classy/classfile"
	"github.com/fatih/color"
)

var (
	HeaderColorizer  *color.Color = color.New(color.FgYellow, color.Bold)
	SuccessColorizer              = color.New(color.BgWhite, color.FgBlack)
	ErrorColorizer                = color.New(color.FgWhite, color.BgRed)
	AuxColorizer                  = color.New(color.FgBlue, color.Bold)
	FieldTypeColor                = color.New(color.FgHiMagenta)
	FieldNameColor                = color.New(color.FgCyan, color.Bold)
	ParamTypeColor                = color.New(color.FgRed)
)

func main() {
	data, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		panic(err)
	}
	classFile := classfile.ReadClassFile(data)

	validMsg := SuccessColorizer.Sprint("valid")
	if classFile.Magic != 0xCAFEBABE {
		validMsg = ErrorColorizer.Sprintf("INVALID")
	}

	AuxColorizer.Printf("Magic:")
	fmt.Printf(" 0x%X (%v)\n", classFile.Magic, validMsg)
	AuxColorizer.Printf("Major:")
	fmt.Printf(" %v\n", classFile.MajorVersion)
	AuxColorizer.Printf("Minor:")
	fmt.Printf(" %v\n", classFile.MinorVersion)

	HeaderColorizer.Printf("\nConstantPool:")
	fmt.Printf(" (%v entries)\n", classFile.ConstantPoolCount)
	printCP(classFile)

	HeaderColorizer.Printf("\nMethods:")
	fmt.Printf(" (%v entries)\n", classFile.MethodsCount)
	printMethods(classFile)

	HeaderColorizer.Printf("\nFields:")
	fmt.Printf(" (%v entries)\n", classFile.FieldsCount)
	printFields(classFile)

	HeaderColorizer.Printf("\nAttrs:")
	fmt.Printf(" (%v entries)\n", classFile.AttrsCount)
	printAttrs(classFile)
}

func printCP(cf *classfile.ClassFile) {
	for i, cpEntry := range cf.ConstantPool {
		// Skip over empty continuation slots for 8-byte constants
		if cpEntry == nil {
			continue
		}
		branch := "├──"
		if i == int(cf.ConstantPoolCount)-2 {
			branch = "└──"
		}
		fmt.Printf("  %v %02d: %v\n", branch, i+1, cpEntry.StringTag())
		fmt.Printf("  │\t\t%v\n", cpEntry.Display(cf.ConstantPool))
	}
}

func printMethods(cf *classfile.ClassFile) {
	for i, meth := range cf.Methods {
		branch := "├──"
		if i == int(cf.MethodsCount)-1 {
			branch = "└──"
		}
		name := meth.Name(cf.ConstantPool)
		paramSlice, ret := classfile.ParseMethodDescriptor(meth.Descriptor(cf.ConstantPool))
		ret = FieldTypeColor.Sprint(ret)
		name = FieldNameColor.Sprint(name)
		params := ParamTypeColor.Sprint(strings.Join(paramSlice, ", "))
		repr := fmt.Sprintf("%v %v(%v)\n", ret, name, params)
		fmt.Printf("  %v %v", branch, repr)
	}
}

func printFields(cf *classfile.ClassFile) {
	for i, field := range cf.Fields {
		branch := "├──"
		if i == int(cf.FieldsCount)-1 {
			branch = "└──"
		}
		desc := FieldTypeColor.Sprint(classfile.ParseFieldDescriptor(field.Descriptor(cf.ConstantPool)))
		name := FieldNameColor.Sprint(field.Name(cf.ConstantPool))
		fmt.Printf("  %v %v %v\n", branch, desc, name)
	}
}

func printAttrs(cf *classfile.ClassFile) {
	for i, attr := range cf.Attrs {
		branch := "├──"
		if i == int(cf.AttrsCount)-1 {
			branch = "└──"
		}
		fmt.Printf("  %v %v\n", branch, attr.Name(cf.ConstantPool))
	}
}
<|endoftext|>


<commit_before>package main

// Copyright 2016 Nils Decker
//
// 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.

import (
	"flag"
	"fmt"
	"net/http"

	"github.com/prometheus/client_golang/prometheus"

	upnp "github.com/ndecker/fritzbox_exporter/fritzbox_upnp"
)

var (
	flag_test = flag.Bool("test", false, "print all available metrics to stdout")
	flag_addr = flag.String("listen-address", ":9133", "The address to listen on for HTTP requests.")

	flag_gateway_address = flag.String("gateway-address", "fritz.box", "The URL of the upnp service")
	flag_gateway_port    = flag.Int("gateway-port", 49000, "The URL of the upnp service")
)

var (
	collect_errors = prometheus.NewCounter(prometheus.CounterOpts{
		Name: "fritzbox_exporter_collect_errors",
		Help: "Number of collection errors.",
	})
)

type Metric struct {
	Service string
	Action  string
	Result  string
	OkValue string

	Desc       *prometheus.Desc
	MetricType prometheus.ValueType
}

var metrics = []*Metric{
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetTotalPacketsReceived",
		Result:  "TotalPacketsReceived",
		Desc: prometheus.NewDesc(
			"gateway_wan_packets_received",
			"packets received on gateway WAN interface",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.CounterValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetTotalPacketsSent",
		Result:  "TotalPacketsSent",
		Desc: prometheus.NewDesc(
			"gateway_wan_packets_sent",
			"packets sent on gateway WAN interface",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.CounterValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetAddonInfos",
		Result:  "TotalBytesReceived",
		Desc: prometheus.NewDesc(
			"gateway_wan_bytes_received",
			"bytes received on gateway WAN interface",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.CounterValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetAddonInfos",
		Result:  "TotalBytesSent",
		Desc: prometheus.NewDesc(
			"gateway_wan_bytes_sent",
			"bytes sent on gateway WAN interface",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.CounterValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetCommonLinkProperties",
		Result:  "Layer1UpstreamMaxBitRate",
		Desc: prometheus.NewDesc(
			"gateway_wan_layer1_upstream_max_bitrate",
			"Layer1 upstream max bitrate",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetCommonLinkProperties",
		Result:  "Layer1DownstreamMaxBitRate",
		Desc: prometheus.NewDesc(
			"gateway_wan_layer1_downstream_max_bitrate",
			"Layer1 downstream max bitrate",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetCommonLinkProperties",
		Result:  "PhysicalLinkStatus",
		OkValue: "Up",
		Desc: prometheus.NewDesc(
			"gateway_wan_layer1_link_status",
			"Status of physical link (Up = 1)",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANIPConnection:1",
		Action:  "GetStatusInfo",
		Result:  "ConnectionStatus",
		OkValue: "Connected",
		Desc: prometheus.NewDesc(
			"gateway_wan_connection_status",
			"WAN connection status (Connected = 1)",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANIPConnection:1",
		Action:  "GetStatusInfo",
		Result:  "Uptime",
		Desc: prometheus.NewDesc(
			"gateway_wan_connection_uptime_seconds",
			"WAN connection uptime",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
}

type FritzboxCollector struct {
	Root    *upnp.Root
	Gateway string
}

func (fc *FritzboxCollector) Describe(ch chan<- *prometheus.Desc) {
	for _, m := range metrics {
		ch <- m.Desc
	}
}

func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) {
	var err error
	var last_service string
	var last_method string
	var last_result upnp.Result

	for _, m := range metrics {
		if m.Service != last_service || m.Action != last_method {
			service, ok := fc.Root.Services[m.Service]
			if !ok {
				// TODO
				fmt.Println("cannot find service", m.Service)
				fmt.Println(fc.Root.Services)
				continue
			}
			action, ok := service.Actions[m.Action]
			if !ok {
				// TODO
				fmt.Println("cannot find action", m.Action)
				continue
			}

			last_result, err = action.Call()
			if err != nil {
				fmt.Println(err)
				collect_errors.Inc()
				continue
			}
		}

		val, ok := last_result[m.Result]
		if !ok {
			fmt.Println("result not found", m.Result)
			collect_errors.Inc()
			continue
		}

		var floatval float64
		switch tval := val.(type) {
		case uint64:
			floatval = float64(tval)
		case bool:
			if tval {
				floatval = 1
			} else {
				floatval = 0
			}
		case string:
			if tval == m.OkValue {
				floatval = 1
			} else {
				floatval = 0
			}
		default:
			fmt.Println("unknown", val)
			collect_errors.Inc()
			continue

		}

		ch <- prometheus.MustNewConstMetric(
			m.Desc,
			m.MetricType,
			floatval,
			fc.Gateway,
		)
	}
}

func main() {
	flag.Parse()

	root, err := upnp.LoadServices(*flag_gateway_address, uint16(*flag_gateway_port))
	if err != nil {
		panic(err)
	}

	if *flag_test {
		for _, s := range root.Services {
			fmt.Printf("%s: %s\n", s.Device.FriendlyName, s.ServiceType)
			for _, a := range s.Actions {
				if !a.IsGetOnly() {
					continue
				}

				res, err := a.Call()
				if err != nil {
					panic(err)
				}

				fmt.Printf("  %s\n", a.Name)
				for _, arg := range a.Arguments {
					fmt.Printf("    %s: %v\n", arg.RelatedStateVariable, res[arg.StateVariable.Name])
				}
			}
		}

		return
	}

	prometheus.MustRegister(&FritzboxCollector{root, *flag_gateway_address})
	prometheus.MustRegister(collect_errors)

	// Since we are dealing with custom Collector implementations, it might
	// be a good idea to enable the collect checks in the registry.
	prometheus.EnableCollectChecks(true)

	http.Handle("/metrics", prometheus.Handler())
	http.ListenAndServe(*flag_addr, nil)
}
<commit_msg>fix panic when fritzbox is not reachable on startup<commit_after>package main

// Copyright 2016 Nils Decker
//
// 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.

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

	"github.com/prometheus/client_golang/prometheus"

	upnp "github.com/ndecker/fritzbox_exporter/fritzbox_upnp"
)

const serviceLoadRetryTime = 1 * time.Minute

var (
	flag_test = flag.Bool("test", false, "print all available metrics to stdout")
	flag_addr = flag.String("listen-address", ":9133", "The address to listen on for HTTP requests.")

	flag_gateway_address = flag.String("gateway-address", "fritz.box", "The URL of the upnp service")
	flag_gateway_port    = flag.Int("gateway-port", 49000, "The URL of the upnp service")
)

var (
	collect_errors = prometheus.NewCounter(prometheus.CounterOpts{
		Name: "fritzbox_exporter_collect_errors",
		Help: "Number of collection errors.",
	})
)

type Metric struct {
	Service string
	Action  string
	Result  string
	OkValue string

	Desc       *prometheus.Desc
	MetricType prometheus.ValueType
}

var metrics = []*Metric{
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetTotalPacketsReceived",
		Result:  "TotalPacketsReceived",
		Desc: prometheus.NewDesc(
			"gateway_wan_packets_received",
			"packets received on gateway WAN interface",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.CounterValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetTotalPacketsSent",
		Result:  "TotalPacketsSent",
		Desc: prometheus.NewDesc(
			"gateway_wan_packets_sent",
			"packets sent on gateway WAN interface",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.CounterValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetAddonInfos",
		Result:  "TotalBytesReceived",
		Desc: prometheus.NewDesc(
			"gateway_wan_bytes_received",
			"bytes received on gateway WAN interface",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.CounterValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetAddonInfos",
		Result:  "TotalBytesSent",
		Desc: prometheus.NewDesc(
			"gateway_wan_bytes_sent",
			"bytes sent on gateway WAN interface",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.CounterValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetCommonLinkProperties",
		Result:  "Layer1UpstreamMaxBitRate",
		Desc: prometheus.NewDesc(
			"gateway_wan_layer1_upstream_max_bitrate",
			"Layer1 upstream max bitrate",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetCommonLinkProperties",
		Result:  "Layer1DownstreamMaxBitRate",
		Desc: prometheus.NewDesc(
			"gateway_wan_layer1_downstream_max_bitrate",
			"Layer1 downstream max bitrate",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
		Action:  "GetCommonLinkProperties",
		Result:  "PhysicalLinkStatus",
		OkValue: "Up",
		Desc: prometheus.NewDesc(
			"gateway_wan_layer1_link_status",
			"Status of physical link (Up = 1)",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANIPConnection:1",
		Action:  "GetStatusInfo",
		Result:  "ConnectionStatus",
		OkValue: "Connected",
		Desc: prometheus.NewDesc(
			"gateway_wan_connection_status",
			"WAN connection status (Connected = 1)",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
	{
		Service: "urn:schemas-upnp-org:service:WANIPConnection:1",
		Action:  "GetStatusInfo",
		Result:  "Uptime",
		Desc: prometheus.NewDesc(
			"gateway_wan_connection_uptime_seconds",
			"WAN connection uptime",
			[]string{"gateway"},
			nil,
		),
		MetricType: prometheus.GaugeValue,
	},
}

type FritzboxCollector struct {
	Gateway string
	Port    uint16

	sync.Mutex // protects Root
	Root       *upnp.Root
}

// LoadServices tries to load the service information. Retries until success.
func (fc *FritzboxCollector) LoadServices() {
	for {
		root, err := upnp.LoadServices(fc.Gateway, fc.Port)
		if err != nil {
			fmt.Printf("cannot load services: %s\n", err)

			time.Sleep(serviceLoadRetryTime)
			continue
		}

		fmt.Printf("services loaded\n")

		fc.Lock()
		fc.Root = root
		fc.Unlock()
		return
	}
}

func (fc *FritzboxCollector) Describe(ch chan<- *prometheus.Desc) {
	for _, m := range metrics {
		ch <- m.Desc
	}
}

func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) {
	fc.Lock()
	root := fc.Root
	fc.Unlock()

	if root == nil {
		// Services not loaded yet
		return
	}

	var err error
	var last_service string
	var last_method string
	var last_result upnp.Result

	for _, m := range metrics {
		if m.Service != last_service || m.Action != last_method {
			service, ok := root.Services[m.Service]
			if !ok {
				// TODO
				fmt.Println("cannot find service", m.Service)
				fmt.Println(root.Services)
				continue
			}
			action, ok := service.Actions[m.Action]
			if !ok {
				// TODO
				fmt.Println("cannot find action", m.Action)
				continue
			}

			last_result, err = action.Call()
			if err != nil {
				fmt.Println(err)
				collect_errors.Inc()
				continue
			}
		}

		val, ok := last_result[m.Result]
		if !ok {
			fmt.Println("result not found", m.Result)
			collect_errors.Inc()
			continue
		}

		var floatval float64
		switch tval := val.(type) {
		case uint64:
			floatval = float64(tval)
		case bool:
			if tval {
				floatval = 1
			} else {
				floatval = 0
			}
		case string:
			if tval == m.OkValue {
				floatval = 1
			} else {
				floatval = 0
			}
		default:
			fmt.Println("unknown", val)
			collect_errors.Inc()
			continue

		}

		ch <- prometheus.MustNewConstMetric(
			m.Desc,
			m.MetricType,
			floatval,
			fc.Gateway,
		)
	}
}

func test() {
	root, err := upnp.LoadServices(*flag_gateway_address, uint16(*flag_gateway_port))
	if err != nil {
		panic(err)
	}

	for _, s := range root.Services {
		fmt.Printf("%s: %s\n", s.Device.FriendlyName, s.ServiceType)
		for _, a := range s.Actions {
			if !a.IsGetOnly() {
				continue
			}

			res, err := a.Call()
			if err != nil {
				panic(err)
			}

			fmt.Printf("  %s\n", a.Name)
			for _, arg := range a.Arguments {
				fmt.Printf("    %s: %v\n", arg.RelatedStateVariable, res[arg.StateVariable.Name])
			}
		}
	}
}

func main() {
	flag.Parse()

	if *flag_test {
		test()
		return
	}

	collector := &FritzboxCollector{
		Gateway: *flag_gateway_address,
		Port:    uint16(*flag_gateway_port),
	}

	go collector.LoadServices()

	prometheus.MustRegister(collector)
	prometheus.MustRegister(collect_errors)

	// Since we are dealing with custom Collector implementations, it might
	// be a good idea to enable the collect checks in the registry.
	prometheus.EnableCollectChecks(true)

	http.Handle("/metrics", prometheus.Handler())
	http.ListenAndServe(*flag_addr, nil)
}
<|endoftext|>