<commit_before>// Package presence provides simple user presence system
package presence

// Status defines what is the current status of a user
// in presence system
type Status int

const (
	// Offline is for displaying user as offline in the system
	Offline Status = iota

	// Online is for displaying user as online in the system
	Online
)

// Backend represents basic interface for all required backend operations for
// presence package
type Backend interface {
	Online(...string) error
	Offline(...string) error
	Status(...string) ([]Event, error)
	Close() error
	Error() chan error
	ListenStatusChanges() chan Event
}

// Event is the data type for
// occuring events in the system
type Event struct {
	// ID is the given key by the application
	ID string

	// Status holds the changing type of event
	Status Status
}

// Session holds the backend and provides accessor methods for communication
type Session struct {
	// holds the interface
	backend Backend
}

// New creates a session for any broker system that is architected to use,
// communicate, forward events to the presence system
func New(backend Backend) (*Session, error) {
	return &Session{backend: backend}, nil
}

// Online sets given ids as online
func (s *Session) Online(ids ...string) error {
	return s.backend.Online(ids...)
}

// Offline sets given ids as offline
func (s *Session) Offline(ids ...string) error {
	return s.backend.Offline(ids...)
}

// Status returns the current status of multiple keys from system
func (s *Session) Status(ids ...string) ([]Event, error) {
	return s.backend.Status(ids...)
}

// Close closes the backend connection gracefully
func (s *Session) Close() error {
	return s.backend.Close()
}

// Error returns error if it happens while listening  to status changes
func (s *Session) Error() chan error {
	return s.backend.Error()
}

// ListenStatusChanges subscribes the backend and
// gets online and offline status changes from it
func (s *Session) ListenStatusChanges() chan Event {
	return s.backend.ListenStatusChanges()
}
<commit_msg>Presence: implement stringer interface<commit_after>// Package presence provides simple user presence system
package presence

// Status defines what is the current status of a user
// in presence system
type Status int

func (s Status) String() string {
	switch s {
	case Offline:
		return "OFFLINE"
	case Online:
		return "ONLINE"

	}

	return "UNKNOWN"
}

const (
	// Offline is for displaying user as offline in the system
	Offline Status = iota

	// Online is for displaying user as online in the system
	Online
)

// Backend represents basic interface for all required backend operations for
// presence package
type Backend interface {
	Online(...string) error
	Offline(...string) error
	Status(...string) ([]Event, error)
	Close() error
	Error() chan error
	ListenStatusChanges() chan Event
}

// Event is the data type for
// occuring events in the system
type Event struct {
	// ID is the given key by the application
	ID string

	// Status holds the changing type of event
	Status Status
}

// Session holds the backend and provides accessor methods for communication
type Session struct {
	// holds the interface
	backend Backend
}

// New creates a session for any broker system that is architected to use,
// communicate, forward events to the presence system
func New(backend Backend) (*Session, error) {
	return &Session{backend: backend}, nil
}

// Online sets given ids as online
func (s *Session) Online(ids ...string) error {
	return s.backend.Online(ids...)
}

// Offline sets given ids as offline
func (s *Session) Offline(ids ...string) error {
	return s.backend.Offline(ids...)
}

// Status returns the current status of multiple keys from system
func (s *Session) Status(ids ...string) ([]Event, error) {
	return s.backend.Status(ids...)
}

// Close closes the backend connection gracefully
func (s *Session) Close() error {
	return s.backend.Close()
}

// Error returns error if it happens while listening  to status changes
func (s *Session) Error() chan error {
	return s.backend.Error()
}

// ListenStatusChanges subscribes the backend and
// gets online and offline status changes from it
func (s *Session) ListenStatusChanges() chan Event {
	return s.backend.ListenStatusChanges()
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"strings"
	"time"

	"github.com/c9s/goprocinfo/linux"
)

type ProcDiff []*Proc

func (pd *ProcDiff) Contains(cmd ...string) {

	maxp, e := linux.ReadMaxPID("/proc/sys/kernel/pid_max")
	check(e)
	for p := uint64(1); p < maxp; p++ {
		// Do not check it's own PID
		if int(p) == selfPID {
			continue
		}
		proc, e := linux.ReadProcess(p, "/proc")
		if e != nil {
			continue
		}
		for _, c := range cmd {
			// Tgid != Pid for a thread
			if strings.Contains(proc.Cmdline, c) && proc.Status.Tgid == proc.Status.Pid {
				*pd = append(*pd, &Proc{p, proc.Cmdline, nil, nil, 0})
				break
			}
		}
	}
}

func cpu() uint64 {

	cpu, e := linux.ReadStat("/proc/stat")
	check(e)

	c := cpu.CPUStatAll
	return c.User + c.Nice + c.System + c.Idle
}

func (pd *ProcDiff) Percentage() {

	for _, p := range *pd {
		r, e := linux.ReadProcess(p.Pid, "/proc")
		check(e)
		p.Fir = r
	}
	cpu1 := cpu()

	time.Sleep(time.Second)

	for _, p := range *pd {
		r, e := linux.ReadProcess(p.Pid, "/proc")
		check(e)
		p.Sec = r
	}

	cpu2 := cpu()

	for _, p := range *pd {

		p1 := p.Fir
		p2 := p.Sec
		user := int64(p2.Stat.Utime-p1.Stat.Utime) + (p2.Stat.Cutime - p1.Stat.Cutime)
		system := int64(p2.Stat.Stime-p1.Stat.Stime) + (p2.Stat.Cstime - p1.Stat.Cstime)

		p.Per = (float64(user+system) / float64((cpu2-cpu1)/8)) * 100
	}
}

func checkProcs(ss ...string) []uint64 {
	pd := new(ProcDiff)

	pd.Contains(ss...)
	pd.Percentage()

	pdf := []uint64{}

	for _, p := range *pd {
		if p.Per > *fper {
			log.Printf("[WARNING]: %v: %v%% (%v)", p.Pid, p.Per, p.Cmd)
			pdf = append(pdf, p.Pid)
		}
	}
	log.Printf("Found \033[1m%v\033[0m corresponding processes, with \033[1m%v\033[0m > %v%%.\n", len(*pd), len(pdf), *fper)

	return pdf
}

func killProcs(pd, npd []uint64) {
	for _, np := range npd {
		for _, p := range pd {
			if p != np {
				continue
			}
			proc, e := os.FindProcess(int(p))
			check(e)
			log.Printf("[KILL] sent signal %s to %v\n", *fsys, proc.Pid)
			e = proc.Signal(sysc[*fsys])
			check(e)
		}
	}
}
<commit_msg>fix numCPU<commit_after>package main

import (
	"log"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/c9s/goprocinfo/linux"
)

type ProcDiff []*Proc

func (pd *ProcDiff) Contains(cmd ...string) {

	maxp, e := linux.ReadMaxPID("/proc/sys/kernel/pid_max")
	check(e)
	for p := uint64(1); p < maxp; p++ {
		// Do not check it's own PID
		if int(p) == selfPID {
			continue
		}
		proc, e := linux.ReadProcess(p, "/proc")
		if e != nil {
			continue
		}
		for _, c := range cmd {
			// Tgid != Pid for a thread
			if strings.Contains(proc.Cmdline, c) && proc.Status.Tgid == proc.Status.Pid {
				*pd = append(*pd, &Proc{p, proc.Cmdline, nil, nil, 0})
				break
			}
		}
	}
}

func cpu() uint64 {

	cpu, e := linux.ReadStat("/proc/stat")
	check(e)

	c := cpu.CPUStatAll
	return c.User + c.Nice + c.System + c.Idle
}

func (pd *ProcDiff) Percentage() {

	for _, p := range *pd {
		r, e := linux.ReadProcess(p.Pid, "/proc")
		check(e)
		p.Fir = r
	}
	cpu1 := cpu()

	time.Sleep(time.Second)

	for _, p := range *pd {
		r, e := linux.ReadProcess(p.Pid, "/proc")
		check(e)
		p.Sec = r
	}

	cpu2 := cpu()

	for _, p := range *pd {

		p1 := p.Fir
		p2 := p.Sec
		user := int64(p2.Stat.Utime-p1.Stat.Utime) + (p2.Stat.Cutime - p1.Stat.Cutime)
		system := int64(p2.Stat.Stime-p1.Stat.Stime) + (p2.Stat.Cstime - p1.Stat.Cstime)

		p.Per = (float64(user+system) / float64((cpu2-cpu1)/uint64(runtime.NumCPU()))) * 100
	}
}

func checkProcs(ss ...string) []uint64 {
	pd := new(ProcDiff)

	pd.Contains(ss...)
	pd.Percentage()

	pdf := []uint64{}

	for _, p := range *pd {
		if p.Per > *fper {
			log.Printf("[WARNING]: %v: %v%% (%v)", p.Pid, p.Per, p.Cmd)
			pdf = append(pdf, p.Pid)
		}
	}
	log.Printf("Found \033[1m%v\033[0m corresponding processes, with \033[1m%v\033[0m > %v%%.\n", len(*pd), len(pdf), *fper)

	return pdf
}

func killProcs(pd, npd []uint64) {
	for _, np := range npd {
		for _, p := range pd {
			if p != np {
				continue
			}
			proc, e := os.FindProcess(int(p))
			check(e)
			log.Printf("[KILL] sent signal %s to %v\n", *fsys, proc.Pid)
			e = proc.Signal(sysc[*fsys])
			check(e)
		}
	}
}
<|endoftext|>


<commit_before>package mpb

import (
	"bytes"
	"container/heap"
	"context"
	"fmt"
	"io"
	"math"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/v8/cwriter"
)

const (
	prr = 150 * time.Millisecond // default RefreshRate
)

// DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
var DoneError = fmt.Errorf("%T instance can't be reused after it's done!", (*Progress)(nil))

// Progress represents a container that renders one or more progress bars.
type Progress struct {
	ctx          context.Context
	uwg          *sync.WaitGroup
	cwg          *sync.WaitGroup
	bwg          *sync.WaitGroup
	operateState chan func(*pState)
	interceptIo  chan func(io.Writer)
	done         chan struct{}
	refreshCh    chan time.Time
	once         sync.Once
}

// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
type pState struct {
	bHeap       priorityQueue
	heapUpdated bool
	pMatrix     map[int][]chan int
	aMatrix     map[int][]chan int

	// following are provided/overrided by user
	idCount          int
	reqWidth         int
	popPriority      int
	popCompleted     bool
	outputDiscarded  bool
	rr               time.Duration
	uwg              *sync.WaitGroup
	externalRefresh  <-chan interface{}
	renderDelay      <-chan struct{}
	shutdownNotifier chan struct{}
	queueBars        map[*Bar]*Bar
	output           io.Writer
	debugOut         io.Writer
}

// New creates new Progress container instance. It's not possible to
// reuse instance after (*Progress).Wait method has been called.
func New(options ...ContainerOption) *Progress {
	return NewWithContext(context.Background(), options...)
}

// NewWithContext creates new Progress container instance with provided
// context. It's not possible to reuse instance after (*Progress).Wait
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
	s := &pState{
		bHeap:       priorityQueue{},
		rr:          prr,
		queueBars:   make(map[*Bar]*Bar),
		output:      os.Stdout,
		popPriority: math.MinInt32,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		ctx:          ctx,
		uwg:          s.uwg,
		cwg:          new(sync.WaitGroup),
		bwg:          new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		interceptIo:  make(chan func(io.Writer)),
		done:         make(chan struct{}),
	}

	p.cwg.Add(1)
	go p.serve(s, cwriter.New(s.output))
	return p
}

// AddBar creates a bar with default bar filler.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	return p.New(total, BarStyle(), options...)
}

// AddSpinner creates a bar with default spinner filler.
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
	return p.New(total, SpinnerStyle(), options...)
}

// New creates a bar with provided BarFillerBuilder.
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
	return p.Add(total, builder.Build(), options...)
}

// Add creates a bar which renders itself by provided filler.
// If `total <= 0` triggering complete event by increment methods is disabled.
// Panics if *Progress instance is done, i.e. called after (*Progress).Wait().
func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
	if filler == nil {
		filler = NopStyle().Build()
	}
	p.bwg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(ps *pState) {
		bs := ps.makeBarState(total, filler, options...)
		bar := newBar(p, bs)
		if bs.wait.bar != nil {
			ps.queueBars[bs.wait.bar] = bar
		} else {
			heap.Push(&ps.bHeap, bar)
			ps.heapUpdated = true
		}
		ps.idCount++
		result <- bar
	}:
		bar := <-result
		return bar
	case <-p.done:
		p.bwg.Done()
		panic(DoneError)
	}
}

