<commit_before>package xxhash

import (
	"errors"
	"hash"
	"unsafe"
)

const (
	PRIME32_1 = 2654435761
	PRIME32_2 = 2246822519
	PRIME32_3 = 3266489917
	PRIME32_4 = 668265263
	PRIME32_5 = 374761393
)

type XXHash struct {
	seed, v1, v2, v3, v4 uint32
	total_len            uint64
	memory               [16]byte
	memsize              int
}

func New(seed uint32) hash.Hash32 {
	return &XXHash{
		seed: seed,
		v1:   seed + PRIME32_1 + PRIME32_2,
		v2:   seed + PRIME32_2,
		v3:   seed,
		v4:   seed - PRIME32_1,
	}
}

func (self *XXHash) BlockSize() int {
	return 1
}

// Size returns the number of bytes Sum will return.
func (self *XXHash) Size() int {
	return 4
}

func (self *XXHash) feed(in []byte) uint32 {
	p := 0
	bEnd := len(in)

	self.total_len += uint64(bEnd)

	// fill in tmp buffer
	if self.memsize+bEnd < 16 {
		copy(self.memory[self.memsize:], in)
		self.memsize += bEnd
		return 0
	}

	if self.memsize > 0 {
		copy(self.memory[self.memsize:], in[:16-self.memsize])
		self.v1 += *(*uint32)(unsafe.Pointer(&self.memory[0])) * PRIME32_2
		self.v1 = ((self.v1 << 13) | (self.v1 >> (32 - 13))) * PRIME32_1

		self.v2 += *(*uint32)(unsafe.Pointer(&self.memory[4])) * PRIME32_2
		self.v2 = ((self.v2 << 13) | (self.v2 >> (32 - 13))) * PRIME32_1

		self.v3 += *(*uint32)(unsafe.Pointer(&self.memory[8])) * PRIME32_2
		self.v3 = ((self.v3 << 13) | (self.v3 >> (32 - 13))) * PRIME32_1

		self.v4 += *(*uint32)(unsafe.Pointer(&self.memory[12])) * PRIME32_2
		self.v4 = ((self.v4 << 13) | (self.v4 >> (32 - 13))) * PRIME32_1

		s := 16 - self.memsize
		p += s
		self.memsize = 0
	}

	limit := bEnd - 16
	v1, v2, v3, v4 := self.v1, self.v2, self.v3, self.v4

	for ; p <= limit; p += 16 {
		v1 += *(*uint32)(unsafe.Pointer(&in[p])) * PRIME32_2
		v1 = ((v1 << 13) | (v1 >> (32 - 13))) * PRIME32_1

		v2 += *(*uint32)(unsafe.Pointer(&in[p+4])) * PRIME32_2
		v2 = ((v2 << 13) | (v2 >> (32 - 13))) * PRIME32_1

		v3 += *(*uint32)(unsafe.Pointer(&in[p+8])) * PRIME32_2
		v3 = ((v3 << 13) | (v3 >> (32 - 13))) * PRIME32_1

		v4 += *(*uint32)(unsafe.Pointer(&in[p+12])) * PRIME32_2
		v4 = ((v4 << 13) | (v4 >> (32 - 13))) * PRIME32_1
	}

	limit = bEnd - p

	if limit > 0 {
		copy(self.memory[:], in[p:bEnd])
		self.memsize = limit
	}

	self.v1 = v1
	self.v2 = v2
	self.v3 = v3
	self.v4 = v4

	return 0
}

func (self *XXHash) Sum32() uint32 {
	p := 0
	bEnd := self.memsize
	h32 := uint32(0)

	if self.total_len >= 16 {
		h32 = ((self.v1 << 1) | (self.v1 >> (32 - 1))) +
			((self.v2 << 7) | (self.v2 >> (32 - 7))) +
			((self.v3 << 12) | (self.v3 >> (32 - 12))) +
			((self.v4 << 18) | (self.v4 >> (32 - 18)))
	} else {
		h32 = self.seed + PRIME32_5
	}

	h32 += uint32(self.total_len)

	for p <= bEnd-4 {
		h32 += *(*uint32)(unsafe.Pointer(&self.memory[p])) * PRIME32_3
		h32 = ((h32 << 17) | (h32 >> (32 - 17))) * PRIME32_4
		p += 4
	}

	for p < bEnd {
		h32 += uint32(self.memory[p]) * PRIME32_5
		h32 = ((h32 << 11) | (h32 >> (32 - 11))) * PRIME32_1
		p++
	}

	h32 ^= h32 >> 15
	h32 *= PRIME32_2
	h32 ^= h32 >> 13
	h32 *= PRIME32_3
	h32 ^= h32 >> 16

	return h32
}

func (self *XXHash) Sum(in []byte) []byte {
	h := self.Sum32()
	in = append(in, byte(h>>24))
	in = append(in, byte(h>>16))
	in = append(in, byte(h>>8))
	in = append(in, byte(h))
	return in
}

func (self *XXHash) Reset() {
	seed := self.seed
	self.v1 = seed + PRIME32_1 + PRIME32_2
	self.v2 = seed + PRIME32_2
	self.v3 = seed
	self.v4 = seed - PRIME32_1
	self.total_len = 0
	self.memsize = 0
}

// Write adds more data to the running hash.
// Length of data MUST BE less than 1 Gigabytes.
func (self *XXHash) Write(data []byte) (nn int, err error) {
	l := len(data)
	if l > 1<<30 {
		return 0, errors.New("Cannot add more than 1 Gigabytes at once.")
	}
	self.feed(data)
	return len(data), nil
}

// Checksum32Seed returns the xxhash32 checksum of data using a seed. Length of data MUST BE less than 2 Gigabytes.
func Checksum32(data []byte) uint32 {
	return Checksum32Seed(data, 0)
}

// Checksum32 returns the xxhash32 checksum of data. Length of data MUST BE less than 2 Gigabytes.
func Checksum32Seed(data []byte, seed uint32) uint32 {
	p := 0
	bEnd := len(data)
	h32 := uint32(0)

	if bEnd >= 16 {
		limit := bEnd - 16

		v1 := seed + PRIME32_1 + PRIME32_2
		v2 := seed + PRIME32_2
		v3 := seed + 0
		v4 := seed - PRIME32_1
		for {
			v1 += *(*uint32)(unsafe.Pointer(&data[p])) * PRIME32_2
			v1 = ((v1 << 13) | (v1 >> (32 - 13))) * PRIME32_1

			v2 += *(*uint32)(unsafe.Pointer(&data[p+4])) * PRIME32_2
			v2 = ((v2 << 13) | (v2 >> (32 - 13))) * PRIME32_1

			v3 += *(*uint32)(unsafe.Pointer(&data[p+8])) * PRIME32_2
			v3 = ((v3 << 13) | (v3 >> (32 - 13))) * PRIME32_1

			v4 += *(*uint32)(unsafe.Pointer(&data[p+12])) * PRIME32_2
			v4 = ((v4 << 13) | (v4 >> (32 - 13))) * PRIME32_1

			p += 16
			if p > limit {
				break
			}
		}
		h32 = ((v1 << 1) | (v1 >> (32 - 1))) +
			((v2 << 7) | (v2 >> (32 - 7))) +
			((v3 << 12) | (v3 >> (32 - 12))) +
			((v4 << 18) | (v4 >> (32 - 18)))
	} else {
		h32 = seed + PRIME32_5
	}

	h32 += uint32(bEnd)

	for p <= bEnd-4 {
		h32 += *(*uint32)(unsafe.Pointer(&data[p])) * PRIME32_3
		h32 = ((h32 << 17) | (h32 >> (32 - 17))) * PRIME32_4
		p += 4
	}

	for p < bEnd {
		h32 += uint32(data[p]) * PRIME32_5
		h32 = ((h32 << 11) | (h32 >> (32 - 11))) * PRIME32_1
		p++
	}

	h32 ^= h32 >> 15
	h32 *= PRIME32_2
	h32 ^= h32 >> 13
	h32 *= PRIME32_3
	h32 ^= h32 >> 16

	return h32
}
<commit_msg>More unsafe code to increase performance.<commit_after>package xxhash

import (
	"errors"
	"hash"
	"unsafe"
)

const (
	PRIME32_1 = 2654435761
	PRIME32_2 = 2246822519
	PRIME32_3 = 3266489917
	PRIME32_4 = 668265263
	PRIME32_5 = 374761393
)

type XXHash struct {
	seed, v1, v2, v3, v4 uint32
	total_len            uint64
	memory               [16]byte
	memsize              int
}

func New(seed uint32) hash.Hash32 {
	return &XXHash{
		seed: seed,
		v1:   seed + PRIME32_1 + PRIME32_2,
		v2:   seed + PRIME32_2,
		v3:   seed,
		v4:   seed - PRIME32_1,
	}
}

func (self *XXHash) BlockSize() int {
	return 1
}

// Size returns the number of bytes Sum will return.
func (self *XXHash) Size() int {
	return 4
}

func (self *XXHash) feed(in []byte) uint32 {
	p := uintptr(unsafe.Pointer(&in[0]))
	pTemp := p
	l := len(in)
	bEnd := p + uintptr(l)

	self.total_len += uint64(l)

	// fill in tmp buffer
	if self.memsize+l < 16 {
		copy(self.memory[self.memsize:], in)
		self.memsize += l
		return 0
	}

	if self.memsize > 0 {
		copy(self.memory[self.memsize:], in[:16-self.memsize])
		p2 := uintptr(unsafe.Pointer(&self.memory[0]))
		self.v1 += (*(*uint32)(unsafe.Pointer(p2))) * PRIME32_2
		self.v1 = ((self.v1 << 13) | (self.v1 >> (32 - 13))) * PRIME32_1

		self.v2 += (*(*uint32)(unsafe.Pointer(p2 + 4))) * PRIME32_2
		self.v2 = ((self.v2 << 13) | (self.v2 >> (32 - 13))) * PRIME32_1

		self.v3 += (*(*uint32)(unsafe.Pointer(p2 + 8))) * PRIME32_2
		self.v3 = ((self.v3 << 13) | (self.v3 >> (32 - 13))) * PRIME32_1

		self.v4 += (*(*uint32)(unsafe.Pointer(p2 + 12))) * PRIME32_2
		self.v4 = ((self.v4 << 13) | (self.v4 >> (32 - 13))) * PRIME32_1

		p += 16 - uintptr(self.memsize)
		self.memsize = 0
	}

	limit := bEnd - 16
	v1, v2, v3, v4 := self.v1, self.v2, self.v3, self.v4

	for ; p <= limit; p += 16 {
		v1 += (*(*uint32)(unsafe.Pointer(p))) * PRIME32_2
		v1 = ((v1 << 13) | (v1 >> (32 - 13))) * PRIME32_1

		v2 += (*(*uint32)(unsafe.Pointer(p + 4))) * PRIME32_2
		v2 = ((v2 << 13) | (v2 >> (32 - 13))) * PRIME32_1

		v3 += (*(*uint32)(unsafe.Pointer(p + 8))) * PRIME32_2
		v3 = ((v3 << 13) | (v3 >> (32 - 13))) * PRIME32_1

		v4 += (*(*uint32)(unsafe.Pointer(p + 12))) * PRIME32_2
		v4 = ((v4 << 13) | (v4 >> (32 - 13))) * PRIME32_1
	}

	self.v1 = v1
	self.v2 = v2
	self.v3 = v3
	self.v4 = v4

	limit = bEnd - p

	if limit > 0 {
		copy(self.memory[:], in[p-pTemp:bEnd-pTemp])
		self.memsize = int(limit)
	}

	return 0
}

func (self *XXHash) Sum32() uint32 {
	p := uintptr(unsafe.Pointer(&self.memory[0]))
	bEnd := p + uintptr(self.memsize)
	h32 := uint32(0)

	if self.total_len >= 16 {
		h32 = ((self.v1 << 1) | (self.v1 >> (32 - 1))) +
			((self.v2 << 7) | (self.v2 >> (32 - 7))) +
			((self.v3 << 12) | (self.v3 >> (32 - 12))) +
			((self.v4 << 18) | (self.v4 >> (32 - 18)))
	} else {
		h32 = self.seed + PRIME32_5
	}

	h32 += uint32(self.total_len)

	for p <= bEnd-4 {
		h32 += (*(*uint32)(unsafe.Pointer(p))) * PRIME32_3
		h32 = ((h32 << 17) | (h32 >> (32 - 17))) * PRIME32_4
		p += 4
	}

	for p < bEnd {
		h32 += uint32(*(*byte)(unsafe.Pointer(p))) * PRIME32_5
		h32 = ((h32 << 11) | (h32 >> (32 - 11))) * PRIME32_1
		p++
	}

	h32 ^= h32 >> 15
	h32 *= PRIME32_2
	h32 ^= h32 >> 13
	h32 *= PRIME32_3
	h32 ^= h32 >> 16

	return h32
}

func (self *XXHash) Sum(in []byte) []byte {
	h := self.Sum32()
	in = append(in, byte(h>>24))
	in = append(in, byte(h>>16))
	in = append(in, byte(h>>8))
	in = append(in, byte(h))
	return in
}

