<commit_before>package specs

import "os"

// LinuxSpec is the full specification for linux containers.
type LinuxRuntimeSpec struct {
	RuntimeSpec
	// Linux is platform specific configuration for linux based containers.
	LinuxRuntime Linux `json:"linux"`
}

type LinuxRuntime struct {
	// UidMapping specifies user mappings for supporting user namespaces on linux.
	UidMappings []IDMapping `json:"uidMappings"`
	// UidMapping specifies group mappings for supporting user namespaces on linux.
	GidMappings []IDMapping `json:"gidMappings"`
	// Rlimits specifies rlimit options to apply to the container's process.
	Rlimits []Rlimit `json:"rlimits"`
	// Sysctl are a set of key value pairs that are set for the container on start
	Sysctl map[string]string `json:"sysctl"`
	// Resources contain cgroup information for handling resource constraints
	// for the container
	Resources Resources `json:"resources"`
	// Namespaces contains the namespaces that are created and/or joined by the container
	Namespaces []Namespace `json:"namespaces"`
	// Devices are a list of device nodes that are created and enabled for the container
	Devices []Device `json:"devices"`
	// ApparmorProfile specified the apparmor profile for the container.
	ApparmorProfile string `json:"apparmorProfile"`
	// SelinuxProcessLabel specifies the selinux context that the container process is run as.
	SelinuxProcessLabel string `json:"selinuxProcessLabel"`
	// Seccomp specifies the seccomp security settings for the container.
	Seccomp Seccomp `json:"seccomp"`
	// RootfsPropagation is the rootfs mount propagation mode for the container
	RootfsPropagation string `json:"rootfsPropagation"`
}

// Namespace is the configuration for a linux namespace.
type Namespace struct {
	// Type is the type of Linux namespace
	Type string `json:"type"`
	// Path is a path to an existing namespace persisted on disk that can be joined
	// and is of the same type
	Path string `json:"path"`
}

// IDMapping specifies UID/GID mappings
type IDMapping struct {
	// HostID is the UID/GID of the host user or group
	HostID int32 `json:"hostID"`
	// ContainerID is the UID/GID of the container's user or group
	ContainerID int32 `json:"containerID"`
	// Size is the length of the range of IDs mapped between the two namespaces
	Size int32 `json:"size"`
}

// Rlimit type and restrictions
type Rlimit struct {
	// Type of the rlimit to set
	Type int `json:"type"`
	// Hard is the hard limit for the specified type
	Hard uint64 `json:"hard"`
	// Soft is the soft limit for the specified type
	Soft uint64 `json:"soft"`
}

// HugepageLimit structure corresponds to limiting kernel hugepages
type HugepageLimit struct {
	Pagesize string `json:"pageSize"`
	Limit    int    `json:"limit"`
}

// InterfacePriority for network interfaces
type InterfacePriority struct {
	// Name is the name of the network interface
	Name string `json:"name"`
	// Priority for the interface
	Priority int64 `json:"priority"`
}

// BlockIO for Linux cgroup 'blockio' resource management
type BlockIO struct {
	// Specifies per cgroup weight, range is from 10 to 1000
	Weight int64 `json:"blkioWeight"`
	// Weight per cgroup per device, can override BlkioWeight
	WeightDevice string `json:"blkioWeightDevice"`
	// IO read rate limit per cgroup per device, bytes per second
	ThrottleReadBpsDevice string `json:"blkioThrottleReadBpsDevice"`
	// IO write rate limit per cgroup per divice, bytes per second
	ThrottleWriteBpsDevice string `json:"blkioThrottleWriteBpsDevice"`
	// IO read rate limit per cgroup per device, IO per second
	ThrottleReadIOpsDevice string `json:"blkioThrottleReadIopsDevice"`
	// IO write rate limit per cgroup per device, IO per second
	ThrottleWriteIOpsDevice string `json:"blkioThrottleWriteIopsDevice"`
}

// Memory for Linux cgroup 'memory' resource management
type Memory struct {
	// Memory limit (in bytes)
	Limit int64 `json:"limit"`
	// Memory reservation or soft_limit (in bytes)
	Reservation int64 `json:"reservation"`
	// Total memory usage (memory + swap); set `-1' to disable swap
	Swap int64 `json:"swap"`
	// Kernel memory limit (in bytes)
	Kernel int64 `json:"kernel"`
	// How aggressive the kernel will swap memory pages. Range from 0 to 100. Set -1 to use system default
	Swappiness int64 `json:"swappiness"`
}

// CPU for Linux cgroup 'cpu' resource management
type CPU struct {
	// CPU shares (relative weight vs. other cgroups with cpu shares)
	Shares int64 `json:"shares"`
	// CPU hardcap limit (in usecs). Allowed cpu time in a given period
	Quota int64 `json:"quota"`
	// CPU period to be used for hardcapping (in usecs). 0 to use system default
	Period int64 `json:"period"`
	// How many time CPU will use in realtime scheduling (in usecs)
	RealtimeRuntime int64 `json:"realtimeRuntime"`
	// CPU period to be used for realtime scheduling (in usecs)
	RealtimePeriod int64 `json:"realtimePeriod"`
	// CPU to use within the cpuset
	Cpus string `json:"cpus"`
	// MEM to use within the cpuset
	Mems string `json:"mems"`
}

// Network identification and priority configuration
type Network struct {
	// Set class identifier for container's network packets
	ClassID string `json:"classId"`
	// Set priority of network traffic for container
	Priorities []InterfacePriority `json:"priorities"`
}

// Resources has container runtime resource constraints
type Resources struct {
	// DisableOOMKiller disables the OOM killer for out of memory conditions
	DisableOOMKiller bool `json:"disableOOMKiller"`
	// Memory restriction configuration
	Memory Memory `json:"memory"`
	// CPU resource restriction configuration
	CPU CPU `json:"cpu"`
	// BlockIO restriction configuration
	BlockIO BlockIO `json:"blockIO"`
	// Hugetlb limit (in bytes)
	HugepageLimits []HugepageLimit `json:"hugepageLimits"`
	// Network restriction configuration
	Network Network `json:"network"`
}

type Device struct {
	// Path to the device.
	Path string `json:"path"`
	// Device type, block, char, etc.
	Type rune `json:"type"`
	// Major is the device's major number.
	Major int64 `json:"major"`
	// Minor is the device's minor number.
	Minor int64 `json:"minor"`
	// Cgroup permissions format, rwm.
	Permissions string `json:"permissions"`
	// FileMode permission bits for the device.
	FileMode os.FileMode `json:"fileMode"`
	// UID of the device.
	UID uint32 `json:"uid"`
	// Gid of the device.
	GID uint32 `json:"gid"`
}

// Seccomp represents syscall restrictions
type Seccomp struct {
	DefaultAction Action     `json:"defaultAction"`
	Syscalls      []*Syscall `json:"syscalls"`
}

// Action taken upon Seccomp rule match
type Action string

// Operator used to match syscall arguments in Seccomp
type Operator string

// Arg used for matching specific syscall arguments in Seccomp
type Arg struct {
	Index    uint     `json:"index"`
	Value    uint64   `json:"value"`
	ValueTwo uint64   `json:"valueTwo"`
	Op       Operator `json:"op"`
}

// Syscall is used to match a syscall in Seccomp
type Syscall struct {
	Name   string `json:"name"`
	Action Action `json:"action"`
	Args   []*Arg `json:"args"`
}
<commit_msg>runtime_config_linux: Fix 'LinuxSpec' -> 'LinuxRuntimeSpec' in comment<commit_after>package specs

import "os"

// LinuxRuntimeSpec is the full specification for linux containers.
type LinuxRuntimeSpec struct {
	RuntimeSpec
	// Linux is platform specific configuration for linux based containers.
	LinuxRuntime Linux `json:"linux"`
}

type LinuxRuntime struct {
	// UidMapping specifies user mappings for supporting user namespaces on linux.
	UidMappings []IDMapping `json:"uidMappings"`
	// UidMapping specifies group mappings for supporting user namespaces on linux.
	GidMappings []IDMapping `json:"gidMappings"`
	// Rlimits specifies rlimit options to apply to the container's process.
	Rlimits []Rlimit `json:"rlimits"`
	// Sysctl are a set of key value pairs that are set for the container on start
	Sysctl map[string]string `json:"sysctl"`
	// Resources contain cgroup information for handling resource constraints
	// for the container
	Resources Resources `json:"resources"`
	// Namespaces contains the namespaces that are created and/or joined by the container
	Namespaces []Namespace `json:"namespaces"`
	// Devices are a list of device nodes that are created and enabled for the container
	Devices []Device `json:"devices"`
	// ApparmorProfile specified the apparmor profile for the container.
	ApparmorProfile string `json:"apparmorProfile"`
	// SelinuxProcessLabel specifies the selinux context that the container process is run as.
	SelinuxProcessLabel string `json:"selinuxProcessLabel"`
	// Seccomp specifies the seccomp security settings for the container.
	Seccomp Seccomp `json:"seccomp"`
	// RootfsPropagation is the rootfs mount propagation mode for the container
	RootfsPropagation string `json:"rootfsPropagation"`
}

// Namespace is the configuration for a linux namespace.
type Namespace struct {
	// Type is the type of Linux namespace
	Type string `json:"type"`
	// Path is a path to an existing namespace persisted on disk that can be joined
	// and is of the same type
	Path string `json:"path"`
}

// IDMapping specifies UID/GID mappings
type IDMapping struct {
	// HostID is the UID/GID of the host user or group
	HostID int32 `json:"hostID"`
	// ContainerID is the UID/GID of the container's user or group
	ContainerID int32 `json:"containerID"`
	// Size is the length of the range of IDs mapped between the two namespaces
	Size int32 `json:"size"`
}

// Rlimit type and restrictions
type Rlimit struct {
	// Type of the rlimit to set
	Type int `json:"type"`
	// Hard is the hard limit for the specified type
	Hard uint64 `json:"hard"`
	// Soft is the soft limit for the specified type
	Soft uint64 `json:"soft"`
}