func (p *Progress) traverseBars(cb func(b *Bar) bool) {
	sync := make(chan struct{})
	select {
	case p.operateState <- func(s *pState) {
		for i := 0; i < s.bHeap.Len(); i++ {
			bar := s.bHeap[i]
			if !cb(bar) {
				break
			}
		}
		close(sync)
	}:
		<-sync
	case <-p.done:
	}
}

// UpdateBarPriority same as *Bar.SetPriority(int).
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		b.priority = priority
		heap.Fix(&s.bHeap, b.index)
	}:
	case <-p.done:
	}
}

// BarCount returns bars count.
func (p *Progress) BarCount() int {
	result := make(chan int)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Write is implementation of io.Writer.
// Writing to `*mpb.Progress` will print lines above a running bar.
// Writes aren't flushed immediatly, but at next refresh cycle.
// If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
// is returned.
func (p *Progress) Write(b []byte) (int, error) {
	type result struct {
		n   int
		err error
	}
	ch := make(chan *result)
	select {
	case p.interceptIo <- func(w io.Writer) {
		n, err := w.Write(b)
		ch <- &result{n, err}
	}:
		res := <-ch
		return res.n, res.err
	case <-p.done:
		return 0, DoneError
	}
}

// Wait waits for all bars to complete and finally shutdowns container.
// After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() {
	// wait for user wg, if any
	if p.uwg != nil {
		p.uwg.Wait()
	}

	// wait for bars to quit, if any
	p.bwg.Wait()

	p.once.Do(p.shutdown)

	// wait for container to quit
	p.cwg.Wait()
}

func (p *Progress) shutdown() {
	close(p.done)
}

func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
	defer p.cwg.Done()

	render := func(debugOut io.Writer) {
		err := s.render(cw)
		for err != nil {
			if debugOut != nil {
				_, err = fmt.Fprintln(debugOut, err)
			} else {
				panic(err)
			}
			debugOut = nil
		}
	}

	p.refreshCh = s.newTicker(p.done)

	for {
		select {
		case op := <-p.operateState:
			op(s)
		case fn := <-p.interceptIo:
			fn(cw)
		case <-p.refreshCh:
			render(s.debugOut)
		case <-s.shutdownNotifier:
			for s.heapUpdated {
				render(s.debugOut)
			}
			return
		}
	}
}

func (s *pState) render(cw *cwriter.Writer) error {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	width, height, err := cw.GetTermSize()
	if err != nil {
		width = s.reqWidth
		height = s.bHeap.Len()
	}
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		go bar.render(width)
	}

	return s.flush(cw, height)
}

func (s *pState) flush(cw *cwriter.Writer, height int) error {
	var wg sync.WaitGroup
	var popCount int
	rows := make([]io.Reader, 0, height)
	pool := make([]*Bar, 0, s.bHeap.Len())
	for s.bHeap.Len() > 0 {
		var usedRows int
		b := heap.Pop(&s.bHeap).(*Bar)
		frame := <-b.frameCh
		for i := len(frame.rows) - 1; i >= 0; i-- {
			if len(rows) < height {
				rows = append(rows, frame.rows[i])
				usedRows++
			} else {
				wg.Add(1)
				go func(discardRow io.Reader) {
					_, _ = io.Copy(io.Discard, discardRow)
					wg.Done()
				}(frame.rows[i])
			}
		}
		if frame.shutdown != 0 {
			b.Wait() // waiting for b.done, so it's safe to read b.bs
			drop := b.bs.dropOnComplete
			if qb, ok := s.queueBars[b]; ok {
				delete(s.queueBars, b)
				qb.priority = b.priority
				pool = append(pool, qb)
				drop = true
			} else if s.popCompleted && !b.bs.noPop {
				if frame.shutdown > 1 {
					popCount += usedRows
					drop = true
				} else {
					s.popPriority++
					b.priority = s.popPriority
				}
			}
			if drop {
				s.heapUpdated = true
				continue
			}
		}
		pool = append(pool, b)
	}

	for _, b := range pool {
		heap.Push(&s.bHeap, b)
	}

	for i := len(rows) - 1; i >= 0; i-- {
		_, err := cw.ReadFrom(rows[i])
		if err != nil {
			return err
		}
	}

	wg.Wait()
	return cw.Flush(len(rows) - popCount)
}

func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
	ch := make(chan time.Time)
	if s.shutdownNotifier == nil {
		s.shutdownNotifier = make(chan struct{})
	}
	go func() {
		if s.renderDelay != nil {
			<-s.renderDelay
		}
		var internalRefresh <-chan time.Time
		if !s.outputDiscarded {
			if s.externalRefresh == nil {
				ticker := time.NewTicker(s.rr)
				defer ticker.Stop()
				internalRefresh = ticker.C
			}
		} else {
			s.externalRefresh = nil
		}
		for {
			select {
			case t := <-internalRefresh:
				ch <- t
			case x := <-s.externalRefresh:
				if t, ok := x.(time.Time); ok {
					ch <- t
				} else {
					ch <- time.Now()
				}
			case <-done:
				close(s.shutdownNotifier)
				return
			}
		}
	}()
	return ch
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
	bs := &bState{
		id:       s.idCount,
		priority: s.idCount,
		reqWidth: s.reqWidth,
		total:    total,
		filler:   filler,
		debugOut: s.debugOut,
	}

	if total > 0 {
		bs.triggerComplete = true
	}

	for _, opt := range options {
		if opt != nil {
			opt(bs)
		}
	}

	if bs.middleware != nil {
		bs.filler = bs.middleware(filler)
		bs.middleware = nil
	}

	for i := 0; i < len(bs.buffers); i++ {
		bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
	}

	bs.subscribeDecorators()

	return bs
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		go maxWidthDistributor(column)
	}
}

func maxWidthDistributor(column []chan int) {
	var maxWidth int
	for _, ch := range column {
		if w := <-ch; w > maxWidth {
			maxWidth = w
		}
	}
	for _, ch := range column {
		ch <- maxWidth
	}
}
<commit_msg>minor: wg.Wait err branch<commit_after>package mpb

import (
	"bytes"
	"container/heap"
	"context"
	"fmt"
	"io"
	"math"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/v8/cwriter"
)

const (
	prr = 150 * time.Millisecond // default RefreshRate
)

// DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
var DoneError = fmt.Errorf("%T instance can't be reused after it's done!", (*Progress)(nil))

// Progress represents a container that renders one or more progress bars.
type Progress struct {
	ctx          context.Context
	uwg          *sync.WaitGroup
	cwg          *sync.WaitGroup
	bwg          *sync.WaitGroup
	operateState chan func(*pState)
	interceptIo  chan func(io.Writer)
	done         chan struct{}
	refreshCh    chan time.Time
	once         sync.Once
}

// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
type pState struct {
	bHeap       priorityQueue
	heapUpdated bool
	pMatrix     map[int][]chan int
	aMatrix     map[int][]chan int

	// following are provided/overrided by user
	idCount          int
	reqWidth         int
	popPriority      int
	popCompleted     bool
	outputDiscarded  bool
	rr               time.Duration
	uwg              *sync.WaitGroup
	externalRefresh  <-chan interface{}
	renderDelay      <-chan struct{}
	shutdownNotifier chan struct{}
	queueBars        map[*Bar]*Bar
	output           io.Writer
	debugOut         io.Writer
}

// New creates new Progress container instance. It's not possible to
// reuse instance after (*Progress).Wait method has been called.
func New(options ...ContainerOption) *Progress {
	return NewWithContext(context.Background(), options...)
}

// NewWithContext creates new Progress container instance with provided
// context. It's not possible to reuse instance after (*Progress).Wait
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
	s := &pState{
		bHeap:       priorityQueue{},
		rr:          prr,
		queueBars:   make(map[*Bar]*Bar),
		output:      os.Stdout,
		popPriority: math.MinInt32,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		ctx:          ctx,
		uwg:          s.uwg,
		cwg:          new(sync.WaitGroup),
		bwg:          new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		interceptIo:  make(chan func(io.Writer)),
		done:         make(chan struct{}),
	}

	p.cwg.Add(1)
	go p.serve(s, cwriter.New(s.output))
	return p
}

// AddBar creates a bar with default bar filler.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	return p.New(total, BarStyle(), options...)
}

// AddSpinner creates a bar with default spinner filler.
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
	return p.New(total, SpinnerStyle(), options...)
}

// New creates a bar with provided BarFillerBuilder.
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
	return p.Add(total, builder.Build(), options...)
}

// Add creates a bar which renders itself by provided filler.
// If `total <= 0` triggering complete event by increment methods is disabled.
// Panics if *Progress instance is done, i.e. called after (*Progress).Wait().
func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
	if filler == nil {
		filler = NopStyle().Build()
	}
	p.bwg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(ps *pState) {
		bs := ps.makeBarState(total, filler, options...)
		bar := newBar(p, bs)
		if bs.wait.bar != nil {
			ps.queueBars[bs.wait.bar] = bar
		} else {
			heap.Push(&ps.bHeap, bar)
			ps.heapUpdated = true
		}
		ps.idCount++
		result <- bar
	}:
		bar := <-result
		return bar
	case <-p.done:
		p.bwg.Done()
		panic(DoneError)
	}
}

func (p *Progress) traverseBars(cb func(b *Bar) bool) {
	sync := make(chan struct{})
	select {
	case p.operateState <- func(s *pState) {
		for i := 0; i < s.bHeap.Len(); i++ {
			bar := s.bHeap[i]
			if !cb(bar) {
				break
			}
		}
		close(sync)
	}:
		<-sync
	case <-p.done:
	}
}

// UpdateBarPriority same as *Bar.SetPriority(int).
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		b.priority = priority
		heap.Fix(&s.bHeap, b.index)
	}:
	case <-p.done:
	}
}

// BarCount returns bars count.
func (p *Progress) BarCount() int {
	result := make(chan int)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Write is implementation of io.Writer.
// Writing to `*mpb.Progress` will print lines above a running bar.
// Writes aren't flushed immediatly, but at next refresh cycle.
// If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
// is returned.
func (p *Progress) Write(b []byte) (int, error) {
	type result struct {
		n   int
		err error
	}
	ch := make(chan *result)
	select {
	case p.interceptIo <- func(w io.Writer) {
		n, err := w.Write(b)
		ch <- &result{n, err}
	}:
		res := <-ch
		return res.n, res.err
	case <-p.done:
		return 0, DoneError
	}
}

// Wait waits for all bars to complete and finally shutdowns container.
// After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() {
	// wait for user wg, if any
	if p.uwg != nil {
		p.uwg.Wait()
	}

	// wait for bars to quit, if any
	p.bwg.Wait()

	p.once.Do(p.shutdown)

	// wait for container to quit
	p.cwg.Wait()
}

func (p *Progress) shutdown() {
	close(p.done)
}

func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
	defer p.cwg.Done()

	render := func(debugOut io.Writer) {
		err := s.render(cw)
		for err != nil {
			if debugOut != nil {
				_, err = fmt.Fprintln(debugOut, err)
			} else {
				panic(err)
			}
			debugOut = nil
		}
	}

	p.refreshCh = s.newTicker(p.done)

	for {
		select {
		case op := <-p.operateState:
			op(s)
		case fn := <-p.interceptIo:
			fn(cw)
		case <-p.refreshCh:
			render(s.debugOut)
		case <-s.shutdownNotifier:
			for s.heapUpdated {
				render(s.debugOut)
			}
			return
		}
	}
}

func (s *pState) render(cw *cwriter.Writer) error {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	width, height, err := cw.GetTermSize()
	if err != nil {
		width = s.reqWidth
		height = s.bHeap.Len()
	}
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		go bar.render(width)
	}

	return s.flush(cw, height)
}

