<commit_before>// Copyright 2013 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 runtime_test

import (
	"fmt"
	"strings"
	"testing"
)

const size = 10

func BenchmarkHashStringSpeed(b *testing.B) {
	strings := make([]string, size)
	for i := 0; i < size; i++ {
		strings[i] = fmt.Sprintf("string#%d", i)
	}
	sum := 0
	m := make(map[string]int, size)
	for i := 0; i < size; i++ {
		m[strings[i]] = 0
	}
	idx := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum += m[strings[idx]]
		idx++
		if idx == size {
			idx = 0
		}
	}
}

func BenchmarkHashInt32Speed(b *testing.B) {
	ints := make([]int32, size)
	for i := 0; i < size; i++ {
		ints[i] = int32(i)
	}
	sum := 0
	m := make(map[int32]int, size)
	for i := 0; i < size; i++ {
		m[ints[i]] = 0
	}
	idx := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum += m[ints[idx]]
		idx++
		if idx == size {
			idx = 0
		}
	}
}

func BenchmarkHashInt64Speed(b *testing.B) {
	ints := make([]int64, size)
	for i := 0; i < size; i++ {
		ints[i] = int64(i)
	}
	sum := 0
	m := make(map[int64]int, size)
	for i := 0; i < size; i++ {
		m[ints[i]] = 0
	}
	idx := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum += m[ints[idx]]
		idx++
		if idx == size {
			idx = 0
		}
	}
}
func BenchmarkHashStringArraySpeed(b *testing.B) {
	stringpairs := make([][2]string, size)
	for i := 0; i < size; i++ {
		for j := 0; j < 2; j++ {
			stringpairs[i][j] = fmt.Sprintf("string#%d/%d", i, j)
		}
	}
	sum := 0
	m := make(map[[2]string]int, size)
	for i := 0; i < size; i++ {
		m[stringpairs[i]] = 0
	}
	idx := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum += m[stringpairs[idx]]
		idx++
		if idx == size {
			idx = 0
		}
	}
}

func BenchmarkMegMap(b *testing.B) {
	m := make(map[string]bool)
	for suffix := 'A'; suffix <= 'G'; suffix++ {
		m[strings.Repeat("X", 1<<20-1)+fmt.Sprint(suffix)] = true
	}
	key := strings.Repeat("X", 1<<20-1) + "k"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[key]
	}
}

func BenchmarkMegOneMap(b *testing.B) {
	m := make(map[string]bool)
	m[strings.Repeat("X", 1<<20)] = true
	key := strings.Repeat("Y", 1<<20)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[key]
	}
}

func BenchmarkMegEmptyMap(b *testing.B) {
	m := make(map[string]bool)
	key := strings.Repeat("X", 1<<20)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[key]
	}
}

func BenchmarkSmallStrMap(b *testing.B) {
	m := make(map[string]bool)
	for suffix := 'A'; suffix <= 'G'; suffix++ {
		m[fmt.Sprint(suffix)] = true
	}
	key := "k"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[key]
	}
}
func BenchmarkIntMap(b *testing.B) {
	m := make(map[int]bool)
	for i := 0; i < 8; i++ {
		m[i] = true
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[7]
	}
}
<commit_msg>runtime: additional map benchmarks for repeated lookups<commit_after>// Copyright 2013 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 runtime_test

import (
	"fmt"
	"strings"
	"testing"
)

const size = 10

func BenchmarkHashStringSpeed(b *testing.B) {
	strings := make([]string, size)
	for i := 0; i < size; i++ {
		strings[i] = fmt.Sprintf("string#%d", i)
	}
	sum := 0
	m := make(map[string]int, size)
	for i := 0; i < size; i++ {
		m[strings[i]] = 0
	}
	idx := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum += m[strings[idx]]
		idx++
		if idx == size {
			idx = 0
		}
	}
}

func BenchmarkHashInt32Speed(b *testing.B) {
	ints := make([]int32, size)
	for i := 0; i < size; i++ {
		ints[i] = int32(i)
	}
	sum := 0
	m := make(map[int32]int, size)
	for i := 0; i < size; i++ {
		m[ints[i]] = 0
	}
	idx := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum += m[ints[idx]]
		idx++
		if idx == size {
			idx = 0
		}
	}
}

func BenchmarkHashInt64Speed(b *testing.B) {
	ints := make([]int64, size)
	for i := 0; i < size; i++ {
		ints[i] = int64(i)
	}
	sum := 0
	m := make(map[int64]int, size)
	for i := 0; i < size; i++ {
		m[ints[i]] = 0
	}
	idx := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum += m[ints[idx]]
		idx++
		if idx == size {
			idx = 0
		}
	}
}
func BenchmarkHashStringArraySpeed(b *testing.B) {
	stringpairs := make([][2]string, size)
	for i := 0; i < size; i++ {
		for j := 0; j < 2; j++ {
			stringpairs[i][j] = fmt.Sprintf("string#%d/%d", i, j)
		}
	}
	sum := 0
	m := make(map[[2]string]int, size)
	for i := 0; i < size; i++ {
		m[stringpairs[i]] = 0
	}
	idx := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum += m[stringpairs[idx]]
		idx++
		if idx == size {
			idx = 0
		}
	}
}

func BenchmarkMegMap(b *testing.B) {
	m := make(map[string]bool)
	for suffix := 'A'; suffix <= 'G'; suffix++ {
		m[strings.Repeat("X", 1<<20-1)+fmt.Sprint(suffix)] = true
	}
	key := strings.Repeat("X", 1<<20-1) + "k"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[key]
	}
}

func BenchmarkMegOneMap(b *testing.B) {
	m := make(map[string]bool)
	m[strings.Repeat("X", 1<<20)] = true
	key := strings.Repeat("Y", 1<<20)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[key]
	}
}

func BenchmarkMegEmptyMap(b *testing.B) {
	m := make(map[string]bool)
	key := strings.Repeat("X", 1<<20)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[key]
	}
}

func BenchmarkSmallStrMap(b *testing.B) {
	m := make(map[string]bool)
	for suffix := 'A'; suffix <= 'G'; suffix++ {
		m[fmt.Sprint(suffix)] = true
	}
	key := "k"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[key]
	}
}

func BenchmarkIntMap(b *testing.B) {
	m := make(map[int]bool)
	for i := 0; i < 8; i++ {
		m[i] = true
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = m[7]
	}
}

// Accessing the same keys in a row.
func benchmarkRepeatedLookup(b *testing.B, lookupKeySize int) {
	m := make(map[string]bool)
	// At least bigger than a single bucket:
	for i := 0; i < 64; i++ {
		m[fmt.Sprintf("some key %d", i)] = true
	}
	base := strings.Repeat("x", lookupKeySize-1)
	key1 := base + "1"
	key2 := base + "2"
	b.ResetTimer()
	for i := 0; i < b.N/4; i++ {
		_ = m[key1]
		_ = m[key1]
		_ = m[key2]
		_ = m[key2]
	}
}

func BenchmarkRepeatedLookupStrMapKey32(b *testing.B) { benchmarkRepeatedLookup(b, 32) }
func BenchmarkRepeatedLookupStrMapKey1M(b *testing.B) { benchmarkRepeatedLookup(b, 1<<20) }
<|endoftext|>


<commit_before>// 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.

// Netlink sockets and messages

package syscall

import "unsafe"

// Round the length of a netlink message up to align it properly.
func nlmAlignOf(msglen int) int {
	return (msglen + NLMSG_ALIGNTO - 1) & ^(NLMSG_ALIGNTO - 1)
}

// Round the length of a netlink route attribute up to align it
// properly.
func rtaAlignOf(attrlen int) int {
	return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1)
}

// NetlinkRouteRequest represents a request message to receive routing
// and link states from the kernel.
type NetlinkRouteRequest struct {
	Header NlMsghdr
	Data   RtGenmsg
}

func (rr *NetlinkRouteRequest) toWireFormat() []byte {
	b := make([]byte, rr.Header.Len)
	*(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len
	*(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type
	*(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags
	*(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq
	*(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid
	b[16] = byte(rr.Data.Family)
	return b
}

func newNetlinkRouteRequest(proto, seq, family int) []byte {
	rr := &NetlinkRouteRequest{}
	rr.Header.Len = uint32(NLMSG_HDRLEN + SizeofRtGenmsg)
	rr.Header.Type = uint16(proto)
	rr.Header.Flags = NLM_F_DUMP | NLM_F_REQUEST
	rr.Header.Seq = uint32(seq)
	rr.Data.Family = uint8(family)
	return rr.toWireFormat()
}

// NetlinkRIB returns routing information base, as known as RIB, which
// consists of network facility information, states and parameters.
func NetlinkRIB(proto, family int) ([]byte, error) {
	s, err := Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)
	if err != nil {
		return nil, err
	}
	defer Close(s)
	lsa := &SockaddrNetlink{Family: AF_NETLINK}
	if err := Bind(s, lsa); err != nil {
		return nil, err
	}
	wb := newNetlinkRouteRequest(proto, 1, family)
	if err := Sendto(s, wb, 0, lsa); err != nil {
		return nil, err
	}
	var tab []byte
done:
	for {
		rb := make([]byte, Getpagesize())
		nr, _, err := Recvfrom(s, rb, 0)
		if err != nil {
			return nil, err
		}
		if nr < NLMSG_HDRLEN {
			return nil, EINVAL
		}
		rb = rb[:nr]
		tab = append(tab, rb...)
		msgs, err := ParseNetlinkMessage(rb)
		if err != nil {
			return nil, err
		}
		for _, m := range msgs {
			lsa, err := Getsockname(s)
			if err != nil {
				return nil, err
			}
			switch v := lsa.(type) {
			case *SockaddrNetlink:
				if m.Header.Seq != 1 || m.Header.Pid != v.Pid {
					return nil, EINVAL
				}
			default:
				return nil, EINVAL
			}
			if m.Header.Type == NLMSG_DONE {
				break done
			}
			if m.Header.Type == NLMSG_ERROR {
				return nil, EINVAL
			}
		}
	}
	return tab, nil
}

// NetlinkMessage represents a netlink message.
type NetlinkMessage struct {
	Header NlMsghdr
	Data   []byte
}

// ParseNetlinkMessage parses b as an array of netlink messages and
// returns the slice containing the NetlinkMessage structures.
func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {
	var msgs []NetlinkMessage
	for len(b) >= NLMSG_HDRLEN {
		h, dbuf, dlen, err := netlinkMessageHeaderAndData(b)
		if err != nil {
			return nil, err
		}
		m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}
		msgs = append(msgs, m)
		b = b[dlen:]
	}
	return msgs, nil
}

func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) {
	h := (*NlMsghdr)(unsafe.Pointer(&b[0]))
	if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(b) {
		return nil, nil, 0, EINVAL
	}
	return h, b[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
}

// NetlinkRouteAttr represents a netlink route attribute.
type NetlinkRouteAttr struct {
	Attr  RtAttr
	Value []byte
}

// ParseNetlinkRouteAttr parses m's payload as an array of netlink
// route attributes and returns the slice containing the
// NetlinkRouteAttr structures.
func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) {
	var b []byte
	switch m.Header.Type {
	case RTM_NEWLINK, RTM_DELLINK:
		b = m.Data[SizeofIfInfomsg:]
	case RTM_NEWADDR, RTM_DELADDR:
		b = m.Data[SizeofIfAddrmsg:]
	case RTM_NEWROUTE, RTM_DELROUTE:
		b = m.Data[SizeofRtMsg:]
	default:
		return nil, EINVAL
	}
	var attrs []NetlinkRouteAttr
	for len(b) >= SizeofRtAttr {
		a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
		if err != nil {
			return nil, err
		}
		ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]}
		attrs = append(attrs, ra)
		b = b[alen:]
	}
	return attrs, nil
}

