<commit_before>package pooly

import (
	"github.com/cactus/go-statsd-client/statsd"
	"log"
	"runtime"
	"sync"
	"time"
)

// Computer describes the interface responsible of computing the resulting score of a host.
// It takes the initial score and returns one computed using a predefined function (e.g exp, log ...)
type Computer interface {
	Compute(float64) float64
}

// Selecter describes the interface responsible of selecting a host among the ones registered in the service.
type Selecter interface {
	Select(map[string]*Host) *Host
}

// ServiceConfig defines the service configuration options.
type ServiceConfig struct {
	PoolConfig

	// Number of connections to prespawn on hosts additions (DefaultPrespawnConns by default).
	PrespawnConns uint

	// Number of attempts to get a connection from the service before giving up (DefaultGetAttempts by default).
	GetAttempts uint

	// Deadline after which pools are forced closed (see Pool.ForceClose) (DefaultCloseDeadline by default).
	CloseDeadline time.Duration

	// Defines the time interval taken into account when scores are computed (DefaultDecayDuration by default).
	// Scores are calculated using a weighted average over the course of this duration (recent feedbacks get higher weight).
	DecayDuration time.Duration

	// Time interval between two successive hosts scores computations.
	// Each score is calculated and cached for this duration (DefaultMemoizeScoreDuration by default).
	MemoizeScoreDuration time.Duration

	// Optional score calculator (none by default).
	ScoreCalculator Computer

	// Multi-armed bandit strategy used for host selection (RoundRobin by default).
	// The tradeoff faced by the service at each GetConn is between "exploitation" (choose hosts having the highest score)
	// and "exploration" (find about the expected score of other hosts).
	// The key here is to find the right balance between "exploration" and "exploitation" of hosts given their respective score.
	// Some strategies will favor fairness while others will prefer to pick hosts based on how well they perform.
	BanditStrategy Selecter

	// Address and port of a statsd server to collect and aggregate pooly service metrics (none by default).
	StatsdAddr string
}

// Service manages several hosts, every one of them having a connection pool (see Pool).
// It computes periodically hosts scores and learns about the best alternatives according to the BanditStrategy option.
// Hosts are added or removed from the service via Add and Remove respectively.
// The application calls the GetConn method to get a connection and releases it through the Conn.Release interface.
// When one is done with the pool, Close will cleanup all the service resources.
type Service struct {
	*ServiceConfig

	sync.RWMutex
	name    string
	hosts   map[string]*Host
	decay   *time.Ticker
	memoize *time.Ticker
	add, rm chan string
	stop    chan struct{}
	stats   statsd.Statter
}

// NewService creates a new service given a unique name.
// If no configuration is specified (nil), defaults values are used.
func NewService(name string, c *ServiceConfig) *Service {
	var err error

	if c == nil {
		c = new(ServiceConfig)
	}
	if c.PrespawnConns == 0 {
		c.PrespawnConns = DefaultPrespawnConns
	}
	if c.GetAttempts == 0 {
		c.GetAttempts = DefaultGetAttempts
	}
	if c.CloseDeadline == 0 {
		c.CloseDeadline = DefaultCloseDeadline
	}
	if c.DecayDuration == 0 {
		c.DecayDuration = DefaultDecayDuration
	}
	if c.MemoizeScoreDuration == 0 {
		c.MemoizeScoreDuration = DefaultMemoizeScoreDuration
	}
	if c.BanditStrategy == nil {
		c.BanditStrategy = NewRoundRobin()
	}

	s := &Service{
		ServiceConfig: c,
		name:          name,
		hosts:         make(map[string]*Host),
		add:           make(chan string),
		rm:            make(chan string),
		stop:          make(chan struct{}),
	}
	if _, ok := s.BanditStrategy.(*RoundRobin); !ok {
		s.decay = time.NewTicker(c.DecayDuration / seriesNum)
		s.memoize = time.NewTicker(c.MemoizeScoreDuration)
	}
	if c.StatsdAddr != "" {
		s.stats, err = statsd.New(c.StatsdAddr, "service."+name)
		log.Println("pooly:", err)
	}
	if s.stats == nil {
		s.stats, _ = statsd.NewNoop()
	} else {
		runtime.SetFinalizer(s.stats, func(s statsd.Statter) { s.Close() })
		s.stats.Gauge("conns.count", 0, sampleRate)
		s.stats.Gauge("hosts.count", 0, sampleRate)
		go s.monitor()
	}

	go s.serve()
	return s
}

func (s *Service) monitor() {
	t := time.NewTicker(1 * time.Second)
	for {
		select {
		case <-t.C:
			status := s.Status()
			n := int64(len(status))
			s.stats.Gauge("hosts.count", n, sampleRate)
			n = 0
			for _, c := range status {
				n += int64(c)
			}
			s.stats.Gauge("conns.count", n, sampleRate)

		case <-s.stop:
			t.Stop()
			return
		}
	}
}

func (s *Service) serve() {
	var decay, memoize <-chan time.Time

	if s.decay != nil {
		decay = s.decay.C
	}
	if s.memoize != nil {
		memoize = s.memoize.C
	}
	for {
		select {
		case a := <-s.add:
			s.newHost(a)
		case a := <-s.rm:
			s.deleteHost(a)
		case <-decay:
			for _, h := range s.hosts {
				h.decay()
			}
		case <-memoize:
			// XXX lock to prevent selecting hosts during scores computation
			//s.Lock()
			for _, h := range s.hosts {
				h.computeScore(s.ScoreCalculator)
			}
			//c.Unlock()
		case <-s.stop:
			for a := range s.hosts {
				s.deleteHost(a)
			}
			if s.decay != nil {
				s.decay.Stop()
			}
			if s.memoize != nil {
				s.memoize.Stop()
			}
			return
		}
	}
}

func (s *Service) newHost(a string) {
	s.Lock()
	if h := s.hosts[a]; h != nil {
		s.Unlock()
		return
	}

	p := NewPool(a, &s.PoolConfig)
	p.setStats(s.stats)

	p.New(s.PrespawnConns)
	s.hosts[a] = &Host{
		pool:       p,
		timeSeries: make([]serie, 1, seriesNum),
		score:      -1,
		stats:      s.stats,
	}
	s.Unlock()
}

func (s *Service) deleteHost(a string) {
	s.Lock()
	h := s.hosts[a]
	delete(s.hosts, a)
	s.Unlock()

	if h == nil {
		return
	}
	go func() {
		time.AfterFunc(s.CloseDeadline, func() {
			h.pool.ForceClose()
		})
		h.pool.Close()
	}()
}

// Name returns the name of the service.
func (s *Service) Name() string {
	return s.name
}

// Add adds a given host to the service.
// The effect of such operation may not be reflected immediately.
func (s *Service) Add(address string) {
	s.add <- address
}

// Remove removes a given host from the service.
// The effect of such operation may not be reflected immediately.
func (s *Service) Remove(address string) {
	s.rm <- address
}

// GetConn returns a connection from the service.
// The host serving the connection is chosen according to the BanditStrategy policy in place.
func (s *Service) GetConn() (*Conn, error) {
	var attempts uint

	start := time.Now()
again:
	s.RLock()
	if len(s.hosts) == 0 {
		s.RUnlock()
		if attempts < s.GetAttempts {
			attempts++
			goto again
		}
		return nil, ErrNoHostAvailable
	}
	h := s.BanditStrategy.Select(s.hosts)
	s.RUnlock()

	c, err := h.pool.Get()
	if err != nil {
		// Pool is closed or timed out, demote the host and start over
		s.stats.Inc("conns.get.fails", 1, sampleRate)
		h.rate(HostDown)
		if attempts < s.GetAttempts {
			attempts++
			goto again
		}
		return nil, err
	}

	// Send statsd metrics
	end := time.Now()
	dt := int64(end.Sub(start).Seconds() * 1000)
	s.stats.Timing("conns.get.delay", dt, sampleRate)
	s.stats.Inc("conns.get.count", 1, sampleRate)
	if _, ok := s.BanditStrategy.(*RoundRobin); !ok {
		p := int64(h.Score() * 100)
		s.stats.Timing("hosts.score", p, sampleRate)
	}

	c.setTime(end)
	c.setHost(h)
	return c, nil
}

// Status returns every host addresses managed by the service along with
// the number of connections handled by their respective pool thus far.
func (s *Service) Status() map[string]int32 {
	s.RLock()
	m := make(map[string]int32, len(s.hosts))
	for a, h := range s.hosts {
		m[a] = h.pool.ActiveConns()
	}
	s.RUnlock()
	return m
}

// Close closes the service, thus destroying all hosts and their respective pool.
// After a call to Close, the service can not be used again.
func (s *Service) Close() {
	close(s.stop)
}
<commit_msg>service: append the name of the service in the error returned by GetConn<commit_after>package pooly

import (
	"fmt"
	"github.com/cactus/go-statsd-client/statsd"
	"log"
	"runtime"
	"sync"
	"time"
)

// Computer describes the interface responsible of computing the resulting score of a host.
// It takes the initial score and returns one computed using a predefined function (e.g exp, log ...)
type Computer interface {
	Compute(float64) float64
}

// Selecter describes the interface responsible of selecting a host among the ones registered in the service.
type Selecter interface {
	Select(map[string]*Host) *Host
}

// ServiceConfig defines the service configuration options.
type ServiceConfig struct {
	PoolConfig

	// Number of connections to prespawn on hosts additions (DefaultPrespawnConns by default).
	PrespawnConns uint

	// Number of attempts to get a connection from the service before giving up (DefaultGetAttempts by default).
	GetAttempts uint

	// Deadline after which pools are forced closed (see Pool.ForceClose) (DefaultCloseDeadline by default).
	CloseDeadline time.Duration

	// Defines the time interval taken into account when scores are computed (DefaultDecayDuration by default).
	// Scores are calculated using a weighted average over the course of this duration (recent feedbacks get higher weight).
	DecayDuration time.Duration

	// Time interval between two successive hosts scores computations.
	// Each score is calculated and cached for this duration (DefaultMemoizeScoreDuration by default).
	MemoizeScoreDuration time.Duration

	// Optional score calculator (none by default).
	ScoreCalculator Computer

	// Multi-armed bandit strategy used for host selection (RoundRobin by default).
	// The tradeoff faced by the service at each GetConn is between "exploitation" (choose hosts having the highest score)
	// and "exploration" (find about the expected score of other hosts).
	// The key here is to find the right balance between "exploration" and "exploitation" of hosts given their respective score.
	// Some strategies will favor fairness while others will prefer to pick hosts based on how well they perform.
	BanditStrategy Selecter

	// Address and port of a statsd server to collect and aggregate pooly service metrics (none by default).
	StatsdAddr string
}

// Service manages several hosts, every one of them having a connection pool (see Pool).
// It computes periodically hosts scores and learns about the best alternatives according to the BanditStrategy option.
// Hosts are added or removed from the service via Add and Remove respectively.
// The application calls the GetConn method to get a connection and releases it through the Conn.Release interface.
// When one is done with the pool, Close will cleanup all the service resources.
type Service struct {
	*ServiceConfig

	sync.RWMutex
	name    string
	hosts   map[string]*Host
	decay   *time.Ticker
	memoize *time.Ticker
	add, rm chan string
	stop    chan struct{}
	stats   statsd.Statter
}

// NewService creates a new service given a unique name.
// If no configuration is specified (nil), defaults values are used.
func NewService(name string, c *ServiceConfig) *Service {
	var err error

	if c == nil {
		c = new(ServiceConfig)
	}
	if c.PrespawnConns == 0 {
		c.PrespawnConns = DefaultPrespawnConns
	}
	if c.GetAttempts == 0 {
		c.GetAttempts = DefaultGetAttempts
	}
	if c.CloseDeadline == 0 {
		c.CloseDeadline = DefaultCloseDeadline
	}
	if c.DecayDuration == 0 {
		c.DecayDuration = DefaultDecayDuration
	}
	if c.MemoizeScoreDuration == 0 {
		c.MemoizeScoreDuration = DefaultMemoizeScoreDuration
	}
	if c.BanditStrategy == nil {
		c.BanditStrategy = NewRoundRobin()
	}

	s := &Service{
		ServiceConfig: c,
		name:          name,
		hosts:         make(map[string]*Host),
		add:           make(chan string),
		rm:            make(chan string),
		stop:          make(chan struct{}),
	}
	if _, ok := s.BanditStrategy.(*RoundRobin); !ok {
		s.decay = time.NewTicker(c.DecayDuration / seriesNum)
		s.memoize = time.NewTicker(c.MemoizeScoreDuration)
	}
	if c.StatsdAddr != "" {
		s.stats, err = statsd.New(c.StatsdAddr, "service."+name)
		log.Println("pooly:", err)
	}
	if s.stats == nil {
		s.stats, _ = statsd.NewNoop()
	} else {
		runtime.SetFinalizer(s.stats, func(s statsd.Statter) { s.Close() })
		s.stats.Gauge("conns.count", 0, sampleRate)
		s.stats.Gauge("hosts.count", 0, sampleRate)
		go s.monitor()
	}

	go s.serve()
	return s
}