func (s *pState) flush(cw *cwriter.Writer, height int) error {
	var wg sync.WaitGroup
	var popCount int
	rows := make([]io.Reader, 0, height)
	pool := make([]*Bar, 0, s.bHeap.Len())
	for s.bHeap.Len() > 0 {
		var usedRows int
		b := heap.Pop(&s.bHeap).(*Bar)
		frame := <-b.frameCh
		for i := len(frame.rows) - 1; i >= 0; i-- {
			if len(rows) < height {
				rows = append(rows, frame.rows[i])
				usedRows++
			} else {
				wg.Add(1)
				go func(discardRow io.Reader) {
					_, _ = io.Copy(io.Discard, discardRow)
					wg.Done()
				}(frame.rows[i])
			}
		}
		if frame.shutdown != 0 {
			b.Wait() // waiting for b.done, so it's safe to read b.bs
			drop := b.bs.dropOnComplete
			if qb, ok := s.queueBars[b]; ok {
				delete(s.queueBars, b)
				qb.priority = b.priority
				pool = append(pool, qb)
				drop = true
			} else if s.popCompleted && !b.bs.noPop {
				if frame.shutdown > 1 {
					popCount += usedRows
					drop = true
				} else {
					s.popPriority++
					b.priority = s.popPriority
				}
			}
			if drop {
				s.heapUpdated = true
				continue
			}
		}
		pool = append(pool, b)
	}

	for _, b := range pool {
		heap.Push(&s.bHeap, b)
	}

	for i := len(rows) - 1; i >= 0; i-- {
		_, err := cw.ReadFrom(rows[i])
		if err != nil {
			wg.Wait()
			return err
		}
	}

	wg.Wait()
	return cw.Flush(len(rows) - popCount)
}

func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
	ch := make(chan time.Time)
	if s.shutdownNotifier == nil {
		s.shutdownNotifier = make(chan struct{})
	}
	go func() {
		if s.renderDelay != nil {
			<-s.renderDelay
		}
		var internalRefresh <-chan time.Time
		if !s.outputDiscarded {
			if s.externalRefresh == nil {
				ticker := time.NewTicker(s.rr)
				defer ticker.Stop()
				internalRefresh = ticker.C
			}
		} else {
			s.externalRefresh = nil
		}
		for {
			select {
			case t := <-internalRefresh:
				ch <- t
			case x := <-s.externalRefresh:
				if t, ok := x.(time.Time); ok {
					ch <- t
				} else {
					ch <- time.Now()
				}
			case <-done:
				close(s.shutdownNotifier)
				return
			}
		}
	}()
	return ch
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
	bs := &bState{
		id:       s.idCount,
		priority: s.idCount,
		reqWidth: s.reqWidth,
		total:    total,
		filler:   filler,
		debugOut: s.debugOut,
	}

	if total > 0 {
		bs.triggerComplete = true
	}

	for _, opt := range options {
		if opt != nil {
			opt(bs)
		}
	}

	if bs.middleware != nil {
		bs.filler = bs.middleware(filler)
		bs.middleware = nil
	}

	for i := 0; i < len(bs.buffers); i++ {
		bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
	}

	bs.subscribeDecorators()

	return bs
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		go maxWidthDistributor(column)
	}
}

func maxWidthDistributor(column []chan int) {
	var maxWidth int
	for _, ch := range column {
		if w := <-ch; w > maxWidth {
			maxWidth = w
		}
	}
	for _, ch := range column {
		ch <- maxWidth
	}
}
<|endoftext|>


<commit_before>package swag

import (
	"fmt"
	"go/ast"
	"strings"
)

type propertyName struct {
	SchemaType string
	ArrayType  string
	CrossPkg   string
}

func parseFieldSelectorExpr(astTypeSelectorExpr *ast.SelectorExpr, parser *Parser) propertyName {
	// TODO: In the future, add functions and make them solve for each user
	// Support for time.Time as a structure field
	if "Time" == astTypeSelectorExpr.Sel.Name {
		return propertyName{SchemaType: "string", ArrayType: "string"}
	}

	// Support bson.ObjectId type
	if "ObjectId" == astTypeSelectorExpr.Sel.Name {
		return propertyName{SchemaType: "string", ArrayType: "string"}
	}

	// Supprt UUID
	if "UUID" == strings.ToUpper(astTypeSelectorExpr.Sel.Name) {
		return propertyName{SchemaType: "string", ArrayType: "string"}
	}

	// Supprt shopspring/decimal
	if "Decimal" == astTypeSelectorExpr.Sel.Name {
		return propertyName{SchemaType: "number", ArrayType: "string"}
	}

	if pkgName, ok := astTypeSelectorExpr.X.(*ast.Ident); ok {
		if typeDefinitions, ok := parser.TypeDefinitions[pkgName.Name][astTypeSelectorExpr.Sel.Name]; ok {
			parser.ParseDefinition(pkgName.Name, typeDefinitions, astTypeSelectorExpr.Sel.Name)
			return propertyName{SchemaType: astTypeSelectorExpr.Sel.Name, ArrayType: "object", CrossPkg: pkgName.Name}
		}
	}

	fmt.Printf("%s is not supported. but it will be set with string temporary. Please report any problems.", astTypeSelectorExpr.Sel.Name)
	return propertyName{SchemaType: "string", ArrayType: "string"}
}

func parseFieldArraySelectorExpr(astTypeSelectorExpr *ast.SelectorExpr, parser *Parser) propertyName {
	// TODO: In the future, add functions and make them solve for each user
	// Support for time.Time as a structure field
	if "Time" == astTypeSelectorExpr.Sel.Name {
		return propertyName{SchemaType: "array", ArrayType: "string"}
	}

	// Support bson.ObjectId type
	if "ObjectId" == astTypeSelectorExpr.Sel.Name {
		return propertyName{SchemaType: "array", ArrayType: "string"}
	}

	// Supprt UUID
	if "UUID" == strings.ToUpper(astTypeSelectorExpr.Sel.Name) {
		return propertyName{SchemaType: "array", ArrayType: "string"}
	}

	// Supprt shopspring/decimal
	if "Decimal" == astTypeSelectorExpr.Sel.Name {
		return propertyName{SchemaType: "array", ArrayType: "number"}
	}

	if pkgName, ok := astTypeSelectorExpr.X.(*ast.Ident); ok {
		if typeDefinitions, ok := parser.TypeDefinitions[pkgName.Name][astTypeSelectorExpr.Sel.Name]; ok {
			parser.ParseDefinition(pkgName.Name, typeDefinitions, astTypeSelectorExpr.Sel.Name)
			return propertyName{SchemaType: "array", ArrayType: astTypeSelectorExpr.Sel.Name, CrossPkg: pkgName.Name}
		}
	}

	fmt.Printf("%s is not supported. but it will be set with string temporary. Please report any problems.", astTypeSelectorExpr.Sel.Name)
	return propertyName{SchemaType: "string", ArrayType: "string"}
}

// getPropertyName returns the string value for the given field if it exists, otherwise it panics.
// allowedValues: array, boolean, integer, null, number, object, string
func getPropertyName(field *ast.Field, parser *Parser) propertyName {
	if astTypeSelectorExpr, ok := field.Type.(*ast.SelectorExpr); ok {
		return parseFieldSelectorExpr(astTypeSelectorExpr, parser)
	}
	if astTypeIdent, ok := field.Type.(*ast.Ident); ok {
		name := astTypeIdent.Name
		schemeType := TransToValidSchemeType(name)
		return propertyName{SchemaType: schemeType, ArrayType: schemeType}
	}
	if ptr, ok := field.Type.(*ast.StarExpr); ok {
		if astTypeSelectorExpr, ok := ptr.X.(*ast.SelectorExpr); ok {
			return parseFieldSelectorExpr(astTypeSelectorExpr, parser)
		}
		if astTypeIdent, ok := ptr.X.(*ast.Ident); ok {
			name := astTypeIdent.Name
			schemeType := TransToValidSchemeType(name)
			return propertyName{SchemaType: schemeType, ArrayType: schemeType}
		}
		if astTypeArray, ok := ptr.X.(*ast.ArrayType); ok { // if array
			if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.SelectorExpr); ok {
				return parseFieldArraySelectorExpr(astTypeArrayExpr, parser)
			}
			if astTypeArrayIdent := astTypeArray.Elt.(*ast.Ident); ok {
				name := astTypeArrayIdent.Name
				return propertyName{SchemaType: "array", ArrayType: name}
			}
		}
	}
	if astTypeArray, ok := field.Type.(*ast.ArrayType); ok { // if array
		if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.SelectorExpr); ok {
			return parseFieldArraySelectorExpr(astTypeArrayExpr, parser)
		}
		if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.StarExpr); ok {
			if astTypeArrayIdent := astTypeArrayExpr.X.(*ast.Ident); ok {
				name := astTypeArrayIdent.Name
				return propertyName{SchemaType: "array", ArrayType: name}
			}
		}
		str := fmt.Sprintf("%s", astTypeArray.Elt)
		return propertyName{SchemaType: "array", ArrayType: str}
	}
	if _, ok := field.Type.(*ast.MapType); ok { // if map
		//TODO: support map
		return propertyName{SchemaType: "object", ArrayType: "object"}
	}
	if _, ok := field.Type.(*ast.StructType); ok { // if struct
		return propertyName{SchemaType: "object", ArrayType: "object"}
	}
	if _, ok := field.Type.(*ast.InterfaceType); ok { // if interface{}
		return propertyName{SchemaType: "object", ArrayType: "object"}
	}
	panic("not supported" + fmt.Sprint(field.Type))
}
<commit_msg>refactor: simplify property.go (#177)<commit_after>package swag

import (
	"errors"
	"fmt"
	"go/ast"
	"strings"
)

// ErrFailedConvertPrimitiveType Failed to convert for swag to interpretable type
var ErrFailedConvertPrimitiveType = errors.New("swag property: failed convert primitive type")

type propertyName struct {
	SchemaType string
	ArrayType  string
	CrossPkg   string
}

type propertyNewFunc func(schemeType string, crossPkg string) propertyName

func newArrayProperty(schemeType string, crossPkg string) propertyName {
	return propertyName{
		SchemaType: "array",
		ArrayType:  schemeType,
		CrossPkg:   crossPkg,
	}
}

func newProperty(schemeType string, crossPkg string) propertyName {
	return propertyName{
		SchemaType: schemeType,
		ArrayType:  "string",
		CrossPkg:   crossPkg,
	}
}

func convertFromSpecificToPrimitive(typeName string) (string, error) {
	typeName = strings.ToUpper(typeName)
	switch typeName {
	case "TIME", "OBJECTID", "UUID":
		return "string", nil
	case "DECIMAL":
		return "number", nil
	}
	return "", ErrFailedConvertPrimitiveType
}

func parseFieldSelectorExpr(astTypeSelectorExpr *ast.SelectorExpr, parser *Parser, propertyNewFunc propertyNewFunc) propertyName {
	if primitiveType, err := convertFromSpecificToPrimitive(astTypeSelectorExpr.Sel.Name); err == nil {
		return propertyNewFunc(primitiveType, "")
	}

	if pkgName, ok := astTypeSelectorExpr.X.(*ast.Ident); ok {
		if typeDefinitions, ok := parser.TypeDefinitions[pkgName.Name][astTypeSelectorExpr.Sel.Name]; ok {
			parser.ParseDefinition(pkgName.Name, typeDefinitions, astTypeSelectorExpr.Sel.Name)
			return propertyNewFunc(astTypeSelectorExpr.Sel.Name, pkgName.Name)
		}
	}

	fmt.Printf("%s is not supported. but it will be set with string temporary. Please report any problems.", astTypeSelectorExpr.Sel.Name)
	return propertyName{SchemaType: "string", ArrayType: "string"}
}

// getPropertyName returns the string value for the given field if it exists, otherwise it panics.
// allowedValues: array, boolean, integer, null, number, object, string
func getPropertyName(field *ast.Field, parser *Parser) propertyName {
	if astTypeSelectorExpr, ok := field.Type.(*ast.SelectorExpr); ok {
		return parseFieldSelectorExpr(astTypeSelectorExpr, parser, newProperty)
	}
	if astTypeIdent, ok := field.Type.(*ast.Ident); ok {
		name := astTypeIdent.Name
		schemeType := TransToValidSchemeType(name)
		return propertyName{SchemaType: schemeType, ArrayType: schemeType}
	}
	if ptr, ok := field.Type.(*ast.StarExpr); ok {
		if astTypeSelectorExpr, ok := ptr.X.(*ast.SelectorExpr); ok {
			return parseFieldSelectorExpr(astTypeSelectorExpr, parser, newProperty)
		}
		if astTypeIdent, ok := ptr.X.(*ast.Ident); ok {
			name := astTypeIdent.Name
			schemeType := TransToValidSchemeType(name)
			return propertyName{SchemaType: schemeType, ArrayType: schemeType}
		}
		if astTypeArray, ok := ptr.X.(*ast.ArrayType); ok { // if array
			if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.SelectorExpr); ok {
				return parseFieldSelectorExpr(astTypeArrayExpr, parser, newArrayProperty)
			}
			if astTypeArrayIdent := astTypeArray.Elt.(*ast.Ident); ok {
				name := astTypeArrayIdent.Name
				return propertyName{SchemaType: "array", ArrayType: name}
			}
		}
	}
	if astTypeArray, ok := field.Type.(*ast.ArrayType); ok { // if array
		if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.SelectorExpr); ok {
			return parseFieldSelectorExpr(astTypeArrayExpr, parser, newArrayProperty)
		}
		if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.StarExpr); ok {
			if astTypeArrayIdent := astTypeArrayExpr.X.(*ast.Ident); ok {
				name := astTypeArrayIdent.Name
				return propertyName{SchemaType: "array", ArrayType: name}
			}
		}
		str := fmt.Sprintf("%s", astTypeArray.Elt)
		return propertyName{SchemaType: "array", ArrayType: str}
	}
	if _, ok := field.Type.(*ast.MapType); ok { // if map
		//TODO: support map
		return propertyName{SchemaType: "object", ArrayType: "object"}
	}
	if _, ok := field.Type.(*ast.StructType); ok { // if struct
		return propertyName{SchemaType: "object", ArrayType: "object"}
	}
	if _, ok := field.Type.(*ast.InterfaceType); ok { // if interface{}
		return propertyName{SchemaType: "object", ArrayType: "object"}
	}
	panic("not supported" + fmt.Sprint(field.Type))
}
<|endoftext|>


