<commit_before>package downloaders

import (
	"os"

	"code.cloudfoundry.org/lager/lagertest"
	"github.com/flavioribeiro/gonfig"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/snickers/snickers/db"
	"github.com/snickers/snickers/db/memory"
	"github.com/snickers/snickers/types"
)

var _ = Describe("Downloaders", func() {
	var (
		logger     *lagertest.TestLogger
		dbInstance db.Storage
		downloader DownloadFunc
		exampleJob types.Job
		configPath string
	)

	BeforeEach(func() {
		logger = lagertest.NewTestLogger("http-download")
		dbInstance, _ = memory.GetDatabase()
		dbInstance.ClearDatabase()
		currentDir, _ := os.Getwd()
		configPath = currentDir + "/../fixtures/config.json"
	})

	runDatabaseSuite := func() {
		It("should return an error if source couldn't be fetched", func() {
			dbInstance.StoreJob(exampleJob)
			err := downloader(logger, configPath, dbInstance, exampleJob.ID)
			Expect(err.Error()).To(SatisfyAny(ContainSubstring("no such host"), ContainSubstring("No filename could be determined")))
		})

		It("Should set the local source and local destination on Job", func() {
			dbInstance.StoreJob(exampleJob)
			downloader(logger, configPath, dbInstance, exampleJob.ID)
			changedJob, _ := dbInstance.RetrieveJob("123")

			cfg, _ := gonfig.FromJsonFile(configPath)
			swapDir, _ := cfg.GetString("SWAP_DIRECTORY", "")

			sourceExpected := swapDir + "123/src/source_here.mp4"
			Expect(changedJob.LocalSource).To(Equal(sourceExpected))

			destinationExpected := swapDir + "123/dst/source_here_240p.mp4"
			Expect(changedJob.LocalDestination).To(Equal(destinationExpected))
		})
	}

	Context("HTTP Downloader", func() {
		BeforeEach(func() {
			downloader = HTTPDownload
			exampleJob = types.Job{
				ID:          "123",
				Source:      "http://source_here.mp4",
				Destination: "s3://user@pass:/bucket/",
				Preset:      types.Preset{Name: "240p", Container: "mp4"},
				Status:      types.JobCreated,
				Details:     "",
			}
		})

		runDatabaseSuite()
	})
})
<commit_msg>downloaders: add some tests for FTPDownloader<commit_after>package downloaders

import (
	"os"

	"code.cloudfoundry.org/lager/lagertest"
	"github.com/flavioribeiro/gonfig"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/snickers/snickers/db"
	"github.com/snickers/snickers/db/memory"
	"github.com/snickers/snickers/types"
)

var _ = Describe("Downloaders", func() {
	var (
		logger     *lagertest.TestLogger
		dbInstance db.Storage
		downloader DownloadFunc
		exampleJob types.Job
		configPath string
	)

	BeforeEach(func() {
		logger = lagertest.NewTestLogger("http-download")
		dbInstance, _ = memory.GetDatabase()
		dbInstance.ClearDatabase()
		currentDir, _ := os.Getwd()
		configPath = currentDir + "/../fixtures/config.json"
	})

	runDatabaseSuite := func() {
		It("should return an error if source couldn't be fetched", func() {
			dbInstance.StoreJob(exampleJob)
			err := downloader(logger, configPath, dbInstance, exampleJob.ID)
			Expect(err.Error()).To(SatisfyAny(ContainSubstring("no such host"), ContainSubstring("No filename could be determined")))
		})

		It("Should set the local source and local destination on Job", func() {
			dbInstance.StoreJob(exampleJob)
			downloader(logger, configPath, dbInstance, exampleJob.ID)
			changedJob, _ := dbInstance.RetrieveJob("123")

			cfg, _ := gonfig.FromJsonFile(configPath)
			swapDir, _ := cfg.GetString("SWAP_DIRECTORY", "")

			sourceExpected := swapDir + "123/src/source_here.mp4"
			Expect(changedJob.LocalSource).To(Equal(sourceExpected))

			destinationExpected := swapDir + "123/dst/source_here_240p.mp4"
			Expect(changedJob.LocalDestination).To(Equal(destinationExpected))
		})
	}

	Context("HTTP Downloader", func() {
		BeforeEach(func() {
			downloader = HTTPDownload
			exampleJob = types.Job{
				ID:          "123",
				Source:      "http://source_here.mp4",
				Destination: "s3://user@pass:/bucket/",
				Preset:      types.Preset{Name: "240p", Container: "mp4"},
				Status:      types.JobCreated,
				Details:     "",
			}
		})

		runDatabaseSuite()
	})

	Context("FTP Downloader", func() {
		BeforeEach(func() {
			downloader = FTPDownload
			exampleJob = types.Job{
				ID:          "123",
				Source:      "ftp://login:password@host/source_here.mp4",
				Destination: "s3://user@pass:/bucket/",
				Preset:      types.Preset{Name: "240p", Container: "mp4"},
				Status:      types.JobCreated,
				Details:     "",
			}
		})

		runDatabaseSuite()
	})

})
<|endoftext|>


<commit_before>package kafka

import (
	"errors"
	"sync"
	"time"
)

// backend stores information about a given backend. All access to this data should be done
// through methods to ensure accurate counting and limiting.
type backend struct {
	conf    BrokerConf
	addr    string
	channel chan *connection

	// Used for storing links to all connections we ever make, this is a debugging
	// tool to try to help find leaks of connections. All access is protected by mu.
	mu        *sync.Mutex
	conns     []*connection
	counter   int
	debugTime time.Time
}

// getIdleConnection returns a connection if and only if there is an active, idle connection
// that already exists.
func (b *backend) GetIdleConnection() *connection {
	for {
		select {
		case conn := <-b.channel:
			if !conn.IsClosed() {
				return conn
			}
			b.removeConnection(conn)

		default:
			return nil
		}
	}
}

// GetConnection does a full connection logic: attempt to return an idle connection, if
// none are available then wait for up to the IdleConnectionWait time for one, else finally
// establish a new connection if we aren't at the limit. If we are, then continue waiting
// in increments of the idle time for a connection or the limit to come down before making
// a new connection. This could potentially block up to the DialTimeout.
func (b *backend) GetConnection() *connection {
	dialTimeout := time.After(b.conf.DialTimeout)
	for {
		select {
		case <-dialTimeout:
			return nil

		case conn := <-b.channel:
			if !conn.IsClosed() {
				return conn
			}
			b.removeConnection(conn)

		case <-time.After(time.Duration(rndIntn(int(b.conf.IdleConnectionWait)))):
			conn, err := b.getNewConnection()
			if err != nil {
				return nil
			} else if conn != nil {
				return conn
			}
		}
	}
}

// debugHitMaxConnections will potentially do some debugging output to help diagnose situations
// where we're hitting connection limits.
func (b *backend) debugHitMaxConnections() {
	b.mu.Lock()
	defer b.mu.Unlock()

	if time.Now().Before(b.debugTime) {
		return
	}
	b.debugTime = time.Now().Add(10 * time.Second)

	log.Warn("DEBUG: hit max connections",
		"counter", b.counter,
		"len(conns)", len(b.conns))
	for idx, conn := range b.conns {
		log.Warn("DEBUG: connection",
			"idx", idx,
			"conn", conn,
			"closed", conn.IsClosed(),
			"age", time.Now().Sub(conn.StartTime()))
	}
}

// getNewConnection establishes a new connection if and only if we haven't hit the limit, else
// it will return nil. If an error is returned, we failed to connect to the server and should
// abort the flow. This takes a lock on the mutex which means we can only have a single new
// connection request in-flight at one time. Takes the mutex.
func (b *backend) getNewConnection() (*connection, error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if b.counter >= b.conf.ConnectionLimit {
		go b.debugHitMaxConnections()
		return nil, nil
	}

	// Be careful about the situation where newTCPConnection could never return, so
	// we want to always make sure getNewConnection eventually returns. Else, we can
	// lose the connection pool.

	type connResult struct {
		conn *connection
		err  error
	}
	connChan := make(chan connResult, 1)

	go func() {
		log.Debug("making new connection", "addr", b.addr)
		if conn, err := newTCPConnection(b.addr, b.conf.DialTimeout); err != nil {
			log.Error("cannot connect", "addr", b.addr, "error", err)
			connChan <- connResult{nil, err}
		} else {
			connChan <- connResult{conn, nil}
		}
	}()

	select {
	case <-time.After(b.conf.DialTimeout):
		log.Error("DEBUG: timeout waiting for dial", "addr", b.addr)
		return nil, nil

	case result := <-connChan:
		if result.err != nil {
			return nil, result.err
		} else {
			b.counter++
			b.conns = append(b.conns, result.conn)
			return result.conn, nil
		}
	}

}

// removeConnection removes the given connection from our tracking. It also decrements the
// open connection count. This takes the mutex.
func (b *backend) removeConnection(conn *connection) {
	b.mu.Lock()
	defer b.mu.Unlock()

	for idx, c := range b.conns {
		if c == conn {
			b.counter--
			b.conns = append(b.conns[0:idx], b.conns[idx+1:]...)
			return
		}
	}
	log.Error("unknown connection in removeConnection", "conn", conn)
}

// Idle is called when a connection should be returned to the store.
func (b *backend) Idle(conn *connection) {
	// If the connection is closed, throw it away. But if the connection pool is closed, then
	// close the connection.
	if conn.IsClosed() {
		b.removeConnection(conn)
		return
	}

	// If we're above the idle connection limit, discard the connection.
	if len(b.channel) >= b.conf.IdleConnectionLimit {
		conn.Close()
		b.removeConnection(conn)
		return
	}

	select {
	case b.channel <- conn:
		// Do nothing, connection was requeued.
	case <-time.After(b.conf.IdleConnectionWait):
		// The queue is full for a while, discard this connection.
		b.removeConnection(conn)
		conn.Close()
	}
}

// NumOpenConnections returns a counter of how may connections are open.
func (b *backend) NumOpenConnections() int {
	b.mu.Lock()
	defer b.mu.Unlock()

	return b.counter
}

// connectionPool is a way for us to manage multiple connections to a Kafka broker in a way
// that balances out throughput with overall number of connections.
type connectionPool struct {
	conf BrokerConf

	// mu protects the below members of this struct. This mutex must only be used by
	// connectionPool.
	mu       *sync.RWMutex
	closed   bool
	backends map[string]*backend
	addrs    []string
}

// newConnectionPool creates a connection pool and initializes it.
func newConnectionPool(conf BrokerConf) *connectionPool {
	return &connectionPool{
		conf:     conf,
		mu:       &sync.RWMutex{},
		backends: make(map[string]*backend),
		addrs:    make([]string, 0),
	}
}

// newBackend creates a new backend structure.
func (cp *connectionPool) newBackend(addr string) *backend {
	return &backend{
		mu:      &sync.Mutex{},
		conf:    cp.conf,
		addr:    addr,
		channel: make(chan *connection, cp.conf.IdleConnectionLimit),
	}
}

// getBackend fetches a channel for a given address. This takes the read lock. If no
// channel exists, nil is returned.
func (cp *connectionPool) getBackend(addr string) *backend {
	cp.mu.RLock()
	defer cp.mu.RUnlock()

	if cp.closed {
		return nil
	}

	if _, ok := cp.backends[addr]; !ok {
		return nil
	}
	return cp.backends[addr]
}

// getOrCreateBackend fetches a channel for a given address and, if one doesn't exist,
// creates it. This function takes the write lock against the pool.
func (cp *connectionPool) getOrCreateBackend(addr string) *backend {
	// Fast path: only gets a read lock
	if be := cp.getBackend(addr); be != nil {
		return be
	}

	// Did not exist, take the slow path and make a new backend
	cp.mu.Lock()
	defer cp.mu.Unlock()

	if cp.closed {
		return nil
	}

	if _, ok := cp.backends[addr]; !ok {
		cp.addrs = append(cp.addrs, addr)
		cp.backends[addr] = cp.newBackend(addr)
	}
	return cp.backends[addr]
}

// GetAllAddrs returns a slice of all addresses we've seen. Can be used for picking a random
// address or iterating the known brokers.
func (cp *connectionPool) GetAllAddrs() []string {
	cp.mu.RLock()
	defer cp.mu.RUnlock()

	ret := make([]string, len(cp.addrs))
	copy(ret, cp.addrs)
	return ret
}

