<commit_before>package mtp

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"time"

	"github.com/hanwen/go-mtpfs/usb"
)

// An MTP device.
type Device struct {
	h   *usb.DeviceHandle
	dev *usb.Device

	claimed bool

	// split off descriptor?
	devDescr    usb.DeviceDescriptor
	ifaceDescr  usb.InterfaceDescriptor
	sendEp      byte
	fetchEp     byte
	eventEp     byte
	configValue byte

	// In milliseconds. Defaults to 2 seconds.
	Timeout int

	// Print request/response codes.
	MTPDebug bool

	// Print USB calls.
	USBDebug bool

	// Print data as it passes over the USB connection.
	DataDebug bool

	// If set, send header in separate write.
	SeparateHeader bool

	session *sessionData
}

type sessionData struct {
	tid uint32
	sid uint32
}

// RCError are return codes from the Container.Code field.
type RCError uint16

func (e RCError) Error() string {
	n, ok := RC_names[int(e)]
	if ok {
		return n
	}
	return fmt.Sprintf("RetCode %x", uint16(e))
}

// Close releases the interface, and closes the device.
func (d *Device) Close() error {
	if d.h == nil {
		return nil // or error?
	}

	if d.session != nil {
		err := d.CloseSession()
		if err != nil {
 			err = d.h.Reset()
			if d.USBDebug {
				log.Printf("USB: Reset, err: %v", err)
			}
		}
	}

	if d.claimed {
		err := d.h.ReleaseInterface(d.ifaceDescr.InterfaceNumber)
		if d.USBDebug {
			log.Printf("USB: ReleaseInterface 0x%x, err: %v", d.ifaceDescr.InterfaceNumber, err)
		}
	}
	err := d.h.Close()
	d.h = nil

	if d.USBDebug {
		log.Printf("USB: Close, err: %v", err)
	}
	return err
}

// Done releases the libusb device reference.
func (d *Device) Done() {
	d.dev.Unref()
	d.dev = nil
}

// Claims the USB interface of the device.
func (d *Device) claim() error {
	if d.h == nil {
		return fmt.Errorf("mtp: claim: device not open")
	}

	err := d.h.ClaimInterface(d.ifaceDescr.InterfaceNumber)
	if d.USBDebug {
		log.Printf("USB: ClaimInterface 0x%x, err: %v", d.ifaceDescr.InterfaceNumber, err)
	}
	if err == nil {
		d.claimed = true
	}

	return err
}

// Open opens an MTP device.
func (d *Device) Open() error {
	if d.Timeout == 0 {
		d.Timeout = 2000
	}

	if d.h != nil {
		return fmt.Errorf("already open")
	}

	var err error
	d.h, err = d.dev.Open()
	if d.USBDebug {
		log.Printf("USB: Open, err: %v", err)
	}
	if err != nil {
		return err
	}

	iface, err := d.h.GetStringDescriptorASCII(d.ifaceDescr.InterfaceStringIndex)
	if err != nil {
		d.Close()
		return err
	}

	if !strings.Contains(iface, "MTP") {
		d.Close()
		return fmt.Errorf("has no MTP in interface string")
	}

	d.claim()
	return nil
}

// ID is the manufacturer + product + serial
func (d *Device) ID() (string, error) {
	if d.h == nil {
		return "", fmt.Errorf("mtp: ID: device not open")
	}

	var ids []string
	for _, b := range []byte{
		d.devDescr.Manufacturer,
		d.devDescr.Product,
		d.devDescr.SerialNumber} {
		s, err := d.h.GetStringDescriptorASCII(b)
		if err != nil {
			if d.USBDebug {
				log.Printf("USB: GetStringDescriptorASCII, err: %v", err)
			}
			return "", err
		}
		ids = append(ids, s)
	}

	return strings.Join(ids, " "), nil
}

func (d *Device) sendReq(req *Container) error {
	c := usbBulkContainer{
		usbBulkHeader: usbBulkHeader{
			Length:        uint32(usbHdrLen + 4*len(req.Param)),
			Type:          USB_CONTAINER_COMMAND,
			Code:          req.Code,
			TransactionID: req.TransactionID,
		},
	}
	for i := range req.Param {
		c.Param[i] = req.Param[i]
	}

	var wData [usbBulkLen]byte
	buf := bytes.NewBuffer(wData[:0])

	err := binary.Write(buf, binary.LittleEndian, c)
	if err != nil {
		panic(err)
	}

	d.dataPrint(d.sendEp, buf.Bytes())
	_, err = d.h.BulkTransfer(d.sendEp, buf.Bytes(), d.Timeout)
	if err != nil {
		return err
	}
	return nil
}

const packetSize = 512

// Fetches one USB packet. The header is split off, and the remainder is returned.
// dest should be at least 512bytes.
func (d *Device) fetchPacket(dest []byte, header *usbBulkHeader) (rest []byte, err error) {
	n, err := d.h.BulkTransfer(d.fetchEp, dest[:packetSize], d.Timeout)
	if n > 0 {
		d.dataPrint(d.fetchEp, dest[:n])
	}

	if err != nil {
		return nil, err
	}

	buf := bytes.NewBuffer(dest[:n])
	err = binary.Read(buf, binary.LittleEndian, header)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (d *Device) decodeRep(h *usbBulkHeader, rest []byte, rep *Container) error {
	if h.Type != USB_CONTAINER_RESPONSE {
		return fmt.Errorf("got type %d in response", h.Type)
	}

	rep.Code = h.Code
	rep.TransactionID = h.TransactionID

	restLen := int(h.Length) - usbHdrLen
	if restLen != len(rest) {
		return fmt.Errorf("header specified 0x%x bytes, but have 0x%x",
			restLen, len(rest))
	}
	nParam := restLen / 4
	for i := 0; i < nParam; i++ {
		rep.Param = append(rep.Param, byteOrder.Uint32(rest[4*i:]))
	}

	if rep.Code != RC_OK {
		return RCError(rep.Code)
	}
	return nil
}

// Runs a single MTP transaction. dest and src cannot be specified at
// the same time.  The request should fill out Code and Param as
// necessary. The response is provided here, but usually only the
// return code is of interest.  If the return code is an error, this
// function will return an RCError instance.
func (d *Device) RunTransaction(req *Container, rep *Container,
	dest io.Writer, src io.Reader, writeSize int64) error {
	if d.h == nil {
		return fmt.Errorf("mtp: cannot run operation %v, device is not open",
			OC_names[int(req.Code)])
	}
	if d.session != nil {
		req.SessionID = d.session.sid
		req.TransactionID = d.session.tid
		d.session.tid++
	}

	if d.MTPDebug {
		log.Printf("MTP request %s %v\n", OC_names[int(req.Code)], req.Param)
	}

	err := d.sendReq(req)

	if err != nil {
		if d.MTPDebug {
			log.Printf("MTP sendreq failed: %v\n", err)
		}
		return err
	}

	if src != nil {
		hdr := usbBulkHeader{
			Type:          USB_CONTAINER_DATA,
			Code:          req.Code,
			Length:        uint32(writeSize),
			TransactionID: req.TransactionID,
		}

		_, err := d.bulkWrite(&hdr, src, writeSize)
		if err != nil {
			d.Close()
			return err
		}
	}
	var data [packetSize]byte
	h := &usbBulkHeader{}
	rest, err := d.fetchPacket(data[:], h)
	if err != nil {
		return err
	}
	var unexpectedData bool
	if h.Type == USB_CONTAINER_DATA {
		if dest == nil {
			dest = &NullWriter{}
			unexpectedData = true
			if d.MTPDebug {
				log.Printf("MTP discarding unexpected data 0x%x bytes", h.Length)
			}
		}
		if d.MTPDebug {
			log.Printf("MTP data 0x%x bytes", h.Length)
		}

		size := int(h.Length)
		dest.Write(rest)
		size -= len(rest) + usbHdrLen
		if size > 0 {
			_, err = d.bulkRead(dest)
			if err != nil {
				return err
			}
		}

		h = &usbBulkHeader{}
		rest, err = d.fetchPacket(data[:], h)
	}

	err = d.decodeRep(h, rest, rep)
	if d.MTPDebug {
		log.Printf("MTP response %s %v", RC_names[int(rep.Code)], rep.Param)
	}
	if unexpectedData {
		return fmt.Errorf("unexpected data for code %s", RC_names[int(req.Code)])
	}

	if err != nil {
		return err
	}
	if d.session != nil && rep.TransactionID != req.TransactionID {
		return fmt.Errorf("transaction ID mismatch got %x want %x",
			rep.TransactionID, req.TransactionID)
	}
	rep.SessionID = req.SessionID
	return nil
}

// Prints data going over the USB connection.
func (d *Device) dataPrint(ep byte, data []byte) {
	if !d.DataDebug {
		return
	}
	dir := "send"
	if 0 != ep&usb.ENDPOINT_IN {
		dir = "recv"
	}
	fmt.Fprintf(os.Stderr, "%s: 0x%x bytes with ep 0x%x:\n", dir, len(data), ep)
	hexDump(data)
}

// The linux usb stack can send 16kb per call, according to libusb.
const rwBufSize = 0x4000

// bulkWrite returns the number of non-header bytes written.
func (d *Device) bulkWrite(hdr *usbBulkHeader, r io.Reader, size int64) (n int64, err error) {
	if hdr != nil {
		if size+usbHdrLen > 0xFFFFFFFF {
			hdr.Length = 0xFFFFFFFF
		} else {
			hdr.Length = uint32(size + usbHdrLen)
		}

		var packetArr [packetSize]byte
		var packet []byte
		if d.SeparateHeader {
			packet = packetArr[:usbHdrLen]
		} else {
			packet = packetArr[:]
		}

		buf := bytes.NewBuffer(packet[:0])
		binary.Write(buf, byteOrder, hdr)
		cpSize := int64(len(packet) - usbHdrLen)
		if cpSize > size {
			cpSize = size
		}

		_, err = io.CopyN(buf, r, cpSize)
		d.dataPrint(d.sendEp, buf.Bytes())
		_, err = d.h.BulkTransfer(d.sendEp, buf.Bytes(), d.Timeout)
		if err != nil {
			return cpSize, err
		}
		size -= cpSize
		n += cpSize
	}

	var buf [rwBufSize]byte
	var lastTransfer int
	for size > 0 {
		var m int
		toread := buf[:]
		if int64(len(toread)) > size {
			toread = buf[:int(size)]
		}

		m, err = r.Read(toread)
		if err != nil {
			break
		}
		size -= int64(m)

		d.dataPrint(d.sendEp, buf[:m])
		lastTransfer, err = d.h.BulkTransfer(d.sendEp, buf[:m], d.Timeout)
		n += int64(lastTransfer)

		if err != nil || lastTransfer == 0 {
			break
		}
	}
	if lastTransfer%packetSize == 0 {
		// write a short packet just to be sure.
		d.h.BulkTransfer(d.sendEp, buf[:0], d.Timeout)
	}

	return n, err
}

func (d *Device) bulkRead(w io.Writer) (n int64, err error) {
	var buf [rwBufSize]byte
	var lastRead int
	for {
		toread := buf[:]
		lastRead, err = d.h.BulkTransfer(d.fetchEp, toread, d.Timeout)
		if err != nil {
			break
		}
		if lastRead > 0 {
			d.dataPrint(d.fetchEp, buf[:lastRead])

			w, err := w.Write(buf[:lastRead])
			n += int64(w)
			if err != nil {
				break
			}
		}
		if lastRead < len(toread) {
			// short read.
			break
		}
	}
	if lastRead%packetSize == 0 {
		// Null read.
		d.h.BulkTransfer(d.fetchEp, buf[:0], d.Timeout)
	}
	return n, err
}

// Configure is a robust version of OpenSession. On failure, it resets
// the device and reopens the device and the session.
func (d *Device) Configure() error {
	if d.h == nil {
		err := d.Open()
		if err != nil {
			return err
		}
	}
	err := d.OpenSession()
	if err == RCError(RC_SessionAlreadyOpened) {
		// It's open, so close the session. Fortunately, this
		// even works without a transaction ID, at least on Android.
		d.CloseSession()
		err = d.OpenSession()
	}

 	if err != nil {
		log.Printf("OpenSession failed: %v; attempting reset", err)
		if d.h != nil {
			d.h.Reset()
		}
		d.Close()

		// Give the device some rest.
		time.Sleep(1000 * time.Millisecond)
		if err := d.Open(); err != nil {
			return fmt.Errorf("opening after reset: %v", err)
		}
		if err := d.OpenSession(); err != nil {
			return fmt.Errorf("OpenSession after reset: %v", err)
		}
	}
	return nil
}
<commit_msg>Write USB packets the right size for the given opcode.<commit_after>package mtp

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"time"

	"github.com/hanwen/go-mtpfs/usb"
)