func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) {
	a := (*RtAttr)(unsafe.Pointer(&b[0]))
	if int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) {
		return nil, nil, 0, EINVAL
	}
	return a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
}
<commit_msg>syscall: NetlinkRIB, avoid allocation in loop<commit_after>// 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.

// Netlink sockets and messages

package syscall

import "unsafe"

// Round the length of a netlink message up to align it properly.
func nlmAlignOf(msglen int) int {
	return (msglen + NLMSG_ALIGNTO - 1) & ^(NLMSG_ALIGNTO - 1)
}

// Round the length of a netlink route attribute up to align it
// properly.
func rtaAlignOf(attrlen int) int {
	return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1)
}

// NetlinkRouteRequest represents a request message to receive routing
// and link states from the kernel.
type NetlinkRouteRequest struct {
	Header NlMsghdr
	Data   RtGenmsg
}

func (rr *NetlinkRouteRequest) toWireFormat() []byte {
	b := make([]byte, rr.Header.Len)
	*(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len
	*(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type
	*(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags
	*(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq
	*(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid
	b[16] = byte(rr.Data.Family)
	return b
}

func newNetlinkRouteRequest(proto, seq, family int) []byte {
	rr := &NetlinkRouteRequest{}
	rr.Header.Len = uint32(NLMSG_HDRLEN + SizeofRtGenmsg)
	rr.Header.Type = uint16(proto)
	rr.Header.Flags = NLM_F_DUMP | NLM_F_REQUEST
	rr.Header.Seq = uint32(seq)
	rr.Data.Family = uint8(family)
	return rr.toWireFormat()
}

// NetlinkRIB returns routing information base, as known as RIB, which
// consists of network facility information, states and parameters.
func NetlinkRIB(proto, family int) ([]byte, error) {
	s, err := Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)
	if err != nil {
		return nil, err
	}
	defer Close(s)
	lsa := &SockaddrNetlink{Family: AF_NETLINK}
	if err := Bind(s, lsa); err != nil {
		return nil, err
	}
	wb := newNetlinkRouteRequest(proto, 1, family)
	if err := Sendto(s, wb, 0, lsa); err != nil {
		return nil, err
	}
	var tab []byte
	rbNew := make([]byte, Getpagesize())
done:
	for {
		rb := rbNew
		nr, _, err := Recvfrom(s, rb, 0)
		if err != nil {
			return nil, err
		}
		if nr < NLMSG_HDRLEN {
			return nil, EINVAL
		}
		rb = rb[:nr]
		tab = append(tab, rb...)
		msgs, err := ParseNetlinkMessage(rb)
		if err != nil {
			return nil, err
		}
		for _, m := range msgs {
			lsa, err := Getsockname(s)
			if err != nil {
				return nil, err
			}
			switch v := lsa.(type) {
			case *SockaddrNetlink:
				if m.Header.Seq != 1 || m.Header.Pid != v.Pid {
					return nil, EINVAL
				}
			default:
				return nil, EINVAL
			}
			if m.Header.Type == NLMSG_DONE {
				break done
			}
			if m.Header.Type == NLMSG_ERROR {
				return nil, EINVAL
			}
		}
	}
	return tab, nil
}

// NetlinkMessage represents a netlink message.
type NetlinkMessage struct {
	Header NlMsghdr
	Data   []byte
}

// ParseNetlinkMessage parses b as an array of netlink messages and
// returns the slice containing the NetlinkMessage structures.
func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {
	var msgs []NetlinkMessage
	for len(b) >= NLMSG_HDRLEN {
		h, dbuf, dlen, err := netlinkMessageHeaderAndData(b)
		if err != nil {
			return nil, err
		}
		m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}
		msgs = append(msgs, m)
		b = b[dlen:]
	}
	return msgs, nil
}

func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) {
	h := (*NlMsghdr)(unsafe.Pointer(&b[0]))
	if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(b) {
		return nil, nil, 0, EINVAL
	}
	return h, b[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
}

// NetlinkRouteAttr represents a netlink route attribute.
type NetlinkRouteAttr struct {
	Attr  RtAttr
	Value []byte
}

// ParseNetlinkRouteAttr parses m's payload as an array of netlink
// route attributes and returns the slice containing the
// NetlinkRouteAttr structures.
func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) {
	var b []byte
	switch m.Header.Type {
	case RTM_NEWLINK, RTM_DELLINK:
		b = m.Data[SizeofIfInfomsg:]
	case RTM_NEWADDR, RTM_DELADDR:
		b = m.Data[SizeofIfAddrmsg:]
	case RTM_NEWROUTE, RTM_DELROUTE:
		b = m.Data[SizeofRtMsg:]
	default:
		return nil, EINVAL
	}
	var attrs []NetlinkRouteAttr
	for len(b) >= SizeofRtAttr {
		a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
		if err != nil {
			return nil, err
		}
		ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]}
		attrs = append(attrs, ra)
		b = b[alen:]
	}
	return attrs, nil
}

func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) {
	a := (*RtAttr)(unsafe.Pointer(&b[0]))
	if int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) {
		return nil, nil, 0, EINVAL
	}
	return a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
}
<|endoftext|>


<commit_before>// Copyright 2009 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 time

import (
	"errors"
	"syscall"
)

// TODO(rsc): Fall back to copy of zoneinfo files.

// BUG(brainman,rsc): On Windows, the operating system does not provide complete
// time zone information.
// The implementation assumes that this year's rules for daylight savings
// time apply to all previous and future years as well.
// Also, time zone abbreviations are unavailable.  The implementation constructs
// them using the capital letters from a longer time zone description.

// abbrev returns the abbreviation to use for the given zone name.
func abbrev(name []uint16) string {
	// name is 'Pacific Standard Time' but we want 'PST'.
	// Extract just capital letters.  It's not perfect but the
	// information we need is not available from the kernel.
	// Because time zone abbreviations are not unique,
	// Windows refuses to expose them.
	//
	// http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb
	// http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net
	short := make([]rune, len(name))
	w := 0
	for _, c := range name {
		if 'A' <= c && c <= 'Z' {
			short[w] = rune(c)
			w++
		}
	}
	return string(short)
}

// pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
// denoted by the system date+time d in the given year.
// It is up to the caller to convert this local time into a UTC-based time.
func pseudoUnix(year int, d *syscall.Systemtime) int64 {
	// Windows specifies daylight savings information in "day in month" format:
	// d.Month is month number (1-12)
	// d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
	// d.Day is week within the month (1 to 5, where 5 is last week of the month)
	// d.Hour, d.Minute and d.Second are absolute time
	day := 1
	t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC)
	i := int(d.DayOfWeek) - int(t.Weekday())
	if i < 0 {
		i += 7
	}
	day += i
	if week := int(d.Day) - 1; week < 4 {
		day += week * 7
	} else {
		// "Last" instance of the day.
		day += 4 * 7
		if day > daysIn(Month(d.Month), year) {
			day -= 7
		}
	}
	return t.sec + int64(day-1)*secondsPerDay
}

func initLocalFromTZI(i *syscall.Timezoneinformation) {
	l := &localLoc

	nzone := 1
	if i.StandardDate.Month > 0 {
		nzone++
	}
	l.zone = make([]zone, nzone)

	std := &l.zone[0]
	std.name = abbrev(i.StandardName[0:])
	std.offset = -int(i.StandardBias) * 60
	if nzone == 1 {
		// No daylight savings.
		l.cacheStart = -1 << 63
		l.cacheEnd = 1<<63 - 1
		l.cacheZone = std
		return
	}

	dst := &l.zone[1]
	dst.name = abbrev(i.DaylightName[0:])
	dst.offset = std.offset + -int(i.DaylightBias)*60
	dst.isDST = true

	// Arrange so that d0 is first transition date, d1 second,
	// i0 is index of zone after first transition, i1 second.
	d0 := &i.StandardDate
	d1 := &i.DaylightDate
	i0 := 0
	i1 := 1
	if d0.Month > d1.Month {
		d0, d1 = d1, d0
		i0, i1 = i1, i0
	}

	// 2 tx per year, 100 years on each side of this year
	l.tx = make([]zoneTrans, 400)

	t := Now().UTC()
	year := t.Year()
	txi := 0
	for y := year - 100; y < year+100; y++ {
		tx := &l.tx[txi]
		tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
		tx.index = uint8(i0)
		txi++

		tx = &l.tx[txi]
		tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
		tx.index = uint8(i1)
		txi++
	}
}

var usPacific = syscall.Timezoneinformation{
	Bias: 8 * 60,
	StandardName: [32]uint16{
		'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
	},
	StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2},
	DaylightName: [32]uint16{
		'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
	},
	DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2},
	DaylightBias: -60,
}

func initTestingZone() {
	initLocalFromTZI(&usPacific)
}

func initLocal() {
	var i syscall.Timezoneinformation
	if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
		localLoc.name = "UTC"
		return
	}
	initLocalFromTZI(&i)
}

// TODO(rsc): Implement.
func loadLocation(name string) (*Location, error) {
	return nil, errors.New("unknown time zone " + name)
}
<commit_msg>time: fix windows build<commit_after>// Copyright 2009 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 time

import (
	"errors"
	"syscall"
)

// TODO(rsc): Fall back to copy of zoneinfo files.

// BUG(brainman,rsc): On Windows, the operating system does not provide complete
// time zone information.
// The implementation assumes that this year's rules for daylight savings
// time apply to all previous and future years as well.
// Also, time zone abbreviations are unavailable.  The implementation constructs
// them using the capital letters from a longer time zone description.

// abbrev returns the abbreviation to use for the given zone name.
func abbrev(name []uint16) string {
	// name is 'Pacific Standard Time' but we want 'PST'.
	// Extract just capital letters.  It's not perfect but the
	// information we need is not available from the kernel.
	// Because time zone abbreviations are not unique,
	// Windows refuses to expose them.
	//
	// http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb
	// http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net
	var short []rune
	for _, c := range name {
		if 'A' <= c && c <= 'Z' {
			short = append(short, rune(c))
		}
	}
	return string(short)
}

// pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
// denoted by the system date+time d in the given year.
// It is up to the caller to convert this local time into a UTC-based time.
func pseudoUnix(year int, d *syscall.Systemtime) int64 {
	// Windows specifies daylight savings information in "day in month" format:
	// d.Month is month number (1-12)
	// d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
	// d.Day is week within the month (1 to 5, where 5 is last week of the month)
	// d.Hour, d.Minute and d.Second are absolute time
	day := 1
	t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC)
	i := int(d.DayOfWeek) - int(t.Weekday())
	if i < 0 {
		i += 7
	}
	day += i
	if week := int(d.Day) - 1; week < 4 {
		day += week * 7
	} else {
		// "Last" instance of the day.
		day += 4 * 7
		if day > daysIn(Month(d.Month), year) {
			day -= 7
		}
	}
	return t.sec + int64(day-1)*secondsPerDay
}