// HugepageLimit structure corresponds to limiting kernel hugepages
type HugepageLimit struct {
	Pagesize string `json:"pageSize"`
	Limit    int    `json:"limit"`
}

// InterfacePriority for network interfaces
type InterfacePriority struct {
	// Name is the name of the network interface
	Name string `json:"name"`
	// Priority for the interface
	Priority int64 `json:"priority"`
}

// BlockIO for Linux cgroup 'blockio' resource management
type BlockIO struct {
	// Specifies per cgroup weight, range is from 10 to 1000
	Weight int64 `json:"blkioWeight"`
	// Weight per cgroup per device, can override BlkioWeight
	WeightDevice string `json:"blkioWeightDevice"`
	// IO read rate limit per cgroup per device, bytes per second
	ThrottleReadBpsDevice string `json:"blkioThrottleReadBpsDevice"`
	// IO write rate limit per cgroup per divice, bytes per second
	ThrottleWriteBpsDevice string `json:"blkioThrottleWriteBpsDevice"`
	// IO read rate limit per cgroup per device, IO per second
	ThrottleReadIOpsDevice string `json:"blkioThrottleReadIopsDevice"`
	// IO write rate limit per cgroup per device, IO per second
	ThrottleWriteIOpsDevice string `json:"blkioThrottleWriteIopsDevice"`
}

// Memory for Linux cgroup 'memory' resource management
type Memory struct {
	// Memory limit (in bytes)
	Limit int64 `json:"limit"`
	// Memory reservation or soft_limit (in bytes)
	Reservation int64 `json:"reservation"`
	// Total memory usage (memory + swap); set `-1' to disable swap
	Swap int64 `json:"swap"`
	// Kernel memory limit (in bytes)
	Kernel int64 `json:"kernel"`
	// How aggressive the kernel will swap memory pages. Range from 0 to 100. Set -1 to use system default
	Swappiness int64 `json:"swappiness"`
}

// CPU for Linux cgroup 'cpu' resource management
type CPU struct {
	// CPU shares (relative weight vs. other cgroups with cpu shares)
	Shares int64 `json:"shares"`
	// CPU hardcap limit (in usecs). Allowed cpu time in a given period
	Quota int64 `json:"quota"`
	// CPU period to be used for hardcapping (in usecs). 0 to use system default
	Period int64 `json:"period"`
	// How many time CPU will use in realtime scheduling (in usecs)
	RealtimeRuntime int64 `json:"realtimeRuntime"`
	// CPU period to be used for realtime scheduling (in usecs)
	RealtimePeriod int64 `json:"realtimePeriod"`
	// CPU to use within the cpuset
	Cpus string `json:"cpus"`
	// MEM to use within the cpuset
	Mems string `json:"mems"`
}

// Network identification and priority configuration
type Network struct {
	// Set class identifier for container's network packets
	ClassID string `json:"classId"`
	// Set priority of network traffic for container
	Priorities []InterfacePriority `json:"priorities"`
}

// Resources has container runtime resource constraints
type Resources struct {
	// DisableOOMKiller disables the OOM killer for out of memory conditions
	DisableOOMKiller bool `json:"disableOOMKiller"`
	// Memory restriction configuration
	Memory Memory `json:"memory"`
	// CPU resource restriction configuration
	CPU CPU `json:"cpu"`
	// BlockIO restriction configuration
	BlockIO BlockIO `json:"blockIO"`
	// Hugetlb limit (in bytes)
	HugepageLimits []HugepageLimit `json:"hugepageLimits"`
	// Network restriction configuration
	Network Network `json:"network"`
}

type Device struct {
	// Path to the device.
	Path string `json:"path"`
	// Device type, block, char, etc.
	Type rune `json:"type"`
	// Major is the device's major number.
	Major int64 `json:"major"`
	// Minor is the device's minor number.
	Minor int64 `json:"minor"`
	// Cgroup permissions format, rwm.
	Permissions string `json:"permissions"`
	// FileMode permission bits for the device.
	FileMode os.FileMode `json:"fileMode"`
	// UID of the device.
	UID uint32 `json:"uid"`
	// Gid of the device.
	GID uint32 `json:"gid"`
}

// Seccomp represents syscall restrictions
type Seccomp struct {
	DefaultAction Action     `json:"defaultAction"`
	Syscalls      []*Syscall `json:"syscalls"`
}

// Action taken upon Seccomp rule match
type Action string

// Operator used to match syscall arguments in Seccomp
type Operator string

// Arg used for matching specific syscall arguments in Seccomp
type Arg struct {
	Index    uint     `json:"index"`
	Value    uint64   `json:"value"`
	ValueTwo uint64   `json:"valueTwo"`
	Op       Operator `json:"op"`
}

// Syscall is used to match a syscall in Seccomp
type Syscall struct {
	Name   string `json:"name"`
	Action Action `json:"action"`
	Args   []*Arg `json:"args"`
}
<|endoftext|>


<commit_before>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"github.com/briandowns/spinner"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/disc"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/packet"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/services/httpd"
	"github.com/mehrdadrad/mylg/whois"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

const (
	version = "0.2.1"
)

var (
	// register looking glass hosts
	providers = map[string]Provider{"telia": new(lg.Telia), "level3": new(lg.Level3), "cogent": new(lg.Cogent)}
	pNames    = providerNames()
	req       = make(chan string, 1)
	nxt       = make(chan struct{}, 1)
	spin      = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	args      string
	prompt    string
	cPName    string
	nsr       *ns.Request
	c         *cli.Readline
)

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}
func init() {
	// initialize cli
	c = cli.Init("local", version)
	go c.Run(req, nxt)
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// start web server
	go httpd.Run()
	// set default provider
	cPName = "local"
}

