<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	bashPath    string
	debugging   = false
	erroring    = false
	maxprocs    = 4
	testPattern = regexp.MustCompile(`test[/\\]test-([a-z\-]+)\.sh$`)
)

func mainIntegration() {
	if len(os.Getenv("DEBUG")) > 0 {
		debugging = true
	}

	setBash()

	if max, _ := strconv.Atoi(os.Getenv("GIT_LFS_TEST_MAXPROCS")); max > 0 {
		maxprocs = max
	}

	files := testFiles()

	if len(files) == 0 {
		fmt.Println("no tests to run")
		os.Exit(1)
	}

	var wg sync.WaitGroup
	tests := make(chan string, len(files))
	output := make(chan string, len(files))

	for _, file := range files {
		tests <- file
	}

	go printOutput(output)
	for i := 0; i < maxprocs; i++ {
		wg.Add(1)
		go worker(tests, output, &wg)
	}

	close(tests)
	wg.Wait()
	close(output)
	printOutput(output)

	if erroring {
		os.Exit(1)
	}
}

func runTest(output chan string, testname string) {
	buf := &bytes.Buffer{}
	cmd := exec.Command(bashPath, testname)
	cmd.Stdout = buf
	cmd.Stderr = buf

	err := cmd.Start()
	if err != nil {
		sendTestOutput(output, testname, buf, err)
		return
	}

	done := make(chan error)
	go func() {
		if err := cmd.Wait(); err != nil {
			done <- err
		}
		close(done)
	}()

	select {
	case err = <-done:
		sendTestOutput(output, testname, buf, err)
		return
	case <-time.After(3 * time.Minute):
		sendTestOutput(output, testname, buf, errors.New("Timed out"))
		cmd.Process.Kill()
		return
	}

	sendTestOutput(output, testname, buf, nil)
}

func sendTestOutput(output chan string, testname string, buf *bytes.Buffer, err error) {
	cli := strings.TrimSpace(buf.String())
	if len(cli) == 0 {
		cli = fmt.Sprintf("<no output for %s>", testname)
	}

	if err == nil {
		output <- cli
	} else {
		basetestname := filepath.Base(testname)
		if debugging {
			fmt.Printf("Error on %s: %s\n", basetestname, err)
		}
		erroring = true
		output <- fmt.Sprintf("error: %s => %s\n%s", basetestname, err, cli)
	}
}

func printOutput(output <-chan string) {
	for {
		select {
		case out, ok := <-output:
			if !ok {
				return
			}

			fmt.Println(out)
		}
	}
}

func worker(tests <-chan string, output chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case testname, ok := <-tests:
			if !ok {
				return
			}
			runTest(output, testname)
		}
	}
}

func testFiles() []string {
	if len(os.Args) < 4 {
		return allTestFiles()
	}

	fileMap := make(map[string]bool)
	for _, file := range allTestFiles() {
		fileMap[file] = true
	}

	files := make([]string, 0, len(os.Args)-3)
	for _, arg := range os.Args {
		fullname := "test/test-" + arg + ".sh"
		if fileMap[fullname] {
			files = append(files, fullname)
		}
	}

	return files
}

func allTestFiles() []string {
	files := make([]string, 0, 100)
	filepath.Walk("test", func(path string, info os.FileInfo, err error) error {
		if debugging {
			fmt.Println("FOUND:", path)
		}
		if err != nil || info.IsDir() || !testPattern.MatchString(path) {
			return nil
		}

		if debugging {
			fmt.Println("MATCHING:", path)
		}
		files = append(files, path)
		return nil
	})
	return files
}

func setBash() {
	findcmd := "which"
	if runtime.GOOS == "windows" {
		// Can't use paths returned from which even if it's on PATH in Windows
		// Because our Go binary is a separate Windows app & not MinGW, it
		// can't understand paths like '/usr/bin/bash', needs Windows version
		findcmd = "where"
	}
	out, err := exec.Command(findcmd, "bash").Output()
	if err != nil {
		fmt.Println("Unable to find bash:", err)
		os.Exit(1)
	}

	bashPath = strings.TrimSpace(string(out))
	if debugging {
		fmt.Println("Using", bashPath)
	}

	// Test
	_, err = exec.Command(bashPath, "--version").CombinedOutput()
	if err != nil {
		fmt.Println("Error calling bash:", err)
		os.Exit(1)
	}
}
<commit_msg>unreachable<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	bashPath    string
	debugging   = false
	erroring    = false
	maxprocs    = 4
	testPattern = regexp.MustCompile(`test[/\\]test-([a-z\-]+)\.sh$`)
)

func mainIntegration() {
	if len(os.Getenv("DEBUG")) > 0 {
		debugging = true
	}

	setBash()

	if max, _ := strconv.Atoi(os.Getenv("GIT_LFS_TEST_MAXPROCS")); max > 0 {
		maxprocs = max
	}

	files := testFiles()

	if len(files) == 0 {
		fmt.Println("no tests to run")
		os.Exit(1)
	}

	var wg sync.WaitGroup
	tests := make(chan string, len(files))
	output := make(chan string, len(files))

	for _, file := range files {
		tests <- file
	}

	go printOutput(output)
	for i := 0; i < maxprocs; i++ {
		wg.Add(1)
		go worker(tests, output, &wg)
	}

	close(tests)
	wg.Wait()
	close(output)
	printOutput(output)

	if erroring {
		os.Exit(1)
	}
}

func runTest(output chan string, testname string) {
	buf := &bytes.Buffer{}
	cmd := exec.Command(bashPath, testname)
	cmd.Stdout = buf
	cmd.Stderr = buf

	err := cmd.Start()
	if err != nil {
		sendTestOutput(output, testname, buf, err)
		return
	}

	done := make(chan error)
	go func() {
		if err := cmd.Wait(); err != nil {
			done <- err
		}
		close(done)
	}()

	select {
	case err = <-done:
		sendTestOutput(output, testname, buf, err)
		return
	case <-time.After(3 * time.Minute):
		sendTestOutput(output, testname, buf, errors.New("Timed out"))
		cmd.Process.Kill()
		return
	}
}

func sendTestOutput(output chan string, testname string, buf *bytes.Buffer, err error) {
	cli := strings.TrimSpace(buf.String())
	if len(cli) == 0 {
		cli = fmt.Sprintf("<no output for %s>", testname)
	}

	if err == nil {
		output <- cli
	} else {
		basetestname := filepath.Base(testname)
		if debugging {
			fmt.Printf("Error on %s: %s\n", basetestname, err)
		}
		erroring = true
		output <- fmt.Sprintf("error: %s => %s\n%s", basetestname, err, cli)
	}
}

func printOutput(output <-chan string) {
	for {
		select {
		case out, ok := <-output:
			if !ok {
				return
			}

			fmt.Println(out)
		}
	}
}

func worker(tests <-chan string, output chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case testname, ok := <-tests:
			if !ok {
				return
			}
			runTest(output, testname)
		}
	}
}

func testFiles() []string {
	if len(os.Args) < 4 {
		return allTestFiles()
	}

	fileMap := make(map[string]bool)
	for _, file := range allTestFiles() {
		fileMap[file] = true
	}

	files := make([]string, 0, len(os.Args)-3)
	for _, arg := range os.Args {
		fullname := "test/test-" + arg + ".sh"
		if fileMap[fullname] {
			files = append(files, fullname)
		}
	}

	return files
}

func allTestFiles() []string {
	files := make([]string, 0, 100)
	filepath.Walk("test", func(path string, info os.FileInfo, err error) error {
		if debugging {
			fmt.Println("FOUND:", path)
		}
		if err != nil || info.IsDir() || !testPattern.MatchString(path) {
			return nil
		}

		if debugging {
			fmt.Println("MATCHING:", path)
		}
		files = append(files, path)
		return nil
	})
	return files
}