// InitializeAddrs takes in a set of addresses and just sets up the structures for them. This
// doesn't start any connecting. This is done so that we have a set of addresses for other
// parts of the system to use.
func (cp *connectionPool) InitializeAddrs(addrs []string) {
	cp.mu.Lock()
	defer cp.mu.Unlock()

	for _, addr := range addrs {
		if _, ok := cp.backends[addr]; !ok {
			cp.addrs = append(cp.addrs, addr)
			cp.backends[addr] = cp.newBackend(addr)
		}
	}
}

// GetIdleConnection returns a random idle connection from the set of connections that we
// happen to have open. If no connections are available or idle, this returns nil.
func (cp *connectionPool) GetIdleConnection() *connection {
	addrs := cp.GetAllAddrs()

	for _, idx := range rndPerm(len(addrs)) {
		if be := cp.getOrCreateBackend(addrs[idx]); be != nil {
			if conn := be.GetIdleConnection(); conn != nil {
				return conn
			}
		}
	}
	return nil
}

// GetConnectionByAddr takes an address and returns a valid/open connection to this server.
// We attempt to reuse connections if we can, but if a connection is not available within
// IdleConnectionWait then we'll establish a new one. This can block a long time.
func (cp *connectionPool) GetConnectionByAddr(addr string) (*connection, error) {
	if cp.IsClosed() {
		return nil, errors.New("connection pool is closed")
	}

	if be := cp.getOrCreateBackend(addr); be != nil {
		if conn := be.GetConnection(); conn != nil {
			return conn, nil
		}
	}
	return nil, errors.New("failed to get connection")
}

// Close sets the connection pool's end state, no further connections will be returned
// and any existing connections will be closed out.
func (cp *connectionPool) Close() {
	cp.mu.Lock()
	defer cp.mu.Unlock()

	for _, backend := range cp.backends {
	Loop:
		for {
			select {
			case conn := <-backend.channel:
				defer conn.Close()
			default:
				break Loop
			}
		}
	}

	cp.closed = true
}

// IsClosed returns whether or not this pool is closed.
func (cp *connectionPool) IsClosed() bool {
	cp.mu.RLock()
	defer cp.mu.RUnlock()

	return cp.closed
}

// Idle takes a now idle connection and makes it available for other users. This should be
// called in a goroutine so as not to block the original caller, as this function may take
// some time to return.
func (cp *connectionPool) Idle(conn *connection) {
	if cp.IsClosed() {
		conn.Close()
		return
	}

	if be := cp.getOrCreateBackend(conn.addr); be != nil {
		be.Idle(conn)
	} else {
		conn.Close()
	}
}
<commit_msg>Avoid leaking closed connections<commit_after>package kafka

import (
	"errors"
	"sync"
	"time"
)

// backend stores information about a given backend. All access to this data should be done
// through methods to ensure accurate counting and limiting.
type backend struct {
	conf    BrokerConf
	addr    string
	channel chan *connection

	// Used for storing links to all connections we ever make, this is a debugging
	// tool to try to help find leaks of connections. All access is protected by mu.
	mu        *sync.Mutex
	conns     []*connection
	counter   int
	debugTime time.Time
}

// getIdleConnection returns a connection if and only if there is an active, idle connection
// that already exists.
func (b *backend) GetIdleConnection() *connection {
	for {
		select {
		case conn := <-b.channel:
			if !conn.IsClosed() {
				return conn
			}
			b.removeConnection(conn)

		default:
			return nil
		}
	}
}

// GetConnection does a full connection logic: attempt to return an idle connection, if
// none are available then wait for up to the IdleConnectionWait time for one, else finally
// establish a new connection if we aren't at the limit. If we are, then continue waiting
// in increments of the idle time for a connection or the limit to come down before making
// a new connection. This could potentially block up to the DialTimeout.
func (b *backend) GetConnection() *connection {
	dialTimeout := time.After(b.conf.DialTimeout)
	for {
		select {
		case <-dialTimeout:
			return nil

		case conn := <-b.channel:
			if !conn.IsClosed() {
				return conn
			}
			b.removeConnection(conn)

		case <-time.After(time.Duration(rndIntn(int(b.conf.IdleConnectionWait)))):
			conn, err := b.getNewConnection()
			if err != nil {
				return nil
			} else if conn != nil {
				return conn
			}
		}
	}
}

// debugHitMaxConnections will potentially do some debugging output to help diagnose situations
// where we're hitting connection limits.
func (b *backend) debugHitMaxConnections() {
	b.mu.Lock()
	defer b.mu.Unlock()

	if time.Now().Before(b.debugTime) {
		return
	}
	b.debugTime = time.Now().Add(10 * time.Second)

	log.Warn("DEBUG: hit max connections",
		"counter", b.counter,
		"len(conns)", len(b.conns))
	for idx, conn := range b.conns {
		log.Warn("DEBUG: connection",
			"idx", idx,
			"conn", conn,
			"closed", conn.IsClosed(),
			"age", time.Now().Sub(conn.StartTime()))
	}
}

// getNewConnection establishes a new connection if and only if we haven't hit the limit, else
// it will return nil. If an error is returned, we failed to connect to the server and should
// abort the flow. This takes a lock on the mutex which means we can only have a single new
// connection request in-flight at one time. Takes the mutex.
func (b *backend) getNewConnection() (*connection, error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if b.counter >= b.conf.ConnectionLimit {
		go b.debugHitMaxConnections()
		return nil, nil
	}

	// Be careful about the situation where newTCPConnection could never return, so
	// we want to always make sure getNewConnection eventually returns. Else, we can
	// lose the connection pool.

	type connResult struct {
		conn *connection
		err  error
	}
	connChan := make(chan connResult, 1)

	go func() {
		log.Debug("making new connection", "addr", b.addr)
		if conn, err := newTCPConnection(b.addr, b.conf.DialTimeout); err != nil {
			log.Error("cannot connect", "addr", b.addr, "error", err)
			connChan <- connResult{nil, err}
		} else {
			connChan <- connResult{conn, nil}
		}
	}()

	select {
	case <-time.After(b.conf.DialTimeout):
		log.Error("DEBUG: timeout waiting for dial", "addr", b.addr)
		return nil, nil

	case result := <-connChan:
		if result.err != nil {
			return nil, result.err
		} else {
			b.counter++
			b.conns = append(b.conns, result.conn)
			return result.conn, nil
		}
	}

}

// removeConnection removes the given connection from our tracking. It also decrements the
// open connection count. This takes the mutex.
func (b *backend) removeConnection(conn *connection) {
	b.mu.Lock()
	defer b.mu.Unlock()

	for idx, c := range b.conns {
		if c == conn {
			b.counter--
			b.conns = append(b.conns[0:idx], b.conns[idx+1:]...)
			return
		}
	}
	log.Error("unknown connection in removeConnection", "conn", conn)
}

// Idle is called when a connection should be returned to the store.
func (b *backend) Idle(conn *connection) {
	// If the connection is closed, throw it away. But if the connection pool is closed, then
	// close the connection.
	if conn.IsClosed() {
		b.removeConnection(conn)
		return
	}

	// If we're above the idle connection limit, discard the connection.
	if len(b.channel) >= b.conf.IdleConnectionLimit {
		conn.Close()
		b.removeConnection(conn)
		return
	}

	select {
	case b.channel <- conn:
		// Do nothing, connection was requeued.
	case <-time.After(b.conf.IdleConnectionWait):
		// The queue is full for a while, discard this connection.
		b.removeConnection(conn)
		conn.Close()
	}
}

// NumOpenConnections returns a counter of how may connections are open.
func (b *backend) NumOpenConnections() int {
	b.mu.Lock()
	defer b.mu.Unlock()

	return b.counter
}

// connectionPool is a way for us to manage multiple connections to a Kafka broker in a way
// that balances out throughput with overall number of connections.
type connectionPool struct {
	conf BrokerConf

	// mu protects the below members of this struct. This mutex must only be used by
	// connectionPool.
	mu       *sync.RWMutex
	closed   bool
	backends map[string]*backend
	addrs    []string
}

// newConnectionPool creates a connection pool and initializes it.
func newConnectionPool(conf BrokerConf) *connectionPool {
	return &connectionPool{
		conf:     conf,
		mu:       &sync.RWMutex{},
		backends: make(map[string]*backend),
		addrs:    make([]string, 0),
	}
}

// newBackend creates a new backend structure.
func (cp *connectionPool) newBackend(addr string) *backend {
	return &backend{
		mu:      &sync.Mutex{},
		conf:    cp.conf,
		addr:    addr,
		channel: make(chan *connection, cp.conf.IdleConnectionLimit),
	}
}

// getBackend fetches a channel for a given address. This takes the read lock. If no
// channel exists, nil is returned.
func (cp *connectionPool) getBackend(addr string) *backend {
	cp.mu.RLock()
	defer cp.mu.RUnlock()

	if cp.closed {
		return nil
	}

	if _, ok := cp.backends[addr]; !ok {
		return nil
	}
	return cp.backends[addr]
}

// getOrCreateBackend fetches a channel for a given address and, if one doesn't exist,
// creates it. This function takes the write lock against the pool.
func (cp *connectionPool) getOrCreateBackend(addr string) *backend {
	// Fast path: only gets a read lock
	if be := cp.getBackend(addr); be != nil {
		return be
	}

	// Did not exist, take the slow path and make a new backend
	cp.mu.Lock()
	defer cp.mu.Unlock()

	if cp.closed {
		return nil
	}

	if _, ok := cp.backends[addr]; !ok {
		cp.addrs = append(cp.addrs, addr)
		cp.backends[addr] = cp.newBackend(addr)
	}
	return cp.backends[addr]
}

// GetAllAddrs returns a slice of all addresses we've seen. Can be used for picking a random
// address or iterating the known brokers.
func (cp *connectionPool) GetAllAddrs() []string {
	cp.mu.RLock()
	defer cp.mu.RUnlock()

	ret := make([]string, len(cp.addrs))
	copy(ret, cp.addrs)
	return ret
}

// InitializeAddrs takes in a set of addresses and just sets up the structures for them. This
// doesn't start any connecting. This is done so that we have a set of addresses for other
// parts of the system to use.
func (cp *connectionPool) InitializeAddrs(addrs []string) {
	cp.mu.Lock()
	defer cp.mu.Unlock()

	for _, addr := range addrs {
		if _, ok := cp.backends[addr]; !ok {
			cp.addrs = append(cp.addrs, addr)
			cp.backends[addr] = cp.newBackend(addr)
		}
	}
}

// GetIdleConnection returns a random idle connection from the set of connections that we
// happen to have open. If no connections are available or idle, this returns nil.
func (cp *connectionPool) GetIdleConnection() *connection {
	addrs := cp.GetAllAddrs()

	for _, idx := range rndPerm(len(addrs)) {
		if be := cp.getOrCreateBackend(addrs[idx]); be != nil {
			if conn := be.GetIdleConnection(); conn != nil {
				return conn
			}
		}
	}
	return nil
}

// GetConnectionByAddr takes an address and returns a valid/open connection to this server.
// We attempt to reuse connections if we can, but if a connection is not available within
// IdleConnectionWait then we'll establish a new one. This can block a long time.
func (cp *connectionPool) GetConnectionByAddr(addr string) (*connection, error) {
	if cp.IsClosed() {
		return nil, errors.New("connection pool is closed")
	}

	if be := cp.getOrCreateBackend(addr); be != nil {
		if conn := be.GetConnection(); conn != nil {
			return conn, nil
		}
	}
	return nil, errors.New("failed to get connection")
}

// Close sets the connection pool's end state, no further connections will be returned
// and any existing connections will be closed out.
func (cp *connectionPool) Close() {
	cp.mu.Lock()
	defer cp.mu.Unlock()

	for _, backend := range cp.backends {
	Loop:
		for {
			select {
			case conn := <-backend.channel:
				defer conn.Close()
			default:
				break Loop
			}
		}
	}

	cp.closed = true
}

// IsClosed returns whether or not this pool is closed.
func (cp *connectionPool) IsClosed() bool {
	cp.mu.RLock()
	defer cp.mu.RUnlock()

	return cp.closed
}