func (self *XXHash) Reset() {
	seed := self.seed
	self.v1 = seed + PRIME32_1 + PRIME32_2
	self.v2 = seed + PRIME32_2
	self.v3 = seed
	self.v4 = seed - PRIME32_1
	self.total_len = 0
	self.memsize = 0
}

// Write adds more data to the running hash.
// Length of data MUST BE less than 1 Gigabytes.
func (self *XXHash) Write(data []byte) (nn int, err error) {
	if data == nil {
		return 0, errors.New("Data cannot be nil.")
	}
	l := len(data)
	if l > 1<<30 {
		return 0, errors.New("Cannot add more than 1 Gigabytes at once.")
	}
	self.feed(data)
	return len(data), nil
}

// Checksum32Seed returns the xxhash32 checksum of data using a seed. Length of data MUST BE less than 2 Gigabytes.
func Checksum32(data []byte) uint32 {
	return Checksum32Seed(data, 0)
}

// Checksum32 returns the xxhash32 checksum of data. Length of data MUST BE less than 2 Gigabytes.
func Checksum32Seed(data []byte, seed uint32) uint32 {
	if data == nil {
		panic("Data cannot be nil.")
	}
	p := uintptr(unsafe.Pointer(&data[0]))
	l := len(data)
	bEnd := p + uintptr(l)
	h32 := uint32(0)

	if l >= 16 {
		limit := bEnd - 16

		v1 := seed + PRIME32_1 + PRIME32_2
		v2 := seed + PRIME32_2
		v3 := seed + 0
		v4 := seed - PRIME32_1
		for {
			v1 += (*(*uint32)(unsafe.Pointer(p))) * PRIME32_2
			v1 = ((v1 << 13) | (v1 >> (32 - 13))) * PRIME32_1

			v2 += (*(*uint32)(unsafe.Pointer(p + 4))) * PRIME32_2
			v2 = ((v2 << 13) | (v2 >> (32 - 13))) * PRIME32_1

			v3 += (*(*uint32)(unsafe.Pointer(p + 8))) * PRIME32_2
			v3 = ((v3 << 13) | (v3 >> (32 - 13))) * PRIME32_1

			v4 += (*(*uint32)(unsafe.Pointer(p + 12))) * PRIME32_2
			v4 = ((v4 << 13) | (v4 >> (32 - 13))) * PRIME32_1

			p += 16
			if p > limit {
				break
			}
		}
		h32 = ((v1 << 1) | (v1 >> (32 - 1))) +
			((v2 << 7) | (v2 >> (32 - 7))) +
			((v3 << 12) | (v3 >> (32 - 12))) +
			((v4 << 18) | (v4 >> (32 - 18)))
	} else {
		h32 = seed + PRIME32_5
	}

	h32 += uint32(l)

	for p <= bEnd-4 {
		h32 += (*(*uint32)(unsafe.Pointer(p))) * PRIME32_3
		h32 = ((h32 << 17) | (h32 >> (32 - 17))) * PRIME32_4
		p += 4
	}

	for p < bEnd {
		h32 += uint32(*(*byte)(unsafe.Pointer(p))) * PRIME32_5
		h32 = ((h32 << 11) | (h32 >> (32 - 11))) * PRIME32_1
		p++
	}

	h32 ^= h32 >> 15
	h32 *= PRIME32_2
	h32 ^= h32 >> 13
	h32 *= PRIME32_3
	h32 ^= h32 >> 16

	return h32
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, fromkeith
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * 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.
 *
 * * Neither the name of the fromkeith nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * 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 HOLDER 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 dynamo

import (
    "github.com/fromkeith/awsgo"
    "errors"
    "encoding/json"
)

type BatchWriteItemDeleteRequest struct {
    Key         map[string]interface{}
}

type BatchWriteItemPutRequest struct {
    Item         map[string]interface{}
}

type BatchWriteItem struct {
    DeleteRequest *BatchWriteItemDeleteRequest `json:",omitempty"`
    PutRequest *BatchWriteItemPutRequest `json:",omitempty"`
}

type BatchWriteItemRequest struct {
    awsgo.RequestBuilder

    RequestItems            map[string][]BatchWriteItem
    ReturnConsumedCapacity  string
    ReturnItemCollectionMetrics string
}

type BatchWriteItemResponse struct {
    ConsumedCapacity *CapacityResult             `json:",omitempty"`
    ItemCollectionMetrics * ItemCollectionMetricsStruct `json:",omitempty"`
    UnprocessedItems    map[string][]BatchWriteItem `json:"-"`
                    //     table  | operations| key/item | name     | item type/value
    RawUnprocessItems  map[string][]map[string]map[string]map[string]map[string]interface{} `json:"UnprocessedItems"`
}

func (req *BatchWriteItemRequest) AddTable(name string) {
    req.RequestItems[name] = make([]BatchWriteItem, 25)[0:0]
}

func (req * BatchWriteItemRequest) checkAndGrow(table string) ([]BatchWriteItem) {
    var genericItems []BatchWriteItem
    var ok bool
    if genericItems, ok = req.RequestItems[table]; !ok {
        req.AddTable(table)
        genericItems = req.RequestItems[table]
    }
    if len(genericItems) == cap(genericItems) {
        newItems := make([]BatchWriteItem, len(genericItems) * 2)
        for i := range genericItems {
            newItems[i] = genericItems[i]
        }
        genericItems = newItems[:len(genericItems)]
    }
    genericItems = genericItems[:len(genericItems) + 1]
    return genericItems
}

/** Adds the delete request items into the request.
 * @param table the name of the table to modify
 * @param items a map of awsgo.AwsStringItem and awsgo.AwsNumberItem
 */
func (req *BatchWriteItemRequest) AddDeleteRequest(table string, items map[string]interface{}) {
    genericItems := req.checkAndGrow(table)

    writeReq := new(BatchWriteItemDeleteRequest)
    writeReq.Key = make(map[string]interface{})
    for key, val := range items {
        writeReq.Key[key] = val
    }

    genericItems[len(genericItems) - 1] = BatchWriteItem{DeleteRequest: writeReq}
    req.RequestItems[table] = genericItems
}

/** Adds the put request items into the request.
 * @param table the name of the table to modify
 * @param items a map of awsgo.AwsStringItem and awsgo.AwsNumberItem
 */
func (req *BatchWriteItemRequest) AddPutRequest(table string, items map[string]interface{}) {
    genericItems := req.checkAndGrow(table)

    writeReq := new(BatchWriteItemPutRequest)
    writeReq.Item = make(map[string]interface{})
    for key, val := range items {
        writeReq.Item[key] = val
    }

    genericItems[len(genericItems) - 1] = BatchWriteItem{PutRequest: writeReq}
    req.RequestItems[table] = genericItems
}

func NewBatchWriteItemRequest() * BatchWriteItemRequest {
    req := new(BatchWriteItemRequest)
    req.RequestItems = make(map[string][]BatchWriteItem)
    req.ReturnConsumedCapacity = ConsumedCapacity_NONE
    req.ReturnItemCollectionMetrics = ItemCollectionMetrics_NONE
    req.Headers = make(map[string]string)
    req.Headers["X-Amz-Target"] = BatchWriteItemTarget
    req.RequestMethod = "POST"
    req.CanonicalUri = "/"
    return req
}

func (gir BatchWriteItemRequest) DeMarshalResponse(response []byte, headers map[string]string, statusCode int) (interface{}) {
    if err := CheckForErrorResponse(response, statusCode); err != nil {
        return err
    }
    giResponse := new(BatchWriteItemResponse)
    err := json.Unmarshal([]byte(response), giResponse)
    if err != nil {
        return err
    }
    giResponse.UnprocessedItems = make(map[string][]BatchWriteItem)

    //                        table  | operations| key/item | name     | item type/value
    // RawUnprocessItems  map[string][]map[string]map[string]map[string]map[string]string `json:"UnprocessedItems"`
    for table, operations := range giResponse.RawUnprocessItems {
        giResponse.UnprocessedItems[table] = make([]BatchWriteItem, len(operations))
        for op := range operations {
            if opName, ok := operations[op]["DeleteRequest"]; ok {
                var del BatchWriteItemDeleteRequest
                del.Key = make(map[string]interface{})
                awsgo.FromRawMapToEasyTypedMap(opName["Key"], del.Key)
                giResponse.UnprocessedItems[table][op] = BatchWriteItem{DeleteRequest: &del}
            } else if opName, ok := operations[op]["PutRequest"]; ok {
                var put BatchWriteItemPutRequest
                put.Item = make(map[string]interface{})
                awsgo.FromRawMapToEasyTypedMap(opName["Item"], put.Item)
                giResponse.UnprocessedItems[table][op] = BatchWriteItem{PutRequest: &put}
            }
        }
    }
    if giResponse.ItemCollectionMetrics != nil {
        giResponse.ItemCollectionMetrics.ItemCollectionKey = make(map[string]interface{})
        awsgo.FromRawMapToEasyTypedMap(giResponse.ItemCollectionMetrics.RawItemCollectionKey, giResponse.ItemCollectionMetrics.ItemCollectionKey)
    }
    return giResponse
}

func (gir * BatchWriteItemRequest) VerifyInput() (error) {
    gir.Host.Service = "dynamodb"
    if len(gir.RequestItems) == 0 {
        return errors.New("RequestItems cannot be empty")
    }
    if len(gir.Host.Region) == 0 {
        return errors.New("Host.Region cannot be empty")
    }
    for _, batchRequest := range gir.RequestItems {
        for s := range batchRequest {
            if batchRequest[s].DeleteRequest != nil {
                for k, v := range batchRequest[s].DeleteRequest.Key {
                    batchRequest[s].DeleteRequest.Key[k] = awsgo.ConvertToAwsItem(v)
                }
            }
            if batchRequest[s].PutRequest != nil {
                for k, v := range batchRequest[s].PutRequest.Item {
                    batchRequest[s].PutRequest.Item[k] = awsgo.ConvertToAwsItem(v)
                }
            }
        }
    }
    return nil
}

func (gir BatchWriteItemRequest) Request() (*BatchWriteItemResponse, error) {
    request, err := awsgo.NewAwsRequest(&gir, gir)
    if err != nil {
        return nil, err
    }
    request.RequestSigningType = awsgo.RequestSigningType_AWS4
    resp, err := request.DoAndDemarshall(&gir)
    if resp == nil {
        return nil, err
    }
    return resp.(*BatchWriteItemResponse), err
}
<commit_msg>Provide a helper method in dynamo.BatchWriteItem so that the user doesn't have to worry about their requests having more then 25 items. Also add a helper so we don't have to worry about unprocessed items.<commit_after>/*
 * Copyright (c) 2013, fromkeith
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * 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.
 *
 * * Neither the name of the fromkeith nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * 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 HOLDER 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 dynamo

import (
    "github.com/fromkeith/awsgo"
    "errors"
    "encoding/json"
    "time"
)

type BatchWriteItemDeleteRequest struct {
    Key         map[string]interface{}
}

type BatchWriteItemPutRequest struct {
    Item         map[string]interface{}
}

type BatchWriteItem struct {
    DeleteRequest *BatchWriteItemDeleteRequest `json:",omitempty"`
    PutRequest *BatchWriteItemPutRequest `json:",omitempty"`
}

type BatchWriteItemRequest struct {
    awsgo.RequestBuilder

    RequestItems            map[string][]BatchWriteItem
    ReturnConsumedCapacity  string
    ReturnItemCollectionMetrics string
}

type BatchWriteItemResponse struct {
    ConsumedCapacity *CapacityResult             `json:",omitempty"`
    ItemCollectionMetrics * ItemCollectionMetricsStruct `json:",omitempty"`
    UnprocessedItems    map[string][]BatchWriteItem `json:"-"`
                    //     table  | operations| key/item | name     | item type/value
    RawUnprocessItems  map[string][]map[string]map[string]map[string]map[string]interface{} `json:"UnprocessedItems"`
}

func (req *BatchWriteItemRequest) AddTable(name string) {
    req.RequestItems[name] = make([]BatchWriteItem, 25)[0:0]
}

func (req * BatchWriteItemRequest) checkAndGrow(table string) ([]BatchWriteItem) {
    var genericItems []BatchWriteItem
    var ok bool
    if genericItems, ok = req.RequestItems[table]; !ok {
        req.AddTable(table)
        genericItems = req.RequestItems[table]
    }
    if len(genericItems) == cap(genericItems) {
        newItems := make([]BatchWriteItem, len(genericItems) * 2)
        for i := range genericItems {
            newItems[i] = genericItems[i]
        }
        genericItems = newItems[:len(genericItems)]
    }
    genericItems = genericItems[:len(genericItems) + 1]
    return genericItems
}

/** Adds the delete request items into the request.
 * @param table the name of the table to modify
 * @param items a map of awsgo.AwsStringItem and awsgo.AwsNumberItem
 */
func (req *BatchWriteItemRequest) AddDeleteRequest(table string, items map[string]interface{}) {
    genericItems := req.checkAndGrow(table)

    writeReq := new(BatchWriteItemDeleteRequest)
    writeReq.Key = make(map[string]interface{})
    for key, val := range items {
        writeReq.Key[key] = val
    }

    genericItems[len(genericItems) - 1] = BatchWriteItem{DeleteRequest: writeReq}
    req.RequestItems[table] = genericItems
}

/** Adds the put request items into the request.
 * @param table the name of the table to modify
 * @param items a map of awsgo.AwsStringItem and awsgo.AwsNumberItem
 */
func (req *BatchWriteItemRequest) AddPutRequest(table string, items map[string]interface{}) {
    genericItems := req.checkAndGrow(table)

    writeReq := new(BatchWriteItemPutRequest)
    writeReq.Item = make(map[string]interface{})
    for key, val := range items {
        writeReq.Item[key] = val
    }

    genericItems[len(genericItems) - 1] = BatchWriteItem{PutRequest: writeReq}
    req.RequestItems[table] = genericItems
}

func NewBatchWriteItemRequest() * BatchWriteItemRequest {
    req := new(BatchWriteItemRequest)
    req.RequestItems = make(map[string][]BatchWriteItem)
    req.ReturnConsumedCapacity = ConsumedCapacity_NONE
    req.ReturnItemCollectionMetrics = ItemCollectionMetrics_NONE
    req.Headers = make(map[string]string)
    req.Headers["X-Amz-Target"] = BatchWriteItemTarget
    req.RequestMethod = "POST"
    req.CanonicalUri = "/"
    return req
}

func (gir BatchWriteItemRequest) DeMarshalResponse(response []byte, headers map[string]string, statusCode int) (interface{}) {
    if err := CheckForErrorResponse(response, statusCode); err != nil {
        return err
    }
    giResponse := new(BatchWriteItemResponse)
    err := json.Unmarshal([]byte(response), giResponse)
    if err != nil {
        return err
    }
    giResponse.UnprocessedItems = make(map[string][]BatchWriteItem)

    //                        table  | operations| key/item | name     | item type/value
    // RawUnprocessItems  map[string][]map[string]map[string]map[string]map[string]string `json:"UnprocessedItems"`
    for table, operations := range giResponse.RawUnprocessItems {
        giResponse.UnprocessedItems[table] = make([]BatchWriteItem, len(operations))
        for op := range operations {
            if opName, ok := operations[op]["DeleteRequest"]; ok {
                var del BatchWriteItemDeleteRequest
                del.Key = make(map[string]interface{})
                awsgo.FromRawMapToEasyTypedMap(opName["Key"], del.Key)
                giResponse.UnprocessedItems[table][op] = BatchWriteItem{DeleteRequest: &del}
            } else if opName, ok := operations[op]["PutRequest"]; ok {
                var put BatchWriteItemPutRequest
                put.Item = make(map[string]interface{})
                awsgo.FromRawMapToEasyTypedMap(opName["Item"], put.Item)
                giResponse.UnprocessedItems[table][op] = BatchWriteItem{PutRequest: &put}
            }
        }
    }
    if giResponse.ItemCollectionMetrics != nil {
        giResponse.ItemCollectionMetrics.ItemCollectionKey = make(map[string]interface{})
        awsgo.FromRawMapToEasyTypedMap(giResponse.ItemCollectionMetrics.RawItemCollectionKey, giResponse.ItemCollectionMetrics.ItemCollectionKey)
    }
    return giResponse
}

func (gir * BatchWriteItemRequest) VerifyInput() (error) {
    gir.Host.Service = "dynamodb"
    if len(gir.RequestItems) == 0 {
        return errors.New("RequestItems cannot be empty")
    }
    if len(gir.Host.Region) == 0 {
        return errors.New("Host.Region cannot be empty")
    }
    for _, batchRequest := range gir.RequestItems {
        for s := range batchRequest {
            if batchRequest[s].DeleteRequest != nil {
                for k, v := range batchRequest[s].DeleteRequest.Key {
                    batchRequest[s].DeleteRequest.Key[k] = awsgo.ConvertToAwsItem(v)
                }
            }
            if batchRequest[s].PutRequest != nil {
                for k, v := range batchRequest[s].PutRequest.Item {
                    batchRequest[s].PutRequest.Item[k] = awsgo.ConvertToAwsItem(v)
                }
            }
        }
    }
    return nil
}

func (gir BatchWriteItemRequest) Request() (*BatchWriteItemResponse, error) {
    request, err := awsgo.NewAwsRequest(&gir, gir)
    if err != nil {
        return nil, err
    }
    request.RequestSigningType = awsgo.RequestSigningType_AWS4
    resp, err := request.DoAndDemarshall(&gir)
    if resp == nil {
        return nil, err
    }
    return resp.(*BatchWriteItemResponse), err
}

// makes the request, and tries to include the unprocessed request items in subsequent requests
func (gir BatchWriteItemRequest) RequestIncludingUnprocessed() (*BatchWriteItemResponse, error) {
    resp, err := gir.Request()
    if err != nil {
        return resp, err
    }
    lastSize := len(resp.UnprocessedItems)
    for backOff := 0; len(resp.UnprocessedItems) > 0; {
        if lastSize == len(resp.UnprocessedItems) {
            backOff ++
        }
        lastSize = len(resp.UnprocessedItems)
        if backOff > 5 {
            return resp, errors.New("Backoff exceeded. Giving up.")
        }
        time.Sleep(time.Duration(backOff * 100))

        retryRequest := NewBatchWriteItemRequest()
        retryRequest.RequestBuilder = gir.deepCopyRequestBuilder()
        retryRequest.RequestItems = resp.UnprocessedItems
        resp, err = retryRequest.Request()
        if err != nil {
            return resp, err
        }
    }
    return resp, nil
}

func (gir BatchWriteItemRequest) deepCopyRequestBuilder() awsgo.RequestBuilder {
    var theCopy awsgo.RequestBuilder
    theCopy.Host.Service = gir.Host.Service
    theCopy.Host.Region = gir.Host.Region
    theCopy.Host.Domain = gir.Host.Domain
    theCopy.Host.Override = gir.Host.Override
    // ignore the cert for now
    // theCopy.Host.CustomCertificates

    theCopy.Key = gir.Key
    theCopy.Headers = make(map[string]string)
    for k, v := range gir.Headers {
        theCopy.Headers[k] = v
    }
    theCopy.RequestMethod = gir.RequestMethod
    theCopy.CanonicalUri = gir.CanonicalUri
    return theCopy
}

// Makes multiple requests, if too many actions were added.
// Also automatically retries unprocessed items.
// returns on the first error.
func (gir BatchWriteItemRequest) RequestSplit() ([]*BatchWriteItemResponse, error) {

    responses := make([]*BatchWriteItemResponse, 0, 10)

    var curSubRequest *BatchWriteItemRequest
    itemsInSet := 0
    for table, reqs := range gir.RequestItems {
        for i := range reqs {
            if curSubRequest == nil {
                curSubRequest = NewBatchWriteItemRequest()
                curSubRequest.RequestBuilder = gir.deepCopyRequestBuilder()
                curSubRequest.ReturnConsumedCapacity = gir.ReturnConsumedCapacity
                curSubRequest.ReturnItemCollectionMetrics = gir.ReturnItemCollectionMetrics
                itemsInSet = 0
            }
            if reqs[i].DeleteRequest != nil {
                curSubRequest.AddDeleteRequest(table, reqs[i].DeleteRequest.Key)
            } else if reqs[i].PutRequest != nil {
                curSubRequest.AddPutRequest(table, reqs[i].PutRequest.Item)
            }
            itemsInSet ++
            if itemsInSet >= 25 {
                resp, err := curSubRequest.RequestIncludingUnprocessed()
                if err != nil {
                    return responses, err
                }
                if len(responses) == cap(responses) {
                    newItems := make([]*BatchWriteItemResponse, len(responses) * 2)
                    for i := range responses {
                        newItems[i] = responses[i]
                    }
                    responses = newItems[:len(responses)]
                }
                responses = responses[:len(responses) + 1]
                responses[len(responses) - 1] = resp
                itemsInSet = 0
                curSubRequest = nil
            }
        }
    }
    if curSubRequest != nil {
        resp, err := curSubRequest.RequestIncludingUnprocessed()
        if err != nil {
            return responses, err
        }
        if len(responses) == cap(responses) {
            newItems := make([]*BatchWriteItemResponse, len(responses) * 2)
            for i := range responses {
                newItems[i] = responses[i]
            }
            responses = newItems[:len(responses)]
        }
        responses = responses[:len(responses) + 1]
        responses[len(responses) - 1] = resp
    }
    return responses, nil
}
<|endoftext|>


<commit_before>package arn

// CharacterName ...
type CharacterName struct {
	Canonical string   `json:"canonical" editable:"true"`
	English   string   `json:"english" editable:"true"`
	Japanese  string   `json:"japanese" editable:"true"`
	Synonyms  []string `json:"synonyms" editable:"true"`
}
<commit_msg>Added CharacterName.ByUser()<commit_after>package arn

// CharacterName ...
type CharacterName struct {
	Canonical string   `json:"canonical" editable:"true"`
	English   string   `json:"english" editable:"true"`
	Japanese  string   `json:"japanese" editable:"true"`
	Synonyms  []string `json:"synonyms" editable:"true"`
}

// ByUser returns the preferred name for the given user.
func (name *CharacterName) ByUser(user *User) string {
	if user == nil {
		return name.Canonical
	}

	switch user.Settings().TitleLanguage {
	case "canonical", "romaji":
		return name.Canonical
	case "english":
		if name.English == "" {
			return name.Canonical
		}

		return name.English
	case "japanese":
		if name.Japanese == "" {
			return name.Canonical
		}

		return name.Japanese
	default:
		panic("Invalid name language")
	}
}
<|endoftext|>


<commit_before>package awsutil

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

// Request represents an expected AWS API Operation.
type Request struct {
	Operation string
	Body      string
}

func (r *Request) String() string {
	body, err := formatJSON(strings.NewReader(r.Body))
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("Operation: %s\nBody: %s", r.Operation, body)
}

// Response represents a predefined response.
type Response struct {
	StatusCode int
	Body       string
}

// Handler is an http.Handler that will play back scenarios.
type Handler struct {
	scenarios map[string]Response
}

// NewHandler returns a new Handler instance.
func NewHandler(m map[Request]Response) *Handler {
	s := make(map[string]Response)

	for req, res := range m {
		s[req.String()] = res
	}

	return &Handler{
		scenarios: s,
	}
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	body, err := formatJSON(r.Body)
	if err != nil {
		panic(err)
	}

	match := Request{
		Operation: r.Header.Get("X-Amz-Target"),
		Body:      body,
	}

	if res, ok := h.scenarios[match.String()]; ok {
		w.WriteHeader(res.StatusCode)
		io.WriteString(w, res.Body)
	} else {
		fmt.Println(match.String())
		w.WriteHeader(404)
	}
}

func formatJSON(r io.Reader) (string, error) {
	var body map[string]interface{}
	if err := json.NewDecoder(r).Decode(&body); err != nil {
		return "", err
	}

	raw, err := json.MarshalIndent(&body, "", "  ")
	if err != nil {
		return "", err
	}

	return string(raw), nil
}
<commit_msg>Allow awsutil.Handler to fallback to raw io unable to json parse<commit_after>package awsutil

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

// Request represents an expected AWS API Operation.
type Request struct {
	Operation string
	Body      string
}

func (r *Request) String() string {
	body, err := formatJSON(strings.NewReader(r.Body))
	if err != nil {
		body = r.Body
	}
	return fmt.Sprintf("Operation: %s\nBody: %s", r.Operation, body)
}

// Response represents a predefined response.
type Response struct {
	StatusCode int
	Body       string
}

// Handler is an http.Handler that will play back scenarios.
type Handler struct {
	scenarios map[string]Response
}

// NewHandler returns a new Handler instance.
func NewHandler(m map[Request]Response) *Handler {
	s := make(map[string]Response)

	for req, res := range m {
		s[req.String()] = res
	}

	return &Handler{
		scenarios: s,
	}
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	raw, err := ioutil.ReadAll(r.Body)
	if err != nil {
		panic(err)
	}

	body, err := formatJSON(r.Body)
	if err != nil {
		body = string(raw)
	}

	match := Request{
		Operation: r.Header.Get("X-Amz-Target"),
		Body:      body,
	}

	if res, ok := h.scenarios[match.String()]; ok {
		w.WriteHeader(res.StatusCode)
		io.WriteString(w, res.Body)
	} else {
		fmt.Println(match.String())
		w.WriteHeader(404)
	}
}

func formatJSON(r io.Reader) (string, error) {
	var body map[string]interface{}
	if err := json.NewDecoder(r).Decode(&body); err != nil {
		return "", err
	}

	raw, err := json.MarshalIndent(&body, "", "  ")
	if err != nil {
		return "", err
	}

	return string(raw), nil
}
<|endoftext|>


<commit_before>package enginetest

import (
	"fmt"
	"io"
	"sync"

	vnc "github.com/mitchellh/go-vnc"
	"github.com/taskcluster/taskcluster-worker/engines"
	"github.com/taskcluster/taskcluster-worker/runtime/ioext"
)

// The DisplayTestCase contains information sufficient to test the interactive
// display provided by a Sandbox
type DisplayTestCase struct {
	*EngineProvider
	// List of display that should be returned from Sandbox.ListDisplays(),
	// They will all be opened to ensure that they are in fact VNC connections.
	Displays []engines.Display
	// Name of a display that does not exist, it will be attempted opened to
	// check that this failure is handled gracefully.
	InvalidDisplayName string
	// Payload for the engine that will contain an interactive environment as
	// described above.
	Payload string
}

// TestListDisplays tests that listDisplays works and returns Displays
func (c *DisplayTestCase) TestListDisplays() {
	debug("## TestListDisplays")
	r := c.newRun()
	defer r.Dispose()
	r.NewSandboxBuilder(c.Payload)
	r.StartSandbox()

	// List displays
	displays, err := r.sandbox.ListDisplays()
	nilOrPanic(err, "Failed to ListDisplays")

	// Check that we has many displays as we declared
	assert(len(displays) == len(c.Displays), "Expected: ", len(c.Displays),
		" displays, but we only got: ", len(displays))

	// Check that displays are all declared
	for _, display := range displays {
		ok := false
		for _, d := range c.Displays {
			if d.Name == display.Name {
				ok = true
				// Test properties that are declared to not have empty value
				assert(d.Description == "" || d.Description == display.Description,
					"Description was declared and didn't match, got: ", display.Description)
				assert(d.Width == 0 || d.Width == display.Width,
					"Width was declared and didn't match, got: ", display.Width)
				assert(d.Height == 0 || d.Height == display.Height,
					"Height was declared and didn't match, got: ", display.Height)
			}
		}
		if !ok {
			panic(fmt.Sprintf("ListDisplays returned unexpected display: %#v", display))
		}
	}
}

// TestDisplays tests that we can connect to all Displays listed, and that the
// resolution is correct if advertized (ie. non-zero). To facilitate that
// resolution changes in the test sandbox this test will only require that the
// resolution either before or after connecting matches what is listed.
func (c *DisplayTestCase) TestDisplays() {
	debug("## TestDisplays")
	r := c.newRun()
	defer r.Dispose()
	r.NewSandboxBuilder(c.Payload)
	r.StartSandbox()

	// List displays
	displays, err := r.sandbox.ListDisplays()
	nilOrPanic(err, "Failed to ListDisplays")

	// Test each display
	resToCheckLater := make(map[string]resolution)
	for _, display := range displays {
		debug(" - Opening display: %s", display.Name)
		c, err2 := r.sandbox.OpenDisplay(display.Name)
		nilOrPanic(err2, "Failed to OpenDisplay for: ", display.Name)

		debug(" - Querying for resolution")
		res, err2 := getDisplayResolution(c)
		nilOrPanic(err2, "Failed to connect to display")
		debug(" - Got resolution, width: %d, height: %d", res.width, res.height)

		// If the actual resolution doesn't match what ListDisplays returned we
		// will check it later, running ListDisplays again to ensure we support
		// resolutions changing at least once during testing
		if res.width != display.Width || res.height != display.Height {
			resToCheckLater[display.Name] = res
		}
	}

	displays, err = r.sandbox.ListDisplays()
	nilOrPanic(err, "Failed to ListDisplays 2nd time")
	for _, display := range displays {
		// Don't check any that haven't got a resolution
		if display.Width == 0 && display.Height == 0 {
			continue
		}
		// Only check, the ones that we recorded we needed to check later
		if res, ok := resToCheckLater[display.Name]; ok {
			assert(res.width == display.Width && res.height == display.Height,
				"Resolution was defined in ListDisplays, but didn't match VNC client")
		}
	}
}

// TestInvalidDisplayName test that IpenDisplay on InvalidDisplayName is
// properly handled.
func (c *DisplayTestCase) TestInvalidDisplayName() {
	debug("## TestInvalidDisplayName")
	r := c.newRun()
	defer r.Dispose()
	r.NewSandboxBuilder(c.Payload)
	r.StartSandbox()

	conn, err := r.sandbox.OpenDisplay(c.InvalidDisplayName)
	assert(err == engines.ErrNoSuchDisplay, "Expected ErrNoSuchDisplay")
	assert(conn == nil, "Expected nil when we got error")
}

// Test runs all tests in parallel
func (c *DisplayTestCase) Test() {
	wg := sync.WaitGroup{}
	wg.Add(3)
	go func() { c.TestListDisplays(); wg.Done() }()
	go func() { c.TestDisplays(); wg.Done() }()
	go func() { c.TestInvalidDisplayName(); wg.Done() }()
	wg.Wait()
}

type resolution struct {
	width  int
	height int
}

func getDisplayResolution(c io.ReadWriteCloser) (resolution, error) {
	client, err := vnc.Client(ioext.NopConn(c), &vnc.ClientConfig{})
	if err != nil {
		return resolution{}, err
	}
	client.Close()
	return resolution{
		width:  int(client.FrameBufferWidth),
		height: int(client.FrameBufferHeight),
	}, nil
}
<commit_msg>Improved error message<commit_after>package enginetest

import (
	"fmt"
	"io"
	"sync"

	vnc "github.com/mitchellh/go-vnc"
	"github.com/taskcluster/taskcluster-worker/engines"
	"github.com/taskcluster/taskcluster-worker/runtime/ioext"
)

// The DisplayTestCase contains information sufficient to test the interactive
// display provided by a Sandbox
type DisplayTestCase struct {
	*EngineProvider
	// List of display that should be returned from Sandbox.ListDisplays(),
	// They will all be opened to ensure that they are in fact VNC connections.
	Displays []engines.Display
	// Name of a display that does not exist, it will be attempted opened to
	// check that this failure is handled gracefully.
	InvalidDisplayName string
	// Payload for the engine that will contain an interactive environment as
	// described above.
	Payload string
}

// TestListDisplays tests that listDisplays works and returns Displays
func (c *DisplayTestCase) TestListDisplays() {
	debug("## TestListDisplays")
	r := c.newRun()
	defer r.Dispose()
	r.NewSandboxBuilder(c.Payload)
	r.StartSandbox()

	// List displays
	displays, err := r.sandbox.ListDisplays()
	nilOrPanic(err, "Failed to ListDisplays")

	// Check that we has many displays as we declared
	assert(len(displays) == len(c.Displays), "Expected: ", len(c.Displays),
		" displays, but we only got: ", len(displays))

	// Check that displays are all declared
	for _, display := range displays {
		ok := false
		for _, d := range c.Displays {
			if d.Name == display.Name {
				ok = true
				// Test properties that are declared to not have empty value
				assert(d.Description == "" || d.Description == display.Description,
					"Description was declared and didn't match, got: ", display.Description)
				assert(d.Width == 0 || d.Width == display.Width,
					"Width was declared and didn't match, got: ", display.Width)
				assert(d.Height == 0 || d.Height == display.Height,
					"Height was declared and didn't match, got: ", display.Height)
			}
		}
		if !ok {
			panic(fmt.Sprintf("ListDisplays returned unexpected display: %#v", display))
		}
	}
}

// TestDisplays tests that we can connect to all Displays listed, and that the
// resolution is correct if advertized (ie. non-zero). To facilitate that
// resolution changes in the test sandbox this test will only require that the
// resolution either before or after connecting matches what is listed.
func (c *DisplayTestCase) TestDisplays() {
	debug("## TestDisplays")
	r := c.newRun()
	defer r.Dispose()
	r.NewSandboxBuilder(c.Payload)
	r.StartSandbox()

	// List displays
	displays, err := r.sandbox.ListDisplays()
	nilOrPanic(err, "Failed to ListDisplays")

	// Test each display
	resToCheckLater := make(map[string]resolution)
	for _, display := range displays {
		debug(" - Opening display: %s", display.Name)
		c, err2 := r.sandbox.OpenDisplay(display.Name)
		nilOrPanic(err2, "Failed to OpenDisplay for: ", display.Name)

		debug(" - Querying for resolution")
		res, err2 := getDisplayResolution(c)
		nilOrPanic(err2, "Failed to connect to display, err: ")
		debug(" - Got resolution, width: %d, height: %d", res.width, res.height)

		// If the actual resolution doesn't match what ListDisplays returned we
		// will check it later, running ListDisplays again to ensure we support
		// resolutions changing at least once during testing
		if res.width != display.Width || res.height != display.Height {
			resToCheckLater[display.Name] = res
		}
	}

	displays, err = r.sandbox.ListDisplays()
	nilOrPanic(err, "Failed to ListDisplays 2nd time")
	for _, display := range displays {
		// Don't check any that haven't got a resolution
		if display.Width == 0 && display.Height == 0 {
			continue
		}
		// Only check, the ones that we recorded we needed to check later
		if res, ok := resToCheckLater[display.Name]; ok {
			assert(res.width == display.Width && res.height == display.Height,
				"Resolution was defined in ListDisplays, but didn't match VNC client")
		}
	}
}

// TestInvalidDisplayName test that IpenDisplay on InvalidDisplayName is
// properly handled.
func (c *DisplayTestCase) TestInvalidDisplayName() {
	debug("## TestInvalidDisplayName")
	r := c.newRun()
	defer r.Dispose()
	r.NewSandboxBuilder(c.Payload)
	r.StartSandbox()

	conn, err := r.sandbox.OpenDisplay(c.InvalidDisplayName)
	assert(err == engines.ErrNoSuchDisplay, "Expected ErrNoSuchDisplay")
	assert(conn == nil, "Expected nil when we got error")
}

// Test runs all tests in parallel
func (c *DisplayTestCase) Test() {
	wg := sync.WaitGroup{}
	wg.Add(3)
	go func() { c.TestListDisplays(); wg.Done() }()
	go func() { c.TestDisplays(); wg.Done() }()
	go func() { c.TestInvalidDisplayName(); wg.Done() }()
	wg.Wait()
}

type resolution struct {
	width  int
	height int
}

func getDisplayResolution(c io.ReadWriteCloser) (resolution, error) {
	client, err := vnc.Client(ioext.NopConn(c), &vnc.ClientConfig{})
	if err != nil {
		return resolution{}, err
	}
	client.Close()
	return resolution{
		width:  int(client.FrameBufferWidth),
		height: int(client.FrameBufferHeight),
	}, nil
}
<|endoftext|>


<commit_before>package logstash

import (
	"crypto/tls"
	"fmt"
	"math/rand"
	"net"
	"strings"
	"time"

	"github.com/elastic/beats/libbeat/logp"
)

// TransportClient interfaces adds (re-)connect support to net.Conn.
type TransportClient interface {
	net.Conn
	Connect(timeout time.Duration) error
	IsConnected() bool
}

type tcpClient struct {
	hostport  string
	connected bool
	conn      net.Conn
}

type tlsClient struct {
	tcpClient
	tls tls.Config
}

func newTCPClient(host string, defaultPort int) (*tcpClient, error) {
	return &tcpClient{hostport: fullAddress(host, defaultPort)}, nil
}

func (c *tcpClient) Connect(timeout time.Duration) error {
	if c.IsConnected() {
		_ = c.Close()
	}

	host, port, err := net.SplitHostPort(c.hostport)
	if err != nil {
		return err
	}

	// TODO: address lookup copied from logstash-forwarded. Really required?
	addresses, err := net.LookupHost(host)
	c.conn = nil
	if err != nil {
		logp.Warn("DNS lookup failure \"%s\": %s", host, err)
		return err
	}

	// connect to random address
	// Use randomization on DNS reported addresses combined with timeout and ACKs
	// to spread potential load when starting up large number of beats using
	// lumberjack.
	//
	// RFCs discussing reasons for ignoring order of DNS records:
	// http://www.ietf.org/rfc/rfc3484.txt
	// > is specific to locality-based address selection for multiple dns
	// > records, but exists as prior art in "Choose some different ordering for
	// > the dns records" done by a client
	//
	// https://tools.ietf.org/html/rfc1794
	// > "Clients, of course, may reorder this information" - with respect to
	// > handling order of dns records in a response. address :=
	address := addresses[rand.Int()%len(addresses)]
	addressport := net.JoinHostPort(address, port)
	conn, err := net.DialTimeout("tcp", addressport, timeout)
	if err != nil {
		return err
	}

	c.conn = conn
	c.connected = true
	return nil
}

func (c *tcpClient) IsConnected() bool {
	return c.connected
}

func (c *tcpClient) Close() error {
	if c.connected {
		debug("closing")
		c.connected = false
		return c.conn.Close()
	}
	return nil
}

func (c *tcpClient) Read(b []byte) (int, error) {
	if !c.connected {
		return 0, ErrNotConnected
	}

	debug("try read: %v", len(b))
	n, err := c.conn.Read(b)
	return n, c.handleError(err)
}

func (c *tcpClient) Write(b []byte) (int, error) {
	if !c.connected {
		return 0, ErrNotConnected
	}

	n, err := c.conn.Write(b)
	return n, c.handleError(err)
}

func (c *tcpClient) LocalAddr() net.Addr {
	if !c.connected {
		return nil
	}
	return c.conn.LocalAddr()
}

func (c *tcpClient) RemoteAddr() net.Addr {
	if !c.connected {
		return nil
	}
	return c.conn.RemoteAddr()
}

func (c *tcpClient) SetDeadline(t time.Time) error {
	if !c.connected {
		return ErrNotConnected
	}
	err := c.conn.SetDeadline(t)
	return c.handleError(err)
}

func (c *tcpClient) SetReadDeadline(t time.Time) error {
	if !c.connected {
		return ErrNotConnected
	}
	err := c.conn.SetReadDeadline(t)
	return c.handleError(err)
}

func (c *tcpClient) SetWriteDeadline(t time.Time) error {
	if !c.connected {
		return ErrNotConnected
	}
	err := c.conn.SetWriteDeadline(t)
	return c.handleError(err)
}

func (c *tcpClient) handleError(err error) error {
	if err != nil {
		debug("handle error: %v", err)

		if nerr, ok := err.(net.Error); !(ok && (nerr.Temporary() || nerr.Timeout())) {
			c.Close()
		}
	}
	return err
}

func newTLSClient(host string, defaultPort int, tls *tls.Config) (*tlsClient, error) {
	c := tlsClient{}
	c.hostport = fullAddress(host, defaultPort)
	c.tls = *tls
	return &c, nil
}

func (c *tlsClient) Connect(timeout time.Duration) error {
	host, _, err := net.SplitHostPort(c.hostport)
	if err != nil {
		return err
	}

	if err := c.tcpClient.Connect(timeout); err != nil {
		return c.onFail(err)
	}

	tlsconfig := c.tls
	tlsconfig.ServerName = host
	socket := tls.Client(c.conn, &tlsconfig)
	if err := socket.SetDeadline(time.Now().Add(timeout)); err != nil {
		_ = socket.Close()
		return c.onFail(err)
	}
	if err := socket.Handshake(); err != nil {
		_ = socket.Close()
		return c.onFail(err)
	}

	c.conn = socket
	c.connected = true
	return nil
}

func (c *tlsClient) onFail(err error) error {
	logp.Err("SSL client failed to connect with: %v", err)
	c.conn = nil
	c.connected = false
	return err
}

func fullAddress(host string, defaultPort int) string {
	if _, _, err := net.SplitHostPort(host); err == nil {
		return host
	}

	idx := strings.Index(host, ":")
	if idx >= 0 {
		// IPv6 address detected
		return fmt.Sprintf("[%v]:%v", host, defaultPort)
	}
	return fmt.Sprintf("%v:%v", host, defaultPort)
}
<commit_msg>use mutex to protect connection state<commit_after>package logstash

import (
	"crypto/tls"
	"fmt"
	"math/rand"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/elastic/beats/libbeat/logp"
)

// TransportClient interfaces adds (re-)connect support to net.Conn.
type TransportClient interface {
	net.Conn
	Connect(timeout time.Duration) error
	IsConnected() bool
}

type tcpClient struct {
	hostport  string
	connected bool
	conn      net.Conn
	mutex     sync.Mutex
}

type tlsClient struct {
	tcpClient
	tls *tls.Config
}

func newTCPClient(host string, defaultPort int) (*tcpClient, error) {
	return &tcpClient{hostport: fullAddress(host, defaultPort)}, nil
}

func (c *tcpClient) Connect(timeout time.Duration) error {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	return c.doConnect(timeout)
}

func (c *tcpClient) doConnect(timeout time.Duration) error {
	if c.connected {
		c.connected = false
		_ = c.conn.Close()
	}

	host, port, err := net.SplitHostPort(c.hostport)
	if err != nil {
		return err
	}

	// TODO: address lookup copied from logstash-forwarded. Really required?
	addresses, err := net.LookupHost(host)
	c.conn = nil
	if err != nil {
		logp.Warn("DNS lookup failure \"%s\": %s", host, err)
		return err
	}

	// connect to random address
	// Use randomization on DNS reported addresses combined with timeout and ACKs
	// to spread potential load when starting up large number of beats using
	// lumberjack.
	//
	// RFCs discussing reasons for ignoring order of DNS records:
	// http://www.ietf.org/rfc/rfc3484.txt
	// > is specific to locality-based address selection for multiple dns
	// > records, but exists as prior art in "Choose some different ordering for
	// > the dns records" done by a client
	//
	// https://tools.ietf.org/html/rfc1794
	// > "Clients, of course, may reorder this information" - with respect to
	// > handling order of dns records in a response. address :=
	address := addresses[rand.Int()%len(addresses)]
	addressport := net.JoinHostPort(address, port)
	conn, err := net.DialTimeout("tcp", addressport, timeout)
	if err != nil {
		return err
	}

	c.conn = conn
	c.connected = true
	return nil
}

func (c *tcpClient) IsConnected() bool {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	return c.connected
}

func (c *tcpClient) Close() error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if c.connected {
		debug("closing")
		c.connected = false
		return c.conn.Close()
	}
	return nil
}