func setBash() {
	findcmd := "which"
	if runtime.GOOS == "windows" {
		// Can't use paths returned from which even if it's on PATH in Windows
		// Because our Go binary is a separate Windows app & not MinGW, it
		// can't understand paths like '/usr/bin/bash', needs Windows version
		findcmd = "where"
	}
	out, err := exec.Command(findcmd, "bash").Output()
	if err != nil {
		fmt.Println("Unable to find bash:", err)
		os.Exit(1)
	}

	bashPath = strings.TrimSpace(string(out))
	if debugging {
		fmt.Println("Using", bashPath)
	}

	// Test
	_, err = exec.Command(bashPath, "--version").CombinedOutput()
	if err != nil {
		fmt.Println("Error calling bash:", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package bfchroma

import (
	"io"

	"github.com/alecthomas/chroma"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/alecthomas/chroma/lexers"
	"github.com/alecthomas/chroma/styles"

	bf "gopkg.in/russross/blackfriday.v2"
)

func (r *ChromaRenderer) renderWithChroma(w io.Writer, text []byte, data bf.CodeBlockData) error {
	var lexer chroma.Lexer
	if len(data.Info) > 0 {
		lexer = lexers.Get(string(data.Info))
	} else {
		lexer = lexers.Analyse(string(text))
	}
	if lexer == nil {
		lexer = lexers.Fallback
	}
	cstyle := styles.Get(r.Style)
	if cstyle == nil {
		cstyle = styles.Fallback
	}
	formatter := html.New()
	iterator, err := lexer.Tokenise(nil, string(text))
	if err != nil {
		return err
	}
	return formatter.Format(w, cstyle, iterator)
}

// ChromaRenderer is a custom Blackfriday renderer that uses the capabilities of
// chroma to highlight code with triple backtick notation
type ChromaRenderer struct {
	Base  *bf.HTMLRenderer
	Style string
}

// RenderNode satisfies the Renderer interface
func (r *ChromaRenderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus {
	switch node.Type {
	case bf.CodeBlock:
		if err := r.renderWithChroma(w, node.Literal, node.CodeBlockData); err != nil {
			return r.Base.RenderNode(w, node, entering)
		}
		return bf.SkipChildren
	default:
		return r.Base.RenderNode(w, node, entering)
	}
}

// RenderHeader satisfies the Renderer interface
func (r *ChromaRenderer) RenderHeader(w io.Writer, ast *bf.Node) {
	r.Base.RenderHeader(w, ast)
}

// RenderFooter satisfies the Renderer interface
func (r *ChromaRenderer) RenderFooter(w io.Writer, ast *bf.Node) {
	r.Base.RenderFooter(w, ast)
}
<commit_msg>Fixing renderer<commit_after>package bfchroma

import (
	"io"

	"github.com/alecthomas/chroma"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/alecthomas/chroma/lexers"
	"github.com/alecthomas/chroma/styles"

	bf "gopkg.in/russross/blackfriday.v2"
)

func (r *Renderer) renderWithChroma(w io.Writer, text []byte, data bf.CodeBlockData) error {
	var lexer chroma.Lexer
	if len(data.Info) > 0 {
		lexer = lexers.Get(string(data.Info))
	} else {
		lexer = lexers.Analyse(string(text))
	}
	if lexer == nil {
		lexer = lexers.Fallback
	}
	cstyle := styles.Get(r.Style)
	if cstyle == nil {
		cstyle = styles.Fallback
	}
	formatter := html.New()
	iterator, err := lexer.Tokenise(nil, string(text))
	if err != nil {
		return err
	}
	return formatter.Format(w, cstyle, iterator)
}

// Renderer is a custom Blackfriday renderer that uses the capabilities of
// chroma to highlight code with triple backtick notation
type Renderer struct {
	Base  *bf.HTMLRenderer
	Style string
}

// RenderNode satisfies the Renderer interface
func (r *Renderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus {
	switch node.Type {
	case bf.CodeBlock:
		if err := r.renderWithChroma(w, node.Literal, node.CodeBlockData); err != nil {
			return r.Base.RenderNode(w, node, entering)
		}
		return bf.SkipChildren
	default:
		return r.Base.RenderNode(w, node, entering)
	}
}

// RenderHeader satisfies the Renderer interface
func (r *Renderer) RenderHeader(w io.Writer, ast *bf.Node) {
	r.Base.RenderHeader(w, ast)
}

// RenderFooter satisfies the Renderer interface
func (r *Renderer) RenderFooter(w io.Writer, ast *bf.Node) {
	r.Base.RenderFooter(w, ast)
}
<|endoftext|>


<commit_before>package util

import (
	"os"
	"os/user"
	"path/filepath"
	"sync"
	"github.com/op/go-logging"
	"github.com/spf13/viper"
	"strings"
)

var log = logging.MustGetLogger("LoginService")

// Config is the data structure for passing configuration info
type Config struct {
	Version int
	Storage struct {
			Filepath string
		}
	Authn   struct {
			MintKeyName      string
			ValidateKeyNames []string
			TokenTTL         int
			PwdProvider struct {
				PwdFileName string
				Salt		string
			}
		}

}

// Sanitize the configuration
func sanitize(c *Config) {
	s := c.Storage.Filepath
	if len(s) > 0 {
		// Make sure the db path ends with a forwardslash
		if string(s[len(s) - 1]) != "/" {
			s = s + "/"
			log.Debugf("Added forwardslash to db path '%s' ", s)
		}
		// Handle relative paths
		if string(s[0]) != "/" {
			pwd, _ := os.Getwd()
			s = pwd + "/" + s
			log.Debugf("Added pwd to db path '%s' ", s)
		}
		c.Storage.Filepath = s
	}
}

// LoadConfig loads configuration using a hard-coded name
// This is what gets called during normal operation
func LoadConfig() {
	LoadConfigByName("config")
}

// LoadConfigByName loads a config from a specific file
// Used for separating test from operational configuration
func LoadConfigByName(name string) {
	var isFatal bool
	var tmp *Config

	tmp = new(Config)

	cLock.RLock()
	isFatal = (config == nil)
	cLock.RUnlock()

	userName := getUserName()
	log.Debugf("Current user is %s", userName)

	viper.SetConfigName(name)
	viper.SetConfigType("json")

	configFolder := getConfigPath(userName)
	viper.AddConfigPath(configFolder)
	viper.AddConfigPath(".") // default path

	if err := viper.ReadInConfig(); err != nil {
		// No config to start up on
		if isFatal {
			log.Debugf("Looking for config in: %s", configFolder)
			panic(err)
		} else {
			log.Errorf("Failed to load configuration from %s\n", name)
			return
		}
	}

	log.Infof("Config file found: %s\n", viper.ConfigFileUsed())

	viper.Unmarshal(tmp)
	sanitize(tmp)

	// TODO viper can reload config too. Remove this?
	// Nope, the versioning is so we can trigger reloading of keys
	cLock.Lock()
	if config == nil {
		tmp.Version = 1
	} else {
		tmp.Version = config.Version + 1
	}

	config = tmp
	cLock.Unlock()

	log.Infof("Success loading configuration ver %d from %s", config.Version, viper.ConfigFileUsed())
}

func GetConfig() *Config {
	cLock.RLock()
	defer cLock.RUnlock()
	return config
}

// Return currently logged in user's username
func getUserName() string {
	u, err := user.Current()
	if err != nil {
		log.Errorf("Cannot find current user")
	}
	return u.Username
}

// Generate path to config folder
func getConfigPath(userName string) string {
	sep := string(filepath.Separator)
	wd, _ := os.Getwd()

	pathEl := strings.Split(wd, sep)
	iSrc := lastIndexOf(pathEl, "src")
	iBin := lastIndexOf(pathEl, "bin")

	cfgPath := ""
	log.Debugf("Found bin at %d", iBin)
	log.Debugf("Found src at %d", iSrc)
	var a []string
	if iBin > iSrc {
		a = pathEl[:iBin + 1] // take up to bin (inclusive)
	}else {
		a = pathEl[:iSrc + 1] // take up to src (inclusive)
		// If neither bin nor source is found, we are probably at
		// project home
		if iBin == -1{
			a = append(pathEl, "src")
		}
	}

	if len(a) > 0 {
		cfgPath = strings.Join(a, sep) + sep
		cfgPath += "config" + sep + userName + sep
	}

	return cfgPath
}

func lastIndexOf(h []string, n string) int {
	for i := len(h) - 1; i > 0; i-- {
		if h[i] == n {
			return i
		}
	}
	return -1
}

// Global to hold the conf and a lock
var (
	config *Config
	cLock = new(sync.RWMutex)
)
<commit_msg>Fixed config path bug<commit_after>package util

import (
	"os"
	"os/user"
	"path/filepath"
	"sync"
	"github.com/op/go-logging"
	"github.com/spf13/viper"
	"strings"
)

var log = logging.MustGetLogger("LoginService")

// Config is the data structure for passing configuration info
type Config struct {
	Version int
	Storage struct {
			Filepath string
		}
	Authn   struct {
			MintKeyName      string
			ValidateKeyNames []string
			TokenTTL         int
			PwdProvider struct {
				PwdFileName string
				Salt		string
			}
		}

}

// Sanitize the configuration
func sanitize(c *Config) {
	s := c.Storage.Filepath
	if len(s) > 0 {
		// Make sure the db path ends with a forwardslash
		if string(s[len(s) - 1]) != "/" {
			s = s + "/"
			log.Debugf("Added forwardslash to db path '%s' ", s)
		}
		// Handle relative paths
		if string(s[0]) != "/" {
			pwd, _ := os.Getwd()
			s = pwd + "/" + s
			log.Debugf("Added pwd to db path '%s' ", s)
		}
		c.Storage.Filepath = s
	}
}

// LoadConfig loads configuration using a hard-coded name
// This is what gets called during normal operation
func LoadConfig() {
	LoadConfigByName("config")
}

// LoadConfigByName loads a config from a specific file
// Used for separating test from operational configuration
func LoadConfigByName(name string) {
	var isFatal bool
	var tmp *Config

	tmp = new(Config)

	cLock.RLock()
	isFatal = (config == nil)
	cLock.RUnlock()

	userName := getUserName()
	log.Debugf("Current user is %s", userName)

	viper.SetConfigName(name)
	viper.SetConfigType("json")

	configFolder := getConfigPath(userName)
	viper.AddConfigPath(configFolder)
	viper.AddConfigPath(".") // default path

	if err := viper.ReadInConfig(); err != nil {
		// No config to start up on
		if isFatal {
			log.Debugf("Looking for config in: %s", configFolder)
			panic(err)
		} else {
			log.Errorf("Failed to load configuration from %s\n", name)
			return
		}
	}

	log.Infof("Config file found: %s\n", viper.ConfigFileUsed())

	viper.Unmarshal(tmp)
	sanitize(tmp)

	// TODO viper can reload config too. Remove this?
	// Nope, the versioning is so we can trigger reloading of keys
	cLock.Lock()
	if config == nil {
		tmp.Version = 1
	} else {
		tmp.Version = config.Version + 1
	}

	config = tmp
	cLock.Unlock()

	log.Infof("Success loading configuration ver %d from %s", config.Version, viper.ConfigFileUsed())
}

func GetConfig() *Config {
	cLock.RLock()
	defer cLock.RUnlock()
	return config
}

// Return currently logged in user's username
func getUserName() string {
	u, err := user.Current()
	if err != nil {
		log.Errorf("Cannot find current user")
	}
	return u.Username
}

// Generate path to config folder
func getConfigPath(userName string) string {
	sep := string(filepath.Separator)
	wd, _ := os.Getwd()

	pathEl := strings.Split(wd, sep)
	iSrc := lastIndexOf(pathEl, "src")
	iBin := lastIndexOf(pathEl, "bin")

	cfgPath := ""
	var a []string
	if iBin > iSrc {
		a = pathEl[:iBin + 1] // take up to bin (inclusive)
	}else {
		a = pathEl[:iSrc + 1] // take up to src (inclusive)
		// If neither bin nor source is found, we are probably at
		// project home
		if iSrc == -1{
			a = append(pathEl, "src")
		}
	}

	if len(a) > 0 {
		cfgPath = strings.Join(a, sep) + sep
		cfgPath += "config" + sep + userName + sep
	}

	return cfgPath
}

func lastIndexOf(h []string, n string) int {
	for i := len(h) - 1; i > 0; i-- {
		if h[i] == n {
			return i
		}
	}
	return -1
}

// Global to hold the conf and a lock
var (
	config *Config
	cLock = new(sync.RWMutex)
)
<|endoftext|>


<commit_before>package meter

import (
	"errors"
	"sync"
)

var (
	ErrDuplicateEvent    = errors.New("Duplicate event registration.")
	ErrNilRegistry       = errors.New("Registry is nil")
	ErrNilEvent          = errors.New("Event is nil")
	ErrNilDesc           = errors.New("Desc is nil")
	ErrUnregisteredEvent = errors.New("Unregistered event")
)

type Registry struct {
	events map[string]Event
	mu     sync.RWMutex
}
type Resolver interface {
	Get(eventName string) Event
}

type ResolverFunc func(eventName string) Event

func (rf ResolverFunc) Get(eventName string) Event {
	return rf(eventName)
}

func NewRegistry() *Registry {
	return &Registry{
		events: make(map[string]Event),
	}
}

var defaultRegistry = NewRegistry()

func (c *Registry) Get(name string) (e Event) {
	c.mu.RLock()
	e = c.events[name]
	c.mu.RUnlock()
	return
}

func (c *Registry) Events() []Event {
	c.mu.RLock()
	events := make([]Event, 0, len(c.events))
	for _, event := range c.events {
		events = append(events, event)
	}
	c.mu.RUnlock()
	return events
}

func (c *Registry) Register(event Event) error {
	if event == nil {
		return ErrNilEvent
	}
	desc := event.Describe()
	if desc == nil {
		return ErrNilDesc
	}
	if err := desc.Error(); err != nil {
		return err
	}
	name := desc.Name()
	c.mu.Lock()
	defer c.mu.Unlock()
	if d, duplicate := c.events[name]; duplicate && d != nil {
		return ErrDuplicateEvent
	}
	c.events[name] = event
	return nil
}

func (c *Registry) MustRegister(event Event) {
	if err := c.Register(event); err != nil {
		panic(err)
	}
}

type Registries []*Registry

func (r Registries) Get(name string) Event {
	for i := 0; i < len(r); i++ {
		if event := r[i].Get(name); event != nil {
			return event
		}
	}
	return nil
}
<commit_msg>Add NewRegistryEvents() and make MustRegister variadic<commit_after>package meter

import (
	"errors"
	"sync"
)

var (
	ErrDuplicateEvent    = errors.New("Duplicate event registration.")
	ErrNilRegistry       = errors.New("Registry is nil")
	ErrNilEvent          = errors.New("Event is nil")
	ErrNilDesc           = errors.New("Desc is nil")
	ErrUnregisteredEvent = errors.New("Unregistered event")
)

type Registry struct {
	events map[string]Event
	mu     sync.RWMutex
}
type Resolver interface {
	Get(eventName string) Event
}

type ResolverFunc func(eventName string) Event

func (rf ResolverFunc) Get(eventName string) Event {
	return rf(eventName)
}

func NewRegistry() *Registry {
	return &Registry{
		events: make(map[string]Event),
	}
}

func NewRegistryEvents(events ...Event) *Registry {
	r := NewRegistry()
	r.MustRegister(events...)
	return r
}

var defaultRegistry = NewRegistry()

func (c *Registry) Get(name string) (e Event) {
	c.mu.RLock()
	e = c.events[name]
	c.mu.RUnlock()
	return
}

func (c *Registry) Events() []Event {
	c.mu.RLock()
	events := make([]Event, 0, len(c.events))
	for _, event := range c.events {
		events = append(events, event)
	}
	c.mu.RUnlock()
	return events
}

func (c *Registry) Register(event Event) error {
	if event == nil {
		return ErrNilEvent
	}
	desc := event.Describe()
	if desc == nil {
		return ErrNilDesc
	}
	if err := desc.Error(); err != nil {
		return err
	}
	name := desc.Name()
	c.mu.Lock()
	defer c.mu.Unlock()
	if d, duplicate := c.events[name]; duplicate && d != nil {
		return ErrDuplicateEvent
	}
	c.events[name] = event
	return nil
}

func (c *Registry) MustRegister(events ...Event) {
	for _, e := range events {
		if err := c.Register(e); err != nil {
			panic(err)
		}
	}
}

type Registries []*Registry

func (r Registries) Get(name string) Event {
	for i := 0; i < len(r); i++ {
		if event := r[i].Get(name); event != nil {
			return event
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package rest

import (
	"os"

	"github.com/mdigger/log"
)

// Глобальные переменные библиотеки, позволяющие переопределять особенности
// ее поведения. Сюда вынесено все, что может быть использовано библиотекой
// глобально, а не в контексте одного запроса, и что может потребовать
// переопределения пользователем.
//
// В частности, если появится необходимость использования вместо формата JSON,
// например, MsgPacл, то это можно достаточно легко осуществить, просто заменим
// Encoder соответствующим обработчиком. Кстати, подпроект codex как раз
// содержит пример одной из возможных его имплементаций.
var (
	// Взведенный флаг Debug указывает, что описания ошибок возвращаются как
	// есть. В противном случае всегда возвращается только стандартное описание
	// статуса HTTP, сформированное на базе этой ошибки.
	Debug bool = false

	// Флаг Compress разрешает сжатие данных. Чтобы запретить сжимать данные,
	// установите значение данного флага в false. При инициализации сжатия
	// проверяется, что оно уже не включено, например, на уровне глобального
	// обработчика запросов и, в этом случае, сжатие не будет включено, даже
	// если флаг установлен.
	Compress bool = true

	// Encoder описывает функции, используемые для разбора запроса и кодирования
	// ответа. MaxBody задает максимальный размер поддерживаемого запроса.
	// Если размер превышает указанный, то возвращается ошибка. Если не хочется
	// ограничений, то можно установить значение 0, тогда проверка производиться
	// не будет.
	Encoder Coder = JSONCoder{1 << 15, true} // 32 мегабайта и отступы

	// EncodeError управляет форматом вывода ошибок: если флаг не взведен, то
	// ошибки отдаются как текст. В противном случае описание ошибок
	// кодируется с помощью Encoder и содержат статус и описание ошибки.
	EncodeError bool = true

	// Logger отвечает за вывод лога обращений к HTTP серверу.
	Logger = log.New(log.NewPlainHandler(os.Stderr, log.LstdFlags))
)
<commit_msg>default logger<commit_after>package rest

import "github.com/mdigger/log"

// Глобальные переменные библиотеки, позволяющие переопределять особенности
// ее поведения. Сюда вынесено все, что может быть использовано библиотекой
// глобально, а не в контексте одного запроса, и что может потребовать
// переопределения пользователем.
//
// В частности, если появится необходимость использования вместо формата JSON,
// например, MsgPacл, то это можно достаточно легко осуществить, просто заменим
// Encoder соответствующим обработчиком. Кстати, подпроект codex как раз
// содержит пример одной из возможных его имплементаций.
var (
	// Взведенный флаг Debug указывает, что описания ошибок возвращаются как
	// есть. В противном случае всегда возвращается только стандартное описание
	// статуса HTTP, сформированное на базе этой ошибки.
	Debug bool = false

	// Флаг Compress разрешает сжатие данных. Чтобы запретить сжимать данные,
	// установите значение данного флага в false. При инициализации сжатия
	// проверяется, что оно уже не включено, например, на уровне глобального
	// обработчика запросов и, в этом случае, сжатие не будет включено, даже
	// если флаг установлен.
	Compress bool = true

	// Encoder описывает функции, используемые для разбора запроса и кодирования
	// ответа. MaxBody задает максимальный размер поддерживаемого запроса.
	// Если размер превышает указанный, то возвращается ошибка. Если не хочется
	// ограничений, то можно установить значение 0, тогда проверка производиться
	// не будет.
	Encoder Coder = JSONCoder{1 << 15, true} // 32 мегабайта и отступы

	// EncodeError управляет форматом вывода ошибок: если флаг не взведен, то
	// ошибки отдаются как текст. В противном случае описание ошибок
	// кодируется с помощью Encoder и содержат статус и описание ошибки.
	EncodeError bool = true

	// Logger отвечает за вывод лога обращений к HTTP серверу.
	Logger = log.Default
)
<|endoftext|>


<commit_before>package main

import (
	"os"
)

// Initial structure of configuration
type Configuration struct {
	adminInterface string
	mode           string
	destination    string
	middleware     string
	databaseName   string
}

// AppCondig stores application configuration
var AppConfig Configuration

func initSettings() {
	// admin interface port
	AppConfig.adminInterface = ":8888"

	databaseName := os.Getenv("HOVERFLY_DB")
	if databaseName == "" {
		databaseName = "requests.db"
	}
	AppConfig.databaseName = databaseName

	// middleware configuration
	AppConfig.middleware = os.Getenv("HoverflyMiddleware")

}
<commit_msg>tidying up<commit_after>package main

import (
	"os"
)

// Configuration - initial structure of configuration
type Configuration struct {
	adminInterface string
	mode           string
	destination    string
	middleware     string
	databaseName   string
}

// AppConfig stores application configuration
var AppConfig Configuration

func initSettings() {
	// admin interface port
	AppConfig.adminInterface = ":8888"

	databaseName := os.Getenv("HOVERFLY_DB")
	if databaseName == "" {
		databaseName = "requests.db"
	}
	AppConfig.databaseName = databaseName

	// middleware configuration
	AppConfig.middleware = os.Getenv("HoverflyMiddleware")
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"sync"
)

// Configuration - initial structure of configuration
type Configuration struct {
	adminPort    string
	proxyPort    string
	mode         string
	destination  string
	middleware   string
	databaseName string
	verbose      bool

	mu sync.Mutex
}

// SetMode - provides safe way to set new mode
func (c *Configuration) SetMode(mode string) {
	c.mu.Lock()
	c.mode = mode
	c.mu.Unlock()
}

// GetMode - provides safe way to get current mode
func (c *Configuration) GetMode() (mode string) {
	c.mu.Lock()
	mode = c.mode
	c.mu.Unlock()
	return
}

// DefaultPort - default proxy port
const DefaultPort = "8500"

// DefaultAdminPort - default admin interface port
const DefaultAdminPort = "8888"

// InitSettings gets and returns initial configuration from env
// variables or sets defaults
func InitSettings() *Configuration {

	var appConfig Configuration
	// getting default admin interface port
	if os.Getenv("AdminPort") != "" {
		appConfig.adminPort = os.Getenv("AdminPort")
	} else {
		appConfig.adminPort = DefaultAdminPort
	}

	// getting proxy port
	if os.Getenv("ProxyPort") != "" {
		appConfig.proxyPort = os.Getenv("ProxyPort")
	} else {
		appConfig.proxyPort = DefaultPort
	}

	databaseName := os.Getenv("HoverflyDB")
	if databaseName == "" {
		databaseName = "requests.db"
	}
	appConfig.databaseName = databaseName

	// middleware configuration
	appConfig.middleware = os.Getenv("HoverflyMiddleware")

	return &appConfig
}
<commit_msg>added const for database name<commit_after>package main

import (
	"os"
	"sync"
)

// Configuration - initial structure of configuration
type Configuration struct {
	adminPort    string
	proxyPort    string
	mode         string
	destination  string
	middleware   string
	databaseName string
	verbose      bool

	mu sync.Mutex
}

// SetMode - provides safe way to set new mode
func (c *Configuration) SetMode(mode string) {
	c.mu.Lock()
	c.mode = mode
	c.mu.Unlock()
}

// GetMode - provides safe way to get current mode
func (c *Configuration) GetMode() (mode string) {
	c.mu.Lock()
	mode = c.mode
	c.mu.Unlock()
	return
}

// DefaultPort - default proxy port
const DefaultPort = "8500"

// DefaultAdminPort - default admin interface port
const DefaultAdminPort = "8888"

// DefaultDatabaseName - default database name that will be created
// or used by Hoverfly
const DefaultDatabaseName = "requests.db"

// InitSettings gets and returns initial configuration from env
// variables or sets defaults
func InitSettings() *Configuration {

	var appConfig Configuration
	// getting default admin interface port
	if os.Getenv("AdminPort") != "" {
		appConfig.adminPort = os.Getenv("AdminPort")
	} else {
		appConfig.adminPort = DefaultAdminPort
	}

	// getting proxy port
	if os.Getenv("ProxyPort") != "" {
		appConfig.proxyPort = os.Getenv("ProxyPort")
	} else {
		appConfig.proxyPort = DefaultPort
	}

	databaseName := os.Getenv("HoverflyDB")
	if databaseName == "" {
		databaseName = DefaultDatabaseName
	}
	appConfig.databaseName = databaseName

	// middleware configuration
	appConfig.middleware = os.Getenv("HoverflyMiddleware")

	return &appConfig
}
<|endoftext|>


<commit_before>/* A TCP proxy that allow you to deploy new code then switch traffic to it
   without downtime.

   Switching server is done with HTTP interface with the following API:
   /switch?backend=address - will switch traffic to new backend
   /current - will return (in plain text) current server

   Work flow:
	   Start first backend at port 4444
	   Run `./seamless 8080 localhost:4444`

	   Direct all traffic to port 8080 on local machine.

	   When you need to upgrade the backend, start a new one (with new code on
	   a different port, say 4445).
	   The `curl http://localhost:6777/switch?backend=localhost:4445.
	   New traffic will be directed to new server.

Original forward code by Roger Peppe (see http://bit.ly/Oc1YtF)
*/
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
)

// Current backend
var backend string

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: seamless LISTEN_PORT BACKEND\n")
		fmt.Fprintf(os.Stderr, "command line switches:\n")
		flag.PrintDefaults()
	}
	port := flag.Int("httpPort", 6777, "http interface port")
	flag.Parse()
	if flag.NArg() != 2 {
		flag.Usage()
		os.Exit(1)
	}
	localAddr := fmt.Sprintf(":%s", flag.Arg(0))
	backend = flag.Arg(1)

	local, err := net.Listen("tcp", localAddr)
	if local == nil {
		die(fmt.Sprintf("cannot listen: %v", err))
	}

	go startHttpServer(*port)

	for {
		conn, err := local.Accept()
		if conn == nil {
			die(fmt.Sprintf("accept failed: %v", err))
		}
		go forward(conn, backend)
	}
}

// forward proxies traffic between local socket and remote backend
func forward(local net.Conn, remoteAddr string) {
	remote, err := net.Dial("tcp", remoteAddr)
	if remote == nil {
		log.Printf("remote dial failed: %v\n", err)
		local.Close()
		return
	}
	go io.Copy(local, remote)
	go io.Copy(remote, local)
}

// die prints error message and aborts the program
func die(msg string) {
	fmt.Fprintf(os.Stderr, "error: %s\n", msg)
	os.Exit(1)
}

// startHttpServer start the HTTP server interface in a given port
func startHttpServer(port int) {
	http.HandleFunc("/switch", switchHandler)
	http.HandleFunc("/current", currentHandler)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

// switchHandler handler /switch and switches backend
func switchHandler(w http.ResponseWriter, req *http.Request) {
	newBackend := req.FormValue("backend")
	if len(newBackend) == 0 {
		msg := "error: missing 'backend' parameter"
		log.Println(msg)
		http.Error(w, msg, http.StatusBadRequest)
		return
	}
	backend = newBackend
	currentHandler(w, req)
}

// currentHandler handles /current and return the current backend
func currentHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s\n", backend)
}
<commit_msg>die with varargs, handle HTTP errors<commit_after>/* A TCP proxy that allow you to deploy new code then switch traffic to it
   without downtime.

   Switching server is done with HTTP interface with the following API:
   /switch?backend=address - will switch traffic to new backend
   /current - will return (in plain text) current server

   Work flow:
	   Start first backend at port 4444
	   Run `./seamless 8080 localhost:4444`

	   Direct all traffic to port 8080 on local machine.

	   When you need to upgrade the backend, start a new one (with new code on
	   a different port, say 4445).
	   The `curl http://localhost:6777/switch?backend=localhost:4445.
	   New traffic will be directed to new server.

Original forward code by Roger Peppe (see http://bit.ly/Oc1YtF)
*/
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
)

// Current backend
var backend string

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: seamless LISTEN_PORT BACKEND\n")
		fmt.Fprintf(os.Stderr, "command line switches:\n")
		flag.PrintDefaults()
	}
	port := flag.Int("httpPort", 6777, "http interface port")
	flag.Parse()
	if flag.NArg() != 2 {
		flag.Usage()
		os.Exit(1)
	}
	localAddr := fmt.Sprintf(":%s", flag.Arg(0))
	backend = flag.Arg(1)

	local, err := net.Listen("tcp", localAddr)
	if local == nil {
		die("cannot listen: %v", err)
	}

	go func() {
		if err := startHttpServer(*port); err != nil {
			die("cannot listen on %d: %v", *port, err)
		}
	}()

	for {
		conn, err := local.Accept()
		if conn == nil {
			die("accept failed: %v", err)
		}
		go forward(conn, backend)
	}
}

// forward proxies traffic between local socket and remote backend
func forward(local net.Conn, remoteAddr string) {
	remote, err := net.Dial("tcp", remoteAddr)
	if remote == nil {
		log.Printf("remote dial failed: %v\n", err)
		local.Close()
		return
	}
	go io.Copy(local, remote)
	go io.Copy(remote, local)
}

// die prints error message and aborts the program
func die(format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	fmt.Fprintf(os.Stderr, "error: %s\n", msg)
	os.Exit(1)
}

// startHttpServer start the HTTP server interface in a given port
func startHttpServer(port int) error {
	http.HandleFunc("/switch", switchHandler)
	http.HandleFunc("/current", currentHandler)
	return http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

// switchHandler handler /switch and switches backend
func switchHandler(w http.ResponseWriter, req *http.Request) {
	newBackend := req.FormValue("backend")
	if len(newBackend) == 0 {
		msg := "error: missing 'backend' parameter"
		log.Println(msg)
		http.Error(w, msg, http.StatusBadRequest)
		return
	}
	backend = newBackend
	currentHandler(w, req)
}

// currentHandler handles /current and return the current backend
func currentHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s\n", backend)
}
<|endoftext|>


<commit_before>package edward_test

import (
	"strings"
	"testing"

	"github.com/theothertomelliott/must"
	"github.com/yext/edward/common"
	"github.com/yext/edward/config"
	"github.com/yext/edward/edward"
	"github.com/yext/edward/home"
)

func TestStatus(t *testing.T) {
	var tests = []struct {
		name      string
		path      string
		config    string
		services  []string
		skipBuild bool
		tail      bool
		noWatch   bool
		exclude   []string
		err       error
	}{
		{
			name:     "single service",
			path:     "testdata/single",
			config:   "edward.json",
			services: []string{"service"},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			// Set up edward home directory
			if err := home.EdwardConfig.Initialize(); err != nil {
				t.Fatal(err)
			}

			var err error

			// Copy test content into a temp dir on the GOPATH & defer deletion
			cleanup := createWorkingDir(t, test.name, test.path)
			defer cleanup()

			err = config.LoadSharedConfig(test.config, common.EdwardVersion, nil)
			if err != nil {
				t.Fatal(err)
			}

			client := edward.NewClient()

			client.Config = test.config
			tf := newTestFollower()
			client.Follower = tf

			client.EdwardExecutable = edwardExecutable

			err = client.Start(test.services, test.skipBuild, false, test.noWatch, test.exclude)
			if err != nil {
				t.Fatal(err)
			}

			// TODO: Support specifying services
			output, err := client.Status([]string{})
			for _, service := range test.services {
				if !strings.Contains(output, service) {
					t.Error("No status entry found for: ", service)
				}
			}
			must.BeEqualErrors(t, test.err, err)

			err = client.Stop(test.services, true, test.exclude)
			if err != nil {
				t.Fatal(err)
			}
		})
	}
}
<commit_msg>Add tests to demonstrate getting group status. #123<commit_after>package edward_test

import (
	"strings"
	"testing"

	"github.com/theothertomelliott/must"
	"github.com/yext/edward/common"
	"github.com/yext/edward/config"
	"github.com/yext/edward/edward"
	"github.com/yext/edward/home"
)

func TestStatus(t *testing.T) {
	var tests = []struct {
		name             string
		path             string
		config           string
		runningServices  []string
		inServices       []string
		expectedServices []string
		err              error
	}{
		{
			name:             "single service",
			path:             "testdata/single",
			config:           "edward.json",
			runningServices:  []string{"service"},
			expectedServices: []string{"service"},
		},
		{
			name:             "multiple services",
			path:             "testdata/multiple",
			config:           "edward.json",
			runningServices:  []string{"service1", "service2"},
			expectedServices: []string{"service1", "service2"},
		},
		{
			name:             "multiple services - one specified",
			path:             "testdata/multiple",
			config:           "edward.json",
			runningServices:  []string{"service1", "service2"},
			inServices:       []string{"service2"},
			expectedServices: []string{"service2"},
		},
		{
			name:             "full group",
			path:             "testdata/group",
			config:           "edward.json",
			runningServices:  []string{"group"},
			inServices:       []string{"group"},
			expectedServices: []string{"service1", "service2", "service3"},
		},
		{
			name:             "partial group",
			path:             "testdata/group",
			config:           "edward.json",
			runningServices:  []string{"service2", "service3"},
			inServices:       []string{"group"},
			expectedServices: []string{"service2", "service3"},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			// Set up edward home directory
			if err := home.EdwardConfig.Initialize(); err != nil {
				t.Fatal(err)
			}

			var err error

			// Copy test content into a temp dir on the GOPATH & defer deletion
			cleanup := createWorkingDir(t, test.name, test.path)
			defer cleanup()

			err = config.LoadSharedConfig(test.config, common.EdwardVersion, nil)
			if err != nil {
				t.Fatal(err)
			}

			client := edward.NewClient()

			client.Config = test.config
			tf := newTestFollower()
			client.Follower = tf

			client.EdwardExecutable = edwardExecutable

			err = client.Start(test.runningServices, false, false, false, nil)
			if err != nil {
				t.Fatal(err)
			}

			output, err := client.Status(test.inServices)
			for _, service := range test.expectedServices {
				if !strings.Contains(output, service) {
					t.Error("No status entry found for: ", service)
				}
			}
			must.BeEqualErrors(t, test.err, err)

			err = client.Stop(test.runningServices, true, nil)
			if err != nil {
				t.Fatal(err)
			}
		})
	}
}
<|endoftext|>


