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

package rootio

import (
	"fmt"
	"reflect"
)

type tleaf struct {
	rvers    int16
	named    tnamed
	len      int
	etype    int
	offset   int
	hasrange bool
	unsigned bool
	count    leafCount
	branch   Branch
}

// Name returns the name of the instance
func (leaf *tleaf) Name() string {
	return leaf.named.Name()
}

// Title returns the title of the instance
func (leaf *tleaf) Title() string {
	return leaf.named.Title()
}

func (leaf *tleaf) Class() string {
	return "TLeaf"
}

func (leaf *tleaf) ArrayDim() int {
	panic("not implemented")
}

func (leaf *tleaf) setBranch(b Branch) {
	leaf.branch = b
}

func (leaf *tleaf) Branch() Branch {
	return leaf.branch
}

func (leaf *tleaf) HasRange() bool {
	return leaf.hasrange
}

func (leaf *tleaf) IsUnsigned() bool {
	return leaf.unsigned
}

func (leaf *tleaf) LeafCount() Leaf {
	return leaf.count
}

func (leaf *tleaf) Len() int {
	if leaf.count != nil {
		// variable length array
		n := leaf.count.ivalue()
		max := leaf.count.imax()
		if n > max {
			n = max
		}
		return leaf.len * n
	}
	return leaf.len
}

func (leaf *tleaf) LenType() int {
	return leaf.etype
}

func (leaf *tleaf) MaxIndex() []int {
	panic("not implemented")
}

func (leaf *tleaf) Offset() int {
	return leaf.offset
}

func (leaf *tleaf) Kind() reflect.Kind {
	panic("not implemented")
}

func (leaf *tleaf) Type() reflect.Type {
	panic("not implemented")
}

func (leaf *tleaf) Value(i int) interface{} {
	panic("not implemented")
}

func (leaf *tleaf) value() interface{} {
	panic("not implemented")
}

func (leaf *tleaf) readBasket(r *RBuffer) error {
	panic("not implemented")
}

func (leaf *tleaf) scan(r *RBuffer, ptr interface{}) error {
	panic("not implemented")
}

func (leaf *tleaf) TypeName() string {
	panic("not implemented")
}

func (leaf *tleaf) UnmarshalROOT(r *RBuffer) error {
	if r.err != nil {
		return r.err
	}

	start := r.Pos()
	vers, pos, bcnt := r.ReadVersion()
	leaf.rvers = vers

	if err := leaf.named.UnmarshalROOT(r); err != nil {
		r.err = err
		return r.err
	}

	leaf.len = int(r.ReadI32())
	leaf.etype = int(r.ReadI32())
	leaf.offset = int(r.ReadI32())
	leaf.hasrange = r.ReadBool()
	leaf.unsigned = r.ReadBool()

	leaf.count = nil
	ptr := r.ReadObjectAny()
	if ptr != nil {
		leaf.count = ptr.(leafCount)
	}

	r.CheckByteCount(pos, bcnt, start, "TLeaf")
	if leaf.len == 0 {
		leaf.len = 1
	}

	return r.Err()
}

// tleafElement is a Leaf for a general object derived from Object.
type tleafElement struct {
	rvers int16
	tleaf
	id    int32 // element serial number in fInfo
	ltype int32 // leaf type

	ptr       interface{}
	src       reflect.Value
	rstreamer RStreamer
	streamers []StreamerElement
}

func (leaf *tleafElement) Class() string {
	return "TLeafElement"
}

func (leaf *tleafElement) ivalue() int {
	return int(leaf.src.Int())
}

func (leaf *tleafElement) imax() int {
	panic("not implemented")
}

func (leaf *tleafElement) Kind() reflect.Kind {
	return leaf.src.Kind()
}

func (leaf *tleafElement) Type() reflect.Type {
	return leaf.src.Type()
}

func (leaf *tleafElement) TypeName() string {
	name := leaf.src.Type().Name()
	if name == "" {
		panic(fmt.Errorf("rootio: invalid typename for leaf %q", leaf.Name()))
	}
	return name
}

func (leaf *tleafElement) UnmarshalROOT(r *RBuffer) error {
	if r.err != nil {
		return r.err
	}
	beg := r.Pos()

	vers, pos, bcnt := r.ReadVersion()
	leaf.rvers = vers

	if err := leaf.tleaf.UnmarshalROOT(r); err != nil {
		r.err = err
		return r.err
	}

	leaf.id = r.ReadI32()
	leaf.ltype = r.ReadI32()

	r.CheckByteCount(pos, bcnt, beg, "TLeafElement")
	return r.err
}

func (leaf *tleafElement) readBasket(r *RBuffer) error {
	if r.err != nil {
		return r.err
	}

	if leaf.rstreamer == nil {
		panic("rootio: nil streamer (leaf: " + leaf.Name() + ")")
	}

	err := leaf.rstreamer.RStream(r)
	if err != nil {
		return err
	}

	return nil
}

func (leaf *tleafElement) scan(r *RBuffer, ptr interface{}) error {
	if r.err != nil {
		return r.err
	}

	rv := reflect.Indirect(reflect.ValueOf(ptr))
	switch rv.Kind() {
	case reflect.Struct:
		for i := 0; i < rv.Type().NumField(); i++ {
			f := rv.Field(i)
			ft := rv.Type().Field(i)
			f.Set(leaf.src.FieldByName(ft.Name))
		}
	case reflect.Array:
		reflect.Copy(rv, leaf.src)
	case reflect.Slice:
		if rv.UnsafeAddr() != leaf.src.UnsafeAddr() {
			sli := leaf.src
			rv.Set(reflect.MakeSlice(sli.Type(), sli.Len(), sli.Cap()))
			reflect.Copy(rv, sli)
		}
	default:
		rv.Set(leaf.src)
	}
	return r.err
}

func (leaf *tleafElement) setAddress(ptr interface{}) error {
	var err error
	leaf.ptr = ptr
	leaf.src = reflect.ValueOf(leaf.ptr).Elem()

	var impl rstreamerImpl
	sictx := leaf.branch.getTree().getFile()
	for _, elt := range leaf.streamers {
		impl.funcs = append(impl.funcs, rstreamerFrom(elt, ptr, leaf.count, sictx))
	}
	leaf.rstreamer = &impl
	return err
}

func init() {
	{
		f := func() reflect.Value {
			o := &tleaf{}
			return reflect.ValueOf(o)
		}
		Factory.add("TLeaf", f)
		Factory.add("*rootio.tleaf", f)
	}
	{
		f := func() reflect.Value {
			o := &tleafElement{}
			return reflect.ValueOf(o)
		}
		Factory.add("TLeafElement", f)
		Factory.add("*rootio.tleafElement", f)
	}
}

var _ Object = (*tleaf)(nil)
var _ Named = (*tleaf)(nil)
var _ Leaf = (*tleaf)(nil)
var _ ROOTUnmarshaler = (*tleaf)(nil)

var _ Object = (*tleafElement)(nil)
var _ Named = (*tleafElement)(nil)
var _ Leaf = (*tleafElement)(nil)
var _ ROOTUnmarshaler = (*tleafElement)(nil)
<commit_msg>rootio: implement ROOTMarshaler for TLeaf<commit_after>// Copyright 2017 The go-hep Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rootio

import (
	"fmt"
	"reflect"
)

type tleaf struct {
	rvers    int16
	named    tnamed
	len      int
	etype    int
	offset   int
	hasrange bool
	unsigned bool
	count    leafCount
	branch   Branch
}

// Name returns the name of the instance
func (leaf *tleaf) Name() string {
	return leaf.named.Name()
}

// Title returns the title of the instance
func (leaf *tleaf) Title() string {
	return leaf.named.Title()
}

func (leaf *tleaf) Class() string {
	return "TLeaf"
}

func (leaf *tleaf) ArrayDim() int {
	panic("not implemented")
}

func (leaf *tleaf) setBranch(b Branch) {
	leaf.branch = b
}

func (leaf *tleaf) Branch() Branch {
	return leaf.branch
}

func (leaf *tleaf) HasRange() bool {
	return leaf.hasrange
}

func (leaf *tleaf) IsUnsigned() bool {
	return leaf.unsigned
}

func (leaf *tleaf) LeafCount() Leaf {
	return leaf.count
}

func (leaf *tleaf) Len() int {
	if leaf.count != nil {
		// variable length array
		n := leaf.count.ivalue()
		max := leaf.count.imax()
		if n > max {
			n = max
		}
		return leaf.len * n
	}
	return leaf.len
}

func (leaf *tleaf) LenType() int {
	return leaf.etype
}

func (leaf *tleaf) MaxIndex() []int {
	panic("not implemented")
}

func (leaf *tleaf) Offset() int {
	return leaf.offset
}

func (leaf *tleaf) Kind() reflect.Kind {
	panic("not implemented")
}

func (leaf *tleaf) Type() reflect.Type {
	panic("not implemented")
}

func (leaf *tleaf) Value(i int) interface{} {
	panic("not implemented")
}

func (leaf *tleaf) value() interface{} {
	panic("not implemented")
}

func (leaf *tleaf) readBasket(r *RBuffer) error {
	panic("not implemented")
}

func (leaf *tleaf) scan(r *RBuffer, ptr interface{}) error {
	panic("not implemented")
}

func (leaf *tleaf) TypeName() string {
	panic("not implemented")
}

func (leaf *tleaf) MarshalROOT(w *WBuffer) (int, error) {
	if w.err != nil {
		return 0, w.err
	}

	pos := w.Pos()
	w.WriteVersion(leaf.rvers)
	leaf.named.MarshalROOT(w)

	w.WriteI32(int32(leaf.len))
	w.WriteI32(int32(leaf.etype))
	w.WriteI32(int32(leaf.offset))
	w.WriteBool(leaf.hasrange)
	w.WriteBool(leaf.unsigned)
	w.WriteObjectAny(leaf.count)
	return w.SetByteCount(pos, "TLeaf")
}

func (leaf *tleaf) UnmarshalROOT(r *RBuffer) error {
	if r.err != nil {
		return r.err
	}

	start := r.Pos()
	vers, pos, bcnt := r.ReadVersion()
	leaf.rvers = vers

	if err := leaf.named.UnmarshalROOT(r); err != nil {
		r.err = err
		return r.err
	}

	leaf.len = int(r.ReadI32())
	leaf.etype = int(r.ReadI32())
	leaf.offset = int(r.ReadI32())
	leaf.hasrange = r.ReadBool()
	leaf.unsigned = r.ReadBool()

	leaf.count = nil
	ptr := r.ReadObjectAny()
	if ptr != nil {
		leaf.count = ptr.(leafCount)
	}

	r.CheckByteCount(pos, bcnt, start, "TLeaf")
	if leaf.len == 0 {
		leaf.len = 1
	}

	return r.Err()
}

// tleafElement is a Leaf for a general object derived from Object.
type tleafElement struct {
	rvers int16
	tleaf
	id    int32 // element serial number in fInfo
	ltype int32 // leaf type

	ptr       interface{}
	src       reflect.Value
	rstreamer RStreamer
	streamers []StreamerElement
}

func (leaf *tleafElement) Class() string {
	return "TLeafElement"
}

func (leaf *tleafElement) ivalue() int {
	return int(leaf.src.Int())
}

func (leaf *tleafElement) imax() int {
	panic("not implemented")
}

func (leaf *tleafElement) Kind() reflect.Kind {
	return leaf.src.Kind()
}

func (leaf *tleafElement) Type() reflect.Type {
	return leaf.src.Type()
}

func (leaf *tleafElement) TypeName() string {
	name := leaf.src.Type().Name()
	if name == "" {
		panic(fmt.Errorf("rootio: invalid typename for leaf %q", leaf.Name()))
	}
	return name
}

func (leaf *tleafElement) UnmarshalROOT(r *RBuffer) error {
	if r.err != nil {
		return r.err
	}
	beg := r.Pos()

	vers, pos, bcnt := r.ReadVersion()
	leaf.rvers = vers

	if err := leaf.tleaf.UnmarshalROOT(r); err != nil {
		r.err = err
		return r.err
	}

	leaf.id = r.ReadI32()
	leaf.ltype = r.ReadI32()

	r.CheckByteCount(pos, bcnt, beg, "TLeafElement")
	return r.err
}

func (leaf *tleafElement) readBasket(r *RBuffer) error {
	if r.err != nil {
		return r.err
	}

	if leaf.rstreamer == nil {
		panic("rootio: nil streamer (leaf: " + leaf.Name() + ")")
	}

	err := leaf.rstreamer.RStream(r)
	if err != nil {
		return err
	}

	return nil
}

func (leaf *tleafElement) scan(r *RBuffer, ptr interface{}) error {
	if r.err != nil {
		return r.err
	}

	rv := reflect.Indirect(reflect.ValueOf(ptr))
	switch rv.Kind() {
	case reflect.Struct:
		for i := 0; i < rv.Type().NumField(); i++ {
			f := rv.Field(i)
			ft := rv.Type().Field(i)
			f.Set(leaf.src.FieldByName(ft.Name))
		}
	case reflect.Array:
		reflect.Copy(rv, leaf.src)
	case reflect.Slice:
		if rv.UnsafeAddr() != leaf.src.UnsafeAddr() {
			sli := leaf.src
			rv.Set(reflect.MakeSlice(sli.Type(), sli.Len(), sli.Cap()))
			reflect.Copy(rv, sli)
		}
	default:
		rv.Set(leaf.src)
	}
	return r.err
}

