<commit_before>/*
 * Copyright (c) 2014-2015 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/btcsuite/btcd/btcjson"
	"github.com/btcsuite/btcd/wire"
	rpc "github.com/btcsuite/btcrpcclient"
	"github.com/btcsuite/btcutil"
)

// minFee is the minimum tx fee that can be paid
const minFee btcutil.Amount = 1e4 // 0.0001 BTC

// utxoQueue is the queue of utxos belonging to a actor
// utxos are queued after a block is received and are dispatched
// to their respective owner from com.poolUtxos
// they are dequeued from simulateTx and splitUtxos
type utxoQueue struct {
	utxos   []*TxOut
	enqueue chan *TxOut
	dequeue chan *TxOut
}

// Actor describes an actor on the simulation network.  Each actor runs
// independantly without external input to decide it's behavior.
type Actor struct {
	*Node
	quit             chan struct{}
	wg               sync.WaitGroup
	ownedAddresses   []btcutil.Address
	utxoQueue        *utxoQueue
	miningAddr       chan btcutil.Address
	walletPassphrase string
}

// TxOut is a valid tx output that can be used to generate transactions
type TxOut struct {
	OutPoint *wire.OutPoint
	Amount   btcutil.Amount
}

// NewActor creates a new actor which runs its own wallet process connecting
// to the btcd node server specified by node, and listening for simulator
// websocket connections on the specified port.
func NewActor(node *Node, port uint16) (*Actor, error) {
	// Please don't run this as root.
	if port < 1024 {
		return nil, errors.New("invalid actor port")
	}

	// Set btcwallet node args
	args, err := newBtcwalletArgs(port, node.Args.(*btcdArgs))
	if err != nil {
		return nil, err
	}

	logFile, err := getLogFile(args.prefix)
	if err != nil {
		log.Printf("Cannot get log file, logging disabled: %v", err)
	}
	btcwallet, err := NewNodeFromArgs(args, nil, logFile)
	if err != nil {
		return nil, err
	}

	a := Actor{
		Node:             btcwallet,
		quit:             make(chan struct{}),
		ownedAddresses:   make([]btcutil.Address, *maxAddresses),
		miningAddr:       make(chan btcutil.Address),
		walletPassphrase: "password",
		utxoQueue: &utxoQueue{
			enqueue: make(chan *TxOut),
			dequeue: make(chan *TxOut),
		},
	}
	return &a, nil
}

// Start creates the command to execute a wallet process and starts the
// command in the background, attaching the command's stderr and stdout
// to the passed writers. Nil writers may be used to discard output.
//
// In addition to starting the wallet process, this runs goroutines to
// handle wallet notifications and requests the wallet process to create
// an intial encrypted wallet, so that it can actually send and receive BTC.
//
// If the RPC client connection cannot be established or wallet cannot
// be created, the wallet process is killed and the actor directory
// removed.
func (a *Actor) Start(stderr, stdout io.Writer, com *Communication) error {
	connected := make(chan struct{})
	const timeoutSecs int64 = 3600 * 24

	if err := a.Node.Start(); err != nil {
		a.Shutdown()
		com.errChan <- struct{}{}
		return err
	}

	ntfnHandlers := &rpc.NotificationHandlers{
		OnClientConnected: func() {
			connected <- struct{}{}
		},
	}
	a.handlers = ntfnHandlers

	if err := a.Connect(); err != nil {
		a.Shutdown()
		com.errChan <- struct{}{}
		return err
	}

	// Wait for btcd to connect
	<-connected

	// Wait for wallet sync
	for i := 0; i < *maxConnRetries; i++ {
		if _, err := a.client.GetBalance(""); err != nil {
			time.Sleep(time.Duration(i) * 50 * time.Millisecond)
			continue
		}
		break
	}

	// Create wallet addresses and unlock wallet.
	log.Printf("%s: Creating wallet addresses...", a)
	for i := range a.ownedAddresses {
		fmt.Printf("\r%d/%d", i+1, len(a.ownedAddresses))
		addr, err := a.client.GetNewAddress()
		if err != nil {
			log.Printf("%s: Cannot create address #%d", a, i+1)
			com.errChan <- struct{}{}
			return err
		}
		a.ownedAddresses[i] = addr
	}
	fmt.Printf("\n")

	if err := a.client.WalletPassphrase(a.walletPassphrase, timeoutSecs); err != nil {
		log.Printf("%s: Cannot unlock wallet: %v", a, err)
		com.errChan <- struct{}{}
		return err
	}

	// Send a random address that will be used by the cpu miner.
	a.miningAddr <- a.ownedAddresses[rand.Int()%len(a.ownedAddresses)]

	// Start a goroutine that queues up a set of utxos belonging to this
	// actor. The utxos are sent from com.poolUtxos which in turn receives
	// block notifications from sim.go
	a.wg.Add(1)
	go a.queueUtxos()

	// Start a goroutine to simulate transactions.
	a.wg.Add(1)
	go a.simulateTx(com.downstream, com.txpool)

	// Start a goroutine to split utxos
	a.wg.Add(1)
	go a.splitUtxos(com.split, com.txpool)

	return nil
}

// simulateTx runs as a goroutine and simulates transactions between actors
//
// It receives a random address downstream, dequeues a utxo, sends a raw
// transaction to the address using the utxo as input
func (a *Actor) simulateTx(downstream <-chan btcutil.Address, txpool chan<- struct{}) {
	defer a.wg.Done()

	for {
		select {
		case utxo := <-a.utxoQueue.dequeue:
			select {
			case addr := <-downstream:
				// Create a raw transaction
				inputs := []btcjson.TransactionInput{{
					Txid: utxo.OutPoint.Hash.String(),
					Vout: utxo.OutPoint.Index,
				}}

				// Provide a fees of minFee to ensure the tx gets mined
				// the utxo amount is guaranteed to be > maxSplit*minFee
				amt := utxo.Amount - minFee
				amounts := map[btcutil.Address]btcutil.Amount{
					addr: amt,
				}

				err := a.sendRawTransaction(inputs, amounts)
				if err != nil {
					log.Printf("%s: Error sending raw transaction: %v", a, err)
					select {
					case txpool <- struct{}{}:
					case <-a.quit:
						return
					}
					continue
				}

			case <-a.quit:
				return
			}
		case <-a.quit:
			return
		}
	}
}

// splitUtxos runs as a goroutine and builds up a large set of utxos that
// can be used to simulate large tx/block ratios
//
// It receives a 'split' which is int that indicates the number of resultant utxos
// the tx is sent to addresses from the same actor since we're only interested in
// building up the utxo set
func (a *Actor) splitUtxos(split <-chan int, txpool chan<- struct{}) {
	defer a.wg.Done()

	for {
		select {
		case utxo := <-a.utxoQueue.dequeue:
			select {
			case split := <-split:
				// Create a raw transaction
				inputs := []btcjson.TransactionInput{{
					Txid: utxo.OutPoint.Hash.String(),
					Vout: utxo.OutPoint.Index,
				}}

				// Provide a fees of minFee to ensure the tx gets mined
				// the utxo amount is guaranteed to be > maxSplit*minFee
				amt := utxo.Amount - minFee
				amounts := map[btcutil.Address]btcutil.Amount{}

				// Create a output of random amount and sent it
				// to a random address from the address space
				// total number of outputs is split+1, taking into
				// account this utxo which is consumed in the process

				// set a rand start index for getting different random addrs
				randomIndex := rand.Int() % len(a.ownedAddresses)
				for i := 0; i <= split; i++ {
					var to btcutil.Address
					var change btcutil.Amount
					// pick a random address
					if randomIndex+i < len(a.ownedAddresses) {
						to = a.ownedAddresses[randomIndex+i]
					} else {
						// wrap around incase of an overflow
						to = a.ownedAddresses[randomIndex+i-len(a.ownedAddresses)]
					}
					if i == split {
						// last split, so just set the change
						change = amt
					} else {
						// pick a random change amount which is less than amt
						// but have a lower bound at minFee
						change = btcutil.Amount(rand.Int63n(int64(amt) / 2))
						if change < minFee {
							change = minFee
						}
						amt -= change
					}
					amounts[to] = change
				}

				err := a.sendRawTransaction(inputs, amounts)
				if err != nil {
					log.Printf("%s: Error sending raw transaction: %v", a, err)
					select {
					case txpool <- struct{}{}:
					case <-a.quit:
						return
					}
					continue
				}

			case <-a.quit:
				return
			}
		case <-a.quit:
			return
		}
	}
}

// sendRawTransaction creates a raw transaction, signs it and sends it
func (a *Actor) sendRawTransaction(inputs []btcjson.TransactionInput, amounts map[btcutil.Address]btcutil.Amount) error {
	msgTx, err := a.client.CreateRawTransaction(inputs, amounts)
	if err != nil {
		return err
	}
	// sign it
	msgTx, ok, err := a.client.SignRawTransaction(msgTx)
	if err != nil {
		return err
	}
	if !ok {
		return err
	}
	// and finally send it.
	if _, err := a.client.SendRawTransaction(msgTx, false); err != nil {
		return err
	}
	return nil
}

// queueUtxos receives utxos belonging to this actor and queues them up
func (a *Actor) queueUtxos() {
	defer a.wg.Done()

	enqueue := a.utxoQueue.enqueue
	var dequeue chan *TxOut
	var next *TxOut
out:
	for {
		select {
		case n, ok := <-enqueue:
			if !ok {
				// If no utxos are queued for handling,
				// the queue is finished.
				if len(a.utxoQueue.utxos) == 0 {
					break out
				}
				// nil channel so no more reads can occur.
				enqueue = nil
				continue
			}
			if len(a.utxoQueue.utxos) == 0 {
				next = n
				dequeue = a.utxoQueue.dequeue
			}
			a.utxoQueue.utxos = append(a.utxoQueue.utxos, n)
		case dequeue <- next:
			a.utxoQueue.utxos[0] = nil
			a.utxoQueue.utxos = a.utxoQueue.utxos[1:]
			if len(a.utxoQueue.utxos) != 0 {
				next = a.utxoQueue.utxos[0]
			} else {
				// If no more utxos can be enqueued, the
				// queue is finished.
				if enqueue == nil {
					break out
				}
				dequeue = nil
			}
		case <-a.quit:
			break out
		}
	}
	close(a.utxoQueue.dequeue)
}

// Shutdown performs a shutdown down the actor by first signalling
// all goroutines to stop, waiting for them to stop and them cleaning up
func (a *Actor) Shutdown() {
	select {
	case <-a.quit:
	default:
		close(a.quit)
		a.WaitForShutdown()
		a.Node.Shutdown()
	}
}

// WaitForShutdown waits until every actor goroutine has returned
func (a *Actor) WaitForShutdown() {
	a.wg.Wait()
}
<commit_msg>Update to btcjson v2<commit_after>/*
 * Copyright (c) 2014-2015 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/btcsuite/btcd/btcjson/v2/btcjson"
	"github.com/btcsuite/btcd/wire"
	rpc "github.com/btcsuite/btcrpcclient"
	"github.com/btcsuite/btcutil"
)

// minFee is the minimum tx fee that can be paid
const minFee btcutil.Amount = 1e4 // 0.0001 BTC

// utxoQueue is the queue of utxos belonging to a actor
// utxos are queued after a block is received and are dispatched
// to their respective owner from com.poolUtxos
// they are dequeued from simulateTx and splitUtxos
type utxoQueue struct {
	utxos   []*TxOut
	enqueue chan *TxOut
	dequeue chan *TxOut
}

// Actor describes an actor on the simulation network.  Each actor runs
// independantly without external input to decide it's behavior.
type Actor struct {
	*Node
	quit             chan struct{}
	wg               sync.WaitGroup
	ownedAddresses   []btcutil.Address
	utxoQueue        *utxoQueue
	miningAddr       chan btcutil.Address
	walletPassphrase string
}

// TxOut is a valid tx output that can be used to generate transactions
type TxOut struct {
	OutPoint *wire.OutPoint
	Amount   btcutil.Amount
}

// NewActor creates a new actor which runs its own wallet process connecting
// to the btcd node server specified by node, and listening for simulator
// websocket connections on the specified port.
func NewActor(node *Node, port uint16) (*Actor, error) {
	// Please don't run this as root.
	if port < 1024 {
		return nil, errors.New("invalid actor port")
	}

	// Set btcwallet node args
	args, err := newBtcwalletArgs(port, node.Args.(*btcdArgs))
	if err != nil {
		return nil, err
	}

	logFile, err := getLogFile(args.prefix)
	if err != nil {
		log.Printf("Cannot get log file, logging disabled: %v", err)
	}
	btcwallet, err := NewNodeFromArgs(args, nil, logFile)
	if err != nil {
		return nil, err
	}

	a := Actor{
		Node:             btcwallet,
		quit:             make(chan struct{}),
		ownedAddresses:   make([]btcutil.Address, *maxAddresses),
		miningAddr:       make(chan btcutil.Address),
		walletPassphrase: "password",
		utxoQueue: &utxoQueue{
			enqueue: make(chan *TxOut),
			dequeue: make(chan *TxOut),
		},
	}
	return &a, nil
}

// Start creates the command to execute a wallet process and starts the
// command in the background, attaching the command's stderr and stdout
// to the passed writers. Nil writers may be used to discard output.
//
// In addition to starting the wallet process, this runs goroutines to
// handle wallet notifications and requests the wallet process to create
// an intial encrypted wallet, so that it can actually send and receive BTC.
//
// If the RPC client connection cannot be established or wallet cannot
// be created, the wallet process is killed and the actor directory
// removed.
func (a *Actor) Start(stderr, stdout io.Writer, com *Communication) error {
	connected := make(chan struct{})
	const timeoutSecs int64 = 3600 * 24

	if err := a.Node.Start(); err != nil {
		a.Shutdown()
		com.errChan <- struct{}{}
		return err
	}

	ntfnHandlers := &rpc.NotificationHandlers{
		OnClientConnected: func() {
			connected <- struct{}{}
		},
	}
	a.handlers = ntfnHandlers

	if err := a.Connect(); err != nil {
		a.Shutdown()
		com.errChan <- struct{}{}
		return err
	}

	// Wait for btcd to connect
	<-connected

	// Wait for wallet sync
	for i := 0; i < *maxConnRetries; i++ {
		if _, err := a.client.GetBalance(""); err != nil {
			time.Sleep(time.Duration(i) * 50 * time.Millisecond)
			continue
		}
		break
	}

	// Create wallet addresses and unlock wallet.
	log.Printf("%s: Creating wallet addresses...", a)
	for i := range a.ownedAddresses {
		fmt.Printf("\r%d/%d", i+1, len(a.ownedAddresses))
		addr, err := a.client.GetNewAddress()
		if err != nil {
			log.Printf("%s: Cannot create address #%d", a, i+1)
			com.errChan <- struct{}{}
			return err
		}
		a.ownedAddresses[i] = addr
	}
	fmt.Printf("\n")

	if err := a.client.WalletPassphrase(a.walletPassphrase, timeoutSecs); err != nil {
		log.Printf("%s: Cannot unlock wallet: %v", a, err)
		com.errChan <- struct{}{}
		return err
	}

	// Send a random address that will be used by the cpu miner.
	a.miningAddr <- a.ownedAddresses[rand.Int()%len(a.ownedAddresses)]

	// Start a goroutine that queues up a set of utxos belonging to this
	// actor. The utxos are sent from com.poolUtxos which in turn receives
	// block notifications from sim.go
	a.wg.Add(1)
	go a.queueUtxos()

	// Start a goroutine to simulate transactions.
	a.wg.Add(1)
	go a.simulateTx(com.downstream, com.txpool)

	// Start a goroutine to split utxos
	a.wg.Add(1)
	go a.splitUtxos(com.split, com.txpool)

	return nil
}

// simulateTx runs as a goroutine and simulates transactions between actors
//
// It receives a random address downstream, dequeues a utxo, sends a raw
// transaction to the address using the utxo as input
func (a *Actor) simulateTx(downstream <-chan btcutil.Address, txpool chan<- struct{}) {
	defer a.wg.Done()

	for {
		select {
		case utxo := <-a.utxoQueue.dequeue:
			select {
			case addr := <-downstream:
				// Create a raw transaction
				inputs := []btcjson.TransactionInput{{
					Txid: utxo.OutPoint.Hash.String(),
					Vout: utxo.OutPoint.Index,
				}}

				// Provide a fees of minFee to ensure the tx gets mined
				// the utxo amount is guaranteed to be > maxSplit*minFee
				amt := utxo.Amount - minFee
				amounts := map[btcutil.Address]btcutil.Amount{
					addr: amt,
				}

				err := a.sendRawTransaction(inputs, amounts)
				if err != nil {
					log.Printf("%s: Error sending raw transaction: %v", a, err)
					select {
					case txpool <- struct{}{}:
					case <-a.quit:
						return
					}
					continue
				}

			case <-a.quit:
				return
			}
		case <-a.quit:
			return
		}
	}
}

// splitUtxos runs as a goroutine and builds up a large set of utxos that
// can be used to simulate large tx/block ratios
//
// It receives a 'split' which is int that indicates the number of resultant utxos
// the tx is sent to addresses from the same actor since we're only interested in
// building up the utxo set
func (a *Actor) splitUtxos(split <-chan int, txpool chan<- struct{}) {
	defer a.wg.Done()

	for {
		select {
		case utxo := <-a.utxoQueue.dequeue:
			select {
			case split := <-split:
				// Create a raw transaction
				inputs := []btcjson.TransactionInput{{
					Txid: utxo.OutPoint.Hash.String(),
					Vout: utxo.OutPoint.Index,
				}}

				// Provide a fees of minFee to ensure the tx gets mined
				// the utxo amount is guaranteed to be > maxSplit*minFee
				amt := utxo.Amount - minFee
				amounts := map[btcutil.Address]btcutil.Amount{}

				// Create a output of random amount and sent it
				// to a random address from the address space
				// total number of outputs is split+1, taking into
				// account this utxo which is consumed in the process

				// set a rand start index for getting different random addrs
				randomIndex := rand.Int() % len(a.ownedAddresses)
				for i := 0; i <= split; i++ {
					var to btcutil.Address
					var change btcutil.Amount
					// pick a random address
					if randomIndex+i < len(a.ownedAddresses) {
						to = a.ownedAddresses[randomIndex+i]
					} else {
						// wrap around incase of an overflow
						to = a.ownedAddresses[randomIndex+i-len(a.ownedAddresses)]
					}
					if i == split {
						// last split, so just set the change
						change = amt
					} else {
						// pick a random change amount which is less than amt
						// but have a lower bound at minFee
						change = btcutil.Amount(rand.Int63n(int64(amt) / 2))
						if change < minFee {
							change = minFee
						}
						amt -= change
					}
					amounts[to] = change
				}

				err := a.sendRawTransaction(inputs, amounts)
				if err != nil {
					log.Printf("%s: Error sending raw transaction: %v", a, err)
					select {
					case txpool <- struct{}{}:
					case <-a.quit:
						return
					}
					continue
				}

			case <-a.quit:
				return
			}
		case <-a.quit:
			return
		}
	}
}

// sendRawTransaction creates a raw transaction, signs it and sends it
func (a *Actor) sendRawTransaction(inputs []btcjson.TransactionInput, amounts map[btcutil.Address]btcutil.Amount) error {
	msgTx, err := a.client.CreateRawTransaction(inputs, amounts)
	if err != nil {
		return err
	}
	// sign it
	msgTx, ok, err := a.client.SignRawTransaction(msgTx)
	if err != nil {
		return err
	}
	if !ok {
		return err
	}
	// and finally send it.
	if _, err := a.client.SendRawTransaction(msgTx, false); err != nil {
		return err
	}
	return nil
}

// queueUtxos receives utxos belonging to this actor and queues them up
func (a *Actor) queueUtxos() {
	defer a.wg.Done()

	enqueue := a.utxoQueue.enqueue
	var dequeue chan *TxOut
	var next *TxOut
out:
	for {
		select {
		case n, ok := <-enqueue:
			if !ok {
				// If no utxos are queued for handling,
				// the queue is finished.
				if len(a.utxoQueue.utxos) == 0 {
					break out
				}
				// nil channel so no more reads can occur.
				enqueue = nil
				continue
			}
			if len(a.utxoQueue.utxos) == 0 {
				next = n
				dequeue = a.utxoQueue.dequeue
			}
			a.utxoQueue.utxos = append(a.utxoQueue.utxos, n)
		case dequeue <- next:
			a.utxoQueue.utxos[0] = nil
			a.utxoQueue.utxos = a.utxoQueue.utxos[1:]
			if len(a.utxoQueue.utxos) != 0 {
				next = a.utxoQueue.utxos[0]
			} else {
				// If no more utxos can be enqueued, the
				// queue is finished.
				if enqueue == nil {
					break out
				}
				dequeue = nil
			}
		case <-a.quit:
			break out
		}
	}
	close(a.utxoQueue.dequeue)
}

// Shutdown performs a shutdown down the actor by first signalling
// all goroutines to stop, waiting for them to stop and them cleaning up
func (a *Actor) Shutdown() {
	select {
	case <-a.quit:
	default:
		close(a.quit)
		a.WaitForShutdown()
		a.Node.Shutdown()
	}
}

// WaitForShutdown waits until every actor goroutine has returned
func (a *Actor) WaitForShutdown() {
	a.wg.Wait()
}
<|endoftext|>


<commit_before>package skunk

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
	"text/tabwriter"
	"time"
)

// MinuteCycle, QuarterHourCycle, HalfHourCycle, and HourCycle all represent useful reporting cycles for an agent. Other
// cycles are permitted, provide they would not violate the two-POSTs-per-minute limit on NewRelic APIs.
const (
	MinuteCycle   = time.Minute
	HalfHourCycle = time.Minute * 30
	HourCycle     = time.Hour
)

// NewRelicAPI is the URL to POST NewRelic metrics data to. This may be altered to change the
// default endpoint of new agents. Already-initialized agents do not use this.
var NewRelicAPI = `https://platform-api.newrelic.com/platform/v1/metrics`

type opFunc func(*Agent) error

type Agent struct {
	// Initialization fields -- these may not change after Start is called. Prior to calling Start, you may tweak
	// them to your heart's content.
	Cycle      time.Duration
	Client     *http.Client
	Log        io.Writer
	LogMetrics bool

	apiURL string
	apiKey string

	// Access to the following fields controlled by runloop after init
	body     *Body
	err      error
	lastPoll time.Time
	ticker   *time.Ticker
	ops      chan<- opFunc
}

func New(version, apiKey string) (*Agent, error) {
	host, err := os.Hostname()
	if err != nil {
		// Hostnames are required.
		return nil, err
	}

	rep := AgentRep{
		Host:    host,
		PID:     os.Getpid(),
		Version: version,
	}

	return NewWithRep(apiKey, rep)
}

func NewWithRep(apiKey string, rep AgentRep) (agent *Agent, err error) {
	switch {
	case len(apiKey) == 0:
		return nil, mkerr(ErrNoAPIKey, nil)
	case len(rep.Version) == 0:
		return nil, mkerr(ErrNoVersion, nil)
	case len(rep.Host) == 0:
		return nil, mkerr(ErrNoHost, nil)
	}

	return &Agent{
		Client:     http.DefaultClient,
		Cycle:      MinuteCycle,
		Log:        ioutil.Discard,
		LogMetrics: false,

		apiURL: NewRelicAPI,
		apiKey: apiKey,

		body: &Body{
			Agent:      rep,
			Components: nil,
		},

		lastPoll: time.Now(),
		ticker:   nil,
	}, nil
}

func (a *Agent) Start() {
	if a.ops != nil {
		return
	}

	if a.Log == nil {
		a.Log = ioutil.Discard
	}

	ops := make(chan opFunc)
	a.ops = ops

	go a.run(ops)
}

// Close kills the agent's runloop and makes it completely inert. Using the agent afterward will result in a panic. Any
// error held by the agent prior to shutdown is returned.
//
// When calling Close, you must ensure that the agent is no longer in use and will not be used by any goroutine after
// Close is called.
func (a *Agent) Close() error {
	err := a.Err()
	a.ops <- shutdown
	return err
}

// shutdown is an opFunc that closes an agent's ops channel.
func shutdown(a *Agent) error {
	if err := a.sendRequest(time.Now()); iserr(err, errMustRetry) {
		fmt.Fprintln(a.Log, "skunk: received 50x error from NewRelic on shutdown flush - dropping payload on the floor")
	} else if err != nil {
		fmt.Fprintln(a.Log, "skunk: received error on sending to NewRelic:", err)
		a.err = err
	}
	close(a.ops)
	return mkerr(errShuttingDown, nil)
}

type opGetErr chan<- error

func (c opGetErr) Exec(a *Agent) error {
	c <- a.err
	return nil
}

func (a *Agent) Err() (err error) {
	out := make(chan error)
	a.ops <- opGetErr(out).Exec
	return <-out
}

func (a *Agent) run(ops <-chan opFunc) {
	var timer *time.Timer
	var retry <-chan time.Time
	retryNeeded := false

	a.ticker = time.NewTicker(a.Cycle)
	defer a.ticker.Stop()

	trySend := func(from time.Time) {
		if err := a.sendRequest(from); err == nil {
			retryNeeded = false
			a.lastPoll = from
			a.clear()
		} else if iserr(err, errMustRetry) {
			if timer == nil {
				timer = time.NewTimer(time.Minute)
				retry = timer.C
			} else {
				timer.Reset(time.Minute)
			}
			retryNeeded = true
		} else {
			a.err = err
		}
	}

	for {
		select {
		case from := <-retry:
			trySend(from)
		case from := <-a.ticker.C:
			if a.LogMetrics {
				a.logMetrics()
			}

			if !retryNeeded {
				// Let the retry loop take over until things are back to normal.
				trySend(from)
			}
		case op, ok := <-ops:
			if !ok {
				return
			} else if op == nil {
				// This should be impossible. If it happens, log it and skip the op.
				fmt.Fprintln(a.Log, ErrNilOpReceived)
				continue
			}

			if err := op(a); iserr(err, errShuttingDown) {
				return
			} else if err != nil {
				a.err = err
			}
		}
	}
}

func (a *Agent) logMetrics() {
	if a.Log == ioutil.Discard {
		return
	}

	components := make([]Component, len(a.body.Components))
	i := 0
	for _, c := range a.body.Components {
		if len(c.Metrics) > 0 {
			components[i] = *c
			i++
		}
	}

	if i == 0 {
		return
	}

	go logComponentMetrics(a.Log, components[:i])
}

func logComponentMetrics(w io.Writer, components []Component) {
	if len(components) == 0 {
		return
	}

	var buf bytes.Buffer
	tw := tabwriter.NewWriter(&buf, 1, 8, 1, ' ', tabwriter.TabIndent)

	var keys []string
	for _, com := range components {
		if len(com.Metrics) == 0 {
			continue
		}

		// Sort keys so the output is easier to sift through.
		for key := range com.Metrics {
			keys = append(keys, key)
		}
		sort.Strings(keys)

		fmt.Fprintf(tw, "%s (%s) %v\n\tName\tCount\tTotal\tAverage\tMin\tMax\tSS\n", com.Name, com.GUID, com.Duration.Duration)
		for _, key := range keys {
			m := com.Metrics[key]
			switch m := m.(type) {
			case RangeMetric:
				fmt.Fprintf(tw, "\t%s\t%v\t%v\t%v\t%v\t%v\t%v\n",
					key, m.Count, m.Total, m.Total/float64(m.Count), m.Min, m.Max, m.Square-((m.Total*m.Total)/float64(m.Count)))
			case ScalarMetric:
				f := float64(m)
				fmt.Fprintf(tw, "\t%s\t1\t%v\t%v\t%v\t%v\t0\n",
					key, f, f, f, f)
			}
		}
		tw.Flush()

		keys = keys[0:0]
	}

	if _, err := buf.WriteTo(w); err != nil {
		fmt.Fprintf(w, "skunk: error writing metrics log entries: %v\n", err)
	}
}

func (a *Agent) sendRequest(from time.Time) (err error) {
	var buf bytes.Buffer
	compressed := true
tryGetPayload:
	err = a.getPayload(&buf, from, compressed)
	switch {
	case err == nil:
	case iserr(err, errNoMetrics):
		return nil // Nothing to do.
	default:
		if _, ok := err.(*json.MarshalerError); ok {
			// Can't do anything about this. This error might be worth panicking over.
			return mkerr(ErrEncodingJSON, err)
		}

		if compressed {
			// Try without compression in case it's some anomalous unknown compression error that's eluded
			// everyone but me (i.e., should be almost impossible).
			compressed = false
			buf.Reset()
			goto tryGetPayload
		}
		return err
	}

	req, err := http.NewRequest("POST", a.apiURL, &buf)
	if err != nil {
		// No idea what happened here, assume the worst.
		return err
	}

	// Set headers
	req.Header.Set("X-License-Key", a.apiKey)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	if compressed {
		req.Header.Set("Content-Encoding", "gzip")
	}

	resp, err := a.Client.Do(req)
	if resp != nil {
		defer func() {
			closeErr := resp.Body.Close()
			if closeErr != nil {
				fmt.Fprintf(a.Log, "skunk: error closing response body: %v\n", closeErr)
			}
		}()
	}

	if err != nil {
		return err
	}

	if resp.StatusCode == 200 {
		return nil
	}

	var nrErr struct {
		Error string `json:"error"`
	}
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(nrErr); err == nil && len(nrErr.Error) > 0 {
		fmt.Fprintf(a.Log, "skunk: received NewRelic error: %s\n", nrErr.Error)
	}
	if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
		fmt.Fprintf(a.Log, "skunk: error discarding body remainder: %v\n", err)
	}

	return statusError(resp)
}

func statusError(resp *http.Response) error {
	code := resp.StatusCode
	switch {
	case code >= 200 && code < 300:
		return nil
	case code == 400:
		return mkerr(ErrBadPayload, nil)
	case code == 403:
		return mkerr(ErrForbidden, nil)
	case code == 404:
		return mkerr(ErrBadRequest, nil)
	case code == 405:
		return mkerr(ErrBadRequest, nil)
	case code == 413:
		return mkerr(ErrBodyTooLarge, nil)
	case code >= 500 && code < 600:
		return mkerr(errMustRetry, nil)
	default:
		return fmt.Errorf("skunk: got unexpected status code %d %s from NewRelic.", code, resp.Status)
	}
}

// Component gets a component with the given name and GUID from the Agent. If no such component exists, then a new one
// is allocated and it is returned. No tests are done to ensure that components with the same name but a different GUID
// or vice versa are allocated, so it is possible to end up with potentially inconsistent data.
func (a *Agent) Component(name, guid string) (*Component, error) {
	if len(name) > 32 {
		// Per NewRelic, names must be <= 32 characters in length.
		return nil, mkerr(ErrNameTooLong, nil)
	}

	out := make(chan *Component)
	a.ops <- (addComponent{name, guid, out}).Exec
	return <-out, nil
}

// addComponent is a small wrapper around a parameter bundle to create a component and add it to an agent, or raturn an
// agent with the same name and guid.
type addComponent struct {
	name, guid string
	out        chan<- *Component
}

// Exec searches for a component of the name and guid described by ac. If it finds a component in the agent, a, it is
// sent on ac's out channel. Otherwise, a new component is created, added to the agent, and sent on the out channel.
func (ac addComponent) Exec(a *Agent) error {
	body := a.body
	for _, c := range body.Components {
		if c.Name == ac.name && c.GUID == ac.guid {
			ac.out <- c
			return nil
		}
	}

	c := &Component{
		Name:    ac.name,
		GUID:    ac.guid,
		Metrics: make(map[string]Metric),
		agent:   a,
	}
	body.Components = append(body.Components, c)
	ac.out <- c

	return nil
}

// clear empties out all metrics held by the agent. This should be called after a payload has been successfully sent to
// reset all components to a pristine state.
func (a *Agent) clear() {
	for _, c := range a.body.Components {
		// Allocate a new Metrics map and reuse the old map's length as its capacity.
		c.Metrics = make(map[string]Metric, len(c.Metrics))
		c.Duration.Duration = 0 // Should be zero, but clear() says it'll make it pristine, so zero it anyway.
		c.start = time.Time{}
	}
}

// getPayload returns a JSON payload as a byte slice to send to NewRelic as its POSTed body. The resulting JSON does not
// include components without metrics.
func (a *Agent) getPayload(w io.Writer, from time.Time, compressed bool) (err error) {
	// Make a copy of the body and exclude components without metrics.
	body := *a.body
	body.Components = make([]*Component, 0, len(body.Components))
	for _, com := range a.body.Components {
		if len(com.Metrics) == 0 || com.start.IsZero() {
			continue
		}

		dupe := *com
		dupe.Duration.Duration = from.Sub(com.start)
		if dupe.Duration.Duration < 0 {
			// Metrics from the future aren't allowed.
			dupe.Duration.Duration = 0
		}

		body.Components = append(body.Components, &dupe)
	}

	if len(body.Components) == 0 {
		return mkerr(errNoMetrics, nil)
	}

	if compressed {
		zipWriter := gzip.NewWriter(w)
		defer func() {
			if err == nil {
				err = zipWriter.Close()
			}
		}()
		w = zipWriter
	}
	encoder := json.NewEncoder(w)
	return encoder.Encode(body)
}
<commit_msg>Log something for unknown metric types<commit_after>package skunk

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
	"text/tabwriter"
	"time"
)

// MinuteCycle, QuarterHourCycle, HalfHourCycle, and HourCycle all represent useful reporting cycles for an agent. Other
// cycles are permitted, provide they would not violate the two-POSTs-per-minute limit on NewRelic APIs.
const (
	MinuteCycle   = time.Minute
	HalfHourCycle = time.Minute * 30
	HourCycle     = time.Hour
)

// NewRelicAPI is the URL to POST NewRelic metrics data to. This may be altered to change the
// default endpoint of new agents. Already-initialized agents do not use this.
var NewRelicAPI = `https://platform-api.newrelic.com/platform/v1/metrics`

type opFunc func(*Agent) error

type Agent struct {
	// Initialization fields -- these may not change after Start is called. Prior to calling Start, you may tweak
	// them to your heart's content.
	Cycle      time.Duration
	Client     *http.Client
	Log        io.Writer
	LogMetrics bool

	apiURL string
	apiKey string

	// Access to the following fields controlled by runloop after init
	body     *Body
	err      error
	lastPoll time.Time
	ticker   *time.Ticker
	ops      chan<- opFunc
}

func New(version, apiKey string) (*Agent, error) {
	host, err := os.Hostname()
	if err != nil {
		// Hostnames are required.
		return nil, err
	}

	rep := AgentRep{
		Host:    host,
		PID:     os.Getpid(),
		Version: version,
	}

	return NewWithRep(apiKey, rep)
}

func NewWithRep(apiKey string, rep AgentRep) (agent *Agent, err error) {
	switch {
	case len(apiKey) == 0:
		return nil, mkerr(ErrNoAPIKey, nil)
	case len(rep.Version) == 0:
		return nil, mkerr(ErrNoVersion, nil)
	case len(rep.Host) == 0:
		return nil, mkerr(ErrNoHost, nil)
	}

	return &Agent{
		Client:     http.DefaultClient,
		Cycle:      MinuteCycle,
		Log:        ioutil.Discard,
		LogMetrics: false,

		apiURL: NewRelicAPI,
		apiKey: apiKey,

		body: &Body{
			Agent:      rep,
			Components: nil,
		},

		lastPoll: time.Now(),
		ticker:   nil,
	}, nil
}

func (a *Agent) Start() {
	if a.ops != nil {
		return
	}

	if a.Log == nil {
		a.Log = ioutil.Discard
	}

	ops := make(chan opFunc)
	a.ops = ops

	go a.run(ops)
}

// Close kills the agent's runloop and makes it completely inert. Using the agent afterward will result in a panic. Any
// error held by the agent prior to shutdown is returned.
//
// When calling Close, you must ensure that the agent is no longer in use and will not be used by any goroutine after
// Close is called.
func (a *Agent) Close() error {
	err := a.Err()
	a.ops <- shutdown
	return err
}

// shutdown is an opFunc that closes an agent's ops channel.
func shutdown(a *Agent) error {
	if err := a.sendRequest(time.Now()); iserr(err, errMustRetry) {
		fmt.Fprintln(a.Log, "skunk: received 50x error from NewRelic on shutdown flush - dropping payload on the floor")
	} else if err != nil {
		fmt.Fprintln(a.Log, "skunk: received error on sending to NewRelic:", err)
		a.err = err
	}
	close(a.ops)
	return mkerr(errShuttingDown, nil)
}

type opGetErr chan<- error

func (c opGetErr) Exec(a *Agent) error {
	c <- a.err
	return nil
}

func (a *Agent) Err() (err error) {
	out := make(chan error)
	a.ops <- opGetErr(out).Exec
	return <-out
}

func (a *Agent) run(ops <-chan opFunc) {
	var timer *time.Timer
	var retry <-chan time.Time
	retryNeeded := false

	a.ticker = time.NewTicker(a.Cycle)
	defer a.ticker.Stop()

	trySend := func(from time.Time) {
		if err := a.sendRequest(from); err == nil {
			retryNeeded = false
			a.lastPoll = from
			a.clear()
		} else if iserr(err, errMustRetry) {
			if timer == nil {
				timer = time.NewTimer(time.Minute)
				retry = timer.C
			} else {
				timer.Reset(time.Minute)
			}
			retryNeeded = true
		} else {
			a.err = err
		}
	}

	for {
		select {
		case from := <-retry:
			trySend(from)
		case from := <-a.ticker.C:
			if a.LogMetrics {
				a.logMetrics()
			}

			if !retryNeeded {
				// Let the retry loop take over until things are back to normal.
				trySend(from)
			}
		case op, ok := <-ops:
			if !ok {
				return
			} else if op == nil {
				// This should be impossible. If it happens, log it and skip the op.
				fmt.Fprintln(a.Log, ErrNilOpReceived)
				continue
			}

			if err := op(a); iserr(err, errShuttingDown) {
				return
			} else if err != nil {
				a.err = err
			}
		}
	}
}

func (a *Agent) logMetrics() {
	if a.Log == ioutil.Discard {
		return
	}

	components := make([]Component, len(a.body.Components))
	i := 0
	for _, c := range a.body.Components {
		if len(c.Metrics) > 0 {
			components[i] = *c
			i++
		}
	}

	if i == 0 {
		return
	}

	go logComponentMetrics(a.Log, components[:i])
}

func logComponentMetrics(w io.Writer, components []Component) {
	if len(components) == 0 {
		return
	}

	var buf bytes.Buffer
	tw := tabwriter.NewWriter(&buf, 1, 8, 1, ' ', tabwriter.TabIndent)

	var keys []string
	for _, com := range components {
		if len(com.Metrics) == 0 {
			continue
		}

		// Sort keys so the output is easier to sift through.
		for key := range com.Metrics {
			keys = append(keys, key)
		}
		sort.Strings(keys)

		fmt.Fprintf(tw, "%s (%s) %v\n\tName\tCount\tTotal\tAverage\tMin\tMax\tSS\n", com.Name, com.GUID, com.Duration.Duration)
		for _, key := range keys {
			m := com.Metrics[key]
			switch m := m.(type) {
			case RangeMetric:
				fmt.Fprintf(tw, "\t%s\t%v\t%v\t%v\t%v\t%v\t%v\n",
					key, m.Count, m.Total, m.Total/float64(m.Count), m.Min, m.Max, m.Square-((m.Total*m.Total)/float64(m.Count)))
			case ScalarMetric:
				f := float64(m)
				fmt.Fprintf(tw, "\t%s\t1\t%v\t%v\t%v\t%v\t0\n",
					key, f, f, f, f)
			default:
				// Unknown type (at least emit something for now) -- will likely need to add accessor
				// methods to the Metric interface later to handle these cases.
				fmt.Fprintf(tw, "\t%s\tNA\tNA\tNA\tNA\tNA\tNA\n")
			}
		}
		tw.Flush()

		keys = keys[0:0]
	}

	if _, err := buf.WriteTo(w); err != nil {
		fmt.Fprintf(w, "skunk: error writing metrics log entries: %v\n", err)
	}
}

func (a *Agent) sendRequest(from time.Time) (err error) {
	var buf bytes.Buffer
	compressed := true
tryGetPayload:
	err = a.getPayload(&buf, from, compressed)
	switch {
	case err == nil:
	case iserr(err, errNoMetrics):
		return nil // Nothing to do.
	default:
		if _, ok := err.(*json.MarshalerError); ok {
			// Can't do anything about this. This error might be worth panicking over.
			return mkerr(ErrEncodingJSON, err)
		}

		if compressed {
			// Try without compression in case it's some anomalous unknown compression error that's eluded
			// everyone but me (i.e., should be almost impossible).
			compressed = false
			buf.Reset()
			goto tryGetPayload
		}
		return err
	}

	req, err := http.NewRequest("POST", a.apiURL, &buf)
	if err != nil {
		// No idea what happened here, assume the worst.
		return err
	}

	// Set headers
	req.Header.Set("X-License-Key", a.apiKey)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	if compressed {
		req.Header.Set("Content-Encoding", "gzip")
	}

	resp, err := a.Client.Do(req)
	if resp != nil {
		defer func() {
			closeErr := resp.Body.Close()
			if closeErr != nil {
				fmt.Fprintf(a.Log, "skunk: error closing response body: %v\n", closeErr)
			}
		}()
	}

	if err != nil {
		return err
	}

	if resp.StatusCode == 200 {
		return nil
	}

	var nrErr struct {
		Error string `json:"error"`
	}
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(nrErr); err == nil && len(nrErr.Error) > 0 {
		fmt.Fprintf(a.Log, "skunk: received NewRelic error: %s\n", nrErr.Error)
	}
	if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
		fmt.Fprintf(a.Log, "skunk: error discarding body remainder: %v\n", err)
	}

	return statusError(resp)
}

func statusError(resp *http.Response) error {
	code := resp.StatusCode
	switch {
	case code >= 200 && code < 300:
		return nil
	case code == 400:
		return mkerr(ErrBadPayload, nil)
	case code == 403:
		return mkerr(ErrForbidden, nil)
	case code == 404:
		return mkerr(ErrBadRequest, nil)
	case code == 405:
		return mkerr(ErrBadRequest, nil)
	case code == 413:
		return mkerr(ErrBodyTooLarge, nil)
	case code >= 500 && code < 600:
		return mkerr(errMustRetry, nil)
	default:
		return fmt.Errorf("skunk: got unexpected status code %d %s from NewRelic.", code, resp.Status)
	}
}

// Component gets a component with the given name and GUID from the Agent. If no such component exists, then a new one
// is allocated and it is returned. No tests are done to ensure that components with the same name but a different GUID
// or vice versa are allocated, so it is possible to end up with potentially inconsistent data.
func (a *Agent) Component(name, guid string) (*Component, error) {
	if len(name) > 32 {
		// Per NewRelic, names must be <= 32 characters in length.
		return nil, mkerr(ErrNameTooLong, nil)
	}

	out := make(chan *Component)
	a.ops <- (addComponent{name, guid, out}).Exec
	return <-out, nil
}

// addComponent is a small wrapper around a parameter bundle to create a component and add it to an agent, or raturn an
// agent with the same name and guid.
type addComponent struct {
	name, guid string
	out        chan<- *Component
}

// Exec searches for a component of the name and guid described by ac. If it finds a component in the agent, a, it is
// sent on ac's out channel. Otherwise, a new component is created, added to the agent, and sent on the out channel.
func (ac addComponent) Exec(a *Agent) error {
	body := a.body
	for _, c := range body.Components {
		if c.Name == ac.name && c.GUID == ac.guid {
			ac.out <- c
			return nil
		}
	}

	c := &Component{
		Name:    ac.name,
		GUID:    ac.guid,
		Metrics: make(map[string]Metric),
		agent:   a,
	}
	body.Components = append(body.Components, c)
	ac.out <- c

	return nil
}

// clear empties out all metrics held by the agent. This should be called after a payload has been successfully sent to
// reset all components to a pristine state.
func (a *Agent) clear() {
	for _, c := range a.body.Components {
		// Allocate a new Metrics map and reuse the old map's length as its capacity.
		c.Metrics = make(map[string]Metric, len(c.Metrics))
		c.Duration.Duration = 0 // Should be zero, but clear() says it'll make it pristine, so zero it anyway.
		c.start = time.Time{}
	}
}

// getPayload returns a JSON payload as a byte slice to send to NewRelic as its POSTed body. The resulting JSON does not
// include components without metrics.
func (a *Agent) getPayload(w io.Writer, from time.Time, compressed bool) (err error) {
	// Make a copy of the body and exclude components without metrics.
	body := *a.body
	body.Components = make([]*Component, 0, len(body.Components))
	for _, com := range a.body.Components {
		if len(com.Metrics) == 0 || com.start.IsZero() {
			continue
		}

		dupe := *com
		dupe.Duration.Duration = from.Sub(com.start)
		if dupe.Duration.Duration < 0 {
			// Metrics from the future aren't allowed.
			dupe.Duration.Duration = 0
		}

		body.Components = append(body.Components, &dupe)
	}

	if len(body.Components) == 0 {
		return mkerr(errNoMetrics, nil)
	}

	if compressed {
		zipWriter := gzip.NewWriter(w)
		defer func() {
			if err == nil {
				err = zipWriter.Close()
			}
		}()
		w = zipWriter
	}
	encoder := json.NewEncoder(w)
	return encoder.Encode(body)
}
<|endoftext|>


<commit_before>// Package ca provides easy to use certificate authority related functions.
// This is a lightweight wrapper around "crypto/x509" package for
// creating CA certs, client certs, signing requests, and more.
//
// Any "cert, key []byte" type of function parameters and return types are
// always PEM encoded X.509 certificate and private key pairs.
// You can store the certificate/key pair with standard naming as
// "cert.pem" and "key.pem" in the file system.
//
// This package is mostly based on the example code provided at:
// http://golang.org/src/crypto/tls/generate_cert.go
package ca

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"fmt"
	"math/big"
	"net"
	"strings"
	"time"
)

// CertChain is a ready to use certificate chain object, created by GenCertChain function.
// Byte slices are the PEM encoded X.509 certificate and private key pairs.
// ServerTLSConf/ClientTLSConf are tead to use tls.Config objects for a TLS server and client.
type CertChain struct {
	RootCACert,
	RootCAKey,
	IntCACert,
	IntCAKey,
	ServerCert,
	ServerKey,
	ClientCert,
	ClientKey []byte
	ServerTLSConf,
	ClientTLSConf *tls.Config
}

// GenCertChain generates an entire certificate chain with the following hierarchy:
// Root CA -> Intermediate CA -> Server Certificate & Client Certificate
//
// name = Certificate name. i.e. FooBar
// host = Comma-separated hostnames and IPs to generate the server certificate for. i.e. "localhost,127.0.0.1"
// hostName = Server host address. i.e. foobar.com
func GenCertChain(name, host, hostName string, validFor time.Duration, keyLength int) (c CertChain, err error) {

	// create certificate chain
	if c.RootCACert, c.RootCAKey, err = GenCACert(pkix.Name{
		Organization:       []string{name},
		OrganizationalUnit: []string{name + " Certificate Authority"},
		CommonName:         name + " Root CA",
	}, time.Hour, keyLength, nil, nil); err != nil {
		err = fmt.Errorf("Failed to create root CA certificate: %v", err)
		return
	}

	if c.IntCACert, c.IntCAKey, err = GenCACert(pkix.Name{
		Organization:       []string{name},
		OrganizationalUnit: []string{name + " Intermediate Certificate Authority"},
		CommonName:         name + " Intermadiate CA",
	}, time.Hour, keyLength, c.RootCACert, c.RootCAKey); err != nil {
		err = fmt.Errorf("Failed to create intermediate CA certificate: %v", err)
		return
	}

	if c.ServerCert, c.ServerKey, err = GenServerCert(pkix.Name{
		Organization: []string{name},
		CommonName:   hostName,
	}, host, time.Hour, keyLength, c.IntCACert, c.IntCAKey); err != nil {
		err = fmt.Errorf("Failed to create server certificate: %v", err)
		return
	}

	if c.ClientCert, c.ClientKey, err = GenClientCert(pkix.Name{
		Organization: []string{name},
		CommonName:   name,
	}, time.Hour, keyLength, c.IntCACert, c.IntCAKey); err != nil {
		err = fmt.Errorf("Failed to create client certificate: %v", err)
		return
	}

	// crate server tls.Config object
	serverTLSCert, e := tls.X509KeyPair(c.ServerCert, c.ServerKey)
	if e != nil {
		err = fmt.Errorf("Failed to parse the newly created server certificate or the key: %v", e)
		return
	}

	// serverTLSCert.Certificate = append(serverTLSCert.Certificate, c.IntCACert, c.RootCACert)
	// if serverTLSCert.Leaf, err = x509.ParseCertificate(c.ServerCert); err != nil {
	// 	err = fmt.Errorf("Failed to parse the newly created server certificate: %v", err)
	// 	return
	// }

	clientCACertPool := x509.NewCertPool()
	if !clientCACertPool.AppendCertsFromPEM(c.IntCACert) {
		err = errors.New("Failed to parse the newly created intermediate CA certificate.")
		return
	}

	c.ServerTLSConf = &tls.Config{
		Certificates: []tls.Certificate{serverTLSCert},
		ClientCAs:    clientCACertPool,
		ClientAuth:   tls.RequireAndVerifyClientCert,
	}

	// create client tls.Config object
	clientTLSCert, e := tls.X509KeyPair(c.ClientCert, c.ClientKey)
	if e != nil {
		err = fmt.Errorf("Failed to parse the newly created client certificate or the key: %v", e)
		return
	}

	// clientTLSCert.Certificate = append(clientTLSCert.Certificate, c.IntCACert, c.RootCACert)
	// if clientTLSCert.Leaf, err = x509.ParseCertificate(c.ClientCert); err != nil {
	// 	err = fmt.Errorf("Failed to parse the newly created client certificate: %v", err)
	// 	return
	// }

	rootCACertPool := x509.NewCertPool()
	if !rootCACertPool.AppendCertsFromPEM(c.IntCACert) {
		err = errors.New("Failed to parse the newly created intermediate CA certificate.")
		return
	}

	c.ClientTLSConf = &tls.Config{
		Certificates: []tls.Certificate{clientTLSCert},
		RootCAs:      clientCACertPool,
	}

	return
}

// GenCACert generates a CA certificate.
// If signingCert and signingKey are not provided, the certificate is created as a self-signed root CA.
// If signingCert and signingKey are provided, the certificate is created as an intermediate CA, signed with provided certificate.
// The generated certificate can only be used for signing other certificates and CRLs.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func GenCACert(subject pkix.Name, validFor time.Duration, keyLength int, signingCert, signingKey []byte) (cert, key []byte, err error) {
	var (
		sc, c *x509.Certificate
		sk, k *rsa.PrivateKey
	)

	if c, k, err = createBaseCert(subject, validFor, keyLength); err != nil {
		return
	}

	if signingCert == nil || signingKey == nil {
		sc = c
		sk = k
	} else if sc, sk, err = parseCertAndKey(signingCert, signingKey); err != nil {
		return
	}

	c.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
	c.IsCA = true

	cert, key, err = signAndEncodeCert(sc, sk, c, k)
	return
}

// GenServerCert generates a hosting certificate for TLS servers.
// host = Comma-separated hostnames and IPs to generate a certificate for. i.e. "localhost,127.0.0.1"
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func GenServerCert(subject pkix.Name, host string, validFor time.Duration, keyLength int, signingCert, signingKey []byte) (cert, key []byte, err error) {
	var (
		sc, c *x509.Certificate
		sk, k *rsa.PrivateKey
	)

	if sc, sk, err = parseCertAndKey(signingCert, signingKey); err != nil {
		return
	}

	if c, k, err = createBaseCert(subject, validFor, keyLength); err != nil {
		return
	}

	c.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
	c.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	c.IsCA = false
	setHosts(host, c)

	cert, key, err = signAndEncodeCert(sc, sk, c, k)
	return
}

// GenClientCert generates a client certificate.
// The generated certificate will have its extended key usage set to 'client authentication' and will be ready for use in TLS client authentication.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func GenClientCert(subject pkix.Name, validFor time.Duration, keyLength int, signingCert, signingKey []byte) (cert, key []byte, err error) {
	var (
		sc, c *x509.Certificate
		sk, k *rsa.PrivateKey
	)

	if sc, sk, err = parseCertAndKey(signingCert, signingKey); err != nil {
		return
	}

	if c, k, err = createBaseCert(subject, validFor, keyLength); err != nil {
		return
	}

	c.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
	c.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
	c.IsCA = false

	cert, key, err = signAndEncodeCert(sc, sk, c, k)
	return
}

// ExportCertChain takes individual PEM encoded X.509 certificates in a trust chain and produces a single certificate chain file.
// Input certificates should start with leaf certificate and end with the root CA certificate. i.e. [0] Leaf, [1] Intermediate CA, [2] Root CA
func ExportCertChain(certs []byte) ([]byte, error) {
	return nil, nil
}

// createBaseCert creates and returns x509.Certificate (unsigned) and rsa.PrivateKey objects with basic paramters set.
func createBaseCert(subject pkix.Name, validFor time.Duration, keyLength int) (*x509.Certificate, *rsa.PrivateKey, error) {
	privKey, err := rsa.GenerateKey(rand.Reader, keyLength)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate certificate private key using RSA: %v", err)
	}

	notBefore := time.Now()
	notAfter := notBefore.Add(validFor)
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate the certificate serial number: %v", err)
	}

	cert := x509.Certificate{
		SerialNumber:          serialNumber,
		Subject:               subject,
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		BasicConstraintsValid: true,
	}

	return &cert, privKey, nil
}

// setHosts parses the comma separated host name / IP list and adds them to Subject Alternate Name list of a server/hosting certificate.
func setHosts(host string, cert *x509.Certificate) {
	hosts := strings.Split(host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			cert.IPAddresses = append(cert.IPAddresses, ip)
		} else {
			cert.DNSNames = append(cert.DNSNames, h)
		}
	}
}

// Parses PEM encoded X.509 certificate and private key pair into x509.Certificate and rsa.PrivateKey objects.
func parseCertAndKey(cert, key []byte) (c *x509.Certificate, k *rsa.PrivateKey, err error) {
	pc, _ := pem.Decode(cert)
	if c, err = x509.ParseCertificate(pc.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse private key with error: %v", err)
		return
	}

	pk, _ := pem.Decode(key)
	if k, err = x509.ParsePKCS1PrivateKey(pk.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse certificate with error: %v", err)
		return
	}

	return
}

// signAndEncodeCert signs a given certificate with given signing cert/key pair and encodes resulting signed cert and private key in PEM format and returns.
func signAndEncodeCert(signingCert *x509.Certificate, signingKey *rsa.PrivateKey, c *x509.Certificate, k *rsa.PrivateKey) (cert, key []byte, err error) {
	certDerBytes, err := x509.CreateCertificate(rand.Reader, c, signingCert, &k.PublicKey, signingKey)
	if err != nil {
		return
	}

	cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDerBytes})
	key = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)})
	return
}
<commit_msg>parse leaf certs to accelerate client auth<commit_after>// Package ca provides easy to use certificate authority related functions.
// This is a lightweight wrapper around "crypto/x509" package for
// creating CA certs, client certs, signing requests, and more.
//
// Any "cert, key []byte" type of function parameters and return types are
// always PEM encoded X.509 certificate and private key pairs.
// You can store the certificate/key pair with standard naming as
// "cert.pem" and "key.pem" in the file system.
//
// This package is mostly based on the example code provided at:
// http://golang.org/src/crypto/tls/generate_cert.go
package ca

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"fmt"
	"math/big"
	"net"
	"strings"
	"time"
)

// CertChain is a ready to use certificate chain object, created by GenCertChain function.
// Byte slices are the PEM encoded X.509 certificate and private key pairs.
// ServerTLSConf/ClientTLSConf are tead to use tls.Config objects for a TLS server and client.
type CertChain struct {
	RootCACert,
	RootCAKey,
	IntCACert,
	IntCAKey,
	ServerCert,
	ServerKey,
	ClientCert,
	ClientKey []byte
	ServerTLSConf,
	ClientTLSConf *tls.Config
}

// GenCertChain generates an entire certificate chain with the following hierarchy:
// Root CA -> Intermediate CA -> Server Certificate & Client Certificate
//
// name = Certificate name. i.e. FooBar
// host = Comma-separated hostnames and IPs to generate the server certificate for. i.e. "localhost,127.0.0.1"
// hostName = Server host address. i.e. foobar.com
func GenCertChain(name, host, hostName string, validFor time.Duration, keyLength int) (c CertChain, err error) {

	// create certificate chain
	if c.RootCACert, c.RootCAKey, err = GenCACert(pkix.Name{
		Organization:       []string{name},
		OrganizationalUnit: []string{name + " Certificate Authority"},
		CommonName:         name + " Root CA",
	}, time.Hour, keyLength, nil, nil); err != nil {
		err = fmt.Errorf("Failed to create root CA certificate: %v", err)
		return
	}

	if c.IntCACert, c.IntCAKey, err = GenCACert(pkix.Name{
		Organization:       []string{name},
		OrganizationalUnit: []string{name + " Intermediate Certificate Authority"},
		CommonName:         name + " Intermadiate CA",
	}, time.Hour, keyLength, c.RootCACert, c.RootCAKey); err != nil {
		err = fmt.Errorf("Failed to create intermediate CA certificate: %v", err)
		return
	}

	if c.ServerCert, c.ServerKey, err = GenServerCert(pkix.Name{
		Organization: []string{name},
		CommonName:   hostName,
	}, host, time.Hour, keyLength, c.IntCACert, c.IntCAKey); err != nil {
		err = fmt.Errorf("Failed to create server certificate: %v", err)
		return
	}

	if c.ClientCert, c.ClientKey, err = GenClientCert(pkix.Name{
		Organization: []string{name},
		CommonName:   name,
	}, time.Hour, keyLength, c.IntCACert, c.IntCAKey); err != nil {
		err = fmt.Errorf("Failed to create client certificate: %v", err)
		return
	}

	// crate server tls.Config object
	serverTLSCert, e := tls.X509KeyPair(c.ServerCert, c.ServerKey)
	if e != nil {
		err = fmt.Errorf("Failed to parse the newly created server certificate or the key: %v", e)
		return
	}

	// todo #1: I'm not use if current Go TLS implementation verifies the entire cert chain (doesn't seem like so) so below line might not be needed atm (also see todo #3 down below)
	// serverTLSCert.Certificate = append(serverTLSCert.Certificate, c.IntCACert, c.RootCACert)

	sc, _ := pem.Decode(c.ServerCert)
	if serverTLSCert.Leaf, err = x509.ParseCertificate(sc.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse the newly created server certificate: %v", err)
		return
	}

	clientCACertPool := x509.NewCertPool()
	if !clientCACertPool.AppendCertsFromPEM(c.IntCACert) {
		err = errors.New("Failed to parse the newly created intermediate CA certificate.")
		return
	}

	c.ServerTLSConf = &tls.Config{
		Certificates: []tls.Certificate{serverTLSCert},
		ClientCAs:    clientCACertPool,
		ClientAuth:   tls.RequireAndVerifyClientCert,
	}

	// create client tls.Config object
	clientTLSCert, e := tls.X509KeyPair(c.ClientCert, c.ClientKey)
	if e != nil {
		err = fmt.Errorf("Failed to parse the newly created client certificate or the key: %v", e)
		return
	}

	// todo #1: (same as above)
	// clientTLSCert.Certificate = append(clientTLSCert.Certificate, c.IntCACert, c.RootCACert)

	cc, _ := pem.Decode(c.ClientCert)
	if clientTLSCert.Leaf, err = x509.ParseCertificate(cc.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse the newly created client certificate: %v", err)
		return
	}

	rootCACertPool := x509.NewCertPool()
	if !rootCACertPool.AppendCertsFromPEM(c.IntCACert) {
		err = errors.New("Failed to parse the newly created intermediate CA certificate.")
		return
	}

	c.ClientTLSConf = &tls.Config{
		Certificates: []tls.Certificate{clientTLSCert},
		RootCAs:      clientCACertPool,
	}

	return
}

// GenCACert generates a CA certificate.
// If signingCert and signingKey are not provided, the certificate is created as a self-signed root CA.
// If signingCert and signingKey are provided, the certificate is created as an intermediate CA, signed with provided certificate.
// The generated certificate can only be used for signing other certificates and CRLs.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func GenCACert(subject pkix.Name, validFor time.Duration, keyLength int, signingCert, signingKey []byte) (cert, key []byte, err error) {
	var (
		sc, c *x509.Certificate
		sk, k *rsa.PrivateKey
	)

	if c, k, err = createBaseCert(subject, validFor, keyLength); err != nil {
		return
	}

	if signingCert == nil || signingKey == nil {
		sc = c
		sk = k
	} else if sc, sk, err = parseCertAndKey(signingCert, signingKey); err != nil {
		return
	}

	c.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
	c.IsCA = true

	cert, key, err = signAndEncodeCert(sc, sk, c, k)
	return
}

// GenServerCert generates a hosting certificate for TLS servers.
// host = Comma-separated hostnames and IPs to generate a certificate for. i.e. "localhost,127.0.0.1"
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func GenServerCert(subject pkix.Name, host string, validFor time.Duration, keyLength int, signingCert, signingKey []byte) (cert, key []byte, err error) {
	var (
		sc, c *x509.Certificate
		sk, k *rsa.PrivateKey
	)

	if sc, sk, err = parseCertAndKey(signingCert, signingKey); err != nil {
		return
	}

	if c, k, err = createBaseCert(subject, validFor, keyLength); err != nil {
		return
	}

	c.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
	c.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	c.IsCA = false
	setHosts(host, c)

	cert, key, err = signAndEncodeCert(sc, sk, c, k)
	return
}

// GenClientCert generates a client certificate.
// The generated certificate will have its extended key usage set to 'client authentication' and will be ready for use in TLS client authentication.
// The returned slices are the PEM encoded X.509 certificate and private key pair.
func GenClientCert(subject pkix.Name, validFor time.Duration, keyLength int, signingCert, signingKey []byte) (cert, key []byte, err error) {
	var (
		sc, c *x509.Certificate
		sk, k *rsa.PrivateKey
	)

	if sc, sk, err = parseCertAndKey(signingCert, signingKey); err != nil {
		return
	}

	if c, k, err = createBaseCert(subject, validFor, keyLength); err != nil {
		return
	}

	c.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
	c.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
	c.IsCA = false

	cert, key, err = signAndEncodeCert(sc, sk, c, k)
	return
}

// todo #3: this might be only useful if todo #1 is necessary
// // ExportCertChain takes individual PEM encoded X.509 certificates in a trust chain and produces a single certificate chain file.
// // Input certificates should start with leaf certificate and end with the root CA certificate. i.e. [0] Leaf, [1] Intermediate CA, [2] Root CA
// func ExportCertChain(certs []byte) ([]byte, error) {
// 	return nil, nil
// }

// createBaseCert creates and returns x509.Certificate (unsigned) and rsa.PrivateKey objects with basic paramters set.
func createBaseCert(subject pkix.Name, validFor time.Duration, keyLength int) (*x509.Certificate, *rsa.PrivateKey, error) {
	privKey, err := rsa.GenerateKey(rand.Reader, keyLength)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate certificate private key using RSA: %v", err)
	}

	notBefore := time.Now()
	notAfter := notBefore.Add(validFor)
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate the certificate serial number: %v", err)
	}

	cert := x509.Certificate{
		SerialNumber:          serialNumber,
		Subject:               subject,
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		BasicConstraintsValid: true,
	}

	return &cert, privKey, nil
}

// setHosts parses the comma separated host name / IP list and adds them to Subject Alternate Name list of a server/hosting certificate.
func setHosts(host string, cert *x509.Certificate) {
	hosts := strings.Split(host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			cert.IPAddresses = append(cert.IPAddresses, ip)
		} else {
			cert.DNSNames = append(cert.DNSNames, h)
		}
	}
}

// Parses PEM encoded X.509 certificate and private key pair into x509.Certificate and rsa.PrivateKey objects.
func parseCertAndKey(cert, key []byte) (c *x509.Certificate, k *rsa.PrivateKey, err error) {
	pc, _ := pem.Decode(cert)
	if c, err = x509.ParseCertificate(pc.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse private key with error: %v", err)
		return
	}

	pk, _ := pem.Decode(key)
	if k, err = x509.ParsePKCS1PrivateKey(pk.Bytes); err != nil {
		err = fmt.Errorf("Failed to parse certificate with error: %v", err)
		return
	}

	return
}

// signAndEncodeCert signs a given certificate with given signing cert/key pair and encodes resulting signed cert and private key in PEM format and returns.
func signAndEncodeCert(signingCert *x509.Certificate, signingKey *rsa.PrivateKey, c *x509.Certificate, k *rsa.PrivateKey) (cert, key []byte, err error) {
	certDerBytes, err := x509.CreateCertificate(rand.Reader, c, signingCert, &k.PublicKey, signingKey)
	if err != nil {
		return
	}

	cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDerBytes})
	key = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)})
	return
}
<|endoftext|>


<commit_before>package main

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

const (
	VERSION = "0.0.1"
)

func apiReq(meth string, url string) interface{} {
	client := &http.Client{}
	req, err := http.NewRequest(meth, url, nil)
	req.SetBasicAuth("x", os.Getenv("HEROKU_API_KEY"))
	req.Header.Add("User-Agent", fmt.Sprintf("hk/%s", VERSION))
	req.Header.Add("Accept", "application/json")
	res, err := client.Do(req)
		if err != nil {
		panic(err)
	}
	if (res.StatusCode == 401) {
		error("Unauthorized")
	}
	if (res.StatusCode != 200) {
		error("Unexpected error")
	}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	var data interface{}
	err = json.Unmarshal(body, &data)
	if err != nil {
		panic(err)
	}
	return data
}

func error(msg string) {
	fmt.Fprintf(os.Stderr, "Error: %s.\n", msg)
	os.Exit(1)
}

func unrecArg(arg string) {
	error(fmt.Sprintf("Unrecognized argument '%s'", arg))
}

func unrecCmd(cmd string) {
	error(fmt.Sprintf("'%s' is not an hk command. See 'hk help'", cmd))
}

func envHelp() {
	fmt.Printf("Usage: hk env -a <app>\n\n")
	fmt.Printf("Show all config vars.")
	os.Exit(0)
}

func env() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help env")
	}
	appName := os.Args[3]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	config := data.(map[string]interface{})
	for k, v := range config {
		fmt.Printf("%s=%v\n", k, v)
	}
	os.Exit(0)
}

func getHelp() {
	fmt.Printf("Usage: hk get -a <app> <key>\n\n")
	fmt.Printf("Get the value of a config var.\n")
	os.Exit(0)
}

func get() {
	if (len(os.Args) != 5) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help get")
	}
	appName := os.Args[3]
	key := os.Args[4]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	config := data.(map[string]interface{})
	value, found := config[key]
	if !found {
		error(fmt.Sprintf("No such key as '%s'", key))
	}
	fmt.Println(value)
	os.Exit(0)
}

func listHelp() {
	fmt.Printf("Usage: hk list\n\n")
	fmt.Printf("List accessible apps.\n")
	os.Exit(0)
}

func list() {
	if len(os.Args) != 2 {
		unrecArg(os.Args[2])
	}
	data := apiReq("GET", "https://api.heroku.com/apps")
	apps := data.([]interface{})
	for i := range apps {
		app := apps[i].(map[string]interface{})
		fmt.Printf("%s\n", app["name"])
	}
	os.Exit(0)
}

func psHelp() {
	fmt.Printf("Usage: hk ps -a <app>\n\n")
	fmt.Printf("List apps processes.\n")
	os.Exit(0)
}

func ps() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help ps")
	}
	appName := os.Args[3]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/ps", appName))
	processes := data.([]interface{})
	for i := range processes {
		process := processes[i].(map[string]interface{})
		fmt.Printf("%v\n", process)
	}
	os.Exit(0)
}

func versionHelp() {
	fmt.Printf("Usage: hk version\n\n")
	fmt.Printf("Show hk client version.\n")
	os.Exit(0)
}

func version() {
	if len(os.Args) != 2 {
	  unrecArg(os.Args[2])
  }
	fmt.Printf("%s\n", VERSION)
	os.Exit(0)
}

func usage() {
	fmt.Printf("Usage: hk <command> [-a <app>] [command-specific-options]\n\n")
	fmt.Printf("Supported hk commands are:\n")
	fmt.Printf("  addons          List add-ons\n")
	fmt.Printf("  addons-add      Add an add-on\n")
	fmt.Printf("  addons-open     Open an add-on page\n")
	fmt.Printf("  addons-remove   Remove an add-on \n")
	fmt.Printf("  create          Create an app\n")
	fmt.Printf("  destroy         Destroy an app\n")
	fmt.Printf("  env             List config vars\n")
	fmt.Printf("  get             Get config var\n")
	fmt.Printf("  help            Show this help\n")
	fmt.Printf("  info            Show app info\n")
	fmt.Printf("  list            List apps\n")
	fmt.Printf("  login           Log in\n")
	fmt.Printf("  logout          Log out\n")
	fmt.Printf("  logs            Show logs\n")
	fmt.Printf("  open            Open app\n")
	fmt.Printf("  pg              List databases\n")
	fmt.Printf("  pg-info         Show database info\n")
	fmt.Printf("  pg-promote      Promote a database\n")
	fmt.Printf("  ps-psql         Open a psql database shell\n")
	fmt.Printf("  pg-wait         Await a database\n")
	fmt.Printf("  ps              List processes\n")
	fmt.Printf("  release         Show release info\n")
	fmt.Printf("  releases        List releases\n")
	fmt.Printf("  rename          Rename an app\n")
	fmt.Printf("  restart         Restart processes\n")
	fmt.Printf("  rollback        Rollback to a previous release\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  set             Set config var\n")
	fmt.Printf("  scale           Scale processes\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  stop            Stop a process\n")
	fmt.Printf("  token           Show auth token\n")
	fmt.Printf("  unset           Unset config vars\n")
	fmt.Printf("  version         Display version\n\n")
	fmt.Printf("See 'hk help <command>' for more information on a specific command.\n")
	os.Exit(0)
}

func help() {
	if len(os.Args) <= 2 {
		usage()
	} else {
		cmd := os.Args[2]
		switch cmd {
	  case "env":
		  envHelp()
	  case "get":
			getHelp()
		case "list":
		  listHelp()
	  case "ps":
		  psHelp()
		case "version":
			versionHelp()
		}
		unrecCmd(cmd)
	}
}

func main() {
	if len(os.Args) <= 1 {
		usage()
	} else {
		cmd := os.Args[1]
		switch cmd {
		case "env":
			env()
		case "get":
			get()
		case "help":
			help()
		case "list":
			list()
		case "ps":
			ps()
		case "version":
			version()
		}
		unrecCmd(cmd)
	}
}
<commit_msg>sort by i have found you<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
)

const (
	VERSION = "0.0.1"
)

// sort interface-typed arrays by first-class functions
type ByFn struct{
	elems []interface{}
	comp func(a, b interface{}) bool
}
func (c ByFn) Len() int { return len(c.elems)  }
func (c ByFn) Less(i, j int) bool { return c.comp(c.elems[i], c.elems[j]) }
func (c ByFn) Swap(i, j int) { c.elems[i], c.elems[j] = c.elems[j], c.elems[i] }

// generic api requests
func apiReq(meth string, url string) interface{} {
	client := &http.Client{}
	req, err := http.NewRequest(meth, url, nil)
	req.SetBasicAuth("x", os.Getenv("HEROKU_API_KEY"))
	req.Header.Add("User-Agent", fmt.Sprintf("hk/%s", VERSION))
	req.Header.Add("Accept", "application/json")
	res, err := client.Do(req)
		if err != nil {
		panic(err)
	}
	if (res.StatusCode == 401) {
		error("Unauthorized")
	}
	if (res.StatusCode != 200) {
		error("Unexpected error")
	}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	var data interface{}
	err = json.Unmarshal(body, &data)
	if err != nil {
		panic(err)
	}
	return data
}

// error formatting
func error(msg string) {
	fmt.Fprintf(os.Stderr, "Error: %s.\n", msg)
	os.Exit(1)
}

func unrecArg(arg string) {
	error(fmt.Sprintf("Unrecognized argument '%s'", arg))
}

func unrecCmd(cmd string) {
	error(fmt.Sprintf("'%s' is not an hk command. See 'hk help'", cmd))
}

// commands
func envHelp() {
	fmt.Printf("Usage: hk env -a <app>\n\n")
	fmt.Printf("Show all config vars.")
	os.Exit(0)
}

func env() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help env")
	}
	appName := os.Args[3]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	config := data.(map[string]interface{})
	for k, v := range config {
		fmt.Printf("%s=%v\n", k, v)
	}
	os.Exit(0)
}

func getHelp() {
	fmt.Printf("Usage: hk get -a <app> <key>\n\n")
	fmt.Printf("Get the value of a config var.\n")
	os.Exit(0)
}

func get() {
	if (len(os.Args) != 5) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help get")
	}
	appName := os.Args[3]
	key := os.Args[4]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	config := data.(map[string]string)
	value, found := config[key]
	if !found {
		error(fmt.Sprintf("No such key as '%s'", key))
	}
	fmt.Println(value)
	os.Exit(0)
}

func listHelp() {
	fmt.Printf("Usage: hk list\n\n")
	fmt.Printf("List accessible apps.\n")
	os.Exit(0)
}

func list() {
	if len(os.Args) != 2 {
		unrecArg(os.Args[2])
	}
	data := apiReq("GET", "https://api.heroku.com/apps")
	apps := data.([]interface{})
	for i := range apps {
		app := apps[i].(map[string]interface{})
		fmt.Printf("%s\n", app["name"])
	}
	os.Exit(0)
}

func psHelp() {
	fmt.Printf("Usage: hk ps -a <app>\n\n")
	fmt.Printf("List apps processes.\n")
	os.Exit(0)
}

func ps() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See hk help ps")
	}
	appName := os.Args[3]
	data := apiReq("GET", fmt.Sprintf("https://api.heroku.com/apps/%s/ps", appName))
	processes := data.([]interface{})
	sort.Sort(ByFn{
		processes,
		func(a, b interface{}) bool {
			p1 := a.(map[string]interface{})["process"].(string)
			p2 := b.(map[string]interface{})["process"].(string)
		  return p1 < p2
	  }})
	for i := range processes {
		process := processes[i].(map[string]interface{})
		fmt.Printf("%v\n", process["process"])
	}
	os.Exit(0)
}

func versionHelp() {
	fmt.Printf("Usage: hk version\n\n")
	fmt.Printf("Show hk client version.\n")
	os.Exit(0)
}

func version() {
	if len(os.Args) != 2 {
	  unrecArg(os.Args[2])
  }
	fmt.Printf("%s\n", VERSION)
	os.Exit(0)
}

func help() {
	if len(os.Args) <= 2 {
		usage()
	} else {
		cmd := os.Args[2]
		switch cmd {
	  case "env":
		  envHelp()
	  case "get":
			getHelp()
		case "list":
		  listHelp()
	  case "ps":
		  psHelp()
		case "version":
			versionHelp()
		}
		unrecCmd(cmd)
	}
}

// top-level usage
func usage() {
	fmt.Printf("Usage: hk <command> [-a <app>] [command-specific-options]\n\n")
	fmt.Printf("Supported hk commands are:\n")
	fmt.Printf("  addons          List add-ons\n")
	fmt.Printf("  addons-add      Add an add-on\n")
	fmt.Printf("  addons-open     Open an add-on page\n")
	fmt.Printf("  addons-remove   Remove an add-on \n")
	fmt.Printf("  create          Create an app\n")
	fmt.Printf("  destroy         Destroy an app\n")
	fmt.Printf("  env             List config vars\n")
	fmt.Printf("  get             Get config var\n")
	fmt.Printf("  help            Show this help\n")
	fmt.Printf("  info            Show app info\n")
	fmt.Printf("  list            List apps\n")
	fmt.Printf("  login           Log in\n")
	fmt.Printf("  logout          Log out\n")
	fmt.Printf("  logs            Show logs\n")
	fmt.Printf("  open            Open app\n")
	fmt.Printf("  pg              List databases\n")
	fmt.Printf("  pg-info         Show database info\n")
	fmt.Printf("  pg-promote      Promote a database\n")
	fmt.Printf("  ps-psql         Open a psql database shell\n")
	fmt.Printf("  pg-wait         Await a database\n")
	fmt.Printf("  ps              List processes\n")
	fmt.Printf("  release         Show release info\n")
	fmt.Printf("  releases        List releases\n")
	fmt.Printf("  rename          Rename an app\n")
	fmt.Printf("  restart         Restart processes\n")
	fmt.Printf("  rollback        Rollback to a previous release\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  set             Set config var\n")
	fmt.Printf("  scale           Scale processes\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  stop            Stop a process\n")
	fmt.Printf("  token           Show auth token\n")
	fmt.Printf("  unset           Unset config vars\n")
	fmt.Printf("  version         Display version\n\n")
	fmt.Printf("See 'hk help <command>' for more information on a specific command.\n")
	os.Exit(0)
}

// entry point
func main() {
	if len(os.Args) <= 1 {
		usage()
	} else {
		cmd := os.Args[1]
		switch cmd {
		case "env":
			env()
		case "get":
			get()
		case "help":
			help()
		case "list":
			list()
		case "ps":
			ps()
		case "version":
			version()
		}
		unrecCmd(cmd)
	}
}
<|endoftext|>


<commit_before>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/smtp"

	"appengine"
	"appengine/urlfetch"
)

var statusContent string = "Default status"

type webPage struct {
	Title string
}

type nodeStats struct {
	nodeID           int    `json:"ID"`
	hasTask          bool   `json:"hasTask"`
	taskStatus       string `json:"taskStatus"`
	taskResult       string `json:"taskResult"`
	taskFragmentBody string `json:"taskFragmentBody"`
	taskBody         string `json:"taskBody"`
}

type gmailUser struct {
	name string
	pswd string
}

func sendMail(msg string) {
	mailUser := gmailUser{
		"golangapplication@gmail.com",
		"",
	}
	auth := smtp.PlainAuth("",
		mailUser.name,
		mailUser.pswd,
		"smtp.gmail.com",
	)
	err := smtp.SendMail(
		"smtp.gmail.com:587",
		auth,
		mailUser.name,
		[]string{"rec@mail.com"},
		[]byte(msg),
	)
	if err != nil {
		log.Fatal(err)
	}
}

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)
		newStr := buf.String()

		thisNodeStats := nodeStats{
			1,
			false,
			"not running",
			"empty",
			"empty fragment",
			"empty",
		}

		jsonNodeStats, err1 := json.Marshal(thisNodeStats)
		if err1 != nil {
			panic(err1)
		}

		fmt.Fprintf(w, "Get data by params in POST - OK "+string(jsonNodeStats))
		//statusContent = "POST request handled, " + "Node id: " + string(nodeSends.id) + ", Echo content: " + nodeSends.content
		statusContent = "POST request handled, " + newStr
	}
}

func testEcho(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	c := appengine.NewContext(r)
	var jsonStr = []byte(`{"Message from another node":"` + r.FormValue("echoContent") + `"}`)
	//bs := []byte{1, 2, 3}
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode"+r.FormValue("nodeId")+".appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)
}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/echo", testEcho)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<commit_msg>json fix<commit_after>//Command to test application without deploy:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/smtp"

	"appengine"
	"appengine/urlfetch"
)

var statusContent string = "Default status"

type webPage struct {
	Title string
}

type nodeStats struct {
	NodeID           int    `json:"ID"`
	HasTask          bool   `json:"hasTask"`
	TaskStatus       string `json:"taskStatus"`
	TaskResult       string `json:"taskResult"`
	TaskFragmentBody string `json:"taskFragmentBody"`
	TaskBody         string `json:"taskBody"`
}

type gmailUser struct {
	name string
	pswd string
}

func sendMail(msg string) {
	mailUser := gmailUser{
		"golangapplication@gmail.com",
		"",
	}
	auth := smtp.PlainAuth("",
		mailUser.name,
		mailUser.pswd,
		"smtp.gmail.com",
	)
	err := smtp.SendMail(
		"smtp.gmail.com:587",
		auth,
		mailUser.name,
		[]string{"rec@mail.com"},
		[]byte(msg),
	)
	if err != nil {
		log.Fatal(err)
	}
}

//wrong func for Google App Engine deployment. Need to use appengine libs...=(
func echo() {

	url := "http://golangappnode1.appspot.com/status"

	var jsonStr = []byte(`{"msg":"Hello!"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go echo()
		fmt.Fprintf(w, "Successful read command/input from web-interface! Input contains - "+r.FormValue("nodeId")+" "+r.FormValue("echoContent"))
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)
		newStr := buf.String()

		thisNodeStats := nodeStats{
			1,
			false,
			"not running",
			"empty",
			"empty fragment",
			"empty",
		}

		jsonNodeStats, err1 := json.Marshal(thisNodeStats)
		if err1 != nil {
			panic(err1)
		}

		fmt.Fprintf(w, "Get data by params in POST - OK "+string(jsonNodeStats))
		//statusContent = "POST request handled, " + "Node id: " + string(nodeSends.id) + ", Echo content: " + nodeSends.content
		statusContent = "POST request handled, " + newStr
	}
}

func testEcho(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	c := appengine.NewContext(r)
	var jsonStr = []byte(`{"Message from another node":"` + r.FormValue("echoContent") + `"}`)
	//bs := []byte{1, 2, 3}
	buf := bytes.NewBuffer(jsonStr)
	client := http.Client{Transport: &urlfetch.Transport{Context: c}}
	resp, err := client.Post("http://goappnode"+r.FormValue("nodeId")+".appspot.com/status", "application/octet-stream", buf)
	if err != nil {
		statusContent = err.Error()
		fmt.Println(err)
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	statusContent = "Response from node - " + string(respBody)
}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	http.HandleFunc("/status", statusServer)
	http.HandleFunc("/echo", testEcho)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<|endoftext|>


<commit_before>package objconv

import (
	"bytes"
	"io"
	"unicode/utf8"
)

// EOL represent end-of-line terminaators.
type EOL string

const (
	// LF is a simple new line sequence.
	LF EOL = "\n"

	// CRLF is a two-byte new line sequence.
	CRLF EOL = "\r\n"
)

// Reader implements the io.Reader interface but reports errors through panics
// instead of returning them.
//
// It's not safe to use the reader concurrently from multiple goroutines.
type Reader struct {
	R io.Reader
	n int
	c [4]byte   // ReadByte and ReadRune buffer
	b [100]byte // ReadLine buffer
}

// NewReader returns a Reader that reads from r.
func NewReader(r io.Reader) *Reader {
	switch v := r.(type) {
	case *Reader:
		return v
	default:
		return &Reader{R: r}
	}
}

// Read reads bytes into b from r, panics if there was an error.
//
// The method returns an error to satisfy the io.Read interface, it will always
// be nil and can be ignored.
func (r *Reader) Read(b []byte) (n int, err error) {
	n, err = r.R.Read(b)

	if n > 0 {
		err = nil
		r.n += n
	} else if err != nil {
		if err == io.EOF && r.n != 0 {
			err = io.ErrUnexpectedEOF
		}
		panic(err)
	}

	return
}

// ReadByte reads a byte from r, panics if there was an error.
//
// The method returns an error to satisfy the io.ByteReader interface, it will
// always be nil and can be ignored.
func (r *Reader) ReadByte() (c byte, err error) {
	r.Read(r.c[:1])
	c = r.c[0]
	return
}

// ReadRune reads a rune from r, panics if there was an error.
//
// The method returns an error to satisfy the io.RuneReader interface, it will
// always be nil and can be ignored.
func (r *Reader) ReadRune() (c rune, n int, err error) {
	b, _ := r.ReadByte()

	if (b & 0x80) == 0 {
		c = rune(b)
		n = 1
		return

	} else if (b & 0xE0) == 0xC0 {
		n = 2

	} else if (b & 0xF0) == 0xE0 {
		n = 3

	} else {
		n = 4
	}

	r.c[0] = b
	r.ReadFull(r.c[1:n])
	c, _ = utf8.DecodeRune(r.c[:n])
	return
}

// ReadLine reads a line ending with eol from r, panics if there was an error.
func (r *Reader) ReadLine(eol EOL) (line []byte) {
	line = r.b[:0]
	end := []byte(eol)

	for !bytes.HasSuffix(line, end) {
		b, _ := r.ReadByte()
		line = append(line, b)
	}

	return line[:len(line)-len(eol)]
}

// ReadFull fills up b with data read from r, panics if there was an error.
func (r *Reader) ReadFull(b []byte) {
	io.ReadFull(r, b)
}

// Writer implements the io.Writer interface but reports errors through panics
// instead of returning them.
//
// It's not safe to use the writer concurrently from multiple goroutines.
type Writer struct {
	W io.Writer
	b [64]byte // buffer
	e error
}

// NewWriter returns a Writer that reads from r.
func NewWriter(w io.Writer) *Writer {
	switch x := w.(type) {
	case *Writer:
		return x
	default:
		return &Writer{W: w}
	}
}

// Write writes b to w, panics if there was an error.
func (w *Writer) Write(b []byte) (n int, err error) {
	if err = w.e; err == nil {
		n, err = w.W.Write(b)
		w.e = err
	}
	return
}

// WriteByte writes b to w, panics if there was an error.
func (w *Writer) WriteByte(b byte) (err error) {
	w.b[0] = b
	_, err = w.Write(w.b[:1])
	return
}

// WriteRune writes r to w, panics if there was an error.
func (w *Writer) WriteRune(r rune) (n int, err error) {
	return w.Write(w.b[:utf8.EncodeRune(w.b[:], r)])
}

// WriteString writes s to w, panics if there was an error.
func (w *Writer) WriteString(s string) (n int, err error) {
	// Writes the string by chunks of len(w.b) at a time.
	for len(s) != 0 {
		n1 := len(w.b)
		n2 := len(s)
		n3 := n1
		n4 := 0

		if n3 > n2 {
			n3 = n2
		}

		copy(w.b[:], s[:n3])
		n4, err = w.Write(w.b[:n3])
		n += n4
		s = s[n3:]
	}
	return
}

type runeWriter interface {
	WriteRune(rune) (int, error)
}

type stringWriter interface {
	WriteString(string) (int, error)
}

type counter struct{ n int }

func (c *counter) Write(b []byte) (n int, err error) {
	n = len(b)
	c.n += n
	return
}

func (c *counter) WriteByte(b byte) (err error) {
	c.n++
	return
}

func (c *counter) WriteRune(r rune) (n int, err error) {
	n = utf8.RuneLen(r)
	c.n += n
	return
}

func (c *counter) WriteString(s string) (n int, err error) {
	n = len(s)
	c.n += n
	return
}
<commit_msg>update doc<commit_after>package objconv

import (
	"bytes"
	"io"
	"unicode/utf8"
)

// EOL represent end-of-line terminaators.
type EOL string

const (
	// LF is a simple new line sequence.
	LF EOL = "\n"

	// CRLF is a two-byte new line sequence.
	CRLF EOL = "\r\n"
)

// Reader implements the io.Reader interface but reports errors through panics
// instead of returning them.
//
// It's not safe to use the reader concurrently from multiple goroutines.
type Reader struct {
	R io.Reader
	n int
	c [4]byte   // ReadByte and ReadRune buffer
	b [100]byte // ReadLine buffer
}

// NewReader returns a Reader that reads from r.
func NewReader(r io.Reader) *Reader {
	switch v := r.(type) {
	case *Reader:
		return v
	default:
		return &Reader{R: r}
	}
}

// Read reads bytes into b from r, panics if there was an error.
//
// The method returns an error to satisfy the io.Read interface, it will always
// be nil and can be ignored.
func (r *Reader) Read(b []byte) (n int, err error) {
	n, err = r.R.Read(b)

	if n > 0 {
		err = nil
		r.n += n
	} else if err != nil {
		if err == io.EOF && r.n != 0 {
			err = io.ErrUnexpectedEOF
		}
		panic(err)
	}

	return
}

// ReadByte reads a byte from r, panics if there was an error.
//
// The method returns an error to satisfy the io.ByteReader interface, it will
// always be nil and can be ignored.
func (r *Reader) ReadByte() (c byte, err error) {
	r.Read(r.c[:1])
	c = r.c[0]
	return
}

// ReadRune reads a rune from r, panics if there was an error.
//
// The method returns an error to satisfy the io.RuneReader interface, it will
// always be nil and can be ignored.
func (r *Reader) ReadRune() (c rune, n int, err error) {
	b, _ := r.ReadByte()

	if (b & 0x80) == 0 {
		c = rune(b)
		n = 1
		return

	} else if (b & 0xE0) == 0xC0 {
		n = 2

	} else if (b & 0xF0) == 0xE0 {
		n = 3

	} else {
		n = 4
	}

	r.c[0] = b
	r.ReadFull(r.c[1:n])
	c, _ = utf8.DecodeRune(r.c[:n])
	return
}

// ReadLine reads a line ending with eol from r, panics if there was an error.
func (r *Reader) ReadLine(eol EOL) (line []byte) {
	line = r.b[:0]
	end := []byte(eol)

	for !bytes.HasSuffix(line, end) {
		b, _ := r.ReadByte()
		line = append(line, b)
	}

	return line[:len(line)-len(eol)]
}

// ReadFull fills up b with data read from r, panics if there was an error.
func (r *Reader) ReadFull(b []byte) {
	io.ReadFull(r, b)
}

// Writer implements the io.Writer interface.
//
// The writer is used to optimize output operations throughout the objconv
// package and sub-packages. It wraps around a generic io.Writer and capture
// the errors it would return, allowing the encoder to detect whether an
// error occured or not after serializing values. It makes it easier to write
// emitters since they don't need to do error checking in their implementations.
//
// It's not safe to use the writer concurrently from multiple goroutines.
type Writer struct {
	W io.Writer
	b [64]byte // buffer
	e error
}

// NewWriter returns a Writer that reads from r.
func NewWriter(w io.Writer) *Writer {
	switch x := w.(type) {
	case *Writer:
		return x
	default:
		return &Writer{W: w}
	}
}

// Write writes b to w.
func (w *Writer) Write(b []byte) (n int, err error) {
	if err = w.e; err == nil {
		n, err = w.W.Write(b)
		w.e = err
	}
	return
}

// WriteByte writes b to w.
func (w *Writer) WriteByte(b byte) (err error) {
	w.b[0] = b
	_, err = w.Write(w.b[:1])
	return
}

// WriteRune writes r to w.
func (w *Writer) WriteRune(r rune) (n int, err error) {
	return w.Write(w.b[:utf8.EncodeRune(w.b[:], r)])
}

// WriteString writes s to w.
func (w *Writer) WriteString(s string) (n int, err error) {
	// Writes the string by chunks of len(w.b) at a time.
	for len(s) != 0 {
		n1 := len(w.b)
		n2 := len(s)
		n3 := n1
		n4 := 0

		if n3 > n2 {
			n3 = n2
		}

		copy(w.b[:], s[:n3])
		n4, err = w.Write(w.b[:n3])
		n += n4
		s = s[n3:]
	}
	return
}

type runeWriter interface {
	WriteRune(rune) (int, error)
}

type stringWriter interface {
	WriteString(string) (int, error)
}

type counter struct{ n int }

func (c *counter) Write(b []byte) (n int, err error) {
	n = len(b)
	c.n += n
	return
}

func (c *counter) WriteByte(b byte) (err error) {
	c.n++
	return
}

func (c *counter) WriteRune(r rune) (n int, err error) {
	n = utf8.RuneLen(r)
	c.n += n
	return
}

func (c *counter) WriteString(s string) (n int, err error) {
	n = len(s)
	c.n += n
	return
}
<|endoftext|>