<commit_before>package daemon

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"syscall"
)

// Mark of daemon process - system environment variable _GO_DAEMON=1
const (
	MARK_NAME  = "_GO_DAEMON"
	MARK_VALUE = "1"
)

// Default file permissions for log and pid files.
const FILE_PERM = os.FileMode(0640)

// A Context describes daemon context.
type Context struct {
	// If PidFileName is non-empty, parent process will try to create and lock
	// pid file with given name. Child process writes process id to file.
	PidFileName string
	// Permissions for new pid file.
	PidFilePerm os.FileMode

	// If LogFileName is non-empty, parent process will create file with given name
	// and will link to fd 2 (stderr) for child process.
	LogFileName string
	// Permissions for new log file.
	LogFilePerm os.FileMode

	// If WorkDir is non-empty, the child changes into the directory before
	// creating the process.
	WorkDir string
	// If Chroot is non-empty, the child changes root directory
	Chroot string

	// If Env is non-nil, it gives the environment variables for the
	// daemon-process in the form returned by os.Environ.
	// If it is nil, the result of os.Environ will be used.
	Env []string
	// If Args is non-nil, it gives the command-line args for the
	// daemon-process. If it is nil, the result of os.Args will be used
	// (without program name).
	Args []string

	// Credential holds user and group identities to be assumed by a daemon-process.
	Credential *syscall.Credential
	// If Umask is non-zero, the daemon-process call Umask() func with given value.
	Umask int

	// Struct contains only serializable public fields (!!!)
	abspath  string
	pidFile  *LockFile
	logFile  *os.File
	nullFile *os.File

	rpipe, wpipe *os.File
}

// Reborn runs second copy of current process in the given context.
// function executes separate parts of code in child process and parent process
// and provides demonization of child process. It look similar as the
// fork-daemonization, but goroutine-safe.
// In success returns *os.Process in parent process and nil in child process.
// Otherwise returns error.
func (d *Context) Reborn() (child *os.Process, err error) {
	if !WasReborn() {
		child, err = d.parent()
	} else {
		err = d.child()
	}
	return
}

// Search search daemons process by given in context pid file name.
// If success returns pointer on daemons os.Process structure,
// else returns error. Returns nil if filename is empty.
func (d *Context) Search() (daemon *os.Process, err error) {
	if len(d.PidFileName) > 0 {
		var pid int
		if pid, err = ReadPidFile(d.PidFileName); err != nil {
			return
		}
		daemon, err = os.FindProcess(pid)
	}
	return
}

// WasReborn returns true in child process (daemon) and false in parent process.
func WasReborn() bool {
	return os.Getenv(MARK_NAME) == MARK_VALUE
}

func (d *Context) parent() (child *os.Process, err error) {
	if err = d.prepareEnv(); err != nil {
		return
	}

	defer d.closeFiles()
	if err = d.openFiles(); err != nil {
		return
	}

	attr := &os.ProcAttr{
		Dir:   d.WorkDir,
		Env:   d.Env,
		Files: d.files(),
		Sys: &syscall.SysProcAttr{
			Setsid:     true,
		},
	}

	if child, err = os.StartProcess(d.abspath, d.Args, attr); err != nil {
		if d.pidFile != nil {
			d.pidFile.Remove()
		}
		return
	}

	d.rpipe.Close()
	encoder := json.NewEncoder(d.wpipe)
	err = encoder.Encode(d)

	return
}

func (d *Context) openFiles() (err error) {
	if d.PidFilePerm == 0 {
		d.PidFilePerm = FILE_PERM
	}
	if d.LogFilePerm == 0 {
		d.LogFilePerm = FILE_PERM
	}

	if d.nullFile, err = os.Open(os.DevNull); err != nil {
		return
	}

	if len(d.PidFileName) > 0 {
		if d.pidFile, err = OpenLockFile(d.PidFileName, d.PidFilePerm); err != nil {
			return
		}
		if err = d.pidFile.Lock(); err != nil {
			return
		}
	}

	if len(d.LogFileName) > 0 {
		if d.logFile, err = os.OpenFile(d.LogFileName,
			os.O_WRONLY|os.O_CREATE|os.O_APPEND, d.LogFilePerm); err != nil {
			return
		}
	}

	d.rpipe, d.wpipe, err = os.Pipe()
	return
}

func (d *Context) closeFiles() (err error) {
	cl := func(file **os.File) {
		if *file != nil {
			(*file).Close()
			*file = nil
		}
	}
	cl(&d.rpipe)
	cl(&d.wpipe)
	cl(&d.logFile)
	cl(&d.nullFile)
	if d.pidFile != nil {
		d.pidFile.Close()
		d.pidFile = nil
	}
	return
}

func (d *Context) prepareEnv() (err error) {
	if d.abspath, err = filepath.Abs(os.Args[0]); err != nil {
		return
	}

	if len(d.Args) == 0 {
		d.Args = os.Args
	}

	mark := fmt.Sprintf("%s=%s", MARK_NAME, MARK_VALUE)
	if len(d.Env) == 0 {
		d.Env = os.Environ()
	}
	d.Env = append(d.Env, mark)

	return
}

func (d *Context) files() (f []*os.File) {
	log := d.nullFile
	if d.logFile != nil {
		log = d.logFile
	}

	f = []*os.File{
		d.rpipe,    // (0) stdin
		log,        // (1) stdout
		log,        // (2) stderr
		d.nullFile, // (3) dup on fd 0 after initialization
	}

	if d.pidFile != nil {
		f = append(f, d.pidFile.File) // (4) pid file
	}
	return
}

var initialized = false

func (d *Context) child() (err error) {
	if initialized {
		return os.ErrInvalid
	}
	initialized = true

	decoder := json.NewDecoder(os.Stdin)
	if err = decoder.Decode(d); err != nil {
		return
	}

	if err = syscall.Close(0); err != nil {
		return
	}
	if err = syscall.Dup2(3, 0); err != nil {
		return
	}

	if len(d.PidFileName) > 0 {
		d.pidFile = NewLockFile(os.NewFile(4, d.PidFileName))
		if err = d.pidFile.WritePid(); err != nil {
			return
		}
	}

	if d.Umask != 0 {
		syscall.Umask(int(d.Umask))
	}
	if len(d.Chroot) > 0 {
		err = syscall.Chroot(d.Chroot)
	}
	if d.Credential.Gid > 0 {
		if err = syscall.Setgid(int(d.Credential.Gid)); err != nil {
			return
		}
	}
	if d.Credential.Uid > 0 {
		if err = syscall.Setuid(int(d.Credential.Uid)); err != nil {
			return
		}
	}

	return
}

// Release provides correct pid-file release in daemon.
func (d *Context) Release() (err error) {
	if !initialized {
		return
	}
	if d.pidFile != nil {
		err = d.pidFile.Remove()
	}
	return
}
<commit_msg>quickfix for https://github.com/sevlyar/go-daemon/issues/4<commit_after>package daemon

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"syscall"
)

// Mark of daemon process - system environment variable _GO_DAEMON=1
const (
	MARK_NAME  = "_GO_DAEMON"
	MARK_VALUE = "1"
)

// Default file permissions for log and pid files.
const FILE_PERM = os.FileMode(0640)

// A Context describes daemon context.
type Context struct {
	// If PidFileName is non-empty, parent process will try to create and lock
	// pid file with given name. Child process writes process id to file.
	PidFileName string
	// Permissions for new pid file.
	PidFilePerm os.FileMode

	// If LogFileName is non-empty, parent process will create file with given name
	// and will link to fd 2 (stderr) for child process.
	LogFileName string
	// Permissions for new log file.
	LogFilePerm os.FileMode

	// If WorkDir is non-empty, the child changes into the directory before
	// creating the process.
	WorkDir string
	// If Chroot is non-empty, the child changes root directory
	Chroot string

	// If Env is non-nil, it gives the environment variables for the
	// daemon-process in the form returned by os.Environ.
	// If it is nil, the result of os.Environ will be used.
	Env []string
	// If Args is non-nil, it gives the command-line args for the
	// daemon-process. If it is nil, the result of os.Args will be used
	// (without program name).
	Args []string

	// Credential holds user and group identities to be assumed by a daemon-process.
	Credential *syscall.Credential
	// If Umask is non-zero, the daemon-process call Umask() func with given value.
	Umask int

	// Struct contains only serializable public fields (!!!)
	abspath  string
	pidFile  *LockFile
	logFile  *os.File
	nullFile *os.File

	rpipe, wpipe *os.File
}

// Reborn runs second copy of current process in the given context.
// function executes separate parts of code in child process and parent process
// and provides demonization of child process. It look similar as the
// fork-daemonization, but goroutine-safe.
// In success returns *os.Process in parent process and nil in child process.
// Otherwise returns error.
func (d *Context) Reborn() (child *os.Process, err error) {
	if !WasReborn() {
		child, err = d.parent()
	} else {
		err = d.child()
	}
	return
}

// Search search daemons process by given in context pid file name.
// If success returns pointer on daemons os.Process structure,
// else returns error. Returns nil if filename is empty.
func (d *Context) Search() (daemon *os.Process, err error) {
	if len(d.PidFileName) > 0 {
		var pid int
		if pid, err = ReadPidFile(d.PidFileName); err != nil {
			return
		}
		daemon, err = os.FindProcess(pid)
	}
	return
}

// WasReborn returns true in child process (daemon) and false in parent process.
func WasReborn() bool {
	return os.Getenv(MARK_NAME) == MARK_VALUE
}

func (d *Context) parent() (child *os.Process, err error) {
	if err = d.prepareEnv(); err != nil {
		return
	}

	defer d.closeFiles()
	if err = d.openFiles(); err != nil {
		return
	}

	attr := &os.ProcAttr{
		Dir:   d.WorkDir,
		Env:   d.Env,
		Files: d.files(),
		Sys: &syscall.SysProcAttr{
			Setsid:     true,
		},
	}

	if child, err = os.StartProcess(d.abspath, d.Args, attr); err != nil {
		if d.pidFile != nil {
			d.pidFile.Remove()
		}
		return
	}

	d.rpipe.Close()
	encoder := json.NewEncoder(d.wpipe)
	err = encoder.Encode(d)

	return
}

func (d *Context) openFiles() (err error) {
	if d.PidFilePerm == 0 {
		d.PidFilePerm = FILE_PERM
	}
	if d.LogFilePerm == 0 {
		d.LogFilePerm = FILE_PERM
	}

	if d.nullFile, err = os.Open(os.DevNull); err != nil {
		return
	}

	if len(d.PidFileName) > 0 {
		if d.pidFile, err = OpenLockFile(d.PidFileName, d.PidFilePerm); err != nil {
			return
		}
		if err = d.pidFile.Lock(); err != nil {
			return
		}
	}

	if len(d.LogFileName) > 0 {
		if d.logFile, err = os.OpenFile(d.LogFileName,
			os.O_WRONLY|os.O_CREATE|os.O_APPEND, d.LogFilePerm); err != nil {
			return
		}
	}

	d.rpipe, d.wpipe, err = os.Pipe()
	return
}

func (d *Context) closeFiles() (err error) {
	cl := func(file **os.File) {
		if *file != nil {
			(*file).Close()
			*file = nil
		}
	}
	cl(&d.rpipe)
	cl(&d.wpipe)
	cl(&d.logFile)
	cl(&d.nullFile)
	if d.pidFile != nil {
		d.pidFile.Close()
		d.pidFile = nil
	}
	return
}

func (d *Context) prepareEnv() (err error) {
	if d.abspath, err = filepath.Abs(os.Args[0]); err != nil {
		return
	}

	if len(d.Args) == 0 {
		d.Args = os.Args
	}

	mark := fmt.Sprintf("%s=%s", MARK_NAME, MARK_VALUE)
	if len(d.Env) == 0 {
		d.Env = os.Environ()
	}
	d.Env = append(d.Env, mark)

	return
}

func (d *Context) files() (f []*os.File) {
	log := d.nullFile
	if d.logFile != nil {
		log = d.logFile
	}

	f = []*os.File{
		d.rpipe,    // (0) stdin
		log,        // (1) stdout
		log,        // (2) stderr
		d.nullFile, // (3) dup on fd 0 after initialization
	}

	if d.pidFile != nil {
		f = append(f, d.pidFile.File) // (4) pid file
	}
	return
}

var initialized = false