func initLocalFromTZI(i *syscall.Timezoneinformation) {
	l := &localLoc

	nzone := 1
	if i.StandardDate.Month > 0 {
		nzone++
	}
	l.zone = make([]zone, nzone)

	std := &l.zone[0]
	std.name = abbrev(i.StandardName[0:])
	if nzone == 1 {
		// No daylight savings.
		std.offset = -int(i.Bias) * 60
		l.cacheStart = -1 << 63
		l.cacheEnd = 1<<63 - 1
		l.cacheZone = std
		return
	}

	// StandardBias must be ignored if StandardDate is not set,
	// so this computation is delayed until after the nzone==1
	// return above.
	std.offset = -int(i.Bias+i.StandardBias) * 60

	dst := &l.zone[1]
	dst.name = abbrev(i.DaylightName[0:])
	dst.offset = -int(i.Bias+i.DaylightBias) * 60
	dst.isDST = true

	// Arrange so that d0 is first transition date, d1 second,
	// i0 is index of zone after first transition, i1 second.
	d0 := &i.StandardDate
	d1 := &i.DaylightDate
	i0 := 0
	i1 := 1
	if d0.Month > d1.Month {
		d0, d1 = d1, d0
		i0, i1 = i1, i0
	}

	// 2 tx per year, 100 years on each side of this year
	l.tx = make([]zoneTrans, 400)

	t := Now().UTC()
	year := t.Year()
	txi := 0
	for y := year - 100; y < year+100; y++ {
		tx := &l.tx[txi]
		tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
		tx.index = uint8(i0)
		txi++

		tx = &l.tx[txi]
		tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
		tx.index = uint8(i1)
		txi++
	}
}

var usPacific = syscall.Timezoneinformation{
	Bias: 8 * 60,
	StandardName: [32]uint16{
		'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
	},
	StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2},
	DaylightName: [32]uint16{
		'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
	},
	DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2},
	DaylightBias: -60,
}

func initTestingZone() {
	initLocalFromTZI(&usPacific)
}

func initLocal() {
	var i syscall.Timezoneinformation
	if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
		localLoc.name = "UTC"
		return
	}
	initLocalFromTZI(&i)
}

// TODO(rsc): Implement.
func loadLocation(name string) (*Location, error) {
	return nil, errors.New("unknown time zone " + name)
}
<|endoftext|>


<commit_before>package vcs

import (
	"io/ioutil"
	"os"
	"testing"
)

func ExampleNewRepo() {
	remote := "https://github.com/Masterminds/vcs"
	local, _ := ioutil.TempDir("", "go-vcs")
	repo, _ := NewRepo(remote, local)
	// Returns: instance of GitRepo

	repo.Vcs()
	// Returns Git as this is a Git repo
}

func TestTypeSwitch(t *testing.T) {

	// To test repo type switching we checkout as SVN and then try to get it as
	// a git repo afterwards.
	tempDir, err := ioutil.TempDir("", "go-vcs-svn-tests")
	if err != nil {
		t.Error(err)
	}
	defer func() {
		err = os.RemoveAll(tempDir)
		if err != nil {
			t.Error(err)
		}
	}()

	repo, err := NewSvnRepo("https://github.com/Masterminds/VCSTestRepo/trunk", tempDir+"/VCSTestRepo")
	if err != nil {
		t.Error(err)
	}
	err = repo.Get()
	if err != nil {
		t.Errorf("Unable to checkout SVN repo for repo switching tests. Err was %s", err)
	}

	_, err = NewRepo("https://github.com/Masterminds/VCSTestRepo", tempDir+"/VCSTestRepo")
	if err != ErrWrongVCS {
		t.Errorf("Not detecting repo switch from SVN to Git")
	}
}
<commit_msg>Added more to the example.<commit_after>package vcs

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func ExampleNewRepo() {
	remote := "https://github.com/Masterminds/vcs"
	local, _ := ioutil.TempDir("", "go-vcs")
	repo, _ := NewRepo(remote, local)
	// Returns: instance of GitRepo

	repo.Vcs()
	// Returns Git as this is a Git repo

	err := repo.Get()
	// Pulls down a repo, or a checkout in the case of SVN, and returns an
	// error if that didn't happen successfully.
	if err != nil {
		fmt.Println(err)
	}

	err = repo.UpdateVersion("master")
	// Checkouts out a specific version. In most cases this can be a commit id,
	// branch, or tag.
	if err != nil {
		fmt.Println(err)
	}
}

func TestTypeSwitch(t *testing.T) {

	// To test repo type switching we checkout as SVN and then try to get it as
	// a git repo afterwards.
	tempDir, err := ioutil.TempDir("", "go-vcs-svn-tests")
	if err != nil {
		t.Error(err)
	}
	defer func() {
		err = os.RemoveAll(tempDir)
		if err != nil {
			t.Error(err)
		}
	}()

	repo, err := NewSvnRepo("https://github.com/Masterminds/VCSTestRepo/trunk", tempDir+"/VCSTestRepo")
	if err != nil {
		t.Error(err)
	}
	err = repo.Get()
	if err != nil {
		t.Errorf("Unable to checkout SVN repo for repo switching tests. Err was %s", err)
	}

	_, err = NewRepo("https://github.com/Masterminds/VCSTestRepo", tempDir+"/VCSTestRepo")
	if err != ErrWrongVCS {
		t.Errorf("Not detecting repo switch from SVN to Git")
	}
}
<|endoftext|>


<commit_before>package dnssd

import (
	"context"
	"fmt"
	"github.com/brutella/dnssd/log"
	"github.com/miekg/dns"
	"math/rand"
	"net"
	"strings"
	"sync"
	"time"
)

type ReadFunc func(*Request)

// Responder represents a mDNS responder.
type Responder interface {
	// Add adds a service to the responder.
	// Use the returned service handle to update service properties.
	Add(srv Service) (ServiceHandle, error)

	// Remove removes the service associated with the service handle from the responder.
	Remove(srv ServiceHandle)

	// Respond makes the receiver announcing and managing services.
	Respond(ctx context.Context) error

	// Debug calls a function for every dns request the responder receives.
	Debug(ctx context.Context, fn ReadFunc)
}

type responder struct {
	isRunning bool

	conn      MDNSConn
	unmanaged []*serviceHandle
	managed   []*serviceHandle

	mutex     *sync.Mutex
	truncated *Request
	random    *rand.Rand
	upIfaces  []string
}

func NewResponder() (Responder, error) {
	conn, err := newMDNSConn()
	if err != nil {
		return nil, err
	}

	return newResponder(conn), nil
}

func newResponder(conn MDNSConn) *responder {
	return &responder{
		isRunning: false,
		conn:      conn,
		unmanaged: []*serviceHandle{},
		managed:   []*serviceHandle{},
		mutex:     &sync.Mutex{},
		random:    rand.New(rand.NewSource(time.Now().UnixNano())),
		upIfaces:  []string{},
	}
}

func (r *responder) Remove(h ServiceHandle) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	for i, s := range r.managed {
		if h == s {
			handle := h.(*serviceHandle)
			r.unannounce([]*Service{handle.service})
			r.managed = append(r.managed[:i], r.managed[i+1:]...)
			return
		}
	}
}

func (r *responder) Add(srv Service) (ServiceHandle, error) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	if r.isRunning {
		ctx, cancel := context.WithCancel(context.TODO())
		defer cancel()

		if srv, err := r.register(ctx, srv); err != nil {
			return nil, err
		} else {
			return r.addManaged(srv), nil
		}
	}

	return r.addUnmanaged(srv), nil
}

func (r *responder) Respond(ctx context.Context) error {
	r.mutex.Lock()
	r.isRunning = true
	for _, h := range r.unmanaged {
		log.Debug.Println(h.service)
		if srv, err := r.register(ctx, *h.service); err != nil {
			return err
		} else {
			h.service = &srv
			r.managed = append(r.managed, h)
		}
	}
	r.unmanaged = []*serviceHandle{}
	r.mutex.Unlock()

	return r.respond(ctx)
}

// announce sends announcement messages including all services.
func (r *responder) announce(services []*Service) {
	for _, service := range services {
		for _, iface := range service.Interfaces() {
			go r.announceAtInterface(service, iface)
		}
	}
}

func (r *responder) announceAtInterface(service *Service, iface net.Interface) {
	ips := service.IPsAtInterface(iface)
	if len(ips) == 0 {
		log.Debug.Printf("No IPs for service %s at %s\n", service.ServiceInstanceName(), iface.Name)
		return
	}

	var answer []dns.RR
	answer = append(answer, SRV(*service))
	answer = append(answer, PTR(*service))
	answer = append(answer, TXT(*service))
	for _, a := range A(*service, iface) {
		answer = append(answer, a)
	}
	for _, aaaa := range AAAA(*service, iface) {
		answer = append(answer, aaaa)
	}
	msg := new(dns.Msg)
	msg.Answer = answer
	msg.Response = true
	msg.Authoritative = true

	setAnswerCacheFlushBit(msg)

	resp := &Response{msg: msg, iface: &iface}

	log.Debug.Println("Sending 1st announcement", msg)
	r.conn.SendResponse(resp)
	time.Sleep(1 * time.Second)
	log.Debug.Println("Sending 2nd announcement", msg)
	r.conn.SendResponse(resp)
}

func (r *responder) register(ctx context.Context, srv Service) (Service, error) {
	if !r.isRunning {
		return srv, fmt.Errorf("cannot register service when responder is not responding")
	}

	log.Debug.Printf("Probing for host %s and service %s…\n", srv.Hostname(), srv.ServiceInstanceName())
	probed, err := ProbeService(ctx, srv)
	if err != nil {
		return srv, err
	}

	srvs := []*Service{&probed}
	for _, h := range r.managed {
		srvs = append(srvs, h.service)
	}
	go r.announce(srvs)

	return probed, nil
}

func (r *responder) addManaged(srv Service) ServiceHandle {
	h := &serviceHandle{&srv}
	r.managed = append(r.managed, h)
	return h
}

func (r *responder) addUnmanaged(srv Service) ServiceHandle {
	h := &serviceHandle{&srv}
	r.unmanaged = append(r.unmanaged, h)
	return h
}

func (r *responder) respond(ctx context.Context) error {
	if !r.isRunning {
		return fmt.Errorf("isRunning should be true before calling respond()")
	}

	readCtx, readCancel := context.WithCancel(ctx)
	defer readCancel()
	ch := r.conn.Read(readCtx)

	for {
		select {
		case req := <-ch:
			r.mutex.Lock()
			r.handleRequest(req)
			r.mutex.Unlock()

		case <-ctx.Done():
			r.unannounce(services(r.managed))
			r.conn.Close()
			r.isRunning = false
			return ctx.Err()
		}
	}
}

func (r *responder) handleRequest(req *Request) {
	if len(r.managed) == 0 {
		// Ignore requests when no services are managed
		return
	}

	// If messages is truncated, we wait for the next message to come (RFC6762 18.5)
	if req.msg.Truncated {
		r.truncated = req
		log.Debug.Println("Waiting for additional answers...")
		return
	}

	// append request
	if r.truncated != nil && r.truncated.from.IP.Equal(req.from.IP) {
		log.Debug.Println("Add answers to truncated message")
		msgs := []*dns.Msg{r.truncated.msg, req.msg}
		r.truncated = nil
		req.msg = mergeMsgs(msgs)
	}

	// Conflicting records remove managed services from
	// the responder and trigger reprobing
	conflicts := findConflicts(req, r.managed)
	for _, h := range conflicts {
		log.Debug.Println("Reprobe for", h.service)
		go r.reprobe(h)

		for i, m := range r.managed {
			if h == m {
				r.managed = append(r.managed[:i], r.managed[i+1:]...)
				break
			}
		}
	}

	r.handleQuery(req, services(r.managed))
}

