<commit_before>// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package chain

import (
	"errors"
	"sync"
	"time"

	"github.com/roasbeef/btcd/btcjson"
	"github.com/roasbeef/btcd/chaincfg"
	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcrpcclient"
	"github.com/roasbeef/btcutil"
	"github.com/roasbeef/btcwallet/waddrmgr"
	"github.com/roasbeef/btcwallet/wtxmgr"
)

// RPCClient represents a persistent client connection to a bitcoin RPC server
// for information regarding the current best block chain.
type RPCClient struct {
	*btcrpcclient.Client
	connConfig        *btcrpcclient.ConnConfig // Work around unexported field
	chainParams       *chaincfg.Params
	reconnectAttempts int

	enqueueNotification chan interface{}
	dequeueNotification chan interface{}
	currentBlock        chan *waddrmgr.BlockStamp

	quit    chan struct{}
	wg      sync.WaitGroup
	started bool
	quitMtx sync.Mutex
}

// NewRPCClient creates a client connection to the server described by the
// connect string.  If disableTLS is false, the remote RPC certificate must be
// provided in the certs slice.  The connection is not established immediately,
// but must be done using the Start method.  If the remote server does not
// operate on the same bitcoin network as described by the passed chain
// parameters, the connection will be disconnected.
func NewRPCClient(chainParams *chaincfg.Params, connect, user, pass string, certs []byte,
	disableTLS bool, reconnectAttempts int) (*RPCClient, error) {

	if reconnectAttempts < 0 {
		return nil, errors.New("reconnectAttempts must be positive")
	}

	client := &RPCClient{
		connConfig: &btcrpcclient.ConnConfig{
			Host:                 connect,
			Endpoint:             "ws",
			User:                 user,
			Pass:                 pass,
			Certificates:         certs,
			DisableAutoReconnect: false,
			DisableConnectOnNew:  true,
			DisableTLS:           disableTLS,
		},
		chainParams:         chainParams,
		reconnectAttempts:   reconnectAttempts,
		enqueueNotification: make(chan interface{}),
		dequeueNotification: make(chan interface{}),
		currentBlock:        make(chan *waddrmgr.BlockStamp),
		quit:                make(chan struct{}),
	}
	ntfnCallbacks := &btcrpcclient.NotificationHandlers{
		OnClientConnected:   client.onClientConnect,
		OnBlockConnected:    client.onBlockConnected,
		OnBlockDisconnected: client.onBlockDisconnected,
		OnRecvTx:            client.onRecvTx,
		OnRedeemingTx:       client.onRedeemingTx,
		OnRescanFinished:    client.onRescanFinished,
		OnRescanProgress:    client.onRescanProgress,
	}
	rpcClient, err := btcrpcclient.New(client.connConfig, ntfnCallbacks)
	if err != nil {
		return nil, err
	}
	client.Client = rpcClient
	return client, nil
}

// Start attempts to establish a client connection with the remote server.
// If successful, handler goroutines are started to process notifications
// sent by the server.  After a limited number of connection attempts, this
// function gives up, and therefore will not block forever waiting for the
// connection to be established to a server that may not exist.
func (c *RPCClient) Start() error {
	err := c.Connect(c.reconnectAttempts)
	if err != nil {
		return err
	}

	// Verify that the server is running on the expected network.
	net, err := c.GetCurrentNet()
	if err != nil {
		c.Disconnect()
		return err
	}
	if net != c.chainParams.Net {
		c.Disconnect()
		return errors.New("mismatched networks")
	}

	c.quitMtx.Lock()
	c.started = true
	c.quitMtx.Unlock()

	c.wg.Add(1)
	go c.handler()
	return nil
}

// Stop disconnects the client and signals the shutdown of all goroutines
// started by Start.
func (c *RPCClient) Stop() {
	c.quitMtx.Lock()
	select {
	case <-c.quit:
	default:
		close(c.quit)
		c.Client.Shutdown()

		if !c.started {
			close(c.dequeueNotification)
		}
	}
	c.quitMtx.Unlock()
}

// WaitForShutdown blocks until both the client has finished disconnecting
// and all handlers have exited.
func (c *RPCClient) WaitForShutdown() {
	c.Client.WaitForShutdown()
	c.wg.Wait()
}

// Notification types.  These are defined here and processed from from reading
// a notificationChan to avoid handling these notifications directly in
// btcrpcclient callbacks, which isn't very Go-like and doesn't allow
// blocking client calls.
type (
	// ClientConnected is a notification for when a client connection is
	// opened or reestablished to the chain server.
	ClientConnected struct{}

	// BlockConnected is a notification for a newly-attached block to the
	// best chain.
	BlockConnected wtxmgr.BlockMeta

	// BlockDisconnected is a notifcation that the block described by the
	// BlockStamp was reorganized out of the best chain.
	BlockDisconnected wtxmgr.BlockMeta

	// RelevantTx is a notification for a transaction which spends wallet
	// inputs or pays to a watched address.
	RelevantTx struct {
		TxRecord *wtxmgr.TxRecord
		Block    *wtxmgr.BlockMeta // nil if unmined
	}

	// RescanProgress is a notification describing the current status
	// of an in-progress rescan.
	RescanProgress struct {
		Hash   *chainhash.Hash
		Height int32
		Time   time.Time
	}

	// RescanFinished is a notification that a previous rescan request
	// has finished.
	RescanFinished struct {
		Hash   *chainhash.Hash
		Height int32
		Time   time.Time
	}
)

// Notifications returns a channel of parsed notifications sent by the remote
// bitcoin RPC server.  This channel must be continually read or the process
// may abort for running out memory, as unread notifications are queued for
// later reads.
func (c *RPCClient) Notifications() <-chan interface{} {
	return c.dequeueNotification
}

// BlockStamp returns the latest block notified by the client, or an error
// if the client has been shut down.
func (c *RPCClient) BlockStamp() (*waddrmgr.BlockStamp, error) {
	select {
	case bs := <-c.currentBlock:
		return bs, nil
	case <-c.quit:
		return nil, errors.New("disconnected")
	}
}

// parseBlock parses a btcws definition of the block a tx is mined it to the
// Block structure of the wtxmgr package, and the block index.  This is done
// here since btcrpcclient doesn't parse this nicely for us.
func parseBlock(block *btcjson.BlockDetails) (*wtxmgr.BlockMeta, error) {
	if block == nil {
		return nil, nil
	}
	blkHash, err := chainhash.NewHashFromStr(block.Hash)
	if err != nil {
		return nil, err
	}
	blk := &wtxmgr.BlockMeta{
		Block: wtxmgr.Block{
			Height: block.Height,
			Hash:   *blkHash,
		},
		Time: time.Unix(block.Time, 0),
	}
	return blk, nil
}

func (c *RPCClient) onClientConnect() {
	select {
	case c.enqueueNotification <- ClientConnected{}:
	case <-c.quit:
	}
}

func (c *RPCClient) onBlockConnected(hash *chainhash.Hash, height int32, time time.Time) {
	select {
	case c.enqueueNotification <- BlockConnected{
		Block: wtxmgr.Block{
			Hash:   *hash,
			Height: height,
		},
		Time: time,
	}:
	case <-c.quit:
	}
}

func (c *RPCClient) onBlockDisconnected(hash *chainhash.Hash, height int32, time time.Time) {
	select {
	case c.enqueueNotification <- BlockDisconnected{
		Block: wtxmgr.Block{
			Hash:   *hash,
			Height: height,
		},
		Time: time,
	}:
	case <-c.quit:
	}
}

func (c *RPCClient) onRecvTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
	blk, err := parseBlock(block)
	if err != nil {
		// Log and drop improper notification.
		log.Errorf("recvtx notification bad block: %v", err)
		return
	}

	rec, err := wtxmgr.NewTxRecordFromMsgTx(tx.MsgTx(), time.Now())
	if err != nil {
		log.Errorf("Cannot create transaction record for relevant "+
			"tx: %v", err)
		return
	}
	select {
	case c.enqueueNotification <- RelevantTx{rec, blk}:
	case <-c.quit:
	}
}

func (c *RPCClient) onRedeemingTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
	// Handled exactly like recvtx notifications.
	c.onRecvTx(tx, block)
}

func (c *RPCClient) onRescanProgress(hash *chainhash.Hash, height int32, blkTime time.Time) {
	select {
	case c.enqueueNotification <- &RescanProgress{hash, height, blkTime}:
	case <-c.quit:
	}
}

func (c *RPCClient) onRescanFinished(hash *chainhash.Hash, height int32, blkTime time.Time) {
	select {
	case c.enqueueNotification <- &RescanFinished{hash, height, blkTime}:
	case <-c.quit:
	}

}

// handler maintains a queue of notifications and the current state (best
// block) of the chain.
func (c *RPCClient) handler() {
	hash, height, err := c.GetBestBlock()
	if err != nil {
		log.Errorf("Failed to receive best block from chain server: %v", err)
		c.Stop()
		c.wg.Done()
		return
	}

	bs := &waddrmgr.BlockStamp{Hash: *hash, Height: height}

	// TODO: Rather than leaving this as an unbounded queue for all types of
	// notifications, try dropping ones where a later enqueued notification
	// can fully invalidate one waiting to be processed.  For example,
	// blockconnected notifications for greater block heights can remove the
	// need to process earlier blockconnected notifications still waiting
	// here.

	var notifications []interface{}
	enqueue := c.enqueueNotification
	var dequeue chan interface{}
	var next interface{}
	pingChan := time.After(time.Minute)
out:
	for {
		select {
		case n, ok := <-enqueue:
			if !ok {
				// If no notifications are queued for handling,
				// the queue is finished.
				if len(notifications) == 0 {
					break out
				}
				// nil channel so no more reads can occur.
				enqueue = nil
				continue
			}
			if len(notifications) == 0 {
				next = n
				dequeue = c.dequeueNotification
			}
			notifications = append(notifications, n)
			pingChan = time.After(time.Minute)

		case dequeue <- next:
			if n, ok := next.(BlockConnected); ok {
				bs = &waddrmgr.BlockStamp{
					Height: n.Height,
					Hash:   n.Hash,
				}
			}

			notifications[0] = nil
			notifications = notifications[1:]
			if len(notifications) != 0 {
				next = notifications[0]
			} else {
				// If no more notifications can be enqueued, the
				// queue is finished.
				if enqueue == nil {
					break out
				}
				dequeue = nil
			}

		case <-pingChan:
			// No notifications were received in the last 60s.
			// Ensure the connection is still active by making a new
			// request to the server.
			// TODO: A minute timeout is used to prevent the handler
			// loop from blocking here forever, but this is much larger
			// than it needs to be due to btcd processing websocket
			// requests synchronously (see
			// https://github.com/roasbeef/btcd/issues/504).  Decrease
			// this to something saner like 3s when the above issue is
			// fixed.
			type sessionResult struct {
				err error
			}
			sessionResponse := make(chan sessionResult, 1)
			go func() {
				_, err := c.Session()
				sessionResponse <- sessionResult{err}
			}()

			select {
			case resp := <-sessionResponse:
				if resp.err != nil {
					log.Errorf("Failed to receive session "+
						"result: %v", resp.err)
					c.Stop()
					break out
				}
				pingChan = time.After(time.Minute)

			case <-time.After(time.Minute):
				log.Errorf("Timeout waiting for session RPC")
				c.Stop()
				break out
			}

		case c.currentBlock <- bs:

		case <-c.quit:
			break out
		}
	}

	c.Stop()
	close(c.dequeueNotification)
	c.wg.Done()
}

// POSTClient creates the equivalent HTTP POST btcrpcclient.Client.
func (c *RPCClient) POSTClient() (*btcrpcclient.Client, error) {
	configCopy := *c.connConfig
	configCopy.HTTPPostMode = true
	return btcrpcclient.New(&configCopy, nil)
}
<commit_msg>Fix RPC ping/pong deadlock and timeout issue.<commit_after>// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package chain