func main() {
	var (
		request string
		loop    = true
	)

	for loop {
		select {
		case request, loop = <-req:
			if !loop {
				break
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := cli.CMDReg.FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			switch {
			case cmd == "hping" && cPName == "local":
				hping()
			case cmd == "ping" && cPName == "local":
				pingLocal()
			case cmd == "ping":
				pingLG()
			case cmd == "trace":
				trace()
			case cmd == "bgp":
				BGP()
			case cmd == "dig":
				nsr.Dig(args)
			case cmd == "node":
				node()
			case cmd == "local":
				nsr.Local()
				cPName = "local"
				c.SetPrompt(cPName)
			case cmd == "connect":
				connect()
			case cmd == "lg":
				c.SetPrompt("lg")
				c.UpdateCompleter("connect", pNames)
			case cmd == "ns":
				c.UpdateCompleter("connect", nsr.CountryList())
				c.UpdateCompleter("node", []string{})
				c.SetPrompt("ns")
			case cmd == "whois":
				whois.Lookup(args)
			case cmd == "peering":
				peeringdb.Search(args)
			case cmd == "scan":
				scanPorts()
			case cmd == "mode":
				mode()
			case cmd == "web":
				web()
			case cmd == "dump":
				dump()
			case cmd == "disc":
				discovery()
			case cmd == "help":
				c.Help()
			case cmd == "exit", cmd == "quit":
				c.Close(nxt)
				close(req)
			}
			// next line
			c.Next()
		}
	}
}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// web tries to open web interface at default web browser
func web() {
	var openCmd = "open"
	println("opening default web broswer ...")
	if runtime.GOOS != "darwin" {
		openCmd = "xdg-open"
	}
	cmd := exec.Command(openCmd, "http://localhost:8080")
	err := cmd.Start()
	if err != nil {
		println("error opening default browser")
	}

}

// dump provides decoding packets
func dump() {
	p, err := packet.NewPacket(args)
	if p == nil || err != nil {
		return
	}
	for l := range p.Open() {
		l.PrintPretty()
	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace, err := icmp.NewTrace(args)
		if err != nil {
			println(err.Error())
		}
		if trace == nil {
			break
		}
		trace.PrintPretty()
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	p, err := ping.NewPing(args)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	p, err := icmp.NewPing(args)
	if err != nil {
		println(err.Error())
	}
	if p == nil {
		return
	}
	p.PrintPretty()
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}

// discovery handles disc command
func discovery() {
	var (
		wg sync.WaitGroup
		//ts = time.Now()
	)

	d := disc.New(args)
	// help requested
	if d == nil {
		return
	}

	spin.Prefix = "please wait "
	spin.Start()

	// load OUI async
	go func() {
		wg.Add(1)
		d.LoadOUI()
		wg.Done()
	}()
	d.PingLan()
	//s := 10 - int(time.Since(ts).Seconds())
	time.Sleep(5 * time.Second)
	if err := d.GetARPTable(); err != nil {
		println(err.Error())
		return
	}
	wg.Wait()
	spin.Stop()

	println("Network LAN Discovery")
	d.PrintPretty()
}
<commit_msg>bump version to 0.2.2<commit_after>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"github.com/briandowns/spinner"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/disc"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/packet"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/services/httpd"
	"github.com/mehrdadrad/mylg/whois"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

const (
	version = "0.2.2"
)

var (
	// register looking glass hosts
	providers = map[string]Provider{"telia": new(lg.Telia), "level3": new(lg.Level3), "cogent": new(lg.Cogent)}
	pNames    = providerNames()
	req       = make(chan string, 1)
	nxt       = make(chan struct{}, 1)
	spin      = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	args      string
	prompt    string
	cPName    string
	nsr       *ns.Request
	c         *cli.Readline
)

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}
func init() {
	// initialize cli
	c = cli.Init("local", version)
	go c.Run(req, nxt)
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// start web server
	go httpd.Run()
	// set default provider
	cPName = "local"
}

func main() {
	var (
		request string
		loop    = true
	)

	for loop {
		select {
		case request, loop = <-req:
			if !loop {
				break
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := cli.CMDReg.FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			switch {
			case cmd == "hping" && cPName == "local":
				hping()
			case cmd == "ping" && cPName == "local":
				pingLocal()
			case cmd == "ping":
				pingLG()
			case cmd == "trace":
				trace()
			case cmd == "bgp":
				BGP()
			case cmd == "dig":
				nsr.Dig(args)
			case cmd == "node":
				node()
			case cmd == "local":
				nsr.Local()
				cPName = "local"
				c.SetPrompt(cPName)
			case cmd == "connect":
				connect()
			case cmd == "lg":
				c.SetPrompt("lg")
				c.UpdateCompleter("connect", pNames)
			case cmd == "ns":
				c.UpdateCompleter("connect", nsr.CountryList())
				c.UpdateCompleter("node", []string{})
				c.SetPrompt("ns")
			case cmd == "whois":
				whois.Lookup(args)
			case cmd == "peering":
				peeringdb.Search(args)
			case cmd == "scan":
				scanPorts()
			case cmd == "mode":
				mode()
			case cmd == "web":
				web()
			case cmd == "dump":
				dump()
			case cmd == "disc":
				discovery()
			case cmd == "help":
				c.Help()
			case cmd == "exit", cmd == "quit":
				c.Close(nxt)
				close(req)
			}
			// next line
			c.Next()
		}
	}
}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// web tries to open web interface at default web browser
func web() {
	var openCmd = "open"
	println("opening default web broswer ...")
	if runtime.GOOS != "darwin" {
		openCmd = "xdg-open"
	}
	cmd := exec.Command(openCmd, "http://localhost:8080")
	err := cmd.Start()
	if err != nil {
		println("error opening default browser")
	}

}

// dump provides decoding packets
func dump() {
	p, err := packet.NewPacket(args)
	if p == nil || err != nil {
		return
	}
	for l := range p.Open() {
		l.PrintPretty()
	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace, err := icmp.NewTrace(args)
		if err != nil {
			println(err.Error())
		}
		if trace == nil {
			break
		}
		trace.PrintPretty()
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	p, err := ping.NewPing(args)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	p, err := icmp.NewPing(args)
	if err != nil {
		println(err.Error())
	}
	if p == nil {
		return
	}
	p.PrintPretty()
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}

// discovery handles disc command
func discovery() {
	var (
		wg sync.WaitGroup
		//ts = time.Now()
	)

	d := disc.New(args)
	// help requested
	if d == nil {
		return
	}

	spin.Prefix = "please wait "
	spin.Start()

	// load OUI async
	go func() {
		wg.Add(1)
		d.LoadOUI()
		wg.Done()
	}()
	d.PingLan()
	//s := 10 - int(time.Since(ts).Seconds())
	time.Sleep(5 * time.Second)
	if err := d.GetARPTable(); err != nil {
		println(err.Error())
		return
	}
	wg.Wait()
	spin.Stop()

	println("Network LAN Discovery")
	d.PrintPretty()
}
<|endoftext|>


<commit_before>// Copyright ©2013 The bíogo.ncbi Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ncbi provides support for interaction with the NCBI services, Entrez and Blast.
//
// Please check the relevant usage policy when using these services. Note that the Blast
// and Entrez server requests are subject to frequency limits.
//
// Required parameters are specified by name in the function call.
//
// The following two parameters should be included in all requests.
//
//  tool   Name of application making the call. Its value must be a string with no internal
//         spaces.
//
//  email  E-mail address of the user. Its value must be a string with no internal spaces,
//         and should be a valid e-mail address.
package ncbi

import (
	"code.google.com/p/biogo.ncbi/xml"
	"io"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
)

// Limiter implements a thread-safe event frequency limit.
type Limiter struct {
	m     sync.Mutex
	delay time.Duration
	next  time.Time
}

// NewLimiter returns a Limiter that will wait for the specified duration between Wait calls.
func NewLimiter(d time.Duration) *Limiter {
	return &Limiter{delay: d}
}

// Wait blocks until the Limiter's specified duration has passed since the last Wait call.
func (d *Limiter) Wait() {
	d.m.Lock()
	defer d.m.Unlock()
	now := time.Now()
	if d.next.After(now) {
		time.Sleep(d.next.Sub(now))
		now = time.Now()
	}
	d.next = now.Add(d.delay)
}

// Util implements low level request generator for interaction with the NCBI services. It is the
// clients responsibility to provide appropriate program parameters and deserialise the returned
// data using the appropriate unmarshaling method.
type Util string

// NewRequest returns an http.Request for the utility, ut using the given method. Parameters to be
// sent to the utility program should be places in db, v, tool and email. NewRequest is subject to
// a limit that prevents requests being sent more frequently than allowed by l. This is easy to
// circumvent, this may result in IP blocking by the NCBI servers, so please do not do this.
func (ut Util) NewRequest(method, db string, v url.Values, tool, email string, l *Limiter) (*http.Request, error) {
	if db != "" {
		v["db"] = []string{db}
	}
	u, err := ut.Prepare(v, tool, email)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest(method, u.String(), nil)
	if err != nil {
		return nil, err
	}
	l.Wait()
	return req, nil
}

// Prepare constructs a URL with the base provided by ut and the parameters provided by v, tool and email.
func (ut Util) Prepare(v url.Values, tool, email string) (*url.URL, error) {
	u, err := url.Parse(string(ut))
	if err != nil {
		return nil, err
	}
	if tool != "" {
		v["tool"] = []string{tool}
	}
	if email != "" {
		v["email"] = []string{email}
	}
	u.RawQuery = v.Encode()
	return u, nil
}

// GetMethodLimit is the maximum length of a constructed URL that will be retrieved by
// the high level API functions using the GET method.
var GetMethodLimit = 2048

// GetXML performs a GET or POST method call to the URI in ut, passing the parameters in v,
// tool and email. The returned stream is unmarshaled into d. The decision on which
// method to use is based on the length of the constructed URL the value of GetMethodLimit.
func (ut Util) GetXML(v url.Values, tool, email string, l *Limiter, d interface{}) error {
	u, err := ut.Prepare(v, tool, email)
	var resp *http.Response
	l.Wait()
	if len(ut)+len(u.RawQuery) < GetMethodLimit {
		resp, err = http.Get(u.String())
	} else {
		buf := strings.NewReader(u.RawQuery)
		u.RawQuery = ""
		resp, err = http.Post(u.String(), "", buf)
	}
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return xml.NewDecoder(resp.Body).Decode(d)
}

// Get performs a GET or POST method call to the URI in ut, passing the parameters in v,
// tool and email. The decision on which method to use is based on the length of the
// constructed URL the value of GetMethodLimit. An io.ReadCloser is returned for a successful
// request. It is the caller's responsibility to close this.
func (ut Util) Get(v url.Values, tool, email string, l *Limiter) (io.ReadCloser, error) {
	u, err := ut.Prepare(v, tool, email)
	var resp *http.Response
	l.Wait()
	if len(ut)+len(u.RawQuery) < GetMethodLimit {
		resp, err = http.Get(u.String())
	} else {
		buf := strings.NewReader(u.RawQuery)
		u.RawQuery = ""
		resp, err = http.Post(u.String(), "", buf)
	}
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}
<commit_msg>Improve wording on limit circumvention comment<commit_after>// Copyright ©2013 The bíogo.ncbi Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ncbi provides support for interaction with the NCBI services, Entrez and Blast.
//
// Please check the relevant usage policy when using these services. Note that the Blast
// and Entrez server requests are subject to frequency limits.
//
// Required parameters are specified by name in the function call.
//
// The following two parameters should be included in all requests.
//
//  tool   Name of application making the call. Its value must be a string with no internal
//         spaces.
//
//  email  E-mail address of the user. Its value must be a string with no internal spaces,
//         and should be a valid e-mail address.
package ncbi

import (
	"code.google.com/p/biogo.ncbi/xml"
	"io"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
)

// Limiter implements a thread-safe event frequency limit.
type Limiter struct {
	m     sync.Mutex
	delay time.Duration
	next  time.Time
}

// NewLimiter returns a Limiter that will wait for the specified duration between Wait calls.
func NewLimiter(d time.Duration) *Limiter {
	return &Limiter{delay: d}
}

// Wait blocks until the Limiter's specified duration has passed since the last Wait call.
func (d *Limiter) Wait() {
	d.m.Lock()
	defer d.m.Unlock()
	now := time.Now()
	if d.next.After(now) {
		time.Sleep(d.next.Sub(now))
		now = time.Now()
	}
	d.next = now.Add(d.delay)
}

// Util implements low level request generator for interaction with the NCBI services. It is the
// clients responsibility to provide appropriate program parameters and deserialise the returned
// data using the appropriate unmarshaling method.
type Util string

// NewRequest returns an http.Request for the utility, ut using the given method. Parameters to be
// sent to the utility program should be places in db, v, tool and email. NewRequest is subject to
// a limit that prevents requests being sent more frequently than allowed by l. The limit is easy to
// circumvent, though circumvention may result in IP blocking by the NCBI servers, so please do not
// do this.
func (ut Util) NewRequest(method, db string, v url.Values, tool, email string, l *Limiter) (*http.Request, error) {
	if db != "" {
		v["db"] = []string{db}
	}
	u, err := ut.Prepare(v, tool, email)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest(method, u.String(), nil)
	if err != nil {
		return nil, err
	}
	l.Wait()
	return req, nil
}

// Prepare constructs a URL with the base provided by ut and the parameters provided by v, tool and email.
func (ut Util) Prepare(v url.Values, tool, email string) (*url.URL, error) {
	u, err := url.Parse(string(ut))
	if err != nil {
		return nil, err
	}
	if tool != "" {
		v["tool"] = []string{tool}
	}
	if email != "" {
		v["email"] = []string{email}
	}
	u.RawQuery = v.Encode()
	return u, nil
}

// GetMethodLimit is the maximum length of a constructed URL that will be retrieved by
// the high level API functions using the GET method.
var GetMethodLimit = 2048

// GetXML performs a GET or POST method call to the URI in ut, passing the parameters in v,
// tool and email. The returned stream is unmarshaled into d. The decision on which
// method to use is based on the length of the constructed URL the value of GetMethodLimit.
func (ut Util) GetXML(v url.Values, tool, email string, l *Limiter, d interface{}) error {
	u, err := ut.Prepare(v, tool, email)
	var resp *http.Response
	l.Wait()
	if len(ut)+len(u.RawQuery) < GetMethodLimit {
		resp, err = http.Get(u.String())
	} else {
		buf := strings.NewReader(u.RawQuery)
		u.RawQuery = ""
		resp, err = http.Post(u.String(), "", buf)
	}
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return xml.NewDecoder(resp.Body).Decode(d)
}

// Get performs a GET or POST method call to the URI in ut, passing the parameters in v,
// tool and email. The decision on which method to use is based on the length of the
// constructed URL the value of GetMethodLimit. An io.ReadCloser is returned for a successful
// request. It is the caller's responsibility to close this.
func (ut Util) Get(v url.Values, tool, email string, l *Limiter) (io.ReadCloser, error) {
	u, err := ut.Prepare(v, tool, email)
	var resp *http.Response
	l.Wait()
	if len(ut)+len(u.RawQuery) < GetMethodLimit {
		resp, err = http.Get(u.String())
	} else {
		buf := strings.NewReader(u.RawQuery)
		u.RawQuery = ""
		resp, err = http.Post(u.String(), "", buf)
	}
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}
<|endoftext|>


<commit_before>package artnet

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

	"github.com/jsimonetti/go-artnet/packet"
	"github.com/jsimonetti/go-artnet/packet/code"
)

// Node is the information known about a node
type Node struct {
	// Config holds the configuration of this node
	Config NodeConfig

	// conn is the UDP connection this node will listen on
	conn   net.PacketConn
	bconn  net.Conn
	sendCh chan netPayload
	recvCh chan netPayload

	// shutdownCh will be closed on shutdown of the node
	shutdownCh   chan struct{}
	shutdown     bool
	shutdownErr  error
	shutdownLock sync.Mutex

	// pollCh will receive ArtPoll packets
	pollCh chan packet.ArtPollPacket
	// pollCh will send ArtPollReply packets
	pollReplyCh chan packet.ArtPollReplyPacket

	log Logger
}

// netPayload contains bytes read from the network and/or an error
type netPayload struct {
	address net.UDPAddr
	err     error
	data    []byte
}

// NewNode return a Node
func NewNode(name string, style code.StyleCode, ip net.IP, log Logger) *Node {
	n := &Node{
		Config: NodeConfig{
			Name: name,
			Type: style,
		},
		conn:     nil,
		shutdown: true,
		log:      log.With(Fields{"type": "Node"}),
	}
	if len(ip) > 0 {
		n.Config.IP = ip
	}
	//n.Config.IP = GenerateIP()
	return n
}

// Stop will stop all running routines and close the network connection
func (n *Node) Stop() {
	n.shutdownLock.Lock()
	n.shutdown = true
	n.shutdownLock.Unlock()
	close(n.shutdownCh)
	if n.conn != nil {
		n.conn.Close()
	}
	if n.bconn != nil {
		n.bconn.Close()
	}
}

// Start will start the controller
func (n *Node) Start() error {
	n.log.With(Fields{"ip": n.Config.IP.String(), "type": n.Config.Type.String()}).Debug("node started")

	n.sendCh = make(chan netPayload, 10)
	n.recvCh = make(chan netPayload, 10)
	n.pollCh = make(chan packet.ArtPollPacket, 10)
	n.pollReplyCh = make(chan packet.ArtPollReplyPacket, 10)
	n.shutdownCh = make(chan struct{})
	n.shutdown = false

	var err error

	n.bconn, err = net.Dial("udp4", "2.255.255.255:6454")
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenUDP")
		return err
	}

	rcvListenAddress := "0.0.0.0:6454"
	if len(n.Config.IP) > 0 {
		rcvListenAddress = fmt.Sprintf("%v:6454", n.Config.IP.String())
	}

	n.conn, err = net.ListenPacket("udp4", rcvListenAddress)
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenUDP")
		return err
	}

	go n.recvLoop()
	go n.sendLoop()

	return nil
}