func (r *responder) unannounce(services []*Service) {
	if len(services) == 0 {
		return
	}

	log.Debug.Println("Send goodbye for", services)

	// collect records per interface
	rrsByIfaceName := map[string][]dns.RR{}
	for _, srv := range services {
		rr := PTR(*srv)
		rr.Header().Ttl = 0
		for _, iface := range srv.Interfaces() {
			ips := srv.IPsAtInterface(iface)
			if len(ips) == 0 {
				continue
			}
			if rrs, ok := rrsByIfaceName[iface.Name]; ok {
				rrsByIfaceName[iface.Name] = append(rrs, rr)
			} else {
				rrsByIfaceName[iface.Name] = []dns.RR{rr}
			}
		}
	}

	// send on goodbye packet on every interface
	for name, rrs := range rrsByIfaceName {
		iface, err := net.InterfaceByName(name)
		if err != nil {
			log.Debug.Printf("Interface %s not found\n", name)
			continue
		}
		msg := new(dns.Msg)
		msg.Answer = rrs
		msg.Response = true
		msg.Authoritative = true

		log.Debug.Printf("Send %v to %s\n", msg, name)
		resp := &Response{msg: msg, iface: iface}
		r.conn.SendResponse(resp)
		time.Sleep(250 * time.Millisecond)
		r.conn.SendResponse(resp)
	}
}

func (r *responder) handleQuery(req *Request, services []*Service) {
	for _, q := range req.msg.Question {
		msgs := []*dns.Msg{}
		for _, srv := range services {
			log.Debug.Printf("%s tries to give response to question %v\n", srv.ServiceInstanceName(), q)
			if msg := r.handleQuestion(q, req, *srv); msg != nil {
				msgs = append(msgs, msg)
			} else {
				log.Debug.Println("No response")
			}
		}

		msg := mergeMsgs(msgs)
		msg.SetReply(req.msg)
		msg.Question = nil
		msg.Response = true
		msg.Authoritative = true

		if len(msg.Answer) == 0 {
			log.Debug.Println("No answers")
			continue
		}

		if isUnicastQuestion(q) {
			resp := &Response{msg: msg, addr: req.from, iface: req.iface}
			log.Debug.Printf("Send unicast response\n%v to %v\n", msg, resp.addr)
			if err := r.conn.SendResponse(resp); err != nil {
				log.Debug.Println(err)
			}
		} else {
			resp := &Response{msg: msg, iface: req.iface}
			log.Debug.Printf("Send multicast response\n%v\n", msg)
			if err := r.conn.SendResponse(resp); err != nil {
				log.Debug.Println(err)
			}
		}
	}
}

func (r *responder) reprobe(h *serviceHandle) {
	ctx, cancel := context.WithCancel(context.TODO())
	defer cancel()

	probed, err := ReprobeService(ctx, *h.service)
	if err != nil {
		return
	}
	h.service = &probed

	r.mutex.Lock()
	managed := append(r.managed, h)
	r.managed = managed
	r.mutex.Unlock()

	log.Debug.Println("Reannouncing services", managed)
	go r.announce(services(managed))
}

func (r *responder) handleQuestion(q dns.Question, req *Request, srv Service) *dns.Msg {
	resp := new(dns.Msg)

	switch strings.ToLower(q.Name) {
	case strings.ToLower(srv.ServiceName()):
		ptr := PTR(srv)
		resp.Answer = []dns.RR{ptr}

		extra := []dns.RR{SRV(srv), TXT(srv)}

		for _, a := range A(srv, *req.iface) {
			extra = append(extra, a)
		}

		for _, aaaa := range AAAA(srv, *req.iface) {
			extra = append(extra, aaaa)
		}

		extra = append(extra, NSEC(ptr, srv, *req.iface))
		resp.Extra = extra

		// Wait 20-125 msec for shared resource responses
		delay := time.Duration(r.random.Intn(105)+20) * time.Millisecond
		log.Debug.Println("Shared record response wait", delay)
		time.Sleep(delay)

	case strings.ToLower(srv.ServiceInstanceName()):
		resp.Answer = []dns.RR{SRV(srv), TXT(srv), PTR(srv)}

		var extra []dns.RR

		for _, a := range A(srv, *req.iface) {
			extra = append(extra, a)
		}

		for _, aaaa := range AAAA(srv, *req.iface) {
			extra = append(extra, aaaa)
		}

		nsec := NSEC(SRV(srv), srv, *req.iface)
		if nsec != nil {
			extra = append(extra, nsec)
		}

		resp.Extra = extra

		// Set cache flush bit for non-shared records
		setAnswerCacheFlushBit(resp)

	case strings.ToLower(srv.Hostname()):
		var answer []dns.RR

		for _, a := range A(srv, *req.iface) {
			answer = append(answer, a)
		}

		for _, aaaa := range AAAA(srv, *req.iface) {
			answer = append(answer, aaaa)
		}

		resp.Answer = answer
		nsec := NSEC(SRV(srv), srv, *req.iface)

		if nsec != nil {
			resp.Extra = []dns.RR{nsec}
		}

		// Set cache flush bit for non-shared records
		setAnswerCacheFlushBit(resp)

	case strings.ToLower(srv.ServicesMetaQueryName()):
		resp.Answer = []dns.RR{DNSSDServicesPTR(srv)}

	default:
		return nil
	}

	// Supress known answers
	resp.Answer = remove(req.msg.Answer, resp.Answer)

	resp.SetReply(req.msg)
	resp.Question = nil
	resp.Response = true
	resp.Authoritative = true

	return resp
}

func findConflicts(req *Request, hs []*serviceHandle) []*serviceHandle {
	var conflicts []*serviceHandle
	for _, h := range hs {
		if containsConflictingAnswers(req, h) {
			log.Debug.Println("Received conflicting record", req.msg)
			conflicts = append(conflicts, h)
		}
	}

	return conflicts
}

func services(hs []*serviceHandle) []*Service {
	var result []*Service
	for _, h := range hs {
		result = append(result, h.service)
	}

	return result
}

func containsConflictingAnswers(req *Request, handle *serviceHandle) bool {
	as := A(*handle.service, *req.iface)
	aaaas := AAAA(*handle.service, *req.iface)
	srv := SRV(*handle.service)

	reqAs, reqAAAAs, reqSRVs := splitRecords(filterRecords(req.msg, handle.service))

	if len(reqAs) > 0 && areDenyingAs(reqAs, as) {
		log.Debug.Printf("%v != %v\n", reqAs, as)
		return true
	}

	if len(reqAAAAs) > 0 && areDenyingAAAAs(reqAAAAs, aaaas) {
		log.Debug.Printf("%v != %v\n", reqAAAAs, aaaas)
		return true
	}

	for _, reqSRV := range reqSRVs {
		if isDenyingSRV(reqSRV, srv) {
			return true
		}
	}

	return false
}
<commit_msg>Remove log output<commit_after>package dnssd

import (
	"context"
	"fmt"
	"github.com/brutella/dnssd/log"
	"github.com/miekg/dns"
	"math/rand"
	"net"
	"strings"
	"sync"
	"time"
)

type ReadFunc func(*Request)

// Responder represents a mDNS responder.
type Responder interface {
	// Add adds a service to the responder.
	// Use the returned service handle to update service properties.
	Add(srv Service) (ServiceHandle, error)

	// Remove removes the service associated with the service handle from the responder.
	Remove(srv ServiceHandle)

	// Respond makes the receiver announcing and managing services.
	Respond(ctx context.Context) error

	// Debug calls a function for every dns request the responder receives.
	Debug(ctx context.Context, fn ReadFunc)
}

type responder struct {
	isRunning bool

	conn      MDNSConn
	unmanaged []*serviceHandle
	managed   []*serviceHandle

	mutex     *sync.Mutex
	truncated *Request
	random    *rand.Rand
	upIfaces  []string
}

func NewResponder() (Responder, error) {
	conn, err := newMDNSConn()
	if err != nil {
		return nil, err
	}

	return newResponder(conn), nil
}

func newResponder(conn MDNSConn) *responder {
	return &responder{
		isRunning: false,
		conn:      conn,
		unmanaged: []*serviceHandle{},
		managed:   []*serviceHandle{},
		mutex:     &sync.Mutex{},
		random:    rand.New(rand.NewSource(time.Now().UnixNano())),
		upIfaces:  []string{},
	}
}

func (r *responder) Remove(h ServiceHandle) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	for i, s := range r.managed {
		if h == s {
			handle := h.(*serviceHandle)
			r.unannounce([]*Service{handle.service})
			r.managed = append(r.managed[:i], r.managed[i+1:]...)
			return
		}
	}
}

func (r *responder) Add(srv Service) (ServiceHandle, error) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	if r.isRunning {
		ctx, cancel := context.WithCancel(context.TODO())
		defer cancel()

		if srv, err := r.register(ctx, srv); err != nil {
			return nil, err
		} else {
			return r.addManaged(srv), nil
		}
	}

	return r.addUnmanaged(srv), nil
}

func (r *responder) Respond(ctx context.Context) error {
	r.mutex.Lock()
	r.isRunning = true
	for _, h := range r.unmanaged {
		log.Debug.Println(h.service)
		if srv, err := r.register(ctx, *h.service); err != nil {
			return err
		} else {
			h.service = &srv
			r.managed = append(r.managed, h)
		}
	}
	r.unmanaged = []*serviceHandle{}
	r.mutex.Unlock()

	return r.respond(ctx)
}

// announce sends announcement messages including all services.
func (r *responder) announce(services []*Service) {
	for _, service := range services {
		for _, iface := range service.Interfaces() {
			go r.announceAtInterface(service, iface)
		}
	}
}

func (r *responder) announceAtInterface(service *Service, iface net.Interface) {
	ips := service.IPsAtInterface(iface)
	if len(ips) == 0 {
		log.Debug.Printf("No IPs for service %s at %s\n", service.ServiceInstanceName(), iface.Name)
		return
	}

	var answer []dns.RR
	answer = append(answer, SRV(*service))
	answer = append(answer, PTR(*service))
	answer = append(answer, TXT(*service))
	for _, a := range A(*service, iface) {
		answer = append(answer, a)
	}
	for _, aaaa := range AAAA(*service, iface) {
		answer = append(answer, aaaa)
	}
	msg := new(dns.Msg)
	msg.Answer = answer
	msg.Response = true
	msg.Authoritative = true

	setAnswerCacheFlushBit(msg)

	resp := &Response{msg: msg, iface: &iface}

	log.Debug.Println("Sending 1st announcement", msg)
	r.conn.SendResponse(resp)
	time.Sleep(1 * time.Second)
	log.Debug.Println("Sending 2nd announcement", msg)
	r.conn.SendResponse(resp)
}

func (r *responder) register(ctx context.Context, srv Service) (Service, error) {
	if !r.isRunning {
		return srv, fmt.Errorf("cannot register service when responder is not responding")
	}

	log.Debug.Printf("Probing for host %s and service %s…\n", srv.Hostname(), srv.ServiceInstanceName())
	probed, err := ProbeService(ctx, srv)
	if err != nil {
		return srv, err
	}

	srvs := []*Service{&probed}
	for _, h := range r.managed {
		srvs = append(srvs, h.service)
	}
	go r.announce(srvs)

	return probed, nil
}