import (
	"errors"
	"sync"
	"time"

	"github.com/roasbeef/btcd/btcjson"
	"github.com/roasbeef/btcd/chaincfg"
	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcrpcclient"
	"github.com/roasbeef/btcutil"
	"github.com/roasbeef/btcwallet/waddrmgr"
	"github.com/roasbeef/btcwallet/wtxmgr"
)

// RPCClient represents a persistent client connection to a bitcoin RPC server
// for information regarding the current best block chain.
type RPCClient struct {
	*btcrpcclient.Client
	connConfig        *btcrpcclient.ConnConfig // Work around unexported field
	chainParams       *chaincfg.Params
	reconnectAttempts int

	enqueueNotification chan interface{}
	dequeueNotification chan interface{}
	currentBlock        chan *waddrmgr.BlockStamp

	quit    chan struct{}
	wg      sync.WaitGroup
	started bool
	quitMtx sync.Mutex
}

// NewRPCClient creates a client connection to the server described by the
// connect string.  If disableTLS is false, the remote RPC certificate must be
// provided in the certs slice.  The connection is not established immediately,
// but must be done using the Start method.  If the remote server does not
// operate on the same bitcoin network as described by the passed chain
// parameters, the connection will be disconnected.
func NewRPCClient(chainParams *chaincfg.Params, connect, user, pass string, certs []byte,
	disableTLS bool, reconnectAttempts int) (*RPCClient, error) {

	if reconnectAttempts < 0 {
		return nil, errors.New("reconnectAttempts must be positive")
	}

	client := &RPCClient{
		connConfig: &btcrpcclient.ConnConfig{
			Host:                 connect,
			Endpoint:             "ws",
			User:                 user,
			Pass:                 pass,
			Certificates:         certs,
			DisableAutoReconnect: false,
			DisableConnectOnNew:  true,
			DisableTLS:           disableTLS,
		},
		chainParams:         chainParams,
		reconnectAttempts:   reconnectAttempts,
		enqueueNotification: make(chan interface{}),
		dequeueNotification: make(chan interface{}),
		currentBlock:        make(chan *waddrmgr.BlockStamp),
		quit:                make(chan struct{}),
	}
	ntfnCallbacks := &btcrpcclient.NotificationHandlers{
		OnClientConnected:   client.onClientConnect,
		OnBlockConnected:    client.onBlockConnected,
		OnBlockDisconnected: client.onBlockDisconnected,
		OnRecvTx:            client.onRecvTx,
		OnRedeemingTx:       client.onRedeemingTx,
		OnRescanFinished:    client.onRescanFinished,
		OnRescanProgress:    client.onRescanProgress,
	}
	rpcClient, err := btcrpcclient.New(client.connConfig, ntfnCallbacks)
	if err != nil {
		return nil, err
	}
	client.Client = rpcClient
	return client, nil
}

// Start attempts to establish a client connection with the remote server.
// If successful, handler goroutines are started to process notifications
// sent by the server.  After a limited number of connection attempts, this
// function gives up, and therefore will not block forever waiting for the
// connection to be established to a server that may not exist.
func (c *RPCClient) Start() error {
	err := c.Connect(c.reconnectAttempts)
	if err != nil {
		return err
	}

	// Verify that the server is running on the expected network.
	net, err := c.GetCurrentNet()
	if err != nil {
		c.Disconnect()
		return err
	}
	if net != c.chainParams.Net {
		c.Disconnect()
		return errors.New("mismatched networks")
	}

	c.quitMtx.Lock()
	c.started = true
	c.quitMtx.Unlock()

	c.wg.Add(1)
	go c.handler()
	return nil
}

// Stop disconnects the client and signals the shutdown of all goroutines
// started by Start.
func (c *RPCClient) Stop() {
	c.quitMtx.Lock()
	select {
	case <-c.quit:
	default:
		close(c.quit)
		c.Client.Shutdown()

		if !c.started {
			close(c.dequeueNotification)
		}
	}
	c.quitMtx.Unlock()
}

// WaitForShutdown blocks until both the client has finished disconnecting
// and all handlers have exited.
func (c *RPCClient) WaitForShutdown() {
	c.Client.WaitForShutdown()
	c.wg.Wait()
}

// Notification types.  These are defined here and processed from from reading
// a notificationChan to avoid handling these notifications directly in
// btcrpcclient callbacks, which isn't very Go-like and doesn't allow
// blocking client calls.
type (
	// ClientConnected is a notification for when a client connection is
	// opened or reestablished to the chain server.
	ClientConnected struct{}

	// BlockConnected is a notification for a newly-attached block to the
	// best chain.
	BlockConnected wtxmgr.BlockMeta

	// BlockDisconnected is a notifcation that the block described by the
	// BlockStamp was reorganized out of the best chain.
	BlockDisconnected wtxmgr.BlockMeta

	// RelevantTx is a notification for a transaction which spends wallet
	// inputs or pays to a watched address.
	RelevantTx struct {
		TxRecord *wtxmgr.TxRecord
		Block    *wtxmgr.BlockMeta // nil if unmined
	}

	// RescanProgress is a notification describing the current status
	// of an in-progress rescan.
	RescanProgress struct {
		Hash   *chainhash.Hash
		Height int32
		Time   time.Time
	}

	// RescanFinished is a notification that a previous rescan request
	// has finished.
	RescanFinished struct {
		Hash   *chainhash.Hash
		Height int32
		Time   time.Time
	}
)

// Notifications returns a channel of parsed notifications sent by the remote
// bitcoin RPC server.  This channel must be continually read or the process
// may abort for running out memory, as unread notifications are queued for
// later reads.
func (c *RPCClient) Notifications() <-chan interface{} {
	return c.dequeueNotification
}

// BlockStamp returns the latest block notified by the client, or an error
// if the client has been shut down.
func (c *RPCClient) BlockStamp() (*waddrmgr.BlockStamp, error) {
	select {
	case bs := <-c.currentBlock:
		return bs, nil
	case <-c.quit:
		return nil, errors.New("disconnected")
	}
}

// parseBlock parses a btcws definition of the block a tx is mined it to the
// Block structure of the wtxmgr package, and the block index.  This is done
// here since btcrpcclient doesn't parse this nicely for us.
func parseBlock(block *btcjson.BlockDetails) (*wtxmgr.BlockMeta, error) {
	if block == nil {
		return nil, nil
	}
	blkHash, err := chainhash.NewHashFromStr(block.Hash)
	if err != nil {
		return nil, err
	}
	blk := &wtxmgr.BlockMeta{
		Block: wtxmgr.Block{
			Height: block.Height,
			Hash:   *blkHash,
		},
		Time: time.Unix(block.Time, 0),
	}
	return blk, nil
}

func (c *RPCClient) onClientConnect() {
	select {
	case c.enqueueNotification <- ClientConnected{}:
	case <-c.quit:
	}
}

func (c *RPCClient) onBlockConnected(hash *chainhash.Hash, height int32, time time.Time) {
	select {
	case c.enqueueNotification <- BlockConnected{
		Block: wtxmgr.Block{
			Hash:   *hash,
			Height: height,
		},
		Time: time,
	}:
	case <-c.quit:
	}
}

func (c *RPCClient) onBlockDisconnected(hash *chainhash.Hash, height int32, time time.Time) {
	select {
	case c.enqueueNotification <- BlockDisconnected{
		Block: wtxmgr.Block{
			Hash:   *hash,
			Height: height,
		},
		Time: time,
	}:
	case <-c.quit:
	}
}

func (c *RPCClient) onRecvTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
	blk, err := parseBlock(block)
	if err != nil {
		// Log and drop improper notification.
		log.Errorf("recvtx notification bad block: %v", err)
		return
	}

	rec, err := wtxmgr.NewTxRecordFromMsgTx(tx.MsgTx(), time.Now())
	if err != nil {
		log.Errorf("Cannot create transaction record for relevant "+
			"tx: %v", err)
		return
	}
	select {
	case c.enqueueNotification <- RelevantTx{rec, blk}:
	case <-c.quit:
	}
}

func (c *RPCClient) onRedeemingTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
	// Handled exactly like recvtx notifications.
	c.onRecvTx(tx, block)
}

func (c *RPCClient) onRescanProgress(hash *chainhash.Hash, height int32, blkTime time.Time) {
	select {
	case c.enqueueNotification <- &RescanProgress{hash, height, blkTime}:
	case <-c.quit:
	}
}

func (c *RPCClient) onRescanFinished(hash *chainhash.Hash, height int32, blkTime time.Time) {
	select {
	case c.enqueueNotification <- &RescanFinished{hash, height, blkTime}:
	case <-c.quit:
	}

}

// handler maintains a queue of notifications and the current state (best
// block) of the chain.
func (c *RPCClient) handler() {
	hash, height, err := c.GetBestBlock()
	if err != nil {
		log.Errorf("Failed to receive best block from chain server: %v", err)
		c.Stop()
		c.wg.Done()
		return
	}

	bs := &waddrmgr.BlockStamp{Hash: *hash, Height: height}

	// TODO: Rather than leaving this as an unbounded queue for all types of
	// notifications, try dropping ones where a later enqueued notification
	// can fully invalidate one waiting to be processed.  For example,
	// blockconnected notifications for greater block heights can remove the
	// need to process earlier blockconnected notifications still waiting
	// here.

	var notifications []interface{}
	enqueue := c.enqueueNotification
	var dequeue chan interface{}
	var next interface{}
	pingChan := time.After(time.Minute)
	pingChanReset := make(chan (<-chan time.Time))
out:
	for {
		select {
		case n, ok := <-enqueue:
			if !ok {
				// If no notifications are queued for handling,
				// the queue is finished.
				if len(notifications) == 0 {
					break out
				}
				// nil channel so no more reads can occur.
				enqueue = nil
				continue
			}
			if len(notifications) == 0 {
				next = n
				dequeue = c.dequeueNotification
			}
			notifications = append(notifications, n)
			pingChan = time.After(time.Minute)

		case dequeue <- next:
			if n, ok := next.(BlockConnected); ok {
				bs = &waddrmgr.BlockStamp{
					Height: n.Height,
					Hash:   n.Hash,
				}
			}

			notifications[0] = nil
			notifications = notifications[1:]
			if len(notifications) != 0 {
				next = notifications[0]
			} else {
				// If no more notifications can be enqueued, the
				// queue is finished.
				if enqueue == nil {
					break out
				}
				dequeue = nil
			}

		case <-pingChan:
			// No notifications were received in the last 60s. Ensure the
			// connection is still active by making a new request to the server.
			//
			// This MUST wait for the response in a new goroutine so as to not
			// block channel sends enqueueing more notifications.  Doing so
			// would cause a deadlock and after the timeout expires, the client
			// would be shut down.
			//
			// TODO: A minute timeout is used to prevent the handler loop from
			// blocking here forever, but this is much larger than it needs to
			// be due to dcrd processing websocket requests synchronously (see
			// https://github.com/btcsuite/btcd/issues/504).  Decrease this to
			// something saner like 3s when the above issue is fixed.
			type sessionResult struct {
				err error
			}
			sessionResponse := make(chan sessionResult, 1)
			go func() {
				_, err := c.Session()
				sessionResponse <- sessionResult{err}
			}()
			go func() {
				select {
				case resp := <-sessionResponse:
					if resp.err != nil {
						log.Errorf("Failed to receive session "+
							"result: %v", resp.err)
						c.Stop()
					}
					pingChanReset <- time.After(time.Minute)

				case <-time.After(time.Minute):
					log.Errorf("Timeout waiting for session RPC")
					c.Stop()
				}
			}()

		case ch := <-pingChanReset:
			pingChan = ch

		case c.currentBlock <- bs:

		case <-c.quit:
			break out
		}
	}

	c.Stop()
	close(c.dequeueNotification)
	c.wg.Done()
}

// POSTClient creates the equivalent HTTP POST btcrpcclient.Client.
func (c *RPCClient) POSTClient() (*btcrpcclient.Client, error) {
	configCopy := *c.connConfig
	configCopy.HTTPPostMode = true
	return btcrpcclient.New(&configCopy, nil)
}
<|endoftext|>


<commit_before>package chance

import (
	"chance"
	"testing"
	"time"
)