func (leaf *tleafElement) setAddress(ptr interface{}) error {
	var err error
	leaf.ptr = ptr
	leaf.src = reflect.ValueOf(leaf.ptr).Elem()

	var impl rstreamerImpl
	sictx := leaf.branch.getTree().getFile()
	for _, elt := range leaf.streamers {
		impl.funcs = append(impl.funcs, rstreamerFrom(elt, ptr, leaf.count, sictx))
	}
	leaf.rstreamer = &impl
	return err
}

func init() {
	{
		f := func() reflect.Value {
			o := &tleaf{}
			return reflect.ValueOf(o)
		}
		Factory.add("TLeaf", f)
		Factory.add("*rootio.tleaf", f)
	}
	{
		f := func() reflect.Value {
			o := &tleafElement{}
			return reflect.ValueOf(o)
		}
		Factory.add("TLeafElement", f)
		Factory.add("*rootio.tleafElement", f)
	}
}

var (
	_ Object          = (*tleaf)(nil)
	_ Named           = (*tleaf)(nil)
	_ Leaf            = (*tleaf)(nil)
	_ ROOTMarshaler   = (*tleaf)(nil)
	_ ROOTUnmarshaler = (*tleaf)(nil)
)

var _ Object = (*tleafElement)(nil)
var _ Named = (*tleafElement)(nil)
var _ Leaf = (*tleafElement)(nil)
var _ ROOTUnmarshaler = (*tleafElement)(nil)
<|endoftext|>


<commit_before>package helper

import (
	"bytes"
	"context"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync/atomic"

	"github.com/ansel1/merry"
	"github.com/go-graphite/carbonapi/limiter"
	"github.com/go-graphite/carbonapi/pkg/parser"
	util "github.com/go-graphite/carbonapi/util/ctx"
	zipper_errors "github.com/go-graphite/carbonapi/zipper/errors"
	"github.com/go-graphite/carbonapi/zipper/types"
	"go.uber.org/zap"
)

func HttpCode(err error) int {
	if err == nil {
		return http.StatusOK
	}
	if _, ok := err.(*net.OpError); ok {
		return http.StatusServiceUnavailable
	}
	if urlErr, ok := err.(*url.Error); ok {
		if _, ok = urlErr.Err.(*net.OpError); ok {
			return http.StatusServiceUnavailable
		} else if errors.Is(err, context.DeadlineExceeded) {
			return http.StatusServiceUnavailable
		}
	}
	return merry.HTTPCode(err)
}

func MergeHttpErrors(errors []merry.Error) (int, []string) {
	returnCode := http.StatusNotFound
	errMsgs := make([]string, 0)
	for _, err := range errors {
		var code int
		c := merry.RootCause(err)
		if c == nil {
			c = err
		}
		if merry.Is(c, zipper_errors.ErrTimeout) {
			code = http.StatusGatewayTimeout
		} else {
			code = HttpCode(c)
		}

		if code == http.StatusNotFound || merry.Is(c, parser.ErrSeriesDoesNotExist) {
			continue
		}
		if msg := merry.Message(c); len(msg) > 0 {
			errMsgs = append(errMsgs, strings.TrimRight(msg, "\n"))
		} else {
			errMsgs = append(errMsgs, c.Error())
		}

		if code == http.StatusGatewayTimeout || code == http.StatusBadGateway {
			// simplify code, one error type for communications errors, all we can retry
			code = http.StatusServiceUnavailable
		}

		if code == http.StatusBadRequest {
			// The 400 is returned on wrong requests, e.g. non-existent functions
			returnCode = code
		} else if returnCode == http.StatusNotFound || code == http.StatusForbidden {
			// First error or access denied (may be limits or other)
			returnCode = code
		} else if code != http.StatusServiceUnavailable {
			returnCode = code
		}
	}

	return returnCode, errMsgs
}

func MergeHttpErrorMap(errorsMap map[string]merry.Error) (int, []string) {
	errors := make([]merry.Error, len(errorsMap))
	i := 0
	for _, err := range errorsMap {
		errors[i] = err
		i++
	}

	return MergeHttpErrors(errors)
}

func HttpErrorByCode(err merry.Error) merry.Error {
	var returnErr merry.Error
	if err == nil {
		returnErr = types.ErrNoMetricsFetched.WithHTTPCode(404)
	} else {
		code := merry.HTTPCode(err)
		msg := merry.Message(err)
		if code == http.StatusForbidden {
			returnErr = types.ErrForbidden.WithHTTPCode(403)
			if len(msg) > 0 {
				// pass message to caller
				returnErr = returnErr.WithMessage(msg)
			}
		} else if code == http.StatusServiceUnavailable || code == http.StatusBadGateway || code == http.StatusGatewayTimeout {
			returnErr = types.ErrFailedToFetch.WithHTTPCode(code)
		} else {
			returnErr = types.ErrFailed.WithHTTPCode(code)
		}
	}

	return returnErr
}

type ServerResponse struct {
	Server   string
	Response []byte
}

type HttpQuery struct {
	groupName string
	servers   []string
	maxTries  int
	limiter   limiter.ServerLimiter
	client    *http.Client
	encoding  string

	counter uint64
}

func NewHttpQuery(groupName string, servers []string, maxTries int, limiter limiter.ServerLimiter, client *http.Client, encoding string) *HttpQuery {
	return &HttpQuery{
		groupName: groupName,
		servers:   servers,
		maxTries:  maxTries,
		limiter:   limiter,
		client:    client,
		encoding:  encoding,
	}
}

func (c *HttpQuery) pickServer(logger *zap.Logger) string {
	if len(c.servers) == 1 {
		// No need to do heavy operations here
		return c.servers[0]
	}
	logger = logger.With(zap.String("function", "picker"))
	counter := atomic.AddUint64(&(c.counter), 1)
	idx := counter % uint64(len(c.servers))
	srv := c.servers[int(idx)]
	logger.Debug("picked",
		zap.Uint64("counter", counter),
		zap.Uint64("idx", idx),
		zap.String("server", srv),
	)

	return srv
}

func (c *HttpQuery) doRequest(ctx context.Context, logger *zap.Logger, server, uri string, r types.Request) (*ServerResponse, merry.Error) {
	logger = logger.With(
		zap.String("function", "HttpQuery.doRequest"),
	)

	u, err := url.Parse(server + uri)
	if err != nil {
		return nil, merry.Here(err).WithValue("server", server)
	}

	var reader io.Reader
	var body []byte
	if r != nil {
		body, err = r.Marshal()
		if err != nil {
			return nil, merry.Here(err).WithValue("server", server)
		}
		if body != nil {
			reader = bytes.NewReader(body)
		}
	}
	logger = logger.With(
		zap.String("server", server),
		zap.String("name", c.groupName),
		zap.String("uri", u.String()),
	)

	// TODO: change to NewRequestWithContext
	req, err := http.NewRequest("GET", u.String(), reader)
	if err != nil {
		return nil, merry.Here(err).WithValue("server", server)
	}

	req.Header.Set("Accept", c.encoding)
	req = util.MarshalPassHeaders(ctx, util.MarshalCtx(ctx, util.MarshalCtx(ctx, req, util.HeaderUUIDZipper), util.HeaderUUIDAPI))

	logger.Debug("trying to get slot",
		zap.String("name", server),
	)
	err = c.limiter.Enter(ctx, server)
	if err != nil {
		logger.Debug("timeout waiting for a slot")
		return nil, merry.Here(err).WithValue("server", server)
	}

	defer c.limiter.Leave(ctx, server)

	logger.Debug("got slot for server",
		zap.String("name", server),
	)

	if r != nil {
		logger = logger.With(zap.Any("payloadData", r.LogInfo()))
	}
	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		logger.Debug("error fetching result",
			zap.Error(err),
		)

		if urlErr, ok := err.(*url.Error); ok {
			if netErr, ok := urlErr.Err.(*net.OpError); ok {
				return nil, merry.Here(err).WithValue("server", server).WithCause(netErr)
			} else if merry.Is(urlErr, context.DeadlineExceeded) {
				return nil, merry.Here(err).WithValue("server", server).WithCause(urlErr)
			}
		}
		return nil, merry.Here(err).WithValue("server", server).WithHTTPCode(HttpCode(err))
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	// we don't need to process any further if the response is empty.
	if resp.StatusCode == http.StatusNotFound {
		return &ServerResponse{Server: server}, nil
	}

	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Debug("error reading body",
			zap.Error(err),
		)
		return nil, merry.Here(err).WithValue("server", server)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, types.ErrFailedToFetch.WithValue("server", server).WithMessage(string(body)).WithHTTPCode(resp.StatusCode)
	}

	return &ServerResponse{Server: server, Response: body}, nil
}

func (c *HttpQuery) DoQuery(ctx context.Context, logger *zap.Logger, uri string, r types.Request) (*ServerResponse, merry.Error) {
	maxTries := c.maxTries
	if len(c.servers) > maxTries {
		maxTries = len(c.servers)
	}

	e := types.ErrFailedToFetch.WithValue("uri", uri)
	for try := 0; try < maxTries; try++ {
		server := c.pickServer(logger)
		res, err := c.doRequest(ctx, logger, server, uri, r)
		if err != nil {
			logger.Debug("have errors",
				zap.Error(err),
			)

			e = e.WithCause(err)
			continue
		}

		return res, nil
	}

	return nil, types.ErrMaxTriesExceeded.WithCause(e)
}

func (c *HttpQuery) DoQueryToAll(ctx context.Context, logger *zap.Logger, uri string, r types.Request) ([]*ServerResponse, merry.Error) {
	maxTries := c.maxTries
	if len(c.servers) > maxTries {
		maxTries = len(c.servers)
	}

	res := make([]*ServerResponse, len(c.servers))
	e := types.ErrFailedToFetch.WithValue("uri", uri)
	responseCount := 0
	for i := range c.servers {
		for try := 0; try < maxTries; try++ {
			response, err := c.doRequest(ctx, logger, c.servers[i], uri, r)
			if err != nil {
				logger.Debug("have errors",
					zap.Error(err),
				)

				e = e.WithCause(err)
				continue
			}

			res[i] = response
			responseCount++
			break
		}
	}

	if responseCount == len(c.servers) {
		return res, nil
	}

	return res, types.ErrMaxTriesExceeded.WithCause(e)
}
<commit_msg>some zipper error handling refactor<commit_after>package helper

import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync/atomic"

	"github.com/ansel1/merry"
	"github.com/go-graphite/carbonapi/limiter"
	"github.com/go-graphite/carbonapi/pkg/parser"
	util "github.com/go-graphite/carbonapi/util/ctx"
	zipper_errors "github.com/go-graphite/carbonapi/zipper/errors"
	"github.com/go-graphite/carbonapi/zipper/types"
	"go.uber.org/zap"
)

func requestError(err error, server string) merry.Error {
	if urlErr, ok := err.(*url.Error); ok {
		if netErr, ok := urlErr.Err.(*net.OpError); ok {
			return merry.Here(err).WithValue("server", server).WithCause(netErr)
		} else if merry.Is(urlErr, context.DeadlineExceeded) {
			return merry.Here(err).WithValue("server", server).WithCause(urlErr)
		}
	}
	return merry.Here(err).WithValue("server", server).WithHTTPCode(HttpCode(err))
}

func HttpCode(err error) int {
	if err == nil {
		return http.StatusOK
	}
	if _, ok := err.(*net.OpError); ok {
		return http.StatusServiceUnavailable
	}
	if urlErr, ok := err.(*url.Error); ok {
		if _, ok = urlErr.Err.(*net.OpError); ok {
			return http.StatusServiceUnavailable
		} else if merry.Is(err, context.DeadlineExceeded) {
			return http.StatusServiceUnavailable
		}
	}
	return merry.HTTPCode(err)
}

func MergeHttpErrors(errors []merry.Error) (int, []string) {
	returnCode := http.StatusNotFound
	errMsgs := make([]string, 0)
	for _, err := range errors {
		var code int
		c := merry.RootCause(err)
		if c == nil {
			c = err
		}
		if merry.Is(c, zipper_errors.ErrTimeout) {
			code = http.StatusGatewayTimeout
		} else {
			code = HttpCode(c)
		}

		if code == http.StatusNotFound || merry.Is(c, parser.ErrSeriesDoesNotExist) {
			continue
		}
		if msg := merry.Message(c); len(msg) > 0 {
			errMsgs = append(errMsgs, strings.TrimRight(msg, "\n"))
		} else {
			errMsgs = append(errMsgs, c.Error())
		}

		if code == http.StatusGatewayTimeout || code == http.StatusBadGateway {
			// simplify code, one error type for communications errors, all we can retry
			code = http.StatusServiceUnavailable
		}

		if code == http.StatusBadRequest {
			// The 400 is returned on wrong requests, e.g. non-existent functions
			returnCode = code
		} else if returnCode == http.StatusNotFound || code == http.StatusForbidden {
			// First error or access denied (may be limits or other)
			returnCode = code
		} else if code != http.StatusServiceUnavailable {
			returnCode = code
		}
	}

	return returnCode, errMsgs
}

func MergeHttpErrorMap(errorsMap map[string]merry.Error) (int, []string) {
	errors := make([]merry.Error, len(errorsMap))
	i := 0
	for _, err := range errorsMap {
		errors[i] = err
		i++
	}

	return MergeHttpErrors(errors)
}