func (c *tcpClient) getConn() net.Conn {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if !c.connected {
		return nil
	}
	return c.conn
}

func (c *tcpClient) Read(b []byte) (int, error) {
	conn := c.getConn()
	if conn == nil {
		return 0, ErrNotConnected
	}

	debug("try read: %v", len(b))
	n, err := conn.Read(b)
	return n, c.handleError(err)
}

func (c *tcpClient) Write(b []byte) (int, error) {
	conn := c.getConn()
	if conn == nil {
		return 0, ErrNotConnected
	}

	n, err := c.conn.Write(b)
	return n, c.handleError(err)
}

func (c *tcpClient) LocalAddr() net.Addr {
	conn := c.getConn()
	if conn != nil {
		return c.conn.LocalAddr()
	}
	return nil

}

func (c *tcpClient) RemoteAddr() net.Addr {
	conn := c.getConn()
	if conn != nil {
		return c.conn.LocalAddr()
	}
	return nil
}

func (c *tcpClient) SetDeadline(t time.Time) error {
	conn := c.getConn()
	if conn == nil {
		return ErrNotConnected
	}

	err := conn.SetDeadline(t)
	return c.handleError(err)
}

func (c *tcpClient) SetReadDeadline(t time.Time) error {
	conn := c.getConn()
	if conn == nil {
		return ErrNotConnected
	}

	err := conn.SetReadDeadline(t)
	return c.handleError(err)
}