var testScoreTable = []struct {
	ups      int
	downs    int
	expected int
}{
	{100, 0, 100},
	{1000, 0, 1000},
	{0, 0, 0},
	{0, 100, -100},
	{100, 99, 1},
}

func TestScore(t *testing.T) {
	for _, testdata := range testScoreTable {
		score := chance.Score(testdata.ups, testdata.downs)

		if score != testdata.expected {
			t.Errorf("Expexted %d from score(%d,%d)", testdata.expected, testdata.ups, testdata.downs)
		}
	}
}

var testHotTable = []struct {
	ups      int
	downs    int
	date     time.Time
	expected float64
}{
	{100, 0, time.Date(2013, time.August, 8, 6, 0, 0, 0, time.UTC), 5377.857711111111},
	{1000, 900, time.Date(2013, time.August, 8, 6, 0, 0, 0, time.UTC), 5377.857711111111},
	{100, 0, time.Date(2013, time.August, 8, 5, 0, 0, 0, time.UTC), 5377.777711111111},
	{100, 0, time.Date(2012, time.August, 8, 5, 0, 0, 0, time.UTC), 4676.977711111111},
}

func TestHot(t *testing.T) {
	for _, testdata := range testHotTable {
		hottness := chance.Hot(testdata.ups, testdata.downs, testdata.date)

		if hottness != testdata.expected {
			t.Error("Hotness", hottness, "Expected", testdata.expected)
		}
	}
}

var testConfidenceTable = []struct {
	ups      int
	downs    int
	expected float64
}{
	{100, 0, 0.9925588255880455},
	{1000, 0, 0.9992505931958449},
	{0, 0, 0},
	{0, 100, 0.06983532663200934},
	{100, 99, 0.7062135168679761},
}

func TestConfidence(t *testing.T) {
	for _, testdata := range testConfidenceTable {
		score := chance.Confidence(testdata.ups, testdata.downs)

		if score != testdata.expected {
			t.Errorf("Expexted %d from confidence(%d,%d)", testdata.expected, testdata.ups, testdata.downs)
		}
	}
}
<commit_msg>rm typo<commit_after>package chance

import (
	"chance"
	"testing"
	"time"
)

var testScoreTable = []struct {
	ups      int
	downs    int
	expected int
}{
	{100, 0, 100},
	{1000, 0, 1000},
	{0, 0, 0},
	{0, 100, -100},
	{100, 99, 1},
}

func TestScore(t *testing.T) {
	for _, testdata := range testScoreTable {
		score := chance.Score(testdata.ups, testdata.downs)

		if score != testdata.expected {
			t.Errorf("Expected %d from score(%d,%d)", testdata.expected, testdata.ups, testdata.downs)
		}
	}
}

var testHotTable = []struct {
	ups      int
	downs    int
	date     time.Time
	expected float64
}{
	{100, 0, time.Date(2013, time.August, 8, 6, 0, 0, 0, time.UTC), 5377.857711111111},
	{1000, 900, time.Date(2013, time.August, 8, 6, 0, 0, 0, time.UTC), 5377.857711111111},
	{100, 0, time.Date(2013, time.August, 8, 5, 0, 0, 0, time.UTC), 5377.777711111111},
	{100, 0, time.Date(2012, time.August, 8, 5, 0, 0, 0, time.UTC), 4676.977711111111},
}

func TestHot(t *testing.T) {
	for _, testdata := range testHotTable {
		hottness := chance.Hot(testdata.ups, testdata.downs, testdata.date)

		if hottness != testdata.expected {
			t.Error("Hotness", hottness, "Expected", testdata.expected)
		}
	}
}

var testConfidenceTable = []struct {
	ups      int
	downs    int
	expected float64
}{
	{100, 0, 0.9925588255880455},
	{1000, 0, 0.9992505931958449},
	{0, 0, 0},
	{0, 100, 0.06983532663200934},
	{100, 99, 0.7062135168679761},
}

func TestConfidence(t *testing.T) {
	for _, testdata := range testConfidenceTable {
		confidence := chance.Confidence(testdata.ups, testdata.downs)

		if confidence != testdata.expected {
			t.Errorf("Expected %d from confidence(%d,%d)", testdata.expected, testdata.ups, testdata.downs)
		}
	}
}
<|endoftext|>


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

package butlerproto

import (
	"bytes"
	"compress/zlib"
	"errors"
	"fmt"
	"io"

	"github.com/golang/protobuf/proto"
	"go.chromium.org/luci/common/data/recordio"
	"go.chromium.org/luci/logdog/api/logpb"
	"go.chromium.org/luci/logdog/common/types"
)

const (
	// DefaultCompressThreshold is the byte size threshold for compressing message
	// data. Messages whose byte count is less than or equal to this threshold
	// will not be compressed.
	//
	// This is the value used by Akamai for its compression threshold:
	// "The reasons 860 bytes is the minimum size for compression is twofold:
	// (1) The overhead of compressing an object under 860 bytes outweighs
	// performance gain. (2) Objects under 860 bytes can be transmitted via a
	// single packet anyway, so there isn't a compelling reason to compress them."
	DefaultCompressThreshold = 860
)

// protoBase is the base type of protocol reader/writer objects.
type protoBase struct {
	// maxSize is the maximum Butler protocol data size. By default, it is
	// types.MaxButlerLogBundleSize. However, it can be overridden for testing
	// here.
	maxSize int64
}

func (p *protoBase) getMaxSize() int64 {
	if p.maxSize == 0 {
		return types.MaxButlerLogBundleSize
	}
	return p.maxSize
}

// Reader is a protocol reader instance.
type Reader struct {
	protoBase

	// Metadata is the unpacked ButlerMetadata. It is populated when the
	// metadata has been read.
	Metadata *logpb.ButlerMetadata

	// Bundle is the unpacked ButlerLogBundle. It is populated when the
	// protocol data has been read and the Metadata indicates a ButlerLogBundle
	// type.
	Bundle *logpb.ButlerLogBundle
}

// ReadMetadata reads the metadata header frame.
func (r *Reader) readMetadata(fr recordio.Reader) error {
	data, err := fr.ReadFrameAll()
	if err != nil {
		return err
	}

	md := logpb.ButlerMetadata{}
	if err := proto.Unmarshal(data, &md); err != nil {
		return fmt.Errorf("butlerproto: failed to unmarshal Metadata frame: %s", err)
	}
	r.Metadata = &md
	return nil
}

func (r *Reader) readData(fr recordio.Reader) ([]byte, error) {
	size, br, err := fr.ReadFrame()
	if err != nil {
		return nil, fmt.Errorf("failed to read bundle frame: %s", err)
	}

	// Read the frame through a zlib reader.
	switch r.Metadata.Compression {
	case logpb.ButlerMetadata_NONE:
		break

	case logpb.ButlerMetadata_ZLIB:
		br, err = zlib.NewReader(br)
		if err != nil {
			return nil, fmt.Errorf("failed to initialize zlib reader: %s", err)
		}

	default:
		return nil, fmt.Errorf("unknown compression type: %v", r.Metadata.Compression)
	}

	// Wrap our reader in a limitErrorReader so we don't pull data beyond our
	// soft maximum.
	br = &limitErrorReader{
		Reader: br,
		limit:  r.getMaxSize(),
	}

	buf := bytes.Buffer{}
	buf.Grow(int(size))
	_, err = buf.ReadFrom(br)
	if err != nil {
		return nil, fmt.Errorf("butlerproto: failed to buffer bundle frame: %s", err)
	}
	return buf.Bytes(), nil
}

func (r *Reader) Read(ir io.Reader) error {
	fr := recordio.NewReader(ir, r.getMaxSize())

	// Ensure that we have our Metadata.
	if err := r.readMetadata(fr); err != nil {
		return err
	}

	switch r.Metadata.Type {
	case logpb.ButlerMetadata_ButlerLogBundle:
		data, err := r.readData(fr)
		if err != nil {
			return fmt.Errorf("butlerproto: failed to read Bundle data: %s", err)
		}

		if r.Metadata.ProtoVersion == logpb.Version {
			bundle := logpb.ButlerLogBundle{}
			if err := proto.Unmarshal(data, &bundle); err != nil {
				return fmt.Errorf("butlerproto: failed to unmarshal Bundle frame: %s", err)
			}
			r.Bundle = &bundle
		}
		return nil

	default:
		return fmt.Errorf("butlerproto: unknown data type: %s", r.Metadata.Type)
	}
}

// limitErrorReader is similar to io.LimitReader, except that it returns
// a custom error instead of io.EOF.
//
// This is important, as it allows us to distinguish between the end of
// the compressed reader's data and a limit being hit.
type limitErrorReader struct {
	io.Reader       // underlying reader
	limit     int64 // max bytes remaining
}

func (r *limitErrorReader) Read(p []byte) (int, error) {
	if r.limit <= 0 {
		return 0, errors.New("limit exceeded")
	}
	if int64(len(p)) > r.limit {
		p = p[0:r.limit]
	}
	n, err := r.Reader.Read(p)
	r.limit -= int64(n)
	return n, err
}

// Writer writes Butler messages that the Reader can read.
type Writer struct {
	protoBase

	// ProtoVersion is the protocol version string to use. If empty, the current
	// ProtoVersion will be used.
	ProtoVersion string

	// Compress, if true, allows the Writer to choose to compress data when
	// applicable.
	Compress bool

	// CompressThreshold is the minimum size that data must be in order to
	CompressThreshold int

	compressBuf    bytes.Buffer
	compressWriter *zlib.Writer
}

func (w *Writer) writeData(fw recordio.Writer, t logpb.ButlerMetadata_ContentType, data []byte) error {
	if int64(len(data)) > w.getMaxSize() {
		return fmt.Errorf("butlerproto: serialized size exceeds soft cap (%d > %d)", len(data), w.getMaxSize())
	}

	pv := w.ProtoVersion
	if pv == "" {
		pv = logpb.Version
	}
	md := logpb.ButlerMetadata{
		Type:         t,
		ProtoVersion: pv,
	}

	// If we're configured to compress and the data is below our threshold,
	// compress.
	if w.Compress && len(data) >= w.CompressThreshold {
		w.compressBuf.Reset()
		if w.compressWriter == nil {
			w.compressWriter = zlib.NewWriter(&w.compressBuf)
		} else {
			w.compressWriter.Reset(&w.compressBuf)
		}
		if _, err := w.compressWriter.Write(data); err != nil {
			return err
		}
		if err := w.compressWriter.Close(); err != nil {
			return err
		}

		compressed := true
		if compressed {
			md.Compression = logpb.ButlerMetadata_ZLIB
		}
		data = w.compressBuf.Bytes()
	}

	// Write metadata frame.
	mdData, err := proto.Marshal(&md)
	if err != nil {
		return fmt.Errorf("butlerproto: failed to marshal Metadata: %s", err)
	}
	_, err = fw.Write(mdData)
	if err != nil {
		return fmt.Errorf("butlerproto: failed to write Metadata frame: %s", err)
	}
	if err := fw.Flush(); err != nil {
		return fmt.Errorf("butlerproto: failed to flush Metadata frame: %s", err)
	}

	// Write data frame.
	_, err = fw.Write(data)
	if err != nil {
		return fmt.Errorf("butlerproto: failed to write data frame: %s", err)
	}
	if err := fw.Flush(); err != nil {
		return fmt.Errorf("butlerproto: failed to flush data frame: %s", err)
	}
	return nil
}

// WriteWith writes a ButlerLogBundle to the supplied Writer.
func (w *Writer) Write(iw io.Writer, b *logpb.ButlerLogBundle) error {
	return w.WriteWith(recordio.NewWriter(iw), b)
}

// WriteWith writes a ButlerLogBundle to the supplied recordio.Writer.
func (w *Writer) WriteWith(fw recordio.Writer, b *logpb.ButlerLogBundle) error {
	data, err := proto.Marshal(b)
	if err != nil {
		return fmt.Errorf("butlerproto: failed to marshal Bundle: %s", err)
	}

	return w.writeData(fw, logpb.ButlerMetadata_ButlerLogBundle, data)
}
<commit_msg>logdog butler: temporary verbosity++ proto marshaling error.<commit_after>// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package butlerproto