// An MTP device.
type Device struct {
	h   *usb.DeviceHandle
	dev *usb.Device

	claimed bool

	// split off descriptor?
	devDescr    usb.DeviceDescriptor
	ifaceDescr  usb.InterfaceDescriptor
	sendEp      byte
	fetchEp     byte
	eventEp     byte
	configValue byte

	// In milliseconds. Defaults to 2 seconds.
	Timeout int

	// Print request/response codes.
	MTPDebug bool

	// Print USB calls.
	USBDebug bool

	// Print data as it passes over the USB connection.
	DataDebug bool

	// If set, send header in separate write.
	SeparateHeader bool

	session *sessionData
}

type sessionData struct {
	tid uint32
	sid uint32
}

// RCError are return codes from the Container.Code field.
type RCError uint16

func (e RCError) Error() string {
	n, ok := RC_names[int(e)]
	if ok {
		return n
	}
	return fmt.Sprintf("RetCode %x", uint16(e))
}

// Close releases the interface, and closes the device.
func (d *Device) Close() error {
	if d.h == nil {
		return nil // or error?
	}

	if d.session != nil {
		err := d.CloseSession()
		if err != nil {
 			err = d.h.Reset()
			if d.USBDebug {
				log.Printf("USB: Reset, err: %v", err)
			}
		}
	}

	if d.claimed {
		err := d.h.ReleaseInterface(d.ifaceDescr.InterfaceNumber)
		if d.USBDebug {
			log.Printf("USB: ReleaseInterface 0x%x, err: %v", d.ifaceDescr.InterfaceNumber, err)
		}
	}
	err := d.h.Close()
	d.h = nil

	if d.USBDebug {
		log.Printf("USB: Close, err: %v", err)
	}
	return err
}

// Done releases the libusb device reference.
func (d *Device) Done() {
	d.dev.Unref()
	d.dev = nil
}

// Claims the USB interface of the device.
func (d *Device) claim() error {
	if d.h == nil {
		return fmt.Errorf("mtp: claim: device not open")
	}

	err := d.h.ClaimInterface(d.ifaceDescr.InterfaceNumber)
	if d.USBDebug {
		log.Printf("USB: ClaimInterface 0x%x, err: %v", d.ifaceDescr.InterfaceNumber, err)
	}
	if err == nil {
		d.claimed = true
	}

	return err
}

// Open opens an MTP device.
func (d *Device) Open() error {
	if d.Timeout == 0 {
		d.Timeout = 2000
	}

	if d.h != nil {
		return fmt.Errorf("already open")
	}

	var err error
	d.h, err = d.dev.Open()
	if d.USBDebug {
		log.Printf("USB: Open, err: %v", err)
	}
	if err != nil {
		return err
	}

	iface, err := d.h.GetStringDescriptorASCII(d.ifaceDescr.InterfaceStringIndex)
	if err != nil {
		d.Close()
		return err
	}

	if !strings.Contains(iface, "MTP") {
		d.Close()
		return fmt.Errorf("has no MTP in interface string")
	}

	d.claim()
	return nil
}

// ID is the manufacturer + product + serial
func (d *Device) ID() (string, error) {
	if d.h == nil {
		return "", fmt.Errorf("mtp: ID: device not open")
	}

	var ids []string
	for _, b := range []byte{
		d.devDescr.Manufacturer,
		d.devDescr.Product,
		d.devDescr.SerialNumber} {
		s, err := d.h.GetStringDescriptorASCII(b)
		if err != nil {
			if d.USBDebug {
				log.Printf("USB: GetStringDescriptorASCII, err: %v", err)
			}
			return "", err
		}
		ids = append(ids, s)
	}

	return strings.Join(ids, " "), nil
}

func (d *Device) sendReq(req *Container) error {
	c := usbBulkContainer{
		usbBulkHeader: usbBulkHeader{
			Length:        uint32(usbHdrLen + 4*len(req.Param)),
			Type:          USB_CONTAINER_COMMAND,
			Code:          req.Code,
			TransactionID: req.TransactionID,
		},
	}
	for i := range req.Param {
		c.Param[i] = req.Param[i]
	}

	var wData [usbBulkLen]byte
	buf := bytes.NewBuffer(wData[:0])

	binary.Write(buf, binary.LittleEndian, c.usbBulkHeader)
	err := binary.Write(buf, binary.LittleEndian, c.Param[:len(req.Param)])
	if err != nil {
		panic(err)
	}

	d.dataPrint(d.sendEp, buf.Bytes())
	_, err = d.h.BulkTransfer(d.sendEp, buf.Bytes(), d.Timeout)
	if err != nil {
		return err
	}
	return nil
}

const packetSize = 512