func HttpErrorByCode(err merry.Error) merry.Error {
	var returnErr merry.Error
	if err == nil {
		returnErr = types.ErrNoMetricsFetched.WithHTTPCode(404)
	} else {
		code := merry.HTTPCode(err)
		msg := merry.Message(err)
		if code == http.StatusForbidden {
			returnErr = types.ErrForbidden.WithHTTPCode(403)
			if len(msg) > 0 {
				// pass message to caller
				returnErr = returnErr.WithMessage(msg)
			}
		} else if code == http.StatusServiceUnavailable || code == http.StatusBadGateway || code == http.StatusGatewayTimeout {
			returnErr = types.ErrFailedToFetch.WithHTTPCode(code)
		} else {
			returnErr = types.ErrFailed.WithHTTPCode(code)
		}
	}

	return returnErr
}

type ServerResponse struct {
	Server   string
	Response []byte
}

type HttpQuery struct {
	groupName string
	servers   []string
	maxTries  int
	limiter   limiter.ServerLimiter
	client    *http.Client
	encoding  string

	counter uint64
}

func NewHttpQuery(groupName string, servers []string, maxTries int, limiter limiter.ServerLimiter, client *http.Client, encoding string) *HttpQuery {
	return &HttpQuery{
		groupName: groupName,
		servers:   servers,
		maxTries:  maxTries,
		limiter:   limiter,
		client:    client,
		encoding:  encoding,
	}
}

func (c *HttpQuery) pickServer(logger *zap.Logger) string {
	if len(c.servers) == 1 {
		// No need to do heavy operations here
		return c.servers[0]
	}
	logger = logger.With(zap.String("function", "picker"))
	counter := atomic.AddUint64(&(c.counter), 1)
	idx := counter % uint64(len(c.servers))
	srv := c.servers[int(idx)]
	logger.Debug("picked",
		zap.Uint64("counter", counter),
		zap.Uint64("idx", idx),
		zap.String("server", srv),
	)

	return srv
}

func (c *HttpQuery) doRequest(ctx context.Context, logger *zap.Logger, server, uri string, r types.Request) (*ServerResponse, merry.Error) {
	logger = logger.With(
		zap.String("function", "HttpQuery.doRequest"),
	)

	u, err := url.Parse(server + uri)
	if err != nil {
		return nil, merry.Here(err).WithValue("server", server)
	}

	var reader io.Reader
	var body []byte
	if r != nil {
		body, err = r.Marshal()
		if err != nil {
			return nil, merry.Here(err).WithValue("server", server)
		}
		if body != nil {
			reader = bytes.NewReader(body)
		}
	}
	logger = logger.With(
		zap.String("server", server),
		zap.String("name", c.groupName),
		zap.String("uri", u.String()),
	)

	// TODO: change to NewRequestWithContext
	req, err := http.NewRequest("GET", u.String(), reader)
	if err != nil {
		return nil, merry.Here(err).WithValue("server", server)
	}

	req.Header.Set("Accept", c.encoding)
	req = util.MarshalPassHeaders(ctx, util.MarshalCtx(ctx, util.MarshalCtx(ctx, req, util.HeaderUUIDZipper), util.HeaderUUIDAPI))

	logger.Debug("trying to get slot",
		zap.String("name", server),
	)
	err = c.limiter.Enter(ctx, server)
	if err != nil {
		logger.Debug("timeout waiting for a slot")
		return nil, merry.Here(err).WithValue("server", server)
	}

	defer c.limiter.Leave(ctx, server)

	logger.Debug("got slot for server",
		zap.String("name", server),
	)

	if r != nil {
		logger = logger.With(zap.Any("payloadData", r.LogInfo()))
	}
	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		logger.Debug("error fetching result",
			zap.Error(err),
		)

		return nil, requestError(err, server)

	}
	defer func() {
		_ = resp.Body.Close()
	}()

	// we don't need to process any further if the response is empty.
	if resp.StatusCode == http.StatusNotFound {
		return &ServerResponse{Server: server}, nil
	}

	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Debug("error reading body",
			zap.Error(err),
		)
		return nil, merry.Here(err).WithValue("server", server)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, types.ErrFailedToFetch.WithValue("server", server).WithMessage(string(body)).WithHTTPCode(resp.StatusCode)
	}

	return &ServerResponse{Server: server, Response: body}, nil
}

func (c *HttpQuery) DoQuery(ctx context.Context, logger *zap.Logger, uri string, r types.Request) (*ServerResponse, merry.Error) {
	maxTries := c.maxTries
	if len(c.servers) > maxTries {
		maxTries = len(c.servers)
	}

	e := types.ErrFailedToFetch.WithValue("uri", uri)
	for try := 0; try < maxTries; try++ {
		server := c.pickServer(logger)
		res, err := c.doRequest(ctx, logger, server, uri, r)
		if err != nil {
			logger.Debug("have errors",
				zap.Error(err),
			)

			e = e.WithCause(err)
			continue
		}

		return res, nil
	}

	return nil, types.ErrMaxTriesExceeded.WithCause(e)
}

func (c *HttpQuery) DoQueryToAll(ctx context.Context, logger *zap.Logger, uri string, r types.Request) ([]*ServerResponse, merry.Error) {
	maxTries := c.maxTries
	if len(c.servers) > maxTries {
		maxTries = len(c.servers)
	}

	res := make([]*ServerResponse, len(c.servers))
	e := types.ErrFailedToFetch.WithValue("uri", uri)
	responseCount := 0
	for i := range c.servers {
		for try := 0; try < maxTries; try++ {
			response, err := c.doRequest(ctx, logger, c.servers[i], uri, r)
			if err != nil {
				logger.Debug("have errors",
					zap.Error(err),
				)

				e = e.WithCause(err)
				continue
			}

			res[i] = response
			responseCount++
			break
		}
	}

	if responseCount == len(c.servers) {
		return res, nil
	}

	return res, types.ErrMaxTriesExceeded.WithCause(e)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"time"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type getter interface {
	getSlaves() (*pbd.ServiceList, error)
	getJobs(context.Context, *pbd.RegistryEntry) ([]*pbs.JobAssignment, error)
	getConfig(context.Context, *pbd.RegistryEntry) ([]*pbs.Requirement, error)
}

type checker interface {
	assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config)
	discover() *pbd.ServiceList
	master(entry *pbd.RegistryEntry, master bool) (bool, error)
	setprev([]string)
	getprev() []string
}

func getFleetStatus(ctx context.Context, c checker) (map[string]*pbs.JobList, map[string]*pbs.Config) {
	resJ := make(map[string]*pbs.JobList)
	resC := make(map[string]*pbs.Config)

	curr := make([]string, 0)
	for _, service := range c.discover().Services {
		if service.Name == "gobuildslave" {
			curr = append(curr, service.Identifier)
			joblist, config := c.assess(ctx, service.Identifier)
			resJ[service.Identifier] = joblist
			resC[service.Identifier] = config
		}
	}

	c.setprev(curr)

	return resJ, resC
}

// Find the first available server
func chooseServer(ctx context.Context, job *pbs.JobSpec, c checker) string {
	services := c.discover().Services
	for _, i := range rand.Perm(len(services)) {
		service := services[i]
		if service.Name == "gobuildslave" && (job.GetServer() == "" || job.GetServer() == service.GetIdentifier()) {
			jobs, sc := c.assess(ctx, service.Identifier)

			//Don't accept a server which is already running this job
			jobfine := true
			for _, j := range jobs.Details {
				if j.Spec.Name == job.Name {
					jobfine = false
				}

			}
			if jobfine {
				if sc.Disk > job.Disk && (!job.External || sc.External) && (!job.GetCds() || sc.GetSupportsCds()) {
					return service.Identifier
				}
			}
		}
	}
	return ""
}

func (s *Server) addAccessPoint(ctx context.Context, ap string) {
	s.accessPointsMutex.Lock()
	defer s.accessPointsMutex.Unlock()

	for key, val := range s.accessPoints {
		if time.Now().Sub(val) > time.Hour {
			s.RaiseIssue(ctx, fmt.Sprintf("Access point Missing"), fmt.Sprintf("%v has been missing since %v", key, val), false)
		}
	}

	switch ap {
	case "70:3A:CB:17:CF:BB":
		s.accessPoints["LR2"] = time.Now()
	case "70:3A:CB:17:CC:CF":
		s.accessPoints["Bedroom"] = time.Now()
	case "70:3A:CB:17:CE:E3":
		s.accessPoints["LR"] = time.Now()
	case "70:3A:CB:17:CF:BF":
		s.accessPoints["LR2"] = time.Now()
	}
}

// Find the first available server
func (s *Server) selectServer(ctx context.Context, job *pbs.Job, g getter) string {
	services, _ := g.getSlaves()
	for _, i := range rand.Perm(len(services.Services)) {
		jobs, _ := g.getJobs(ctx, services.Services[i])
		//Don't accept a server which is already running this job
		jobfine := true
		for _, j := range jobs {
			if j.Job.Name == job.Name {
				jobfine = false
			}

		}
		if jobfine {
			requirements, err := g.getConfig(ctx, services.Services[i])
			if err == nil {
				allmatch := true
				for _, req := range job.Requirements {
					localmatch := false
					for _, r := range requirements {
						if r.Category == pbs.RequirementCategory_ACCESS_POINT {
							s.addAccessPoint(ctx, r.Properties)
						}
						if r.Category == req.Category && r.Properties == req.Properties {
							localmatch = true

						}
					}

					if !localmatch {
						allmatch = false
					}
				}
				if allmatch {
					return services.Services[i].Identifier
				}
			}
		}
	}
	return ""
}

func configDiff(cm, cs *pb.Config) *pb.Config {
	retConfig := &pb.Config{}

	for _, entry := range cm.Intents {
		nIntent := &pb.Intent{}
		nIntent.Spec = entry.Spec
		nIntent.Count = entry.Count
		retConfig.Intents = append(retConfig.Intents, nIntent)
	}

	for _, entry := range cs.Intents {
		for _, pair := range retConfig.Intents {
			if entry.Spec.Name == pair.Spec.Name {
				pair.Count -= entry.Count
			}
		}
	}

	return retConfig
}

func loadConfig(f string) (*pb.Config, error) {
	toload := &pb.Config{}
	bytes, _ := ioutil.ReadFile(f)
	proto.UnmarshalText(string(bytes), toload)
	return toload, nil
}

func runJobs(c *pb.Config) []*pbs.JobSpec {
	var jobs []*pbs.JobSpec
	for _, j := range c.Intents {
		for i := 0; i < int(j.Count); i++ {
			jobs = append(jobs, j.Spec)
		}
	}

	return jobs
}
<commit_msg>Increase missing time. This closes #613<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"time"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type getter interface {
	getSlaves() (*pbd.ServiceList, error)
	getJobs(context.Context, *pbd.RegistryEntry) ([]*pbs.JobAssignment, error)
	getConfig(context.Context, *pbd.RegistryEntry) ([]*pbs.Requirement, error)
}

type checker interface {
	assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config)
	discover() *pbd.ServiceList
	master(entry *pbd.RegistryEntry, master bool) (bool, error)
	setprev([]string)
	getprev() []string
}

func getFleetStatus(ctx context.Context, c checker) (map[string]*pbs.JobList, map[string]*pbs.Config) {
	resJ := make(map[string]*pbs.JobList)
	resC := make(map[string]*pbs.Config)

	curr := make([]string, 0)
	for _, service := range c.discover().Services {
		if service.Name == "gobuildslave" {
			curr = append(curr, service.Identifier)
			joblist, config := c.assess(ctx, service.Identifier)
			resJ[service.Identifier] = joblist
			resC[service.Identifier] = config
		}
	}

	c.setprev(curr)

	return resJ, resC
}

// Find the first available server
func chooseServer(ctx context.Context, job *pbs.JobSpec, c checker) string {
	services := c.discover().Services
	for _, i := range rand.Perm(len(services)) {
		service := services[i]
		if service.Name == "gobuildslave" && (job.GetServer() == "" || job.GetServer() == service.GetIdentifier()) {
			jobs, sc := c.assess(ctx, service.Identifier)

			//Don't accept a server which is already running this job
			jobfine := true
			for _, j := range jobs.Details {
				if j.Spec.Name == job.Name {
					jobfine = false
				}

			}
			if jobfine {
				if sc.Disk > job.Disk && (!job.External || sc.External) && (!job.GetCds() || sc.GetSupportsCds()) {
					return service.Identifier
				}
			}
		}
	}
	return ""
}

func (s *Server) addAccessPoint(ctx context.Context, ap string) {
	s.accessPointsMutex.Lock()
	defer s.accessPointsMutex.Unlock()

	for key, val := range s.accessPoints {
		if time.Now().Sub(val) > time.Hour*24 {
			s.RaiseIssue(ctx, fmt.Sprintf("Access point Missing"), fmt.Sprintf("%v has been missing since %v", key, val), false)
		}
	}

	switch ap {
	case "70:3A:CB:17:CF:BB":
		s.accessPoints["LR2"] = time.Now()
	case "70:3A:CB:17:CC:CF":
		s.accessPoints["Bedroom"] = time.Now()
	case "70:3A:CB:17:CE:E3":
		s.accessPoints["LR"] = time.Now()
	case "70:3A:CB:17:CF:BF":
		s.accessPoints["LR2"] = time.Now()
	}
}

