<commit_before>package orasrv

import (
	"context"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"reflect"
	"sync"

	"github.com/gogo/protobuf/proto"
	"github.com/pkg/errors"
	bp "github.com/tgulacsi/go/bufpool"
	oracall "github.com/tgulacsi/oracall/lib"

	"github.com/go-kit/kit/log"
	"github.com/oklog/ulid"

	"github.com/grpc-ecosystem/go-grpc-middleware"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	goracle "gopkg.in/goracle.v2"
)

var bufpool = bp.New(4096)

func GRPCServer(logger log.Logger, verbose bool, checkAuth func(ctx context.Context, path string) error, options ...grpc.ServerOption) *grpc.Server {
	erroredMethods := make(map[string]struct{})
	var erroredMethodsMu sync.RWMutex

	getLogger := func(ctx context.Context, fullMethod string) (log.Logger, func(error), context.Context) {
		reqID := ContextGetReqID(ctx)
		ctx = ContextWithReqID(ctx, reqID)
		lgr := log.With(logger, "reqID", reqID)
		ctx = ContextWithLogger(ctx, lgr)
		verbose := verbose
		var wasThere bool
		if !verbose {
			erroredMethodsMu.RLock()
			_, verbose = erroredMethods[fullMethod]
			erroredMethodsMu.RUnlock()
			wasThere = verbose
		}
		if verbose {
			ctx = goracle.ContextWithLog(ctx, log.With(lgr, "lib", "goracle").Log)
		}
		commit := func(err error) {
			if wasThere && err == nil {
				erroredMethodsMu.Lock()
				delete(erroredMethods, fullMethod)
				erroredMethodsMu.Unlock()
			} else if err != nil && !wasThere {
				erroredMethodsMu.Lock()
				erroredMethods[fullMethod] = struct{}{}
				erroredMethodsMu.Unlock()
			}
		}
		return lgr, commit, ctx
	}

	opts := []grpc.ServerOption{
		//lint:ignore SA1019 the UseCompressor API is experimental yet.
		grpc.RPCCompressor(grpc.NewGZIPCompressor()),
		//lint:ignore SA1019 the UseCompressor API is experimental yet.
		grpc.RPCDecompressor(grpc.NewGZIPDecompressor()),
		grpc.StreamInterceptor(
			func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
				defer func() {
					if r := recover(); r != nil {
						var ok bool
						if err, ok = r.(error); ok {
							logger.Log("PANIC", err)
							return
						}
						err = errors.Errorf("%+v", r)
						logger.Log("PANIC", fmt.Sprintf("%+v", err))
					}
				}()
				lgr, commit, ctx := getLogger(ss.Context(), info.FullMethod)

				buf := bufpool.Get()
				defer bufpool.Put(buf)
				jenc := json.NewEncoder(buf)
				if err = jenc.Encode(srv); err != nil {
					lgr.Log("marshal error", err, "srv", srv)
				}
				lgr.Log("REQ", info.FullMethod, "srv", buf.String())
				if err = checkAuth(ctx, info.FullMethod); err != nil {
					return status.Error(codes.Unauthenticated, err.Error())
				}

				wss := grpc_middleware.WrapServerStream(ss)
				wss.WrappedContext = ctx
				err = handler(srv, wss)

				lgr.Log("RESP", info.FullMethod, "error", err)
				commit(err)
				return StatusError(err)
			}),

		grpc.UnaryInterceptor(
			func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
				defer func() {
					if r := recover(); r != nil {
						var ok bool
						if err, ok = r.(error); ok {
							logger.Log("PANIC", err)
							return
						}
						err = errors.Errorf("%+v", r)
						logger.Log("PANIC", fmt.Sprintf("%+v", err))
					}
				}()
				logger, commit, ctx := getLogger(ctx, info.FullMethod)

				if err = checkAuth(ctx, info.FullMethod); err != nil {
					return nil, status.Error(codes.Unauthenticated, err.Error())
				}

				buf := bufpool.Get()
				defer bufpool.Put(buf)
				jenc := json.NewEncoder(buf)
				if err = jenc.Encode(req); err != nil {
					logger.Log("marshal error", err, "req", req)
				}
				logger.Log("REQ", info.FullMethod, "req", buf.String())

				// Fill PArgsHidden
				if r := reflect.ValueOf(req).Elem(); r.Kind() != reflect.Struct {
					logger.Log("error", "not struct", "req", fmt.Sprintf("%T %#v", req, req))
				} else {
					if f := r.FieldByName("PArgsHidden"); f.IsValid() {
						f.Set(reflect.ValueOf(buf.String()))
					}
				}

				res, err := handler(ctx, req)

				logger.Log("RESP", info.FullMethod, "error", err)
				commit(err)

				buf.Reset()
				if err = jenc.Encode(res); err != nil {
					logger.Log("marshal error", err, "res", res)
				}
				logger.Log("RESP", res, "error", err)

				return res, StatusError(err)
			}),
	}
	return grpc.NewServer(opts...)
}

func StatusError(err error) error {
	if err == nil {
		return err
	}
	var code codes.Code
	cerr := errors.Cause(err)
	if cerr == oracall.ErrInvalidArgument {
		code = codes.InvalidArgument
	} else if sc, ok := cerr.(interface {
		Code() codes.Code
	}); ok {
		code = sc.Code()
	}
	if code == 0 {
		return err
	}
	s := status.New(code, err.Error())
	if sd, sErr := s.WithDetails(&pbMessage{Message: fmt.Sprintf("%+v", err)}); sErr == nil {
		s = sd
	}
	return s.Err()
}

type pbMessage struct {
	Message string
}

func (m pbMessage) ProtoMessage()   {}
func (m *pbMessage) Reset()         { m.Message = "" }
func (m *pbMessage) String() string { return proto.MarshalTextString(m) }

type ctxKey string

const reqIDCtxKey = ctxKey("reqID")
const loggerCtxKey = ctxKey("logger")

func ContextWithLogger(ctx context.Context, logger log.Logger) context.Context {
	return context.WithValue(ctx, loggerCtxKey, logger)
}
func ContextGetLogger(ctx context.Context) log.Logger {
	if lgr, ok := ctx.Value(loggerCtxKey).(log.Logger); ok {
		return lgr
	}
	return nil
}
func ContextWithReqID(ctx context.Context, reqID string) context.Context {
	if reqID == "" {
		reqID = NewULID()
	}
	return context.WithValue(ctx, reqIDCtxKey, reqID)
}
func ContextGetReqID(ctx context.Context) string {
	if reqID, ok := ctx.Value(reqIDCtxKey).(string); ok {
		return reqID
	}
	return NewULID()
}
func NewULID() string {
	return ulid.MustNew(ulid.Now(), rand.Reader).String()
}
<commit_msg>orasrv: fix error shadowing<commit_after>package orasrv

import (
	"context"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"reflect"
	"sync"

	"github.com/gogo/protobuf/proto"
	"github.com/pkg/errors"
	bp "github.com/tgulacsi/go/bufpool"
	oracall "github.com/tgulacsi/oracall/lib"

	"github.com/go-kit/kit/log"
	"github.com/oklog/ulid"

	"github.com/grpc-ecosystem/go-grpc-middleware"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	goracle "gopkg.in/goracle.v2"
)

var bufpool = bp.New(4096)

func GRPCServer(logger log.Logger, verbose bool, checkAuth func(ctx context.Context, path string) error, options ...grpc.ServerOption) *grpc.Server {
	erroredMethods := make(map[string]struct{})
	var erroredMethodsMu sync.RWMutex

	getLogger := func(ctx context.Context, fullMethod string) (log.Logger, func(error), context.Context) {
		reqID := ContextGetReqID(ctx)
		ctx = ContextWithReqID(ctx, reqID)
		lgr := log.With(logger, "reqID", reqID)
		ctx = ContextWithLogger(ctx, lgr)
		verbose := verbose
		var wasThere bool
		if !verbose {
			erroredMethodsMu.RLock()
			_, verbose = erroredMethods[fullMethod]
			erroredMethodsMu.RUnlock()
			wasThere = verbose
		}
		if verbose {
			ctx = goracle.ContextWithLog(ctx, log.With(lgr, "lib", "goracle").Log)
		}
		commit := func(err error) {
			if wasThere && err == nil {
				erroredMethodsMu.Lock()
				delete(erroredMethods, fullMethod)
				erroredMethodsMu.Unlock()
			} else if err != nil && !wasThere {
				erroredMethodsMu.Lock()
				erroredMethods[fullMethod] = struct{}{}
				erroredMethodsMu.Unlock()
			}
		}
		return lgr, commit, ctx
	}

	opts := []grpc.ServerOption{
		//lint:ignore SA1019 the UseCompressor API is experimental yet.
		grpc.RPCCompressor(grpc.NewGZIPCompressor()),
		//lint:ignore SA1019 the UseCompressor API is experimental yet.
		grpc.RPCDecompressor(grpc.NewGZIPDecompressor()),
		grpc.StreamInterceptor(
			func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
				defer func() {
					if r := recover(); r != nil {
						var ok bool
						if err, ok = r.(error); ok {
							logger.Log("PANIC", err)
							return
						}
						err = errors.Errorf("%+v", r)
						logger.Log("PANIC", fmt.Sprintf("%+v", err))
					}
				}()
				lgr, commit, ctx := getLogger(ss.Context(), info.FullMethod)

				buf := bufpool.Get()
				defer bufpool.Put(buf)
				jenc := json.NewEncoder(buf)
				if err = jenc.Encode(srv); err != nil {
					lgr.Log("marshal error", err, "srv", srv)
				}
				lgr.Log("REQ", info.FullMethod, "srv", buf.String())
				if err = checkAuth(ctx, info.FullMethod); err != nil {
					return status.Error(codes.Unauthenticated, err.Error())
				}

				wss := grpc_middleware.WrapServerStream(ss)
				wss.WrappedContext = ctx
				err = handler(srv, wss)

				lgr.Log("RESP", info.FullMethod, "error", err)
				commit(err)
				return StatusError(err)
			}),

		grpc.UnaryInterceptor(
			func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
				defer func() {
					if r := recover(); r != nil {
						var ok bool
						if err, ok = r.(error); ok {
							logger.Log("PANIC", err)
							return
						}
						err = errors.Errorf("%+v", r)
						logger.Log("PANIC", fmt.Sprintf("%+v", err))
					}
				}()
				logger, commit, ctx := getLogger(ctx, info.FullMethod)

				if err = checkAuth(ctx, info.FullMethod); err != nil {
					return nil, status.Error(codes.Unauthenticated, err.Error())
				}

				buf := bufpool.Get()
				defer bufpool.Put(buf)
				jenc := json.NewEncoder(buf)
				if err = jenc.Encode(req); err != nil {
					logger.Log("marshal error", err, "req", req)
				}
				logger.Log("REQ", info.FullMethod, "req", buf.String())

				// Fill PArgsHidden
				if r := reflect.ValueOf(req).Elem(); r.Kind() != reflect.Struct {
					logger.Log("error", "not struct", "req", fmt.Sprintf("%T %#v", req, req))
				} else {
					if f := r.FieldByName("PArgsHidden"); f.IsValid() {
						f.Set(reflect.ValueOf(buf.String()))
					}
				}

				res, err := handler(ctx, req)

				logger.Log("RESP", info.FullMethod, "error", err)
				commit(err)

				buf.Reset()
				if jErr := jenc.Encode(res); err != nil {
					logger.Log("marshal error", jErr, "res", res)
				}
				logger.Log("RESP", res, "error", err)

				return res, StatusError(err)
			}),
	}
	return grpc.NewServer(opts...)
}

func StatusError(err error) error {
	if err == nil {
		return err
	}
	var code codes.Code
	cerr := errors.Cause(err)
	if cerr == oracall.ErrInvalidArgument {
		code = codes.InvalidArgument
	} else if sc, ok := cerr.(interface {
		Code() codes.Code
	}); ok {
		code = sc.Code()
	}
	if code == 0 {
		return err
	}
	s := status.New(code, err.Error())
	if sd, sErr := s.WithDetails(&pbMessage{Message: fmt.Sprintf("%+v", err)}); sErr == nil {
		s = sd
	}
	return s.Err()
}

type pbMessage struct {
	Message string
}

func (m pbMessage) ProtoMessage()   {}
func (m *pbMessage) Reset()         { m.Message = "" }
func (m *pbMessage) String() string { return proto.MarshalTextString(m) }

type ctxKey string

const reqIDCtxKey = ctxKey("reqID")
const loggerCtxKey = ctxKey("logger")