func (s *Service) monitor() {
	t := time.NewTicker(1 * time.Second)
	for {
		select {
		case <-t.C:
			status := s.Status()
			n := int64(len(status))
			s.stats.Gauge("hosts.count", n, sampleRate)
			n = 0
			for _, c := range status {
				n += int64(c)
			}
			s.stats.Gauge("conns.count", n, sampleRate)

		case <-s.stop:
			t.Stop()
			return
		}
	}
}

func (s *Service) serve() {
	var decay, memoize <-chan time.Time

	if s.decay != nil {
		decay = s.decay.C
	}
	if s.memoize != nil {
		memoize = s.memoize.C
	}
	for {
		select {
		case a := <-s.add:
			s.newHost(a)
		case a := <-s.rm:
			s.deleteHost(a)
		case <-decay:
			for _, h := range s.hosts {
				h.decay()
			}
		case <-memoize:
			// XXX lock to prevent selecting hosts during scores computation
			//s.Lock()
			for _, h := range s.hosts {
				h.computeScore(s.ScoreCalculator)
			}
			//c.Unlock()
		case <-s.stop:
			for a := range s.hosts {
				s.deleteHost(a)
			}
			if s.decay != nil {
				s.decay.Stop()
			}
			if s.memoize != nil {
				s.memoize.Stop()
			}
			return
		}
	}
}

func (s *Service) newHost(a string) {
	s.Lock()
	if h := s.hosts[a]; h != nil {
		s.Unlock()
		return
	}

	p := NewPool(a, &s.PoolConfig)
	p.setStats(s.stats)

	p.New(s.PrespawnConns)
	s.hosts[a] = &Host{
		pool:       p,
		timeSeries: make([]serie, 1, seriesNum),
		score:      -1,
		stats:      s.stats,
	}
	s.Unlock()
}

func (s *Service) deleteHost(a string) {
	s.Lock()
	h := s.hosts[a]
	delete(s.hosts, a)
	s.Unlock()

	if h == nil {
		return
	}
	go func() {
		time.AfterFunc(s.CloseDeadline, func() {
			h.pool.ForceClose()
		})
		h.pool.Close()
	}()
}

// Name returns the name of the service.
func (s *Service) Name() string {
	return s.name
}

// Add adds a given host to the service.
// The effect of such operation may not be reflected immediately.
func (s *Service) Add(address string) {
	s.add <- address
}

// Remove removes a given host from the service.
// The effect of such operation may not be reflected immediately.
func (s *Service) Remove(address string) {
	s.rm <- address
}

// GetConn returns a connection from the service.
// The host serving the connection is chosen according to the BanditStrategy policy in place.
func (s *Service) GetConn() (*Conn, error) {
	var attempts uint

	start := time.Now()
again:
	s.RLock()
	if len(s.hosts) == 0 {
		s.RUnlock()
		if attempts < s.GetAttempts {
			attempts++
			goto again
		}
		return nil, ErrNoHostAvailable
	}
	h := s.BanditStrategy.Select(s.hosts)
	s.RUnlock()

	c, err := h.pool.Get()
	if err != nil {
		// Pool is closed or timed out, demote the host and start over
		s.stats.Inc("conns.get.fails", 1, sampleRate)
		h.rate(HostDown)
		if attempts < s.GetAttempts {
			attempts++
			goto again
		}
		return nil, fmt.Errorf("%s: %v", s.name, err)
	}

	// Send statsd metrics
	end := time.Now()
	dt := int64(end.Sub(start).Seconds() * 1000)
	s.stats.Timing("conns.get.delay", dt, sampleRate)
	s.stats.Inc("conns.get.count", 1, sampleRate)
	if _, ok := s.BanditStrategy.(*RoundRobin); !ok {
		p := int64(h.Score() * 100)
		s.stats.Timing("hosts.score", p, sampleRate)
	}

	c.setTime(end)
	c.setHost(h)
	return c, nil
}

// Status returns every host addresses managed by the service along with
// the number of connections handled by their respective pool thus far.
func (s *Service) Status() map[string]int32 {
	s.RLock()
	m := make(map[string]int32, len(s.hosts))
	for a, h := range s.hosts {
		m[a] = h.pool.ActiveConns()
	}
	s.RUnlock()
	return m
}

// Close closes the service, thus destroying all hosts and their respective pool.
// After a call to Close, the service can not be used again.
func (s *Service) Close() {
	close(s.stop)
}
<|endoftext|>


<commit_before>package ethchain

import (
	"github.com/ethereum/eth-go/ethcrypto"
	"github.com/ethereum/eth-go/ethlog"
	"github.com/ethereum/eth-go/ethreact"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/obscuren/sha3"
	"hash"
	"math/big"
	"math/rand"
	"time"
)

var powlogger = ethlog.NewLogger("POW")

type PoW interface {
	Search(block *Block, reactChan chan ethreact.Event) []byte
	Verify(hash []byte, diff *big.Int, nonce []byte) bool
	GetHashrate() int64
}

type EasyPow struct {
	hash     *big.Int
	HashRate int64
}

func (pow *EasyPow) GetHashrate() int64 {
	return pow.HashRate
}

func (pow *EasyPow) Search(block *Block, reactChan chan ethreact.Event) []byte {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	hash := block.HashNoNonce()
	diff := block.Difficulty
	i := int64(0)
	start := time.Now().UnixNano()

	for {
		select {
		case <-reactChan:
			powlogger.Infoln("Breaking from mining")
			return nil
		default:
			i++
			if i%1234567 == 0 {
				elapsed := time.Now().UnixNano() - start
				hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
				pow.HashRate = int64(hashes)
				powlogger.Infoln("Hashing @", int64(pow.HashRate), "khash")
			}

			sha := ethcrypto.Sha3Bin(big.NewInt(r.Int63()).Bytes())
			if pow.Verify(hash, diff, sha) {
				return sha
			}
		}
	}

	return nil
}

func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool {
	sha := sha3.NewKeccak256()

	d := append(hash, nonce...)
	sha.Write(d)

	v := ethutil.BigPow(2, 256)
	ret := new(big.Int).Div(v, diff)

	res := new(big.Int)
	res.SetBytes(sha.Sum(nil))

	return res.Cmp(ret) == -1
}

func (pow *EasyPow) SetHash(hash *big.Int) {
}

type Dagger struct {
	hash *big.Int
	xn   *big.Int
}

var Found bool

func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	for i := 0; i < 1000; i++ {
		rnd := r.Int63()

		res := dag.Eval(big.NewInt(rnd))
		powlogger.Infof("rnd %v\nres %v\nobj %v\n", rnd, res, obj)
		if res.Cmp(obj) < 0 {
			// Post back result on the channel
			resChan <- rnd
			// Notify other threads we've found a valid nonce
			Found = true
		}

		// Break out if found
		if Found {
			break
		}
	}

	resChan <- 0
}

func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
	// TODO fix multi threading. Somehow it results in the wrong nonce
	amountOfRoutines := 1

	dag.hash = hash

	obj := ethutil.BigPow(2, 256)
	obj = obj.Div(obj, diff)

	Found = false
	resChan := make(chan int64, 3)
	var res int64

	for k := 0; k < amountOfRoutines; k++ {
		go dag.Find(obj, resChan)

		// Wait for each go routine to finish
	}
	for k := 0; k < amountOfRoutines; k++ {
		// Get the result from the channel. 0 = quit
		if r := <-resChan; r != 0 {
			res = r
		}
	}

	return big.NewInt(res)
}

func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool {
	dag.hash = hash

	obj := ethutil.BigPow(2, 256)
	obj = obj.Div(obj, diff)

	return dag.Eval(nonce).Cmp(obj) < 0
}

func DaggerVerify(hash, diff, nonce *big.Int) bool {
	dagger := &Dagger{}
	dagger.hash = hash

	obj := ethutil.BigPow(2, 256)
	obj = obj.Div(obj, diff)

	return dagger.Eval(nonce).Cmp(obj) < 0
}

func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
	if L == i {
		return dag.hash
	}

	var m *big.Int
	if L == 9 {
		m = big.NewInt(16)
	} else {
		m = big.NewInt(3)
	}

	sha := sha3.NewKeccak256()
	sha.Reset()
	d := sha3.NewKeccak256()
	b := new(big.Int)
	ret := new(big.Int)

	for k := 0; k < int(m.Uint64()); k++ {
		d.Reset()
		d.Write(dag.hash.Bytes())
		d.Write(dag.xn.Bytes())
		d.Write(big.NewInt(int64(L)).Bytes())
		d.Write(big.NewInt(int64(i)).Bytes())
		d.Write(big.NewInt(int64(k)).Bytes())

		b.SetBytes(Sum(d))
		pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
		sha.Write(dag.Node(L-1, pk).Bytes())
	}

	ret.SetBytes(Sum(sha))

	return ret
}

func Sum(sha hash.Hash) []byte {
	//in := make([]byte, 32)
	return sha.Sum(nil)
}

func (dag *Dagger) Eval(N *big.Int) *big.Int {
	pow := ethutil.BigPow(2, 26)
	dag.xn = pow.Div(N, pow)

	sha := sha3.NewKeccak256()
	sha.Reset()
	ret := new(big.Int)

	for k := 0; k < 4; k++ {
		d := sha3.NewKeccak256()
		b := new(big.Int)

		d.Reset()
		d.Write(dag.hash.Bytes())
		d.Write(dag.xn.Bytes())
		d.Write(N.Bytes())
		d.Write(big.NewInt(int64(k)).Bytes())

		b.SetBytes(Sum(d))
		pk := (b.Uint64() & 0x1ffffff)

		sha.Write(dag.Node(9, pk).Bytes())
	}

	return ret.SetBytes(Sum(sha))
}
<commit_msg>Turbo mode<commit_after>package ethchain

import (
	"hash"
	"math/big"
	"math/rand"
	"time"

	"github.com/ethereum/eth-go/ethcrypto"
	"github.com/ethereum/eth-go/ethlog"
	"github.com/ethereum/eth-go/ethreact"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/obscuren/sha3"
)

var powlogger = ethlog.NewLogger("POW")

type PoW interface {
	Search(block *Block, reactChan chan ethreact.Event) []byte
	Verify(hash []byte, diff *big.Int, nonce []byte) bool
	GetHashrate() int64
	Turbo(bool)
}

type EasyPow struct {
	hash     *big.Int
	HashRate int64
	turbo    bool
}

func (pow *EasyPow) GetHashrate() int64 {
	return pow.HashRate
}

func (pow *EasyPow) Turbo(on bool) {
	pow.turbo = on
}

func (pow *EasyPow) Search(block *Block, reactChan chan ethreact.Event) []byte {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	hash := block.HashNoNonce()
	diff := block.Difficulty
	i := int64(0)
	start := time.Now().UnixNano()

	for {
		select {
		case <-reactChan:
			powlogger.Infoln("Breaking from mining")
			return nil
		default:
			i++
			if i%1234567 == 0 {
				elapsed := time.Now().UnixNano() - start
				hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
				pow.HashRate = int64(hashes)
				powlogger.Infoln("Hashing @", int64(pow.HashRate), "khash")
			}

			sha := ethcrypto.Sha3Bin(big.NewInt(r.Int63()).Bytes())
			if pow.Verify(hash, diff, sha) {
				return sha
			}
		}

		if !pow.turbo {
			time.Sleep(500 * time.Millisecond)
		}
	}

	return nil
}

func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool {
	sha := sha3.NewKeccak256()

	d := append(hash, nonce...)
	sha.Write(d)

	v := ethutil.BigPow(2, 256)
	ret := new(big.Int).Div(v, diff)

	res := new(big.Int)
	res.SetBytes(sha.Sum(nil))

	return res.Cmp(ret) == -1
}

func (pow *EasyPow) SetHash(hash *big.Int) {
}

type Dagger struct {
	hash *big.Int
	xn   *big.Int
}

var Found bool