func (r *responder) addManaged(srv Service) ServiceHandle {
	h := &serviceHandle{&srv}
	r.managed = append(r.managed, h)
	return h
}

func (r *responder) addUnmanaged(srv Service) ServiceHandle {
	h := &serviceHandle{&srv}
	r.unmanaged = append(r.unmanaged, h)
	return h
}

func (r *responder) respond(ctx context.Context) error {
	if !r.isRunning {
		return fmt.Errorf("isRunning should be true before calling respond()")
	}

	readCtx, readCancel := context.WithCancel(ctx)
	defer readCancel()
	ch := r.conn.Read(readCtx)

	for {
		select {
		case req := <-ch:
			r.mutex.Lock()
			r.handleRequest(req)
			r.mutex.Unlock()

		case <-ctx.Done():
			r.unannounce(services(r.managed))
			r.conn.Close()
			r.isRunning = false
			return ctx.Err()
		}
	}
}

func (r *responder) handleRequest(req *Request) {
	if len(r.managed) == 0 {
		// Ignore requests when no services are managed
		return
	}

	// If messages is truncated, we wait for the next message to come (RFC6762 18.5)
	if req.msg.Truncated {
		r.truncated = req
		log.Debug.Println("Waiting for additional answers...")
		return
	}

	// append request
	if r.truncated != nil && r.truncated.from.IP.Equal(req.from.IP) {
		log.Debug.Println("Add answers to truncated message")
		msgs := []*dns.Msg{r.truncated.msg, req.msg}
		r.truncated = nil
		req.msg = mergeMsgs(msgs)
	}

	// Conflicting records remove managed services from
	// the responder and trigger reprobing
	conflicts := findConflicts(req, r.managed)
	for _, h := range conflicts {
		log.Debug.Println("Reprobe for", h.service)
		go r.reprobe(h)

		for i, m := range r.managed {
			if h == m {
				r.managed = append(r.managed[:i], r.managed[i+1:]...)
				break
			}
		}
	}

	r.handleQuery(req, services(r.managed))
}

func (r *responder) unannounce(services []*Service) {
	if len(services) == 0 {
		return
	}

	log.Debug.Println("Send goodbye for", services)

	// collect records per interface
	rrsByIfaceName := map[string][]dns.RR{}
	for _, srv := range services {
		rr := PTR(*srv)
		rr.Header().Ttl = 0
		for _, iface := range srv.Interfaces() {
			ips := srv.IPsAtInterface(iface)
			if len(ips) == 0 {
				continue
			}
			if rrs, ok := rrsByIfaceName[iface.Name]; ok {
				rrsByIfaceName[iface.Name] = append(rrs, rr)
			} else {
				rrsByIfaceName[iface.Name] = []dns.RR{rr}
			}
		}
	}

	// send on goodbye packet on every interface
	for name, rrs := range rrsByIfaceName {
		iface, err := net.InterfaceByName(name)
		if err != nil {
			log.Debug.Printf("Interface %s not found\n", name)
			continue
		}
		msg := new(dns.Msg)
		msg.Answer = rrs
		msg.Response = true
		msg.Authoritative = true
		resp := &Response{msg: msg, iface: iface}
		r.conn.SendResponse(resp)
		time.Sleep(250 * time.Millisecond)
		r.conn.SendResponse(resp)
	}
}

func (r *responder) handleQuery(req *Request, services []*Service) {
	for _, q := range req.msg.Question {
		msgs := []*dns.Msg{}
		for _, srv := range services {
			log.Debug.Printf("%s tries to give response to question %v\n", srv.ServiceInstanceName(), q)
			if msg := r.handleQuestion(q, req, *srv); msg != nil {
				msgs = append(msgs, msg)
			} else {
				log.Debug.Println("No response")
			}
		}

		msg := mergeMsgs(msgs)
		msg.SetReply(req.msg)
		msg.Question = nil
		msg.Response = true
		msg.Authoritative = true

		if len(msg.Answer) == 0 {
			log.Debug.Println("No answers")
			continue
		}

		if isUnicastQuestion(q) {
			resp := &Response{msg: msg, addr: req.from, iface: req.iface}
			log.Debug.Printf("Send unicast response\n%v to %v\n", msg, resp.addr)
			if err := r.conn.SendResponse(resp); err != nil {
				log.Debug.Println(err)
			}
		} else {
			resp := &Response{msg: msg, iface: req.iface}
			log.Debug.Printf("Send multicast response\n%v\n", msg)
			if err := r.conn.SendResponse(resp); err != nil {
				log.Debug.Println(err)
			}
		}
	}
}

func (r *responder) reprobe(h *serviceHandle) {
	ctx, cancel := context.WithCancel(context.TODO())
	defer cancel()

	probed, err := ReprobeService(ctx, *h.service)
	if err != nil {
		return
	}
	h.service = &probed

	r.mutex.Lock()
	managed := append(r.managed, h)
	r.managed = managed
	r.mutex.Unlock()

	log.Debug.Println("Reannouncing services", managed)
	go r.announce(services(managed))
}

func (r *responder) handleQuestion(q dns.Question, req *Request, srv Service) *dns.Msg {
	resp := new(dns.Msg)

	switch strings.ToLower(q.Name) {
	case strings.ToLower(srv.ServiceName()):
		ptr := PTR(srv)
		resp.Answer = []dns.RR{ptr}

		extra := []dns.RR{SRV(srv), TXT(srv)}

		for _, a := range A(srv, *req.iface) {
			extra = append(extra, a)
		}

		for _, aaaa := range AAAA(srv, *req.iface) {
			extra = append(extra, aaaa)
		}

		extra = append(extra, NSEC(ptr, srv, *req.iface))
		resp.Extra = extra

		// Wait 20-125 msec for shared resource responses
		delay := time.Duration(r.random.Intn(105)+20) * time.Millisecond
		log.Debug.Println("Shared record response wait", delay)
		time.Sleep(delay)

	case strings.ToLower(srv.ServiceInstanceName()):
		resp.Answer = []dns.RR{SRV(srv), TXT(srv), PTR(srv)}

		var extra []dns.RR

		for _, a := range A(srv, *req.iface) {
			extra = append(extra, a)
		}

		for _, aaaa := range AAAA(srv, *req.iface) {
			extra = append(extra, aaaa)
		}

		nsec := NSEC(SRV(srv), srv, *req.iface)
		if nsec != nil {
			extra = append(extra, nsec)
		}

		resp.Extra = extra

		// Set cache flush bit for non-shared records
		setAnswerCacheFlushBit(resp)

	case strings.ToLower(srv.Hostname()):
		var answer []dns.RR

		for _, a := range A(srv, *req.iface) {
			answer = append(answer, a)
		}

		for _, aaaa := range AAAA(srv, *req.iface) {
			answer = append(answer, aaaa)
		}

		resp.Answer = answer
		nsec := NSEC(SRV(srv), srv, *req.iface)

		if nsec != nil {
			resp.Extra = []dns.RR{nsec}
		}

		// Set cache flush bit for non-shared records
		setAnswerCacheFlushBit(resp)

	case strings.ToLower(srv.ServicesMetaQueryName()):
		resp.Answer = []dns.RR{DNSSDServicesPTR(srv)}

	default:
		return nil
	}

	// Supress known answers
	resp.Answer = remove(req.msg.Answer, resp.Answer)

	resp.SetReply(req.msg)
	resp.Question = nil
	resp.Response = true
	resp.Authoritative = true

	return resp
}

func findConflicts(req *Request, hs []*serviceHandle) []*serviceHandle {
	var conflicts []*serviceHandle
	for _, h := range hs {
		if containsConflictingAnswers(req, h) {
			log.Debug.Println("Received conflicting record", req.msg)
			conflicts = append(conflicts, h)
		}
	}

	return conflicts
}

func services(hs []*serviceHandle) []*Service {
	var result []*Service
	for _, h := range hs {
		result = append(result, h.service)
	}

	return result
}

func containsConflictingAnswers(req *Request, handle *serviceHandle) bool {
	as := A(*handle.service, *req.iface)
	aaaas := AAAA(*handle.service, *req.iface)
	srv := SRV(*handle.service)

	reqAs, reqAAAAs, reqSRVs := splitRecords(filterRecords(req.msg, handle.service))

	if len(reqAs) > 0 && areDenyingAs(reqAs, as) {
		log.Debug.Printf("%v != %v\n", reqAs, as)
		return true
	}

	if len(reqAAAAs) > 0 && areDenyingAAAAs(reqAAAAs, aaaas) {
		log.Debug.Printf("%v != %v\n", reqAAAAs, aaaas)
		return true
	}

	for _, reqSRV := range reqSRVs {
		if isDenyingSRV(reqSRV, srv) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package ace

import (
	"github.com/julienschmidt/httprouter"
	"github.com/plimble/utils/pool"
	"net/http"
	"sync"
)

var bufPool = pool.NewBufferPool(100)

type Ace struct {
	*Router
	httprouter   *httprouter.Router
	pool         sync.Pool
	render       Renderer
	panicFunc    PanicHandler
	notfoundFunc HandlerFunc
}

type PanicHandler func(c *C, rcv interface{})
type HandlerFunc func(c *C)

func GetPool() *pool.BufferPool {
	return bufPool
}

//New server
func New() *Ace {
	a := &Ace{}
	a.Router = &Router{
		handlers: nil,
		prefix:   "/",
		ace:      a,
	}
	a.panicFunc = defaultPanic
	a.notfoundFunc = defaultNotfound
	a.httprouter = httprouter.New()
	a.pool.New = func() interface{} {
		c := &C{}
		c.index = -1
		c.Writer = &c.writercache
		return c
	}

	a.httprouter.PanicHandler = func(w http.ResponseWriter, req *http.Request, rcv interface{}) {
		c := a.createContext(w, req)
		a.panicFunc(c, rcv)
		a.pool.Put(c)
	}

	a.httprouter.NotFound = func(w http.ResponseWriter, req *http.Request) {
		c := a.createContext(w, req)
		a.notfoundFunc(c)
		a.pool.Put(c)
	}

	return a
}

//Default server white recovery and logger middleware
func Default() *Ace {
	a := New()
	a.Use(Logger())
	return a
}

//SetPoolSize of buffer
func (a *Ace) SetPoolSize(poolSize int) {
	bufPool = pool.NewBufferPool(poolSize)
}

//Run server with specific address and port
func (a *Ace) Run(addr string) {
	if err := http.ListenAndServe(addr, a); err != nil {
		panic(err)
	}
}

//RunTLS server with specific address and port
func (a *Ace) RunTLS(addr string, cert string, key string) {
	if err := http.ListenAndServeTLS(addr, cert, key, a); err != nil {
		panic(err)
	}
}

//ServeHTTP implement http.Handler
func (a *Ace) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	a.httprouter.ServeHTTP(w, req)
}
<commit_msg>fix using http.Handler on NotFound<commit_after>package ace

import (
	"github.com/julienschmidt/httprouter"
	"github.com/plimble/utils/pool"
	"net/http"
	"sync"
)

var bufPool = pool.NewBufferPool(100)

type Ace struct {
	*Router
	httprouter   *httprouter.Router
	pool         sync.Pool
	render       Renderer
	panicFunc    PanicHandler
	notfoundFunc HandlerFunc
}

type PanicHandler func(c *C, rcv interface{})
type HandlerFunc func(c *C)

func GetPool() *pool.BufferPool {
	return bufPool
}

//New server
func New() *Ace {
	a := &Ace{}
	a.Router = &Router{
		handlers: nil,
		prefix:   "/",
		ace:      a,
	}
	a.panicFunc = defaultPanic
	a.notfoundFunc = defaultNotfound
	a.httprouter = httprouter.New()
	a.pool.New = func() interface{} {
		c := &C{}
		c.index = -1
		c.Writer = &c.writercache
		return c
	}

	a.httprouter.PanicHandler = func(w http.ResponseWriter, req *http.Request, rcv interface{}) {
		c := a.createContext(w, req)
		a.panicFunc(c, rcv)
		a.pool.Put(c)
	}

	a.httprouter.NotFound = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		c := a.createContext(w, req)
		a.notfoundFunc(c)
		a.pool.Put(c)
	})

	return a
}