func ContextWithLogger(ctx context.Context, logger log.Logger) context.Context {
	return context.WithValue(ctx, loggerCtxKey, logger)
}
func ContextGetLogger(ctx context.Context) log.Logger {
	if lgr, ok := ctx.Value(loggerCtxKey).(log.Logger); ok {
		return lgr
	}
	return nil
}
func ContextWithReqID(ctx context.Context, reqID string) context.Context {
	if reqID == "" {
		reqID = NewULID()
	}
	return context.WithValue(ctx, reqIDCtxKey, reqID)
}
func ContextGetReqID(ctx context.Context) string {
	if reqID, ok := ctx.Value(reqIDCtxKey).(string); ok {
		return reqID
	}
	return NewULID()
}
func NewULID() string {
	return ulid.MustNew(ulid.Now(), rand.Reader).String()
}
<|endoftext|>


<commit_before>package ddns

import (
	"errors"
	"net"
	"time"

	"github.com/inimei/backup/log"
	"github.com/inimei/ddns/config"
	"github.com/inimei/ddns/data"
	"github.com/miekg/dns"
)

type NetType int

const (
	NetTCP NetType = 1
	NetUDP         = 2
)

const (
	notIPQuery = 0
	_IP4Query  = 4
	_IP6Query  = 6
)

type Question struct {
	qname  string
	qtype  string
	qclass string
}

func (q *Question) String() string {
	return q.qname + " " + q.qclass + " " + q.qtype
}

type DDNSHandler struct {
	resolver        *Resolver
	cache, negCache Cache
	hosts           Hosts
	dbrecodes       *DBRecodes
}

func NewHandler(db data.IDatabase) *DDNSHandler {

	var (
		cacheConfig     config.CacheSettings
		resolver        *Resolver
		cache, negCache Cache
	)

	if config.Data.Resolv.Enable {
		resolvConfig := config.Data.Resolv
		path := resolvConfig.ResolvFile
		if path[0] != '/' {
			path = config.CurDir() + "/" + path
		}

		clientConfig, err := dns.ClientConfigFromFile(path)
		if err != nil {
			log.Warn(":%s is not a valid resolv.conf file\n", path)
			log.Error("%v", err)
			panic(err)
		}
		clientConfig.Timeout = resolvConfig.Timeout
		resolver = &Resolver{clientConfig}
	}

	cacheConfig = config.Data.Cache
	switch cacheConfig.Backend {
	case "memory":
		cache = &MemoryCache{
			Backend:  make(map[string]Mesg, cacheConfig.Maxcount),
			Expire:   time.Duration(cacheConfig.Expire) * time.Second,
			Maxcount: cacheConfig.Maxcount,
		}
		negCache = &MemoryCache{
			Backend:  make(map[string]Mesg),
			Expire:   time.Duration(cacheConfig.Expire) * time.Second / 2,
			Maxcount: cacheConfig.Maxcount,
		}
	default:
		log.Error("Invalid cache backend %s", cacheConfig.Backend)
		panic("Invalid cache backend")
	}

	var hosts Hosts
	if config.Data.Hosts.Enable {
		hosts = NewHosts(config.Data.Hosts)
	}

	var recodes *DBRecodes
	if db != nil {
		recodes = NewDBRecodes(db)
	}

	return &DDNSHandler{resolver, cache, negCache, hosts, recodes}
}

func (h *DDNSHandler) close() {
	if h.hosts.hostWatcher != nil {
		h.hosts.hostWatcher.Close()
	}
}

func (h *DDNSHandler) do(netType NetType, w dns.ResponseWriter, req *dns.Msg) {

	q := req.Question[0]
	Q := Question{UnFqdn(q.Name), dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}

	var remote net.IP
	if netType == NetTCP {
		remote = w.RemoteAddr().(*net.TCPAddr).IP
	} else {
		remote = w.RemoteAddr().(*net.UDPAddr).IP
	}
	log.Info("%s lookup　%s", remote, Q.String())

	IPQuery := h.isIPQuery(q)

	rspByIps := func(ips []net.IP, ttl uint32) {
		m := new(dns.Msg)
		m.SetReply(req)

		switch IPQuery {
		case _IP4Query:
			rr_header := dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    ttl,
			}
			for _, ip := range ips {
				a := &dns.A{rr_header, ip}
				m.Answer = append(m.Answer, a)
			}
		case _IP6Query:
			rr_header := dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypeAAAA,
				Class:  dns.ClassINET,
				Ttl:    ttl,
			}
			for _, ip := range ips {
				aaaa := &dns.AAAA{rr_header, ip}
				m.Answer = append(m.Answer, aaaa)
			}
		}

		w.WriteMsg(m)
	}

	//
	//	query in database
	//
	if h.dbrecodes != nil {
		if ips, ttl, ok := h.dbrecodes.Get(Q.qname, q.Qtype); ok {
			rspByIps(ips, uint32(ttl))
			log.Debug("%s found in database", Q.String())
			return
		}
		log.Debug("%s didn't found in database", Q.String())
	}

	//
	//	query in host file
	//
	if config.Data.Hosts.Enable && IPQuery > 0 {
		if ips, ok := h.hosts.Get(Q.qname, q.Qtype); ok {
			rspByIps(ips, config.Data.Hosts.TTL)
			log.Debug("%s found in hosts file", Q.String())
			return
		}
		log.Debug("%s didn't found in hosts file", Q.String())
	}

	//
	// query in cache
	//
	key := KeyGen(Q)
	if IPQuery > 0 {
		mesg, err := h.cache.Get(key)
		if err != nil {
			if mesg, err = h.negCache.Get(key); err != nil {
				log.Debug("%s didn't hit cache", Q.String())
			} else {
				log.Debug("%s hit negative cache", Q.String())
				dns.HandleFailed(w, req)
				return
			}
		} else {
			log.Debug("%s hit cache", Q.String())
			// we need this copy against concurrent modification of Id
			msg := *mesg
			msg.Id = req.Id
			w.WriteMsg(&msg)
			return
		}
	}

	//
	//	external resolution
	//
	var err error
	var mesg *dns.Msg
	if config.Data.Resolv.Enable {
		mesg, err = h.resolver.Lookup(netType, req)
	} else {
		err = errors.New("no external resolution")
	}

	if err != nil {
		log.Debug("Resolve query error %s", err)
		dns.HandleFailed(w, req)

		// cache the failure, too!
		if err = h.negCache.Set(key, nil); err != nil {
			log.Debug("Set %s negative cache failed: %v", Q.String(), err)
		}
		return
	}

	w.WriteMsg(mesg)

	if IPQuery > 0 && len(mesg.Answer) > 0 {
		err = h.cache.Set(key, mesg)
		if err != nil {
			log.Debug("Set %s cache failed: %s", Q.String(), err.Error())
		}
		log.Debug("Insert %s into cache", Q.String())
	}
}

func (h *DDNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
	h.do(NetTCP, w, req)
}

func (h *DDNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
	h.do(NetUDP, w, req)
}

func (h *DDNSHandler) isIPQuery(q dns.Question) int {
	if q.Qclass != dns.ClassINET {
		return notIPQuery
	}

	switch q.Qtype {
	case dns.TypeA:
		return _IP4Query
	case dns.TypeAAAA:
		return _IP6Query
	default:
		return notIPQuery
	}
}

func UnFqdn(s string) string {
	if dns.IsFqdn(s) {
		return s[:len(s)-1]
	}
	return s
}
<commit_msg>RecursionAvailable<commit_after>package ddns

import (
	"errors"
	"net"
	"time"

	"github.com/inimei/backup/log"
	"github.com/inimei/ddns/config"
	"github.com/inimei/ddns/data"
	"github.com/miekg/dns"
)

type NetType int

const (
	NetTCP NetType = 1
	NetUDP         = 2
)

const (
	notIPQuery = 0
	_IP4Query  = 4
	_IP6Query  = 6
)

type Question struct {
	qname  string
	qtype  string
	qclass string
}

func (q *Question) String() string {
	return q.qname + " " + q.qclass + " " + q.qtype
}

type DDNSHandler struct {
	resolver        *Resolver
	cache, negCache Cache
	hosts           Hosts
	dbrecodes       *DBRecodes
}

func NewHandler(db data.IDatabase) *DDNSHandler {

	var (
		cacheConfig     config.CacheSettings
		resolver        *Resolver
		cache, negCache Cache
	)

	if config.Data.Resolv.Enable {
		resolvConfig := config.Data.Resolv
		path := resolvConfig.ResolvFile
		if path[0] != '/' {
			path = config.CurDir() + "/" + path
		}

		clientConfig, err := dns.ClientConfigFromFile(path)
		if err != nil {
			log.Warn(":%s is not a valid resolv.conf file\n", path)
			log.Error("%v", err)
			panic(err)
		}
		clientConfig.Timeout = resolvConfig.Timeout
		resolver = &Resolver{clientConfig}
	}

	cacheConfig = config.Data.Cache
	switch cacheConfig.Backend {
	case "memory":
		cache = &MemoryCache{
			Backend:  make(map[string]Mesg, cacheConfig.Maxcount),
			Expire:   time.Duration(cacheConfig.Expire) * time.Second,
			Maxcount: cacheConfig.Maxcount,
		}
		negCache = &MemoryCache{
			Backend:  make(map[string]Mesg),
			Expire:   time.Duration(cacheConfig.Expire) * time.Second / 2,
			Maxcount: cacheConfig.Maxcount,
		}
	default:
		log.Error("Invalid cache backend %s", cacheConfig.Backend)
		panic("Invalid cache backend")
	}

	var hosts Hosts
	if config.Data.Hosts.Enable {
		hosts = NewHosts(config.Data.Hosts)
	}

	var recodes *DBRecodes
	if db != nil {
		recodes = NewDBRecodes(db)
	}

	return &DDNSHandler{resolver, cache, negCache, hosts, recodes}
}

func (h *DDNSHandler) close() {
	if h.hosts.hostWatcher != nil {
		h.hosts.hostWatcher.Close()
	}
}

func (h *DDNSHandler) do(netType NetType, w dns.ResponseWriter, req *dns.Msg) {

	q := req.Question[0]
	if q.Qtype == dns.TypeANY {
		m.Authoritative = false
		m.Rcode = dns.RcodeRefused
		m.RecursionAvailable = false
		m.RecursionDesired = false
		m.Compress = false
		w.WriteMsg(m)
		return
	}

	Q := Question{UnFqdn(q.Name), dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}

	var remote net.IP
	if netType == NetTCP {
		remote = w.RemoteAddr().(*net.TCPAddr).IP
	} else {
		remote = w.RemoteAddr().(*net.UDPAddr).IP
	}
	log.Info("%s lookup　%s", remote, Q.String())

	IPQuery := h.isIPQuery(q)

	rspByIps := func(ips []net.IP, ttl uint32) {
		m := new(dns.Msg)
		m.SetReply(req)
		m.RecursionAvailable = true

		switch IPQuery {
		case _IP4Query:
			rr_header := dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    ttl,
			}
			for _, ip := range ips {
				a := &dns.A{rr_header, ip}
				m.Answer = append(m.Answer, a)
			}
		case _IP6Query:
			rr_header := dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypeAAAA,
				Class:  dns.ClassINET,
				Ttl:    ttl,
			}
			for _, ip := range ips {
				aaaa := &dns.AAAA{rr_header, ip}
				m.Answer = append(m.Answer, aaaa)
			}
		}

		w.WriteMsg(m)
	}

	//
	//	query in database
	//
	if h.dbrecodes != nil {
		if ips, ttl, ok := h.dbrecodes.Get(Q.qname, q.Qtype); ok {
			rspByIps(ips, uint32(ttl))
			log.Debug("%s found in database", Q.String())
			return
		}
		log.Debug("%s didn't found in database", Q.String())
	}

	//
	//	query in host file
	//
	if config.Data.Hosts.Enable && IPQuery > 0 {
		if ips, ok := h.hosts.Get(Q.qname, q.Qtype); ok {
			rspByIps(ips, config.Data.Hosts.TTL)
			log.Debug("%s found in hosts file", Q.String())
			return
		}
		log.Debug("%s didn't found in hosts file", Q.String())
	}

	//
	// query in cache
	//
	key := KeyGen(Q)
	if IPQuery > 0 {
		mesg, err := h.cache.Get(key)
		if err != nil {
			if mesg, err = h.negCache.Get(key); err != nil {
				log.Debug("%s didn't hit cache", Q.String())
			} else {
				log.Debug("%s hit negative cache", Q.String())
				dns.HandleFailed(w, req)
				return
			}
		} else {
			log.Debug("%s hit cache", Q.String())
			// we need this copy against concurrent modification of Id
			msg := *mesg
			msg.Id = req.Id
			w.WriteMsg(&msg)
			return
		}
	}

	//
	//	external resolution
	//
	var err error
	var mesg *dns.Msg
	if config.Data.Resolv.Enable {
		mesg, err = h.resolver.Lookup(netType, req)
	} else {
		err = errors.New("no external resolution")
	}

	if err != nil {
		log.Debug("Resolve query error %s", err)
		dns.HandleFailed(w, req)

		// cache the failure, too!
		if err = h.negCache.Set(key, nil); err != nil {
			log.Debug("Set %s negative cache failed: %v", Q.String(), err)
		}
		return
	}

	w.WriteMsg(mesg)

	if IPQuery > 0 && len(mesg.Answer) > 0 {
		err = h.cache.Set(key, mesg)
		if err != nil {
			log.Debug("Set %s cache failed: %s", Q.String(), err.Error())
		}
		log.Debug("Insert %s into cache", Q.String())
	}
}