// Idle takes a now idle connection and makes it available for other users. This should be
// called in a goroutine so as not to block the original caller, as this function may take
// some time to return.
func (cp *connectionPool) Idle(conn *connection) {
	if be := cp.getOrCreateBackend(conn.addr); be != nil {
		be.Idle(conn)
	} else {
		conn.Close()
	}
}
<|endoftext|>


<commit_before>package gatt

import (
	"log"
	"sync"

	"github.com/currantlabs/ble"
	"github.com/currantlabs/ble/linux/att"
)

// NewServer ...
func NewServer() (*Server, error) {
	return &Server{
		svcs: defaultServices("Gopher"),
		db:   att.NewDB(defaultServices("Gopher"), uint16(1)),
	}, nil
}

// Server ...
type Server struct {
	sync.Mutex

	svcs []*ble.Service
	db   *att.DB
}

// AddService ...
func (s *Server) AddService(svc *ble.Service) error {
	s.Lock()
	defer s.Unlock()
	s.svcs = append(s.svcs, svc)
	s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
	return nil
}

// RemoveAllServices ...
func (s *Server) RemoveAllServices() error {
	s.Lock()
	defer s.Unlock()
	s.svcs = defaultServices("Gopher")
	s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
	return nil
}

// SetServices ...
func (s *Server) SetServices(svcs []*ble.Service) error {
	s.Lock()
	defer s.Unlock()
	s.svcs = append(defaultServices("Gopher"), svcs...)
	s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
	return nil
}

// DB ...
func (s *Server) DB() *att.DB {
	return s.db
}