// Fetches one USB packet. The header is split off, and the remainder is returned.
// dest should be at least 512bytes.
func (d *Device) fetchPacket(dest []byte, header *usbBulkHeader) (rest []byte, err error) {
	n, err := d.h.BulkTransfer(d.fetchEp, dest[:packetSize], d.Timeout)
	if n > 0 {
		d.dataPrint(d.fetchEp, dest[:n])
	}

	if err != nil {
		return nil, err
	}

	buf := bytes.NewBuffer(dest[:n])
	err = binary.Read(buf, binary.LittleEndian, header)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (d *Device) decodeRep(h *usbBulkHeader, rest []byte, rep *Container) error {
	if h.Type != USB_CONTAINER_RESPONSE {
		return fmt.Errorf("got type %d in response", h.Type)
	}

	rep.Code = h.Code
	rep.TransactionID = h.TransactionID

	restLen := int(h.Length) - usbHdrLen
	if restLen != len(rest) {
		return fmt.Errorf("header specified 0x%x bytes, but have 0x%x",
			restLen, len(rest))
	}
	nParam := restLen / 4
	for i := 0; i < nParam; i++ {
		rep.Param = append(rep.Param, byteOrder.Uint32(rest[4*i:]))
	}

	if rep.Code != RC_OK {
		return RCError(rep.Code)
	}
	return nil
}

// Runs a single MTP transaction. dest and src cannot be specified at
// the same time.  The request should fill out Code and Param as
// necessary. The response is provided here, but usually only the
// return code is of interest.  If the return code is an error, this
// function will return an RCError instance.
func (d *Device) RunTransaction(req *Container, rep *Container,
	dest io.Writer, src io.Reader, writeSize int64) error {
	if d.h == nil {
		return fmt.Errorf("mtp: cannot run operation %v, device is not open",
			OC_names[int(req.Code)])
	}
	if d.session != nil {
		req.SessionID = d.session.sid
		req.TransactionID = d.session.tid
		d.session.tid++
	}

	if d.MTPDebug {
		log.Printf("MTP request %s %v\n", OC_names[int(req.Code)], req.Param)
	}

	err := d.sendReq(req)

	if err != nil {
		if d.MTPDebug {
			log.Printf("MTP sendreq failed: %v\n", err)
		}
		return err
	}

	if src != nil {
		hdr := usbBulkHeader{
			Type:          USB_CONTAINER_DATA,
			Code:          req.Code,
			Length:        uint32(writeSize),
			TransactionID: req.TransactionID,
		}

		_, err := d.bulkWrite(&hdr, src, writeSize)
		if err != nil {
			d.Close()
			return err
		}
	}
	var data [packetSize]byte
	h := &usbBulkHeader{}
	rest, err := d.fetchPacket(data[:], h)
	if err != nil {
		return err
	}
	var unexpectedData bool
	if h.Type == USB_CONTAINER_DATA {
		if dest == nil {
			dest = &NullWriter{}
			unexpectedData = true
			if d.MTPDebug {
				log.Printf("MTP discarding unexpected data 0x%x bytes", h.Length)
			}
		}
		if d.MTPDebug {
			log.Printf("MTP data 0x%x bytes", h.Length)
		}

		size := int(h.Length)
		dest.Write(rest)
		size -= len(rest) + usbHdrLen
		if size > 0 {
			_, err = d.bulkRead(dest)
			if err != nil {
				return err
			}
		}

		h = &usbBulkHeader{}
		rest, err = d.fetchPacket(data[:], h)
	}

	err = d.decodeRep(h, rest, rep)
	if d.MTPDebug {
		log.Printf("MTP response %s %v", RC_names[int(rep.Code)], rep.Param)
	}
	if unexpectedData {
		return fmt.Errorf("unexpected data for code %s", RC_names[int(req.Code)])
	}

	if err != nil {
		return err
	}
	if d.session != nil && rep.TransactionID != req.TransactionID {
		return fmt.Errorf("transaction ID mismatch got %x want %x",
			rep.TransactionID, req.TransactionID)
	}
	rep.SessionID = req.SessionID
	return nil
}

// Prints data going over the USB connection.
func (d *Device) dataPrint(ep byte, data []byte) {
	if !d.DataDebug {
		return
	}
	dir := "send"
	if 0 != ep&usb.ENDPOINT_IN {
		dir = "recv"
	}
	fmt.Fprintf(os.Stderr, "%s: 0x%x bytes with ep 0x%x:\n", dir, len(data), ep)
	hexDump(data)
}

// The linux usb stack can send 16kb per call, according to libusb.
const rwBufSize = 0x4000

// bulkWrite returns the number of non-header bytes written.
func (d *Device) bulkWrite(hdr *usbBulkHeader, r io.Reader, size int64) (n int64, err error) {
	if hdr != nil {
		if size+usbHdrLen > 0xFFFFFFFF {
			hdr.Length = 0xFFFFFFFF
		} else {
			hdr.Length = uint32(size + usbHdrLen)
		}

		var packetArr [packetSize]byte
		var packet []byte
		if d.SeparateHeader {
			packet = packetArr[:usbHdrLen]
		} else {
			packet = packetArr[:]
		}

		buf := bytes.NewBuffer(packet[:0])
		binary.Write(buf, byteOrder, hdr)
		cpSize := int64(len(packet) - usbHdrLen)
		if cpSize > size {
			cpSize = size
		}

		_, err = io.CopyN(buf, r, cpSize)
		d.dataPrint(d.sendEp, buf.Bytes())
		_, err = d.h.BulkTransfer(d.sendEp, buf.Bytes(), d.Timeout)
		if err != nil {
			return cpSize, err
		}
		size -= cpSize
		n += cpSize
	}

	var buf [rwBufSize]byte
	var lastTransfer int
	for size > 0 {
		var m int
		toread := buf[:]
		if int64(len(toread)) > size {
			toread = buf[:int(size)]
		}

		m, err = r.Read(toread)
		if err != nil {
			break
		}
		size -= int64(m)

		d.dataPrint(d.sendEp, buf[:m])
		lastTransfer, err = d.h.BulkTransfer(d.sendEp, buf[:m], d.Timeout)
		n += int64(lastTransfer)

		if err != nil || lastTransfer == 0 {
			break
		}
	}
	if lastTransfer%packetSize == 0 {
		// write a short packet just to be sure.
		d.h.BulkTransfer(d.sendEp, buf[:0], d.Timeout)
	}

	return n, err
}

func (d *Device) bulkRead(w io.Writer) (n int64, err error) {
	var buf [rwBufSize]byte
	var lastRead int
	for {
		toread := buf[:]
		lastRead, err = d.h.BulkTransfer(d.fetchEp, toread, d.Timeout)
		if err != nil {
			break
		}
		if lastRead > 0 {
			d.dataPrint(d.fetchEp, buf[:lastRead])

			w, err := w.Write(buf[:lastRead])
			n += int64(w)
			if err != nil {
				break
			}
		}
		if lastRead < len(toread) {
			// short read.
			break
		}
	}
	if lastRead%packetSize == 0 {
		// Null read.
		d.h.BulkTransfer(d.fetchEp, buf[:0], d.Timeout)
	}
	return n, err
}

// Configure is a robust version of OpenSession. On failure, it resets
// the device and reopens the device and the session.
func (d *Device) Configure() error {
	if d.h == nil {
		err := d.Open()
		if err != nil {
			return err
		}
	}
	err := d.OpenSession()
	if err == RCError(RC_SessionAlreadyOpened) {
		// It's open, so close the session. Fortunately, this
		// even works without a transaction ID, at least on Android.
		d.CloseSession()
		err = d.OpenSession()
	}

 	if err != nil {
		log.Printf("OpenSession failed: %v; attempting reset", err)
		if d.h != nil {
			d.h.Reset()
		}
		d.Close()

		// Give the device some rest.
		time.Sleep(1000 * time.Millisecond)
		if err := d.Open(); err != nil {
			return fmt.Errorf("opening after reset: %v", err)
		}
		if err := d.OpenSession(); err != nil {
			return fmt.Errorf("OpenSession after reset: %v", err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package pdf

type Image struct {
	Url string  `mapstructure:"url"`
	X   float64 `mapstructure:"x"`
	Y   float64 `mapstructure:"y"`
	W   float64 `mapstructure:"w"`
	H   float64 `mapstructure:"h"`
}

// Parse puts a HTTP Image on the PDF.
func (i *Image) Parse(pdf Pdf) {
	pdf.HttpImage(i.Url, i.X, i.Y, i.W, i.H, false, "", 0, "")
}

// Load will look if there is a url key in the user input and if so, it will
// overwrite the given URL from the template with this URL.
func (i *Image) Load(t Template, block_data, user_input map[string]interface{}) {
	if url, ok := user_input["url"]; ok {
		i.Url = url.(string)
	}
}
<commit_msg>Image: correct mapstructure.<commit_after>package pdf

type Image struct {
	Url string  `mapstructure:"url"`
	X   float64 `mapstructure:"x"`
	Y   float64 `mapstructure:"y"`
	W   float64 `mapstructure:"width"`
	H   float64 `mapstructure:"height"`
}

// Parse puts a HTTP Image on the PDF.
func (i *Image) Parse(pdf Pdf) {
	pdf.HttpImage(i.Url, i.X, i.Y, i.W, i.H, false, "", 0, "")
}

// Load will look if there is a url key in the user input and if so, it will
// overwrite the given URL from the template with this URL.
func (i *Image) Load(t Template, block_data, user_input map[string]interface{}) {
	if url, ok := user_input["url"]; ok {
		i.Url = url.(string)
	}
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"github.com/Symantec/Dominator/lib/filegen"
	"github.com/Symantec/Dominator/lib/fsutil"
	"strings"
)

type configFunc func(*filegen.Manager, []string) error

type configType struct {
	minParams  int
	maxParams  int
	configFunc configFunc
}

var configs = map[string]configType{}

func loadConfiguration(manager *filegen.Manager, filename string) error {
	if filename == "" {
		return nil
	}
	lines, err := fsutil.LoadLines(filename)
	if err != nil {
		return fmt.Errorf("error loading configuration file: %s", err)
	}
	for _, line := range lines {
		fields := strings.Fields(line)
		if len(fields) < 2 {
			continue
		}
		config, ok := configs[fields[0]]
		if !ok {
			continue
		}
		numParams := len(fields) - 1
		if numParams < config.minParams {
			return fmt.Errorf("insufficient params in line: %s", line)
		}
		if config.maxParams >= 0 && numParams > config.maxParams {
			return fmt.Errorf("too many params in line: %s", line)
		}
		// TODO(rgooch): Implement.
	}
	return nil
}
<commit_msg>Add support for DynamicTemplateFile,File,StaticTemplateFile.<commit_after>package util

import (
	"fmt"
	"github.com/Symantec/Dominator/lib/filegen"
	"github.com/Symantec/Dominator/lib/fsutil"
	"strings"
)

type configFunc func(*filegen.Manager, string, []string) error

type configType struct {
	minParams  int
	maxParams  int
	configFunc configFunc
}

var configs = map[string]configType{
	"DynamicTemplateFile": {1, 1, dynamicTemplateFileGenerator},
	"File":                {1, 1, fileGenerator},
	"StaticTemplateFile":  {1, 1, staticTemplateFileGenerator},
}

func loadConfiguration(manager *filegen.Manager, filename string) error {
	if filename == "" {
		return nil
	}
	lines, err := fsutil.LoadLines(filename)
	if err != nil {
		return fmt.Errorf("error loading configuration file: %s", err)
	}
	for _, line := range lines {
		fields := strings.Fields(line)
		if len(fields) < 2 {
			return fmt.Errorf("insufficient fields in line: \"%s\"", line)
		}
		config, ok := configs[fields[0]]
		if !ok {
			return fmt.Errorf("unknown generator type: %s", fields[0])
		}
		numParams := len(fields) - 2
		if numParams < config.minParams {
			return fmt.Errorf("insufficient params in line: \"%s\"", line)
		}
		if config.maxParams >= 0 && numParams > config.maxParams {
			return fmt.Errorf("too many params in line: \"%s\"", line)
		}
		if err := config.configFunc(manager, fields[1],
			fields[2:]); err != nil {
			return err
		}
	}
	return nil
}

func dynamicTemplateFileGenerator(manager *filegen.Manager, pathname string,
	params []string) error {
	return manager.RegisterTemplateFileForPath(pathname, params[0], true)
}

func fileGenerator(manager *filegen.Manager, pathname string,
	params []string) error {
	manager.RegisterFileForPath(pathname, params[0])
	return nil
}

func staticTemplateFileGenerator(manager *filegen.Manager, pathname string,
	params []string) error {
	return manager.RegisterTemplateFileForPath(pathname, params[0], false)
}
<|endoftext|>


<commit_before>package lion

import (
	"context"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"net/url"
)

// Context key to store *ctx
var ctxKey = &struct{}{}

var (
	ErrInvalidRedirectStatusCode = errors.New("Invalid redirect status code")

	contentTypeJSON      = "application/json; charset=utf-8"
	contentTypeXML       = "application/xml; charset=utf-8"
	contentTypeTextPlain = "text/plain; charset=utf-8"
	contentTypeTextHTML  = "text/html; charset=utf-8"
)

// Check Context implements net.Context
var _ context.Context = (*ctx)(nil)
var _ Context = (*ctx)(nil)
var _ http.ResponseWriter = (*ctx)(nil)

type Context interface {
	context.Context
	http.ResponseWriter
	Param(key string) string
	ParamOk(key string) (string, bool)
	Clone() Context

	Request() *http.Request

	// Request
	Cookie(name string) (*http.Cookie, error)
	Query(name string) string
	GetHeader(key string) string

	// Response
	WithStatus(code int) Context
	WithHeader(key, value string) Context
	WithCookie(cookie *http.Cookie) Context

	// Rendering
	JSON(data interface{}) error
	XML(data interface{}) error
	String(format string, a ...interface{}) error
	Error(err error) error
	File(path string) error
	Attachment(path, filename string) error
	Redirect(urlStr string) error
}

// Context implements context.Context and stores values of url parameters
type ctx struct {
	context.Context
	http.ResponseWriter

	parent context.Context
	req    *http.Request

	params []parameter

	code          int
	statusWritten bool
}

// newContext creates a new context instance
func newContext() *ctx {
	return newContextWithParent(context.Background())
}

// newContextWithParent creates a new context with a parent context specified
func newContextWithParent(c context.Context) *ctx {
	return newContextWithResReq(c, nil, nil)
}

func newContextWithResReq(c context.Context, w http.ResponseWriter, r *http.Request) *ctx {
	return &ctx{
		parent:         c,
		ResponseWriter: w,
		req:            r,
	}
}

// Value returns the value for the passed key. If it is not found in the url params it returns parent's context Value
func (c *ctx) Value(key interface{}) interface{} {
	if key == ctxKey {
		return c
	}

	if k, ok := key.(string); ok {
		if val, exist := c.ParamOk(k); exist {
			return val
		}
	}

	return c.parent.Value(key)
}

func (c *ctx) AddParam(key, val string) {
	c.params = append(c.params, parameter{key, val})
}

// Param returns the value of a param.
// If it does not exist it returns an empty string
func (c *ctx) Param(key string) string {
	val, _ := c.ParamOk(key)
	return val
}

// ParamOk returns the value of a param and a boolean that indicates if the param exists.
func (c *ctx) ParamOk(key string) (string, bool) {
	for _, p := range c.params {
		if p.key == key {
			return p.val, true
		}
	}

	return "", false
}

func (c *ctx) Clone() Context {
	nc := newContext()
	nc.parent = c.parent
	nc.params = make([]parameter, len(c.params), cap(c.params))
	copy(nc.params, c.params)

	// shallow copy of request
	nr := &c.req
	nc.req = *nr

	return nc
}

///////////// REQUEST UTILS ////////////////

func (c *ctx) Request() *http.Request {
	return c.req
}

func (c *ctx) Cookie(name string) (*http.Cookie, error) {
	return c.Request().Cookie(name)
}

func (c *ctx) Query(name string) string {
	return c.urlQueries().Get(name)
}

func (c *ctx) urlQueries() url.Values {
	return c.Request().URL.Query()
}

func (c *ctx) GetHeader(key string) string {
	return c.Request().Header.Get(key)
}

///////////// REQUEST UTILS ////////////////

///////////// RESPONSE MODIFIERS /////////////

// WithStatus sets the status code for the current request.
// If the status has already been written it will not change the current status code
func (c *ctx) WithStatus(code int) Context {
	c.code = code
	return c
}

func (c *ctx) writeHeader() {
	if !c.isStatusWritten() {
		c.WriteHeader(c.code)
		c.statusWritten = true
	}
}

func (c *ctx) isStatusWritten() bool {
	return c.statusWritten
}

// WithHeader is a convenient alias for http.ResponseWriter.Header().Set()
func (c *ctx) WithHeader(key, value string) Context {
	c.Header().Set(key, value)
	return c
}

func (c *ctx) WithCookie(cookie *http.Cookie) Context {
	http.SetCookie(c, cookie)
	return c
}

///////////// RESPONSE MODIFIERS /////////////

///////////// RESPONSE RENDERING /////////////

func (c *ctx) JSON(data interface{}) error {
	b, err := json.Marshal(data)
	if err != nil {
		return err
	}
	return c.raw(b, contentTypeJSON)
}

func (c *ctx) String(format string, a ...interface{}) error {
	str := fmt.Sprintf(format, a...)
	return c.raw([]byte(str), contentTypeTextPlain)
}

func (c *ctx) Error(err error) error {
	if herr, ok := err.(HTTPError); ok {
		return c.WithStatus(herr.Status()).
			String(err.Error())
	}
	return c.String(err.Error())
}

func (c *ctx) XML(data interface{}) error {
	b, err := xml.Marshal(data)
	if err != nil {
		return err
	}
	return c.raw(b, contentTypeXML)
}

func (c *ctx) File(path string) error {
	http.ServeFile(c, c.Request(), path)
	return nil
}

func (c *ctx) Attachment(path, filename string) error {
	return c.WithHeader("Content-Disposition", "attachment; filename="+filename).
		File(path)
}

func (c *ctx) setContentType(ctype string) {
	c.Header().Set("Content-Type", ctype)
}

func (c *ctx) raw(b []byte, contentType string) error {
	c.setContentType(contentType)
	c.writeHeader()
	_, err := c.Write(b)
	return err
}

///////////// RESPONSE RENDERING /////////////

func (c *ctx) Redirect(urlStr string) error {
	if c.code < 300 || c.code > 308 {
		return ErrInvalidRedirectStatusCode
	}

	http.Redirect(c, c.Request(), urlStr, c.code)
	return nil
}

func (c *ctx) Reset() {
	c.params = c.params[:0]
	c.parent = nil
	c.req = nil
	c.ResponseWriter = nil
}

func (c *ctx) Remove(key string) {
	i := c.indexOf(key)
	if i < 0 {
		panicl("Cannot remove unknown key '%s' from context", key)
	}

	c.params = append(c.params[:i], c.params[i+1:]...)
}

func (c *ctx) indexOf(key string) int {
	for i := len(c.params) - 1; i >= 0; i-- {
		if c.params[i].key == key {
			return i
		}
	}
	return -1
}

// C returns a Context based on a context.Context passed. If it does not convert to Context, it creates a new one with the context passed as argument.
func C(req *http.Request) Context {
	c := req.Context()
	if val := c.Value(ctxKey); val != nil {
		if ctx, ok := val.(*ctx); ok {
			return ctx
		}
	}
	return nil
}

// Param returns the value of a url param base on the passed context
func Param(req *http.Request, key string) string {
	return C(req).Param(key)
}

func setParamContext(req *http.Request, c *ctx) *http.Request {
	c.parent = req.Context()
	return req.WithContext(c)
}

type parameter struct {
	key string
	val string
}

type HTTPError interface {
	error
	Status() int
}

var (
	// 4xx
	ErrorBadRequest       HTTPError = httpError{http.StatusBadRequest}
	ErrorUnauthorized     HTTPError = httpError{http.StatusUnauthorized}
	ErrorForbidden        HTTPError = httpError{http.StatusForbidden}
	ErrorNotFound         HTTPError = httpError{http.StatusNotFound}
	ErrorMethodNotAllowed HTTPError = httpError{http.StatusMethodNotAllowed}

	// 5xx
	ErrorInternalServer HTTPError = httpError{http.StatusInternalServerError}
)

type httpError struct{ code int }

func (e httpError) Error() string {
	return http.StatusText(e.code)
}

func (e httpError) Status() int {
	return e.code
}
<commit_msg>Fix Context not reseting status code<commit_after>package lion

import (
	"context"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"net/url"
)

// Context key to store *ctx
var ctxKey = &struct{}{}

var (
	ErrInvalidRedirectStatusCode = errors.New("Invalid redirect status code")

	contentTypeJSON      = "application/json; charset=utf-8"
	contentTypeXML       = "application/xml; charset=utf-8"
	contentTypeTextPlain = "text/plain; charset=utf-8"
	contentTypeTextHTML  = "text/html; charset=utf-8"
)

// Check Context implements net.Context
var _ context.Context = (*ctx)(nil)
var _ Context = (*ctx)(nil)
var _ http.ResponseWriter = (*ctx)(nil)

type Context interface {
	context.Context
	http.ResponseWriter
	Param(key string) string
	ParamOk(key string) (string, bool)
	Clone() Context

	Request() *http.Request

	// Request
	Cookie(name string) (*http.Cookie, error)
	Query(name string) string
	GetHeader(key string) string

	// Response
	WithStatus(code int) Context
	WithHeader(key, value string) Context
	WithCookie(cookie *http.Cookie) Context

	// Rendering
	JSON(data interface{}) error
	XML(data interface{}) error
	String(format string, a ...interface{}) error
	Error(err error) error
	File(path string) error
	Attachment(path, filename string) error
	Redirect(urlStr string) error
}

// Context implements context.Context and stores values of url parameters
type ctx struct {
	context.Context
	http.ResponseWriter

	parent context.Context
	req    *http.Request

	params []parameter

	code          int
	statusWritten bool
}

// newContext creates a new context instance
func newContext() *ctx {
	return newContextWithParent(context.Background())
}

// newContextWithParent creates a new context with a parent context specified
func newContextWithParent(c context.Context) *ctx {
	return newContextWithResReq(c, nil, nil)
}

func newContextWithResReq(c context.Context, w http.ResponseWriter, r *http.Request) *ctx {
	return &ctx{
		parent:         c,
		ResponseWriter: w,
		req:            r,
	}
}

// Value returns the value for the passed key. If it is not found in the url params it returns parent's context Value
func (c *ctx) Value(key interface{}) interface{} {
	if key == ctxKey {
		return c
	}

	if k, ok := key.(string); ok {
		if val, exist := c.ParamOk(k); exist {
			return val
		}
	}

	return c.parent.Value(key)
}

func (c *ctx) AddParam(key, val string) {
	c.params = append(c.params, parameter{key, val})
}

// Param returns the value of a param.
// If it does not exist it returns an empty string
func (c *ctx) Param(key string) string {
	val, _ := c.ParamOk(key)
	return val
}

// ParamOk returns the value of a param and a boolean that indicates if the param exists.
func (c *ctx) ParamOk(key string) (string, bool) {
	for _, p := range c.params {
		if p.key == key {
			return p.val, true
		}
	}

	return "", false
}

func (c *ctx) Clone() Context {
	nc := newContext()
	nc.parent = c.parent
	nc.params = make([]parameter, len(c.params), cap(c.params))
	copy(nc.params, c.params)

	// shallow copy of request
	nr := &c.req
	nc.req = *nr

	return nc
}

///////////// REQUEST UTILS ////////////////

func (c *ctx) Request() *http.Request {
	return c.req
}

func (c *ctx) Cookie(name string) (*http.Cookie, error) {
	return c.Request().Cookie(name)
}

func (c *ctx) Query(name string) string {
	return c.urlQueries().Get(name)
}

func (c *ctx) urlQueries() url.Values {
	return c.Request().URL.Query()
}

func (c *ctx) GetHeader(key string) string {
	return c.Request().Header.Get(key)
}

///////////// REQUEST UTILS ////////////////

///////////// RESPONSE MODIFIERS /////////////

// WithStatus sets the status code for the current request.
// If the status has already been written it will not change the current status code
func (c *ctx) WithStatus(code int) Context {
	c.code = code
	return c
}

func (c *ctx) writeHeader() {
	if !c.isStatusWritten() {
		c.WriteHeader(c.code)
		c.statusWritten = true
	}
}

func (c *ctx) isStatusWritten() bool {
	return c.statusWritten
}

// WithHeader is a convenient alias for http.ResponseWriter.Header().Set()
func (c *ctx) WithHeader(key, value string) Context {
	c.Header().Set(key, value)
	return c
}

func (c *ctx) WithCookie(cookie *http.Cookie) Context {
	http.SetCookie(c, cookie)
	return c
}

///////////// RESPONSE MODIFIERS /////////////

///////////// RESPONSE RENDERING /////////////

func (c *ctx) JSON(data interface{}) error {
	b, err := json.Marshal(data)
	if err != nil {
		return err
	}
	return c.raw(b, contentTypeJSON)
}

func (c *ctx) String(format string, a ...interface{}) error {
	str := fmt.Sprintf(format, a...)
	return c.raw([]byte(str), contentTypeTextPlain)
}

func (c *ctx) Error(err error) error {
	if herr, ok := err.(HTTPError); ok {
		return c.WithStatus(herr.Status()).
			String(err.Error())
	}
	return c.String(err.Error())
}

func (c *ctx) XML(data interface{}) error {
	b, err := xml.Marshal(data)
	if err != nil {
		return err
	}
	return c.raw(b, contentTypeXML)
}

func (c *ctx) File(path string) error {
	http.ServeFile(c, c.Request(), path)
	return nil
}

func (c *ctx) Attachment(path, filename string) error {
	return c.WithHeader("Content-Disposition", "attachment; filename="+filename).
		File(path)
}

func (c *ctx) setContentType(ctype string) {
	c.Header().Set("Content-Type", ctype)
}

func (c *ctx) raw(b []byte, contentType string) error {
	c.setContentType(contentType)
	c.writeHeader()
	_, err := c.Write(b)
	return err
}

///////////// RESPONSE RENDERING /////////////

func (c *ctx) Redirect(urlStr string) error {
	if c.code < 300 || c.code > 308 {
		return ErrInvalidRedirectStatusCode
	}

	http.Redirect(c, c.Request(), urlStr, c.code)
	return nil
}

func (c *ctx) Reset() {
	c.params = c.params[:0]
	c.parent = nil
	c.req = nil
	c.ResponseWriter = nil
	c.code = 0
	c.statusWritten = false
}

func (c *ctx) Remove(key string) {
	i := c.indexOf(key)
	if i < 0 {
		panicl("Cannot remove unknown key '%s' from context", key)
	}

	c.params = append(c.params[:i], c.params[i+1:]...)
}

func (c *ctx) indexOf(key string) int {
	for i := len(c.params) - 1; i >= 0; i-- {
		if c.params[i].key == key {
			return i
		}
	}
	return -1
}

// C returns a Context based on a context.Context passed. If it does not convert to Context, it creates a new one with the context passed as argument.
func C(req *http.Request) Context {
	c := req.Context()
	if val := c.Value(ctxKey); val != nil {
		if ctx, ok := val.(*ctx); ok {
			return ctx
		}
	}
	return nil
}

// Param returns the value of a url param base on the passed context
func Param(req *http.Request, key string) string {
	return C(req).Param(key)
}

func setParamContext(req *http.Request, c *ctx) *http.Request {
	c.parent = req.Context()
	return req.WithContext(c)
}

type parameter struct {
	key string
	val string
}

type HTTPError interface {
	error
	Status() int
}

var (
	// 4xx
	ErrorBadRequest       HTTPError = httpError{http.StatusBadRequest}
	ErrorUnauthorized     HTTPError = httpError{http.StatusUnauthorized}
	ErrorForbidden        HTTPError = httpError{http.StatusForbidden}
	ErrorNotFound         HTTPError = httpError{http.StatusNotFound}
	ErrorMethodNotAllowed HTTPError = httpError{http.StatusMethodNotAllowed}

	// 5xx
	ErrorInternalServer HTTPError = httpError{http.StatusInternalServerError}
)

type httpError struct{ code int }

func (e httpError) Error() string {
	return http.StatusText(e.code)
}

func (e httpError) Status() int {
	return e.code
}
<|endoftext|>


<commit_before>package minion

import (
	"math"
	"net/http"

	"github.com/google/jsonapi"
	"github.com/unrolled/render"
)

// Context the context of each request
type Context struct {
	Writer  http.ResponseWriter
	Req     *http.Request
	Session Session
	Engine  *Engine
	render  *render.Render
	// writer   writer
	handlers []HandlerFunc
	index    int8
}

const (
	abortIndex = math.MaxInt8 / 2
)

// Next should be used only in the middlewares.
// It executes the pending handlers in the chain inside the calling handler.
func (c *Context) Next() {
	c.index++
	s := int8(len(c.handlers))
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

// SetHeader sets a response header.
func (c *Context) SetHeader(key, value string) {
	c.Writer.Header().Set(key, value)
}

// Abort Forces the system to do not continue calling the pending handlers in the chain.
func (c *Context) Abort() {
	c.index = abortIndex
}

// Redirect returns a HTTP redirect to the specific location. default for 302
func (c *Context) Redirect(status int, location string) {
	c.SetHeader("Location", location)
	if status > 0 {
		http.Redirect(c.Writer, c.Req, location, status)
	} else {
		http.Redirect(c.Writer, c.Req, location, http.StatusTemporaryRedirect)
	}
}

// JSON Serializes the given struct as JSON into the response body in a fast and efficient way.
// It also sets the Content-Type as "application/json".
func (c *Context) JSON(status int, data interface{}) {
	c.render.JSON(c.Writer, status, data)
}

// JSONP Serializes the given struct as JSONP into the response body in a fast and efficient way.
// It also sets the Content-Type as "application/javascript".
func (c *Context) JSONP(status int, data interface{}, callback string) {
	c.render.JSONP(c.Writer, status, callback, data)
}

// Text Writes the given string into the response body and sets the Content-Type to "text/plain".
func (c *Context) Text(status int, data string) {
	c.render.Text(c.Writer, status, data)
}

// HTML renders the html template and sets the Content-Type to "text/html".
func (c *Context) HTML(status int, tmpl string, data interface{}) {
	c.render.HTML(c.Writer, status, tmpl, data)
}

// MarshalOnePayload marshal the struct and return as jsonaapi
func (c *Context) MarshalOnePayload(status int, model interface{}) {
	c.Writer.WriteHeader(status)
	c.Writer.Header().Set("Content-Type", "application/vnd.api+json")
	if err := jsonapi.MarshalOnePayload(c.Writer, model); err != nil {
		http.Error(c.Writer, err.Error(), 500)
	}
}

// MarshalManyPayload marshal the struct and return as jsonaapi
func (c *Context) MarshalManyPayload(status int, models []interface{}) {
	c.Writer.WriteHeader(status)
	c.Writer.Header().Set("Content-Type", "application/vnd.api+json")
	if err := jsonapi.MarshalManyPayload(c.Writer, models); err != nil {
		http.Error(c.Writer, err.Error(), 500)
	}
}

func (c *Engine) createContext(rw http.ResponseWriter, req *http.Request, handlers []HandlerFunc) *Context {
	ctx := c.pool.Get().(*Context)
	ctx.Writer = rw
	ctx.Req = req
	ctx.handlers = handlers
	ctx.index = -1
	return ctx
}

func (c *Engine) reuseContext(ctx *Context) {
	c.pool.Put(ctx)
}
<commit_msg>Remove Session reference<commit_after>package minion

import (
	"math"
	"net/http"

	"github.com/google/jsonapi"
	"github.com/unrolled/render"
)

// Context the context of each request
type Context struct {
	Writer http.ResponseWriter
	Req    *http.Request
	Engine *Engine
	render *render.Render
	// writer   writer
	handlers []HandlerFunc
	index    int8
}

const (
	abortIndex = math.MaxInt8 / 2
)

// Next should be used only in the middlewares.
// It executes the pending handlers in the chain inside the calling handler.
func (c *Context) Next() {
	c.index++
	s := int8(len(c.handlers))
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

// SetHeader sets a response header.
func (c *Context) SetHeader(key, value string) {
	c.Writer.Header().Set(key, value)
}

// Abort Forces the system to do not continue calling the pending handlers in the chain.
func (c *Context) Abort() {
	c.index = abortIndex
}

// Redirect returns a HTTP redirect to the specific location. default for 302
func (c *Context) Redirect(status int, location string) {
	c.SetHeader("Location", location)
	if status > 0 {
		http.Redirect(c.Writer, c.Req, location, status)
	} else {
		http.Redirect(c.Writer, c.Req, location, http.StatusTemporaryRedirect)
	}
}

// JSON Serializes the given struct as JSON into the response body in a fast and efficient way.
// It also sets the Content-Type as "application/json".
func (c *Context) JSON(status int, data interface{}) {
	c.render.JSON(c.Writer, status, data)
}

// JSONP Serializes the given struct as JSONP into the response body in a fast and efficient way.
// It also sets the Content-Type as "application/javascript".
func (c *Context) JSONP(status int, data interface{}, callback string) {
	c.render.JSONP(c.Writer, status, callback, data)
}

// Text Writes the given string into the response body and sets the Content-Type to "text/plain".
func (c *Context) Text(status int, data string) {
	c.render.Text(c.Writer, status, data)
}

// HTML renders the html template and sets the Content-Type to "text/html".
func (c *Context) HTML(status int, tmpl string, data interface{}) {
	c.render.HTML(c.Writer, status, tmpl, data)
}

// MarshalOnePayload marshal the struct and return as jsonaapi
func (c *Context) MarshalOnePayload(status int, model interface{}) {
	c.Writer.WriteHeader(status)
	c.Writer.Header().Set("Content-Type", "application/vnd.api+json")
	if err := jsonapi.MarshalOnePayload(c.Writer, model); err != nil {
		http.Error(c.Writer, err.Error(), 500)
	}
}

// MarshalManyPayload marshal the struct and return as jsonaapi
func (c *Context) MarshalManyPayload(status int, models []interface{}) {
	c.Writer.WriteHeader(status)
	c.Writer.Header().Set("Content-Type", "application/vnd.api+json")
	if err := jsonapi.MarshalManyPayload(c.Writer, models); err != nil {
		http.Error(c.Writer, err.Error(), 500)
	}
}

func (c *Engine) createContext(rw http.ResponseWriter, req *http.Request, handlers []HandlerFunc) *Context {
	ctx := c.pool.Get().(*Context)
	ctx.Writer = rw
	ctx.Req = req
	ctx.handlers = handlers
	ctx.index = -1
	return ctx
}

func (c *Engine) reuseContext(ctx *Context) {
	c.pool.Put(ctx)
}
<|endoftext|>


<commit_before>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package context

import (
	"net/http"
	"sync"
	"time"
)

var (
	mutex sync.Mutex
	data  = make(map[*http.Request]map[interface{}]interface{})
	datat = make(map[*http.Request]int64)
)

// Set stores a value for a given key in a given request.
func Set(r *http.Request, key, val interface{}) {
	mutex.Lock()
	defer mutex.Unlock()
	if data[r] == nil {
		data[r] = make(map[interface{}]interface{})
		datat[r] = time.Now().Unix()
	}
	data[r][key] = val
}

// Get returns a value stored for a given key in a given request.
func Get(r *http.Request, key interface{}) interface{} {
	mutex.Lock()
	defer mutex.Unlock()
	if data[r] != nil {
		return data[r][key]
	}
	return nil
}

// GetOk returns stored value and presence state like multi-value return of map access.
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
	mutex.Lock()
	defer mutex.Unlock()
	if _, ok := data[r]; ok {
		value, ok := data[r][key]
		return value, ok
	}
	return nil, false
}

// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
func GetAll(r *http.Request) map[interface{}]interface{} {
	mutex.Lock()
	defer mutex.Unlock()

	if context, ok := data[r]; ok {
		return context
	}
	return nil
}

// GetAllOk returns all stored values for the request as a map. It returns not
// ok if the request was never registered.
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
	mutex.Lock()
	defer mutex.Unlock()

	context, ok := data[r]
	return context, ok
}

// Delete removes a value stored for a given key in a given request.
func Delete(r *http.Request, key interface{}) {
	mutex.Lock()
	defer mutex.Unlock()
	if data[r] != nil {
		delete(data[r], key)
	}
}

// Clear removes all values stored for a given request.
//
// This is usually called by a handler wrapper to clean up request
// variables at the end of a request lifetime. See ClearHandler().
func Clear(r *http.Request) {
	mutex.Lock()
	defer mutex.Unlock()
	clear(r)
}

// clear is Clear without the lock.
func clear(r *http.Request) {
	delete(data, r)
	delete(datat, r)
}

// Purge removes request data stored for longer than maxAge, in seconds.
// It returns the amount of requests removed.
//
// If maxAge <= 0, all request data is removed.
//
// This is only used for sanity check: in case context cleaning was not
// properly set some request data can be kept forever, consuming an increasing
// amount of memory. In case this is detected, Purge() must be called
// periodically until the problem is fixed.
func Purge(maxAge int) int {
	mutex.Lock()
	defer mutex.Unlock()
	count := 0
	if maxAge <= 0 {
		count = len(data)
		data = make(map[*http.Request]map[interface{}]interface{})
		datat = make(map[*http.Request]int64)
	} else {
		min := time.Now().Unix() - int64(maxAge)
		for r := range data {
			if datat[r] < min {
				clear(r)
				count++
			}
		}
	}
	return count
}

// ClearHandler wraps an http.Handler and clears request values at the end
// of a request lifetime.
func ClearHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer Clear(r)
		h.ServeHTTP(w, r)
	})
}
<commit_msg>Switch to RWMutex<commit_after>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package context

import (
	"net/http"
	"sync"
	"time"
)

var (
	mutex sync.RWMutex
	data  = make(map[*http.Request]map[interface{}]interface{})
	datat = make(map[*http.Request]int64)
)

// Set stores a value for a given key in a given request.
func Set(r *http.Request, key, val interface{}) {
	mutex.Lock()
	defer mutex.Unlock()
	if data[r] == nil {
		data[r] = make(map[interface{}]interface{})
		datat[r] = time.Now().Unix()
	}
	data[r][key] = val
}

// Get returns a value stored for a given key in a given request.
func Get(r *http.Request, key interface{}) interface{} {
	mutex.RLock()
	defer mutex.RUnlock()
	if data[r] != nil {
		return data[r][key]
	}
	return nil
}

// GetOk returns stored value and presence state like multi-value return of map access.
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
	mutex.RLock()
	defer mutex.RUnlock()
	if _, ok := data[r]; ok {
		value, ok := data[r][key]
		return value, ok
	}
	return nil, false
}

// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
func GetAll(r *http.Request) map[interface{}]interface{} {
	mutex.RLock()
	defer mutex.RUnlock()

	if context, ok := data[r]; ok {
		return context
	}
	return nil
}

// GetAllOk returns all stored values for the request as a map. It returns not
// ok if the request was never registered.
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
	mutex.RLock()
	defer mutex.RUnlock()

	context, ok := data[r]
	return context, ok
}