<commit_before>// Package routeros provides a Client interface to to the Mikrotik RouterOS API
package routeros

import (
	"bytes"
	"fmt"
	"github.com/jcelliott/lumber"
	"net"
	"strconv"
	"strings"
	//"crypto/md5"
)

// A Client is a RouterOS API client.
type Client struct {
	// Network Address.
	// E.g. "10.0.0.1:8728" or "router.example.com:8728"
	address  string
	user     string
	password string
	logger   *lumber.Logger
	debug    bool     // debug logging enabled
	ready    bool     // Ready for work (login ok and connection not terminated)
	conn     net.Conn // Connection to pass around
}

// use slices of pairs instead of map because we care about order
type Pair struct {
	Key   string
	Value string
}

func NewRouterOSClient(address string) (*Client, error) {
	// basic validation of host address
	_, _, err := net.SplitHostPort(address)
	if err != nil {
		return nil, err
	}

	var c Client
	c.address = address

	return &c, nil
}

func (c *Client) Connect(user string, password string) error {
	conn, err := net.Dial("tcp", c.address)
	if err != nil {
		return err
	}
	defer conn.Close()

	// stash conn in instance
	c.conn = conn

	// try to log in
	res, err := c.Call("/login", nil)
	if err != nil {
		return err
	}
	fmt.Println(res)

	// handle challenge

	return nil
}

// Encode and send a single line
func (c *Client) send(word string) error {
	bword := []byte(word)
	prefix := prefixlen(len(bword))

	_, err := c.conn.Write(prefix.Bytes())
	if err != nil {
		return err
	}

	_, err = c.conn.Write(bword)
	if err != nil {
		return err
	}

	return nil
}

// Listen for reply
func (c *Client) receive() ([]Pair, error) {
	var pairs []Pair

	for {
		length := c.getlen()
		if length == 0 {
			break
		}
		inbuf := make([]byte, length)
		c.conn.Read(inbuf)
		word := string(inbuf)

		if word == "!done" {
			continue
		}

		if strings.Contains(word, "=") {
			var p Pair
			parts := strings.SplitN(word, "=", 3)
			p.Key = parts[1]
			p.Value = parts[2]
			pairs = append(pairs, p)
		}

	}

	return pairs, nil
}

func (c *Client) Call(command string, params []Pair) ([]Pair, error) {
	err := c.send(command)
	if err != nil {
		return nil, err
	}

	// send params if we got them
	if len(params) > 0 {
		for _, v := range params {
			word := fmt.Sprintf("=%s=%s", v.Key, v.Value)
			c.send(word)
		}
	}

	// send terminator
	err = c.send("")
	if err != nil {
		return nil, err
	}

	res, err := c.receive()
	if err != nil {
		return nil, err
	}

	return res, nil
}

// Enabled logging by providing a Logger instance from Lumber.
func (c *Client) Logging(l *lumber.Logger, level string) {
	c.logger = l
	if level == "DEBUG" {
		c.debug = true
	}
}

// Get just one byte because MT's size prefix is overoptimized
func (c *Client) getone() int {
	charlet := make([]byte, 1)
	c.conn.Read(charlet)
	numlet := int(charlet[0])
	return numlet
}

// Decode RouterOS API Word Size Prefix / Figure out how much to read
// TODO: based on MT Docs. Look for way to make this cleaner later
func (client *Client) getlen() int64 {
	c := int64(client.getone())

	if (c & 0x80) == 0x00 {

	} else if (c & 0xC0) == 0x80 {
		c &= ^0xC0
		c <<= 8
		c += int64(client.getone())
	} else if (c & 0xE0) == 0xC0 {
		c &= ^0xE0
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
	} else if (c & 0xF0) == 0xE0 {
		c &= ^0xF0
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
	} else if (c & 0xF8) == 0xF0 {
		c = int64(client.getone())
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
	}

	return c
}

// Calculate RouterOS API Word Size Prefix
// TODO: based on MT Docs. Look for way to make this cleaner later
func prefixlen(l int) bytes.Buffer {
	var b bytes.Buffer

	if l < 0x80 {
		b.Write([]byte(string(l)))
	} else if l < 0x4000 {
		l |= 0x8000
		b.Write([]byte(strconv.Itoa((l >> 8) & 0xFF)))
		b.Write([]byte(strconv.Itoa(l & 0xFF)))
	} else if l < 0x200000 {
		l |= 0xC00000
		b.Write([]byte(strconv.Itoa((l >> 16) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 8) & 0xFF)))
		b.Write([]byte(strconv.Itoa(l & 0xFF)))
	} else if l < 0x10000000 {
		l |= 0xE0000000
		b.Write([]byte(strconv.Itoa((l >> 24) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 16) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 8) & 0xFF)))
		b.Write([]byte(strconv.Itoa(l & 0xFF)))
	} else {
		b.Write([]byte(strconv.Itoa(0xF0)))
		b.Write([]byte(strconv.Itoa((l >> 24) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 16) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 8) & 0xFF)))
		b.Write([]byte(strconv.Itoa(l & 0xFF)))
	}

	return b
}
<commit_msg>checkpoint work on login / moving to desktop<commit_after>// Package routeros provides a Client interface to to the Mikrotik RouterOS API
package routeros

import (
	"bytes"
	"fmt"
	"github.com/jcelliott/lumber"
	"net"
	"strconv"
	"strings"
	"errors"
	"crypto/md5"
	"io"
)

// A Client is a RouterOS API client.
type Client struct {
	// Network Address.
	// E.g. "10.0.0.1:8728" or "router.example.com:8728"
	address  string
	user     string
	password string
	logger   *lumber.Logger
	debug    bool     // debug logging enabled
	ready    bool     // Ready for work (login ok and connection not terminated)
	conn     net.Conn // Connection to pass around
}

// use slices of pairs instead of map because we care about order
type Pair struct {
	Key   string
	Value string
}

func NewPair(key string, value string) *Pair {
	p := new(Pair)
	p.Key = key
	p.Value = value
	return p
}

// Get value for a specific key
func getPairValue(p []Pair, key string) (string, error) {
	for _, v := range p {
		if v.Key == key {
			return v.Value, nil
		}
	}
	return "", errors.New("key not found")
}

func NewRouterOSClient(address string) (*Client, error) {
	// basic validation of host address
	_, _, err := net.SplitHostPort(address)
	if err != nil {
		return nil, err
	}

	var c Client
	c.address = address

	return &c, nil
}

func (c *Client) Connect(user string, password string) error {
	conn, err := net.Dial("tcp", c.address)
	if err != nil {
		return err
	}
	defer conn.Close()

	// stash conn in instance
	c.conn = conn

	// try to log in
	res, err := c.Call("/login", nil)
	if err != nil {
		return err
	}

	// handle challenge/response
	challenge, err := getPairValue(res, "ret")
	if err != nil {
		return errors.New("Didn't get challenge from ROS")
	}
	h := md5.New()
	io.WriteString(h, "\x00")
	io.WriteString(h, password)
	io.WriteString(h, challenge)
	resp := fmt.Sprintf("%x", h.Sum(nil))
	var loginParams []Pair
	loginParams = append(loginParams, *NewPair("name", password))
	loginParams = append(loginParams, *NewPair("response", resp))

	res, err = c.Call("/login", loginParams)
	if err != nil {
		return err
	}
	fmt.Println(res)
	// handle challenge

	return nil
}

// Encode and send a single line
func (c *Client) send(word string) error {
	bword := []byte(word)
	prefix := prefixlen(len(bword))

	_, err := c.conn.Write(prefix.Bytes())
	if err != nil {
		return err
	}

	_, err = c.conn.Write(bword)
	if err != nil {
		return err
	}

	return nil
}

// Listen for reply
func (c *Client) receive() ([]Pair, error) {
	var pairs []Pair

	for {
		length := c.getlen()
		if length == 0 {
			break
		}
		inbuf := make([]byte, length)
		c.conn.Read(inbuf)
		word := string(inbuf)

		if word == "!done" {
			continue
		}

		if strings.Contains(word, "=") {
			var p Pair
			parts := strings.SplitN(word, "=", 3)
			p.Key = parts[1]
			p.Value = parts[2]
			pairs = append(pairs, p)
		}

	}

	return pairs, nil
}

func (c *Client) Call(command string, params []Pair) ([]Pair, error) {
	err := c.send(command)
	if err != nil {
		return nil, err
	}

	// send params if we got them
	if len(params) > 0 {
		for _, v := range params {
			word := fmt.Sprintf("=%s=%s", v.Key, v.Value)
			c.send(word)
		}
	}

	// send terminator
	err = c.send("")
	if err != nil {
		return nil, err
	}

	res, err := c.receive()
	if err != nil {
		return nil, err
	}

	return res, nil
}

// Enabled logging by providing a Logger instance from Lumber.
func (c *Client) Logging(l *lumber.Logger, level string) {
	c.logger = l
	if level == "DEBUG" {
		c.debug = true
	}
}

// Get just one byte because MT's size prefix is overoptimized
func (c *Client) getone() int {
	charlet := make([]byte, 1)
	c.conn.Read(charlet)
	numlet := int(charlet[0])
	return numlet
}

// Decode RouterOS API Word Size Prefix / Figure out how much to read
// TODO: based on MT Docs. Look for way to make this cleaner later
func (client *Client) getlen() int64 {
	c := int64(client.getone())

	if (c & 0x80) == 0x00 {

	} else if (c & 0xC0) == 0x80 {
		c &= ^0xC0
		c <<= 8
		c += int64(client.getone())
	} else if (c & 0xE0) == 0xC0 {
		c &= ^0xE0
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
	} else if (c & 0xF0) == 0xE0 {
		c &= ^0xF0
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
	} else if (c & 0xF8) == 0xF0 {
		c = int64(client.getone())
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
		c <<= 8
		c += int64(client.getone())
	}

	return c
}

// Calculate RouterOS API Word Size Prefix
// TODO: based on MT Docs. Look for way to make this cleaner later
func prefixlen(l int) bytes.Buffer {
	var b bytes.Buffer

	if l < 0x80 {
		b.Write([]byte(string(l)))
	} else if l < 0x4000 {
		l |= 0x8000
		b.Write([]byte(strconv.Itoa((l >> 8) & 0xFF)))
		b.Write([]byte(strconv.Itoa(l & 0xFF)))
	} else if l < 0x200000 {
		l |= 0xC00000
		b.Write([]byte(strconv.Itoa((l >> 16) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 8) & 0xFF)))
		b.Write([]byte(strconv.Itoa(l & 0xFF)))
	} else if l < 0x10000000 {
		l |= 0xE0000000
		b.Write([]byte(strconv.Itoa((l >> 24) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 16) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 8) & 0xFF)))
		b.Write([]byte(strconv.Itoa(l & 0xFF)))
	} else {
		b.Write([]byte(strconv.Itoa(0xF0)))
		b.Write([]byte(strconv.Itoa((l >> 24) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 16) & 0xFF)))
		b.Write([]byte(strconv.Itoa((l >> 8) & 0xFF)))
		b.Write([]byte(strconv.Itoa(l & 0xFF)))
	}

	return b
}
<|endoftext|>