//Default server white recovery and logger middleware
func Default() *Ace {
	a := New()
	a.Use(Logger())
	return a
}

//SetPoolSize of buffer
func (a *Ace) SetPoolSize(poolSize int) {
	bufPool = pool.NewBufferPool(poolSize)
}

//Run server with specific address and port
func (a *Ace) Run(addr string) {
	if err := http.ListenAndServe(addr, a); err != nil {
		panic(err)
	}
}

//RunTLS server with specific address and port
func (a *Ace) RunTLS(addr string, cert string, key string) {
	if err := http.ListenAndServeTLS(addr, cert, key, a); err != nil {
		panic(err)
	}
}

//ServeHTTP implement http.Handler
func (a *Ace) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	a.httprouter.ServeHTTP(w, req)
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Antoine Tenart <antoine.tenart@ack.tf>

package acl

import (
	"errors"
	"net"
	"net/http"
	"strconv"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

type ACL struct {
	Next	httpserver.Handler
	Config	[]ACLBlockConfig
}

// describe one acl{} configuration block
type ACLBlockConfig struct {
	Paths	[]string
	Allow	[]*net.IPNet
	Deny	[]*net.IPNet
	Status	int
}

func parsePrefix(c *caddy.Controller) (*net.IPNet, error) {
	if !c.NextArg() {
		return nil, c.ArgErr()
	}

	_, ret, err := net.ParseCIDR(c.Val())
	if err != nil {
		return nil, err
	}

	return ret, nil
}

func parseConfigurationBlock(c *caddy.Controller) (ACLBlockConfig, error) {
	var cfg ACLBlockConfig

	// get acl{} block first arguments (base paths)
	// TODO: sort the paths: most specific first
	cfg.Paths = c.RemainingArgs()
	if len(cfg.Paths) == 0 {
		return cfg, c.ArgErr()
	}

	// navigate through statements in an acl{} block
	for c.NextBlock() {
		val := c.Val()

		switch val {
		case "allow":
			rule, err := parsePrefix(c)
			if err != nil {
				return cfg, err
			}

			cfg.Allow = append(cfg.Allow, rule)
		case "deny":
			rule, err := parsePrefix(c)
			if err != nil {
				return cfg, err
			}

			cfg.Deny = append(cfg.Deny, rule)
		case "status":
			if !c.NextArg() {
				return cfg, c.ArgErr()
			}

			status, _ := strconv.Atoi(c.Val())
			if http.StatusText(status) == "" {
				// invalid HTTP status code
				return cfg, c.Err("acl: invalid status code")
			}

			cfg.Status = status
		}
	}

	return cfg, nil
}

func parseConfiguration(c *caddy.Controller) ([]ACLBlockConfig, error) {
	var config []ACLBlockConfig

	// navigate through the acl{} blocks
	for c.Next() {
		blockConf, err := parseConfigurationBlock(c)
		if err != nil {
			return config, err
		}

		config = append(config, blockConf)
	}

	return config, nil
}

func isBehindACL(urlPath string, cfg ACLBlockConfig) bool {
	for _, path := range cfg.Paths {
		if httpserver.Path(urlPath).Matches(path) {
			return true
		}
	}
	return false
}

func clientIP(r *http.Request) (net.IP, error) {
	var ip string
	var err error

	// TODO: handle user defined headers

	if tmp := r.Header.Get("X-Forwarded-For"); tmp != "" {
		ip = tmp
	} else {
		ip, _, err = net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			return nil, err
		}
	}

	ret := net.ParseIP(ip)
	if ret == nil {
		return nil, errors.New("acl: unable to parse address")
	}

	return ret, nil
}

func isInSubnets(client net.IP, subs []*net.IPNet) bool {
	for _, subnet := range subs {
		if subnet.Contains(client) {
			return true
		}
	}
	return false
}

func deny(w *http.ResponseWriter) (int, error) {
	return http.StatusForbidden, nil
}

func (self ACL) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, cfg := range self.Config {
		// check if the URL path is behind this acl{} block
		if !isBehindACL(r.URL.Path, cfg) {
			continue
		}

		client, err := clientIP(r)
		if err != nil {
			return http.StatusInternalServerError, err
		}

		if isInSubnets(client, cfg.Deny) {
			return deny(&w)
		}

		if !isInSubnets(client, cfg.Allow) && len(cfg.Allow) > 0 {
			return deny(&w)
		}
	}

	return self.Next.ServeHTTP(w, r)
}

func setup(c *caddy.Controller) error {
	// parse Caddyfile for acl configuration statements
	aclConf, err := parseConfiguration(c)
	if err != nil {
		return err
	}

	// create a new Caddy middleware
	mw := func(next httpserver.Handler) httpserver.Handler {
		return &ACL{
			Next:	next,
			Config:	aclConf,
		}
	}

	// register the new Caddy middleware
	cfg := httpserver.GetConfig(c)
	cfg.AddMiddleware(mw)

	return nil
}

func init() {
	caddy.RegisterPlugin("acl", caddy.Plugin{
		ServerType:	"http",
		Action:		setup,
	})
}
<commit_msg>Take in account the user defined status code<commit_after>// Copyright (C) 2016 Antoine Tenart <antoine.tenart@ack.tf>

package acl

import (
	"errors"
	"net"
	"net/http"
	"strconv"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

type ACL struct {
	Next	httpserver.Handler
	Config	[]ACLBlockConfig
}

// describe one acl{} configuration block
type ACLBlockConfig struct {
	Paths	[]string
	Allow	[]*net.IPNet
	Deny	[]*net.IPNet
	Status	int
}

func parsePrefix(c *caddy.Controller) (*net.IPNet, error) {
	if !c.NextArg() {
		return nil, c.ArgErr()
	}

	_, ret, err := net.ParseCIDR(c.Val())
	if err != nil {
		return nil, err
	}

	return ret, nil
}

func parseConfigurationBlock(c *caddy.Controller) (ACLBlockConfig, error) {
	var cfg ACLBlockConfig
	cfg.Status = -1

	// get acl{} block first arguments (base paths)
	// TODO: sort the paths: most specific first
	cfg.Paths = c.RemainingArgs()
	if len(cfg.Paths) == 0 {
		return cfg, c.ArgErr()
	}

	// navigate through statements in an acl{} block
	for c.NextBlock() {
		val := c.Val()

		switch val {
		case "allow":
			rule, err := parsePrefix(c)
			if err != nil {
				return cfg, err
			}

			cfg.Allow = append(cfg.Allow, rule)
		case "deny":
			rule, err := parsePrefix(c)
			if err != nil {
				return cfg, err
			}

			cfg.Deny = append(cfg.Deny, rule)
		case "status":
			if !c.NextArg() {
				return cfg, c.ArgErr()
			}

			status, _ := strconv.Atoi(c.Val())
			if http.StatusText(status) == "" {
				// invalid HTTP status code
				return cfg, c.Err("acl: invalid status code")
			}

			cfg.Status = status
		}
	}

	return cfg, nil
}

func parseConfiguration(c *caddy.Controller) ([]ACLBlockConfig, error) {
	var config []ACLBlockConfig

	// navigate through the acl{} blocks
	for c.Next() {
		blockConf, err := parseConfigurationBlock(c)
		if err != nil {
			return config, err
		}

		config = append(config, blockConf)
	}

	return config, nil
}

func isBehindACL(urlPath string, cfg ACLBlockConfig) bool {
	for _, path := range cfg.Paths {
		if httpserver.Path(urlPath).Matches(path) {
			return true
		}
	}
	return false
}

func clientIP(r *http.Request) (net.IP, error) {
	var ip string
	var err error

	// TODO: handle user defined headers

	if tmp := r.Header.Get("X-Forwarded-For"); tmp != "" {
		ip = tmp
	} else {
		ip, _, err = net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			return nil, err
		}
	}

	ret := net.ParseIP(ip)
	if ret == nil {
		return nil, errors.New("acl: unable to parse address")
	}

	return ret, nil
}

func isInSubnets(client net.IP, subs []*net.IPNet) bool {
	for _, subnet := range subs {
		if subnet.Contains(client) {
			return true
		}
	}
	return false
}

func deny(w *http.ResponseWriter, cfg ACLBlockConfig) (int, error) {
	if cfg.Status != -1 {
		return cfg.Status, nil
	}

	return http.StatusForbidden, nil
}

func (self ACL) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, cfg := range self.Config {
		// check if the URL path is behind this acl{} block
		if !isBehindACL(r.URL.Path, cfg) {
			continue
		}

		client, err := clientIP(r)
		if err != nil {
			return http.StatusInternalServerError, err
		}

		if isInSubnets(client, cfg.Deny) {
			return deny(&w, cfg)
		}

		if !isInSubnets(client, cfg.Allow) && len(cfg.Allow) > 0 {
			return deny(&w, cfg)
		}
	}

	return self.Next.ServeHTTP(w, r)
}

func setup(c *caddy.Controller) error {
	// parse Caddyfile for acl configuration statements
	aclConf, err := parseConfiguration(c)
	if err != nil {
		return err
	}

	// create a new Caddy middleware
	mw := func(next httpserver.Handler) httpserver.Handler {
		return &ACL{
			Next:	next,
			Config:	aclConf,
		}
	}

	// register the new Caddy middleware
	cfg := httpserver.GetConfig(c)
	cfg.AddMiddleware(mw)

	return nil
}

func init() {
	caddy.RegisterPlugin("acl", caddy.Plugin{
		ServerType:	"http",
		Action:		setup,
	})
}
<|endoftext|>


<commit_before>// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

// Package compress is a collection of compression libraries.
package compress

import "io"
import "bufio"

// The Error interface identifies all compression related errors.
type Error interface {
	error
	CompressError()
}

// ByteReader is a common interface accepted by all compression Readers.
// It guarantees that the decompressor never reads more data than is necessary
// from the underlying io.Reader.
type ByteReader interface {
	io.Reader
	io.ByteReader
}

var _ ByteReader = (*bufio.Reader)(nil)

// BufferedReader is another interface accepted by all compression Readers.
// It guarantees that the decompressor never reads more data than is necessary
// from the underlying io.Reader. Since BufferedReader allows a decompressor
// to peek at bytes further along in the stream without advancing the read
// pointer, decompression can experience a significant performance gain when
// provided a reader that satisfies this interface. Thus, a decompressor will
// prefer this interface over ByteReader for performance reasons.
//
// The bufio.Reader satisfies this interface.
type BufferedReader interface {
	io.Reader

	// Buffered returns the number of bytes currently buffered.
	Buffered() int

	// Peek returns the next n bytes without advancing the reader.
	//
	// If Peek returns fewer than n bytes, it also returns an error explaining
	// why the peek is short. Peek must support peeking of at least 8 bytes.
	// If 0 <= n <= Buffered(), Peek is guaranteed to succeed without reading
	// from the underlying io.Reader.
	Peek(n int) ([]byte, error)

	// Discard skips the next n bytes, returning the number of bytes discarded.
	//
	// If Discard skips fewer than n bytes, it also returns an error.
	// If 0 <= n <= Buffered(), Discard is guaranteed to succeed without reading
	// from the underlying io.Reader.
	Discard(n int) (int, error)
}