<commit_before>package complete

// Args describes command line arguments
type Args struct {
	All           []string
	Completed     []string
	Last          string
	LastCompleted string
}

func newArgs(line []string) Args {
	completed := removeLast(line)
	return Args{
		All:           line[1:],
		Completed:     completed,
		Last:          last(line),
		LastCompleted: last(completed),
	}
}

func (a Args) from(i int) Args {
	a.All = a.All[i:]
	a.Completed = a.Completed[i:]
	return a
}

func removeLast(a []string) []string {
	if len(a) > 0 {
		return a[:len(a)-1]
	}
	return a
}

func last(args []string) (last string) {
	if len(args) > 0 {
		last = args[len(args)-1]
	}
	return
}
<commit_msg>Add doc to Args<commit_after>package complete

// Args describes command line arguments
type Args struct {
	// All lists of all arguments in command line (not including the command itself)
	All           []string
	// Completed lists of all completed arguments in command line,
	// If the last one is still being typed - no space after it,
	// it won't appear in this list of arguments.
	Completed     []string
	// Last argument in command line, the one being typed, if the last
	// character in the command line is a space, this argument will be empty,
	// otherwise this would be the last word.
	Last          string
	// LastCompleted is the last argument that was fully typed.
	// If the last character in the command line is space, this would be the
	// last word, otherwise, it would be the word before that.
	LastCompleted string
}