func (h *DDNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
	h.do(NetTCP, w, req)
}

func (h *DDNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
	h.do(NetUDP, w, req)
}

func (h *DDNSHandler) isIPQuery(q dns.Question) int {
	if q.Qclass != dns.ClassINET {
		return notIPQuery
	}

	switch q.Qtype {
	case dns.TypeA:
		return _IP4Query
	case dns.TypeAAAA:
		return _IP6Query
	default:
		return notIPQuery
	}
}

func UnFqdn(s string) string {
	if dns.IsFqdn(s) {
		return s[:len(s)-1]
	}
	return s
}
<|endoftext|>


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

// Google's Omaha application update protocol, version 3.
//
// Omaha is a poll based protocol using XML. Requests are made by clients to
// check for updates or report events of an update process. Responses are given
// by the server to provide update information, if any, or to simply
// acknowledge the receipt of event status.
//
// http://code.google.com/p/omaha/wiki/ServerProtocol
package omaha

import (
	"encoding/xml"
)

type Request struct {
	XMLName        xml.Name `xml:"request" datastore:"-"`
	Os             Os       `xml:"os"`
	Apps           []*App   `xml:"app"`
	Protocol       string   `xml:"protocol,attr"`
	Version        string   `xml:"version,attr,omitempty"`
	IsMachine      string   `xml:"ismachine,attr,omitempty"`
	SessionId      string   `xml:"sessionid,attr,omitempty"`
	UserId         string   `xml:"userid,attr,omitempty"`
	InstallSource  string   `xml:"installsource,attr,omitempty"`
	TestSource     string   `xml:"testsource,attr,omitempty"`
	RequestId      string   `xml:"requestid,attr,omitempty"`
	UpdaterVersion string   `xml:"updaterversion,attr,omitempty"`
}

func NewRequest(version string, platform string, sp string, arch string) *Request {
	r := new(Request)
	r.Protocol = "3.0"
	r.Os = Os{Version: version, Platform: platform, Sp: sp, Arch: arch}
	return r
}

func (r *Request) AddApp(id string, version string) *App {
	a := NewApp(id)
	a.Version = version
	r.Apps = append(r.Apps, a)
	return a
}

type Response struct {
	XMLName  xml.Name `xml:"response" datastore:"-" json:"-"`
	DayStart DayStart `xml:"daystart"`
	Apps     []*App   `xml:"app"`
	Protocol string   `xml:"protocol,attr"`
	Server   string   `xml:"server,attr"`
}

func NewResponse(server string) *Response {
	r := &Response{Server: server, Protocol: "3.0"}
	r.DayStart.ElapsedSeconds = "0"
	return r
}

type DayStart struct {
	ElapsedSeconds string `xml:"elapsed_seconds,attr"`
}

func (r *Response) AddApp(id string) *App {
	a := NewApp(id)
	r.Apps = append(r.Apps, a)
	return a
}

type App struct {
	XMLName     xml.Name     `xml:"app" datastore"-" json:"-"`
	Ping        *Ping        `xml:"ping"`
	UpdateCheck *UpdateCheck `xml:"updatecheck"`
	Events      []*Event     `xml:"event" json:",omitempty"`
	Id          string       `xml:"appid,attr,omitempty"`
	Version     string       `xml:"version,attr,omitempty"`
	NextVersion string       `xml:"nextversion,attr,omitempty"`
	Lang        string       `xml:"lang,attr,omitempty"`
	Client      string       `xml:"client,attr,omitempty"`
	InstallAge  string       `xml:"installage,attr,omitempty"`
	Status      string       `xml:"status,attr,omitempty"`

	// update engine extensions
	Track     string `xml:"track,attr,omitempty"`
	FromTrack string `xml:"from_track,attr,omitempty"`

	// coreos update engine extensions
	BootId    string `xml:"bootid,attr,omitempty"`
	MachineID string `xml:"machineid,attr,omitempty"`
	OEM       string `xml:"oem,attr,omitempty"`
}

func NewApp(id string) *App {
	a := &App{Id: id}
	return a
}

func (a *App) AddUpdateCheck() *UpdateCheck {
	a.UpdateCheck = new(UpdateCheck)
	return a.UpdateCheck
}

func (a *App) AddPing() *Ping {
	a.Ping = new(Ping)
	return a.Ping
}

func (a *App) AddEvent() *Event {
	event := new(Event)
	a.Events = append(a.Events, event)
	return event
}

type UpdateCheck struct {
	XMLName             xml.Name  `xml:"updatecheck" datastore:"-" json:"-"`
	Urls                *Urls     `xml:"urls"`
	Manifest            *Manifest `xml:"manifest"`
	TargetVersionPrefix string    `xml:"targetversionprefix,attr,omitempty"`
	Status              string    `xml:"status,attr,omitempty"`
}

func (u *UpdateCheck) AddUrl(codebase string) *Url {
	if u.Urls == nil {
		u.Urls = new(Urls)
	}
	url := new(Url)
	url.CodeBase = codebase
	u.Urls.Urls = append(u.Urls.Urls, *url)
	return url
}

func (u *UpdateCheck) AddManifest(version string) *Manifest {
	u.Manifest = &Manifest{Version: version}
	return u.Manifest
}

type Ping struct {
	XMLName        xml.Name `xml:"ping" datastore:"-" json:"-"`
	LastReportDays string   `xml:"r,attr,omitempty"`
	Status         string   `xml:"status,attr,omitempty"`
}

type Os struct {
	XMLName  xml.Name `xml:"os" datastore:"-" json:"-"`
	Platform string   `xml:"platform,attr,omitempty"`
	Version  string   `xml:"version,attr,omitempty"`
	Sp       string   `xml:"sp,attr,omitempty"`
	Arch     string   `xml:"arch,attr,omitempty"`
}

func NewOs(platform string, version string, sp string, arch string) *Os {
	o := &Os{Version: version, Platform: platform, Sp: sp, Arch: arch}
	return o
}

type Event struct {
	XMLName         xml.Name `xml:"event" datastore:"-" json:"-"`
	Type            string   `xml:"eventtype,attr,omitempty"`
	Result          string   `xml:"eventresult,attr,omitempty"`
	PreviousVersion string   `xml:"previousversion,attr,omitempty"`
	ErrorCode       string   `xml:"errorcode,attr,omitempty"`
}

type Urls struct {
	XMLName xml.Name `xml:"urls" datastore:"-" json:"-"`
	Urls    []Url    `xml:"url" json:",omitempty"`
}

type Url struct {
	XMLName  xml.Name `xml:"url" datastore:"-" json:"-"`
	CodeBase string   `xml:"codebase,attr"`
}

type Manifest struct {
	XMLName  xml.Name `xml:"manifest" datastore:"-" json:"-"`
	Packages Packages `xml:"packages"`
	Actions  Actions  `xml:"actions"`
	Version  string   `xml:"version,attr"`
}

type Packages struct {
	XMLName  xml.Name  `xml:"packages" datastore:"-" json:"-"`
	Packages []Package `xml:"package" json:",omitempty"`
}

type Package struct {
	XMLName  xml.Name `xml:"package" datastore:"-" json:"-"`
	Hash     string   `xml:"hash,attr"`
	Name     string   `xml:"name,attr"`
	Size     string   `xml:"size,attr"`
	Required bool     `xml:"required,attr"`
}

func (m *Manifest) AddPackage(hash string, name string, size string, required bool) *Package {
	p := &Package{Hash: hash, Name: name, Size: size, Required: required}
	m.Packages.Packages = append(m.Packages.Packages, *p)
	return p
}

type Actions struct {
	XMLName xml.Name  `xml:"actions" datastore:"-" json:"-"`
	Actions []*Action `xml:"action" json:",omitempty"`
}

type Action struct {
	XMLName xml.Name `xml:"action" datastore:"-" json:"-"`
	Event   string   `xml:"event,attr"`

	// Extensions added by update_engine
	ChromeOSVersion       string `xml:"ChromeOSVersion,attr"`
	Sha256                string `xml:"sha256,attr"`
	NeedsAdmin            bool   `xml:"needsadmin,attr"`
	IsDelta               bool   `xml:"IsDelta,attr"`
	DisablePayloadBackoff bool   `xml:"DisablePayloadBackoff,attr,omitempty"`
	MetadataSignatureRsa  string `xml:"MetadataSignatureRsa,attr,omitempty"`
	MetadataSize          string `xml:"MetadataSize,attr,omitempty"`
	Deadline              string `xml:"deadline,attr,omitempty"`
}

func (m *Manifest) AddAction(event string) *Action {
	a := &Action{Event: event}
	m.Actions.Actions = append(m.Actions.Actions, a)
	return a
}

var EventTypes = map[int]string{
	0:   "unknown",
	1:   "download complete",
	2:   "install complete",
	3:   "update complete",
	4:   "uninstall",
	5:   "download started",
	6:   "install started",
	9:   "new application install started",
	10:  "setup started",
	11:  "setup finished",
	12:  "update application started",
	13:  "update download started",
	14:  "update download finished",
	15:  "update installer started",
	16:  "setup update begin",
	17:  "setup update complete",
	20:  "register product complete",
	30:  "OEM install first check",
	40:  "app-specific command started",
	41:  "app-specific command ended",
	100: "setup failure",
	102: "COM server failure",
	103: "setup update failure",
	800: "ping",
}

var EventResults = map[int]string{
	0:  "error",
	1:  "success",
	2:  "success reboot",
	3:  "success restart browser",
	4:  "cancelled",
	5:  "error installer MSI",
	6:  "error installer other",
	7:  "noupdate",
	8:  "error installer system",
	9:  "update deferred",
	10: "handoff error",
}
<commit_msg>omaha: remove XMLName from nested structures<commit_after>// Copyright 2013-2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Google's Omaha application update protocol, version 3.
//
// Omaha is a poll based protocol using XML. Requests are made by clients to
// check for updates or report events of an update process. Responses are given
// by the server to provide update information, if any, or to simply
// acknowledge the receipt of event status.
//
// http://code.google.com/p/omaha/wiki/ServerProtocol
package omaha

import (
	"encoding/xml"
)

type Request struct {
	XMLName        xml.Name `xml:"request" json:"-"`
	Os             Os       `xml:"os"`
	Apps           []*App   `xml:"app"`
	Protocol       string   `xml:"protocol,attr"`
	Version        string   `xml:"version,attr,omitempty"`
	IsMachine      string   `xml:"ismachine,attr,omitempty"`
	SessionId      string   `xml:"sessionid,attr,omitempty"`
	UserId         string   `xml:"userid,attr,omitempty"`
	InstallSource  string   `xml:"installsource,attr,omitempty"`
	TestSource     string   `xml:"testsource,attr,omitempty"`
	RequestId      string   `xml:"requestid,attr,omitempty"`
	UpdaterVersion string   `xml:"updaterversion,attr,omitempty"`
}

func NewRequest(version string, platform string, sp string, arch string) *Request {
	r := new(Request)
	r.Protocol = "3.0"
	r.Os = Os{Version: version, Platform: platform, Sp: sp, Arch: arch}
	return r
}

func (r *Request) AddApp(id string, version string) *App {
	a := NewApp(id)
	a.Version = version
	r.Apps = append(r.Apps, a)
	return a
}

type Response struct {
	XMLName  xml.Name `xml:"response" json:"-"`
	DayStart DayStart `xml:"daystart"`
	Apps     []*App   `xml:"app"`
	Protocol string   `xml:"protocol,attr"`
	Server   string   `xml:"server,attr"`
}

func NewResponse(server string) *Response {
	r := &Response{Server: server, Protocol: "3.0"}
	r.DayStart.ElapsedSeconds = "0"
	return r
}

type DayStart struct {
	ElapsedSeconds string `xml:"elapsed_seconds,attr"`
}

func (r *Response) AddApp(id string) *App {
	a := NewApp(id)
	r.Apps = append(r.Apps, a)
	return a
}