func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	for i := 0; i < 1000; i++ {
		rnd := r.Int63()

		res := dag.Eval(big.NewInt(rnd))
		powlogger.Infof("rnd %v\nres %v\nobj %v\n", rnd, res, obj)
		if res.Cmp(obj) < 0 {
			// Post back result on the channel
			resChan <- rnd
			// Notify other threads we've found a valid nonce
			Found = true
		}

		// Break out if found
		if Found {
			break
		}
	}

	resChan <- 0
}

func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
	// TODO fix multi threading. Somehow it results in the wrong nonce
	amountOfRoutines := 1

	dag.hash = hash

	obj := ethutil.BigPow(2, 256)
	obj = obj.Div(obj, diff)

	Found = false
	resChan := make(chan int64, 3)
	var res int64

	for k := 0; k < amountOfRoutines; k++ {
		go dag.Find(obj, resChan)

		// Wait for each go routine to finish
	}
	for k := 0; k < amountOfRoutines; k++ {
		// Get the result from the channel. 0 = quit
		if r := <-resChan; r != 0 {
			res = r
		}
	}

	return big.NewInt(res)
}

func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool {
	dag.hash = hash

	obj := ethutil.BigPow(2, 256)
	obj = obj.Div(obj, diff)

	return dag.Eval(nonce).Cmp(obj) < 0
}

func DaggerVerify(hash, diff, nonce *big.Int) bool {
	dagger := &Dagger{}
	dagger.hash = hash

	obj := ethutil.BigPow(2, 256)
	obj = obj.Div(obj, diff)

	return dagger.Eval(nonce).Cmp(obj) < 0
}

func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
	if L == i {
		return dag.hash
	}

	var m *big.Int
	if L == 9 {
		m = big.NewInt(16)
	} else {
		m = big.NewInt(3)
	}

	sha := sha3.NewKeccak256()
	sha.Reset()
	d := sha3.NewKeccak256()
	b := new(big.Int)
	ret := new(big.Int)

	for k := 0; k < int(m.Uint64()); k++ {
		d.Reset()
		d.Write(dag.hash.Bytes())
		d.Write(dag.xn.Bytes())
		d.Write(big.NewInt(int64(L)).Bytes())
		d.Write(big.NewInt(int64(i)).Bytes())
		d.Write(big.NewInt(int64(k)).Bytes())

		b.SetBytes(Sum(d))
		pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
		sha.Write(dag.Node(L-1, pk).Bytes())
	}

	ret.SetBytes(Sum(sha))

	return ret
}

func Sum(sha hash.Hash) []byte {
	//in := make([]byte, 32)
	return sha.Sum(nil)
}

func (dag *Dagger) Eval(N *big.Int) *big.Int {
	pow := ethutil.BigPow(2, 26)
	dag.xn = pow.Div(N, pow)

	sha := sha3.NewKeccak256()
	sha.Reset()
	ret := new(big.Int)

	for k := 0; k < 4; k++ {
		d := sha3.NewKeccak256()
		b := new(big.Int)

		d.Reset()
		d.Write(dag.hash.Bytes())
		d.Write(dag.xn.Bytes())
		d.Write(N.Bytes())
		d.Write(big.NewInt(int64(k)).Bytes())

		b.SetBytes(Sum(d))
		pk := (b.Uint64() & 0x1ffffff)

		sha.Write(dag.Node(9, pk).Bytes())
	}

	return ret.SetBytes(Sum(sha))
}
<|endoftext|>


<commit_before>package eval

import (
	"fmt"
	"reflect"
	"strconv"
	"unicode/utf8"

	"github.com/elves/elvish/eval/types"
)

// Conversion between Go value and Value.

func toFloat(arg types.Value) (float64, error) {
	if _, ok := arg.(types.String); !ok {
		return 0, fmt.Errorf("must be string")
	}
	s := string(arg.(types.String))
	num, err := strconv.ParseFloat(s, 64)
	if err != nil {
		num, err2 := strconv.ParseInt(s, 0, 64)
		if err2 != nil {
			return 0, err
		}
		return float64(num), nil
	}
	return num, nil
}

func floatToString(f float64) types.String {
	return types.String(strconv.FormatFloat(f, 'g', -1, 64))
}

func toInt(arg types.Value) (int, error) {
	arg, ok := arg.(types.String)
	if !ok {
		return 0, fmt.Errorf("must be string")
	}
	num, err := strconv.ParseInt(string(arg.(types.String)), 0, 0)
	if err != nil {
		return 0, err
	}
	return int(num), nil
}

func toRune(arg types.Value) (rune, error) {
	ss, ok := arg.(types.String)
	if !ok {
		return -1, fmt.Errorf("must be string")
	}
	s := string(ss)
	r, size := utf8.DecodeRuneInString(s)
	if r == utf8.RuneError {
		return -1, fmt.Errorf("string is not valid UTF-8")
	}
	if size != len(s) {
		return -1, fmt.Errorf("string has multiple runes")
	}
	return r, nil
}

// scanValueToGo converts Value to Go data, depending on the type of the
// destination.
func scanValueToGo(src types.Value, dstPtr interface{}) {
	switch dstPtr := dstPtr.(type) {
	case *string:
		s, ok := src.(types.String)
		if !ok {
			throwf("cannot convert %T to string", src)
		}
		*dstPtr = string(s)
	case *int:
		i, err := toInt(src)
		maybeThrow(err)
		*dstPtr = i
	case *float64:
		f, err := toFloat(src)
		maybeThrow(err)
		*dstPtr = f
	default:
		ptr := reflect.ValueOf(dstPtr)
		if ptr.Kind() != reflect.Ptr {
			throwf("internal bug: %T to ScanArgs, need pointer", dstPtr)
		}
		dstReflect := reflect.Indirect(ptr)
		if reflect.TypeOf(src).ConvertibleTo(dstReflect.Type()) {
			dstReflect.Set(reflect.ValueOf(src).Convert(dstReflect.Type()))
		} else {
			throwf("need %T argument, got %s", dstReflect.Interface(), src.Kind())
		}
	}
}
<commit_msg>eval: Handle failures to scan into interface correctly.<commit_after>package eval

import (
	"fmt"
	"reflect"
	"strconv"
	"unicode/utf8"

	"github.com/elves/elvish/eval/types"
)

// Conversion between Go value and Value.

func toFloat(arg types.Value) (float64, error) {
	if _, ok := arg.(types.String); !ok {
		return 0, fmt.Errorf("must be string")
	}
	s := string(arg.(types.String))
	num, err := strconv.ParseFloat(s, 64)
	if err != nil {
		num, err2 := strconv.ParseInt(s, 0, 64)
		if err2 != nil {
			return 0, err
		}
		return float64(num), nil
	}
	return num, nil
}

func floatToString(f float64) types.String {
	return types.String(strconv.FormatFloat(f, 'g', -1, 64))
}

func toInt(arg types.Value) (int, error) {
	arg, ok := arg.(types.String)
	if !ok {
		return 0, fmt.Errorf("must be string")
	}
	num, err := strconv.ParseInt(string(arg.(types.String)), 0, 0)
	if err != nil {
		return 0, err
	}
	return int(num), nil
}

func toRune(arg types.Value) (rune, error) {
	ss, ok := arg.(types.String)
	if !ok {
		return -1, fmt.Errorf("must be string")
	}
	s := string(ss)
	r, size := utf8.DecodeRuneInString(s)
	if r == utf8.RuneError {
		return -1, fmt.Errorf("string is not valid UTF-8")
	}
	if size != len(s) {
		return -1, fmt.Errorf("string has multiple runes")
	}
	return r, nil
}

// scanValueToGo converts Value to Go data, depending on the type of the
// destination.
func scanValueToGo(src types.Value, dstPtr interface{}) {
	switch dstPtr := dstPtr.(type) {
	case *string:
		s, ok := src.(types.String)
		if !ok {
			throwf("cannot convert %T to string", src)
		}
		*dstPtr = string(s)
	case *int:
		i, err := toInt(src)
		maybeThrow(err)
		*dstPtr = i
	case *float64:
		f, err := toFloat(src)
		maybeThrow(err)
		*dstPtr = f
	default:
		ptr := reflect.ValueOf(dstPtr)
		if ptr.Kind() != reflect.Ptr {
			throwf("internal bug: %T to ScanArgs, need pointer", dstPtr)
		}
		dstReflect := reflect.Indirect(ptr)
		if reflect.TypeOf(src).ConvertibleTo(dstReflect.Type()) {
			dstReflect.Set(reflect.ValueOf(src).Convert(dstReflect.Type()))
		} else {
			throwf("need %s argument, got %s", dstReflect.Type().Name(), src.Kind())
		}
	}
}
<|endoftext|>


<commit_before>package mirango

import (
	"net/http"

	"github.com/wlMalk/mirango/framework"
)

type Request struct {
	*http.Request
	Input    framework.ParamValues
	route    *Route
	sessions framework.Sessions
}

func NewRequest(r *http.Request) *Request {
	return &Request{
		Request: r,
		Input:   framework.ParamValues{},
	}
}

// Param returns the input parameter value by its name.
func (r *Request) Param(name string) framework.ParamValue {
	return r.Input[name]
}

// ParamOk returns the input parameter value by its name.
func (r *Request) ParamOk(name string) (framework.ParamValue, bool) {
	p, ok := r.Input[name]
	return p, ok
}

func (r *Request) Path() string {
	return r.RequestURI
}
func (r *Request) Method() string {
	return r.Request.Method
}

func (r *Request) Sessions() framework.Sessions {
	return r.sessions
}

func (r *Request) Session(name string) (framework.Session, error) {
	return r.sessions.Get(name)
}

func (r *Request) SetSessionValue(string, interface{}, interface{}) error {
	return nil
}

func (r *Request) GetSessionValue(string, interface{}) (framework.Value, error) {
	return nil, nil
}
<commit_msg>Updated Request functions<commit_after>package mirango

import (
	"net/http"

	"github.com/wlMalk/mirango/framework"
)

type Request struct {
	*http.Request
	Input    framework.ParamValues
	route    *Route
	sessions framework.Sessions
}

func NewRequest(r *http.Request) *Request {
	return &Request{
		Request: r,
		Input:   framework.ParamValues{},
	}
}

// Param returns the input parameter value by its name.
func (r *Request) Param(name string) framework.ParamValue {
	return r.Input[name]
}

// ParamOk returns the input parameter value by its name.
func (r *Request) ParamOk(name string) (framework.ParamValue, bool) {
	p, ok := r.Input[name]
	return p, ok
}

func (r *Request) Params(names ...string) framework.ParamValues {
	if len(names) == 0 {
		return r.Input
	}
	params := framework.ParamValues{}
	for _, n := range names {
		p, ok := r.Input[n]
		if !ok {
			continue
		}
		params[n] = p
	}
	return params
}

func (r *Request) Path() string {
	return r.RequestURI
}
func (r *Request) Method() string {
	return r.Request.Method
}

func (r *Request) Sessions() framework.Sessions {
	return r.sessions
}

func (r *Request) Session(name string) (framework.Session, error) {
	return r.sessions.Get(name)
}

func (r *Request) SetSessionValue(string, interface{}, interface{}) error {
	return nil
}

func (r *Request) GetSessionValue(string, interface{}) (framework.Value, error) {
	return nil, nil
}
<|endoftext|>


<commit_before>package goinsta

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type reqOptions struct {
	// Endpoint is the request path of instagram api
	Endpoint string

	// IsPost setted to true will send request with POST method.
	//
	// By default this option is false.
	IsPost bool

	// Query is the parameters of the request
	//
	// This parameters are independents of the request method (POST|GET)
	Query map[string]string
}

func (insta *Instagram) sendSimpleRequest(uri string, a ...interface{}) (body []byte, err error) {
	return insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(uri, a...),
		},
	)
}