func newArgs(line []string) Args {
	completed := removeLast(line)
	return Args{
		All:           line[1:],
		Completed:     completed,
		Last:          last(line),
		LastCompleted: last(completed),
	}
}

func (a Args) from(i int) Args {
	a.All = a.All[i:]
	a.Completed = a.Completed[i:]
	return a
}

func removeLast(a []string) []string {
	if len(a) > 0 {
		return a[:len(a)-1]
	}
	return a
}

func last(args []string) (last string) {
	if len(args) > 0 {
		last = args[len(args)-1]
	}
	return
}
<|endoftext|>


<commit_before>package gocli

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

const (
	STRING  = "string"
	INTEGER = "int"
	BOOL    = "bool"
)

var (
	re = regexp.MustCompile("^([\\-]+.*)")
)

type FlagMap map[string]*Flag
type ArgumentMap map[string]*Argument

type Args struct {
	Args        []string
	Attributes  map[string][]string
	currentKey  string
	FlagMap     FlagMap
	ArgumentMap ArgumentMap
	Flags       []*Flag
}

func NewArgs(mapping FlagMap) *Args {
	a := &Args{}
	for key, flag := range mapping {
		flag.CliFlag = key
		a.RegisterFlag(flag)
	}
	return a
}

type Argument struct {
	Key      string
	Index    int
	Multiple bool
}

func (a *Args) RegisterArgs(args string) {
	if a.ArgumentMap == nil {
		a.ArgumentMap = ArgumentMap{}
	}
	for i, arg := range strings.Split(args, " ") {
		a.ArgumentMap[arg] = &Argument{Key: arg, Index: i}
	}
	return
}

var RE_FLAG_PREFIX = regexp.MustCompile("^([\\-]+)")

func (a *Args) getWithDefaults(key string) []string {
	flag := a.FlagMap[key]
	if flag == nil {
		panic("no flag found for " + key)
	}
	values := a.Attributes[key]
	if len(values) == 0 {
		if flag.DefaultValue != "" {
			values = []string{flag.DefaultValue}
		}
	}
	return values
}

func (a *Args) AttributesMap() map[string]string {
	m := make(map[string]string)
	for k, flag := range a.FlagMap {
		key := flag.Key
		if key == "" {
			key = RE_FLAG_PREFIX.ReplaceAllString(k, "")
		}
		v := a.getWithDefaults(k)
		if len(v) == 1 {
			m[key] = v[0]
		} else if len(v) > 0 {
			m[key] = strings.Join(v, ",")
		}
	}
	return m
}

func (a *Args) RegisterFlag(flag *Flag) {
	if a.FlagMap == nil {
		a.FlagMap = make(map[string]*Flag)
	}
	a.FlagMap[flag.CliFlag] = flag
	a.Flags = append(a.Flags, flag)
}

func (a *Args) BuildStringFlag(key string, required bool, defaultValue, description string) *Flag {
	return &Flag{
		Type:         STRING,
		CliFlag:      key,
		Required:     required,
		DefaultValue: defaultValue,
		Description:  description,
	}
}

func (a *Args) RegisterString(cliKey string, key string, required bool, defaultValue, description string) {
	a.RegisterFlag(
		&Flag{
			Type:         STRING,
			Key:          key,
			CliFlag:      cliKey,
			Required:     required,
			DefaultValue: defaultValue,
			Description:  description,
		},
	)
}

func (a *Args) RegisterInt(cliKey string, key string, required bool, defaultValue int, description string) {
	a.RegisterFlag(
		&Flag{
			Type:         INTEGER,
			CliFlag:      cliKey,
			Key:          key,
			Required:     required,
			DefaultValue: strconv.Itoa(defaultValue),
			Description:  description,
		},
	)
}

func (a *Args) RegisterBool(cliKey string, key string, required bool, defaultValue bool, description string) {
	a.RegisterFlag(
		&Flag{
			Type:         BOOL,
			Key:          key,
			CliFlag:      cliKey,
			Required:     required,
			DefaultValue: strconv.FormatBool(required),
			Description:  description,
		},
	)
}

func (a *Args) Usage() string {
	table := NewTable()
	table.Separator = " "
	for _, flag := range a.Flags {
		table.AddStrings(flag.Usage())
	}
	return table.String()
}

func (a *Args) lookup(key string) (flags []*Flag) {
	for i := range a.Flags {
		arg := a.Flags[i]
		if arg.Matches(key) {
			flags = append(flags, arg)
		}
	}
	return flags
}

func (a *Args) Length() int {
	return len(a.Args)
}

func (a *Args) String(key string) {
	a.AddFlag(key, STRING)
}

func (a *Args) Bool(key string) {
	a.AddFlag(key, BOOL)
}

func (a *Args) AddFlag(key, value string) {
	a.RegisterFlag(&Flag{Type: value, CliFlag: key})
}

func (a *Args) AddAttribute(k, v string) {
	if a.Attributes == nil {
		a.Attributes = map[string][]string{}
	}
	a.Attributes[k] = append(a.Attributes[k], v)
}

func (a *Args) Parse(args []string) error {
	a.Args = make([]string, 0, 10)
	a.Attributes = make(map[string][]string)
	for _, arg := range args {
		if e := a.handleArg(arg); e != nil {
			return e
		}
	}
	return nil
}

func (a *Args) TypeOf(key string) (out string, e error) {
	flags := a.lookup(key)
	switch len(flags) {
	case 0:
		e = fmt.Errorf("no mapping defined for %s", key)
	case 1:
		out = flags[0].Type
	default:
		e = fmt.Errorf("mapping for %s not uniq", key)
	}
	return out, e
}

func (a *Args) handleArgFlag(flag string) error {
	if t, e := a.TypeOf(flag); e != nil {
		return e
	} else {
		switch t {
		case STRING, INTEGER:
			a.currentKey = flag
		case BOOL:
			a.AddAttribute(flag, "true")
		default:
			return fmt.Errorf("no mapping defined for %s", flag)
		}
	}
	return nil
}

func (a *Args) handleArg(arg string) error {
	if parts := re.FindStringSubmatch(arg); len(parts) == 2 {
		chunks := strings.Split(parts[1], "=")
		if len(chunks) == 2 {
			key, value := chunks[0], chunks[1]
			a.AddAttribute(key, value)
			return nil
		} else {
			if e := a.handleArgFlag(chunks[0]); e != nil {
				return e
			}
			return nil
		}
	} else if a.currentKey != "" {
		a.AddAttribute(a.currentKey, arg)
		a.currentKey = ""
		return nil
	}
	a.Args = append(a.Args, arg)
	return nil
}

func (a *Args) Get(key string) []string {
	return a.Attributes[key]
}

func (a *Args) GetInt(key string) (int, error) {
	s, e := a.GetString(key)
	if e != nil {
		return 0, e
	}
	return strconv.Atoi(s)
}

func (a *Args) MustGetInt(key string) int {
	i, e := a.GetInt(key)
	if e != nil {
		panic(e.Error())
	}
	return i
}