type App struct {
	Ping        *Ping        `xml:"ping"`
	UpdateCheck *UpdateCheck `xml:"updatecheck"`
	Events      []*Event     `xml:"event" json:",omitempty"`
	Id          string       `xml:"appid,attr,omitempty"`
	Version     string       `xml:"version,attr,omitempty"`
	NextVersion string       `xml:"nextversion,attr,omitempty"`
	Lang        string       `xml:"lang,attr,omitempty"`
	Client      string       `xml:"client,attr,omitempty"`
	InstallAge  string       `xml:"installage,attr,omitempty"`
	Status      string       `xml:"status,attr,omitempty"`

	// update engine extensions
	Track     string `xml:"track,attr,omitempty"`
	FromTrack string `xml:"from_track,attr,omitempty"`

	// coreos update engine extensions
	BootId    string `xml:"bootid,attr,omitempty"`
	MachineID string `xml:"machineid,attr,omitempty"`
	OEM       string `xml:"oem,attr,omitempty"`
}

func NewApp(id string) *App {
	a := &App{Id: id}
	return a
}

func (a *App) AddUpdateCheck() *UpdateCheck {
	a.UpdateCheck = new(UpdateCheck)
	return a.UpdateCheck
}

func (a *App) AddPing() *Ping {
	a.Ping = new(Ping)
	return a.Ping
}

func (a *App) AddEvent() *Event {
	event := new(Event)
	a.Events = append(a.Events, event)
	return event
}

type UpdateCheck struct {
	Urls                *Urls     `xml:"urls"`
	Manifest            *Manifest `xml:"manifest"`
	TargetVersionPrefix string    `xml:"targetversionprefix,attr,omitempty"`
	Status              string    `xml:"status,attr,omitempty"`
}

func (u *UpdateCheck) AddUrl(codebase string) *Url {
	if u.Urls == nil {
		u.Urls = new(Urls)
	}
	url := new(Url)
	url.CodeBase = codebase
	u.Urls.Urls = append(u.Urls.Urls, *url)
	return url
}

func (u *UpdateCheck) AddManifest(version string) *Manifest {
	u.Manifest = &Manifest{Version: version}
	return u.Manifest
}

type Ping struct {
	LastReportDays string `xml:"r,attr,omitempty"`
	Status         string `xml:"status,attr,omitempty"`
}

type Os struct {
	Platform string `xml:"platform,attr,omitempty"`
	Version  string `xml:"version,attr,omitempty"`
	Sp       string `xml:"sp,attr,omitempty"`
	Arch     string `xml:"arch,attr,omitempty"`
}

func NewOs(platform string, version string, sp string, arch string) *Os {
	o := &Os{Version: version, Platform: platform, Sp: sp, Arch: arch}
	return o
}

type Event struct {
	Type            string `xml:"eventtype,attr,omitempty"`
	Result          string `xml:"eventresult,attr,omitempty"`
	PreviousVersion string `xml:"previousversion,attr,omitempty"`
	ErrorCode       string `xml:"errorcode,attr,omitempty"`
}

type Urls struct {
	Urls []Url `xml:"url" json:",omitempty"`
}

type Url struct {
	CodeBase string `xml:"codebase,attr"`
}

type Manifest struct {
	Packages Packages `xml:"packages"`
	Actions  Actions  `xml:"actions"`
	Version  string   `xml:"version,attr"`
}

type Packages struct {
	Packages []Package `xml:"package" json:",omitempty"`
}

type Package struct {
	Hash     string `xml:"hash,attr"`
	Name     string `xml:"name,attr"`
	Size     string `xml:"size,attr"`
	Required bool   `xml:"required,attr"`
}

func (m *Manifest) AddPackage(hash string, name string, size string, required bool) *Package {
	p := &Package{Hash: hash, Name: name, Size: size, Required: required}
	m.Packages.Packages = append(m.Packages.Packages, *p)
	return p
}

type Actions struct {
	Actions []*Action `xml:"action" json:",omitempty"`
}

type Action struct {
	Event string `xml:"event,attr"`

	// Extensions added by update_engine
	ChromeOSVersion       string `xml:"ChromeOSVersion,attr"`
	Sha256                string `xml:"sha256,attr"`
	NeedsAdmin            bool   `xml:"needsadmin,attr"`
	IsDelta               bool   `xml:"IsDelta,attr"`
	DisablePayloadBackoff bool   `xml:"DisablePayloadBackoff,attr,omitempty"`
	MetadataSignatureRsa  string `xml:"MetadataSignatureRsa,attr,omitempty"`
	MetadataSize          string `xml:"MetadataSize,attr,omitempty"`
	Deadline              string `xml:"deadline,attr,omitempty"`
}

func (m *Manifest) AddAction(event string) *Action {
	a := &Action{Event: event}
	m.Actions.Actions = append(m.Actions.Actions, a)
	return a
}

var EventTypes = map[int]string{
	0:   "unknown",
	1:   "download complete",
	2:   "install complete",
	3:   "update complete",
	4:   "uninstall",
	5:   "download started",
	6:   "install started",
	9:   "new application install started",
	10:  "setup started",
	11:  "setup finished",
	12:  "update application started",
	13:  "update download started",
	14:  "update download finished",
	15:  "update installer started",
	16:  "setup update begin",
	17:  "setup update complete",
	20:  "register product complete",
	30:  "OEM install first check",
	40:  "app-specific command started",
	41:  "app-specific command ended",
	100: "setup failure",
	102: "COM server failure",
	103: "setup update failure",
	800: "ping",
}

var EventResults = map[int]string{
	0:  "error",
	1:  "success",
	2:  "success reboot",
	3:  "success restart browser",
	4:  "cancelled",
	5:  "error installer MSI",
	6:  "error installer other",
	7:  "noupdate",
	8:  "error installer system",
	9:  "update deferred",
	10: "handoff error",
}
<|endoftext|>


<commit_before>package services

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gexec"
)

type LastOperation struct {
	State string `json:"state"`
}

type Service struct {
	Name          string        `json:"name"`
	LastOperation LastOperation `json:"last_operation"`
}

type Resource struct {
	Entity Service `json:"entity"`
}

type Response struct {
	Resources []Resource `json:"resources"`
}

var _ = Describe("Service Instance Lifecycle", func() {
	var broker ServiceBroker

	waitForAsyncProvisioning := func(broker ServiceBroker, instanceName string) {
		// TODO: Use this code when CLI supports async
		// Eventually(func() string {
		// 	serviceDetails := cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
		// 	Expect(createService).To(Exit(0), "failed getting service instance details")
		// 	return string(serviceDetails.Out.Contents())
		// }, 5*time.Minute, 15*time.Second).Should(ContainSubstring("succeeded"))

		Eventually(func() string {
			serviceDetails := cf.Cf("curl", fmt.Sprintf("/v2/spaces/%s/service_instances", broker.GetSpaceGuid())).Wait(DEFAULT_TIMEOUT)
			Expect(serviceDetails).To(Exit(0), "failed getting service instance details")

			var response Response
			Expect(json.Unmarshal(serviceDetails.Out.Contents(), &response)).ToNot(HaveOccurred())

			for _, resource := range response.Resources {
				if resource.Entity.Name == instanceName {
					return resource.Entity.LastOperation.State
				}
			}
			return ""
		}, 5*time.Minute, 15*time.Second).Should(Equal("succeeded"))
	}

	Context("Sync broker", func() {
		BeforeEach(func() {
			broker = NewServiceBroker(generator.RandomName(), assets.NewAssets().ServiceBroker, context)
			broker.Plans = append(broker.Plans, Plan{Name: generator.RandomName(), ID: generator.RandomName()})
			broker.Push()
			broker.Configure()
			broker.Create()
			broker.PublicizePlans()
		})

		AfterEach(func() {
			broker.Destroy()
		})

		Context("just service instances", func() {
			It("can create, update, and delete a service instance", func() {
				instanceName := generator.RandomName()
				createService := cf.Cf("create-service", broker.Service.Name, broker.Plans[0].Name, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(createService).To(Exit(0))

				serviceInfo := cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring(fmt.Sprintf("Plan: %s", broker.Plans[0].Name)))

				updateService := cf.Cf("update-service", instanceName, "-p", broker.Plans[1].Name).Wait(DEFAULT_TIMEOUT)
				Expect(updateService).To(Exit(0))

				serviceInfo = cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring(fmt.Sprintf("Plan: %s", broker.Plans[1].Name)))

				deleteService := cf.Cf("delete-service", instanceName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteService).To(Exit(0))

				serviceInfo = cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring("not found"))
			})
		})

		Context("service instances with an app", func() {
			It("can bind and unbind service to app and check app env and events", func() {
				appName := generator.RandomName()
				createApp := cf.Cf("push", appName, "-p", assets.NewAssets().Dora).Wait(CF_PUSH_TIMEOUT)
				Expect(createApp).To(Exit(0), "failed creating app")

				checkForEvents(appName, []string{"audit.app.create"})

				instanceName := generator.RandomName()
				createService := cf.Cf("create-service", broker.Service.Name, broker.Plans[0].Name, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(createService).To(Exit(0), "failed creating service")

				bindService := cf.Cf("bind-service", appName, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(bindService).To(Exit(0), "failed binding app to service")

				checkForEvents(appName, []string{"audit.app.update"})

				restageApp := cf.Cf("restage", appName).Wait(CF_PUSH_TIMEOUT)
				Expect(restageApp).To(Exit(0), "failed restaging app")

				checkForEvents(appName, []string{"audit.app.restage"})

				appEnv := cf.Cf("env", appName).Wait(DEFAULT_TIMEOUT)
				Expect(appEnv).To(Exit(0), "failed get env for app")
				Expect(appEnv.Out.Contents()).To(ContainSubstring(fmt.Sprintf("credentials")))

				unbindService := cf.Cf("unbind-service", appName, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(unbindService).To(Exit(0), "failed unbinding app to service")

				checkForEvents(appName, []string{"audit.app.update"})

				appEnv = cf.Cf("env", appName).Wait(DEFAULT_TIMEOUT)
				Expect(appEnv).To(Exit(0), "failed get env for app")
				Expect(appEnv.Out.Contents()).ToNot(ContainSubstring(fmt.Sprintf("credentials")))

				deleteService := cf.Cf("delete-service", instanceName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteService).To(Exit(0))

				deleteApp := cf.Cf("delete", appName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteApp).To(Exit(0))
			})
		})
	})

	Context("Async broker", func() {
		BeforeEach(func() {
			broker = NewServiceBroker(generator.RandomName(), assets.NewAssets().AsyncServiceBroker, context)
			broker.Plans = append(broker.Plans, Plan{Name: generator.RandomName(), ID: generator.RandomName()})
			broker.Push()
			broker.Configure()
			broker.Create()
			broker.PublicizePlans()
		})

		AfterEach(func() {
			broker.Destroy()
		})

		Context("just service instances", func() {
			It("can create, update, and delete a service instance", func() {
				instanceName := generator.RandomName()
				createService := cf.Cf("create-service", broker.Service.Name, broker.Plans[0].Name, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(createService).To(Exit(0))

				waitForAsyncProvisioning(broker, instanceName)

				serviceInfo := cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring(fmt.Sprintf("Plan: %s", broker.Plans[0].Name)))

				updateService := cf.Cf("update-service", instanceName, "-p", broker.Plans[1].Name).Wait(DEFAULT_TIMEOUT)
				Expect(updateService).To(Exit(0))

				serviceInfo = cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo).To(Exit(0), "failed getting service instance details")
				// TODO: uncomment when CLI supports async
				// Expect(serviceInfo.Out.Contents()).To(ContainSubstring("Status: create succeeded"))
				// Expect(serviceInfo.Out.Contents()).To(ContainSubstring("Message: 100% done"))
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring(fmt.Sprintf("Plan: %s", broker.Plans[1].Name)))

				deleteService := cf.Cf("delete-service", instanceName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteService).To(Exit(0))

				serviceInfo = cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring("not found"))
			})
		})

		Context("service instances with an app", func() {
			It("can bind and unbind service to app and check app env and events", func() {
				appName := generator.RandomName()
				createApp := cf.Cf("push", appName, "-p", assets.NewAssets().Dora).Wait(CF_PUSH_TIMEOUT)
				Expect(createApp).To(Exit(0), "failed creating app")

				checkForEvents(appName, []string{"audit.app.create"})

				instanceName := generator.RandomName()
				createService := cf.Cf("create-service", broker.Service.Name, broker.Plans[0].Name, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(createService).To(Exit(0), "failed creating service")

				waitForAsyncProvisioning(broker, instanceName)

				bindService := cf.Cf("bind-service", appName, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(bindService).To(Exit(0), "failed binding app to service")

				checkForEvents(appName, []string{"audit.app.update"})

				restageApp := cf.Cf("restage", appName).Wait(CF_PUSH_TIMEOUT)
				Expect(restageApp).To(Exit(0), "failed restaging app")

				checkForEvents(appName, []string{"audit.app.restage"})

				appEnv := cf.Cf("env", appName).Wait(DEFAULT_TIMEOUT)
				Expect(appEnv).To(Exit(0), "failed get env for app")
				Expect(appEnv.Out.Contents()).To(ContainSubstring(fmt.Sprintf("credentials")))

				unbindService := cf.Cf("unbind-service", appName, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(unbindService).To(Exit(0), "failed unbinding app to service")

				checkForEvents(appName, []string{"audit.app.update"})

				appEnv = cf.Cf("env", appName).Wait(DEFAULT_TIMEOUT)
				Expect(appEnv).To(Exit(0), "failed get env for app")
				Expect(appEnv.Out.Contents()).ToNot(ContainSubstring(fmt.Sprintf("credentials")))

				deleteService := cf.Cf("delete-service", instanceName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteService).To(Exit(0))

				deleteApp := cf.Cf("delete", appName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteApp).To(Exit(0))
			})
		})
	})
})

func checkForEvents(name string, eventNames []string) {
	events := cf.Cf("events", name).Wait(DEFAULT_TIMEOUT)
	Expect(events).To(Exit(0), fmt.Sprintf("failed getting events for %s", name))

	for _, eventName := range eventNames {
		Expect(events.Out.Contents()).To(ContainSubstring(eventName), "failed to find event")
	}
}
<commit_msg>Unified async tests for CATs.<commit_after>package services

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gexec"
)