func (c *tcpClient) SetWriteDeadline(t time.Time) error {
	conn := c.getConn()
	if conn == nil {
		return ErrNotConnected
	}

	err := conn.SetWriteDeadline(t)
	return c.handleError(err)
}

func (c *tcpClient) handleError(err error) error {
	if err != nil {
		debug("handle error: %v", err)

		if nerr, ok := err.(net.Error); !(ok && (nerr.Temporary() || nerr.Timeout())) {
			c.Close()
		}
	}
	return err
}

func newTLSClient(host string, defaultPort int, tls *tls.Config) (*tlsClient, error) {
	c := tlsClient{}
	c.hostport = fullAddress(host, defaultPort)
	c.tls = tls
	return &c, nil
}

func (c *tlsClient) Connect(timeout time.Duration) error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	host, _, err := net.SplitHostPort(c.hostport)
	if err != nil {
		return err
	}

	if err := c.tcpClient.doConnect(timeout); err != nil {
		return c.onFail(err)
	}

	tlsconfig := c.tls
	tlsconfig.ServerName = host
	socket := tls.Client(c.conn, tlsconfig)
	if err := socket.SetDeadline(time.Now().Add(timeout)); err != nil {
		_ = socket.Close()
		return c.onFail(err)
	}
	if err := socket.Handshake(); err != nil {
		_ = socket.Close()
		return c.onFail(err)
	}

	c.conn = socket
	c.connected = true
	return nil
}

