<commit_before>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| encoding/decoder.go                                      |
|                                                          |
| LastModified: Apr 25, 2020                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package encoding

import (
	"bytes"
	"io"
	"math"
	"reflect"
)

// LongType represents the default type for decode long integer
type LongType int8

const (
	// LongTypeBigInt represents the default type is *big.Int
	LongTypeBigInt LongType = iota
	// LongTypeInt64 represents the default type is int64
	LongTypeInt64
	// LongTypeUint64 represents the default type is uint64
	LongTypeUint64
)

// RealType represents the default type for decode real number
type RealType int8

const (
	// RealTypeFloat64 represents the default type is float64
	RealTypeFloat64 RealType = iota
	// RealTypeFloat32 represents the default type is float32
	RealTypeFloat32
	// RealTypeBigFloat represents the default type is *big.Float
	RealTypeBigFloat
)

const defaultBufferSize = 8192

// Decoder is a io.Reader like object, with hprose specific read functions.
// Error is not returned as return value, but stored as Error member on this decoder instance.
type Decoder struct {
	reader io.Reader
	buf    []byte
	head   int
	tail   int
	refer  *decoderRefer
	ref    []reflect.Type
	Error  error
	LongType
	RealType
}

// NewDecoder creates an Decoder instance from byte array
func NewDecoder(input []byte) *Decoder {
	return &Decoder{
		reader: nil,
		buf:    input,
		head:   0,
		tail:   len(input),
	}
}

// NewDecoderFromReader creates an Decoder instance from io.Reader
func NewDecoderFromReader(reader io.Reader, bufSize int) *Decoder {
	if bufSize < 32 {
		bufSize = defaultBufferSize
	}
	return &Decoder{
		reader: reader,
		buf:    make([]byte, bufSize),
		head:   0,
		tail:   0,
	}
}

func (dec *Decoder) decode(p interface{}, tag byte) {
	switch p.(type) {
	case *int:
		intdec.Decode(dec, p, tag)
	case *int8:
		int8dec.Decode(dec, p, tag)
	case *int16:
		int16dec.Decode(dec, p, tag)
	case *int32:
		int32dec.Decode(dec, p, tag)
	case *int64:
		int64dec.Decode(dec, p, tag)
	case *uint:
		uintdec.Decode(dec, p, tag)
	case *uint8:
		uint8dec.Decode(dec, p, tag)
	case *uint16:
		uint16dec.Decode(dec, p, tag)
	case *uint32:
		uint32dec.Decode(dec, p, tag)
	case *uint64:
		uint64dec.Decode(dec, p, tag)
	case *float32:
		f32dec.Decode(dec, p, tag)
	case *float64:
		f64dec.Decode(dec, p, tag)
	case *string:
		strdec.Decode(dec, p, tag)
	case *interface{}, **interface{}:
		ifacedec.Decode(dec, p, tag)
	}
	if valdec := GetValueDecoder(p); valdec != nil {
		valdec.Decode(dec, p, tag)
	}
}

// Decode a data from the Decoder
func (dec *Decoder) Decode(p interface{}) {
	dec.decode(p, dec.NextByte())
}

// Reset the value reference and struct type reference
func (dec *Decoder) Reset() {
	if dec.refer != nil {
		dec.refer.Reset()
	}
	dec.ref = dec.ref[:0]
}

// Simple resets the encoder to simple mode or not
func (dec *Decoder) Simple(simple bool) {
	if simple {
		dec.refer = nil
	} else {
		dec.refer = &decoderRefer{}
	}
	dec.ref = dec.ref[:0]
}

// AddReference adds o to the reference
func (dec *Decoder) AddReference(o interface{}) {
	if dec.refer != nil {
		dec.refer.Add(o)
	}
}

// SetReference sets o to the reference at index i
func (dec *Decoder) SetReference(i int, o interface{}) {
	if dec.refer != nil {
		dec.refer.Set(i, o)
	}
}

// LastReferenceIndex returns the last index of the reference
func (dec *Decoder) LastReferenceIndex() int {
	if dec.refer != nil {
		dec.refer.Last()
	}
	return -1
}

// ResetReader reuse decoder instance by specifying another reader
func (dec *Decoder) ResetReader(reader io.Reader) *Decoder {
	dec.reader = reader
	dec.head = 0
	dec.tail = 0
	return dec
}

// ResetBytes reuse decoder instance by specifying another byte array as input
func (dec *Decoder) ResetBytes(input []byte) *Decoder {
	dec.reader = nil
	dec.buf = input
	dec.head = 0
	dec.tail = len(input)
	return dec
}

// NextByte reads and returns the next byte from the dec. If no byte is available, it returns 0.
func (dec *Decoder) NextByte() (b byte) {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return 0
	}
	b = dec.buf[dec.head]
	dec.head++
	return b
}

// Skip the next byte from the dec.
func (dec *Decoder) Skip() {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return
	}
	dec.head++
}

// Next returns a slice containing the next n bytes from the buffer,
// advancing the buffer as if the bytes had been returned by Read.
// If there are fewer than n bytes in the buffer, Next returns the entire buffer.
// The slice is only valid until the next call to a read method.
func (dec *Decoder) Next(n int) (data []byte) {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return
	}
	remain := dec.tail - dec.head
	if remain >= n {
		data = dec.buf[dec.head : dec.head+n]
		dec.head += n
		return
	}
	data = make([]byte, 0, n)
	data = append(data, dec.buf[dec.head:dec.tail]...)
	n -= remain
	for {
		if !dec.loadMore() {
			return
		}
		if dec.tail >= n {
			dec.head = n
			return append(data, dec.buf[0:n]...)
		}
		data = append(data, dec.buf[:dec.tail]...)
		n -= dec.tail
	}
}

// Remains reads and returns all bytes data in this iter that has not been read.
func (dec *Decoder) Remains() (data []byte) {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return
	}
	for {
		data = append(data, dec.buf[dec.head:dec.tail]...)
		if !dec.loadMore() {
			return data
		}
	}
}

// Until reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and not including the delimiter.
func (dec *Decoder) Until(delim byte) (data []byte) {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return
	}
	if i := bytes.IndexByte(dec.buf[dec.head:dec.tail], delim); i >= 0 {
		data = dec.buf[dec.head : dec.head+i]
		dec.head += i + 1
		return
	}
	for {
		data = append(data, dec.buf[dec.head:dec.tail]...)
		if !dec.loadMore() {
			return
		}
		if i := bytes.IndexByte(dec.buf[dec.head:dec.tail], delim); i >= 0 {
			data = append(data, dec.buf[dec.head:dec.head+i]...)
			dec.head += i + 1
			return
		}
	}
}

func (dec *Decoder) loadMore() bool {
	if dec.reader == nil {
		dec.head = dec.tail
		if dec.Error == nil {
			dec.Error = io.EOF
		}
		return false
	} else if dec.buf == nil {
		dec.buf = make([]byte, defaultBufferSize)
	}
	for {
		n, err := dec.reader.Read(dec.buf)
		dec.head = 0
		dec.tail = n
		if n > 0 {
			return true
		}
		if err != nil {
			if dec.Error == nil {
				dec.Error = err
			}
			return false
		}
	}
}

func (dec *Decoder) decodeStringError(s string, typename string) {
	if dec.Error == nil {
		dec.Error = DecodeError("hprose/encoding: can not parse '" + s + "' to " + typename)
	}
}

func (dec *Decoder) decodeError(p interface{}, tag byte) {
	if dec.Error == nil {
		var iface interface{}
		dec.decode(&iface, tag)
		if dec.Error != nil {
			return
		}
		dest := reflect.TypeOf(p).Elem()
		if v, ok := iface.(float64); ok {
			switch {
			case math.IsNaN(v):
				dec.Error = DecodeError("hprose/encoding: can not parse NaN to " + dest.String())
				return
			case math.IsInf(v, 1):
				dec.Error = DecodeError("hprose/encoding: can not parse +Inf to " + dest.String())
				return
			case math.IsInf(v, -1):
				dec.Error = DecodeError("hprose/encoding: can not parse -Inf to " + dest.String())
				return
			}
		}
		dec.Error = &CastError{
			Source:      reflect.TypeOf(iface),
			Destination: dest,
		}
	}
}
<commit_msg>Update decoder.go<commit_after>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| encoding/decoder.go                                      |
|                                                          |
| LastModified: May 1, 2020                                |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package encoding

import (
	"bytes"
	"io"
	"math"
	"reflect"
)

// LongType represents the default type for decode long integer
type LongType int8

const (
	// LongTypeBigInt represents the default type is *big.Int
	LongTypeBigInt LongType = iota
	// LongTypeInt64 represents the default type is int64
	LongTypeInt64
	// LongTypeUint64 represents the default type is uint64
	LongTypeUint64
)

// RealType represents the default type for decode real number
type RealType int8

const (
	// RealTypeFloat64 represents the default type is float64
	RealTypeFloat64 RealType = iota
	// RealTypeFloat32 represents the default type is float32
	RealTypeFloat32
	// RealTypeBigFloat represents the default type is *big.Float
	RealTypeBigFloat
)

const defaultBufferSize = 8192

// Decoder is a io.Reader like object, with hprose specific read functions.
// Error is not returned as return value, but stored as Error member on this decoder instance.
type Decoder struct {
	reader io.Reader
	buf    []byte
	head   int
	tail   int
	refer  *decoderRefer
	ref    []reflect.Type
	Error  error
	LongType
	RealType
}

// NewDecoder creates an Decoder instance from byte array
func NewDecoder(input []byte) *Decoder {
	return &Decoder{
		reader: nil,
		buf:    input,
		head:   0,
		tail:   len(input),
	}
}

// NewDecoderFromReader creates an Decoder instance from io.Reader
func NewDecoderFromReader(reader io.Reader, bufSize int) *Decoder {
	if bufSize < 32 {
		bufSize = defaultBufferSize
	}
	return &Decoder{
		reader: reader,
		buf:    make([]byte, bufSize),
		head:   0,
		tail:   0,
	}
}

