<commit_before>package smpp34

import (
	"encoding/hex"
	"fmt"
	. "launchpad.net/gocheck"
	"net"
	"time"
)

func (x *MySuite) Test_Smpp(c *C) {
	go startServer(c)
	time.Sleep(1 * time.Second)

	s, err := NewSmppConnect("localhost", 8080)

	if err != nil {
		fmt.Println("Connect Err:", err)
		return
	}

	p, err := s.Bind(BIND_TRANSCEIVER, "test", "pass", &Params{})
	if err != nil {
		fmt.Println("Bind:", err)
		return
	}
	s.Write(p)

	p, err = s.Read()
	if err != nil {
		fmt.Println("BindResp:", err)
		return
	}

	c.Check(p.GetHeader().Id, Equals, BIND_TRANSCEIVER_RESP)
	c.Check(p.GetField(SYSTEM_ID).String(), Equals, "hugo")

	_, err = s.Read()
	c.Check(err, NotNil)
	c.Check(err.Error(), Equals, "Unknown PDU Type. ID:2415919125")

	_, err = s.Read()
	c.Check(err, NotNil)
	c.Check(err.Error(), Equals, "PDU Len different than read bytes")

	_, err = s.Read()
	c.Check(err, NotNil)
	c.Check(err.Error(), Equals, "PDU Len larger than MAX_PDU_SIZE")

}

func startServer(c *C) {
	ln, err := net.Listen("tcp", ":8080")

	if err != nil {
		// handle error
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			// handle error
			continue
		}
		go handleConnection(conn, c)
	}
}

func handleConnection(n net.Conn, c *C) {
	l := make([]byte, 1024)
	i, err := n.Read(l)

	if err != nil {
		fmt.Println("server read error")
	}

	// Bind Resp
	d, _ := hex.DecodeString("000000158000000900000000000000016875676f00")
	n.Write(d)

	time.Sleep(100 * time.Millisecond)

	// Invalid CMD ID
	d, _ = hex.DecodeString("0000001090000015000000005222b523")
	n.Write(d)

	time.Sleep(100 * time.Millisecond)

	// PDU Len different than read bytes
	d, _ = hex.DecodeString("000000178000000900000000000000016875676f00")
	n.Write(d)

	time.Sleep(100 * time.Millisecond)

	// Max PDU Len err
	d, _ = hex.DecodeString("0000F01080000015000000005222b526")
	n.Write(d)

	fmt.Println(i)
}
<commit_msg>removed unused var<commit_after>package smpp34

import (
	"encoding/hex"
	"fmt"
	. "launchpad.net/gocheck"
	"net"
	"time"
)

func (x *MySuite) Test_Smpp(c *C) {
	go startServer(c)
	time.Sleep(1 * time.Second)

	s, err := NewSmppConnect("localhost", 8080)

	if err != nil {
		fmt.Println("Connect Err:", err)
		return
	}

	p, err := s.Bind(BIND_TRANSCEIVER, "test", "pass", &Params{})
	if err != nil {
		fmt.Println("Bind:", err)
		return
	}
	s.Write(p)

	p, err = s.Read()
	if err != nil {
		fmt.Println("BindResp:", err)
		return
	}

	c.Check(p.GetHeader().Id, Equals, BIND_TRANSCEIVER_RESP)
	c.Check(p.GetField(SYSTEM_ID).String(), Equals, "hugo")

	_, err = s.Read()
	c.Check(err, NotNil)
	c.Check(err.Error(), Equals, "Unknown PDU Type. ID:2415919125")

	_, err = s.Read()
	c.Check(err, NotNil)
	c.Check(err.Error(), Equals, "PDU Len different than read bytes")

	_, err = s.Read()
	c.Check(err, NotNil)
	c.Check(err.Error(), Equals, "PDU Len larger than MAX_PDU_SIZE")

}

func startServer(c *C) {
	ln, err := net.Listen("tcp", ":8080")

	if err != nil {
		// handle error
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			// handle error
			continue
		}
		go handleConnection(conn, c)
	}
}

func handleConnection(n net.Conn, c *C) {
	l := make([]byte, 1024)
	_, err := n.Read(l)

	if err != nil {
		fmt.Println("server read error")
	}

	// Bind Resp
	d, _ := hex.DecodeString("000000158000000900000000000000016875676f00")
	n.Write(d)

	time.Sleep(100 * time.Millisecond)

	// Invalid CMD ID
	d, _ = hex.DecodeString("0000001090000015000000005222b523")
	n.Write(d)

	time.Sleep(100 * time.Millisecond)

	// PDU Len different than read bytes
	d, _ = hex.DecodeString("000000178000000900000000000000016875676f00")
	n.Write(d)

	time.Sleep(100 * time.Millisecond)

	// Max PDU Len err
	d, _ = hex.DecodeString("0000F01080000015000000005222b526")
	n.Write(d)
}
<|endoftext|>


<commit_before>package helpers

const HOUR_BALL_CAP = 11
const FIVE_MIN_BALL_CAP = 11
const ONE_MIN_BALL_CAP = 4

func IsFinished(queue *helpers.BallQueue, balls int) bool {
	if queue.Len() != balls {
		return false
	}

	q := *queue

	if q[0] != 1 || q[balls - 1] != balls {
		return false
	}

	for i := 2; i < balls; i++ {
		if q[i - 1] != i {
		 return false
		}
	}

	return true
}

func CycleQueue(queue, mainQueue *helpers.BallQueue) {
	for queue.Len() > 0 {
		mainQueue.Push(queue.Pop())
	}
}

func Process(mainQueue, minQueue, fiveMinQueue, hourQueue *helpers.BallQueue) int {
	ball := mainQueue.Shift()

	if minQueue.Len() < ONE_MIN_BALL_CAP {
		minQueue.Push(ball)
		return 0
	}

	cycleQueue(minQueue, mainQueue)

	if fiveMinQueue.Len() < FIVE_MIN_BALL_CAP {
		fiveMinQueue.Push(ball)
		return 0
	}

	cycleQueue(fiveMinQueue, mainQueue)

	if hourQueue.Len() < HOUR_BALL_CAP {
		hourQueue.Push(ball)
		return 0
	}

	cycleQueue(hourQueue, mainQueue)

	mainQueue.Push(ball)
	return 1
}
<commit_msg>Fixing nameing<commit_after>package helpers

const HOUR_BALL_CAP = 11
const FIVE_MIN_BALL_CAP = 11
const ONE_MIN_BALL_CAP = 4

func IsFinished(queue *BallQueue, balls int) bool {
	if queue.Len() != balls {
		return false
	}

	q := *queue

	if q[0] != 1 || q[balls - 1] != balls {
		return false
	}

	for i := 2; i < balls; i++ {
		if q[i - 1] != i {
		 return false
		}
	}

	return true
}

func CycleQueue(queue, mainQueue *BallQueue) {
	for queue.Len() > 0 {
		mainQueue.Push(queue.Pop())
	}
}

func Process(mainQueue, minQueue, fiveMinQueue, hourQueue *BallQueue) int {
	ball := mainQueue.Shift()

	if minQueue.Len() < ONE_MIN_BALL_CAP {
		minQueue.Push(ball)
		return 0
	}

	cycleQueue(minQueue, mainQueue)

	if fiveMinQueue.Len() < FIVE_MIN_BALL_CAP {
		fiveMinQueue.Push(ball)
		return 0
	}

	cycleQueue(fiveMinQueue, mainQueue)

	if hourQueue.Len() < HOUR_BALL_CAP {
		hourQueue.Push(ball)
		return 0
	}

	cycleQueue(hourQueue, mainQueue)

	mainQueue.Push(ball)
	return 1
}
<|endoftext|>


<commit_before>package spi

type Topology struct {
	Substrates []Substrate
	Elements   []Element
}

type Substrate struct {
	Name     string
	Capacity Capacity
	Latency  Latency
}

type Element interface{}

type Computer struct {
	Name      string
	Interface []Interface
	OSs       []OS
}

type Interface struct {
	Name      string
	Substrate string
	Capacity  Capacity
	Latency   Latency
}

type OS struct {
	Name    string
	Version string
}

type Capacity struct {
	Rate float64
	Kind Kind
}

type Latency struct {
	Time float64
	Kind Kind
}

type Kind struct {
	Value String //should be either "max" or "average"
}
<commit_msg>whoops<commit_after>package spi

type Topology struct {
	Substrates []Substrate
	Elements   []Element
}

type Substrate struct {
	Name     string
	Capacity Capacity
	Latency  Latency
}

type Element interface{}

type Computer struct {
	Name      string
	Interface []Interface
	OSs       []OS
}

type Interface struct {
	Name      string
	Substrate string
	Capacity  Capacity
	Latency   Latency
}

type OS struct {
	Name    string
	Version string
}

type Capacity struct {
	Rate float64
	Kind Kind
}

type Latency struct {
	Time float64
	Kind Kind
}

type Kind struct {
	Value string //should be either "max" or "average"
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net"
	"os"

	pb "github.com/pgombola/gomad/status"
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

var (
	tls = flag.Bool("tls", false, "True to use TLS for connection, false for plaintext.")
	// Add additional flags for certs
	port = flag.Int("port", 10000, "The server port.")
)

type clusterStatusServer struct {
}

func (s *clusterStatusServer) ListHosts(req *pb.HostsRequest, stream pb.ClusterStatus_ListHostsServer) error {
	stream.Send(&pb.HostReply{Hostname: "server-1", Port: -1, Status: pb.HostReply_STARTED})
	return nil
}

func newServer() *clusterStatusServer {
	s := new(clusterStatusServer)
	return s
}

func main() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		grpclog.Fatalf("Failed to listen: %v", err)
		os.Exit(-1)
	}
	var opts []grpc.ServerOption
	grpcServer := grpc.NewServer(opts...)
	pb.RegisterClusterStatusServer(grpcServer, newServer())
	grpcServer.Serve(lis)
}
<commit_msg>Modified grpc server to call gomad client<commit_after>package main

import (
	"flag"
	"fmt"
	"net"
	"os"

	"github.com/pgombola/gomad/client"
	pb "github.com/pgombola/gomad/status"
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

var (
	tls = flag.Bool("tls", false, "True to use TLS for connection, false for plaintext.")
	// Add additional flags for certs
	port = flag.Int("port", 10000, "The server port.")
)

type statusError struct {
	S string
}

type clusterStatusServer struct {
}

func (s *clusterStatusServer) ListHosts(req *pb.HostsRequest, stream pb.ClusterStatus_ListHostsServer) error {
	hosts := client.PopulateHosts("http://10.10.20.31:4646")
	clarifyJob := findClarifyJob(client.PopulateJobs("http://10.10.20.31:4646"))

	for _, host := range hosts {
		stream.Send(&pb.HostReply{Hostname: host.Name, Status: status(clarifyJob)})
	}
	return nil
}

func findClarifyJob(jobs []client.Job) *client.Job {
	for _, job := range jobs {
		if job.Name == "clarify" {
			return &job
		}
	}
	return &client.Job{Status: "stopped"}
}

func status(job *client.Job) pb.HostReply_HostStatus {
	switch job.Status {
	case "running":
		return pb.HostReply_STARTED
	case "stopped":
		return pb.HostReply_STOPPED
	}
	return pb.HostReply_MIXED
}

func clarifyStarted(job client.Job) bool {
	return job.Status == "running"
}

func newServer() *clusterStatusServer {
	s := new(clusterStatusServer)
	return s
}

func main() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		grpclog.Fatalf("Failed to listen: %v", err)
		os.Exit(-1)
	}
	var opts []grpc.ServerOption
	grpcServer := grpc.NewServer(opts...)
	pb.RegisterClusterStatusServer(grpcServer, newServer())
	grpcServer.Serve(lis)
}
<|endoftext|>


<commit_before>package gogithub

const (
	// NormalURL represents a GitHub normal URL.
	NormalURL = "https://github.com"
	// APIURL represents a GitHub API URL.
	APIURL = "https://api.github.com"
	// AccessTokenURL represents an OAuth access token API URL.
	AccessTokenURL = NormalURL + "/login/oauth/access_token"
	// AuthenticatedUserURL represents a get user API URL.
	AuthenticatedUserURL = APIURL + "/user"
	// URLParamPrefix represents a URL parameter prefix.
	URLParamPrefix = "?"
	// ParamKeyAccessToken represents a parameter key of an access token.
	ParamKeyAccessToken = "access_token"
	// GetContentsURL represents a get contents API URL.
	GetContentsURL = APIURL + "/repos/%s/%s/contents/%s"
	// GetContentURL represents a get content API URL.
	GetContentURL = "https://raw.githubusercontent.com/%s/%s/%s/%s"
	// SearchRepositoriesURL represents a search repositories API URL.
	SearchRepositoriesURL = APIURL + "/search/repositories"
)
<commit_msg>Update consts.go.<commit_after>package gogithub