func (a *Args) MustGetString(key string) string {
	s, e := a.GetString(key)
	if e != nil {
		panic(e.Error())
	}
	return s
}

func (a *Args) GetString(key string) (string, error) {
	flag, ok := a.FlagMap[key]
	if !ok {
		return "", fmt.Errorf("no mapping defined for %s", key)
	}
	values := a.Get(key)
	if len(values) > 0 {
		return values[len(values)-1], nil
	}
	if flag.Required {
		return "", fmt.Errorf("flag %s is required", key)
	}
	return flag.DefaultValue, nil
}

func (a *Args) GetBool(key string) bool {
	args := a.Attributes[key]
	return (len(args) == 1 && args[0] == "true")
}
<commit_msg>add method KeyForCliKey<commit_after>package gocli

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

const (
	STRING  = "string"
	INTEGER = "int"
	BOOL    = "bool"
)

var (
	re = regexp.MustCompile("^([\\-]+.*)")
)

type FlagMap map[string]*Flag
type ArgumentMap map[string]*Argument

type Args struct {
	Args        []string
	Attributes  map[string][]string
	currentKey  string
	FlagMap     FlagMap
	ArgumentMap ArgumentMap
	Flags       []*Flag
}

func NewArgs(mapping FlagMap) *Args {
	a := &Args{}
	for key, flag := range mapping {
		flag.CliFlag = key
		a.RegisterFlag(flag)
	}
	return a
}

type Argument struct {
	Key      string
	Index    int
	Multiple bool
}

func (a *Args) KeyForCliKey(cliKey string) string {
	for _, flag := range a.Flags {
		if flag.CliFlag == cliKey {
			return flag.Key
		}
	}
	return ""
}

func (a *Args) RegisterArgs(args string) {
	if a.ArgumentMap == nil {
		a.ArgumentMap = ArgumentMap{}
	}
	for i, arg := range strings.Split(args, " ") {
		a.ArgumentMap[arg] = &Argument{Key: arg, Index: i}
	}
	return
}

var RE_FLAG_PREFIX = regexp.MustCompile("^([\\-]+)")

func (a *Args) getWithDefaults(key string) []string {
	flag := a.FlagMap[key]
	if flag == nil {
		panic("no flag found for " + key)
	}
	values := a.Attributes[key]
	if len(values) == 0 {
		if flag.DefaultValue != "" {
			values = []string{flag.DefaultValue}
		}
	}
	return values
}

func (a *Args) AttributesMap() map[string]string {
	m := make(map[string]string)
	for k, flag := range a.FlagMap {
		key := flag.Key
		if key == "" {
			key = RE_FLAG_PREFIX.ReplaceAllString(k, "")
		}
		v := a.getWithDefaults(k)
		if len(v) == 1 {
			m[key] = v[0]
		} else if len(v) > 0 {
			m[key] = strings.Join(v, ",")
		}
	}
	return m
}

func (a *Args) RegisterFlag(flag *Flag) {
	if a.FlagMap == nil {
		a.FlagMap = make(map[string]*Flag)
	}
	a.FlagMap[flag.CliFlag] = flag
	a.Flags = append(a.Flags, flag)
}

func (a *Args) BuildStringFlag(key string, required bool, defaultValue, description string) *Flag {
	return &Flag{
		Type:         STRING,
		CliFlag:      key,
		Required:     required,
		DefaultValue: defaultValue,
		Description:  description,
	}
}

func (a *Args) RegisterString(cliKey string, key string, required bool, defaultValue, description string) {
	a.RegisterFlag(
		&Flag{
			Type:         STRING,
			Key:          key,
			CliFlag:      cliKey,
			Required:     required,
			DefaultValue: defaultValue,
			Description:  description,
		},
	)
}

func (a *Args) RegisterInt(cliKey string, key string, required bool, defaultValue int, description string) {
	a.RegisterFlag(
		&Flag{
			Type:         INTEGER,
			CliFlag:      cliKey,
			Key:          key,
			Required:     required,
			DefaultValue: strconv.Itoa(defaultValue),
			Description:  description,
		},
	)
}

func (a *Args) RegisterBool(cliKey string, key string, required bool, defaultValue bool, description string) {
	a.RegisterFlag(
		&Flag{
			Type:         BOOL,
			Key:          key,
			CliFlag:      cliKey,
			Required:     required,
			DefaultValue: strconv.FormatBool(required),
			Description:  description,
		},
	)
}

func (a *Args) Usage() string {
	table := NewTable()
	table.Separator = " "
	for _, flag := range a.Flags {
		table.AddStrings(flag.Usage())
	}
	return table.String()
}

func (a *Args) lookup(key string) (flags []*Flag) {
	for i := range a.Flags {
		arg := a.Flags[i]
		if arg.Matches(key) {
			flags = append(flags, arg)
		}
	}
	return flags
}

func (a *Args) Length() int {
	return len(a.Args)
}

func (a *Args) String(key string) {
	a.AddFlag(key, STRING)
}

func (a *Args) Bool(key string) {
	a.AddFlag(key, BOOL)
}

func (a *Args) AddFlag(key, value string) {
	a.RegisterFlag(&Flag{Type: value, CliFlag: key})
}

func (a *Args) AddAttribute(k, v string) {
	if a.Attributes == nil {
		a.Attributes = map[string][]string{}
	}
	a.Attributes[k] = append(a.Attributes[k], v)
}

func (a *Args) Parse(args []string) error {
	a.Args = make([]string, 0, 10)
	a.Attributes = make(map[string][]string)
	for _, arg := range args {
		if e := a.handleArg(arg); e != nil {
			return e
		}
	}
	return nil
}

func (a *Args) TypeOf(key string) (out string, e error) {
	flags := a.lookup(key)
	switch len(flags) {
	case 0:
		e = fmt.Errorf("no mapping defined for %s", key)
	case 1:
		out = flags[0].Type
	default:
		e = fmt.Errorf("mapping for %s not uniq", key)
	}
	return out, e
}

func (a *Args) handleArgFlag(flag string) error {
	if t, e := a.TypeOf(flag); e != nil {
		return e
	} else {
		switch t {
		case STRING, INTEGER:
			a.currentKey = flag
		case BOOL:
			a.AddAttribute(flag, "true")
		default:
			return fmt.Errorf("no mapping defined for %s", flag)
		}
	}
	return nil
}

func (a *Args) handleArg(arg string) error {
	if parts := re.FindStringSubmatch(arg); len(parts) == 2 {
		chunks := strings.Split(parts[1], "=")
		if len(chunks) == 2 {
			key, value := chunks[0], chunks[1]
			a.AddAttribute(key, value)
			return nil
		} else {
			if e := a.handleArgFlag(chunks[0]); e != nil {
				return e
			}
			return nil
		}
	} else if a.currentKey != "" {
		a.AddAttribute(a.currentKey, arg)
		a.currentKey = ""
		return nil
	}
	a.Args = append(a.Args, arg)
	return nil
}

func (a *Args) Get(key string) []string {
	return a.Attributes[key]
}

func (a *Args) GetInt(key string) (int, error) {
	s, e := a.GetString(key)
	if e != nil {
		return 0, e
	}
	return strconv.Atoi(s)
}

func (a *Args) MustGetInt(key string) int {
	i, e := a.GetInt(key)
	if e != nil {
		panic(e.Error())
	}
	return i
}

func (a *Args) MustGetString(key string) string {
	s, e := a.GetString(key)
	if e != nil {
		panic(e.Error())
	}
	return s
}

func (a *Args) GetString(key string) (string, error) {
	flag, ok := a.FlagMap[key]
	if !ok {
		return "", fmt.Errorf("no mapping defined for %s", key)
	}
	values := a.Get(key)
	if len(values) > 0 {
		return values[len(values)-1], nil
	}
	if flag.Required {
		return "", fmt.Errorf("flag %s is required", key)
	}
	return flag.DefaultValue, nil
}

func (a *Args) GetBool(key string) bool {
	args := a.Attributes[key]
	return (len(args) == 1 && args[0] == "true")
}
<|endoftext|>


<commit_before>package main

type Selector interface {
	RequireHeader() bool
	ParseHeader(header []string) error
	Select(recode []string) ([]string, error)
}
<commit_msg>Implement Indexes as rough<commit_after>package main

type Selector interface {
	RequireHeader() bool
	ParseHeader(header []string) error
	Select(recode []string) ([]string, error)
}

type Indexes struct {
	indexes []int
}

func NewIndexes(indexes []int) *Indexes {
	return &Indexes{
		indexes: indexes,
	}
}

func (i *Indexes) RequireHeader() bool {
	return false
}

func (i *Indexes) ParseHeader(header []string) error {
	return nil
}

func (i *Indexes) Select(recode []string) ([]string, error) {
	var values []string
	for _, index := range i.indexes {
		values = append(values, recode[index])
	}
	return values, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"path"
	"regexp"
	"strings"
	"time"

	"github.com/aerokube/selenoid/session"
)

type request struct {
	*http.Request
}

type sess struct {
	addr string
	id   string
}

// TODO There is simpler way to do this
func (r request) localaddr() string {
	addr := r.Context().Value(http.LocalAddrContextKey).(net.Addr).String()
	_, port, _ := net.SplitHostPort(addr)
	return net.JoinHostPort("127.0.0.1", port)
}

func (r request) session(id string) *sess {
	return &sess{r.localaddr(), id}
}

func (s *sess) url() string {
	return fmt.Sprintf("http://%s/wd/hub/session/%s", s.addr, s.id)
}

func jsonError(w http.ResponseWriter, msg string, code int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	json.NewEncoder(w).Encode(
		map[string]interface{}{
			"value": map[string]string{
				"message": msg,
			},
			"status": 13,
		})
}

func (s *sess) Delete() {
	log.Printf("[SESSION_TIMED_OUT] [%s]\n", s.id)
	r, err := http.NewRequest(http.MethodDelete, s.url(), nil)
	if err != nil {
		log.Printf("[DELETE_FAILED] [%s] [%v]\n", s.id, err)
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	resp, err := http.DefaultClient.Do(r.WithContext(ctx))
	if resp != nil {
		defer resp.Body.Close()
	}
	if err == nil && resp.StatusCode == http.StatusOK {
		return
	}
	if err != nil {
		log.Printf("[DELETE_FAILED] [%s] [%v]\n", s.id, err)
	} else {
		log.Printf("[DELETE_FAILED] [%s] [%s]\n", s.id, resp.Status)
	}
}

func create(w http.ResponseWriter, r *http.Request) {
	quota, _, ok := r.BasicAuth()
	if !ok {
		quota = "unknown"
	}
	body, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		log.Printf("[ERROR_READING_REQUEST] [%s]\n", err.Error())
		jsonError(w, err.Error(), http.StatusBadRequest)
		queue.Drop()
		return
	}
	var browser struct {
		Caps struct {
			Name             string `json:"browserName"`
			Version          string `json:"version"`
			ScreenResolution string `json:"screenResolution"`
		} `json:"desiredCapabilities"`
	}
	err = json.Unmarshal(body, &browser)
	if err != nil {
		log.Printf("[BAD_JSON_FORMAT] [%s]\n", err.Error())
		jsonError(w, err.Error(), http.StatusBadRequest)
		queue.Drop()
		return
	}
	if browser.Caps.ScreenResolution != "" {
		exp := regexp.MustCompile(`^[0-9]+x[0-9]+x(8|16|24)$`)
		if !exp.MatchString(browser.Caps.ScreenResolution) {
			jsonError(w, fmt.Sprintf("Malformed screenResolution capability: %s. Correct format is WxHxD, e.g. 1920x1080x24.",
				browser.Caps.ScreenResolution), http.StatusBadRequest)
			queue.Drop()
			return
		}
	}
	starter, ok := manager.Find(browser.Caps.Name, &browser.Caps.Version, browser.Caps.ScreenResolution)
	if !ok {
		log.Printf("[ENVIRONMENT_NOT_AVAILABLE] [%s-%s]\n", browser.Caps.Name, browser.Caps.Version)
		jsonError(w, "Requested environment is not available", http.StatusBadRequest)
		queue.Drop()
		return
	}
	u, cancel, err := starter.StartWithCancel()
	if err != nil {
		log.Printf("[SERVICE_STARTUP_FAILED] [%s]\n", err.Error())
		jsonError(w, err.Error(), http.StatusInternalServerError)
		queue.Drop()
		return
	}
	r.URL.Host, r.URL.Path = u.Host, path.Clean(u.Path+r.URL.Path)
	var resp *http.Response
	for i := 1; ; i++ {
		req, _ := http.NewRequest(http.MethodPost, r.URL.String(), bytes.NewReader(body))
		ctx, _ := context.WithTimeout(r.Context(), 10*time.Second)
		log.Printf("[SESSION_ATTEMPTED] [%s] [%d]\n", u.String(), i)
		rsp, err := http.DefaultClient.Do(req.WithContext(ctx))
		select {
		case <-ctx.Done():
			if rsp != nil {
				rsp.Body.Close()
			}
			switch ctx.Err() {
			case context.DeadlineExceeded:
				log.Printf("[SESSION_ATTEMPT_TIMED_OUT]\n")
				continue
			case context.Canceled:
				log.Printf("[CLIENT_DISCONNECTED]\n")
				queue.Drop()
				cancel()
				return
			}
		default:
		}
		if err != nil {
			if rsp != nil {
				rsp.Body.Close()
			}
			log.Printf("[SESSION_FAILED] [%s] - [%s]\n", u.String(), err)
			jsonError(w, err.Error(), http.StatusInternalServerError)
			queue.Drop()
			cancel()
			return
		} else {
			resp = rsp
			break
		}
	}
	defer resp.Body.Close()
	w.WriteHeader(resp.StatusCode)
	var s struct {
		Value struct {
			ID string `json:"sessionId"`
		}
		ID string `json:"sessionId"`
	}
	tee := io.TeeReader(resp.Body, w)
	json.NewDecoder(tee).Decode(&s)
	if s.ID == "" {
		s.ID = s.Value.ID
	}
	if s.ID == "" {
		log.Printf("[SESSION_FAILED] Bad response from [%s] - [%v]\n", u.String(), resp.Status)
		queue.Drop()
		cancel()
		return
	}
	sessions.Put(s.ID, &session.Session{
		Quota:   quota,
		Browser: browser.Caps.Name,
		Version: browser.Caps.Version,
		URL:     u,
		Cancel:  cancel,
		Timeout: onTimeout(timeout, func() {
			request{r}.session(s.ID).Delete()
		})})
	queue.Create()
	log.Printf("[SESSION_CREATED] [%s] [%s]\n", s.ID, u)
}