func (dec *Decoder) decode(p interface{}, tag byte) {
	switch p.(type) {
	case *int, **int:
		intdec.Decode(dec, p, tag)
	case *int8, **int8:
		int8dec.Decode(dec, p, tag)
	case *int16, **int16:
		int16dec.Decode(dec, p, tag)
	case *int32, **int32:
		int32dec.Decode(dec, p, tag)
	case *int64, **int64:
		int64dec.Decode(dec, p, tag)
	case *uint, **uint:
		uintdec.Decode(dec, p, tag)
	case *uint8, **uint8:
		uint8dec.Decode(dec, p, tag)
	case *uint16, **uint16:
		uint16dec.Decode(dec, p, tag)
	case *uint32, **uint32:
		uint32dec.Decode(dec, p, tag)
	case *uint64, **uint64:
		uint64dec.Decode(dec, p, tag)
	case *float32, **float32:
		f32dec.Decode(dec, p, tag)
	case *float64, **float64:
		f64dec.Decode(dec, p, tag)
	case *string, **string:
		strdec.Decode(dec, p, tag)
	case *interface{}, **interface{}:
		ifacedec.Decode(dec, p, tag)
	}
	if valdec := GetValueDecoder(p); valdec != nil {
		valdec.Decode(dec, p, tag)
	}
}

// Decode a data from the Decoder
func (dec *Decoder) Decode(p interface{}) {
	dec.decode(p, dec.NextByte())
}

// Reset the value reference and struct type reference
func (dec *Decoder) Reset() {
	if dec.refer != nil {
		dec.refer.Reset()
	}
	dec.ref = dec.ref[:0]
}

// Simple resets the encoder to simple mode or not
func (dec *Decoder) Simple(simple bool) {
	if simple {
		dec.refer = nil
	} else {
		dec.refer = &decoderRefer{}
	}
	dec.ref = dec.ref[:0]
}

// AddReference adds o to the reference
func (dec *Decoder) AddReference(o interface{}) {
	if dec.refer != nil {
		dec.refer.Add(o)
	}
}

// SetReference sets o to the reference at index i
func (dec *Decoder) SetReference(i int, o interface{}) {
	if dec.refer != nil {
		dec.refer.Set(i, o)
	}
}

// LastReferenceIndex returns the last index of the reference
func (dec *Decoder) LastReferenceIndex() int {
	if dec.refer != nil {
		dec.refer.Last()
	}
	return -1
}

// ResetReader reuse decoder instance by specifying another reader
func (dec *Decoder) ResetReader(reader io.Reader) *Decoder {
	dec.reader = reader
	dec.head = 0
	dec.tail = 0
	return dec
}

// ResetBytes reuse decoder instance by specifying another byte array as input
func (dec *Decoder) ResetBytes(input []byte) *Decoder {
	dec.reader = nil
	dec.buf = input
	dec.head = 0
	dec.tail = len(input)
	return dec
}

// NextByte reads and returns the next byte from the dec. If no byte is available, it returns 0.
func (dec *Decoder) NextByte() (b byte) {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return 0
	}
	b = dec.buf[dec.head]
	dec.head++
	return b
}

// Skip the next byte from the dec.
func (dec *Decoder) Skip() {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return
	}
	dec.head++
}

// Next returns a slice containing the next n bytes from the buffer,
// advancing the buffer as if the bytes had been returned by Read.
// If there are fewer than n bytes in the buffer, Next returns the entire buffer.
// The slice is only valid until the next call to a read method.
func (dec *Decoder) Next(n int) (data []byte) {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return
	}
	remain := dec.tail - dec.head
	if remain >= n {
		data = dec.buf[dec.head : dec.head+n]
		dec.head += n
		return
	}
	data = make([]byte, 0, n)
	data = append(data, dec.buf[dec.head:dec.tail]...)
	n -= remain
	for {
		if !dec.loadMore() {
			return
		}
		if dec.tail >= n {
			dec.head = n
			return append(data, dec.buf[0:n]...)
		}
		data = append(data, dec.buf[:dec.tail]...)
		n -= dec.tail
	}
}

// Remains reads and returns all bytes data in this iter that has not been read.
func (dec *Decoder) Remains() (data []byte) {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return
	}
	for {
		data = append(data, dec.buf[dec.head:dec.tail]...)
		if !dec.loadMore() {
			return data
		}
	}
}

// Until reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and not including the delimiter.
func (dec *Decoder) Until(delim byte) (data []byte) {
	if (dec.head == dec.tail) && !dec.loadMore() {
		return
	}
	if i := bytes.IndexByte(dec.buf[dec.head:dec.tail], delim); i >= 0 {
		data = dec.buf[dec.head : dec.head+i]
		dec.head += i + 1
		return
	}
	for {
		data = append(data, dec.buf[dec.head:dec.tail]...)
		if !dec.loadMore() {
			return
		}
		if i := bytes.IndexByte(dec.buf[dec.head:dec.tail], delim); i >= 0 {
			data = append(data, dec.buf[dec.head:dec.head+i]...)
			dec.head += i + 1
			return
		}
	}
}

func (dec *Decoder) loadMore() bool {
	if dec.reader == nil {
		dec.head = dec.tail
		if dec.Error == nil {
			dec.Error = io.EOF
		}
		return false
	} else if dec.buf == nil {
		dec.buf = make([]byte, defaultBufferSize)
	}
	for {
		n, err := dec.reader.Read(dec.buf)
		dec.head = 0
		dec.tail = n
		if n > 0 {
			return true
		}
		if err != nil {
			if dec.Error == nil {
				dec.Error = err
			}
			return false
		}
	}
}

func (dec *Decoder) decodeStringError(s string, typename string) {
	if dec.Error == nil {
		dec.Error = DecodeError(`hprose/encoding: can not parse "` + s + `" to ` + typename)
	}
}

func (dec *Decoder) decodeError(p interface{}, tag byte) {
	if dec.Error == nil {
		var iface interface{}
		dec.decode(&iface, tag)
		if dec.Error != nil {
			return
		}
		dest := reflect.TypeOf(p).Elem()
		if v, ok := iface.(float64); ok {
			switch {
			case math.IsNaN(v):
				dec.Error = DecodeError("hprose/encoding: can not parse NaN to " + dest.String())
				return
			case math.IsInf(v, 1):
				dec.Error = DecodeError("hprose/encoding: can not parse +Inf to " + dest.String())
				return
			case math.IsInf(v, -1):
				dec.Error = DecodeError("hprose/encoding: can not parse -Inf to " + dest.String())
				return
			}
		}
		dec.Error = &CastError{
			Source:      reflect.TypeOf(iface),
			Destination: dest,
		}
	}
}
<|endoftext|>


<commit_before>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| encoding/relect.go                                       |
|                                                          |
| LastModified: Jan 23, 2021                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package encoding

import (
	"container/list"
	"math/big"
	"reflect"
	"time"
	"unsafe"

	"github.com/google/uuid"
	"github.com/modern-go/reflect2"
)

type eface struct {
	typ uintptr
	ptr unsafe.Pointer
}

func unpackEFace(ptr *interface{}) *eface {
	return (*eface)(unsafe.Pointer(ptr))
}

func unsafeString(bytes []byte) string {
	return *(*string)(unsafe.Pointer(&bytes))
}

// sliceHeader is a safe version of SliceHeader used within this package.
type sliceHeader struct {
	Data unsafe.Pointer
	Len  int
	Cap  int
}

func setSliceHeader(slicePtr unsafe.Pointer, arrayPtr unsafe.Pointer, length int) {
	sliceHeader := (*sliceHeader)(slicePtr)
	sliceHeader.Data = arrayPtr
	sliceHeader.Len = length
	sliceHeader.Cap = length
}

func unsafeToSlice(array interface{}, count int) unsafe.Pointer {
	return unsafe.Pointer(&sliceHeader{
		Data: reflect2.PtrOf(array),
		Len:  count,
		Cap:  count,
	})
}

func toSlice(array interface{}) (slice interface{}) {
	t := reflect.TypeOf(array)
	sliceType := reflect.SliceOf(t.Elem())
	sliceStruct := unpackEFace(&slice)
	sliceStruct.typ = (uintptr)(reflect2.PtrOf(sliceType))
	sliceStruct.ptr = unsafeToSlice(array, t.Len())
	return
}

var boolType = reflect.TypeOf(false)
var intType = reflect.TypeOf((int)(0))
var int8Type = reflect.TypeOf((int8)(0))
var int16Type = reflect.TypeOf((int16)(0))
var int32Type = reflect.TypeOf((int32)(0))
var int64Type = reflect.TypeOf((int64)(0))
var uintType = reflect.TypeOf((uint)(0))
var uint8Type = reflect.TypeOf((uint8)(0))
var uint16Type = reflect.TypeOf((uint16)(0))
var uint32Type = reflect.TypeOf((uint32)(0))
var uint64Type = reflect.TypeOf((uint64)(0))
var uintptrType = reflect.TypeOf((uintptr)(0))
var float32Type = reflect.TypeOf((float32)(0))
var float64Type = reflect.TypeOf((float64)(0))
var complex64Type = reflect.TypeOf((complex64)(0))
var complex128Type = reflect.TypeOf((complex128)(0))
var interfaceType = reflect.TypeOf((interface{})(nil))
var bytesType = reflect.TypeOf(([]byte)(nil))
var stringType = reflect.TypeOf("")
var timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
var uuidType = reflect.TypeOf((*uuid.UUID)(nil)).Elem()
var bigIntValueType = reflect.TypeOf((*big.Int)(nil)).Elem()
var bigFloatValueType = reflect.TypeOf((*big.Float)(nil)).Elem()
var bigRatValueType = reflect.TypeOf((*big.Rat)(nil)).Elem()

var boolPtrType = reflect.TypeOf((*bool)(nil))
var intPtrType = reflect.TypeOf((*int)(nil))
var int8PtrType = reflect.TypeOf((*int8)(nil))
var int16PtrType = reflect.TypeOf((*int16)(nil))
var int32PtrType = reflect.TypeOf((*int32)(nil))
var int64PtrType = reflect.TypeOf((*int64)(nil))
var uintPtrType = reflect.TypeOf((*uint)(nil))
var uint8PtrType = reflect.TypeOf((*uint8)(nil))
var uint16PtrType = reflect.TypeOf((*uint16)(nil))
var uint32PtrType = reflect.TypeOf((*uint32)(nil))
var uint64PtrType = reflect.TypeOf((*uint64)(nil))
var uintptrPtrType = reflect.TypeOf((*uintptr)(nil))
var float32PtrType = reflect.TypeOf((*float32)(nil))
var float64PtrType = reflect.TypeOf((*float64)(nil))
var complex64PtrType = reflect.TypeOf((*complex64)(nil))
var complex128PtrType = reflect.TypeOf((*complex128)(nil))
var interfacePtrType = reflect.TypeOf((*interface{})(nil))
var bytesPtrType = reflect.TypeOf((*[]byte)(nil))
var stringPtrType = reflect.TypeOf((*string)(nil))
var timePtrType = reflect.TypeOf((*time.Time)(nil))
var uuidPtrType = reflect.TypeOf((*uuid.UUID)(nil))
var bigIntType = reflect.TypeOf((*big.Int)(nil))
var bigFloatType = reflect.TypeOf((*big.Float)(nil))
var bigRatType = reflect.TypeOf((*big.Rat)(nil))