// Delete removes a value stored for a given key in a given request.
func Delete(r *http.Request, key interface{}) {
	mutex.Lock()
	defer mutex.Unlock()
	if data[r] != nil {
		delete(data[r], key)
	}
}

// Clear removes all values stored for a given request.
//
// This is usually called by a handler wrapper to clean up request
// variables at the end of a request lifetime. See ClearHandler().
func Clear(r *http.Request) {
	mutex.Lock()
	defer mutex.Unlock()
	clear(r)
}

// clear is Clear without the lock.
func clear(r *http.Request) {
	delete(data, r)
	delete(datat, r)
}

// Purge removes request data stored for longer than maxAge, in seconds.
// It returns the amount of requests removed.
//
// If maxAge <= 0, all request data is removed.
//
// This is only used for sanity check: in case context cleaning was not
// properly set some request data can be kept forever, consuming an increasing
// amount of memory. In case this is detected, Purge() must be called
// periodically until the problem is fixed.
func Purge(maxAge int) int {
	mutex.Lock()
	defer mutex.Unlock()
	count := 0
	if maxAge <= 0 {
		count = len(data)
		data = make(map[*http.Request]map[interface{}]interface{})
		datat = make(map[*http.Request]int64)
	} else {
		min := time.Now().Unix() - int64(maxAge)
		for r := range data {
			if datat[r] < min {
				clear(r)
				count++
			}
		}
	}
	return count
}