func defaultServices(name string) []*ble.Service {
	// https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.ble.appearance.xml
	var gapCharAppearanceGenericComputer = []byte{0x00, 0x80}

	gapSvc := ble.NewService(ble.GAPUUID)
	gapSvc.NewCharacteristic(ble.DeviceNameUUID).SetValue([]byte(name))
	gapSvc.NewCharacteristic(ble.AppearanceUUID).SetValue(gapCharAppearanceGenericComputer)
	gapSvc.NewCharacteristic(ble.PeripheralPrivacyUUID).SetValue([]byte{0x00})
	gapSvc.NewCharacteristic(ble.ReconnectionAddrUUID).SetValue([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
	gapSvc.NewCharacteristic(ble.PeferredParamsUUID).SetValue([]byte{0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0x07})

	gattSvc := ble.NewService(ble.GATTUUID)
	gattSvc.NewCharacteristic(ble.ServiceChangedUUID).HandleIndicate(
		ble.NotifyHandlerFunc(func(r ble.Request, n ble.Notifier) {
			log.Printf("TODO: indicate client when the services are changed")
			for {
				select {
				case <-n.Context().Done():
					log.Printf("count: Notification unsubscribed")
					return
				}
			}
		}))
	return []*ble.Service{gapSvc, gattSvc}
}
<commit_msg>Allow overriding device name<commit_after>package gatt

import (
	"log"
	"sync"

	"github.com/currantlabs/ble"
	"github.com/currantlabs/ble/linux/att"
)

// NewServer ...
func NewServerWithName(name string) (*Server, error) {
	return &Server{
		name:name,
		svcs: defaultServices(name),
		db:   att.NewDB(defaultServices(name), uint16(1)),
	}, nil
}

// NewServer ...
func NewServer() (*Server, error) {
	return NewServerWithName("Gopher")
}

// Server ...
type Server struct {
	sync.Mutex
	name string

	svcs []*ble.Service
	db   *att.DB
}

// AddService ...
func (s *Server) AddService(svc *ble.Service) error {
	s.Lock()
	defer s.Unlock()
	s.svcs = append(s.svcs, svc)
	s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
	return nil
}

// RemoveAllServices ...
func (s *Server) RemoveAllServices() error {
	s.Lock()
	defer s.Unlock()
	s.svcs = defaultServices(s.name)
	s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
	return nil
}

// SetServices ...
func (s *Server) SetServices(svcs []*ble.Service) error {
	s.Lock()
	defer s.Unlock()
	s.svcs = append(defaultServices(s.name), svcs...)
	s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
	return nil
}

// DB ...
func (s *Server) DB() *att.DB {
	return s.db
}

func defaultServices(name string) []*ble.Service {
	// https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.ble.appearance.xml
	var gapCharAppearanceGenericComputer = []byte{0x00, 0x80}

	gapSvc := ble.NewService(ble.GAPUUID)
	gapSvc.NewCharacteristic(ble.DeviceNameUUID).SetValue([]byte(name))
	gapSvc.NewCharacteristic(ble.AppearanceUUID).SetValue(gapCharAppearanceGenericComputer)
	gapSvc.NewCharacteristic(ble.PeripheralPrivacyUUID).SetValue([]byte{0x00})
	gapSvc.NewCharacteristic(ble.ReconnectionAddrUUID).SetValue([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
	gapSvc.NewCharacteristic(ble.PeferredParamsUUID).SetValue([]byte{0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0x07})

	gattSvc := ble.NewService(ble.GATTUUID)
	gattSvc.NewCharacteristic(ble.ServiceChangedUUID).HandleIndicate(
		ble.NotifyHandlerFunc(func(r ble.Request, n ble.Notifier) {
			log.Printf("TODO: indicate client when the services are changed")
			for {
				select {
				case <-n.Context().Done():
					log.Printf("count: Notification unsubscribed")
					return
				}
			}
		}))
	return []*ble.Service{gapSvc, gattSvc}
}
<|endoftext|>


<commit_before>package logging

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"testing"

	"github.com/mongodb/grip/level"
	"github.com/mongodb/grip/message"
	"github.com/mongodb/grip/send"
	"github.com/stretchr/testify/suite"
)

type GripInternalSuite struct {
	grip *Grip
	name string
	suite.Suite
}

func TestGripSuite(t *testing.T) {
	suite.Run(t, new(GripInternalSuite))
}

func (s *GripInternalSuite) SetupSuite() {
	s.name = "test"
	s.grip = NewGrip(s.name)
	s.Equal(s.grip.Name(), s.name)
}

func (s *GripInternalSuite) SetupTest() {
	s.grip.SetName(s.name)
	sender, err := send.NewNativeLogger(s.grip.Name(), s.grip.GetSender().Level())
	s.NoError(err)
	s.NoError(s.grip.SetSender(sender))
}

func (s *GripInternalSuite) TestPanicSenderActuallyPanics() {
	// both of these are in anonymous functions so that the defers
	// cover the correct area.

	func() {
		// first make sure that the default send method doesn't panic
		defer func() {
			s.Nil(recover())
		}()

		s.grip.GetSender().Send(message.NewLineMessage(s.grip.DefaultLevel(), "foo"))
	}()

	func() {
		// call a panic function with a recoverer set.
		defer func() {
			s.NotNil(recover())
		}()

		s.grip.sendPanic(message.NewLineMessage(s.grip.DefaultLevel(), "foo"))
	}()
}

func (s *GripInternalSuite) TestPanicSenderRespectsTThreshold() {
	s.True(level.Debug < s.grip.DefaultLevel())

	// test that there is a no panic if the message isn't "logabble"
	defer func() {
		s.Nil(recover())
	}()

	s.grip.sendPanic(message.NewLineMessage(level.Debug, "foo"))
}

func (s *GripInternalSuite) TestConditionalSend() {
	// because sink is an internal type (implementation of
	// sender,) and "GetMessage" isn't in the interface, though it
	// is exported, we can't pass the sink between functions.
	sink, err := send.NewInternalLogger("sink", s.grip.GetSender().Level())
	s.NoError(err)
	s.NoError(s.grip.SetSender(sink))

	msg := message.NewLineMessage(level.Info, "foo")
	msgTwo := message.NewLineMessage(level.Notice, "bar")

	// when the conditional argument is true, it should work
	s.grip.conditionalSend(true, msg)
	s.Equal(sink.GetMessage().Message, msg)

	// when the conditional argument is true, it should work, and the channel is fifo
	s.grip.conditionalSend(false, msgTwo)
	s.grip.conditionalSend(true, msg)
	s.Equal(sink.GetMessage().Message, msg)

	// change the order
	s.grip.conditionalSend(true, msg)
	s.grip.conditionalSend(false, msgTwo)
	s.Equal(sink.GetMessage().Message, msg)
}

func (s *GripInternalSuite) TestCatchMethods() {

	sink, err := send.NewInternalLogger("sink", send.LevelInfo{level.Trace, level.Trace})
	s.NoError(err)
	s.NoError(s.grip.SetSender(sink))

	cases := []interface{}{
		s.grip.CatchAlert,
		s.grip.CatchCritical,
		s.grip.CatchDebug,
		s.grip.CatchEmergency,
		s.grip.CatchError,
		s.grip.CatchInfo,
		s.grip.CatchNotice,
		s.grip.CatchWarning,

		s.grip.Alert,
		s.grip.Critical,
		s.grip.Debug,
		s.grip.Emergency,
		s.grip.Error,
		s.grip.Info,
		s.grip.Notice,
		s.grip.Warning,

		s.grip.Alertln,
		s.grip.Criticalln,
		s.grip.Debugln,
		s.grip.Emergencyln,
		s.grip.Errorln,
		s.grip.Infoln,
		s.grip.Noticeln,
		s.grip.Warningln,

		s.grip.Alertf,
		s.grip.Criticalf,
		s.grip.Debugf,
		s.grip.Emergencyf,
		s.grip.Errorf,
		s.grip.Infof,
		s.grip.Noticef,
		s.grip.Warningf,

		s.grip.AlertWhen,
		s.grip.CriticalWhen,
		s.grip.DebugWhen,
		s.grip.EmergencyWhen,
		s.grip.ErrorWhen,
		s.grip.InfoWhen,
		s.grip.NoticeWhen,
		s.grip.WarningWhen,

		s.grip.AlertWhenln,
		s.grip.CriticalWhenln,
		s.grip.DebugWhenln,
		s.grip.EmergencyWhenln,
		s.grip.ErrorWhenln,
		s.grip.InfoWhenln,
		s.grip.NoticeWhenln,
		s.grip.WarningWhenln,

		s.grip.AlertWhenf,
		s.grip.CriticalWhenf,
		s.grip.DebugWhenf,
		s.grip.EmergencyWhenf,
		s.grip.ErrorWhenf,
		s.grip.InfoWhenf,
		s.grip.NoticeWhenf,
		s.grip.WarningWhenf,

		s.grip.AlertMany,
		s.grip.CriticalMany,
		s.grip.DebugMany,
		s.grip.EmergencyMany,
		s.grip.ErrorMany,
		s.grip.InfoMany,
		s.grip.NoticeMany,
		s.grip.WarningMany,

		s.grip.AlertManyWhen,
		s.grip.CriticalManyWhen,
		s.grip.DebugManyWhen,
		s.grip.EmergencyManyWhen,
		s.grip.ErrorManyWhen,
		s.grip.InfoManyWhen,
		s.grip.NoticeManyWhen,
		s.grip.WarningManyWhen,
	}

	const msg = "hello world!"
	multiMessage := []message.Composer{
		message.ConvertToComposer(0, nil),
		message.ConvertToComposer(0, msg),
	}

	for _, logger := range cases {
		s.Equal(0, sink.Len())
		s.False(sink.HasMessage())

		switch log := logger.(type) {
		case func(error):
			log(errors.New(msg))
		case func(interface{}):
			log(msg)
		case func(...interface{}):
			log(msg, "", nil)
		case func(string, ...interface{}):
			log("%s", msg)
		case func(bool, interface{}):
			log(false, msg)
			log(true, msg)
		case func(bool, ...interface{}):
			log(false, msg, "", nil)
			log(true, msg, "", nil)
		case func(bool, string, ...interface{}):
			log(false, "%s", msg)
			log(true, "%s", msg)
		case func(...message.Composer):
			log(multiMessage...)
		case func(bool, ...message.Composer):
			log(false, multiMessage...)
			log(true, multiMessage...)
		default:
			panic(fmt.Sprintf("%T is not supported\n", log))
		}

		if sink.Len() == 1 {
			s.True(sink.Len() == 1)
			s.True(sink.HasMessage())
			out := sink.GetMessage()
			s.Equal(out.Rendered, msg)
			if out.Priority != level.Debug {
				s.True(out.Logged, fmt.Sprintf("%T %s", logger, out.Priority))
			}
		} else {

			var numLogged int
			out := sink.GetMessage()
			for i := 0; i < sink.Len(); i++ {
				out = sink.GetMessage()
				if out.Logged {
					numLogged++
					s.Equal(out.Rendered, msg)
				}
			}
			if out.Priority != level.Debug {
				s.True(numLogged == 1, fmt.Sprintf("%T: %d %s", logger, numLogged, out.Priority))
			}
		}
	}

}

// This testing method uses the technique outlined in:
// http://stackoverflow.com/a/33404435 to test a function that exits
// since it's impossible to "catch" an os.Exit
func TestSendFatalExits(t *testing.T) {
	grip := NewGrip("test")
	if os.Getenv("SHOULD_CRASH") == "1" {
		grip.sendFatal(message.NewLineMessage(grip.DefaultLevel(), "foo"))
		return
	}

	cmd := exec.Command(os.Args[0], "-test.run=TestSendFatalExits")
	cmd.Env = append(os.Environ(), "SHOULD_CRASH=1")
	err := cmd.Run()
	if err == nil {
		t.Errorf("sendFatal should have exited 0, instead: %+v", err)
	}
}
<commit_msg>add additional test cases for logging methods<commit_after>package logging

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"testing"

	"github.com/mongodb/grip/level"
	"github.com/mongodb/grip/message"
	"github.com/mongodb/grip/send"
	"github.com/stretchr/testify/suite"
)

type GripInternalSuite struct {
	grip *Grip
	name string
	suite.Suite
}

func TestGripSuite(t *testing.T) {
	suite.Run(t, new(GripInternalSuite))
}

func (s *GripInternalSuite) SetupSuite() {
	s.name = "test"
	s.grip = NewGrip(s.name)
	s.Equal(s.grip.Name(), s.name)
	s.grip.SetThreshold(level.Trace)
}

func (s *GripInternalSuite) SetupTest() {
	s.grip.SetName(s.name)
	sender, err := send.NewNativeLogger(s.grip.Name(), s.grip.GetSender().Level())
	s.NoError(err)
	s.NoError(s.grip.SetSender(sender))
}

func (s *GripInternalSuite) TestPanicSenderActuallyPanics() {
	// both of these are in anonymous functions so that the defers
	// cover the correct area.

	func() {
		// first make sure that the default send method doesn't panic
		defer func() {
			s.Nil(recover())
		}()

		s.grip.GetSender().Send(message.NewLineMessage(s.grip.DefaultLevel(), "foo"))
	}()

	func() {
		// call a panic function with a recoverer set.
		defer func() {
			s.NotNil(recover())
		}()

		s.grip.sendPanic(message.NewLineMessage(s.grip.DefaultLevel(), "foo"))
	}()
}

func (s *GripInternalSuite) TestPanicSenderRespectsTThreshold() {
	s.grip.SetThreshold(level.Notice)
	s.True(level.Debug < s.grip.DefaultLevel())

	// test that there is a no panic if the message isn't "logabble"
	defer func() {
		s.Nil(recover())
	}()

	s.grip.sendPanic(message.NewLineMessage(level.Debug, "foo"))
}

func (s *GripInternalSuite) TestConditionalSend() {
	// because sink is an internal type (implementation of
	// sender,) and "GetMessage" isn't in the interface, though it
	// is exported, we can't pass the sink between functions.
	sink, err := send.NewInternalLogger("sink", s.grip.GetSender().Level())
	s.NoError(err)
	s.NoError(s.grip.SetSender(sink))

	msg := message.NewLineMessage(level.Info, "foo")
	msgTwo := message.NewLineMessage(level.Notice, "bar")

	// when the conditional argument is true, it should work
	s.grip.conditionalSend(true, msg)
	s.Equal(sink.GetMessage().Message, msg)

	// when the conditional argument is true, it should work, and the channel is fifo
	s.grip.conditionalSend(false, msgTwo)
	s.grip.conditionalSend(true, msg)
	s.Equal(sink.GetMessage().Message, msg)

	// change the order
	s.grip.conditionalSend(true, msg)
	s.grip.conditionalSend(false, msgTwo)
	s.Equal(sink.GetMessage().Message, msg)
}

func (s *GripInternalSuite) TestCatchMethods() {

	sink, err := send.NewInternalLogger("sink", send.LevelInfo{level.Trace, level.Trace})
	s.NoError(err)
	s.NoError(s.grip.SetSender(sink))

	cases := []interface{}{
		s.grip.CatchAlert,
		s.grip.CatchCritical,
		s.grip.CatchDebug,
		s.grip.CatchEmergency,
		s.grip.CatchError,
		s.grip.CatchInfo,
		s.grip.CatchNotice,
		s.grip.CatchWarning,

		s.grip.Alert,
		s.grip.Critical,
		s.grip.Debug,
		s.grip.Emergency,
		s.grip.Error,
		s.grip.Info,
		s.grip.Notice,
		s.grip.Warning,

		s.grip.Alertln,
		s.grip.Criticalln,
		s.grip.Debugln,
		s.grip.Emergencyln,
		s.grip.Errorln,
		s.grip.Infoln,
		s.grip.Noticeln,
		s.grip.Warningln,

		s.grip.Alertf,
		s.grip.Criticalf,
		s.grip.Debugf,
		s.grip.Emergencyf,
		s.grip.Errorf,
		s.grip.Infof,
		s.grip.Noticef,
		s.grip.Warningf,

		s.grip.AlertWhen,
		s.grip.CriticalWhen,
		s.grip.DebugWhen,
		s.grip.EmergencyWhen,
		s.grip.ErrorWhen,
		s.grip.InfoWhen,
		s.grip.NoticeWhen,
		s.grip.WarningWhen,

		s.grip.AlertWhenln,
		s.grip.CriticalWhenln,
		s.grip.DebugWhenln,
		s.grip.EmergencyWhenln,
		s.grip.ErrorWhenln,
		s.grip.InfoWhenln,
		s.grip.NoticeWhenln,
		s.grip.WarningWhenln,

		s.grip.AlertWhenf,
		s.grip.CriticalWhenf,
		s.grip.DebugWhenf,
		s.grip.EmergencyWhenf,
		s.grip.ErrorWhenf,
		s.grip.InfoWhenf,
		s.grip.NoticeWhenf,
		s.grip.WarningWhenf,

		s.grip.AlertMany,
		s.grip.CriticalMany,
		s.grip.DebugMany,
		s.grip.EmergencyMany,
		s.grip.ErrorMany,
		s.grip.InfoMany,
		s.grip.NoticeMany,
		s.grip.WarningMany,

		s.grip.AlertManyWhen,
		s.grip.CriticalManyWhen,
		s.grip.DebugManyWhen,
		s.grip.EmergencyManyWhen,
		s.grip.ErrorManyWhen,
		s.grip.InfoManyWhen,
		s.grip.NoticeManyWhen,
		s.grip.WarningManyWhen,

		func(err error) { s.grip.CatchLog(level.Info, err) },
		func(w bool, m interface{}) { s.grip.LogWhen(w, level.Info, m) },
		func(w bool, m ...interface{}) { s.grip.LogWhenln(w, level.Info, m...) },
		func(w bool, m string, a ...interface{}) { s.grip.LogWhenf(w, level.Info, m, a...) },
		func(m interface{}) { s.grip.Log(level.Info, m) },
		func(m string, a ...interface{}) { s.grip.Logf(level.Info, m, a...) },
		func(m ...interface{}) { s.grip.Logln(level.Info, m...) },
		func(m ...message.Composer) { s.grip.LogMany(level.Info, m...) },
		func(w bool, m ...message.Composer) { s.grip.LogManyWhen(w, level.Info, m...) },
	}

	const msg = "hello world!"
	multiMessage := []message.Composer{
		message.ConvertToComposer(0, nil),
		message.ConvertToComposer(0, msg),
	}

	for _, logger := range cases {
		s.Equal(0, sink.Len())
		s.False(sink.HasMessage())

		switch log := logger.(type) {
		case func(error):
			log(errors.New(msg))
		case func(interface{}):
			log(msg)
		case func(...interface{}):
			log(msg, "", nil)
		case func(string, ...interface{}):
			log("%s", msg)
		case func(bool, interface{}):
			log(false, msg)
			log(true, msg)
		case func(bool, ...interface{}):
			log(false, msg, "", nil)
			log(true, msg, "", nil)
		case func(bool, string, ...interface{}):
			log(false, "%s", msg)
			log(true, "%s", msg)
		case func(...message.Composer):
			log(multiMessage...)
		case func(bool, ...message.Composer):
			log(false, multiMessage...)
			log(true, multiMessage...)
		default:
			panic(fmt.Sprintf("%T is not supported\n", log))
		}

		if sink.Len() > 1 {
			// this is the many case
			var numLogged int
			out := sink.GetMessage()
			for i := 0; i < sink.Len(); i++ {
				out = sink.GetMessage()
				if out.Logged {
					numLogged++
					s.Equal(out.Rendered, msg)
				}
			}

			s.True(numLogged == 1, fmt.Sprintf("%T: %d %s", logger, numLogged, out.Priority))

			continue
		}

		s.True(sink.Len() == 1)
		s.True(sink.HasMessage())
		out := sink.GetMessage()
		s.Equal(out.Rendered, msg)
		s.True(out.Logged, fmt.Sprintf("%T %s", logger, out.Priority))
	}
}

// This testing method uses the technique outlined in:
// http://stackoverflow.com/a/33404435 to test a function that exits
// since it's impossible to "catch" an os.Exit
func TestSendFatalExits(t *testing.T) {
	grip := NewGrip("test")
	if os.Getenv("SHOULD_CRASH") == "1" {
		grip.sendFatal(message.NewLineMessage(grip.DefaultLevel(), "foo"))
		return
	}

	cmd := exec.Command(os.Args[0], "-test.run=TestSendFatalExits")
	cmd.Env = append(os.Environ(), "SHOULD_CRASH=1")
	err := cmd.Run()
	if err == nil {
		t.Errorf("sendFatal should have exited 0, instead: %+v", err)
	}
}
<|endoftext|>


<commit_before>package chardet

import (
	"bytes"
)

var (
	utf16beBom = []byte{0xFE, 0xFF}
	utf16leBom = []byte{0xFF, 0xFE}
	utf32beBom = []byte{0x00, 0x00, 0xFE, 0xFF}
	utf32leBom = []byte{0xFF, 0xFE, 0x00, 0x00}
)

type recognizerUtf16be struct {
}

func (*recognizerUtf16be) Match(input *recognizerInput) (output recognizerOutput) {
	output = recognizerOutput{
		Charset: "UTF-16BE",
	}
	if bytes.HasPrefix(input.raw, utf16beBom) {
		output.Confidence = 100
	}
	return
}

type recognizerUtf16le struct {
}

func (*recognizerUtf16le) Match(input *recognizerInput) (output recognizerOutput) {
	output = recognizerOutput{
		Charset: "UTF-16LE",
	}
	if bytes.HasPrefix(input.raw, utf16leBom) && !bytes.HasPrefix(input.raw, utf32leBom) {
		output.Confidence = 100
	}
	return
}

type recognizerUtf32 struct {
	name string
	bom []byte
	decodeChar func(input []byte) rune
}

func decodeUtf32be(input []byte) rune {
	return rune(input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3])
}

func decodeUtf32le(input []byte) rune {
	return rune(input[3] << 24 | input[2] << 16 | input[1] << 8 | input[0])
}

func newRecognizerUtf32be() *recognizerUtf32 {
	return &recognizerUtf32{
		"UTF-32BE",
		utf32beBom,
		decodeUtf32be,
	}
}

func newRecognizerUtf32le() *recognizerUtf32 {
	return &recognizerUtf32{
		"UTF-32LE",
		utf32leBom,
		decodeUtf32le,
	}
}

func (r *recognizerUtf32) Match(input *recognizerInput) (output recognizerOutput) {
	output = recognizerOutput {
		Charset: r.name,
	}
	hasBom := bytes.HasPrefix(input.raw, r.bom)
	var numValid, numInvalid uint32
	for b := input.raw; len(b) >= 4; b = b[4:] {
		if c := r.decodeChar(b); c < 0 || c >= 0x10FFFF || (c >= 0xD800 && c <= 0xDFFF) {
			numInvalid++
		} else {
			numValid++
		}
	}
	if hasBom && numInvalid == 0 {
		output.Confidence = 100
	} else if hasBom && numValid > numInvalid*10 {
		output.Confidence = 80
	} else if numValid > 3 && numInvalid == 0 {
		output.Confidence = 100
	} else if numValid > 0 && numInvalid == 0 {
		output.Confidence = 80
	} else if numValid > numInvalid*10 {
		output.Confidence = 25
	}
	return
}
<commit_msg>Apply go fmt<commit_after>package chardet

import (
	"bytes"
)

var (
	utf16beBom = []byte{0xFE, 0xFF}
	utf16leBom = []byte{0xFF, 0xFE}
	utf32beBom = []byte{0x00, 0x00, 0xFE, 0xFF}
	utf32leBom = []byte{0xFF, 0xFE, 0x00, 0x00}
)

type recognizerUtf16be struct {
}

func (*recognizerUtf16be) Match(input *recognizerInput) (output recognizerOutput) {
	output = recognizerOutput{
		Charset: "UTF-16BE",
	}
	if bytes.HasPrefix(input.raw, utf16beBom) {
		output.Confidence = 100
	}
	return
}

type recognizerUtf16le struct {
}

func (*recognizerUtf16le) Match(input *recognizerInput) (output recognizerOutput) {
	output = recognizerOutput{
		Charset: "UTF-16LE",
	}
	if bytes.HasPrefix(input.raw, utf16leBom) && !bytes.HasPrefix(input.raw, utf32leBom) {
		output.Confidence = 100
	}
	return
}

type recognizerUtf32 struct {
	name       string
	bom        []byte
	decodeChar func(input []byte) rune
}

func decodeUtf32be(input []byte) rune {
	return rune(input[0]<<24 | input[1]<<16 | input[2]<<8 | input[3])
}

func decodeUtf32le(input []byte) rune {
	return rune(input[3]<<24 | input[2]<<16 | input[1]<<8 | input[0])
}

func newRecognizerUtf32be() *recognizerUtf32 {
	return &recognizerUtf32{
		"UTF-32BE",
		utf32beBom,
		decodeUtf32be,
	}
}

func newRecognizerUtf32le() *recognizerUtf32 {
	return &recognizerUtf32{
		"UTF-32LE",
		utf32leBom,
		decodeUtf32le,
	}
}

func (r *recognizerUtf32) Match(input *recognizerInput) (output recognizerOutput) {
	output = recognizerOutput{
		Charset: r.name,
	}
	hasBom := bytes.HasPrefix(input.raw, r.bom)
	var numValid, numInvalid uint32
	for b := input.raw; len(b) >= 4; b = b[4:] {
		if c := r.decodeChar(b); c < 0 || c >= 0x10FFFF || (c >= 0xD800 && c <= 0xDFFF) {
			numInvalid++
		} else {
			numValid++
		}
	}
	if hasBom && numInvalid == 0 {
		output.Confidence = 100
	} else if hasBom && numValid > numInvalid*10 {
		output.Confidence = 80
	} else if numValid > 3 && numInvalid == 0 {
		output.Confidence = 100
	} else if numValid > 0 && numInvalid == 0 {
		output.Confidence = 80
	} else if numValid > numInvalid*10 {
		output.Confidence = 25
	}
	return
}
<|endoftext|>


<commit_before>// Package response defines the how the default microservice response must look and behave like.
package response

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

	"github.com/LUSHDigital/microservice-core-golang/pagination"
)