func (c *tlsClient) onFail(err error) error {
	logp.Err("SSL client failed to connect with: %v", err)
	c.conn = nil
	c.connected = false
	return err
}

func fullAddress(host string, defaultPort int) string {
	if _, _, err := net.SplitHostPort(host); err == nil {
		return host
	}

	idx := strings.Index(host, ":")
	if idx >= 0 {
		// IPv6 address detected
		return fmt.Sprintf("[%v]:%v", host, defaultPort)
	}
	return fmt.Sprintf("%v:%v", host, defaultPort)
}
<|endoftext|>


<commit_before>package validation

import (
	"DNA/core/ledger"
	tx "DNA/core/transaction"
	. "DNA/errors"
	"errors"
	"fmt"
)

func VerifyBlock(block *ledger.Block, ld *ledger.Ledger, completely bool) error {
	if block.Blockdata.Height == 0 {
		return nil
	}
	err := VerifyBlockData(block.Blockdata, ld)
	if err != nil {
		return err
	}

	flag, err := VerifySignableData(block)
	if flag && err == nil {
		return nil
	} else {
		return err
	}

	if block.Transactions == nil {
		return errors.New(fmt.Sprintf("No Transactions Exist in Block."))
	}
	if block.Transactions[0].TxType != tx.BookKeeping {
		return errors.New(fmt.Sprintf("Blockdata Verify failed first Transacion in block is not BookKeeping type."))
	}
	for index, v := range block.Transactions {
		if v.TxType == tx.BookKeeping && index != 0 {
			return errors.New(fmt.Sprintf("This Block Has BookKeeping transaction after first transaction in block."))
		}
	}

	//verfiy block's transactions
	if completely {
	/*
		mineraddress, err := ledger.GetMinerAddress(ld.Blockchain.GetMinersByTXs(block.Transactions))
		if err != nil {
			return errors.New(fmt.Sprintf("GetMinerAddress Failed."))
		}
		if block.Blockdata.NextMiner != mineraddress {
			return errors.New(fmt.Sprintf("Miner is not validate."))
		}
	*/
		//TODO: NextMiner Check.
		for _, txVerify := range block.Transactions {
			transpool := []*tx.Transaction{}
			for _, tx := range block.Transactions {
				if tx.Hash() != txVerify.Hash() {
					transpool = append(transpool, tx)
				}
			}
			err := VerifyTransaction(txVerify, ld, transpool)
			if err != nil {
				return errors.New(fmt.Sprintf("The Input is exist in serval transaction in one block."))
			}
		}
	}

	return nil
}

func VerifyHeader(bd *ledger.Header, ledger *ledger.Ledger) error {
	return VerifyBlockData( bd.Blockdata, ledger )
}

func VerifyBlockData(bd *ledger.Blockdata, ledger *ledger.Ledger) error {
	if bd.Height == 0 {
		return nil
	}

	prevHeader, err := ledger.Blockchain.GetHeader(bd.PrevBlockHash)
	if err != nil {
		return NewDetailErr(err, ErrNoCode, "[BlockValidator], Cannnot find prevHeader..")
	}
	if prevHeader == nil {
		return NewDetailErr(err, ErrNoCode, "[BlockValidator], Cannnot find previous block.")
	}

	if prevHeader.Blockdata.Height+1 != bd.Height {
		return NewDetailErr(err, ErrNoCode, "[BlockValidator], block height is incorrect.")
	}

	if prevHeader.Blockdata.Timestamp >= bd.Timestamp {
		return NewDetailErr(err, ErrNoCode, "[BlockValidator], block timestamp is incorrect.")
	}

	return nil
}
<commit_msg>when VerifySignableData ok should not return(complete the verify), otherwise it will skip the transaction verify. append has a large performance loss,so changed to only 2 append process.<commit_after>package validation

import (
	"DNA/core/ledger"
	tx "DNA/core/transaction"
	. "DNA/errors"
	"errors"
	"fmt"
)

func VerifyBlock(block *ledger.Block, ld *ledger.Ledger, completely bool) error {
	if block.Blockdata.Height == 0 {
		return nil
	}
	err := VerifyBlockData(block.Blockdata, ld)
	if err != nil {
		return err
	}

	flag, err := VerifySignableData(block)
	if flag == false || err != nil {
		return err
	}

	if block.Transactions == nil {
		return errors.New(fmt.Sprintf("No Transactions Exist in Block."))
	}
	if block.Transactions[0].TxType != tx.BookKeeping {
		return errors.New(fmt.Sprintf("Blockdata Verify failed first Transacion in block is not BookKeeping type."))
	}
	for index, v := range block.Transactions {
		if v.TxType == tx.BookKeeping && index != 0 {
			return errors.New(fmt.Sprintf("This Block Has BookKeeping transaction after first transaction in block."))
		}
	}

	//verfiy block's transactions
	if completely {
		/*
			mineraddress, err := ledger.GetMinerAddress(ld.Blockchain.GetMinersByTXs(block.Transactions))
			if err != nil {
				return errors.New(fmt.Sprintf("GetMinerAddress Failed."))
			}
			if block.Blockdata.NextMiner != mineraddress {
				return errors.New(fmt.Sprintf("Miner is not validate."))
			}
		*/
		//TODO: NextMiner Check.
		for num, txVerify := range block.Transactions {
			transpool := []*tx.Transaction{}
			transpool = append(transpool, block.Transactions[:num]...)
			transpool = append(transpool, block.Transactions[num+1:]...)
			err := VerifyTransaction(txVerify, ld, transpool)
			if err != nil {
				return errors.New(fmt.Sprintf("The Input is exist in serval transaction in one block."))
			}
		}
	}

	return nil
}