// ClearHandler wraps an http.Handler and clears request values at the end
// of a request lifetime.
func ClearHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer Clear(r)
		h.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 - Cloud Instruments Co. Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package seelog

import (
	"os"
	"errors"
	"runtime"
	"strings"
	"path/filepath"
	"time"
)

// Represents runtime caller context
type logContext struct {
	funcName  string
	shortPath string
	fullPath  string
	fileName string
	callTime time.Time
}

// Returns context of the caller
func currentContext() (*logContext, error) {
	return specificContext(1)
}

// Returns context of the function with placed "skip" stack frames of the caller
// If skip == 0 then behaves like currentContext
func specificContext(skip int) (*logContext, error) {
	if skip < 0 {
		return nil, errors.New("Can not skip negative stack frames")
	}

	fullPath, shortPath, function, err := extractCallerInfo(skip + 2)
	if err != nil {
		return nil, err
	}
	_, fileName := filepath.Split(fullPath)
	return &logContext{function, shortPath, fullPath, fileName, time.Now()}, nil
}

func (context *logContext) Func() string {
	return context.funcName
}

func (context *logContext) ShortPath() string {
	return context.shortPath
}

func (context *logContext) FullPath() string {
	return context.fullPath
}

func (context *logContext) FileName() string {
	return context.fileName
}