var _ BufferedReader = (*bufio.Reader)(nil)
<commit_msg>api: minor changes to documentation<commit_after>// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

// Package compress is a collection of compression libraries.
package compress

import "io"
import "bufio"

// The Error interface identifies all compression related errors.
type Error interface {
	error
	CompressError()
}

// ByteReader is an interface accepted by all decompression Readers.
// It guarantees that the decompressor never reads more data than is necessary
// from the underlying io.Reader.
type ByteReader interface {
	io.Reader
	io.ByteReader
}

var _ ByteReader = (*bufio.Reader)(nil)

// BufferedReader is an interface accepted by all decompression Readers.
// It guarantees that the decompressor never reads more data than is necessary
// from the underlying io.Reader. Since BufferedReader allows a decompressor
// to peek at bytes further along in the stream without advancing the read
// pointer, decompression can experience a significant performance gain when
// provided a reader that satisfies this interface. Thus, a decompressor will
// prefer this interface over ByteReader for performance reasons.
//
// The bufio.Reader satisfies this interface.
type BufferedReader interface {
	io.Reader

	// Buffered returns the number of bytes currently buffered.
	//
	// This value becomes invalid following the next Read/Discard operation.
	Buffered() int

	// Peek returns the next n bytes without advancing the reader.
	//
	// If Peek returns fewer than n bytes, it also returns an error explaining
	// why the peek is short. Peek must support peeking of at least 8 bytes.
	// If 0 <= n <= Buffered(), Peek is guaranteed to succeed without reading
	// from the underlying io.Reader.
	//
	// This result becomes invalid following the next Read/Discard operation.
	Peek(n int) ([]byte, error)

	// Discard skips the next n bytes, returning the number of bytes discarded.
	//
	// If Discard skips fewer than n bytes, it also returns an error.
	// If 0 <= n <= Buffered(), Discard is guaranteed to succeed without reading
	// from the underlying io.Reader.
	Discard(n int) (int, error)
}

var _ BufferedReader = (*bufio.Reader)(nil)
<|endoftext|>


<commit_before>package demo

import (
	"bytes"
	"fmt"
	"io"
	"net/http"

	"appengine"

	newappengine "google.golang.org/appengine"
)

const contactsFeedUrl = `https://www.google.com/m8/feeds/contacts/%s/%s?v=3.0&start-index=%d&max-results=%d&alt=%s`

func getContactsFeedUrl(feedtype, domain string, full bool, start, number int) string {
	var projection = `full`
	if !full {
		projection = `thin`
	}

	if feedtype != `rss` && feedtype != `json` {
		feedtype = `atom`
	}

	return fmt.Sprintf(contactsFeedUrl, domain, projection, start, number, feedtype)
}

func getContactsBatchUrl(links []Link) string {
	for _, link := range links {
		if link.Rel == `http://schemas.google.com/g/2005#batch` {
			return link.Href
		}
	}

	return ``
}

func loadAllContacts(domain string, client *http.Client, context appengine.Context) (buf *bytes.Buffer) {
	res, err := client.Get(getContactsFeedUrl(`atom`, domain, false, 1, 100))
	if err != nil {
		context.Errorf("get: %v", err)
		return
	}

	defer res.Body.Close()

	buf = new(bytes.Buffer)
	io.Copy(buf, res.Body)
	return
}

func getOAuthClient(context appengine.Context, r *http.Request) *http.Client {
	newctx := newappengine.NewContext(r)

	tok, err := config.Exchange(newctx, r.FormValue("code"))
	if err != nil {
		context.Errorf("exchange error: %v", err)
		return nil
	}

	context.Infof("tok: %v", tok)

	return config.Client(newctx, tok)
}

const batchFeedTemplate = `<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom'
      xmlns:gContact='http://schemas.google.com/contact/2008'
      xmlns:gd='http://schemas.google.com/g/2005'
      xmlns:batch='http://schemas.google.com/gdata/batch'>
  <category scheme='http://schemas.google.com/g/2005#kind'
      term='http://schemas.google.com/g/2008#contact' />
  %s
</feed>`
<commit_msg>add copyright notice header<commit_after>// Copyright 2016 Takbok, Inc. All rights reserved.
//
// 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 demo

import (
	"bytes"
	"fmt"
	"io"
	"net/http"

	"appengine"

	newappengine "google.golang.org/appengine"
)

const contactsFeedUrl = `https://www.google.com/m8/feeds/contacts/%s/%s?v=3.0&start-index=%d&max-results=%d&alt=%s`

func getContactsFeedUrl(feedtype, domain string, full bool, start, number int) string {
	var projection = `full`
	if !full {
		projection = `thin`
	}

	if feedtype != `rss` && feedtype != `json` {
		feedtype = `atom`
	}

	return fmt.Sprintf(contactsFeedUrl, domain, projection, start, number, feedtype)
}

func getContactsBatchUrl(links []Link) string {
	for _, link := range links {
		if link.Rel == `http://schemas.google.com/g/2005#batch` {
			return link.Href
		}
	}

	return ``
}

func loadAllContacts(domain string, client *http.Client, context appengine.Context) (buf *bytes.Buffer) {
	res, err := client.Get(getContactsFeedUrl(`atom`, domain, false, 1, 100))
	if err != nil {
		context.Errorf("get: %v", err)
		return
	}

	defer res.Body.Close()

	buf = new(bytes.Buffer)
	io.Copy(buf, res.Body)
	return
}

func getOAuthClient(context appengine.Context, r *http.Request) *http.Client {
	newctx := newappengine.NewContext(r)

	tok, err := config.Exchange(newctx, r.FormValue("code"))
	if err != nil {
		context.Errorf("exchange error: %v", err)
		return nil
	}

	context.Infof("tok: %v", tok)

	return config.Client(newctx, tok)
}

const batchFeedTemplate = `<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom'
      xmlns:gContact='http://schemas.google.com/contact/2008'
      xmlns:gd='http://schemas.google.com/g/2005'
      xmlns:batch='http://schemas.google.com/gdata/batch'>
  <category scheme='http://schemas.google.com/g/2005#kind'
      term='http://schemas.google.com/g/2008#contact' />
  %s
</feed>`
<|endoftext|>


<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type UsersResource struct {
	Data []User `json:"data"`
}

type UserResource struct {
	Data User `json:"data"`
}

// Structure representing error
type errorResponse struct {
	Msg string `json:"msg"`
}

var ServerName = "http://localhost:8080"

func responseDetailsFromMongoError(error interface{}) (content errorResponse, code int) {
	content = errorResponse{Msg: fmt.Sprint(error)}
	code = 400
	if content.Msg == "not found" {
		code = 404
	}
	return content, code
}

// write json response to http response
func writeJsonResponse(w http.ResponseWriter, content *[]byte, code int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	w.Write(*content)
}

// addUserHandler used to add new user
func (h *HTTPClientHandler) addUserHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", ServerName)
	// adding new user to database
	var userRequest User

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &userRequest)

	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(422) // can't process this entity
		return
	}

	log.WithFields(log.Fields{
		"firstName":     userRequest.FirstName,
		"lastName":      userRequest.LastName,
		"userID":        userRequest.UserID,
		"profilePicUrl": userRequest.ProfilePicUrl,
		"gender":        userRequest.Gender,
		"body":          string(body),
	}).Info("Got user info")

	// adding user
	err = h.db.addUser(userRequest)

	if err == nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(201) // user inserted
		return
	} else {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warn("Failed to insert..")

		content, code := responseDetailsFromMongoError(err)

		// Marshal provided interface into JSON structure
		uj, _ := json.Marshal(content)

		// Write content-type, statuscode, payload
		writeJsonResponse(w, &uj, code)

	}

}

// getAllUsersHandler used to get all users
func (h *HTTPClientHandler) getAllUsersHandler(w http.ResponseWriter, r *http.Request) {

	userid, _ := r.URL.Query()["q"]
	// looking for specific user
	if len(userid) > 0 {
		log.WithFields(log.Fields{
			"userid": userid[0],
		}).Info("Looking for user..")

		user, err := h.db.getUser(userid[0])

		if err == nil {
			// Marshal provided interface into JSON structure
			response := UserResource{Data: user}
			uj, _ := json.Marshal(response)

			// Write content-type, statuscode, payload
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(200)
			fmt.Fprintf(w, "%s", uj)
			return
		} else {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Warn("Failed to insert..")

			content, code := responseDetailsFromMongoError(err)

			// Marshal provided interface into JSON structure
			uj, _ := json.Marshal(content)

			// Write content-type, statuscode, payload
			writeJsonResponse(w, &uj, code)
			return

		}
	}

	log.Warn(len(userid))
	// displaying all users
	results, err := h.db.getUsers()
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error when tried to get all users")
	}

	log.WithFields(log.Fields{
		"count": len(results),
	}).Info("number of users")

	// Marshal provided interface into JSON structure
	response := UsersResource{Data: results}
	uj, _ := json.Marshal(response)

	// Write content-type, statuscode, payload
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	fmt.Fprintf(w, "%s", uj)
}

// addPlaceHandler add new hosting place, provide json
func (h *HTTPClientHandler) addPlaceHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", ServerName)
	// adding new hosting place to database
	var hostingPlaceRequest HostingPlace
	log.Info("adding place........")
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &hostingPlaceRequest)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Failed to unmarshal json!")
	}

	log.WithFields(log.Fields{
		"body":   string(body),
		"host":   hostingPlaceRequest.Host,
		"active": hostingPlaceRequest.Active,
		"lat":    hostingPlaceRequest.Lat,
		"long":   hostingPlaceRequest.Long,
	}).Info("Got place info")

	err = h.db.addHostingPlace(hostingPlaceRequest)

	if err == nil {
		// adding it to esri
		h.addEsriNode(hostingPlaceRequest)

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(201) // place inserted
		return
	} else {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warn("Failed to insert hosting place..")

		content, code := responseDetailsFromMongoError(err)

		// Marshal provided interface into JSON structure
		uj, _ := json.Marshal(content)

		// Write content-type, statuscode, payload
		writeJsonResponse(w, &uj, code)

	}

}

func (h *HTTPClientHandler) getPlaceHandler(w http.ResponseWriter, r *http.Request) {

}
<commit_msg>add booking api<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type UsersResource struct {
	Data []User `json:"data"`
}

type UserResource struct {
	Data User `json:"data"`
}

// Structure representing error
type errorResponse struct {
	Msg string `json:"msg"`
}

var ServerName = "http://localhost:8080"

func responseDetailsFromMongoError(error interface{}) (content errorResponse, code int) {
	content = errorResponse{Msg: fmt.Sprint(error)}
	code = 400
	if content.Msg == "not found" {
		code = 404
	}
	return content, code
}

// write json response to http response
func writeJsonResponse(w http.ResponseWriter, content *[]byte, code int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	w.Write(*content)
}