// pollReplyLoop loops to reply to ArtPoll packets
// when a controller asks for continuous updates, we do that using a ticker
func (n *Node) pollReplyLoop() {
	var timer time.Ticker

	// loop untill shutdown
	for {
		select {
		case <-timer.C:
			// if we should regularly send replies (can be requested by the controller)
			// we send it here

		case poll := <-n.pollCh:
			// reply with pollReply
			n.log.With(Fields{"poll": poll}).Debugf("poll received, now send a reply")

			// if we are asked to send changes regularyl, set the Ticker here

		case <-n.shutdownCh:
			return
		}
	}
}

// sendLoop is used to send packets to the network
func (n *Node) sendLoop() {
	// loop untill shutdown
	for {
		select {
		case payload := <-n.sendCh:
			n.shutdownLock.Lock()
			if !n.shutdown {
				var num int
				var err error
				if payload.address.IP.Equal(broadcastAddr.IP) {
					num, err = n.bconn.Write(payload.data)
				} else {
					num, err = n.conn.WriteTo(payload.data, &payload.address)
				}
				if err != nil {
					n.log.With(Fields{"error": err}).Debugf("error writing packet")
					continue
				}
				n.log.With(Fields{"dst": payload.address.String(), "bytes": num}).Debugf("packet sent")
			}
			n.shutdownLock.Unlock()
		case <-n.shutdownCh:
			return
		}
	}
}

// AddrToUDPAddr will turn a net.Addr into a net.UDPAddr
func AddrToUDPAddr(addr net.Addr) net.UDPAddr {
	udp := addr.(*net.UDPAddr)
	return *udp
}

// recvLoop is used to receive packets from the network
// it starts a goroutine for dumping the msgs onto a channel,
// the payload from that channel is then fed into a handler
// due to the nature of broadcasting, we see our own sent
// packets to, but we ignore them
func (n *Node) recvLoop() {
	// start a routine that will read data from n.conn
	// and (if not shutdown), send to the recvCh
	go func() {
		b := make([]byte, 4096)
		for {
			num, src, err := n.conn.ReadFrom(b)
			n.shutdownLock.Lock()
			if !n.shutdown {
				n.shutdownLock.Unlock()
				from := AddrToUDPAddr(src)
				if n.Config.IP.Equal(from.IP) {
					// this was sent by me, so we ignore it
					//n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("ignoring received packet from self")
					continue
				}

				n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("received packet")
				if err != nil && err != io.EOF {
					n.recvCh <- netPayload{
						address: from,
						data:    b[:num],
						err:     err,
					}
					continue
				}
				n.recvCh <- netPayload{
					address: from,
					data:    b[:num],
					err:     err,
				}
				continue
			}
			n.shutdownLock.Unlock()
			return
		}
	}()

	// loop untill shutdown
	for {
		select {
		case payload := <-n.recvCh:
			p, err := packet.Unmarshal(payload.data)
			if err != nil {
				n.log.Printf("failed to parse packet from %s: %v", payload.address.String(), err)
				continue
			}
			go n.handlePacket(p)

		case <-n.shutdownCh:
			return
		}
	}
}

// handlePacket contains the logic for dealing with incoming packets
func (n *Node) handlePacket(p packet.ArtNetPacket) {
	switch p := p.(type) {
	case *packet.ArtPollReplyPacket:
		// only handle these packets if we are a controller
		if n.Config.Type == code.StController {
			n.pollReplyCh <- *p
		}

	default:
		n.log.With(Fields{"packet": p}).Debugf("unknown packet type")
	}

}
<commit_msg>Remove listening only for Node IP UDP package<commit_after>package artnet

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

	"github.com/jsimonetti/go-artnet/packet"
	"github.com/jsimonetti/go-artnet/packet/code"
)

// Node is the information known about a node
type Node struct {
	// Config holds the configuration of this node
	Config NodeConfig

	// conn is the UDP connection this node will listen on
	conn   net.PacketConn
	bconn  net.Conn
	sendCh chan netPayload
	recvCh chan netPayload

	// shutdownCh will be closed on shutdown of the node
	shutdownCh   chan struct{}
	shutdown     bool
	shutdownErr  error
	shutdownLock sync.Mutex

	// pollCh will receive ArtPoll packets
	pollCh chan packet.ArtPollPacket
	// pollCh will send ArtPollReply packets
	pollReplyCh chan packet.ArtPollReplyPacket

	log Logger
}

// netPayload contains bytes read from the network and/or an error
type netPayload struct {
	address net.UDPAddr
	err     error
	data    []byte
}

// NewNode return a Node
func NewNode(name string, style code.StyleCode, ip net.IP, log Logger) *Node {
	n := &Node{
		Config: NodeConfig{
			Name: name,
			Type: style,
		},
		conn:     nil,
		shutdown: true,
		log:      log.With(Fields{"type": "Node"}),
	}
	if len(ip) > 0 {
		n.Config.IP = ip
	}
	//n.Config.IP = GenerateIP()
	return n
}

// Stop will stop all running routines and close the network connection
func (n *Node) Stop() {
	n.shutdownLock.Lock()
	n.shutdown = true
	n.shutdownLock.Unlock()
	close(n.shutdownCh)
	if n.conn != nil {
		n.conn.Close()
	}
	if n.bconn != nil {
		n.bconn.Close()
	}
}

// Start will start the controller
func (n *Node) Start() error {
	n.log.With(Fields{"ip": n.Config.IP.String(), "type": n.Config.Type.String()}).Debug("node started")

	n.sendCh = make(chan netPayload, 10)
	n.recvCh = make(chan netPayload, 10)
	n.pollCh = make(chan packet.ArtPollPacket, 10)
	n.pollReplyCh = make(chan packet.ArtPollReplyPacket, 10)
	n.shutdownCh = make(chan struct{})
	n.shutdown = false

	var err error

	n.bconn, err = net.Dial("udp4", "2.255.255.255:6454")
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenUDP")
		return err
	}

	n.conn, err = net.ListenPacket("udp4", "0.0.0.0:6454")
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenUDP")
		return err
	}

	go n.recvLoop()
	go n.sendLoop()

	return nil
}