func (context *logContext) CallTime() time.Time {
	return context.callTime;
}

var workingDir = ""

func init() {
	setWorkDir()
}

func setWorkDir() {
	workDir, workingDirError := os.Getwd()
	if workingDirError != nil {
		workingDir = "/"
		return
	}

	workingDir = workDir + "/"
}

func extractCallerInfo(skip int) (fullPath string, shortPath string, funcName string,err error) {
	pc, fullPath, _, ok := runtime.Caller(skip)

	if !ok {
		return "", "", "", errors.New("Error during runtime.Caller")
	}

	if strings.HasPrefix(fullPath, workingDir) {
		shortPath = fullPath[len(workingDir):]
	} else {
		shortPath = fullPath
	}

	funName := runtime.FuncForPC(pc).Name()
	var functionName string
	if strings.HasPrefix(funName, workingDir) {
		functionName = funName[len(workingDir):len(funName)]
	} else {
		functionName = funName
	}

	return fullPath, shortPath, functionName, nil
}
<commit_msg>Changed path separator in context<commit_after>// Copyright (c) 2012 - Cloud Instruments Co. Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package seelog

import (
	"os"
	"errors"
	"runtime"
	"strings"
	"path/filepath"
	"time"
)

// Represents runtime caller context
type logContext struct {
	funcName  string
	shortPath string
	fullPath  string
	fileName string
	callTime time.Time
}

// Returns context of the caller
func currentContext() (*logContext, error) {
	return specificContext(1)
}

// Returns context of the function with placed "skip" stack frames of the caller
// If skip == 0 then behaves like currentContext
func specificContext(skip int) (*logContext, error) {
	if skip < 0 {
		return nil, errors.New("Can not skip negative stack frames")
	}

	fullPath, shortPath, function, err := extractCallerInfo(skip + 2)
	if err != nil {
		return nil, err
	}
	_, fileName := filepath.Split(fullPath)
	return &logContext{function, shortPath, fullPath, fileName, time.Now()}, nil
}

func (context *logContext) Func() string {
	return context.funcName
}

func (context *logContext) ShortPath() string {
	return context.shortPath
}

func (context *logContext) FullPath() string {
	return context.fullPath
}

func (context *logContext) FileName() string {
	return context.fileName
}

func (context *logContext) CallTime() time.Time {
	return context.callTime;
}

var workingDir = ""

func init() {
	setWorkDir()
}

func setWorkDir() {
	workDir, workingDirError := os.Getwd()
	if workingDirError != nil {
		workingDir = string(os.PathSeparator)
		return
	}

	workingDir = workDir + string(os.PathSeparator)
}

func extractCallerInfo(skip int) (fullPath string, shortPath string, funcName string,err error) {
	pc, fullPath, _, ok := runtime.Caller(skip)

	if !ok {
		return "", "", "", errors.New("Error during runtime.Caller")
	}

	if strings.HasPrefix(fullPath, workingDir) {
		shortPath = fullPath[len(workingDir):]
	} else {
		shortPath = fullPath
	}

	funName := runtime.FuncForPC(pc).Name()
	var functionName string
	if strings.HasPrefix(funName, workingDir) {
		functionName = funName[len(workingDir):len(funName)]
	} else {
		functionName = funName
	}

	return fullPath, shortPath, functionName, nil
}
<|endoftext|>


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

package spinner

import (
	"bytes"
	"fmt"
	"reflect"
	"sync"
	"testing"
	"time"
)

const baseWait = 3

// syncBuffer
type syncBuffer struct {
	sync.Mutex
	bytes.Buffer
}

// Write
func (b *syncBuffer) Write(data []byte) (int, error) {
	b.Lock()
	defer b.Unlock()
	return b.Buffer.Write(data)
}

// withOutput
func withOutput(a []string, d time.Duration) (*Spinner, *syncBuffer) {
	var out syncBuffer
	s := New(a, d)
	s.Writer = &out
	return s, &out
}

// TestNew verifies that the returned instance is of the proper type
func TestNew(t *testing.T) {
	s := New(CharSets[1], 1*time.Second)
	if reflect.TypeOf(s).String() != "*spinner.Spinner" {
		t.Error("New returned incorrect type")
	}
}

// TestStart will verify a spinner can be started
func TestStart(t *testing.T) {
	s := New(CharSets[1], 100*time.Millisecond)
	s.Color("red")
	s.Start()
	time.Sleep(baseWait * time.Second)
	s.Stop()
	time.Sleep(100 * time.Millisecond)
}

// TestStop will verify a spinner can be stopped
func TestStop(t *testing.T) {
	p, out := withOutput(CharSets[14], 100*time.Millisecond)
	p.Color("yellow")
	p.Start()
	time.Sleep(500 * time.Millisecond)
	p.Stop()
	// because the spinner will print an appropriate number of backspaces before stopping,
	// let it complete that sleep
	time.Sleep(100 * time.Millisecond)
	out.Lock()
	len1 := out.Len()
	out.Unlock()
	time.Sleep(300 * time.Millisecond)
	out.Lock()
	defer out.Unlock()
	len2 := out.Len()
	if len1 != len2 {
		t.Errorf("expected equal, got %v != %v", len1, len2)
	}
	p = nil
}

// TestRestart will verify a spinner can be stopped and started again
func TestRestart(t *testing.T) {
	s := New(CharSets[4], 50*time.Millisecond)
	var out syncBuffer
	s.Writer = &out
	s.Start()
	s.Color("cyan")
	time.Sleep(200 * time.Millisecond)
	s.Restart()
	time.Sleep(200 * time.Millisecond)
	s.Stop()
	time.Sleep(50 * time.Millisecond)
	out.Lock()
	defer out.Unlock()
	result := out.Bytes()
	first := result[:len(result)/2]
	secnd := result[len(result)/2:]
	if string(first) != string(secnd) {
		t.Errorf("Expected ==, got \n%#v != \n%#v", first, secnd)
	}
	s = nil
}

// TestReverse will verify that the given spinner can stop and start again reversed
func TestReverse(t *testing.T) {
	a := New(CharSets[10], 1*time.Second)
	a.Color("red")
	a.Start()
	time.Sleep(baseWait * time.Second)
	a.Reverse()
	a.Restart()
	time.Sleep(baseWait * time.Second)
	a.Reverse()
	a.Restart()
	time.Sleep(baseWait * time.Second)
	a.Stop()
	a = nil
}

// TestUpdateSpeed verifies that the delay can be updated
func TestUpdateSpeed(t *testing.T) {
	s := New(CharSets[10], 1*time.Second)
	delay1 := s.Delay
	s.UpdateSpeed(baseWait * time.Second)
	delay2 := s.Delay
	if delay1 == delay2 {
		t.Error("update of speed failed")
	}
	s = nil
}

// TestUpdateCharSet verifies that character sets can be updated
func TestUpdateCharSet(t *testing.T) {
	s := New(CharSets[14], 1*time.Second)
	charSet1 := s.chars
	s.UpdateCharSet(CharSets[1])
	charSet2 := s.chars
	for i := range charSet1 {
		if charSet1[i] == charSet2[i] {
			t.Error("update of char set failed")
		}
	}
	s = nil
}

// TestGenerateNumberSequence verifies that a string slice of a spefic size is returned
func TestGenerateNumberSequence(t *testing.T) {
	elementCount := 100
	seq := GenerateNumberSequence(elementCount)
	if reflect.TypeOf(seq).String() != "[]string" {
		t.Error("received incorrect type in return from GenerateNumberSequence")
	}
	t.Log("In: ", elementCount)
	t.Log("Out: ", len(seq))
	if len(seq) != elementCount {
		t.Error("number of elements in slice doesn't match expected count")
	}
}

// TestBackspace proves that the correct number of characters are removed.
func TestBackspace(t *testing.T) {
	// Because of buffering of output and time weirdness, somethings
	// are broken for an indeterminant reason without a wait
	time.Sleep(75 * time.Millisecond)
	fmt.Println()
	s := New(CharSets[0], 100*time.Millisecond)
	s.Color("blue")
	s.Start()
	fmt.Print("This is on the same line as the spinner: ")
	time.Sleep(baseWait * time.Second)
	s.Stop()
}

// TestColorError tests that if an invalid color string is passed to the Color
// function, the invalid color error is returned
func TestColorError(t *testing.T) {
	s := New(CharSets[0], 100*time.Millisecond)

	invalidColorName := "bluez"
	validColorName := "green"

	if s.Color(invalidColorName) != errInvalidColor {
		t.Error("Color function did not return an error when given an invalid color.")
	}

	if s.Color(validColorName) != nil {
		t.Error("Color function returned an error when given a valid color.")
	}
}

/*
Benchmarks
*/

// BenchmarkNew runs a benchmark for the New() function
func BenchmarkNew(b *testing.B) {
	for n := 0; n < b.N; n++ {
		New(CharSets[1], 1*time.Second)
	}
}

func BenchmarkNewStartStop(b *testing.B) {
	for n := 0; n < b.N; n++ {
		s := New(CharSets[1], 1*time.Second)
		s.Start()
		s.Stop()
	}
}
<commit_msg>updated failed test case messages<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package spinner

import (
	"bytes"
	"fmt"
	"reflect"
	"sync"
	"testing"
	"time"
)

const baseWait = 3

// syncBuffer
type syncBuffer struct {
	sync.Mutex
	bytes.Buffer
}

// Write
func (b *syncBuffer) Write(data []byte) (int, error) {
	b.Lock()
	defer b.Unlock()
	return b.Buffer.Write(data)
}

// withOutput
func withOutput(a []string, d time.Duration) (*Spinner, *syncBuffer) {
	var out syncBuffer
	s := New(a, d)
	s.Writer = &out
	return s, &out
}

// TestNew verifies that the returned instance is of the proper type
func TestNew(t *testing.T) {
	s := New(CharSets[1], 1*time.Second)
	if reflect.TypeOf(s).String() != "*spinner.Spinner" {
		t.Error("New returned incorrect type")
	}
}

// TestStart will verify a spinner can be started
func TestStart(t *testing.T) {
	s := New(CharSets[1], 100*time.Millisecond)
	s.Color("red")
	s.Start()
	time.Sleep(baseWait * time.Second)
	s.Stop()
	time.Sleep(100 * time.Millisecond)
}

// TestStop will verify a spinner can be stopped
func TestStop(t *testing.T) {
	p, out := withOutput(CharSets[14], 100*time.Millisecond)
	p.Color("yellow")
	p.Start()
	time.Sleep(500 * time.Millisecond)
	p.Stop()
	// because the spinner will print an appropriate number of backspaces before stopping,
	// let it complete that sleep
	time.Sleep(100 * time.Millisecond)
	out.Lock()
	len1 := out.Len()
	out.Unlock()
	time.Sleep(300 * time.Millisecond)
	out.Lock()
	defer out.Unlock()
	len2 := out.Len()
	if len1 != len2 {
		t.Errorf("expected equal, got %v != %v", len1, len2)
	}
	p = nil
}