// consts definition
const (
	NormalURL             = "https://github.com"
	APIURL                = "https://api.github.com"
	AccessTokenURL        = NormalURL + "/login/oauth/access_token"
	AuthenticatedUserURL  = APIURL + "/user"
	URLParamPrefix        = "?"
	ParamKeyAccessToken   = "access_token"
	GetContentsURL        = APIURL + "/repos/%s/%s/contents/%s"
	GetContentURL         = "https://raw.githubusercontent.com/%s/%s/%s/%s"
	SearchRepositoriesURL = APIURL + "/search/repositories"
)
<|endoftext|>


<commit_before>package cfutil

import (
	"errors"
	"fmt"
	consul "github.com/hashicorp/consul/api"
	"net/url"
	"os"
)

func Services() (map[string]*consul.AgentService, error) {
	client, err := NewConsulClient()
	if err != nil {
		return nil, err
	}
	return client.Agent().Services()
}

func ServiceRegister(name string, path string, tags ...string) error {
	appEnv, _ := Current()
	client, err := NewConsulClient()
	if err != nil {
		return err
	}
	err = client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		Name:    name,
		Address: appEnv.ApplicationURIs[0],
		Tags:    tags,
		Check: &consul.AgentServiceCheck{
			HTTP:     fmt.Sprintf(schemaForServices() + "://" + appEnv.ApplicationURIs[0] + path),
			Interval: "60s",
		},
	})
	if err != nil {
		return err
	}
	return nil
}

func NewConsulClient() (*consul.Client, error) {
	dialScheme, dialHost, err := consulDialstring("consul")
	if err != nil {
		return nil, err
	}
	client, consulErr := consul.NewClient(&consul.Config{
		Address: dialHost,
		Scheme:  dialScheme,
		Token:   os.Getenv("CONSUL_TOKEN"),
	})
	if consulErr != nil {
		return nil, consulErr
	}
	return client, nil
}

func consulDialstring(serviceName string) (string, string, error) {
	if consulMaster := os.Getenv("CONSUL_MASTER"); consulMaster != "" {
		parsed, err := url.Parse(consulMaster)
		if err == nil {
			return parsed.Scheme, parsed.Host, nil
		}
	}
	// Fallack to Consul service. Deprecated
	appEnv, _ := Current()
	consulService, err := appEnv.Services.WithName(serviceName)
	if err != nil {
		return schemaForConsul(), "", err
	}
	port := "8500"

	uri, ok := consulService.Credentials["uri"].(string)
	if ok {
		u, err := url.Parse(uri)
		if err == nil {
			return schemaForConsul(), fmt.Sprintf("%s", u.Host), nil
		}
		// Fallback to hostname/port lookup
	}

	hostname, ok := consulService.Credentials["hostname"].(string)
	if !ok {
		return schemaForConsul(), "", errors.New("consul service not available")
	}
	port, ok = consulService.Credentials["port"].(string)
	if ok {
		return schemaForConsul(), fmt.Sprintf("%s:%s", hostname, port), nil
	}
	return schemaForConsul(), fmt.Sprintf("%s:8500", hostname), nil
}

func schemaForServices() string {
	if ForceHTTP() {
		return "http"
	}
	return "https"
}

func schemaForConsul() string {
	return "http"
}
<commit_msg>Kill support for consul service<commit_after>package cfutil

import (
	"errors"
	"fmt"
	consul "github.com/hashicorp/consul/api"
	"net/url"
	"os"
)

func Services() (map[string]*consul.AgentService, error) {
	client, err := NewConsulClient()
	if err != nil {
		return nil, err
	}
	return client.Agent().Services()
}

func ServiceRegister(name string, path string, tags ...string) error {
	appEnv, _ := Current()
	client, err := NewConsulClient()
	if err != nil {
		return err
	}
	err = client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		Name:    name,
		Address: appEnv.ApplicationURIs[0],
		Tags:    tags,
		Check: &consul.AgentServiceCheck{
			HTTP:     fmt.Sprintf(schemaForServices() + "://" + appEnv.ApplicationURIs[0] + path),
			Interval: "60s",
		},
	})
	if err != nil {
		return err
	}
	return nil
}

func NewConsulClient() (*consul.Client, error) {
	dialScheme, dialHost, err := consulDialstring("consul")
	if err != nil {
		return nil, err
	}
	client, consulErr := consul.NewClient(&consul.Config{
		Address: dialHost,
		Scheme:  dialScheme,
		Token:   os.Getenv("CONSUL_TOKEN"),
	})
	if consulErr != nil {
		return nil, consulErr
	}
	return client, nil
}

func consulDialstring(serviceName string) (string, string, error) {
	consulMaster := ""
	if consulMaster = os.Getenv("CONSUL_MASTER"); consulMaster != "" {
		parsed, err := url.Parse(consulMaster)
		if err == nil {
			return parsed.Scheme, parsed.Host, nil
		}
	}
	return "", "", errors.New("CONSUL_MASTER not found or invalid url")
}

func schemaForServices() string {
	if ForceHTTP() {
		return "http"
	}
	return "https"
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"net"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"

	"github.com/xthexder/go-jack"
	"github.com/xthexder/rawstreamer"
)

var Client *jack.Client
var Ports []*jack.Port
var Buffers []unsafe.Pointer
var BufferPool chan []jack.AudioSample
var Listener net.Listener
var ClientWaitGroup sync.WaitGroup
var ShuttingDown chan struct{}

func process(nframes uint32) int {
	lsamples := Ports[0].GetBuffer(nframes)
	rsamples := Ports[1].GetBuffer(nframes)
	for client, bufp := range Buffers {
		buf := (*chan []jack.AudioSample)(atomic.LoadPointer(&bufp))
		if buf == nil {
			continue
		}

		n := 0
		for n < len(lsamples) {
			buffer := <-BufferPool
			nl := copy(buffer[:len(buffer)/2], lsamples[n:])
			buffer = buffer[:nl*2]
			copy(buffer[nl:], rsamples[n:])
			n += nl

			select {
			case *buf <- buffer:
			default:
				fmt.Println("Channel full for client:", client)
				n = len(lsamples)
			}
		}
	}
	return 0
}

func initPortMirror() {
	if len(mirror) <= 0 {
		return
	}

	ports := Client.GetPorts("^"+mirror, Ports[0].GetType(), jack.PortIsInput)
	for i, name := range ports {
		if i < len(Ports) {
			port := Client.GetPortByName(name)
			connections := port.GetConnections()
			for _, conn := range connections {
				Client.Connect(conn, Ports[i].GetName())
			}
		}
	}
}

func updateProcs() {
	if len(procName) <= 0 {
		return
	}

	ports := Client.GetPorts("^alsa-jack\\.jackP\\.", Ports[0].GetType(), jack.PortIsOutput)
	procs := make(map[string]int)
	for _, name := range ports {
		clientName := strings.SplitN(name, ":", 2)[0]
		pidStr := strings.SplitN(clientName[16:], ".", 2)[0]
		pid, err := strconv.Atoi(pidStr)
		if err != nil {
			continue
		}
		procs[clientName] = pid
	}
	for clientName, pid := range procs {
		out, err := exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "comm=").Output()
		if err != nil {
			fmt.Println("Error finding process:", err)
			return
		}
		proc := string(out)
		if strings.Contains(proc, procName) {
			ports = Client.GetPorts("^"+clientName+":", Ports[0].GetType(), jack.PortIsOutput)
			for i, name := range ports {
				if i < len(Ports) {
					Client.Connect(name, Ports[i].GetName())
				}
			}
		}
	}
}

func updateSources() {
	if len(source) <= 0 {
		return
	}

	ports := Client.GetPorts("^"+source, Ports[0].GetType(), jack.PortIsOutput)
	sources := make(map[string][]string)
	for _, name := range ports {
		clientName := strings.SplitN(name, ":", 2)[0]
		sources[clientName] = append(sources[clientName], name)
	}
	for _, names := range sources {
		if len(names) == 1 { // Mono source
			for _, port := range Ports {
				Client.Connect(names[0], port.GetName())
			}
		} else {
			for i, name := range names {
				if i < len(Ports) {
					Client.Connect(name, Ports[i].GetName())
				}
			}
		}
	}
}

func portRegistered(portId jack.PortId, registered bool) {
	if registered && !Client.IsPortMine(Client.GetPortById(portId)) {
		go updateProcs()
		go updateSources()
	}
}

func disconnectClients() {
	for _, bufp := range Buffers {
		buf := (*chan []jack.AudioSample)(atomic.LoadPointer(&bufp))
		if buf == nil {
			continue
		}
		close(*buf)
	}
}

func sampleRateChanged(sampleRate uint32) int {
	printStreamInfo()
	disconnectClients()
	return 0
}

func bufferSizeChanged(bufferSize uint32) int {
	atomic.StoreUint32(&jackBufferSize, bufferSize*2)
	return 0
}

func portConnect(portAId, portBId jack.PortId, connected bool) {
	portA := Client.GetPortById(portAId)
	portB := Client.GetPortById(portBId)
	if Client.IsPortMine(portB) {
		return
	}

	if len(mirror) > 0 && strings.HasPrefix(portB.GetName(), mirror) {
		clientName := portB.GetClientName()
		go func() {
			ports := Client.GetPorts("^"+clientName+":", Ports[0].GetType(), jack.PortIsInput)
			for i, name := range ports {
				if name == portB.GetName() && i < len(Ports) {
					if connected {
						Client.ConnectPorts(portA, Ports[i])
					} else {
						Client.DisconnectPorts(portA, Ports[i])
					}
					break
				}
			}
		}()
	}
}

func shutdown() {
	fmt.Println("Shutting down")
	close(ShuttingDown)
	Listener.Close()
	disconnectClients()
}

func streamConnection(conn *net.TCPConn) {
	defer conn.Close()
	defer ClientWaitGroup.Done()
	bufi, buf := getBuffer()
	if buf == nil {
		conn.Write([]byte{'R', 0})
		conn.Close()
		return
	}

	defer atomic.StorePointer(&Buffers[bufi], nil)

	numBytes := bits / 8
	conn.Write([]byte{'R', 1, formatFlag, byte(numBytes)})
	bytes := make([]byte, 4)
	sampleRate := Client.GetSampleRate()
	endianness.PutUint32(bytes, sampleRate)
	conn.Write(bytes[:4])

	sample := make([]byte, bits/4)
	align := 8 % len(sample)
	if align > 0 {
		// Add extra padding to make the header an even number of samples in length
		conn.Write(sample[:len(sample)-align])
	}

	bytes = make([]byte, 0, int(Client.GetBufferSize())*2)
	for {
		buffer := <-buf
		if buffer == nil {
			return
		}

		lsamples := buffer[:len(buffer)/2]
		rsamples := buffer[len(buffer)/2:]

		for i := range lsamples {
			rawstreamer.WriteFloat32(sample[:numBytes], float32(lsamples[i]), formatFlag, endianness)
			rawstreamer.WriteFloat32(sample[numBytes:], float32(rsamples[i]), formatFlag, endianness)

			bytes = append(bytes, sample...)
		}

		conn.SetWriteDeadline(time.Now().Add(bufferLen))
		count := 0
		for count < len(bytes) {
			n, err := conn.Write(bytes[count:])
			if err != nil {
				if err2, ok := err.(*net.OpError); ok && err2.Timeout() {
					fmt.Println("Write timeout!")

					// Add extra padding to make sure we're still aligned
					align := count % len(sample)
					conn.SetWriteDeadline(time.Time{})
					for align > 0 {
						n, err := conn.Write(sample[:len(sample)-align])
						if err != nil {
							fmt.Println("Realign write error: %v\n", err)
							return
						}
						align -= n
					}
					break
				} else {
					fmt.Printf("Sample write error: %v\n", err)
					returnToPool(buffer)
					return
				}
			}
			count += n
		}
		returnToPool(buffer)

		bytes = bytes[:0]
	}
}

var bufSync sync.Mutex

func getBufferChanSize() int {
	bufferSize := time.Duration(atomic.LoadUint32(&jackBufferSize) / 2)
	sampleRate := time.Duration(Client.GetSampleRate())
	return int(bufferLen*sampleRate/bufferSize/time.Second) + 1
}