func (d *Context) child() (err error) {
	if initialized {
		return os.ErrInvalid
	}
	initialized = true

	decoder := json.NewDecoder(os.Stdin)
	if err = decoder.Decode(d); err != nil {
		return
	}

	if err = syscall.Close(0); err != nil {
		return
	}
	if err = syscall.Dup2(3, 0); err != nil {
		return
	}

	if len(d.PidFileName) > 0 {
		d.pidFile = NewLockFile(os.NewFile(4, d.PidFileName))
		if err = d.pidFile.WritePid(); err != nil {
			return
		}
	}

	if d.Umask != 0 {
		syscall.Umask(int(d.Umask))
	}
	if len(d.Chroot) > 0 {
		err = syscall.Chroot(d.Chroot)
	}
	if d.Credential != nil {
		if d.Credential.Gid > 0 {
			if err = syscall.Setgid(int(d.Credential.Gid)); err != nil {
				return
			}
		}
		if d.Credential.Uid > 0 {
			if err = syscall.Setuid(int(d.Credential.Uid)); err != nil {
				return
			}
		}
	}

	return
}

// Release provides correct pid-file release in daemon.
func (d *Context) Release() (err error) {
	if !initialized {
		return
	}
	if d.pidFile != nil {
		err = d.pidFile.Remove()
	}
	return
}
<|endoftext|>


<commit_before>package web

import (
	"fmt"
	"log"
	"net/http"
	"sync"
)

type mLayer struct {
	fn   func(*C, http.Handler) http.Handler
	orig interface{}
}

type mStack struct {
	lock   sync.Mutex
	stack  []mLayer
	pool   *cPool
	router internalRouter
}

type internalRouter interface {
	route(*C, http.ResponseWriter, *http.Request)
}

/*
Constructing a middleware stack involves a lot of allocations: at the very least
each layer will have to close over the layer after (inside) it, and perhaps a
context object. Instead of doing this on every request, let's cache fully
assembled middleware stacks (the "c" stands for "cached").

A lot of the complexity here (in particular the "pool" parameter, and the
behavior of release() and invalidate() below) is due to the fact that when the
middleware stack is mutated we need to create a "cache barrier," where no
cStack created before the middleware stack mutation is returned to the active
cache pool (and is therefore eligible for subsequent reuse). The way we do this
is a bit ugly: each cStack maintains a pointer to the pool it originally came
from, and will only return itself to that pool. If the mStack's pool has been
rotated since then (meaning that this cStack is invalid), it will either try
(and likely fail) to insert itself into the stale pool, or it will drop the
cStack on the floor.
*/
type cStack struct {
	C
	m    http.Handler
	pool *cPool
}

func (s *cStack) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.C = C{}
	s.m.ServeHTTP(w, r)
}
func (s *cStack) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
	s.C = c
	s.m.ServeHTTP(w, r)
}

func (m *mStack) appendLayer(fn interface{}) {
	ml := mLayer{orig: fn}
	switch fn.(type) {
	case func(http.Handler) http.Handler:
		unwrapped := fn.(func(http.Handler) http.Handler)
		ml.fn = func(c *C, h http.Handler) http.Handler {
			return unwrapped(h)
		}
	case func(*C, http.Handler) http.Handler:
		ml.fn = fn.(func(*C, http.Handler) http.Handler)
	default:
		log.Fatalf(`Unknown middleware type %v. Expected a function `+
			`with signature "func(http.Handler) http.Handler" or `+
			`"func(*web.C, http.Handler) http.Handler".`, fn)
	}
	m.stack = append(m.stack, ml)
}

func (m *mStack) findLayer(l interface{}) int {
	for i, middleware := range m.stack {
		if funcEqual(l, middleware.orig) {
			return i
		}
	}
	return -1
}

func (m *mStack) invalidate() {
	m.pool = makeCPool()
}

func (m *mStack) newStack() *cStack {
	m.lock.Lock()
	defer m.lock.Unlock()

	cs := cStack{}
	router := m.router

	cs.m = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		router.route(&cs.C, w, r)
	})
	for i := len(m.stack) - 1; i >= 0; i-- {
		cs.m = m.stack[i].fn(&cs.C, cs.m)
	}

	return &cs
}

func (m *mStack) alloc() *cStack {
	// This is a little sloppy: this is only safe if this pointer
	// dereference is atomic. Maybe someday I'll replace it with
	// sync/atomic, but for now I happen to know that on all the
	// architectures I care about it happens to be atomic.
	p := m.pool
	cs := p.alloc()
	if cs == nil {
		cs = m.newStack()
	}

	cs.pool = p
	return cs
}

func (m *mStack) release(cs *cStack) {
	cs.C = C{}
	if cs.pool != m.pool {
		return
	}
	cs.pool.release(cs)
	cs.pool = nil
}

// Append the given middleware to the middleware stack. See the documentation
// for type Mux for a list of valid middleware types.
//
// No attempt is made to enforce the uniqueness of middlewares.
func (m *mStack) Use(middleware interface{}) {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.appendLayer(middleware)
	m.invalidate()
}

// Insert the given middleware immediately before a given existing middleware in
// the stack. See the documentation for type Mux for a list of valid middleware
// types. Returns an error if no middleware has the name given by "before."
//
// No attempt is made to enforce the uniqueness of middlewares. If the insertion
// point is ambiguous, the first (outermost) one is chosen.
func (m *mStack) Insert(middleware, before interface{}) error {
	m.lock.Lock()
	defer m.lock.Unlock()
	i := m.findLayer(before)
	if i < 0 {
		return fmt.Errorf("web: unknown middleware %v", before)
	}

	m.appendLayer(middleware)
	inserted := m.stack[len(m.stack)-1]
	copy(m.stack[i+1:], m.stack[i:])
	m.stack[i] = inserted

	m.invalidate()
	return nil
}

// Remove the given middleware from the middleware stack. Returns an error if
// no such middleware can be found.
//
// If the name of the middleware to delete is ambiguous, the first (outermost)
// one is chosen.
func (m *mStack) Abandon(middleware interface{}) error {
	m.lock.Lock()
	defer m.lock.Unlock()
	i := m.findLayer(middleware)
	if i < 0 {
		return fmt.Errorf("web: unknown middleware %v", middleware)
	}

	copy(m.stack[i:], m.stack[i+1:])
	m.stack = m.stack[:len(m.stack)-1 : len(m.stack)]

	m.invalidate()
	return nil
}
<commit_msg>Eliminate a lock in the request hot path<commit_after>package web

import (
	"fmt"
	"log"
	"net/http"
	"sync"
)

type mLayer struct {
	fn   func(*C, http.Handler) http.Handler
	orig interface{}
}

type mStack struct {
	lock   sync.Mutex
	stack  []mLayer
	pool   *cPool
	router internalRouter
}

type internalRouter interface {
	route(*C, http.ResponseWriter, *http.Request)
}

/*
Constructing a middleware stack involves a lot of allocations: at the very least
each layer will have to close over the layer after (inside) it, and perhaps a
context object. Instead of doing this on every request, let's cache fully
assembled middleware stacks (the "c" stands for "cached").

A lot of the complexity here (in particular the "pool" parameter, and the
behavior of release() and invalidate() below) is due to the fact that when the
middleware stack is mutated we need to create a "cache barrier," where no
cStack created before the middleware stack mutation is returned to the active
cache pool (and is therefore eligible for subsequent reuse). The way we do this
is a bit ugly: each cStack maintains a pointer to the pool it originally came
from, and will only return itself to that pool. If the mStack's pool has been
rotated since then (meaning that this cStack is invalid), it will either try
(and likely fail) to insert itself into the stale pool, or it will drop the
cStack on the floor.
*/
type cStack struct {
	C
	m    http.Handler
	pool *cPool
}

func (s *cStack) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.C = C{}
	s.m.ServeHTTP(w, r)
}
func (s *cStack) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
	s.C = c
	s.m.ServeHTTP(w, r)
}

func (m *mStack) appendLayer(fn interface{}) {
	ml := mLayer{orig: fn}
	switch fn.(type) {
	case func(http.Handler) http.Handler:
		unwrapped := fn.(func(http.Handler) http.Handler)
		ml.fn = func(c *C, h http.Handler) http.Handler {
			return unwrapped(h)
		}
	case func(*C, http.Handler) http.Handler:
		ml.fn = fn.(func(*C, http.Handler) http.Handler)
	default:
		log.Fatalf(`Unknown middleware type %v. Expected a function `+
			`with signature "func(http.Handler) http.Handler" or `+
			`"func(*web.C, http.Handler) http.Handler".`, fn)
	}
	m.stack = append(m.stack, ml)
}

func (m *mStack) findLayer(l interface{}) int {
	for i, middleware := range m.stack {
		if funcEqual(l, middleware.orig) {
			return i
		}
	}
	return -1
}

func (m *mStack) invalidate() {
	m.pool = makeCPool()
}

func (m *mStack) newStack() *cStack {
	cs := cStack{}
	router := m.router

	cs.m = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		router.route(&cs.C, w, r)
	})
	for i := len(m.stack) - 1; i >= 0; i-- {
		cs.m = m.stack[i].fn(&cs.C, cs.m)
	}

	return &cs
}

func (m *mStack) alloc() *cStack {
	p := m.pool
	cs := p.alloc()
	if cs == nil {
		cs = m.newStack()
	}

	cs.pool = p
	return cs
}

func (m *mStack) release(cs *cStack) {
	cs.C = C{}
	if cs.pool != m.pool {
		return
	}
	cs.pool.release(cs)
	cs.pool = nil
}

// Append the given middleware to the middleware stack. See the documentation
// for type Mux for a list of valid middleware types.
//
// No attempt is made to enforce the uniqueness of middlewares. It is illegal to
// call this function concurrently with active requests.
func (m *mStack) Use(middleware interface{}) {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.appendLayer(middleware)
	m.invalidate()
}

// Insert the given middleware immediately before a given existing middleware in
// the stack. See the documentation for type Mux for a list of valid middleware
// types. Returns an error if no middleware has the name given by "before."
//
// No attempt is made to enforce the uniqueness of middlewares. If the insertion
// point is ambiguous, the first (outermost) one is chosen. It is illegal to
// call this function concurrently with active requests.
func (m *mStack) Insert(middleware, before interface{}) error {
	m.lock.Lock()
	defer m.lock.Unlock()
	i := m.findLayer(before)
	if i < 0 {
		return fmt.Errorf("web: unknown middleware %v", before)
	}

	m.appendLayer(middleware)
	inserted := m.stack[len(m.stack)-1]
	copy(m.stack[i+1:], m.stack[i:])
	m.stack[i] = inserted

	m.invalidate()
	return nil
}

// Remove the given middleware from the middleware stack. Returns an error if
// no such middleware can be found.
//
// If the name of the middleware to delete is ambiguous, the first (outermost)
// one is chosen. It is illegal to call this function concurrently with active
// requests.
func (m *mStack) Abandon(middleware interface{}) error {
	m.lock.Lock()
	defer m.lock.Unlock()
	i := m.findLayer(middleware)
	if i < 0 {
		return fmt.Errorf("web: unknown middleware %v", middleware)
	}

	copy(m.stack[i:], m.stack[i+1:])
	m.stack = m.stack[:len(m.stack)-1 : len(m.stack)]

	m.invalidate()
	return nil
}
<|endoftext|>


<commit_before>package data

import (
	"errors"
	"fmt"
	"reflect"
)

// ReadWire parses types received via the peer network
func ReadWire(r Reader, typ NodeType, ledgerSequence uint32, nodeId Hash256) (Hashable, error) {
	version, err := readHashPrefix(r)
	if err != nil {
		return nil, err
	}
	switch version {
	case HP_LEAF_NODE:
		return ReadLedgerEntry(r, nodeId)
	case HP_TRANSACTION_NODE:
		return readTransactionWithMetadata(r, ledgerSequence, nodeId)
	case HP_INNER_NODE:
		return readCompressedInnerNode(r, typ, nodeId)
	default:
		return nil, fmt.Errorf("Unknown hash prefix: %s", version.String())
	}
}

// ReadPrefix parses types received from the nodestore
func ReadPrefix(r Reader, nodeId Hash256) (Storer, error) {
	header, err := readHeader(r)
	if err != nil {
		return nil, err
	}
	version, err := readHashPrefix(r)
	if err != nil {
		return nil, err
	}
	switch {
	case version == HP_INNER_NODE:
		return readInnerNode(r, header.NodeType, nodeId)
	case header.NodeType == NT_LEDGER:
		return ReadLedger(r, nodeId)
	case header.NodeType == NT_TRANSACTION_NODE:
		return readTransactionWithMetadata(r, header.LedgerSequence, nodeId)
	case header.NodeType == NT_ACCOUNT_NODE:
		return ReadLedgerEntry(r, nodeId)
	default:
		return nil, fmt.Errorf("Unknown node type")
	}
}