import (
	"bytes"
	"compress/zlib"
	"errors"
	"fmt"
	"io"

	"github.com/golang/protobuf/proto"
	"go.chromium.org/luci/common/data/recordio"
	"go.chromium.org/luci/logdog/api/logpb"
	"go.chromium.org/luci/logdog/common/types"
)

const (
	// DefaultCompressThreshold is the byte size threshold for compressing message
	// data. Messages whose byte count is less than or equal to this threshold
	// will not be compressed.
	//
	// This is the value used by Akamai for its compression threshold:
	// "The reasons 860 bytes is the minimum size for compression is twofold:
	// (1) The overhead of compressing an object under 860 bytes outweighs
	// performance gain. (2) Objects under 860 bytes can be transmitted via a
	// single packet anyway, so there isn't a compelling reason to compress them."
	DefaultCompressThreshold = 860
)

// protoBase is the base type of protocol reader/writer objects.
type protoBase struct {
	// maxSize is the maximum Butler protocol data size. By default, it is
	// types.MaxButlerLogBundleSize. However, it can be overridden for testing
	// here.
	maxSize int64
}

func (p *protoBase) getMaxSize() int64 {
	if p.maxSize == 0 {
		return types.MaxButlerLogBundleSize
	}
	return p.maxSize
}

// Reader is a protocol reader instance.
type Reader struct {
	protoBase

	// Metadata is the unpacked ButlerMetadata. It is populated when the
	// metadata has been read.
	Metadata *logpb.ButlerMetadata

	// Bundle is the unpacked ButlerLogBundle. It is populated when the
	// protocol data has been read and the Metadata indicates a ButlerLogBundle
	// type.
	Bundle *logpb.ButlerLogBundle
}

// ReadMetadata reads the metadata header frame.
func (r *Reader) readMetadata(fr recordio.Reader) error {
	data, err := fr.ReadFrameAll()
	if err != nil {
		return err
	}

	md := logpb.ButlerMetadata{}
	if err := proto.Unmarshal(data, &md); err != nil {
		return fmt.Errorf("butlerproto: failed to unmarshal Metadata frame: %s", err)
	}
	r.Metadata = &md
	return nil
}

func (r *Reader) readData(fr recordio.Reader) ([]byte, error) {
	size, br, err := fr.ReadFrame()
	if err != nil {
		return nil, fmt.Errorf("failed to read bundle frame: %s", err)
	}

	// Read the frame through a zlib reader.
	switch r.Metadata.Compression {
	case logpb.ButlerMetadata_NONE:
		break

	case logpb.ButlerMetadata_ZLIB:
		br, err = zlib.NewReader(br)
		if err != nil {
			return nil, fmt.Errorf("failed to initialize zlib reader: %s", err)
		}

	default:
		return nil, fmt.Errorf("unknown compression type: %v", r.Metadata.Compression)
	}

	// Wrap our reader in a limitErrorReader so we don't pull data beyond our
	// soft maximum.
	br = &limitErrorReader{
		Reader: br,
		limit:  r.getMaxSize(),
	}

	buf := bytes.Buffer{}
	buf.Grow(int(size))
	_, err = buf.ReadFrom(br)
	if err != nil {
		return nil, fmt.Errorf("butlerproto: failed to buffer bundle frame: %s", err)
	}
	return buf.Bytes(), nil
}

func (r *Reader) Read(ir io.Reader) error {
	fr := recordio.NewReader(ir, r.getMaxSize())

	// Ensure that we have our Metadata.
	if err := r.readMetadata(fr); err != nil {
		return err
	}

	switch r.Metadata.Type {
	case logpb.ButlerMetadata_ButlerLogBundle:
		data, err := r.readData(fr)
		if err != nil {
			return fmt.Errorf("butlerproto: failed to read Bundle data: %s", err)
		}

		if r.Metadata.ProtoVersion == logpb.Version {
			bundle := logpb.ButlerLogBundle{}
			if err := proto.Unmarshal(data, &bundle); err != nil {
				return fmt.Errorf("butlerproto: failed to unmarshal Bundle frame: %s", err)
			}
			r.Bundle = &bundle
		}
		return nil

	default:
		return fmt.Errorf("butlerproto: unknown data type: %s", r.Metadata.Type)
	}
}

// limitErrorReader is similar to io.LimitReader, except that it returns
// a custom error instead of io.EOF.
//
// This is important, as it allows us to distinguish between the end of
// the compressed reader's data and a limit being hit.
type limitErrorReader struct {
	io.Reader       // underlying reader
	limit     int64 // max bytes remaining
}

func (r *limitErrorReader) Read(p []byte) (int, error) {
	if r.limit <= 0 {
		return 0, errors.New("limit exceeded")
	}
	if int64(len(p)) > r.limit {
		p = p[0:r.limit]
	}
	n, err := r.Reader.Read(p)
	r.limit -= int64(n)
	return n, err
}

// Writer writes Butler messages that the Reader can read.
type Writer struct {
	protoBase

	// ProtoVersion is the protocol version string to use. If empty, the current
	// ProtoVersion will be used.
	ProtoVersion string

	// Compress, if true, allows the Writer to choose to compress data when
	// applicable.
	Compress bool

	// CompressThreshold is the minimum size that data must be in order to
	CompressThreshold int

	compressBuf    bytes.Buffer
	compressWriter *zlib.Writer
}

func (w *Writer) writeData(fw recordio.Writer, t logpb.ButlerMetadata_ContentType, data []byte) error {
	if int64(len(data)) > w.getMaxSize() {
		return fmt.Errorf("butlerproto: serialized size exceeds soft cap (%d > %d)", len(data), w.getMaxSize())
	}

	pv := w.ProtoVersion
	if pv == "" {
		pv = logpb.Version
	}
	md := logpb.ButlerMetadata{
		Type:         t,
		ProtoVersion: pv,
	}

	// If we're configured to compress and the data is below our threshold,
	// compress.
	if w.Compress && len(data) >= w.CompressThreshold {
		w.compressBuf.Reset()
		if w.compressWriter == nil {
			w.compressWriter = zlib.NewWriter(&w.compressBuf)
		} else {
			w.compressWriter.Reset(&w.compressBuf)
		}
		if _, err := w.compressWriter.Write(data); err != nil {
			return err
		}
		if err := w.compressWriter.Close(); err != nil {
			return err
		}

		compressed := true
		if compressed {
			md.Compression = logpb.ButlerMetadata_ZLIB
		}
		data = w.compressBuf.Bytes()
	}

	// Write metadata frame.
	mdData, err := proto.Marshal(&md)
	if err != nil {
		return fmt.Errorf("butlerproto: failed to marshal Metadata: %s", err)
	}
	_, err = fw.Write(mdData)
	if err != nil {
		return fmt.Errorf("butlerproto: failed to write Metadata frame: %s", err)
	}
	if err := fw.Flush(); err != nil {
		return fmt.Errorf("butlerproto: failed to flush Metadata frame: %s", err)
	}

	// Write data frame.
	_, err = fw.Write(data)
	if err != nil {
		return fmt.Errorf("butlerproto: failed to write data frame: %s", err)
	}
	if err := fw.Flush(); err != nil {
		return fmt.Errorf("butlerproto: failed to flush data frame: %s", err)
	}
	return nil
}

// WriteWith writes a ButlerLogBundle to the supplied Writer.
func (w *Writer) Write(iw io.Writer, b *logpb.ButlerLogBundle) error {
	return w.WriteWith(recordio.NewWriter(iw), b)
}

// WriteWith writes a ButlerLogBundle to the supplied recordio.Writer.
func (w *Writer) WriteWith(fw recordio.Writer, b *logpb.ButlerLogBundle) error {
	data, err := proto.Marshal(b)
	if err != nil {
		entries := b.GetEntries()
		// TODO(tandrii, hinoka): leave just error after crbug.com/859995 is fixed.
		if len(entries) > 100 {
			return fmt.Errorf("butlerproto: failed to marshal Bundle of len %d with first 100 entries %s: %s",
				len(entries), entries[:100], err)
		} else {
			return fmt.Errorf("butlerproto: failed to marshal Bundle %s: %s", entries, err)
		}
	}

	return w.writeData(fw, logpb.ButlerMetadata_ButlerLogBundle, data)
}
<|endoftext|>


<commit_before>package command

import (
	"os"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/helper/copy"
	"github.com/mitchellh/cli"
)

func TestVersionCommand_implements(t *testing.T) {
	var _ cli.Command = &VersionCommand{}
}

func TestVersion_json(t *testing.T) {
	fixtureDir := "testdata/providers-schema/basic"
	td := tempDir(t)
	copy.CopyDir(fixtureDir, td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	ui := new(cli.MockUi)

	providerSource, close := newMockProviderSource(t, map[string][]string{
		"test": []string{"1.2.3"},
	})
	defer close()

	m := Meta{
		testingOverrides: metaOverridesForProvider(testProvider()),
		Ui:               ui,
		ProviderSource:   providerSource,
	}

	// `terrafrom init`
	ic := &InitCommand{
		Meta: m,
	}
	if code := ic.Run([]string{}); code != 0 {
		t.Fatalf("init failed\n%s", ui.ErrorWriter)
	}
	// flush the init output from the mock ui
	ui.OutputWriter.Reset()

	// `terraform version -json` without prerelease
	c := &VersionCommand{
		Meta:    m,
		Version: "4.5.6",
	}
	if code := c.Run([]string{"-json"}); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}

	actual := strings.TrimSpace(ui.OutputWriter.String())
	expected := "{\n  \"terraform_version\": \"4.5.6\",\n  \"terraform_revision\": \"\",\n  \"provider_selections\": {\n    \"registry.terraform.io/hashicorp/test\": \"1.2.3\"\n  },\n  \"terraform_outdated\": false\n}"
	if actual != expected {
		t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected)
	}

	// flush the output from the mock ui
	ui.OutputWriter.Reset()

	// `terraform version -json` with prerelease
	c = &VersionCommand{
		Meta:              m,
		Version:           "4.5.6",
		VersionPrerelease: "foo",
	}
	if code := c.Run([]string{"-json"}); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}

	actual = strings.TrimSpace(ui.OutputWriter.String())
	expected = "{\n  \"terraform_version\": \"4.5.6-foo\",\n  \"terraform_revision\": \"\",\n  \"provider_selections\": {\n    \"registry.terraform.io/hashicorp/test\": \"1.2.3\"\n  },\n  \"terraform_outdated\": false\n}"
	if actual != expected {
		t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected)
	}

}
<commit_msg>add test for terraform version<commit_after>package command

import (
	"os"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/helper/copy"
	"github.com/mitchellh/cli"
)

func TestVersionCommand_implements(t *testing.T) {
	var _ cli.Command = &VersionCommand{}
}

func TestVersion(t *testing.T) {
	fixtureDir := "testdata/providers-schema/basic"
	td := tempDir(t)
	copy.CopyDir(fixtureDir, td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	ui := new(cli.MockUi)

	providerSource, close := newMockProviderSource(t, map[string][]string{
		"test": []string{"1.2.3"},
	})
	defer close()

	m := Meta{
		testingOverrides: metaOverridesForProvider(testProvider()),
		Ui:               ui,
		ProviderSource:   providerSource,
	}

	// `terrafrom init`
	ic := &InitCommand{
		Meta: m,
	}
	if code := ic.Run([]string{}); code != 0 {
		t.Fatalf("init failed\n%s", ui.ErrorWriter)
	}
	// flush the init output from the mock ui
	ui.OutputWriter.Reset()

	// `terraform version`
	c := &VersionCommand{
		Meta:              m,
		Version:           "4.5.6",
		VersionPrerelease: "foo",
	}
	if code := c.Run([]string{}); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}

	actual := strings.TrimSpace(ui.OutputWriter.String())
	expected := "Terraform v4.5.6-foo\n+ provider registry.terraform.io/hashicorp/test v1.2.3"
	if actual != expected {
		t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected)
	}

}