func proxy(w http.ResponseWriter, r *http.Request) {
	done := make(chan func())
	go func(w http.ResponseWriter, r *http.Request) {
		cancel := func() {}
		defer func() {
			done <- cancel
		}()
		(&httputil.ReverseProxy{
			Director: func(r *http.Request) {
				fragments := strings.Split(r.URL.Path, "/")
				id := fragments[2]
				sess, ok := sessions.Get(id)
				if ok {
					sess.Lock.Lock()
					defer sess.Lock.Unlock()
					r.URL.Host, r.URL.Path = sess.URL.Host, path.Clean(sess.URL.Path+r.URL.Path)
					close(sess.Timeout)
					if r.Method == http.MethodDelete && len(fragments) == 3 {
						cancel = sess.Cancel
						sessions.Remove(id)
						queue.Release()
						log.Printf("[SESSION_DELETED] [%s]\n", id)
					} else {
						sess.Timeout = onTimeout(timeout, func() {
							request{r}.session(id).Delete()
						})
					}
					return
				}
				r.URL.Path = "/error"
			},
		}).ServeHTTP(w, r)
	}(w, r)
	go (<-done)()
}

func onTimeout(t time.Duration, f func()) chan struct{} {
	cancel := make(chan struct{})
	go func(cancel chan struct{}) {
		select {
		case <-time.After(t):
			f()
		case <-cancel:
		}
	}(cancel)
	return cancel
}
<commit_msg>Added request ID and attempt number to logs (related to #54)<commit_after>package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"path"
	"regexp"
	"strings"
	"time"

	"github.com/aerokube/selenoid/session"
	"sync"
)

var (
	num      uint64
	numLock  sync.Mutex
)

type request struct {
	*http.Request
}

type sess struct {
	addr string
	id   string
}

// TODO There is simpler way to do this
func (r request) localaddr() string {
	addr := r.Context().Value(http.LocalAddrContextKey).(net.Addr).String()
	_, port, _ := net.SplitHostPort(addr)
	return net.JoinHostPort("127.0.0.1", port)
}

func (r request) session(id string) *sess {
	return &sess{r.localaddr(), id}
}

func (s *sess) url() string {
	return fmt.Sprintf("http://%s/wd/hub/session/%s", s.addr, s.id)
}

func jsonError(w http.ResponseWriter, msg string, code int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	json.NewEncoder(w).Encode(
		map[string]interface{}{
			"value": map[string]string{
				"message": msg,
			},
			"status": 13,
		})
}

func (s *sess) Delete() {
	log.Printf("[SESSION_TIMED_OUT] [%s]\n", s.id)
	r, err := http.NewRequest(http.MethodDelete, s.url(), nil)
	if err != nil {
		log.Printf("[DELETE_FAILED] [%s] [%v]\n", s.id, err)
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	resp, err := http.DefaultClient.Do(r.WithContext(ctx))
	if resp != nil {
		defer resp.Body.Close()
	}
	if err == nil && resp.StatusCode == http.StatusOK {
		return
	}
	if err != nil {
		log.Printf("[DELETE_FAILED] [%s] [%v]\n", s.id, err)
	} else {
		log.Printf("[DELETE_FAILED] [%s] [%s]\n", s.id, resp.Status)
	}
}

func serial() uint64 {
	numLock.Lock()
	defer numLock.Unlock()
	id := num
	num++
	return id
}

func create(w http.ResponseWriter, r *http.Request) {
	id := serial()
	quota, _, ok := r.BasicAuth()
	if !ok {
		quota = "unknown"
	}
	body, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		log.Printf("[%d] [ERROR_READING_REQUEST] [%s] [%v]\n", id, quota, err)
		jsonError(w, err.Error(), http.StatusBadRequest)
		queue.Drop()
		return
	}
	var browser struct {
		Caps struct {
			Name             string `json:"browserName"`
			Version          string `json:"version"`
			ScreenResolution string `json:"screenResolution"`
		} `json:"desiredCapabilities"`
	}
	err = json.Unmarshal(body, &browser)
	if err != nil {
		log.Printf("[%d] [BAD_JSON_FORMAT] [%s] [%v]\n", id, quota, err)
		jsonError(w, err.Error(), http.StatusBadRequest)
		queue.Drop()
		return
	}
	if browser.Caps.ScreenResolution != "" {
		exp := regexp.MustCompile(`^[0-9]+x[0-9]+x(8|16|24)$`)
		if !exp.MatchString(browser.Caps.ScreenResolution) {
			log.Printf("[%d] [BAD_SCREEN_RESOLUTION] [%s] [%s]\n", id, quota, browser.Caps.ScreenResolution)
			jsonError(w, fmt.Sprintf("Malformed screenResolution capability: %s. Correct format is WxHxD, e.g. 1920x1080x24.",
				browser.Caps.ScreenResolution), http.StatusBadRequest)
			queue.Drop()
			return
		}
	}
	starter, ok := manager.Find(browser.Caps.Name, &browser.Caps.Version, browser.Caps.ScreenResolution)
	if !ok {
		log.Printf("[%d] [ENVIRONMENT_NOT_AVAILABLE] [%s] [%s-%s]\n", id, quota, browser.Caps.Name, browser.Caps.Version)
		jsonError(w, "Requested environment is not available", http.StatusBadRequest)
		queue.Drop()
		return
	}
	u, cancel, err := starter.StartWithCancel()
	if err != nil {
		log.Printf("[%d] [SERVICE_STARTUP_FAILED] [%s] [%v]\n", id, quota, err)
		jsonError(w, err.Error(), http.StatusInternalServerError)
		queue.Drop()
		return
	}
	r.URL.Host, r.URL.Path = u.Host, path.Clean(u.Path+r.URL.Path)
	var resp *http.Response
	i := 1
	for ; ; i++ {
		req, _ := http.NewRequest(http.MethodPost, r.URL.String(), bytes.NewReader(body))
		ctx, _ := context.WithTimeout(r.Context(), 10*time.Second)
		log.Printf("[%d] [SESSION_ATTEMPTED] [%s] [%s] [%d]\n", id, quota, u.String(), i)
		rsp, err := http.DefaultClient.Do(req.WithContext(ctx))
		select {
		case <-ctx.Done():
			if rsp != nil {
				rsp.Body.Close()
			}
			switch ctx.Err() {
			case context.DeadlineExceeded:
				log.Printf("[%d] [SESSION_ATTEMPT_TIMED_OUT] [%s]\n", id, quota)
				continue
			case context.Canceled:
				log.Printf("[%d] [CLIENT_DISCONNECTED] [%s]\n", id, quota)
				queue.Drop()
				cancel()
				return
			}
		default:
		}
		if err != nil {
			if rsp != nil {
				rsp.Body.Close()
			}
			log.Printf("[%d] [SESSION_FAILED] [%s] - [%s]\n", id, u.String(), err)
			jsonError(w, err.Error(), http.StatusInternalServerError)
			queue.Drop()
			cancel()
			return
		} else {
			resp = rsp
			break
		}
	}
	defer resp.Body.Close()
	w.WriteHeader(resp.StatusCode)
	var s struct {
		Value struct {
			ID string `json:"sessionId"`
		}
		ID string `json:"sessionId"`
	}
	tee := io.TeeReader(resp.Body, w)
	json.NewDecoder(tee).Decode(&s)
	if s.ID == "" {
		s.ID = s.Value.ID
	}
	if s.ID == "" {
		log.Printf("[%d] [SESSION_FAILED] [%s] [Bad response from %s - %v]\n", id, quota, u.String(), resp.Status)
		queue.Drop()
		cancel()
		return
	}
	sessions.Put(s.ID, &session.Session{
		Quota:   quota,
		Browser: browser.Caps.Name,
		Version: browser.Caps.Version,
		URL:     u,
		Cancel:  cancel,
		Timeout: onTimeout(timeout, func() {
			request{r}.session(s.ID).Delete()
		})})
	queue.Create()
	log.Printf("[%d] [SESSION_CREATED] [%s] [%s] [%s] [%d]\n", id, quota, s.ID, u, i)
}

func proxy(w http.ResponseWriter, r *http.Request) {
	done := make(chan func())
	go func(w http.ResponseWriter, r *http.Request) {
		cancel := func() {}
		defer func() {
			done <- cancel
		}()
		(&httputil.ReverseProxy{
			Director: func(r *http.Request) {
				fragments := strings.Split(r.URL.Path, "/")
				id := fragments[2]
				sess, ok := sessions.Get(id)
				if ok {
					sess.Lock.Lock()
					defer sess.Lock.Unlock()
					r.URL.Host, r.URL.Path = sess.URL.Host, path.Clean(sess.URL.Path+r.URL.Path)
					close(sess.Timeout)
					if r.Method == http.MethodDelete && len(fragments) == 3 {
						cancel = sess.Cancel
						sessions.Remove(id)
						queue.Release()
						log.Printf("[SESSION_DELETED] [%s]\n", id)
					} else {
						sess.Timeout = onTimeout(timeout, func() {
							request{r}.session(id).Delete()
						})
					}
					return
				}
				r.URL.Path = "/error"
			},
		}).ServeHTTP(w, r)
	}(w, r)
	go (<-done)()
}

func onTimeout(t time.Duration, f func()) chan struct{} {
	cancel := make(chan struct{})
	go func(cancel chan struct{}) {
		select {
		case <-time.After(t):
			f()
		case <-cancel:
		}
	}(cancel)
	return cancel
}
<|endoftext|>


<commit_before>package libnetwork

import (
	"fmt"
	"net"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/iptables"
	"github.com/miekg/dns"
)

// Resolver represents the embedded DNS server in Docker. It operates
// by listening on container's loopback interface for DNS queries.
type Resolver interface {
	// Start starts the name server for the container
	Start() error
	// Stop stops the name server for the container. Stopped resolver
	// can be reused after running the SetupFunc again.
	Stop()
	// SetupFunc() provides the setup function that should be run
	// in the container's network namespace.
	SetupFunc() func()
	// NameServer() returns the IP of the DNS resolver for the
	// containers.
	NameServer() string
	// To configure external name servers the resolver should use
	SetExtServers([]string)
	// ResolverOptions returns resolv.conf options that should be set
	ResolverOptions() []string
}

const (
	resolverIP    = "127.0.0.11"
	dnsPort       = "53"
	ptrIPv4domain = ".in-addr.arpa."
	ptrIPv6domain = ".ip6.arpa."
	respTTL       = 1800
	maxExtDNS     = 3 //max number of external servers to try
)

// resolver implements the Resolver interface
type resolver struct {
	sb        *sandbox
	extDNS    []string
	server    *dns.Server
	conn      *net.UDPConn
	tcpServer *dns.Server
	tcpListen *net.TCPListener
	err       error
}

// NewResolver creates a new instance of the Resolver
func NewResolver(sb *sandbox) Resolver {
	return &resolver{
		sb:  sb,
		err: fmt.Errorf("setup not done yet"),
	}
}