<commit_before>package google

import (
	"encoding/json"
	"fmt"

	"github.com/hashicorp/terraform/helper/pathorcontents"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
)

// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
	return &schema.Provider{
		Schema: map[string]*schema.Schema{
			"account_file": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				DefaultFunc:  schema.EnvDefaultFunc("GOOGLE_ACCOUNT_FILE", nil),
				ValidateFunc: validateAccountFile,
				Deprecated:   "Use the credentials field instead",
			},

			"credentials": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
					"GOOGLE_CREDENTIALS",
					"GOOGLE_CLOUD_KEYFILE_JSON",
				}, nil),
				ValidateFunc: validateCredentials,
			},

			"project": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				DefaultFunc: schema.EnvDefaultFunc("GOOGLE_PROJECT", nil),
			},

			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				DefaultFunc: schema.EnvDefaultFunc("GOOGLE_REGION", nil),
			},
		},

		ResourcesMap: map[string]*schema.Resource{
			"google_compute_autoscaler":             resourceComputeAutoscaler(),
			"google_compute_address":                resourceComputeAddress(),
			"google_compute_backend_service":        resourceComputeBackendService(),
			"google_compute_disk":                   resourceComputeDisk(),
			"google_compute_firewall":               resourceComputeFirewall(),
			"google_compute_forwarding_rule":        resourceComputeForwardingRule(),
			"google_compute_global_address":         resourceComputeGlobalAddress(),
			"google_compute_global_forwarding_rule": resourceComputeGlobalForwardingRule(),
			"google_compute_http_health_check":      resourceComputeHttpHealthCheck(),
			"google_compute_https_health_check":     resourceComputeHttpsHealthCheck(),
			"google_compute_instance":               resourceComputeInstance(),
			"google_compute_instance_group":         resourceComputeInstanceGroup(),
			"google_compute_instance_group_manager": resourceComputeInstanceGroupManager(),
			"google_compute_instance_template":      resourceComputeInstanceTemplate(),
			"google_compute_network":                resourceComputeNetwork(),
			"google_compute_project_metadata":       resourceComputeProjectMetadata(),
			"google_compute_route":                  resourceComputeRoute(),
			"google_compute_ssl_certificate":        resourceComputeSslCertificate(),
			"google_compute_subnetwork":             resourceComputeSubnetwork(),
			"google_compute_target_http_proxy":      resourceComputeTargetHttpProxy(),
			"google_compute_target_https_proxy":     resourceComputeTargetHttpsProxy(),
			"google_compute_target_pool":            resourceComputeTargetPool(),
			"google_compute_url_map":                resourceComputeUrlMap(),
			"google_compute_vpn_gateway":            resourceComputeVpnGateway(),
			"google_compute_vpn_tunnel":             resourceComputeVpnTunnel(),
			"google_container_cluster":              resourceContainerCluster(),
			"google_dns_managed_zone":               resourceDnsManagedZone(),
			"google_dns_record_set":                 resourceDnsRecordSet(),
			"google_sql_database":                   resourceSqlDatabase(),
			"google_sql_database_instance":          resourceSqlDatabaseInstance(),
			"google_sql_user":                       resourceSqlUser(),
			"google_pubsub_topic":                   resourcePubsubTopic(),
			"google_pubsub_subscription":            resourcePubsubSubscription(),
			"google_storage_bucket":                 resourceStorageBucket(),
			"google_storage_bucket_acl":             resourceStorageBucketAcl(),
			"google_storage_bucket_object":          resourceStorageBucketObject(),
			"google_storage_object_acl":             resourceStorageObjectAcl(),
		},

		ConfigureFunc: providerConfigure,
	}
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
	credentials := d.Get("credentials").(string)
	if credentials == "" {
		credentials = d.Get("account_file").(string)
	}
	config := Config{
		Credentials: credentials,
		Project:     d.Get("project").(string),
		Region:      d.Get("region").(string),
	}

	if err := config.loadAndValidate(); err != nil {
		return nil, err
	}

	return &config, nil
}

func validateAccountFile(v interface{}, k string) (warnings []string, errors []error) {
	if v == nil {
		return
	}

	value := v.(string)

	if value == "" {
		return
	}

	contents, wasPath, err := pathorcontents.Read(value)
	if err != nil {
		errors = append(errors, fmt.Errorf("Error loading Account File: %s", err))
	}
	if wasPath {
		warnings = append(warnings, `account_file was provided as a path instead of
as file contents. This support will be removed in the future. Please update
your configuration to use ${file("filename.json")} instead.`)
	}

	var account accountFile
	if err := json.Unmarshal([]byte(contents), &account); err != nil {
		errors = append(errors,
			fmt.Errorf("account_file not valid JSON '%s': %s", contents, err))
	}

	return
}

func validateCredentials(v interface{}, k string) (warnings []string, errors []error) {
	if v == nil || v.(string) == "" {
		return
	}
	creds := v.(string)
	var account accountFile
	if err := json.Unmarshal([]byte(creds), &account); err != nil {
		errors = append(errors,
			fmt.Errorf("credentials are not valid JSON '%s': %s", creds, err))
	}

	return
}

// getRegionFromZone returns the region from a zone for Google cloud.
func getRegionFromZone(zone string) string {
	if zone != "" && len(zone) > 2 {
		region := zone[:len(zone)-2]
		return region
	}
	return ""
}
<commit_msg>Make GCP provider "project" attribute optional<commit_after>package google

import (
	"encoding/json"
	"fmt"

	"github.com/hashicorp/terraform/helper/pathorcontents"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
)

// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
	return &schema.Provider{
		Schema: map[string]*schema.Schema{
			"account_file": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				DefaultFunc:  schema.EnvDefaultFunc("GOOGLE_ACCOUNT_FILE", nil),
				ValidateFunc: validateAccountFile,
				Deprecated:   "Use the credentials field instead",
			},

			"credentials": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
					"GOOGLE_CREDENTIALS",
					"GOOGLE_CLOUD_KEYFILE_JSON",
				}, nil),
				ValidateFunc: validateCredentials,
			},

			"project": &schema.Schema{
				Type:        schema.TypeString,
				Required:    false,
				DefaultFunc: schema.EnvDefaultFunc("GOOGLE_PROJECT", nil),
			},

			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				DefaultFunc: schema.EnvDefaultFunc("GOOGLE_REGION", nil),
			},
		},

		ResourcesMap: map[string]*schema.Resource{
			"google_compute_autoscaler":             resourceComputeAutoscaler(),
			"google_compute_address":                resourceComputeAddress(),
			"google_compute_backend_service":        resourceComputeBackendService(),
			"google_compute_disk":                   resourceComputeDisk(),
			"google_compute_firewall":               resourceComputeFirewall(),
			"google_compute_forwarding_rule":        resourceComputeForwardingRule(),
			"google_compute_global_address":         resourceComputeGlobalAddress(),
			"google_compute_global_forwarding_rule": resourceComputeGlobalForwardingRule(),
			"google_compute_http_health_check":      resourceComputeHttpHealthCheck(),
			"google_compute_https_health_check":     resourceComputeHttpsHealthCheck(),
			"google_compute_instance":               resourceComputeInstance(),
			"google_compute_instance_group":         resourceComputeInstanceGroup(),
			"google_compute_instance_group_manager": resourceComputeInstanceGroupManager(),
			"google_compute_instance_template":      resourceComputeInstanceTemplate(),
			"google_compute_network":                resourceComputeNetwork(),
			"google_compute_project_metadata":       resourceComputeProjectMetadata(),
			"google_compute_route":                  resourceComputeRoute(),
			"google_compute_ssl_certificate":        resourceComputeSslCertificate(),
			"google_compute_subnetwork":             resourceComputeSubnetwork(),
			"google_compute_target_http_proxy":      resourceComputeTargetHttpProxy(),
			"google_compute_target_https_proxy":     resourceComputeTargetHttpsProxy(),
			"google_compute_target_pool":            resourceComputeTargetPool(),
			"google_compute_url_map":                resourceComputeUrlMap(),
			"google_compute_vpn_gateway":            resourceComputeVpnGateway(),
			"google_compute_vpn_tunnel":             resourceComputeVpnTunnel(),
			"google_container_cluster":              resourceContainerCluster(),
			"google_dns_managed_zone":               resourceDnsManagedZone(),
			"google_dns_record_set":                 resourceDnsRecordSet(),
			"google_sql_database":                   resourceSqlDatabase(),
			"google_sql_database_instance":          resourceSqlDatabaseInstance(),
			"google_sql_user":                       resourceSqlUser(),
			"google_pubsub_topic":                   resourcePubsubTopic(),
			"google_pubsub_subscription":            resourcePubsubSubscription(),
			"google_storage_bucket":                 resourceStorageBucket(),
			"google_storage_bucket_acl":             resourceStorageBucketAcl(),
			"google_storage_bucket_object":          resourceStorageBucketObject(),
			"google_storage_object_acl":             resourceStorageObjectAcl(),
		},

		ConfigureFunc: providerConfigure,
	}
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
	credentials := d.Get("credentials").(string)
	if credentials == "" {
		credentials = d.Get("account_file").(string)
	}
	config := Config{
		Credentials: credentials,
		Project:     d.Get("project").(string),
		Region:      d.Get("region").(string),
	}

	if err := config.loadAndValidate(); err != nil {
		return nil, err
	}

	return &config, nil
}

func validateAccountFile(v interface{}, k string) (warnings []string, errors []error) {
	if v == nil {
		return
	}

	value := v.(string)

	if value == "" {
		return
	}

	contents, wasPath, err := pathorcontents.Read(value)
	if err != nil {
		errors = append(errors, fmt.Errorf("Error loading Account File: %s", err))
	}
	if wasPath {
		warnings = append(warnings, `account_file was provided as a path instead of
as file contents. This support will be removed in the future. Please update
your configuration to use ${file("filename.json")} instead.`)
	}

	var account accountFile
	if err := json.Unmarshal([]byte(contents), &account); err != nil {
		errors = append(errors,
			fmt.Errorf("account_file not valid JSON '%s': %s", contents, err))
	}

	return
}

func validateCredentials(v interface{}, k string) (warnings []string, errors []error) {
	if v == nil || v.(string) == "" {
		return
	}
	creds := v.(string)
	var account accountFile
	if err := json.Unmarshal([]byte(creds), &account); err != nil {
		errors = append(errors,
			fmt.Errorf("credentials are not valid JSON '%s': %s", creds, err))
	}

	return
}

// getRegionFromZone returns the region from a zone for Google cloud.
func getRegionFromZone(zone string) string {
	if zone != "" && len(zone) > 2 {
		region := zone[:len(zone)-2]
		return region
	}
	return ""
}
<|endoftext|>


<commit_before>package source

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

	"github.com/anchore/syft/internal/file"
)

var _ Resolver = (*MockResolver)(nil)

// MockResolver implements the Resolver interface and is intended for use *only in test code*.
// It provides an implementation that can resolve local filesystem paths using only a provided discrete list of file
// paths, which are typically paths to test fixtures.
type MockResolver struct {
	Locations []Location
}

// NewMockResolverForPaths creates a new MockResolver, where the only resolvable
// files are those specified by the supplied paths.
func NewMockResolverForPaths(paths ...string) *MockResolver {
	var locations []Location
	for _, p := range paths {
		locations = append(locations, NewLocation(p))
	}

	return &MockResolver{Locations: locations}
}

// String returns the string representation of the MockResolver.
func (r MockResolver) String() string {
	return fmt.Sprintf("mock:(%s,...)", r.Locations[0].Path)
}

// FileContentsByLocation fetches file contents for a single location. If the
// path does not exist, an error is returned.
func (r MockResolver) FileContentsByLocation(location Location) (string, error) {
	for _, l := range r.Locations {
		if l == location {
			return stringContent(location.Path)
		}
	}

	return "", fmt.Errorf("no file for location: %v", location)
}

// MultipleFileContentsByLocation returns the file contents for all specified Locations.
func (r MockResolver) MultipleFileContentsByLocation(locations []Location) (map[Location]string, error) {
	results := make(map[Location]string)
	for _, l := range locations {
		contents, err := r.FileContentsByLocation(l)
		if err != nil {
			return nil, err
		}
		results[l] = contents
	}

	return results, nil
}