// TestRestart will verify a spinner can be stopped and started again
func TestRestart(t *testing.T) {
	s := New(CharSets[4], 50*time.Millisecond)
	var out syncBuffer
	s.Writer = &out
	s.Start()
	s.Color("cyan")
	time.Sleep(200 * time.Millisecond)
	s.Restart()
	time.Sleep(200 * time.Millisecond)
	s.Stop()
	time.Sleep(50 * time.Millisecond)
	out.Lock()
	defer out.Unlock()
	result := out.Bytes()
	first := result[:len(result)/2]
	secnd := result[len(result)/2:]
	if string(first) != string(secnd) {
		t.Errorf("Expected ==, got \n%#v != \n%#v", first, secnd)
	}
	s = nil
}

// TestReverse will verify that the given spinner can stop and start again reversed
func TestReverse(t *testing.T) {
	a := New(CharSets[10], 1*time.Second)
	a.Color("red")
	a.Start()
	time.Sleep(baseWait * time.Second)
	a.Reverse()
	a.Restart()
	time.Sleep(baseWait * time.Second)
	a.Reverse()
	a.Restart()
	time.Sleep(baseWait * time.Second)
	a.Stop()
	a = nil
}

// TestUpdateSpeed verifies that the delay can be updated
func TestUpdateSpeed(t *testing.T) {
	s := New(CharSets[10], 1*time.Second)
	delay1 := s.Delay
	s.UpdateSpeed(baseWait * time.Second)
	delay2 := s.Delay
	if delay1 == delay2 {
		t.Error("update of speed failed")
	}
	s = nil
}

// TestUpdateCharSet verifies that character sets can be updated
func TestUpdateCharSet(t *testing.T) {
	s := New(CharSets[14], 1*time.Second)
	charSet1 := s.chars
	s.UpdateCharSet(CharSets[1])
	charSet2 := s.chars
	for i := range charSet1 {
		if charSet1[i] == charSet2[i] {
			t.Error("update of char set failed")
		}
	}
	s = nil
}

// TestGenerateNumberSequence verifies that a string slice of a spefic size is returned
func TestGenerateNumberSequence(t *testing.T) {
	elementCount := 100
	seq := GenerateNumberSequence(elementCount)
	if reflect.TypeOf(seq).String() != "[]string" {
		t.Error("received incorrect type in return from GenerateNumberSequence")
	}
	t.Log("In: ", elementCount)
	t.Log("Out: ", len(seq))
	if len(seq) != elementCount {
		t.Error("number of elements in slice doesn't match expected count")
	}
}

// TestBackspace proves that the correct number of characters are removed.
func TestBackspace(t *testing.T) {
	// Because of buffering of output and time weirdness, somethings
	// are broken for an indeterminant reason without a wait
	time.Sleep(75 * time.Millisecond)
	fmt.Println()
	s := New(CharSets[0], 100*time.Millisecond)
	s.Color("blue")
	s.Start()
	fmt.Print("This is on the same line as the spinner: ")
	time.Sleep(baseWait * time.Second)
	s.Stop()
}

// TestColorError tests that if an invalid color string is passed to the Color
// function, the invalid color error is returned
func TestColorError(t *testing.T) {
	s := New(CharSets[0], 100*time.Millisecond)

	invalidColorName := "bluez"
	validColorName := "green"

	if s.Color(invalidColorName) != errInvalidColor {
		t.Error("Color method did not return an error when given an invalid color.")
	}

	if s.Color(validColorName) != nil {
		t.Error("Color method did not return nil when given a valid color name.")
	}
}

/*
Benchmarks
*/

// BenchmarkNew runs a benchmark for the New() function
func BenchmarkNew(b *testing.B) {
	for n := 0; n < b.N; n++ {
		New(CharSets[1], 1*time.Second)
	}
}

func BenchmarkNewStartStop(b *testing.B) {
	for n := 0; n < b.N; n++ {
		s := New(CharSets[1], 1*time.Second)
		s.Start()
		s.Stop()
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin freebsd linux netbsd openbsd

package test

// functional test harness for unix.

import (
	"bytes"
	"crypto"
	"crypto/dsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"testing"
	"text/template"
	"time"

	"code.google.com/p/go.crypto/ssh"
)

const sshd_config = `
Protocol 2
HostKey {{.Dir}}/ssh_host_rsa_key
HostKey {{.Dir}}/ssh_host_dsa_key
HostKey {{.Dir}}/ssh_host_ecdsa_key
Pidfile {{.Dir}}/sshd.pid
#UsePrivilegeSeparation no
KeyRegenerationInterval 3600
ServerKeyBits 768
SyslogFacility AUTH
LogLevel DEBUG2
LoginGraceTime 120
PermitRootLogin no
StrictModes no
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile	{{.Dir}}/authorized_keys
IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no
`

var (
	configTmpl template.Template
	sshd       string // path to sshd
	rsakey     *rsa.PrivateKey
)

func init() {
	template.Must(configTmpl.Parse(sshd_config))
	block, _ := pem.Decode([]byte(testClientPrivateKey))
	rsakey, _ = x509.ParsePKCS1PrivateKey(block.Bytes)
}

type server struct {
	t          *testing.T
	cleanup    func() // executed during Shutdown
	configfile string
	cmd        *exec.Cmd
	output     bytes.Buffer // holds stderr from sshd process
}

func username() string {
	var username string
	if user, err := user.Current(); err == nil {
		username = user.Username
	} else {
		// user.Current() currently requires cgo. If an error is
		// returned attempt to get the username from the environment.
		log.Printf("user.Current: %v; falling back on $USER", err)
		username = os.Getenv("USER")
	}
	if username == "" {
		panic("Unable to get username")
	}
	return username
}

func clientConfig() *ssh.ClientConfig {
	kc := new(keychain)
	kc.keys = append(kc.keys, rsakey)
	config := &ssh.ClientConfig{
		User: username(),
		Auth: []ssh.ClientAuth{
			ssh.ClientAuthKeyring(kc),
		},
	}
	return config
}

func (s *server) Dial(config *ssh.ClientConfig) *ssh.ClientConn {
	s.cmd = exec.Command("sshd", "-f", s.configfile, "-i")
	stdin, err := s.cmd.StdinPipe()
	if err != nil {
		s.t.Fatal(err)
	}
	stdout, err := s.cmd.StdoutPipe()
	if err != nil {
		s.t.Fatal(err)
	}
	s.cmd.Stderr = os.Stderr // &s.output
	err = s.cmd.Start()
	if err != nil {
		s.t.FailNow()
		s.Shutdown()
		s.t.Fatal(err)
	}
	conn, err := ssh.Client(&client{stdin, stdout}, config)
	if err != nil {
		s.t.FailNow()
		s.Shutdown()
		s.t.Fatal(err)
	}
	return conn
}

func (s *server) Shutdown() {
	if s.cmd != nil && s.cmd.Process != nil {
		if err := s.cmd.Process.Kill(); err != nil {
			s.t.Error(err)
		}
		s.cmd.Wait()
	}
	if s.t.Failed() {
		// log any output from sshd process
		s.t.Log(s.output.String())
	}
	s.cleanup()
}

// client wraps a pair of Reader/WriteClosers to implement the
// net.Conn interface.
type client struct {
	io.WriteCloser
	io.Reader
}

func (c *client) LocalAddr() net.Addr              { return nil }
func (c *client) RemoteAddr() net.Addr             { return nil }
func (c *client) SetDeadline(time.Time) error      { return nil }
func (c *client) SetReadDeadline(time.Time) error  { return nil }
func (c *client) SetWriteDeadline(time.Time) error { return nil }

// newServer returns a new mock ssh server.
func newServer(t *testing.T) *server {
	dir, err := ioutil.TempDir("", "sshtest")
	if err != nil {
		t.Fatal(err)
	}
	f, err := os.Create(filepath.Join(dir, "sshd_config"))
	if err != nil {
		t.Fatal(err)
	}
	err = configTmpl.Execute(f, map[string]string{
		"Dir": dir,
	})
	if err != nil {
		t.Fatal(err)
	}
	f.Close()

	for k, v := range keys {
		f, err := os.OpenFile(filepath.Join(dir, k), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
		if err != nil {
			t.Fatal(err)
		}
		if _, err := f.Write([]byte(v)); err != nil {
			t.Fatal(err)
		}
		f.Close()
	}

	return &server{
		t:          t,
		configfile: f.Name(),
		cleanup: func() {
			if err := os.RemoveAll(dir); err != nil {
				t.Error(err)
			}
		},
	}
}

// keychain implements the ClientKeyring interface
type keychain struct {
	keys []interface{}
}

func (k *keychain) Key(i int) (interface{}, error) {
	if i < 0 || i >= len(k.keys) {
		return nil, nil
	}
	switch key := k.keys[i].(type) {
	case *rsa.PrivateKey:
		return &key.PublicKey, nil
	case *dsa.PrivateKey:
		return &key.PublicKey, nil
	}
	panic("unknown key type")
}

func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
	hashFunc := crypto.SHA1
	h := hashFunc.New()
	h.Write(data)
	digest := h.Sum(nil)
	switch key := k.keys[i].(type) {
	case *rsa.PrivateKey:
		return rsa.SignPKCS1v15(rand, key, hashFunc, digest)
	}
	return nil, errors.New("ssh: unknown key type")
}

func (k *keychain) loadPEM(file string) error {
	buf, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}
	block, _ := pem.Decode(buf)
	if block == nil {
		return errors.New("ssh: no key found")
	}
	r, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return err
	}
	k.keys = append(k.keys, r)
	return nil
}
<commit_msg>go.crypto/ssh/test: Replace FailNow with Fail where it is obvious that the test doesn't want to fail now.<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin freebsd linux netbsd openbsd

package test

// functional test harness for unix.

import (
	"bytes"
	"crypto"
	"crypto/dsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"testing"
	"text/template"
	"time"

	"code.google.com/p/go.crypto/ssh"
)

const sshd_config = `
Protocol 2
HostKey {{.Dir}}/ssh_host_rsa_key
HostKey {{.Dir}}/ssh_host_dsa_key
HostKey {{.Dir}}/ssh_host_ecdsa_key
Pidfile {{.Dir}}/sshd.pid
#UsePrivilegeSeparation no
KeyRegenerationInterval 3600
ServerKeyBits 768
SyslogFacility AUTH
LogLevel DEBUG2
LoginGraceTime 120
PermitRootLogin no
StrictModes no
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile	{{.Dir}}/authorized_keys
IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no
`

var (
	configTmpl template.Template
	sshd       string // path to sshd
	rsakey     *rsa.PrivateKey
)

func init() {
	template.Must(configTmpl.Parse(sshd_config))
	block, _ := pem.Decode([]byte(testClientPrivateKey))
	rsakey, _ = x509.ParsePKCS1PrivateKey(block.Bytes)
}

type server struct {
	t          *testing.T
	cleanup    func() // executed during Shutdown
	configfile string
	cmd        *exec.Cmd
	output     bytes.Buffer // holds stderr from sshd process
}

func username() string {
	var username string
	if user, err := user.Current(); err == nil {
		username = user.Username
	} else {
		// user.Current() currently requires cgo. If an error is
		// returned attempt to get the username from the environment.
		log.Printf("user.Current: %v; falling back on $USER", err)
		username = os.Getenv("USER")
	}
	if username == "" {
		panic("Unable to get username")
	}
	return username
}

func clientConfig() *ssh.ClientConfig {
	kc := new(keychain)
	kc.keys = append(kc.keys, rsakey)
	config := &ssh.ClientConfig{
		User: username(),
		Auth: []ssh.ClientAuth{
			ssh.ClientAuthKeyring(kc),
		},
	}
	return config
}