func ReadLedger(r Reader, nodeId Hash256) (*Ledger, error) {
	ledger := new(Ledger)
	if err := read(r, &ledger.LedgerHeader); err != nil {
		return nil, err
	}
	ledger.Hash = nodeId
	return ledger, nil
}

func ReadValidation(r Reader) (*Validation, error) {
	validation := new(Validation)
	v := reflect.ValueOf(validation)
	if err := readObject(r, &v); err != nil {
		return nil, err
	}
	return validation, nil
}

func ReadTransaction(r Reader) (Transaction, error) {
	txType, err := expectType(r, "TransactionType")
	if err != nil {
		return nil, err
	}
	tx := TxFactory[txType]()
	v := reflect.ValueOf(tx)
	if err := readObject(r, &v); err != nil {
		return nil, err
	}
	return tx, nil
}

// ReadTransactionAndMetadata combines the inputs from the two
// readers into a TransactionWithMetaData
func ReadTransactionAndMetadata(tx, meta Reader, hash Hash256, ledger uint32) (*TransactionWithMetaData, error) {
	t, err := ReadTransaction(tx)
	if err != nil {
		return nil, err
	}
	txm := &TransactionWithMetaData{
		Transaction:    t,
		LedgerSequence: ledger,
	}
	m := reflect.ValueOf(&txm.MetaData)
	if err := readObject(meta, &m); err != nil {
		return nil, err
	}
	*txm.GetHash() = hash
	if txm.Id, err = NodeId(txm); err != nil {
		return nil, err
	}
	return txm, nil
}

// For internal use when reading Prefix format
func readTransactionWithMetadata(r Reader, ledger uint32, nodeId Hash256) (*TransactionWithMetaData, error) {
	br, err := NewVariableByteReader(r)
	if err != nil {
		return nil, err
	}
	tx, err := ReadTransaction(br)
	if err != nil {
		return nil, err
	}
	txm := &TransactionWithMetaData{
		Transaction:    tx,
		LedgerSequence: ledger,
		Id:             nodeId,
	}
	br, err = NewVariableByteReader(r)
	if err != nil {
		return nil, err
	}
	meta := reflect.ValueOf(&txm.MetaData)
	if err := readObject(br, &meta); err != nil {
		return nil, err
	}
	hash, err := readHash(r)
	if err != nil {
		return nil, err
	}
	copy(txm.GetHash()[:], hash.Bytes())
	return txm, nil
}

func readInnerNode(r Reader, typ NodeType, nodeId Hash256) (*InnerNode, error) {
	var inner InnerNode
	inner.Type = typ
	for i := range inner.Children {
		if _, err := r.Read(inner.Children[i][:]); err != nil {
			return nil, err
		}
	}
	copy(inner.Id[:], nodeId.Bytes())
	return &inner, nil
}

func readCompressedInnerNode(r Reader, typ NodeType, nodeId Hash256) (*InnerNode, error) {
	var inner InnerNode
	inner.Type = typ
	var entry CompressedNodeEntry
	for read(r, &entry) == nil {
		inner.Children[entry.Pos] = entry.Hash
	}
	copy(inner.Id[:], nodeId.Bytes())
	return &inner, nil
}

func ReadLedgerEntry(r Reader, nodeId Hash256) (LedgerEntry, error) {
	leType, err := expectType(r, "LedgerEntryType")
	if err != nil {
		return nil, err
	}
	factory, ok := LedgerEntryFactory[leType]
	if !ok {
		return nil, fmt.Errorf("unknown ledger entry: %d", leType)
	}
	le := factory()
	v := reflect.ValueOf(le)
	// LedgerEntries have 32 bytes of index suffixed
	// but don't have a variable bytes indicator
	lr := LimitedByteReader(r, int64(r.Len()-32))
	if err := readObject(lr, &v); err != nil {
		return nil, err
	}
	hash, err := readHash(r)
	if err != nil {
		return nil, err
	}
	copy(le.GetHash()[:], hash.Bytes())
	copy(le.NodeId()[:], nodeId.Bytes())
	return le, nil
}

func readHashPrefix(r Reader) (HashPrefix, error) {
	var version HashPrefix
	return version, read(r, &version)
}

func readHeader(r Reader) (*NodeHeader, error) {
	header := new(NodeHeader)
	return header, read(r, header)
}

func readHash(r Reader) (*Hash256, error) {
	var h Hash256
	n, err := r.Read(h[:])
	switch {
	case err != nil:
		return nil, err
	case n != len(h):
		return nil, fmt.Errorf("Bad hash")
	default:
		return &h, nil
	}
}

func expectType(r Reader, expected string) (uint16, error) {
	enc, err := readEncoding(r)
	if err != nil {
		return 0, err
	}
	name := encodings[*enc]
	if name != expected {
		return 0, fmt.Errorf("Unexpected type: %s expected: %s", name, expected)
	}
	var typ uint16
	return typ, read(r, &typ)
}

var (
	errorEndOfObject = errors.New("EndOfObject")
	errorEndOfArray  = errors.New("EndOfArray")
)

func readObject(r Reader, v *reflect.Value) error {
	var err error
	for enc, err := readEncoding(r); err == nil; enc, err = readEncoding(r) {
		name := encodings[*enc]
		// fmt.Println(name, v, v.IsValid(), enc.typ, enc.field)
		switch enc.typ {
		case ST_ARRAY:
			if name == "EndOfArray" {
				return errorEndOfArray
			}
			array := getField(v, enc)
		loop:
			for {
				child := reflect.New(array.Type().Elem()).Elem()
				err := readObject(r, &child)
				switch err {
				case errorEndOfArray:
					break loop
				case errorEndOfObject:
					array.Set(reflect.Append(*array, child))
				default:
					return err
				}
			}
		case ST_OBJECT:
			switch name {
			case "EndOfObject":
				return errorEndOfObject
			case "PreviousFields", "NewFields", "FinalFields":
				leType := LedgerEntryType(v.Elem().FieldByName("LedgerEntryType").Uint())
				le := LedgerEntryFactory[leType]()
				fields := reflect.ValueOf(le)
				v.Elem().FieldByName(name).Set(fields)
				if err := readObject(r, &fields); err != nil && err != errorEndOfObject {
					return err
				}
				// var fields Fields
				// f := reflect.ValueOf(&fields)
				// v.Elem().FieldByName(name).Set(f)
				// if readObject(r, &f); err != nil && err != errorEndOfObject {
				// 	return err
				// }
			case "ModifiedNode", "DeletedNode", "CreatedNode":
				var node AffectedNode
				n := reflect.ValueOf(&node)
				var effect NodeEffect
				e := reflect.ValueOf(&effect)
				e.Elem().FieldByName(name).Set(n)
				v.Set(e.Elem())
				return readObject(r, &n)
			case "SignerEntry":
				var signerEntry SignerEntry
				s := reflect.ValueOf(&signerEntry)
				err := readObject(r, &s)
				v.Set(s.Elem())
				return err
			case "Majority":
				var majority Majority
				m := reflect.ValueOf(&majority)
				err := readObject(r, &m)
				v.Set(m.Elem())
				return err
			case "Memo":
				var memo Memo
				m := reflect.ValueOf(&memo)
				inner := reflect.ValueOf(&memo.Memo)
				err := readObject(r, &inner)
				v.Set(m.Elem())
				return err
			default:
				return fmt.Errorf("Unexpected object: %s for field: %s", v.Type(), name)
			}
		default:
			if v.Kind() == reflect.Struct {
				return fmt.Errorf("Unexpected struct: %s for field: %s", v.Type(), name)
			}
			field := getField(v, enc)
			if !field.CanAddr() {
				return fmt.Errorf("Missing field: %s %+v", name, enc)
			}
			switch v := field.Addr().Interface().(type) {
			case Wire:
				if err := v.Unmarshal(r); err != nil {
					return err
				}
			default:
				if err := read(r, v); err != nil {
					return err
				}
			}
		}
	}
	return err
}

func getField(v *reflect.Value, e *enc) *reflect.Value {
	name := encodings[*e]
	field := v.Elem().FieldByName(name)
	if field.Kind() == reflect.Ptr {
		field.Set(reflect.New(field.Type().Elem()))
		field = field.Elem()
	}
	return &field
}
<commit_msg>Revert decoder.go<commit_after>package data

import (
	"errors"
	"fmt"
	"reflect"
)

// ReadWire parses types received via the peer network
func ReadWire(r Reader, typ NodeType, ledgerSequence uint32, nodeId Hash256) (Hashable, error) {
	version, err := readHashPrefix(r)
	if err != nil {
		return nil, err
	}
	switch version {
	case HP_LEAF_NODE:
		return ReadLedgerEntry(r, nodeId)
	case HP_TRANSACTION_NODE:
		return readTransactionWithMetadata(r, ledgerSequence, nodeId)
	case HP_INNER_NODE:
		return readCompressedInnerNode(r, typ, nodeId)
	default:
		return nil, fmt.Errorf("Unknown hash prefix: %s", version.String())
	}
}

// ReadPrefix parses types received from the nodestore
func ReadPrefix(r Reader, nodeId Hash256) (Storer, error) {
	header, err := readHeader(r)
	if err != nil {
		return nil, err
	}
	version, err := readHashPrefix(r)
	if err != nil {
		return nil, err
	}
	switch {
	case version == HP_INNER_NODE:
		return readInnerNode(r, header.NodeType, nodeId)
	case header.NodeType == NT_LEDGER:
		return ReadLedger(r, nodeId)
	case header.NodeType == NT_TRANSACTION_NODE:
		return readTransactionWithMetadata(r, header.LedgerSequence, nodeId)
	case header.NodeType == NT_ACCOUNT_NODE:
		return ReadLedgerEntry(r, nodeId)
	default:
		return nil, fmt.Errorf("Unknown node type")
	}
}

func ReadLedger(r Reader, nodeId Hash256) (*Ledger, error) {
	ledger := new(Ledger)
	if err := read(r, &ledger.LedgerHeader); err != nil {
		return nil, err
	}
	ledger.Hash = nodeId
	return ledger, nil
}

func ReadValidation(r Reader) (*Validation, error) {
	validation := new(Validation)
	v := reflect.ValueOf(validation)
	if err := readObject(r, &v); err != nil {
		return nil, err
	}
	return validation, nil
}

func ReadTransaction(r Reader) (Transaction, error) {
	txType, err := expectType(r, "TransactionType")
	if err != nil {
		return nil, err
	}
	tx := TxFactory[txType]()
	v := reflect.ValueOf(tx)
	if err := readObject(r, &v); err != nil {
		return nil, err
	}
	return tx, nil
}

// ReadTransactionAndMetadata combines the inputs from the two
// readers into a TransactionWithMetaData
func ReadTransactionAndMetadata(tx, meta Reader, hash Hash256, ledger uint32) (*TransactionWithMetaData, error) {
	t, err := ReadTransaction(tx)
	if err != nil {
		return nil, err
	}
	txm := &TransactionWithMetaData{
		Transaction:    t,
		LedgerSequence: ledger,
	}
	m := reflect.ValueOf(&txm.MetaData)
	if err := readObject(meta, &m); err != nil {
		return nil, err
	}
	*txm.GetHash() = hash
	if txm.Id, err = NodeId(txm); err != nil {
		return nil, err
	}
	return txm, nil
}

// For internal use when reading Prefix format
func readTransactionWithMetadata(r Reader, ledger uint32, nodeId Hash256) (*TransactionWithMetaData, error) {
	br, err := NewVariableByteReader(r)
	if err != nil {
		return nil, err
	}
	tx, err := ReadTransaction(br)
	if err != nil {
		return nil, err
	}
	txm := &TransactionWithMetaData{
		Transaction:    tx,
		LedgerSequence: ledger,
		Id:             nodeId,
	}
	br, err = NewVariableByteReader(r)
	if err != nil {
		return nil, err
	}
	meta := reflect.ValueOf(&txm.MetaData)
	if err := readObject(br, &meta); err != nil {
		return nil, err
	}
	hash, err := readHash(r)
	if err != nil {
		return nil, err
	}
	copy(txm.GetHash()[:], hash.Bytes())
	return txm, nil
}