func (inst *Instagram) sendRequest(o *reqOptions) (body []byte, err error) {
	method := "GET"
	if o.IsPost {
		method = "POST"
	}

	u, err := url.Parse(goInstaAPIUrl + o.Endpoint)
	if err != nil {
		return nil, err
	}

	bf := bytes.NewBuffer([]byte{})

	q := u.Query()
	for k, v := range o.Query {
		q.Add(k, v)
	}

	if o.IsPost {
		bf.WriteString(q.Encode())
	} else {
		u.RawQuery = q.Encode()
	}

	var req *http.Request
	req, err = http.NewRequest(method, u.String(), bf)
	if err != nil {
		return
	}

	req.Header.Set("Connection", "close")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Content-type", "application/x-www-form-urlencoded; charset=UTF-8")
	req.Header.Set("Cookie2", "$Version=1")
	req.Header.Set("Accept-Language", "en-US")
	req.Header.Set("User-Agent", goInstaUserAgent)

	resp, err := inst.c.Do(req)
	if err != nil {
		return body, err
	}
	defer resp.Body.Close()

	u, _ = url.Parse(goInstaAPIUrl)
	for _, value := range inst.c.Jar.Cookies(u) {
		if strings.Contains(value.Name, "csrftoken") {
			inst.token = value.Value
		}
	}

	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	switch resp.StatusCode {
	case 200:
	default:
		ierr := instaError{}
		err = json.Unmarshal(body, &ierr)
		if err != nil {
			return nil, fmt.Errorf("Invalid status code %s", string(body))
		}
		return nil, instaToErr(ierr)
	}

	return body, err
}

func (insta *Instagram) prepareData(other ...map[string]interface{}) (string, error) {
	data := map[string]interface{}{
		"_uuid":      insta.uuid,
		"_uid":       insta.Account.ID,
		"_csrftoken": insta.token,
	}
	for i := range other {
		for key, value := range other[i] {
			data[key] = value
		}
	}
	b, err := json.Marshal(data)
	if err == nil {
		return b2s(b), err
	}
	return "", err
}
<commit_msg>Changed status code error<commit_after>package goinsta

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type reqOptions struct {
	// Endpoint is the request path of instagram api
	Endpoint string

	// IsPost setted to true will send request with POST method.
	//
	// By default this option is false.
	IsPost bool

	// Query is the parameters of the request
	//
	// This parameters are independents of the request method (POST|GET)
	Query map[string]string
}

func (insta *Instagram) sendSimpleRequest(uri string, a ...interface{}) (body []byte, err error) {
	return insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(uri, a...),
		},
	)
}

func (inst *Instagram) sendRequest(o *reqOptions) (body []byte, err error) {
	method := "GET"
	if o.IsPost {
		method = "POST"
	}

	u, err := url.Parse(goInstaAPIUrl + o.Endpoint)
	if err != nil {
		return nil, err
	}

	bf := bytes.NewBuffer([]byte{})

	q := u.Query()
	for k, v := range o.Query {
		q.Add(k, v)
	}

	if o.IsPost {
		bf.WriteString(q.Encode())
	} else {
		u.RawQuery = q.Encode()
	}

	var req *http.Request
	req, err = http.NewRequest(method, u.String(), bf)
	if err != nil {
		return
	}

	req.Header.Set("Connection", "close")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Content-type", "application/x-www-form-urlencoded; charset=UTF-8")
	req.Header.Set("Cookie2", "$Version=1")
	req.Header.Set("Accept-Language", "en-US")
	req.Header.Set("User-Agent", goInstaUserAgent)

	resp, err := inst.c.Do(req)
	if err != nil {
		return body, err
	}
	defer resp.Body.Close()

	u, _ = url.Parse(goInstaAPIUrl)
	for _, value := range inst.c.Jar.Cookies(u) {
		if strings.Contains(value.Name, "csrftoken") {
			inst.token = value.Value
		}
	}

	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	switch resp.StatusCode {
	case 200:
	default:
		ierr := instaError{}
		err = json.Unmarshal(body, &ierr)
		if err != nil {
			return nil, fmt.Errorf("Invalid status code: %d", resp.StatusCode)
		}
		return nil, instaToErr(ierr)
	}

	return body, err
}

func (insta *Instagram) prepareData(other ...map[string]interface{}) (string, error) {
	data := map[string]interface{}{
		"_uuid":      insta.uuid,
		"_uid":       insta.Account.ID,
		"_csrftoken": insta.token,
	}
	for i := range other {
		for key, value := range other[i] {
			data[key] = value
		}
	}
	b, err := json.Marshal(data)
	if err == nil {
		return b2s(b), err
	}
	return "", err
}
<|endoftext|>


<commit_before>package typhon

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

	"github.com/monzo/terrors"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

// A Request is Typhon's wrapper around http.Request, used by both clients and servers.
//
// Note that Typhon makes no guarantees that a Request is safe to access or mutate concurrently. If a single Request
// object is to be used by multiple goroutines concurrently, callers must make sure to properly synchronise accesses.
type Request struct {
	http.Request
	context.Context
	err      error // Any error from request construction; read by ErrorFilter
	hijacker http.Hijacker
	server   *Server
}

// unwrappedContext returns the most "unwrapped" Context possible for that in the request.
// This is useful as it's very often the case that Typhon users will use a parent request
// as a parent for a child request. The context library knows how to unwrap its own
// types to most efficiently perform certain operations (eg. cancellation chaining), but
// it can't do that with Typhon-wrapped contexts.
func (r *Request) unwrappedContext() context.Context {
	switch c := r.Context.(type) {
	case Request:
		return c.unwrappedContext()
	case *Request:
		return c.unwrappedContext()
	default:
		return c
	}
}

// Encode maps to to EncodeAsJSON
// TODO: Remove in the next major release and require encoding to explicitly go through either EncodeAsJSON, EncodeAsProtoJSON or EncodeAsProtobuf
func (r *Request) Encode(v interface{}) {
	r.EncodeAsJSON(v)
}

// EncodeAsJSON serialises the passed object as JSON into the body (and sets appropriate headers).
func (r *Request) EncodeAsJSON(v interface{}) {
	// If we were given an io.ReadCloser or an io.Reader (that is not also a json.Marshaler), use it directly
	switch v := v.(type) {
	case json.Marshaler:
	case io.ReadCloser:
		r.Body = v
		r.ContentLength = -1
		return
	case io.Reader:
		r.Body = ioutil.NopCloser(v)
		r.ContentLength = -1
		return
	}

	if err := json.NewEncoder(r).Encode(v); err != nil {
		r.err = terrors.Wrap(err, nil)
		return
	}
	r.Header.Set("Content-Type", "application/json")
}

// TODO: Add EncodeAsProtoJSON to replace EncodeAsJSON in a later version (this will break compatibility so needs to be a major release)

// EncodeAsProtobuf serialises the passed object as protobuf into the body
func (r *Request) EncodeAsProtobuf(m proto.Message) {
	out, err := proto.Marshal(m)
	if err != nil {
		r.err = terrors.Wrap(err, nil)
		return
	}

	n, err := r.Write(out)
	if err != nil {
		r.err = terrors.Wrap(err, nil)
		return
	}
	r.Header.Set("Content-Type", "application/protobuf")
	r.ContentLength = int64(n)
}

// Decode de-serialises the body into the passed object.
func (r Request) Decode(v interface{}) error {
	b, err := r.BodyBytes(true)
	if err != nil {
		return terrors.WrapWithCode(err, nil, terrors.ErrBadRequest)
	}

	switch r.Header.Get("Content-Type") {
	// application/x-protobuf is the "canonical" use, application/protobuf is defined in an expired IETF draft.
	// See: https://datatracker.ietf.org/doc/html/draft-rfernando-protocol-buffers-00#section-3.2
	// See: https://github.com/google/protorpc/blob/eb03145/python/protorpc/protobuf.py#L49-L51
	case "application/octet-stream", "application/x-google-protobuf", "application/protobuf", "application/x-protobuf":
		m, ok := v.(proto.Message)
		if !ok {
			return terrors.InternalService("invalid_type", "could not decode proto message", nil)
		}
		err = proto.Unmarshal(b, m)
	// Proper JSON handling requires the protojson package in Go. application/jsonpb is a suggestion by grpc-gateway:
	// https://github.com/grpc-ecosystem/grpc-gateway/blob/f4371f7/runtime/marshaler_registry.go#L89-L90
	// This is a backward compatibility break for those using google.golang.org/protobuf/proto.Message incorrectly.

	// Older versions of typhon marshal/unmarshal using json, to prevent a regression, we only use protojson if the
	// content-type hints that this message is protojson
	case "application/jsonpb", "application/protojson":
		m, ok := v.(proto.Message)
		if !ok {
			return terrors.InternalService("invalid_type", "could not decode proto message", nil)
		}
		err = protojson.Unmarshal(b, m)

	default:
		m, ok := v.(proto.Message)
		if !ok {
			return terrors.InternalService("invalid_type", "could not decode proto message", nil)
		}
		err = json.Unmarshal(b, m)
	}
	return terrors.WrapWithCode(err, nil, terrors.ErrBadRequest)
}

// Write writes the passed bytes to the request's body.
func (r *Request) Write(b []byte) (n int, err error) {
	switch rc := r.Body.(type) {
	// In the "normal" case, the response body will be a buffer, to which we can write
	case io.Writer:
		n, err = rc.Write(b)
		if err != nil {
			return n, err
		}
	// If a caller manually sets Response.Body, then we may not be able to write to it. In that case, we need to be
	// cleverer.
	default:
		buf := &bufCloser{}
		if rc != nil {
			if _, err := io.Copy(buf, rc); err != nil {
				// This can be quite bad; we have consumed (and possibly lost) some of the original body
				return 0, err
			}
			// rc will never again be accessible: once it's copied it must be closed
			rc.Close()
		}
		r.Body = buf
		n, err = buf.Write(b)
		if err != nil {
			return n, err
		}
	}

	if r.ContentLength >= 0 {
		r.ContentLength += int64(n)
		// If this write pushed the content length above the chunking threshold,
		// set to -1 (unknown) to trigger chunked encoding
		if r.ContentLength >= chunkThreshold {
			r.ContentLength = -1
		}
	}
	return n, nil
}

// BodyBytes fully reads the request body and returns the bytes read.
//
// If consume is true, this is equivalent to ioutil.ReadAll; if false, the caller will observe the body to be in
// the same state that it was before (ie. any remaining unread body can be read again).
func (r *Request) BodyBytes(consume bool) ([]byte, error) {
	if consume {
		defer r.Body.Close()
		return ioutil.ReadAll(r.Body)
	}

	switch rc := r.Body.(type) {
	case *bufCloser:
		return rc.Bytes(), nil
	default:
		buf := &bufCloser{}
		r.Body = buf
		rdr := io.TeeReader(rc, buf)
		// rc will never again be accessible: once it's copied it must be closed
		defer rc.Close()
		return ioutil.ReadAll(rdr)
	}
}

// Send round-trips the request via the default Client. It does not block, instead returning a ResponseFuture
// representing the asynchronous operation to produce the response. It is equivalent to:
//
//  r.SendVia(Client)
func (r Request) Send() *ResponseFuture {
	return Send(r)
}

// SendVia round-trips the request via the passed Service. It does not block, instead returning a ResponseFuture
// representing the asynchronous operation to produce the response.
func (r Request) SendVia(svc Service) *ResponseFuture {
	return SendVia(r, svc)
}

// Response constructs a new Response to the request, and if non-nil, encodes the given body into it.
func (r Request) Response(body interface{}) Response {
	rsp := NewResponse(r)
	if body != nil {
		rsp.Encode(body)
	}
	return rsp
}

// ResponseWithCode constructs a new Response with the given status code to the request, and if non-nil, encodes the
// given body into it.
func (r Request) ResponseWithCode(body interface{}, statusCode int) Response {
	rsp := NewResponseWithCode(r, statusCode)
	if body != nil {
		rsp.Encode(body)
	}
	return rsp
}

func (r Request) String() string {
	if r.URL == nil {
		return "Request(Unknown)"
	}
	return fmt.Sprintf("Request(%s %s://%s%s)", r.Method, r.URL.Scheme, r.Host, r.URL.Path)
}

// NewRequest constructs a new Request with the given parameters, and if non-nil, encodes the given body into it.
func NewRequest(ctx context.Context, method, url string, body interface{}) Request {
	if ctx == nil {
		ctx = context.Background()
	}
	httpReq, err := http.NewRequest(method, url, nil)
	req := Request{
		Context: ctx,
		err:     err}
	if httpReq != nil {
		httpReq.ContentLength = 0
		httpReq.Body = &bufCloser{}
		req.Request = *httpReq

		// Attach any metadata in the context to the request as headers.
		meta := MetadataFromContext(ctx)
		for k, v := range meta {
			req.Header[strings.ToLower(k)] = v
		}
	}
	if body != nil && err == nil {
		req.EncodeAsJSON(body)
	}
	return req
}
<commit_msg>Add EncodeAsProtoJSON<commit_after>package typhon

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

	"github.com/monzo/terrors"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