var listType = reflect.TypeOf((*list.List)(nil))
<commit_msg>Fixed typo<commit_after>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| encoding/reflect.go                                      |
|                                                          |
| LastModified: Jan 23, 2021                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package encoding

import (
	"container/list"
	"math/big"
	"reflect"
	"time"
	"unsafe"

	"github.com/google/uuid"
	"github.com/modern-go/reflect2"
)

type eface struct {
	typ uintptr
	ptr unsafe.Pointer
}

func unpackEFace(ptr *interface{}) *eface {
	return (*eface)(unsafe.Pointer(ptr))
}

func unsafeString(bytes []byte) string {
	return *(*string)(unsafe.Pointer(&bytes))
}

// sliceHeader is a safe version of SliceHeader used within this package.
type sliceHeader struct {
	Data unsafe.Pointer
	Len  int
	Cap  int
}

func setSliceHeader(slicePtr unsafe.Pointer, arrayPtr unsafe.Pointer, length int) {
	sliceHeader := (*sliceHeader)(slicePtr)
	sliceHeader.Data = arrayPtr
	sliceHeader.Len = length
	sliceHeader.Cap = length
}

func unsafeToSlice(array interface{}, count int) unsafe.Pointer {
	return unsafe.Pointer(&sliceHeader{
		Data: reflect2.PtrOf(array),
		Len:  count,
		Cap:  count,
	})
}

func toSlice(array interface{}) (slice interface{}) {
	t := reflect.TypeOf(array)
	sliceType := reflect.SliceOf(t.Elem())
	sliceStruct := unpackEFace(&slice)
	sliceStruct.typ = (uintptr)(reflect2.PtrOf(sliceType))
	sliceStruct.ptr = unsafeToSlice(array, t.Len())
	return
}

var boolType = reflect.TypeOf(false)
var intType = reflect.TypeOf((int)(0))
var int8Type = reflect.TypeOf((int8)(0))
var int16Type = reflect.TypeOf((int16)(0))
var int32Type = reflect.TypeOf((int32)(0))
var int64Type = reflect.TypeOf((int64)(0))
var uintType = reflect.TypeOf((uint)(0))
var uint8Type = reflect.TypeOf((uint8)(0))
var uint16Type = reflect.TypeOf((uint16)(0))
var uint32Type = reflect.TypeOf((uint32)(0))
var uint64Type = reflect.TypeOf((uint64)(0))
var uintptrType = reflect.TypeOf((uintptr)(0))
var float32Type = reflect.TypeOf((float32)(0))
var float64Type = reflect.TypeOf((float64)(0))
var complex64Type = reflect.TypeOf((complex64)(0))
var complex128Type = reflect.TypeOf((complex128)(0))
var interfaceType = reflect.TypeOf((interface{})(nil))
var bytesType = reflect.TypeOf(([]byte)(nil))
var stringType = reflect.TypeOf("")
var timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
var uuidType = reflect.TypeOf((*uuid.UUID)(nil)).Elem()
var bigIntValueType = reflect.TypeOf((*big.Int)(nil)).Elem()
var bigFloatValueType = reflect.TypeOf((*big.Float)(nil)).Elem()
var bigRatValueType = reflect.TypeOf((*big.Rat)(nil)).Elem()

var boolPtrType = reflect.TypeOf((*bool)(nil))
var intPtrType = reflect.TypeOf((*int)(nil))
var int8PtrType = reflect.TypeOf((*int8)(nil))
var int16PtrType = reflect.TypeOf((*int16)(nil))
var int32PtrType = reflect.TypeOf((*int32)(nil))
var int64PtrType = reflect.TypeOf((*int64)(nil))
var uintPtrType = reflect.TypeOf((*uint)(nil))
var uint8PtrType = reflect.TypeOf((*uint8)(nil))
var uint16PtrType = reflect.TypeOf((*uint16)(nil))
var uint32PtrType = reflect.TypeOf((*uint32)(nil))
var uint64PtrType = reflect.TypeOf((*uint64)(nil))
var uintptrPtrType = reflect.TypeOf((*uintptr)(nil))
var float32PtrType = reflect.TypeOf((*float32)(nil))
var float64PtrType = reflect.TypeOf((*float64)(nil))
var complex64PtrType = reflect.TypeOf((*complex64)(nil))
var complex128PtrType = reflect.TypeOf((*complex128)(nil))
var interfacePtrType = reflect.TypeOf((*interface{})(nil))
var bytesPtrType = reflect.TypeOf((*[]byte)(nil))
var stringPtrType = reflect.TypeOf((*string)(nil))
var timePtrType = reflect.TypeOf((*time.Time)(nil))
var uuidPtrType = reflect.TypeOf((*uuid.UUID)(nil))
var bigIntType = reflect.TypeOf((*big.Int)(nil))
var bigFloatType = reflect.TypeOf((*big.Float)(nil))
var bigRatType = reflect.TypeOf((*big.Rat)(nil))

var listType = reflect.TypeOf((*list.List)(nil))
<|endoftext|>


<commit_before>// Package circuitbreaker implements the Circuit Breaker pattern. It will wrap
// a function call (typically one which uses remote services) and monitors for
// failures and/or time outs. When a threshold of failures or time outs has been
// reached, future calls to the function will not run. During this state, the
// breaker will periodically allow the function to run and, if it is successful,
// will start running the function again.
//
// The package also provides a wrapper around an http.Client that wraps all of
// the http.Client functions with a CircuitBreaker.
//
package circuitbreaker

import (
	"errors"
	"sync/atomic"
	"time"
	"unsafe"
)

// CircuitBreaker is the circuit breaker interface. It provides two
// fields for functions, BreakerOpen and BreakerClosed that will run
// when the circuit breaker opens and closes, respectively.
type CircuitBreaker struct {
	// BreakerOpen, if set, will be called whenever the CircuitBreaker
	// moves from the closed state to the open state. It is passed the
	// CircuitBreaker object and last error that occured.
	BreakerOpen func(*CircuitBreaker, error)

	// BreakerClosed, if set, will be called whenever the CircuitBreaker
	// moves from the closed state to the open state. It is passed the
	// CircuitBreaker object.
	BreakerClosed func(*CircuitBreaker)

	Timeout      int
	Threshold    int64
	ResetTimeout time.Duration

	failures     int64
	_lastFailure unsafe.Pointer
	halfOpens    int64
}

type circuit func() error
type state int

const (
	open     state = iota
	halfopen state = iota
	closed   state = iota
)

// Error codes returned by Call
var (
	ErrBreakerOpen    = errors.New("breaker open")
	ErrBreakerTimeout = errors.New("breaker time out")
)

// NewCircuitBreaker sets up a CircuitBreaker with a failure threshold and
// no time out. With this method, the call may block forever, though it can
// handle time outs itself and return an error.
func NewCircuitBreaker(threshold int) *CircuitBreaker {
	return NewTimeoutCircuitBreaker(0, threshold)
}

// NewTimeoutCircuitBreaker sets up a CircuitBreaker with a failure threshold
// and a time out. If the function takes longer than the time out, the failure
// is recorded and can trip the circuit breaker of the threshold is passed.
func NewTimeoutCircuitBreaker(timeout, threshold int) *CircuitBreaker {
	return &CircuitBreaker{
		Timeout:      timeout,
		Threshold:    int64(threshold),
		ResetTimeout: time.Millisecond * 500}
}

// Call wraps the function the CircuitBreaker will protect. A failure is recorded
// whenever the function returns an error. If the threshold is met, the CircuitBreaker
// will trip.
func (cb *CircuitBreaker) Call(circuit circuit) error {
	state := cb.state()

	if state == open {
		return ErrBreakerOpen
	}

	var err error
	if cb.Timeout > 0 {
		err = cb.callWithTimeout(circuit)
	} else {
		err = circuit()
	}

	if err != nil {
		if state == halfopen {
			atomic.StoreInt64(&cb.halfOpens, 0)
		}
		atomic.AddInt64(&cb.failures, 1)
		now := time.Now()
		atomic.StorePointer(&cb._lastFailure, unsafe.Pointer(&now))

		if cb.BreakerOpen != nil && cb.failures == cb.Threshold {
			cb.BreakerOpen(cb, err)
		}
		return err
	}

	if cb.BreakerClosed != nil && cb.failures > 0 {
		cb.BreakerClosed(cb)
	}

	atomic.StoreInt64(&cb.failures, 0)
	atomic.StoreInt64(&cb.halfOpens, 0)
	return nil
}

func (cb *CircuitBreaker) callWithTimeout(circuit circuit) error {
	c := make(chan int, 1)
	var err error
	go func() {
		err = circuit()
		close(c)
	}()
	select {
	case <-c:
		return err
	case <-time.After(time.Second * time.Duration(cb.Timeout)):
		return ErrBreakerTimeout
	}
}

func (cb *CircuitBreaker) state() state {
	if cb.failures >= cb.Threshold {
		since := time.Since(cb.lastFailure())
		if since > cb.ResetTimeout {
			if cb.halfOpens == 0 {
				atomic.AddInt64(&cb.halfOpens, 1)
				return halfopen
			}
			return open
		}
		return open
	}
	return closed
}

func (cb *CircuitBreaker) lastFailure() time.Time {
	return *(*time.Time)(cb._lastFailure)
}
<commit_msg>Fix race conditions relating to failure count<commit_after>// Package circuitbreaker implements the Circuit Breaker pattern. It will wrap
// a function call (typically one which uses remote services) and monitors for
// failures and/or time outs. When a threshold of failures or time outs has been
// reached, future calls to the function will not run. During this state, the
// breaker will periodically allow the function to run and, if it is successful,
// will start running the function again.
//
// The package also provides a wrapper around an http.Client that wraps all of
// the http.Client functions with a CircuitBreaker.
//
package circuitbreaker