func readInnerNode(r Reader, typ NodeType, nodeId Hash256) (*InnerNode, error) {
	var inner InnerNode
	inner.Type = typ
	for i := range inner.Children {
		if _, err := r.Read(inner.Children[i][:]); err != nil {
			return nil, err
		}
	}
	copy(inner.Id[:], nodeId.Bytes())
	return &inner, nil
}

func readCompressedInnerNode(r Reader, typ NodeType, nodeId Hash256) (*InnerNode, error) {
	var inner InnerNode
	inner.Type = typ
	var entry CompressedNodeEntry
	for read(r, &entry) == nil {
		inner.Children[entry.Pos] = entry.Hash
	}
	copy(inner.Id[:], nodeId.Bytes())
	return &inner, nil
}

func ReadLedgerEntry(r Reader, nodeId Hash256) (LedgerEntry, error) {
	leType, err := expectType(r, "LedgerEntryType")
	if err != nil {
		return nil, err
	}
	le := LedgerEntryFactory[leType]()
	v := reflect.ValueOf(le)
	// LedgerEntries have 32 bytes of index suffixed
	// but don't have a variable bytes indicator
	lr := LimitedByteReader(r, int64(r.Len()-32))
	if err := readObject(lr, &v); err != nil {
		return nil, err
	}
	hash, err := readHash(r)
	if err != nil {
		return nil, err
	}
	copy(le.GetHash()[:], hash.Bytes())
	copy(le.NodeId()[:], nodeId.Bytes())
	return le, nil
}

func readHashPrefix(r Reader) (HashPrefix, error) {
	var version HashPrefix
	return version, read(r, &version)
}

func readHeader(r Reader) (*NodeHeader, error) {
	header := new(NodeHeader)
	return header, read(r, header)
}

func readHash(r Reader) (*Hash256, error) {
	var h Hash256
	n, err := r.Read(h[:])
	switch {
	case err != nil:
		return nil, err
	case n != len(h):
		return nil, fmt.Errorf("Bad hash")
	default:
		return &h, nil
	}
}

func expectType(r Reader, expected string) (uint16, error) {
	enc, err := readEncoding(r)
	if err != nil {
		return 0, err
	}
	name := encodings[*enc]
	if name != expected {
		return 0, fmt.Errorf("Unexpected type: %s expected: %s", name, expected)
	}
	var typ uint16
	return typ, read(r, &typ)
}

var (
	errorEndOfObject = errors.New("EndOfObject")
	errorEndOfArray  = errors.New("EndOfArray")
)

func readObject(r Reader, v *reflect.Value) error {
	var err error
	for enc, err := readEncoding(r); err == nil; enc, err = readEncoding(r) {
		name := encodings[*enc]
		// fmt.Println(name, v, v.IsValid(), enc.typ, enc.field)
		switch enc.typ {
		case ST_ARRAY:
			if name == "EndOfArray" {
				return errorEndOfArray
			}
			array := getField(v, enc)
		loop:
			for {
				child := reflect.New(array.Type().Elem()).Elem()
				err := readObject(r, &child)
				switch err {
				case errorEndOfArray:
					break loop
				case errorEndOfObject:
					array.Set(reflect.Append(*array, child))
				default:
					return err
				}
			}
		case ST_OBJECT:
			switch name {
			case "EndOfObject":
				return errorEndOfObject
			case "PreviousFields", "NewFields", "FinalFields":
				leType := LedgerEntryType(v.Elem().FieldByName("LedgerEntryType").Uint())
				le := LedgerEntryFactory[leType]()
				fields := reflect.ValueOf(le)
				v.Elem().FieldByName(name).Set(fields)
				if err := readObject(r, &fields); err != nil && err != errorEndOfObject {
					return err
				}
				// var fields Fields
				// f := reflect.ValueOf(&fields)
				// v.Elem().FieldByName(name).Set(f)
				// if readObject(r, &f); err != nil && err != errorEndOfObject {
				// 	return err
				// }
			case "ModifiedNode", "DeletedNode", "CreatedNode":
				var node AffectedNode
				n := reflect.ValueOf(&node)
				var effect NodeEffect
				e := reflect.ValueOf(&effect)
				e.Elem().FieldByName(name).Set(n)
				v.Set(e.Elem())
				return readObject(r, &n)
			case "SignerEntry":
				var signerEntry SignerEntry
				s := reflect.ValueOf(&signerEntry)
				err := readObject(r, &s)
				v.Set(s.Elem())
				return err
			case "Majority":
				var majority Majority
				m := reflect.ValueOf(&majority)
				err := readObject(r, &m)
				v.Set(m.Elem())
				return err
			case "Memo":
				var memo Memo
				m := reflect.ValueOf(&memo)
				inner := reflect.ValueOf(&memo.Memo)
				err := readObject(r, &inner)
				v.Set(m.Elem())
				return err
			default:
				return fmt.Errorf("Unexpected object: %s for field: %s", v.Type(), name)
			}
		default:
			if v.Kind() == reflect.Struct {
				return fmt.Errorf("Unexpected struct: %s for field: %s", v.Type(), name)
			}
			field := getField(v, enc)
			if !field.CanAddr() {
				return fmt.Errorf("Missing field: %s %+v", name, enc)
			}
			switch v := field.Addr().Interface().(type) {
			case Wire:
				if err := v.Unmarshal(r); err != nil {
					return err
				}
			default:
				if err := read(r, v); err != nil {
					return err
				}
			}
		}
	}
	return err
}

func getField(v *reflect.Value, e *enc) *reflect.Value {
	name := encodings[*e]
	field := v.Elem().FieldByName(name)
	if field.Kind() == reflect.Ptr {
		field.Set(reflect.New(field.Type().Elem()))
		field = field.Elem()
	}
	return &field
}
<|endoftext|>


<commit_before>package webhook

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/stripe/stripe-go"
)

const ToleranceDefault int64 = 300
const ToleranceIgnoreTimestamp int64 = 0

// ConstructEvent initializes an Event object from a JSON payload.
// It returns an non-nil error when the payload is not valid JSON or when signature verification fails.
// payload is the webhook request body, i.e. `ioutil.ReadAll(r.Body)`.
// sigHeader is the webhook Stripe-Signature header, i.e. `r.Header.Get("Stripe-Signature")`.
// secret is your Signing Secret, i.e. `"whsec_XYZ"`.  See https://dashboard.stripe.com/webhooks
// tolerance (suggested 300) is the max difference in seconds between now and Stripe-Signature's timestamp. If the difference is greater than this tolerance, the signature is rejected and a non-nil error is returned.  If tolerance is 0 or less, then the timestamp is not checked.
// NOTE: your requests will only have Stripe-Signature if you have clicked to reveal your secret
func ConstructEvent(payload []byte, sigHeader string, secret string, tolerance int64) (stripe.Event, error) {
	e := stripe.Event{}

	if err := json.Unmarshal(payload, &e); err != nil {
		return e, fmt.Errorf("Failed to parse webhook body json: %s", err.Error())
	}

	if sigHeader == "" {
		return e, errors.New("Missing Stripe-Signature header")
	}

	// sigHeader looks like "t=1495999758,v1=ABC,v1=DEF,v0=GHI"

	// First extract the timestamp
	var t string
	pairs := strings.Split(sigHeader, ",")
	for _, pair := range pairs {
		parts := strings.Split(pair, "=")
		if len(parts) != 2 || parts[0] != "t" {
			continue
		}

		t = parts[1]
		break
	}

	if t == "" {
		return e, errors.New("Webhook has no timestamp")
	}

	if tolerance > 0 {
		timestamp, err := strconv.ParseInt(t, 10, 64)
		if err != nil {
			return e, errors.New("Webhook has invalid timestamp")
		}

		currentTimestamp := time.Now().Unix()

		diff := timestamp - currentTimestamp
		if diff < 0 {
			diff = -diff
		}

		if diff > tolerance {
			return e, errors.New("Webhook is too old")
		}
	}

	// Compute the expected signature.
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(t))
	mac.Write([]byte("."))
	mac.Write(payload)
	res := mac.Sum(nil)

	// Check all given v1 signatures since multiple v1 can happen in case of rolled secret.
	for _, pair := range pairs {
		parts := strings.Split(pair, "=")
		if len(parts) != 2 || parts[0] != "v1" {
			continue
		}

		sig, err := hex.DecodeString(parts[1])
		if err != nil {
			return e, fmt.Errorf("Invalid v1 signature: %s", err.Error())
		}

		if hmac.Equal(res, sig) {
			// OK
			return e, nil
		}
	}

	return e, errors.New("Correct signature not found")
}
<commit_msg>Overhaul webhook errors.<commit_after>package webhook

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/stripe/stripe-go"
)

const ToleranceDefault int64 = 300
const ToleranceIgnoreTimestamp int64 = 0

var ErrNotSigned error = errors.New("Webhook has no Stripe-Signature header")
var ErrNoTimestamp error = errors.New("Webhook has no timestamp")
var ErrTooOld error = errors.New("Webhook had valid signature but timestamp wasn't within tolerance")
var ErrNoValidSignature error = errors.New("Webhook had no valid signature")

// ConstructEvent initializes an Event object from a JSON payload.
// It returns an non-nil error when the payload is not valid JSON or when signature verification fails.
// payload is the webhook request body, i.e. `ioutil.ReadAll(r.Body)`.
// sigHeader is the webhook Stripe-Signature header, i.e. `r.Header.Get("Stripe-Signature")`.
// secret is your Signing Secret, i.e. `"whsec_XYZ"`.  See https://dashboard.stripe.com/webhooks
// tolerance (suggested 300) is the max difference in seconds between now and Stripe-Signature's timestamp. If the difference is greater than this tolerance, the signature is rejected and a non-nil error is returned.  If tolerance is 0 or less, then the timestamp is not checked.
// NOTE: your requests will only have Stripe-Signature if you have clicked to reveal your secret
func ConstructEvent(payload []byte, sigHeader string, secret string, tolerance int64) (stripe.Event, error) {
	e := stripe.Event{}

	if err := json.Unmarshal(payload, &e); err != nil {
		return e, fmt.Errorf("Failed to parse webhook body json: %s", err.Error())
	}

	if sigHeader == "" {
		return e, ErrNotSigned
	}

	// sigHeader looks like "t=1495999758,v1=ABC,v1=DEF,v0=GHI"

	// First extract the timestamp
	var t string
	pairs := strings.Split(sigHeader, ",")
	for _, pair := range pairs {
		parts := strings.Split(pair, "=")
		if len(parts) != 2 || parts[0] != "t" {
			continue
		}

		t = parts[1]
		break
	}

	if t == "" {
		return e, ErrNoTimestamp
	}

	invalidTimestamp := false
	if tolerance > 0 {
		timestamp, err := strconv.ParseInt(t, 10, 64)
		if err == nil {
			currentTimestamp := time.Now().Unix()

			diff := timestamp - currentTimestamp
			if diff < 0 {
				diff = -diff
			}

			if diff > tolerance {
				invalidTimestamp = true
			}
		} else {
			invalidTimestamp = true
		}
	}

	// Compute the expected signature.
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(t))
	mac.Write([]byte("."))
	mac.Write(payload)
	res := mac.Sum(nil)

	// Check all given v1 signatures since multiple v1 can happen in case of rolled secret.
	for _, pair := range pairs {
		parts := strings.Split(pair, "=")
		if len(parts) != 2 || parts[0] != "v1" {
			continue
		}

		sig, err := hex.DecodeString(parts[1])
		if err != nil {
			// Ignore signature which isn't valid hex.
			continue
		}

		if hmac.Equal(res, sig) {
			if invalidTimestamp {
				return e, ErrTooOld
			}

			// OK
			return e, nil
		}
	}

	return e, ErrNoValidSignature
}
<|endoftext|>


<commit_before>package webhook

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

	"github.com/Sirupsen/logrus"

	"github.com/modcloth/docker-builder/job"
)

const (
	travisSuccess = iota
)

const (
	travisBuildTypePullRequest = "pull_request"
)

type travisRepository struct {
	Owner string `json:"owner_name"`
	Name  string `json:"name"`
}

type travisPayload struct {
	Repository  travisRepository `json:"repository"`
	CommitSHA   string           `json:"commit"`
	BuildStatus int              `json:"status"`
	BuildType   string           `json:"type"`
}