// FilesByPath returns all Locations that match the given paths.
func (r MockResolver) FilesByPath(paths ...string) ([]Location, error) {
	var results []Location
	for _, p := range paths {
		for _, location := range r.Locations {
			if p == location.Path {
				results = append(results, NewLocation(p))
			}
		}
	}

	return results, nil
}

// FilesByGlob returns all Locations that match the given path glob pattern.
func (r MockResolver) FilesByGlob(patterns ...string) ([]Location, error) {
	var results []Location
	for _, pattern := range patterns {
		for _, location := range r.Locations {
			if file.GlobMatch(pattern, location.Path) {
				results = append(results, location)
			}
		}
	}

	return results, nil
}

// RelativeFileByPath returns a single Location for the given path.
func (r MockResolver) RelativeFileByPath(_ Location, path string) *Location {
	paths, err := r.FilesByPath(path)
	if err != nil {
		return nil
	}

	if len(paths) < 1 {
		return nil
	}

	return &paths[0]
}

func stringContent(path string) (string, error) {
	reader, err := os.Open(path)
	if err != nil {
		return "", err
	}

	b, err := ioutil.ReadAll(reader)
	if err != nil {
		return "", err
	}

	return string(b), nil
}
<commit_msg>Fix mock resolver interface implementation<commit_after>package source

import (
	"fmt"
	"io"
	"os"

	"github.com/anchore/syft/internal/file"
)

var _ Resolver = (*MockResolver)(nil)

// MockResolver implements the Resolver interface and is intended for use *only in test code*.
// It provides an implementation that can resolve local filesystem paths using only a provided discrete list of file
// paths, which are typically paths to test fixtures.
type MockResolver struct {
	Locations []Location
}

// NewMockResolverForPaths creates a new MockResolver, where the only resolvable
// files are those specified by the supplied paths.
func NewMockResolverForPaths(paths ...string) *MockResolver {
	var locations []Location
	for _, p := range paths {
		locations = append(locations, NewLocation(p))
	}

	return &MockResolver{Locations: locations}
}

// String returns the string representation of the MockResolver.
func (r MockResolver) String() string {
	return fmt.Sprintf("mock:(%s,...)", r.Locations[0].Path)
}

// FileContentsByLocation fetches file contents for a single location. If the
// path does not exist, an error is returned.
func (r MockResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
	for _, l := range r.Locations {
		if l == location {
			return os.Open(location.Path)
		}
	}

	return nil, fmt.Errorf("no file for location: %v", location)
}

// MultipleFileContentsByLocation returns the file contents for all specified Locations.
func (r MockResolver) MultipleFileContentsByLocation(locations []Location) (map[Location]io.ReadCloser, error) {
	results := make(map[Location]io.ReadCloser)
	for _, l := range locations {
		contents, err := r.FileContentsByLocation(l)
		if err != nil {
			return nil, err
		}
		results[l] = contents
	}

	return results, nil
}

// FilesByPath returns all Locations that match the given paths.
func (r MockResolver) FilesByPath(paths ...string) ([]Location, error) {
	var results []Location
	for _, p := range paths {
		for _, location := range r.Locations {
			if p == location.Path {
				results = append(results, NewLocation(p))
			}
		}
	}

	return results, nil
}

// FilesByGlob returns all Locations that match the given path glob pattern.
func (r MockResolver) FilesByGlob(patterns ...string) ([]Location, error) {
	var results []Location
	for _, pattern := range patterns {
		for _, location := range r.Locations {
			if file.GlobMatch(pattern, location.Path) {
				results = append(results, location)
			}
		}
	}

	return results, nil
}

// RelativeFileByPath returns a single Location for the given path.
func (r MockResolver) RelativeFileByPath(_ Location, path string) *Location {
	paths, err := r.FilesByPath(path)
	if err != nil {
		return nil
	}

	if len(paths) < 1 {
		return nil
	}

	return &paths[0]
}
<|endoftext|>


<commit_before>package r

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gorilla/mux"
)

type Server struct {
	keys     chan string
	sessions chan *Session
}

var cache map[string]string
var cachingEnabled bool

func (s *Server) Call(pkg, fun, args string) (string, error) {
	session := <-s.sessions
	res, err := session.Call(pkg, fun, args)
	s.sessions <- session
	return res, err
}

func (s Server) Get(key, format string) ([]byte, error) {
	session := <-s.sessions
	res, err := session.Get(key, format)
	s.sessions <- session
	return res, err
}

func (s *Server) CallHandler(w http.ResponseWriter, r *http.Request) {

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Println("Call read body failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)
		return
	}

	call := Call{}
	err = json.Unmarshal(body, &call)
	if err != nil {
		fmt.Println("Call unmarshal failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)
		return
	}

	log("Call:", call.Package, call.Function, call.Arguments)

	if cachingEnabled {
		key := call.cacheKey()
		res := cache[key]
		if res != "" {
			log("Cache hit")
			w.Write([]byte(res))
			return
		}
		log("Cache miss")
	}

	res, err := s.Call(call.Package, call.Function, call.Arguments)
	if err != nil {
		fmt.Println("Call failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)

	}
	w.Write([]byte(res))

	if cachingEnabled {
		key := call.cacheKey()
		cache[key] = res
	}

	return
}

func (s *Server) EnableCaching() {
	cachingEnabled = true
	cache = make(map[string]string, 0)
}

func (s *Server) GetHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	key := vars["key"]
	format := vars["format"]

	log("Get", key, format)

	res, err := s.Get(key, format)
	if err != nil {
		fmt.Println("Get failed", err)
		http.Error(w, "Get failed."+err.Error(), 500)
		return
	}

	w.Write(res)
}
<commit_msg>add start server<commit_after>package r

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gorilla/mux"
)

type Server struct {
	keys     chan string
	sessions chan *Session
}

var cache map[string]string
var cachingEnabled bool

func (s *Server) Call(pkg, fun, args string) (string, error) {
	session := <-s.sessions
	res, err := session.Call(pkg, fun, args)
	s.sessions <- session
	return res, err
}

func (s Server) Get(key, format string) ([]byte, error) {
	session := <-s.sessions
	res, err := session.Get(key, format)
	s.sessions <- session
	return res, err
}

func (s *Server) CallHandler(w http.ResponseWriter, r *http.Request) {

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Println("Call read body failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)
		return
	}

	call := Call{}
	err = json.Unmarshal(body, &call)
	if err != nil {
		fmt.Println("Call unmarshal failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)
		return
	}

	log("Call:", call.Package, call.Function, call.Arguments)

	if cachingEnabled {
		key := call.cacheKey()
		res := cache[key]
		if res != "" {
			log("Cache hit")
			w.Write([]byte(res))
			return
		}
		log("Cache miss")
	}

	res, err := s.Call(call.Package, call.Function, call.Arguments)
	if err != nil {
		fmt.Println("Call failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)

	}
	w.Write([]byte(res))

	if cachingEnabled {
		key := call.cacheKey()
		cache[key] = res
	}

	return
}

func (s *Server) GetHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	key := vars["key"]
	format := vars["format"]

	log("Get", key, format)

	res, err := s.Get(key, format)
	if err != nil {
		fmt.Println("Get failed", err)
		http.Error(w, "Get failed."+err.Error(), 500)
		return
	}

	w.Write(res)
}

func (s *Server) EnableCaching() {
	cachingEnabled = true
	cache = make(map[string]string, 0)
}

func (s *Server) Start(port string) error {

	router := mux.NewRouter()
	router.HandleFunc("/call", s.CallHandler)
	router.HandleFunc("/get/{key}/{format}", s.GetHandler)
	http.Handle("/", router)

	return http.ListenAndServe(port, router)

}
<|endoftext|>


<commit_before>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package casbin

// GetRolesForUser gets the roles that a user has.
func (e *Enforcer) GetRolesForUser(name string) []string {
	return e.model["g"]["g"].RM.GetRoles(name)
}

// GetUsersForRole gets the users that has a role.
func (e *Enforcer) GetUsersForRole(name string) []string {
	return e.model["g"]["g"].RM.GetUsers(name)
}

// HasRoleForUser determines whether a user has a role.
func (e *Enforcer) HasRoleForUser(name string, role string) bool {
	roles := e.GetRolesForUser(name)

	hasRole := false
	for _, r := range roles {
		if r == role {
			hasRole = true
			break
		}
	}

	return hasRole
}

// AddRoleForUser adds a role for a user.
// Returns false if the user already has the role.
func (e *Enforcer) AddRoleForUser(user string, role string) bool {
	return e.AddGroupingPolicy(user, role)
}

// DeleteRoleForUser deletes a role for a user.
func (e *Enforcer) DeleteRoleForUser(user string, role string) {
	e.RemoveGroupingPolicy(user, role)
}

// DeleteRolesForUser deletes all roles for a user.
func (e *Enforcer) DeleteRolesForUser(user string) {
	e.RemoveFilteredGroupingPolicy(0, user)
}

// DeleteUser deletes a user.
func (e *Enforcer) DeleteUser(user string) {
	e.RemoveFilteredGroupingPolicy(0, user)
}

// DeleteRole deletes a role.
func (e *Enforcer) DeleteRole(role string) {
	e.RemoveFilteredGroupingPolicy(1, role)
	e.RemoveFilteredPolicy(0, role)
}

// DeletePermission deletes a permission.
func (e *Enforcer) DeletePermission(permission ...string) {
	e.RemoveFilteredPolicy(1, permission...)
}

// AddPermissionForUser adds a permission for a user or role.
// Returns false if the user or role already has the permission.
func (e *Enforcer) AddPermissionForUser(user string, permission ...string) bool {
	params := make([]interface{}, 0, len(permission)+1)

	params = append(params, user)
	for _, perm := range permission {
		params = append(params, perm)
	}

	return e.AddPolicy(params...)
}

// DeletePermissionForUser deletes a permission for a user or role.
func (e *Enforcer) DeletePermissionForUser(user string, permission ...string) {
	params := make([]interface{}, 0, len(permission)+1)

	params = append(params, user)
	for _, perm := range permission {
		params = append(params, perm)
	}

	e.RemovePolicy(params...)
}

// DeletePermissionsForUser deletes permissions for a user or role.
func (e *Enforcer) DeletePermissionsForUser(user string) {
	e.RemoveFilteredPolicy(0, user)
}

// GetPermissionsForUser gets permissions for a user or role.
func (e *Enforcer) GetPermissionsForUser(user string) [][]string {
	return e.GetFilteredPolicy(0, user)
}

// HasPermissionForUser determines whether a user has a permission.
func (e *Enforcer) HasPermissionForUser(user string, permission ...string) bool {
	params := make([]interface{}, 0, len(permission)+1)

	params = append(params, user)
	for _, perm := range permission {
		params = append(params, perm)
	}

	return e.HasPolicy(params...)
}
<commit_msg>Return the affected boolean for RBAC API.<commit_after>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package casbin

// GetRolesForUser gets the roles that a user has.
func (e *Enforcer) GetRolesForUser(name string) []string {
	return e.model["g"]["g"].RM.GetRoles(name)
}

// GetUsersForRole gets the users that has a role.
func (e *Enforcer) GetUsersForRole(name string) []string {
	return e.model["g"]["g"].RM.GetUsers(name)
}

// HasRoleForUser determines whether a user has a role.
func (e *Enforcer) HasRoleForUser(name string, role string) bool {
	roles := e.GetRolesForUser(name)

	hasRole := false
	for _, r := range roles {
		if r == role {
			hasRole = true
			break
		}
	}

	return hasRole
}

// AddRoleForUser adds a role for a user.
// Returns false if the user already has the role (aka not affected).
func (e *Enforcer) AddRoleForUser(user string, role string) bool {
	return e.AddGroupingPolicy(user, role)
}

// DeleteRoleForUser deletes a role for a user.
// Returns false if the user does not have the role (aka not affected).
func (e *Enforcer) DeleteRoleForUser(user string, role string) bool {
	return e.RemoveGroupingPolicy(user, role)
}

// DeleteRolesForUser deletes all roles for a user.
// Returns false if the user does not have any roles (aka not affected).
func (e *Enforcer) DeleteRolesForUser(user string) bool {
	return e.RemoveFilteredGroupingPolicy(0, user)
}

// DeleteUser deletes a user.
// Returns false if the user does not exist (aka not affected).
func (e *Enforcer) DeleteUser(user string) bool {
	return e.RemoveFilteredGroupingPolicy(0, user)
}

// DeleteRole deletes a role.
func (e *Enforcer) DeleteRole(role string) {
	e.RemoveFilteredGroupingPolicy(1, role)
	e.RemoveFilteredPolicy(0, role)
}

// DeletePermission deletes a permission.
// Returns false if the permission does not exist (aka not affected).
func (e *Enforcer) DeletePermission(permission ...string) bool {
	return e.RemoveFilteredPolicy(1, permission...)
}

// AddPermissionForUser adds a permission for a user or role.
// Returns false if the user or role already has the permission (aka not affected).
func (e *Enforcer) AddPermissionForUser(user string, permission ...string) bool {
	params := make([]interface{}, 0, len(permission)+1)

	params = append(params, user)
	for _, perm := range permission {
		params = append(params, perm)
	}

	return e.AddPolicy(params...)
}

// DeletePermissionForUser deletes a permission for a user or role.
// Returns false if the user or role does not have the permission (aka not affected).
func (e *Enforcer) DeletePermissionForUser(user string, permission ...string) bool {
	params := make([]interface{}, 0, len(permission)+1)

	params = append(params, user)
	for _, perm := range permission {
		params = append(params, perm)
	}

	return e.RemovePolicy(params...)
}

// DeletePermissionsForUser deletes permissions for a user or role.
// Returns false if the user or role does not have any permissions (aka not affected).
func (e *Enforcer) DeletePermissionsForUser(user string) bool {
	return e.RemoveFilteredPolicy(0, user)
}

// GetPermissionsForUser gets permissions for a user or role.
func (e *Enforcer) GetPermissionsForUser(user string) [][]string {
	return e.GetFilteredPolicy(0, user)
}

// HasPermissionForUser determines whether a user has a permission.
func (e *Enforcer) HasPermissionForUser(user string, permission ...string) bool {
	params := make([]interface{}, 0, len(permission)+1)

	params = append(params, user)
	for _, perm := range permission {
		params = append(params, perm)
	}

	return e.HasPolicy(params...)
}
<|endoftext|>


<commit_before>/*

TODO modify the code with github.com/4ur3l13n/boot

*/

package goxp

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"

	"github.com/codegansta/inject"
)