func TestVersion_json(t *testing.T) {
	fixtureDir := "testdata/providers-schema/basic"
	td := tempDir(t)
	copy.CopyDir(fixtureDir, td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	ui := new(cli.MockUi)

	providerSource, close := newMockProviderSource(t, map[string][]string{
		"test": []string{"1.2.3"},
	})
	defer close()

	m := Meta{
		testingOverrides: metaOverridesForProvider(testProvider()),
		Ui:               ui,
		ProviderSource:   providerSource,
	}

	// `terrafrom init`
	ic := &InitCommand{
		Meta: m,
	}
	if code := ic.Run([]string{}); code != 0 {
		t.Fatalf("init failed\n%s", ui.ErrorWriter)
	}
	// flush the init output from the mock ui
	ui.OutputWriter.Reset()

	// `terraform version -json` without prerelease
	c := &VersionCommand{
		Meta:    m,
		Version: "4.5.6",
	}
	if code := c.Run([]string{"-json"}); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}

	actual := strings.TrimSpace(ui.OutputWriter.String())
	expected := "{\n  \"terraform_version\": \"4.5.6\",\n  \"terraform_revision\": \"\",\n  \"provider_selections\": {\n    \"registry.terraform.io/hashicorp/test\": \"1.2.3\"\n  },\n  \"terraform_outdated\": false\n}"
	if actual != expected {
		t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected)
	}

	// flush the output from the mock ui
	ui.OutputWriter.Reset()

	// `terraform version -json` with prerelease
	c = &VersionCommand{
		Meta:              m,
		Version:           "4.5.6",
		VersionPrerelease: "foo",
	}
	if code := c.Run([]string{"-json"}); code != 0 {
		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
	}

	actual = strings.TrimSpace(ui.OutputWriter.String())
	expected = "{\n  \"terraform_version\": \"4.5.6-foo\",\n  \"terraform_revision\": \"\",\n  \"provider_selections\": {\n    \"registry.terraform.io/hashicorp/test\": \"1.2.3\"\n  },\n  \"terraform_outdated\": false\n}"
	if actual != expected {
		t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected)
	}

}
<|endoftext|>


<commit_before>package http

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

	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"

	cmds "github.com/jbenet/go-ipfs/commands"
)

const ApiPath = "/api/v0"

func Send(req cmds.Request) (cmds.Response, error) {
	addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API)
	if err != nil {
		return nil, err
	}

	_, host, err := manet.DialArgs(addr)
	if err != nil {
		return nil, err
	}

	url := "http://" + host + ApiPath
	url += "/" + strings.Join(req.Path(), "/")

	// TODO: support other encodings once we have multicodec to decode response
	//       (we shouldn't have to set this here)
	encoding := cmds.JSON
	req.SetOption(cmds.EncShort, encoding)

	query := "?"
	for k, v := range req.Options() {
		query += "&" + k + "=" + v.(string)
	}
	for _, v := range req.Arguments() {
		query += "&arg=" + v
	}

	httpRes, err := http.Post(url+query, "application/octet-stream", req.Stream())
	if err != nil {
		return nil, err
	}

	res := cmds.NewResponse(req)

	contentType := httpRes.Header["Content-Type"][0]
	contentType = strings.Split(contentType, ";")[0]

	if contentType == "application/octet-stream" {
		res.SetValue(httpRes.Body)
		return res, nil
	}

	// TODO: decode based on `encoding`, using multicodec
	dec := json.NewDecoder(httpRes.Body)

	if httpRes.StatusCode >= http.StatusBadRequest {
		e := cmds.Error{}
		err = dec.Decode(&e)
		if err != nil {
			fmt.Println(err)
			return nil, err
		}

		res.SetError(e, e.Code)

	} else {
		var v interface{}
		err = dec.Decode(&v)
		if err != nil {
			fmt.Println(err)
			return nil, err
		}

		res.SetValue(v)
	}

	return res, nil
}
<commit_msg>commands/http: Don't try to parse HTTP response into a Response, just read the output<commit_after>package http

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

	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"

	cmds "github.com/jbenet/go-ipfs/commands"
)

const ApiPath = "/api/v0"

func Send(req cmds.Request) (cmds.Response, error) {
	addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API)
	if err != nil {
		return nil, err
	}

	_, host, err := manet.DialArgs(addr)
	if err != nil {
		return nil, err
	}

	url := "http://" + host + ApiPath
	url += "/" + strings.Join(req.Path(), "/")

	query := "?"
	for k, v := range req.Options() {
		query += "&" + k + "=" + v.(string)
	}
	for _, v := range req.Arguments() {
		query += "&arg=" + v
	}

	httpRes, err := http.Post(url+query, "application/octet-stream", req.Stream())
	if err != nil {
		return nil, err
	}

	// commented out: code to parse HTTP response and turn it into a cmds.Response
	// for now, we are simply reading the data as a stream
	res := cmds.NewResponse(req)
	res.SetValue(httpRes.Body)
	/*res := cmds.NewResponse(req)

	contentType := httpRes.Header["Content-Type"][0]
	contentType = strings.Split(contentType, ";")[0]

	if contentType == "application/octet-stream" {
		res.SetValue(httpRes.Body)
		return res, nil
	}

	// TODO: decode based on `encoding`, using multicodec
	dec := json.NewDecoder(httpRes.Body)

	if httpRes.StatusCode >= http.StatusBadRequest {
		e := cmds.Error{}
		err = dec.Decode(&e)
		if err != nil {
			fmt.Println(err)
			return nil, err
		}

		res.SetError(e, e.Code)

	} else {
		var v interface{}
		err = dec.Decode(&v)
		if err != nil {
			fmt.Println(err)
			return nil, err
		}

		res.SetValue(v)
	}*/

	return res, nil
}
<|endoftext|>


<commit_before>package firewall

import (
	"net"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/firewall/drivers"
)

// Firewall represents an LXD firewall.
type Firewall interface {
	// Lower-level Functions
	NetworkClear(family drivers.Family, table drivers.Table, comment string) error
	InstanceClear(family drivers.Family, table drivers.Table, comment string) error
	VerifyIPv6Module() error

	// Proxy Functions
	InstanceProxySetupNAT(family drivers.Family, connType, address, port string, destAddr net.IP, destPort string, comment string) error

	// NIC Bridged Functions
	InstanceNicBridgedRemoveFilters(m deviceConfig.Device, ipv4 net.IP, ipv6 net.IP) error
	InstanceNicBridgedSetFilters(m deviceConfig.Device, ipv4 net.IP, ipv6 net.IP, comment string) error

	// Network Functions
	NetworkSetupAllowForwarding(family drivers.Family, name string, actionType drivers.Action) error
	NetworkSetupNAT(family drivers.Family, name string, location drivers.Location, args ...string) error
	NetworkSetupIPv4DNSOverrides(name string) error
	NetworkSetupIPv4DHCPWorkaround(name string) error
	NetworkSetupIPv6DNSOverrides(name string) error
	NetworkSetupTunnelNAT(name string, location drivers.Location, overlaySubnet net.IPNet) error
}
<commit_msg>lxd/firewall/firewall/interface: Reworks firewall interface<commit_after>package firewall

import (
	"net"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
)

// Firewall represents an LXD firewall.
type Firewall interface {
	NetworkSetupForwardingPolicy(networkName string, ipVersion uint, allow bool) error
	NetworkSetupOutboundNAT(networkName string, subnet *net.IPNet, srcIP net.IP, append bool) error
	NetworkSetupDHCPDNSAccess(networkName string, ipVersion uint) error
	NetworkSetupDHCPv4Checksum(networkName string) error
	NetworkClear(networkName string, ipVersion uint) error

	InstanceSetupBridgeFilter(projectName, instanceName, deviceName, parentName, hostName, hwAddr string, IPv4, IPv6 net.IP) error
	InstanceClearBridgeFilter(projectName, instanceName, deviceName, parentName, hostName, hwAddr string, IPv4, IPv6 net.IP) error

	InstanceSetupProxyNAT(projectName, instanceName, deviceName string, listen, connect *deviceConfig.ProxyAddress) error
	InstanceClearProxyNAT(projectName, instanceName, deviceName string) error
}
<|endoftext|>


<commit_before>package firewall

import (
	"net"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	drivers "github.com/lxc/lxd/lxd/firewall/drivers"
)

// Firewall represents a LXD firewall.
type Firewall interface {
	String() string
	Compat() (bool, error)

	NetworkSetup(networkName string, opts drivers.Opts) error
	NetworkClear(networkName string, delete bool, ipVersions []uint) error
	NetworkApplyACLRules(networkName string, rules []drivers.ACLRule) error

	InstanceSetupBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4 net.IP, IPv6 net.IP, parentManaged bool) error
	InstanceClearBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4 net.IP, IPv6 net.IP) error

	InstanceSetupProxyNAT(projectName string, instanceName string, deviceName string, listen *deviceConfig.ProxyAddress, connect *deviceConfig.ProxyAddress) error
	InstanceClearProxyNAT(projectName string, instanceName string, deviceName string) error

	InstanceSetupRPFilter(projectName string, instanceName string, deviceName string, hostName string) error
	InstanceClearRPFilter(projectName string, instanceName string, deviceName string) error
}
<commit_msg>lxd/firewall/firewall/interface: Updates InstanceSetupProxyNAT to accept AddressForward<commit_after>package firewall

import (
	"net"

	drivers "github.com/lxc/lxd/lxd/firewall/drivers"
)

// Firewall represents a LXD firewall.
type Firewall interface {
	String() string
	Compat() (bool, error)

	NetworkSetup(networkName string, opts drivers.Opts) error
	NetworkClear(networkName string, delete bool, ipVersions []uint) error
	NetworkApplyACLRules(networkName string, rules []drivers.ACLRule) error

	InstanceSetupBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4 net.IP, IPv6 net.IP, parentManaged bool) error
	InstanceClearBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4 net.IP, IPv6 net.IP) error

	InstanceSetupProxyNAT(projectName string, instanceName string, deviceName string, forward *drivers.AddressForward) error
	InstanceClearProxyNAT(projectName string, instanceName string, deviceName string) error

	InstanceSetupRPFilter(projectName string, instanceName string, deviceName string, hostName string) error
	InstanceClearRPFilter(projectName string, instanceName string, deviceName string) error
}
<|endoftext|>


<commit_before>package daemon

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
)

type Daemon interface {
	Stop()
}

func Main(start func(args []string, errorSink ErrorSink) Daemon) {
	sigs := make(chan os.Signal, 2)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	errorSink := NewErrorSink()

	d := start(os.Args, errorSink)
	exitCode := 0
	var exitSignal os.Signal

	select {
	case err := <-errorSink:
		if err != flag.ErrHelp {
			fmt.Fprintln(os.Stderr, err)
			exitCode = 1
		}
	case exitSignal = <-sigs:
	}

	d.Stop()

	if sig, ok := exitSignal.(syscall.Signal); ok {
		// Now we have cleaned up, re-kill the process with
		// the signal in order to produce a signal exit
		// status:
		signal.Reset(sig)
		syscall.Kill(syscall.Getpid(), sig)
	} else if exitSignal != nil {
		fmt.Fprintln(os.Stderr, "Exiting with signal ", sig)
	}

	os.Exit(exitCode)
}
<commit_msg>All your cpu are belong to us<commit_after>package daemon

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

type Daemon interface {
	Stop()
}

func Main(start func(args []string, errorSink ErrorSink) Daemon) {
	runtime.GOMAXPROCS(runtime.NumCPU())

	sigs := make(chan os.Signal, 2)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	errorSink := NewErrorSink()

	d := start(os.Args, errorSink)
	exitCode := 0
	var exitSignal os.Signal

	select {
	case err := <-errorSink:
		if err != flag.ErrHelp {
			fmt.Fprintln(os.Stderr, err)
			exitCode = 1
		}
	case exitSignal = <-sigs:
	}

	d.Stop()

	if sig, ok := exitSignal.(syscall.Signal); ok {
		// Now we have cleaned up, re-kill the process with
		// the signal in order to produce a signal exit
		// status:
		signal.Reset(sig)
		syscall.Kill(syscall.Getpid(), sig)
	} else if exitSignal != nil {
		fmt.Fprintln(os.Stderr, "Exiting with signal ", sig)
	}

	os.Exit(exitCode)
}
<|endoftext|>