/*
Travis parses a webhook HTTP request from Travis CI and returns a JobSpec.
*/
func Travis(w http.ResponseWriter, req *http.Request) (int, string) {
	payloadBody := req.FormValue("payload")
	var payload = &travisPayload{}

	if err := json.Unmarshal([]byte(payloadBody), payload); err != nil {
		logger.WithField("error", err).Error("error unmarshaling json")
		return 400, "400 bad request"
	}

	if payload.BuildStatus != travisSuccess {
		logger.WithFields(logrus.Fields{
			"owner": payload.Repository.Owner,
			"repo":  payload.Repository.Name,
		}).Error("travis build was a success")
		return 409, "409 conflict"
	}

	if payload.BuildType == travisBuildTypePullRequest {
		logger.WithFields(logrus.Fields{
			"owner": payload.Repository.Owner,
			"repo":  payload.Repository.Name,
		}).Error("build type is a pull request, not building")
		return 409, "409 conflict"
	}

	spec := &job.JobSpec{
		RepoOwner: payload.Repository.Owner,
		RepoName:  payload.Repository.Name,
		GitRef:    payload.CommitSHA,
	}

	return processJobHelper(spec, w, req)
}
<commit_msg>[TRIVIAL] updating error message to be correct<commit_after>package webhook

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

	"github.com/Sirupsen/logrus"

	"github.com/modcloth/docker-builder/job"
)

const (
	travisSuccess = iota
)

const (
	travisBuildTypePullRequest = "pull_request"
)

type travisRepository struct {
	Owner string `json:"owner_name"`
	Name  string `json:"name"`
}

type travisPayload struct {
	Repository  travisRepository `json:"repository"`
	CommitSHA   string           `json:"commit"`
	BuildStatus int              `json:"status"`
	BuildType   string           `json:"type"`
}

/*
Travis parses a webhook HTTP request from Travis CI and returns a JobSpec.
*/
func Travis(w http.ResponseWriter, req *http.Request) (int, string) {
	payloadBody := req.FormValue("payload")
	var payload = &travisPayload{}

	if err := json.Unmarshal([]byte(payloadBody), payload); err != nil {
		logger.WithField("error", err).Error("error unmarshaling json")
		return 400, "400 bad request"
	}

	if payload.BuildStatus != travisSuccess {
		logger.WithFields(logrus.Fields{
			"owner": payload.Repository.Owner,
			"repo":  payload.Repository.Name,
		}).Error("travis build was not a success, not build")
		return 409, "409 conflict"
	}

	if payload.BuildType == travisBuildTypePullRequest {
		logger.WithFields(logrus.Fields{
			"owner": payload.Repository.Owner,
			"repo":  payload.Repository.Name,
		}).Error("build type is a pull request, not building")
		return 409, "409 conflict"
	}

	spec := &job.JobSpec{
		RepoOwner: payload.Repository.Owner,
		RepoName:  payload.Repository.Name,
		GitRef:    payload.CommitSHA,
	}

	return processJobHelper(spec, w, req)
}
<|endoftext|>


<commit_before>package goapns

import (
	"errors"
	"net/http"
	"time"
)

// Service error responses.
var (
	ErrorPayloadEmpty              = errors.New("The message payload was empty, expected HTTP/2 status code is 400.")
	ErrorPayloadTooLarge           = errors.New("The message payload was too large. The maximum payload size is 4096 bytes.")
	ErrorBadTopic                  = errors.New("The apns-topic was invalid.")
	ErrorTopicDisallowed           = errors.New("Pushing to this topic is not allowed.")
	ErrorBadMessageID              = errors.New("The APNSID value is bad.")
	ErrorBadExpirationDate         = errors.New("The expiration value is bad.")
	ErrorBadPriority               = errors.New("The apns-priority value is bad.")
	ErrorMissingDeviceToken        = errors.New("The device token is not specified in the request. Verify that the message is sent to a device token.")
	ErrorBadDeviceToken            = errors.New("The specified device token was bad. Verify that the request contains a valid token and that the token matches the environment.")
	ErrorDeviceTokenNotForTopic    = errors.New("The device token does not match the specified topic.")
	ErrorUnregistered              = errors.New("The device token is inactive for the specified topic. Expected HTTP/2 status code is 410.")
	ErrorDuplicateHeaders          = errors.New("One or more headers were repeated.")
	ErrorBadCertificateEnvironment = errors.New("The client certificate was for the wrong environment.")
	ErrorBadCertificate            = errors.New("The certificate was bad.")
	ErrorForbidden                 = errors.New("The specified action is not allowed.")
	ErrorBadPath                   = errors.New("The request contained a bad :path value.")
	ErrorMethodNotAllowed          = errors.New("The specified :method was not POST.")
	ErrorTooManyRequests           = errors.New("Too many requests were made consecutively to the same device token.")
	ErrorIdleTimeout               = errors.New("Idle time out.")
	ErrorShutdown                  = errors.New("The server is shutting down.")
	ErrorInternalServerError       = errors.New("An internal server error occurred.")
	ErrorServiceUnavailable        = errors.New("The service is unavailable.")
	ErrorMissingTopic              = errors.New("The apns-topic header of the request was not specified and was required. The apns-topic header is mandatory when the client is connected using a certificate that supports multiple topics.")

	// HTTP Status errors.

	ErrorBadRequest = errors.New("Bad request.")
	ErrorUnknown    = errors.New("Unknown error.")
)

// The possible Reason error codes returned from APNs.
// From table 6-6 in the Apple Local and Remote Notification Programming Guide.
var errorReason = map[string]error{
	"PayloadEmpty":              ErrorPayloadEmpty,
	"PayloadTooLarge":           ErrorPayloadTooLarge,
	"BadTopic":                  ErrorBadTopic,
	"TopicDisallowed":           ErrorTopicDisallowed,
	"BadMessageId":              ErrorBadMessageID,
	"BadExpirationDate":         ErrorBadExpirationDate,
	"BadPriority":               ErrorBadPriority,
	"MissingDeviceToken":        ErrorMissingDeviceToken,
	"BadDeviceToken":            ErrorBadDeviceToken,
	"DeviceTokenNotForTopic":    ErrorDeviceTokenNotForTopic,
	"Unregistered":              ErrorUnregistered,
	"DuplicateHeaders":          ErrorDuplicateHeaders,
	"BadCertificateEnvironment": ErrorBadCertificateEnvironment,
	"BadCertificate":            ErrorBadCertificate,
	"Forbidden":                 ErrorForbidden,
	"BadPath":                   ErrorBadPath,
	"MethodNotAllowed":          ErrorMethodNotAllowed,
	"TooManyRequests":           ErrorTooManyRequests,
	"IdleTimeout":               ErrorIdleTimeout,
	"Shutdown":                  ErrorShutdown,
	"InternalServerErroror":     ErrorInternalServerError,
	"ServiceUnavailable":        ErrorServiceUnavailable,
	"MissingTopic":              ErrorMissingTopic,
}

var errorStatus = map[int]error{
	http.StatusBadRequest:            ErrorBadRequest,
	http.StatusForbidden:             ErrorForbidden,
	http.StatusMethodNotAllowed:      ErrorMethodNotAllowed,
	http.StatusGone:                  ErrorUnregistered,
	http.StatusRequestEntityTooLarge: ErrorPayloadTooLarge,
	http.StatusTooManyRequests:       ErrorTooManyRequests,
	http.StatusInternalServerError:   ErrorInternalServerError,
	http.StatusServiceUnavailable:    ErrorServiceUnavailable,
}

//Response defines properties that are useful to inform the calling script what happened with the
//request to Apples Servers. It defines a StatusCode, a Reason (if on is provided by Apple),
//a Timestamt that can be used to identify since when a device became unavailable
//(of type int64, if you need a time.Time object, please use Timestamt() method),
//an Error (nil if everything went fine), the Token of the device the notification was sent to
//and the Message object itself (can be used to figure out, what went wrong).
type Response struct {
	//StatusCode defines the status code that Apples servers returned. (See HTTP status codes)
	StatusCode int

	//Reason is a string representation sent by Apples servers describing why the notification
	//failed in delivery.
	Reason string `json:"reason"`

	//TimestempNumber is an int64 that is populated if Apples servers identified a device token being unavailable
	//for a given amount of time. Apple documentation says:
	//If the value in the :status header is 410, the value of this key is the last time at which APNs confirmed that the device token was no longer valid for the topic.
	//Stop pushing notifications until the device registers a token with a later timestamp with your provider.
	TimestempNumber int64 `json:"timestamp"`

	//Error is nil if everything worked out and not nil if something went wrong by pushing the notification.
	Error error

	//Token of the device to which the notification should be pushed to.
	Token string

	//Message object that failed to sent.
	Message *Message
}

//Sent return true if the notification was sent successfully (http status code == 200).
//Additionally, the Error of the Response will be nil.
func (r *Response) Sent() bool {
	return r.StatusCode == http.StatusOK
}

//Timestamp converts the int64 type from a Response into a time.Time object.
func (r *Response) Timestamp() time.Time {
	// if r.TimestempNumber != 0 {
	return time.Unix(r.TimestempNumber/1000, 0)
	// }

}
<commit_msg>Fixed the damn 'Erroror' mistake<commit_after>package goapns

import (
	"errors"
	"net/http"
	"time"
)

// Service error responses.
var (
	ErrorPayloadEmpty              = errors.New("The message payload was empty, expected HTTP/2 status code is 400.")
	ErrorPayloadTooLarge           = errors.New("The message payload was too large. The maximum payload size is 4096 bytes.")
	ErrorBadTopic                  = errors.New("The apns-topic was invalid.")
	ErrorTopicDisallowed           = errors.New("Pushing to this topic is not allowed.")
	ErrorBadMessageID              = errors.New("The APNSID value is bad.")
	ErrorBadExpirationDate         = errors.New("The expiration value is bad.")
	ErrorBadPriority               = errors.New("The apns-priority value is bad.")
	ErrorMissingDeviceToken        = errors.New("The device token is not specified in the request. Verify that the message is sent to a device token.")
	ErrorBadDeviceToken            = errors.New("The specified device token was bad. Verify that the request contains a valid token and that the token matches the environment.")
	ErrorDeviceTokenNotForTopic    = errors.New("The device token does not match the specified topic.")
	ErrorUnregistered              = errors.New("The device token is inactive for the specified topic. Expected HTTP/2 status code is 410.")
	ErrorDuplicateHeaders          = errors.New("One or more headers were repeated.")
	ErrorBadCertificateEnvironment = errors.New("The client certificate was for the wrong environment.")
	ErrorBadCertificate            = errors.New("The certificate was bad.")
	ErrorForbidden                 = errors.New("The specified action is not allowed.")
	ErrorBadPath                   = errors.New("The request contained a bad :path value.")
	ErrorMethodNotAllowed          = errors.New("The specified :method was not POST.")
	ErrorTooManyRequests           = errors.New("Too many requests were made consecutively to the same device token.")
	ErrorIdleTimeout               = errors.New("Idle time out.")
	ErrorShutdown                  = errors.New("The server is shutting down.")
	ErrorInternalServerError       = errors.New("An internal server error occurred.")
	ErrorServiceUnavailable        = errors.New("The service is unavailable.")
	ErrorMissingTopic              = errors.New("The apns-topic header of the request was not specified and was required. The apns-topic header is mandatory when the client is connected using a certificate that supports multiple topics.")

	// HTTP Status errors.

	ErrorBadRequest = errors.New("Bad request.")
	ErrorUnknown    = errors.New("Unknown error.")
)

// The possible Reason error codes returned from APNs.
// From table 6-6 in the Apple Local and Remote Notification Programming Guide.
var errorReason = map[string]error{
	"PayloadEmpty":              ErrorPayloadEmpty,
	"PayloadTooLarge":           ErrorPayloadTooLarge,
	"BadTopic":                  ErrorBadTopic,
	"TopicDisallowed":           ErrorTopicDisallowed,
	"BadMessageId":              ErrorBadMessageID,
	"BadExpirationDate":         ErrorBadExpirationDate,
	"BadPriority":               ErrorBadPriority,
	"MissingDeviceToken":        ErrorMissingDeviceToken,
	"BadDeviceToken":            ErrorBadDeviceToken,
	"DeviceTokenNotForTopic":    ErrorDeviceTokenNotForTopic,
	"Unregistered":              ErrorUnregistered,
	"DuplicateHeaders":          ErrorDuplicateHeaders,
	"BadCertificateEnvironment": ErrorBadCertificateEnvironment,
	"BadCertificate":            ErrorBadCertificate,
	"Forbidden":                 ErrorForbidden,
	"BadPath":                   ErrorBadPath,
	"MethodNotAllowed":          ErrorMethodNotAllowed,
	"TooManyRequests":           ErrorTooManyRequests,
	"IdleTimeout":               ErrorIdleTimeout,
	"Shutdown":                  ErrorShutdown,
	"InternalServerError":       ErrorInternalServerError,
	"ServiceUnavailable":        ErrorServiceUnavailable,
	"MissingTopic":              ErrorMissingTopic,
}