// pollReplyLoop loops to reply to ArtPoll packets
// when a controller asks for continuous updates, we do that using a ticker
func (n *Node) pollReplyLoop() {
	var timer time.Ticker

	// loop untill shutdown
	for {
		select {
		case <-timer.C:
			// if we should regularly send replies (can be requested by the controller)
			// we send it here

		case poll := <-n.pollCh:
			// reply with pollReply
			n.log.With(Fields{"poll": poll}).Debugf("poll received, now send a reply")

			// if we are asked to send changes regularyl, set the Ticker here

		case <-n.shutdownCh:
			return
		}
	}
}

// sendLoop is used to send packets to the network
func (n *Node) sendLoop() {
	// loop untill shutdown
	for {
		select {
		case payload := <-n.sendCh:
			n.shutdownLock.Lock()
			if !n.shutdown {
				var num int
				var err error
				if payload.address.IP.Equal(broadcastAddr.IP) {
					num, err = n.bconn.Write(payload.data)
				} else {
					num, err = n.conn.WriteTo(payload.data, &payload.address)
				}
				if err != nil {
					n.log.With(Fields{"error": err}).Debugf("error writing packet")
					continue
				}
				n.log.With(Fields{"dst": payload.address.String(), "bytes": num}).Debugf("packet sent")
			}
			n.shutdownLock.Unlock()
		case <-n.shutdownCh:
			return
		}
	}
}

// AddrToUDPAddr will turn a net.Addr into a net.UDPAddr
func AddrToUDPAddr(addr net.Addr) net.UDPAddr {
	udp := addr.(*net.UDPAddr)
	return *udp
}

// recvLoop is used to receive packets from the network
// it starts a goroutine for dumping the msgs onto a channel,
// the payload from that channel is then fed into a handler
// due to the nature of broadcasting, we see our own sent
// packets to, but we ignore them
func (n *Node) recvLoop() {
	// start a routine that will read data from n.conn
	// and (if not shutdown), send to the recvCh
	go func() {
		b := make([]byte, 4096)
		for {
			num, src, err := n.conn.ReadFrom(b)
			n.shutdownLock.Lock()
			if !n.shutdown {
				n.shutdownLock.Unlock()
				from := AddrToUDPAddr(src)
				if n.Config.IP.Equal(from.IP) {
					// this was sent by me, so we ignore it
					//n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("ignoring received packet from self")
					continue
				}

				n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("received packet")
				if err != nil && err != io.EOF {
					n.recvCh <- netPayload{
						address: from,
						data:    b[:num],
						err:     err,
					}
					continue
				}
				n.recvCh <- netPayload{
					address: from,
					data:    b[:num],
					err:     err,
				}
				continue
			}
			n.shutdownLock.Unlock()
			return
		}
	}()

	// loop untill shutdown
	for {
		select {
		case payload := <-n.recvCh:
			p, err := packet.Unmarshal(payload.data)
			if err != nil {
				n.log.Printf("failed to parse packet from %s: %v", payload.address.String(), err)
				continue
			}
			go n.handlePacket(p)

		case <-n.shutdownCh:
			return
		}
	}
}

// handlePacket contains the logic for dealing with incoming packets
func (n *Node) handlePacket(p packet.ArtNetPacket) {
	switch p := p.(type) {
	case *packet.ArtPollReplyPacket:
		// only handle these packets if we are a controller
		if n.Config.Type == code.StController {
			n.pollReplyCh <- *p
		}

	default:
		n.log.With(Fields{"packet": p}).Debugf("unknown packet type")
	}

}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Xuyuan Pang <xuyuanp # gmail dot com>
 *
 * 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 hador

import (
	"container/list"
	"fmt"
	"net/http"
	"regexp"
	"strings"
)

func genSegments(path string) []string {
	if path == "/" {
		path = ""
	}
	return strings.Split(path, "/")[1:]
}

func isReg(segment string) bool {
	if regSegmentRegexp.MatchString(segment) {
		return true
	}
	if strings.HasPrefix(segment, "{") && strings.HasSuffix(segment, "}") {
		return true
	}
	return false
}

var regSegmentRegexp = regexp.MustCompile(`\(\?P<.+>.+\)`)

type dispatcher struct {
	node *Node
}

func (d *dispatcher) Serve(ctx *Context) {
	n := d.node
	segments := ctx.segments
	// path matches
	if len(segments) == n.depth {
		n.doServe(ctx)
		return
	}
	// find next node
	segment := segments[n.depth]
	next := n.findNext(segment)
	if next != nil {
		if next.paramReg != nil {
			ctx.Params()[next.paramName] = segment
		}
		next.Serve(ctx)
	}
	// 404 not found
	ctx.OnError(http.StatusNotFound)
}

// Node struct
type Node struct {
	*FilterChain
	h           *Hador
	parent      *Node
	depth       int
	segment     string
	paramName   string
	paramReg    *regexp.Regexp
	rawChildren map[string]*Node
	regChildren []*Node
	leaves      map[string]*Leaf
}

// NewNode creates new Node instance.
func NewNode(h *Hador, segment string, depth int) *Node {
	paramName, paramReg := resolveSegment(segment)
	n := &Node{
		h:           h,
		segment:     segment,
		depth:       depth,
		paramName:   paramName,
		paramReg:    paramReg,
		rawChildren: make(map[string]*Node),
		regChildren: make([]*Node, 0),
		leaves:      make(map[string]*Leaf),
	}
	n.FilterChain = NewFilterChain(&dispatcher{node: n})
	return n
}

func resolveSegment(segment string) (paramName string, paramReg *regexp.Regexp) {
	var splits []string
	if strings.HasPrefix(segment, "{") && strings.HasSuffix(segment, "}") {
		seg := segment[1 : len(segment)-1]
		splits = strings.SplitN(seg, ":", 2)
		if len(splits) == 1 {
			splits = append(splits, ".+")
		}
	} else if regSegmentRegexp.MatchString(segment) {
		seg := segment[4 : len(segment)-1]
		splits = strings.SplitN(seg, ">", 2)
	}
	if splits != nil && len(splits) == 2 {
		paramName = splits[0]
		regstr := splits[1]
		if !strings.HasPrefix(regstr, "^") {
			regstr = "^" + regstr
		}
		if !strings.HasSuffix(regstr, "$") {
			regstr = regstr + "$"
		}
		paramReg = regexp.MustCompile(regstr)
	}
	return
}

// Options adds route by call AddRoute
func (n *Node) Options(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("OPTIONS", pattern, handler, filters...)
}

// Get adds route by call AddRoute
func (n *Node) Get(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("GET", pattern, handler, filters...)
}

// Head adds route by call AddRoute
func (n *Node) Head(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("HEAD", pattern, handler, filters...)
}

// Post adds route by call AddRoute
func (n *Node) Post(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("POST", pattern, handler, filters...)
}

// Put adds route by call AddRoute
func (n *Node) Put(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("PUT", pattern, handler, filters...)
}

// Delete adds route by call AddRoute
func (n *Node) Delete(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("DELETE", pattern, handler, filters...)
}

// Trace adds route by call AddRoute
func (n *Node) Trace(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("TRACE", pattern, handler, filters...)
}

// Connect adds route by call AddRoute
func (n *Node) Connect(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("CONNECT", pattern, handler, filters...)
}

// Patch adds route by call AddRoute
func (n *Node) Patch(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("PATCH", pattern, handler, filters...)
}

// Any adds route by call AddRoute
func (n *Node) Any(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("ANY", pattern, handler, filters...)
}

// Group adds group routes
func (n *Node) Group(pattern string, f func(Router), filters ...Filter) {
	segments := genSegments(pattern)
	r, _, _ := n.add(segments, "", nil, filters...)
	f(r)
}

// AddRoute adds a new route with method, pattern and handler
func (n *Node) AddRoute(method, pattern string, handler Handler, filters ...Filter) *Leaf {
	segments := genSegments(pattern)
	if _, l, ok := n.add(segments, method, handler, filters...); ok {
		return l
	}
	panic(fmt.Errorf("pattern: %s has been registered", pattern))
}

func (n *Node) add(segments []string, method string, handler Handler, filters ...Filter) (*Node, *Leaf, bool) {
	if len(segments) == 0 {
		if method != "" && handler != nil {
			l, ok := n.handle(method, handler, filters...)
			return n, l, ok
		}
		n.AddFilters(filters...)
		return n, nil, true
	}

	segment := segments[0]
	next := n.findOrCreateNext(segment)
	return next.add(segments[1:], method, handler, filters...)
}

func (n *Node) handle(method string, handler Handler, filters ...Filter) (l *Leaf, ok bool) {
	if _, ok := n.leaves[method]; ok {
		return nil, false
	}
	l = NewLeaf(n, method, handler)
	n.leaves[method] = l
	l.AddFilters(filters...)
	return l, true
}

func (n *Node) findOrCreateNext(segment string) (next *Node) {
	if !isReg(segment) {
		if ne, ok := n.rawChildren[segment]; ok {
			next = ne
		} else {
			next = NewNode(n.h, segment, n.depth+1)
			n.rawChildren[segment] = next
		}
	} else {
		found := false
		for _, next = range n.regChildren {
			if next.segment == segment {
				found = true
				break
			}
		}
		if !found {
			next = NewNode(n.h, segment, n.depth+1)
			n.regChildren = append(n.regChildren, next)
		}
	}
	next.parent = n
	return
}

func (n *Node) findNext(segment string) (next *Node) {
	if ne, ok := n.rawChildren[segment]; ok {
		next = ne
	} else {
		for _, ne := range n.regChildren {
			if ne.MatchRegexp(segment) {
				next = ne
				break
			}
		}
	}
	return
}

func (n *Node) doServe(ctx *Context) {
	// 404 not found
	if len(n.leaves) == 0 {
		ctx.OnError(http.StatusNotFound)
		return
	}
	// method matches
	if l, ok := n.leaves[ctx.Request.Method]; ok {
		l.Serve(ctx)
		return
	}
	// ANY matches
	if l, ok := n.leaves["ANY"]; ok {
		l.Serve(ctx)
		return
	}
	// 405 method not allowed
	methods := make([]string, len(n.leaves))
	i := 0
	for m := range n.leaves {
		methods[i] = m
		i++
	}
	ctx.OnError(http.StatusMethodNotAllowed, methods)
}