// A Request is Typhon's wrapper around http.Request, used by both clients and servers.
//
// Note that Typhon makes no guarantees that a Request is safe to access or mutate concurrently. If a single Request
// object is to be used by multiple goroutines concurrently, callers must make sure to properly synchronise accesses.
type Request struct {
	http.Request
	context.Context
	err      error // Any error from request construction; read by ErrorFilter
	hijacker http.Hijacker
	server   *Server
}

// unwrappedContext returns the most "unwrapped" Context possible for that in the request.
// This is useful as it's very often the case that Typhon users will use a parent request
// as a parent for a child request. The context library knows how to unwrap its own
// types to most efficiently perform certain operations (eg. cancellation chaining), but
// it can't do that with Typhon-wrapped contexts.
func (r *Request) unwrappedContext() context.Context {
	switch c := r.Context.(type) {
	case Request:
		return c.unwrappedContext()
	case *Request:
		return c.unwrappedContext()
	default:
		return c
	}
}

// Encode maps to to EncodeAsJSON
// TODO: Remove in the next major release and require encoding to explicitly go through either EncodeAsJSON, EncodeAsProtoJSON or EncodeAsProtobuf
func (r *Request) Encode(v interface{}) {
	r.EncodeAsJSON(v)
}

// EncodeAsJSON serialises the passed object as JSON into the body (and sets appropriate headers).
func (r *Request) EncodeAsJSON(v interface{}) {
	// If we were given an io.ReadCloser or an io.Reader (that is not also a json.Marshaler), use it directly
	switch v := v.(type) {
	case json.Marshaler:
	case io.ReadCloser:
		r.Body = v
		r.ContentLength = -1
		return
	case io.Reader:
		r.Body = ioutil.NopCloser(v)
		r.ContentLength = -1
		return
	}

	if err := json.NewEncoder(r).Encode(v); err != nil {
		r.err = terrors.Wrap(err, nil)
		return
	}
	r.Header.Set("Content-Type", "application/json")
}

// EncodeAsProtoJSON serialises the passed object as ProtoJSON into the body (and sets appropriate headers).
func (r *Request) EncodeAsProtoJSON(m proto.Message) {
	out, err := protojson.Marshal(m)
	if err != nil {
		r.err = terrors.Wrap(err, nil)
		return
	}

	n, err := r.Write(out)
	if err != nil {
		r.err = terrors.Wrap(err, nil)
		return
	}
	r.Header.Set("Content-Type", "application/jsonpb")
	r.ContentLength = int64(n)
}

// EncodeAsProtobuf serialises the passed object as protobuf into the body (and sets appropriate headers).
func (r *Request) EncodeAsProtobuf(m proto.Message) {
	out, err := proto.Marshal(m)
	if err != nil {
		r.err = terrors.Wrap(err, nil)
		return
	}

	n, err := r.Write(out)
	if err != nil {
		r.err = terrors.Wrap(err, nil)
		return
	}
	r.Header.Set("Content-Type", "application/protobuf")
	r.ContentLength = int64(n)
}

// Decode de-serialises the body into the passed object.
func (r Request) Decode(v interface{}) error {
	b, err := r.BodyBytes(true)
	if err != nil {
		return terrors.WrapWithCode(err, nil, terrors.ErrBadRequest)
	}

	switch r.Header.Get("Content-Type") {
	// application/x-protobuf is the "canonical" use, application/protobuf is defined in an expired IETF draft.
	// See: https://datatracker.ietf.org/doc/html/draft-rfernando-protocol-buffers-00#section-3.2
	// See: https://github.com/google/protorpc/blob/eb03145/python/protorpc/protobuf.py#L49-L51
	case "application/octet-stream", "application/x-google-protobuf", "application/protobuf", "application/x-protobuf":
		m, ok := v.(proto.Message)
		if !ok {
			return terrors.InternalService("invalid_type", "could not decode proto message", nil)
		}
		err = proto.Unmarshal(b, m)
	// Proper JSON handling requires the protojson package in Go. application/jsonpb is a suggestion by grpc-gateway:
	// https://github.com/grpc-ecosystem/grpc-gateway/blob/f4371f7/runtime/marshaler_registry.go#L89-L90
	// This is a backward compatibility break for those using google.golang.org/protobuf/proto.Message incorrectly.

	// Older versions of typhon marshal/unmarshal using json, to prevent a regression, we only use protojson if the
	// content-type hints that this message is protojson
	case "application/jsonpb", "application/protojson":
		m, ok := v.(proto.Message)
		if !ok {
			return terrors.InternalService("invalid_type", "could not decode proto message", nil)
		}
		err = protojson.Unmarshal(b, m)

	default:
		m, ok := v.(proto.Message)
		if !ok {
			return terrors.InternalService("invalid_type", "could not decode proto message", nil)
		}
		err = json.Unmarshal(b, m)
	}
	return terrors.WrapWithCode(err, nil, terrors.ErrBadRequest)
}

// Write writes the passed bytes to the request's body.
func (r *Request) Write(b []byte) (n int, err error) {
	switch rc := r.Body.(type) {
	// In the "normal" case, the response body will be a buffer, to which we can write
	case io.Writer:
		n, err = rc.Write(b)
		if err != nil {
			return n, err
		}
	// If a caller manually sets Response.Body, then we may not be able to write to it. In that case, we need to be
	// cleverer.
	default:
		buf := &bufCloser{}
		if rc != nil {
			if _, err := io.Copy(buf, rc); err != nil {
				// This can be quite bad; we have consumed (and possibly lost) some of the original body
				return 0, err
			}
			// rc will never again be accessible: once it's copied it must be closed
			rc.Close()
		}
		r.Body = buf
		n, err = buf.Write(b)
		if err != nil {
			return n, err
		}
	}

	if r.ContentLength >= 0 {
		r.ContentLength += int64(n)
		// If this write pushed the content length above the chunking threshold,
		// set to -1 (unknown) to trigger chunked encoding
		if r.ContentLength >= chunkThreshold {
			r.ContentLength = -1
		}
	}
	return n, nil
}

// BodyBytes fully reads the request body and returns the bytes read.
//
// If consume is true, this is equivalent to ioutil.ReadAll; if false, the caller will observe the body to be in
// the same state that it was before (ie. any remaining unread body can be read again).
func (r *Request) BodyBytes(consume bool) ([]byte, error) {
	if consume {
		defer r.Body.Close()
		return ioutil.ReadAll(r.Body)
	}

	switch rc := r.Body.(type) {
	case *bufCloser:
		return rc.Bytes(), nil
	default:
		buf := &bufCloser{}
		r.Body = buf
		rdr := io.TeeReader(rc, buf)
		// rc will never again be accessible: once it's copied it must be closed
		defer rc.Close()
		return ioutil.ReadAll(rdr)
	}
}

// Send round-trips the request via the default Client. It does not block, instead returning a ResponseFuture
// representing the asynchronous operation to produce the response. It is equivalent to:
//
//  r.SendVia(Client)
func (r Request) Send() *ResponseFuture {
	return Send(r)
}

// SendVia round-trips the request via the passed Service. It does not block, instead returning a ResponseFuture
// representing the asynchronous operation to produce the response.
func (r Request) SendVia(svc Service) *ResponseFuture {
	return SendVia(r, svc)
}

// Response constructs a new Response to the request, and if non-nil, encodes the given body into it.
func (r Request) Response(body interface{}) Response {
	rsp := NewResponse(r)
	if body != nil {
		rsp.Encode(body)
	}
	return rsp
}

// ResponseWithCode constructs a new Response with the given status code to the request, and if non-nil, encodes the
// given body into it.
func (r Request) ResponseWithCode(body interface{}, statusCode int) Response {
	rsp := NewResponseWithCode(r, statusCode)
	if body != nil {
		rsp.Encode(body)
	}
	return rsp
}

func (r Request) String() string {
	if r.URL == nil {
		return "Request(Unknown)"
	}
	return fmt.Sprintf("Request(%s %s://%s%s)", r.Method, r.URL.Scheme, r.Host, r.URL.Path)
}

// NewRequest constructs a new Request with the given parameters, and if non-nil, encodes the given body into it.
func NewRequest(ctx context.Context, method, url string, body interface{}) Request {
	if ctx == nil {
		ctx = context.Background()
	}
	httpReq, err := http.NewRequest(method, url, nil)
	req := Request{
		Context: ctx,
		err:     err}
	if httpReq != nil {
		httpReq.ContentLength = 0
		httpReq.Body = &bufCloser{}
		req.Request = *httpReq

		// Attach any metadata in the context to the request as headers.
		meta := MetadataFromContext(ctx)
		for k, v := range meta {
			req.Header[strings.ToLower(k)] = v
		}
	}
	if body != nil && err == nil {
		req.EncodeAsJSON(body)
	}
	return req
}
<|endoftext|>


<commit_before>package jsonapi

import (
	"net/http"
	"strconv"
	"strings"
)

// An Intent represents a valid combination of a request method and a URL pattern.
type Intent int

const (
	_ Intent = iota

	// ListResources is a variation of the following request:
	// GET /posts
	ListResources

	// FindResource is a variation of the following request:
	// GET /posts/1
	FindResource

	// CreateResource is a variation of the following request:
	// POST /posts
	CreateResource

	// UpdateResource is a variation of the following request:
	// PATCH /posts/1
	UpdateResource

	// DeleteResource is a variation of the following request:
	// DELETE /posts/1
	DeleteResource

	// GetRelatedResources is a variation of the following requests:
	// GET /posts/1/author
	// GET /posts/1/comments
	GetRelatedResources

	// GetRelationship is a variation of the following requests:
	// GET /posts/1/relationships/author
	// GET /posts/1/relationships/comments
	GetRelationship

	// SetRelationship is a variation of the following requests:
	// PATCH /posts/1/relationships/author.
	// PATCH /posts/1/relationships/comments.
	SetRelationship

	// AppendToRelationship is a variation of the following request:
	// POST /posts/1/relationships/comments
	AppendToRelationship

	// RemoveFromRelationship is a variation of the following request:
	// DELETE /posts/1/relationships/comments
	RemoveFromRelationship

	// CollectionAction is a variation of the following requests:
	// GET /posts/top-titles
	// POST /posts/lock
	// PATCH /posts/settings
	// DELETE /posts/cache
	CollectionAction

	// ResourceAction is a variation of the following requests:
	// GET /posts/1/meta-data
	// POST /posts/1/publish
	// PATCH /posts/1/settings
	// DELETE /posts/1/history
	ResourceAction
)

// DocumentExpected returns whether a request using this intent is expected to
// include a JSON API document.
//
// Note: A response from an API may always include a document that at least
// contains one ore more errors.
func (i Intent) DocumentExpected() bool {
	switch i {
	case CreateResource, UpdateResource, SetRelationship,
		AppendToRelationship, RemoveFromRelationship:
		return true
	}

	return false
}

// RequestMethod returns the matching HTTP request method for an Intent.
func (i Intent) RequestMethod() string {
	switch i {
	case ListResources, FindResource, GetRelatedResources, GetRelationship:
		return "GET"
	case CreateResource, AppendToRelationship:
		return "POST"
	case UpdateResource, SetRelationship:
		return "PATCH"
	case DeleteResource, RemoveFromRelationship:
		return "DELETE"
	}

	return ""
}

// A Request contains all JSON API related information parsed from a low level
// request.
type Request struct {
	// The parsed JSON API intent of the request.
	Intent Intent

	// The prefix of the endpoint e.g. "api". It should not contain any prefix
	// or suffix slashes.
	Prefix string

	// The fragments parsed from the URL of the request. The fragments should not
	// contain any prefix or suffix slashes.
	ResourceType     string
	ResourceID       string
	RelatedResource  string
	Relationship     string
	CollectionAction string
	ResourceAction   string

	// The requested resources to be included in the response. This is read
	// from the "include" query parameter.
	Include []string

	// The pagination details of the request. Zero values mean no pagination
	// details have been provided. These values are read from the "page[number]",
	// "page[size]", "page[offset]" and "page[limit]" query parameters. These
	// parameters do not belong to the standard, but are recommended.
	PageNumber uint64
	PageSize   uint64
	PageOffset uint64
	PageLimit  uint64

	// The sorting that has been requested. This is read from the "sort" query
	// parameter.
	Sorting []string

	// The sparse fields that have been requested. This is read from the "fields"
	// query parameter.
	Fields map[string][]string

	// The filtering that has been requested. This is read from the "filter"
	// query parameter. This parameter does not belong to the standard, but is
	// recommended.
	Filters map[string][]string
}