var errorStatus = map[int]error{
	http.StatusBadRequest:            ErrorBadRequest,
	http.StatusForbidden:             ErrorForbidden,
	http.StatusMethodNotAllowed:      ErrorMethodNotAllowed,
	http.StatusGone:                  ErrorUnregistered,
	http.StatusRequestEntityTooLarge: ErrorPayloadTooLarge,
	http.StatusTooManyRequests:       ErrorTooManyRequests,
	http.StatusInternalServerError:   ErrorInternalServerError,
	http.StatusServiceUnavailable:    ErrorServiceUnavailable,
}

//Response defines properties that are useful to inform the calling script what happened with the
//request to Apples Servers. It defines a StatusCode, a Reason (if on is provided by Apple),
//a Timestamt that can be used to identify since when a device became unavailable
//(of type int64, if you need a time.Time object, please use Timestamt() method),
//an Error (nil if everything went fine), the Token of the device the notification was sent to
//and the Message object itself (can be used to figure out, what went wrong).
type Response struct {
	//StatusCode defines the status code that Apples servers returned. (See HTTP status codes)
	StatusCode int

	//Reason is a string representation sent by Apples servers describing why the notification
	//failed in delivery.
	Reason string `json:"reason"`

	//TimestempNumber is an int64 that is populated if Apples servers identified a device token being unavailable
	//for a given amount of time. Apple documentation says:
	//If the value in the :status header is 410, the value of this key is the last time at which APNs confirmed that the device token was no longer valid for the topic.
	//Stop pushing notifications until the device registers a token with a later timestamp with your provider.
	TimestempNumber int64 `json:"timestamp"`

	//Error is nil if everything worked out and not nil if something went wrong by pushing the notification.
	Error error

	//Token of the device to which the notification should be pushed to.
	Token string

	//Message object that failed to sent.
	Message *Message
}

//Sent return true if the notification was sent successfully (http status code == 200).
//Additionally, the Error of the Response will be nil.
func (r *Response) Sent() bool {
	return r.StatusCode == http.StatusOK
}

//Timestamp converts the int64 type from a Response into a time.Time object.
func (r *Response) Timestamp() time.Time {
	// if r.TimestempNumber != 0 {
	return time.Unix(r.TimestempNumber/1000, 0)
	// }

}
<|endoftext|>


<commit_before>package aero

import "crypto/tls"

// ApplicationSecurity stores the certificate data.
type ApplicationSecurity struct {
	Key         string
	Certificate string
}

// Load expects the path of the certificate and the key.
func (security *ApplicationSecurity) Load(certificate string, key string) {
	security.Certificate = certificate
	security.Key = key
}

// createTLSConfig creates a secure TLS configuration.
func createTLSConfig() *tls.Config {
	return &tls.Config{
		MinVersion:               tls.VersionTLS12,
		PreferServerCipherSuites: true,
		CurvePreferences: []tls.CurveID{
			tls.CurveP521,
			tls.CurveP384,
			tls.CurveP256,
		},
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
		},
	}
}
<commit_msg>Improved performance by supporting ECDSA<commit_after>package aero

import "crypto/tls"

// ApplicationSecurity stores the certificate data.
type ApplicationSecurity struct {
	Key         string
	Certificate string
}

// Load expects the path of the certificate and the key.
func (security *ApplicationSecurity) Load(certificate string, key string) {
	security.Certificate = certificate
	security.Key = key
}

// createTLSConfig creates a secure TLS configuration.
func createTLSConfig() *tls.Config {
	return &tls.Config{
		MinVersion:               tls.VersionTLS12,
		PreferServerCipherSuites: true,
		CurvePreferences: []tls.CurveID{
			tls.CurveP521,
			tls.CurveP384,
			tls.CurveP256,
		},
		CipherSuites: []uint16{
			// ECDSA is about 3 times faster than RSA and should be preferred.
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,

			// RSA is slower but still widely used.
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
		},
	}
}
<|endoftext|>


<commit_before>// Go language actor framework.
//
// Stability: 1 - Experimental
// (see: https://github.com/tristanls/stability-index#stability-1---experimental)
//
//    package main
//
//    import "fmt"
//    import "github.com/tristanls/go-actor"
//
//    // create an actor behavior
//    func Print(context actor.Context, msg actor.Message) {
//      for _, param := range msg.Params {
//        fmt.Println(param.(string))
//      }
//      // starting go routines within an actor behavior is *NOT SAFE*
//      // create actors instead, that's what they're for :)
//    }
//
//    // create a new actor configuration
//    func main() {
//      config := actor.Configuration()
//      config.Trace = true // trace message deliveries
//
//      // create a new actor
//      printer := config.Create(Print)
//
//      // send a message to an actor
//      // both ways of creating a message are identical
//      printer <- config.CreateMessage("hello world")
//      printer <- actor.CreateMessage("hello world")
//      printer <- actor.Message{Params: []interface{}{"hello world"}}
//
//      // wait for actor configuration to finish
//      config.Wait()
//    }
package actor

import (
  "fmt"
  "sync"
)

// Become is a function that takes the behavior to handle the next message
type Become func(Behavior)

// Behavior describes how an actor will respond to a message. Actors are created
// with behaviors. Each behavior takes a context and a message to respond to.
// These behaviors are invoked by the library when executing an actor
// configuration.
type Behavior func(Context, Message)

// Message is just a slice of data
type Message struct {
  Params []interface{}
}

// Reference to an actor is a channel that accepts messages
type Reference chan<- Message

// Send is here for completeness, use `Reference <- Message` notation instead
func (reference Reference) Send(msg Message) {
  reference <- msg
}

// ActorConfiguration holds the state of an actor configuration
// To wait for all actors to finish call ActorConfiguration.Wait(), for example:
//    config := actor.Configuration()
//    // send some messages to actors
//    config.Wait() // wait for all actors to finish
// "finish" in this case means that all actors finished execution and there are
// no more messages in flight
type ActorConfiguration struct {
  Trace bool
  waitGroup sync.WaitGroup
}

// Create creates a new actor as part of the actor configuration
func (configuration *ActorConfiguration) Create(behavior Behavior) Reference {
  instrumentedReference := make(chan Message)
  reference := make(chan Message)
  go actorBehavior(configuration, behavior, reference, instrumentedReference)
  go func() {
    buffer := []Message{}
    receiveLoop:
    for {
      if len(buffer) == 0 {
        message, ok := <-instrumentedReference
        if !ok { break }
        configuration.waitGroup.Add(1)
        if configuration.Trace {
          fmt.Println(instrumentedReference, "queued", message)
        }
        buffer = append(buffer, message)
      }

      select {
      case message, ok := <-instrumentedReference:
        if !ok { break receiveLoop }
        configuration.waitGroup.Add(1)
        if configuration.Trace {
          fmt.Println(instrumentedReference, "queued", message)
        }
        buffer = append(buffer, message)
      case reference <- buffer[0]:
        buffer = buffer[1:]
      }
    }
    for _, message := range buffer {
      reference <- message
    }
  }()
  if configuration.Trace {
    fmt.Println(instrumentedReference, "created")
  }
  return instrumentedReference
}

// CreateMessage is syntactic sugar to create a Message
func (configuration *ActorConfiguration) CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// Wait blocks until the actor configuration finishes executing
func (configuration *ActorConfiguration) Wait() {
  configuration.waitGroup.Wait()
}

// Context is passed to an actor behavior when it is invoked upon a message receive
type Context struct {
	Become Become
	Create func(Behavior) Reference
	Self Reference
}

// Configuration creates a new actor configuration
func Configuration() ActorConfiguration {
  return ActorConfiguration{}
}

// CreateMessage is syntactic sugar to create a Message
func CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// actorBehavior implements execution of actor behaviors, become semantics,
// and making sure that the configuration doesn't exit while messages are still
// in flight.
func actorBehavior(configuration *ActorConfiguration, behavior Behavior, reference chan Message, instrumentedReference chan Message) {
  var become func(Behavior)
  become = func(nextBehavior Behavior) {
    become = func(Behavior){} // become can only be called once
    go actorBehavior(configuration, nextBehavior, reference, instrumentedReference)
    behavior = nil
  }
  for {
    if behavior != nil {
      message := <-reference
      if configuration.Trace {
        fmt.Println(instrumentedReference, "<-", message)
      }
      context := Context{Become: become, Create: configuration.Create, Self: instrumentedReference}
      behavior(context, message)
      // there is a race condition between last message delivery in behavior
      // and Done() being called below, where the message is pulled off of a
      // channel, Done() executes, config finishes, and then only Add(1)
      // is called :/ ... by then, main() exits
      configuration.waitGroup.Done() // one message has been processed
      if configuration.Trace {
        fmt.Println(instrumentedReference, "completed", message)
      }
    } else {
      return // this actor behavior has been replaced.. go away
    }
  }
}<commit_msg>added delay to reporting behavior completion to reduce the chances of killing running actors<commit_after>// Go language actor framework.
//
// Stability: 1 - Experimental
// (see: https://github.com/tristanls/stability-index#stability-1---experimental)
//
//    package main
//
//    import "fmt"
//    import "github.com/tristanls/go-actor"
//
//    // create an actor behavior
//    func Print(context actor.Context, msg actor.Message) {
//      for _, param := range msg.Params {
//        fmt.Println(param.(string))
//      }
//      // starting go routines within an actor behavior is *NOT SAFE*
//      // create actors instead, that's what they're for :)
//    }
//
//    // create a new actor configuration
//    func main() {
//      config := actor.Configuration()
//      config.Trace = true // trace message deliveries
//
//      // create a new actor
//      printer := config.Create(Print)
//
//      // send a message to an actor
//      // both ways of creating a message are identical
//      printer <- config.CreateMessage("hello world")
//      printer <- actor.CreateMessage("hello world")
//      printer <- actor.Message{Params: []interface{}{"hello world"}}
//
//      // wait for actor configuration to finish
//      config.Wait()
//    }
package actor

import (
  "fmt"
  "sync"
  "time"
)

// Become is a function that takes the behavior to handle the next message
type Become func(Behavior)

// Behavior describes how an actor will respond to a message. Actors are created
// with behaviors. Each behavior takes a context and a message to respond to.
// These behaviors are invoked by the library when executing an actor
// configuration.
type Behavior func(Context, Message)

// Message is just a slice of data
type Message struct {
  Params []interface{}
}

// Reference to an actor is a channel that accepts messages
type Reference chan<- Message

// Send is here for completeness, use `Reference <- Message` notation instead
func (reference Reference) Send(msg Message) {
  reference <- msg
}

// ActorConfiguration holds the state of an actor configuration
// To wait for all actors to finish call ActorConfiguration.Wait(), for example:
//    config := actor.Configuration()
//    // send some messages to actors
//    config.Wait() // wait for all actors to finish
// "finish" in this case means that all actors finished execution and there are
// no more messages in flight
type ActorConfiguration struct {
  Trace bool
  waitGroup sync.WaitGroup
}

// Create creates a new actor as part of the actor configuration
func (configuration *ActorConfiguration) Create(behavior Behavior) Reference {
  instrumentedReference := make(chan Message)
  reference := make(chan Message)
  go actorBehavior(configuration, behavior, reference, instrumentedReference)
  go func() {
    buffer := []Message{}
    receiveLoop:
    for {
      if len(buffer) == 0 {
        message, ok := <-instrumentedReference
        if !ok { break }
        configuration.waitGroup.Add(1)
        if configuration.Trace {
          fmt.Println(instrumentedReference, "queued", message)
        }
        buffer = append(buffer, message)
      }

      select {
      case message, ok := <-instrumentedReference:
        if !ok { break receiveLoop }
        configuration.waitGroup.Add(1)
        if configuration.Trace {
          fmt.Println(instrumentedReference, "queued", message)
        }
        buffer = append(buffer, message)
      case reference <- buffer[0]:
        buffer = buffer[1:]
      }
    }
    for _, message := range buffer {
      reference <- message
    }
  }()
  if configuration.Trace {
    fmt.Println(instrumentedReference, "created")
  }
  return instrumentedReference
}

// CreateMessage is syntactic sugar to create a Message
func (configuration *ActorConfiguration) CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// Wait blocks until the actor configuration finishes executing
func (configuration *ActorConfiguration) Wait() {
  configuration.waitGroup.Wait()
}