// Find the first available server
func (s *Server) selectServer(ctx context.Context, job *pbs.Job, g getter) string {
	services, _ := g.getSlaves()
	for _, i := range rand.Perm(len(services.Services)) {
		jobs, _ := g.getJobs(ctx, services.Services[i])
		//Don't accept a server which is already running this job
		jobfine := true
		for _, j := range jobs {
			if j.Job.Name == job.Name {
				jobfine = false
			}

		}
		if jobfine {
			requirements, err := g.getConfig(ctx, services.Services[i])
			if err == nil {
				allmatch := true
				for _, req := range job.Requirements {
					localmatch := false
					for _, r := range requirements {
						if r.Category == pbs.RequirementCategory_ACCESS_POINT {
							s.addAccessPoint(ctx, r.Properties)
						}
						if r.Category == req.Category && r.Properties == req.Properties {
							localmatch = true

						}
					}

					if !localmatch {
						allmatch = false
					}
				}
				if allmatch {
					return services.Services[i].Identifier
				}
			}
		}
	}
	return ""
}

func configDiff(cm, cs *pb.Config) *pb.Config {
	retConfig := &pb.Config{}

	for _, entry := range cm.Intents {
		nIntent := &pb.Intent{}
		nIntent.Spec = entry.Spec
		nIntent.Count = entry.Count
		retConfig.Intents = append(retConfig.Intents, nIntent)
	}

	for _, entry := range cs.Intents {
		for _, pair := range retConfig.Intents {
			if entry.Spec.Name == pair.Spec.Name {
				pair.Count -= entry.Count
			}
		}
	}

	return retConfig
}

func loadConfig(f string) (*pb.Config, error) {
	toload := &pb.Config{}
	bytes, _ := ioutil.ReadFile(f)
	proto.UnmarshalText(string(bytes), toload)
	return toload, nil
}

func runJobs(c *pb.Config) []*pbs.JobSpec {
	var jobs []*pbs.JobSpec
	for _, j := range c.Intents {
		for i := 0; i < int(j.Count); i++ {
			jobs = append(jobs, j.Spec)
		}
	}

	return jobs
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type getter interface {
	getSlaves(context.Context) (*pbd.ServiceList, error)
	getJobs(context.Context, *pbd.RegistryEntry) ([]*pbs.JobAssignment, error)
	getConfig(context.Context, *pbd.RegistryEntry) ([]*pbs.Requirement, error)
}

type checker interface {
	assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config)
	discover() *pbd.ServiceList
	master(entry *pbd.RegistryEntry, master bool) (bool, error)
	setprev([]string)
	getprev() []string
}

func getFleetStatus(ctx context.Context, c checker) (map[string]*pbs.JobList, map[string]*pbs.Config) {
	resJ := make(map[string]*pbs.JobList)
	resC := make(map[string]*pbs.Config)

	curr := make([]string, 0)
	for _, service := range c.discover().Services {
		if service.Name == "gobuildslave" {
			curr = append(curr, service.Identifier)
			joblist, config := c.assess(ctx, service.Identifier)
			resJ[service.Identifier] = joblist
			resC[service.Identifier] = config
		}
	}

	c.setprev(curr)

	return resJ, resC
}

// Find the first available server
func chooseServer(ctx context.Context, job *pbs.JobSpec, c checker) string {
	services := c.discover().Services
	for _, i := range rand.Perm(len(services)) {
		service := services[i]
		if service.Name == "gobuildslave" && (job.GetServer() == "" || job.GetServer() == service.GetIdentifier()) {
			jobs, sc := c.assess(ctx, service.Identifier)

			//Don't accept a server which is already running this job
			jobfine := true
			for _, j := range jobs.Details {
				if j.Spec.Name == job.Name {
					jobfine = false
				}

			}
			if jobfine {
				if sc.Disk > job.Disk && (!job.External || sc.External) && (!job.GetCds() || sc.GetSupportsCds()) {
					return service.Identifier
				}
			}
		}
	}
	return ""
}

func (s *Server) addAccessPoint(ctx context.Context, ap string) {
	s.accessPointsMutex.Lock()
	defer s.accessPointsMutex.Unlock()

	for key, val := range s.accessPoints {
		if time.Now().Sub(val) > time.Hour*24 {
			s.RaiseIssue(fmt.Sprintf("Access point Missing"), fmt.Sprintf("%v has been missing since %v", key, val))
		}
	}

	switch ap {
	case "70:3A:CB:17:CF:BB":
		s.accessPoints["LR2"] = time.Now()
	case "70:3A:CB:17:CC:CF":
		s.accessPoints["Bedroom"] = time.Now()
	case "70:3A:CB:17:CE:E3":
		s.accessPoints["LR"] = time.Now()
	case "70:3A:CB:17:CF:BF":
		s.accessPoints["LR2"] = time.Now()
	}
}

// Find the first available server
func (s *Server) selectServer(ctx context.Context, job *pbs.Job, g getter) string {
	services, _ := g.getSlaves(ctx)
	for _, i := range rand.Perm(len(services.Services)) {
		jobs, _ := g.getJobs(ctx, services.Services[i])
		//Don't accept a server which is already running this job
		jobfine := true
		for _, j := range jobs {
			if j.Job.Name == job.Name {
				jobfine = false
			}

		}
		if jobfine {
			requirements, err := g.getConfig(ctx, services.Services[i])
			if err == nil {
				allmatch := true
				for _, req := range job.Requirements {
					localmatch := false
					for _, r := range requirements {
						if r.Category == pbs.RequirementCategory_ACCESS_POINT {
							s.addAccessPoint(ctx, r.Properties)
						}
						if r.Category == req.Category && r.Properties == req.Properties {
							localmatch = true

						}
					}

					if !localmatch {
						allmatch = false
					}
				}
				if allmatch {
					return services.Services[i].Identifier
				}
			}
		}
	}
	return ""
}

func configDiff(cm, cs *pb.Config) *pb.Config {
	retConfig := &pb.Config{}

	for _, entry := range cm.Intents {
		nIntent := &pb.Intent{}
		nIntent.Spec = entry.Spec
		nIntent.Count = entry.Count
		retConfig.Intents = append(retConfig.Intents, nIntent)
	}

	for _, entry := range cs.Intents {
		for _, pair := range retConfig.Intents {
			if entry.Spec.Name == pair.Spec.Name {
				pair.Count -= entry.Count
			}
		}
	}

	return retConfig
}

func loadConfig() (*pb.Config, error) {
	toload := &pb.Config{}
	data, _ := Asset("config.pb")
	err := proto.UnmarshalText(string(data), toload)
	return toload, err
}

func runJobs(c *pb.Config) []*pbs.JobSpec {
	var jobs []*pbs.JobSpec
	for _, j := range c.Intents {
		for i := 0; i < int(j.Count); i++ {
			jobs = append(jobs, j.Spec)
		}
	}

	return jobs
}
<commit_msg>Allow close match on requirements<commit_after>package main

import (
	"fmt"
	"math/rand"
	"strings"
	"time"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type getter interface {
	getSlaves(context.Context) (*pbd.ServiceList, error)
	getJobs(context.Context, *pbd.RegistryEntry) ([]*pbs.JobAssignment, error)
	getConfig(context.Context, *pbd.RegistryEntry) ([]*pbs.Requirement, error)
}

type checker interface {
	assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config)
	discover() *pbd.ServiceList
	master(entry *pbd.RegistryEntry, master bool) (bool, error)
	setprev([]string)
	getprev() []string
}

func getFleetStatus(ctx context.Context, c checker) (map[string]*pbs.JobList, map[string]*pbs.Config) {
	resJ := make(map[string]*pbs.JobList)
	resC := make(map[string]*pbs.Config)

	curr := make([]string, 0)
	for _, service := range c.discover().Services {
		if service.Name == "gobuildslave" {
			curr = append(curr, service.Identifier)
			joblist, config := c.assess(ctx, service.Identifier)
			resJ[service.Identifier] = joblist
			resC[service.Identifier] = config
		}
	}

	c.setprev(curr)

	return resJ, resC
}

// Find the first available server
func chooseServer(ctx context.Context, job *pbs.JobSpec, c checker) string {
	services := c.discover().Services
	for _, i := range rand.Perm(len(services)) {
		service := services[i]
		if service.Name == "gobuildslave" && (job.GetServer() == "" || job.GetServer() == service.GetIdentifier()) {
			jobs, sc := c.assess(ctx, service.Identifier)

			//Don't accept a server which is already running this job
			jobfine := true
			for _, j := range jobs.Details {
				if j.Spec.Name == job.Name {
					jobfine = false
				}

			}
			if jobfine {
				if sc.Disk > job.Disk && (!job.External || sc.External) && (!job.GetCds() || sc.GetSupportsCds()) {
					return service.Identifier
				}
			}
		}
	}
	return ""
}

func (s *Server) addAccessPoint(ctx context.Context, ap string) {
	s.accessPointsMutex.Lock()
	defer s.accessPointsMutex.Unlock()

	for key, val := range s.accessPoints {
		if time.Now().Sub(val) > time.Hour*24 {
			s.RaiseIssue(fmt.Sprintf("Access point Missing"), fmt.Sprintf("%v has been missing since %v", key, val))
		}
	}

	switch ap {
	case "70:3A:CB:17:CF:BB":
		s.accessPoints["LR2"] = time.Now()
	case "70:3A:CB:17:CC:CF":
		s.accessPoints["Bedroom"] = time.Now()
	case "70:3A:CB:17:CE:E3":
		s.accessPoints["LR"] = time.Now()
	case "70:3A:CB:17:CF:BF":
		s.accessPoints["LR2"] = time.Now()
	}
}

// Find the first available server
func (s *Server) selectServer(ctx context.Context, job *pbs.Job, g getter) string {
	services, _ := g.getSlaves(ctx)
	for _, i := range rand.Perm(len(services.Services)) {
		jobs, _ := g.getJobs(ctx, services.Services[i])
		//Don't accept a server which is already running this job
		jobfine := true
		for _, j := range jobs {
			if j.Job.Name == job.Name {
				jobfine = false
			}

		}
		if jobfine {
			requirements, err := g.getConfig(ctx, services.Services[i])
			if err == nil {
				allmatch := true
				for _, req := range job.Requirements {
					localmatch := false
					for _, r := range requirements {
						if r.Category == pbs.RequirementCategory_ACCESS_POINT {
							s.addAccessPoint(ctx, r.Properties)
						}
						if r.Category == req.Category && strings.Contains(r.Properties, req.Properties) {
							localmatch = true

						}
					}

					if !localmatch {
						allmatch = false
					}
				}
				if allmatch {
					return services.Services[i].Identifier
				}
			}
		}
	}
	return ""
}

func configDiff(cm, cs *pb.Config) *pb.Config {
	retConfig := &pb.Config{}

	for _, entry := range cm.Intents {
		nIntent := &pb.Intent{}
		nIntent.Spec = entry.Spec
		nIntent.Count = entry.Count
		retConfig.Intents = append(retConfig.Intents, nIntent)
	}

	for _, entry := range cs.Intents {
		for _, pair := range retConfig.Intents {
			if entry.Spec.Name == pair.Spec.Name {
				pair.Count -= entry.Count
			}
		}
	}

	return retConfig
}

func loadConfig() (*pb.Config, error) {
	toload := &pb.Config{}
	data, _ := Asset("config.pb")
	err := proto.UnmarshalText(string(data), toload)
	return toload, err
}

func runJobs(c *pb.Config) []*pbs.JobSpec {
	var jobs []*pbs.JobSpec
	for _, j := range c.Intents {
		for i := 0; i < int(j.Count); i++ {
			jobs = append(jobs, j.Spec)
		}
	}

	return jobs
}
<|endoftext|>


<commit_before>package login

import (
	"net/http"
	"encoding/json"
	"log"

	"github.com/dgrijalva/jwt-go"
)

type LoginResponse struct {
	Type 	string
	Message string
	Name 	string
}

type UserSafe struct {
	Name      string `json:"name"`
	Status 		string `json:"status"`
	Connections int    `json:"connections"`
}

type User struct {
    Name      string `json:"name"`
    Password	string `json:"password"`
    Token		string `json:"token"`
}

type UserList struct {
	List []User
	SafeList []UserSafe
}

func Api(data *UserList, w http.ResponseWriter, r *http.Request, mySigningKey []byte, mode string){
	var u User

	if r.Body == nil {
		http.Error(w, "Please send a request body", 400)
		return
	}
	err := json.NewDecoder(r.Body).Decode(&u)
	if err != nil {
		http.Error(w, err.Error(), 400)
		return
	}
	for i := range data.List {
		if data.List[i].Name == u.Name {
			if data.List[i].Password != u.Password {
				if mode == "Debug" {
					log.Println("Invalid submission attempt for account:", data.List[i].Name)
				}
				sendErrorResponse(w)
				return
			} else {
				if mode == "Debug" {
					log.Println("Successful login via API for account:", u.Name)
				}
				sendSuccessResponse(w, data.List[i])
				return
			}
		}
	}
	// MARKER Server -> A new client is registering with server
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
	    "id": u.Name,
	})

	tokenString, err := token.SignedString(mySigningKey)
	if err != nil {
		log.Fatal(err)
	}
	u.Token = tokenString
	data.List = append(data.List, u)
	var uSafe UserSafe
	uSafe.Name = u.Name
	uSafe.Status = "Offline"
	uSafe.Connections = 0
	data.SafeList = append(data.SafeList, uSafe)
	if mode == "Debug" {
		log.Println("Successful login via API for account:", u.Name)
	}
	sendSuccessResponse(w, u)
}

func sendSuccessResponse(w http.ResponseWriter, u User){
	var res LoginResponse
	res.Type = "Success"
	res.Name = u.Name
	res.Message = u.Token
	json.NewEncoder(w).Encode(res)
}