func (s *server) Dial(config *ssh.ClientConfig) *ssh.ClientConn {
	s.cmd = exec.Command("sshd", "-f", s.configfile, "-i")
	stdin, err := s.cmd.StdinPipe()
	if err != nil {
		s.t.Fatal(err)
	}
	stdout, err := s.cmd.StdoutPipe()
	if err != nil {
		s.t.Fatal(err)
	}
	s.cmd.Stderr = os.Stderr // &s.output
	err = s.cmd.Start()
	if err != nil {
		s.t.Fail()
		s.Shutdown()
		s.t.Fatalf("s.cmd.Start: %v", err)
	}
	conn, err := ssh.Client(&client{stdin, stdout}, config)
	if err != nil {
		s.t.Fail()
		s.Shutdown()
		s.t.Fatalf("ssh.Client: %v", err)
	}
	return conn
}

func (s *server) Shutdown() {
	if s.cmd != nil && s.cmd.Process != nil {
		if err := s.cmd.Process.Kill(); err != nil {
			s.t.Error(err)
		}
		s.cmd.Wait()
	}
	if s.t.Failed() {
		// log any output from sshd process
		s.t.Log(s.output.String())
	}
	s.cleanup()
}

// client wraps a pair of Reader/WriteClosers to implement the
// net.Conn interface.
type client struct {
	io.WriteCloser
	io.Reader
}

func (c *client) LocalAddr() net.Addr              { return nil }
func (c *client) RemoteAddr() net.Addr             { return nil }
func (c *client) SetDeadline(time.Time) error      { return nil }
func (c *client) SetReadDeadline(time.Time) error  { return nil }
func (c *client) SetWriteDeadline(time.Time) error { return nil }

// newServer returns a new mock ssh server.
func newServer(t *testing.T) *server {
	dir, err := ioutil.TempDir("", "sshtest")
	if err != nil {
		t.Fatal(err)
	}
	f, err := os.Create(filepath.Join(dir, "sshd_config"))
	if err != nil {
		t.Fatal(err)
	}
	err = configTmpl.Execute(f, map[string]string{
		"Dir": dir,
	})
	if err != nil {
		t.Fatal(err)
	}
	f.Close()

	for k, v := range keys {
		f, err := os.OpenFile(filepath.Join(dir, k), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
		if err != nil {
			t.Fatal(err)
		}
		if _, err := f.Write([]byte(v)); err != nil {
			t.Fatal(err)
		}
		f.Close()
	}

	return &server{
		t:          t,
		configfile: f.Name(),
		cleanup: func() {
			if err := os.RemoveAll(dir); err != nil {
				t.Error(err)
			}
		},
	}
}

// keychain implements the ClientKeyring interface
type keychain struct {
	keys []interface{}
}

func (k *keychain) Key(i int) (interface{}, error) {
	if i < 0 || i >= len(k.keys) {
		return nil, nil
	}
	switch key := k.keys[i].(type) {
	case *rsa.PrivateKey:
		return &key.PublicKey, nil
	case *dsa.PrivateKey:
		return &key.PublicKey, nil
	}
	panic("unknown key type")
}

func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
	hashFunc := crypto.SHA1
	h := hashFunc.New()
	h.Write(data)
	digest := h.Sum(nil)
	switch key := k.keys[i].(type) {
	case *rsa.PrivateKey:
		return rsa.SignPKCS1v15(rand, key, hashFunc, digest)
	}
	return nil, errors.New("ssh: unknown key type")
}

func (k *keychain) loadPEM(file string) error {
	buf, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}
	block, _ := pem.Decode(buf)
	if block == nil {
		return errors.New("ssh: no key found")
	}
	r, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return err
	}
	k.keys = append(k.keys, r)
	return nil
}
<|endoftext|>


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

package stackdriver

import (
	"bytes"
	"fmt"
	"net"
	"net/url"
	"strings"
	"testing"
	"time"

	"github.com/go-kit/kit/log"
	monitoring "google.golang.org/genproto/googleapis/monitoring/v3"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/resolver/manual"
	"google.golang.org/grpc/status"
)

var longErrMessage = strings.Repeat("[error message]", 10)

func newLocalListener() net.Listener {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
			panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
		}
	}
	return l
}

func TestStoreErrorHandling(t *testing.T) {
	tests := []struct {
		status      *status.Status
		recoverable bool
	}{
		{
			status: nil,
		},
		{
			status:      status.New(codes.NotFound, longErrMessage),
			recoverable: false,
		},
		{
			status:      status.New(codes.Unavailable, longErrMessage),
			recoverable: true,
		},
	}

	for i, test := range tests {
		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
			listener := newLocalListener()
			grpcServer := grpc.NewServer()
			monitoring.RegisterMetricServiceServer(grpcServer, &metricServiceServer{test.status})
			go grpcServer.Serve(listener)
			defer grpcServer.Stop()

			serverURL, err := url.Parse("https://" + listener.Addr().String() + "?auth=false")
			if err != nil {
				t.Fatal(err)
			}

			c := NewClient(&ClientConfig{
				URL:     serverURL,
				Timeout: time.Second,
			})

			err = c.Store(&monitoring.CreateTimeSeriesRequest{
				TimeSeries: []*monitoring.TimeSeries{
					&monitoring.TimeSeries{},
				},
			})
			if test.status != nil {
				rerr, recoverable := err.(recoverableError)
				if recoverable != test.recoverable {
					if test.recoverable {
						t.Errorf("expected recoverableError in error %v", err)
					} else {
						t.Errorf("unexpected recoverableError in error %v", err)
					}
				}
				if recoverable {
					err = rerr.error
				}
				status := status.Convert(err)
				if status.Code() != test.status.Code() || status.Message() != test.status.Message() {
					t.Errorf("expected status '%v', got '%v'", test.status.Err(), status.Err())
				}
			}
		})
	}
}

func TestEmptyRequest(t *testing.T) {
	serverURL, err := url.Parse("http://localhost:12345")
	if err != nil {
		t.Fatal(err)
	}
	c := NewClient(&ClientConfig{
		URL:     serverURL,
		Timeout: time.Second,
	})
	if err := c.Store(&monitoring.CreateTimeSeriesRequest{}); err != nil {
		t.Fatal(err)
	}
}

func TestResolver(t *testing.T) {
	addressesToTest := []string{"stackdriver.invalid", "2001:db8::"}
	for _, address := range addressesToTest {
		grpcServer := grpc.NewServer()
		listener := newLocalListener()
		monitoring.RegisterMetricServiceServer(grpcServer, &metricServiceServer{nil})
		go grpcServer.Serve(listener)
		defer grpcServer.Stop()

		logBuffer := &bytes.Buffer{}
		defer func() {
			if logBuffer.Len() > 0 {
				t.Log(logBuffer.String())
			}
		}()
		logger := log.NewLogfmtLogger(logBuffer)

		// Without ?auth=false, the test fails with context deadline exceeded.
		serverURL, err := url.Parse("http://" + address + "?auth=false")
		if err != nil {
			t.Fatal(err)
		}

		res, _ := manual.GenerateAndRegisterManualResolver()
		res.InitialAddrs([]resolver.Address{
			{Addr: listener.Addr().String()},
		})

		c := NewClient(&ClientConfig{
			URL:      serverURL,
			Timeout:  time.Second,
			Resolver: res,
			Logger:   logger,
		})

		err = c.Store(&monitoring.CreateTimeSeriesRequest{
			TimeSeries: []*monitoring.TimeSeries{
				&monitoring.TimeSeries{},
			},
		})
		if err != nil {
			t.Fatal(err)
		}
		requestedTarget := c.conn.Target()
		expectedTarget := c.resolver.Scheme()+":///" + address
		if requestedTarget != expectedTarget {
			t.Errorf("ERROR: Remote address is %s, want " + expectedTarget,
				requestedTarget)
		}
	}
}
<commit_msg>Ran make format. (#139)<commit_after>// Copyright 2017 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package stackdriver

import (
	"bytes"
	"fmt"
	"net"
	"net/url"
	"strings"
	"testing"
	"time"

	"github.com/go-kit/kit/log"
	monitoring "google.golang.org/genproto/googleapis/monitoring/v3"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/resolver/manual"
	"google.golang.org/grpc/status"
)

var longErrMessage = strings.Repeat("[error message]", 10)

func newLocalListener() net.Listener {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
			panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
		}
	}
	return l
}

func TestStoreErrorHandling(t *testing.T) {
	tests := []struct {
		status      *status.Status
		recoverable bool
	}{
		{
			status: nil,
		},
		{
			status:      status.New(codes.NotFound, longErrMessage),
			recoverable: false,
		},
		{
			status:      status.New(codes.Unavailable, longErrMessage),
			recoverable: true,
		},
	}

	for i, test := range tests {
		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
			listener := newLocalListener()
			grpcServer := grpc.NewServer()
			monitoring.RegisterMetricServiceServer(grpcServer, &metricServiceServer{test.status})
			go grpcServer.Serve(listener)
			defer grpcServer.Stop()

			serverURL, err := url.Parse("https://" + listener.Addr().String() + "?auth=false")
			if err != nil {
				t.Fatal(err)
			}

			c := NewClient(&ClientConfig{
				URL:     serverURL,
				Timeout: time.Second,
			})

			err = c.Store(&monitoring.CreateTimeSeriesRequest{
				TimeSeries: []*monitoring.TimeSeries{
					&monitoring.TimeSeries{},
				},
			})
			if test.status != nil {
				rerr, recoverable := err.(recoverableError)
				if recoverable != test.recoverable {
					if test.recoverable {
						t.Errorf("expected recoverableError in error %v", err)
					} else {
						t.Errorf("unexpected recoverableError in error %v", err)
					}
				}
				if recoverable {
					err = rerr.error
				}
				status := status.Convert(err)
				if status.Code() != test.status.Code() || status.Message() != test.status.Message() {
					t.Errorf("expected status '%v', got '%v'", test.status.Err(), status.Err())
				}
			}
		})
	}
}

func TestEmptyRequest(t *testing.T) {
	serverURL, err := url.Parse("http://localhost:12345")
	if err != nil {
		t.Fatal(err)
	}
	c := NewClient(&ClientConfig{
		URL:     serverURL,
		Timeout: time.Second,
	})
	if err := c.Store(&monitoring.CreateTimeSeriesRequest{}); err != nil {
		t.Fatal(err)
	}
}

func TestResolver(t *testing.T) {
	addressesToTest := []string{"stackdriver.invalid", "2001:db8::"}
	for _, address := range addressesToTest {
		grpcServer := grpc.NewServer()
		listener := newLocalListener()
		monitoring.RegisterMetricServiceServer(grpcServer, &metricServiceServer{nil})
		go grpcServer.Serve(listener)
		defer grpcServer.Stop()

		logBuffer := &bytes.Buffer{}
		defer func() {
			if logBuffer.Len() > 0 {
				t.Log(logBuffer.String())
			}
		}()
		logger := log.NewLogfmtLogger(logBuffer)

		// Without ?auth=false, the test fails with context deadline exceeded.
		serverURL, err := url.Parse("http://" + address + "?auth=false")
		if err != nil {
			t.Fatal(err)
		}

		res, _ := manual.GenerateAndRegisterManualResolver()
		res.InitialAddrs([]resolver.Address{
			{Addr: listener.Addr().String()},
		})

		c := NewClient(&ClientConfig{
			URL:      serverURL,
			Timeout:  time.Second,
			Resolver: res,
			Logger:   logger,
		})

		err = c.Store(&monitoring.CreateTimeSeriesRequest{
			TimeSeries: []*monitoring.TimeSeries{
				&monitoring.TimeSeries{},
			},
		})
		if err != nil {
			t.Fatal(err)
		}
		requestedTarget := c.conn.Target()
		expectedTarget := c.resolver.Scheme() + ":///" + address
		if requestedTarget != expectedTarget {
			t.Errorf("ERROR: Remote address is %s, want "+expectedTarget,
				requestedTarget)
		}
	}
}
<|endoftext|>