func getBuffer() (int, chan []jack.AudioSample) {
	bufSync.Lock()
	defer bufSync.Unlock()

	for i, bufp := range Buffers {
		buf := (*chan []jack.AudioSample)(atomic.LoadPointer(&bufp))
		if buf != nil {
			continue
		}
		buf2 := make(chan []jack.AudioSample, getBufferChanSize())
		atomic.StorePointer(&Buffers[i], unsafe.Pointer(&buf2))
		return i, buf2
	}
	return -1, nil
}

var jackBufferSize uint32

func initPool() {
	atomic.StoreUint32(&jackBufferSize, Client.GetBufferSize()*2)
	bufferSize := int(atomic.LoadUint32(&jackBufferSize))
	BufferPool = make(chan []jack.AudioSample, getBufferChanSize()*maxConns)
	for i := 0; i < cap(BufferPool); i++ {
		BufferPool <- make([]jack.AudioSample, bufferSize)
	}
}

func returnToPool(buffer []jack.AudioSample) {
	bufferSize := int(atomic.LoadUint32(&jackBufferSize))
	if cap(buffer) < bufferSize {
		BufferPool <- make([]jack.AudioSample, bufferSize)
	} else {
		BufferPool <- buffer[:bufferSize]
	}
}

func printStreamInfo() {
	fmt.Printf("Stream info: %dHz, ", Client.GetSampleRate())
	fmt.Printf("%dbit %s, ", bits, rawstreamer.EncodingString[formatFlag&rawstreamer.EncodingMask])
	fmt.Printf("%s, %v max buffer\n", endianness.String(), bufferLen)
}

func main() {
	ShuttingDown = make(chan struct{})

	if bits < 8 || bits > 32 || bits%8 != 0 {
		fmt.Println("Bit-depth must be one of: 8, 16, 24, 32")
		return
	}
	formatFlag = 0
	for flag, str := range rawstreamer.EncodingString {
		if format == str {
			formatFlag = flag
			break
		}
	}
	if formatFlag == 0 {
		fmt.Printf("Unsupported stream format: %s\n", format)
		return
	}
	if formatFlag == rawstreamer.EncodingFloatingPoint {
		bits = 32
	}

	if bigEndian {
		endianness = binary.BigEndian
		formatFlag |= rawstreamer.EncodingBigEndian
	} else {
		endianness = binary.LittleEndian
		formatFlag |= rawstreamer.EncodingLittleEndian
	}

	var err error
	bufferLen, err = time.ParseDuration(bufferStr)
	if err != nil {
		fmt.Printf("Invalid buffer length: %v\n", err)
		return
	}

	var status int
	Client, status = jack.ClientOpen("Raw Streamer", jack.NoStartServer)
	if status != 0 {
		fmt.Println("Status:", status)
		return
	}
	defer Client.Close()

	if code := Client.SetProcessCallback(process); code != 0 {
		fmt.Printf("Failed to set process callback: %d\n", code)
		return
	}
	if code := Client.SetSampleRateCallback(sampleRateChanged); code != 0 {
		fmt.Printf("Failed to set sample rate callback: %d\n", code)
		return
	}
	if code := Client.SetBufferSizeCallback(bufferSizeChanged); code != 0 {
		fmt.Printf("Failed to set buffer size callback: %d\n", code)
		return
	}
	if code := Client.SetPortRegistrationCallback(portRegistered); code != 0 {
		fmt.Printf("Failed to set port registration callback: %d\n", code)
		return
	}
	if code := Client.SetPortConnectCallback(portConnect); code != 0 {
		fmt.Printf("Failed to set port connect callback: %d\n", code)
		return
	}
	Client.OnShutdown(shutdown)

	if code := Client.Activate(); code != 0 {
		fmt.Printf("Failed to activate client: %d\n", code)
		return
	}

	for i := 0; i < 2; i++ {
		port := Client.PortRegister(fmt.Sprintf("in_%d", i), jack.DEFAULT_AUDIO_TYPE, jack.PortIsInput, 0)
		Ports = append(Ports, port)
	}
	Buffers = make([]unsafe.Pointer, maxConns)
	initPool()

	initPortMirror()
	updateProcs()
	updateSources()

	Listener, err = net.Listen("tcp", addr)
	if err != nil {
		fmt.Printf("Error listening on address '%s': %v\n", addr, err)
		return
	} else {
		fmt.Printf("Listening on address: %s\n", Listener.Addr().String())
	}
	for {
		conn, err := Listener.Accept()
		if err != nil {
			select {
			case <-ShuttingDown:
				ClientWaitGroup.Wait()
				return
			default:
				fmt.Printf("Error accepting connection: %v\n", err)
				continue
			}
		}
		ClientWaitGroup.Add(1)
		go streamConnection(conn.(*net.TCPConn))
	}
}

var formatFlag byte
var endianness binary.ByteOrder
var bufferLen time.Duration

var addr string
var maxConns int

var bits int
var format string
var bigEndian bool
var littleEndian bool

var bufferStr string

var mirror string
var procName string
var source string

func init() {
	flag.StringVar(&addr, "addr", ":5253", "Listen address")
	flag.IntVar(&maxConns, "max-conn", 128, "Maximum number of connected clients")

	flag.IntVar(&bits, "bits", 24, "Stream bit-depth")
	flag.StringVar(&format, "format", "int", "Stream format (int, uint, float)")
	flag.BoolVar(&bigEndian, "big-endian", false, "Big-endian stream encoding")
	flag.BoolVar(&littleEndian, "little-endian", true, "Little-endian stream encoding (default)")

	flag.StringVar(&bufferStr, "buffer", "100ms", "Max buffer length")

	flag.StringVar(&mirror, "mirror", "", "The name of a port to mirror (prefix matched)")
	flag.StringVar(&procName, "proc-name", "", "An alsa process to auto-connect (substring matched)")
	flag.StringVar(&source, "source", "", "The name of a port to auto-connect (prefix matched)")
	flag.Parse()
}
<commit_msg>Fix alignment issues during timeouts<commit_after>package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"net"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"

	"github.com/xthexder/go-jack"
	"github.com/xthexder/rawstreamer"
)

var Client *jack.Client
var Ports []*jack.Port
var Buffers []unsafe.Pointer
var BufferPool chan []jack.AudioSample
var Listener net.Listener
var ClientWaitGroup sync.WaitGroup
var ShuttingDown chan struct{}

func process(nframes uint32) int {
	lsamples := Ports[0].GetBuffer(nframes)
	rsamples := Ports[1].GetBuffer(nframes)
	for client, bufp := range Buffers {
		buf := (*chan []jack.AudioSample)(atomic.LoadPointer(&bufp))
		if buf == nil {
			continue
		}

		n := 0
		for n < len(lsamples) {
			buffer := <-BufferPool
			nl := copy(buffer[:len(buffer)/2], lsamples[n:])
			buffer = buffer[:nl*2]
			copy(buffer[nl:], rsamples[n:])
			n += nl

			select {
			case *buf <- buffer:
			default:
				fmt.Println("Channel full for client:", client)
				n = len(lsamples)
			}
		}
	}
	return 0
}

func initPortMirror() {
	if len(mirror) <= 0 {
		return
	}

	ports := Client.GetPorts("^"+mirror, Ports[0].GetType(), jack.PortIsInput)
	for i, name := range ports {
		if i < len(Ports) {
			port := Client.GetPortByName(name)
			connections := port.GetConnections()
			for _, conn := range connections {
				Client.Connect(conn, Ports[i].GetName())
			}
		}
	}
}

func updateProcs() {
	if len(procName) <= 0 {
		return
	}

	ports := Client.GetPorts("^alsa-jack\\.jackP\\.", Ports[0].GetType(), jack.PortIsOutput)
	procs := make(map[string]int)
	for _, name := range ports {
		clientName := strings.SplitN(name, ":", 2)[0]
		pidStr := strings.SplitN(clientName[16:], ".", 2)[0]
		pid, err := strconv.Atoi(pidStr)
		if err != nil {
			continue
		}
		procs[clientName] = pid
	}
	for clientName, pid := range procs {
		out, err := exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "comm=").Output()
		if err != nil {
			fmt.Println("Error finding process:", err)
			return
		}
		proc := string(out)
		if strings.Contains(proc, procName) {
			ports = Client.GetPorts("^"+clientName+":", Ports[0].GetType(), jack.PortIsOutput)
			for i, name := range ports {
				if i < len(Ports) {
					Client.Connect(name, Ports[i].GetName())
				}
			}
		}
	}
}

func updateSources() {
	if len(source) <= 0 {
		return
	}

	ports := Client.GetPorts("^"+source, Ports[0].GetType(), jack.PortIsOutput)
	sources := make(map[string][]string)
	for _, name := range ports {
		clientName := strings.SplitN(name, ":", 2)[0]
		sources[clientName] = append(sources[clientName], name)
	}
	for _, names := range sources {
		if len(names) == 1 { // Mono source
			for _, port := range Ports {
				Client.Connect(names[0], port.GetName())
			}
		} else {
			for i, name := range names {
				if i < len(Ports) {
					Client.Connect(name, Ports[i].GetName())
				}
			}
		}
	}
}

func portRegistered(portId jack.PortId, registered bool) {
	if registered && !Client.IsPortMine(Client.GetPortById(portId)) {
		go updateProcs()
		go updateSources()
	}
}

func disconnectClients() {
	for _, bufp := range Buffers {
		buf := (*chan []jack.AudioSample)(atomic.LoadPointer(&bufp))
		if buf == nil {
			continue
		}
		close(*buf)
	}
}

func sampleRateChanged(sampleRate uint32) int {
	printStreamInfo()
	disconnectClients()
	return 0
}

func bufferSizeChanged(bufferSize uint32) int {
	atomic.StoreUint32(&jackBufferSize, bufferSize*2)
	return 0
}

func portConnect(portAId, portBId jack.PortId, connected bool) {
	portA := Client.GetPortById(portAId)
	portB := Client.GetPortById(portBId)
	if Client.IsPortMine(portB) {
		return
	}

	if len(mirror) > 0 && strings.HasPrefix(portB.GetName(), mirror) {
		clientName := portB.GetClientName()
		go func() {
			ports := Client.GetPorts("^"+clientName+":", Ports[0].GetType(), jack.PortIsInput)
			for i, name := range ports {
				if name == portB.GetName() && i < len(Ports) {
					if connected {
						Client.ConnectPorts(portA, Ports[i])
					} else {
						Client.DisconnectPorts(portA, Ports[i])
					}
					break
				}
			}
		}()
	}
}

func shutdown() {
	fmt.Println("Shutting down")
	close(ShuttingDown)
	Listener.Close()
	disconnectClients()
}

var ErrWriteTimeout = fmt.Errorf("write timeout")

func writeAligned(conn *net.TCPConn, buf []byte, timeout time.Time) error {
	conn.SetWriteDeadline(timeout)
	n, err := conn.Write(buf)
	if err != nil {
		if err2, ok := err.(*net.OpError); ok && err2.Timeout() {
			// Add extra padding to make sure we're still aligned
			align := n % (bits / 4)
			if align > 0 {
				fmt.Printf("Realigning: %d + %d\n", n, (bits/4)-align)
				conn.SetWriteDeadline(time.Time{})
				_, err = conn.Write(make([]byte, (bits/4)-align))
				if err != nil {
					return err
				}
			}
			return ErrWriteTimeout
		}
	}
	return err
}

func streamConnection(conn *net.TCPConn) {
	defer conn.Close()
	defer ClientWaitGroup.Done()
	bufi, buf := getBuffer()
	if buf == nil {
		conn.Write([]byte{'R', 0})
		conn.Close()
		return
	}

	defer atomic.StorePointer(&Buffers[bufi], nil)

	numBytes := bits / 8
	_, err := conn.Write([]byte{'R', 1, formatFlag, byte(numBytes)})
	if err != nil {
		return
	}
	bytes := make([]byte, 4)
	sampleRate := Client.GetSampleRate()
	endianness.PutUint32(bytes, sampleRate)
	_, err = conn.Write(bytes[:4])
	if err != nil {
		return
	}

	sample := make([]byte, bits/4)
	align := 8 % len(sample)
	if align > 0 {
		// Add extra padding to make the header an even number of samples in length
		_, err = conn.Write(sample[:len(sample)-align])
		if err != nil {
			return
		}
	}

	bytes = make([]byte, 0, int(Client.GetBufferSize())*2)
	for {
		buffer := <-buf
		if buffer == nil {
			return
		}

		lsamples := buffer[:len(buffer)/2]
		rsamples := buffer[len(buffer)/2:]

		for i := range lsamples {
			rawstreamer.WriteFloat32(sample[:numBytes], float32(lsamples[i]), formatFlag, endianness)
			rawstreamer.WriteFloat32(sample[numBytes:], float32(rsamples[i]), formatFlag, endianness)

			bytes = append(bytes, sample...)
		}

		err = writeAligned(conn, bytes, time.Now().Add(bufferLen))
		if err == ErrWriteTimeout {
			fmt.Println("Write timeout!")
		} else if err != nil {
			fmt.Printf("Sample write error: %v\n", err)
			returnToPool(buffer)
			return
		}

		returnToPool(buffer)

		bytes = bytes[:0]
	}
}