import (
	"errors"
	"sync/atomic"
	"time"
	"unsafe"
)

// CircuitBreaker is the circuit breaker interface. It provides two
// fields for functions, BreakerOpen and BreakerClosed that will run
// when the circuit breaker opens and closes, respectively.
type CircuitBreaker struct {
	// BreakerOpen, if set, will be called whenever the CircuitBreaker
	// moves from the closed state to the open state. It is passed the
	// CircuitBreaker object and last error that occured.
	BreakerOpen func(*CircuitBreaker, error)

	// BreakerClosed, if set, will be called whenever the CircuitBreaker
	// moves from the closed state to the open state. It is passed the
	// CircuitBreaker object.
	BreakerClosed func(*CircuitBreaker)

	Timeout      int
	Threshold    int64
	ResetTimeout time.Duration

	failures     int64
	_lastFailure unsafe.Pointer
	halfOpens    int64
}

type circuit func() error
type state int

const (
	open     state = iota
	halfopen state = iota
	closed   state = iota
)

// Error codes returned by Call
var (
	ErrBreakerOpen    = errors.New("breaker open")
	ErrBreakerTimeout = errors.New("breaker time out")
)

// NewCircuitBreaker sets up a CircuitBreaker with a failure threshold and
// no time out. With this method, the call may block forever, though it can
// handle time outs itself and return an error.
func NewCircuitBreaker(threshold int) *CircuitBreaker {
	return NewTimeoutCircuitBreaker(0, threshold)
}

// NewTimeoutCircuitBreaker sets up a CircuitBreaker with a failure threshold
// and a time out. If the function takes longer than the time out, the failure
// is recorded and can trip the circuit breaker of the threshold is passed.
func NewTimeoutCircuitBreaker(timeout, threshold int) *CircuitBreaker {
	return &CircuitBreaker{
		Timeout:      timeout,
		Threshold:    int64(threshold),
		ResetTimeout: time.Millisecond * 500}
}

// Call wraps the function the CircuitBreaker will protect. A failure is recorded
// whenever the function returns an error. If the threshold is met, the CircuitBreaker
// will trip.
func (cb *CircuitBreaker) Call(circuit circuit) error {
	state := cb.state()

	if state == open {
		return ErrBreakerOpen
	}

	var err error
	if cb.Timeout > 0 {
		err = cb.callWithTimeout(circuit)
	} else {
		err = circuit()
	}

	if err != nil {
		if state == halfopen {
			atomic.StoreInt64(&cb.halfOpens, 0)
		}

		// increment failure count, returning the new count
		failures := atomic.AddInt64(&cb.failures, 1)

		now := time.Now()
		atomic.StorePointer(&cb._lastFailure, unsafe.Pointer(&now))

		// compare the local failure count against the threshold
		if cb.BreakerOpen != nil && failures == cb.Threshold {
			cb.BreakerOpen(cb, err)
		}
		return err
	}

	// zero the failure count, returning the old value
	failures := atomic.SwapInt64(&cb.failures, 0)
	atomic.StoreInt64(&cb.halfOpens, 0)

	if cb.BreakerClosed != nil && failures > 0 {
		cb.BreakerClosed(cb)
	}

	return nil
}

func (cb *CircuitBreaker) callWithTimeout(circuit circuit) error {
	c := make(chan int, 1)
	var err error
	go func() {
		err = circuit()
		close(c)
	}()
	select {
	case <-c:
		return err
	case <-time.After(time.Second * time.Duration(cb.Timeout)):
		return ErrBreakerTimeout
	}
}

func (cb *CircuitBreaker) state() state {
	if cb.failures >= cb.Threshold {
		since := time.Since(cb.lastFailure())
		if since > cb.ResetTimeout {
			if cb.halfOpens == 0 {
				atomic.AddInt64(&cb.halfOpens, 1)
				return halfopen
			}
			return open
		}
		return open
	}
	return closed
}

func (cb *CircuitBreaker) lastFailure() time.Time {
	return *(*time.Time)(cb._lastFailure)
}
<|endoftext|>


<commit_before>// +build !jenkins

package roles

import (
	"os"
	"testing"

	"github.com/Financial-Times/base-ft-rw-app-go/baseftrwapp"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	"github.com/jmcvetta/neoism"
	"github.com/stretchr/testify/assert"
)

var rolesDriver baseftrwapp.Service

func TestDelete(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"

	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToDelete := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToDelete), "Failed to write role")

	found, err := rolesDriver.Delete(uuid)
	assert.True(found, "Didn't manage to delete role for uuid %", uuid)
	assert.NoError(err, "Error deleting role for uuid %s", uuid)

	p, found, err := rolesDriver.Read(uuid)

	assert.Equal(role{}, p, "Found role %s who should have been deleted", p)
	assert.False(found, "Found role for uuid %s who should have been deleted", uuid)
	assert.NoError(err, "Error trying to find role for uuid %s", uuid)
}

func TestCreateAllValuesPresent(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)
}

func TestCreateNoFacsetIdentifierPresent(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)
}

func TestCreateHandlesSpecialCharacters(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)
}

func TestCreateNotAllValuesPresent(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)
}

func TestCreateAddsBoardRoleLabelForBoardRole(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}
	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)

}

func readRoleForUUIDAndCheckFieldsMatch(t *testing.T, uuid string, expectedRole role) {
	assert := assert.New(t)
	storedRole, found, err := rolesDriver.Read(uuid)

	assert.NoError(err, "Error finding role for uuid %s", uuid)
	assert.True(found, "Didn't find role for uuid %s", uuid)
	assert.Equal(expectedRole, storedRole, "roles should be the same")
}

func getRolesCypherDriver(t *testing.T) CypherDriver {
	assert := assert.New(t)
	url := os.Getenv("NEO4J_TEST_URL")
	if url == "" {
		url = "http://localhost:7474/db/data"
	}

	db, err := neoism.Connect(url)
	assert.NoError(err, "Failed to connect to Neo4j")
	return NewCypherDriver(neoutils.StringerDb{db}, db)
}

func cleanUp(t *testing.T, uuid string) {
	assert := assert.New(t)
	found, err := rolesDriver.Delete(uuid)
	assert.True(found, "Didn't manage to delete role for uuid %", uuid)
	assert.NoError(err, "Error deleting role for uuid %s", uuid)
}
<commit_msg>Fix typo<commit_after>// +build !jenkins

package roles

import (
	"os"
	"testing"

	"github.com/Financial-Times/base-ft-rw-app-go/baseftrwapp"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	"github.com/jmcvetta/neoism"
	"github.com/stretchr/testify/assert"
)

var rolesDriver baseftrwapp.Service

func TestDelete(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"

	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToDelete := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToDelete), "Failed to write role")

	found, err := rolesDriver.Delete(uuid)
	assert.True(found, "Didn't manage to delete role for uuid %", uuid)
	assert.NoError(err, "Error deleting role for uuid %s", uuid)

	p, found, err := rolesDriver.Read(uuid)

	assert.Equal(role{}, p, "Found role %s who should have been deleted", p)
	assert.False(found, "Found role for uuid %s who should have been deleted", uuid)
	assert.NoError(err, "Error trying to find role for uuid %s", uuid)
}

func TestCreateAllValuesPresent(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)
}

func TestCreateNoFactsetIdentifierPresent(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)
}

func TestCreateHandlesSpecialCharacters(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)
}

func TestCreateNotAllValuesPresent(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}

	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)
}

func TestCreateAddsBoardRoleLabelForBoardRole(t *testing.T) {
	assert := assert.New(t)
	uuid := "12345"
	rolesDriver = getRolesCypherDriver(t)

	altId := alternativeIdentifiers{UUIDS: []string{"UUID"}, FactsetIdentifier: "FACTSETID"}
	roleToWrite := role{UUID: uuid, PrefLabel: "TestRole", AlternativeIdentifiers: altId}
	assert.NoError(rolesDriver.Write(roleToWrite), "Failed to write role")

	readRoleForUUIDAndCheckFieldsMatch(t, uuid, roleToWrite)

	cleanUp(t, uuid)

}

func readRoleForUUIDAndCheckFieldsMatch(t *testing.T, uuid string, expectedRole role) {
	assert := assert.New(t)
	storedRole, found, err := rolesDriver.Read(uuid)

	assert.NoError(err, "Error finding role for uuid %s", uuid)
	assert.True(found, "Didn't find role for uuid %s", uuid)
	assert.Equal(expectedRole, storedRole, "roles should be the same")
}

func getRolesCypherDriver(t *testing.T) CypherDriver {
	assert := assert.New(t)
	url := os.Getenv("NEO4J_TEST_URL")
	if url == "" {
		url = "http://localhost:7474/db/data"
	}

	db, err := neoism.Connect(url)
	assert.NoError(err, "Failed to connect to Neo4j")
	return NewCypherDriver(neoutils.StringerDb{db}, db)
}