func VerifyHeader(bd *ledger.Header, ledger *ledger.Ledger) error {
	return VerifyBlockData(bd.Blockdata, ledger)
}

func VerifyBlockData(bd *ledger.Blockdata, ledger *ledger.Ledger) error {
	if bd.Height == 0 {
		return nil
	}

	prevHeader, err := ledger.Blockchain.GetHeader(bd.PrevBlockHash)
	if err != nil {
		return NewDetailErr(err, ErrNoCode, "[BlockValidator], Cannnot find prevHeader..")
	}
	if prevHeader == nil {
		return NewDetailErr(err, ErrNoCode, "[BlockValidator], Cannnot find previous block.")
	}

	if prevHeader.Blockdata.Height+1 != bd.Height {
		return NewDetailErr(err, ErrNoCode, "[BlockValidator], block height is incorrect.")
	}

	if prevHeader.Blockdata.Timestamp >= bd.Timestamp {
		return NewDetailErr(err, ErrNoCode, "[BlockValidator], block timestamp is incorrect.")
	}

	return nil
}
<|endoftext|>


<commit_before>package overlay

import (
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/hashicorp/serf/serf"
)

type ovNotify struct {
	action string
	eid    string
	nid    string
}

type logWriter struct{}

func (l *logWriter) Write(p []byte) (int, error) {
	str := string(p)

	switch {
	case strings.Contains(str, "[WARN]"):
		logrus.Warn(str)
	case strings.Contains(str, "[DEBUG]"):
		logrus.Debug(str)
	case strings.Contains(str, "[INFO]"):
		logrus.Info(str)
	case strings.Contains(str, "[ERR]"):
		logrus.Error(str)
	}

	return len(p), nil
}

func (d *driver) serfInit() error {
	var err error

	config := serf.DefaultConfig()
	config.Init()
	config.MemberlistConfig.BindAddr = d.bindAddress

	d.eventCh = make(chan serf.Event, 4)
	config.EventCh = d.eventCh
	config.UserCoalescePeriod = 1 * time.Second
	config.UserQuiescentPeriod = 50 * time.Millisecond

	config.LogOutput = &logWriter{}
	config.MemberlistConfig.LogOutput = config.LogOutput

	s, err := serf.Create(config)
	if err != nil {
		return fmt.Errorf("failed to create cluster node: %v", err)
	}
	defer func() {
		if err != nil {
			s.Shutdown()
		}
	}()

	d.serfInstance = s

	d.notifyCh = make(chan ovNotify)
	d.exitCh = make(chan chan struct{})

	go d.startSerfLoop(d.eventCh, d.notifyCh, d.exitCh)
	return nil
}

func (d *driver) serfJoin(neighIP string) error {
	if neighIP == "" {
		return fmt.Errorf("no neighbor to join")
	}
	if _, err := d.serfInstance.Join([]string{neighIP}, false); err != nil {
		return fmt.Errorf("Failed to join the cluster at neigh IP %s: %v",
			neighIP, err)
	}
	return nil
}

func (d *driver) notifyEvent(event ovNotify) {
	n := d.network(event.nid)
	ep := n.endpoint(event.eid)

	ePayload := fmt.Sprintf("%s %s %s %s", event.action, ep.addr.IP.String(),
		net.IP(ep.addr.Mask).String(), ep.mac.String())
	eName := fmt.Sprintf("jl %s %s %s", d.serfInstance.LocalMember().Addr.String(),
		event.nid, event.eid)

	if err := d.serfInstance.UserEvent(eName, []byte(ePayload), true); err != nil {
		logrus.Errorf("Sending user event failed: %v\n", err)
	}
}

func (d *driver) processEvent(u serf.UserEvent) {
	logrus.Debugf("Received user event name:%s, payload:%s\n", u.Name,
		string(u.Payload))

	var dummy, action, vtepStr, nid, eid, ipStr, maskStr, macStr string
	if _, err := fmt.Sscan(u.Name, &dummy, &vtepStr, &nid, &eid); err != nil {
		fmt.Printf("Failed to scan name string: %v\n", err)
	}

	if _, err := fmt.Sscan(string(u.Payload), &action,
		&ipStr, &maskStr, &macStr); err != nil {
		fmt.Printf("Failed to scan value string: %v\n", err)
	}

	logrus.Debugf("Parsed data = %s/%s/%s/%s/%s/%s\n", nid, eid, vtepStr, ipStr, maskStr, macStr)

	mac, err := net.ParseMAC(macStr)
	if err != nil {
		logrus.Errorf("Failed to parse mac: %v\n", err)
	}

	if d.serfInstance.LocalMember().Addr.String() == vtepStr {
		return
	}

	switch action {
	case "join":
		if err := d.peerAdd(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac,
			net.ParseIP(vtepStr), true); err != nil {
			logrus.Errorf("Peer add failed in the driver: %v\n", err)
		}
	case "leave":
		if err := d.peerDelete(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac,
			net.ParseIP(vtepStr), true); err != nil {
			logrus.Errorf("Peer delete failed in the driver: %v\n", err)
		}
	}
}

func (d *driver) processQuery(q *serf.Query) {
	logrus.Debugf("Received query name:%s, payload:%s\n", q.Name,
		string(q.Payload))

	var nid, ipStr string
	if _, err := fmt.Sscan(string(q.Payload), &nid, &ipStr); err != nil {
		fmt.Printf("Failed to scan query payload string: %v\n", err)
	}

	peerMac, peerIPMask, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr))
	if err != nil {
		return
	}

	q.Respond([]byte(fmt.Sprintf("%s %s %s", peerMac.String(), net.IP(peerIPMask).String(), vtep.String())))
}

func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
	qPayload := fmt.Sprintf("%s %s", string(nid), peerIP.String())
	resp, err := d.serfInstance.Query("peerlookup", []byte(qPayload), nil)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("resolving peer by querying the cluster failed: %v", err)
	}

	respCh := resp.ResponseCh()
	select {
	case r := <-respCh:
		var macStr, maskStr, vtepStr string
		if _, err := fmt.Sscan(string(r.Payload), &macStr, &maskStr, &vtepStr); err != nil {
			return nil, nil, nil, fmt.Errorf("bad response %q for the resolve query: %v", string(r.Payload), err)
		}

		mac, err := net.ParseMAC(macStr)
		if err != nil {
			return nil, nil, nil, fmt.Errorf("failed to parse mac: %v", err)
		}

		return mac, net.IPMask(net.ParseIP(maskStr).To4()), net.ParseIP(vtepStr), nil

	case <-time.After(time.Second):
		return nil, nil, nil, fmt.Errorf("timed out resolving peer by querying the cluster")
	}
}

func (d *driver) startSerfLoop(eventCh chan serf.Event, notifyCh chan ovNotify,
	exitCh chan chan struct{}) {

	for {
		select {
		case notify, ok := <-notifyCh:
			if !ok {
				break
			}

			d.notifyEvent(notify)
		case ch, ok := <-exitCh:
			if !ok {
				break
			}

			if err := d.serfInstance.Leave(); err != nil {
				logrus.Errorf("failed leaving the cluster: %v\n", err)
			}

			d.serfInstance.Shutdown()
			close(ch)
			return
		case e, ok := <-eventCh:
			if !ok {
				break
			}

			if e.EventType() == serf.EventQuery {
				d.processQuery(e.(*serf.Query))
				break
			}

			u, ok := e.(serf.UserEvent)
			if !ok {
				break
			}
			d.processEvent(u)
		}
	}
}

func (d *driver) isSerfAlive() bool {
	d.Lock()
	serfInstance := d.serfInstance
	d.Unlock()
	if serfInstance == nil || serfInstance.State() != serf.SerfAlive {
		return false
	}
	return true
}
<commit_msg>Do not attempt serf query when not initialized<commit_after>package overlay

import (
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/hashicorp/serf/serf"
)

type ovNotify struct {
	action string
	eid    string
	nid    string
}

type logWriter struct{}

func (l *logWriter) Write(p []byte) (int, error) {
	str := string(p)

	switch {
	case strings.Contains(str, "[WARN]"):
		logrus.Warn(str)
	case strings.Contains(str, "[DEBUG]"):
		logrus.Debug(str)
	case strings.Contains(str, "[INFO]"):
		logrus.Info(str)
	case strings.Contains(str, "[ERR]"):
		logrus.Error(str)
	}

	return len(p), nil
}

func (d *driver) serfInit() error {
	var err error

	config := serf.DefaultConfig()
	config.Init()
	config.MemberlistConfig.BindAddr = d.bindAddress

	d.eventCh = make(chan serf.Event, 4)
	config.EventCh = d.eventCh
	config.UserCoalescePeriod = 1 * time.Second
	config.UserQuiescentPeriod = 50 * time.Millisecond

	config.LogOutput = &logWriter{}
	config.MemberlistConfig.LogOutput = config.LogOutput

	s, err := serf.Create(config)
	if err != nil {
		return fmt.Errorf("failed to create cluster node: %v", err)
	}
	defer func() {
		if err != nil {
			s.Shutdown()
		}
	}()

	d.serfInstance = s

	d.notifyCh = make(chan ovNotify)
	d.exitCh = make(chan chan struct{})

	go d.startSerfLoop(d.eventCh, d.notifyCh, d.exitCh)
	return nil
}

func (d *driver) serfJoin(neighIP string) error {
	if neighIP == "" {
		return fmt.Errorf("no neighbor to join")
	}
	if _, err := d.serfInstance.Join([]string{neighIP}, false); err != nil {
		return fmt.Errorf("Failed to join the cluster at neigh IP %s: %v",
			neighIP, err)
	}
	return nil
}

func (d *driver) notifyEvent(event ovNotify) {
	n := d.network(event.nid)
	ep := n.endpoint(event.eid)

	ePayload := fmt.Sprintf("%s %s %s %s", event.action, ep.addr.IP.String(),
		net.IP(ep.addr.Mask).String(), ep.mac.String())
	eName := fmt.Sprintf("jl %s %s %s", d.serfInstance.LocalMember().Addr.String(),
		event.nid, event.eid)

	if err := d.serfInstance.UserEvent(eName, []byte(ePayload), true); err != nil {
		logrus.Errorf("Sending user event failed: %v\n", err)
	}
}

func (d *driver) processEvent(u serf.UserEvent) {
	logrus.Debugf("Received user event name:%s, payload:%s\n", u.Name,
		string(u.Payload))

	var dummy, action, vtepStr, nid, eid, ipStr, maskStr, macStr string
	if _, err := fmt.Sscan(u.Name, &dummy, &vtepStr, &nid, &eid); err != nil {
		fmt.Printf("Failed to scan name string: %v\n", err)
	}

	if _, err := fmt.Sscan(string(u.Payload), &action,
		&ipStr, &maskStr, &macStr); err != nil {
		fmt.Printf("Failed to scan value string: %v\n", err)
	}

	logrus.Debugf("Parsed data = %s/%s/%s/%s/%s/%s\n", nid, eid, vtepStr, ipStr, maskStr, macStr)

	mac, err := net.ParseMAC(macStr)
	if err != nil {
		logrus.Errorf("Failed to parse mac: %v\n", err)
	}

	if d.serfInstance.LocalMember().Addr.String() == vtepStr {
		return
	}

	switch action {
	case "join":
		if err := d.peerAdd(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac,
			net.ParseIP(vtepStr), true); err != nil {
			logrus.Errorf("Peer add failed in the driver: %v\n", err)
		}
	case "leave":
		if err := d.peerDelete(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac,
			net.ParseIP(vtepStr), true); err != nil {
			logrus.Errorf("Peer delete failed in the driver: %v\n", err)
		}
	}
}

func (d *driver) processQuery(q *serf.Query) {
	logrus.Debugf("Received query name:%s, payload:%s\n", q.Name,
		string(q.Payload))

	var nid, ipStr string
	if _, err := fmt.Sscan(string(q.Payload), &nid, &ipStr); err != nil {
		fmt.Printf("Failed to scan query payload string: %v\n", err)
	}

	peerMac, peerIPMask, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr))
	if err != nil {
		return
	}

	q.Respond([]byte(fmt.Sprintf("%s %s %s", peerMac.String(), net.IP(peerIPMask).String(), vtep.String())))
}