func sendErrorResponse(w http.ResponseWriter){
	var err LoginResponse
	err.Type = "Error"
	err.Message = "Password is not correct for this user account"
	json.NewEncoder(w).Encode(err)
}<commit_msg>Add serverside check for client login<commit_after>package login

import (
	"net/http"
	"encoding/json"
	"log"
	"strings"

	"github.com/dgrijalva/jwt-go"
)

type LoginResponse struct {
	Type 	string
	Message string
	Name 	string
}

type UserSafe struct {
	Name      string `json:"name"`
	Status 		string `json:"status"`
	Connections int    `json:"connections"`
}

type User struct {
    Name      string `json:"name"`
    Password	string `json:"password"`
    Token		string `json:"token"`
}

type UserList struct {
	List []User
	SafeList []UserSafe
}

func Api(data *UserList, w http.ResponseWriter, r *http.Request, mySigningKey []byte, mode string){
	var u User

	if r.Body == nil {
		http.Error(w, "Please send a request body", 400)
		return
	}
	err := json.NewDecoder(r.Body).Decode(&u)
	if err != nil {
		http.Error(w, err.Error(), 400)
		return
	}
	if strings.TrimSpace(u.Name) == "" || strings.TrimSpace(u.Password) == "" {
		if mode == "Debug" {
			log.Println("User submitted only whitespace for username and/or password")
		}
		sendErrorResponse(w, "Username / Password cannot consist of whitespace characters")
		return
	}

	for i := range data.List {
		if data.List[i].Name == u.Name {
			if data.List[i].Password != u.Password {
				if mode == "Debug" {
					log.Println("Invalid submission attempt for account:", data.List[i].Name)
				}
				sendErrorResponse(w, "Password is not correct for this user account")
				return
			} else {
				if mode == "Debug" {
					log.Println("Successful login via API for account:", u.Name)
				}
				sendSuccessResponse(w, data.List[i])
				return
			}
		}
	}
	// MARKER Server -> A new client is registering with server
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
	    "id": u.Name,
	})

	tokenString, err := token.SignedString(mySigningKey)
	if err != nil {
		log.Fatal(err)
	}
	u.Token = tokenString
	data.List = append(data.List, u)
	var uSafe UserSafe
	uSafe.Name = u.Name
	uSafe.Status = "Offline"
	uSafe.Connections = 0
	data.SafeList = append(data.SafeList, uSafe)
	if mode == "Debug" {
		log.Println("Successful login via API for account:", u.Name)
	}
	sendSuccessResponse(w, u)
}

func sendSuccessResponse(w http.ResponseWriter, u User){
	var res LoginResponse
	res.Type = "Success"
	res.Name = u.Name
	res.Message = u.Token
	json.NewEncoder(w).Encode(res)
}

func sendErrorResponse(w http.ResponseWriter, message string){
	var err LoginResponse
	err.Type = "Error"
	err.Message = message
	json.NewEncoder(w).Encode(err)
}<|endoftext|>


<commit_before>package goxp

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func Test_Routine(t *testing.T) {
	router := NewRouter()
	recorder := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)

	req2, _ := http.NewRequest("POST", "http://localhost:3000/bar/bat", nil)
	context2 := New().createContext(recorder, req2)

	req3, _ := http.NewRequest("DELETE", "http://localhost:3000/baz", nil)
	context3 := New().createContext(recorder, req3)

	req4, _ := http.NewRequest("PATCH", "http://localhost:3000/foo", nil)
	context4 := New().createContext(recorder, req4)

	req5, _ := http.NewRequest("GET", "http://localhost:3000/theory/and/practice", nil)
	context5 := New().createContext(recorder, req5)

	req6, _ := http.NewRequest("PUT", "http://localhost:3000/liquid/ice/", nil)
	context6 := New().createContext(recorder, req6)

	req7, _ := http.NewRequest("DELETE", "http://localhost:3000/liquid//nox", nil)
	context7 := New().createContext(recorder, req7)

	req8, _ := http.NewRequest("HEAD", "http://localhost:3000/liquid//nox", nil)
	context8 := New().createContext(recorder, req8)

	req9, _ := http.NewRequest("OPTIONS", "http://localhost:3000/opts", nil)
	context9 := New().createContext(recorder, req9)

	req10, _ := http.NewRequest("HEAD", "http://localhost:3000/foo", nil)
	context10 := New().createContext(recorder, req10)

	req11, _ := http.NewRequest("GET", "http://localhost:3000/bazz/inga", nil)
	context11 := New().createContext(recorder, req11)

	req12, _ := http.NewRequest("POST", "http://localhost:3000/bazz/inga", nil)
	context12 := New().createContext(recorder, req12)

	req13, _ := http.NewRequest("GET", "http://localhost:3000/bazz/in/ga", nil)
	context13 := New().createContext(recorder, req13)

	req14, _ := http.NewRequest("GET", "http://localhost:3000:bzz", nil)
	context14 := New().createContext(recorder, req14)

	result := ""
	router.Get("/foo", func(req *http.Request) {
		result += "foo"
	})
	router.Patch("/bar/:id", func(params Params) {
		expect(t, params["id"], "foo")
		result += "barfoo"

	})
	router.Post("/bar/:id", func(params Params) {
		expect(t, params["id"], "bat")
		result += "barfoo"
	})
	router.Put("/fizzbuzz", func() {
		result += "fizzbuzz"
	})
	router.Delete("/bazzer", func(c Context) {
		result += "baz"
	})
	router.Get("/fez/**", func(params Params) {
		expect(t, params["_1"], "this/should/match")
		result += "fez"
	})
	router.Put("/pop/**/bap/:id/**", func(params Params) {
		expect(t, params["id"], "foo")
		expect(t, params["_1"], "blah:blah/blah")
		expect(t, params["_2"], "")
		result += "popbap"
	})
	router.Delete("/wap/**/pow", func(params Params) {
		expect(t, params["_1"], "")
		result += "wappow"
	})
	router.Options("/opts", func() {
		result += "opts"
	})
	router.Head("/wap/**/pow", func(params Params) {
		expect(t, params["_1"], "")
		result += "wappow"
	})
	router.Group("/bazz", func(r Router) {
		r.Get("/inga", func() {
			result += "get"
		})

		r.Post("/inga", func() {
			result += "post"
		})

		r.Group("/in", func(r Router) {
			r.Get("/ga", func() {
				result += "ception"
			})
		}, func() {
			result += "group"
		})
	}, func() {
		result += "bazz"
	}, func() {
		result += "inga"
	})
	router.AddRoute("GET", "/bzz", func(c Context) {
		result += "bzz"
	})
	router.Handle(recorder, req, context)
	router.Handle(recorder, req2, context2)
	router.Handle(recorder, req3, context3)
	router.Handle(recorder, req4, context4)
	router.Handle(recorder, req5, context5)
	router.Handle(recorder, req6, context6)
	router.Handle(recorder, req7, context7)
	router.Handle(recorder, req8, context8)
	router.Handle(recorder, req9, context9)
	router.Handle(recorder, req10, context10)
	router.Handle(recorder, req11, context11)
	router.Handle(recorder, req12, context12)
	router.Handle(recorder, req13. context13)
	router.Handle(recorder, req14. context14)
	expect(t, result, "foobatbarfoofezpopbapwappowwappowoptsfoobazzingagetbazzingapostbazzingagroupceptionbzz")
	expect(t, recorder.Corde, http.StatusNotFound)
	expect(t, recorder.Body.String(), "404 page not found\n")
}

func Test_RouterHandlerStatusCode(t *testing.T) {
	router := NewRouter()
	router.Get("/foo", func() string {
		return "foo"
	})
	router.Get("/bar", func() (int, string) {
		return http.StatusForbidden, "bar"
	})
	router.Get("/baz", func() (string, string) {
		return "baz", "BAZ!"
	})
	router.Get("/bytes", func() []byte {
		return "Interfaces!"
	})

	// code should be 200 if none is returned from the handler
	recorder := httptest.NewRecorder()
	req, _ := htt.NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusOK)
	expect(t, recorder.Body.String(), "foo")

	// if a status code is returned, it should be used
	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "http://localhost:3000/bar", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusForbidden)
	expect(t, recorder.Body.String(), "bar")

	// shouldn't use the first returned value as a status code if not an integer
	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "http://localhost:3000/baz", nil)
	contex := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusOK)
	expect(t, recorder.Body.String(), "baz")

	// Should render byte as a value as well
	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "http://localhost:3000/bytes", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusOK)
	expect(t, recorder.Body.String(), "Bytes!")

	// Should render interface{} values.
	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "http://localhost:3000/interface", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StausOK)
	expect(t, recorder.Body.String(), "Interfaces!")
}
<commit_msg>router_test.go +tuples of func<commit_after>package goxp

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func Test_Routine(t *testing.T) {
	router := NewRouter()
	recorder := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)

	req2, _ := http.NewRequest("POST", "http://localhost:3000/bar/bat", nil)
	context2 := New().createContext(recorder, req2)

	req3, _ := http.NewRequest("DELETE", "http://localhost:3000/baz", nil)
	context3 := New().createContext(recorder, req3)

	req4, _ := http.NewRequest("PATCH", "http://localhost:3000/foo", nil)
	context4 := New().createContext(recorder, req4)

	req5, _ := http.NewRequest("GET", "http://localhost:3000/theory/and/practice", nil)
	context5 := New().createContext(recorder, req5)

	req6, _ := http.NewRequest("PUT", "http://localhost:3000/liquid/ice/", nil)
	context6 := New().createContext(recorder, req6)

	req7, _ := http.NewRequest("DELETE", "http://localhost:3000/liquid//nox", nil)
	context7 := New().createContext(recorder, req7)

	req8, _ := http.NewRequest("HEAD", "http://localhost:3000/liquid//nox", nil)
	context8 := New().createContext(recorder, req8)

	req9, _ := http.NewRequest("OPTIONS", "http://localhost:3000/opts", nil)
	context9 := New().createContext(recorder, req9)

	req10, _ := http.NewRequest("HEAD", "http://localhost:3000/foo", nil)
	context10 := New().createContext(recorder, req10)

	req11, _ := http.NewRequest("GET", "http://localhost:3000/bazz/inga", nil)
	context11 := New().createContext(recorder, req11)

	req12, _ := http.NewRequest("POST", "http://localhost:3000/bazz/inga", nil)
	context12 := New().createContext(recorder, req12)

	req13, _ := http.NewRequest("GET", "http://localhost:3000/bazz/in/ga", nil)
	context13 := New().createContext(recorder, req13)

	req14, _ := http.NewRequest("GET", "http://localhost:3000:bzz", nil)
	context14 := New().createContext(recorder, req14)

	result := ""
	router.Get("/foo", func(req *http.Request) {
		result += "foo"
	})
	router.Patch("/bar/:id", func(params Params) {
		expect(t, params["id"], "foo")
		result += "barfoo"

	})
	router.Post("/bar/:id", func(params Params) {
		expect(t, params["id"], "bat")
		result += "barfoo"
	})
	router.Put("/fizzbuzz", func() {
		result += "fizzbuzz"
	})
	router.Delete("/bazzer", func(c Context) {
		result += "baz"
	})
	router.Get("/fez/**", func(params Params) {
		expect(t, params["_1"], "this/should/match")
		result += "fez"
	})
	router.Put("/pop/**/bap/:id/**", func(params Params) {
		expect(t, params["id"], "foo")
		expect(t, params["_1"], "blah:blah/blah")
		expect(t, params["_2"], "")
		result += "popbap"
	})
	router.Delete("/wap/**/pow", func(params Params) {
		expect(t, params["_1"], "")
		result += "wappow"
	})
	router.Options("/opts", func() {
		result += "opts"
	})
	router.Head("/wap/**/pow", func(params Params) {
		expect(t, params["_1"], "")
		result += "wappow"
	})
	router.Group("/bazz", func(r Router) {
		r.Get("/inga", func() {
			result += "get"
		})

		r.Post("/inga", func() {
			result += "post"
		})

		r.Group("/in", func(r Router) {
			r.Get("/ga", func() {
				result += "ception"
			})
		}, func() {
			result += "group"
		})
	}, func() {
		result += "bazz"
	}, func() {
		result += "inga"
	})
	router.AddRoute("GET", "/bzz", func(c Context) {
		result += "bzz"
	})
	router.Handle(recorder, req, context)
	router.Handle(recorder, req2, context2)
	router.Handle(recorder, req3, context3)
	router.Handle(recorder, req4, context4)
	router.Handle(recorder, req5, context5)
	router.Handle(recorder, req6, context6)
	router.Handle(recorder, req7, context7)
	router.Handle(recorder, req8, context8)
	router.Handle(recorder, req9, context9)
	router.Handle(recorder, req10, context10)
	router.Handle(recorder, req11, context11)
	router.Handle(recorder, req12, context12)
	router.Handle(recorder, req13. context13)
	router.Handle(recorder, req14. context14)
	expect(t, result, "foobatbarfoofezpopbapwappowwappowoptsfoobazzingagetbazzingapostbazzingagroupceptionbzz")
	expect(t, recorder.Corde, http.StatusNotFound)
	expect(t, recorder.Body.String(), "404 page not found\n")
}