type LastOperation struct {
	State string `json:"state"`
}

type Service struct {
	Name          string        `json:"name"`
	LastOperation LastOperation `json:"last_operation"`
}

type Resource struct {
	Entity Service `json:"entity"`
}

type Response struct {
	Resources []Resource `json:"resources"`
}

var _ = Describe("Service Instance Lifecycle", func() {
	var broker ServiceBroker

	waitForAsyncProvisioning := func(broker ServiceBroker, instanceName string) {
		// TODO: Use this code when CLI supports async
		// Eventually(func() string {
		// 	serviceDetails := cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
		// 	Expect(createService).To(Exit(0), "failed getting service instance details")
		// 	return string(serviceDetails.Out.Contents())
		// }, 5*time.Minute, 15*time.Second).Should(ContainSubstring("succeeded"))

		Eventually(func() string {
			serviceDetails := cf.Cf("curl", fmt.Sprintf("/v2/spaces/%s/service_instances", broker.GetSpaceGuid())).Wait(DEFAULT_TIMEOUT)
			Expect(serviceDetails).To(Exit(0), "failed getting service instance details")

			var response Response
			Expect(json.Unmarshal(serviceDetails.Out.Contents(), &response)).ToNot(HaveOccurred())

			for _, resource := range response.Resources {
				if resource.Entity.Name == instanceName {
					return resource.Entity.LastOperation.State
				}
			}
			return ""
		}, 5*time.Minute, 15*time.Second).Should(Equal("succeeded"))
	}

	Context("Sync broker", func() {
		BeforeEach(func() {
			broker = NewServiceBroker(generator.RandomName(), assets.NewAssets().ServiceBroker, context)
			broker.Plans = append(broker.Plans, Plan{Name: generator.RandomName(), ID: generator.RandomName()})
			broker.Push()
			broker.Configure()
			broker.Create()
			broker.PublicizePlans()
		})

		AfterEach(func() {
			broker.Destroy()
		})

		Context("just service instances", func() {
			It("can create, update, and delete a service instance", func() {
				instanceName := generator.RandomName()
				createService := cf.Cf("create-service", broker.Service.Name, broker.Plans[0].Name, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(createService).To(Exit(0))

				serviceInfo := cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring(fmt.Sprintf("Plan: %s", broker.Plans[0].Name)))

				updateService := cf.Cf("update-service", instanceName, "-p", broker.Plans[1].Name).Wait(DEFAULT_TIMEOUT)
				Expect(updateService).To(Exit(0))

				serviceInfo = cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring(fmt.Sprintf("Plan: %s", broker.Plans[1].Name)))

				deleteService := cf.Cf("delete-service", instanceName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteService).To(Exit(0))

				serviceInfo = cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring("not found"))
			})
		})

		Context("service instances with an app", func() {
			It("can bind and unbind service to app and check app env and events", func() {
				appName := generator.RandomName()
				createApp := cf.Cf("push", appName, "-p", assets.NewAssets().Dora).Wait(CF_PUSH_TIMEOUT)
				Expect(createApp).To(Exit(0), "failed creating app")

				checkForEvents(appName, []string{"audit.app.create"})

				instanceName := generator.RandomName()
				createService := cf.Cf("create-service", broker.Service.Name, broker.Plans[0].Name, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(createService).To(Exit(0), "failed creating service")

				bindService := cf.Cf("bind-service", appName, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(bindService).To(Exit(0), "failed binding app to service")

				checkForEvents(appName, []string{"audit.app.update"})

				restageApp := cf.Cf("restage", appName).Wait(CF_PUSH_TIMEOUT)
				Expect(restageApp).To(Exit(0), "failed restaging app")

				checkForEvents(appName, []string{"audit.app.restage"})

				appEnv := cf.Cf("env", appName).Wait(DEFAULT_TIMEOUT)
				Expect(appEnv).To(Exit(0), "failed get env for app")
				Expect(appEnv.Out.Contents()).To(ContainSubstring(fmt.Sprintf("credentials")))

				unbindService := cf.Cf("unbind-service", appName, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(unbindService).To(Exit(0), "failed unbinding app to service")

				checkForEvents(appName, []string{"audit.app.update"})

				appEnv = cf.Cf("env", appName).Wait(DEFAULT_TIMEOUT)
				Expect(appEnv).To(Exit(0), "failed get env for app")
				Expect(appEnv.Out.Contents()).ToNot(ContainSubstring(fmt.Sprintf("credentials")))

				deleteService := cf.Cf("delete-service", instanceName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteService).To(Exit(0))

				deleteApp := cf.Cf("delete", appName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteApp).To(Exit(0))
			})
		})
	})

	Context("Async broker", func() {
		BeforeEach(func() {
			broker = NewServiceBroker(generator.RandomName(), assets.NewAssets().AsyncServiceBroker, context)
			broker.Plans = append(broker.Plans, Plan{Name: generator.RandomName(), ID: generator.RandomName()})
			broker.Push()
			broker.Configure()
			broker.Create()
			broker.PublicizePlans()
		})

		AfterEach(func() {
			broker.Destroy()
		})

		Context("just service instances", func() {
			It("can create, update, bind, unbind, and delete a service instance", func() {
				appName := generator.RandomName()
				createApp := cf.Cf("push", appName, "-p", assets.NewAssets().Dora).Wait(CF_PUSH_TIMEOUT)
				Expect(createApp).To(Exit(0), "failed creating app")

				checkForEvents(appName, []string{"audit.app.create"})

				instanceName := generator.RandomName()
				createService := cf.Cf("create-service", broker.Service.Name, broker.Plans[0].Name, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(createService).To(Exit(0))

				waitForAsyncProvisioning(broker, instanceName)

				serviceInfo := cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring(fmt.Sprintf("Plan: %s", broker.Plans[0].Name)))

				updateService := cf.Cf("update-service", instanceName, "-p", broker.Plans[1].Name).Wait(DEFAULT_TIMEOUT)
				Expect(updateService).To(Exit(0))

				serviceInfo = cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo).To(Exit(0), "failed getting service instance details")
				// TODO: uncomment when CLI supports async
				// Expect(serviceInfo.Out.Contents()).To(ContainSubstring("Status: create succeeded"))
				// Expect(serviceInfo.Out.Contents()).To(ContainSubstring("Message: 100% done"))
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring(fmt.Sprintf("Plan: %s", broker.Plans[1].Name)))

				bindService := cf.Cf("bind-service", appName, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(bindService).To(Exit(0), "failed binding app to service")

				checkForEvents(appName, []string{"audit.app.update"})

				restageApp := cf.Cf("restage", appName).Wait(CF_PUSH_TIMEOUT)
				Expect(restageApp).To(Exit(0), "failed restaging app")

				checkForEvents(appName, []string{"audit.app.restage"})

				appEnv := cf.Cf("env", appName).Wait(DEFAULT_TIMEOUT)
				Expect(appEnv).To(Exit(0), "failed get env for app")
				Expect(appEnv.Out.Contents()).To(ContainSubstring(fmt.Sprintf("credentials")))

				unbindService := cf.Cf("unbind-service", appName, instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(unbindService).To(Exit(0), "failed unbinding app to service")

				checkForEvents(appName, []string{"audit.app.update"})

				appEnv = cf.Cf("env", appName).Wait(DEFAULT_TIMEOUT)
				Expect(appEnv).To(Exit(0), "failed get env for app")
				Expect(appEnv.Out.Contents()).ToNot(ContainSubstring(fmt.Sprintf("credentials")))

				deleteService := cf.Cf("delete-service", instanceName, "-f").Wait(DEFAULT_TIMEOUT)
				Expect(deleteService).To(Exit(0))

				serviceInfo = cf.Cf("service", instanceName).Wait(DEFAULT_TIMEOUT)
				Expect(serviceInfo.Out.Contents()).To(ContainSubstring("not found"))
			})
		})
	})
})

func checkForEvents(name string, eventNames []string) {
	events := cf.Cf("events", name).Wait(DEFAULT_TIMEOUT)
	Expect(events).To(Exit(0), fmt.Sprintf("failed getting events for %s", name))

	for _, eventName := range eventNames {
		Expect(events.Out.Contents()).To(ContainSubstring(eventName), "failed to find event")
	}
}
<|endoftext|>


<commit_before>package opentracing

import (
	"regexp"
	"strings"
)

// TraceContext encpasulates the smallest amount of state needed to describe a
// Span's identity within a larger [potentially distributed] trace. The
// TraceContext is not intended to encode the span's operation name, timing,
// or log data, but merely any unique identifiers (etc) needed to contextualize
// it within a larger trace tree.
//
// TraceContexts are sufficient to propagate the, well, *context* of a
// particular trace between processes.
//
// TraceContext also support a simple string map of "trace tags". These trace
// tags are special in that they are propagated *in-band*, presumably alongside
// application data. See the documentation for SetTraceTag() for more details
// and some important caveats.
type TraceContext interface {
	// NewChild creates a child context for this TraceContext, and returns both
	// that child's own TraceContext as well as any Tags that should be added
	// to the child's Span.
	//
	// The returned TraceContext type must be the same as the type of the
	// TraceContext implementation itself.
	NewChild() (childCtx TraceContext, childSpanTags Tags)

	// SetTraceTag sets a tag on this TraceContext that also propagates to
	// future TraceContext children per TraceContext.NewChild.
	//
	// SetTraceTag() enables powerful functionality given a full-stack
	// opentracing integration (e.g., arbitrary application data from a mobile
	// app can make it, transparently, all the way into the depths of a storage
	// system), and with it some powerful costs: use this feature with care.
	//
	// IMPORTANT NOTE #1: SetTraceTag() will only propagate trace tags to
	// *future* children of the TraceContext (see NewChild()) and/or the
	// Span that references it.
	//
	// IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and
	// value is copied into every local *and remote* child of this
	// TraceContext, and that can add up to a lot of network and cpu
	// overhead.
	//
	// IMPORTANT NOTE #3: Trace tags are case-insensitive: implementations may
	// wish to use them as HTTP header keys (or key suffixes), and of course
	// HTTP headers are case insensitive.
	//
	// `restrictedKey` MUST match the regular expression
	// `(?i:[a-z0-9][-a-z0-9]*)` and is case-insensitive. See
	// CanonicalizeTraceTagKey().
	//
	// Returns a reference to this TraceContext for chaining, etc.
	SetTraceTag(caseInsensitiveKey, value string) TraceContext

	// Gets the value for a trace tag given its key. Returns the empty string
	// if the value isn't found in this TraceContext.
	//
	// `restrictedKey` MUST match the regular expression
	// `(?i:[a-z0-9][-a-z0-9]*)` and is case-insensitive.
	TraceTag(caseInsensitiveKey string) string
}

// TraceContextMarshaler is a simple interface to marshal a TraceContext to a
// binary byte array or a string-to-string map.
type TraceContextMarshaler interface {
	// Converts the TraceContext into marshaled binary data (see
	// TraceContextUnmarshaler.UnmarshalTraceContextBinary()).
	//
	// The first return value must represent the marshaler's serialization of
	// the core identifying information in `tc`.
	//
	// The second return value must represent the marshaler's serialization of
	// the trace tags, per `SetTraceTag` and `TraceTag`.
	MarshalTraceContextBinary(
		tc TraceContext,
	) (
		traceContextID []byte,
		traceTags []byte,
	)

	// Converts the TraceContext into a marshaled string:string map (see
	// TraceContextUnmarshaler.UnmarshalTraceContextStringMap()).
	//
	// The first return value must represent the marshaler's serialization of
	// the core identifying information in `tc`.
	//
	// The second return value must represent the marshaler's serialization of
	// the trace tags, per `SetTraceTag` and `TraceTag`.
	MarshalTraceContextStringMap(
		tc TraceContext,
	) (
		traceContextID map[string]string,
		traceTags map[string]string,
	)
}

// TraceContextUnmarshaler is a simple interface to unmarshal a binary byte
// array or a string-to-string map into a TraceContext.
type TraceContextUnmarshaler interface {
	// Converts the marshaled binary data (see
	// TraceContextMarshaler.MarshalTraceContextBinary()) into a TraceContext.
	//
	// The first parameter contains the marshaler's serialization of the core
	// identifying information in a TraceContext instance.
	//
	// The second parameter contains the marshaler's serialization of the trace
	// tags (per `SetTraceTag` and `TraceTag`) attached to a TraceContext
	// instance.
	UnmarshalTraceContextBinary(
		traceContextID []byte,
		traceTags []byte,
	) (TraceContext, error)

	// Converts the marshaled string:string map (see
	// TraceContextMarshaler.MarshalTraceContextStringMap()) into a TraceContext.
	//
	// The first parameter contains the marshaler's serialization of the core
	// identifying information in a TraceContext instance.
	//
	// The second parameter contains the marshaler's serialization of the trace
	// tags (per `SetTraceTag` and `TraceTag`) attached to a TraceContext
	// instance.
	//
	// It's permissable to pass the same map to both parameters (e.g., an HTTP
	// request headers map): the implementation should only unmarshal the
	// subset its interested in.
	UnmarshalTraceContextStringMap(
		traceContextID map[string]string,
		traceTags map[string]string,
	) (TraceContext, error)
}

// TraceContextSource is a long-lived interface that knows how to create a root
// TraceContext and marshal/unmarshal any other.
type TraceContextSource interface {
	TraceContextMarshaler
	TraceContextUnmarshaler

	// Create a TraceContext which has no parent (and thus begins its own trace).
	// A TraceContextSource must always return the same type in successive calls
	// to NewRootTraceContext().
	NewRootTraceContext() TraceContext
}

var kTraceTagRegexp = regexp.MustCompile("^(?i:[a-z0-9][-a-z0-9]*)$")

// Returns the canonicalized version of trace tag key `key`, and true if and
// only if the key was valid.
func CanonicalizeTraceTagKey(key string) (string, bool) {
	if !kTraceTagRegexp.MatchString(key) {
		return "", false
	}
	return strings.ToLower(key), true
}
<commit_msg>clarify restrictedKey<commit_after>package opentracing

import (
	"regexp"
	"strings"
)

// TraceContext encpasulates the smallest amount of state needed to describe a
// Span's identity within a larger [potentially distributed] trace. The
// TraceContext is not intended to encode the span's operation name, timing,
// or log data, but merely any unique identifiers (etc) needed to contextualize
// it within a larger trace tree.
//
// TraceContexts are sufficient to propagate the, well, *context* of a
// particular trace between processes.
//
// TraceContext also support a simple string map of "trace tags". These trace
// tags are special in that they are propagated *in-band*, presumably alongside
// application data. See the documentation for SetTraceTag() for more details
// and some important caveats.
type TraceContext interface {
	// NewChild creates a child context for this TraceContext, and returns both
	// that child's own TraceContext as well as any Tags that should be added
	// to the child's Span.
	//
	// The returned TraceContext type must be the same as the type of the
	// TraceContext implementation itself.
	NewChild() (childCtx TraceContext, childSpanTags Tags)

	// SetTraceTag sets a tag on this TraceContext that also propagates to
	// future TraceContext children per TraceContext.NewChild.
	//
	// SetTraceTag() enables powerful functionality given a full-stack
	// opentracing integration (e.g., arbitrary application data from a mobile
	// app can make it, transparently, all the way into the depths of a storage
	// system), and with it some powerful costs: use this feature with care.
	//
	// IMPORTANT NOTE #1: SetTraceTag() will only propagate trace tags to
	// *future* children of the TraceContext (see NewChild()) and/or the
	// Span that references it.
	//
	// IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and
	// value is copied into every local *and remote* child of this
	// TraceContext, and that can add up to a lot of network and cpu
	// overhead.
	//
	// IMPORTANT NOTE #3: Trace tags are case-insensitive: implementations may
	// wish to use them as HTTP header keys (or key suffixes), and of course
	// HTTP headers are case insensitive.
	//
	// `restrictedKey` MUST match the regular expression
	// `(?i:[a-z0-9][-a-z0-9]*)` and is case-insensitive. That is, it must
	// start with a letter or number, and the remaining characters must be
	// letters, numbers, or hyphens. See CanonicalizeTraceTagKey().
	//
	// Returns a reference to this TraceContext for chaining, etc.
	SetTraceTag(restrictedKey, value string) TraceContext

	// Gets the value for a trace tag given its key. Returns the empty string
	// if the value isn't found in this TraceContext.
	//
	// See the `SetTraceTag` notes about `restrictedKey`.
	TraceTag(restrictedKey string) string
}

// TraceContextMarshaler is a simple interface to marshal a TraceContext to a
// binary byte array or a string-to-string map.
type TraceContextMarshaler interface {
	// Converts the TraceContext into marshaled binary data (see
	// TraceContextUnmarshaler.UnmarshalTraceContextBinary()).
	//
	// The first return value must represent the marshaler's serialization of
	// the core identifying information in `tc`.
	//
	// The second return value must represent the marshaler's serialization of
	// the trace tags, per `SetTraceTag` and `TraceTag`.
	MarshalTraceContextBinary(
		tc TraceContext,
	) (
		traceContextID []byte,
		traceTags []byte,
	)

	// Converts the TraceContext into a marshaled string:string map (see
	// TraceContextUnmarshaler.UnmarshalTraceContextStringMap()).
	//
	// The first return value must represent the marshaler's serialization of
	// the core identifying information in `tc`.
	//
	// The second return value must represent the marshaler's serialization of
	// the trace tags, per `SetTraceTag` and `TraceTag`.
	MarshalTraceContextStringMap(
		tc TraceContext,
	) (
		traceContextID map[string]string,
		traceTags map[string]string,
	)
}

// TraceContextUnmarshaler is a simple interface to unmarshal a binary byte
// array or a string-to-string map into a TraceContext.
type TraceContextUnmarshaler interface {
	// Converts the marshaled binary data (see
	// TraceContextMarshaler.MarshalTraceContextBinary()) into a TraceContext.
	//
	// The first parameter contains the marshaler's serialization of the core
	// identifying information in a TraceContext instance.
	//
	// The second parameter contains the marshaler's serialization of the trace
	// tags (per `SetTraceTag` and `TraceTag`) attached to a TraceContext
	// instance.
	UnmarshalTraceContextBinary(
		traceContextID []byte,
		traceTags []byte,
	) (TraceContext, error)

	// Converts the marshaled string:string map (see
	// TraceContextMarshaler.MarshalTraceContextStringMap()) into a TraceContext.
	//
	// The first parameter contains the marshaler's serialization of the core
	// identifying information in a TraceContext instance.
	//
	// The second parameter contains the marshaler's serialization of the trace
	// tags (per `SetTraceTag` and `TraceTag`) attached to a TraceContext
	// instance.
	//
	// It's permissable to pass the same map to both parameters (e.g., an HTTP
	// request headers map): the implementation should only unmarshal the
	// subset its interested in.
	UnmarshalTraceContextStringMap(
		traceContextID map[string]string,
		traceTags map[string]string,
	) (TraceContext, error)
}

// TraceContextSource is a long-lived interface that knows how to create a root
// TraceContext and marshal/unmarshal any other.
type TraceContextSource interface {
	TraceContextMarshaler
	TraceContextUnmarshaler

	// Create a TraceContext which has no parent (and thus begins its own trace).
	// A TraceContextSource must always return the same type in successive calls
	// to NewRootTraceContext().
	NewRootTraceContext() TraceContext
}

var kTraceTagRegexp = regexp.MustCompile("^(?i:[a-z0-9][-a-z0-9]*)$")

// Returns the canonicalized version of trace tag key `key`, and true if and
// only if the key was valid.
func CanonicalizeTraceTagKey(key string) (string, bool) {
	if !kTraceTagRegexp.MatchString(key) {
		return "", false
	}
	return strings.ToLower(key), true
}
<|endoftext|>


<commit_before>package identify

import (
	"strings"
	"sync"

	semver "github.com/coreos/go-semver/semver"
	ggio "github.com/gogo/protobuf/io"
	pstore "github.com/ipfs/go-libp2p-peerstore"
	host "github.com/ipfs/go-libp2p/p2p/host"
	mstream "github.com/ipfs/go-libp2p/p2p/metrics/stream"
	inet "github.com/ipfs/go-libp2p/p2p/net"
	pb "github.com/ipfs/go-libp2p/p2p/protocol/identify/pb"
	ma "github.com/jbenet/go-multiaddr"
	msmux "github.com/whyrusleeping/go-multistream"
	context "golang.org/x/net/context"

	lgbl "github.com/ipfs/go-libp2p-loggables"
	logging "github.com/ipfs/go-log"
)

var log = logging.Logger("net/identify")

// ID is the protocol.ID of the Identify Service.
const ID = "/ipfs/id/1.0.0"

// LibP2PVersion holds the current protocol version for a client running this code
// TODO(jbenet): fix the versioning mess.
const LibP2PVersion = "ipfs/0.1.0"
const ClientVersion = "go-libp2p/0.1.0"

// IDService is a structure that implements ProtocolIdentify.
// It is a trivial service that gives the other peer some
// useful information about the local peer. A sort of hello.
//
// The IDService sends:
//  * Our IPFS Protocol Version
//  * Our IPFS Agent Version
//  * Our public Listen Addresses
type IDService struct {
	Host host.Host

	// connections undergoing identification
	// for wait purposes
	currid map[inet.Conn]chan struct{}
	currmu sync.RWMutex

	// our own observed addresses.
	// TODO: instead of expiring, remove these when we disconnect
	observedAddrs ObservedAddrSet
}

func NewIDService(h host.Host) *IDService {
	s := &IDService{
		Host:   h,
		currid: make(map[inet.Conn]chan struct{}),
	}
	h.SetStreamHandler(ID, s.RequestHandler)
	return s
}

// OwnObservedAddrs returns the addresses peers have reported we've dialed from
func (ids *IDService) OwnObservedAddrs() []ma.Multiaddr {
	return ids.observedAddrs.Addrs()
}

func (ids *IDService) IdentifyConn(c inet.Conn) {
	ids.currmu.Lock()
	if wait, found := ids.currid[c]; found {
		ids.currmu.Unlock()
		log.Debugf("IdentifyConn called twice on: %s", c)
		<-wait // already identifying it. wait for it.
		return
	}
	ch := make(chan struct{})
	ids.currid[c] = ch
	ids.currmu.Unlock()

	defer close(ch)

	s, err := c.NewStream()
	if err != nil {
		log.Debugf("error opening initial stream for %s: %s", ID, err)
		log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer())
		c.Close()
		return
	}

	bwc := ids.Host.GetBandwidthReporter()
	s = mstream.WrapStream(s, ID, bwc)

	// ok give the response to our handler.
	if err := msmux.SelectProtoOrFail(ID, s); err != nil {
		log.Debugf("error writing stream header for %s", ID)
		log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer())
		s.Close()
		return
	}

	ids.ResponseHandler(s)

	ids.currmu.Lock()
	_, found := ids.currid[c]
	delete(ids.currid, c)
	ids.currmu.Unlock()

	if !found {
		log.Debugf("IdentifyConn failed to find channel (programmer error) for %s", c)
		return
	}
}