const (
	panicHtml = '
<title>PANIC: %s</title>

PANIC

%s

%s

'
}

var (
	dunno     = []byte("???")
	centerDot = []byte("·")
	dot       = []byte(".")
	slash     = []byte("/")
}

// stack returns a nicely formated stack frame, skipping skip frames
func stack(skip int) []byte {
	buf := new(bytes.Buffer) // the returned data
	// As we loop, we open files and read them. These variables record the currently
	// loadded file.
	var lines [][]byte
	var lastFile string
	for i := skip; ; i++ { // Skip the expected number of frames
		pc, file, line, ok := runtime.Caller(i)
		if lok {
			break
		}
		// Print this much at least. If we can't find the source, it won't show.
		fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
		if file != lastFile {
			data, err := ioutil.ReadFile(file)
			if err != nil {
				continue
			}
			lines = bytes.Split(data, []byte{'\n'})
			lastFile = file
		}
		fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
	}
	return buf.Bytes()
}

//source returns a space-trimmed slice of the n'th line
func source(lines [][]byte, n int) []byte {
	n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
	if n < 0 || n >= len(lines) {
		return dunno
	}
	return bytes.TrimSpace(lines[n])
}

// function returns, if possible, the name of the function containing the PC.
<commit_msg>recovery.go +func function<commit_after>/*

TODO modify the code with github.com/4ur3l13n/boot

*/

package goxp

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"

	"github.com/codegansta/inject"
)

const (
	panicHtml = '
<title>PANIC: %s</title>

PANIC

%s

%s

'
}

var (
	dunno     = []byte("???")
	centerDot = []byte("·")
	dot       = []byte(".")
	slash     = []byte("/")
}

// stack returns a nicely formated stack frame, skipping skip frames
func stack(skip int) []byte {
	buf := new(bytes.Buffer) // the returned data
	// As we loop, we open files and read them. These variables record the currently
	// loadded file.
	var lines [][]byte
	var lastFile string
	for i := skip; ; i++ { // Skip the expected number of frames
		pc, file, line, ok := runtime.Caller(i)
		if lok {
			break
		}
		// Print this much at least. If we can't find the source, it won't show.
		fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
		if file != lastFile {
			data, err := ioutil.ReadFile(file)
			if err != nil {
				continue
			}
			lines = bytes.Split(data, []byte{'\n'})
			lastFile = file
		}
		fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
	}
	return buf.Bytes()
}

//source returns a space-trimmed slice of the n'th line
func source(lines [][]byte, n int) []byte {
	n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
	if n < 0 || n >= len(lines) {
		return dunno
	}
	return bytes.TrimSpace(lines[n])
}

// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
	fn := runtime.FuncForPC(pc)
	if fn == nil {
		return dunno
	}
	name := []byte(fn.Name())
	// The name includes the path name to the package, which is unnecessary
	// since the file name is already included. Plus, it has center dots.
	// That is, we see
	//      runtime/debug.*T·ptrmethod
	// and want
	//      *T.ptrmethod
	// Also the package path might contains dot (e.g. code.google.com/...),
	// so first eliminate the path prefix
	if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
		name = name[lastslash+1:]
	}
	if period := bytes.Index(name, dot); period >= 0 {
		name = name[period+1:]
	}
	name = bytes.Replace(name, centerDot, dot, -1)
	return name
}

<|endoftext|>


<commit_before>package req

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"strings"
)

type RedirectPolicy func(req *http.Request, via []*http.Request) error

// MaxRedirectPolicy specifies the max number of redirect
func MaxRedirectPolicy(noOfRedirect int) RedirectPolicy {
	return func(req *http.Request, via []*http.Request) error {
		if len(via) >= noOfRedirect {
			return fmt.Errorf("stopped after %d redirects", noOfRedirect)
		}
		return nil
	}
}

// NoRedirectPolicy disable redirect behaviour
func NoRedirectPolicy() RedirectPolicy {
	return func(req *http.Request, via []*http.Request) error {
		return errors.New("redirect is disabled")
	}
}

func SameDomainRedirectPolicy() RedirectPolicy {
	return func(req *http.Request, via []*http.Request) error {
		if getDomain(req.URL.Host) != getDomain(via[0].URL.Host) {
			return errors.New("different domain name is not allowed")
		}
		return nil
	}
}

// SameHostRedirectPolicy allows redirect only if the redirected host
// is the same as original host, e.g. redirect to "www.imroc.cc" from
// "imroc.cc" is not the allowed.
func SameHostRedirectPolicy() RedirectPolicy {
	return func(req *http.Request, via []*http.Request) error {
		if getHostname(req.URL.Host) != getHostname(via[0].URL.Host) {
			return errors.New("different host name is not allowed")
		}
		return nil
	}
}

// AllowedHostRedirectPolicy allows redirect only if the redirected host
// match one of the host that specified.
func AllowedHostRedirectPolicy(hosts ...string) RedirectPolicy {
	m := make(map[string]bool)
	for _, h := range hosts {
		m[strings.ToLower(getHostname(h))] = true
	}

	return func(req *http.Request, via []*http.Request) error {
		h := getHostname(req.URL.Host)
		if _, ok := m[h]; !ok {
			return fmt.Errorf("redirect host [%s] is not allowed", h)
		}
		return nil
	}
}

// AllowedDomainRedirectPolicy allows redirect only if the redirected domain
// match one of the domain that specified.
func AllowedDomainRedirectPolicy(hosts ...string) RedirectPolicy {
	domains := make(map[string]bool)
	for _, h := range hosts {
		domains[strings.ToLower(getDomain(h))] = true
	}

	return func(req *http.Request, via []*http.Request) error {
		domain := getDomain(req.URL.Host)
		if _, ok := domains[domain]; !ok {
			return fmt.Errorf("redirect domain [%s] is not allowed", domain)
		}
		return nil
	}
}

func getHostname(host string) (hostname string) {
	if strings.Index(host, ":") > 0 {
		host, _, _ = net.SplitHostPort(host)
	}
	hostname = strings.ToLower(host)
	return
}

func getDomain(host string) string {
	host = getHostname(host)
	ss := strings.Split(host, ".")
	if len(ss) < 3 {
		return host
	}
	ss = ss[1:]
	return strings.Join(ss, ".")
}
<commit_msg>add comments for RedirectPolicy<commit_after>package req

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"strings"
)

// RedirectPolicy represents the redirect policy for Client.
type RedirectPolicy func(req *http.Request, via []*http.Request) error

// MaxRedirectPolicy specifies the max number of redirect
func MaxRedirectPolicy(noOfRedirect int) RedirectPolicy {
	return func(req *http.Request, via []*http.Request) error {
		if len(via) >= noOfRedirect {
			return fmt.Errorf("stopped after %d redirects", noOfRedirect)
		}
		return nil
	}
}

// NoRedirectPolicy disable redirect behaviour
func NoRedirectPolicy() RedirectPolicy {
	return func(req *http.Request, via []*http.Request) error {
		return errors.New("redirect is disabled")
	}
}

// SameDomainRedirectPolicy allows redirect only if the redirected domain
// is the same as original domain, e.g. redirect to "www.imroc.cc" from
// "imroc.cc" is allowed, but redirect to "google.com" is not allowed.
func SameDomainRedirectPolicy() RedirectPolicy {
	return func(req *http.Request, via []*http.Request) error {
		if getDomain(req.URL.Host) != getDomain(via[0].URL.Host) {
			return errors.New("different domain name is not allowed")
		}
		return nil
	}
}

// SameHostRedirectPolicy allows redirect only if the redirected host
// is the same as original host, e.g. redirect to "www.imroc.cc" from
// "imroc.cc" is not the allowed.
func SameHostRedirectPolicy() RedirectPolicy {
	return func(req *http.Request, via []*http.Request) error {
		if getHostname(req.URL.Host) != getHostname(via[0].URL.Host) {
			return errors.New("different host name is not allowed")
		}
		return nil
	}
}

// AllowedHostRedirectPolicy allows redirect only if the redirected host
// match one of the host that specified.
func AllowedHostRedirectPolicy(hosts ...string) RedirectPolicy {
	m := make(map[string]bool)
	for _, h := range hosts {
		m[strings.ToLower(getHostname(h))] = true
	}

	return func(req *http.Request, via []*http.Request) error {
		h := getHostname(req.URL.Host)
		if _, ok := m[h]; !ok {
			return fmt.Errorf("redirect host [%s] is not allowed", h)
		}
		return nil
	}
}

// AllowedDomainRedirectPolicy allows redirect only if the redirected domain
// match one of the domain that specified.
func AllowedDomainRedirectPolicy(hosts ...string) RedirectPolicy {
	domains := make(map[string]bool)
	for _, h := range hosts {
		domains[strings.ToLower(getDomain(h))] = true
	}

	return func(req *http.Request, via []*http.Request) error {
		domain := getDomain(req.URL.Host)
		if _, ok := domains[domain]; !ok {
			return fmt.Errorf("redirect domain [%s] is not allowed", domain)
		}
		return nil
	}
}

func getHostname(host string) (hostname string) {
	if strings.Index(host, ":") > 0 {
		host, _, _ = net.SplitHostPort(host)
	}
	hostname = strings.ToLower(host)
	return
}

func getDomain(host string) string {
	host = getHostname(host)
	ss := strings.Split(host, ".")
	if len(ss) < 3 {
		return host
	}
	ss = ss[1:]
	return strings.Join(ss, ".")
}
<|endoftext|>


<commit_before>package registry

import (
	"github.com/hashicorp/consul/api"

	"net/url"
	"strconv"
	"time"
)

var args []string

const REGISTRY_PREFIX = "registry"

func New(dsn string, osArgs []string) (Registry, error) {
	args = osArgs
	url, err := url.Parse(dsn)

	if err != nil {
		return nil, err
	}
	client, err := api.NewClient(&api.Config{
		Scheme:     url.Scheme,
		Address:    url.Host,
		Datacenter: url.Query().Get("dc"),
		Token:      url.Query().Get("token"),
	})
	if err != nil {
		return nil, err
	}
	registry := registry{
		client:          client,
		datacenter:      url.Query().Get("dc"),
		refreshInterval: 30 * time.Second,
		bindChan:        make(chan struct{}),
	}
	if interval := url.Query().Get("refresh_interval"); len(interval) != 0 {
		if v, err := strconv.ParseInt(interval, 10, 64); err == nil && v > 0 {
			registry.refreshInterval = time.Duration(v) * time.Second
		}
	}
	go registry.supervisor()
	return &registry, nil
}