// MatchRegexp checks if the segment match regexp in node
func (n *Node) MatchRegexp(segment string) bool {
	if n.paramReg != nil && n.paramReg.MatchString(segment) {
		return true
	}
	return false
}

// Path returns the full path from root to the node
func (n *Node) Path() string {
	if n.parent == nil {
		return "/"
	}
	ppath := n.parent.Path()
	if ppath == "/" {
		ppath = ""
	}
	if n.paramName != "" {
		return ppath + "/{" + n.paramName + "}"
	}
	return ppath + "/" + n.segment
}

// Parent returns the node's parent node
func (n *Node) Parent() *Node {
	return n.parent
}

// Depth returns the nodes' depth
func (n *Node) Depth() int {
	return n.depth
}

// Segment returns node's segment
func (n *Node) Segment() string {
	return n.segment
}

// Leaves returns all of node's leaves
func (n *Node) Leaves() []*Leaf {
	leaves := make([]*Leaf, len(n.leaves))
	i := 0
	for _, l := range n.leaves {
		leaves[i] = l
		i++
	}
	return leaves
}

func (n *Node) travel(llist *list.List) {
	for _, l := range n.leaves {
		llist.PushBack(l)
	}
	for _, child := range n.rawChildren {
		child.travel(llist)
	}
	for _, child := range n.regChildren {
		child.travel(llist)
	}
}
<commit_msg>Ended route after matched<commit_after>/*
 * Copyright 2015 Xuyuan Pang <xuyuanp # gmail dot com>
 *
 * 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 hador

import (
	"container/list"
	"fmt"
	"net/http"
	"regexp"
	"strings"
)

func genSegments(path string) []string {
	if path == "/" {
		path = ""
	}
	return strings.Split(path, "/")[1:]
}

func isReg(segment string) bool {
	if regSegmentRegexp.MatchString(segment) {
		return true
	}
	if strings.HasPrefix(segment, "{") && strings.HasSuffix(segment, "}") {
		return true
	}
	return false
}

var regSegmentRegexp = regexp.MustCompile(`\(\?P<.+>.+\)`)

type dispatcher struct {
	node *Node
}

func (d *dispatcher) Serve(ctx *Context) {
	n := d.node
	segments := ctx.segments
	// path matches
	if len(segments) == n.depth {
		n.doServe(ctx)
		return
	}
	// find next node
	segment := segments[n.depth]
	next := n.findNext(segment)
	if next != nil {
		if next.paramReg != nil {
			ctx.Params()[next.paramName] = segment
		}
		next.Serve(ctx)
		return
	}
	// 404 not found
	ctx.OnError(http.StatusNotFound)
}

// Node struct
type Node struct {
	*FilterChain
	h           *Hador
	parent      *Node
	depth       int
	segment     string
	paramName   string
	paramReg    *regexp.Regexp
	rawChildren map[string]*Node
	regChildren []*Node
	leaves      map[string]*Leaf
}

// NewNode creates new Node instance.
func NewNode(h *Hador, segment string, depth int) *Node {
	paramName, paramReg := resolveSegment(segment)
	n := &Node{
		h:           h,
		segment:     segment,
		depth:       depth,
		paramName:   paramName,
		paramReg:    paramReg,
		rawChildren: make(map[string]*Node),
		regChildren: make([]*Node, 0),
		leaves:      make(map[string]*Leaf),
	}
	n.FilterChain = NewFilterChain(&dispatcher{node: n})
	return n
}

func resolveSegment(segment string) (paramName string, paramReg *regexp.Regexp) {
	var splits []string
	if strings.HasPrefix(segment, "{") && strings.HasSuffix(segment, "}") {
		seg := segment[1 : len(segment)-1]
		splits = strings.SplitN(seg, ":", 2)
		if len(splits) == 1 {
			splits = append(splits, ".+")
		}
	} else if regSegmentRegexp.MatchString(segment) {
		seg := segment[4 : len(segment)-1]
		splits = strings.SplitN(seg, ">", 2)
	}
	if splits != nil && len(splits) == 2 {
		paramName = splits[0]
		regstr := splits[1]
		if !strings.HasPrefix(regstr, "^") {
			regstr = "^" + regstr
		}
		if !strings.HasSuffix(regstr, "$") {
			regstr = regstr + "$"
		}
		paramReg = regexp.MustCompile(regstr)
	}
	return
}

// Options adds route by call AddRoute
func (n *Node) Options(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("OPTIONS", pattern, handler, filters...)
}

// Get adds route by call AddRoute
func (n *Node) Get(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("GET", pattern, handler, filters...)
}

// Head adds route by call AddRoute
func (n *Node) Head(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("HEAD", pattern, handler, filters...)
}

// Post adds route by call AddRoute
func (n *Node) Post(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("POST", pattern, handler, filters...)
}

// Put adds route by call AddRoute
func (n *Node) Put(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("PUT", pattern, handler, filters...)
}

// Delete adds route by call AddRoute
func (n *Node) Delete(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("DELETE", pattern, handler, filters...)
}

// Trace adds route by call AddRoute
func (n *Node) Trace(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("TRACE", pattern, handler, filters...)
}

// Connect adds route by call AddRoute
func (n *Node) Connect(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("CONNECT", pattern, handler, filters...)
}

// Patch adds route by call AddRoute
func (n *Node) Patch(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("PATCH", pattern, handler, filters...)
}

// Any adds route by call AddRoute
func (n *Node) Any(pattern string, handler Handler, filters ...Filter) *Leaf {
	return n.AddRoute("ANY", pattern, handler, filters...)
}

// Group adds group routes
func (n *Node) Group(pattern string, f func(Router), filters ...Filter) {
	segments := genSegments(pattern)
	r, _, _ := n.add(segments, "", nil, filters...)
	f(r)
}

// AddRoute adds a new route with method, pattern and handler
func (n *Node) AddRoute(method, pattern string, handler Handler, filters ...Filter) *Leaf {
	segments := genSegments(pattern)
	if _, l, ok := n.add(segments, method, handler, filters...); ok {
		return l
	}
	panic(fmt.Errorf("pattern: %s has been registered", pattern))
}

func (n *Node) add(segments []string, method string, handler Handler, filters ...Filter) (*Node, *Leaf, bool) {
	if len(segments) == 0 {
		if method != "" && handler != nil {
			l, ok := n.handle(method, handler, filters...)
			return n, l, ok
		}
		n.AddFilters(filters...)
		return n, nil, true
	}

	segment := segments[0]
	next := n.findOrCreateNext(segment)
	return next.add(segments[1:], method, handler, filters...)
}

func (n *Node) handle(method string, handler Handler, filters ...Filter) (l *Leaf, ok bool) {
	if _, ok := n.leaves[method]; ok {
		return nil, false
	}
	l = NewLeaf(n, method, handler)
	n.leaves[method] = l
	l.AddFilters(filters...)
	return l, true
}

func (n *Node) findOrCreateNext(segment string) (next *Node) {
	if !isReg(segment) {
		if ne, ok := n.rawChildren[segment]; ok {
			next = ne
		} else {
			next = NewNode(n.h, segment, n.depth+1)
			n.rawChildren[segment] = next
		}
	} else {
		found := false
		for _, next = range n.regChildren {
			if next.segment == segment {
				found = true
				break
			}
		}
		if !found {
			next = NewNode(n.h, segment, n.depth+1)
			n.regChildren = append(n.regChildren, next)
		}
	}
	next.parent = n
	return
}

func (n *Node) findNext(segment string) (next *Node) {
	if ne, ok := n.rawChildren[segment]; ok {
		next = ne
	} else {
		for _, ne := range n.regChildren {
			if ne.MatchRegexp(segment) {
				next = ne
				break
			}
		}
	}
	return
}

func (n *Node) doServe(ctx *Context) {
	// 404 not found
	if len(n.leaves) == 0 {
		ctx.OnError(http.StatusNotFound)
		return
	}
	// method matches
	if l, ok := n.leaves[ctx.Request.Method]; ok {
		l.Serve(ctx)
		return
	}
	// ANY matches
	if l, ok := n.leaves["ANY"]; ok {
		l.Serve(ctx)
		return
	}
	// 405 method not allowed
	methods := make([]string, len(n.leaves))
	i := 0
	for m := range n.leaves {
		methods[i] = m
		i++
	}
	ctx.OnError(http.StatusMethodNotAllowed, methods)
}

// MatchRegexp checks if the segment match regexp in node
func (n *Node) MatchRegexp(segment string) bool {
	if n.paramReg != nil && n.paramReg.MatchString(segment) {
		return true
	}
	return false
}

// Path returns the full path from root to the node
func (n *Node) Path() string {
	if n.parent == nil {
		return "/"
	}
	ppath := n.parent.Path()
	if ppath == "/" {
		ppath = ""
	}
	if n.paramName != "" {
		return ppath + "/{" + n.paramName + "}"
	}
	return ppath + "/" + n.segment
}

// Parent returns the node's parent node
func (n *Node) Parent() *Node {
	return n.parent
}

// Depth returns the nodes' depth
func (n *Node) Depth() int {
	return n.depth
}

// Segment returns node's segment
func (n *Node) Segment() string {
	return n.segment
}

// Leaves returns all of node's leaves
func (n *Node) Leaves() []*Leaf {
	leaves := make([]*Leaf, len(n.leaves))
	i := 0
	for _, l := range n.leaves {
		leaves[i] = l
		i++
	}
	return leaves
}

func (n *Node) travel(llist *list.List) {
	for _, l := range n.leaves {
		llist.PushBack(l)
	}
	for _, child := range n.rawChildren {
		child.travel(llist)
	}
	for _, child := range n.regChildren {
		child.travel(llist)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/gin-gonic/gin"
	"github.com/peteraba/d5/lib/game"
	"github.com/peteraba/d5/lib/german"
	"github.com/peteraba/d5/lib/german/entity"
)

func parseFlags() (int, bool, string, string) {
	port := flag.Int("port", 17182, "Port for server")

	debug := flag.Bool("debug", false, "Enables debug logs")

	finder := flag.String("finder", "http://localhost:17171/", "Finder address")

	scorer := flag.String("scorer", "http://localhost:17172/", "Scorer address")

	flag.Parse()

	return *port, *debug, *finder, *scorer
}

func main() {
	port, debug, finderUrl, scorerUrl := parseFlags()

	if debug == false {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.Default()

	router.GET("/game/:user", makeGameHandle(finderUrl))
	router.POST("/answer/:user", makeCheckAnswerHandle(finderUrl, scorerUrl))

	router.Run(fmt.Sprintf(":%d", port))
}

func makeGameHandle(finderUrl string) func(c *gin.Context) {
	return func(c *gin.Context) {
		var (
			words      []entity.Word
			word       entity.Word
			query      = map[string]string{}
			err        error
			returnCode int
		)

		query["word.category"] = "noun"
		query["word.user"] = c.Param("user")

		words, returnCode, err = game.FetchWords(finderUrl, query, 1)
		if err != nil {
			c.JSON(returnCode, fmt.Sprint(err))

			return
		}

		word = words[0]

		game := game.Game{
			fmt.Sprintf("What's the article of '%s'?", word.GetGerman()),
			"der",
			"die",
			"das",
			"",
			word.GetId().Hex(),
		}

		c.JSON(200, game)
	}
}

func makeCheckAnswerHandle(finderUrl, scorerUrl string) func(c *gin.Context) {
	return func(c *gin.Context) {
		var (
			query      = map[string]string{}
			dictionary german.Dictionary
			noun       entity.Noun
			err        error
			returnCode int
		)

		query["__id"] = c.PostForm("id")

		dictionary, returnCode, err = game.FetchDictionary(finderUrl, query, 1)
		if err != nil {
			c.JSON(returnCode, fmt.Sprint(err))

			return
		}

		if len(dictionary.Nouns) == 0 {
			c.JSON(returnCode, "No noun was found.")

			return
		}

		noun = dictionary.Nouns[0]

		answeredRight := checkAnswer(noun, c.PostForm("result"))

		game.ScoreWords(scorerUrl, 10, []string{c.PostForm("id")})

		c.JSON(200, answeredRight)
	}
}

func checkAnswer(word entity.Noun, result string) bool {
	if word.Articles[0] == entity.Der && result == "1" {
		return true
	}

	if word.Articles[0] == entity.Die && result == "2" {
		return true
	}

	if word.Articles[0] == entity.Das && result == "3" {
		return true
	}

	return false
}
<commit_msg>Checking all articles for correct answer<commit_after>package main

import (
	"flag"
	"fmt"

	"github.com/gin-gonic/gin"
	"github.com/peteraba/d5/lib/game"
	"github.com/peteraba/d5/lib/german"
	"github.com/peteraba/d5/lib/german/entity"
)

func parseFlags() (int, bool, string, string) {
	port := flag.Int("port", 17182, "Port for server")

	debug := flag.Bool("debug", false, "Enables debug logs")

	finder := flag.String("finder", "http://localhost:17171/", "Finder address")

	scorer := flag.String("scorer", "http://localhost:17172/", "Scorer address")

	flag.Parse()

	return *port, *debug, *finder, *scorer
}

func main() {
	port, debug, finderUrl, scorerUrl := parseFlags()

	if debug == false {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.Default()

	router.GET("/game/:user", makeGameHandle(finderUrl))
	router.POST("/answer/:user", makeCheckAnswerHandle(finderUrl, scorerUrl))

	router.Run(fmt.Sprintf(":%d", port))
}

func makeGameHandle(finderUrl string) func(c *gin.Context) {
	return func(c *gin.Context) {
		var (
			words      []entity.Word
			word       entity.Word
			query      = map[string]string{}
			err        error
			returnCode int
		)

		query["word.category"] = "noun"
		query["word.user"] = c.Param("user")

		words, returnCode, err = game.FetchWords(finderUrl, query, 1)
		if err != nil {
			c.JSON(returnCode, fmt.Sprint(err))

			return
		}

		word = words[0]

		game := game.Game{
			fmt.Sprintf("What's the article of '%s'?", word.GetGerman()),
			"der",
			"die",
			"das",
			"",
			word.GetId().Hex(),
		}

		c.JSON(200, game)
	}
}

func makeCheckAnswerHandle(finderUrl, scorerUrl string) func(c *gin.Context) {
	return func(c *gin.Context) {
		var (
			query      = map[string]string{}
			dictionary german.Dictionary
			noun       entity.Noun
			err        error
			returnCode int
		)

		query["__id"] = c.PostForm("id")

		dictionary, returnCode, err = game.FetchDictionary(finderUrl, query, 1)
		if err != nil {
			c.JSON(returnCode, fmt.Sprint(err))

			return
		}

		if len(dictionary.Nouns) == 0 {
			c.JSON(returnCode, "No noun was found.")

			return
		}

		noun = dictionary.Nouns[0]

		answeredRight := checkAnswer(noun, c.PostForm("result"))

		game.ScoreWords(scorerUrl, 10, []string{c.PostForm("id")})

		c.JSON(200, answeredRight)
	}
}

func checkAnswer(word entity.Noun, result string) bool {
	for _, article := range word.Articles {
		if article == entity.Der && result == "1" {
			return true
		}

		if article == entity.Die && result == "2" {
			return true
		}

		if article == entity.Das && result == "3" {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package main
<commit_msg>Use webhook package instead of main<commit_after>package webhook
<|endoftext|>


<commit_before>package async

import (
	"fmt"
	"reflect"
)

// Type used for callback
type Callback func(...interface{}) error

// Type used as a list of functions
type Functions []interface{}

// funcs is the struct used to control the stack
// of functions to be executed.
type funcs struct {
	Stack []reflect.Value
}

// executeNext executes recursively each function of
// the stack until it reachs the bottom of the stack or
// it is interrupted by an error or isn't called by one of the
// functions of the stack
func (f *funcs) executeNext(args ...interface{}) error {
	// end of stack, no need to proceed
	if len(f.Stack) == 0 {
		return nil
	}

	var (
		// true if function has the last argument of type `Callback`
		expectCallback bool
		// Prepare callback that will be passed to function if `expectCallback` is true
		next Callback = Callback(f.executeNext)
		// Get function to be executed
		fn reflect.Value = f.Stack[0]
		// Get type of the function to be executed
		fnt    reflect.Type = fn.Type()
                // Arguments to be sent to the function
		inArgs []reflect.Value
	)

	// If function expect any argument
	if l := fnt.NumIn(); l > 0 {
		// Get last argument of the function
		lastArg := fnt.In(l - 1)

		// Check if the last argument is a Callback
		expectCallback = lastArg.AssignableTo(reflect.TypeOf(next))
	}

	// Transform arguments from interface{} to reflect.Value
	for i := 0; i < len(args); i++ {
		inArgs = append(inArgs, reflect.ValueOf(args[i]))
	}

        // If function is expecting a `Callback`, append next to the function arguments
	if expectCallback {
		inArgs = append(inArgs, reflect.ValueOf(next))
	}

        // Remove current function from the stack
	f.Stack = f.Stack[1:len(f.Stack)]

	resArgs := fn.Call(inArgs)

	if lr := len(resArgs); lr > 0 {
		if e, ok := resArgs[lr-1].Interface().(error); ok {
			return e
		}
	}
	return nil
}

// Waterfall executes every function sequencially.
// The execution flow may be interrupted by not calling the  callback or returning an error.
// `firstArgs` is a slice of parameters to be passed to the first function of the stack.
func Waterfall(stack Functions, firstArgs ...interface{}) error {
	// Init stack of functions
	f := &funcs{}
	// Checks if arguments passed are valid functions.
	// If so, appends functions to `f.Stack`.
	for i := 0; i < len(stack); i++ {
		v := reflect.Indirect(reflect.ValueOf(stack[i]))

		if v.Kind() != reflect.Func {
			return fmt.Errorf("%T must be a Function ", v)
		}

		f.Stack = append(f.Stack, v)
	}

	return f.executeNext(firstArgs...)
}
<commit_msg>Improved check of Callback<commit_after>package async

import (
	"fmt"
	"reflect"
)

// Type used for callback
type Callback func(...interface{}) error

// Type used as a list of functions
type Functions []interface{}

// funcs is the struct used to control the stack
// of functions to be executed.
type funcs struct {
	Stack []reflect.Value
}

// executeNext executes recursively each function of
// the stack until it reachs the bottom of the stack or
// it is interrupted by an error or isn't called by one of the
// functions of the stack
func (f *funcs) executeNext(args ...interface{}) error {
	// end of stack, no need to proceed
	if len(f.Stack) == 0 {
		return nil
	}

	var (
		// true if function has the last argument of type `Callback`
		expectCallback bool
		// Prepare callback that will be passed to function if `expectCallback` is true
		next Callback = Callback(f.executeNext)
		// Get function to be executed
		fn reflect.Value = f.Stack[0]
		// Get type of the function to be executed
		fnt reflect.Type = fn.Type()
		// Arguments to be sent to the function
		inArgs []reflect.Value
	)

	// If function expect any argument
	if l := fnt.NumIn(); l > 0 {
		// Get last argument of the function
		lastArg := fnt.In(l - 1)

		// Check if the last argument is a Callback
		_, expectCallback = reflect.Zero(lastArg).Interface().(Callback)
	}

	// Transform arguments from interface{} to reflect.Value
	for i := 0; i < len(args); i++ {
		inArgs = append(inArgs, reflect.ValueOf(args[i]))
	}

	// If function is expecting a `Callback`, append next to the function arguments
	if expectCallback {
		inArgs = append(inArgs, reflect.ValueOf(next))
	}

	// Remove current function from the stack
	f.Stack = f.Stack[1:len(f.Stack)]

	resArgs := fn.Call(inArgs)

	if lr := len(resArgs); lr > 0 {
		if e, ok := resArgs[lr-1].Interface().(error); ok {
			return e
		}
	}
	return nil
}

// Waterfall executes every function sequencially.
// The execution flow may be interrupted by not calling the  callback or returning an error.
// `firstArgs` is a slice of parameters to be passed to the first function of the stack.
func Waterfall(stack Functions, firstArgs ...interface{}) error {
	// Init stack of functions
	f := &funcs{}
	// Checks if arguments passed are valid functions.
	// If so, appends functions to `f.Stack`.
	for i := 0; i < len(stack); i++ {
		v := reflect.Indirect(reflect.ValueOf(stack[i]))

		if v.Kind() != reflect.Func {
			return fmt.Errorf("%T must be a Function ", v)
		}

		f.Stack = append(f.Stack, v)
	}

	return f.executeNext(firstArgs...)
}
<|endoftext|>


<commit_before>package logging

import (
	"io"
	"os"
	"strings"

	lumberjack "gopkg.in/natefinch/lumberjack.v2"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
)

const (
	StdoutFile = "stdout"
)

var (
	defaultLogger = New(&Options{Level: "DEBUG"})

	callerKey    interface{} = "caller"
	messageKey   interface{} = "msg"
	errorKey     interface{} = "error"
	timestampKey interface{} = "ts"
)

// CallerKey returns the logging key to be used for the stack location of the logging call
func CallerKey() interface{} {
	return callerKey
}

// MessageKey returns the logging key to be used for the textual message of the log entry
func MessageKey() interface{} {
	return messageKey
}

// ErrorKey returns the logging key to be used for error instances
func ErrorKey() interface{} {
	return errorKey
}

// TimestampKey returns the logging key to be used for the timestamp
func TimestampKey() interface{} {
	return timestampKey
}

// DefaultLogger returns a global singleton default that logs to os.Stdout.
// This returned instance is safe for concurrent access.
func DefaultLogger() log.Logger {
	return defaultLogger
}

// Options stores the configuration of a Logger.  Lumberjack is used for rolling files.
type Options struct {
	// File is the system file path for the log file.  If set to "stdout", this will log to os.Stdout.
	// Otherwise, a lumberjack.Logger is created
	File string `json:"file"`

	// MaxSize is the lumberjack MaxSize
	MaxSize int `json:"maxsize"`

	// MaxAge is the lumberjack MaxAge
	MaxAge int `json:"maxage"`

	// MaxBackups is the lumberjack MaxBackups
	MaxBackups int `json:"maxbackups"`

	// JSON is a flag indicating whether JSON logging output is used.  The default is false,
	// meaning that logfmt output is used.
	JSON bool `json:"json"`

	// Level is the error level to output: ERROR, INFO, WARN, or DEBUG.  Any unrecognized string,
	// including the empty string, is equivalent to passing ERROR.
	Level string `json:"level"`

	// LoggerFactory overrides the JSON field if specified.  This function is used to produce
	// a go-kit Logger from an io.Writer.
	LoggerFactory func(io.Writer) log.Logger
}

func (o *Options) json() bool {
	if o != nil {
		return o.JSON
	}

	return false
}

func (o *Options) output() io.Writer {
	if o != nil && o.File != StdoutFile {
		return &lumberjack.Logger{
			Filename:   o.File,
			MaxSize:    o.MaxSize,
			MaxAge:     o.MaxAge,
			MaxBackups: o.MaxBackups,
		}
	}

	return log.NewSyncWriter(os.Stdout)
}

func (o *Options) loggerFactory() func(io.Writer) log.Logger {
	if o != nil {
		if o.LoggerFactory != nil {
			return o.LoggerFactory
		} else if o.JSON {
			return log.NewJSONLogger
		}
	}

	return log.NewLogfmtLogger
}

func (o *Options) level() string {
	if o != nil {
		return o.Level
	}

	return ""
}

// New creates a go-kit Logger from a set of options.  The options object can be nil,
// in which case a default logger that logs to os.Stdout is returned.  The returned logger
// includes the timestamp in UTC format and will filter according to the Level field.
//
// In order to allow arbitrary decoration, this function does not insert the caller information.
// Use DefaultCaller or Caller in this package to do that.
func New(o *Options) log.Logger {
	return NewFilter(
		log.With(
			o.loggerFactory()(o.output()),
			TimestampKey(), log.DefaultTimestampUTC,
		),
		o,
	)
}

// NewFilter applies the Options filtering rules in the package to an arbitrary go-kit Logger.
func NewFilter(next log.Logger, o *Options) log.Logger {
	switch strings.ToUpper(o.level()) {
	case "DEBUG":
		return level.NewFilter(next, level.AllowDebug())

	case "INFO":
		return level.NewFilter(next, level.AllowInfo())

	case "WARN":
		return level.NewFilter(next, level.AllowWarn())

	default:
		return level.NewFilter(next, level.AllowError())
	}
}

// DefaultCaller produces a contextual logger as with log.With, but automatically prepends the
// caller under the CallerKey.
//
// The logger returned by this function should not be further decorated.  This will cause the
// callstack to include the decorators, which is pointless.  Instead, decorate the next parameter
// prior to passing it to this function.
func DefaultCaller(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller}, keyvals...)...,
	)
}

func Error(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller, level.Key(), level.ErrorValue()}, keyvals...)...,
	)
}

func Info(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller, level.Key(), level.InfoValue()}, keyvals...)...,
	)
}

func Warn(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller, level.Key(), level.WarnValue()}, keyvals...)...,
	)
}

func Debug(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller, level.Key(), level.DebugValue()}, keyvals...)...,
	)
}
<commit_msg>interim<commit_after>package logging

import (
	"io"
	"os"
	"strings"

	"gopkg.in/natefinch/lumberjack.v2"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
)

const (
	StdoutFile = "stdout"
)

var (
	defaultLogger = log.NewNopLogger()

	callerKey    interface{} = "caller"
	messageKey   interface{} = "msg"
	errorKey     interface{} = "error"
	timestampKey interface{} = "ts"
)

// CallerKey returns the logging key to be used for the stack location of the logging call
func CallerKey() interface{} {
	return callerKey
}

// MessageKey returns the logging key to be used for the textual message of the log entry
func MessageKey() interface{} {
	return messageKey
}

// ErrorKey returns the logging key to be used for error instances
func ErrorKey() interface{} {
	return errorKey
}

// TimestampKey returns the logging key to be used for the timestamp
func TimestampKey() interface{} {
	return timestampKey
}

// DefaultLogger returns a global singleton NOP logger.
// This returned instance is safe for concurrent access.
func DefaultLogger() log.Logger {
	return defaultLogger
}

// Options stores the configuration of a Logger.  Lumberjack is used for rolling files.
type Options struct {
	// File is the system file path for the log file.  If set to "stdout", this will log to os.Stdout.
	// Otherwise, a lumberjack.Logger is created
	File string `json:"file"`

	// MaxSize is the lumberjack MaxSize
	MaxSize int `json:"maxsize"`

	// MaxAge is the lumberjack MaxAge
	MaxAge int `json:"maxage"`

	// MaxBackups is the lumberjack MaxBackups
	MaxBackups int `json:"maxbackups"`

	// JSON is a flag indicating whether JSON logging output is used.  The default is false,
	// meaning that logfmt output is used.
	JSON bool `json:"json"`

	// Level is the error level to output: ERROR, INFO, WARN, or DEBUG.  Any unrecognized string,
	// including the empty string, is equivalent to passing ERROR.
	Level string `json:"level"`

	// LoggerFactory overrides the JSON field if specified.  This function is used to produce
	// a go-kit Logger from an io.Writer.
	LoggerFactory func(io.Writer) log.Logger
}

func (o *Options) json() bool {
	if o != nil {
		return o.JSON
	}

	return false
}

func (o *Options) output() io.Writer {
	if o != nil && o.File != StdoutFile {
		return &lumberjack.Logger{
			Filename:   o.File,
			MaxSize:    o.MaxSize,
			MaxAge:     o.MaxAge,
			MaxBackups: o.MaxBackups,
		}
	}

	return log.NewSyncWriter(os.Stdout)
}

func (o *Options) loggerFactory() func(io.Writer) log.Logger {
	if o != nil {
		if o.LoggerFactory != nil {
			return o.LoggerFactory
		} else if o.JSON {
			return log.NewJSONLogger
		}
	}

	return log.NewLogfmtLogger
}

func (o *Options) level() string {
	if o != nil {
		return o.Level
	}

	return ""
}

// New creates a go-kit Logger from a set of options.  The options object can be nil,
// in which case a default logger that logs to os.Stdout is returned.  The returned logger
// includes the timestamp in UTC format and will filter according to the Level field.
//
// In order to allow arbitrary decoration, this function does not insert the caller information.
// Use DefaultCaller or Caller in this package to do that.
func New(o *Options) log.Logger {
	return NewFilter(
		log.With(
			o.loggerFactory()(o.output()),
			TimestampKey(), log.DefaultTimestampUTC,
		),
		o,
	)
}

// NewFilter applies the Options filtering rules in the package to an arbitrary go-kit Logger.
func NewFilter(next log.Logger, o *Options) log.Logger {
	switch strings.ToUpper(o.level()) {
	case "DEBUG":
		return level.NewFilter(next, level.AllowDebug())

	case "INFO":
		return level.NewFilter(next, level.AllowInfo())

	case "WARN":
		return level.NewFilter(next, level.AllowWarn())

	default:
		return level.NewFilter(next, level.AllowError())
	}
}

// DefaultCaller produces a contextual logger as with log.With, but automatically prepends the
// caller under the CallerKey.
//
// The logger returned by this function should not be further decorated.  This will cause the
// callstack to include the decorators, which is pointless.  Instead, decorate the next parameter
// prior to passing it to this function.
func DefaultCaller(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller}, keyvals...)...,
	)
}

func Error(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller, level.Key(), level.ErrorValue()}, keyvals...)...,
	)
}

func Info(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller, level.Key(), level.InfoValue()}, keyvals...)...,
	)
}

func Warn(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller, level.Key(), level.WarnValue()}, keyvals...)...,
	)
}

func Debug(next log.Logger, keyvals ...interface{}) log.Logger {
	return log.WithPrefix(
		next,
		append([]interface{}{CallerKey(), log.DefaultCaller, level.Key(), level.DebugValue()}, keyvals...)...,
	)
}
<|endoftext|>