func (ids *IDService) RequestHandler(s inet.Stream) {
	defer s.Close()
	c := s.Conn()

	bwc := ids.Host.GetBandwidthReporter()
	s = mstream.WrapStream(s, ID, bwc)

	w := ggio.NewDelimitedWriter(s)
	mes := pb.Identify{}
	ids.populateMessage(&mes, s.Conn())
	w.WriteMsg(&mes)

	log.Debugf("%s sent message to %s %s", ID,
		c.RemotePeer(), c.RemoteMultiaddr())
}

func (ids *IDService) ResponseHandler(s inet.Stream) {
	defer s.Close()
	c := s.Conn()

	r := ggio.NewDelimitedReader(s, 2048)
	mes := pb.Identify{}
	if err := r.ReadMsg(&mes); err != nil {
		return
	}
	ids.consumeMessage(&mes, c)

	log.Debugf("%s received message from %s %s", ID,
		c.RemotePeer(), c.RemoteMultiaddr())
}

func (ids *IDService) populateMessage(mes *pb.Identify, c inet.Conn) {

	// set protocols this node is currently handling
	protos := ids.Host.Mux().Protocols()
	mes.Protocols = make([]string, len(protos))
	for i, p := range protos {
		mes.Protocols[i] = string(p)
	}

	// observed address so other side is informed of their
	// "public" address, at least in relation to us.
	mes.ObservedAddr = c.RemoteMultiaddr().Bytes()

	// set listen addrs, get our latest addrs from Host.
	laddrs := ids.Host.Addrs()
	mes.ListenAddrs = make([][]byte, len(laddrs))
	for i, addr := range laddrs {
		mes.ListenAddrs[i] = addr.Bytes()
	}
	log.Debugf("%s sent listen addrs to %s: %s", c.LocalPeer(), c.RemotePeer(), laddrs)

	// set protocol versions
	pv := LibP2PVersion
	av := ClientVersion
	mes.ProtocolVersion = &pv
	mes.AgentVersion = &av
}