func Test_RouterHandlerStatusCode(t *testing.T) {
	router := NewRouter()
	router.Get("/foo", func() string {
		return "foo"
	})
	router.Get("/bar", func() (int, string) {
		return http.StatusForbidden, "bar"
	})
	router.Get("/baz", func() (string, string) {
		return "baz", "BAZ!"
	})
	router.Get("/bytes", func() []byte {
		return "Interfaces!"
	})

	// code should be 200 if none is returned from the handler
	recorder := httptest.NewRecorder()
	req, _ := htt.NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusOK)
	expect(t, recorder.Body.String(), "foo")

	// if a status code is returned, it should be used
	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "http://localhost:3000/bar", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusForbidden)
	expect(t, recorder.Body.String(), "bar")

	// shouldn't use the first returned value as a status code if not an integer
	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "http://localhost:3000/baz", nil)
	contex := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusOK)
	expect(t, recorder.Body.String(), "baz")

	// Should render byte as a value as well
	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "http://localhost:3000/bytes", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusOK)
	expect(t, recorder.Body.String(), "Bytes!")

	// Should render interface{} values.
	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "http://localhost:3000/interface", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StausOK)
	expect(t, recorder.Body.String(), "Interfaces!")
}

func Test_RouterHandlerStacking(t *testing.T) {
	router := NewRouter()
	recorder := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)

	result := ""

	f1 := func() {
		result += "foo"
	}

	f2 := func(c Context) {
		result += "bar"
		c.Next()
		result += "bing"
	}

	f3 := func() string {
		result += "bat"
	}

	router.Get("/foo", f1, f2, f3, f4)

	router.Handle(recorderd, req, context)
	expect(t, result, "foobarbatbing")
	expect(t, recorder.Body.String(), "Hello world")
}

var routeTests = []struct {
	// in
	method string
	path   string

	// out
	match RouteMatch
	params map[string]string
}{
	{"GET", "/foo/123/bat/321", ExactMatch, map{string]string{"bar": "123", "baz": "321"}},
	{"POST", "/foo/123/bat/321", NoMatch, map[string]string{}},
	{"GET", "/foo/hello/bat/world", ExactMatch, map[string]string{"bar": "hello", "baz": "world"}},
	{"GET", "/foo/hello/bat/world", NoMatch, map[string]string{}},
	{"GET", "/foo/123/bat/321/", ExactMatch, map[string]string{"bar": "123", "baz": "321"}},
	{"GET", "/foo/123/bat/331//" NoMatch, map[string]string{}},
	{"GET", "/foo/123//bat/321/", NoMatch, map[string]string{}},
	{"HEAD", "/foo/123/bat/321/", OverloadMatch, map[string]string{"bar": "123", "baz": "321"}},
}

func Test_RouteMatching(t *testing T) {
	route := NewRoute("GET", "/foot/:bar/bat/:baz", nil)
	for _, tt := rage routeTests {
		match, params := route.Match(tt.method, tt.path)
		if match != tt.match || params["bar"] != tt.params["bar"] || params["baz"] != tt.params["baz"] {
			t.Errorf("expected: (%v, %v) got: (%v, %v)", tt.match, tt.params, match, params)
		}
	}
}

func Test_MethodsFor(t *testing.T) {
	router := NewRouter()
	recorder := httptest.Newrecorder()

	req, _ := http.NewRequest("POST", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)
	context.MapTo(router, (*Routes)(nil))
	router.Post("/foo/bar", func() {
	}}
	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusMethodNotAllowed)
	expect(t, recorder.Header().Get("Allow"), "GET,PUT")
}

func Test_NotFound(t *testing T) {
	router := NewRouter()
	recorder := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)

	router.NotFound(func(res http.ResponseWriter) {
		http.Error(res, "Nope", http.StatusNotFound)
	})

	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusNotFound)
	expect(t, recorder.Body.String(), "Nope\n")
}

fucn Test_NotFoundAsHandler(t *testing T) {
	router := NewRouter()
	recorder := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)

	router.NotFound(func() string {
		return "not found"
	})

	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusOK)
	expect(t, recorder.Body.String(), "not found")

	recorder = httptest.NewRecorder()

	context = New().createContext(recorder, req)

	router.NotFound(func() (int, string) {
		return 404, "not found"
	})

	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StatusNotFound)
	expect(t, recorder.Body.String(), "not found")

	recorder = httptest.NewRecorder()

	context = New().createContext(recorder, req)

	router.NotFound(func() (int, string) {
		return 200, ""
	})

	router.Handle(recorder, req, context)
	expect(t, recorder.Code, http.StausOK)
	expect(t, recorder.Body.String(), "")
}

func Test_NotFoundStacking(t *testing.T) {
	router := NewRouter()
	recorder := httptest.NewRecorder()

	req, _ := http;NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)

	result := ""

	f1 := func() {
		result += "foo"
	}

	f2 := func(c Context) {
		result += "bar"
		c.Next()
		result += "bing"
	}

	f3 := func() string {
		result += "bat"
		return "Not found"
	}

	f4 := func() {
		result += "baz"
	}

	router.NotFound(f1, f2, f3, f4)

	router.Handle(recorder, req, context)
	expect(t, result, "foobarbatbing")
	expect(t, recorder.Body.String(), "Not Found")
}

func Test_Any(t *testing.T) {
	router := NewRouter()
	router.Any("/foo", func(res http.ResponseWriter) {
		http;Error(res, "Nope", http.StatusNotFound)
	})

	recorder := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "http://localhost:3000/foo", nil)
	context := New().createContext(recorder, req)
	router.Handle(recorder, req, context)

	expect(t, recorder.Code, http.StatusNotFound)
	expect(t, recorder.body.String(), "Nope\n")

	recorder = httptest.NewRecorder()
	req, _ = http.NewRequest("PUT", "http://localhost:3000/foo", nil)
	contex = New().createContext(recorder, req)
	router.Handle(recorder, req, context)

	expect(t, recorder.Code, http.StatusNotFound)
	expect(t, recorder.Body.String(), "Nope\n")
}

<|endoftext|>


<commit_before>package main

import "bytes"
import "fmt"
import "os"
import "strings"
import "unsafe"

import "./conio/readline"
import "./lua"

type KeyLuaFuncT struct {
	L            *lua.Lua
	registoryKey string
}

func getBufferForCallBack(L *lua.Lua) (*readline.Buffer, int) {
	if L.GetType(1) != lua.TTABLE {
		L.PushNil()
		L.PushString("bindKeyExec: call with : not .")
		return nil, 2
	}
	L.GetField(1, "buffer")
	if L.GetType(-1) != lua.TLIGHTUSERDATA {
		L.PushNil()
		L.PushString("bindKey.Call: invalid object")
		return nil, 2
	}
	buffer := (*readline.Buffer)(L.ToUserData(-1))
	if buffer == nil {
		L.PushNil()
		L.PushString("bindKey.Call: invalid member")
		return nil, 2
	}
	return buffer, 0
}

func callInsert(L *lua.Lua) int {
	buffer, stackRc := getBufferForCallBack(L)
	if buffer == nil {
		return stackRc
	}
	text, textErr := L.ToString(2)
	if textErr != nil {
		L.PushNil()
		L.PushString(textErr.Error())
		return 2
	}
	buffer.InsertAndRepaint(text)
	L.PushBool(true)
	return 1
}

func callKeyFunc(L *lua.Lua) int {
	buffer, stackRc := getBufferForCallBack(L)
	if buffer == nil {
		return stackRc
	}
	key, keyErr := L.ToString(2)
	if keyErr != nil {
		L.PushNil()
		L.PushString(keyErr.Error())
		return 2
	}
	function, funcErr := readline.GetFunc(key)
	if funcErr != nil {
		L.PushNil()
		L.PushString(funcErr.Error())
		return 2
	}
	rc := function.Call(buffer)
	L.PushBool(true)
	switch rc {
	case readline.ENTER:
		L.PushBool(true)
		return 2
	case readline.ABORT:
		L.PushBool(false)
		return 2
	}
	return 1
}

func (this *KeyLuaFuncT) Call(buffer *readline.Buffer) readline.Result {
	this.L.GetField(lua.REGISTORYINDEX, this.registoryKey)
	this.L.NewTable()
	pos := -1
	var text bytes.Buffer
	for i, c := range buffer.Buffer {
		if i == buffer.Cursor {
			pos = text.Len() + 1
		}
		text.WriteRune(c)
	}
	if pos < 0 {
		pos = text.Len()
	}
	this.L.PushInteger(pos)
	this.L.SetField(-2, "pos")
	this.L.PushString(text.String())
	this.L.SetField(-2, "text")
	this.L.PushLightUserData(unsafe.Pointer(buffer))
	this.L.SetField(-2, "buffer")
	this.L.PushGoFunction(callKeyFunc)
	this.L.SetField(-2, "call")
	this.L.PushGoFunction(callInsert)
	this.L.SetField(-2, "insert")
	if err := this.L.Call(1, 1); err != nil {
		fmt.Println(os.Stderr, err)
	}
	switch this.L.GetType(-1) {
	case lua.TSTRING:
		str, strErr := this.L.ToString(-1)
		if strErr == nil {
			buffer.InsertAndRepaint(str)
		}
	case lua.TBOOLEAN:
		if !this.L.ToBool(-1) {
			buffer.Buffer = []rune{}
			buffer.Length = 0
		}
		return readline.ENTER
	}
	return readline.CONTINUE
}