<commit_before>package common

import (
	"context"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	getter "github.com/hashicorp/go-getter"
	urlhelper "github.com/hashicorp/go-getter/helper/url"
	"github.com/hashicorp/packer/common/filelock"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

// StepDownload downloads a remote file using the download client within
// this package. This step handles setting up the download configuration,
// progress reporting, interrupt handling, etc.
//
// Uses:
//   cache packer.Cache
//   ui    packer.Ui
type StepDownload struct {
	// The checksum and the type of the checksum for the download
	Checksum     string
	ChecksumType string

	// A short description of the type of download being done. Example:
	// "ISO" or "Guest Additions"
	Description string

	// The name of the key where the final path of the ISO will be put
	// into the state.
	ResultKey string

	// The path where the result should go, otherwise it goes to the
	// cache directory.
	TargetPath string

	// A list of URLs to attempt to download this thing.
	Url []string

	// Extension is the extension to force for the file that is downloaded.
	// Some systems require a certain extension. If this isn't set, the
	// extension on the URL is used. Otherwise, this will be forced
	// on the downloaded file for every URL.
	Extension string
}

func (s *StepDownload) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	defer log.Printf("Leaving retrieve loop for %s", s.Description)

	ui := state.Get("ui").(packer.Ui)
	ui.Say(fmt.Sprintf("Retrieving %s", s.Description))

	var errs []error
	for _, source := range s.Url {
		if ctx.Err() != nil {
			state.Put("error", fmt.Errorf("Download cancelled: %v", errs))
			return multistep.ActionHalt
		}
		ui.Say(fmt.Sprintf("Trying %s", source))
		var err error
		var dst string
		if s.Description == "OVF/OVA" && strings.HasSuffix(source, ".ovf") {
			// TODO(adrien): make go-getter allow using files in place.
			// ovf files usually point to a file in the same directory, so
			// using them in place is the only way.
			ui.Say(fmt.Sprintf("Using ovf inplace"))
			dst = source
		} else {
			dst, err = s.download(ctx, ui, source)
		}
		if err == nil {
			state.Put(s.ResultKey, dst)
			return multistep.ActionContinue
		}
		// may be another url will work
		errs = append(errs, err)
	}

	err := fmt.Errorf("error downloading %s: %v", s.Description, errs)
	state.Put("error", err)
	ui.Error(err.Error())
	return multistep.ActionHalt
}

var (
	getters = getter.Getters
)

func init() {
	if runtime.GOOS == "windows" {
		getters["file"] = &getter.FileGetter{
			// always copy local files instead of symlinking to fix GH-7534. The
			// longer term fix for this would be to change the go-getter so that it
			// can leave the source file where it is & tell us where it is.
			Copy: true,
		}
		getters["smb"] = &getter.FileGetter{
			Copy: true,
		}
	}
}

func (s *StepDownload) download(ctx context.Context, ui packer.Ui, source string) (string, error) {
	if runtime.GOOS == "windows" {
		// Check that the user specified a UNC path, and promote it to an smb:// uri.
		if strings.HasPrefix(source, "\\\\") && len(source) > 2 && source[2] != '?' {
			source = filepath.ToSlash(source[2:])
			source = fmt.Sprintf("smb://%s", source)
		}
	}

	u, err := urlhelper.Parse(source)
	if err != nil {
		return "", fmt.Errorf("url parse: %s", err)
	}
	if checksum := u.Query().Get("checksum"); checksum != "" {
		s.Checksum = checksum
	}
	if s.ChecksumType != "" && s.ChecksumType != "none" {
		// add checksum to url query params as go getter will checksum for us
		q := u.Query()
		q.Set("checksum", s.ChecksumType+":"+s.Checksum)
		u.RawQuery = q.Encode()
	} else if s.Checksum != "" {
		q := u.Query()
		q.Set("checksum", s.Checksum)
		u.RawQuery = q.Encode()
	}

	targetPath := s.TargetPath
	if targetPath == "" {
		// store file under sha1(hash) if set
		// hash can sometimes be a checksum url
		// otherwise, use sha1(source_url)
		var shaSum [20]byte
		if s.Checksum != "" {
			shaSum = sha1.Sum([]byte(s.Checksum))
		} else {
			shaSum = sha1.Sum([]byte(u.String()))
		}
		targetPath = hex.EncodeToString(shaSum[:])
		if s.Extension != "" {
			targetPath += "." + s.Extension
		}
	}
	targetPath, err = packer.CachePath(targetPath)
	if err != nil {
		return "", fmt.Errorf("CachePath: %s", err)
	}
	lockFile := targetPath + ".lock"

	log.Printf("Acquiring lock for: %s (%s)", u.String(), lockFile)
	lock := filelock.New(lockFile)
	lock.Lock()
	defer lock.Unlock()

	wd, err := os.Getwd()
	if err != nil {
		log.Printf("get working directory: %v", err)
		// here we ignore the error in case the
		// working directory is not needed.
		// It would be better if the go-getter
		// could guess it only in cases it is
		// necessary.
	}
	src := u.String()
	if u.Scheme == "" || strings.ToLower(u.Scheme) == "file" {
		// If a local filepath, then we need to preprocess to make sure the
		// path doens't have any multiple successive path separators; if it
		// does, go-getter will read this as a specialized go-getter-specific
		// subdirectory command, which it most likely isn't.
		src = filepath.Clean(u.String())
		if _, err := os.Stat(filepath.Clean(u.Path)); err != nil {
			// Cleaned path isn't present on system so it must be some other
			// scheme. Don't error right away; see if go-getter can figure it
			// out.
			src = u.String()
		}
	}

	ui.Say(fmt.Sprintf("Trying %s", u.String()))
	gc := getter.Client{
		Ctx:              ctx,
		Dst:              targetPath,
		Src:              src,
		ProgressListener: ui,
		Pwd:              wd,
		Dir:              false,
		Getters:          getters,
	}

	switch err := gc.Get(); err.(type) {
	case nil: // success !
		ui.Say(fmt.Sprintf("%s => %s", u.String(), targetPath))
		return targetPath, nil
	case *getter.ChecksumError:
		ui.Say(fmt.Sprintf("Checksum did not match, removing %s", targetPath))
		if err := os.Remove(targetPath); err != nil {
			ui.Error(fmt.Sprintf("Failed to remove cache file. Please remove manually: %s", targetPath))
		}
		return "", err
	default:
		ui.Say(fmt.Sprintf("Download failed %s", err))
		return "", err
	}
}

func (s *StepDownload) Cleanup(multistep.StateBag) {}
<commit_msg>Don't cache when TargetPath is non-nil; this messes up use of the iso_target_path iso template option.<commit_after>package common

import (
	"context"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	getter "github.com/hashicorp/go-getter"
	urlhelper "github.com/hashicorp/go-getter/helper/url"
	"github.com/hashicorp/packer/common/filelock"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

// StepDownload downloads a remote file using the download client within
// this package. This step handles setting up the download configuration,
// progress reporting, interrupt handling, etc.
//
// Uses:
//   cache packer.Cache
//   ui    packer.Ui
type StepDownload struct {
	// The checksum and the type of the checksum for the download
	Checksum     string
	ChecksumType string

	// A short description of the type of download being done. Example:
	// "ISO" or "Guest Additions"
	Description string

	// The name of the key where the final path of the ISO will be put
	// into the state.
	ResultKey string

	// The path where the result should go, otherwise it goes to the
	// cache directory.
	TargetPath string

	// A list of URLs to attempt to download this thing.
	Url []string

	// Extension is the extension to force for the file that is downloaded.
	// Some systems require a certain extension. If this isn't set, the
	// extension on the URL is used. Otherwise, this will be forced
	// on the downloaded file for every URL.
	Extension string
}

func (s *StepDownload) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	defer log.Printf("Leaving retrieve loop for %s", s.Description)

	ui := state.Get("ui").(packer.Ui)
	ui.Say(fmt.Sprintf("Retrieving %s", s.Description))

	var errs []error
	for _, source := range s.Url {
		if ctx.Err() != nil {
			state.Put("error", fmt.Errorf("Download cancelled: %v", errs))
			return multistep.ActionHalt
		}
		ui.Say(fmt.Sprintf("Trying %s", source))
		var err error
		var dst string
		if s.Description == "OVF/OVA" && strings.HasSuffix(source, ".ovf") {
			// TODO(adrien): make go-getter allow using files in place.
			// ovf files usually point to a file in the same directory, so
			// using them in place is the only way.
			ui.Say(fmt.Sprintf("Using ovf inplace"))
			dst = source
		} else {
			dst, err = s.download(ctx, ui, source)
		}
		if err == nil {
			state.Put(s.ResultKey, dst)
			return multistep.ActionContinue
		}
		// may be another url will work
		errs = append(errs, err)
	}

	err := fmt.Errorf("error downloading %s: %v", s.Description, errs)
	state.Put("error", err)
	ui.Error(err.Error())
	return multistep.ActionHalt
}

var (
	getters = getter.Getters
)

func init() {
	if runtime.GOOS == "windows" {
		getters["file"] = &getter.FileGetter{
			// always copy local files instead of symlinking to fix GH-7534. The
			// longer term fix for this would be to change the go-getter so that it
			// can leave the source file where it is & tell us where it is.
			Copy: true,
		}
		getters["smb"] = &getter.FileGetter{
			Copy: true,
		}
	}
}

func (s *StepDownload) download(ctx context.Context, ui packer.Ui, source string) (string, error) {
	if runtime.GOOS == "windows" {
		// Check that the user specified a UNC path, and promote it to an smb:// uri.
		if strings.HasPrefix(source, "\\\\") && len(source) > 2 && source[2] != '?' {
			source = filepath.ToSlash(source[2:])
			source = fmt.Sprintf("smb://%s", source)
		}
	}

	u, err := urlhelper.Parse(source)
	if err != nil {
		return "", fmt.Errorf("url parse: %s", err)
	}
	if checksum := u.Query().Get("checksum"); checksum != "" {
		s.Checksum = checksum
	}
	if s.ChecksumType != "" && s.ChecksumType != "none" {
		// add checksum to url query params as go getter will checksum for us
		q := u.Query()
		q.Set("checksum", s.ChecksumType+":"+s.Checksum)
		u.RawQuery = q.Encode()
	} else if s.Checksum != "" {
		q := u.Query()
		q.Set("checksum", s.Checksum)
		u.RawQuery = q.Encode()
	}

	targetPath := s.TargetPath
	if targetPath == "" {
		// store file under sha1(hash) if set
		// hash can sometimes be a checksum url
		// otherwise, use sha1(source_url)
		var shaSum [20]byte
		if s.Checksum != "" {
			shaSum = sha1.Sum([]byte(s.Checksum))
		} else {
			shaSum = sha1.Sum([]byte(u.String()))
		}
		targetPath = hex.EncodeToString(shaSum[:])
		if s.Extension != "" {
			targetPath += "." + s.Extension
		}
		targetPath, err = packer.CachePath(targetPath)
	}

	lockFile := targetPath + ".lock"

	log.Printf("Acquiring lock for: %s (%s)", u.String(), lockFile)
	lock := filelock.New(lockFile)
	lock.Lock()
	defer lock.Unlock()

	wd, err := os.Getwd()
	if err != nil {
		log.Printf("get working directory: %v", err)
		// here we ignore the error in case the
		// working directory is not needed.
		// It would be better if the go-getter
		// could guess it only in cases it is
		// necessary.
	}
	src := u.String()
	if u.Scheme == "" || strings.ToLower(u.Scheme) == "file" {
		// If a local filepath, then we need to preprocess to make sure the
		// path doens't have any multiple successive path separators; if it
		// does, go-getter will read this as a specialized go-getter-specific
		// subdirectory command, which it most likely isn't.
		src = filepath.Clean(u.String())
		if _, err := os.Stat(filepath.Clean(u.Path)); err != nil {
			// Cleaned path isn't present on system so it must be some other
			// scheme. Don't error right away; see if go-getter can figure it
			// out.
			src = u.String()
		}
	}

	ui.Say(fmt.Sprintf("Trying %s", u.String()))
	gc := getter.Client{
		Ctx:              ctx,
		Dst:              targetPath,
		Src:              src,
		ProgressListener: ui,
		Pwd:              wd,
		Dir:              false,
		Getters:          getters,
	}

	switch err := gc.Get(); err.(type) {
	case nil: // success !
		ui.Say(fmt.Sprintf("%s => %s", u.String(), targetPath))
		return targetPath, nil
	case *getter.ChecksumError:
		ui.Say(fmt.Sprintf("Checksum did not match, removing %s", targetPath))
		if err := os.Remove(targetPath); err != nil {
			ui.Error(fmt.Sprintf("Failed to remove cache file. Please remove manually: %s", targetPath))
		}
		return "", err
	default:
		ui.Say(fmt.Sprintf("Download failed %s", err))
		return "", err
	}
}

