<commit_before>/*
Copyright 2014 Hajime Hoshi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ebiten

import (
	"github.com/go-gl/gl"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"image/color"
)

func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
	r, t, err := opengl.NewZeroRenderTarget(screenWidth*screenScale, screenHeight*screenScale, true)
	if err != nil {
		return nil, err
	}

	screen, err := idsInstance.createRenderTarget(screenWidth, screenHeight, gl.NEAREST)
	if err != nil {
		return nil, err
	}

	c := &graphicsContext{
		currents:     make([]*RenderTarget, 1),
		defaultR:     &RenderTarget{r, &Texture{t}},
		screen:       screen,
		screenWidth:  screenWidth,
		screenHeight: screenHeight,
		screenScale:  screenScale,
	}

	idsInstance.fillRenderTarget(c.screen, color.RGBA{0, 0, 0, 0})

	return c, nil
}

type graphicsContext struct {
	screen       *RenderTarget
	defaultR     *RenderTarget
	currents     []*RenderTarget
	screenWidth  int
	screenHeight int
	screenScale  int
}

var _ GraphicsContext = new(graphicsContext)

func (c *graphicsContext) dispose() {
	// NOTE: Now this method is not used anywhere.
	glRenderTarget := c.screen.glRenderTarget
	texture := c.screen.texture
	glTexture := texture.glTexture

	glRenderTarget.Dispose()
	glTexture.Dispose()
}

func (c *graphicsContext) Clear() error {
	return c.Fill(color.RGBA{0, 0, 0, 0})
}

func (c *graphicsContext) Fill(clr color.Color) error {
	return idsInstance.fillRenderTarget(c.currents[len(c.currents)-1], clr)
}

func (c *graphicsContext) DrawTexture(texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) error {
	current := c.currents[len(c.currents)-1]
	return idsInstance.drawTexture(current, texture, parts, geo, color)
}

func (c *graphicsContext) PushRenderTarget(renderTarget *RenderTarget) {
	c.currents = append(c.currents, renderTarget)
}

func (c *graphicsContext) PopRenderTarget() {
	c.currents = c.currents[:len(c.currents)-1]
}

func (c *graphicsContext) preUpdate() {
	c.currents = c.currents[0:1]
	c.currents[0] = c.defaultR
	c.PushRenderTarget(c.screen)
	c.Clear()
}

func (c *graphicsContext) postUpdate() {
	c.PopRenderTarget()
	c.Clear()

	scale := float64(c.screenScale)
	geo := GeometryMatrixI()
	geo.Concat(ScaleGeometry(scale, scale))
	DrawWholeTexture(c, c.screen.texture, geo, ColorMatrixI())

	gl.Flush()
}
<commit_msg>Bug fix: Screen should be clear with opaque black<commit_after>/*
Copyright 2014 Hajime Hoshi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ebiten

import (
	"github.com/go-gl/gl"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"image/color"
)

func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
	r, t, err := opengl.NewZeroRenderTarget(screenWidth*screenScale, screenHeight*screenScale, true)
	if err != nil {
		return nil, err
	}

	screen, err := idsInstance.createRenderTarget(screenWidth, screenHeight, gl.NEAREST)
	if err != nil {
		return nil, err
	}

	c := &graphicsContext{
		currents:     make([]*RenderTarget, 1),
		defaultR:     &RenderTarget{r, &Texture{t}},
		screen:       screen,
		screenWidth:  screenWidth,
		screenHeight: screenHeight,
		screenScale:  screenScale,
	}

	idsInstance.fillRenderTarget(c.screen, color.RGBA{0, 0, 0, 0})

	return c, nil
}

type graphicsContext struct {
	screen       *RenderTarget
	defaultR     *RenderTarget
	currents     []*RenderTarget
	screenWidth  int
	screenHeight int
	screenScale  int
}

var _ GraphicsContext = new(graphicsContext)

func (c *graphicsContext) dispose() {
	// NOTE: Now this method is not used anywhere.
	glRenderTarget := c.screen.glRenderTarget
	texture := c.screen.texture
	glTexture := texture.glTexture

	glRenderTarget.Dispose()
	glTexture.Dispose()
}

func (c *graphicsContext) Clear() error {
	return c.Fill(color.RGBA{0, 0, 0, 0})
}

func (c *graphicsContext) Fill(clr color.Color) error {
	return idsInstance.fillRenderTarget(c.currents[len(c.currents)-1], clr)
}

func (c *graphicsContext) DrawTexture(texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) error {
	current := c.currents[len(c.currents)-1]
	return idsInstance.drawTexture(current, texture, parts, geo, color)
}

func (c *graphicsContext) PushRenderTarget(renderTarget *RenderTarget) {
	c.currents = append(c.currents, renderTarget)
}

func (c *graphicsContext) PopRenderTarget() {
	c.currents = c.currents[:len(c.currents)-1]
}

func (c *graphicsContext) preUpdate() {
	c.currents = c.currents[0:1]
	c.currents[0] = c.defaultR
	c.PushRenderTarget(c.screen)
	c.Fill(color.RGBA{0, 0, 0, 0xff})
}

func (c *graphicsContext) postUpdate() {
	c.PopRenderTarget()
	c.Clear()

	scale := float64(c.screenScale)
	geo := ScaleGeometry(scale, scale)
	DrawWholeTexture(c, c.screen.texture, geo, ColorMatrixI())

	gl.Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"

	"github.com/eikeon/marvin"
	"github.com/eikeon/marvin/web"
)

var StaticRoot *string

func main() {
	config := flag.String("config", "/etc/marvin.json", "file path to configuration file")
	Address := flag.String("address", ":9999", "http service address")
	StaticRoot = flag.String("root", "static", "...")
	flag.Parse()

	log.Println("starting marvin")

	if marvin, err := marvin.NewMarvinFromFile(*config); err == nil {
		web.AddHandlers(marvin)
		go func() {
			err := http.ListenAndServe(*Address, nil)
			if err != nil {
				log.Print("ListenAndServe:", err)
			}
		}()
		marvin.Run()
	} else {
		log.Println("ERROR:", err)
	}

	log.Println("stopping Marvin")
}
<commit_msg>Added cert and key flag for running secure server.<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"log"
	"net/http"

	"github.com/eikeon/marvin"
	"github.com/eikeon/marvin/web"
)

var StaticRoot *string

func main() {
	config := flag.String("config", "/etc/marvin.json", "file path to configuration file")
	address := flag.String("address", ":9999", "http service address")
	cert := flag.String("cert", "", "certificate file")
	key := flag.String("key", "", "key file")
	StaticRoot = flag.String("root", "static", "...")
	flag.Parse()

	log.Println("starting marvin")

	if marvin, err := marvin.NewMarvinFromFile(*config); err == nil {
		web.AddHandlers(marvin)
		if *cert != "" || *key != "" {
			go func() {
				config := &tls.Config{ClientAuth: tls.RequestClientCert}
				server := &http.Server{Addr: *address, TLSConfig: config}
				err = server.ListenAndServeTLS(*cert, *key)
				if err != nil {
					log.Print("ListenAndServe:", err)
				}
			}()
		} else {
			go func() {
				err := http.ListenAndServe(*address, nil)
				if err != nil {
					log.Print("ListenAndServe:", err)
				}
			}()
		}
		marvin.Run()
	} else {
		log.Println("ERROR:", err)
	}

	log.Println("stopping Marvin")
}
<|endoftext|>


<commit_before>package actions

import (
	"net/http"
	"strconv"

	"github.com/stellar/go-stellar-base/strkey"
	"github.com/stellar/go-stellar-base/xdr"
	"github.com/stellar/horizon/assets"
	"github.com/stellar/horizon/db"
	"github.com/stellar/horizon/render/problem"
)

const (
	// ParamCursor is a query string param name
	ParamCursor = "cursor"
	// ParamOrder is a query string param name
	ParamOrder = "order"
	// ParamLimit is a query string param name
	ParamLimit = "limit"
)

// OrderBookParams is a helper struct that encapsulates the specification for
// an order book
type OrderBookParams struct {
	SellingType   xdr.AssetType
	SellingIssuer string
	SellingCode   string
	BuyingType    xdr.AssetType
	BuyingIssuer  string
	BuyingCode    string
}

// GetString retrieves a string from either the URLParams, form or query string.
// This method uses the priority (URLParams, Form, Query).
func (base *Base) GetString(name string) string {
	if base.Err != nil {
		return ""
	}

	fromURL, ok := base.GojiCtx.URLParams[name]

	if ok {
		return fromURL
	}

	fromForm := base.R.FormValue(name)

	if fromForm != "" {
		return fromForm
	}

	return base.R.URL.Query().Get(name)
}

// GetInt64 retrieves an int64 from the action parameter of the given name.
// Populates err if the value is not a valid int64
func (base *Base) GetInt64(name string) int64 {
	if base.Err != nil {
		return 0
	}

	asStr := base.GetString(name)

	if asStr == "" {
		return 0
	}

	asI64, err := strconv.ParseInt(asStr, 10, 64)

	if err != nil {
		base.SetInvalidField(name, err)
		return 0
	}

	return asI64
}

// ValidateInt64 populates err if the value is not a valid int64
func (base *Base) ValidateInt64(name string) {
	_ = base.GetInt64(name)
}

// GetInt32 retrieves an int32 from the action parameter of the given name.
// Populates err if the value is not a valid int32
func (base *Base) GetInt32(name string) int32 {
	if base.Err != nil {
		return 0
	}

	asStr := base.GetString(name)

	if asStr == "" {
		return 0
	}

	asI64, err := strconv.ParseInt(asStr, 10, 32)

	if err != nil {
		base.SetInvalidField(name, err)
		return 0
	}

	return int32(asI64)
}

// GetPagingParams returns the cursor/order/limit triplet that is the
// standard way of communicating paging data to a horizon endpoint.
func (base *Base) GetPagingParams() (cursor string, order string, limit int32) {
	if base.Err != nil {
		return
	}

	cursor = base.GetString(ParamCursor)
	order = base.GetString(ParamOrder)
	limit = base.GetInt32(ParamLimit)

	if lei := base.R.Header.Get("Last-Event-ID"); lei != "" {
		cursor = lei
	}

	return
}

// GetPageQuery is a helper that returns a new db.PageQuery struct initialized
// using the results from a call to GetPagingParams()
func (base *Base) GetPageQuery() db.PageQuery {
	if base.Err != nil {
		return db.PageQuery{}
	}

	r, err := db.NewPageQuery(base.GetPagingParams())

	if err != nil {
		base.Err = err
	}

	return r
}

func (base *Base) GetAccountID(name string) (result xdr.AccountId) {
	raw, err := strkey.Decode(strkey.VersionByteAccountID, base.GetString(name))

	if base.Err != nil {
		return
	}

	if err != nil {
		base.SetInvalidField(name, err)
		return
	}

	var key xdr.Uint256
	copy(key[:], raw)

	result, err = xdr.NewAccountId(xdr.CryptoKeyTypeKeyTypeEd25519, key)
	if err != nil {
		base.SetInvalidField(name, err)
		return
	}

	return
}

// GetAssetType is a helper that returns a xdr.AssetType by reading a string
func (base *Base) GetAssetType(name string) xdr.AssetType {
	if base.Err != nil {
		return xdr.AssetTypeAssetTypeNative
	}

	r, err := assets.Parse(base.GetString(name))

	if base.Err != nil {
		return xdr.AssetTypeAssetTypeNative
	}

	if err != nil {
		base.SetInvalidField(name, err)
	}

	return r
}

// GetOrderBook returns an OrderBookParams from the url params
func (base *Base) GetOrderBook() (result OrderBookParams) {
	if base.Err != nil {
		return
	}

	result = OrderBookParams{
		SellingType:   base.GetAssetType("selling_asset_type"),
		SellingIssuer: base.GetString("selling_asset_issuer"),
		SellingCode:   base.GetString("selling_asset_code"),
		BuyingType:    base.GetAssetType("buying_asset_type"),
		BuyingIssuer:  base.GetString("buying_asset_issuer"),
		BuyingCode:    base.GetString("buying_asset_code"),
	}

	if base.Err != nil {
		goto InvalidOrderBook
	}

	if result.SellingType != xdr.AssetTypeAssetTypeNative {
		if result.SellingCode == "" {
			goto InvalidOrderBook
		}

		if result.SellingIssuer == "" {
			goto InvalidOrderBook
		}
	}

	if result.BuyingType != xdr.AssetTypeAssetTypeNative {
		if result.BuyingCode == "" {
			goto InvalidOrderBook
		}

		if result.BuyingIssuer == "" {
			goto InvalidOrderBook
		}
	}

	return

InvalidOrderBook:
	base.Err = &problem.P{
		Type:   "invalid_order_book",
		Title:  "Invalid Order Book Parameters",
		Status: http.StatusBadRequest,
		Detail: "The parameters that specify what order book to view are invalid in some way. " +
			"Please ensure that your type parameters (selling_asset_type and buying_asset_type) are one the " +
			"following valid values: native, credit_alphanum4, credit_alphanum12.  Also ensure that you " +
			"have specified selling_asset_code and selling_issuer if selling_asset_type is not 'native', as well " +
			"as buying_asset_code and buying_issuer if buying_asset_type is not 'native'",
	}

	return
}

// GetAsset
func (base *Base) GetAsset(prefix string) (result xdr.Asset) {
	if base.Err != nil {
		return
	}
	var value interface{}

	t := base.GetAssetType(prefix + "asset_type")

	switch t {
	case xdr.AssetTypeAssetTypeCreditAlphanum4:
		a := xdr.AssetAlphaNum4{}
		a.Issuer = base.GetAccountID(prefix + "asset_issuer")

		c := base.GetString(prefix + "asset_code")
		if len(c) > len(a.AssetCode) {
			base.SetInvalidField(prefix+"asset_code", nil)
			return
		}

		copy(a.AssetCode[:len(c)], []byte(c))
		value = a
	case xdr.AssetTypeAssetTypeCreditAlphanum12:
		a := xdr.AssetAlphaNum12{}
		a.Issuer = base.GetAccountID(prefix + "asset_issuer")

		c := base.GetString(prefix + "asset_code")
		if len(c) > len(a.AssetCode) {
			base.SetInvalidField(prefix+"asset_code", nil)
			return
		}

		copy(a.AssetCode[:len(c)], []byte(c))
		value = a
	}

	result, err := xdr.NewAsset(t, value)
	if err != nil {
		panic(err)
	}
	return
}

func (base *Base) SetInvalidField(name string, reason error) {
	br := problem.BadRequest

	br.Extras = map[string]interface{}{}
	br.Extras["invalid_field"] = name
	br.Extras["reason"] = reason.Error()

	base.Err = &br
}

// Path returns the current action's path, as determined by the http.Request of
// this action
func (base *Base) Path() string {
	return base.R.URL.Path
}
<commit_msg>Add GetAddress helper<commit_after>package actions

import (
	"net/http"
	"strconv"

	"github.com/stellar/go-stellar-base/strkey"
	"github.com/stellar/go-stellar-base/xdr"
	"github.com/stellar/horizon/assets"
	"github.com/stellar/horizon/db"
	"github.com/stellar/horizon/render/problem"
)

const (
	// ParamCursor is a query string param name
	ParamCursor = "cursor"
	// ParamOrder is a query string param name
	ParamOrder = "order"
	// ParamLimit is a query string param name
	ParamLimit = "limit"
)

// OrderBookParams is a helper struct that encapsulates the specification for
// an order book
type OrderBookParams struct {
	SellingType   xdr.AssetType
	SellingIssuer string
	SellingCode   string
	BuyingType    xdr.AssetType
	BuyingIssuer  string
	BuyingCode    string
}

// GetString retrieves a string from either the URLParams, form or query string.
// This method uses the priority (URLParams, Form, Query).
func (base *Base) GetString(name string) string {
	if base.Err != nil {
		return ""
	}

	fromURL, ok := base.GojiCtx.URLParams[name]

	if ok {
		return fromURL
	}

	fromForm := base.R.FormValue(name)

	if fromForm != "" {
		return fromForm
	}

	return base.R.URL.Query().Get(name)
}

// GetInt64 retrieves an int64 from the action parameter of the given name.
// Populates err if the value is not a valid int64
func (base *Base) GetInt64(name string) int64 {
	if base.Err != nil {
		return 0
	}

	asStr := base.GetString(name)

	if asStr == "" {
		return 0
	}

	asI64, err := strconv.ParseInt(asStr, 10, 64)

	if err != nil {
		base.SetInvalidField(name, err)
		return 0
	}

	return asI64
}

// ValidateInt64 populates err if the value is not a valid int64
func (base *Base) ValidateInt64(name string) {
	_ = base.GetInt64(name)
}

// GetInt32 retrieves an int32 from the action parameter of the given name.
// Populates err if the value is not a valid int32
func (base *Base) GetInt32(name string) int32 {
	if base.Err != nil {
		return 0
	}

	asStr := base.GetString(name)

	if asStr == "" {
		return 0
	}

	asI64, err := strconv.ParseInt(asStr, 10, 32)

	if err != nil {
		base.SetInvalidField(name, err)
		return 0
	}

	return int32(asI64)
}

// GetPagingParams returns the cursor/order/limit triplet that is the
// standard way of communicating paging data to a horizon endpoint.
func (base *Base) GetPagingParams() (cursor string, order string, limit int32) {
	if base.Err != nil {
		return
	}

	cursor = base.GetString(ParamCursor)
	order = base.GetString(ParamOrder)
	limit = base.GetInt32(ParamLimit)

	if lei := base.R.Header.Get("Last-Event-ID"); lei != "" {
		cursor = lei
	}

	return
}

// GetPageQuery is a helper that returns a new db.PageQuery struct initialized
// using the results from a call to GetPagingParams()
func (base *Base) GetPageQuery() db.PageQuery {
	if base.Err != nil {
		return db.PageQuery{}
	}

	r, err := db.NewPageQuery(base.GetPagingParams())

	if err != nil {
		base.Err = err
	}

	return r
}

func (base *Base) GetAddress(name string) (result string) {
	if base.Err != nil {
		return
	}

	result = base.GetString(name)

	_, err := strkey.Decode(strkey.VersionByteAccountID, result)

	if err != nil {
		base.SetInvalidField(name, err)
	}

	return result
}

func (base *Base) GetAccountID(name string) (result xdr.AccountId) {
	raw, err := strkey.Decode(strkey.VersionByteAccountID, base.GetString(name))

	if base.Err != nil {
		return
	}

	if err != nil {
		base.SetInvalidField(name, err)
		return
	}

	var key xdr.Uint256
	copy(key[:], raw)

	result, err = xdr.NewAccountId(xdr.CryptoKeyTypeKeyTypeEd25519, key)
	if err != nil {
		base.SetInvalidField(name, err)
		return
	}

	return
}

// GetAssetType is a helper that returns a xdr.AssetType by reading a string
func (base *Base) GetAssetType(name string) xdr.AssetType {
	if base.Err != nil {
		return xdr.AssetTypeAssetTypeNative
	}

	r, err := assets.Parse(base.GetString(name))

	if base.Err != nil {
		return xdr.AssetTypeAssetTypeNative
	}

	if err != nil {
		base.SetInvalidField(name, err)
	}

	return r
}

// GetOrderBook returns an OrderBookParams from the url params
func (base *Base) GetOrderBook() (result OrderBookParams) {
	if base.Err != nil {
		return
	}

	result = OrderBookParams{
		SellingType:   base.GetAssetType("selling_asset_type"),
		SellingIssuer: base.GetString("selling_asset_issuer"),
		SellingCode:   base.GetString("selling_asset_code"),
		BuyingType:    base.GetAssetType("buying_asset_type"),
		BuyingIssuer:  base.GetString("buying_asset_issuer"),
		BuyingCode:    base.GetString("buying_asset_code"),
	}

	if base.Err != nil {
		goto InvalidOrderBook
	}

	if result.SellingType != xdr.AssetTypeAssetTypeNative {
		if result.SellingCode == "" {
			goto InvalidOrderBook
		}

		if result.SellingIssuer == "" {
			goto InvalidOrderBook
		}
	}

	if result.BuyingType != xdr.AssetTypeAssetTypeNative {
		if result.BuyingCode == "" {
			goto InvalidOrderBook
		}

		if result.BuyingIssuer == "" {
			goto InvalidOrderBook
		}
	}

	return

InvalidOrderBook:
	base.Err = &problem.P{
		Type:   "invalid_order_book",
		Title:  "Invalid Order Book Parameters",
		Status: http.StatusBadRequest,
		Detail: "The parameters that specify what order book to view are invalid in some way. " +
			"Please ensure that your type parameters (selling_asset_type and buying_asset_type) are one the " +
			"following valid values: native, credit_alphanum4, credit_alphanum12.  Also ensure that you " +
			"have specified selling_asset_code and selling_issuer if selling_asset_type is not 'native', as well " +
			"as buying_asset_code and buying_issuer if buying_asset_type is not 'native'",
	}

	return
}

// GetAsset
func (base *Base) GetAsset(prefix string) (result xdr.Asset) {
	if base.Err != nil {
		return
	}
	var value interface{}

	t := base.GetAssetType(prefix + "asset_type")

	switch t {
	case xdr.AssetTypeAssetTypeCreditAlphanum4:
		a := xdr.AssetAlphaNum4{}
		a.Issuer = base.GetAccountID(prefix + "asset_issuer")

		c := base.GetString(prefix + "asset_code")
		if len(c) > len(a.AssetCode) {
			base.SetInvalidField(prefix+"asset_code", nil)
			return
		}

		copy(a.AssetCode[:len(c)], []byte(c))
		value = a
	case xdr.AssetTypeAssetTypeCreditAlphanum12:
		a := xdr.AssetAlphaNum12{}
		a.Issuer = base.GetAccountID(prefix + "asset_issuer")

		c := base.GetString(prefix + "asset_code")
		if len(c) > len(a.AssetCode) {
			base.SetInvalidField(prefix+"asset_code", nil)
			return
		}

		copy(a.AssetCode[:len(c)], []byte(c))
		value = a
	}

	result, err := xdr.NewAsset(t, value)
	if err != nil {
		panic(err)
	}
	return
}

func (base *Base) SetInvalidField(name string, reason error) {
	br := problem.BadRequest

	br.Extras = map[string]interface{}{}
	br.Extras["invalid_field"] = name
	br.Extras["reason"] = reason.Error()

	base.Err = &br
}

// Path returns the current action's path, as determined by the http.Request of
// this action
func (base *Base) Path() string {
	return base.R.URL.Path
}
<|endoftext|>


<commit_before>package mat64

// Inner computes the generalized inner product between x and y with matrix A.
//  x^T A y
// This is only a true inner product if m is symmetric positive definite, though
// the operation works for any matrix A.
//
// Inner panics if len(x) != m or len(y) != n when A is an m x n matrix.
func Inner(x []float64, A Matrix, y []float64) float64 {
	m, n := A.Dims()
	if len(x) != m {
		panic(ErrShape)
	}
	if len(y) != n {
		panic(ErrShape)
	}
	if m == 0 || n == 0 {
		return 0
	}

	var sum float64

	switch b := A.(type) {
	case RawMatrixer:
		bmat := b.RawMatrix()
		for i, xi := range x {
			for j, yj := range y {
				sum += xi * bmat.Data[i*bmat.Stride+j] * yj

			}
		}
		return sum
	default:
		for i := 0; i < m; i++ {
			xi := x[i]
			for j := 0; j < n; j++ {
				sum += xi * A.At(i, j) * y[j]
			}
		}
		return sum
	}
}
<commit_msg>Made default Inner case use range and removed spurious newline<commit_after>package mat64

// Inner computes the generalized inner product between x and y with matrix A.
//  x^T A y
// This is only a true inner product if m is symmetric positive definite, though
// the operation works for any matrix A.
//
// Inner panics if len(x) != m or len(y) != n when A is an m x n matrix.
func Inner(x []float64, A Matrix, y []float64) float64 {
	m, n := A.Dims()
	if len(x) != m {
		panic(ErrShape)
	}
	if len(y) != n {
		panic(ErrShape)
	}
	if m == 0 || n == 0 {
		return 0
	}

	var sum float64

	switch b := A.(type) {
	case RawMatrixer:
		bmat := b.RawMatrix()
		for i, xi := range x {
			for j, yj := range y {
				sum += xi * bmat.Data[i*bmat.Stride+j] * yj
			}
		}
		return sum
	default:
		for i, xi := range x {
			for j, yj := range y {
				sum += xi * A.At(i, j) * yj
			}
		}
		return sum
	}
}
<|endoftext|>


<commit_before>package match

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

	"github.com/clyphub/munkres"
	"github.com/gocarina/gocsv"
	"github.com/gokapaya/cshelper/ulist"
	"github.com/pkg/errors"
)

type Pair struct {
	Santa  *ulist.User
	Giftee *ulist.User
}

type data struct {
	Santa  string `csv:"santa"`
	Giftee string `csv:"giftee"`
}

// Match takes a list of Users and returns Pairs of Santa and Giftee
func Match(ul *ulist.Ulist) ([]Pair, error) {
	if ul.Len() < 2 {
		return nil, errors.New("user list has < 2 entries")
	}

	m := munkres.NewMatrix(ul.Len())
	m.A = costMatrix(ul, func(santa, giftee ulist.User) int64 {
		var cost int64 = 0
		if santa.Username == giftee.Username {
			cost += 100
		}

		if !santa.International {
			if !ulist.SameCountry(santa.Address.Country, giftee.Address.Country) {
				if !ulist.SameRegion(santa.Address.Country, giftee.Address.Country) {
					cost += 100
				} else {
					cost += 20
				}
			}
		}
		return cost
	})

	result := munkres.ComputeMunkresMin(m)
	// m.Print()
	// printRowCol(ul, result)

	var pairs = make([]Pair, 0)
	for _, rowcol := range result {
		pairs = append(pairs, Pair{
			Santa:  ul.Get(rowcol.Row),
			Giftee: ul.Get(rowcol.Col),
		})
	}
	return pairs, nil
}

func costMatrix(ul *ulist.Ulist, costFn func(ulist.User, ulist.User) int64) []int64 {
	var m = make([][]int64, ul.Len())

	ul.Iter(func(i int, a ulist.User) error {
		m[i] = make([]int64, ul.Len())

		ul.Iter(func(k int, b ulist.User) error {
			var cost int64 = 0

			cost += costFn(a, b)
			r := rand.New(rand.NewSource(time.Now().UnixNano()))
			cost += int64(r.Int63n(20))

			m[i][k] = cost
			return nil

		})
		return nil
	})

	return matrixToSlice(m)
}

func matrixToSlice(matrix [][]int64) []int64 {
	var slice []int64
	for _, array := range matrix {
		for _, num := range array {
			slice = append(slice, num)
		}
	}
	return slice
}

// Eval takes a []Pair and evaluates it agains a set of rules.
func Eval(pairings []Pair) error {
	for _, p := range pairings {
		// check not same name

		if p.Santa.Username == p.Giftee.Username {
			return errors.Errorf("same person\n\n%s == %s", p.Santa.Username, p.Giftee.Username)
		}

		if !p.Santa.International {
			if !ulist.SameRegion(
				p.Santa.Address.Country,
				p.Giftee.Address.Country,
			) {
				return errors.Errorf("santa doesn't want international but has to send out of his region\n\nSanta's country: %s\nGiftee's country: %s", p.Santa.Address.Country, p.Giftee.Address.Country)
			}
		}
	}
	return nil
}

func printRowCol(ul *ulist.Ulist, result []munkres.RowCol) {
	println("result:")
	for _, rc := range result {
		fmt.Printf("% 3v <> % 3v\n\n",
			ul.Get(rc.Row).Username,
			ul.Get(rc.Col).Username,
		)
	}
}

func SavePairings(fpath string, pairings []Pair) error {
	// save .csv file
	fd, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, os.ModePerm)
	if err != nil {
		return errors.Wrapf(err, "unable to open %q for writing", fpath)
	}
	var d []*data
	for _, p := range pairings {
		d = append(d, &data{Santa: p.Santa.Username, Giftee: p.Giftee.Username})
	}
	return gocsv.MarshalFile(d, fd)
}

func LoadPairings(fpath string, ul ulist.Ulist) ([]Pair, error) {
	fd, err := os.Open(fpath)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to open %q for reading", fpath)
	}

	var p []data
	if err := gocsv.UnmarshalFile(fd, p); err != nil {
		return nil, err
	}

	var pairs []Pair
	for _, datap := range p {
		pair := Pair{
			Santa:  ul.GetByName(datap.Santa),
			Giftee: ul.GetByName(datap.Giftee),
		}
		if pair.Santa == nil {
			return nil, errors.Errorf("username not found %q", datap.Santa)
		}
		if pair.Giftee != nil {
			return nil, errors.Errorf("username not found %q", datap.Giftee)
		}
		pairs = append(pairs, pair)
	}
	return pairs, nil
}
<commit_msg>match: fix column names/order<commit_after>package match

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

	"github.com/clyphub/munkres"
	"github.com/gocarina/gocsv"
	"github.com/gokapaya/cshelper/ulist"
	"github.com/pkg/errors"
)

type Pair struct {
	Santa  *ulist.User
	Giftee *ulist.User
}

type data struct {
	Giftee string `csv:"user"`
	Santa  string `csv:"santa"`
}

// Match takes a list of Users and returns Pairs of Santa and Giftee
func Match(ul *ulist.Ulist) ([]Pair, error) {
	if ul.Len() < 2 {
		return nil, errors.New("user list has < 2 entries")
	}

	m := munkres.NewMatrix(ul.Len())
	m.A = costMatrix(ul, func(santa, giftee ulist.User) int64 {
		var cost int64 = 0
		if santa.Username == giftee.Username {
			cost += 100
		}

		if !santa.International {
			if !ulist.SameCountry(santa.Address.Country, giftee.Address.Country) {
				if !ulist.SameRegion(santa.Address.Country, giftee.Address.Country) {
					cost += 100
				} else {
					cost += 20
				}
			}
		}
		return cost
	})

	result := munkres.ComputeMunkresMin(m)
	// m.Print()
	// printRowCol(ul, result)

	var pairs = make([]Pair, 0)
	for _, rowcol := range result {
		pairs = append(pairs, Pair{
			Santa:  ul.Get(rowcol.Row),
			Giftee: ul.Get(rowcol.Col),
		})
	}
	return pairs, nil
}

func costMatrix(ul *ulist.Ulist, costFn func(ulist.User, ulist.User) int64) []int64 {
	var m = make([][]int64, ul.Len())

	ul.Iter(func(i int, a ulist.User) error {
		m[i] = make([]int64, ul.Len())

		ul.Iter(func(k int, b ulist.User) error {
			var cost int64 = 0

			cost += costFn(a, b)
			r := rand.New(rand.NewSource(time.Now().UnixNano()))
			cost += int64(r.Int63n(20))

			m[i][k] = cost
			return nil

		})
		return nil
	})

	return matrixToSlice(m)
}

func matrixToSlice(matrix [][]int64) []int64 {
	var slice []int64
	for _, array := range matrix {
		for _, num := range array {
			slice = append(slice, num)
		}
	}
	return slice
}

// Eval takes a []Pair and evaluates it agains a set of rules.
func Eval(pairings []Pair) error {
	for _, p := range pairings {
		// check not same name

		if p.Santa.Username == p.Giftee.Username {
			return errors.Errorf("same person\n\n%s == %s", p.Santa.Username, p.Giftee.Username)
		}

		if !p.Santa.International {
			if !ulist.SameRegion(
				p.Santa.Address.Country,
				p.Giftee.Address.Country,
			) {
				return errors.Errorf("santa doesn't want international but has to send out of his region\n\nSanta's country: %s\nGiftee's country: %s", p.Santa.Address.Country, p.Giftee.Address.Country)
			}
		}
	}
	return nil
}

func printRowCol(ul *ulist.Ulist, result []munkres.RowCol) {
	println("result:")
	for _, rc := range result {
		fmt.Printf("% 3v <> % 3v\n\n",
			ul.Get(rc.Row).Username,
			ul.Get(rc.Col).Username,
		)
	}
}

func SavePairings(fpath string, pairings []Pair) error {
	// save .csv file
	fd, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, os.ModePerm)
	if err != nil {
		return errors.Wrapf(err, "unable to open %q for writing", fpath)
	}
	var d []*data
	for _, p := range pairings {
		d = append(d, &data{Santa: p.Santa.Username, Giftee: p.Giftee.Username})
	}
	return gocsv.MarshalFile(d, fd)
}

func LoadPairings(fpath string, ul ulist.Ulist) ([]Pair, error) {
	fd, err := os.Open(fpath)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to open %q for reading", fpath)
	}

	var p []data
	if err := gocsv.UnmarshalFile(fd, p); err != nil {
		return nil, err
	}

	var pairs []Pair
	for _, datap := range p {
		pair := Pair{
			Santa:  ul.GetByName(datap.Santa),
			Giftee: ul.GetByName(datap.Giftee),
		}
		if pair.Santa == nil {
			return nil, errors.Errorf("username not found %q", datap.Santa)
		}
		if pair.Giftee != nil {
			return nil, errors.Errorf("username not found %q", datap.Giftee)
		}
		pairs = append(pairs, pair)
	}
	return pairs, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"image"
	"log"
	"math"
	"os"
	"strconv"
	"sync"
	"time"

	_ "image/png"
)

// Img represents an image with explicit
// width and height values
type Img struct {
	i *image.NRGBA
	w int
	h int
}

// Match represents a subimage match rated by a score
type Match struct {
	s float32
	r image.Rectangle
}

// parseDuration takes a user-specified string and tries
// to interpret it as duration
func parseDuration(spec string) time.Duration {
	if sec, err := strconv.Atoi(spec); err == nil {
		return time.Duration(sec) * time.Second
	}

	val, err := strconv.Atoi(spec[:len(spec)-1])
	if err != nil {
		log.Fatal("invalid interval specified - '" + spec + "', I need a string \\d+[ismh]")
		return time.Duration(0)
	}

	switch spec[len(spec)-1] {
	case 'i':
		return time.Duration(val) * time.Millisecond
	case 's':
		return time.Duration(val) * time.Second
	case 'm':
		return time.Duration(val) * time.Minute
	case 'h':
		return time.Duration(val) * time.Hour
	}

	log.Panic("invalid interval duration specifier - '" + spec[len(spec)-1:] + "', must be one of 'ismh'")
	return time.Duration(0)
}

// readImage takes a filepath and reads
// the image into the memory. It returns
// the corresponding Img struct.
func readImage(filepath string) Img {
	// read file
	reader, err := os.Open(filepath)
	if err != nil {
		log.Fatal(err)
	}
	defer reader.Close()
	img, format, err := image.Decode(reader)
	if err != nil {
		log.Fatal(err)
	}

	// width & height
	width := img.Bounds().Max.X
	height := img.Bounds().Max.Y

	// now convert to NRGBA discarding alpha pre-multiplication
	nrgbaImage := image.NewNRGBA(image.Rect(0, 0, width, height))
	for y := 0; y < height; y++ {
		for x := 0; x < width; x++ {
			nrgbaImage.Set(x, y, img.At(x, y))
		}
	}

	// return instance
	log.Printf("%s image (%d×%d) read\n", format, width, height)
	return Img{nrgbaImage, width, height}
}

// compareRow compares row pixels of base and sub images.
// It returns an equivalence score between 0 (eq) and 100 (diff).
func compareRow(base, sub Img, row int, wait *sync.WaitGroup, result *float32) {
	defer wait.Done()

	var scale float64 = 42949672.96
	var r, g, b uint32
	var refR, refG, refB, refA uint32
	var diff float64
	var sum float64

	for x := 0; x < sub.w; x++ {
		r, g, b, _ = base.i.NRGBAAt(x, row).RGBA()
		refR, refG, refB, refA = sub.i.NRGBAAt(x, row).RGBA()
		diff = (math.Abs(float64(refR-r)) + math.Abs(float64(refG-g)) + math.Abs(float64(refB-b))) / 3
		val := (diff / scale) * (float64(refA) / 4294967296.0)
		if val > 100.0 {
			val = 100.0
		}
		sum += val
	}

	*result = float32(sum / float64(sub.w))
}

func main() {
	var w sync.WaitGroup
	d := make(chan bool)

	start := time.Now()

	// CLI options
	if len(os.Args) != 4 {
		fmt.Println("Usage: ./subimage <baseimage.png> <refimage.png> <timeout>")
		fmt.Println("  Compare baseimage with refimage within timeout.")
		fmt.Println("")
		fmt.Println("  Returns an exit code in [0,101]")
		fmt.Println("     101 ... timeout reached")
		fmt.Println("     100 ... no equivalence (too different, different dimensions)")
		fmt.Println("     0   ... no differences (every pixel has same RGB value)")
		fmt.Println("")
		fmt.Println("  Timeout needs to be an integer followed by a flag; one of 'ism'.")
		fmt.Println("  e.g. '2s' or '2' means 2 seconds, i for milliseconds and m for minute")
		fmt.Println("")
		fmt.Println("  Pixels with a positive alpha value are compared in relation")
		fmt.Println("  to the alpha value. A transparent image of same dimension")
		fmt.Println("  is equivalent to every other image. Alpha values in the base image are ignored.")
		fmt.Println("  The implementation uses floating point number; subject to rounding errors")
		os.Exit(1)
	}

	// preparing
	base := readImage(os.Args[1])
	sub := readImage(os.Args[2])
	timeout := parseDuration(os.Args[3])

	// check equivalence in every row
	if base.w != sub.w || base.h != sub.h {
		fmt.Printf("  Base image: %d×%d\n", base.w, base.h)
		fmt.Printf("  Sub image:  %d×%d\n", sub.w, sub.h)
		fmt.Println("Dimensions do not correspond")
		os.Exit(100)
	}
	rowEqs := make([]float32, base.h)
	w.Add(base.h)
	go func() {
		for row := 0; row < base.h; row++ {
			compareRow(base, sub, row, &w, &rowEqs[row])
		}
	}()

	// one goroutine waits for the result
	go func() {
		w.Wait()
		d <- true
	}()

	// either interrupt or wait for result
	timedout := false
wait:
	for {
		select {
		case <-d:
			break wait
		case <-time.After(timeout):
			timedout = true
			break wait
		}
	}

	// determine equivalence score
	var eq float64
	var score int = 128
	for row := 0; row < base.h; row++ {
		eq += float64(rowEqs[row])
	}

	// <screenshot-specific code>
	// makes matching more sensitive
	CORRECTION := float64(base.h * 10000)
	eq *= CORRECTION
	// </screenshot-specific code>

	eq /= float64(base.h)
	for i := 0; i < 100; i++ {
		if float64(i) <= eq && eq < float64(i+1) {
			score = i
		}
	}
	if score == 128 {
		score = 100
	}

	// output
	end := time.Now()
	fmt.Printf("runtime: %v\n", end.Sub(start))
	fmt.Printf("score:   %f\n", eq)

	if timedout {
		fmt.Println("timeout")
		os.Exit(101)
	} else {
		fmt.Println("finished")
		os.Exit(score)
	}
}
<commit_msg>Simplify usage printing<commit_after>package main

import (
	"fmt"
	"image"
	"log"
	"math"
	"os"
	"strconv"
	"sync"
	"time"

	_ "image/png"
)

const USAGE = `
Usage: ./subimage <baseimage.png> <refimage.png> <timeout>
  Compare baseimage with refimage within timeout.

  Returns an exit code in [0,101]
     101 ... timeout reached
     100 ... no equivalence (too different, different dimensions)
     0   ... no differences (every pixel has same RGB value)

  Timeout needs to be an integer followed by a flag; one of 'ism'.
  e.g. '2s' or '2' means 2 seconds, i for milliseconds and m for minute

  Pixels with a positive alpha value are compared in relation
  to the alpha value. A transparent image of same dimension
  is equivalent to every other image. Alpha values in the base image are ignored.
  The implementation uses floating point number; subject to rounding errors
`

// Img represents an image with explicit
// width and height values
type Img struct {
	i *image.NRGBA
	w int
	h int
}

// Match represents a subimage match rated by a score
type Match struct {
	s float32
	r image.Rectangle
}

// parseDuration takes a user-specified string and tries
// to interpret it as duration
func parseDuration(spec string) time.Duration {
	if sec, err := strconv.Atoi(spec); err == nil {
		return time.Duration(sec) * time.Second
	}

	val, err := strconv.Atoi(spec[:len(spec)-1])
	if err != nil {
		log.Fatal("invalid interval specified - '" + spec + "', I need a string \\d+[ismh]")
		return time.Duration(0)
	}

	switch spec[len(spec)-1] {
	case 'i':
		return time.Duration(val) * time.Millisecond
	case 's':
		return time.Duration(val) * time.Second
	case 'm':
		return time.Duration(val) * time.Minute
	case 'h':
		return time.Duration(val) * time.Hour
	}

	log.Panic("invalid interval duration specifier - '" + spec[len(spec)-1:] + "', must be one of 'ismh'")
	return time.Duration(0)
}

// readImage takes a filepath and reads
// the image into the memory. It returns
// the corresponding Img struct.
func readImage(filepath string) Img {
	// read file
	reader, err := os.Open(filepath)
	if err != nil {
		log.Fatal(err)
	}
	defer reader.Close()
	img, format, err := image.Decode(reader)
	if err != nil {
		log.Fatal(err)
	}

	// width & height
	width := img.Bounds().Max.X
	height := img.Bounds().Max.Y

	// now convert to NRGBA discarding alpha pre-multiplication
	nrgbaImage := image.NewNRGBA(image.Rect(0, 0, width, height))
	for y := 0; y < height; y++ {
		for x := 0; x < width; x++ {
			nrgbaImage.Set(x, y, img.At(x, y))
		}
	}

	// return instance
	log.Printf("%s image (%d×%d) read\n", format, width, height)
	return Img{nrgbaImage, width, height}
}

// compareRow compares row pixels of base and sub images.
// It returns an equivalence score between 0 (eq) and 100 (diff).
func compareRow(base, sub Img, row int, wait *sync.WaitGroup, result *float32) {
	defer wait.Done()

	var scale float64 = 42949672.96
	var r, g, b uint32
	var refR, refG, refB, refA uint32
	var diff float64
	var sum float64

	for x := 0; x < sub.w; x++ {
		r, g, b, _ = base.i.NRGBAAt(x, row).RGBA()
		refR, refG, refB, refA = sub.i.NRGBAAt(x, row).RGBA()
		diff = (math.Abs(float64(refR-r)) + math.Abs(float64(refG-g)) + math.Abs(float64(refB-b))) / 3
		val := (diff / scale) * (float64(refA) / 4294967296.0)
		if val > 100.0 {
			val = 100.0
		}
		sum += val
	}

	*result = float32(sum / float64(sub.w))
}

func main() {
	var w sync.WaitGroup
	d := make(chan bool)

	start := time.Now()

	// CLI options
	if len(os.Args) != 4 {
		fmt.Println(USAGE)
		os.Exit(1)
	}

	// preparing
	base := readImage(os.Args[1])
	sub := readImage(os.Args[2])
	timeout := parseDuration(os.Args[3])

	// check equivalence in every row
	if base.w != sub.w || base.h != sub.h {
		fmt.Printf("  Base image: %d×%d\n", base.w, base.h)
		fmt.Printf("  Sub image:  %d×%d\n", sub.w, sub.h)
		fmt.Println("Dimensions do not correspond")
		os.Exit(100)
	}
	rowEqs := make([]float32, base.h)
	w.Add(base.h)
	go func() {
		for row := 0; row < base.h; row++ {
			compareRow(base, sub, row, &w, &rowEqs[row])
		}
	}()

	// one goroutine waits for the result
	go func() {
		w.Wait()
		d <- true
	}()

	// either interrupt or wait for result
	timedout := false
wait:
	for {
		select {
		case <-d:
			break wait
		case <-time.After(timeout):
			timedout = true
			break wait
		}
	}

	// determine equivalence score
	var eq float64
	var score int = 128
	for row := 0; row < base.h; row++ {
		eq += float64(rowEqs[row])
	}

	// <screenshot-specific code>
	// makes matching more sensitive
	CORRECTION := float64(base.h * 10000)
	eq *= CORRECTION
	// </screenshot-specific code>

	eq /= float64(base.h)
	for i := 0; i < 100; i++ {
		if float64(i) <= eq && eq < float64(i+1) {
			score = i
		}
	}
	if score == 128 {
		score = 100
	}

	// output
	end := time.Now()
	fmt.Printf("runtime: %v\n", end.Sub(start))
	fmt.Printf("score:   %f\n", eq)

	if timedout {
		fmt.Println("timeout")
		os.Exit(101)
	} else {
		fmt.Println("finished")
		os.Exit(score)
	}
}
<|endoftext|>


<commit_before>package main

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

	"github.com/blang/semver"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"

	"github.com/concourse/semver-resource/models"
	"github.com/concourse/semver-resource/version"
)

func main() {
	if len(os.Args) < 2 {
		println("usage: " + os.Args[0] + " <destination>")
		os.Exit(1)
	}

	destination := os.Args[1]

	err := os.MkdirAll(destination, 0755)
	if err != nil {
		fatal("creating destination", err)
	}

	var request models.InRequest
	err = json.NewDecoder(os.Stdin).Decode(&request)
	if err != nil {
		fatal("reading request", err)
	}

	auth := aws.Auth{
		AccessKey: request.Source.AccessKeyID,
		SecretKey: request.Source.SecretAccessKey,
	}

	regionName := request.Source.RegionName
	if len(regionName) == 0 {
		regionName = aws.USEast.Name
	}

	region, ok := aws.Regions[regionName]
	if !ok {
		fatal("resolving region name", errors.New(fmt.Sprintf("No such region '%s'", regionName)))
	}

	if len(request.Source.Endpoint) != 0 {
		region = aws.Region{S3Endpoint: fmt.Sprintf("https://%s", request.Source.Endpoint)}
	}

	client := s3.New(auth, region)
	bucket := client.Bucket(request.Source.Bucket)

	inputVersion, err := semver.Parse(request.Version.Number)
	if err != nil {
		fatal("parsing semantic version", err)
	}

	bumped := version.BumpFromParams(request.Params).Apply(inputVersion)

	if !bumped.Equals(inputVersion) {
		fmt.Printf("bumped locally from %s to %s\n", inputVersion, bumped)
	}

	numberFile, err := os.Create(filepath.Join(destination, "number"))
	if err != nil {
		fatal("opening number file", err)
	}

	defer numberFile.Close()

	_, err = fmt.Fprintf(numberFile, "%s", bumped.String())
	if err != nil {
		fatal("writing to number file", err)
	}

	json.NewEncoder(os.Stdout).Encode(models.InResponse{
		Version: request.Version,
		Metadata: models.Metadata{
			{"number", request.Version.Number},
		},
	})
}

func fatal(doing string, err error) {
	println("error " + doing + ": " + err.Error())
	os.Exit(1)
}
<commit_msg>/in doesn't need s3 anymore<commit_after>package main

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

	"github.com/blang/semver"

	"github.com/concourse/semver-resource/models"
	"github.com/concourse/semver-resource/version"
)

func main() {
	if len(os.Args) < 2 {
		println("usage: " + os.Args[0] + " <destination>")
		os.Exit(1)
	}

	destination := os.Args[1]

	err := os.MkdirAll(destination, 0755)
	if err != nil {
		fatal("creating destination", err)
	}

	var request models.InRequest
	err = json.NewDecoder(os.Stdin).Decode(&request)
	if err != nil {
		fatal("reading request", err)
	}

	inputVersion, err := semver.Parse(request.Version.Number)
	if err != nil {
		fatal("parsing semantic version", err)
	}

	bumped := version.BumpFromParams(request.Params).Apply(inputVersion)

	if !bumped.Equals(inputVersion) {
		fmt.Printf("bumped locally from %s to %s\n", inputVersion, bumped)
	}

	numberFile, err := os.Create(filepath.Join(destination, "number"))
	if err != nil {
		fatal("opening number file", err)
	}

	defer numberFile.Close()

	_, err = fmt.Fprintf(numberFile, "%s", bumped.String())
	if err != nil {
		fatal("writing to number file", err)
	}

	json.NewEncoder(os.Stdout).Encode(models.InResponse{
		Version: request.Version,
		Metadata: models.Metadata{
			{"number", request.Version.Number},
		},
	})
}

func fatal(doing string, err error) {
	println("error " + doing + ": " + err.Error())
	os.Exit(1)
}
<|endoftext|>


<commit_before>package knx

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/vapourismo/knx-go/knx/cemi"
	"github.com/vapourismo/knx-go/knx/proto"
)

// TunnelConfig allows you to configure the client's behavior.
type TunnelConfig struct {
	// ResendInterval is how long to wait for a response, until the request is resend. A interval
	// <= 0 can't be used. The default value will be used instead.
	ResendInterval time.Duration

	// HeartbeatDelay specifies the time which has to elapse without any incoming communication,
	// until a heartbeat is triggered. A delay <= 0 will result in the use of a default value.
	HeartbeatDelay time.Duration

	// ResponseTimeout specifies how long to wait for a response. A timeout <= 0 will not be
	// accepted. Instead, the default value will be used.
	ResponseTimeout time.Duration
}

// Default configuration elements
var (
	defaultResendInterval  = 500 * time.Millisecond
	defaultHeartbeatDelay  = 10 * time.Second
	defaultResponseTimeout = 10 * time.Second

	DefaultClientConfig = TunnelConfig{
		defaultResendInterval,
		defaultHeartbeatDelay,
		defaultResponseTimeout,
	}
)

// checkClientConfig makes sure that the configuration is actually usable.
func checkClientConfig(config TunnelConfig) TunnelConfig {
	if config.ResendInterval <= 0 {
		config.ResendInterval = defaultResendInterval
	}

	if config.HeartbeatDelay <= 0 {
		config.HeartbeatDelay = defaultHeartbeatDelay
	}

	if config.ResponseTimeout <= 0 {
		config.ResponseTimeout = defaultResponseTimeout
	}

	return config
}

// tunnelConn is a handle for a tunnel connection.
type tunnelConn struct {
	sock      Socket
	config    TunnelConfig
	channel   uint8
	control   proto.HostInfo
	seqMu     *sync.Mutex
	seqNumber uint8
	ack       chan *proto.TunnelRes
	inbound   chan cemi.CEMI
}

// requestConn repeatedly sends a connection request through the socket until the provided context gets
// canceled, or a response is received. A response that renders the gateway as busy will not stop
// requestConn.
func (conn *tunnelConn) requestConn(ctx context.Context) (err error) {
	req := &proto.ConnReq{}

	// Send the initial request.
	err = conn.sock.Send(req)
	if err != nil {
		return
	}

	// Create a resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	// Cycle until a request gets a response.
	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer triggered.
		case <-ticker.C:
			err = conn.sock.Send(req)
			if err != nil {
				return
			}

		// A message has been received or the channel has been closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel has been closed")
			}

			// We're only interested in connection responses.
			if res, ok := msg.(*proto.ConnRes); ok {
				switch res.Status {
				// Conection has been established.
				case proto.ConnResOk:
					conn.channel = res.Channel
					conn.control = res.Control
					return nil

				// The gateway is busy, but we don't stop yet.
				case proto.ConnResBusy:
					continue

				// Connection request has been denied.
				default:
					return res.Status
				}
			}
		}
	}
}

// requestConnState periodically sends a connection state request to the gateway until it has
// received a response or the context is done.
func (conn *tunnelConn) requestConnState(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
) (proto.ConnState, error) {
	req := &proto.ConnStateReq{Channel: conn.channel, Status: 0, Control: proto.HostInfo{}}

	// Send first connection state request
	err := conn.sock.Send(req)
	if err != nil {
		return proto.ConnStateInactive, err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return proto.ConnStateInactive, ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return proto.ConnStateInactive, err
			}

		// Received a connection state response.
		case res, open := <-heartbeat:
			if !open {
				return proto.ConnStateInactive, errors.New("Connection server has terminated")
			}

			return res, nil
		}
	}
}

// requestDisc sends a disconnect request to the gateway.
func (conn *tunnelConn) requestDisc() error {
	return conn.sock.Send(&proto.DiscReq{
		Channel: conn.channel,
		Status:  0,
		Control: conn.control,
	})
}

// requestTunnel sends a tunnel request to the gateway and waits for an appropriate acknowledgement.
func (conn *tunnelConn) requestTunnel(
	ctx context.Context,
	data cemi.CEMI,
) error {
	// Sequence numbers cannot be reused, therefore we must protect against that.
	conn.seqMu.Lock()
	defer conn.seqMu.Unlock()

	req := &proto.TunnelReq{
		Channel:   conn.channel,
		SeqNumber: conn.seqNumber,
		Payload:   data,
	}

	// Send initial request.
	err := conn.sock.Send(req)
	if err != nil {
		return err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return err
			}

		// Received a tunnel response.
		case res, open := <-conn.ack:
			if !open {
				return errors.New("Connection server has terminated")
			}

			// Ignore mismatching sequence numbers.
			if res.SeqNumber != conn.seqNumber {
				continue
			}

			// Gateway has received the request, therefore we can increase on our side.
			conn.seqNumber++

			// Check if the response confirms the tunnel request.
			if res.Status == 0 {
				return nil
			}

			return fmt.Errorf("Tunnel request has been rejected with status %#x", res.Status)
		}
	}
}

// performHeartbeat uses requestState to determine if the gateway is still alive.
func (conn *tunnelConn) performHeartbeat(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
	timeout chan<- struct{},
) {
	// Setup a child context which will time out with the given heartbeat timeout.
	childCtx, cancel := context.WithTimeout(ctx, conn.config.ResponseTimeout)
	defer cancel()

	// Request the connction state.
	state, err := conn.requestConnState(childCtx, heartbeat)
	if err != nil || state != proto.ConnStateNormal {
		if err != nil {
			log(conn, "conn", "Error while requesting connection state: %v", err)
		} else {
			log(conn, "conn", "Bad connection state: %v", state)
		}

		// Write to timeout as an indication that the heartbeat has failed.
		select {
		case <-ctx.Done():
		case timeout <- struct{}{}:
		}
	}
}

// handleDiscReq validates the request.
func (conn *tunnelConn) handleDiscReq(
	ctx context.Context,
	req *proto.DiscReq,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect request")
	}

	// We don't need to check if this errors or not. It doesn't matter.
	conn.sock.Send(&proto.DiscRes{Channel: req.Channel, Status: 0})

	return nil
}

// handleDiscRes validates the response.
func (conn *tunnelConn) handleDiscRes(
	ctx context.Context,
	res *proto.DiscRes,
) error {
	// Validate the response channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect response")
	}

	return nil
}

// handleTunnelReq validates the request, pushes the data to the client and acknowledges the
// request for the gateway.
func (conn *tunnelConn) handleTunnelReq(
	ctx context.Context,
	req *proto.TunnelReq,
	seqNumber *uint8,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in tunnel request")
	}

	// Is the sequence number what we expected?
	if req.SeqNumber == *seqNumber {
		*seqNumber++

		// Send tunnel data to the client.
		go func() {
			select {
			case <-ctx.Done():
			case conn.inbound <- req.Payload:
			}
		}()
	}

	// Send the acknowledgement.
	return conn.sock.Send(&proto.TunnelRes{
		Channel:   conn.channel,
		SeqNumber: req.SeqNumber,
		Status:    0,
	})
}

// handleTunnelRes validates the response and relays it to a sender that is awaiting an
// acknowledgement.
func (conn *tunnelConn) handleTunnelRes(
	ctx context.Context,
	res *proto.TunnelRes,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send to client.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case conn.ack <- res:
		}
	}()

	return nil
}

// handleConnStateRes validates the response and sends it to the heartbeat routine, if
// there is a waiting one.
func (conn *tunnelConn) handleConnStateRes(
	ctx context.Context,
	res *proto.ConnStateRes,
	heartbeat chan<- proto.ConnState,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send connection state to the heartbeat goroutine.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case heartbeat <- res.Status:
		}
	}()

	return nil
}

// serve processes incoming packets. It will return with nil when a disconnect request or
// response has been received.
func (conn *tunnelConn) serve(
	ctx context.Context,
) error {
	defer close(conn.ack)
	defer close(conn.inbound)

	heartbeat := make(chan proto.ConnState)
	defer close(heartbeat)

	timeout := make(chan struct{})

	var seqNumber uint8

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Heartbeat worker signals a result.
		case <-timeout:
			return errors.New("Heartbeat did not succeed")

		// There were no incoming packets for some time.
		case <-time.After(conn.config.HeartbeatDelay):
			go conn.performHeartbeat(ctx, heartbeat, timeout)

		// A message has been received or the channel is closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel is closed")
			}

			// Determine what to do with the message.
			switch msg := msg.(type) {
			case *proto.DiscReq:
				err := conn.handleDiscReq(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect request %v: %v", msg, err)

			case *proto.DiscRes:
				err := conn.handleDiscRes(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect response %v: %v", msg, err)

			case *proto.TunnelReq:
				err := conn.handleTunnelReq(ctx, msg, &seqNumber)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel request %v: %v", msg, err)
				}

			case *proto.TunnelRes:
				err := conn.handleTunnelRes(ctx, msg)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel response %v: %v", msg, err)
				}

			case *proto.ConnStateRes:
				err := conn.handleConnStateRes(ctx, msg, heartbeat)
				if err != nil {
					log(
						conn, "conn",
						"Error while handling connection state response: %v", err,
					)
				}
			}
		}
	}
}

// Tunnel represents the client endpoint in a connection with a gateway.
type Tunnel struct {
	tunnelConn

	ctx    context.Context
	cancel context.CancelFunc
}

// Connect establishes a connection with a gateway. You can pass a zero initialized ClientConfig;
// the function will take care of filling in the default values.
func Connect(gatewayAddr string, config TunnelConfig) (*Tunnel, error) {
	// Create socket which will be used for communication.
	sock, err := NewClientSocket(gatewayAddr)
	if err != nil {
		return nil, err
	}

	// Prepare a context for the inbound server.
	ctx, cancel := context.WithCancel(context.Background())

	// Initialize the Client structure.
	client := &Tunnel{
		tunnelConn: tunnelConn{
			sock:    sock,
			config:  checkClientConfig(config),
			seqMu:   &sync.Mutex{},
			ack:     make(chan *proto.TunnelRes),
			inbound: make(chan cemi.CEMI),
		},
		ctx:    ctx,
		cancel: cancel,
	}

	// Prepare a context, so that the connection request cannot run forever.
	connectCtx, cancelConnect := context.WithTimeout(ctx, client.config.ResponseTimeout)
	defer cancelConnect()

	// Connect to the gateway.
	err = client.requestConn(connectCtx)
	if err != nil {
		sock.Close()
		return nil, err
	}

	go client.serve(client.ctx)

	return client, nil
}

// Close will terminate the connection.
func (client *Tunnel) Close() {
	client.requestDisc()
	client.cancel()
}

// Inbound retrieves the channel which transmits incoming data.
func (client *Tunnel) Inbound() <-chan cemi.CEMI {
	return client.inbound
}

// Send relays a tunnel request to the gateway with the given contents.
func (client *Tunnel) Send(data cemi.CEMI) error {
	// Prepare a context, so that we won't wait forever for a tunnel response.
	ctx, cancel := context.WithTimeout(client.ctx, client.config.ResponseTimeout)
	defer cancel()

	// Send the tunnel reqest.
	err := client.requestTunnel(ctx, data)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Close socket when Connection is done serving<commit_after>package knx

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/vapourismo/knx-go/knx/cemi"
	"github.com/vapourismo/knx-go/knx/proto"
)

// TunnelConfig allows you to configure the client's behavior.
type TunnelConfig struct {
	// ResendInterval is how long to wait for a response, until the request is resend. A interval
	// <= 0 can't be used. The default value will be used instead.
	ResendInterval time.Duration

	// HeartbeatDelay specifies the time which has to elapse without any incoming communication,
	// until a heartbeat is triggered. A delay <= 0 will result in the use of a default value.
	HeartbeatDelay time.Duration

	// ResponseTimeout specifies how long to wait for a response. A timeout <= 0 will not be
	// accepted. Instead, the default value will be used.
	ResponseTimeout time.Duration
}

// Default configuration elements
var (
	defaultResendInterval  = 500 * time.Millisecond
	defaultHeartbeatDelay  = 10 * time.Second
	defaultResponseTimeout = 10 * time.Second

	DefaultClientConfig = TunnelConfig{
		defaultResendInterval,
		defaultHeartbeatDelay,
		defaultResponseTimeout,
	}
)

// checkClientConfig makes sure that the configuration is actually usable.
func checkClientConfig(config TunnelConfig) TunnelConfig {
	if config.ResendInterval <= 0 {
		config.ResendInterval = defaultResendInterval
	}

	if config.HeartbeatDelay <= 0 {
		config.HeartbeatDelay = defaultHeartbeatDelay
	}

	if config.ResponseTimeout <= 0 {
		config.ResponseTimeout = defaultResponseTimeout
	}

	return config
}

// tunnelConn is a handle for a tunnel connection.
type tunnelConn struct {
	sock      Socket
	config    TunnelConfig
	channel   uint8
	control   proto.HostInfo
	seqMu     *sync.Mutex
	seqNumber uint8
	ack       chan *proto.TunnelRes
	inbound   chan cemi.CEMI
}

// requestConn repeatedly sends a connection request through the socket until the provided context gets
// canceled, or a response is received. A response that renders the gateway as busy will not stop
// requestConn.
func (conn *tunnelConn) requestConn(ctx context.Context) (err error) {
	req := &proto.ConnReq{}

	// Send the initial request.
	err = conn.sock.Send(req)
	if err != nil {
		return
	}

	// Create a resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	// Cycle until a request gets a response.
	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer triggered.
		case <-ticker.C:
			err = conn.sock.Send(req)
			if err != nil {
				return
			}

		// A message has been received or the channel has been closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel has been closed")
			}

			// We're only interested in connection responses.
			if res, ok := msg.(*proto.ConnRes); ok {
				switch res.Status {
				// Conection has been established.
				case proto.ConnResOk:
					conn.channel = res.Channel
					conn.control = res.Control
					return nil

				// The gateway is busy, but we don't stop yet.
				case proto.ConnResBusy:
					continue

				// Connection request has been denied.
				default:
					return res.Status
				}
			}
		}
	}
}

// requestConnState periodically sends a connection state request to the gateway until it has
// received a response or the context is done.
func (conn *tunnelConn) requestConnState(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
) (proto.ConnState, error) {
	req := &proto.ConnStateReq{Channel: conn.channel, Status: 0, Control: proto.HostInfo{}}

	// Send first connection state request
	err := conn.sock.Send(req)
	if err != nil {
		return proto.ConnStateInactive, err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return proto.ConnStateInactive, ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return proto.ConnStateInactive, err
			}

		// Received a connection state response.
		case res, open := <-heartbeat:
			if !open {
				return proto.ConnStateInactive, errors.New("Connection server has terminated")
			}

			return res, nil
		}
	}
}

// requestDisc sends a disconnect request to the gateway.
func (conn *tunnelConn) requestDisc() error {
	return conn.sock.Send(&proto.DiscReq{
		Channel: conn.channel,
		Status:  0,
		Control: conn.control,
	})
}

// requestTunnel sends a tunnel request to the gateway and waits for an appropriate acknowledgement.
func (conn *tunnelConn) requestTunnel(
	ctx context.Context,
	data cemi.CEMI,
) error {
	// Sequence numbers cannot be reused, therefore we must protect against that.
	conn.seqMu.Lock()
	defer conn.seqMu.Unlock()

	req := &proto.TunnelReq{
		Channel:   conn.channel,
		SeqNumber: conn.seqNumber,
		Payload:   data,
	}

	// Send initial request.
	err := conn.sock.Send(req)
	if err != nil {
		return err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return err
			}

		// Received a tunnel response.
		case res, open := <-conn.ack:
			if !open {
				return errors.New("Connection server has terminated")
			}

			// Ignore mismatching sequence numbers.
			if res.SeqNumber != conn.seqNumber {
				continue
			}

			// Gateway has received the request, therefore we can increase on our side.
			conn.seqNumber++

			// Check if the response confirms the tunnel request.
			if res.Status == 0 {
				return nil
			}

			return fmt.Errorf("Tunnel request has been rejected with status %#x", res.Status)
		}
	}
}

// performHeartbeat uses requestState to determine if the gateway is still alive.
func (conn *tunnelConn) performHeartbeat(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
	timeout chan<- struct{},
) {
	// Setup a child context which will time out with the given heartbeat timeout.
	childCtx, cancel := context.WithTimeout(ctx, conn.config.ResponseTimeout)
	defer cancel()

	// Request the connction state.
	state, err := conn.requestConnState(childCtx, heartbeat)
	if err != nil || state != proto.ConnStateNormal {
		if err != nil {
			log(conn, "conn", "Error while requesting connection state: %v", err)
		} else {
			log(conn, "conn", "Bad connection state: %v", state)
		}

		// Write to timeout as an indication that the heartbeat has failed.
		select {
		case <-ctx.Done():
		case timeout <- struct{}{}:
		}
	}
}

// handleDiscReq validates the request.
func (conn *tunnelConn) handleDiscReq(
	ctx context.Context,
	req *proto.DiscReq,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect request")
	}

	// We don't need to check if this errors or not. It doesn't matter.
	conn.sock.Send(&proto.DiscRes{Channel: req.Channel, Status: 0})

	return nil
}

// handleDiscRes validates the response.
func (conn *tunnelConn) handleDiscRes(
	ctx context.Context,
	res *proto.DiscRes,
) error {
	// Validate the response channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect response")
	}

	return nil
}

// handleTunnelReq validates the request, pushes the data to the client and acknowledges the
// request for the gateway.
func (conn *tunnelConn) handleTunnelReq(
	ctx context.Context,
	req *proto.TunnelReq,
	seqNumber *uint8,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in tunnel request")
	}

	// Is the sequence number what we expected?
	if req.SeqNumber == *seqNumber {
		*seqNumber++

		// Send tunnel data to the client.
		go func() {
			select {
			case <-ctx.Done():
			case conn.inbound <- req.Payload:
			}
		}()
	}

	// Send the acknowledgement.
	return conn.sock.Send(&proto.TunnelRes{
		Channel:   conn.channel,
		SeqNumber: req.SeqNumber,
		Status:    0,
	})
}

// handleTunnelRes validates the response and relays it to a sender that is awaiting an
// acknowledgement.
func (conn *tunnelConn) handleTunnelRes(
	ctx context.Context,
	res *proto.TunnelRes,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send to client.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case conn.ack <- res:
		}
	}()

	return nil
}

// handleConnStateRes validates the response and sends it to the heartbeat routine, if
// there is a waiting one.
func (conn *tunnelConn) handleConnStateRes(
	ctx context.Context,
	res *proto.ConnStateRes,
	heartbeat chan<- proto.ConnState,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send connection state to the heartbeat goroutine.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case heartbeat <- res.Status:
		}
	}()

	return nil
}

// serve processes incoming packets. It will return with nil when a disconnect request or
// response has been received.
func (conn *tunnelConn) serve(
	ctx context.Context,
) error {
	defer close(conn.ack)
	defer close(conn.inbound)

	heartbeat := make(chan proto.ConnState)
	defer close(heartbeat)

	timeout := make(chan struct{})

	var seqNumber uint8

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Heartbeat worker signals a result.
		case <-timeout:
			return errors.New("Heartbeat did not succeed")

		// There were no incoming packets for some time.
		case <-time.After(conn.config.HeartbeatDelay):
			go conn.performHeartbeat(ctx, heartbeat, timeout)

		// A message has been received or the channel is closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel is closed")
			}

			// Determine what to do with the message.
			switch msg := msg.(type) {
			case *proto.DiscReq:
				err := conn.handleDiscReq(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect request %v: %v", msg, err)

			case *proto.DiscRes:
				err := conn.handleDiscRes(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect response %v: %v", msg, err)

			case *proto.TunnelReq:
				err := conn.handleTunnelReq(ctx, msg, &seqNumber)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel request %v: %v", msg, err)
				}

			case *proto.TunnelRes:
				err := conn.handleTunnelRes(ctx, msg)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel response %v: %v", msg, err)
				}

			case *proto.ConnStateRes:
				err := conn.handleConnStateRes(ctx, msg, heartbeat)
				if err != nil {
					log(
						conn, "conn",
						"Error while handling connection state response: %v", err,
					)
				}
			}
		}
	}
}

// Tunnel represents the client endpoint in a connection with a gateway.
type Tunnel struct {
	tunnelConn

	ctx    context.Context
	cancel context.CancelFunc
}

// Connect establishes a connection with a gateway. You can pass a zero initialized ClientConfig;
// the function will take care of filling in the default values.
func Connect(gatewayAddr string, config TunnelConfig) (*Tunnel, error) {
	// Create socket which will be used for communication.
	sock, err := NewClientSocket(gatewayAddr)
	if err != nil {
		return nil, err
	}

	// Prepare a context for the inbound server.
	ctx, cancel := context.WithCancel(context.Background())

	// Initialize the Client structure.
	client := &Tunnel{
		tunnelConn: tunnelConn{
			sock:    sock,
			config:  checkClientConfig(config),
			seqMu:   &sync.Mutex{},
			ack:     make(chan *proto.TunnelRes),
			inbound: make(chan cemi.CEMI),
		},
		ctx:    ctx,
		cancel: cancel,
	}

	// Prepare a context, so that the connection request cannot run forever.
	connectCtx, cancelConnect := context.WithTimeout(ctx, client.config.ResponseTimeout)
	defer cancelConnect()

	// Connect to the gateway.
	err = client.requestConn(connectCtx)
	if err != nil {
		sock.Close()
		return nil, err
	}

	go func() {
		client.serve(client.ctx)
		sock.Close()
	}()

	return client, nil
}

// Close will terminate the connection.
func (client *Tunnel) Close() {
	client.requestDisc()
	client.cancel()
}

// Inbound retrieves the channel which transmits incoming data.
func (client *Tunnel) Inbound() <-chan cemi.CEMI {
	return client.inbound
}

// Send relays a tunnel request to the gateway with the given contents.
func (client *Tunnel) Send(data cemi.CEMI) error {
	// Prepare a context, so that we won't wait forever for a tunnel response.
	ctx, cancel := context.WithTimeout(client.ctx, client.config.ResponseTimeout)
	defer cancel()

	// Send the tunnel reqest.
	err := client.requestTunnel(ctx, data)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel

import (
	"errors"
	"fmt"
	"io"
	"time"

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

var errInboundRequestAlreadyActive = errors.New("inbound request is already active; possible duplicate client id")

// handleCallReq handles an incoming call request, registering a message
// exchange to receive further fragments for that call, and dispatching it in
// another goroutine
func (c *Connection) handleCallReq(frame *Frame) bool {
	switch state := c.readState(); state {
	case connectionActive:
		break
	case connectionStartClose, connectionInboundClosed, connectionClosed:
		c.SendSystemError(frame.Header.ID, nil, ErrChannelClosed)
		return true
	case connectionWaitingToRecvInitReq, connectionWaitingToSendInitReq, connectionWaitingToRecvInitRes:
		c.SendSystemError(frame.Header.ID, nil, NewSystemError(ErrCodeDeclined, "connection not ready"))
		return true
	default:
		panic(fmt.Errorf("unknown connection state for call req: %v", state))
	}

	callReq := new(callReq)
	initialFragment, err := parseInboundFragment(c.framePool, frame, callReq)
	if err != nil {
		// TODO(mmihic): Probably want to treat this as a protocol error
		c.log.Errorf("could not decode %s: %v", frame.Header, err)
		return true
	}

	c.log.Debugf("span=%s", callReq.Tracing)
	call := new(InboundCall)
	call.conn = c
	ctx, cancel := newIncomingContext(call, callReq.TimeToLive, &callReq.Tracing)

	mex, err := c.inbound.newExchange(ctx, c.framePool, callReq.messageType(), frame.Header.ID, mexChannelBufferSize)
	if err != nil {
		if err == errDuplicateMex {
			err = errInboundRequestAlreadyActive
		}
		c.log.Errorf("could not register exchange for %s", frame.Header)
		c.SendSystemError(frame.Header.ID, nil, err)
		return true
	}

	// Close may have been called between the time we checked the state and us creating the exchange.
	if c.readState() != connectionActive {
		mex.shutdown()
		return true
	}

	response := new(InboundCallResponse)
	response.Annotations = Annotations{
		reporter: c.traceReporter,
		span:     callReq.Tracing,
		endpoint: TargetEndpoint{
			HostPort:    c.localPeerInfo.HostPort,
			ServiceName: callReq.Service,
		},
		timeNow: c.timeNow,
		binaryAnnotations: []BinaryAnnotation{
			{Key: "cn", Value: callReq.Headers[CallerName]},
			{Key: "as", Value: callReq.Headers[ArgScheme]},
		},
	}
	response.AddAnnotation(AnnotationKeyServerReceive)
	response.mex = mex
	response.conn = c
	response.cancel = cancel
	response.span = callReq.Tracing
	response.log = c.log.WithFields(LogField{"In-Response", callReq.ID()})
	response.contents = newFragmentingWriter(response.log, response, initialFragment.checksumType.New())
	response.headers = transportHeaders{}
	response.messageForFragment = func(initial bool) message {
		if initial {
			callRes := new(callRes)
			callRes.Headers = response.headers
			callRes.ResponseCode = responseOK
			if response.applicationError {
				callRes.ResponseCode = responseApplicationError
			}
			return callRes
		}

		return new(callResContinue)
	}

	call.mex = mex
	call.initialFragment = initialFragment
	call.serviceName = string(callReq.Service)
	call.headers = callReq.Headers
	call.span = callReq.Tracing
	call.response = response
	call.log = c.log.WithFields(LogField{"In-Call", callReq.ID()})
	call.messageForFragment = func(initial bool) message { return new(callReqContinue) }
	call.contents = newFragmentingReader(call.log, call)
	call.statsReporter = c.statsReporter
	call.createStatsTags(c.commonStatsTags)

	response.statsReporter = c.statsReporter
	response.commonStatsTags = call.commonStatsTags

	setResponseHeaders(call.headers, response.headers)
	go c.dispatchInbound(c.connID, callReq.ID(), call)
	return false
}

// handleCallReqContinue handles the continuation of a call request, forwarding
// it to the request channel for that request, where it can be pulled during
// defragmentation
func (c *Connection) handleCallReqContinue(frame *Frame) bool {
	if err := c.inbound.forwardPeerFrame(frame); err != nil {
		// If forward fails, it's due to a timeout.
		c.inbound.timeoutExchange(frame.Header.ID)
		return true
	}
	return false
}

// createStatsTags creates the common stats tags, if they are not already created.
func (call *InboundCall) createStatsTags(connectionTags map[string]string) {
	call.commonStatsTags = map[string]string{
		"calling-service": call.CallerName(),
	}
	for k, v := range connectionTags {
		call.commonStatsTags[k] = v
	}
}

// dispatchInbound ispatches an inbound call to the appropriate handler
func (c *Connection) dispatchInbound(_ uint32, _ uint32, call *InboundCall) {
	c.log.Debugf("Received incoming call for %s from %s", call.ServiceName(), c.remotePeerInfo)

	if err := call.readOperation(); err != nil {
		c.log.Errorf("Could not read operation from %s: %v", c.remotePeerInfo, err)
		return
	}

	call.commonStatsTags["endpoint"] = string(call.operation)
	call.statsReporter.IncCounter("inbound.calls.recvd", call.commonStatsTags, 1)
	call.response.calledAt = c.timeNow()
	call.response.SetOperation(string(call.operation))

	// NB(mmihic): Don't cast operation name to string here - this will
	// create a copy of the byte array, where as aliasing to string in the
	// map look up can be optimized by the compiler to avoid the copy.  See
	// https://github.com/golang/go/issues/3512
	h := c.handlers.find(call.ServiceName(), call.Operation())
	if h == nil {
		// CHeck the subchannel map to see if we find one there
		c.log.Debugf("Checking the subchannel's handlers for %s:%s", call.ServiceName(), call.Operation())
		h = c.subChannels.find(call.ServiceName(), call.Operation())
	}
	if h == nil {
		c.log.Errorf("Could not find handler for %s:%s", call.ServiceName(), call.Operation())
		call.Response().SendSystemError(
			NewSystemError(ErrCodeBadRequest, "no handler for service %q and operation %q", call.ServiceName(), call.Operation()))
		return
	}

	// TODO(prashant): This is an expensive way to check for cancellation. Use a heap for timeouts.
	go func() {
		if <-call.mex.ctx.Done(); call.mex.ctx.Err() == context.DeadlineExceeded {
			call.mex.inboundTimeout()
		}
	}()

	c.log.Debugf("Dispatching %s:%s from %s", call.ServiceName(), call.Operation(), c.remotePeerInfo)
	h.Handle(call.mex.ctx, call)
}

// An InboundCall is an incoming call from a peer
type InboundCall struct {
	reqResReader

	conn            *Connection
	response        *InboundCallResponse
	serviceName     string
	operation       []byte
	headers         transportHeaders
	span            Span
	statsReporter   StatsReporter
	commonStatsTags map[string]string
}

// ServiceName returns the name of the service being called
func (call *InboundCall) ServiceName() string {
	return call.serviceName
}

// Operation returns the operation being called
func (call *InboundCall) Operation() []byte {
	return call.operation
}

// Format the format of the request from the ArgScheme transport header.
func (call *InboundCall) Format() Format {
	return Format(call.headers[ArgScheme])
}

// CallerName returns the caller name from the CallerName transport header.
func (call *InboundCall) CallerName() string {
	return call.headers[CallerName]
}

// ShardKey returns the shard key from the ShardKey transport header.
func (call *InboundCall) ShardKey() string {
	return call.headers[ShardKey]
}

// Reads the entire operation name (arg1) from the request stream.
func (call *InboundCall) readOperation() error {
	var arg1 []byte
	if err := NewArgReader(call.arg1Reader()).Read(&arg1); err != nil {
		return call.failed(err)
	}

	call.operation = arg1
	return nil
}

// Arg2Reader returns an io.ReadCloser to read the second argument.
// The ReadCloser must be closed once the argument has been read.
func (call *InboundCall) Arg2Reader() (io.ReadCloser, error) {
	return call.arg2Reader()
}

// Arg3Reader returns an io.ReadCloser to read the last argument.
// The ReadCloser must be closed once the argument has been read.
func (call *InboundCall) Arg3Reader() (io.ReadCloser, error) {
	return call.arg3Reader()
}

// Response provides access to the InboundCallResponse object which can be used
// to write back to the calling peer
func (call *InboundCall) Response() *InboundCallResponse {
	return call.response
}

func (call *InboundCall) doneReading(unexpected error) {}

// An InboundCallResponse is used to send the response back to the calling peer
type InboundCallResponse struct {
	reqResWriter
	Annotations

	cancel context.CancelFunc
	// calledAt is the time the inbound call was routed to the application.
	calledAt         time.Time
	applicationError bool
	systemError      bool
	headers          transportHeaders
	span             Span
	statsReporter    StatsReporter
	commonStatsTags  map[string]string
}

// SendSystemError returns a system error response to the peer.  The call is considered
// complete after this method is called, and no further data can be written.
func (response *InboundCallResponse) SendSystemError(err error) error {
	// Fail all future attempts to read fragments
	response.state = reqResWriterComplete
	response.systemError = true
	response.doneSending()
	return response.conn.SendSystemError(response.mex.msgID, CurrentSpan(response.mex.ctx), err)
}

// SetApplicationError marks the response as being an application error.  This method can
// only be called before any arguments have been sent to the calling peer.
func (response *InboundCallResponse) SetApplicationError() error {
	if response.state > reqResWriterPreArg2 {
		return response.failed(errReqResWriterStateMismatch{
			state:         response.state,
			expectedState: reqResWriterPreArg2,
		})
	}
	response.applicationError = true
	return nil
}

// Arg2Writer returns a WriteCloser that can be used to write the second argument.
// The returned writer must be closed once the write is complete.
func (response *InboundCallResponse) Arg2Writer() (ArgWriter, error) {
	if err := NewArgWriter(response.arg1Writer()).Write(nil); err != nil {
		return nil, err
	}
	return response.arg2Writer()
}

// Arg3Writer returns a WriteCloser that can be used to write the last argument.
// The returned writer must be closed once the write is complete.
func (response *InboundCallResponse) Arg3Writer() (ArgWriter, error) {
	return response.arg3Writer()
}

// doneSending shuts down the message exchange for this call.
// For incoming calls, the last message is sending the call response.
func (response *InboundCallResponse) doneSending() {
	// TODO(prashant): Move this to when the message is actually being sent.
	response.AddAnnotation(AnnotationKeyServerSend)
	response.Report()

	latency := response.GetTime().Sub(response.calledAt)
	response.statsReporter.RecordTimer("inbound.calls.latency", response.commonStatsTags, latency)

	if response.systemError {
		// TODO(prashant): Report the error code type as per metrics doc and enable.
		// response.statsReporter.IncCounter("inbound.calls.system-errors", response.commonStatsTags, 1)
	} else if response.applicationError {
		response.statsReporter.IncCounter("inbound.calls.app-errors", response.commonStatsTags, 1)
	} else {
		response.statsReporter.IncCounter("inbound.calls.success", response.commonStatsTags, 1)
	}

	// Cancel the context since the response is complete.
	response.cancel()

	// The message exchange is still open if there are no errors, call shutdown.
	if response.err == nil {
		response.mex.shutdown()
	}
}
<commit_msg>Add OperationString to avoid duplicate conversions<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel

import (
	"errors"
	"fmt"
	"io"
	"time"

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

var errInboundRequestAlreadyActive = errors.New("inbound request is already active; possible duplicate client id")

// handleCallReq handles an incoming call request, registering a message
// exchange to receive further fragments for that call, and dispatching it in
// another goroutine
func (c *Connection) handleCallReq(frame *Frame) bool {
	switch state := c.readState(); state {
	case connectionActive:
		break
	case connectionStartClose, connectionInboundClosed, connectionClosed:
		c.SendSystemError(frame.Header.ID, nil, ErrChannelClosed)
		return true
	case connectionWaitingToRecvInitReq, connectionWaitingToSendInitReq, connectionWaitingToRecvInitRes:
		c.SendSystemError(frame.Header.ID, nil, NewSystemError(ErrCodeDeclined, "connection not ready"))
		return true
	default:
		panic(fmt.Errorf("unknown connection state for call req: %v", state))
	}

	callReq := new(callReq)
	initialFragment, err := parseInboundFragment(c.framePool, frame, callReq)
	if err != nil {
		// TODO(mmihic): Probably want to treat this as a protocol error
		c.log.Errorf("could not decode %s: %v", frame.Header, err)
		return true
	}

	c.log.Debugf("span=%s", callReq.Tracing)
	call := new(InboundCall)
	call.conn = c
	ctx, cancel := newIncomingContext(call, callReq.TimeToLive, &callReq.Tracing)

	mex, err := c.inbound.newExchange(ctx, c.framePool, callReq.messageType(), frame.Header.ID, mexChannelBufferSize)
	if err != nil {
		if err == errDuplicateMex {
			err = errInboundRequestAlreadyActive
		}
		c.log.Errorf("could not register exchange for %s", frame.Header)
		c.SendSystemError(frame.Header.ID, nil, err)
		return true
	}

	// Close may have been called between the time we checked the state and us creating the exchange.
	if c.readState() != connectionActive {
		mex.shutdown()
		return true
	}

	response := new(InboundCallResponse)
	response.Annotations = Annotations{
		reporter: c.traceReporter,
		span:     callReq.Tracing,
		endpoint: TargetEndpoint{
			HostPort:    c.localPeerInfo.HostPort,
			ServiceName: callReq.Service,
		},
		timeNow: c.timeNow,
		binaryAnnotations: []BinaryAnnotation{
			{Key: "cn", Value: callReq.Headers[CallerName]},
			{Key: "as", Value: callReq.Headers[ArgScheme]},
		},
	}
	response.AddAnnotation(AnnotationKeyServerReceive)
	response.mex = mex
	response.conn = c
	response.cancel = cancel
	response.span = callReq.Tracing
	response.log = c.log.WithFields(LogField{"In-Response", callReq.ID()})
	response.contents = newFragmentingWriter(response.log, response, initialFragment.checksumType.New())
	response.headers = transportHeaders{}
	response.messageForFragment = func(initial bool) message {
		if initial {
			callRes := new(callRes)
			callRes.Headers = response.headers
			callRes.ResponseCode = responseOK
			if response.applicationError {
				callRes.ResponseCode = responseApplicationError
			}
			return callRes
		}

		return new(callResContinue)
	}

	call.mex = mex
	call.initialFragment = initialFragment
	call.serviceName = string(callReq.Service)
	call.headers = callReq.Headers
	call.span = callReq.Tracing
	call.response = response
	call.log = c.log.WithFields(LogField{"In-Call", callReq.ID()})
	call.messageForFragment = func(initial bool) message { return new(callReqContinue) }
	call.contents = newFragmentingReader(call.log, call)
	call.statsReporter = c.statsReporter
	call.createStatsTags(c.commonStatsTags)

	response.statsReporter = c.statsReporter
	response.commonStatsTags = call.commonStatsTags

	setResponseHeaders(call.headers, response.headers)
	go c.dispatchInbound(c.connID, callReq.ID(), call)
	return false
}

// handleCallReqContinue handles the continuation of a call request, forwarding
// it to the request channel for that request, where it can be pulled during
// defragmentation
func (c *Connection) handleCallReqContinue(frame *Frame) bool {
	if err := c.inbound.forwardPeerFrame(frame); err != nil {
		// If forward fails, it's due to a timeout.
		c.inbound.timeoutExchange(frame.Header.ID)
		return true
	}
	return false
}

// createStatsTags creates the common stats tags, if they are not already created.
func (call *InboundCall) createStatsTags(connectionTags map[string]string) {
	call.commonStatsTags = map[string]string{
		"calling-service": call.CallerName(),
	}
	for k, v := range connectionTags {
		call.commonStatsTags[k] = v
	}
}

// dispatchInbound ispatches an inbound call to the appropriate handler
func (c *Connection) dispatchInbound(_ uint32, _ uint32, call *InboundCall) {
	c.log.Debugf("Received incoming call for %s from %s", call.ServiceName(), c.remotePeerInfo)

	if err := call.readOperation(); err != nil {
		c.log.Errorf("Could not read operation from %s: %v", c.remotePeerInfo, err)
		return
	}

	call.commonStatsTags["endpoint"] = string(call.operation)
	call.statsReporter.IncCounter("inbound.calls.recvd", call.commonStatsTags, 1)
	call.response.calledAt = c.timeNow()
	call.response.SetOperation(string(call.operation))

	// NB(mmihic): Don't cast operation name to string here - this will
	// create a copy of the byte array, where as aliasing to string in the
	// map look up can be optimized by the compiler to avoid the copy.  See
	// https://github.com/golang/go/issues/3512
	h := c.handlers.find(call.ServiceName(), call.Operation())
	if h == nil {
		// CHeck the subchannel map to see if we find one there
		c.log.Debugf("Checking the subchannel's handlers for %s:%s", call.ServiceName(), call.Operation())
		h = c.subChannels.find(call.ServiceName(), call.Operation())
	}
	if h == nil {
		c.log.Errorf("Could not find handler for %s:%s", call.ServiceName(), call.Operation())
		call.Response().SendSystemError(
			NewSystemError(ErrCodeBadRequest, "no handler for service %q and operation %q", call.ServiceName(), call.Operation()))
		return
	}

	// TODO(prashant): This is an expensive way to check for cancellation. Use a heap for timeouts.
	go func() {
		if <-call.mex.ctx.Done(); call.mex.ctx.Err() == context.DeadlineExceeded {
			call.mex.inboundTimeout()
		}
	}()

	c.log.Debugf("Dispatching %s:%s from %s", call.ServiceName(), call.Operation(), c.remotePeerInfo)
	h.Handle(call.mex.ctx, call)
}

// An InboundCall is an incoming call from a peer
type InboundCall struct {
	reqResReader

	conn            *Connection
	response        *InboundCallResponse
	serviceName     string
	operation       []byte
	operationString string
	headers         transportHeaders
	span            Span
	statsReporter   StatsReporter
	commonStatsTags map[string]string
}

// ServiceName returns the name of the service being called
func (call *InboundCall) ServiceName() string {
	return call.serviceName
}

// Operation returns the operation being called
func (call *InboundCall) Operation() []byte {
	return call.operation
}

// OperationString returns the operation being called as a string.
func (call *InboundCall) OperationString() string {
	return call.operationString
}

// Format the format of the request from the ArgScheme transport header.
func (call *InboundCall) Format() Format {
	return Format(call.headers[ArgScheme])
}

// CallerName returns the caller name from the CallerName transport header.
func (call *InboundCall) CallerName() string {
	return call.headers[CallerName]
}

// ShardKey returns the shard key from the ShardKey transport header.
func (call *InboundCall) ShardKey() string {
	return call.headers[ShardKey]
}

// Reads the entire operation name (arg1) from the request stream.
func (call *InboundCall) readOperation() error {
	var arg1 []byte
	if err := NewArgReader(call.arg1Reader()).Read(&arg1); err != nil {
		return call.failed(err)
	}

	call.operation = arg1
	call.operationString = string(arg1)
	return nil
}

// Arg2Reader returns an io.ReadCloser to read the second argument.
// The ReadCloser must be closed once the argument has been read.
func (call *InboundCall) Arg2Reader() (io.ReadCloser, error) {
	return call.arg2Reader()
}

// Arg3Reader returns an io.ReadCloser to read the last argument.
// The ReadCloser must be closed once the argument has been read.
func (call *InboundCall) Arg3Reader() (io.ReadCloser, error) {
	return call.arg3Reader()
}

// Response provides access to the InboundCallResponse object which can be used
// to write back to the calling peer
func (call *InboundCall) Response() *InboundCallResponse {
	return call.response
}

func (call *InboundCall) doneReading(unexpected error) {}

// An InboundCallResponse is used to send the response back to the calling peer
type InboundCallResponse struct {
	reqResWriter
	Annotations

	cancel context.CancelFunc
	// calledAt is the time the inbound call was routed to the application.
	calledAt         time.Time
	applicationError bool
	systemError      bool
	headers          transportHeaders
	span             Span
	statsReporter    StatsReporter
	commonStatsTags  map[string]string
}

// SendSystemError returns a system error response to the peer.  The call is considered
// complete after this method is called, and no further data can be written.
func (response *InboundCallResponse) SendSystemError(err error) error {
	// Fail all future attempts to read fragments
	response.state = reqResWriterComplete
	response.systemError = true
	response.doneSending()
	return response.conn.SendSystemError(response.mex.msgID, CurrentSpan(response.mex.ctx), err)
}

// SetApplicationError marks the response as being an application error.  This method can
// only be called before any arguments have been sent to the calling peer.
func (response *InboundCallResponse) SetApplicationError() error {
	if response.state > reqResWriterPreArg2 {
		return response.failed(errReqResWriterStateMismatch{
			state:         response.state,
			expectedState: reqResWriterPreArg2,
		})
	}
	response.applicationError = true
	return nil
}

// Arg2Writer returns a WriteCloser that can be used to write the second argument.
// The returned writer must be closed once the write is complete.
func (response *InboundCallResponse) Arg2Writer() (ArgWriter, error) {
	if err := NewArgWriter(response.arg1Writer()).Write(nil); err != nil {
		return nil, err
	}
	return response.arg2Writer()
}

// Arg3Writer returns a WriteCloser that can be used to write the last argument.
// The returned writer must be closed once the write is complete.
func (response *InboundCallResponse) Arg3Writer() (ArgWriter, error) {
	return response.arg3Writer()
}

// doneSending shuts down the message exchange for this call.
// For incoming calls, the last message is sending the call response.
func (response *InboundCallResponse) doneSending() {
	// TODO(prashant): Move this to when the message is actually being sent.
	response.AddAnnotation(AnnotationKeyServerSend)
	response.Report()

	latency := response.GetTime().Sub(response.calledAt)
	response.statsReporter.RecordTimer("inbound.calls.latency", response.commonStatsTags, latency)

	if response.systemError {
		// TODO(prashant): Report the error code type as per metrics doc and enable.
		// response.statsReporter.IncCounter("inbound.calls.system-errors", response.commonStatsTags, 1)
	} else if response.applicationError {
		response.statsReporter.IncCounter("inbound.calls.app-errors", response.commonStatsTags, 1)
	} else {
		response.statsReporter.IncCounter("inbound.calls.success", response.commonStatsTags, 1)
	}

	// Cancel the context since the response is complete.
	response.cancel()

	// The message exchange is still open if there are no errors, call shutdown.
	if response.err == nil {
		response.mex.shutdown()
	}
}
<|endoftext|>