type registry struct {
	client          *api.Client
	configs         []config
	refreshInterval time.Duration
	datacenter      string
	bindChan        chan struct{}
}

func (r *registry) KV() KV {
	return &kv{client: r.client.KV()}
}

func (r *registry) Discovery() Discovery {
	return &discovery{
		agent:      r.client.Agent(),
		datacenter: r.datacenter,
	}
}

func (r *registry) supervisor() {
	var (
		refresh = time.Tick(r.refreshInterval)
	)
	for {
		select {
		case <-refresh:
			r.refresh()
		case <-r.bindChan:
			r.refresh()
		}
	}
}

func (r *registry) refresh() {
	var (
		kv   = r.KV()
		keys []string
	)
	for _, config := range r.configs {
		for _, item := range config.items {
			keys = append(keys, item.key)
		}
	}
	values := make(map[string]string, len(keys))
	for _, key := range keys {
		if v, err := kv.Get(key); err == nil {
			values[key] = v
		}
	}
	for _, config := range r.configs {
		config.mutex.Lock()
		for _, item := range config.items {
			if value, ok := values[item.key]; ok {
				item.set(value)
			}
		}
		config.mutex.Unlock()
	}
}
<commit_msg>do not run supervisor when dsn is empty<commit_after>package registry

import (
	"github.com/hashicorp/consul/api"

	"net/url"
	"strconv"
	"time"
)

var args []string

const REGISTRY_PREFIX = "registry"

func New(dsn string, osArgs []string) (Registry, error) {
	args = osArgs
	url, err := url.Parse(dsn)

	if err != nil {
		return nil, err
	}
	client, err := api.NewClient(&api.Config{
		Scheme:     url.Scheme,
		Address:    url.Host,
		Datacenter: url.Query().Get("dc"),
		Token:      url.Query().Get("token"),
	})
	if err != nil {
		return nil, err
	}
	registry := registry{
		client:          client,
		datacenter:      url.Query().Get("dc"),
		refreshInterval: 30 * time.Second,
		bindChan:        make(chan struct{}),
	}
	if interval := url.Query().Get("refresh_interval"); len(interval) != 0 {
		if v, err := strconv.ParseInt(interval, 10, 64); err == nil && v > 0 {
			registry.refreshInterval = time.Duration(v) * time.Second
		}
	}
	if len(dsn) != 0 {
		go registry.supervisor()
	}
	return &registry, nil
}

type registry struct {
	client          *api.Client
	configs         []config
	refreshInterval time.Duration
	datacenter      string
	bindChan        chan struct{}
}

func (r *registry) KV() KV {
	return &kv{client: r.client.KV()}
}

func (r *registry) Discovery() Discovery {
	return &discovery{
		agent:      r.client.Agent(),
		datacenter: r.datacenter,
	}
}

func (r *registry) supervisor() {
	var (
		refresh = time.Tick(r.refreshInterval)
	)
	for {
		select {
		case <-refresh:
			r.refresh()
		case <-r.bindChan:
			r.refresh()
		}
	}
}

func (r *registry) refresh() {
	var (
		kv   = r.KV()
		keys []string
	)
	for _, config := range r.configs {
		for _, item := range config.items {
			keys = append(keys, item.key)
		}
	}
	values := make(map[string]string, len(keys))
	for _, key := range keys {
		if v, err := kv.Get(key); err == nil {
			values[key] = v
		}
	}
	for _, config := range r.configs {
		config.mutex.Lock()
		for _, item := range config.items {
			if value, ok := values[item.key]; ok {
				item.set(value)
			}
		}
		config.mutex.Unlock()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"

	"github.com/bmizerany/mc"
	"github.com/elazarl/goproxy"
	"github.com/jackc/pgx"
)

var (
	cn    *mc.Conn
	pool  *pgx.ConnPool
	proxy *goproxy.ProxyHttpServer
)

func urlHasPrefix(prefix string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		isGET := req.Method == http.MethodGet
		hasPrefix := strings.HasPrefix(req.URL.Path, prefix)
		isSearch := strings.HasPrefix(req.URL.Path, "/packages/search/")
		return isGET && hasPrefix && !isSearch
	}
}

func pathIs(path string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		return req.Method == http.MethodGet && req.URL.Path == path
	}
}

func getEnv(key, def string) string {
	k := os.Getenv(key)
	if k == "" {
		return def
	}
	return k
}

func main() {
	memcachedURL := getEnv("MEMCACHEDCLOUD_SERVERS", "localhost:11211")
	var err error
	cn, err = mc.Dial("tcp", memcachedURL)
	if err != nil {
		log.Fatalf("Memcached connection error: %s", err)
	}

	memcachedUsername := os.Getenv("MEMCACHEDCLOUD_USERNAME")
	memcachedPassword := os.Getenv("MEMCACHEDCLOUD_PASSWORD")
	if memcachedUsername != "" && memcachedPassword != "" {
		if err := cn.Auth(memcachedUsername, memcachedPassword); err != nil {
			log.Fatalf("Memcached auth error: %s", err)
		}
	}

	pgxcfg, err := pgx.ParseURI(os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatalf("Parse URI error: %s", err)
	}
	pool, err = pgx.NewConnPool(pgx.ConnPoolConfig{
		ConnConfig:     pgxcfg,
		MaxConnections: 20,
		AfterConnect: func(conn *pgx.Conn) error {
			_, err := conn.Prepare("getPackage", `SELECT name, url FROM packages WHERE name = $1`)
			return err
		},
	})
	if err != nil {
		log.Fatalf("Connection error: %s", err)
	}
	defer pool.Close()

	binary, err := exec.LookPath("node")
	if err != nil {
		log.Fatalf("Could not lookup node path: %s", err)
	}

	cmd := exec.Command(binary, "--expose_gc", "index.js")
	env := os.Environ()
	env = append([]string{"PORT=3001"}, env...)
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		log.Fatalf("Could not start node: %s", err)
	}
	go func() {
		if err := cmd.Wait(); err != nil {
			log.Fatalf("Node process failed: %s", err)
		}
	}()

	proxy = goproxy.NewProxyHttpServer()
	proxy.Verbose = false
	proxy.NonproxyHandler = http.HandlerFunc(nonProxy)
	proxy.OnRequest(pathIs("/packages")).DoFunc(listPackages)
	proxy.OnRequest(urlHasPrefix("/packages/")).DoFunc(getPackage)

	port := getEnv("PORT", "3000")
	log.Println("Starting web server at port", port)
	log.Fatal(http.ListenAndServe(":"+port, proxy))
}

func nonProxy(w http.ResponseWriter, req *http.Request) {
	req.Host = "registry.bower.io"
	req.URL.Scheme = "http"
	req.URL.Host = "localhost:3001"
	proxy.ServeHTTP(w, req)
}

type Package struct {
	Name string `json:"name"`
	URL  string `json:"url"`
}

func getPackage(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	elements := strings.Split(r.URL.Path, "/")
	packageName := elements[len(elements)-1]

	var name, url string
	if err := pool.QueryRow("getPackage", packageName).Scan(&name, &url); err != nil {
		if err == pgx.ErrNoRows {
			return r, goproxy.NewResponse(r, "text/html", http.StatusNotFound, "Package not found")
		}
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}

	data, err := json.Marshal(Package{Name: name, URL: url})
	if err != nil {
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}
	response := goproxy.NewResponse(r, "application/json", http.StatusOK, string(data))
	response.Header.Add("Cache-Control", "max-age=3600")
	return r, response
}

func listPackages(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	val, _, _, err := cn.Get("packages")
	if err != nil {
		return r, nil
	}
	log.Println("MEMCACHED FROM GO SERVER")
	response := goproxy.NewResponse(r, "application/json", http.StatusOK, val)
	response.Header.Add("Cache-Control", "max-age=3600")
	return r, response
}
<commit_msg>Add public cache control<commit_after>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"

	"github.com/bmizerany/mc"
	"github.com/elazarl/goproxy"
	"github.com/jackc/pgx"
)

var (
	cn    *mc.Conn
	pool  *pgx.ConnPool
	proxy *goproxy.ProxyHttpServer
)

func urlHasPrefix(prefix string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		isGET := req.Method == http.MethodGet
		hasPrefix := strings.HasPrefix(req.URL.Path, prefix)
		isSearch := strings.HasPrefix(req.URL.Path, "/packages/search/")
		return isGET && hasPrefix && !isSearch
	}
}

func pathIs(path string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		return req.Method == http.MethodGet && req.URL.Path == path
	}
}

func getEnv(key, def string) string {
	k := os.Getenv(key)
	if k == "" {
		return def
	}
	return k
}

func main() {
	memcachedURL := getEnv("MEMCACHEDCLOUD_SERVERS", "localhost:11211")
	var err error
	cn, err = mc.Dial("tcp", memcachedURL)
	if err != nil {
		log.Fatalf("Memcached connection error: %s", err)
	}

	memcachedUsername := os.Getenv("MEMCACHEDCLOUD_USERNAME")
	memcachedPassword := os.Getenv("MEMCACHEDCLOUD_PASSWORD")
	if memcachedUsername != "" && memcachedPassword != "" {
		if err := cn.Auth(memcachedUsername, memcachedPassword); err != nil {
			log.Fatalf("Memcached auth error: %s", err)
		}
	}

	pgxcfg, err := pgx.ParseURI(os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatalf("Parse URI error: %s", err)
	}
	pool, err = pgx.NewConnPool(pgx.ConnPoolConfig{
		ConnConfig:     pgxcfg,
		MaxConnections: 20,
		AfterConnect: func(conn *pgx.Conn) error {
			_, err := conn.Prepare("getPackage", `SELECT name, url FROM packages WHERE name = $1`)
			return err
		},
	})
	if err != nil {
		log.Fatalf("Connection error: %s", err)
	}
	defer pool.Close()

	binary, err := exec.LookPath("node")
	if err != nil {
		log.Fatalf("Could not lookup node path: %s", err)
	}

	cmd := exec.Command(binary, "--expose_gc", "index.js")
	env := os.Environ()
	env = append([]string{"PORT=3001"}, env...)
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		log.Fatalf("Could not start node: %s", err)
	}
	go func() {
		if err := cmd.Wait(); err != nil {
			log.Fatalf("Node process failed: %s", err)
		}
	}()

	proxy = goproxy.NewProxyHttpServer()
	proxy.Verbose = false
	proxy.NonproxyHandler = http.HandlerFunc(nonProxy)
	proxy.OnRequest(pathIs("/packages")).DoFunc(listPackages)
	proxy.OnRequest(urlHasPrefix("/packages/")).DoFunc(getPackage)

	port := getEnv("PORT", "3000")
	log.Println("Starting web server at port", port)
	log.Fatal(http.ListenAndServe(":"+port, proxy))
}

func nonProxy(w http.ResponseWriter, req *http.Request) {
	req.Host = "registry.bower.io"
	req.URL.Scheme = "http"
	req.URL.Host = "localhost:3001"
	proxy.ServeHTTP(w, req)
}

type Package struct {
	Name string `json:"name"`
	URL  string `json:"url"`
}

func getPackage(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	elements := strings.Split(r.URL.Path, "/")
	packageName := elements[len(elements)-1]

	var name, url string
	if err := pool.QueryRow("getPackage", packageName).Scan(&name, &url); err != nil {
		if err == pgx.ErrNoRows {
			return r, goproxy.NewResponse(r, "text/html", http.StatusNotFound, "Package not found")
		}
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}

	data, err := json.Marshal(Package{Name: name, URL: url})
	if err != nil {
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}
	response := goproxy.NewResponse(r, "application/json", http.StatusOK, string(data))
	response.Header.Add("Cache-Control", "public, max-age=3600")
	return r, response
}

func listPackages(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	val, _, _, err := cn.Get("packages")
	if err != nil {
		return r, nil
	}
	log.Println("MEMCACHED FROM GO SERVER")
	response := goproxy.NewResponse(r, "application/json", http.StatusOK, val)
	response.Header.Add("Cache-Control", "public, max-age=3600")
	return r, response
}
<|endoftext|>