func cleanUp(t *testing.T, uuid string) {
	assert := assert.New(t)
	found, err := rolesDriver.Delete(uuid)
	assert.True(found, "Didn't manage to delete role for uuid %", uuid)
	assert.NoError(err, "Error deleting role for uuid %s", uuid)
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru 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 routertest

import (
	"errors"
	"fmt"
	"net/url"
	"sync"

	"github.com/tsuru/tsuru/router"
)

var FakeRouter = newFakeRouter()

var HCRouter = hcRouter{fakeRouter: newFakeRouter()}

var ErrForcedFailure = errors.New("Forced failure")

func init() {
	router.Register("fake", createRouter)
	router.Register("fake-hc", createHCRouter)
}

func createRouter(prefix string) (router.Router, error) {
	return &FakeRouter, nil
}

func createHCRouter(prefix string) (router.Router, error) {
	return &HCRouter, nil
}

func newFakeRouter() fakeRouter {
	return fakeRouter{cnames: make(map[string]string), backends: make(map[string][]string), failuresByIp: make(map[string]bool), mutex: &sync.Mutex{}}
}

type fakeRouter struct {
	backends     map[string][]string
	cnames       map[string]string
	failuresByIp map[string]bool
	mutex        *sync.Mutex
}

func (r *fakeRouter) FailForIp(ip string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.failuresByIp[ip] = true
}

func (r *fakeRouter) HasBackend(name string) bool {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	_, ok := r.backends[name]
	return ok
}

func (r *fakeRouter) HasCName(name string) bool {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	_, ok := r.cnames[name]
	return ok
}

func (r *fakeRouter) HasRoute(name, address string) bool {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	routes, ok := r.backends[name]
	if !ok {
		routes, ok = r.backends[r.cnames[name]]
		if !ok {
			return false
		}
	}
	for _, route := range routes {
		if route == address {
			return true
		}
	}
	return false
}

func (r *fakeRouter) AddBackend(name string) error {
	if r.HasBackend(name) {
		return router.ErrBackendExists
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.backends[name] = nil
	return router.Store(name, name, "fake")
}

func (r *fakeRouter) RemoveBackend(name string) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return router.ErrBackendNotFound
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	delete(r.backends, backendName)
	return router.Remove(backendName)
}

func (r *fakeRouter) AddRoute(name string, address *url.URL) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return router.ErrBackendNotFound
	}
	if r.HasRoute(backendName, address.String()) {
		return router.ErrRouteExists
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if r.failuresByIp[address.String()] {
		return ErrForcedFailure
	}
	routes := r.backends[backendName]
	routes = append(routes, address.String())
	r.backends[backendName] = routes
	return nil
}

func (r *fakeRouter) RemoveRoute(name string, address *url.URL) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return router.ErrBackendNotFound
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if r.failuresByIp[address.String()] {
		return ErrForcedFailure
	}
	index := -1
	routes := r.backends[backendName]
	for i := range routes {
		if routes[i] == address.String() {
			index = i
			break
		}
	}
	if index < 0 {
		return router.ErrRouteNotFound
	}
	routes[index] = routes[len(routes)-1]
	r.backends[backendName] = routes[:len(routes)-1]
	return nil
}

func (r *fakeRouter) SetCName(cname, name string) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return nil
	}
	if !router.ValidCName(cname, "fakerouter.com") {
		return router.ErrCNameNotAllowed
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if _, ok := r.cnames[cname]; ok {
		return router.ErrCNameExists
	}
	r.cnames[cname] = backendName
	return nil
}

func (r *fakeRouter) UnsetCName(cname, name string) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return nil
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if _, ok := r.cnames[cname]; !ok {
		return router.ErrCNameNotFound
	}
	delete(r.cnames, cname)
	return nil
}

func (r *fakeRouter) Addr(name string) (string, error) {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return "", err
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if _, ok := r.backends[backendName]; ok {
		return fmt.Sprintf("%s.fakerouter.com", backendName), nil
	}
	return "", router.ErrBackendNotFound
}

func (r *fakeRouter) Reset() {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.backends = make(map[string][]string)
	r.failuresByIp = make(map[string]bool)
}

func (r *fakeRouter) Routes(name string) ([]*url.URL, error) {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return nil, err
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	routes := r.backends[backendName]
	result := make([]*url.URL, len(routes))
	for i, route := range routes {
		result[i], err = url.Parse(route)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

func (r *fakeRouter) Swap(backend1, backend2 string) error {
	return router.Swap(r, backend1, backend2)
}

type hcRouter struct {
	fakeRouter
	err error
}

func (r *hcRouter) SetErr(err error) {
	r.err = err
}

func (r *hcRouter) HealthCheck() error {
	return r.err
}
<commit_msg>router/routertest: clear cnames on reset<commit_after>// Copyright 2015 tsuru 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 routertest

import (
	"errors"
	"fmt"
	"net/url"
	"sync"

	"github.com/tsuru/tsuru/router"
)

var FakeRouter = newFakeRouter()

var HCRouter = hcRouter{fakeRouter: newFakeRouter()}

var ErrForcedFailure = errors.New("Forced failure")

func init() {
	router.Register("fake", createRouter)
	router.Register("fake-hc", createHCRouter)
}

func createRouter(prefix string) (router.Router, error) {
	return &FakeRouter, nil
}

func createHCRouter(prefix string) (router.Router, error) {
	return &HCRouter, nil
}

func newFakeRouter() fakeRouter {
	return fakeRouter{cnames: make(map[string]string), backends: make(map[string][]string), failuresByIp: make(map[string]bool), mutex: &sync.Mutex{}}
}

type fakeRouter struct {
	backends     map[string][]string
	cnames       map[string]string
	failuresByIp map[string]bool
	mutex        *sync.Mutex
}

func (r *fakeRouter) FailForIp(ip string) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.failuresByIp[ip] = true
}

func (r *fakeRouter) HasBackend(name string) bool {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	_, ok := r.backends[name]
	return ok
}

func (r *fakeRouter) HasCName(name string) bool {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	_, ok := r.cnames[name]
	return ok
}

func (r *fakeRouter) HasRoute(name, address string) bool {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	routes, ok := r.backends[name]
	if !ok {
		routes, ok = r.backends[r.cnames[name]]
		if !ok {
			return false
		}
	}
	for _, route := range routes {
		if route == address {
			return true
		}
	}
	return false
}

func (r *fakeRouter) AddBackend(name string) error {
	if r.HasBackend(name) {
		return router.ErrBackendExists
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.backends[name] = nil
	return router.Store(name, name, "fake")
}

func (r *fakeRouter) RemoveBackend(name string) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return router.ErrBackendNotFound
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	delete(r.backends, backendName)
	return router.Remove(backendName)
}

func (r *fakeRouter) AddRoute(name string, address *url.URL) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return router.ErrBackendNotFound
	}
	if r.HasRoute(backendName, address.String()) {
		return router.ErrRouteExists
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if r.failuresByIp[address.String()] {
		return ErrForcedFailure
	}
	routes := r.backends[backendName]
	routes = append(routes, address.String())
	r.backends[backendName] = routes
	return nil
}

func (r *fakeRouter) RemoveRoute(name string, address *url.URL) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return router.ErrBackendNotFound
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if r.failuresByIp[address.String()] {
		return ErrForcedFailure
	}
	index := -1
	routes := r.backends[backendName]
	for i := range routes {
		if routes[i] == address.String() {
			index = i
			break
		}
	}
	if index < 0 {
		return router.ErrRouteNotFound
	}
	routes[index] = routes[len(routes)-1]
	r.backends[backendName] = routes[:len(routes)-1]
	return nil
}

func (r *fakeRouter) SetCName(cname, name string) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return nil
	}
	if !router.ValidCName(cname, "fakerouter.com") {
		return router.ErrCNameNotAllowed
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if _, ok := r.cnames[cname]; ok {
		return router.ErrCNameExists
	}
	r.cnames[cname] = backendName
	return nil
}

func (r *fakeRouter) UnsetCName(cname, name string) error {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return err
	}
	if !r.HasBackend(backendName) {
		return nil
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if _, ok := r.cnames[cname]; !ok {
		return router.ErrCNameNotFound
	}
	delete(r.cnames, cname)
	return nil
}

func (r *fakeRouter) Addr(name string) (string, error) {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return "", err
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if _, ok := r.backends[backendName]; ok {
		return fmt.Sprintf("%s.fakerouter.com", backendName), nil
	}
	return "", router.ErrBackendNotFound
}

func (r *fakeRouter) Reset() {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	r.backends = make(map[string][]string)
	r.failuresByIp = make(map[string]bool)
	r.cnames = make(map[string]string)
}