// Standard response statuses.
const (
	StatusOk   = "ok"
	StatusFail = "fail"
)

// ResponseInterface - Interface for microservice responses.
type ResponseInterface interface {
	// ExtractData returns a particular item of data from the response.
	ExtractData(srcKey string, dst interface{}) error

	// GetCode returns the response code.
	GetCode() int
}

// Response - A standardised response format for a microservice.
type Response struct {
	Status  string `json:"status"`         // Can be 'ok' or 'fail'
	Code    int    `json:"code"`           // Any valid HTTP response code
	Message string `json:"message"`        // Any relevant message (optional)
	Data    *Data  `json:"data,omitempty"` // Data to pass along to the response (optional)
}

// New returns a new Response for a microservice endpoint
// This ensures that all API endpoints return data in a standardised format:
//
//    {
//       "status": "ok or fail",
//       "code": any HTTP response code,
//       "message": "any relevant message (optional)",
//       "data": {[
//          ...
//       ]}
//    }
func New(code int, message string, data *Data) *Response {
	var status string
	switch {
	case code >= http.StatusOK && code < http.StatusBadRequest:
		status = StatusOk
	default:
		status = StatusFail
	}
	return &Response{
		Code:    code,
		Status:  status,
		Message: message,
		Data:    data,
	}
}

// WriteTo - pick a response writer to write the default json response to.
func (r *Response) WriteTo(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(r.Code)
	json.NewEncoder(w).Encode(r)
}

// ExtractData returns a particular item of data from the response.
func (r *Response) ExtractData(srcKey string, dst interface{}) error {
	if !r.Data.Valid() {
		return fmt.Errorf("invalid data provided: %v", r.Data)
	}
	for key, value := range r.Data.Map() {
		if key != srcKey {
			continue
		}

		// Get the raw JSON just for the endpoints.
		rawJSON, err := json.Marshal(value)
		if err != nil {
			return err
		}

		// Decode the raw JSON.
		json.Unmarshal(rawJSON, &dst)
	}

	return nil
}

// GetCode returns the response code.
func (r *Response) GetCode() int {
	return r.Code
}

// PaginatedResponse - A paginated response format for a microservice.
type PaginatedResponse struct {
	Status     string               `json:"status"`         // Can be 'ok' or 'fail'
	Code       int                  `json:"code"`           // Any valid HTTP response code
	Message    string               `json:"message"`        // Any relevant message (optional)
	Data       *Data                `json:"data,omitempty"` // Data to pass along to the response (optional)
	Pagination *pagination.Response `json:"pagination"`     // Pagination data
}

// NewPaginated returns a new PaginatedResponse for a microservice endpoint
func NewPaginated(paginator *pagination.Paginator, code int, message string, data *Data) *PaginatedResponse {
	var status string
	switch {
	case code >= http.StatusOK && code < http.StatusBadRequest:
		status = StatusOk
	default:
		status = StatusFail
	}
	return &PaginatedResponse{
		Code:       code,
		Status:     status,
		Message:    message,
		Data:       data,
		Pagination: paginator.PrepareResponse(),
	}
}

func (p *PaginatedResponse) WriteTo(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(p.Code)
	json.NewEncoder(w).Encode(p)
}

// ExtractData returns a particular item of data from the response.
func (p *PaginatedResponse) ExtractData(srcKey string, dst interface{}) error {
	if !p.Data.Valid() {
		return fmt.Errorf("invalid data provided: %v", p.Data)
	}
	for key, value := range p.Data.Map() {
		if key != srcKey {
			continue
		}

		// Get the raw JSON just for the endpoints.
		rawJSON, err := json.Marshal(value)
		if err != nil {
			return err
		}

		// Decode the raw JSON.
		json.Unmarshal(rawJSON, &dst)
	}

	return nil
}

// GetCode returns the response code.
func (p *PaginatedResponse) GetCode() int {
	return p.Code
}

// Data represents the collection data the the response will return to the consumer.
// Type ends up being the name of the key containing the collection of Content
type Data struct {
	Type    string
	Content interface{}
}

// UnmarshalJSON implements the Unmarshaler interface
// this implementation will fill the type in the case we're been provided a valid single collection
// and set the content to the contents of said collection.
// for every other options, it behaves like normal.
// Despite the fact that we are not suposed to marshal without a type set,
// this is purposefuly left open to unmarshal without a collection name set, in case you may want to set it later,
// and for interop with other systems which may not send the collection properly.
func (d *Data) UnmarshalJSON(b []byte) error {
	if err := json.Unmarshal(b, &d.Content); err != nil {
		log.Printf("cannot unmarshal data: %v", err)
	}

	data, ok := d.Content.(map[string]interface{})
	if ok {
		// count how many collections were provided
		var count int
		for _, value := range data {
			if _, ok := value.(map[string]interface{}); ok {
				count++
			}
			if _, ok := value.([]interface{}); ok {
				count++
			}
		}
		if count > 1 {
			// we can stop there since this is not a single collection
			return nil
		}

		for key, value := range data {
			if _, ok := value.(map[string]interface{}); ok {
				d.Type = key
				d.Content = data[key]
			} else if _, ok := value.([]interface{}); ok {
				d.Type = key
				d.Content = data[key]
			}
		}
	}

	return nil
}

// Valid ensures the Data passed to the response is correct (it must contain a Type along with the data).
func (d *Data) Valid() bool {
	if d.Type != "" {
		return true
	}
	return false
}

// MarshalJSON implements the Marshaler interface and is there to ensure the output
// is correct when we return data to the consumer
func (d *Data) MarshalJSON() ([]byte, error) {
	return json.Marshal(d.Map())
}

// Map returns a version of the data as a map
func (d *Data) Map() map[string]interface{} {
	if !d.Valid() {
		return nil
	}
	d.Type = strings.Replace(strings.ToLower(d.Type), " ", "-", -1)

	return map[string]interface{}{
		d.Type: d.Content,
	}
}
<commit_msg>Adds prepared responses for common use-cases (#10)<commit_after>// Package response defines the how the default microservice response must look and behave like.
package response

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

	"github.com/LUSHDigital/microservice-core-golang/pagination"
	"database/sql"
	"github.com/VividCortex/mysqlerr"
	"github.com/go-sql-driver/mysql"
)

// Standard response statuses.
const (
	StatusOk   = "ok"
	StatusFail = "fail"
)

// ResponseInterface - Interface for microservice responses.
type ResponseInterface interface {
	// ExtractData returns a particular item of data from the response.
	ExtractData(srcKey string, dst interface{}) error

	// GetCode returns the response code.
	GetCode() int
}

// Response - A standardised response format for a microservice.
type Response struct {
	Status  string `json:"status"`         // Can be 'ok' or 'fail'
	Code    int    `json:"code"`           // Any valid HTTP response code
	Message string `json:"message"`        // Any relevant message (optional)
	Data    *Data  `json:"data,omitempty"` // Data to pass along to the response (optional)
}

// New returns a new Response for a microservice endpoint
// This ensures that all API endpoints return data in a standardised format:
//
//    {
//       "status": "ok or fail",
//       "code": any HTTP response code,
//       "message": "any relevant message (optional)",
//       "data": {[
//          ...
//       ]}
//    }
func New(code int, message string, data *Data) *Response {
	var status string
	switch {
	case code >= http.StatusOK && code < http.StatusBadRequest:
		status = StatusOk
	default:
		status = StatusFail
	}
	return &Response{
		Code:    code,
		Status:  status,
		Message: message,
		Data:    data,
	}
}

// SQLError returns a prepared 422 Unprocessable Entity response if the error passed is of type sql.ErrNoRows,
// otherwise, returns a 500 Internal Server Error prepared response.
func SQLError(err error) *Response {
	if err == sql.ErrNoRows {
		return New(http.StatusUnprocessableEntity, "no data found", nil)
	}
	if driverErr, ok := err.(*mysql.MySQLError); ok {
		if driverErr.Number == mysqlerr.ER_DUP_ENTRY {
			return New(http.StatusUnprocessableEntity, "duplicate entry.", nil)
		}
	}
	return New(http.StatusInternalServerError, fmt.Sprintf("db error: %v", err), nil)
}

// JSONError returns a prepared 422 Unprocessable Entity response if the error passed is of type *json.SyntaxError,
// otherwise, returns a 500 Internal Server Error prepared response.
func JSONError(err error) *Response {
	if syn, ok := err.(*json.SyntaxError); ok {
		return New(http.StatusUnprocessableEntity, fmt.Sprintf("invalid json: %v", syn), nil)
	}
	return New(http.StatusInternalServerError, fmt.Sprintf("json error: %v", err), nil)
}