// ParseRequest is a short-hand for Parser.ParseRequest and will be removed in
// future releases.
func ParseRequest(r *http.Request, prefix string) (*Request, error) {
	return (&Parser{Prefix: prefix}).ParseRequest(r)
}

// A Parser is used to parse incoming requests.
type Parser struct {
	// Prefix is the expected prefix of the endpoint.
	Prefix string

	// A list of valid collection actions and the allowed methods.
	//
	// Note: Make sure the actions do not conflict with resource ids.
	CollectionActions map[string][]string

	// A list of valid resource actions and the allowed methods.
	//
	// Note: Make sure the actions do not contain "relationships" or used
	// related resource types.
	ResourceActions map[string][]string
}

// ParseRequest will parse the passed request and return a new Request with the
// parsed data. It will return an error if the content type, request method or
// url is invalid. Any returned error can directly be written using WriteError.
func (p *Parser) ParseRequest(r *http.Request) (*Request, error) {
	// get method
	method := r.Method

	// map method to action
	if method != "GET" && method != "POST" && method != "PATCH" && method != "DELETE" {
		return nil, BadRequest("Unsupported method")
	}

	// allocate new request
	jr := &Request{
		Prefix: strings.Trim(p.Prefix, "/"),
	}

	// check content type header
	contentType := r.Header.Get("Content-Type")
	if contentType != "" && contentType != MediaType {
		return nil, BadRequest("Invalid content type header")
	}

	// check accept header
	accept := r.Header.Get("Accept")
	if accept != "" && accept != "*/*" && accept != "application/*" && accept != "application/json" && accept != MediaType {
		return nil, ErrorFromStatus(http.StatusNotAcceptable, "Invalid accept header")
	}

	// de-prefix and trim path
	location := strings.TrimPrefix(strings.Trim(r.URL.Path, "/"), jr.Prefix+"/")

	// split path
	segments := strings.Split(location, "/")
	if len(segments) == 0 || len(segments) > 4 {
		return nil, BadRequest("Invalid URL segment count")
	}

	// check for invalid segments
	for _, s := range segments {
		if s == "" {
			return nil, BadRequest("Found empty URL segments")
		}
	}

	// set resource
	jr.ResourceType = segments[0]
	level := 1

	// return early if a collection action is provided
	if len(segments) == 2 {
		if action, ok := p.CollectionActions[segments[1]]; ok {
			for _, m := range action {
				if method == m {
					jr.Intent = CollectionAction
					jr.CollectionAction = segments[1]
					return jr, nil
				}
			}
		}
	}

	// set resource id
	if len(segments) > 1 {
		jr.ResourceID = segments[1]
		level = 2
	}

	// return early if a resource action is provided
	if len(segments) == 3 {
		if action, ok := p.ResourceActions[segments[2]]; ok {
			for _, m := range action {
				if method == m {
					jr.Intent = ResourceAction
					jr.ResourceAction = segments[2]
					return jr, nil
				}
			}
		}
	}

	// set related resource
	if len(segments) == 3 && segments[2] != "relationships" {
		jr.RelatedResource = segments[2]
		level = 3
	}

	// set relationship
	if len(segments) == 4 && segments[2] == "relationships" {
		jr.Relationship = segments[3]
		level = 4
	}

	// final check
	if len(segments) > 2 && (jr.RelatedResource == "" && jr.Relationship == "") {
		return nil, BadRequest("Invalid URL relationship format")
	}

	// calculate intent
	switch method {
	case "GET":
		switch level {
		case 1:
			jr.Intent = ListResources
		case 2:
			jr.Intent = FindResource
		case 3:
			jr.Intent = GetRelatedResources
		case 4:
			jr.Intent = GetRelationship
		}
	case "POST":
		switch level {
		case 1:
			jr.Intent = CreateResource
		case 4:
			jr.Intent = AppendToRelationship
		}
	case "PATCH":
		switch level {
		case 2:
			jr.Intent = UpdateResource
		case 4:
			jr.Intent = SetRelationship
		}
	case "DELETE":
		switch level {
		case 2:
			jr.Intent = DeleteResource
		case 4:
			jr.Intent = RemoveFromRelationship
		}
	}

	// check intent
	if jr.Intent == 0 {
		return nil, BadRequest("The URL and method combination is invalid")
	}

	// check if request should come with a document and has content type set
	if jr.Intent.DocumentExpected() && contentType == "" {
		return nil, BadRequest("Missing content type header")
	}

	for key, values := range r.URL.Query() {
		// set included resources
		if key == "include" {
			for _, v := range values {
				jr.Include = append(jr.Include, strings.Split(v, ",")...)
			}

			continue
		}

		// set sorting
		if key == "sort" {
			for _, v := range values {
				jr.Sorting = append(jr.Sorting, strings.Split(v, ",")...)
			}

			continue
		}

		// set page number
		if key == "page[number]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[number]")
			}

			n, err := strconv.ParseUint(values[0], 10, 0)
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[number]")
			}

			jr.PageNumber = n
			continue
		}

		// set page size
		if key == "page[size]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[size]")
			}

			n, err := strconv.ParseUint(values[0], 10, 0)
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[size]")
			}

			jr.PageSize = n
			continue
		}

		// set page offset
		if key == "page[offset]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[offset]")
			}

			n, err := strconv.ParseUint(values[0], 10, 0)
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[offset]")
			}

			jr.PageOffset = n
			continue
		}

		// set page limit
		if key == "page[limit]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[limit]")
			}

			n, err := strconv.ParseUint(values[0], 10, 0)
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[limit]")
			}

			jr.PageLimit = n
			continue
		}

		// set sparse fields
		if strings.HasPrefix(key, "fields[") && strings.HasSuffix(key, "]") {
			if jr.Fields == nil {
				jr.Fields = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				jr.Fields[typ] = append(jr.Fields[typ], strings.Split(v, ",")...)
			}
		}

		// set filters
		if strings.HasPrefix(key, "filter[") && strings.HasSuffix(key, "]") {
			if jr.Filters == nil {
				jr.Filters = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				jr.Filters[typ] = append(jr.Filters[typ], strings.Split(v, ",")...)
			}
		}
	}

	// check that page number is set if page size is set
	if jr.PageNumber > 0 && jr.PageSize <= 0 {
		return nil, BadRequestParam("Missing page size", "page[number]")
	}

	// check that page size is set if page number is set
	if jr.PageSize > 0 && jr.PageNumber <= 0 {
		return nil, BadRequestParam("Missing page number", "page[size]")
	}

	// check that page limit is set if page offset is set
	if jr.PageOffset > 0 && jr.PageLimit <= 0 {
		return nil, BadRequestParam("Missing page limit", "page[limit]")
	}

	return jr, nil
}

// Base will generate the base URL for this request, which includes the type and
// id if present.
func (r *Request) Base() string {
	// prepare segments
	var segments []string

	// add prefix if set
	if r.Prefix != "" {
		segments = append(segments, r.Prefix)
	}

	// add resource type
	segments = append(segments, r.ResourceType)

	// add id if available
	if r.ResourceID != "" {
		segments = append(segments, r.ResourceID)
	}

	return "/" + strings.Join(segments, "/")
}

// Self will generate the "self" URL for this request, which includes all path
// elements if available.
func (r *Request) Self() string {
	// prepare segments
	var segments []string

	// add prefix if set
	if r.Prefix != "" {
		segments = append(segments, r.Prefix)
	}

	// add resource type
	segments = append(segments, r.ResourceType)

	// add id if available
	if r.ResourceID != "" {
		segments = append(segments, r.ResourceID)

		// add related resource or relationship
		if r.RelatedResource != "" {
			segments = append(segments, r.RelatedResource)
		} else if r.Relationship != "" {
			segments = append(segments, "relationships", r.Relationship)
		} else if r.ResourceAction != "" {
			segments = append(segments, r.ResourceAction)
		}
	}

	// add collection action if available
	if r.CollectionAction != "" {
		segments = append(segments, r.CollectionAction)
	}

	return "/" + strings.Join(segments, "/")
}
<commit_msg>fix typo<commit_after>package jsonapi

import (
	"net/http"
	"strconv"
	"strings"
)

// An Intent represents a valid combination of a request method and a URL pattern.
type Intent int

const (
	_ Intent = iota

	// ListResources is a variation of the following request:
	// GET /posts
	ListResources

	// FindResource is a variation of the following request:
	// GET /posts/1
	FindResource

	// CreateResource is a variation of the following request:
	// POST /posts
	CreateResource

	// UpdateResource is a variation of the following request:
	// PATCH /posts/1
	UpdateResource

	// DeleteResource is a variation of the following request:
	// DELETE /posts/1
	DeleteResource

	// GetRelatedResources is a variation of the following requests:
	// GET /posts/1/author
	// GET /posts/1/comments
	GetRelatedResources

	// GetRelationship is a variation of the following requests:
	// GET /posts/1/relationships/author
	// GET /posts/1/relationships/comments
	GetRelationship

	// SetRelationship is a variation of the following requests:
	// PATCH /posts/1/relationships/author.
	// PATCH /posts/1/relationships/comments.
	SetRelationship

	// AppendToRelationship is a variation of the following request:
	// POST /posts/1/relationships/comments
	AppendToRelationship

	// RemoveFromRelationship is a variation of the following request:
	// DELETE /posts/1/relationships/comments
	RemoveFromRelationship

	// CollectionAction is a variation of the following requests:
	// GET /posts/top-titles
	// POST /posts/lock
	// PATCH /posts/settings
	// DELETE /posts/cache
	CollectionAction

	// ResourceAction is a variation of the following requests:
	// GET /posts/1/meta-data
	// POST /posts/1/publish
	// PATCH /posts/1/settings
	// DELETE /posts/1/history
	ResourceAction
)

// DocumentExpected returns whether a request using this intent is expected to
// include a JSON API document.
//
// Note: A response from an API may always include a document that at least
// contains one ore more errors.
func (i Intent) DocumentExpected() bool {
	switch i {
	case CreateResource, UpdateResource, SetRelationship,
		AppendToRelationship, RemoveFromRelationship:
		return true
	}

	return false
}

// RequestMethod returns the matching HTTP request method for an Intent.
func (i Intent) RequestMethod() string {
	switch i {
	case ListResources, FindResource, GetRelatedResources, GetRelationship:
		return "GET"
	case CreateResource, AppendToRelationship:
		return "POST"
	case UpdateResource, SetRelationship:
		return "PATCH"
	case DeleteResource, RemoveFromRelationship:
		return "DELETE"
	}

	return ""
}

// A Request contains all JSON API related information parsed from a low level
// request.
type Request struct {
	// The parsed JSON API intent of the request.
	Intent Intent

	// The prefix of the endpoint e.g. "api". It should not contain any prefix
	// or suffix slashes.
	Prefix string

	// The fragments parsed from the URL of the request. The fragments should not
	// contain any prefix or suffix slashes.
	ResourceType     string
	ResourceID       string
	RelatedResource  string
	Relationship     string
	CollectionAction string
	ResourceAction   string

	// The requested resources to be included in the response. This is read
	// from the "include" query parameter.
	Include []string

	// The pagination details of the request. Zero values mean no pagination
	// details have been provided. These values are read from the "page[number]",
	// "page[size]", "page[offset]" and "page[limit]" query parameters. These
	// parameters do not belong to the standard, but are recommended.
	PageNumber uint64
	PageSize   uint64
	PageOffset uint64
	PageLimit  uint64

	// The sorting that has been requested. This is read from the "sort" query
	// parameter.
	Sorting []string

	// The sparse fields that have been requested. This is read from the "fields"
	// query parameter.
	Fields map[string][]string

	// The filtering that has been requested. This is read from the "filter"
	// query parameter. This parameter does not belong to the standard, but is
	// recommended.
	Filters map[string][]string
}

// ParseRequest is a short-hand for Parser.ParseRequest and will be removed in
// future releases.
func ParseRequest(r *http.Request, prefix string) (*Request, error) {
	return (&Parser{Prefix: prefix}).ParseRequest(r)
}

// A Parser is used to parse incoming requests.
type Parser struct {
	// Prefix is the expected prefix of the endpoint.
	Prefix string

	// A list of valid collection actions and the allowed methods.
	//
	// Note: Make sure the actions do not conflict with resource ids.
	CollectionActions map[string][]string

	// A list of valid resource actions and the allowed methods.
	//
	// Note: Make sure the actions do not contain "relationships" or use
	// related resource types.
	ResourceActions map[string][]string
}