func (ids *IDService) consumeMessage(mes *pb.Identify, c inet.Conn) {
	p := c.RemotePeer()

	// mes.Protocols

	// mes.ObservedAddr
	ids.consumeObservedAddress(mes.GetObservedAddr(), c)

	// mes.ListenAddrs
	laddrs := mes.GetListenAddrs()
	lmaddrs := make([]ma.Multiaddr, 0, len(laddrs))
	for _, addr := range laddrs {
		maddr, err := ma.NewMultiaddrBytes(addr)
		if err != nil {
			log.Debugf("%s failed to parse multiaddr from %s %s", ID,
				p, c.RemoteMultiaddr())
			continue
		}
		lmaddrs = append(lmaddrs, maddr)
	}

	// if the address reported by the connection roughly matches their annoucned
	// listener addresses, its likely to be an external NAT address
	if HasConsistentTransport(c.RemoteMultiaddr(), lmaddrs) {
		lmaddrs = append(lmaddrs, c.RemoteMultiaddr())
	}

	// update our peerstore with the addresses. here, we SET the addresses, clearing old ones.
	// We are receiving from the peer itself. this is current address ground truth.
	ids.Host.Peerstore().SetAddrs(p, lmaddrs, pstore.ConnectedAddrTTL)
	log.Debugf("%s received listen addrs for %s: %s", c.LocalPeer(), c.RemotePeer(), lmaddrs)

	// get protocol versions
	pv := mes.GetProtocolVersion()
	av := mes.GetAgentVersion()

	// version check. if we shouldn't talk, bail.
	// TODO: at this point, we've already exchanged information.
	// move this into a first handshake before the connection can open streams.
	if !protocolVersionsAreCompatible(pv, LibP2PVersion) {
		logProtocolMismatchDisconnect(c, pv, av)
		c.Close()
		return
	}

	ids.Host.Peerstore().Put(p, "ProtocolVersion", pv)
	ids.Host.Peerstore().Put(p, "AgentVersion", av)
}

// HasConsistentTransport returns true if the address 'a' shares a
// protocol set with any address in the green set. This is used
// to check if a given address might be one of the addresses a peer is
// listening on.
func HasConsistentTransport(a ma.Multiaddr, green []ma.Multiaddr) bool {
	protosMatch := func(a, b []ma.Protocol) bool {
		if len(a) != len(b) {
			return false
		}

		for i, p := range a {
			if b[i].Code != p.Code {
				return false
			}
		}
		return true
	}

	protos := a.Protocols()

	for _, ga := range green {
		if protosMatch(protos, ga.Protocols()) {
			return true
		}
	}

	return false
}

// IdentifyWait returns a channel which will be closed once
// "ProtocolIdentify" (handshake3) finishes on given conn.
// This happens async so the connection can start to be used
// even if handshake3 knowledge is not necesary.
// Users **MUST** call IdentifyWait _after_ IdentifyConn
func (ids *IDService) IdentifyWait(c inet.Conn) <-chan struct{} {
	ids.currmu.Lock()
	ch, found := ids.currid[c]
	ids.currmu.Unlock()
	if found {
		return ch
	}

	// if not found, it means we are already done identifying it, or
	// haven't even started. either way, return a new channel closed.
	ch = make(chan struct{})
	close(ch)
	return ch
}

func (ids *IDService) consumeObservedAddress(observed []byte, c inet.Conn) {
	if observed == nil {
		return
	}

	maddr, err := ma.NewMultiaddrBytes(observed)
	if err != nil {
		log.Debugf("error parsing received observed addr for %s: %s", c, err)
		return
	}

	// we should only use ObservedAddr when our connection's LocalAddr is one
	// of our ListenAddrs. If we Dial out using an ephemeral addr, knowing that
	// address's external mapping is not very useful because the port will not be
	// the same as the listen addr.
	ifaceaddrs, err := ids.Host.Network().InterfaceListenAddresses()
	if err != nil {
		log.Infof("failed to get interface listen addrs", err)
		return
	}

	log.Debugf("identify identifying observed multiaddr: %s %s", c.LocalMultiaddr(), ifaceaddrs)
	if !addrInAddrs(c.LocalMultiaddr(), ifaceaddrs) {
		// not in our list
		return
	}

	// ok! we have the observed version of one of our ListenAddresses!
	log.Debugf("added own observed listen addr: %s --> %s", c.LocalMultiaddr(), maddr)
	ids.observedAddrs.Add(maddr, c.RemoteMultiaddr())
}

func addrInAddrs(a ma.Multiaddr, as []ma.Multiaddr) bool {
	for _, b := range as {
		if a.Equal(b) {
			return true
		}
	}
	return false
}

// protocolVersionsAreCompatible checks that the two implementations
// can talk to each other. It will use semver, but for now while
// we're in tight development, we will return false for minor version
// changes too.
func protocolVersionsAreCompatible(v1, v2 string) bool {
	if strings.HasPrefix(v1, "ipfs/") {
		v1 = v1[5:]
	}
	if strings.HasPrefix(v2, "ipfs/") {
		v2 = v2[5:]
	}

	v1s, err := semver.NewVersion(v1)
	if err != nil {
		return false
	}

	v2s, err := semver.NewVersion(v2)
	if err != nil {
		return false
	}

	return v1s.Major == v2s.Major && v1s.Minor == v2s.Minor
}

// netNotifiee defines methods to be used with the IpfsDHT
type netNotifiee IDService

func (nn *netNotifiee) IDService() *IDService {
	return (*IDService)(nn)
}

func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) {
	// TODO: deprecate the setConnHandler hook, and kick off
	// identification here.
}

func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) {
	// undo the setting of addresses to peer.ConnectedAddrTTL we did
	ids := nn.IDService()
	ps := ids.Host.Peerstore()
	addrs := ps.Addrs(v.RemotePeer())
	ps.SetAddrs(v.RemotePeer(), addrs, pstore.RecentlyConnectedAddrTTL)
}

func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr)      {}
func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {}

func logProtocolMismatchDisconnect(c inet.Conn, protocol, agent string) {
	lm := make(lgbl.DeferredMap)
	lm["remotePeer"] = func() interface{} { return c.RemotePeer().Pretty() }
	lm["remoteAddr"] = func() interface{} { return c.RemoteMultiaddr().String() }
	lm["protocolVersion"] = protocol
	lm["agentVersion"] = agent
	log.Event(context.TODO(), "IdentifyProtocolMismatch", lm)
	log.Debug("IdentifyProtocolMismatch %s %s %s (disconnected)", c.RemotePeer(), protocol, agent)
}
<commit_msg>bump agent version to match repo version<commit_after>package identify

import (
	"strings"
	"sync"

	semver "github.com/coreos/go-semver/semver"
	ggio "github.com/gogo/protobuf/io"
	pstore "github.com/ipfs/go-libp2p-peerstore"
	host "github.com/ipfs/go-libp2p/p2p/host"
	mstream "github.com/ipfs/go-libp2p/p2p/metrics/stream"
	inet "github.com/ipfs/go-libp2p/p2p/net"
	pb "github.com/ipfs/go-libp2p/p2p/protocol/identify/pb"
	ma "github.com/jbenet/go-multiaddr"
	msmux "github.com/whyrusleeping/go-multistream"
	context "golang.org/x/net/context"

	lgbl "github.com/ipfs/go-libp2p-loggables"
	logging "github.com/ipfs/go-log"
)

var log = logging.Logger("net/identify")

// ID is the protocol.ID of the Identify Service.
const ID = "/ipfs/id/1.0.0"

// LibP2PVersion holds the current protocol version for a client running this code
// TODO(jbenet): fix the versioning mess.
const LibP2PVersion = "ipfs/0.1.0"
const ClientVersion = "go-libp2p/3.3.4"

// IDService is a structure that implements ProtocolIdentify.
// It is a trivial service that gives the other peer some
// useful information about the local peer. A sort of hello.
//
// The IDService sends:
//  * Our IPFS Protocol Version
//  * Our IPFS Agent Version
//  * Our public Listen Addresses
type IDService struct {
	Host host.Host

	// connections undergoing identification
	// for wait purposes
	currid map[inet.Conn]chan struct{}
	currmu sync.RWMutex

	// our own observed addresses.
	// TODO: instead of expiring, remove these when we disconnect
	observedAddrs ObservedAddrSet
}

func NewIDService(h host.Host) *IDService {
	s := &IDService{
		Host:   h,
		currid: make(map[inet.Conn]chan struct{}),
	}
	h.SetStreamHandler(ID, s.RequestHandler)
	return s
}

// OwnObservedAddrs returns the addresses peers have reported we've dialed from
func (ids *IDService) OwnObservedAddrs() []ma.Multiaddr {
	return ids.observedAddrs.Addrs()
}

func (ids *IDService) IdentifyConn(c inet.Conn) {
	ids.currmu.Lock()
	if wait, found := ids.currid[c]; found {
		ids.currmu.Unlock()
		log.Debugf("IdentifyConn called twice on: %s", c)
		<-wait // already identifying it. wait for it.
		return
	}
	ch := make(chan struct{})
	ids.currid[c] = ch
	ids.currmu.Unlock()

	defer close(ch)

	s, err := c.NewStream()
	if err != nil {
		log.Debugf("error opening initial stream for %s: %s", ID, err)
		log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer())
		c.Close()
		return
	}

	bwc := ids.Host.GetBandwidthReporter()
	s = mstream.WrapStream(s, ID, bwc)

	// ok give the response to our handler.
	if err := msmux.SelectProtoOrFail(ID, s); err != nil {
		log.Debugf("error writing stream header for %s", ID)
		log.Event(context.TODO(), "IdentifyOpenFailed", c.RemotePeer())
		s.Close()
		return
	}

	ids.ResponseHandler(s)

	ids.currmu.Lock()
	_, found := ids.currid[c]
	delete(ids.currid, c)
	ids.currmu.Unlock()

	if !found {
		log.Debugf("IdentifyConn failed to find channel (programmer error) for %s", c)
		return
	}
}

func (ids *IDService) RequestHandler(s inet.Stream) {
	defer s.Close()
	c := s.Conn()

	bwc := ids.Host.GetBandwidthReporter()
	s = mstream.WrapStream(s, ID, bwc)

	w := ggio.NewDelimitedWriter(s)
	mes := pb.Identify{}
	ids.populateMessage(&mes, s.Conn())
	w.WriteMsg(&mes)

	log.Debugf("%s sent message to %s %s", ID,
		c.RemotePeer(), c.RemoteMultiaddr())
}

func (ids *IDService) ResponseHandler(s inet.Stream) {
	defer s.Close()
	c := s.Conn()

	r := ggio.NewDelimitedReader(s, 2048)
	mes := pb.Identify{}
	if err := r.ReadMsg(&mes); err != nil {
		return
	}
	ids.consumeMessage(&mes, c)

	log.Debugf("%s received message from %s %s", ID,
		c.RemotePeer(), c.RemoteMultiaddr())
}

func (ids *IDService) populateMessage(mes *pb.Identify, c inet.Conn) {

	// set protocols this node is currently handling
	protos := ids.Host.Mux().Protocols()
	mes.Protocols = make([]string, len(protos))
	for i, p := range protos {
		mes.Protocols[i] = string(p)
	}

	// observed address so other side is informed of their
	// "public" address, at least in relation to us.
	mes.ObservedAddr = c.RemoteMultiaddr().Bytes()

	// set listen addrs, get our latest addrs from Host.
	laddrs := ids.Host.Addrs()
	mes.ListenAddrs = make([][]byte, len(laddrs))
	for i, addr := range laddrs {
		mes.ListenAddrs[i] = addr.Bytes()
	}
	log.Debugf("%s sent listen addrs to %s: %s", c.LocalPeer(), c.RemotePeer(), laddrs)

	// set protocol versions
	pv := LibP2PVersion
	av := ClientVersion
	mes.ProtocolVersion = &pv
	mes.AgentVersion = &av
}