// ParamError returns a prepared 422 Unprocessable Entity response, including the name of
// the failing parameter in the message field of the response object.
func ParamError(name string) *Response {
	return New(http.StatusUnprocessableEntity, fmt.Sprintf("invalid or missing parameter: %v", name), nil)
}

// ValidationError returns a prepared 422 Unprocessable Entity response, including the name of
// the failing validation/validator in the message field of the response object.
func ValidationError(err error, name string) *Response {
	return New(http.StatusUnprocessableEntity, fmt.Sprintf("validation error on %s: %v", name, err), nil)
}

// NotFoundErr returns a prepared 404 Not Found response, including the message passed by the user
// in the message field of the response object.
func NotFoundErr(msg string) *Response {
	return New(http.StatusNotFound, msg, nil)
}

// ConflictErr returns a prepared 409 Conflict response, including the message passed by the user
// in the message field of the response object.
func ConflictErr(msg string) *Response {
	return New(http.StatusConflict, msg, nil)
}

// InternalError returns a prepared 500 Internal Server Error, including the error
// message in the message field of the response object
func InternalError(err error) *Response {
	return New(http.StatusInternalServerError, fmt.Sprintf("internal server error: %v", err), nil)
}

// WriteTo - pick a response writer to write the default json response to.
func (r *Response) WriteTo(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(r.Code)
	json.NewEncoder(w).Encode(r)
}

// ExtractData returns a particular item of data from the response.
func (r *Response) ExtractData(srcKey string, dst interface{}) error {
	if !r.Data.Valid() {
		return fmt.Errorf("invalid data provided: %v", r.Data)
	}
	for key, value := range r.Data.Map() {
		if key != srcKey {
			continue
		}

		// Get the raw JSON just for the endpoints.
		rawJSON, err := json.Marshal(value)
		if err != nil {
			return err
		}

		// Decode the raw JSON.
		json.Unmarshal(rawJSON, &dst)
	}

	return nil
}

// GetCode returns the response code.
func (r *Response) GetCode() int {
	return r.Code
}

// PaginatedResponse - A paginated response format for a microservice.
type PaginatedResponse struct {
	Status     string               `json:"status"`         // Can be 'ok' or 'fail'
	Code       int                  `json:"code"`           // Any valid HTTP response code
	Message    string               `json:"message"`        // Any relevant message (optional)
	Data       *Data                `json:"data,omitempty"` // Data to pass along to the response (optional)
	Pagination *pagination.Response `json:"pagination"`     // Pagination data
}

// NewPaginated returns a new PaginatedResponse for a microservice endpoint
func NewPaginated(paginator *pagination.Paginator, code int, message string, data *Data) *PaginatedResponse {
	var status string
	switch {
	case code >= http.StatusOK && code < http.StatusBadRequest:
		status = StatusOk
	default:
		status = StatusFail
	}
	return &PaginatedResponse{
		Code:       code,
		Status:     status,
		Message:    message,
		Data:       data,
		Pagination: paginator.PrepareResponse(),
	}
}

func (p *PaginatedResponse) WriteTo(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(p.Code)
	json.NewEncoder(w).Encode(p)
}

// ExtractData returns a particular item of data from the response.
func (p *PaginatedResponse) ExtractData(srcKey string, dst interface{}) error {
	if !p.Data.Valid() {
		return fmt.Errorf("invalid data provided: %v", p.Data)
	}
	for key, value := range p.Data.Map() {
		if key != srcKey {
			continue
		}

		// Get the raw JSON just for the endpoints.
		rawJSON, err := json.Marshal(value)
		if err != nil {
			return err
		}

		// Decode the raw JSON.
		json.Unmarshal(rawJSON, &dst)
	}

	return nil
}

// GetCode returns the response code.
func (p *PaginatedResponse) GetCode() int {
	return p.Code
}

// Data represents the collection data the the response will return to the consumer.
// Type ends up being the name of the key containing the collection of Content
type Data struct {
	Type    string
	Content interface{}
}

// UnmarshalJSON implements the Unmarshaler interface
// this implementation will fill the type in the case we're been provided a valid single collection
// and set the content to the contents of said collection.
// for every other options, it behaves like normal.
// Despite the fact that we are not suposed to marshal without a type set,
// this is purposefuly left open to unmarshal without a collection name set, in case you may want to set it later,
// and for interop with other systems which may not send the collection properly.
func (d *Data) UnmarshalJSON(b []byte) error {
	if err := json.Unmarshal(b, &d.Content); err != nil {
		log.Printf("cannot unmarshal data: %v", err)
	}

	data, ok := d.Content.(map[string]interface{})
	if ok {
		// count how many collections were provided
		var count int
		for _, value := range data {
			if _, ok := value.(map[string]interface{}); ok {
				count++
			}
			if _, ok := value.([]interface{}); ok {
				count++
			}
		}
		if count > 1 {
			// we can stop there since this is not a single collection
			return nil
		}

		for key, value := range data {
			if _, ok := value.(map[string]interface{}); ok {
				d.Type = key
				d.Content = data[key]
			} else if _, ok := value.([]interface{}); ok {
				d.Type = key
				d.Content = data[key]
			}
		}
	}

	return nil
}

// Valid ensures the Data passed to the response is correct (it must contain a Type along with the data).
func (d *Data) Valid() bool {
	if d.Type != "" {
		return true
	}
	return false
}

// MarshalJSON implements the Marshaler interface and is there to ensure the output
// is correct when we return data to the consumer
func (d *Data) MarshalJSON() ([]byte, error) {
	return json.Marshal(d.Map())
}

// Map returns a version of the data as a map
func (d *Data) Map() map[string]interface{} {
	if !d.Valid() {
		return nil
	}
	d.Type = strings.Replace(strings.ToLower(d.Type), " ", "-", -1)

	return map[string]interface{}{
		d.Type: d.Content,
	}
}
<|endoftext|>


<commit_before>package timeout

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"syscall"
	"time"
)

type Timeout struct {
	PreserveStatus bool
	Duration       uint64
	KillAfter      uint64
	Signal         os.Signal
	Command        string
	CommandArgs    []string
}

const (
	exitTimedOut = 124
	exitKilled   = 137
)

func (tio *Timeout) Run() int {
	ch, stdoutPipe, stderrPipe, err := tio.RunCommand()
	if err != nil {
		panic(fmt.Sprintf("something went wrong: %+v", err))
	}
	defer func() {
		stdoutPipe.Close()
		stderrPipe.Close()
	}()

	go readAndOut(stdoutPipe, os.Stdout)
	go readAndOut(stderrPipe, os.Stderr)

	return <-ch
}

func (tio *Timeout) prepareCmd() *exec.Cmd {
	args := tio.CommandArgs
	return exec.Command(tio.Command, args...)
}

func (tio *Timeout) RunCommand() (exitChan chan int, stdoutPipe, stderrPipe io.ReadCloser, err error) {
	cmd := tio.prepareCmd()
	if err != nil {
		return
	}

	stdoutPipe, err = cmd.StdoutPipe()
	if err != nil {
		return
	}
	stderrPipe, err = cmd.StderrPipe()
	if err != nil {
		return
	}
	if err = cmd.Start(); err != nil {
		return
	}

	exitChan = make(chan int)
	go func() {
		exitChan <- tio.handleTimeout(cmd)
	}()

	return
}

func (tio *Timeout) handleTimeout(cmd *exec.Cmd) int {
	exit := 0
	timedOut := false
	exitChan := getExitChan(cmd)

	if tio.Duration > 0 {
		select {
		case exit = <-exitChan:
		case <-time.After(time.Duration(tio.Duration) * time.Second):
			cmd.Process.Signal(tio.Signal)
			timedOut = true
			exit = exitTimedOut
		}
	} else {
		exit = <-exitChan
	}

	killed := false
	if timedOut {
		tmpExit := 0
		if tio.KillAfter > 0 {
			select {
			case tmpExit = <-exitChan:
			case <-time.After(time.Duration(tio.KillAfter) * time.Second):
				cmd.Process.Kill()
				killed = true
				exit = exitKilled
			}
		} else {
			tmpExit = <-exitChan
		}
		if tio.PreserveStatus && !killed {
			exit = tmpExit
		}
	}

	return exit
}

func getExitChan(cmd *exec.Cmd) chan int {
	ch := make(chan int)
	go func() {
		exit := 0
		err := cmd.Wait()
		if err != nil {
			if exiterr, ok := err.(*exec.ExitError); ok {
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					exit = status.ExitStatus()
				}
			} else {
				exit = -1
			}
		}
		ch <- exit
	}()
	return ch
}

func readAndOut(r io.Reader, f *os.File) {
	s := bufio.NewScanner(r)
	for s.Scan() {
		fmt.Fprintln(f, s.Text())
	}
}

var durRe = regexp.MustCompile("^([0-9]+)([smhd])?$")

func parseDuration(durStr string) (uint64, error) {
	matches := durRe.FindStringSubmatch(durStr)
	if len(matches) == 0 {
		return 0, fmt.Errorf("duration format invalid: %s", durStr)
	}

	base, _ := strconv.ParseUint(matches[1], 10, 64)
	switch matches[2] {
	case "", "s":
		return base, nil
	case "m":
		return base * 60, nil
	case "h":
		return base * 60 * 60, nil
	case "d":
		return base * 60 * 60 * 24, nil
	default:
		return 0, fmt.Errorf("something went wrong")
	}
}
<commit_msg>use raw string literal for regexp<commit_after>package timeout

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"syscall"
	"time"
)

type Timeout struct {
	PreserveStatus bool
	Duration       uint64
	KillAfter      uint64
	Signal         os.Signal
	Command        string
	CommandArgs    []string
}

const (
	exitTimedOut = 124
	exitKilled   = 137
)

func (tio *Timeout) Run() int {
	ch, stdoutPipe, stderrPipe, err := tio.RunCommand()
	if err != nil {
		panic(fmt.Sprintf("something went wrong: %+v", err))
	}
	defer func() {
		stdoutPipe.Close()
		stderrPipe.Close()
	}()

	go readAndOut(stdoutPipe, os.Stdout)
	go readAndOut(stderrPipe, os.Stderr)

	return <-ch
}

func (tio *Timeout) prepareCmd() *exec.Cmd {
	args := tio.CommandArgs
	return exec.Command(tio.Command, args...)
}

func (tio *Timeout) RunCommand() (exitChan chan int, stdoutPipe, stderrPipe io.ReadCloser, err error) {
	cmd := tio.prepareCmd()
	if err != nil {
		return
	}

	stdoutPipe, err = cmd.StdoutPipe()
	if err != nil {
		return
	}
	stderrPipe, err = cmd.StderrPipe()
	if err != nil {
		return
	}
	if err = cmd.Start(); err != nil {
		return
	}

	exitChan = make(chan int)
	go func() {
		exitChan <- tio.handleTimeout(cmd)
	}()

	return
}

func (tio *Timeout) handleTimeout(cmd *exec.Cmd) int {
	exit := 0
	timedOut := false
	exitChan := getExitChan(cmd)

	if tio.Duration > 0 {
		select {
		case exit = <-exitChan:
		case <-time.After(time.Duration(tio.Duration) * time.Second):
			cmd.Process.Signal(tio.Signal)
			timedOut = true
			exit = exitTimedOut
		}
	} else {
		exit = <-exitChan
	}

	killed := false
	if timedOut {
		tmpExit := 0
		if tio.KillAfter > 0 {
			select {
			case tmpExit = <-exitChan:
			case <-time.After(time.Duration(tio.KillAfter) * time.Second):
				cmd.Process.Kill()
				killed = true
				exit = exitKilled
			}
		} else {
			tmpExit = <-exitChan
		}
		if tio.PreserveStatus && !killed {
			exit = tmpExit
		}
	}

	return exit
}

func getExitChan(cmd *exec.Cmd) chan int {
	ch := make(chan int)
	go func() {
		exit := 0
		err := cmd.Wait()
		if err != nil {
			if exiterr, ok := err.(*exec.ExitError); ok {
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					exit = status.ExitStatus()
				}
			} else {
				exit = -1
			}
		}
		ch <- exit
	}()
	return ch
}

func readAndOut(r io.Reader, f *os.File) {
	s := bufio.NewScanner(r)
	for s.Scan() {
		fmt.Fprintln(f, s.Text())
	}
}