var bufSync sync.Mutex

func getBufferChanSize() int {
	bufferSize := time.Duration(atomic.LoadUint32(&jackBufferSize) / 2)
	sampleRate := time.Duration(Client.GetSampleRate())
	return int(bufferLen*sampleRate/bufferSize/time.Second) + 1
}

func getBuffer() (int, chan []jack.AudioSample) {
	bufSync.Lock()
	defer bufSync.Unlock()

	for i, bufp := range Buffers {
		buf := (*chan []jack.AudioSample)(atomic.LoadPointer(&bufp))
		if buf != nil {
			continue
		}
		buf2 := make(chan []jack.AudioSample, getBufferChanSize())
		atomic.StorePointer(&Buffers[i], unsafe.Pointer(&buf2))
		return i, buf2
	}
	return -1, nil
}

var jackBufferSize uint32

func initPool() {
	atomic.StoreUint32(&jackBufferSize, Client.GetBufferSize()*2)
	bufferSize := int(atomic.LoadUint32(&jackBufferSize))
	BufferPool = make(chan []jack.AudioSample, getBufferChanSize()*maxConns)
	for i := 0; i < cap(BufferPool); i++ {
		BufferPool <- make([]jack.AudioSample, bufferSize)
	}
}

func returnToPool(buffer []jack.AudioSample) {
	bufferSize := int(atomic.LoadUint32(&jackBufferSize))
	if cap(buffer) < bufferSize {
		BufferPool <- make([]jack.AudioSample, bufferSize)
	} else {
		BufferPool <- buffer[:bufferSize]
	}
}

func printStreamInfo() {
	fmt.Printf("Stream info: %dHz, ", Client.GetSampleRate())
	fmt.Printf("%dbit %s, ", bits, rawstreamer.EncodingString[formatFlag&rawstreamer.EncodingMask])
	fmt.Printf("%s, %v max buffer\n", endianness.String(), bufferLen)
}

func main() {
	ShuttingDown = make(chan struct{})

	if bits < 8 || bits > 32 || bits%8 != 0 {
		fmt.Println("Bit-depth must be one of: 8, 16, 24, 32")
		return
	}
	formatFlag = 0
	for flag, str := range rawstreamer.EncodingString {
		if format == str {
			formatFlag = flag
			break
		}
	}
	if formatFlag == 0 {
		fmt.Printf("Unsupported stream format: %s\n", format)
		return
	}
	if formatFlag == rawstreamer.EncodingFloatingPoint {
		bits = 32
	}

	if bigEndian {
		endianness = binary.BigEndian
		formatFlag |= rawstreamer.EncodingBigEndian
	} else {
		endianness = binary.LittleEndian
		formatFlag |= rawstreamer.EncodingLittleEndian
	}

	var err error
	bufferLen, err = time.ParseDuration(bufferStr)
	if err != nil {
		fmt.Printf("Invalid buffer length: %v\n", err)
		return
	}

	var status int
	Client, status = jack.ClientOpen("Raw Streamer", jack.NoStartServer)
	if status != 0 {
		fmt.Println("Status:", status)
		return
	}
	defer Client.Close()

	if code := Client.SetProcessCallback(process); code != 0 {
		fmt.Printf("Failed to set process callback: %d\n", code)
		return
	}
	if code := Client.SetSampleRateCallback(sampleRateChanged); code != 0 {
		fmt.Printf("Failed to set sample rate callback: %d\n", code)
		return
	}
	if code := Client.SetBufferSizeCallback(bufferSizeChanged); code != 0 {
		fmt.Printf("Failed to set buffer size callback: %d\n", code)
		return
	}
	if code := Client.SetPortRegistrationCallback(portRegistered); code != 0 {
		fmt.Printf("Failed to set port registration callback: %d\n", code)
		return
	}
	if code := Client.SetPortConnectCallback(portConnect); code != 0 {
		fmt.Printf("Failed to set port connect callback: %d\n", code)
		return
	}
	Client.OnShutdown(shutdown)

	if code := Client.Activate(); code != 0 {
		fmt.Printf("Failed to activate client: %d\n", code)
		return
	}

	for i := 0; i < 2; i++ {
		port := Client.PortRegister(fmt.Sprintf("in_%d", i), jack.DEFAULT_AUDIO_TYPE, jack.PortIsInput, 0)
		Ports = append(Ports, port)
	}
	Buffers = make([]unsafe.Pointer, maxConns)
	initPool()

	initPortMirror()
	updateProcs()
	updateSources()

	Listener, err = net.Listen("tcp", addr)
	if err != nil {
		fmt.Printf("Error listening on address '%s': %v\n", addr, err)
		return
	} else {
		fmt.Printf("Listening on address: %s\n", Listener.Addr().String())
	}
	for {
		conn, err := Listener.Accept()
		if err != nil {
			select {
			case <-ShuttingDown:
				ClientWaitGroup.Wait()
				return
			default:
				fmt.Printf("Error accepting connection: %v\n", err)
				continue
			}
		}
		ClientWaitGroup.Add(1)
		go streamConnection(conn.(*net.TCPConn))
	}
}

var formatFlag byte
var endianness binary.ByteOrder
var bufferLen time.Duration

var addr string
var maxConns int

var bits int
var format string
var bigEndian bool
var littleEndian bool

var bufferStr string

var mirror string
var procName string
var source string

func init() {
	flag.StringVar(&addr, "addr", ":5253", "Listen address")
	flag.IntVar(&maxConns, "max-conn", 128, "Maximum number of connected clients")

	flag.IntVar(&bits, "bits", 24, "Stream bit-depth")
	flag.StringVar(&format, "format", "int", "Stream format (int, uint, float)")
	flag.BoolVar(&bigEndian, "big-endian", false, "Big-endian stream encoding")
	flag.BoolVar(&littleEndian, "little-endian", true, "Little-endian stream encoding (default)")

	flag.StringVar(&bufferStr, "buffer", "100ms", "Max buffer length")

	flag.StringVar(&mirror, "mirror", "", "The name of a port to mirror (prefix matched)")
	flag.StringVar(&procName, "proc-name", "", "An alsa process to auto-connect (substring matched)")
	flag.StringVar(&source, "source", "", "The name of a port to auto-connect (prefix matched)")
	flag.Parse()
}
<|endoftext|>


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

package os

import (
	"runtime"
	"syscall"
)

// Options for Wait.
const (
	WNOHANG   = syscall.WNOHANG   // Don't wait if no process has exited.
	WSTOPPED  = syscall.WSTOPPED  // If set, status of stopped subprocesses is also reported.
	WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED.
	WRUSAGE   = 1 << 20           // Record resource usage.
)

// WRUSAGE must not be too high a bit, to avoid clashing with Linux's
// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of
// the options

// Wait waits for the Process to exit or stop, and then returns a
// Waitmsg describing its status and an Error, if any. The options
// (WNOHANG etc.) affect the behavior of the Wait call.
func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
	if p.Pid == -1 {
		return nil, EINVAL
	}
	var status syscall.WaitStatus
	var rusage *syscall.Rusage
	if options&WRUSAGE != 0 {
		rusage = new(syscall.Rusage)
		options ^= WRUSAGE
	}
	pid1, e := syscall.Wait4(p.Pid, &status, options, rusage)
	if e != 0 {
		return nil, NewSyscallError("wait", e)
	}
	if options&WSTOPPED == 0 {
		p.done = true
	}
	w = new(Waitmsg)
	w.Pid = pid1
	w.WaitStatus = status
	w.Rusage = rusage
	return w, nil
}

// Signal sends a signal to the Process.
func (p *Process) Signal(sig Signal) Error {
	if p.done {
		return NewError("os: process already finished")
	}
	if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != 0 {
		return Errno(e)
	}
	return nil
}

// Release releases any resources associated with the Process.
func (p *Process) Release() Error {
	// NOOP for unix.
	p.Pid = -1
	// no need for a finalizer anymore
	runtime.SetFinalizer(p, nil)
	return nil
}

// FindProcess looks for a running process by its pid.
// The Process it returns can be used to obtain information
// about the underlying operating system process.
func FindProcess(pid int) (p *Process, err Error) {
	// NOOP for unix.
	return newProcess(pid, 0), nil
}
<commit_msg>os: fix WNOHANG Waitmsg<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os

import (
	"runtime"
	"syscall"
)

// Options for Wait.
const (
	WNOHANG   = syscall.WNOHANG   // Don't wait if no process has exited.
	WSTOPPED  = syscall.WSTOPPED  // If set, status of stopped subprocesses is also reported.
	WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED.
	WRUSAGE   = 1 << 20           // Record resource usage.
)

// WRUSAGE must not be too high a bit, to avoid clashing with Linux's
// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of
// the options

// Wait waits for the Process to exit or stop, and then returns a
// Waitmsg describing its status and an Error, if any. The options
// (WNOHANG etc.) affect the behavior of the Wait call.
func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
	if p.Pid == -1 {
		return nil, EINVAL
	}
	var status syscall.WaitStatus
	var rusage *syscall.Rusage
	if options&WRUSAGE != 0 {
		rusage = new(syscall.Rusage)
		options ^= WRUSAGE
	}
	pid1, e := syscall.Wait4(p.Pid, &status, options, rusage)
	if e != 0 {
		return nil, NewSyscallError("wait", e)
	}
	// With WNOHANG pid is 0 if child has not exited.
	if pid1 != 0 && options&WSTOPPED == 0 {
		p.done = true
	}
	w = new(Waitmsg)
	w.Pid = pid1
	w.WaitStatus = status
	w.Rusage = rusage
	return w, nil
}

// Signal sends a signal to the Process.
func (p *Process) Signal(sig Signal) Error {
	if p.done {
		return NewError("os: process already finished")
	}
	if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != 0 {
		return Errno(e)
	}
	return nil
}

// Release releases any resources associated with the Process.
func (p *Process) Release() Error {
	// NOOP for unix.
	p.Pid = -1
	// no need for a finalizer anymore
	runtime.SetFinalizer(p, nil)
	return nil
}

// FindProcess looks for a running process by its pid.
// The Process it returns can be used to obtain information
// about the underlying operating system process.
func FindProcess(pid int) (p *Process, err Error) {
	// NOOP for unix.
	return newProcess(pid, 0), nil
}
<|endoftext|>


<commit_before>package htmlinfo

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/net/html/charset"

	"github.com/dyatlov/go-oembed/oembed"
	"github.com/dyatlov/go-opengraph/opengraph"
	"golang.org/x/net/html"

	"github.com/dyatlov/go-readability"
)

// HTMLInfo contains information extracted from HTML page
type HTMLInfo struct {
	url *url.URL
	// http.Client instance to use, if nil then will be used default client
	Client *http.Client `json:"-"`
	// If it's true then parser will fetch oembed data from oembed url if possible
	AllowOembedFetching bool `json:"-"`
	// If it's true parser will extract main page content from html
	AllowMainContentExtraction bool `json:"-"`

	// We'll forward it to Oembed' fetchOembed method
	AcceptLanguage string `json:"-"`

	Title         string `json:"title"`
	Description   string `json:"description"`
	CanonicalURL  string `json:"canonical_url"`
	OembedJSONURL string `json:"oembed_json_url"`
	OembedXMLURL  string `json:"oembed_xml_url"`
	FaviconURL    string `json:"favicon_url"`
	ImageSrcURL   string `json:"image_src_url"`
	// Readability package is being used inside
	MainContent string               `json:"main_content"`
	OGInfo      *opengraph.OpenGraph `json:"opengraph"`
	OembedInfo  *oembed.Info         `json:"oembed"`
}

var (
	cleanHTMLTagsRegex    = regexp.MustCompile(`<.*?>`)
	replaceNewLinesRegex  = regexp.MustCompile(`[\r\n]+`)
	clearWhitespacesRegex = regexp.MustCompile(`\s+`)
	getImageRegex         = regexp.MustCompile(`(?i)<img[^>]+?src=("|')?(.*?)("|'|\s|>)`)
)