// addUserHandler used to add new user
func (h *HTTPClientHandler) addUserHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", ServerName)
	// adding new user to database
	var userRequest User

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &userRequest)

	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(422) // can't process this entity
		return
	}

	log.WithFields(log.Fields{
		"firstName":     userRequest.FirstName,
		"lastName":      userRequest.LastName,
		"userID":        userRequest.UserID,
		"profilePicUrl": userRequest.ProfilePicUrl,
		"gender":        userRequest.Gender,
		"body":          string(body),
	}).Info("Got user info")

	// adding user
	err = h.db.addUser(userRequest)

	if err == nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(201) // user inserted
		return
	} else {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warn("Failed to insert..")

		content, code := responseDetailsFromMongoError(err)

		// Marshal provided interface into JSON structure
		uj, _ := json.Marshal(content)

		// Write content-type, statuscode, payload
		writeJsonResponse(w, &uj, code)

	}

}

// getAllUsersHandler used to get all users
func (h *HTTPClientHandler) getAllUsersHandler(w http.ResponseWriter, r *http.Request) {

	userid, _ := r.URL.Query()["q"]
	// looking for specific user
	if len(userid) > 0 {
		log.WithFields(log.Fields{
			"userid": userid[0],
		}).Info("Looking for user..")

		user, err := h.db.getUser(userid[0])

		if err == nil {
			// Marshal provided interface into JSON structure
			response := UserResource{Data: user}
			uj, _ := json.Marshal(response)

			// Write content-type, statuscode, payload
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(200)
			fmt.Fprintf(w, "%s", uj)
			return
		} else {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Warn("Failed to insert..")

			content, code := responseDetailsFromMongoError(err)

			// Marshal provided interface into JSON structure
			uj, _ := json.Marshal(content)

			// Write content-type, statuscode, payload
			writeJsonResponse(w, &uj, code)
			return

		}
	}

	log.Warn(len(userid))
	// displaying all users
	results, err := h.db.getUsers()
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error when tried to get all users")
	}

	log.WithFields(log.Fields{
		"count": len(results),
	}).Info("number of users")

	// Marshal provided interface into JSON structure
	response := UsersResource{Data: results}
	uj, _ := json.Marshal(response)

	// Write content-type, statuscode, payload
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	fmt.Fprintf(w, "%s", uj)
}

// addPlaceHandler add new hosting place, provide json
func (h *HTTPClientHandler) addPlaceHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", ServerName)
	// adding new hosting place to database
	var hostingPlaceRequest HostingPlace
	log.Info("adding place........")
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &hostingPlaceRequest)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Failed to unmarshal json!")
	}

	log.WithFields(log.Fields{
		"body":   string(body),
		"host":   hostingPlaceRequest.Host,
		"active": hostingPlaceRequest.Active,
		"lat":    hostingPlaceRequest.Lat,
		"long":   hostingPlaceRequest.Long,
	}).Info("Got place info")

	err = h.db.addHostingPlace(hostingPlaceRequest)

	if err == nil {
		// adding it to esri
		h.addEsriNode(hostingPlaceRequest)

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(201) // place inserted
		return
	} else {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warn("Failed to insert hosting place..")

		content, code := responseDetailsFromMongoError(err)

		// Marshal provided interface into JSON structure
		uj, _ := json.Marshal(content)

		// Write content-type, statuscode, payload
		writeJsonResponse(w, &uj, code)

	}

}

func (h *HTTPClientHandler) getPlaceHandler(w http.ResponseWriter, r *http.Request) {

}

func (h *HTTPClientHandler) addBookingHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", ServerName)
	// adding new hosting place to database
	var bookingRequest Booking
	log.Info("adding place........")
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &bookingRequest)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Failed to unmarshal json!")
	}

	log.WithFields(log.Fields{
		"body": string(body),
		"host": bookingRequest.Host,
		"user": bookingRequest.User,
		"lat":  bookingRequest.Lat,
		"long": bookingRequest.Long,
	}).Info("Got place info")

	err = h.db.addBooking(bookingRequest)

	if err == nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(201) // booking inserted
		return
	} else {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warn("Failed to insert booking, fork it..")

		content, code := responseDetailsFromMongoError(err)

		// Marshal provided interface into JSON structure
		uj, _ := json.Marshal(content)

		// Write content-type, statuscode, payload
		writeJsonResponse(w, &uj, code)

	}
}
<|endoftext|>


<commit_before>package sirius

import (
	"errors"
	"fmt"
	"github.com/nlopes/slack"
	"strings"
)

type SlackID struct {
	UserID string
	TeamID string
}

type Connection interface {
	ID() (error, SlackID)
	Listen()
	Messages() chan Message
	Update(*Message) error
}

type RTMConnection struct {
	Rtm           *slack.RTM
	id            SlackID
	messages      chan Message
	authenticated bool
	client        *slack.Client
	token         string
}

func NewSlackID(userID, teamID string) SlackID {
	return SlackID{
		UserID: userID,
		TeamID: teamID,
	}
}

/*
Notice that user IDs are not guaranteed to be globally unique across all Slack users.
The combination of user ID and team ID, on the other hand, is guaranteed to be globally unique.

- Slack API documentation
*/
func (s *SlackID) Equals(o *SlackID) bool {
	return s.UserID == o.UserID && s.TeamID == o.TeamID
}

func NewRTMConnection(token string) *RTMConnection {
	client := slack.New(token)

	rtm := client.NewRTM()
	msg := make(chan Message)

	return &RTMConnection{
		Rtm:      rtm,
		messages: msg,
		client:   client,
		token:    token,
	}
}

func (conn *RTMConnection) ID() (error, SlackID) {
	if !conn.authenticated {
		return errors.New("Cannot get UserID before authentication completes"), SlackID{}
	}

	return nil, conn.id
}

func (conn *RTMConnection) Listen() {
	go conn.Rtm.ManageConnection()

	for !conn.authenticated {
		select {
		case ev := <-conn.Rtm.IncomingEvents:
			switch msg := ev.Data.(type) {
			case *slack.InvalidAuthEvent:
				panic(msg)
			case *slack.ConnectedEvent:
				conn.authenticate(msg)
				conn.handleIncomingEvent(ev)
			}
		}
	}

	for {
		select {
		case ev := <-conn.Rtm.IncomingEvents:
			conn.handleIncomingEvent(ev)
		}
	}
}

func (conn *RTMConnection) Messages() chan Message {
	return conn.messages
}

func (conn *RTMConnection) SendMessage(msg *Message) {
	omsg := conn.Rtm.NewOutgoingMessage(msg.Text, msg.Channel)
	conn.Rtm.SendMessage(omsg)
}

func (conn *RTMConnection) Update(msg *Message) error {
	_, _, _, err := conn.Rtm.UpdateMessage(msg.Channel, msg.Timestamp, msg.Text)
	return err
}

func (conn *RTMConnection) authenticate(e *slack.ConnectedEvent) bool {
	info := e.Info
	conn.id = NewSlackID(info.User.ID, info.Team.ID)
	conn.authenticated = true

	return true
}

func (conn *RTMConnection) handleIncomingEvent(ev slack.RTMEvent) {
	switch msg := ev.Data.(type) {
	case *slack.MessageEvent:
		conn.handleIncomingMessage(msg)

	case *slack.RTMError:
		fmt.Printf("Error: %s\n", msg.Error())
		panic(msg)
	}
}

func (conn *RTMConnection) handleIncomingMessage(ev *slack.MessageEvent) {
	text := removeEscapeCharacters(ev.Text)
	msg := NewMessage(text, ev.User, ev.Team, ev.Channel, ev.Timestamp)
	conn.messages <- msg
}

var escapeCharacters map[string]string = map[string]string{
	"<":  "<",
	">":  ">",
	"&": "&",
}

func removeEscapeCharacters(msg string) string {
	for html, unicode := range escapeCharacters {
		msg = strings.Replace(msg, html, unicode, -1)
	}

	return msg
}
<commit_msg>Don’t export RTM client<commit_after>package sirius

import (
	"errors"
	"fmt"
	"github.com/nlopes/slack"
	"strings"
)

type SlackID struct {
	UserID string
	TeamID string
}

type Connection interface {
	ID() (error, SlackID)
	Listen()
	Messages() chan Message
	Update(*Message) error
}

type RTMConnection struct {
	rtm           *slack.RTM
	id            SlackID
	messages      chan Message
	authenticated bool
	client        *slack.Client
	token         string
}

func NewSlackID(userID, teamID string) SlackID {
	return SlackID{
		UserID: userID,
		TeamID: teamID,
	}
}

/*
Notice that user IDs are not guaranteed to be globally unique across all Slack users.
The combination of user ID and team ID, on the other hand, is guaranteed to be globally unique.

- Slack API documentation
*/
func (s *SlackID) Equals(o *SlackID) bool {
	return s.UserID == o.UserID && s.TeamID == o.TeamID
}

func NewRTMConnection(token string) *RTMConnection {
	client := slack.New(token)

	rtm := client.NewRTM()
	msg := make(chan Message)

	return &RTMConnection{
		rtm:      rtm,
		messages: msg,
		client:   client,
		token:    token,
	}
}

func (conn *RTMConnection) ID() (error, SlackID) {
	if !conn.authenticated {
		return errors.New("Cannot get UserID before authentication completes"), SlackID{}
	}

	return nil, conn.id
}

func (conn *RTMConnection) Listen() {
	go conn.rtm.ManageConnection()

	for !conn.authenticated {
		select {
		case ev := <-conn.rtm.IncomingEvents:
			switch msg := ev.Data.(type) {
			case *slack.InvalidAuthEvent:
				panic(msg)
			case *slack.ConnectedEvent:
				conn.authenticate(msg)
				conn.handleIncomingEvent(ev)
			}
		}
	}

	for {
		select {
		case ev := <-conn.rtm.IncomingEvents:
			conn.handleIncomingEvent(ev)
		}
	}
}

func (conn *RTMConnection) Messages() chan Message {
	return conn.messages
}

func (conn *RTMConnection) SendMessage(msg *Message) {
	omsg := conn.rtm.NewOutgoingMessage(msg.Text, msg.Channel)
	conn.rtm.SendMessage(omsg)
}

func (conn *RTMConnection) Update(msg *Message) error {
	_, _, _, err := conn.rtm.UpdateMessage(msg.Channel, msg.Timestamp, msg.Text)
	return err
}

func (conn *RTMConnection) authenticate(e *slack.ConnectedEvent) bool {
	info := e.Info
	conn.id = NewSlackID(info.User.ID, info.Team.ID)
	conn.authenticated = true

	return true
}

func (conn *RTMConnection) handleIncomingEvent(ev slack.RTMEvent) {
	switch msg := ev.Data.(type) {
	case *slack.MessageEvent:
		conn.handleIncomingMessage(msg)

	case *slack.RTMError:
		fmt.Printf("Error: %s\n", msg.Error())
		panic(msg)
	}
}

func (conn *RTMConnection) handleIncomingMessage(ev *slack.MessageEvent) {
	text := removeEscapeCharacters(ev.Text)
	msg := NewMessage(text, ev.User, ev.Team, ev.Channel, ev.Timestamp)
	conn.messages <- msg
}

var escapeCharacters map[string]string = map[string]string{
	"<":  "<",
	">":  ">",
	"&": "&",
}

func removeEscapeCharacters(msg string) string {
	for html, unicode := range escapeCharacters {
		msg = strings.Replace(msg, html, unicode, -1)
	}

	return msg
}
<|endoftext|>