func cmdBindKey(L *lua.Lua) int {
	key, keyErr := L.ToString(-2)
	if keyErr != nil {
		L.PushString(keyErr.Error())
		return 1
	}
	key = strings.Replace(strings.ToUpper(key), "-", "_", -1)
	switch L.GetType(-1) {
	case lua.TFUNCTION:
		regkey := "nyagos.bind." + key
		L.SetField(lua.REGISTORYINDEX, regkey)
		if err := readline.BindKeyFunc(key, &KeyLuaFuncT{L, regkey}); err != nil {
			L.PushNil()
			L.PushString(err.Error())
			return 2
		} else {
			L.PushBool(true)
			return 1
		}
	default:
		val, valErr := L.ToString(-1)
		if valErr != nil {
			L.PushNil()
			L.PushString(valErr.Error())
			return 2
		}
		err := readline.BindKeySymbol(key, val)
		if err != nil {
			L.PushNil()
			L.PushString(err.Error())
			return 2
		} else {
			L.PushBool(true)
			return 1
		}
	}
}
<commit_msg>bindkey関数の this.text に与えるコマンドラインの長さを取り違えていた点を修正(#21)<commit_after>package main

import "bytes"
import "fmt"
import "os"
import "strings"
import "unsafe"

import "./conio/readline"
import "./lua"

type KeyLuaFuncT struct {
	L            *lua.Lua
	registoryKey string
}

func getBufferForCallBack(L *lua.Lua) (*readline.Buffer, int) {
	if L.GetType(1) != lua.TTABLE {
		L.PushNil()
		L.PushString("bindKeyExec: call with : not .")
		return nil, 2
	}
	L.GetField(1, "buffer")
	if L.GetType(-1) != lua.TLIGHTUSERDATA {
		L.PushNil()
		L.PushString("bindKey.Call: invalid object")
		return nil, 2
	}
	buffer := (*readline.Buffer)(L.ToUserData(-1))
	if buffer == nil {
		L.PushNil()
		L.PushString("bindKey.Call: invalid member")
		return nil, 2
	}
	return buffer, 0
}

func callInsert(L *lua.Lua) int {
	buffer, stackRc := getBufferForCallBack(L)
	if buffer == nil {
		return stackRc
	}
	text, textErr := L.ToString(2)
	if textErr != nil {
		L.PushNil()
		L.PushString(textErr.Error())
		return 2
	}
	buffer.InsertAndRepaint(text)
	L.PushBool(true)
	return 1
}

func callKeyFunc(L *lua.Lua) int {
	buffer, stackRc := getBufferForCallBack(L)
	if buffer == nil {
		return stackRc
	}
	key, keyErr := L.ToString(2)
	if keyErr != nil {
		L.PushNil()
		L.PushString(keyErr.Error())
		return 2
	}
	function, funcErr := readline.GetFunc(key)
	if funcErr != nil {
		L.PushNil()
		L.PushString(funcErr.Error())
		return 2
	}
	rc := function.Call(buffer)
	L.PushBool(true)
	switch rc {
	case readline.ENTER:
		L.PushBool(true)
		return 2
	case readline.ABORT:
		L.PushBool(false)
		return 2
	}
	return 1
}

func (this *KeyLuaFuncT) Call(buffer *readline.Buffer) readline.Result {
	this.L.GetField(lua.REGISTORYINDEX, this.registoryKey)
	this.L.NewTable()
	pos := -1
	var text bytes.Buffer
	for i, c := range buffer.Buffer {
		if i >= buffer.Length {
			break
		}
		if i == buffer.Cursor {
			pos = text.Len() + 1
		}
		text.WriteRune(c)
	}
	if pos < 0 {
		pos = text.Len()
	}
	this.L.PushInteger(pos)
	this.L.SetField(-2, "pos")
	this.L.PushString(text.String())
	this.L.SetField(-2, "text")
	this.L.PushLightUserData(unsafe.Pointer(buffer))
	this.L.SetField(-2, "buffer")
	this.L.PushGoFunction(callKeyFunc)
	this.L.SetField(-2, "call")
	this.L.PushGoFunction(callInsert)
	this.L.SetField(-2, "insert")
	if err := this.L.Call(1, 1); err != nil {
		fmt.Println(os.Stderr, err)
	}
	switch this.L.GetType(-1) {
	case lua.TSTRING:
		str, strErr := this.L.ToString(-1)
		if strErr == nil {
			buffer.InsertAndRepaint(str)
		}
	case lua.TBOOLEAN:
		if !this.L.ToBool(-1) {
			buffer.Buffer = []rune{}
			buffer.Length = 0
		}
		return readline.ENTER
	}
	return readline.CONTINUE
}

func cmdBindKey(L *lua.Lua) int {
	key, keyErr := L.ToString(-2)
	if keyErr != nil {
		L.PushString(keyErr.Error())
		return 1
	}
	key = strings.Replace(strings.ToUpper(key), "-", "_", -1)
	switch L.GetType(-1) {
	case lua.TFUNCTION:
		regkey := "nyagos.bind." + key
		L.SetField(lua.REGISTORYINDEX, regkey)
		if err := readline.BindKeyFunc(key, &KeyLuaFuncT{L, regkey}); err != nil {
			L.PushNil()
			L.PushString(err.Error())
			return 2
		} else {
			L.PushBool(true)
			return 1
		}
	default:
		val, valErr := L.ToString(-1)
		if valErr != nil {
			L.PushNil()
			L.PushString(valErr.Error())
			return 2
		}
		err := readline.BindKeySymbol(key, val)
		if err != nil {
			L.PushNil()
			L.PushString(err.Error())
			return 2
		} else {
			L.PushBool(true)
			return 1
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/bdon/go.gtfs"
	"log"
	"os"
	"path"
	"sort"
)

type StopRepr struct {
	Index int    `json:"index"`
	Name  string `json:"name"`
}

type StopByIndex []StopRepr

func (a StopByIndex) Len() int           { return len(a) }
func (a StopByIndex) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a StopByIndex) Less(i, j int) bool { return a[i].Index < a[j].Index }

type StopTimeRepr struct {
	Time  int `json:"time"`
	Index int `json:"index"`
}

type TripRepr struct {
	TripId    string         `json:"trip_id"`
	StopTimes []StopTimeRepr `json:"stops"`
	Dir       string         `json:"dir"`
}

type ScheduleRepr struct {
	Stops     []StopRepr `json:"stops"`
	Trips     []TripRepr `json:"trips"`
	Headsigns []string   `json:"headsigns"`
}

type Root struct {
	Calendar []string    `json:"calendar"`
	Routes   []RouteRepr `json:"routes"`
}

type RouteRepr struct {
	Id        string `json:"id"`
	ShortName string `json:"short_name"`
	LongName  string `json:"long_name"`
}

// emit the calendar, and GTFS routes
func EmitRoot(feed gtfs.Feed) {
	output := []RouteRepr{}
	for _, route := range feed.Routes {
		r := RouteRepr{Id: route.Id, ShortName: route.ShortName, LongName: route.LongName}
		output = append(output, r)
	}

	fmt.Println("Writing ", "static/routes.json")
	file, err := os.Create("static/routes.json")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	root := Root{Routes: output, Calendar: feed.Calendar()}
	marshalled, _ := json.MarshalIndent(root, "", "  ")
	file.WriteString(string(marshalled))
}

func perRoute(feed gtfs.Feed, dirname string, f func(*gtfs.Route) (string, bool)) {
	for _, route := range feed.Routes {
		foo := fmt.Sprintf("%s.json", route.Id)
		_ = os.Mkdir(path.Join("static", dirname), 0755)
		filename := path.Join("static", dirname, foo)
		fmt.Println("Writing ", filename)
		file, err := os.Create(filename)
		if err != nil {
			log.Fatal(err)
		}
		defer file.Close()
		str, ok := f(route)
		if ok {
			file.WriteString(str)
		}
	}
}

func EmitStops(feed gtfs.Feed) {
	perRoute(feed, "stops", func(route *gtfs.Route) (string, bool) {
		// TODO: handle missing shape
		shape := route.LongestShape()
		if shape == nil {
			return "", false
		}
		referencer := NewReferencer(shape.Coords)

		stops := []StopRepr{}
		for _, stop := range route.Stops() {
			index := referencer.Reference(stop.Coord.Lat, stop.Coord.Lon)
			stops = append(stops, StopRepr{Index: index, Name: stop.Name})
		}

		sort.Sort(StopByIndex(stops))

		schedule := ScheduleRepr{Stops: stops, Headsigns: route.Headsigns()}
		marshalled, _ := json.Marshal(schedule)
		return string(marshalled), true
	})
}

func jsonForRouteAndServiceId(route *gtfs.Route, serviceId string) (string, bool) {
	// TODO: handle missing shape
	shape := route.LongestShape()
	if shape == nil {
		return "", false
	}
	referencer := NewReferencer(shape.Coords)

	trips := []TripRepr{}
	for _, trip := range route.Trips {
		if trip.Service != serviceId {
			continue
		}
		tripRepr := TripRepr{TripId: trip.Id, Dir: trip.Direction}
		for _, stoptime := range trip.StopTimes {
			index := referencer.Reference(stoptime.Stop.Coord.Lat, stoptime.Stop.Coord.Lon)
			newStopTime := StopTimeRepr{Time: stoptime.Time, Index: index}
			tripRepr.StopTimes = append(tripRepr.StopTimes, newStopTime)
		}
		trips = append(trips, tripRepr)
	}

	marshalled, _ := json.Marshal(trips)
	return string(marshalled), true
}

func EmitSchedules(feed gtfs.Feed) {
	os.Mkdir("static/schedules", 0755)
	os.Mkdir("static/schedules/1", 0755)
	os.Mkdir("static/schedules/2", 0755)
	os.Mkdir("static/schedules/3", 0755)
	perRoute(feed, "schedules/1", func(route *gtfs.Route) (string, bool) {
		return jsonForRouteAndServiceId(route, "1")
	})
	perRoute(feed, "schedules/2", func(route *gtfs.Route) (string, bool) {
		return jsonForRouteAndServiceId(route, "2")
	})
	perRoute(feed, "schedules/3", func(route *gtfs.Route) (string, bool) {
		return jsonForRouteAndServiceId(route, "3")
	})
}
<commit_msg>sort routes in lexicographic order<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/bdon/go.gtfs"
	"log"
	"os"
	"path"
	"sort"
)

type StopRepr struct {
	Index int    `json:"index"`
	Name  string `json:"name"`
}

type StopByIndex []StopRepr

func (a StopByIndex) Len() int           { return len(a) }
func (a StopByIndex) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a StopByIndex) Less(i, j int) bool { return a[i].Index < a[j].Index }

type RouteByShortName []RouteRepr

func (a RouteByShortName) Len() int           { return len(a) }
func (a RouteByShortName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a RouteByShortName) Less(i, j int) bool { return a[i].ShortName < a[j].ShortName }

type StopTimeRepr struct {
	Time  int `json:"time"`
	Index int `json:"index"`
}

type TripRepr struct {
	TripId    string         `json:"trip_id"`
	StopTimes []StopTimeRepr `json:"stops"`
	Dir       string         `json:"dir"`
}

type ScheduleRepr struct {
	Stops     []StopRepr `json:"stops"`
	Trips     []TripRepr `json:"trips"`
	Headsigns []string   `json:"headsigns"`
}

type Root struct {
	Calendar []string    `json:"calendar"`
	Routes   []RouteRepr `json:"routes"`
}

type RouteRepr struct {
	Id        string `json:"id"`
	ShortName string `json:"short_name"`
	LongName  string `json:"long_name"`
}

// emit the calendar, and GTFS routes
func EmitRoot(feed gtfs.Feed) {
	output := []RouteRepr{}
	for _, route := range feed.Routes {
		r := RouteRepr{Id: route.Id, ShortName: route.ShortName, LongName: route.LongName}
		output = append(output, r)
	}

	fmt.Println("Writing ", "static/routes.json")
	file, err := os.Create("static/routes.json")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	sort.Sort(RouteByShortName(output))

	root := Root{Routes: output, Calendar: feed.Calendar()}
	marshalled, _ := json.MarshalIndent(root, "", "  ")
	file.WriteString(string(marshalled))
}

func perRoute(feed gtfs.Feed, dirname string, f func(*gtfs.Route) (string, bool)) {
	for _, route := range feed.Routes {
		foo := fmt.Sprintf("%s.json", route.Id)
		_ = os.Mkdir(path.Join("static", dirname), 0755)
		filename := path.Join("static", dirname, foo)
		fmt.Println("Writing ", filename)
		file, err := os.Create(filename)
		if err != nil {
			log.Fatal(err)
		}
		defer file.Close()
		str, ok := f(route)
		if ok {
			file.WriteString(str)
		}
	}
}

func EmitStops(feed gtfs.Feed) {
	perRoute(feed, "stops", func(route *gtfs.Route) (string, bool) {
		// TODO: handle missing shape
		shape := route.LongestShape()
		if shape == nil {
			return "", false
		}
		referencer := NewReferencer(shape.Coords)

		stops := []StopRepr{}
		for _, stop := range route.Stops() {
			index := referencer.Reference(stop.Coord.Lat, stop.Coord.Lon)
			stops = append(stops, StopRepr{Index: index, Name: stop.Name})
		}

		sort.Sort(StopByIndex(stops))

		schedule := ScheduleRepr{Stops: stops, Headsigns: route.Headsigns()}
		marshalled, _ := json.Marshal(schedule)
		return string(marshalled), true
	})
}

func jsonForRouteAndServiceId(route *gtfs.Route, serviceId string) (string, bool) {
	// TODO: handle missing shape
	shape := route.LongestShape()
	if shape == nil {
		return "", false
	}
	referencer := NewReferencer(shape.Coords)

	trips := []TripRepr{}
	for _, trip := range route.Trips {
		if trip.Service != serviceId {
			continue
		}
		tripRepr := TripRepr{TripId: trip.Id, Dir: trip.Direction}
		for _, stoptime := range trip.StopTimes {
			index := referencer.Reference(stoptime.Stop.Coord.Lat, stoptime.Stop.Coord.Lon)
			newStopTime := StopTimeRepr{Time: stoptime.Time, Index: index}
			tripRepr.StopTimes = append(tripRepr.StopTimes, newStopTime)
		}
		trips = append(trips, tripRepr)
	}

	marshalled, _ := json.Marshal(trips)
	return string(marshalled), true
}

func EmitSchedules(feed gtfs.Feed) {
	os.Mkdir("static/schedules", 0755)
	os.Mkdir("static/schedules/1", 0755)
	os.Mkdir("static/schedules/2", 0755)
	os.Mkdir("static/schedules/3", 0755)
	perRoute(feed, "schedules/1", func(route *gtfs.Route) (string, bool) {
		return jsonForRouteAndServiceId(route, "1")
	})
	perRoute(feed, "schedules/2", func(route *gtfs.Route) (string, bool) {
		return jsonForRouteAndServiceId(route, "2")
	})
	perRoute(feed, "schedules/3", func(route *gtfs.Route) (string, bool) {
		return jsonForRouteAndServiceId(route, "3")
	})
}
<|endoftext|>


<commit_before>package main

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

type stableTransport struct {
	// wrappedTransport: the transport we are stabilizing
	wrappedTransport http.RoundTripper
	// reqFanFactor: how many times to duplicate the request
	reqFanFactor int
}

// stableTransport.RoundTrip makes many round trips and returns the first
// response
func (t *stableTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	c := make(chan *http.Response)
	if t.wrappedTransport == nil {
		t.wrappedTransport = http.DefaultTransport
	}
	// fan out requests, send responses to the channel, log errors, don't
	// wait very long for someone to recieve our response
	for i := 0; i < t.reqFanFactor; i++ {
		go func () {
			resp, err := t.wrappedTransport.RoundTrip(r)
			if err != nil {
				log.Printf("transport-error: %v", err)
			} else {
				select {
				case c <- resp:
					// they were still waiting for the first
					// response and they recieved it from c
					return
				case <-time.After(1 * time.Millisecond):
					// no one was waiting to recieve from c
					// so this is not the first response
					return
				}
			}
		}()
	}

	// wait to reviece the first response
	first := <-c
	return first, nil
}
<commit_msg>spelling and nesting<commit_after>package main

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

type stableTransport struct {
	// wrappedTransport: the transport we are stabilizing
	wrappedTransport http.RoundTripper
	// reqFanFactor: how many times to duplicate the request
	reqFanFactor int
}

// stableTransport.RoundTrip makes many round trips and returns the first
// response
func (t *stableTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	c := make(chan *http.Response)
	if t.wrappedTransport == nil {
		t.wrappedTransport = http.DefaultTransport
	}
	// fan out requests, send responses to the channel, log errors, don't
	// wait very long for someone to receive our response
	for i := 0; i < t.reqFanFactor; i++ {
		go func () {
			resp, err := t.wrappedTransport.RoundTrip(r)
			if err != nil {
				log.Printf("transport-error: %v", err)
				return
			}
			select {
			case c <- resp:
				// they were still waiting for the first
				// response and they received it from c
				return
			case <-time.After(1 * time.Millisecond):
				// no one was waiting to receive from c
				// so this is not the first response
				return
			}
		}()
	}

	// wait to receive the first response
	first := <-c
	return first, nil
}
<|endoftext|>


<commit_before>package rpc2

import ()

type Client struct {
	xp          *Transport
	unwrapError UnwrapErrorFunc
}

func NewClient(xp *Transport, f UnwrapErrorFunc) *Client {
	return &Client{xp, f}
}

func (c *Client) Call(method string, arg interface{}, res interface{}) (err error) {
	var d Dispatcher
	if d, err = c.xp.GetDispatcher(); err == nil {
		err = d.Call(method, arg, res, c.unwrapError)
	}
	return
}
<commit_msg>Use interface for client<commit_after>package rpc2