// ParseRequest will parse the passed request and return a new Request with the
// parsed data. It will return an error if the content type, request method or
// url is invalid. Any returned error can directly be written using WriteError.
func (p *Parser) ParseRequest(r *http.Request) (*Request, error) {
	// get method
	method := r.Method

	// map method to action
	if method != "GET" && method != "POST" && method != "PATCH" && method != "DELETE" {
		return nil, BadRequest("Unsupported method")
	}

	// allocate new request
	jr := &Request{
		Prefix: strings.Trim(p.Prefix, "/"),
	}

	// check content type header
	contentType := r.Header.Get("Content-Type")
	if contentType != "" && contentType != MediaType {
		return nil, BadRequest("Invalid content type header")
	}

	// check accept header
	accept := r.Header.Get("Accept")
	if accept != "" && accept != "*/*" && accept != "application/*" && accept != "application/json" && accept != MediaType {
		return nil, ErrorFromStatus(http.StatusNotAcceptable, "Invalid accept header")
	}

	// de-prefix and trim path
	location := strings.TrimPrefix(strings.Trim(r.URL.Path, "/"), jr.Prefix+"/")

	// split path
	segments := strings.Split(location, "/")
	if len(segments) == 0 || len(segments) > 4 {
		return nil, BadRequest("Invalid URL segment count")
	}

	// check for invalid segments
	for _, s := range segments {
		if s == "" {
			return nil, BadRequest("Found empty URL segments")
		}
	}

	// set resource
	jr.ResourceType = segments[0]
	level := 1

	// return early if a collection action is provided
	if len(segments) == 2 {
		if action, ok := p.CollectionActions[segments[1]]; ok {
			for _, m := range action {
				if method == m {
					jr.Intent = CollectionAction
					jr.CollectionAction = segments[1]
					return jr, nil
				}
			}
		}
	}

	// set resource id
	if len(segments) > 1 {
		jr.ResourceID = segments[1]
		level = 2
	}

	// return early if a resource action is provided
	if len(segments) == 3 {
		if action, ok := p.ResourceActions[segments[2]]; ok {
			for _, m := range action {
				if method == m {
					jr.Intent = ResourceAction
					jr.ResourceAction = segments[2]
					return jr, nil
				}
			}
		}
	}

	// set related resource
	if len(segments) == 3 && segments[2] != "relationships" {
		jr.RelatedResource = segments[2]
		level = 3
	}

	// set relationship
	if len(segments) == 4 && segments[2] == "relationships" {
		jr.Relationship = segments[3]
		level = 4
	}

	// final check
	if len(segments) > 2 && (jr.RelatedResource == "" && jr.Relationship == "") {
		return nil, BadRequest("Invalid URL relationship format")
	}

	// calculate intent
	switch method {
	case "GET":
		switch level {
		case 1:
			jr.Intent = ListResources
		case 2:
			jr.Intent = FindResource
		case 3:
			jr.Intent = GetRelatedResources
		case 4:
			jr.Intent = GetRelationship
		}
	case "POST":
		switch level {
		case 1:
			jr.Intent = CreateResource
		case 4:
			jr.Intent = AppendToRelationship
		}
	case "PATCH":
		switch level {
		case 2:
			jr.Intent = UpdateResource
		case 4:
			jr.Intent = SetRelationship
		}
	case "DELETE":
		switch level {
		case 2:
			jr.Intent = DeleteResource
		case 4:
			jr.Intent = RemoveFromRelationship
		}
	}

	// check intent
	if jr.Intent == 0 {
		return nil, BadRequest("The URL and method combination is invalid")
	}

	// check if request should come with a document and has content type set
	if jr.Intent.DocumentExpected() && contentType == "" {
		return nil, BadRequest("Missing content type header")
	}

	for key, values := range r.URL.Query() {
		// set included resources
		if key == "include" {
			for _, v := range values {
				jr.Include = append(jr.Include, strings.Split(v, ",")...)
			}

			continue
		}

		// set sorting
		if key == "sort" {
			for _, v := range values {
				jr.Sorting = append(jr.Sorting, strings.Split(v, ",")...)
			}

			continue
		}

		// set page number
		if key == "page[number]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[number]")
			}

			n, err := strconv.ParseUint(values[0], 10, 0)
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[number]")
			}

			jr.PageNumber = n
			continue
		}

		// set page size
		if key == "page[size]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[size]")
			}

			n, err := strconv.ParseUint(values[0], 10, 0)
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[size]")
			}

			jr.PageSize = n
			continue
		}

		// set page offset
		if key == "page[offset]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[offset]")
			}

			n, err := strconv.ParseUint(values[0], 10, 0)
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[offset]")
			}

			jr.PageOffset = n
			continue
		}

		// set page limit
		if key == "page[limit]" {
			if len(values) != 1 {
				return nil, BadRequestParam("More than one value", "page[limit]")
			}

			n, err := strconv.ParseUint(values[0], 10, 0)
			if err != nil {
				return nil, BadRequestParam("Not a number", "page[limit]")
			}

			jr.PageLimit = n
			continue
		}

		// set sparse fields
		if strings.HasPrefix(key, "fields[") && strings.HasSuffix(key, "]") {
			if jr.Fields == nil {
				jr.Fields = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				jr.Fields[typ] = append(jr.Fields[typ], strings.Split(v, ",")...)
			}
		}

		// set filters
		if strings.HasPrefix(key, "filter[") && strings.HasSuffix(key, "]") {
			if jr.Filters == nil {
				jr.Filters = make(map[string][]string)
			}

			typ := key[7 : len(key)-1]

			for _, v := range values {
				jr.Filters[typ] = append(jr.Filters[typ], strings.Split(v, ",")...)
			}
		}
	}

	// check that page number is set if page size is set
	if jr.PageNumber > 0 && jr.PageSize <= 0 {
		return nil, BadRequestParam("Missing page size", "page[number]")
	}

	// check that page size is set if page number is set
	if jr.PageSize > 0 && jr.PageNumber <= 0 {
		return nil, BadRequestParam("Missing page number", "page[size]")
	}

	// check that page limit is set if page offset is set
	if jr.PageOffset > 0 && jr.PageLimit <= 0 {
		return nil, BadRequestParam("Missing page limit", "page[limit]")
	}

	return jr, nil
}

// Base will generate the base URL for this request, which includes the type and
// id if present.
func (r *Request) Base() string {
	// prepare segments
	var segments []string

	// add prefix if set
	if r.Prefix != "" {
		segments = append(segments, r.Prefix)
	}

	// add resource type
	segments = append(segments, r.ResourceType)

	// add id if available
	if r.ResourceID != "" {
		segments = append(segments, r.ResourceID)
	}

	return "/" + strings.Join(segments, "/")
}

// Self will generate the "self" URL for this request, which includes all path
// elements if available.
func (r *Request) Self() string {
	// prepare segments
	var segments []string

	// add prefix if set
	if r.Prefix != "" {
		segments = append(segments, r.Prefix)
	}

	// add resource type
	segments = append(segments, r.ResourceType)

	// add id if available
	if r.ResourceID != "" {
		segments = append(segments, r.ResourceID)

		// add related resource or relationship
		if r.RelatedResource != "" {
			segments = append(segments, r.RelatedResource)
		} else if r.Relationship != "" {
			segments = append(segments, "relationships", r.Relationship)
		} else if r.ResourceAction != "" {
			segments = append(segments, r.ResourceAction)
		}
	}

	// add collection action if available
	if r.CollectionAction != "" {
		segments = append(segments, r.CollectionAction)
	}

	return "/" + strings.Join(segments, "/")
}
<|endoftext|>


<commit_before>package milter

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"io"
	"log"
	"net"
	"net/textproto"
	"strings"
)

// OptAction sets which actions the milter wants to perform.
// Multiple options can be set using a bitmask.
type OptAction uint32

// OptProtocol masks out unwanted parts of the SMTP transaction.
// Multiple options can be set using a bitmask.
type OptProtocol uint32

const (
	// set which actions the milter wants to perform
	OptAddHeader    OptAction = 0x01
	OptChangeBody   OptAction = 0x02
	OptAddRcpt      OptAction = 0x04
	OptRemoveRcpt   OptAction = 0x08
	OptChangeHeader OptAction = 0x10
	OptQuarantine   OptAction = 0x20
    OptChangeFrom   OptAction = 0x40

	// mask out unwanted parts of the SMTP transaction
	OptNoConnect  OptProtocol = 0x01
	OptNoHelo     OptProtocol = 0x02
	OptNoMailFrom OptProtocol = 0x04
	OptNoRcptTo   OptProtocol = 0x08
	OptNoBody     OptProtocol = 0x10
	OptNoHeaders  OptProtocol = 0x20
	OptNoEOH      OptProtocol = 0x40
)

// milterSession keeps session state during MTA communication
type milterSession struct {
	actions  OptAction
	protocol OptProtocol
	sock     io.ReadWriteCloser
	headers  textproto.MIMEHeader
	macros   map[string]string
	milter   Milter
}

// ReadPacket reads incoming milter packet
func (c *milterSession) ReadPacket() (*Message, error) {
	// read packet length
	var length uint32
	if err := binary.Read(c.sock, binary.BigEndian, &length); err != nil {
		return nil, err
	}

	// read packet data
	data := make([]byte, length)
	if _, err := io.ReadFull(c.sock, data); err != nil {
		return nil, err
	}

	// prepare response data
	message := Message{
		Code: data[0],
		Data: data[1:],
	}

	return &message, nil
}

// WritePacket sends a milter response packet to socket stream
func (m *milterSession) WritePacket(msg *Message) error {
	buffer := bufio.NewWriter(m.sock)

	// calculate and write response length
	length := uint32(len(msg.Data) + 1)
	if err := binary.Write(buffer, binary.BigEndian, length); err != nil {
		return err
	}

	// write response code
	if err := buffer.WriteByte(msg.Code); err != nil {
		return err
	}

	// write response data
	if _, err := buffer.Write(msg.Data); err != nil {
		return err
	}

	// flush data to network socket stream
	if err := buffer.Flush(); err != nil {
		return err
	}

	return nil
}

// Process processes incoming milter commands
func (m *milterSession) Process(msg *Message) (Response, error) {
	switch msg.Code {
	case 'A':
		// abort current message and start over
		m.headers = nil
		m.macros = nil
		// do not send response
		return nil, nil

	case 'B':
		// body chunk
		return m.milter.BodyChunk(msg.Data, newModifier(m))

	case 'C':
		// new connection, get hostname
		Hostname := readCString(msg.Data)
		msg.Data = msg.Data[len(Hostname)+1:]
		// get protocol family
		protocolFamily := msg.Data[0]
		msg.Data = msg.Data[1:]
		// get port
		var Port uint16
		if protocolFamily == '4' || protocolFamily == '6' {
			if len(msg.Data) < 2 {
				return RespTempFail, nil
			}
			Port = binary.BigEndian.Uint16(msg.Data)
			msg.Data = msg.Data[2:]
			if protocolFamily == '6' {
				// trim IPv6 prefix when necessary
				Prefix := []byte("IPv6:")
				if bytes.HasPrefix(msg.Data, Prefix) {
					msg.Data = bytes.TrimPrefix(msg.Data, Prefix)
				}
			}
		}
		// get address
		Address := readCString(msg.Data)
		// convert address and port to human readable string
		family := map[byte]string{
			'U': "unknown",
			'L': "unix",
			'4': "tcp4",
			'6': "tcp6",
		}
		// run handler and return
		return m.milter.Connect(
			Hostname,
			family[protocolFamily],
			Port,
			net.ParseIP(Address),
			newModifier(m))

	case 'D':
		// define macros
		m.macros = make(map[string]string)
		// convert data to Go strings
		data := decodeCStrings(msg.Data[1:])
		if len(data) != 0 {
			// store data in a map
			for i := 0; i < len(data); i += 2 {
				m.macros[data[i]] = data[i+1]
			}
		}
		// do not send response
		return nil, nil

	case 'E':
		// call and return milter handler
		return m.milter.Body(newModifier(m))

	case 'H':
		// helo command
		name := strings.TrimSuffix(string(msg.Data), null)
		return m.milter.Helo(name, newModifier(m))

	case 'L':
		// make sure headers is initialized
		if m.headers == nil {
			m.headers = make(textproto.MIMEHeader)
		}
		// add new header to headers map
		HeaderData := decodeCStrings(msg.Data)
		if len(HeaderData) == 2 {
			m.headers.Add(HeaderData[0], HeaderData[1])
			// call and return milter handler
			return m.milter.Header(HeaderData[0], HeaderData[1], newModifier(m))
		}

	case 'M':
		// envelope from address
		envfrom := readCString(msg.Data)
		return m.milter.MailFrom(strings.Trim(envfrom, "<>"), newModifier(m))

	case 'N':
		// end of headers
		return m.milter.Headers(m.headers, newModifier(m))

	case 'O':
		// ignore request and prepare response buffer
		buffer := new(bytes.Buffer)
		// prepare response data
		for _, value := range []uint32{2, uint32(m.actions), uint32(m.protocol)} {
			if err := binary.Write(buffer, binary.BigEndian, value); err != nil {
				return nil, err
			}
		}
		// build and send packet
		return NewResponse('O', buffer.Bytes()), nil

	case 'Q':
		// client requested session close
		return nil, eCloseSession

	case 'R':
		// envelope to address
		envto := readCString(msg.Data)
		return m.milter.RcptTo(strings.Trim(envto, "<>"), newModifier(m))

	case 'T':
		// data, ignore

	default:
		// print error and close session
		log.Printf("Unrecognized command code: %c", msg.Code)
		return nil, eCloseSession
	}

	// by default continue with next milter message
	return RespContinue, nil
}