func (r *fakeRouter) Routes(name string) ([]*url.URL, error) {
	backendName, err := router.Retrieve(name)
	if err != nil {
		return nil, err
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	routes := r.backends[backendName]
	result := make([]*url.URL, len(routes))
	for i, route := range routes {
		result[i], err = url.Parse(route)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

func (r *fakeRouter) Swap(backend1, backend2 string) error {
	return router.Swap(r, backend1, backend2)
}

type hcRouter struct {
	fakeRouter
	err error
}

func (r *hcRouter) SetErr(err error) {
	r.err = err
}

func (r *hcRouter) HealthCheck() error {
	return r.err
}
<|endoftext|>


<commit_before>package hooky

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

	"github.com/sebest/hooky/models"
	"github.com/sebest/hooky/restapi"
	"gopkg.in/yaml.v2"
)

type Retry struct {
	// Attempts is the current number of attempts we did.
	Attempts *int `yaml:"attempts,omitempty" json:"attempts,omitempty"`
	// MaxAttempts is the maximum number of attempts we will try.
	MaxAttempts *int `yaml:"max_attempts,omitempty" json:"maxAttempts,omitempty"`
	// Factor is factor to increase the duration between each attempts.
	Factor *float64 `yaml:"factor,omitempty" json:"factor,omitempty"`
	// Min is the minimum duration between each attempts in seconds.
	Min *int `yaml:"min,omitempty" json:"min,omitempty"`
	// Max is the maximum duration between each attempts in seconds.
	Max *int `yaml:"max,omitempty" json:"max,omitempty"`
}

type HTTPAuth struct {
	Username *string `yaml:"username,omitempty" json:"username,omitempty"`
	Password *string `yaml:"password,omitempty" json:"password,omitempty"`
}

type Account struct {
	// ID is the ID of the Account creating the Tasks.
	ID string `yaml:"id"`

	// Key is the key of the Account creating the Tasks.
	Key string `yaml:"key"`
}

type TaskDefaults struct {
	// Queue is the name of the default Queue.
	Queue string `yaml:"queue"`

	// HTTPAuth is the HTTP authentication to use if any.
	HTTPAuth *HTTPAuth `yaml:"auth,omitempty"`

	// Retry
	Retry *Retry `yaml:"retry,omitempty"`

	// Active is the task active.
	Active *bool `yaml:"active,omitempty"`
}

type Task struct {
	// Queue is the name of the parent Queue.
	Queue *string `yaml:"queue,omitempty" json:"queue,omitempty"`

	// Name is the task's name.
	Name string `yaml:"name" json:"-"`

	// URL is the URL that the worker with requests.
	URL string `yaml:"url" json:"url"`

	// HTTPAuth is the HTTP authentication to use if any.
	HTTPAuth *HTTPAuth `yaml:"auth,omitempty" json:"auth,omitempty"`

	// Method is the HTTP method that will be used to execute the request.
	Method *string `yaml:"method,omitempty" json:"method,omitempty"`

	// Headers are the HTTP headers that will be used schedule executing the request.
	Headers *map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`

	// Payload is arbitrary data that will be POSTed on the URL.
	Payload *string `yaml:"payload,omitempty" json:"payload,omitempty"`

	// Schedule is a cron specification describing the recurrency.
	Schedule string `yaml:"schedule" json:"schedule"`

	// Active is the task active.
	Active *bool `yaml:"active,omitempty" json:"active,omitempty"`

	// Retry is the retry strategy parameters in case of errors.
	Retry *Retry `yaml:"retry,omitempty" json:"retry,omitempty"`
}

type Crontab struct {
	Account      Account       `yaml:"account"`
	TaskDefaults *TaskDefaults `yaml:"tasks_defaults,omitempty"`
	Tasks        []*Task       `yaml:"tasks"`
	Application  string        `yaml:"application"`
}

func NewCrontabFromFile(path string) (*Crontab, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	crontab := &Crontab{}

	err = yaml.Unmarshal(content, crontab)
	if err != nil {
		return nil, err
	}
	return crontab, nil
}

func newReq(method string, url string, username string, password string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("User-Agent", "Hooky")
	req.Header.Add("Content-Type", "application/json")
	if username != "" || password != "" {
		req.SetBasicAuth(username, password)
	}
	return req, nil
}

func SyncCrontab(baseURL string, crontab *Crontab) error {
	// Application
	app := crontab.Application
	if app == "" {
		app = "default"
	}

	client := &http.Client{}

	currentTasks := make(map[string]bool, 0)

	// Set all the tasks from the crontab
	for _, task := range crontab.Tasks {
		currentTasks[task.Name] = true
		payload, err := json.Marshal(task)
		if err != nil {
			return err
		}

		url := fmt.Sprintf("%s/accounts/%s/applications/%s/tasks/%s", baseURL, crontab.Account.ID, app, task.Name)
		req, err := newReq("PUT", url, crontab.Account.ID, crontab.Account.Key, bytes.NewBuffer(payload))
		if err != nil {
			return err
		}
		resp, err := client.Do(req)
		if err != nil {
			return err
		}
		defer resp.Body.Close()
	}

	// Find tasks that have a schedule
	url := fmt.Sprintf("%s/accounts/%s/applications/%s/tasks?filters=schedule:true", baseURL, crontab.Account.ID, app)
	req, err := newReq("GET", url, crontab.Account.ID, crontab.Account.Key, nil)
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	payload, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var tasks []*restapi.Task
	lr := &models.ListResult{
		List: &tasks,
	}
	if err := json.Unmarshal(payload, lr); err != nil {
		fmt.Println(err)
		return err
	}

	// Delete tasks that should not be there
	for _, task := range tasks {
		if _, ok := currentTasks[task.Name]; ok == false {
			url := fmt.Sprintf("%s/accounts/%s/applications/%s/tasks/%s", baseURL, crontab.Account.ID, app, task.Name)
			req, err := newReq("DELETE", url, crontab.Account.ID, crontab.Account.Key, nil)
			resp, err := client.Do(req)
			if err != nil {
				return err
			}
			defer resp.Body.Close()
		}
	}

	return nil
}
<commit_msg>Crontab: display errors when they occur<commit_after>package hooky

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

	"github.com/sebest/hooky/models"
	"github.com/sebest/hooky/restapi"
	"gopkg.in/yaml.v2"
)

type Retry struct {
	// Attempts is the current number of attempts we did.
	Attempts *int `yaml:"attempts,omitempty" json:"attempts,omitempty"`
	// MaxAttempts is the maximum number of attempts we will try.
	MaxAttempts *int `yaml:"max_attempts,omitempty" json:"maxAttempts,omitempty"`
	// Factor is factor to increase the duration between each attempts.
	Factor *float64 `yaml:"factor,omitempty" json:"factor,omitempty"`
	// Min is the minimum duration between each attempts in seconds.
	Min *int `yaml:"min,omitempty" json:"min,omitempty"`
	// Max is the maximum duration between each attempts in seconds.
	Max *int `yaml:"max,omitempty" json:"max,omitempty"`
}

type HTTPAuth struct {
	Username *string `yaml:"username,omitempty" json:"username,omitempty"`
	Password *string `yaml:"password,omitempty" json:"password,omitempty"`
}

type Account struct {
	// ID is the ID of the Account creating the Tasks.
	ID string `yaml:"id"`

	// Key is the key of the Account creating the Tasks.
	Key string `yaml:"key"`
}

type TaskDefaults struct {
	// Queue is the name of the default Queue.
	Queue string `yaml:"queue"`

	// HTTPAuth is the HTTP authentication to use if any.
	HTTPAuth *HTTPAuth `yaml:"auth,omitempty"`

	// Retry
	Retry *Retry `yaml:"retry,omitempty"`

	// Active is the task active.
	Active *bool `yaml:"active,omitempty"`
}

type Task struct {
	// Queue is the name of the parent Queue.
	Queue *string `yaml:"queue,omitempty" json:"queue,omitempty"`

	// Name is the task's name.
	Name string `yaml:"name" json:"-"`

	// URL is the URL that the worker with requests.
	URL string `yaml:"url" json:"url"`

	// HTTPAuth is the HTTP authentication to use if any.
	HTTPAuth *HTTPAuth `yaml:"auth,omitempty" json:"auth,omitempty"`

	// Method is the HTTP method that will be used to execute the request.
	Method *string `yaml:"method,omitempty" json:"method,omitempty"`

	// Headers are the HTTP headers that will be used schedule executing the request.
	Headers *map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`

	// Payload is arbitrary data that will be POSTed on the URL.
	Payload *string `yaml:"payload,omitempty" json:"payload,omitempty"`

	// Schedule is a cron specification describing the recurrency.
	Schedule string `yaml:"schedule" json:"schedule"`

	// Active is the task active.
	Active *bool `yaml:"active,omitempty" json:"active,omitempty"`

	// Retry is the retry strategy parameters in case of errors.
	Retry *Retry `yaml:"retry,omitempty" json:"retry,omitempty"`
}

type Crontab struct {
	Account      Account       `yaml:"account"`
	TaskDefaults *TaskDefaults `yaml:"tasks_defaults,omitempty"`
	Tasks        []*Task       `yaml:"tasks"`
	Application  string        `yaml:"application"`
}

func NewCrontabFromFile(path string) (*Crontab, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	crontab := &Crontab{}

	err = yaml.Unmarshal(content, crontab)
	if err != nil {
		return nil, err
	}
	return crontab, nil
}

func newReq(method string, url string, username string, password string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("User-Agent", "Hooky")
	req.Header.Add("Content-Type", "application/json")
	if username != "" || password != "" {
		req.SetBasicAuth(username, password)
	}
	return req, nil
}

func SyncCrontab(baseURL string, crontab *Crontab) error {
	// Application
	app := crontab.Application
	if app == "" {
		app = "default"
	}

	client := &http.Client{}

	currentTasks := make(map[string]bool, 0)

	// Set all the tasks from the crontab
	for _, task := range crontab.Tasks {
		currentTasks[task.Name] = true
		payload, err := json.Marshal(task)
		if err != nil {
			return err
		}

		url := fmt.Sprintf("%s/accounts/%s/applications/%s/tasks/%s", baseURL, crontab.Account.ID, app, task.Name)
		req, err := newReq("PUT", url, crontab.Account.ID, crontab.Account.Key, bytes.NewBuffer(payload))
		if err != nil {
			return err
		}
		resp, err := client.Do(req)
		if err != nil {
			return err
		}
		defer resp.Body.Close()
		respBody, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		if resp.StatusCode != 200 {
			fmt.Println(string(respBody))
		}
	}

	// Find tasks that have a schedule
	url := fmt.Sprintf("%s/accounts/%s/applications/%s/tasks?filters=schedule:true", baseURL, crontab.Account.ID, app)
	req, err := newReq("GET", url, crontab.Account.ID, crontab.Account.Key, nil)
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	payload, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var tasks []*restapi.Task
	lr := &models.ListResult{
		List: &tasks,
	}
	if err := json.Unmarshal(payload, lr); err != nil {
		fmt.Println(err)
		return err
	}

	// Delete tasks that should not be there
	for _, task := range tasks {
		if _, ok := currentTasks[task.Name]; ok == false {
			url := fmt.Sprintf("%s/accounts/%s/applications/%s/tasks/%s", baseURL, crontab.Account.ID, app, task.Name)
			req, err := newReq("DELETE", url, crontab.Account.ID, crontab.Account.Key, nil)
			resp, err := client.Do(req)
			if err != nil {
				return err
			}
			defer resp.Body.Close()
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 go-swagger maintainers
//
// 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 client

import (
	"fmt"
	"mime"
	"net/http"
	"net/http/httputil"
	"os"
	"path"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"

	"github.com/go-openapi/runtime"
	"github.com/go-openapi/strfmt"
)

// DefaultTimeout the default request timeout
var DefaultTimeout = 30 * time.Second

// Runtime represents an API client that uses the transport
// to make http requests based on a swagger specification.
type Runtime struct {
	DefaultMediaType      string
	DefaultAuthentication runtime.ClientAuthInfoWriter
	Consumers             map[string]runtime.Consumer
	Producers             map[string]runtime.Producer

	Transport http.RoundTripper
	Jar       http.CookieJar
	//Spec      *spec.Document
	Host     string
	BasePath string
	Formats  strfmt.Registry
	Debug    bool
	Context  context.Context

	clientOnce *sync.Once
	client     *http.Client
	schemes    []string
	do         func(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error)
}

// New creates a new default runtime for a swagger api runtime.Client
func New(host, basePath string, schemes []string) *Runtime {
	var rt Runtime
	rt.DefaultMediaType = runtime.JSONMime

	// TODO: actually infer this stuff from the spec
	rt.Consumers = map[string]runtime.Consumer{
		runtime.JSONMime:    runtime.JSONConsumer(),
		runtime.XMLMime:     runtime.XMLConsumer(),
		runtime.TextMime:    runtime.TextConsumer(),
		runtime.DefaultMime: runtime.ByteStreamConsumer(),
	}
	rt.Producers = map[string]runtime.Producer{
		runtime.JSONMime:    runtime.JSONProducer(),
		runtime.XMLMime:     runtime.XMLProducer(),
		runtime.TextMime:    runtime.TextProducer(),
		runtime.DefaultMime: runtime.ByteStreamProducer(),
	}
	rt.Transport = http.DefaultTransport
	rt.Jar = nil
	rt.Host = host
	rt.BasePath = basePath
	rt.Context = context.Background()
	rt.clientOnce = new(sync.Once)
	if !strings.HasPrefix(rt.BasePath, "/") {
		rt.BasePath = "/" + rt.BasePath
	}
	rt.Debug = os.Getenv("DEBUG") == "1"
	if len(schemes) > 0 {
		rt.schemes = schemes
	}
	rt.do = ctxhttp.Do
	return &rt
}

// NewWithClient allows you to create a new transport with a configured http.Client
func NewWithClient(host, basePath string, schemes []string, client *http.Client) *Runtime {
	rt := New(host, basePath, schemes)
	if client != nil {
		rt.clientOnce.Do(func() {
			rt.client = client
		})
	}
	return rt
}

func (r *Runtime) pickScheme(schemes []string) string {
	if v := r.selectScheme(r.schemes); v != "" {
		return v
	}
	if v := r.selectScheme(schemes); v != "" {
		return v
	}
	return "http"
}

func (r *Runtime) selectScheme(schemes []string) string {
	schLen := len(schemes)
	if schLen == 0 {
		return ""
	}

	scheme := schemes[0]
	// prefer https, but skip when not possible
	if scheme != "https" && schLen > 1 {
		for _, sch := range schemes {
			if sch == "https" {
				scheme = sch
				break
			}
		}
	}
	return scheme
}

// Submit a request and when there is a body on success it will turn that into the result
// all other things are turned into an api error for swagger which retains the status code
func (r *Runtime) Submit(operation *runtime.ClientOperation) (interface{}, error) {
	params, readResponse, auth := operation.Params, operation.Reader, operation.AuthInfo

	request, err := newRequest(operation.Method, operation.PathPattern, params)
	if err != nil {
		return nil, err
	}

	var accept []string
	for _, mimeType := range operation.ProducesMediaTypes {
		accept = append(accept, mimeType)
	}
	request.SetHeaderParam(runtime.HeaderAccept, accept...)

	if auth == nil && r.DefaultAuthentication != nil {
		auth = r.DefaultAuthentication
	}
	if auth != nil {
		if err := auth.AuthenticateRequest(request, r.Formats); err != nil {
			return nil, err
		}
	}

	// TODO: pick appropriate media type
	cmt := r.DefaultMediaType
	if len(operation.ConsumesMediaTypes) > 0 {
		cmt = operation.ConsumesMediaTypes[0]
	}

	req, err := request.BuildHTTP(cmt, r.Producers, r.Formats)
	if err != nil {
		return nil, err
	}
	req.URL.Scheme = r.pickScheme(operation.Schemes)
	req.URL.Host = r.Host
	var reinstateSlash bool
	if req.URL.Path != "" && req.URL.Path != "/" && req.URL.Path[len(req.URL.Path)-1] == '/' {
		reinstateSlash = true
	}
	req.URL.Path = path.Join(r.BasePath, req.URL.Path)
	if reinstateSlash {
		req.URL.Path = req.URL.Path + "/"
	}

	r.clientOnce.Do(func() {
		r.client = &http.Client{
			Transport: r.Transport,
			Jar:       r.Jar,
		}
	})

	if r.Debug {
		b, err2 := httputil.DumpRequestOut(req, true)
		if err2 != nil {
			return nil, err2
		}
		fmt.Fprintln(os.Stderr, string(b))
	}

	var hasTimeout bool
	pctx := operation.Context
	if pctx == nil {
		pctx = r.Context
	} else {
		hasTimeout = true
	}
	if pctx == nil {
		pctx = context.Background()
	}
	var ctx context.Context
	var cancel context.CancelFunc
	if hasTimeout {
		ctx, cancel = context.WithCancel(pctx)
	} else {
		ctx, cancel = context.WithTimeout(pctx, request.timeout)
	}
	defer cancel()

	client := operation.Client
	if client == nil {
		client = r.client
	}
	res, err := r.do(ctx, client, req) // make requests, by default follows 10 redirects before failing
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if r.Debug {
		b, err2 := httputil.DumpResponse(res, true)
		if err2 != nil {
			return nil, err2
		}
		fmt.Fprintln(os.Stderr, string(b))
	}

	ct := res.Header.Get(runtime.HeaderContentType)
	if ct == "" { // this should really really never occur
		ct = r.DefaultMediaType
	}

	mt, _, err := mime.ParseMediaType(ct)
	if err != nil {
		return nil, fmt.Errorf("parse content type: %s", err)
	}

	cons, ok := r.Consumers[mt]
	if !ok {
		// scream about not knowing what to do
		return nil, fmt.Errorf("no consumer: %q", ct)
	}
	return readResponse.ReadResponse(response{res}, cons)
}
<commit_msg>add lazy do function<commit_after>// Copyright 2015 go-swagger maintainers
//
// 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 client

import (
	"fmt"
	"mime"
	"net/http"
	"net/http/httputil"
	"os"
	"path"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"

	"github.com/go-openapi/runtime"
	"github.com/go-openapi/strfmt"
)

// DefaultTimeout the default request timeout
var DefaultTimeout = 30 * time.Second

// Runtime represents an API client that uses the transport
// to make http requests based on a swagger specification.
type Runtime struct {
	DefaultMediaType      string
	DefaultAuthentication runtime.ClientAuthInfoWriter
	Consumers             map[string]runtime.Consumer
	Producers             map[string]runtime.Producer

	Transport http.RoundTripper
	Jar       http.CookieJar
	//Spec      *spec.Document
	Host     string
	BasePath string
	Formats  strfmt.Registry
	Debug    bool
	Context  context.Context

	clientOnce *sync.Once
	client     *http.Client
	schemes    []string
	do         func(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error)
}

// New creates a new default runtime for a swagger api runtime.Client
func New(host, basePath string, schemes []string) *Runtime {
	var rt Runtime
	rt.DefaultMediaType = runtime.JSONMime

	// TODO: actually infer this stuff from the spec
	rt.Consumers = map[string]runtime.Consumer{
		runtime.JSONMime:    runtime.JSONConsumer(),
		runtime.XMLMime:     runtime.XMLConsumer(),
		runtime.TextMime:    runtime.TextConsumer(),
		runtime.DefaultMime: runtime.ByteStreamConsumer(),
	}
	rt.Producers = map[string]runtime.Producer{
		runtime.JSONMime:    runtime.JSONProducer(),
		runtime.XMLMime:     runtime.XMLProducer(),
		runtime.TextMime:    runtime.TextProducer(),
		runtime.DefaultMime: runtime.ByteStreamProducer(),
	}
	rt.Transport = http.DefaultTransport
	rt.Jar = nil
	rt.Host = host
	rt.BasePath = basePath
	rt.Context = context.Background()
	rt.clientOnce = new(sync.Once)
	if !strings.HasPrefix(rt.BasePath, "/") {
		rt.BasePath = "/" + rt.BasePath
	}
	rt.Debug = os.Getenv("DEBUG") == "1"
	if len(schemes) > 0 {
		rt.schemes = schemes
	}
	rt.do = ctxhttp.Do
	return &rt
}

// NewWithClient allows you to create a new transport with a configured http.Client
func NewWithClient(host, basePath string, schemes []string, client *http.Client) *Runtime {
	rt := New(host, basePath, schemes)
	if client != nil {
		rt.clientOnce.Do(func() {
			rt.client = client
		})
	}
	return rt
}

func (r *Runtime) pickScheme(schemes []string) string {
	if v := r.selectScheme(r.schemes); v != "" {
		return v
	}
	if v := r.selectScheme(schemes); v != "" {
		return v
	}
	return "http"
}

func (r *Runtime) selectScheme(schemes []string) string {
	schLen := len(schemes)
	if schLen == 0 {
		return ""
	}

	scheme := schemes[0]
	// prefer https, but skip when not possible
	if scheme != "https" && schLen > 1 {
		for _, sch := range schemes {
			if sch == "https" {
				scheme = sch
				break
			}
		}
	}
	return scheme
}

// Submit a request and when there is a body on success it will turn that into the result
// all other things are turned into an api error for swagger which retains the status code
func (r *Runtime) Submit(operation *runtime.ClientOperation) (interface{}, error) {
	params, readResponse, auth := operation.Params, operation.Reader, operation.AuthInfo

	request, err := newRequest(operation.Method, operation.PathPattern, params)
	if err != nil {
		return nil, err
	}

	var accept []string
	for _, mimeType := range operation.ProducesMediaTypes {
		accept = append(accept, mimeType)
	}
	request.SetHeaderParam(runtime.HeaderAccept, accept...)

	if auth == nil && r.DefaultAuthentication != nil {
		auth = r.DefaultAuthentication
	}
	if auth != nil {
		if err := auth.AuthenticateRequest(request, r.Formats); err != nil {
			return nil, err
		}
	}

	// TODO: pick appropriate media type
	cmt := r.DefaultMediaType
	if len(operation.ConsumesMediaTypes) > 0 {
		cmt = operation.ConsumesMediaTypes[0]
	}

	req, err := request.BuildHTTP(cmt, r.Producers, r.Formats)
	if err != nil {
		return nil, err
	}
	req.URL.Scheme = r.pickScheme(operation.Schemes)
	req.URL.Host = r.Host
	var reinstateSlash bool
	if req.URL.Path != "" && req.URL.Path != "/" && req.URL.Path[len(req.URL.Path)-1] == '/' {
		reinstateSlash = true
	}
	req.URL.Path = path.Join(r.BasePath, req.URL.Path)
	if reinstateSlash {
		req.URL.Path = req.URL.Path + "/"
	}

	r.clientOnce.Do(func() {
		r.client = &http.Client{
			Transport: r.Transport,
			Jar:       r.Jar,
		}
	})

	if r.Debug {
		b, err2 := httputil.DumpRequestOut(req, true)
		if err2 != nil {
			return nil, err2
		}
		fmt.Fprintln(os.Stderr, string(b))
	}

	var hasTimeout bool
	pctx := operation.Context
	if pctx == nil {
		pctx = r.Context
	} else {
		hasTimeout = true
	}
	if pctx == nil {
		pctx = context.Background()
	}
	var ctx context.Context
	var cancel context.CancelFunc
	if hasTimeout {
		ctx, cancel = context.WithCancel(pctx)
	} else {
		ctx, cancel = context.WithTimeout(pctx, request.timeout)
	}
	defer cancel()

	client := operation.Client
	if client == nil {
		client = r.client
	}
	if r.do == nil {
		r.do = ctxhttp.Do
	}
	res, err := r.do(ctx, client, req) // make requests, by default follows 10 redirects before failing
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if r.Debug {
		b, err2 := httputil.DumpResponse(res, true)
		if err2 != nil {
			return nil, err2
		}
		fmt.Fprintln(os.Stderr, string(b))
	}

	ct := res.Header.Get(runtime.HeaderContentType)
	if ct == "" { // this should really really never occur
		ct = r.DefaultMediaType
	}

	mt, _, err := mime.ParseMediaType(ct)
	if err != nil {
		return nil, fmt.Errorf("parse content type: %s", err)
	}

	cons, ok := r.Consumers[mt]
	if !ok {
		// scream about not knowing what to do
		return nil, fmt.Errorf("no consumer: %q", ct)
	}
	return readResponse.ReadResponse(response{res}, cons)
}
<|endoftext|>


<commit_before>/*
 * Minio Client (C) 2017 Minio, Inc.
 *
 * 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 cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio/pkg/madmin"
	"github.com/minio/minio/pkg/probe"
)

var (
	adminHealFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "recursive, r",
			Usage: "List recursively.",
		},
		cli.BoolFlag{
			Name:  "fake, k",
			Usage: "Issue a fake heal operation.",
		},
	}
)

var adminHealCmd = cli.Command{
	Name:   "heal",
	Usage:  "Manage heal tasks.",
	Before: setGlobalsFromContext,
	Action: mainAdminHeal,
	Flags:  append(adminHealFlags, globalFlags...),
	Subcommands: []cli.Command{
		adminHealListCmd,
	},
	CustomHelpTemplate: `NAME:
   {{.Name}} - {{.Usage}}

USAGE:
   {{.Name}} [FLAGS] COMMAND

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}

COMMANDS:
   {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
   {{end}}

EXAMPLES:
    1. Heal 'testbucket' in a Minio server represented by its alias 'play'.
       $ mc admin {{.Name}} play/testbucket/
    2. Heal all objects under 'dir' prefix
       $ mc admin {{.Name}} --recursive play/testbucket/dir/
    3. Issue a fake heal operation to see what the server could report
       $ mc admin {{.Name}} --fake play/testbucket/dir/

`,
}

// healObjectMessage container to hold repair information.
type healObjectMessage struct {
	Status string            `json:"status"`
	Bucket string            `json:"bucket"`
	Object madmin.ObjectInfo `json:"object"`
}

// String colorized service status message.
func (u healObjectMessage) String() string {
	msg := fmt.Sprintf("Object %s/%s is healed.", u.Bucket, u.Object.Key)
	return console.Colorize("Heal", msg)
}

// JSON jsonified service status Message message.
func (u healObjectMessage) JSON() string {
	u.Status = "success"
	statusJSONBytes, e := json.Marshal(u)
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")

	return string(statusJSONBytes)
}

// checkAdminHealSyntax - validate all the passed arguments
func checkAdminHealSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		cli.ShowSubcommandHelp(ctx)
		os.Exit(1)
	}
}

// mainAdminHeal - the entry function of heal command
func mainAdminHeal(ctx *cli.Context) error {

	// Check for command syntax
	checkAdminHealSyntax(ctx)

	// Get the alias parameter from cli
	args := ctx.Args()
	aliasedURL := args.Get(0)

	isRecursive := ctx.Bool("recursive")
	isFake := ctx.Bool("fake")

	console.SetColor("Heal", color.New(color.FgGreen, color.Bold))

	// Create a new Minio Admin Client
	client, err := newAdminClient(aliasedURL)
	if err != nil {
		return err.ToGoError()
	}

	// Compute bucket and object from the aliased URL
	aliasedURL = filepath.ToSlash(aliasedURL)
	splits := splitStr(aliasedURL, "/", 3)
	bucket, object := splits[1], splits[2]

	var e error

	// Heal format if bucket is not specified and quit immediately
	if bucket == "" {
		e = client.HealFormat(isFake)
		fatalIf(probe.NewError(e), "Cannot heal the specified storage format.")
		return nil
	}

	// Heal the specified bucket
	e = client.HealBucket(bucket, isFake)
	fatalIf(probe.NewError(e), "Cannot repair bucket.")

	// Search for objects that need healing
	doneCh := make(chan struct{})
	healObjectsCh, e := client.ListObjectsHeal(bucket, object, isRecursive, doneCh)
	fatalIf(probe.NewError(e), "Cannot list objects that need to be healed.")

	// Iterate over objects that need healing
	for obj := range healObjectsCh {
		// Return for any error
		fatalIf(probe.NewError(obj.Err), "Cannot list objects that need to be healed.")

		// Check the heal status, and call heal object API only when an object can be healed
		switch healInfo := *obj.HealObjectInfo; healInfo.Status {
		case madmin.CanHeal:
			// Heal Object
			e = client.HealObject(bucket, obj.Key, isFake)
			errorIf(probe.NewError(e), "Cannot repair object: `"+obj.Key+"`")

			// Print successful message
			printMsg(healObjectMessage{Bucket: bucket, Object: obj})

		case madmin.QuorumUnavailable:
			errorIf(errDummy().Trace(), obj.Key+" cannot be healed until quorum is available.")
		case madmin.Corrupted:
			errorIf(errDummy().Trace(), obj.Key+" cannot be healed, not enough information.")
		}
	}

	return nil
}
<commit_msg>heal: Don't print info if heal obj returns an err (#1988)<commit_after>/*
 * Minio Client (C) 2017 Minio, Inc.
 *
 * 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 cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio/pkg/madmin"
	"github.com/minio/minio/pkg/probe"
)

var (
	adminHealFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "recursive, r",
			Usage: "List recursively.",
		},
		cli.BoolFlag{
			Name:  "fake, k",
			Usage: "Issue a fake heal operation.",
		},
	}
)

var adminHealCmd = cli.Command{
	Name:   "heal",
	Usage:  "Manage heal tasks.",
	Before: setGlobalsFromContext,
	Action: mainAdminHeal,
	Flags:  append(adminHealFlags, globalFlags...),
	Subcommands: []cli.Command{
		adminHealListCmd,
	},
	CustomHelpTemplate: `NAME:
   {{.Name}} - {{.Usage}}

USAGE:
   {{.Name}} [FLAGS] COMMAND

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}

COMMANDS:
   {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
   {{end}}

EXAMPLES:
    1. Heal 'testbucket' in a Minio server represented by its alias 'play'.
       $ mc admin {{.Name}} play/testbucket/
    2. Heal all objects under 'dir' prefix
       $ mc admin {{.Name}} --recursive play/testbucket/dir/
    3. Issue a fake heal operation to see what the server could report
       $ mc admin {{.Name}} --fake play/testbucket/dir/

`,
}

// healObjectMessage container to hold repair information.
type healObjectMessage struct {
	Status string            `json:"status"`
	Bucket string            `json:"bucket"`
	Object madmin.ObjectInfo `json:"object"`
}

// String colorized service status message.
func (u healObjectMessage) String() string {
	msg := fmt.Sprintf("Object %s/%s is healed.", u.Bucket, u.Object.Key)
	return console.Colorize("Heal", msg)
}

// JSON jsonified service status Message message.
func (u healObjectMessage) JSON() string {
	u.Status = "success"
	statusJSONBytes, e := json.Marshal(u)
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")

	return string(statusJSONBytes)
}

// checkAdminHealSyntax - validate all the passed arguments
func checkAdminHealSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		cli.ShowSubcommandHelp(ctx)
		os.Exit(1)
	}
}

// mainAdminHeal - the entry function of heal command
func mainAdminHeal(ctx *cli.Context) error {

	// Check for command syntax
	checkAdminHealSyntax(ctx)

	// Get the alias parameter from cli
	args := ctx.Args()
	aliasedURL := args.Get(0)

	isRecursive := ctx.Bool("recursive")
	isFake := ctx.Bool("fake")

	console.SetColor("Heal", color.New(color.FgGreen, color.Bold))

	// Create a new Minio Admin Client
	client, err := newAdminClient(aliasedURL)
	if err != nil {
		return err.ToGoError()
	}

	// Compute bucket and object from the aliased URL
	aliasedURL = filepath.ToSlash(aliasedURL)
	splits := splitStr(aliasedURL, "/", 3)
	bucket, object := splits[1], splits[2]

	var e error

	// Heal format if bucket is not specified and quit immediately
	if bucket == "" {
		e = client.HealFormat(isFake)
		fatalIf(probe.NewError(e), "Cannot heal the specified storage format.")
		return nil
	}

	// Heal the specified bucket
	e = client.HealBucket(bucket, isFake)
	fatalIf(probe.NewError(e), "Cannot repair bucket.")

	// Search for objects that need healing
	doneCh := make(chan struct{})
	healObjectsCh, e := client.ListObjectsHeal(bucket, object, isRecursive, doneCh)
	fatalIf(probe.NewError(e), "Cannot list objects that need to be healed.")

	// Iterate over objects that need healing
	for obj := range healObjectsCh {
		// Return for any error
		fatalIf(probe.NewError(obj.Err), "Cannot list objects that need to be healed.")

		// Check the heal status, and call heal object API only when an object can be healed
		switch healInfo := *obj.HealObjectInfo; healInfo.Status {
		case madmin.CanHeal:
			// Heal Object
			e = client.HealObject(bucket, obj.Key, isFake)
			if e != nil {
				errorIf(probe.NewError(e), "Cannot repair object: `"+obj.Key+"`")
				continue
			}

			// Print successful message
			printMsg(healObjectMessage{Bucket: bucket, Object: obj})

		case madmin.QuorumUnavailable:
			errorIf(errDummy().Trace(), obj.Key+" cannot be healed until quorum is available.")
		case madmin.Corrupted:
			errorIf(errDummy().Trace(), obj.Key+" cannot be healed, not enough information.")
		}
	}

	return nil
}
<|endoftext|>