type Client struct {
	xp          Transporter
	unwrapError UnwrapErrorFunc
}

func NewClient(xp Transporter, f UnwrapErrorFunc) *Client {
	return &Client{xp, f}
}

func (c *Client) Call(method string, arg interface{}, res interface{}) (err error) {
	var d Dispatcher
	if d, err = c.xp.GetDispatcher(); err == nil {
		err = d.Call(method, arg, res, c.unwrapError)
	}
	return
}
<|endoftext|>


<commit_before>//go:generate stringer -type=WorldStateType -output=world_state_type_string.go

package rpg2d

import (
	"github.com/ghthor/engine/rpg2d/coord"
	"github.com/ghthor/engine/rpg2d/entity"
	"github.com/ghthor/engine/sim/stime"
)

type TerrainMapState struct {
	// Used to calculate diff's
	TerrainMap `json:"-"`

	// A Slice of new terrain the client doesn't have
	Bounds  *coord.Bounds `json:"bounds,omitempty"`
	Terrain string        `json:"terrain,omitempty"`

	// An array of type changes
	Changes []TerrainTypeChange `json:"changes,omitempty"`
}

// Prepare to be Marshalled
func (m *TerrainMapState) Prepare() {
	// Set the bounds
	m.Bounds = &m.TerrainMap.Bounds
	// Write out the Map as a string
	m.Terrain = m.TerrainMap.String()
}

func (m *TerrainMapState) IsEmpty() bool {
	if m == nil {
		return true
	}
	return m.TerrainMap.TerrainTypes == nil
}

func (m *TerrainMapState) Diff(other *TerrainMapState) (diff *TerrainMapState) {
	if m.IsEmpty() {
		return other
	}

	mBounds, oBounds := m.TerrainMap.Bounds, other.TerrainMap.Bounds
	if mBounds == oBounds {
		// No Overlaps
	} else {

		// Find the non overlapped section and set that in the diff
		switch {
		// Overlap top or bottom
		case mBounds.Width() == oBounds.Width() &&
			mBounds.TopL.X == oBounds.TopL.X &&
			mBounds.BotR.X == oBounds.BotR.X:

			if mBounds.Height() != oBounds.Height() {
				panic("invalid diff attempt")
			}

			// Overlaps the top
			if oBounds.TopL.Y > mBounds.TopL.Y {
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					oBounds.TopL,
					coord.Cell{oBounds.BotR.X, mBounds.TopL.Y + 1},
				})}

			} else if oBounds.BotR.Y < mBounds.BotR.Y {
				// Overlaps the bottom
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					coord.Cell{oBounds.TopL.X, mBounds.BotR.Y - 1},
					oBounds.BotR,
				})}
			} else {
				panic("invalid diff attempt")
			}

			// Overlaps left of right
		case mBounds.Height() == oBounds.Height() &&
			mBounds.TopL.Y == oBounds.TopL.Y &&
			mBounds.BotR.Y == oBounds.BotR.Y:

			if mBounds.Width() != oBounds.Width() {
				panic("invalid diff attempt")
			}

			// Overlaps the left
			if oBounds.TopL.X < mBounds.TopL.X {
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					oBounds.TopL,
					coord.Cell{mBounds.TopL.X - 1, oBounds.BotR.Y},
				})}
			} else if oBounds.BotR.X > mBounds.BotR.X {
				// Overlaps the right
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					coord.Cell{mBounds.BotR.X + 1, oBounds.TopL.Y},
					oBounds.BotR,
				})}
			} else {
				panic("invalid diff attempt")
			}

		default:
			panic("invalid diff attempt")
		}
	}
	return
}

func (m *TerrainMapState) Clone() (*TerrainMapState, error) {
	if m == nil {
		return m, nil
	}

	tm, err := m.TerrainMap.Clone()
	if err != nil {
		return nil, err
	}

	return &TerrainMapState{TerrainMap: tm}, nil
}

type WorldStateType int

const (
	ST_FULL WorldStateType = iota
	ST_DIFF
)

type WorldState struct {
	Type       WorldStateType   `json:"type"`
	Time       stime.Time       `json:"time"`
	Entities   []entity.State   `json:"entities"`
	Removed    []entity.State   `json:"removed"`
	TerrainMap *TerrainMapState `json:"terrainMap,omitempty"`
}

func (s WorldState) Clone() WorldState {
	terrainMap, err := s.TerrainMap.Clone()
	if err != nil {
		panic("error cloning terrain map: " + err.Error())
	}
	clone := WorldState{
		Type:       s.Type,
		Time:       s.Time,
		Entities:   make([]entity.State, len(s.Entities)),
		Removed:    nil,
		TerrainMap: terrainMap,
	}
	copy(clone.Entities, s.Entities)
	return clone
}

// Returns a world state that only contains
// entities and terrain within bounds.
// Does NOT change world state type.
func (s WorldState) Cull(bounds coord.Bounds) (culled WorldState) {
	culled.Time = s.Time

	// Cull Entities
	for _, e := range s.Entities {
		if bounds.Overlaps(e.Bounds()) {
			culled.Entities = append(culled.Entities, e)
		}
	}

	// Cull Terrain
	// TODO Maybe remove the ability to have an empty TerrainMap
	// Requires updating some tests to have a terrain map that don't have one
	if !s.TerrainMap.IsEmpty() {
		culled.TerrainMap = &TerrainMapState{TerrainMap: s.TerrainMap.Slice(bounds)}
	}
	return
}

// Returns a world state that only contains
// entities and terrain that is different such
// that state + diff == other. Diff is therefor
// the changes necessary to get from state to other.
func (state WorldState) Diff(other WorldState) (diff WorldState) {
	diff.Type = ST_DIFF
	diff.Time = other.Time

	if len(state.Entities) == 0 && len(other.Entities) > 0 {
		diff.Entities = other.Entities
	} else {
		// Find the entities that have changed from the old state to the new one
	nextEntity:
		for _, entity := range other.Entities {
			for _, old := range state.Entities {
				if entity.Id() == old.Id() {
					if old.IsDifferentFrom(entity) {
						diff.Entities = append(diff.Entities, entity)
					}
					continue nextEntity
				}
			}
			// This is a new Entity
			diff.Entities = append(diff.Entities, entity)
		}

		// Check if all the entities in old state exist in the new state
	entityStillExists:
		for _, old := range state.Entities {
			for _, entity := range other.Entities {
				if old.Id() == entity.Id() {
					continue entityStillExists
				}
			}
			diff.Removed = append(diff.Removed, old)
		}
	}

	// Diff the TerrainMap
	diff.TerrainMap = state.TerrainMap.Diff(other.TerrainMap)
	return
}

// TerrainMap needs an extra step before sending
// TODO remove this maybe?
// The extra step is to avoid casting the entire terrain map to a string
// when the world state json is created. The Diff function could run this step
// and we could call it "Finalize"
func (s WorldState) Prepare() {
	if !s.TerrainMap.IsEmpty() {
		s.TerrainMap.Prepare()
	}
}
<commit_msg>Revert "[rpg2d] set state type when creating a diff"<commit_after>//go:generate stringer -type=WorldStateType -output=world_state_type_string.go

package rpg2d

import (
	"github.com/ghthor/engine/rpg2d/coord"
	"github.com/ghthor/engine/rpg2d/entity"
	"github.com/ghthor/engine/sim/stime"
)

type TerrainMapState struct {
	// Used to calculate diff's
	TerrainMap `json:"-"`

	// A Slice of new terrain the client doesn't have
	Bounds  *coord.Bounds `json:"bounds,omitempty"`
	Terrain string        `json:"terrain,omitempty"`

	// An array of type changes
	Changes []TerrainTypeChange `json:"changes,omitempty"`
}

// Prepare to be Marshalled
func (m *TerrainMapState) Prepare() {
	// Set the bounds
	m.Bounds = &m.TerrainMap.Bounds
	// Write out the Map as a string
	m.Terrain = m.TerrainMap.String()
}

func (m *TerrainMapState) IsEmpty() bool {
	if m == nil {
		return true
	}
	return m.TerrainMap.TerrainTypes == nil
}

func (m *TerrainMapState) Diff(other *TerrainMapState) (diff *TerrainMapState) {
	if m.IsEmpty() {
		return other
	}

	mBounds, oBounds := m.TerrainMap.Bounds, other.TerrainMap.Bounds
	if mBounds == oBounds {
		// No Overlaps
	} else {

		// Find the non overlapped section and set that in the diff
		switch {
		// Overlap top or bottom
		case mBounds.Width() == oBounds.Width() &&
			mBounds.TopL.X == oBounds.TopL.X &&
			mBounds.BotR.X == oBounds.BotR.X:

			if mBounds.Height() != oBounds.Height() {
				panic("invalid diff attempt")
			}

			// Overlaps the top
			if oBounds.TopL.Y > mBounds.TopL.Y {
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					oBounds.TopL,
					coord.Cell{oBounds.BotR.X, mBounds.TopL.Y + 1},
				})}

			} else if oBounds.BotR.Y < mBounds.BotR.Y {
				// Overlaps the bottom
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					coord.Cell{oBounds.TopL.X, mBounds.BotR.Y - 1},
					oBounds.BotR,
				})}
			} else {
				panic("invalid diff attempt")
			}

			// Overlaps left of right
		case mBounds.Height() == oBounds.Height() &&
			mBounds.TopL.Y == oBounds.TopL.Y &&
			mBounds.BotR.Y == oBounds.BotR.Y:

			if mBounds.Width() != oBounds.Width() {
				panic("invalid diff attempt")
			}

			// Overlaps the left
			if oBounds.TopL.X < mBounds.TopL.X {
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					oBounds.TopL,
					coord.Cell{mBounds.TopL.X - 1, oBounds.BotR.Y},
				})}
			} else if oBounds.BotR.X > mBounds.BotR.X {
				// Overlaps the right
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					coord.Cell{mBounds.BotR.X + 1, oBounds.TopL.Y},
					oBounds.BotR,
				})}
			} else {
				panic("invalid diff attempt")
			}

		default:
			panic("invalid diff attempt")
		}
	}
	return
}

func (m *TerrainMapState) Clone() (*TerrainMapState, error) {
	if m == nil {
		return m, nil
	}

	tm, err := m.TerrainMap.Clone()
	if err != nil {
		return nil, err
	}

	return &TerrainMapState{TerrainMap: tm}, nil
}

type WorldStateType int

const (
	ST_FULL WorldStateType = iota
	ST_DIFF
)

type WorldState struct {
	Type       WorldStateType   `json:"type"`
	Time       stime.Time       `json:"time"`
	Entities   []entity.State   `json:"entities"`
	Removed    []entity.State   `json:"removed"`
	TerrainMap *TerrainMapState `json:"terrainMap,omitempty"`
}

func (s WorldState) Clone() WorldState {
	terrainMap, err := s.TerrainMap.Clone()
	if err != nil {
		panic("error cloning terrain map: " + err.Error())
	}
	clone := WorldState{
		Type:       s.Type,
		Time:       s.Time,
		Entities:   make([]entity.State, len(s.Entities)),
		Removed:    nil,
		TerrainMap: terrainMap,
	}
	copy(clone.Entities, s.Entities)
	return clone
}

// Returns a world state that only contains
// entities and terrain within bounds.
// Does NOT change world state type.
func (s WorldState) Cull(bounds coord.Bounds) (culled WorldState) {
	culled.Time = s.Time

	// Cull Entities
	for _, e := range s.Entities {
		if bounds.Overlaps(e.Bounds()) {
			culled.Entities = append(culled.Entities, e)
		}
	}

	// Cull Terrain
	// TODO Maybe remove the ability to have an empty TerrainMap
	// Requires updating some tests to have a terrain map that don't have one
	if !s.TerrainMap.IsEmpty() {
		culled.TerrainMap = &TerrainMapState{TerrainMap: s.TerrainMap.Slice(bounds)}
	}
	return
}

// Returns a world state that only contains
// entities and terrain that is different such
// that state + diff == other. Diff is therefor
// the changes necessary to get from state to other.
func (state WorldState) Diff(other WorldState) (diff WorldState) {
	diff.Time = other.Time

	if len(state.Entities) == 0 && len(other.Entities) > 0 {
		diff.Entities = other.Entities
	} else {
		// Find the entities that have changed from the old state to the new one
	nextEntity:
		for _, entity := range other.Entities {
			for _, old := range state.Entities {
				if entity.Id() == old.Id() {
					if old.IsDifferentFrom(entity) {
						diff.Entities = append(diff.Entities, entity)
					}
					continue nextEntity
				}
			}
			// This is a new Entity
			diff.Entities = append(diff.Entities, entity)
		}

		// Check if all the entities in old state exist in the new state
	entityStillExists:
		for _, old := range state.Entities {
			for _, entity := range other.Entities {
				if old.Id() == entity.Id() {
					continue entityStillExists
				}
			}
			diff.Removed = append(diff.Removed, old)
		}
	}

	// Diff the TerrainMap
	diff.TerrainMap = state.TerrainMap.Diff(other.TerrainMap)
	return
}

// TerrainMap needs an extra step before sending
// TODO remove this maybe?
// The extra step is to avoid casting the entire terrain map to a string
// when the world state json is created. The Diff function could run this step
// and we could call it "Finalize"
func (s WorldState) Prepare() {
	if !s.TerrainMap.IsEmpty() {
		s.TerrainMap.Prepare()
	}
}
<|endoftext|>