func (s *StepDownload) Cleanup(multistep.StateBag) {}
<|endoftext|>


<commit_before>package digioceanserverless

func (digioceanserverless *Digioceanserverless) Getfunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}

func (digioceanserverless *Digioceanserverless) Createfunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}

func (digioceanserverless *Digioceanserverless) Callfunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}

func (digioceanserverless *Digioceanserverless) Listfunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}

func (digioceanserverless *Digioceanserverless) Deletefunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}
<commit_msg>codacy code style<commit_after>package digioceanserverless

//Getfunction  describe serverless function.
func (digioceanserverless *Digioceanserverless) Getfunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}

//Createfunction  create serverless function.
func (digioceanserverless *Digioceanserverless) Createfunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}

//Callfunction  call serverless function.
func (digioceanserverless *Digioceanserverless) Callfunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}

//Listfunction  list serverless function.
func (digioceanserverless *Digioceanserverless) Listfunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}

//Deletefunction  delete serverless function.
func (digioceanserverless *Digioceanserverless) Deletefunction(request interface{}) (resp interface{}, err error) {

	return resp, err
}
<|endoftext|>


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

package prometheus

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

var instLabels = []string{"method", "code"}

type nower interface {
	Now() time.Time
}

type nowFunc func() time.Time

func (n nowFunc) Now() time.Time {
	return n()
}

var now nower = nowFunc(func() time.Time {
	return time.Now()
})

func nowSeries(t ...time.Time) nower {
	return nowFunc(func() time.Time {
		defer func() {
			t = t[1:]
		}()

		return t[0]
	})
}

// InstrumentHandler wraps the given HTTP handler for instrumentation. It
// registers four metric vector collectors (if not already done) and reports
// http metrics to the (newly or already) registered collectors:
// http_requests_total (CounterVec), http_request_duration_microseconds
// (SummaryVec), http_request_size_bytes (SummaryVec), http_response_size_bytes
// (SummaryVec). Each has three labels: handler, method, code. The value of the
// handler label is set by the handlerName parameter of this function.
func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
	return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
}

// InstrumentHandlerFunc wraps the given function for instrumentation. It
// registers four metric vector collectors (if not already done) and reports
// http metrics to the (newly or already) registered collectors:
// http_requests_total (CounterVec), http_request_duration_microseconds
// (SummaryVec), http_request_size_bytes (SummaryVec), http_response_size_bytes
// (SummaryVec). Each has three labels: handler, method, code. The value of the
// handler label is set by the handlerName parameter of this function.
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return InstrumentHandlerFuncWithOpts(
		SummaryOpts{
			Subsystem:   "http",
			ConstLabels: Labels{"handler": handlerName},
		},
		handlerFunc,
	)
}

// InstrumentHandlerWithOpts works like InstrumentHandler but provides more
// flexibility (at the cost of a more complex call syntax). As
// InstrumentHandler, this function registers four metric vector collectors, but
// it uses the provided SummaryOpts to create them. However, the fields "Name"
// and "Help" in the SummaryOpts are ignored. "Name" is replaced by
// "requests_total", "request_duration_microseconds", "request_size_bytes", and
// "response_size_bytes", respectively. "Help" is replaced by an appropriate
// help string. The names of the variable labels of the vector collectors are
// "method" (get, post, etc.), and "code" (HTTP status code).
//
// If InstrumentHandlerWithOpts is called as follows, it mimics exactly the
// behavior of InstrumentHandler:
//
//     prometheus.InstrumentHandlerWithOpts(
//         prometheus.SummaryOpts{
//              Subsystem:   "http",
//              ConstLabels: prometheus.Labels{"handler": handlerName},
//         },
//         handler,
//     )
//
// Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it
// cannot use SummaryOpts. Instead, a CounterOpts struct is created internally,
// and all its fields are set to the equally named fields in the provided
// SummaryOpts.
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
	return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
}

// InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc but provides
// more flexibility (at the cost of a more complex call syntax). See
// InstrumentHandlerWithOpts for details how the provided SummaryOpts are used.
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	reqCnt := NewCounterVec(
		CounterOpts{
			Namespace:   opts.Namespace,
			Subsystem:   opts.Subsystem,
			Name:        "requests_total",
			Help:        "Total number of HTTP requests made.",
			ConstLabels: opts.ConstLabels,
		},
		instLabels,
	)

	opts.Name = "request_duration_microseconds"
	opts.Help = "The HTTP request latencies in microseconds."
	reqDur := NewSummaryVec(opts, instLabels)

	opts.Name = "request_size_bytes"
	opts.Help = "The HTTP request sizes in bytes."
	reqSz := NewSummaryVec(opts, instLabels)

	opts.Name = "response_size_bytes"
	opts.Help = "The HTTP response sizes in bytes."
	resSz := NewSummaryVec(opts, instLabels)

	regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec)
	regReqDur := MustRegisterOrGet(reqDur).(*SummaryVec)
	regReqSz := MustRegisterOrGet(reqSz).(*SummaryVec)
	regResSz := MustRegisterOrGet(resSz).(*SummaryVec)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		now := time.Now()

		delegate := &responseWriterDelegator{ResponseWriter: w}
		out := make(chan int)
		go computeApproximateRequestSize(r, out)
		handlerFunc(delegate, r)

		elapsed := float64(time.Since(now)) / float64(time.Microsecond)

		method := sanitizeMethod(r.Method)
		code := sanitizeCode(delegate.status)
		regReqCnt.WithLabelValues(method, code).Inc()
		regReqDur.WithLabelValues(method, code).Observe(elapsed)
		regResSz.WithLabelValues(method, code).Observe(float64(delegate.written))
		regReqSz.WithLabelValues(method, code).Observe(float64(<-out))
	})
}

func computeApproximateRequestSize(r *http.Request, out chan int) {
	s := len(r.Method)
	if r.URL != nil {
		s += len(r.URL.String())
	}
	s += len(r.Proto)
	for name, values := range r.Header {
		s += len(name)
		for _, value := range values {
			s += len(value)
		}
	}
	s += len(r.Host)

	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.

	if r.ContentLength != -1 {
		s += int(r.ContentLength)
	}
	out <- s
}

type responseWriterDelegator struct {
	http.ResponseWriter

	handler, method string
	status          int
	written         int
	wroteHeader     bool
}

func (r *responseWriterDelegator) WriteHeader(code int) {
	r.status = code
	r.wroteHeader = true
	r.ResponseWriter.WriteHeader(code)
}

func (r *responseWriterDelegator) Write(b []byte) (int, error) {
	if !r.wroteHeader {
		r.WriteHeader(http.StatusOK)
	}
	n, err := r.ResponseWriter.Write(b)
	r.written += n
	return n, err
}

func sanitizeMethod(m string) string {
	switch m {
	case "GET", "get":
		return "get"
	case "PUT", "put":
		return "put"
	case "HEAD", "head":
		return "head"
	case "POST", "post":
		return "post"
	case "DELETE", "delete":
		return "delete"
	case "CONNECT", "connect":
		return "connect"
	case "OPTIONS", "options":
		return "options"
	case "NOTIFY", "notify":
		return "notify"
	default:
		return strings.ToLower(m)
	}
}

func sanitizeCode(s int) string {
	switch s {
	case 100:
		return "100"
	case 101:
		return "101"

	case 200:
		return "200"
	case 201:
		return "201"
	case 202:
		return "202"
	case 203:
		return "203"
	case 204:
		return "204"
	case 205:
		return "205"
	case 206:
		return "206"

	case 300:
		return "300"
	case 301:
		return "301"
	case 302:
		return "302"
	case 304:
		return "304"
	case 305:
		return "305"
	case 307:
		return "307"

	case 400:
		return "400"
	case 401:
		return "401"
	case 402:
		return "402"
	case 403:
		return "403"
	case 404:
		return "404"
	case 405:
		return "405"
	case 406:
		return "406"
	case 407:
		return "407"
	case 408:
		return "408"
	case 409:
		return "409"
	case 410:
		return "410"
	case 411:
		return "411"
	case 412:
		return "412"
	case 413:
		return "413"
	case 414:
		return "414"
	case 415:
		return "415"
	case 416:
		return "416"
	case 417:
		return "417"
	case 418:
		return "418"

	case 500:
		return "500"
	case 501:
		return "501"
	case 502:
		return "502"
	case 503:
		return "503"
	case 504:
		return "504"
	case 505:
		return "505"

	case 428:
		return "428"
	case 429:
		return "429"
	case 431:
		return "431"
	case 511:
		return "511"

	default:
		return strconv.Itoa(s)
	}
}
<commit_msg>Fix a race condition in the http instrumentation.<commit_after>// Copyright 2014 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package prometheus

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

var instLabels = []string{"method", "code"}

type nower interface {
	Now() time.Time
}

type nowFunc func() time.Time

func (n nowFunc) Now() time.Time {
	return n()
}

var now nower = nowFunc(func() time.Time {
	return time.Now()
})

func nowSeries(t ...time.Time) nower {
	return nowFunc(func() time.Time {
		defer func() {
			t = t[1:]
		}()

		return t[0]
	})
}

// InstrumentHandler wraps the given HTTP handler for instrumentation. It
// registers four metric vector collectors (if not already done) and reports
// http metrics to the (newly or already) registered collectors:
// http_requests_total (CounterVec), http_request_duration_microseconds
// (SummaryVec), http_request_size_bytes (SummaryVec), http_response_size_bytes
// (SummaryVec). Each has three labels: handler, method, code. The value of the
// handler label is set by the handlerName parameter of this function.
func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
	return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
}

// InstrumentHandlerFunc wraps the given function for instrumentation. It
// registers four metric vector collectors (if not already done) and reports
// http metrics to the (newly or already) registered collectors:
// http_requests_total (CounterVec), http_request_duration_microseconds
// (SummaryVec), http_request_size_bytes (SummaryVec), http_response_size_bytes
// (SummaryVec). Each has three labels: handler, method, code. The value of the
// handler label is set by the handlerName parameter of this function.
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return InstrumentHandlerFuncWithOpts(
		SummaryOpts{
			Subsystem:   "http",
			ConstLabels: Labels{"handler": handlerName},
		},
		handlerFunc,
	)
}

// InstrumentHandlerWithOpts works like InstrumentHandler but provides more
// flexibility (at the cost of a more complex call syntax). As
// InstrumentHandler, this function registers four metric vector collectors, but
// it uses the provided SummaryOpts to create them. However, the fields "Name"
// and "Help" in the SummaryOpts are ignored. "Name" is replaced by
// "requests_total", "request_duration_microseconds", "request_size_bytes", and
// "response_size_bytes", respectively. "Help" is replaced by an appropriate
// help string. The names of the variable labels of the vector collectors are
// "method" (get, post, etc.), and "code" (HTTP status code).
//
// If InstrumentHandlerWithOpts is called as follows, it mimics exactly the
// behavior of InstrumentHandler:
//
//     prometheus.InstrumentHandlerWithOpts(
//         prometheus.SummaryOpts{
//              Subsystem:   "http",
//              ConstLabels: prometheus.Labels{"handler": handlerName},
//         },
//         handler,
//     )
//
// Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it
// cannot use SummaryOpts. Instead, a CounterOpts struct is created internally,
// and all its fields are set to the equally named fields in the provided
// SummaryOpts.
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
	return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
}

// InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc but provides
// more flexibility (at the cost of a more complex call syntax). See
// InstrumentHandlerWithOpts for details how the provided SummaryOpts are used.
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	reqCnt := NewCounterVec(
		CounterOpts{
			Namespace:   opts.Namespace,
			Subsystem:   opts.Subsystem,
			Name:        "requests_total",
			Help:        "Total number of HTTP requests made.",
			ConstLabels: opts.ConstLabels,
		},
		instLabels,
	)

	opts.Name = "request_duration_microseconds"
	opts.Help = "The HTTP request latencies in microseconds."
	reqDur := NewSummaryVec(opts, instLabels)

	opts.Name = "request_size_bytes"
	opts.Help = "The HTTP request sizes in bytes."
	reqSz := NewSummaryVec(opts, instLabels)

	opts.Name = "response_size_bytes"
	opts.Help = "The HTTP response sizes in bytes."
	resSz := NewSummaryVec(opts, instLabels)

	regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec)
	regReqDur := MustRegisterOrGet(reqDur).(*SummaryVec)
	regReqSz := MustRegisterOrGet(reqSz).(*SummaryVec)
	regResSz := MustRegisterOrGet(resSz).(*SummaryVec)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		now := time.Now()

		delegate := &responseWriterDelegator{ResponseWriter: w}
		out := make(chan int)
		urlLen := 0
		if r.URL != nil {
			urlLen = len(r.URL.String())
		}
		go computeApproximateRequestSize(r, out, urlLen)
		handlerFunc(delegate, r)

		elapsed := float64(time.Since(now)) / float64(time.Microsecond)

		method := sanitizeMethod(r.Method)
		code := sanitizeCode(delegate.status)
		regReqCnt.WithLabelValues(method, code).Inc()
		regReqDur.WithLabelValues(method, code).Observe(elapsed)
		regResSz.WithLabelValues(method, code).Observe(float64(delegate.written))
		regReqSz.WithLabelValues(method, code).Observe(float64(<-out))
	})
}

func computeApproximateRequestSize(r *http.Request, out chan int, s int) {
	s += len(r.Method)
	s += len(r.Proto)
	for name, values := range r.Header {
		s += len(name)
		for _, value := range values {
			s += len(value)
		}
	}
	s += len(r.Host)

	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.

	if r.ContentLength != -1 {
		s += int(r.ContentLength)
	}
	out <- s
}

type responseWriterDelegator struct {
	http.ResponseWriter

	handler, method string
	status          int
	written         int
	wroteHeader     bool
}

func (r *responseWriterDelegator) WriteHeader(code int) {
	r.status = code
	r.wroteHeader = true
	r.ResponseWriter.WriteHeader(code)
}

func (r *responseWriterDelegator) Write(b []byte) (int, error) {
	if !r.wroteHeader {
		r.WriteHeader(http.StatusOK)
	}
	n, err := r.ResponseWriter.Write(b)
	r.written += n
	return n, err
}

func sanitizeMethod(m string) string {
	switch m {
	case "GET", "get":
		return "get"
	case "PUT", "put":
		return "put"
	case "HEAD", "head":
		return "head"
	case "POST", "post":
		return "post"
	case "DELETE", "delete":
		return "delete"
	case "CONNECT", "connect":
		return "connect"
	case "OPTIONS", "options":
		return "options"
	case "NOTIFY", "notify":
		return "notify"
	default:
		return strings.ToLower(m)
	}
}

func sanitizeCode(s int) string {
	switch s {
	case 100:
		return "100"
	case 101:
		return "101"

	case 200:
		return "200"
	case 201:
		return "201"
	case 202:
		return "202"
	case 203:
		return "203"
	case 204:
		return "204"
	case 205:
		return "205"
	case 206:
		return "206"

	case 300:
		return "300"
	case 301:
		return "301"
	case 302:
		return "302"
	case 304:
		return "304"
	case 305:
		return "305"
	case 307:
		return "307"

	case 400:
		return "400"
	case 401:
		return "401"
	case 402:
		return "402"
	case 403:
		return "403"
	case 404:
		return "404"
	case 405:
		return "405"
	case 406:
		return "406"
	case 407:
		return "407"
	case 408:
		return "408"
	case 409:
		return "409"
	case 410:
		return "410"
	case 411:
		return "411"
	case 412:
		return "412"
	case 413:
		return "413"
	case 414:
		return "414"
	case 415:
		return "415"
	case 416:
		return "416"
	case 417:
		return "417"
	case 418:
		return "418"

	case 500:
		return "500"
	case 501:
		return "501"
	case 502:
		return "502"
	case 503:
		return "503"
	case 504:
		return "504"
	case 505:
		return "505"

	case 428:
		return "428"
	case 429:
		return "429"
	case 431:
		return "431"
	case 511:
		return "511"

	default:
		return strconv.Itoa(s)
	}
}
<|endoftext|>


<commit_before>// This downloads and installs the protobuf compiler

//go:generate rm -rf include
//go:generate rm -rf bin
//go:generate rm -rf readme.txt
//go:generate rm -rf protoc.zip
//go:generate curl https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip -o protoc.zip -L
//go:generate unzip protoc.zip -d .
package protoc
<commit_msg>Switch protoc to v3.8<commit_after>// This downloads and installs the protobuf compiler

//go:generate rm -rf include
//go:generate rm -rf bin
//go:generate rm -rf readme.txt
//go:generate rm -rf protoc.zip
//go:generate curl https://github.com/google/protobuf/releases/download/v3.8.0/protoc-3.8.0-linux-x86_64.zip -o protoc.zip -L
//go:generate unzip protoc.zip -d .
package protoc
<|endoftext|>


<commit_before>package encryptor

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"errors"

	"github.com/silenceper/wechat/v2/miniprogram/context"
)

//Encryptor struct
type Encryptor struct {
	*context.Context
}

//NewEncryptor 实例
func NewEncryptor(context *context.Context) *Encryptor {
	basic := new(Encryptor)
	basic.Context = context
	return basic
}

var (
	// ErrAppIDNotMatch appid不匹配
	ErrAppIDNotMatch = errors.New("app id not match")
	// ErrInvalidBlockSize block size不合法
	ErrInvalidBlockSize = errors.New("invalid block size")
	// ErrInvalidPKCS7Data PKCS7数据不合法
	ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data")
	// ErrInvalidPKCS7Padding 输入padding失败
	ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
)

// PlainData 用户信息/手机号信息
type PlainData struct {
	OpenID          string `json:"openId"`
	UnionID         string `json:"unionId"`
	NickName        string `json:"nickName"`
	Gender          int    `json:"gender"`
	City            string `json:"city"`
	Province        string `json:"province"`
	Country         string `json:"country"`
	AvatarURL       string `json:"avatarUrl"`
	Language        string `json:"language"`
	PhoneNumber     string `json:"phoneNumber"`
	PurePhoneNumber string `json:"purePhoneNumber"`
	CountryCode     string `json:"countryCode"`
	Watermark       struct {
		Timestamp int64  `json:"timestamp"`
		AppID     string `json:"appid"`
	} `json:"watermark"`
}

// pkcs7Unpad returns slice of the original data without padding
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
	if blockSize <= 0 {
		return nil, ErrInvalidBlockSize
	}
	if len(data)%blockSize != 0 || len(data) == 0 {
		return nil, ErrInvalidPKCS7Data
	}
	c := data[len(data)-1]
	n := int(c)
	if n == 0 || n > len(data) {
		return nil, ErrInvalidPKCS7Padding
	}
	for i := 0; i < n; i++ {
		if data[len(data)-n+i] != c {
			return nil, ErrInvalidPKCS7Padding
		}
	}
	return data[:len(data)-n], nil
}

// getCipherText returns slice of the cipher text
func getCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
	aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, err
	}
	cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, err
	}
	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, err
	}
	mode := cipher.NewCBCDecrypter(block, ivBytes)
	mode.CryptBlocks(cipherText, cipherText)
	cipherText, err = pkcs7Unpad(cipherText, block.BlockSize())
	if err != nil {
		return nil, err
	}
	return cipherText, nil
}

// Decrypt 解密数据
func (encryptor *Encryptor) Decrypt(sessionKey, encryptedData, iv string) (*PlainData, error) {
	cipherText, err := getCipherText(sessionKey, encryptedData, iv)
	if err != nil {
		return nil, err
	}
	var plainData PlainData
	err = json.Unmarshal(cipherText, &plainData)
	if err != nil {
		return nil, err
	}
	if plainData.Watermark.AppID != encryptor.AppID {
		return nil, ErrAppIDNotMatch
	}
	return &plainData, nil
}
<commit_msg>Update encryptor.go (#405)<commit_after>package encryptor

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"errors"

	"github.com/silenceper/wechat/v2/miniprogram/context"
)

//Encryptor struct
type Encryptor struct {
	*context.Context
}

//NewEncryptor 实例
func NewEncryptor(context *context.Context) *Encryptor {
	basic := new(Encryptor)
	basic.Context = context
	return basic
}

var (
	// ErrAppIDNotMatch appid不匹配
	ErrAppIDNotMatch = errors.New("app id not match")
	// ErrInvalidBlockSize block size不合法
	ErrInvalidBlockSize = errors.New("invalid block size")
	// ErrInvalidPKCS7Data PKCS7数据不合法
	ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data")
	// ErrInvalidPKCS7Padding 输入padding失败
	ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
)

// PlainData 用户信息/手机号信息
type PlainData struct {
	OpenID          string `json:"openId"`
	UnionID         string `json:"unionId"`
	NickName        string `json:"nickName"`
	Gender          int    `json:"gender"`
	City            string `json:"city"`
	Province        string `json:"province"`
	Country         string `json:"country"`
	AvatarURL       string `json:"avatarUrl"`
	Language        string `json:"language"`
	PhoneNumber     string `json:"phoneNumber"`
	PurePhoneNumber string `json:"purePhoneNumber"`
	CountryCode     string `json:"countryCode"`
	Watermark       struct {
		Timestamp int64  `json:"timestamp"`
		AppID     string `json:"appid"`
	} `json:"watermark"`
}

// pkcs7Unpad returns slice of the original data without padding
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
	if blockSize <= 0 {
		return nil, ErrInvalidBlockSize
	}
	if len(data)%blockSize != 0 || len(data) == 0 {
		return nil, ErrInvalidPKCS7Data
	}
	c := data[len(data)-1]
	n := int(c)
	if n == 0 || n > len(data) {
		return nil, ErrInvalidPKCS7Padding
	}
	for i := 0; i < n; i++ {
		if data[len(data)-n+i] != c {
			return nil, ErrInvalidPKCS7Padding
		}
	}
	return data[:len(data)-n], nil
}

// GetCipherText returns slice of the cipher text
func GetCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
	aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, err
	}
	cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, err
	}
	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, err
	}
	mode := cipher.NewCBCDecrypter(block, ivBytes)
	mode.CryptBlocks(cipherText, cipherText)
	cipherText, err = pkcs7Unpad(cipherText, block.BlockSize())
	if err != nil {
		return nil, err
	}
	return cipherText, nil
}

// Decrypt 解密数据
func (encryptor *Encryptor) Decrypt(sessionKey, encryptedData, iv string) (*PlainData, error) {
	cipherText, err := GetCipherText(sessionKey, encryptedData, iv)
	if err != nil {
		return nil, err
	}
	var plainData PlainData
	err = json.Unmarshal(cipherText, &plainData)
	if err != nil {
		return nil, err
	}
	if plainData.Watermark.AppID != encryptor.AppID {
		return nil, ErrAppIDNotMatch
	}
	return &plainData, nil
}
<|endoftext|>