// Context is passed to an actor behavior when it is invoked upon a message receive
type Context struct {
	Become Become
	Create func(Behavior) Reference
	Self Reference
}

// Configuration creates a new actor configuration
func Configuration() ActorConfiguration {
  return ActorConfiguration{}
}

// CreateMessage is syntactic sugar to create a Message
func CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// actorBehavior implements execution of actor behaviors, become semantics,
// and making sure that the configuration doesn't exit while messages are still
// in flight.
func actorBehavior(configuration *ActorConfiguration, behavior Behavior, reference chan Message, instrumentedReference chan Message) {
  var become func(Behavior)
  become = func(nextBehavior Behavior) {
    become = func(Behavior){} // become can only be called once
    go actorBehavior(configuration, nextBehavior, reference, instrumentedReference)
    behavior = nil
  }
  for {
    if behavior != nil {
      message := <-reference
      if configuration.Trace {
        fmt.Println(instrumentedReference, "<-", message)
      }
      context := Context{Become: become, Create: configuration.Create, Self: instrumentedReference}
      behavior(context, message)
      // there is a race condition between last message delivery in behavior
      // and Done() being called below, where the message is pulled off of a
      // channel, Done() executes, config finishes, and then only Add(1)
      // is called :/ ... by then, main() exits
      // the current fix is to wait a set amount of time before
      // reporting finishing the behavior (behavior already finished, only
      // the report is delayed to allow for other messages to be delivered)
      // perhaps this parameter should be configurable?
      go func() {
        time.Sleep(1000 * time.Millisecond)
        configuration.waitGroup.Done() // one message has been processed
      }()
      if configuration.Trace {
        fmt.Println(instrumentedReference, "completed", message)
      }
    } else {
      return // this actor behavior has been replaced.. go away
    }
  }
}<|endoftext|>


<commit_before>package ocf

import (
	"log"
	"os"
	"strings"
)

type Agent struct {
	r Resource
}

// initiate new Agent to represent resource
func New(r Resource) *Agent {
	return &Agent{r: r}
}

// return error from executed resource handler
func (a *Agent) run(command string, ctx *Context) error {
	switch command {
	case "start":
		return a.r.Start(ctx)

	case "stop":
		return a.r.Stop(ctx)

	case "monitor":
		return a.r.Monitor(ctx)

	case "notify":
		return a.r.Notify(ctx)

	case "promote":
		return a.r.Promote(ctx)

	case "demote":
		return a.r.Demote(ctx)

	case "meta-data":
		return a.r.Metadata()
	}

	return ErrUnimplemented
}

// run agent with argv parameters and environment variables.
// returns nil on success
func (a *Agent) Run(args []string, env Env) (err error) {
	if len(args) < 2 {
		err = ErrArgs
		return
	}

	ri, err := ParseResourceInstance(env.Get("OCF_RESOURCE_INSTANCE"))
	if err != nil && err != ErrUnimplemented {
		return
	}

	ctx := &Context{
		Resource: ri,
		Keys:     env.Fields("OCF_RESKEY_"),
	}

	err = a.run(strings.TrimSpace(args[1]), ctx)
	if err != nil {
		return
	}

	return nil
}

// this is a hack to get around terminating while
// executing tests
var exitFunc func(int) = os.Exit

// accepts an Error or runtime.error, printing appropriate error message to STDERR
// and exiting with relevant OCF exit code
func Exit(err error) {
	var status int = 0

	switch t := err.(type) {
	case Error:
		log.Println("error:", t.Message)
		status = t.Exit

	case error:
		log.Println("error:", t.Error())
		status = 1
	}

	exitFunc(status)
}
<commit_msg>fix merge bug dropping notify parsing<commit_after>package ocf

import (
	"log"
	"os"
	"strings"
)

type Agent struct {
	r Resource
}

// initiate new Agent to represent resource
func New(r Resource) *Agent {
	return &Agent{r: r}
}

// return error from executed resource handler
func (a *Agent) run(command string, ctx *Context) error {
	switch command {
	case "start":
		return a.r.Start(ctx)

	case "stop":
		return a.r.Stop(ctx)

	case "monitor":
		return a.r.Monitor(ctx)

	case "notify":
		ctx.readNotifyType()
		return a.r.Notify(ctx)

	case "promote":
		return a.r.Promote(ctx)

	case "demote":
		return a.r.Demote(ctx)

	case "meta-data":
		return a.r.Metadata()
	}

	return ErrUnimplemented
}

// run agent with argv parameters and environment variables.
// returns nil on success
func (a *Agent) Run(args []string, env Env) (err error) {
	if len(args) < 2 {
		err = ErrArgs
		return
	}

	ri, err := ParseResourceInstance(env.Get("OCF_RESOURCE_INSTANCE"))
	if err != nil && err != ErrUnimplemented {
		return
	}

	ctx := &Context{
		Resource: ri,
		Keys:     env.Fields("OCF_RESKEY_"),
	}

	err = a.run(strings.TrimSpace(args[1]), ctx)
	if err != nil {
		return
	}

	return nil
}

// this is a hack to get around terminating while
// executing tests
var exitFunc func(int) = os.Exit

// accepts an Error or runtime.error, printing appropriate error message to STDERR
// and exiting with relevant OCF exit code
func Exit(err error) {
	var status int = 0

	switch t := err.(type) {
	case Error:
		log.Println("error:", t.Message)
		status = t.Exit

	case error:
		log.Println("error:", t.Error())
		status = 1
	}

	exitFunc(status)
}
<|endoftext|>


<commit_before>package main

import (
  "os"
  "path"
  "fmt"
  "log"
  "github.com/codegangsta/cli"
  "github.com/buildboxhq/buildbox-agent/buildbox"
)

var AppHelpTemplate = `The agent performs builds and sends the results back to Buildbox.

Usage:

  {{.Name}} command [arguments]

The comamnds are:

  {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
  {{end}}
Use "buildbox-agent help [command]" for more information about a command.

`

var CommandHelpTemplate = `Usage: buildbox-agent {{.Name}} [command options] [arguments...]

{{.Description}}

Options:
   {{range .Flags}}{{.}}
   {{end}}
`

var StartHelpDescription = `When a job is ready to run it will call the "bootstrap-script"
and pass it all the environment variables required for the job to run.
This script is responsible for checking out the code, and running the
actual build script defined in the project.

The agent will run any jobs within a PTY (Pseudo terminal) if available.

Example:

buildbox-agent start --access-token [access-token] \
                     --bootstrap-script ~/.buildbox/bootstrap.sh`

var RunHelpDescription = `Manually run a scheduled job. If the job has already assigned
to another agent, it will not run.

Example:

buildbox-agent run [job-id] --access-token [access-token]`

var BootstrapScriptDefault = "$HOME/.buildbox/bootstrap.sh"

var AgentAccessTokenEnv = "BUILDBOX_AGENT_ACCESS_TOKEN"
var AgentAccessTokenDefault = "$" + AgentAccessTokenEnv

func main() {
  cli.AppHelpTemplate = AppHelpTemplate
  cli.CommandHelpTemplate = CommandHelpTemplate

  app := cli.NewApp()
  app.Name = "buildbox-agent"
  app.Version = buildbox.Version

  // Define the actions for our CLI
  app.Commands = []cli.Command{
    {
      Name:  "start",
      Usage: "Starts the Buildbox agent",
      Description: StartHelpDescription,
      Flags: []cli.Flag {
        cli.StringFlag{"access-token", AgentAccessTokenDefault, "The access token used to identify the agent."},
        cli.StringFlag{"bootstrap-script", BootstrapScriptDefault, "Path to the bootstrap script."},
        cli.StringFlag{"url", "https://agent.buildbox.io/v1", "The Agent API endpoint."},
        cli.BoolFlag{"debug", "Enable debug mode."},
      },
      Action: func(c *cli.Context) {
        // Create the agent from the CLI options
        agent := setupAgentFromCli(c, "start")

        // Setup signal monitoring
        agent.MonitorSignals()

        // Start the agent
        agent.Start()
      },
    },
    {
      Name:  "run",
      Usage: "Manually run a scheduled job",
      Description: RunHelpDescription,
      Flags: []cli.Flag {
        cli.StringFlag{"access-token", "", "The access token used to identify the agent."},
        cli.StringFlag{"bootstrap-script", BootstrapScriptDefault, "Path to the bootstrap script."},
        cli.StringFlag{"url", "https://agent.buildbox.io/v1", "The Agent API endpoint."},
        cli.BoolFlag{"debug", "Enable debug mode."},
      },
      Action: func(c *cli.Context) {
        // Create the agent from the CLI options
        agent := setupAgentFromCli(c, "run")

        // Grab the first argument and use as the job id
        id := c.Args().First()

        // Validate the job id
        if id == "" {
          fmt.Printf("buildbox-agent: no job id specified.\nSee 'buildbox-agent help run'")
          os.Exit(1)
        }

        agent.Run(id)
      },
    },
  }

  // Default the default action
  app.Action = func(c *cli.Context) {
    cli.ShowAppHelp(c)
    os.Exit(1)
  }

  app.Run(os.Args)
}

func setupAgentFromCli(c *cli.Context, command string) *buildbox.Agent {
  agentAccessToken := c.String("access-token")

  // Should we look to the environment for the agent access token?
  if agentAccessToken == AgentAccessTokenDefault {
    agentAccessToken = os.Getenv(AgentAccessTokenEnv)
  }

  if agentAccessToken == "" {
    fmt.Println("buildbox-agent: missing agent access token\nSee 'buildbox-agent start --help'")
    os.Exit(1)
  }

  bootstrapScript := c.String("bootstrap-script")

  // Go doesn't provide an a mechanism to turn $HOME into an absolute
  // path, so if they're using the default, I'll remake it using real
  // values.
  if c.String("bootstrap-script") == BootstrapScriptDefault {
    homeDir := os.Getenv("HOME")

    bootstrapScript = path.Join(homeDir, ".buildbox", "bootstrap.sh")
  }

  // Make sure the boostrap script exists.
  if _, err := os.Stat(bootstrapScript); os.IsNotExist(err) {
    print("buildbox-agent: no such file " + bootstrapScript + "\n")
    os.Exit(1)
  }

  // Set the agent options
  var agent buildbox.Agent;
  agent.Debug = c.Bool("debug")
  agent.BootstrapScript = bootstrapScript

  // Client specific options
  agent.Client.AgentAccessToken = agentAccessToken
  agent.Client.URL = c.String("url")
  agent.Client.Debug = agent.Debug

  // Tell the user that debug mode has been enabled
  if agent.Debug {
    log.Printf("Debug mode enabled")
  }

  // Setup the agent
  agent.Setup()

  // A nice welcome message
  log.Printf("Started buildbox-agent `%s` (version %s)\n", agent.Name, buildbox.Version)

  return &agent
}
<commit_msg>Expand the default path using ExpandEnv<commit_after>package main

import (
  "os"
  "fmt"
  "log"
  "github.com/codegangsta/cli"
  "github.com/buildboxhq/buildbox-agent/buildbox"
)

var AppHelpTemplate = `The agent performs builds and sends the results back to Buildbox.

Usage:

  {{.Name}} command [arguments]

The comamnds are:

  {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
  {{end}}
Use "buildbox-agent help [command]" for more information about a command.

`

var CommandHelpTemplate = `Usage: buildbox-agent {{.Name}} [command options] [arguments...]

{{.Description}}

Options:
   {{range .Flags}}{{.}}
   {{end}}
`

var StartHelpDescription = `When a job is ready to run it will call the "bootstrap-script"
and pass it all the environment variables required for the job to run.
This script is responsible for checking out the code, and running the
actual build script defined in the project.

The agent will run any jobs within a PTY (Pseudo terminal) if available.

Example:

buildbox-agent start --access-token [access-token] \
                     --bootstrap-script ~/.buildbox/bootstrap.sh`

var RunHelpDescription = `Manually run a scheduled job. If the job has already assigned
to another agent, it will not run.

Example:

buildbox-agent run [job-id] --access-token [access-token]`

var BootstrapScriptDefault = "$HOME/.buildbox/bootstrap.sh"

var AgentAccessTokenEnv = "BUILDBOX_AGENT_ACCESS_TOKEN"
var AgentAccessTokenDefault = "$" + AgentAccessTokenEnv