func (r *resolver) SetupFunc() func() {
	return (func() {
		var err error

		// DNS operates primarily on UDP
		addr := &net.UDPAddr{
			IP: net.ParseIP(resolverIP),
		}

		r.conn, err = net.ListenUDP("udp", addr)
		if err != nil {
			r.err = fmt.Errorf("error in opening name server socket %v", err)
			return
		}
		laddr := r.conn.LocalAddr()
		_, ipPort, _ := net.SplitHostPort(laddr.String())

		// Listen on a TCP as well
		tcpaddr := &net.TCPAddr{
			IP: net.ParseIP(resolverIP),
		}

		r.tcpListen, err = net.ListenTCP("tcp", tcpaddr)
		if err != nil {
			r.err = fmt.Errorf("error in opening name TCP server socket %v", err)
			return
		}
		ltcpaddr := r.tcpListen.Addr()
		_, tcpPort, _ := net.SplitHostPort(ltcpaddr.String())
		rules := [][]string{
			{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", laddr.String()},
			{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
			{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "tcp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", ltcpaddr.String()},
			{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "tcp", "--sport", tcpPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
		}

		for _, rule := range rules {
			r.err = iptables.RawCombinedOutput(rule...)
			if r.err != nil {
				return
			}
		}
		r.err = nil
	})
}

func (r *resolver) Start() error {
	// make sure the resolver has been setup before starting
	if r.err != nil {
		return r.err
	}
	s := &dns.Server{Handler: r, PacketConn: r.conn}
	r.server = s
	go func() {
		s.ActivateAndServe()
	}()

	tcpServer := &dns.Server{Handler: r, Listener: r.tcpListen}
	r.tcpServer = tcpServer
	go func() {
		tcpServer.ActivateAndServe()
	}()
	return nil
}

func (r *resolver) Stop() {
	if r.server != nil {
		r.server.Shutdown()
	}
	if r.tcpServer != nil {
		r.tcpServer.Shutdown()
	}
	r.conn = nil
	r.tcpServer = nil
	r.err = fmt.Errorf("setup not done yet")
}

func (r *resolver) SetExtServers(dns []string) {
	r.extDNS = dns
}

func (r *resolver) NameServer() string {
	return resolverIP
}

func (r *resolver) ResolverOptions() []string {
	return []string{"ndots:0"}
}

func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) {
	addr := r.sb.ResolveName(name)
	if addr == nil {
		return nil, nil
	}

	log.Debugf("Lookup for %s: IP %s", name, addr.String())

	resp := new(dns.Msg)
	resp.SetReply(query)

	rr := new(dns.A)
	rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
	rr.A = addr
	resp.Answer = append(resp.Answer, rr)
	return resp, nil
}

func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) {
	parts := []string{}

	if strings.HasSuffix(ptr, ptrIPv4domain) {
		parts = strings.Split(ptr, ptrIPv4domain)
	} else if strings.HasSuffix(ptr, ptrIPv6domain) {
		parts = strings.Split(ptr, ptrIPv6domain)
	} else {
		return nil, fmt.Errorf("invalid PTR query, %v", ptr)
	}

	host := r.sb.ResolveIP(parts[0])
	if len(host) == 0 {
		return nil, nil
	}

	log.Debugf("Lookup for IP %s: name %s", parts[0], host)
	fqdn := dns.Fqdn(host)

	resp := new(dns.Msg)
	resp.SetReply(query)

	rr := new(dns.PTR)
	rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
	rr.Ptr = fqdn
	resp.Answer = append(resp.Answer, rr)
	return resp, nil
}

func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
	var (
		resp *dns.Msg
		err  error
	)

	name := query.Question[0].Name
	if query.Question[0].Qtype == dns.TypeA {
		resp, err = r.handleIPv4Query(name, query)
	} else if query.Question[0].Qtype == dns.TypePTR {
		resp, err = r.handlePTRQuery(name, query)
	}

	if err != nil {
		log.Error(err)
		return
	}

	if resp == nil {
		if len(r.extDNS) == 0 {
			return
		}

		num := maxExtDNS
		if len(r.extDNS) < maxExtDNS {
			num = len(r.extDNS)
		}
		for i := 0; i < num; i++ {
			log.Debugf("Querying ext dns %s:%s for %s[%d]", w.LocalAddr().Network(), r.extDNS[i], name, query.Question[0].Qtype)

			c := &dns.Client{Net: w.LocalAddr().Network()}
			addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53)

			resp, _, err = c.Exchange(query, addr)
			if err == nil {
				break
			}
			log.Errorf("external resolution failed, %s", err)
		}
		if resp == nil {
			return
		}
	}

	err = w.WriteMsg(resp)
	if err != nil {
		log.Errorf("error writing resolver resp, %s", err)
	}
}
<commit_msg>Set Recursion Available bit in query response and reduce the ttl<commit_after>package libnetwork

import (
	"fmt"
	"net"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/iptables"
	"github.com/miekg/dns"
)

// Resolver represents the embedded DNS server in Docker. It operates
// by listening on container's loopback interface for DNS queries.
type Resolver interface {
	// Start starts the name server for the container
	Start() error
	// Stop stops the name server for the container. Stopped resolver
	// can be reused after running the SetupFunc again.
	Stop()
	// SetupFunc() provides the setup function that should be run
	// in the container's network namespace.
	SetupFunc() func()
	// NameServer() returns the IP of the DNS resolver for the
	// containers.
	NameServer() string
	// To configure external name servers the resolver should use
	SetExtServers([]string)
	// ResolverOptions returns resolv.conf options that should be set
	ResolverOptions() []string
}

const (
	resolverIP    = "127.0.0.11"
	dnsPort       = "53"
	ptrIPv4domain = ".in-addr.arpa."
	ptrIPv6domain = ".ip6.arpa."
	respTTL       = 600
	maxExtDNS     = 3 //max number of external servers to try
)

// resolver implements the Resolver interface
type resolver struct {
	sb        *sandbox
	extDNS    []string
	server    *dns.Server
	conn      *net.UDPConn
	tcpServer *dns.Server
	tcpListen *net.TCPListener
	err       error
}

// NewResolver creates a new instance of the Resolver
func NewResolver(sb *sandbox) Resolver {
	return &resolver{
		sb:  sb,
		err: fmt.Errorf("setup not done yet"),
	}
}

func (r *resolver) SetupFunc() func() {
	return (func() {
		var err error

		// DNS operates primarily on UDP
		addr := &net.UDPAddr{
			IP: net.ParseIP(resolverIP),
		}

		r.conn, err = net.ListenUDP("udp", addr)
		if err != nil {
			r.err = fmt.Errorf("error in opening name server socket %v", err)
			return
		}
		laddr := r.conn.LocalAddr()
		_, ipPort, _ := net.SplitHostPort(laddr.String())

		// Listen on a TCP as well
		tcpaddr := &net.TCPAddr{
			IP: net.ParseIP(resolverIP),
		}

		r.tcpListen, err = net.ListenTCP("tcp", tcpaddr)
		if err != nil {
			r.err = fmt.Errorf("error in opening name TCP server socket %v", err)
			return
		}
		ltcpaddr := r.tcpListen.Addr()
		_, tcpPort, _ := net.SplitHostPort(ltcpaddr.String())
		rules := [][]string{
			{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", laddr.String()},
			{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
			{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "tcp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", ltcpaddr.String()},
			{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "tcp", "--sport", tcpPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
		}

		for _, rule := range rules {
			r.err = iptables.RawCombinedOutput(rule...)
			if r.err != nil {
				return
			}
		}
		r.err = nil
	})
}

func (r *resolver) Start() error {
	// make sure the resolver has been setup before starting
	if r.err != nil {
		return r.err
	}
	s := &dns.Server{Handler: r, PacketConn: r.conn}
	r.server = s
	go func() {
		s.ActivateAndServe()
	}()

	tcpServer := &dns.Server{Handler: r, Listener: r.tcpListen}
	r.tcpServer = tcpServer
	go func() {
		tcpServer.ActivateAndServe()
	}()
	return nil
}

func (r *resolver) Stop() {
	if r.server != nil {
		r.server.Shutdown()
	}
	if r.tcpServer != nil {
		r.tcpServer.Shutdown()
	}
	r.conn = nil
	r.tcpServer = nil
	r.err = fmt.Errorf("setup not done yet")
}

func (r *resolver) SetExtServers(dns []string) {
	r.extDNS = dns
}

func (r *resolver) NameServer() string {
	return resolverIP
}

func (r *resolver) ResolverOptions() []string {
	return []string{"ndots:0"}
}

func setCommonFlags(msg *dns.Msg) {
	msg.RecursionAvailable = true
}

func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) {
	addr := r.sb.ResolveName(name)
	if addr == nil {
		return nil, nil
	}

	log.Debugf("Lookup for %s: IP %s", name, addr.String())

	resp := new(dns.Msg)
	resp.SetReply(query)
	setCommonFlags(resp)

	rr := new(dns.A)
	rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
	rr.A = addr
	resp.Answer = append(resp.Answer, rr)
	return resp, nil
}

func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) {
	parts := []string{}

	if strings.HasSuffix(ptr, ptrIPv4domain) {
		parts = strings.Split(ptr, ptrIPv4domain)
	} else if strings.HasSuffix(ptr, ptrIPv6domain) {
		parts = strings.Split(ptr, ptrIPv6domain)
	} else {
		return nil, fmt.Errorf("invalid PTR query, %v", ptr)
	}

	host := r.sb.ResolveIP(parts[0])
	if len(host) == 0 {
		return nil, nil
	}

	log.Debugf("Lookup for IP %s: name %s", parts[0], host)
	fqdn := dns.Fqdn(host)

	resp := new(dns.Msg)
	resp.SetReply(query)
	setCommonFlags(resp)

	rr := new(dns.PTR)
	rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
	rr.Ptr = fqdn
	resp.Answer = append(resp.Answer, rr)
	return resp, nil
}

func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
	var (
		resp *dns.Msg
		err  error
	)

	name := query.Question[0].Name
	if query.Question[0].Qtype == dns.TypeA {
		resp, err = r.handleIPv4Query(name, query)
	} else if query.Question[0].Qtype == dns.TypePTR {
		resp, err = r.handlePTRQuery(name, query)
	}

	if err != nil {
		log.Error(err)
		return
	}

	if resp == nil {
		if len(r.extDNS) == 0 {
			return
		}

		num := maxExtDNS
		if len(r.extDNS) < maxExtDNS {
			num = len(r.extDNS)
		}
		for i := 0; i < num; i++ {
			log.Debugf("Querying ext dns %s:%s for %s[%d]", w.LocalAddr().Network(), r.extDNS[i], name, query.Question[0].Qtype)

			c := &dns.Client{Net: w.LocalAddr().Network()}
			addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53)

			resp, _, err = c.Exchange(query, addr)
			if err == nil {
				break
			}
			log.Errorf("external resolution failed, %s", err)
		}
		if resp == nil {
			return
		}
	}

	err = w.WriteMsg(resp)
	if err != nil {
		log.Errorf("error writing resolver resp, %s", err)
	}
}
<|endoftext|>


<commit_before>// Package sendgrid provides a simple interface to interact with the SendGrid API
package sendgrid

import (
	"github.com/sendgrid/rest" // depends on version 2.2.0
	"github.com/sendgrid/sendgrid-go/helpers/mail"
)

// Version is this client library's current version
const Version = "3.1.0"

// Client ...
type Client struct {
	// rest.Request
	rest.Request
}

// GetRequest returns a default request object.
func GetRequest(key string, endpoint string, host string) rest.Request {
	if host == "" {
		host = "https://api.sendgrid.com"
	}
	baseURL := host + endpoint
	requestHeaders := make(map[string]string)
	requestHeaders["Authorization"] = "Bearer " + key
	requestHeaders["User-Agent"] = "sendgrid/" + Version + ";go"
	requestHeaders["Accept"] = "application/json"
	request := rest.Request{
		BaseURL: baseURL,
		Headers: requestHeaders,
	}
	return request
}

//Send ...
func (cl *Client) Send(email *mail.SGMailV3) (*rest.Response, error) {
	cl.Body = mail.GetRequestBody(email)
	return API(cl.Request)
}

// NewSendClient ...
func NewSendClient(key string) *Client {
	request := GetRequest(key, "/v3/mail/send", "")
	request.Method = "POST"
	return &Client{request}
}

// DefaultClient is used if no custom HTTP client is defined
var DefaultClient = rest.DefaultClient

// API sets up the request to the SendGrid API, this is main interface.
func API(request rest.Request) (*rest.Response, error) {
	return DefaultClient.API(request)
}
<commit_msg>Updated to be an array - from a previous merge today<commit_after>// Package sendgrid provides a simple interface to interact with the SendGrid API
package sendgrid