var durRe = regexp.MustCompile(`^([0-9]+)([smhd])?$`)

func parseDuration(durStr string) (uint64, error) {
	matches := durRe.FindStringSubmatch(durStr)
	if len(matches) == 0 {
		return 0, fmt.Errorf("duration format invalid: %s", durStr)
	}

	base, _ := strconv.ParseUint(matches[1], 10, 64)
	switch matches[2] {
	case "", "s":
		return base, nil
	case "m":
		return base * 60, nil
	case "h":
		return base * 60 * 60, nil
	case "d":
		return base * 60 * 60 * 24, nil
	default:
		return 0, fmt.Errorf("something went wrong")
	}
}
<|endoftext|>


<commit_before>package onthefly

import (
	"fmt"
	"strconv"
)

func NewTinySVG(x, y, w, h int) (*Page, *Tag) {
	page := NewPage("", `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">`)
	svg := page.root.AddNewTag("svg")
	svg.AddAttrib("xmlns", "http://www.w3.org/2000/svg")
	svg.AddAttrib("version", "1.1")
	svg.AddAttrib("baseProfile", "tiny")
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	svg.AddAttrib("viewBox", sx+" "+sy+" "+sw+" "+sh)
	return page, svg
}

func (svg *Tag) AddRect(x, y, w, h int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	rect := svg.AddNewTag("rect")
	rect.AddAttrib("x", sx)
	rect.AddAttrib("y", sy)
	rect.AddAttrib("width", sw)
	rect.AddAttrib("height", sh)
	return rect
}

func (svg *Tag) Circle(x, y, radius int, color string) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sradius := strconv.Itoa(radius)
	circle := svg.AddNewTag("circle")
	circle.AddAttrib("cx", sx)
	circle.AddAttrib("cy", sy)
	circle.AddAttrib("r", sradius)
	circle.Fill(color)
	return circle
}

func (svg *Tag) AddCircle(x, y, radius int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sradius := strconv.Itoa(radius)
	circle := svg.AddNewTag("circle")
	circle.AddAttrib("cx", sx)
	circle.AddAttrib("cy", sy)
	circle.AddAttrib("r", sradius)
	return circle
}

func (rect *Tag) Fill(color string) {
	rect.AddAttrib("fill", color)
}

// Converts r, g and b which are integers in the range from 0..255
// to a color-string on the form "#nnnnnn".
func ColorString(r, g, b int) string {
	rs := strconv.FormatInt(int64(r), 16)
	gs := strconv.FormatInt(int64(g), 16)
	bs := strconv.FormatInt(int64(b), 16)
	if len(rs) == 1 {
		rs = "0" + rs
	}
	if len(gs) == 1 {
		gs = "0" + gs
	}
	if len(bs) == 1 {
		bs = "0" + bs
	}
	return "#" + rs + gs + bs
}

// Creates a rectangle that is 1 wide with the given color
// Note that the size of the "pixel" depends on how large the viewBox is
func (svg *Tag) Pixel(x, y, r, g, b int) *Tag {
	color := ColorString(r, g, b)
	rect := svg.AddRect(x, y, 1, 1)
	rect.Fill(color)
	return rect
}

func (svg *Tag) AlphaDot(cx, cy, r, g, b int, a float32) *Tag {
	color := fmt.Sprintf("rgba(%d, %d, %d, %f)", r, g, b, a)
	circle := svg.AddCircle(cx, cy, 1)
	circle.Fill(color)
	return circle
}

func (svg *Tag) Dot(cx, cy, r, g, b int) *Tag {
	color := ColorString(r, g, b)
	circle := svg.AddCircle(cx, cy, 1)
	circle.Fill(color)
	return circle
}

func SampleSVG1() *Page {
	page, svg := NewTinySVG(0, 0, 30, 30)
	desc := svg.AddNewTag("desc")
	desc.AddContent("Sample SVG file 1")
	rect := svg.AddRect(10, 10, 10, 10)
	rect.Fill("green")
	svg.Pixel(10, 10, 255, 0, 0)
	svg.AlphaDot(5, 5, 0, 0, 255, 0.5)
	return page
}

func SampleSVG2() *Page {
	w := 160
	h := 90
	stepx := 8
	stepy := 8
	page, svg := NewTinySVG(0, 0, w, h)
	desc := svg.AddNewTag("desc")
	desc.AddContent("Sample SVG file 2")
	increase := 0
	decrease := 0
	for y := stepy; y < h; y += stepy {
		for x := stepx; x < w; x += stepx {
			increase = int((float32(x) / float32(w)) * 255.0)
			decrease = 255 - increase
			svg.Dot(x, y, 255, decrease, increase)
		}
	}
	return page
}
<commit_msg>Added a method for drawing a box<commit_after>package onthefly

import (
	"fmt"
	"strconv"
)

func NewTinySVG(x, y, w, h int) (*Page, *Tag) {
	page := NewPage("", `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">`)
	svg := page.root.AddNewTag("svg")
	svg.AddAttrib("xmlns", "http://www.w3.org/2000/svg")
	svg.AddAttrib("version", "1.1")
	svg.AddAttrib("baseProfile", "tiny")
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	svg.AddAttrib("viewBox", sx+" "+sy+" "+sw+" "+sh)
	return page, svg
}

func (svg *Tag) AddRect(x, y, w, h int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	rect := svg.AddNewTag("rect")
	rect.AddAttrib("x", sx)
	rect.AddAttrib("y", sy)
	rect.AddAttrib("width", sw)
	rect.AddAttrib("height", sh)
	return rect
}

// Add a box/rectangle, given x and y position, radius and a color
func (svg *Tag) Box(x, y, w, h int, color string) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	rect := svg.AddNewTag("rect")
	rect.AddAttrib("x", sx)
	rect.AddAttrib("y", sy)
	rect.AddAttrib("width", sw)
	rect.AddAttrib("height", sh)
	rect.Fill(color)
	return rect
}

// Add a circle, given x and y position, radius and a color
func (svg *Tag) Circle(x, y, radius int, color string) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sradius := strconv.Itoa(radius)
	circle := svg.AddNewTag("circle")
	circle.AddAttrib("cx", sx)
	circle.AddAttrib("cy", sy)
	circle.AddAttrib("r", sradius)
	circle.Fill(color)
	return circle
}

func (svg *Tag) AddCircle(x, y, radius int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sradius := strconv.Itoa(radius)
	circle := svg.AddNewTag("circle")
	circle.AddAttrib("cx", sx)
	circle.AddAttrib("cy", sy)
	circle.AddAttrib("r", sradius)
	return circle
}

func (rect *Tag) Fill(color string) {
	rect.AddAttrib("fill", color)
}

// Converts r, g and b which are integers in the range from 0..255
// to a color-string on the form "#nnnnnn".
func ColorString(r, g, b int) string {
	rs := strconv.FormatInt(int64(r), 16)
	gs := strconv.FormatInt(int64(g), 16)
	bs := strconv.FormatInt(int64(b), 16)
	if len(rs) == 1 {
		rs = "0" + rs
	}
	if len(gs) == 1 {
		gs = "0" + gs
	}
	if len(bs) == 1 {
		bs = "0" + bs
	}
	return "#" + rs + gs + bs
}

// Creates a rectangle that is 1 wide with the given color
// Note that the size of the "pixel" depends on how large the viewBox is
func (svg *Tag) Pixel(x, y, r, g, b int) *Tag {
	color := ColorString(r, g, b)
	rect := svg.Box(x, y, 1, 1, color)
	return rect
}

func (svg *Tag) AlphaDot(cx, cy, r, g, b int, a float32) *Tag {
	color := fmt.Sprintf("rgba(%d, %d, %d, %f)", r, g, b, a)
	circle := svg.AddCircle(cx, cy, 1)
	circle.Fill(color)
	return circle
}

func (svg *Tag) Dot(cx, cy, r, g, b int) *Tag {
	color := ColorString(r, g, b)
	circle := svg.AddCircle(cx, cy, 1)
	circle.Fill(color)
	return circle
}

func SampleSVG1() *Page {
	page, svg := NewTinySVG(0, 0, 30, 30)
	desc := svg.AddNewTag("desc")
	desc.AddContent("Sample SVG file 1")
	rect := svg.AddRect(10, 10, 10, 10)
	rect.Fill("green")
	svg.Pixel(10, 10, 255, 0, 0)
	svg.AlphaDot(5, 5, 0, 0, 255, 0.5)
	return page
}

func SampleSVG2() *Page {
	w := 160
	h := 90
	stepx := 8
	stepy := 8
	page, svg := NewTinySVG(0, 0, w, h)
	desc := svg.AddNewTag("desc")
	desc.AddContent("Sample SVG file 2")
	increase := 0
	decrease := 0
	for y := stepy; y < h; y += stepy {
		for x := stepx; x < w; x += stepx {
			increase = int((float32(x) / float32(w)) * 255.0)
			decrease = 255 - increase
			svg.Dot(x, y, 255, decrease, increase)
		}
	}
	return page
}
<|endoftext|>


<commit_before>package context

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

	"github.com/gulien/orbit/helpers"

	"github.com/joho/godotenv"
	"gopkg.in/yaml.v2"
)

type (
	// OrbitContext struct contains the data necessary for generating a file from a template.
	OrbitContext struct {
		// TemplateFilePath is the path of the template.
		TemplateFilePath string
		// Values map contains data from YAML files.
		Values map[string]interface{}
		// EnvFiles map contains pairs from .env files.
		EnvFiles map[string]map[string]string
		// Env map contains pairs from environment variables.
		Env map[string]string
		// Os is the OS name at runtime.
		Os string
	}

	// OrbitFileMap struct represents a parameter given to some flags of an Orbit command.
	// Flags: -v --values, -e --env
	OrbitFileMap struct {
		// Name is the given name of the file.
		Name string
		// Path is the path of the file.
		Path string
	}
)

// NewOrbitContext function instantiates a new OrbitContext.
func NewOrbitContext(templateFilePath string, valuesFiles string, envFiles string) (*OrbitContext, error) {
	// as the template is mandatory, we must check its validity.
	if templateFilePath == "" || helpers.FileDoesNotExist(templateFilePath) {
		return nil, fmt.Errorf("Template file %s does not exist", templateFilePath)
	}

	// let's instantiates our OrbitContext!
	ctx := &OrbitContext{
		TemplateFilePath: templateFilePath,
		Os:               runtime.GOOS,
	}

	// checks if a file with values has been specified.
	if valuesFiles != "" {
		data, err := getValuesMap(valuesFiles)
		if err != nil {
			return nil, err
		}

		ctx.Values = data
	}

	// checks if a .env file has been specified.
	if envFiles != "" {
		data, err := getEnvFilesMap(envFiles)
		if err != nil {
			return nil, err
		}

		ctx.EnvFiles = data
	}

	// last but not least, populates the Env map.
	ctx.Env = getEnvMap()

	return ctx, nil
}

// getValuesMap function retrieves values from YAML files.
func getValuesMap(valuesFiles string) (map[string]interface{}, error) {
	filesMap, err := getFilesMap(valuesFiles)
	if err != nil {
		return nil, err
	}

	valuesMap := make(map[string]interface{})
	for _, f := range filesMap {
		// first, checks if the file exists
		if helpers.FileDoesNotExist(f.Path) {
			return nil, fmt.Errorf("Values file %s does not exist", f.Path)
		}

		// the file containing values must be a valid YAML file.
		if !helpers.IsYAML(f.Path) {
			return nil, fmt.Errorf("Values file %s is not a valid YAML file", f.Path)
		}

		// alright, let's read it to retrieve its data!
		data, err := ioutil.ReadFile(f.Path)
		if err != nil {
			return nil, fmt.Errorf("Failed to read the values file %s:\n%s", f.Path, err)
		}

		// last but not least, parses the YAML.
		var values interface{}
		if err := yaml.Unmarshal(data, &values); err != nil {
			return nil, fmt.Errorf("Values file %s is not a valid YAML file:\n%s", f.Path, err)
		}

		valuesMap[f.Name] = values
	}

	return valuesMap, nil
}