// NewHTMLInfo return new instance of HTMLInfo
func NewHTMLInfo() *HTMLInfo {
	info := &HTMLInfo{AllowOembedFetching: true, AllowMainContentExtraction: true, OGInfo: opengraph.NewOpenGraph(), AcceptLanguage: "en-us"}
	return info
}

func (info *HTMLInfo) toAbsoluteURL(u string) string {
	if info.url == nil {
		return u
	}

	tu, _ := url.Parse(u)

	if tu != nil {
		if tu.Host == "" {
			tu.Scheme = info.url.Scheme
			tu.Host = info.url.Host
			tu.User = info.url.User
			tu.Opaque = info.url.Opaque
			if len(tu.Path) == 0 || tu.Path[0] != '/' {
				tu.Path = info.url.Path + tu.Path
			}
		} else if tu.Scheme == "" {
			tu.Scheme = info.url.Scheme
		}

		return tu.String()
	}

	return u
}

func (info *HTMLInfo) parseHead(n *html.Node) {
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if c.Type == html.ElementNode && c.Data == "title" {
			info.Title = c.FirstChild.Data
		} else if c.Type == html.ElementNode && c.Data == "link" {
			m := make(map[string]string)
			for _, a := range c.Attr {
				m[a.Key] = a.Val
			}
			if m["rel"] == "canonical" {
				info.CanonicalURL = info.toAbsoluteURL(m["href"])
			} else if m["rel"] == "alternate" && m["type"] == "application/json+oembed" {
				info.OembedJSONURL = info.toAbsoluteURL(m["href"])
			} else if m["rel"] == "alternate" && m["type"] == "application/xml+oembed" {
				info.OembedXMLURL = info.toAbsoluteURL(m["href"])
			} else if m["rel"] == "shortcut icon" {
				info.FaviconURL = info.toAbsoluteURL(m["href"])
			} else if m["rel"] == "image_src" {
				info.ImageSrcURL = info.toAbsoluteURL(m["href"])
			}
		} else if c.Type == html.ElementNode && c.Data == "meta" {
			m := make(map[string]string)
			for _, a := range c.Attr {
				m[a.Key] = a.Val
			}

			if m["name"] == "description" {
				info.Description = m["content"]
			}

			info.OGInfo.ProcessMeta(m)
		}
	}
}

func (info *HTMLInfo) parseBody(n *html.Node) {
	if !info.AllowMainContentExtraction {
		return
	}

	buf := new(bytes.Buffer)
	err := html.Render(buf, n)
	if err != nil {
		return
	}
	bufStr := buf.String()
	doc, err := readability.NewDocument(bufStr)
	if err != nil {
		return
	}

	doc.WhitelistTags = []string{"div", "p", "img"}
	doc.WhitelistAttrs["img"] = []string{"src", "title", "alt"}

	content := doc.Content()
	content = html.UnescapeString(content)

	info.MainContent = strings.Trim(content, "\r\n\t ")
}

// Parse return information about page
// @param s - contains page source
// @params pageURL - contains URL from where the data was taken [optional]
// @params contentType - contains Content-Type header value [optional]
// if no url is given then parser won't attempt to parse oembed info
func (info *HTMLInfo) Parse(s io.Reader, pageURL *string, contentType *string) error {
	contentTypeStr := "text/html"
	if contentType != nil && len(*contentType) > 0 {
		contentTypeStr = *contentType
	}
	utf8s, err := charset.NewReader(s, contentTypeStr)
	if err != nil {
		return err
	}

	if pageURL != nil {
		tu, _ := url.Parse(*pageURL)
		info.url = tu
	}

	doc, err := html.Parse(utf8s)
	if err != nil {
		return err
	}

	var f func(*html.Node)
	f = func(n *html.Node) {
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			if c.Type == html.ElementNode {
				if c.Data == "head" {
					info.parseHead(c)
					continue
				} else if c.Data == "body" {
					info.parseBody(c)
					continue
				}
			}
			f(c)
		}
	}
	f(doc)

	if info.AllowOembedFetching && pageURL != nil && len(info.OembedJSONURL) > 0 {
		pu, _ := url.Parse(info.OembedJSONURL)
		siteName := info.OGInfo.SiteName
		siteURL := strings.ToLower(pu.Scheme) + "://" + pu.Host

		if len(siteName) == 0 {
			siteName = pu.Host
		}

		oiItem := &oembed.Item{EndpointURL: info.OembedJSONURL, ProviderName: siteName, ProviderURL: siteURL, IsEndpointURLComplete: true}
		oi, _ := oiItem.FetchOembedWithLocale(*pageURL, info.Client, info.AcceptLanguage)

		if oi != nil && oi.Status < 300 {
			info.OembedInfo = oi
		}
	}

	return nil
}

func (info *HTMLInfo) trimText(text string, maxLen int) string {
	var numRunes = 0
	for index := range text {
		numRunes++
		if numRunes > maxLen {
			return text[:index-3] + "..."
		}
	}
	return text
}

// GenerateOembedFor return Oembed Info for given url based on previously parsed data
// The returned oembed data is also updated in info.OembedInfo
// Example:
//
// info := NewHTMLInfo()
// info.Parse(dataReader, &sourceURL)
// oembed := info.GenerateOembedFor(sourceURL)
func (info *HTMLInfo) GenerateOembedFor(pageURL string) *oembed.Info {
	pu, _ := url.Parse(pageURL)

	if pu == nil {
		return nil
	}

	siteName := info.OGInfo.SiteName
	siteURL := strings.ToLower(pu.Scheme) + "://" + pu.Host

	if len(siteName) == 0 {
		siteName = pu.Host
	}

	title := info.OGInfo.Title
	if len(title) == 0 {
		title = info.Title
	}

	description := info.OGInfo.Description
	if len(description) == 0 {
		description = info.Description
		if len(description) == 0 {
			if len(info.MainContent) > 0 {
				description = cleanHTMLTagsRegex.ReplaceAllString(info.MainContent, " ")
				description = replaceNewLinesRegex.ReplaceAllString(description, " ")
				description = clearWhitespacesRegex.ReplaceAllString(description, " ")
				description = strings.Trim(description, " ")
				description = info.trimText(description, 200)
			}
		}
	}

	baseInfo := &oembed.Info{}

	baseInfo.Type = "link"
	baseInfo.URL = pageURL
	baseInfo.ProviderURL = siteURL
	baseInfo.ProviderName = siteName
	baseInfo.Title = title
	baseInfo.Description = description

	if len(info.ImageSrcURL) > 0 {
		baseInfo.ThumbnailURL = info.toAbsoluteURL(info.ImageSrcURL)
	}

	if len(info.OGInfo.Images) > 0 {
		baseInfo.ThumbnailURL = info.toAbsoluteURL(info.OGInfo.Images[0].URL)
		baseInfo.ThumbnailWidth = json.Number(strconv.FormatInt(int64(info.OGInfo.Images[0].Width), 10))
		baseInfo.ThumbnailHeight = json.Number(strconv.FormatInt(int64(info.OGInfo.Images[0].Height), 10))
	}

	if len(baseInfo.ThumbnailURL) == 0 && len(info.MainContent) > 0 {
		// get first image from body
		matches := getImageRegex.FindStringSubmatch(info.MainContent)
		if len(matches) > 0 {
			baseInfo.ThumbnailURL = info.toAbsoluteURL(matches[2])
		}
	}

	// first we check if there is link to oembed resource
	if info.OembedInfo != nil {
		info.OembedInfo.MergeWith(baseInfo)
		return info.OembedInfo
	}

	return baseInfo
}

// ToJSON return json represenation of structure, simple wrapper around json package
func (info *HTMLInfo) ToJSON() ([]byte, error) {
	return json.Marshal(info)
}

func (info *HTMLInfo) String() string {
	data, err := info.ToJSON()
	if err != nil {
		return err.Error()
	}
	return string(data[:])
}
<commit_msg>fixed conflicts<commit_after>package htmlinfo

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/net/html/charset"

	"github.com/dyatlov/go-oembed/oembed"
	"github.com/dyatlov/go-opengraph/opengraph"
	"golang.org/x/net/html"

	"github.com/dyatlov/go-readability"
)

// TouchIcon contains all icons parsed from page header, including Apple touch icons
type TouchIcon struct {
	URL        string `json:"url"`
	Type       string `json:"type"`
	Width      uint64 `json:"width"`
	Height     uint64 `json:"height"`
	IsScalable bool   `json:"is_scalable"`
}

// HTMLInfo contains information extracted from HTML page
type HTMLInfo struct {
	url *url.URL
	// http.Client instance to use, if nil then will be used default client
	Client *http.Client `json:"-"`
	// If it's true then parser will fetch oembed data from oembed url if possible
	AllowOembedFetching bool `json:"-"`
	// If it's true parser will extract main page content from html
	AllowMainContentExtraction bool `json:"-"`
	// We'll forward it to Oembed' fetchOembed method
	AcceptLanguage string `json:"-"`

	Title         string       `json:"title"`
	Description   string       `json:"description"`
	CanonicalURL  string       `json:"canonical_url"`
	OembedJSONURL string       `json:"oembed_json_url"`
	OembedXMLURL  string       `json:"oembed_xml_url"`
	FaviconURL    string       `json:"favicon_url"`
	TouchIcons    []*TouchIcon `json:"touch_icons"`
	ImageSrcURL   string       `json:"image_src_url"`
	// Readability package is being used inside
	MainContent string               `json:"main_content"`
	OGInfo      *opengraph.OpenGraph `json:"opengraph"`
	OembedInfo  *oembed.Info         `json:"oembed"`
}

var (
	cleanHTMLTagsRegex    = regexp.MustCompile(`<.*?>`)
	replaceNewLinesRegex  = regexp.MustCompile(`[\r\n]+`)
	clearWhitespacesRegex = regexp.MustCompile(`\s+`)
	getImageRegex         = regexp.MustCompile(`(?i)<img[^>]+?src=("|')?(.*?)("|'|\s|>)`)
	linkWithIconsRegex    = regexp.MustCompile(`\b(icon|image_src)\b`)
	sizesRegex            = regexp.MustCompile(`(\d+)[^\d]+(\d+)`) // some websites use crazy unicode chars between height and width
)

// NewHTMLInfo return new instance of HTMLInfo
func NewHTMLInfo() *HTMLInfo {
	info := &HTMLInfo{AllowOembedFetching: true, AllowMainContentExtraction: true, OGInfo: opengraph.NewOpenGraph(), AcceptLanguage: "en-us"}
	return info
}

func (info *HTMLInfo) toAbsoluteURL(u string) string {
	if info.url == nil {
		return u
	}

	tu, _ := url.Parse(u)

	if tu != nil {
		if tu.Host == "" {
			tu.Scheme = info.url.Scheme
			tu.Host = info.url.Host
			tu.User = info.url.User
			tu.Opaque = info.url.Opaque
			if len(tu.Path) == 0 || tu.Path[0] != '/' {
				tu.Path = info.url.Path + tu.Path
			}
		} else if tu.Scheme == "" {
			tu.Scheme = info.url.Scheme
		}

		return tu.String()
	}

	return u
}

func (info *HTMLInfo) appendTouchIcons(url string, rel string, sizes []string) {
	for _, size := range sizes {
		icon := &TouchIcon{URL: url, Type: rel, IsScalable: (size == "any")}
		matches := sizesRegex.FindStringSubmatch(size)
		if len(matches) >= 3 {
			icon.Height, _ = strconv.ParseUint(matches[1], 10, 64)
			icon.Width, _ = strconv.ParseUint(matches[2], 10, 64)
		}
		info.TouchIcons = append(info.TouchIcons, icon)
	}
}

func (info *HTMLInfo) parseLinkIcon(attrs map[string]string) {
	rels := strings.Split(attrs["rel"], " ")
	url := info.toAbsoluteURL(attrs["href"])
	sizesString, present := attrs["sizes"]
	if !present {
		sizesString = "0x0"
	}
	sizes := strings.Split(sizesString, " ")

	for _, rel := range rels {
		if rel == "image_src" {
			info.ImageSrcURL = url
		} else if rel == "icon" {
			info.FaviconURL = url
			info.appendTouchIcons(url, rel, sizes)
		} else if rel == "apple-touch-icon" || rel == "apple-touch-icon-precomposed" {
			info.appendTouchIcons(url, rel, sizes)
		}
	}
}