func main() {
  cli.AppHelpTemplate = AppHelpTemplate
  cli.CommandHelpTemplate = CommandHelpTemplate

  app := cli.NewApp()
  app.Name = "buildbox-agent"
  app.Version = buildbox.Version

  // Define the actions for our CLI
  app.Commands = []cli.Command{
    {
      Name:  "start",
      Usage: "Starts the Buildbox agent",
      Description: StartHelpDescription,
      Flags: []cli.Flag {
        cli.StringFlag{"access-token", AgentAccessTokenDefault, "The access token used to identify the agent."},
        cli.StringFlag{"bootstrap-script", BootstrapScriptDefault, "Path to the bootstrap script."},
        cli.StringFlag{"url", "https://agent.buildbox.io/v1", "The Agent API endpoint."},
        cli.BoolFlag{"debug", "Enable debug mode."},
      },
      Action: func(c *cli.Context) {
        // Create the agent from the CLI options
        agent := setupAgentFromCli(c, "start")

        // Setup signal monitoring
        agent.MonitorSignals()

        // Start the agent
        agent.Start()
      },
    },
    {
      Name:  "run",
      Usage: "Manually run a scheduled job",
      Description: RunHelpDescription,
      Flags: []cli.Flag {
        cli.StringFlag{"access-token", "", "The access token used to identify the agent."},
        cli.StringFlag{"bootstrap-script", BootstrapScriptDefault, "Path to the bootstrap script."},
        cli.StringFlag{"url", "https://agent.buildbox.io/v1", "The Agent API endpoint."},
        cli.BoolFlag{"debug", "Enable debug mode."},
      },
      Action: func(c *cli.Context) {
        // Create the agent from the CLI options
        agent := setupAgentFromCli(c, "run")

        // Grab the first argument and use as the job id
        id := c.Args().First()

        // Validate the job id
        if id == "" {
          fmt.Printf("buildbox-agent: no job id specified.\nSee 'buildbox-agent help run'")
          os.Exit(1)
        }

        agent.Run(id)
      },
    },
  }

  // Default the default action
  app.Action = func(c *cli.Context) {
    cli.ShowAppHelp(c)
    os.Exit(1)
  }

  app.Run(os.Args)
}

func setupAgentFromCli(c *cli.Context, command string) *buildbox.Agent {
  agentAccessToken := c.String("access-token")

  // Should we look to the environment for the agent access token?
  if agentAccessToken == AgentAccessTokenDefault {
    agentAccessToken = os.Getenv(AgentAccessTokenEnv)
  }

  if agentAccessToken == "" {
    fmt.Println("buildbox-agent: missing agent access token\nSee 'buildbox-agent start --help'")
    os.Exit(1)
  }

  bootstrapScript := c.String("bootstrap-script")

  // Expand the envionment variable.
  if bootstrapScript == BootstrapScriptDefault {
    bootstrapScript = os.ExpandEnv(bootstrapScript)
  }

  // Make sure the boostrap script exists.
  if _, err := os.Stat(bootstrapScript); os.IsNotExist(err) {
    print("buildbox-agent: no such file " + bootstrapScript + "\n")
    os.Exit(1)
  }

  // Set the agent options
  var agent buildbox.Agent;
  agent.Debug = c.Bool("debug")
  agent.BootstrapScript = bootstrapScript

  // Client specific options
  agent.Client.AgentAccessToken = agentAccessToken
  agent.Client.URL = c.String("url")
  agent.Client.Debug = agent.Debug

  // Tell the user that debug mode has been enabled
  if agent.Debug {
    log.Printf("Debug mode enabled")
  }

  // Setup the agent
  agent.Setup()

  // A nice welcome message
  log.Printf("Started buildbox-agent `%s` (version %s)\n", agent.Name, buildbox.Version)

  return &agent
}
<|endoftext|>


<commit_before>package anidb

import (
	"github.com/Kovensky/go-anidb/misc"
	"strconv"
	"time"
)

// See the constants list for the valid values.
type AnimeType string

const (
	AnimeTypeTVSeries   = AnimeType("TV Series")  // Anime was a regular TV broadcast series
	AnimeTypeTVSpecial  = AnimeType("TV Special") // Anime was broadcast on TV as a special
	AnimeTypeMovie      = AnimeType("Movie")      // Anime was a feature film
	AnimeTypeOVA        = AnimeType("OVA")        // Anime was released direct-to-video
	AnimeTypeWeb        = AnimeType("Web")        // Anime was released through online streaming or downloads
	AnimeTypeMusicVideo = AnimeType("Music Video")
)

type Rating struct {
	Rating    float32
	VoteCount int
}

type Resource []string

// Links to third party websites
type Resources struct {
	AniDB,
	ANN,
	MyAnimeList,
	AnimeNfo,
	OfficialJapanese,
	OfficialEnglish,
	WikipediaEnglish,
	WikipediaJapanese,
	SyoboiSchedule,
	AllCinema,
	Anison,
	VNDB,
	MaruMegane Resource
}

type UniqueTitleMap map[Language]string
type TitleMap map[Language][]string

type Anime struct {
	AID AID  // The Anime ID.
	R18 bool // Whether this anime is considered porn.

	Type          AnimeType         // Production/distribution type.
	TotalEpisodes int               // Total number of regular episodes.
	EpisodeCount  misc.EpisodeCount // Known numbers of the various types of episodes.

	StartDate time.Time // Date of first episode release, if available.
	EndDate   time.Time // Date of last episode release, if available.

	PrimaryTitle   string         // The primary title in the database; almost always a romanization of the Japanese title.
	OfficialTitles UniqueTitleMap // The official title for each language.
	ShortTitles    TitleMap       // Shortcut titles used for searches
	Synonyms       TitleMap       // Synonyms for each language, or unofficial titles

	OfficialURL string // URL for original official website.
	Picture     string // URL for the page picture on AniDB.

	Description string

	Votes          Rating // Votes from people who watched the whole thing.
	TemporaryVotes Rating // Votes from people who are still watching this.
	Reviews        Rating // Votes from reviewers.

	Episodes []*Episode // List of episodes.

	Awards    []string
	Resources Resources

	Incomplete bool      // Set if the UDP API part of the query failed.
	Updated    time.Time // When the data was last modified in the server.
	Cached     time.Time // When the data was retrieved from the server.
}

// Convenience method that runs AnimeByID on the result of
// SearchAnime.
func (adb *AniDB) AnimeByName(name string) <-chan *Anime {
	return adb.AnimeByID(SearchAnime(name))
}

// Convenience method that runs AnimeByID on the result of
// SearchAnimeFold.
func (adb *AniDB) AnimeByNameFold(name string) <-chan *Anime {
	return adb.AnimeByID(SearchAnimeFold(name))
}

// Returns a list of all Episodes in this Anime's Episodes list
// that are contained by the given EpisodeContainer.
func (a *Anime) EpisodeList(c misc.EpisodeContainer) (eps []*Episode) {
	if a == nil || c == nil {
		return nil
	}

	for i, e := range a.Episodes {
		if c.ContainsEpisodes(&e.Episode) {
			eps = append(eps, a.Episodes[i])
		}
	}
	return
}

// Searches for the given Episode in this Anime's Episodes list
// and returns the match.
//
// Returns nil if there is no match.
func (a *Anime) Episode(ep *misc.Episode) *Episode {
	switch list := a.EpisodeList(ep); len(list) {
	case 0:
		return nil
	case 1:
		return list[0]
	default:
		panic("Single episode search returned more than one result")
	}
}

// Convenience method that parses the string into an Episode
// before doing the Episode search.
func (a *Anime) EpisodeByString(name string) *Episode {
	return a.Episode(misc.ParseEpisode(name))
}

// Convenience method that parses the int into an Episode
// before doing the Episode search.
//
// Only works with regular (i.e. not special, etc) episodes.
func (a *Anime) EpisodeByNumber(number int) *Episode {
	return a.EpisodeByString(strconv.Itoa(number))
}

func (a *Anime) EpisodeByEID(eid EID) *Episode {
	if a == nil {
		return nil
	}
	for _, ep := range a.Episodes {
		if ep.EID == eid {
			return ep
		}
	}
	return nil
}
<commit_msg>anidb: (*Anime).Episode(): be more helpful in the panic message<commit_after>package anidb

import (
	"fmt"
	"github.com/Kovensky/go-anidb/misc"
	"strconv"
	"time"
)

// See the constants list for the valid values.
type AnimeType string

const (
	AnimeTypeTVSeries   = AnimeType("TV Series")  // Anime was a regular TV broadcast series
	AnimeTypeTVSpecial  = AnimeType("TV Special") // Anime was broadcast on TV as a special
	AnimeTypeMovie      = AnimeType("Movie")      // Anime was a feature film
	AnimeTypeOVA        = AnimeType("OVA")        // Anime was released direct-to-video
	AnimeTypeWeb        = AnimeType("Web")        // Anime was released through online streaming or downloads
	AnimeTypeMusicVideo = AnimeType("Music Video")
)

type Rating struct {
	Rating    float32
	VoteCount int
}

type Resource []string

// Links to third party websites
type Resources struct {
	AniDB,
	ANN,
	MyAnimeList,
	AnimeNfo,
	OfficialJapanese,
	OfficialEnglish,
	WikipediaEnglish,
	WikipediaJapanese,
	SyoboiSchedule,
	AllCinema,
	Anison,
	VNDB,
	MaruMegane Resource
}

type UniqueTitleMap map[Language]string
type TitleMap map[Language][]string

type Anime struct {
	AID AID  // The Anime ID.
	R18 bool // Whether this anime is considered porn.

	Type          AnimeType         // Production/distribution type.
	TotalEpisodes int               // Total number of regular episodes.
	EpisodeCount  misc.EpisodeCount // Known numbers of the various types of episodes.

	StartDate time.Time // Date of first episode release, if available.
	EndDate   time.Time // Date of last episode release, if available.

	PrimaryTitle   string         // The primary title in the database; almost always a romanization of the Japanese title.
	OfficialTitles UniqueTitleMap // The official title for each language.
	ShortTitles    TitleMap       // Shortcut titles used for searches
	Synonyms       TitleMap       // Synonyms for each language, or unofficial titles

	OfficialURL string // URL for original official website.
	Picture     string // URL for the page picture on AniDB.

	Description string

	Votes          Rating // Votes from people who watched the whole thing.
	TemporaryVotes Rating // Votes from people who are still watching this.
	Reviews        Rating // Votes from reviewers.

	Episodes []*Episode // List of episodes.

	Awards    []string
	Resources Resources

	Incomplete bool      // Set if the UDP API part of the query failed.
	Updated    time.Time // When the data was last modified in the server.
	Cached     time.Time // When the data was retrieved from the server.
}

// Convenience method that runs AnimeByID on the result of
// SearchAnime.
func (adb *AniDB) AnimeByName(name string) <-chan *Anime {
	return adb.AnimeByID(SearchAnime(name))
}

// Convenience method that runs AnimeByID on the result of
// SearchAnimeFold.
func (adb *AniDB) AnimeByNameFold(name string) <-chan *Anime {
	return adb.AnimeByID(SearchAnimeFold(name))
}

// Returns a list of all Episodes in this Anime's Episodes list
// that are contained by the given EpisodeContainer.
func (a *Anime) EpisodeList(c misc.EpisodeContainer) (eps []*Episode) {
	if a == nil || c == nil {
		return nil
	}

	for i, e := range a.Episodes {
		if c.ContainsEpisodes(&e.Episode) {
			eps = append(eps, a.Episodes[i])
		}
	}
	return
}

// Searches for the given Episode in this Anime's Episodes list
// and returns the match.
//
// Returns nil if there is no match.
func (a *Anime) Episode(ep *misc.Episode) *Episode {
	switch list := a.EpisodeList(ep); len(list) {
	case 0:
		return nil
	case 1:
		return list[0]
	default:
		panic(fmt.Sprintf("Single episode search returned more than one result (wanted %v, got %v)", ep, list))
	}
}

// Convenience method that parses the string into an Episode
// before doing the Episode search.
func (a *Anime) EpisodeByString(name string) *Episode {
	return a.Episode(misc.ParseEpisode(name))
}

// Convenience method that parses the int into an Episode
// before doing the Episode search.
//
// Only works with regular (i.e. not special, etc) episodes.
func (a *Anime) EpisodeByNumber(number int) *Episode {
	return a.EpisodeByString(strconv.Itoa(number))
}

func (a *Anime) EpisodeByEID(eid EID) *Episode {
	if a == nil {
		return nil
	}
	for _, ep := range a.Episodes {
		if ep.EID == eid {
			return ep
		}
	}
	return nil
}
<|endoftext|>