func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
	if d.serfInstance == nil {
		return nil, nil, nil, fmt.Errorf("could not resolve peer: serf instance not initialized")
	}

	qPayload := fmt.Sprintf("%s %s", string(nid), peerIP.String())
	resp, err := d.serfInstance.Query("peerlookup", []byte(qPayload), nil)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("resolving peer by querying the cluster failed: %v", err)
	}

	respCh := resp.ResponseCh()
	select {
	case r := <-respCh:
		var macStr, maskStr, vtepStr string
		if _, err := fmt.Sscan(string(r.Payload), &macStr, &maskStr, &vtepStr); err != nil {
			return nil, nil, nil, fmt.Errorf("bad response %q for the resolve query: %v", string(r.Payload), err)
		}

		mac, err := net.ParseMAC(macStr)
		if err != nil {
			return nil, nil, nil, fmt.Errorf("failed to parse mac: %v", err)
		}

		return mac, net.IPMask(net.ParseIP(maskStr).To4()), net.ParseIP(vtepStr), nil

	case <-time.After(time.Second):
		return nil, nil, nil, fmt.Errorf("timed out resolving peer by querying the cluster")
	}
}

func (d *driver) startSerfLoop(eventCh chan serf.Event, notifyCh chan ovNotify,
	exitCh chan chan struct{}) {

	for {
		select {
		case notify, ok := <-notifyCh:
			if !ok {
				break
			}

			d.notifyEvent(notify)
		case ch, ok := <-exitCh:
			if !ok {
				break
			}

			if err := d.serfInstance.Leave(); err != nil {
				logrus.Errorf("failed leaving the cluster: %v\n", err)
			}

			d.serfInstance.Shutdown()
			close(ch)
			return
		case e, ok := <-eventCh:
			if !ok {
				break
			}

			if e.EventType() == serf.EventQuery {
				d.processQuery(e.(*serf.Query))
				break
			}

			u, ok := e.(serf.UserEvent)
			if !ok {
				break
			}
			d.processEvent(u)
		}
	}
}

func (d *driver) isSerfAlive() bool {
	d.Lock()
	serfInstance := d.serfInstance
	d.Unlock()
	if serfInstance == nil || serfInstance.State() != serf.SerfAlive {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>// errstr.go -- Error strings when there is no strerror_r.

// Copyright 2011 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.

package syscall

import (
	"sync"
	"unsafe"
)

//sysnb	strerror(errnum int) *byte
//strerror(errnum int) *byte

var errstr_lock sync.Mutex

func Errstr(errno int) string {
	errstr_lock.Lock()

	bp := libc_strerror(errno)
	b := (*[1000]byte)(unsafe.Pointer(bp))
	i := 0
	for b[i] != 0 {
		i++
	}
	s := string(b[:i])

	errstr_lock.Unlock()

	return s
}
<commit_msg>syscall: Fix Errstr on systems without strerror_r.<commit_after>// errstr.go -- Error strings when there is no strerror_r.

// Copyright 2011 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.

package syscall

import (
	"sync"
	"unsafe"
)

//sysnb	strerror(errnum int) (buf *byte)
//strerror(errnum int) *byte

var errstr_lock sync.Mutex

func Errstr(errno int) string {
	errstr_lock.Lock()

	bp := strerror(errno)
	b := (*[1000]byte)(unsafe.Pointer(bp))
	i := 0
	for b[i] != 0 {
		i++
	}
	s := string(b[:i])

	errstr_lock.Unlock()

	return s
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd"
)

func NewLsCommand() cli.Command {
	return cli.Command{
		Name:  "ls",
		Usage: "retrieve a directory",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "recursive", Usage: "returns all values for key and child keys"},
		},
		Action: func(c *cli.Context) {
			handleLs(c, lsCommandFunc)
		},
	}
}

// handleLs handles a request that intends to do ls-like operations.
func handleLs(c *cli.Context, fn handlerFunc) {
	handlePrint(c, fn, printLs)
}

// printLs writes a response out in a manner similar to the `ls` command in unix.
// Non-empty directories list their contents and files list their name.
func printLs(resp *etcd.Response, format string) {
	if !resp.Node.Dir {
		fmt.Println(resp.Node.Key)
	}
	for _, node := range resp.Node.Nodes {
		rPrint(node)
	}
}

// lsCommandFunc executes the "ls" command.
func lsCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) {
	key := "/"
	if len(c.Args()) != 0 {
		key = c.Args()[0]
	}
	recursive := c.Bool("recursive")

	// Retrieve the value from the server.
	return client.Get(key, false, recursive)
}

// rPrint recursively prints out the nodes in the node structure.
func rPrint(n *etcd.Node) {
	fmt.Println(n.Key)
	for _, node := range n.Nodes {
		rPrint(node)
	}
}
<commit_msg>etcdctl: add --sort flag to ls command<commit_after>package command

import (
	"fmt"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-etcd/etcd"
)

func NewLsCommand() cli.Command {
	return cli.Command{
		Name:  "ls",
		Usage: "retrieve a directory",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "sort", Usage: "returns result in sorted order"},
			cli.BoolFlag{Name: "recursive", Usage: "returns all values for key and child keys"},
		},
		Action: func(c *cli.Context) {
			handleLs(c, lsCommandFunc)
		},
	}
}

// handleLs handles a request that intends to do ls-like operations.
func handleLs(c *cli.Context, fn handlerFunc) {
	handlePrint(c, fn, printLs)
}

// printLs writes a response out in a manner similar to the `ls` command in unix.
// Non-empty directories list their contents and files list their name.
func printLs(resp *etcd.Response, format string) {
	if !resp.Node.Dir {
		fmt.Println(resp.Node.Key)
	}
	for _, node := range resp.Node.Nodes {
		rPrint(node)
	}
}

// lsCommandFunc executes the "ls" command.
func lsCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) {
	key := "/"
	if len(c.Args()) != 0 {
		key = c.Args()[0]
	}
	recursive := c.Bool("recursive")
	sort := c.Bool("sort")

	// Retrieve the value from the server.
	return client.Get(key, sort, recursive)
}

// rPrint recursively prints out the nodes in the node structure.
func rPrint(n *etcd.Node) {
	fmt.Println(n.Key)
	for _, node := range n.Nodes {
		rPrint(node)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"sort"
	"time"

	"github.com/octo/icestat/bahn"
)

var (
	interval    = flag.Duration("interval", 10*time.Second, "Interval in which to report statistics.")
	count       = flag.Int("count", -1, "Number of iterations.")
	destination = flag.String("destination", "", "Optional destination to anticipate.")
)

type speedDistribution struct {
	data []float64
}

func (s *speedDistribution) add(kmh float64) {
	s.data = append(s.data, kmh)
	sort.Float64s(s.data)
}

func (s speedDistribution) max() float64 {
	if len(s.data) == 0 {
		return math.NaN()
	}

	return s.data[len(s.data)-1]
}

func (s speedDistribution) percentile(p float64) float64 {
	if len(s.data) == 0 {
		return math.NaN()
	}

	idx := float64(len(s.data)) * p / 100.0
	return s.data[int(idx-0.5)]
}

func (s speedDistribution) median() float64 {
	return s.percentile(50.0)
}

func (s speedDistribution) average() float64 {
	var sum float64
	var num int

	for _, speed := range s.data {
		if !math.IsNaN(speed) {
			sum += speed
			num++
		}
	}

	if num == 0 {
		return math.NaN()
	}

	return sum / float64(num)
}

func formatDuration(d time.Duration) string {
	h, m := math.Modf(d.Hours())
	m *= 60.0

	return fmt.Sprintf("%.0f:%02.0f", h, m)
}

func main() {
	var sd speedDistribution

	flag.Parse()

	for *count != 0 {
		if *count > 0 {
			*count -= 1
		}

		trip, err := bahn.TripInfo()
		if err != nil {
			log.Print("TripInfo: ", err)
			continue
		}

		s, err := bahn.StatusInfo()
		if err != nil {
			log.Print("StatusInfo: ", err)
			continue
		}

		sd.add(s.Speed)

		nextStop := trip.NextStop
		finalStop := trip.Stops[len(trip.Stops)-1]
		if *destination != "" {
			var ok bool
			finalStop, ok = trip.FindStop(*destination)

			if !ok {
				log.Printf("stop %q not found. Valid stops are:", *destination)
				for _, stop := range trip.Stops {
					log.Printf("  * %q", stop.Station)
				}
				os.Exit(1)
			}
		}

		if finalStop.Passed {
			fmt.Printf("Train has passed %v\n", finalStop)
			*count = 0
			continue
		}

		if nextStop == nil {
			fmt.Printf("Train arrived in %v\n", trip.Stops[len(trip.Stops)-1])
			*count = 0
			continue
		}

		if nextStop == nil || finalStop == nil {
			continue
		}

		fmt.Printf("%s%s to %q (via %q): "+
			"distance=%.0f(%.0f) km, "+
			"eta=%s(%s), "+
			"delay=%s(%s), "+
			"speed=%.0f/%.0f/%.0f km/h (cur/avg/max)\n",
			trip.TrainType, trip.TrainID, finalStop.Station, nextStop.Station,
			trip.DistanceTo(finalStop), trip.DistanceTo(nextStop),
			formatDuration(finalStop.ETA()), formatDuration(nextStop.ETA()),
			formatDuration(finalStop.Delay()), formatDuration(nextStop.Delay()),
			s.Speed, sd.average(), sd.max())

		if *count != 0 {
			time.Sleep(*interval)
		}
	}
}
<commit_msg>icestat: Sleep before continuing on error.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"sort"
	"time"

	"github.com/octo/icestat/bahn"
)

var (
	interval    = flag.Duration("interval", 10*time.Second, "Interval in which to report statistics.")
	count       = flag.Int("count", -1, "Number of iterations.")
	destination = flag.String("destination", "", "Optional destination to anticipate.")
)

type speedDistribution struct {
	data []float64
}

func (s *speedDistribution) add(kmh float64) {
	s.data = append(s.data, kmh)
	sort.Float64s(s.data)
}

func (s speedDistribution) max() float64 {
	if len(s.data) == 0 {
		return math.NaN()
	}

	return s.data[len(s.data)-1]
}

func (s speedDistribution) percentile(p float64) float64 {
	if len(s.data) == 0 {
		return math.NaN()
	}

	idx := float64(len(s.data)) * p / 100.0
	return s.data[int(idx-0.5)]
}

func (s speedDistribution) median() float64 {
	return s.percentile(50.0)
}

func (s speedDistribution) average() float64 {
	var sum float64
	var num int

	for _, speed := range s.data {
		if !math.IsNaN(speed) {
			sum += speed
			num++
		}
	}

	if num == 0 {
		return math.NaN()
	}

	return sum / float64(num)
}

func formatDuration(d time.Duration) string {
	h, m := math.Modf(d.Hours())
	m *= 60.0

	return fmt.Sprintf("%.0f:%02.0f", h, m)
}