func (info *HTMLInfo) parseHead(n *html.Node) {
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if c.Type == html.ElementNode && c.Data == "title" {
			info.Title = c.FirstChild.Data
		} else if c.Type == html.ElementNode && c.Data == "link" {
			m := make(map[string]string)
			for _, a := range c.Attr {
				m[a.Key] = a.Val
			}
			if m["rel"] == "canonical" {
				info.CanonicalURL = info.toAbsoluteURL(m["href"])
			} else if m["rel"] == "alternate" && m["type"] == "application/json+oembed" {
				info.OembedJSONURL = info.toAbsoluteURL(m["href"])
			} else if m["rel"] == "alternate" && m["type"] == "application/xml+oembed" {
				info.OembedXMLURL = info.toAbsoluteURL(m["href"])
			} else if linkWithIconsRegex.MatchString(m["rel"]) {
				info.parseLinkIcon(m)
			}
		} else if c.Type == html.ElementNode && c.Data == "meta" {
			m := make(map[string]string)
			for _, a := range c.Attr {
				m[a.Key] = a.Val
			}

			if m["name"] == "description" {
				info.Description = m["content"]
			}

			info.OGInfo.ProcessMeta(m)
		}
	}
}

func (info *HTMLInfo) parseBody(n *html.Node) {
	if !info.AllowMainContentExtraction {
		return
	}

	buf := new(bytes.Buffer)
	err := html.Render(buf, n)
	if err != nil {
		return
	}
	bufStr := buf.String()
	doc, err := readability.NewDocument(bufStr)
	if err != nil {
		return
	}

	doc.WhitelistTags = []string{"div", "p", "img"}
	doc.WhitelistAttrs["img"] = []string{"src", "title", "alt"}

	content := doc.Content()
	content = html.UnescapeString(content)

	info.MainContent = strings.Trim(content, "\r\n\t ")
}

// Parse return information about page
// @param s - contains page source
// @params pageURL - contains URL from where the data was taken [optional]
// @params contentType - contains Content-Type header value [optional]
// if no url is given then parser won't attempt to parse oembed info
func (info *HTMLInfo) Parse(s io.Reader, pageURL *string, contentType *string) error {
	contentTypeStr := "text/html"
	if contentType != nil && len(*contentType) > 0 {
		contentTypeStr = *contentType
	}
	utf8s, err := charset.NewReader(s, contentTypeStr)
	if err != nil {
		return err
	}

	if pageURL != nil {
		tu, _ := url.Parse(*pageURL)
		info.url = tu
	}

	doc, err := html.Parse(utf8s)
	if err != nil {
		return err
	}

	var f func(*html.Node)
	f = func(n *html.Node) {
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			if c.Type == html.ElementNode {
				if c.Data == "head" {
					info.parseHead(c)
					continue
				} else if c.Data == "body" {
					info.parseBody(c)
					continue
				}
			}
			f(c)
		}
	}
	f(doc)

	if info.AllowOembedFetching && pageURL != nil && len(info.OembedJSONURL) > 0 {
		pu, _ := url.Parse(info.OembedJSONURL)
		siteName := info.OGInfo.SiteName
		siteURL := strings.ToLower(pu.Scheme) + "://" + pu.Host

		if len(siteName) == 0 {
			siteName = pu.Host
		}

		oiItem := &oembed.Item{EndpointURL: info.OembedJSONURL, ProviderName: siteName, ProviderURL: siteURL, IsEndpointURLComplete: true}
		oi, _ := oiItem.FetchOembedWithLocale(*pageURL, info.Client, info.AcceptLanguage)

		if oi != nil && oi.Status < 300 {
			info.OembedInfo = oi
		}
	}

	return nil
}

func (info *HTMLInfo) trimText(text string, maxLen int) string {
	var numRunes = 0
	for index := range text {
		numRunes++
		if numRunes > maxLen {
			return text[:index-3] + "..."
		}
	}
	return text
}

// GenerateOembedFor return Oembed Info for given url based on previously parsed data
// The returned oembed data is also updated in info.OembedInfo
// Example:
//
// info := NewHTMLInfo()
// info.Parse(dataReader, &sourceURL)
// oembed := info.GenerateOembedFor(sourceURL)
func (info *HTMLInfo) GenerateOembedFor(pageURL string) *oembed.Info {
	pu, _ := url.Parse(pageURL)

	if pu == nil {
		return nil
	}

	siteName := info.OGInfo.SiteName
	siteURL := strings.ToLower(pu.Scheme) + "://" + pu.Host

	if len(siteName) == 0 {
		siteName = pu.Host
	}

	title := info.OGInfo.Title
	if len(title) == 0 {
		title = info.Title
	}

	description := info.OGInfo.Description
	if len(description) == 0 {
		description = info.Description
		if len(description) == 0 {
			if len(info.MainContent) > 0 {
				description = cleanHTMLTagsRegex.ReplaceAllString(info.MainContent, " ")
				description = replaceNewLinesRegex.ReplaceAllString(description, " ")
				description = clearWhitespacesRegex.ReplaceAllString(description, " ")
				description = strings.Trim(description, " ")
				description = info.trimText(description, 200)
			}
		}
	}

	baseInfo := &oembed.Info{}

	baseInfo.Type = "link"
	baseInfo.URL = pageURL
	baseInfo.ProviderURL = siteURL
	baseInfo.ProviderName = siteName
	baseInfo.Title = title
	baseInfo.Description = description

	if len(info.ImageSrcURL) > 0 {
		baseInfo.ThumbnailURL = info.toAbsoluteURL(info.ImageSrcURL)
	}

	if len(info.OGInfo.Images) > 0 {
		baseInfo.ThumbnailURL = info.toAbsoluteURL(info.OGInfo.Images[0].URL)
		baseInfo.ThumbnailWidth = json.Number(strconv.FormatInt(int64(info.OGInfo.Images[0].Width), 10))
		baseInfo.ThumbnailHeight = json.Number(strconv.FormatInt(int64(info.OGInfo.Images[0].Height), 10))
	}

	if len(baseInfo.ThumbnailURL) == 0 && len(info.MainContent) > 0 {
		// get first image from body
		matches := getImageRegex.FindStringSubmatch(info.MainContent)
		if len(matches) > 0 {
			baseInfo.ThumbnailURL = info.toAbsoluteURL(matches[2])
		}
	}

	// first we check if there is link to oembed resource
	if info.OembedInfo != nil {
		info.OembedInfo.MergeWith(baseInfo)
		return info.OembedInfo
	}

	return baseInfo
}

// ToJSON return json represenation of structure, simple wrapper around json package
func (info *HTMLInfo) ToJSON() ([]byte, error) {
	return json.Marshal(info)
}

func (info *HTMLInfo) String() string {
	data, err := info.ToJSON()
	if err != nil {
		return err.Error()
	}
	return string(data[:])
}
<|endoftext|>


<commit_before>package httpbackend

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"

	"github.com/khades/servbot/models"
	"github.com/khades/servbot/repos"
)

type tokenResponse struct {
	Token string `json:"access_token"`
}
type nameResponse struct {
	Username string `json:"name"`
}

func oauth(w http.ResponseWriter, r *http.Request) {
	code := r.URL.Query().Get("code")
	if code == "" {
		http.Error(w, "Incoming Twitch code is missing", http.StatusUnprocessableEntity)
		return
	}
	fmt.Fprintf(w, "K, we parsed %s", code)

	resp, err := http.PostForm("https://api.twitch.tv/kraken/oauth2/token",
		url.Values{
			"client_id":     {repos.Config.ClientID},
			"client_secret": {repos.Config.ClientSecret},
			"grant_type":    {"authorization_code"},
			"redirect_uri":  {repos.Config.AppOauthURL},
			"code":          {code}})
	//"state":         {}
	if err != nil {
		log.Println(err)
		http.Error(w, "Twitch Error", http.StatusUnprocessableEntity)
		return
	}
	var tokenStruct = new(tokenResponse)

	marshallError := json.NewDecoder(resp.Body).Decode(tokenStruct)
	if marshallError != nil {
		log.Println(marshallError)
		http.Error(w, "Twitch Error", http.StatusUnprocessableEntity)
		return
	}
	nameResp, err := http.Get("https://api.twitch.tv/kraken/user?client_id=" + repos.Config.ClientID + "&oauth_token=" + tokenStruct.Token)
	if err != nil {
		log.Println(err)
		http.Error(w, "Twitch Error", http.StatusUnprocessableEntity)
		return
	}

	var usernameStruct = new(nameResponse)
	nameMarshallError := json.NewDecoder(nameResp.Body).Decode(usernameStruct)
	if nameMarshallError != nil {
		log.Println(marshallError)
		http.Error(w, "Twitch Error", http.StatusUnprocessableEntity)
		return
	}
	log.Println("We got credentials of ", usernameStruct.Username)
	session, err := repos.GetSession(r)
	session.Values["sessions"] = models.HTTPSession{Username: usernameStruct.Username, Key: tokenStruct.Token}
	session.Save(r, w)
	defer resp.Body.Close()
}
<commit_msg>Working with oauth<commit_after>package httpbackend

import (
	"encoding/json"
	"log"
	"net/http"
	"net/url"

	"github.com/khades/servbot/models"
	"github.com/khades/servbot/repos"
)

type tokenResponse struct {
	Token string `json:"access_token"`
}
type nameResponse struct {
	Username string `json:"name"`
}

func oauth(w http.ResponseWriter, r *http.Request) {
	code := r.URL.Query().Get("code")
	if code == "" {
		http.Error(w, "Incoming Twitch code is missing", http.StatusUnprocessableEntity)
		return
	}
	resp, err := http.PostForm("https://api.twitch.tv/kraken/oauth2/token",
		url.Values{
			"client_id":     {repos.Config.ClientID},
			"client_secret": {repos.Config.ClientSecret},
			"grant_type":    {"authorization_code"},
			"redirect_uri":  {repos.Config.AppOauthURL},
			"code":          {code}})
	//"state":         {}
	if err != nil {
		log.Println(err)
		http.Error(w, "Twitch Error", http.StatusUnprocessableEntity)
		return
	}
	var tokenStruct = new(tokenResponse)

	marshallError := json.NewDecoder(resp.Body).Decode(tokenStruct)
	if marshallError != nil {
		log.Println(marshallError)
		http.Error(w, "Twitch Error", http.StatusUnprocessableEntity)
		return
	}
	nameResp, err := http.Get("https://api.twitch.tv/kraken/user?client_id=" + repos.Config.ClientID + "&oauth_token=" + tokenStruct.Token)
	if err != nil {
		log.Println(err)
		http.Error(w, "Twitch Error", http.StatusUnprocessableEntity)
		return
	}

	var usernameStruct = new(nameResponse)
	nameMarshallError := json.NewDecoder(nameResp.Body).Decode(usernameStruct)
	if nameMarshallError != nil {
		log.Println(marshallError)
		http.Error(w, "Twitch Error", http.StatusUnprocessableEntity)
		return
	}
	log.Println("We got credentials of ", usernameStruct.Username)
	session, err := repos.GetSession(r)
	session.Values["sessions"] = models.HTTPSession{Username: usernameStruct.Username, Key: tokenStruct.Token}
	session.Save(r, w)
	defer resp.Body.Close()
}
<|endoftext|>


<commit_before>package reception

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

	consul "github.com/hashicorp/consul/api"
	"golang.org/x/net/context"
)

type (
	consulClient struct {
		api    consulAPI
		config *consulConfig
	}

	consulWatcher struct {
		name  string
		api   consulAPI
		index uint64
		stop  chan struct{}
	}

	consulConfig struct {
		host                   string
		endpoint               string
		checkTimeout           string
		checkInterval          string
		checkDeregisterTimeout string
		logger                 Logger
	}

	ConsulConfig func(*consulConfig)
)

var ErrIllegalHost = errors.New("illegal host")

func DialConsul(addr string, configs ...ConsulConfig) (Client, error) {
	client, err := consul.NewClient(consul.DefaultConfig())
	if err != nil {
		return nil, err
	}

	shim := &consulShim{
		agent:   client.Agent(),
		catalog: client.Catalog(),
	}

	return newConsulClient(shim, configs...), nil
}

func newConsulClient(api consulAPI, configs ...ConsulConfig) Client {
	config := &consulConfig{
		host:                   os.Getenv("HOST"),
		endpoint:               "",
		checkTimeout:           "10s",
		checkInterval:          "5s",
		checkDeregisterTimeout: "30s",
		logger:                 &defaultLogger{},
	}

	for _, f := range configs {
		f(config)
	}

	return &consulClient{
		api:    api,
		config: config,
	}
}