// getEnvFilesMap function retrieves pairs from .env files.
func getEnvFilesMap(envFiles string) (map[string]map[string]string, error) {
	filesMap, err := getFilesMap(envFiles)
	if err != nil {
		return nil, err
	}

	envFilesMap := make(map[string]map[string]string)
	for _, f := range filesMap {
		// first, checks if the file exists
		if helpers.FileDoesNotExist(f.Path) {
			return nil, fmt.Errorf("Env file %s does not exist", f.Path)
		}

		// then parses the .env file to retrieve pairs.
		envFilesMap[f.Name], err = godotenv.Read(f.Path)
		if err != nil {
			return nil, fmt.Errorf("Unable to parse the env file %s:\n%s", f.Path, err)
		}
	}

	return envFilesMap, nil
}

// getEnvMap function retrieves all pairs from environment variables.
func getEnvMap() map[string]string {
	envMap := make(map[string]string)
	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		envMap[pair[0]] = pair[1]
	}

	return envMap
}

// getFilesMap function reads a string and populates an array of OrbitFileMap instances.
func getFilesMap(s string) ([]*OrbitFileMap, error) {
	var filesMap []*OrbitFileMap

	// checks if the given string is map of files:
	// if not, considers the string as a path.
	// otherwise tries to populate an array of OrbitFileMap instances.
	parts := strings.Split(s, ";")
	if len(parts) == 1 {
		filesMap = append(filesMap, &OrbitFileMap{"default", s})
	} else {
		for _, part := range parts {
			data := strings.Split(part, ",")
			if len(data) != 2 {
				return filesMap, fmt.Errorf("Unable to process the files map %s", s)
			}

			filesMap = append(filesMap, &OrbitFileMap{data[0], data[1]})
		}
	}

	return filesMap, nil
}
<commit_msg>context is now able to handle a single file map<commit_after>package context

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

	"github.com/gulien/orbit/helpers"

	"github.com/joho/godotenv"
	"gopkg.in/yaml.v2"
)

type (
	// OrbitContext struct contains the data necessary for generating a file from a template.
	OrbitContext struct {
		// TemplateFilePath is the path of the template.
		TemplateFilePath string
		// Values map contains data from YAML files.
		Values map[string]interface{}
		// EnvFiles map contains pairs from .env files.
		EnvFiles map[string]map[string]string
		// Env map contains pairs from environment variables.
		Env map[string]string
		// Os is the OS name at runtime.
		Os string
	}

	// OrbitFileMap struct represents a parameter given to some flags of an Orbit command.
	// Flags: -v --values, -e --env
	OrbitFileMap struct {
		// Name is the given name of the file.
		Name string
		// Path is the path of the file.
		Path string
	}
)

// NewOrbitContext function instantiates a new OrbitContext.
func NewOrbitContext(templateFilePath string, valuesFiles string, envFiles string) (*OrbitContext, error) {
	// as the template is mandatory, we must check its validity.
	if templateFilePath == "" || helpers.FileDoesNotExist(templateFilePath) {
		return nil, fmt.Errorf("Template file %s does not exist", templateFilePath)
	}

	// let's instantiates our OrbitContext!
	ctx := &OrbitContext{
		TemplateFilePath: templateFilePath,
		Os:               runtime.GOOS,
	}

	// checks if a file with values has been specified.
	if valuesFiles != "" {
		data, err := getValuesMap(valuesFiles)
		if err != nil {
			return nil, err
		}

		ctx.Values = data
	}

	// checks if a .env file has been specified.
	if envFiles != "" {
		data, err := getEnvFilesMap(envFiles)
		if err != nil {
			return nil, err
		}

		ctx.EnvFiles = data
	}

	// last but not least, populates the Env map.
	ctx.Env = getEnvMap()

	return ctx, nil
}

// getValuesMap function retrieves values from YAML files.
func getValuesMap(valuesFiles string) (map[string]interface{}, error) {
	filesMap, err := getFilesMap(valuesFiles)
	if err != nil {
		return nil, err
	}

	valuesMap := make(map[string]interface{})
	for _, f := range filesMap {
		// first, checks if the file exists
		if helpers.FileDoesNotExist(f.Path) {
			return nil, fmt.Errorf("Values file %s does not exist", f.Path)
		}

		// the file containing values must be a valid YAML file.
		if !helpers.IsYAML(f.Path) {
			return nil, fmt.Errorf("Values file %s is not a valid YAML file", f.Path)
		}

		// alright, let's read it to retrieve its data!
		data, err := ioutil.ReadFile(f.Path)
		if err != nil {
			return nil, fmt.Errorf("Failed to read the values file %s:\n%s", f.Path, err)
		}

		// last but not least, parses the YAML.
		var values interface{}
		if err := yaml.Unmarshal(data, &values); err != nil {
			return nil, fmt.Errorf("Values file %s is not a valid YAML file:\n%s", f.Path, err)
		}

		valuesMap[f.Name] = values
	}

	return valuesMap, nil
}

// getEnvFilesMap function retrieves pairs from .env files.
func getEnvFilesMap(envFiles string) (map[string]map[string]string, error) {
	filesMap, err := getFilesMap(envFiles)
	if err != nil {
		return nil, err
	}

	envFilesMap := make(map[string]map[string]string)
	for _, f := range filesMap {
		// first, checks if the file exists
		if helpers.FileDoesNotExist(f.Path) {
			return nil, fmt.Errorf("Env file %s does not exist", f.Path)
		}

		// then parses the .env file to retrieve pairs.
		envFilesMap[f.Name], err = godotenv.Read(f.Path)
		if err != nil {
			return nil, fmt.Errorf("Unable to parse the env file %s:\n%s", f.Path, err)
		}
	}

	return envFilesMap, nil
}

// getEnvMap function retrieves all pairs from environment variables.
func getEnvMap() map[string]string {
	envMap := make(map[string]string)
	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		envMap[pair[0]] = pair[1]
	}

	return envMap
}

// getFilesMap function reads a string and populates an array of OrbitFileMap instances.
func getFilesMap(s string) ([]*OrbitFileMap, error) {
	var filesMap []*OrbitFileMap

	// checks if the given string is map of files:
	// if not, considers the string as a path.
	// otherwise tries to populate an array of OrbitFileMap instances.
	parts := strings.Split(s, ";")
	if len(parts) == 1 && len(strings.Split(s, ",")) == 1 {
		filesMap = append(filesMap, &OrbitFileMap{"default", s})
	} else {
		for _, part := range parts {
			data := strings.Split(part, ",")
			if len(data) != 2 {
				return filesMap, fmt.Errorf("Unable to process the files map %s", s)
			}

			filesMap = append(filesMap, &OrbitFileMap{data[0], data[1]})
		}
	}

	return filesMap, nil
}
<|endoftext|>


<commit_before>package sendmail

import (
	log "github.com/Sirupsen/logrus"
	"fmt"
	"io"
	"net/smtp"
	"strings"
	"time"
)

// job for delivering mail
type DeliverJob struct {
	unlimited bool
	cancel bool
	retries int

	bounce Bouncer
	visit func(func(*smtp.Client) error) error
	delivered func(string, string)

	recip string
	from string

	body []byte

	Result chan bool
}

func extractAddr(email string) (addr string) {
	if strings.HasSuffix(email, "@") {
		addr = "localhost"
	} else {
		idx_at := strings.Index(email, "@")
		if strings.HasSuffix(email, ".b32.i2p") {
			addr = email[idx_at+1:]
		} else if strings.HasSuffix(email, ".i2p") {
			idx_i2p := strings.LastIndex(email, ".i2p")
			addr = fmt.Sprintf("smtp.%s.i2p", email[idx_at+1:idx_i2p])
		} else {
			addr = email[idx_at+1:]
		}
	}
  addr = strings.Trim(addr, ",= \t\r\n\f\b")
  return

}

// cancel delivery
func (d *DeliverJob) Cancel() {
	d.cancel = true
}

// run delivery
func (d *DeliverJob) run() {
	tries := 0
	sec := time.Duration(1)
	var err error
	for (d.unlimited || tries < d.retries) && !d.cancel {
		// try visiting connection with tryDeliver method
		err = d.visit(d.tryDeliver)
		if err == nil {
			// it worked, mail delivered
			if d.delivered != nil {
				// call delivered callback
				d.delivered(d.recip, d.from)
			}
			// inform waiting
			d.Result <- true
			return
		} else {
			// failed to deliver
			tries ++
			log.Warnf("failed to deliver message to %s from %s: %s", d.recip, d.from, err.Error())
			sec *= 2
			if sec > 1024 {
				sec = 1024
			}
			time.Sleep(sec * time.Second)
		}
	}
	// failed to deliver
	log.Errorf("delivery of message to %s failed", d.recip)
	if d.bounce != nil {
		// call bounce hook as needed
		d.bounce(d.recip, d.from, err)
	}
	// inform waiting
	d.Result <- false
}

// try delivery
func (d *DeliverJob) tryDeliver(cl *smtp.Client) (err error) {
	// recpt to
	err = cl.Rcpt(d.recip)
	if err != nil {
		return
	}
	// mail from
	err = cl.Mail(d.from)
	if err != nil {
		return
	}
	// data
	var wr io.WriteCloser
	wr, err = cl.Data()
	if err != nil {
		return
	}
	// ... full write
	n := 0
	for n < len(d.body) && err == nil {
		n, err = wr.Write(d.body[n:])
	}
	if err != nil {
		return
	}
	// ... flush
	err = wr.Close()
	if err != nil {
		return
	}
	// reset
	err = cl.Reset()
	return
}
<commit_msg>fix order<commit_after>package sendmail

import (
	log "github.com/Sirupsen/logrus"
	"fmt"
	"io"
	"net/smtp"
	"strings"
	"time"
)

// job for delivering mail
type DeliverJob struct {
	unlimited bool
	cancel bool
	retries int

	bounce Bouncer
	visit func(func(*smtp.Client) error) error
	delivered func(string, string)

	recip string
	from string

	body []byte

	Result chan bool
}

func extractAddr(email string) (addr string) {
	if strings.HasSuffix(email, "@") {
		addr = "localhost"
	} else {
		idx_at := strings.Index(email, "@")
		if strings.HasSuffix(email, ".b32.i2p") {
			addr = email[idx_at+1:]
		} else if strings.HasSuffix(email, ".i2p") {
			idx_i2p := strings.LastIndex(email, ".i2p")
			addr = fmt.Sprintf("smtp.%s.i2p", email[idx_at+1:idx_i2p])
		} else {
			addr = email[idx_at+1:]
		}
	}
  addr = strings.Trim(addr, ",= \t\r\n\f\b")
  return

}

// cancel delivery
func (d *DeliverJob) Cancel() {
	d.cancel = true
}

// run delivery
func (d *DeliverJob) run() {
	tries := 0
	sec := time.Duration(1)
	var err error
	for (d.unlimited || tries < d.retries) && !d.cancel {
		// try visiting connection with tryDeliver method
		err = d.visit(d.tryDeliver)
		if err == nil {
			// it worked, mail delivered
			if d.delivered != nil {
				// call delivered callback
				d.delivered(d.recip, d.from)
			}
			// inform waiting
			d.Result <- true
			return
		} else {
			// failed to deliver
			tries ++
			log.Warnf("failed to deliver message to %s from %s: %s", d.recip, d.from, err.Error())
			sec *= 2
			if sec > 1024 {
				sec = 1024
			}
			time.Sleep(sec * time.Second)
		}
	}
	// failed to deliver
	log.Errorf("delivery of message to %s failed", d.recip)
	if d.bounce != nil {
		// call bounce hook as needed
		d.bounce(d.recip, d.from, err)
	}
	// inform waiting
	d.Result <- false
}

// try delivery
func (d *DeliverJob) tryDeliver(cl *smtp.Client) (err error) {
	// mail from
	err = cl.Mail(d.from)
	if err != nil {
		return
	}
	// recpt to
	err = cl.Rcpt(d.recip)
	if err != nil {
		return
	}
	// data
	var wr io.WriteCloser
	wr, err = cl.Data()
	if err != nil {
		return
	}
	// ... full write
	n := 0
	for n < len(d.body) && err == nil {
		n, err = wr.Write(d.body[n:])
	}
	if err != nil {
		return
	}
	// ... flush
	err = wr.Close()
	if err != nil {
		return
	}
	// reset
	err = cl.Reset()
	return
}
<|endoftext|>