// HandleMilterComands processes all milter commands in the same connection
func (m *milterSession) HandleMilterCommands() {
	// close session socket on exit
	defer m.sock.Close()

	for {
		// ReadPacket
		msg, err := m.ReadPacket()
		if err != nil {
			if err != io.EOF {
				log.Printf("Error reading milter command: %v", err)
			}
			return
		}

		// process command
		resp, err := m.Process(msg)
		if err != nil {
			if err != eCloseSession {
				// log error condition
				log.Printf("Error performing milter command: %v", err)
			}
			return
		}

		// ignore empty responses
		if resp != nil {
			// send back response message
			if err = m.WritePacket(resp.Response()); err != nil {
				log.Printf("Error writing packet: %v", err)
				return
			}

			if !resp.Continue() {
				return
			}

		}
	}
}
<commit_msg>go fmt<commit_after>package milter

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"io"
	"log"
	"net"
	"net/textproto"
	"strings"
)

// OptAction sets which actions the milter wants to perform.
// Multiple options can be set using a bitmask.
type OptAction uint32

// OptProtocol masks out unwanted parts of the SMTP transaction.
// Multiple options can be set using a bitmask.
type OptProtocol uint32

const (
	// set which actions the milter wants to perform
	OptAddHeader    OptAction = 0x01
	OptChangeBody   OptAction = 0x02
	OptAddRcpt      OptAction = 0x04
	OptRemoveRcpt   OptAction = 0x08
	OptChangeHeader OptAction = 0x10
	OptQuarantine   OptAction = 0x20
	OptChangeFrom   OptAction = 0x40

	// mask out unwanted parts of the SMTP transaction
	OptNoConnect  OptProtocol = 0x01
	OptNoHelo     OptProtocol = 0x02
	OptNoMailFrom OptProtocol = 0x04
	OptNoRcptTo   OptProtocol = 0x08
	OptNoBody     OptProtocol = 0x10
	OptNoHeaders  OptProtocol = 0x20
	OptNoEOH      OptProtocol = 0x40
)

// milterSession keeps session state during MTA communication
type milterSession struct {
	actions  OptAction
	protocol OptProtocol
	sock     io.ReadWriteCloser
	headers  textproto.MIMEHeader
	macros   map[string]string
	milter   Milter
}

// ReadPacket reads incoming milter packet
func (c *milterSession) ReadPacket() (*Message, error) {
	// read packet length
	var length uint32
	if err := binary.Read(c.sock, binary.BigEndian, &length); err != nil {
		return nil, err
	}

	// read packet data
	data := make([]byte, length)
	if _, err := io.ReadFull(c.sock, data); err != nil {
		return nil, err
	}

	// prepare response data
	message := Message{
		Code: data[0],
		Data: data[1:],
	}

	return &message, nil
}

// WritePacket sends a milter response packet to socket stream
func (m *milterSession) WritePacket(msg *Message) error {
	buffer := bufio.NewWriter(m.sock)

	// calculate and write response length
	length := uint32(len(msg.Data) + 1)
	if err := binary.Write(buffer, binary.BigEndian, length); err != nil {
		return err
	}

	// write response code
	if err := buffer.WriteByte(msg.Code); err != nil {
		return err
	}

	// write response data
	if _, err := buffer.Write(msg.Data); err != nil {
		return err
	}

	// flush data to network socket stream
	if err := buffer.Flush(); err != nil {
		return err
	}

	return nil
}

// Process processes incoming milter commands
func (m *milterSession) Process(msg *Message) (Response, error) {
	switch msg.Code {
	case 'A':
		// abort current message and start over
		m.headers = nil
		m.macros = nil
		// do not send response
		return nil, nil

	case 'B':
		// body chunk
		return m.milter.BodyChunk(msg.Data, newModifier(m))

	case 'C':
		// new connection, get hostname
		Hostname := readCString(msg.Data)
		msg.Data = msg.Data[len(Hostname)+1:]
		// get protocol family
		protocolFamily := msg.Data[0]
		msg.Data = msg.Data[1:]
		// get port
		var Port uint16
		if protocolFamily == '4' || protocolFamily == '6' {
			if len(msg.Data) < 2 {
				return RespTempFail, nil
			}
			Port = binary.BigEndian.Uint16(msg.Data)
			msg.Data = msg.Data[2:]
			if protocolFamily == '6' {
				// trim IPv6 prefix when necessary
				Prefix := []byte("IPv6:")
				if bytes.HasPrefix(msg.Data, Prefix) {
					msg.Data = bytes.TrimPrefix(msg.Data, Prefix)
				}
			}
		}
		// get address
		Address := readCString(msg.Data)
		// convert address and port to human readable string
		family := map[byte]string{
			'U': "unknown",
			'L': "unix",
			'4': "tcp4",
			'6': "tcp6",
		}
		// run handler and return
		return m.milter.Connect(
			Hostname,
			family[protocolFamily],
			Port,
			net.ParseIP(Address),
			newModifier(m))

	case 'D':
		// define macros
		m.macros = make(map[string]string)
		// convert data to Go strings
		data := decodeCStrings(msg.Data[1:])
		if len(data) != 0 {
			// store data in a map
			for i := 0; i < len(data); i += 2 {
				m.macros[data[i]] = data[i+1]
			}
		}
		// do not send response
		return nil, nil

	case 'E':
		// call and return milter handler
		return m.milter.Body(newModifier(m))

	case 'H':
		// helo command
		name := strings.TrimSuffix(string(msg.Data), null)
		return m.milter.Helo(name, newModifier(m))

	case 'L':
		// make sure headers is initialized
		if m.headers == nil {
			m.headers = make(textproto.MIMEHeader)
		}
		// add new header to headers map
		HeaderData := decodeCStrings(msg.Data)
		if len(HeaderData) == 2 {
			m.headers.Add(HeaderData[0], HeaderData[1])
			// call and return milter handler
			return m.milter.Header(HeaderData[0], HeaderData[1], newModifier(m))
		}

	case 'M':
		// envelope from address
		envfrom := readCString(msg.Data)
		return m.milter.MailFrom(strings.Trim(envfrom, "<>"), newModifier(m))

	case 'N':
		// end of headers
		return m.milter.Headers(m.headers, newModifier(m))

	case 'O':
		// ignore request and prepare response buffer
		buffer := new(bytes.Buffer)
		// prepare response data
		for _, value := range []uint32{2, uint32(m.actions), uint32(m.protocol)} {
			if err := binary.Write(buffer, binary.BigEndian, value); err != nil {
				return nil, err
			}
		}
		// build and send packet
		return NewResponse('O', buffer.Bytes()), nil

	case 'Q':
		// client requested session close
		return nil, eCloseSession

	case 'R':
		// envelope to address
		envto := readCString(msg.Data)
		return m.milter.RcptTo(strings.Trim(envto, "<>"), newModifier(m))

	case 'T':
		// data, ignore

	default:
		// print error and close session
		log.Printf("Unrecognized command code: %c", msg.Code)
		return nil, eCloseSession
	}

	// by default continue with next milter message
	return RespContinue, nil
}

// HandleMilterComands processes all milter commands in the same connection
func (m *milterSession) HandleMilterCommands() {
	// close session socket on exit
	defer m.sock.Close()

	for {
		// ReadPacket
		msg, err := m.ReadPacket()
		if err != nil {
			if err != io.EOF {
				log.Printf("Error reading milter command: %v", err)
			}
			return
		}

		// process command
		resp, err := m.Process(msg)
		if err != nil {
			if err != eCloseSession {
				// log error condition
				log.Printf("Error performing milter command: %v", err)
			}
			return
		}

		// ignore empty responses
		if resp != nil {
			// send back response message
			if err = m.WritePacket(resp.Response()); err != nil {
				log.Printf("Error writing packet: %v", err)
				return
			}

			if !resp.Continue() {
				return
			}

		}
	}
}
<|endoftext|>


<commit_before>package stackongo

import (
	"http"
	"json"
	"io/ioutil"
	"url"
	"os"
)

var host string = "http://api.stackexchange.com"
var transport http.Transport

type Session struct {
	Site string
}

func NewSession(site string) *Session {
	return &Session{Site: site}
}

func setHost(url string) {
	host = url
}

func getTransport() http.RoundTripper {
	if transport != nil {
		return transport
	}
	return http.DefaultTransport
}

func SetTransport(t http.Transport) {
	transport = t
}

// construct the endpoint URL
func setupEndpoint(path string, params map[string]string) *url.URL {
	base_url, _ := url.Parse(host)
	endpoint, _ := base_url.Parse("/2.0/" + path)

	query := endpoint.Query()
	for key, value := range params {
		query.Set(key, value)
	}

	endpoint.RawQuery = query.Encode()

	return endpoint
}

// parse the response
func parseResponse(response *http.Response, result interface{}) (error os.Error) {
	// close the body when done reading
	defer response.Body.Close()

	//read the response
	bytes, error := ioutil.ReadAll(response.Body)

	if error != nil {
		return
	}

	//parse JSON
	error = json.Unmarshal(bytes, result)

	if error != nil {
		print(error.String())
	}

	//check whether the response is a bad request
	if response.StatusCode == 400 {
		error = os.NewError("Bad Request")
	}

	return
}

func (session Session) get(section string, params map[string]string, collection interface{}) (error os.Error) {
	//set parameters for querystring
	params["site"] = session.Site

	return get(section, params, collection)
}

func get(section string, params map[string]string, collection interface{}) (error os.Error) {
	client := &http.Client{Transport: getTransport()}

	response, error := client.Get(setupEndpoint(section, params).String())

	if error != nil {
		return
	}

	error = parseResponse(response, collection)

	return

}
<commit_msg>modified the transport type<commit_after>package stackongo

import (
	"http"
	"json"
	"io/ioutil"
	"url"
	"os"
)

var host string = "http://api.stackexchange.com"
var transport http.RoundTripper

type Session struct {
	Site string
}

func NewSession(site string) *Session {
	return &Session{Site: site}
}

func setHost(url string) {
	host = url
}

func getTransport() http.RoundTripper {
	if transport != nil {
		return transport
	}
	return http.DefaultTransport
}

func SetTransport(t http.RoundTripper) {
	transport = t
}

// construct the endpoint URL
func setupEndpoint(path string, params map[string]string) *url.URL {
	base_url, _ := url.Parse(host)
	endpoint, _ := base_url.Parse("/2.0/" + path)

	query := endpoint.Query()
	for key, value := range params {
		query.Set(key, value)
	}

	endpoint.RawQuery = query.Encode()

	return endpoint
}

// parse the response
func parseResponse(response *http.Response, result interface{}) (error os.Error) {
	// close the body when done reading
	defer response.Body.Close()

	//read the response
	bytes, error := ioutil.ReadAll(response.Body)

	if error != nil {
		return
	}

	//parse JSON
	error = json.Unmarshal(bytes, result)

	if error != nil {
		print(error.String())
	}

	//check whether the response is a bad request
	if response.StatusCode == 400 {
		error = os.NewError("Bad Request")
	}

	return
}

func (session Session) get(section string, params map[string]string, collection interface{}) (error os.Error) {
	//set parameters for querystring
	params["site"] = session.Site

	return get(section, params, collection)
}

func get(section string, params map[string]string, collection interface{}) (error os.Error) {
	client := &http.Client{Transport: getTransport()}

	response, error := client.Get(setupEndpoint(section, params).String())

	if error != nil {
		return
	}

	error = parseResponse(response, collection)

	return

}
<|endoftext|>