func WithHost(host string) ConsulConfig {
	return func(c *consulConfig) { c.host = host }
}

func WithEndpoint(endpoint string) ConsulConfig {
	return func(c *consulConfig) { c.endpoint = endpoint }
}

func WithCheckTimeout(time.Duration) ConsulConfig {
	return func(c *consulConfig) { c.checkTimeout = fmt.Sprintf("%#v", c) }
}

func WithCheckInterval(time.Duration) ConsulConfig {
	return func(c *consulConfig) { c.checkInterval = fmt.Sprintf("%#v", c) }
}

func WithCheckDeregisterTimeout(time.Duration) ConsulConfig {
	return func(c *consulConfig) { c.checkDeregisterTimeout = fmt.Sprintf("%#v", c) }
}

func WithLogger(logger Logger) ConsulConfig {
	return func(c *consulConfig) { c.logger = logger }
}

//
// Client

func (c *consulClient) Register(service *Service) error {
	endpoint, err := makeCheckEndpoint(c.config.host, c.config.endpoint, c.config.logger)
	if err != nil {
		return err
	}

	return c.api.Register(&consul.AgentServiceRegistration{
		ID:      service.ID,
		Name:    service.Name,
		Address: service.Address,
		Port:    service.Port,
		Tags:    []string{string(service.serializeAttributes())},
		Check: &consul.AgentServiceCheck{
			HTTP:                           endpoint,
			Status:                         "passing",
			Timeout:                        c.config.checkTimeout,
			Interval:                       c.config.checkInterval,
			DeregisterCriticalServiceAfter: c.config.checkDeregisterTimeout,
		},
	})
}

func (c *consulClient) ListServices(name string) ([]*Service, error) {
	services, err := c.api.List(name)
	if err != nil {
		return nil, err
	}

	return mapConsulServices(services, name), nil
}

func (c *consulClient) NewWatcher(name string) Watcher {
	return &consulWatcher{
		name:  name,
		api:   c.api,
		index: 0,
		stop:  make(chan struct{}),
	}
}

func (w *consulWatcher) Start() (<-chan *ServiceState, error) {
	ch := make(chan *ServiceState)

	go func() {
		defer close(ch)

		for {
			var (
				lastIndex = w.index
				services  []*consul.CatalogService
				err       error
			)

			again, err := withContext(w.stop, func(ctx context.Context) error {
				services, w.index, err = w.api.Watch(w.name, w.index, ctx)
				return err
			})

			if err != nil {
				sendOrStop(ch, w.stop, &ServiceState{Err: err})
				return
			}

			if !again {
				return
			}

			if w.index != lastIndex && !sendOrStop(ch, w.stop, &ServiceState{Services: mapConsulServices(services, w.name)}) {
				return
			}
		}
	}()

	return ch, nil
}

func (w *consulWatcher) Stop() {
	close(w.stop)
}

//
// Helpers

func mapConsulServices(services []*consul.CatalogService, name string) []*Service {
	serviceMap := map[int]*Service{}
	for _, service := range services {
		s := &Service{
			ID:      service.ServiceID,
			Name:    name,
			Address: service.ServiceAddress,
			Port:    service.ServicePort,
		}

		if len(service.ServiceTags) > 0 && s.parseAttributes([]byte(service.ServiceTags[0])) {
			serviceMap[int(service.CreateIndex)] = s
		}
	}

	return sortServiceMap(serviceMap)
}

func makeCheckEndpoint(host, endpoint string, logger Logger) (string, error) {
	if endpoint != "" {
		return endpoint, nil
	}

	if host == "" {
		return "", ErrIllegalHost
	}

	return makeCheckServer(host, logger)
}

func makeCheckServer(host string, logger Logger) (string, error) {
	listener, err := net.Listen("tcp", "0.0.0.0:0")
	if err != nil {
		return "", err
	}

	port := listener.Addr().(*net.TCPAddr).Port
	addr := fmt.Sprintf("http://%s:%d/health", host, port)

	mux := http.NewServeMux()
	mux.HandleFunc("/health", makeCheckHTTPFunc(logger))

	go func() {
		logger.Printf("Running health check at %s\n", addr)
		http.Serve(listener, mux)
	}()

	return addr, nil
}

func makeCheckHTTPFunc(logger Logger) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		logger.Printf("Consul performing health check\n")

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"alive": true}`))
	}
}
<commit_msg>Actually use addr in DialConsul.<commit_after>package reception

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

	consul "github.com/hashicorp/consul/api"
	"golang.org/x/net/context"
)

type (
	consulClient struct {
		api    consulAPI
		config *consulConfig
	}

	consulWatcher struct {
		name  string
		api   consulAPI
		index uint64
		stop  chan struct{}
	}

	consulConfig struct {
		host                   string
		endpoint               string
		checkTimeout           string
		checkInterval          string
		checkDeregisterTimeout string
		logger                 Logger
	}

	ConsulConfig func(*consulConfig)
)

var ErrIllegalHost = errors.New("illegal host")

func DialConsul(addr string, configs ...ConsulConfig) (Client, error) {
	client, err := consul.NewClient(&consul.Config{Address: addr})
	if err != nil {
		return nil, err
	}

	shim := &consulShim{
		agent:   client.Agent(),
		catalog: client.Catalog(),
	}

	return newConsulClient(shim, configs...), nil
}

func newConsulClient(api consulAPI, configs ...ConsulConfig) Client {
	config := &consulConfig{
		host:                   os.Getenv("HOST"),
		endpoint:               "",
		checkTimeout:           "10s",
		checkInterval:          "5s",
		checkDeregisterTimeout: "30s",
		logger:                 &defaultLogger{},
	}

	for _, f := range configs {
		f(config)
	}

	return &consulClient{
		api:    api,
		config: config,
	}
}

func WithHost(host string) ConsulConfig {
	return func(c *consulConfig) { c.host = host }
}

func WithEndpoint(endpoint string) ConsulConfig {
	return func(c *consulConfig) { c.endpoint = endpoint }
}

func WithCheckTimeout(time.Duration) ConsulConfig {
	return func(c *consulConfig) { c.checkTimeout = fmt.Sprintf("%#v", c) }
}

func WithCheckInterval(time.Duration) ConsulConfig {
	return func(c *consulConfig) { c.checkInterval = fmt.Sprintf("%#v", c) }
}

func WithCheckDeregisterTimeout(time.Duration) ConsulConfig {
	return func(c *consulConfig) { c.checkDeregisterTimeout = fmt.Sprintf("%#v", c) }
}

func WithLogger(logger Logger) ConsulConfig {
	return func(c *consulConfig) { c.logger = logger }
}

//
// Client

func (c *consulClient) Register(service *Service) error {
	endpoint, err := makeCheckEndpoint(c.config.host, c.config.endpoint, c.config.logger)
	if err != nil {
		return err
	}

	return c.api.Register(&consul.AgentServiceRegistration{
		ID:      service.ID,
		Name:    service.Name,
		Address: service.Address,
		Port:    service.Port,
		Tags:    []string{string(service.serializeAttributes())},
		Check: &consul.AgentServiceCheck{
			HTTP:                           endpoint,
			Status:                         "passing",
			Timeout:                        c.config.checkTimeout,
			Interval:                       c.config.checkInterval,
			DeregisterCriticalServiceAfter: c.config.checkDeregisterTimeout,
		},
	})
}

func (c *consulClient) ListServices(name string) ([]*Service, error) {
	services, err := c.api.List(name)
	if err != nil {
		return nil, err
	}

	return mapConsulServices(services, name), nil
}

func (c *consulClient) NewWatcher(name string) Watcher {
	return &consulWatcher{
		name:  name,
		api:   c.api,
		index: 0,
		stop:  make(chan struct{}),
	}
}

func (w *consulWatcher) Start() (<-chan *ServiceState, error) {
	ch := make(chan *ServiceState)

	go func() {
		defer close(ch)

		for {
			var (
				lastIndex = w.index
				services  []*consul.CatalogService
				err       error
			)

			again, err := withContext(w.stop, func(ctx context.Context) error {
				services, w.index, err = w.api.Watch(w.name, w.index, ctx)
				return err
			})

			if err != nil {
				sendOrStop(ch, w.stop, &ServiceState{Err: err})
				return
			}

			if !again {
				return
			}

			if w.index != lastIndex && !sendOrStop(ch, w.stop, &ServiceState{Services: mapConsulServices(services, w.name)}) {
				return
			}
		}
	}()

	return ch, nil
}

func (w *consulWatcher) Stop() {
	close(w.stop)
}

//
// Helpers

func mapConsulServices(services []*consul.CatalogService, name string) []*Service {
	serviceMap := map[int]*Service{}
	for _, service := range services {
		s := &Service{
			ID:      service.ServiceID,
			Name:    name,
			Address: service.ServiceAddress,
			Port:    service.ServicePort,
		}

		if len(service.ServiceTags) > 0 && s.parseAttributes([]byte(service.ServiceTags[0])) {
			serviceMap[int(service.CreateIndex)] = s
		}
	}

	return sortServiceMap(serviceMap)
}

func makeCheckEndpoint(host, endpoint string, logger Logger) (string, error) {
	if endpoint != "" {
		return endpoint, nil
	}

	if host == "" {
		return "", ErrIllegalHost
	}

	return makeCheckServer(host, logger)
}

func makeCheckServer(host string, logger Logger) (string, error) {
	listener, err := net.Listen("tcp", "0.0.0.0:0")
	if err != nil {
		return "", err
	}

	port := listener.Addr().(*net.TCPAddr).Port
	addr := fmt.Sprintf("http://%s:%d/health", host, port)

	mux := http.NewServeMux()
	mux.HandleFunc("/health", makeCheckHTTPFunc(logger))

	go func() {
		logger.Printf("Running health check at %s\n", addr)
		http.Serve(listener, mux)
	}()

	return addr, nil
}

func makeCheckHTTPFunc(logger Logger) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		logger.Printf("Consul performing health check\n")

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"alive": true}`))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/spf13/cobra"

	"arvika.pulcy.com/pulcy/droplets/providers"
)

const (
	defaultDomain         = "pulcy.com"
	defaultClusterImage   = "coreos-stable"
	defaultClusterRegion  = "ams3"
	defaultClusterSize    = "512mb"
	defaultInstanceCount  = 3
	defaultYardImage      = "pulcy/yard:0.4.1"
	sshKey                = "ewout@prangsma.net"
	defaultRebootStrategy = "etcd-lock"
)

var (
	cmdCreate = &cobra.Command{
		Use: "create",
		Run: showUsage,
	}
	cmdCreateCluster = &cobra.Command{
		Use: "cluster",
		Run: createCluster,
	}

	createClusterFlags providers.CreateClusterOptions
)

func init() {
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Domain, "domain", def("DROPLETS_DOMAIN", defaultDomain), "Cluster domain")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Name, "name", "", "Cluster name")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Image, "image", defaultClusterImage, "OS image to run on new droplets")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Region, "region", defaultClusterRegion, "Region to create the droplets in")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Size, "size", defaultClusterSize, "Size of the new droplet")
	cmdCreateCluster.Flags().IntVar(&createClusterFlags.InstanceCount, "instance-count", defaultInstanceCount, "Number of instances in cluster")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.YardImage, "yard-image", defaultYardImage, "Image containing encrypted yard")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.YardPassphrase, "yard-passphrase", def("YARD_PASSPHRASE", ""), "Passphrase used to decrypt yard.gpg")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.RebootStrategy, "reboot-strategy", defaultRebootStrategy, "CoreOS reboot strategy")
	cmdCreate.AddCommand(cmdCreateCluster)
	cmdMain.AddCommand(cmdCreate)
}

func createCluster(cmd *cobra.Command, args []string) {
	createClusterFlags.SSHKeyNames = []string{sshKey}
	provider := newProvider()

	// Validate
	if err := createClusterFlags.Validate(); err != nil {
		Exitf("Create failed: %s\n", err.Error())
	}

	// See if there are already instances for the given cluster
	instances, err := provider.GetInstances(&createClusterFlags.ClusterInfo)
	if err != nil {
		Exitf("Failed to query existing instances: %v\n", err)
	}
	if len(instances) > 0 {
		Exitf("Cluster %s.%s already exists.\n", createClusterFlags.Name, createClusterFlags.Domain)
	}

	// Create
	err = provider.CreateCluster(&createClusterFlags, newDnsProvider())
	if err != nil {
		Exitf("Failed to create new cluster: %v\n", err)
	}

	Infof("Cluster created\n")
}
<commit_msg>Using yard 0.4.4<commit_after>package main