func (ids *IDService) consumeMessage(mes *pb.Identify, c inet.Conn) {
	p := c.RemotePeer()

	// mes.Protocols

	// mes.ObservedAddr
	ids.consumeObservedAddress(mes.GetObservedAddr(), c)

	// mes.ListenAddrs
	laddrs := mes.GetListenAddrs()
	lmaddrs := make([]ma.Multiaddr, 0, len(laddrs))
	for _, addr := range laddrs {
		maddr, err := ma.NewMultiaddrBytes(addr)
		if err != nil {
			log.Debugf("%s failed to parse multiaddr from %s %s", ID,
				p, c.RemoteMultiaddr())
			continue
		}
		lmaddrs = append(lmaddrs, maddr)
	}

	// if the address reported by the connection roughly matches their annoucned
	// listener addresses, its likely to be an external NAT address
	if HasConsistentTransport(c.RemoteMultiaddr(), lmaddrs) {
		lmaddrs = append(lmaddrs, c.RemoteMultiaddr())
	}

	// update our peerstore with the addresses. here, we SET the addresses, clearing old ones.
	// We are receiving from the peer itself. this is current address ground truth.
	ids.Host.Peerstore().SetAddrs(p, lmaddrs, pstore.ConnectedAddrTTL)
	log.Debugf("%s received listen addrs for %s: %s", c.LocalPeer(), c.RemotePeer(), lmaddrs)

	// get protocol versions
	pv := mes.GetProtocolVersion()
	av := mes.GetAgentVersion()

	// version check. if we shouldn't talk, bail.
	// TODO: at this point, we've already exchanged information.
	// move this into a first handshake before the connection can open streams.
	if !protocolVersionsAreCompatible(pv, LibP2PVersion) {
		logProtocolMismatchDisconnect(c, pv, av)
		c.Close()
		return
	}

	ids.Host.Peerstore().Put(p, "ProtocolVersion", pv)
	ids.Host.Peerstore().Put(p, "AgentVersion", av)
}

// HasConsistentTransport returns true if the address 'a' shares a
// protocol set with any address in the green set. This is used
// to check if a given address might be one of the addresses a peer is
// listening on.
func HasConsistentTransport(a ma.Multiaddr, green []ma.Multiaddr) bool {
	protosMatch := func(a, b []ma.Protocol) bool {
		if len(a) != len(b) {
			return false
		}

		for i, p := range a {
			if b[i].Code != p.Code {
				return false
			}
		}
		return true
	}

	protos := a.Protocols()

	for _, ga := range green {
		if protosMatch(protos, ga.Protocols()) {
			return true
		}
	}

	return false
}

// IdentifyWait returns a channel which will be closed once
// "ProtocolIdentify" (handshake3) finishes on given conn.
// This happens async so the connection can start to be used
// even if handshake3 knowledge is not necesary.
// Users **MUST** call IdentifyWait _after_ IdentifyConn
func (ids *IDService) IdentifyWait(c inet.Conn) <-chan struct{} {
	ids.currmu.Lock()
	ch, found := ids.currid[c]
	ids.currmu.Unlock()
	if found {
		return ch
	}

	// if not found, it means we are already done identifying it, or
	// haven't even started. either way, return a new channel closed.
	ch = make(chan struct{})
	close(ch)
	return ch
}

func (ids *IDService) consumeObservedAddress(observed []byte, c inet.Conn) {
	if observed == nil {
		return
	}

	maddr, err := ma.NewMultiaddrBytes(observed)
	if err != nil {
		log.Debugf("error parsing received observed addr for %s: %s", c, err)
		return
	}

	// we should only use ObservedAddr when our connection's LocalAddr is one
	// of our ListenAddrs. If we Dial out using an ephemeral addr, knowing that
	// address's external mapping is not very useful because the port will not be
	// the same as the listen addr.
	ifaceaddrs, err := ids.Host.Network().InterfaceListenAddresses()
	if err != nil {
		log.Infof("failed to get interface listen addrs", err)
		return
	}

	log.Debugf("identify identifying observed multiaddr: %s %s", c.LocalMultiaddr(), ifaceaddrs)
	if !addrInAddrs(c.LocalMultiaddr(), ifaceaddrs) {
		// not in our list
		return
	}

	// ok! we have the observed version of one of our ListenAddresses!
	log.Debugf("added own observed listen addr: %s --> %s", c.LocalMultiaddr(), maddr)
	ids.observedAddrs.Add(maddr, c.RemoteMultiaddr())
}

func addrInAddrs(a ma.Multiaddr, as []ma.Multiaddr) bool {
	for _, b := range as {
		if a.Equal(b) {
			return true
		}
	}
	return false
}

// protocolVersionsAreCompatible checks that the two implementations
// can talk to each other. It will use semver, but for now while
// we're in tight development, we will return false for minor version
// changes too.
func protocolVersionsAreCompatible(v1, v2 string) bool {
	if strings.HasPrefix(v1, "ipfs/") {
		v1 = v1[5:]
	}
	if strings.HasPrefix(v2, "ipfs/") {
		v2 = v2[5:]
	}

	v1s, err := semver.NewVersion(v1)
	if err != nil {
		return false
	}

	v2s, err := semver.NewVersion(v2)
	if err != nil {
		return false
	}

	return v1s.Major == v2s.Major && v1s.Minor == v2s.Minor
}

// netNotifiee defines methods to be used with the IpfsDHT
type netNotifiee IDService

func (nn *netNotifiee) IDService() *IDService {
	return (*IDService)(nn)
}

func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) {
	// TODO: deprecate the setConnHandler hook, and kick off
	// identification here.
}

func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) {
	// undo the setting of addresses to peer.ConnectedAddrTTL we did
	ids := nn.IDService()
	ps := ids.Host.Peerstore()
	addrs := ps.Addrs(v.RemotePeer())
	ps.SetAddrs(v.RemotePeer(), addrs, pstore.RecentlyConnectedAddrTTL)
}

func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr)      {}
func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {}

func logProtocolMismatchDisconnect(c inet.Conn, protocol, agent string) {
	lm := make(lgbl.DeferredMap)
	lm["remotePeer"] = func() interface{} { return c.RemotePeer().Pretty() }
	lm["remoteAddr"] = func() interface{} { return c.RemoteMultiaddr().String() }
	lm["protocolVersion"] = protocol
	lm["agentVersion"] = agent
	log.Event(context.TODO(), "IdentifyProtocolMismatch", lm)
	log.Debug("IdentifyProtocolMismatch %s %s %s (disconnected)", c.RemotePeer(), protocol, agent)
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/lucasb-eyer/go-colorful"
    "image/color"
    "math"
    "math/rand"
    "testing"
)

func RandomColour() (a color.NRGBA) {
    x := rand.Uint32()
    a = color.NRGBA{uint8(x & 255), uint8((x >> 8) & 255), uint8((x >> 16) & 255), 255}
    return
}

var x, y color.NRGBA = RandomColour(), RandomColour()
var bench_result int32

func BenchmarkColourDiffRgbInt(b *testing.B) {
    var r int32
    for n := 0; n < b.N; n++ {
        r = ColourDiffRgb(x, y)
    }
    bench_result = r
}

func BenchmarkColourDiffRgbFloat(b *testing.B) {
    var r float64
    _x, _y := MakeColorful(x), MakeColorful(y)
    for n := 0; n < b.N; n++ {
        r = _x.DistanceRgb(_y)
    }
    bench_result = int32(r)
}

func BenchmarkColourDiffLab(b *testing.B) {
    var r int32
    for n := 0; n < b.N; n++ {
        r = ColourDiffLab(x, y)
    }
    bench_result = r
}

func BenchmarkColourDiffLabNoConversion(b *testing.B) {
    var r float64
    _x, _y := MakeColorful(x), MakeColorful(y)
    for n := 0; n < b.N; n++ {
        r = _x.DistanceLab(_y)
    }
    bench_result = int32(r)
}

func sq(x float64) float64 {
    return x*x
}

func BenchmarkColourDiffLabLinearRgb(b *testing.B) {
    var r float64
    _x, _y := MakeColorful(x), MakeColorful(y)
    for n := 0; n < b.N; n++ {
        xr, xg, xb := _x.LinearRgb()
        yr, yg, yb := _y.LinearRgb()
        xx, xy, xz := colorful.LinearRgbToXyz(xr, xg, xb)
        yx, yy, yz := colorful.LinearRgbToXyz(yr, yg, yb)
        l1, a1, b1 := colorful.XyzToLab(xx, xy, xz)
        l2, a2, b2 := colorful.XyzToLab(yx, yy, yz)
        r = math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
    }
    bench_result = int32(r)
}

func BenchmarkColourDiffLabFastLinearRgb(b *testing.B) {
    var r float64
    _x, _y := MakeColorful(x), MakeColorful(y)
    for n := 0; n < b.N; n++ {
        xr, xg, xb := _x.FastLinearRgb()
        yr, yg, yb := _y.FastLinearRgb()
        xx, xy, xz := colorful.LinearRgbToXyz(xr, xg, xb)
        yx, yy, yz := colorful.LinearRgbToXyz(yr, yg, yb)
        l1, a1, b1 := colorful.XyzToLab(xx, xy, xz)
        l2, a2, b2 := colorful.XyzToLab(yx, yy, yz)
        r = math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
    }
    bench_result = int32(r)
}

<commit_msg>More benchmarks<commit_after>package main

import (
    "github.com/lucasb-eyer/go-colorful"
    "image/color"
    "math"
    "math/rand"
    "testing"
)

func RandomColour() (a color.NRGBA) {
    x := rand.Uint32()
    a = color.NRGBA{uint8(x & 255), uint8((x >> 8) & 255), uint8((x >> 16) & 255), 255}
    return
}

var bench_result int32

func BenchmarkColourDiffRgbInt(b *testing.B) {
    var r int32
    for n := 0; n < b.N; n++ {
        r = ColourDiffRgb(RandomColour(), RandomColour())
    }
    bench_result = r
}

func BenchmarkColourDiffRgbFloat(b *testing.B) {
    var r float64
    rand.Seed(1)
    for n := 0; n < b.N; n++ {
        r = MakeColorful(RandomColour()).DistanceRgb(MakeColorful(RandomColour()))
    }
    bench_result = int32(r)
}

func BenchmarkColourDiffLab(b *testing.B) {
    var r int32
    rand.Seed(1)
    for n := 0; n < b.N; n++ {
        r = ColourDiffLab(RandomColour(), RandomColour())
    }
    bench_result = r
}

func BenchmarkColourDiffLabNoConversion(b *testing.B) {
    var r float64
    rand.Seed(1)
    for n := 0; n < b.N; n++ {
        r = MakeColorful(RandomColour()).DistanceLab(MakeColorful(RandomColour()))
    }
    bench_result = int32(r)
}

func sq(x float64) float64 {
    return x*x
}

func BenchmarkColourDiffLabLinearRgb(b *testing.B) {
    var r float64
    rand.Seed(1)
    for n := 0; n < b.N; n++ {
        _x, _y := MakeColorful(RandomColour()), MakeColorful(RandomColour())
        xr, xg, xb := _x.LinearRgb()
        yr, yg, yb := _y.LinearRgb()
        xx, xy, xz := colorful.LinearRgbToXyz(xr, xg, xb)
        yx, yy, yz := colorful.LinearRgbToXyz(yr, yg, yb)
        l1, a1, b1 := colorful.XyzToLab(xx, xy, xz)
        l2, a2, b2 := colorful.XyzToLab(yx, yy, yz)
        r = math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
    }
    bench_result = int32(r)
}

func BenchmarkColourDiffLabFastLinearRgb(b *testing.B) {
    var r float64
    rand.Seed(1)
    for n := 0; n < b.N; n++ {
        _x, _y := MakeColorful(RandomColour()), MakeColorful(RandomColour())
        xr, xg, xb := _x.FastLinearRgb()
        yr, yg, yb := _y.FastLinearRgb()
        xx, xy, xz := colorful.LinearRgbToXyz(xr, xg, xb)
        yx, yy, yz := colorful.LinearRgbToXyz(yr, yg, yb)
        l1, a1, b1 := colorful.XyzToLab(xx, xy, xz)
        l2, a2, b2 := colorful.XyzToLab(yx, yy, yz)
        r = math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
    }
    bench_result = int32(r)
}

func BenchmarkColourDiffLabFasterLinearRgb(b *testing.B) {
    var r float64
    rand.Seed(1)
    for n := 0; n < b.N; n++ {
        _x, _y := MakeColorful(RandomColour()), MakeColorful(RandomColour())
        xr, xg, xb := sq(_x.R), sq(_x.G), sq(_x.B)
        yr, yg, yb := sq(_y.R), sq(_y.G), sq(_y.B)
        xx, xy, xz := colorful.LinearRgbToXyz(xr, xg, xb)
        yx, yy, yz := colorful.LinearRgbToXyz(yr, yg, yb)
        l1, a1, b1 := colorful.XyzToLab(xx, xy, xz)
        l2, a2, b2 := colorful.XyzToLab(yx, yy, yz)
        r = math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
    }
    bench_result = int32(r)
}

<|endoftext|>