func main() {
	var sd speedDistribution

	flag.Parse()

	for *count != 0 {
		if *count > 0 {
			*count -= 1
		}

		trip, err := bahn.TripInfo()
		if err != nil {
			log.Print("TripInfo: ", err)
			time.Sleep(*interval)
			continue
		}

		s, err := bahn.StatusInfo()
		if err != nil {
			log.Print("StatusInfo: ", err)
			time.Sleep(*interval)
			continue
		}

		sd.add(s.Speed)

		nextStop := trip.NextStop
		finalStop := trip.Stops[len(trip.Stops)-1]
		if *destination != "" {
			var ok bool
			finalStop, ok = trip.FindStop(*destination)

			if !ok {
				log.Printf("stop %q not found. Valid stops are:", *destination)
				for _, stop := range trip.Stops {
					log.Printf("  * %q", stop.Station)
				}
				os.Exit(1)
			}
		}

		if finalStop.Passed {
			fmt.Printf("Train has passed %v\n", finalStop)
			break
		}

		if nextStop == nil {
			fmt.Printf("Train arrived in %v\n", trip.Stops[len(trip.Stops)-1])
			break
		}

		if nextStop == nil || finalStop == nil {
			time.Sleep(*interval)
			continue
		}

		fmt.Printf("%s%s to %q (via %q): "+
			"distance=%.0f(%.0f) km, "+
			"eta=%s(%s), "+
			"delay=%s(%s), "+
			"speed=%.0f/%.0f/%.0f km/h (cur/avg/max)\n",
			trip.TrainType, trip.TrainID, finalStop.Station, nextStop.Station,
			trip.DistanceTo(finalStop), trip.DistanceTo(nextStop),
			formatDuration(finalStop.ETA()), formatDuration(nextStop.ETA()),
			formatDuration(finalStop.Delay()), formatDuration(nextStop.Delay()),
			s.Speed, sd.average(), sd.max())

		if *count != 0 {
			time.Sleep(*interval)
		}
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Nan Deng
 *
 * 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 webhook

import (
	"bytes"
	"encoding/json"
	"github.com/uniqush/uniqush-conn/rpc"
	"net"
	"net/http"
	"time"
)

type WebHook interface {
	SetURL(urls ...string)
	SetTimeout(timeout time.Duration)
	SetDefault(d int)
}

type webHook struct {
	URLs    []string
	Timeout time.Duration
	Default int
}

func (self *webHook) SetURL(urls ...string) {
	self.URLs = urls
}

func (self *webHook) SetTimeout(timeout time.Duration) {
	self.Timeout = timeout
}

func (self *webHook) SetDefault(d int) {
	self.Default = d
}

func timeoutDialler(ns time.Duration) func(net, addr string) (c net.Conn, err error) {
	return func(netw, addr string) (net.Conn, error) {
		c, err := net.Dial(netw, addr)
		if err != nil {
			return nil, err
		}
		if ns.Seconds() > 0.0 {
			c.SetDeadline(time.Now().Add(ns))
		}
		return c, nil
	}
}

func (self *webHook) post(data interface{}, out interface{}, requireOut bool) int {
	ret := self.Default
	for _, url := range self.URLs {
		status := self.postSingle(url, data, out, requireOut)
		if status == 200 {
			if out != nil {
				return status
			}
			ret = status
		}
	}
	return ret
}

func (self *webHook) postSingle(url string, data interface{}, out interface{}, requireOut bool) int {
	if len(url) == 0 || url == "none" {
		return self.Default
	}
	jdata, err := json.Marshal(data)
	if err != nil {
		return self.Default
	}
	c := http.Client{
		Transport: &http.Transport{
			Dial: timeoutDialler(self.Timeout),
		},
	}
	resp, err := c.Post(url, "application/json", bytes.NewReader(jdata))
	if err != nil {
		return self.Default
	}
	defer resp.Body.Close()

	if out != nil {
		e := json.NewDecoder(resp.Body)
		err = e.Decode(out)
		if err != nil && requireOut {
			return self.Default
		}
	}
	return resp.StatusCode
}

type loginEvent struct {
	Service  string `json:"service"`
	Username string `json:"username"`
	ConnID   string `json:"connId"`
	Addr     string `json:"addr"`
}

type LoginHandler struct {
	webHook
}

func (self *LoginHandler) OnLogin(service, username, connId, addr string) {
	self.post(&loginEvent{service, username, connId, addr}, nil, false)
}

type logoutEvent struct {
	Service  string `json:"service"`
	Username string `json:"username"`
	ConnID   string `json:"connId"`
	Addr     string `json:"addr"`
	Reason   string `json:"reason,omitempty"`
}

type LogoutHandler struct {
	webHook
}

func (self *LogoutHandler) OnLogout(service, username, connId, addr string, reason error) {
	evt := &logoutEvent{
		Service:  service,
		Username: username,
		ConnID:   connId,
		Addr:     addr,
	}

	if reason != nil {
		evt.Reason = reason.Error()
	}
	self.post(evt, nil, false)
}

type messageEvent struct {
	ConnID   string       `json:"connId"`
	Msg      *rpc.Message `json:"msg"`
	Service  string       `json:"service"`
	Username string       `json:"username"`
}

type MessageHandler struct {
	webHook
}

func (self *MessageHandler) OnMessage(service, username, connId string, msg *rpc.Message) {
	evt := &messageEvent{
		Service:  service,
		Username: username,
		ConnID:   connId,
		Msg:      msg,
	}
	self.post(evt, nil, false)
}

type errorEvent struct {
	Service  string `json:"service"`
	Username string `json:"username"`
	ConnID   string `json:"connId"`
	Addr     string `json:"addr"`
	Reason   string `json:"reason"`
}

type ErrorHandler struct {
	webHook
}

func (self *ErrorHandler) OnError(service, username, connId, addr string, reason error) {
	self.post(&errorEvent{service, username, connId, addr, reason.Error()}, nil, false)
}

type ForwardRequestHandler struct {
	webHook
	maxTTL time.Duration
}

type forwardEvent struct {
	SenderService   string        `json:"sender-service"`
	Sender          string        `json:"sender"`
	ReceiverService string        `json:"receiver-service"`
	Receivers       []string      `json:"receivers"`
	Message         *rpc.Message  `json:"msg"`
	TTL             time.Duration `json:"ttl"`
}

type forwardDecision struct {
	ShouldForward bool              `json:"should-forward"`
	ShouldPush    bool              `json:"should-push"`
	PushInfo      map[string]string `json:"push-info"`
}

func (self *ForwardRequestHandler) ShouldForward(senderService, sender, receiverService string, receivers []string,
	ttl time.Duration, msg *rpc.Message) (shouldForward, shouldPush bool, pushInfo map[string]string) {
	fwd := &forwardEvent{
		Sender:          sender,
		SenderService:   senderService,
		Receivers:       receivers,
		ReceiverService: receiverService,
		TTL:             ttl,
		Message:         msg,
	}

	res := &forwardDecision{
		ShouldForward: true,
		ShouldPush:    true,
		PushInfo:      make(map[string]string, 10),
	}

	status := self.post(fwd, res, true)
	if status != 200 {
		res.ShouldForward = false
		res.ShouldPush = false
	}
	return res.ShouldForward, res.ShouldPush, res.PushInfo
}

func (self *ForwardRequestHandler) SetMaxTTL(ttl time.Duration) {
	self.maxTTL = ttl
}

func (self *ForwardRequestHandler) MaxTTL() time.Duration {
	return self.maxTTL
}

type authEvent struct {
	Service  string `json:"service"`
	Username string `json:"username"`
	ConnId   string `json:"conn-id"`
	Token    string `json:"token"`
	Addr     string `json:"addr"`
}

type AuthHandler struct {
	webHook
}

func (self *AuthHandler) Authenticate(srv, usr, connId, token, addr string) (pass bool, redir []string, err error) {
	evt := new(authEvent)
	evt.Service = srv
	evt.Username = usr
	evt.ConnId = connId
	evt.Token = token
	evt.Addr = addr
	pass = self.post(evt, redir, false) == 200
	return
}

type pushRelatedEvent struct {
	Service  string            `json:"service"`
	Username string            `json:"username"`
	Info     map[string]string `json:"info"`
}

type SubscribeHandler struct {
	webHook
}

func (self *SubscribeHandler) ShouldSubscribe(service, username string, info map[string]string) bool {
	evt := new(pushRelatedEvent)
	evt.Service = service
	evt.Username = username
	evt.Info = info
	return self.post(evt, nil, false) == 200
}

type UnsubscribeHandler struct {
	webHook
}

func (self *UnsubscribeHandler) OnUnsubscribe(service, username string, info map[string]string) {
	evt := &pushRelatedEvent{}
	evt.Service = service
	evt.Username = username
	evt.Info = info
	self.post(evt, nil, false)
	return
}
<commit_msg>forward hook could provide empty reply with 200 status code.<commit_after>/*
 * Copyright 2013 Nan Deng
 *
 * 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 webhook

import (
	"bytes"
	"encoding/json"
	"github.com/uniqush/uniqush-conn/rpc"
	"net"
	"net/http"
	"time"
)

type WebHook interface {
	SetURL(urls ...string)
	SetTimeout(timeout time.Duration)
	SetDefault(d int)
}

type webHook struct {
	URLs    []string
	Timeout time.Duration
	Default int
}

func (self *webHook) SetURL(urls ...string) {
	self.URLs = urls
}

func (self *webHook) SetTimeout(timeout time.Duration) {
	self.Timeout = timeout
}

func (self *webHook) SetDefault(d int) {
	self.Default = d
}

func timeoutDialler(ns time.Duration) func(net, addr string) (c net.Conn, err error) {
	return func(netw, addr string) (net.Conn, error) {
		c, err := net.Dial(netw, addr)
		if err != nil {
			return nil, err
		}
		if ns.Seconds() > 0.0 {
			c.SetDeadline(time.Now().Add(ns))
		}
		return c, nil
	}
}

func (self *webHook) post(data interface{}, out interface{}, requireOut bool) int {
	ret := self.Default
	for _, url := range self.URLs {
		status := self.postSingle(url, data, out, requireOut)
		if status == 200 {
			if out != nil {
				return status
			}
			ret = status
		}
	}
	return ret
}

func (self *webHook) postSingle(url string, data interface{}, out interface{}, requireOut bool) int {
	if len(url) == 0 || url == "none" {
		return self.Default
	}
	jdata, err := json.Marshal(data)
	if err != nil {
		return self.Default
	}
	c := http.Client{
		Transport: &http.Transport{
			Dial: timeoutDialler(self.Timeout),
		},
	}
	resp, err := c.Post(url, "application/json", bytes.NewReader(jdata))
	if err != nil {
		return self.Default
	}
	defer resp.Body.Close()

	if out != nil {
		e := json.NewDecoder(resp.Body)
		err = e.Decode(out)
		if err != nil && requireOut {
			return self.Default
		}
	}
	return resp.StatusCode
}

type loginEvent struct {
	Service  string `json:"service"`
	Username string `json:"username"`
	ConnID   string `json:"connId"`
	Addr     string `json:"addr"`
}

type LoginHandler struct {
	webHook
}

func (self *LoginHandler) OnLogin(service, username, connId, addr string) {
	self.post(&loginEvent{service, username, connId, addr}, nil, false)
}

type logoutEvent struct {
	Service  string `json:"service"`
	Username string `json:"username"`
	ConnID   string `json:"connId"`
	Addr     string `json:"addr"`
	Reason   string `json:"reason,omitempty"`
}

type LogoutHandler struct {
	webHook
}

func (self *LogoutHandler) OnLogout(service, username, connId, addr string, reason error) {
	evt := &logoutEvent{
		Service:  service,
		Username: username,
		ConnID:   connId,
		Addr:     addr,
	}

	if reason != nil {
		evt.Reason = reason.Error()
	}
	self.post(evt, nil, false)
}

type messageEvent struct {
	ConnID   string       `json:"connId"`
	Msg      *rpc.Message `json:"msg"`
	Service  string       `json:"service"`
	Username string       `json:"username"`
}

type MessageHandler struct {
	webHook
}

func (self *MessageHandler) OnMessage(service, username, connId string, msg *rpc.Message) {
	evt := &messageEvent{
		Service:  service,
		Username: username,
		ConnID:   connId,
		Msg:      msg,
	}
	self.post(evt, nil, false)
}

type errorEvent struct {
	Service  string `json:"service"`
	Username string `json:"username"`
	ConnID   string `json:"connId"`
	Addr     string `json:"addr"`
	Reason   string `json:"reason"`
}

type ErrorHandler struct {
	webHook
}

func (self *ErrorHandler) OnError(service, username, connId, addr string, reason error) {
	self.post(&errorEvent{service, username, connId, addr, reason.Error()}, nil, false)
}

type ForwardRequestHandler struct {
	webHook
	maxTTL time.Duration
}

type forwardEvent struct {
	SenderService   string        `json:"sender-service"`
	Sender          string        `json:"sender"`
	ReceiverService string        `json:"receiver-service"`
	Receivers       []string      `json:"receivers"`
	Message         *rpc.Message  `json:"msg"`
	TTL             time.Duration `json:"ttl"`
}

type forwardDecision struct {
	ShouldForward bool              `json:"should-forward"`
	ShouldPush    bool              `json:"should-push"`
	PushInfo      map[string]string `json:"push-info"`
}

func (self *ForwardRequestHandler) ShouldForward(senderService, sender, receiverService string, receivers []string,
	ttl time.Duration, msg *rpc.Message) (shouldForward, shouldPush bool, pushInfo map[string]string) {
	fwd := &forwardEvent{
		Sender:          sender,
		SenderService:   senderService,
		Receivers:       receivers,
		ReceiverService: receiverService,
		TTL:             ttl,
		Message:         msg,
	}

	res := &forwardDecision{
		ShouldForward: true,
		ShouldPush:    true,
		PushInfo:      make(map[string]string, 10),
	}

	status := self.post(fwd, res, false)
	if status != 200 {
		res.ShouldForward = false
		res.ShouldPush = false
	}
	return res.ShouldForward, res.ShouldPush, res.PushInfo
}

func (self *ForwardRequestHandler) SetMaxTTL(ttl time.Duration) {
	self.maxTTL = ttl
}

func (self *ForwardRequestHandler) MaxTTL() time.Duration {
	return self.maxTTL
}

type authEvent struct {
	Service  string `json:"service"`
	Username string `json:"username"`
	ConnId   string `json:"conn-id"`
	Token    string `json:"token"`
	Addr     string `json:"addr"`
}

type AuthHandler struct {
	webHook
}

func (self *AuthHandler) Authenticate(srv, usr, connId, token, addr string) (pass bool, redir []string, err error) {
	evt := new(authEvent)
	evt.Service = srv
	evt.Username = usr
	evt.ConnId = connId
	evt.Token = token
	evt.Addr = addr
	pass = self.post(evt, redir, false) == 200
	return
}

type pushRelatedEvent struct {
	Service  string            `json:"service"`
	Username string            `json:"username"`
	Info     map[string]string `json:"info"`
}

type SubscribeHandler struct {
	webHook
}

func (self *SubscribeHandler) ShouldSubscribe(service, username string, info map[string]string) bool {
	evt := new(pushRelatedEvent)
	evt.Service = service
	evt.Username = username
	evt.Info = info
	return self.post(evt, nil, false) == 200
}

type UnsubscribeHandler struct {
	webHook
}

func (self *UnsubscribeHandler) OnUnsubscribe(service, username string, info map[string]string) {
	evt := &pushRelatedEvent{}
	evt.Service = service
	evt.Username = username
	evt.Info = info
	self.post(evt, nil, false)
	return
}
<|endoftext|>