import (
	"github.com/spf13/cobra"

	"arvika.pulcy.com/pulcy/droplets/providers"
)

const (
	defaultDomain         = "pulcy.com"
	defaultClusterImage   = "coreos-stable"
	defaultClusterRegion  = "ams3"
	defaultClusterSize    = "512mb"
	defaultInstanceCount  = 3
	defaultYardImage      = "pulcy/yard:0.4.4"
	sshKey                = "ewout@prangsma.net"
	defaultRebootStrategy = "etcd-lock"
)

var (
	cmdCreate = &cobra.Command{
		Use: "create",
		Run: showUsage,
	}
	cmdCreateCluster = &cobra.Command{
		Use: "cluster",
		Run: createCluster,
	}

	createClusterFlags providers.CreateClusterOptions
)

func init() {
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Domain, "domain", def("DROPLETS_DOMAIN", defaultDomain), "Cluster domain")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Name, "name", "", "Cluster name")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Image, "image", defaultClusterImage, "OS image to run on new droplets")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Region, "region", defaultClusterRegion, "Region to create the droplets in")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.Size, "size", defaultClusterSize, "Size of the new droplet")
	cmdCreateCluster.Flags().IntVar(&createClusterFlags.InstanceCount, "instance-count", defaultInstanceCount, "Number of instances in cluster")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.YardImage, "yard-image", defaultYardImage, "Image containing encrypted yard")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.YardPassphrase, "yard-passphrase", def("YARD_PASSPHRASE", ""), "Passphrase used to decrypt yard.gpg")
	cmdCreateCluster.Flags().StringVar(&createClusterFlags.RebootStrategy, "reboot-strategy", defaultRebootStrategy, "CoreOS reboot strategy")
	cmdCreate.AddCommand(cmdCreateCluster)
	cmdMain.AddCommand(cmdCreate)
}

func createCluster(cmd *cobra.Command, args []string) {
	createClusterFlags.SSHKeyNames = []string{sshKey}
	provider := newProvider()

	// Validate
	if err := createClusterFlags.Validate(); err != nil {
		Exitf("Create failed: %s\n", err.Error())
	}

	// See if there are already instances for the given cluster
	instances, err := provider.GetInstances(&createClusterFlags.ClusterInfo)
	if err != nil {
		Exitf("Failed to query existing instances: %v\n", err)
	}
	if len(instances) > 0 {
		Exitf("Cluster %s.%s already exists.\n", createClusterFlags.Name, createClusterFlags.Domain)
	}

	// Create
	err = provider.CreateCluster(&createClusterFlags, newDnsProvider())
	if err != nil {
		Exitf("Failed to create new cluster: %v\n", err)
	}

	Infof("Cluster created\n")
}
<|endoftext|>


<commit_before>// Package cookoo is a Chain-of-Command (CoCo) framework for writing
// applications.
//
// Tutorials:
//
// * Building Web Apps with Cookoo: https://github.com/Masterminds/cookoo-web-tutorial
//
// * Building CLI Apps with Cookoo: https://github.com/Masterminds/cookoo-cli-tutorial
//
// A chain of command framework works as follows:
//
// 	* A "route" is constructed as a chain of commands -- a series of
// 	single-purpose tasks that are run in sequence.
// 	* An application is composed of one or more routes.
// 	* Commands in a route communicate using a Context.
// 	* An application Router is used to receive a route name and then
// 	execute the appropriate chain of commands.
//
// To create a new Cookoo application, use cookoo.Cookoo(). This will
// configure and create a new registry, request router, and context.
// From there, use the Registry to build chains of commands, and then
// use the Router to execute chains of commands.
//
// Example:
//
// 	package main
//
// 	import (
// 		//This is the path to Cookoo
// 		"github.com/Masterminds/cookoo/src/cookoo"
// 		"fmt"
// 	)
//
// 	func main() {
// 		// Build a new Cookoo app.
// 		registry, router, context := cookoo.Cookoo()
//
// 		// Fill the registry.
// 		registry.Route("TEST", "A test route").Does(HelloWorld, "hi") //...
//
// 		// Execute the route.
// 		router.HandleRequest("TEST", context, false)
// 	}
//
// 	// An example command
// 	func HelloWorld(cxt cookoo.Context, params *cookoo.Params) (interface{}, Interrupt) {
// 		fmt.Println("Hello World")
// 		return true, nil
// 	}
//
// Unlike other CoCo implementations (like Pronto.js or Fortissimo),
// Cookoo commands are just functions.
//
// Interrupts:
//
// There are four types of interrupts that you may wish to return:
//
// 	1. FatalError: This will stop the route immediately.
// 	2. RecoverableError: This will allow the route to continue moving.
// 	3. Stop: This will stop the current request, but not as an error.
// 	4. Reroute: This will stop executing the current route, and switch to executing another route.
//
// To learn how to write Cookoo applications, you may wish to examine
// the small Skunk application: https://github.com/technosophos/skunk.
package cookoo

// VERSION provides the current version of Cookoo.
const VERSION = "1.3.0"

// Cookoo creates a new Cookoo app.
//
// This is the main progenitor of a Cookoo application. Whether a plain
// Cookoo app, or a Web or CLI program, this is the function you will use
// to bootstrap.
//
// The `*Registry` is used to declare new routes, where a "route" may be thought
// of as a task composed of a series of steps (commands).
//
// The `*Router` is responsible for the actual execution of a Cookoo route. The
// main method used to call a route is `Router.HandleRequest()`.
//
// The `Context` is a container for passing information down a chain of commands.
// Apps may insert "global" information to a context at startup and make it
// available to all commands.
func Cookoo() (reg *Registry, router *Router, cxt Context) {
	cxt = NewContext()
	reg = NewRegistry()
	router = NewRouter(reg)
	return
}

// Command executes a command and returns a result.
// A Cookoo app has a registry, which has zero or more routes. Each route
// executes a sequence of zero or more commands. A command is of this type.
type Command func(cxt Context, params *Params) (interface{}, Interrupt)

// Interrupt is a generic return for a command.
// Generally, a command should return one of the following in the interrupt slot:
// - A FatalError, which will stop processing.
// - A RecoverableError, which will continue the chain.
// - A Reroute, which will cause a different route to be run.
type Interrupt interface{}

// Reroute is a command can return a Reroute to tell the router to execute a
// different route.
//
// A `Command` may return a `Reroute` to cause Cookoo to stop executing the
// current route and jump to another.
//
// 	func Forward(c Context, p *Params) (interface{}, Interrupt) {
// 		return nil, &Reroute{"anotherRoute"}
// 	}
type Reroute struct {
	Route string
}

// RouteTo returns the route to reroute to.
func (rr *Reroute) RouteTo() string {
	return rr.Route
}

// Stop a route, but not as an error condition.
//
// When Cookoo encounters a `Stop`, it will not execute any more commands on a
// given route. However, it will not emit an error, either.
type Stop struct{}

// RecoverableError is an error that should not cause the router to stop processing.
//
// When Cookoo encounters a `RecoverableError`, it will log the error as a
// warning, but will then continue to execute the next command in the route.
type RecoverableError struct {
	Message string
}

// Error returns the error message.
func (err *RecoverableError) Error() string {
	return err.Message
}

// FatalError is a fatal error, which will stop the router from continuing a route.
//
// When Cookoo encounters a `FatalError`, it will log the error and immediately
// stop processing the route.
//
// Note that by default Cookoo treats and unhandled `error` as if it were a
// `FatalError`.
type FatalError struct {
	Message string
}

// Error returns the error message.
func (err *FatalError) Error() string {
	return err.Message
}
<commit_msg>Removed : off of document subsections.<commit_after>// Package cookoo is a Chain-of-Command (CoCo) framework for writing
// applications.
//
// Tutorials
//
// * Building Web Apps with Cookoo: https://github.com/Masterminds/cookoo-web-tutorial
//
// * Building CLI Apps with Cookoo: https://github.com/Masterminds/cookoo-cli-tutorial
//
// A chain of command framework works as follows:
//
// 	* A "route" is constructed as a chain of commands -- a series of
// 	single-purpose tasks that are run in sequence.
// 	* An application is composed of one or more routes.
// 	* Commands in a route communicate using a Context.
// 	* An application Router is used to receive a route name and then
// 	execute the appropriate chain of commands.
//
// To create a new Cookoo application, use cookoo.Cookoo(). This will
// configure and create a new registry, request router, and context.
// From there, use the Registry to build chains of commands, and then
// use the Router to execute chains of commands.
//
// Example
//
// 	package main
//
// 	import (
// 		//This is the path to Cookoo
// 		"github.com/Masterminds/cookoo/src/cookoo"
// 		"fmt"
// 	)
//
// 	func main() {
// 		// Build a new Cookoo app.
// 		registry, router, context := cookoo.Cookoo()
//
// 		// Fill the registry.
// 		registry.Route("TEST", "A test route").Does(HelloWorld, "hi") //...
//
// 		// Execute the route.
// 		router.HandleRequest("TEST", context, false)
// 	}
//
// 	// An example command
// 	func HelloWorld(cxt cookoo.Context, params *cookoo.Params) (interface{}, Interrupt) {
// 		fmt.Println("Hello World")
// 		return true, nil
// 	}
//
// Unlike other CoCo implementations (like Pronto.js or Fortissimo),
// Cookoo commands are just functions.
//
// Interrupts
//
// There are four types of interrupts that you may wish to return:
//
// 	1. FatalError: This will stop the route immediately.
// 	2. RecoverableError: This will allow the route to continue moving.
// 	3. Stop: This will stop the current request, but not as an error.
// 	4. Reroute: This will stop executing the current route, and switch to executing another route.
//
// To learn how to write Cookoo applications, you may wish to examine
// the small Skunk application: https://github.com/technosophos/skunk.
package cookoo

// VERSION provides the current version of Cookoo.
const VERSION = "1.3.0"

// Cookoo creates a new Cookoo app.
//
// This is the main progenitor of a Cookoo application. Whether a plain
// Cookoo app, or a Web or CLI program, this is the function you will use
// to bootstrap.
//
// The `*Registry` is used to declare new routes, where a "route" may be thought
// of as a task composed of a series of steps (commands).
//
// The `*Router` is responsible for the actual execution of a Cookoo route. The
// main method used to call a route is `Router.HandleRequest()`.
//
// The `Context` is a container for passing information down a chain of commands.
// Apps may insert "global" information to a context at startup and make it
// available to all commands.
func Cookoo() (reg *Registry, router *Router, cxt Context) {
	cxt = NewContext()
	reg = NewRegistry()
	router = NewRouter(reg)
	return
}

// Command executes a command and returns a result.
// A Cookoo app has a registry, which has zero or more routes. Each route
// executes a sequence of zero or more commands. A command is of this type.
type Command func(cxt Context, params *Params) (interface{}, Interrupt)

// Interrupt is a generic return for a command.
// Generally, a command should return one of the following in the interrupt slot:
// - A FatalError, which will stop processing.
// - A RecoverableError, which will continue the chain.
// - A Reroute, which will cause a different route to be run.
type Interrupt interface{}

// Reroute is a command can return a Reroute to tell the router to execute a
// different route.
//
// A `Command` may return a `Reroute` to cause Cookoo to stop executing the
// current route and jump to another.
//
// 	func Forward(c Context, p *Params) (interface{}, Interrupt) {
// 		return nil, &Reroute{"anotherRoute"}
// 	}
type Reroute struct {
	Route string
}

// RouteTo returns the route to reroute to.
func (rr *Reroute) RouteTo() string {
	return rr.Route
}

// Stop a route, but not as an error condition.
//
// When Cookoo encounters a `Stop`, it will not execute any more commands on a
// given route. However, it will not emit an error, either.
type Stop struct{}

// RecoverableError is an error that should not cause the router to stop processing.
//
// When Cookoo encounters a `RecoverableError`, it will log the error as a
// warning, but will then continue to execute the next command in the route.
type RecoverableError struct {
	Message string
}

// Error returns the error message.
func (err *RecoverableError) Error() string {
	return err.Message
}

// FatalError is a fatal error, which will stop the router from continuing a route.
//
// When Cookoo encounters a `FatalError`, it will log the error and immediately
// stop processing the route.
//
// Note that by default Cookoo treats and unhandled `error` as if it were a
// `FatalError`.
type FatalError struct {
	Message string
}

// Error returns the error message.
func (err *FatalError) Error() string {
	return err.Message
}
<|endoftext|>