import (
	"github.com/sendgrid/rest" // depends on version 2.2.0
	"github.com/sendgrid/sendgrid-go/helpers/mail"
)

// Version is this client library's current version
const Version = "3.1.0"

// Client ...
type Client struct {
	// rest.Request
	rest.Request
}

// GetRequest returns a default request object.
func GetRequest(key string, endpoint string, host string) rest.Request {
	if host == "" {
		host = "https://api.sendgrid.com"
	}
	baseURL := host + endpoint
	requestHeaders := map[string]string{
		requestHeaders["Authorization"] = "Bearer " + key
		requestHeaders["User-Agent"] = "sendgrid/" + Version + ";go"
		requestHeaders["Accept"] = "application/json"
	}
	request := rest.Request{
		BaseURL: baseURL,
		Headers: requestHeaders,
	}
	return request
}

//Send ...
func (cl *Client) Send(email *mail.SGMailV3) (*rest.Response, error) {
	cl.Body = mail.GetRequestBody(email)
	return API(cl.Request)
}

// NewSendClient ...
func NewSendClient(key string) *Client {
	request := GetRequest(key, "/v3/mail/send", "")
	request.Method = "POST"
	return &Client{request}
}

// DefaultClient is used if no custom HTTP client is defined
var DefaultClient = rest.DefaultClient

// API sets up the request to the SendGrid API, this is main interface.
func API(request rest.Request) (*rest.Response, error) {
	return DefaultClient.API(request)
}
<|endoftext|>


<commit_before>package tokenex

import (
	"encoding/json"
	"errors"
	"github.com/parnurzeal/gorequest"
	"log"
)

type (
	BaseResponse struct {
		Data            map[string]interface{}
		Error           string
		ReferenceNumber string
		Success         bool
	}

	TokenResponse struct {
		BaseResponse
		Token string
	}

	ValueResponse struct {
		BaseResponse
		Value string
	}

	ValidateResponse struct {
		BaseResponse
		Valid bool
	}

	DeleteResponse struct {
		BaseResponse
	}
)

func (b *BaseResponse) result(v interface{}) error {
	err := json.Unmarshal([]byte(request(b.Data)), &v)
	errStr := ""
	if err == nil {
		switch v.(type) {
		case *TokenResponse:
			errStr = v.(*TokenResponse).Error
		case *ValueResponse:
			errStr = v.(*ValueResponse).Error
		case *ValidateResponse:
			errStr = v.(*ValidateResponse).Error
		case *DeleteResponse:
			errStr = v.(*DeleteResponse).Error
		}
		if errStr != "" {
			err = errors.New(errStr)
		}
	}

	return err
}

func request(data map[string]interface{}) string {
	if config.baseUrl == "" {
		log.Fatalf("config.baseUrl not set")
	} else if config.id == "" {
		log.Fatalf("config.id not set")
	} else if config.apiKey == "" {
		log.Fatalf("config.apiKey not set")
	}
	baseUrl := config.baseUrl
	m := map[string]interface{}{
		"TokenExID": config.id,
		"APIKey":    config.apiKey,
	}

	for key, value := range data {
		m[key] = value
	}
	mJson, _ := json.Marshal(m)
	request := gorequest.New()
	_, body, _ := request.Post(baseUrl + "/" + data["Action"].(string)).
		Send(string(mJson)).
		End()
	return body
}
<commit_msg>:penguin: Isolate config validation<commit_after>package tokenex

import (
	"encoding/json"
	"errors"
	"github.com/parnurzeal/gorequest"
	"log"
)

type (
	BaseResponse struct {
		Data            map[string]interface{}
		Error           string
		ReferenceNumber string
		Success         bool
	}

	TokenResponse struct {
		BaseResponse
		Token string
	}

	ValueResponse struct {
		BaseResponse
		Value string
	}

	ValidateResponse struct {
		BaseResponse
		Valid bool
	}

	DeleteResponse struct {
		BaseResponse
	}
)

func (b *BaseResponse) result(v interface{}) error {
	err := json.Unmarshal([]byte(request(b.Data)), &v)
	errStr := ""
	if err == nil {
		switch v.(type) {
		case *TokenResponse:
			errStr = v.(*TokenResponse).Error
		case *ValueResponse:
			errStr = v.(*ValueResponse).Error
		case *ValidateResponse:
			errStr = v.(*ValidateResponse).Error
		case *DeleteResponse:
			errStr = v.(*DeleteResponse).Error
		}
		if errStr != "" {
			err = errors.New(errStr)
		}
	}

	return err
}

func request(data map[string]interface{}) string {
	validateConfig()
	baseUrl := config.baseUrl
	m := map[string]interface{}{
		"TokenExID": config.id,
		"APIKey":    config.apiKey,
	}

	for key, value := range data {
		m[key] = value
	}
	mJson, _ := json.Marshal(m)
	request := gorequest.New()
	_, body, _ := request.Post(baseUrl + "/" + data["Action"].(string)).
		Send(string(mJson)).
		End()
	return body
}

func validateConfig() {
	if config.baseUrl == "" {
		log.Fatalf("config.baseUrl not set")
	} else if config.id == "" {
		log.Fatalf("config.id not set")
	} else if config.apiKey == "" {
		log.Fatalf("config.apiKey not set")
	}
}
<|endoftext|>


<commit_before>package garnish

import (
	"net/http"
	"strconv"
)

// An interface used for a Closable response
type ByteCloser interface {
	Len() int
	Close() error
	Bytes() []byte
}

// A pre-built response for a 404
var NotFound = Respond([]byte("not found")).Status(404)

// A pre-built response for a 500
var InternalError = Respond([]byte("internal error")).Status(500)

type Response interface {
	// Get the response's header
	GetHeader() http.Header

	// Get the response's body
	GetBody() []byte

	// Get the response's status
	GetStatus() int

	//set the response's status
	SetStatus(status int)

	// Close the response
	Close() error

	// Detaches the response from any underlying resourcs.
	// In cases where Close is a no-op, this should probably
	// return self. Otherwise, the response should do whatever
	// it has to so that it can be long-lived (clone itself into
	// a normal response and close itself)
	Detach() Response
}

// A in-memory response with a chainable API. Should be created
// via the Respond() method
type ResponseBuilder struct {
	Response
}

// Set a cache-control for the specified duration
func (b *ResponseBuilder) Cache(duration int) *ResponseBuilder {
	return b.Header("Cache-Control", "private,max-age="+strconv.Itoa(duration))
}

// Set a header
func (b *ResponseBuilder) Header(key, value string) *ResponseBuilder {
	b.GetHeader().Set(key, value)
	return b
}

// Set a header
func (b *ResponseBuilder) Status(status int) *ResponseBuilder {
	b.Response.SetStatus(status)
	return b
}

// Creates a Response
func Respond(body interface{}) *ResponseBuilder {
	h := make(http.Header)
	switch b := body.(type) {
	case string:
		return &ResponseBuilder{&InMemoryResponse{h, []byte(b), 200}}
	case []byte:
		return &ResponseBuilder{&InMemoryResponse{h, b, 200}}
	case ByteCloser:
		return &ResponseBuilder{&ClosableResponse{h, b, 200}}
	default:
		return &ResponseBuilder{&InMemoryResponse{h, []byte("invalid body"), 500}}
	}
}

type InMemoryResponse struct {
	H http.Header
	B []byte
	S int
}

// Get headers
func (r *InMemoryResponse) GetHeader() http.Header {
	return r.H
}

// Get the body
func (r *InMemoryResponse) GetBody() []byte {
	return r.B
}

// Get the status
func (r *InMemoryResponse) GetStatus() int {
	return r.S
}

// Sets the status
func (r *InMemoryResponse) SetStatus(status int) {
	r.S = status
}

// close the response (noop)
func (r *InMemoryResponse) Close() error {
	return nil
}

// deatches the response from any underlying resources (noop)
func (r *InMemoryResponse) Detach() Response {
	return r
}

// A in-memory response with a chainable API which uses a bytepool
// as the body
type ClosableResponse struct {
	H http.Header
	B ByteCloser
	S int
}

// Get headers
func (r *ClosableResponse) GetHeader() http.Header {
	return r.H
}

// Get the body
func (r *ClosableResponse) GetBody() []byte {
	return r.B.Bytes()
}

// Get the status
func (r *ClosableResponse) GetStatus() int {
	return r.S
}

// Sets the status
func (r *ClosableResponse) SetStatus(status int) {
	r.S = status
}

// closes the underlying bytepool
func (r *ClosableResponse) Close() error {
	return r.B.Close()
}

// Detaches the response from the underlying bytepool,
// turning this into an InMemoryResponse
func (r *ClosableResponse) Detach() Response {
	defer r.B.Close()
	clone := &InMemoryResponse{
		S: r.S,
		H: r.H,
	}
	clone.B = make([]byte, r.B.Len())
	copy(clone.B, r.B.Bytes())
	return clone
}
<commit_msg>Json() helper added, which wraps the Respond() helper but sets the content type<commit_after>package garnish

import (
	"net/http"
	"strconv"
)

// An interface used for a Closable response
type ByteCloser interface {
	Len() int
	Close() error
	Bytes() []byte
}

// A pre-built response for a 404
var NotFound = Respond([]byte("not found")).Status(404)

// A pre-built response for a 500
var InternalError = Respond([]byte("internal error")).Status(500)

type Response interface {
	// Get the response's header
	GetHeader() http.Header

	// Get the response's body
	GetBody() []byte

	// Get the response's status
	GetStatus() int

	//set the response's status
	SetStatus(status int)

	// Close the response
	Close() error

	// Detaches the response from any underlying resourcs.
	// In cases where Close is a no-op, this should probably
	// return self. Otherwise, the response should do whatever
	// it has to so that it can be long-lived (clone itself into
	// a normal response and close itself)
	Detach() Response
}

// A in-memory response with a chainable API. Should be created
// via the Respond() method
type ResponseBuilder struct {
	Response
}

// Set a cache-control for the specified duration
func (b *ResponseBuilder) Cache(duration int) *ResponseBuilder {
	return b.Header("Cache-Control", "private,max-age="+strconv.Itoa(duration))
}

// Set a header
func (b *ResponseBuilder) Header(key, value string) *ResponseBuilder {
	b.GetHeader().Set(key, value)
	return b
}

// Set a header
func (b *ResponseBuilder) Status(status int) *ResponseBuilder {
	b.Response.SetStatus(status)
	return b
}

func Json(body interface{}) *ResponseBuilder {
	rb := Respond(body)
	rb.Header("Content-Type", "application/json; charset=utf-8")
	return rb
}

// Creates a Response
func Respond(body interface{}) *ResponseBuilder {
	h := make(http.Header)
	switch b := body.(type) {
	case string:
		return &ResponseBuilder{&InMemoryResponse{h, []byte(b), 200}}
	case []byte:
		return &ResponseBuilder{&InMemoryResponse{h, b, 200}}
	case ByteCloser:
		return &ResponseBuilder{&ClosableResponse{h, b, 200}}
	default:
		return &ResponseBuilder{&InMemoryResponse{h, []byte("invalid body"), 500}}
	}
}

type InMemoryResponse struct {
	H http.Header
	B []byte
	S int
}

// Get headers
func (r *InMemoryResponse) GetHeader() http.Header {
	return r.H
}

// Get the body
func (r *InMemoryResponse) GetBody() []byte {
	return r.B
}

// Get the status
func (r *InMemoryResponse) GetStatus() int {
	return r.S
}

// Sets the status
func (r *InMemoryResponse) SetStatus(status int) {
	r.S = status
}

// close the response (noop)
func (r *InMemoryResponse) Close() error {
	return nil
}

// deatches the response from any underlying resources (noop)
func (r *InMemoryResponse) Detach() Response {
	return r
}

// A in-memory response with a chainable API which uses a bytepool
// as the body
type ClosableResponse struct {
	H http.Header
	B ByteCloser
	S int
}

// Get headers
func (r *ClosableResponse) GetHeader() http.Header {
	return r.H
}

// Get the body
func (r *ClosableResponse) GetBody() []byte {
	return r.B.Bytes()
}

// Get the status
func (r *ClosableResponse) GetStatus() int {
	return r.S
}

// Sets the status
func (r *ClosableResponse) SetStatus(status int) {
	r.S = status
}

// closes the underlying bytepool
func (r *ClosableResponse) Close() error {
	return r.B.Close()
}

// Detaches the response from the underlying bytepool,
// turning this into an InMemoryResponse
func (r *ClosableResponse) Detach() Response {
	defer r.B.Close()
	clone := &InMemoryResponse{
		S: r.S,
		H: r.H,
	}
	clone.B = make([]byte, r.B.Len())
	copy(clone.B, r.B.Bytes())
	return clone
}
<|endoftext|>