<commit_before>package middleware

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"

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

type (
	// JWTConfig defines the config for JWT middleware.
	JWTConfig struct {
		// Skipper defines a function to skip middleware.
		Skipper Skipper

		// Signing key to validate token.
		// Required.
		SigningKey interface{}

		// Signing method, used to check token signing method.
		// Optional. Default value HS256.
		SigningMethod string

		// Context key to store user information from the token into context.
		// Optional. Default value "user".
		ContextKey string

		// Claims are extendable claims data defining token content.
		// Optional. Default value jwt.MapClaims
		Claims jwt.Claims

		// TokenLookup is a string in the form of "<source>:<name>" that is used
		// to extract token from the request.
		// Optional. Default value "header:Authorization".
		// Possible values:
		// - "header:<name>"
		// - "query:<name>"
		// - "cookie:<name>"
		TokenLookup string

		// AuthScheme to be used in the Authorization header.
		// Optional. Default value "Bearer".
		AuthScheme string

		keyFunc jwt.Keyfunc
	}

	jwtExtractor func(echo.Context) (string, error)
)

// Algorithms
const (
	AlgorithmHS256 = "HS256"
)

// Errors
var (
	ErrJWTInvalid = echo.NewHTTPError(http.StatusBadRequest, "Missing or invalid jwt")
)

var (
	// DefaultJWTConfig is the default JWT auth middleware config.
	DefaultJWTConfig = JWTConfig{
		Skipper:       DefaultSkipper,
		SigningMethod: AlgorithmHS256,
		ContextKey:    "user",
		TokenLookup:   "header:" + echo.HeaderAuthorization,
		AuthScheme:    "Bearer",
		Claims:        jwt.MapClaims{},
	}
)

// JWT returns a JSON Web Token (JWT) auth middleware.
//
// For valid token, it sets the user in context and calls next handler.
// For invalid token, it returns "401 - Unauthorized" error.
// For missing token, it returns "400 - Bad Request" error.
//
// See: https://jwt.io/introduction
// See `JWTConfig.TokenLookup`
func JWT(key interface{}) echo.MiddlewareFunc {
	c := DefaultJWTConfig
	c.SigningKey = key
	return JWTWithConfig(c)
}

// JWTWithConfig returns a JWT auth middleware with config.
// See: `JWT()`.
func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
	// Defaults
	if config.Skipper == nil {
		config.Skipper = DefaultJWTConfig.Skipper
	}
	if config.SigningKey == nil {
		panic("echo: jwt middleware requires signing key")
	}
	if config.SigningMethod == "" {
		config.SigningMethod = DefaultJWTConfig.SigningMethod
	}
	if config.ContextKey == "" {
		config.ContextKey = DefaultJWTConfig.ContextKey
	}
	if config.Claims == nil {
		config.Claims = DefaultJWTConfig.Claims
	}
	if config.TokenLookup == "" {
		config.TokenLookup = DefaultJWTConfig.TokenLookup
	}
	if config.AuthScheme == "" {
		config.AuthScheme = DefaultJWTConfig.AuthScheme
	}
	config.keyFunc = func(t *jwt.Token) (interface{}, error) {
		// Check the signing method
		if t.Method.Alg() != config.SigningMethod {
			return nil, fmt.Errorf("Unexpected jwt signing method=%v", t.Header["alg"])
		}
		return config.SigningKey, nil
	}

	// Initialize
	parts := strings.Split(config.TokenLookup, ":")
	extractor := jwtFromHeader(parts[1], config.AuthScheme)
	switch parts[0] {
	case "query":
		extractor = jwtFromQuery(parts[1])
	case "cookie":
		extractor = jwtFromCookie(parts[1])
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			if config.Skipper(c) {
				return next(c)
			}

			auth, err := extractor(c)
			if err != nil {
				return err
			}
			token := new(jwt.Token)
			// Issue #647, #656
			if _, ok := config.Claims.(jwt.MapClaims); ok {
				token, err = jwt.Parse(auth, config.keyFunc)
			} else {
				t := reflect.ValueOf(config.Claims).Type().Elem()
				claims := reflect.New(t).Interface().(jwt.Claims)
				token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc)
			}
			if err == nil && token.Valid {
				// Store user information from token into context.
				c.Set(config.ContextKey, token)
				return next(c)
			}
			he := echo.NewHTTPError(http.StatusUnauthorized, "Invalid or expired jwt")
			he.Inner = err
			return he
		}
	}
}

// jwtFromHeader returns a `jwtExtractor` that extracts token from the request header.
func jwtFromHeader(header string, authScheme string) jwtExtractor {
	return func(c echo.Context) (string, error) {
		auth := c.Request().Header.Get(header)
		l := len(authScheme)
		if len(auth) > l+1 && auth[:l] == authScheme {
			return auth[l+1:], nil
		}
		return "", ErrJWTInvalid
	}
}

// jwtFromQuery returns a `jwtExtractor` that extracts token from the query string.
func jwtFromQuery(param string) jwtExtractor {
	return func(c echo.Context) (string, error) {
		token := c.QueryParam(param)
		if token == "" {
			return "", ErrJWTInvalid
		}
		return token, nil
	}
}

// jwtFromCookie returns a `jwtExtractor` that extracts token from the named cookie.
func jwtFromCookie(name string) jwtExtractor {
	return func(c echo.Context) (string, error) {
		cookie, err := c.Cookie(name)
		if err != nil {
			return "", ErrJWTInvalid
		}
		return cookie.Value, nil
	}
}
<commit_msg>Exposed JWT error<commit_after>package middleware

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"

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

type (
	// JWTConfig defines the config for JWT middleware.
	JWTConfig struct {
		// Skipper defines a function to skip middleware.
		Skipper Skipper

		// Signing key to validate token.
		// Required.
		SigningKey interface{}

		// Signing method, used to check token signing method.
		// Optional. Default value HS256.
		SigningMethod string

		// Context key to store user information from the token into context.
		// Optional. Default value "user".
		ContextKey string

		// Claims are extendable claims data defining token content.
		// Optional. Default value jwt.MapClaims
		Claims jwt.Claims

		// TokenLookup is a string in the form of "<source>:<name>" that is used
		// to extract token from the request.
		// Optional. Default value "header:Authorization".
		// Possible values:
		// - "header:<name>"
		// - "query:<name>"
		// - "cookie:<name>"
		TokenLookup string

		// AuthScheme to be used in the Authorization header.
		// Optional. Default value "Bearer".
		AuthScheme string

		keyFunc jwt.Keyfunc
	}

	jwtExtractor func(echo.Context) (string, error)
)

// Algorithms
const (
	AlgorithmHS256 = "HS256"
)

// Errors
var (
	ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "Missing or malformed jwt")
	ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "Invalid or expired jwt")
)

var (
	// DefaultJWTConfig is the default JWT auth middleware config.
	DefaultJWTConfig = JWTConfig{
		Skipper:       DefaultSkipper,
		SigningMethod: AlgorithmHS256,
		ContextKey:    "user",
		TokenLookup:   "header:" + echo.HeaderAuthorization,
		AuthScheme:    "Bearer",
		Claims:        jwt.MapClaims{},
	}
)

// JWT returns a JSON Web Token (JWT) auth middleware.
//
// For valid token, it sets the user in context and calls next handler.
// For invalid token, it returns "401 - Unauthorized" error.
// For missing token, it returns "400 - Bad Request" error.
//
// See: https://jwt.io/introduction
// See `JWTConfig.TokenLookup`
func JWT(key interface{}) echo.MiddlewareFunc {
	c := DefaultJWTConfig
	c.SigningKey = key
	return JWTWithConfig(c)
}

// JWTWithConfig returns a JWT auth middleware with config.
// See: `JWT()`.
func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
	// Defaults
	if config.Skipper == nil {
		config.Skipper = DefaultJWTConfig.Skipper
	}
	if config.SigningKey == nil {
		panic("echo: jwt middleware requires signing key")
	}
	if config.SigningMethod == "" {
		config.SigningMethod = DefaultJWTConfig.SigningMethod
	}
	if config.ContextKey == "" {
		config.ContextKey = DefaultJWTConfig.ContextKey
	}
	if config.Claims == nil {
		config.Claims = DefaultJWTConfig.Claims
	}
	if config.TokenLookup == "" {
		config.TokenLookup = DefaultJWTConfig.TokenLookup
	}
	if config.AuthScheme == "" {
		config.AuthScheme = DefaultJWTConfig.AuthScheme
	}
	config.keyFunc = func(t *jwt.Token) (interface{}, error) {
		// Check the signing method
		if t.Method.Alg() != config.SigningMethod {
			return nil, fmt.Errorf("Unexpected jwt signing method=%v", t.Header["alg"])
		}
		return config.SigningKey, nil
	}

	// Initialize
	parts := strings.Split(config.TokenLookup, ":")
	extractor := jwtFromHeader(parts[1], config.AuthScheme)
	switch parts[0] {
	case "query":
		extractor = jwtFromQuery(parts[1])
	case "cookie":
		extractor = jwtFromCookie(parts[1])
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			if config.Skipper(c) {
				return next(c)
			}

			auth, err := extractor(c)
			if err != nil {
				return err
			}
			token := new(jwt.Token)
			// Issue #647, #656
			if _, ok := config.Claims.(jwt.MapClaims); ok {
				token, err = jwt.Parse(auth, config.keyFunc)
			} else {
				t := reflect.ValueOf(config.Claims).Type().Elem()
				claims := reflect.New(t).Interface().(jwt.Claims)
				token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc)
			}
			if err == nil && token.Valid {
				// Store user information from token into context.
				c.Set(config.ContextKey, token)
				return next(c)
			}
			return &echo.HTTPError{
				Code:    ErrJWTInvalid.Code,
				Message: ErrJWTInvalid.Message,
				Inner:   err,
			}
		}
	}
}

// jwtFromHeader returns a `jwtExtractor` that extracts token from the request header.
func jwtFromHeader(header string, authScheme string) jwtExtractor {
	return func(c echo.Context) (string, error) {
		auth := c.Request().Header.Get(header)
		l := len(authScheme)
		if len(auth) > l+1 && auth[:l] == authScheme {
			return auth[l+1:], nil
		}
		return "", ErrJWTMissing
	}
}

// jwtFromQuery returns a `jwtExtractor` that extracts token from the query string.
func jwtFromQuery(param string) jwtExtractor {
	return func(c echo.Context) (string, error) {
		token := c.QueryParam(param)
		if token == "" {
			return "", ErrJWTMissing
		}
		return token, nil
	}
}

// jwtFromCookie returns a `jwtExtractor` that extracts token from the named cookie.
func jwtFromCookie(name string) jwtExtractor {
	return func(c echo.Context) (string, error) {
		cookie, err := c.Cookie(name)
		if err != nil {
			return "", ErrJWTMissing
		}
		return cookie.Value, nil
	}
}
<|endoftext|>


<commit_before>package misc

// UnionFind disjoint set structure
type UnionFind struct {
	count  int
	parent []int
	rank   []int
}

// NewUnionFind returns pointer to UnionFind
func NewUnionFind(size int) *UnionFind {
	u := &UnionFind{
		count:  size,
		parent: make([]int, size),
		rank:   make([]int, size),
	}
	for i := 0; i < size; i++ {
		u.parent[i] = i
	}
	return u
}

// Count returns number of independent sets
func (u *UnionFind) Count() int {
	return u.count
}

// Union unites two sets
func (u *UnionFind) Union(x, y int) {
	x, y = u.find(x), u.find(y)
	if x == y {
		return
	}
	if u.rank[x] < u.rank[y] {
		u.parent[x] = y
		u.rank[y] += u.rank[x]
	} else {
		u.parent[y] = x
		u.rank[x] += u.rank[y]
	}
	u.count--
}

// IsUnited returns true if two sets are connected
func (u *UnionFind) IsUnited(x, y int) bool {
	return u.find(x) == u.find(y)
}

func (u *UnionFind) find(x int) int {
	for u.parent[x] != x {
		x = u.parent[x]
		u.parent[x] = x
	}
	return x
}
<commit_msg>misc UnionFind optimiza storage<commit_after>package misc

// UnionFind disjoint set structure
type UnionFind struct {
	count  int
	parent []int
}

// NewUnionFind returns pointer to UnionFind
func NewUnionFind(size int) *UnionFind {
	u := &UnionFind{
		count:  size,
		parent: make([]int, size),
	}
	for i := 0; i < size; i++ {
		u.parent[i] = -1
	}
	return u
}

// Count returns number of independent sets
func (u *UnionFind) Count() int {
	return u.count
}

// Size returns number of independent sets
func (u *UnionFind) Size(x int) int {
	return -u.parent[x]
}

// Union unites two sets
func (u *UnionFind) Union(x, y int) {
	x, y = u.find(x), u.find(y)
	if x == y {
		return
	}
	if u.parent[x] > u.parent[y] {
		x, y = y, x
	}
	u.parent[x] += u.parent[y]
	u.parent[y] = x
	u.count--
}

// IsUnited returns true if two sets are connected
func (u *UnionFind) IsUnited(x, y int) bool {
	return u.find(x) == u.find(y)
}

func (u *UnionFind) find(x int) int {
	for u.parent[x] >= 0 {
		x = u.parent[x]
	}
	return x
}
<|endoftext|>


<commit_before>package aws

import "github.com/tatsuyafw/amc/util"

type ec2 struct {
	query string
}

var querys = map[string]string{
	"instances": "Instances",
}

func (a ec2) URL() string {
	b := "REGION.console.aws.amazon.com/ec2/v2/home?REGION&region=REGION"
	if a.query != "" {
		b += "#" + querys[a.query]
	}
	return url(b)
}

func (a ec2) Validate() bool {
	if a.query == "" {
		return true
	}
	return util.IncludeStr(util.KeysStr(querys), a.query)
}
<commit_msg>Fix typo<commit_after>package aws

import "github.com/tatsuyafw/amc/util"

type ec2 struct {
	query string
}

var queries = map[string]string{
	"instances": "Instances",
}

func (a ec2) URL() string {
	b := "REGION.console.aws.amazon.com/ec2/v2/home?REGION&region=REGION"
	if a.query != "" {
		b += "#" + queries[a.query]
	}
	return url(b)
}

func (a ec2) Validate() bool {
	if a.query == "" {
		return true
	}
	return util.IncludeStr(util.KeysStr(queries), a.query)
}
<|endoftext|>


<commit_before>package dawa

import (
	"encoding/gob"
	"strings"
	"time"
)

// Since date/time is not a standard encoded field, we must create out own type.
type AwsTime time.Time

var location *time.Location

func init() {
	var err error
	location, err = time.LoadLocation("Europe/Copenhagen")
	if err != nil {
		panic(err)
	}
	// Register it as Gob
	gob.Register(AwsTime{})
}

// ParseTime will return the time encoding for a single field
// It the input must be AWS formatted encoding
func ParseTime(s string) (*AwsTime, error) {
	result, err := time.ParseInLocation("2006-01-02T15:04:05.000", string(s), location)
	if err != nil {
		return nil, err
	}
	t := AwsTime(result)
	return &t, nil
}

// MustParseTime will return the time encoding for a single field
// It the input must be AWS formatted encoding
func MustParseTime(s string) AwsTime {
	result, err := time.ParseInLocation("2006-01-02T15:04:05.000", string(s), location)
	if err != nil {
		panic(err)
	}
	return AwsTime(result)
}

func (t AwsTime) MarshalText() (text []byte, err error) {
	return t.MarshalJSON()
}

func (t *AwsTime) UnmarshalText(text []byte) error {
	return t.UnmarshalJSON(text)
}

// UnmarshalJSON a single time field
// It will attempt AWS encoding, and if that fails standard UnmarshalJSON for time.Time
func (t *AwsTime) UnmarshalJSON(b []byte) error {
	unquoted := strings.Trim(string(b), "\"")
	result, err := time.ParseInLocation("2006-01-02T15:04:05.000", unquoted, location)

	// Could not parse, attempt standard unmarshall
	if err != nil {
		var t2 time.Time
		err = t2.UnmarshalJSON([]byte(unquoted))
		if err != nil {
			return err
		}
		*t = AwsTime(t2)
		return nil
	}

	*t = AwsTime(result)
	return nil
}

// Time will return the underlying time.Time object
func (t AwsTime) Time() time.Time {
	return time.Time(t)
}

// MarshalJSON will send it as ordinary Javascipt date
func (t AwsTime) MarshalJSON() ([]byte, error) {
	return time.Time(t).MarshalJSON()
}

// GobEncode (as time.Time)
func (t AwsTime) GobEncode() ([]byte, error) {
	return time.Time(t).GobEncode()
}

// GobDecode (as time.Time)
func (t *AwsTime) GobDecode(data []byte) error {
	return (*time.Time)(t).GobDecode(data)
}

/*
// GetBSON provides BSON encoding of the Kid
func (t AwsTime) GetBSON() (interface{}, error) {
	return time.Time(t), nil
}

// SetBSON provides BSON decoding
func (t *AwsTime) SetBSON(raw bson.Raw) error {
	var t2 time.Time
	err := raw.Unmarshal(&t2)
	*t = AwsTime(t2)
	return errgo.Mask(err)
}
*/
<commit_msg>Trim spaces as well.<commit_after>package dawa

import (
	"encoding/gob"
	"strings"
	"time"
)

// Since date/time is not a standard encoded field, we must create out own type.
type AwsTime time.Time

var location *time.Location

func init() {
	var err error
	location, err = time.LoadLocation("Europe/Copenhagen")
	if err != nil {
		panic(err)
	}
	// Register it as Gob
	gob.Register(AwsTime{})
}

// ParseTime will return the time encoding for a single field
// It the input must be AWS formatted encoding
func ParseTime(s string) (*AwsTime, error) {
	result, err := time.ParseInLocation("2006-01-02T15:04:05.000", string(s), location)
	if err != nil {
		return nil, err
	}
	t := AwsTime(result)
	return &t, nil
}

// MustParseTime will return the time encoding for a single field
// It the input must be AWS formatted encoding
func MustParseTime(s string) AwsTime {
	result, err := time.ParseInLocation("2006-01-02T15:04:05.000", string(s), location)
	if err != nil {
		panic(err)
	}
	return AwsTime(result)
}

func (t AwsTime) MarshalText() (text []byte, err error) {
	return t.MarshalJSON()
}

func (t *AwsTime) UnmarshalText(text []byte) error {
	return t.UnmarshalJSON(text)
}

// UnmarshalJSON a single time field
// It will attempt AWS encoding, and if that fails standard UnmarshalJSON for time.Time
func (t *AwsTime) UnmarshalJSON(b []byte) error {
	unquoted := strings.Trim(string(b), "\" ")
	result, err := time.ParseInLocation("2006-01-02T15:04:05.000", unquoted, location)

	// Could not parse, attempt standard unmarshall
	if err != nil {
		var t2 time.Time
		err = t2.UnmarshalJSON([]byte(unquoted))
		if err != nil {
			return err
		}
		*t = AwsTime(t2)
		return nil
	}

	*t = AwsTime(result)
	return nil
}

// Time will return the underlying time.Time object
func (t AwsTime) Time() time.Time {
	return time.Time(t)
}

// MarshalJSON will send it as ordinary Javascipt date
func (t AwsTime) MarshalJSON() ([]byte, error) {
	return time.Time(t).MarshalJSON()
}

// GobEncode (as time.Time)
func (t AwsTime) GobEncode() ([]byte, error) {
	return time.Time(t).GobEncode()
}

// GobDecode (as time.Time)
func (t *AwsTime) GobDecode(data []byte) error {
	return (*time.Time)(t).GobDecode(data)
}

/*
// GetBSON provides BSON encoding of the Kid
func (t AwsTime) GetBSON() (interface{}, error) {
	return time.Time(t), nil
}

// SetBSON provides BSON decoding
func (t *AwsTime) SetBSON(raw bson.Raw) error {
	var t2 time.Time
	err := raw.Unmarshal(&t2)
	*t = AwsTime(t2)
	return errgo.Mask(err)
}
*/
<|endoftext|>


<commit_before>package b3m

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

const DefaultTimeout = 200

// commands
type CommandType byte

const CmdLoad CommandType = 1
const CmdSave CommandType = 2
const CmdRead CommandType = 3
const CmdWrite CommandType = 4
const CmdReset CommandType = 5
const CmdPosition CommandType = 6

// error status
const StatusSystemError = 1
const StatusMotorError = 2
const StatusUartError = 4
const StatusCommandError = 8

// servo modes
const RunNormal byte = 0
const RunFree byte = 2
const RunHold byte = 3

// control modes
const ControlPosition = 0
const ControlVelocity = 4
const ControlTorque = 8
const ControlFForword = 12

// trajectory
type TrajectoryType byte

const TrajectoryNormal TrajectoryType = 0
const TrajectoryEven TrajectoryType = 1
const TrajectoryThirdPoly TrajectoryType = 3
const TrajectoryFourthPoly TrajectoryType = 4
const TrajectoryFifthPoly TrajectoryType = 5

type Command struct {
	Cmd    CommandType
	Option byte
	Id     byte
	Data   []byte
}

func Send(s io.Writer, c *Command) (int, error) {
	buf := make([]byte, len(c.Data)+5)
	buf[0] = (byte)(len(c.Data) + 5)
	buf[1] = (byte)(c.Cmd)
	buf[2] = c.Option
	buf[3] = c.Id
	copy(buf[4:], c.Data)
	var sum byte = 0
	for i := 0; i < len(buf)-1; i++ {
		sum += buf[i]
	}
	buf[len(buf)-1] = sum
	return s.Write(buf)
}

func Recv(s io.Reader) (*Command, error) {
	buf := make([]byte, 256)
	n, err := s.Read(buf[0:1])
	if err != nil {
		return nil, err
	}
	if n == 0 {
		return nil, errors.New("timeout1")
	}
	sz := (int)(buf[0])
	for i := 1; i < sz; {
		n, err = s.Read(buf[i:])
		if err != nil {
			return nil, err
		}
		i += n
	}
	data := make([]byte, sz-5)
	copy(data, buf[4:])
	cmd := &Command{(CommandType)(buf[1]), buf[2], buf[3], data}
	return cmd, nil
}

type CommandResult struct {
	value *Command
	err   error
}

type Conn struct {
	s io.ReadWriter
	recv chan *CommandResult
	close chan <-int
}

func New(s io.ReadWriter) *Conn {
	recv := make(chan *CommandResult, 1)
	close := make(chan int, 1)
	go func(){
		for {
			ret, err := Recv(s)
			recv <- &CommandResult{ret, err}
			if err == io.EOF {
				break
			}
		}
	}()
	return &Conn{s, recv, close}
}

func (c *Conn) Clear() {
	for {
		select {
		case <-c.recv:
			// do nothing
		default:
			return
		}
	}
}

func (c *Conn) Recv(timeout int) (*Command, error) {
	select {
	case ret := <-c.recv:
		return ret.value, ret.err
	case <-time.After(time.Millisecond * time.Duration(timeout)):
		return nil, errors.New("timeout")
	}
}

func (c *Conn) Send(cmd *Command) (int, error) {
	return Send(c.s, cmd)
}

func (c *Conn) GetServo(id byte) *Servo {
	return &Servo{c, id, DefaultTimeout, 0}
}

func ReadMem(s *Conn, id byte, addr int, size int, timeout int) (*Command, error) {
	cmd := &Command{CmdRead, 0, id, []byte{(byte)(addr), (byte)(size)}}
	s.Clear()
	_, err := s.Send(cmd)
	if err != nil {
		return nil, err
	}
	return s.Recv(timeout)
}

func WriteMem(s *Conn, id byte, addr int, data []byte, timeout int) (*Command, error) {
	buf := make([]byte, len(data)+2)
	copy(buf, data)
	buf[len(buf)-2] = (byte)(addr)
	buf[len(buf)-1] = 1
	cmd := &Command{CmdWrite, 0, id, buf}
	s.Clear()
	_, err := s.Send(cmd)
	if err != nil {
		return nil, err
	}
	if id == 255 {
		return cmd, nil
	}
	return s.Recv(timeout)
}

type Servo struct {
	io        *Conn         // serial port connection
	Id        byte          // device id
	TimeoutMs int           // timeout for replay.
	Status    byte          // last status
}

func GetServo(io *Conn, id byte) *Servo {
	return &Servo{io, id, DefaultTimeout, 0}
}

func (s *Servo) ReadMem(addr int, size int) ([]byte, error) {
	res, err := ReadMem(s.io, s.Id, addr, size, s.TimeoutMs)
	if err != nil {
		return nil, err
	}
	s.Status = res.Option
	return res.Data, nil
}

func (s *Servo) WriteMem(addr int, data []byte) error {
	res, err := WriteMem(s.io, s.Id, addr, data, s.TimeoutMs)
	if err != nil {
		return err
	}
	s.Status = res.Option
	return nil
}

func (s *Servo) GetVersion() (model string, version string, err error) {
	buf, err := s.ReadMem(0xA2, 12)
	if err != nil {
		return "", "", err
	}
	model = fmt.Sprintf("B3M-%c%c-%v%v%v-%c", buf[7], buf[6], buf[3], buf[2], buf[1], buf[0])
	version = fmt.Sprintf("%v.%v.%v.%v", buf[11], buf[10], buf[9], buf[8])
	return
}

func (s *Servo) GetMode() (byte, error) {
	buf, err := s.ReadMem(0x28, 1)
	if err != nil {
		return 0, err
	}
	return buf[0], nil
}

func (s *Servo) SetMode(mode byte) error {
	return s.WriteMem(0x28, []byte{mode})
}

func (s *Servo) Reset(timeAfter byte) error {
	cmd := &Command{CmdReset, 0, s.Id, []byte{timeAfter}}
	_, err := s.io.Send(cmd)
	return err
}

func (s *Servo) Load() error {
	_, err := s.io.Send(&Command{CmdLoad, 0, s.Id, []byte{}})
	if err != nil {
		return err
	}
	if s.Id != 255 {
		_, err = s.io.Recv(s.TimeoutMs)
	}
	return err
}

func (s *Servo) Save() error {
	_, err := s.io.Send(&Command{CmdSave, 0, s.Id, []byte{}})
	if err != nil {
		return err
	}
	if s.Id != 255 {
		_, err = s.io.Recv(s.TimeoutMs)
	}
	return err
}

func (s *Servo) SetTrajectoryMode(trajectory TrajectoryType) error {
	return s.WriteMem(0x29, []byte{byte(trajectory)})
}

func (s *Servo) SetPosition(pos int16) error {
	return s.WriteMem(0x2A, []byte{(byte)(pos), (byte)(pos >> 8)})
}

func (s *Servo) GetCurrentPosition() (int16, error) {
	res, err := s.ReadMem(0x2C, 2)
	if err != nil {
		return 0, err
	}
	return (int16)(res[0]) | ((int16)(res[1]) << 8), nil
}

func (s *Servo) SetVelocity(v int16) error {
	return s.WriteMem(0x30, []byte{(byte)(v), (byte)(v >> 8)})
}

func (s *Servo) SetTorque(torque int16) error {
	return s.WriteMem(0x3C, []byte{(byte)(torque), (byte)(torque >> 8)})
}

func (s *Servo) SetPosition2(pos, time int16) error {
	_, err := s.io.Send(&Command{CmdPosition, 0, s.Id, []byte{(byte)(pos), (byte)(pos >> 8), (byte)(time), (byte)(time >> 8)}})
	if err != nil {
		return err
	}
	if s.Id != 255 {
		_, err = s.io.Recv(s.TimeoutMs)
	}
	return err
}
<commit_msg>add no recv mode.<commit_after>package b3m

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

const DefaultTimeout = 200

// commands
type CommandType byte

const CmdLoad CommandType = 1
const CmdSave CommandType = 2
const CmdRead CommandType = 3
const CmdWrite CommandType = 4
const CmdReset CommandType = 5
const CmdPosition CommandType = 6

// error status
const StatusSystemError = 1
const StatusMotorError = 2
const StatusUartError = 4
const StatusCommandError = 8

// servo modes
const RunNormal byte = 0
const RunFree byte = 2
const RunHold byte = 3

// control modes
const ControlPosition = 0
const ControlVelocity = 4
const ControlTorque = 8
const ControlFForword = 12

// trajectory
type TrajectoryType byte

const TrajectoryNormal TrajectoryType = 0
const TrajectoryEven TrajectoryType = 1
const TrajectoryThirdPoly TrajectoryType = 3
const TrajectoryFourthPoly TrajectoryType = 4
const TrajectoryFifthPoly TrajectoryType = 5

type Command struct {
	Cmd    CommandType
	Option byte
	Id     byte
	Data   []byte
}

func Send(s io.Writer, c *Command) (int, error) {
	buf := make([]byte, len(c.Data)+5)
	buf[0] = byte(len(c.Data) + 5)
	buf[1] = byte(c.Cmd)
	buf[2] = c.Option
	buf[3] = c.Id
	copy(buf[4:], c.Data)
	var sum byte = 0
	for i := 0; i < len(buf)-1; i++ {
		sum += buf[i]
	}
	buf[len(buf)-1] = sum
	return s.Write(buf)
}

func Recv(s io.Reader) (*Command, error) {
	buf := make([]byte, 256)
	n, err := s.Read(buf[0:1])
	if err != nil {
		return nil, err
	}
	if n == 0 {
		return nil, errors.New("timeout1")
	}
	sz := int(buf[0])
	for i := 1; i < sz; {
		n, err = s.Read(buf[i:])
		if err != nil {
			return nil, err
		}
		i += n
	}
	data := make([]byte, sz-5)
	copy(data, buf[4:])
	cmd := &Command{(CommandType)(buf[1]), buf[2], buf[3], data}
	return cmd, nil
}

type CommandResult struct {
	value *Command
	err   error
}

type Conn struct {
	s io.ReadWriter
	recv chan *CommandResult
	close chan <-int
}

func New(s io.ReadWriter) *Conn {
	recv := make(chan *CommandResult, 1)
	close := make(chan int, 1)
	go func(){
		for {
			ret, err := Recv(s)
			recv <- &CommandResult{ret, err}
			if err == io.EOF {
				break
			}
		}
	}()
	return &Conn{s, recv, close}
}

func (c *Conn) Clear() {
	for {
		select {
		case <-c.recv:
			// do nothing
		default:
			return
		}
	}
}

func (c *Conn) Recv(timeout int) (*Command, error) {
	select {
	case ret := <-c.recv:
		return ret.value, ret.err
	case <-time.After(time.Millisecond * time.Duration(timeout)):
		return nil, errors.New("timeout")
	}
}

func (c *Conn) Send(cmd *Command) (int, error) {
	return Send(c.s, cmd)
}

func (c *Conn) GetServo(id byte) *Servo {
	return &Servo{c, id, DefaultTimeout, 0, id == 255}
}

func ReadMem(s *Conn, id byte, addr int, size int, timeout int) (*Command, error) {
	cmd := &Command{CmdRead, 0, id, []byte{byte(addr), byte(size)}}
	s.Clear()
	_, err := s.Send(cmd)
	if err != nil {
		return nil, err
	}
	return s.Recv(timeout)
}

func WriteMem(s *Conn, id byte, addr int, data []byte, timeout int, mute bool) (*Command, error) {
	buf := make([]byte, len(data)+2)
	copy(buf, data)
	buf[len(buf)-2] = byte(addr)
	buf[len(buf)-1] = 1
	cmd := &Command{CmdWrite, 0, id, buf}
	s.Clear()
	_, err := s.Send(cmd)
	if err != nil {
		return nil, err
	}
	if mute {
		return cmd, nil
	}
	return s.Recv(timeout)
}

type Servo struct {
	io        *Conn         // serial port connection
	Id        byte          // device id
	TimeoutMs int           // timeout for replay.
	Status    byte          // last status
	Mute      bool          // clone mode or Id=255
}

func (s *Servo) ReadMem(addr int, size int) ([]byte, error) {
	res, err := ReadMem(s.io, s.Id, addr, size, s.TimeoutMs)
	if err != nil {
		return nil, err
	}
	s.Status = res.Option
	return res.Data, nil
}

func (s *Servo) WriteMem(addr int, data []byte) error {
	res, err := WriteMem(s.io, s.Id, addr, data, s.TimeoutMs, s.Mute)
	if err != nil {
		return err
	}
	s.Status = res.Option
	return nil
}

func (s *Servo) Reset(timeAfter byte) error {
	cmd := &Command{CmdReset, 0, s.Id, []byte{timeAfter}}
	_, err := s.io.Send(cmd)
	return err
}

func (s *Servo) Load() error {
	_, err := s.io.Send(&Command{CmdLoad, 0, s.Id, []byte{}})
	if err != nil {
		return err
	}
	if !s.Mute {
		_, err = s.io.Recv(s.TimeoutMs)
	}
	return err
}

func (s *Servo) GetVersion() (model string, version string, err error) {
	buf, err := s.ReadMem(0xA2, 12)
	if err != nil {
		return "", "", err
	}
	model = fmt.Sprintf("B3M-%c%c-%v%v%v-%c", buf[7], buf[6], buf[3], buf[2], buf[1], buf[0])
	version = fmt.Sprintf("%v.%v.%v.%v", buf[11], buf[10], buf[9], buf[8])
	return
}

func (s *Servo) Save() error {
	_, err := s.io.Send(&Command{CmdSave, 0, s.Id, []byte{}})
	if err != nil {
		return err
	}
	if !s.Mute {
		_, err = s.io.Recv(s.TimeoutMs)
	}
	return err
}

func (s *Servo) GetMode() (byte, error) {
	buf, err := s.ReadMem(0x28, 1)
	if err != nil {
		return 0, err
	}
	return buf[0], nil
}

func (s *Servo) SetMode(mode byte) error {
	return s.WriteMem(0x28, []byte{mode})
}

func (s *Servo) SetTrajectoryMode(trajectory TrajectoryType) error {
	return s.WriteMem(0x29, []byte{byte(trajectory)})
}

func (s *Servo) SetPosition(pos int16) error {
	return s.WriteMem(0x2A, []byte{byte(pos), byte(pos >> 8)})
}

func (s *Servo) GetCurrentPosition() (int16, error) {
	res, err := s.ReadMem(0x2C, 2)
	if err != nil {
		return 0, err
	}
	return (int16)(res[0]) | ((int16)(res[1]) << 8), nil
}

func (s *Servo) SetVelocity(v int16) error {
	return s.WriteMem(0x30, []byte{byte(v), byte(v >> 8)})
}

func (s *Servo) SetTorque(torque int16) error {
	return s.WriteMem(0x3C, []byte{byte(torque), byte(torque >> 8)})
}

func (s *Servo) SetPosition2(pos, time int16) error {
	_, err := s.io.Send(&Command{CmdPosition, 0, s.Id, []byte{byte(pos), byte(pos >> 8), byte(time), byte(time >> 8)}})
	if err != nil {
		return err
	}
	if !s.Mute {
		_, err = s.io.Recv(s.TimeoutMs)
	}
	return err
}
<|endoftext|>


<commit_before>package discovery

import (
	"sync"

	"github.com/btcsuite/btcd/btcec"
	"github.com/lightningnetwork/lnd/lnpeer"
	"github.com/lightningnetwork/lnd/lnwire"
)

// reliableSenderCfg contains all of necessary items for the reliableSender to
// carry out its duties.
type reliableSenderCfg struct {
	// NotifyWhenOnline is a function that allows the gossiper to be
	// notified when a certain peer comes online, allowing it to
	// retry sending a peer message.
	//
	// NOTE: The peerChan channel must be buffered.
	//
	// TODO(wilmer): use [33]byte to avoid unnecessary serializations.
	NotifyWhenOnline func(peer *btcec.PublicKey, peerChan chan<- lnpeer.Peer)

	// NotifyWhenOffline is a function that allows the gossiper to be
	// notified when a certain peer disconnects, allowing it to request a
	// notification for when it reconnects.
	NotifyWhenOffline func(peerPubKey [33]byte) <-chan struct{}

	// MessageStore is a persistent storage of gossip messages which we will
	// use to determine which messages need to be resent for a given peer.
	MessageStore GossipMessageStore

	// IsMsgStale determines whether a message retrieved from the backing
	// MessageStore is seen as stale by the current graph.
	IsMsgStale func(lnwire.Message) bool
}

// peerManager contains the set of channels required for the peerHandler to
// properly carry out its duties.
type peerManager struct {
	// msgs is the channel through which messages will be streamed to the
	// handler in order to send the message to the peer while they're
	// online.
	msgs chan lnwire.Message

	// done is a channel that will be closed to signal that the handler for
	// the given peer has been torn down for whatever reason.
	done chan struct{}
}

// reliableSender is a small subsystem of the gossiper used to reliably send
// gossip messages to peers.
type reliableSender struct {
	start sync.Once
	stop  sync.Once

	cfg reliableSenderCfg

	// activePeers keeps track of whether a peerHandler exists for a given
	// peer. A peerHandler is tasked with handling requests for messages
	// that should be reliably sent to peers while also taking into account
	// the peer's connection lifecycle.
	activePeers    map[[33]byte]peerManager
	activePeersMtx sync.Mutex

	wg   sync.WaitGroup
	quit chan struct{}
}

// newReliableSender returns a new reliableSender backed by the given config.
func newReliableSender(cfg *reliableSenderCfg) *reliableSender {
	return &reliableSender{
		cfg:         *cfg,
		activePeers: make(map[[33]byte]peerManager),
		quit:        make(chan struct{}),
	}
}

// Start spawns message handlers for any peers with pending messages.
func (s *reliableSender) Start() error {
	var err error
	s.start.Do(func() {
		err = s.resendPendingMsgs()
	})
	return err
}

// Stop halts the reliable sender from sending messages to peers.
func (s *reliableSender) Stop() {
	s.stop.Do(func() {
		close(s.quit)
		s.wg.Wait()
	})
}

// sendMessage constructs a request to send a message reliably to a peer. In the
// event that the peer is currently offline, this will only write the message to
// disk. Once the peer reconnects, this message, along with any others pending,
// will be sent to the peer.
func (s *reliableSender) sendMessage(msg lnwire.Message, peerPubKey [33]byte) error {
	// We'll start by persisting the message to disk. This allows us to
	// resend the message upon restarts and peer reconnections.
	if err := s.cfg.MessageStore.AddMessage(msg, peerPubKey); err != nil {
		return err
	}

	// Then, we'll spawn a peerHandler for this peer to handle resending its
	// pending messages while taking into account its connection lifecycle.
spawnHandler:
	msgHandler, ok := s.spawnPeerHandler(peerPubKey)

	// If the handler wasn't previously active, we can exit now as we know
	// that the message will be sent once the peer online notification is
	// received. This prevents us from potentially sending the message
	// twice.
	if !ok {
		return nil
	}

	// Otherwise, we'll attempt to stream the message to the handler.
	// There's a subtle race condition where the handler can be torn down
	// due to all of the messages sent being stale, so we'll handle this
	// gracefully by spawning another one to prevent blocking.
	select {
	case msgHandler.msgs <- msg:
	case <-msgHandler.done:
		goto spawnHandler
	case <-s.quit:
		return ErrGossiperShuttingDown
	}

	return nil
}

// spawnPeerMsgHandler spawns a peerHandler for the given peer if there isn't
// one already active. The boolean returned signals whether there was already
// one active or not.
func (s *reliableSender) spawnPeerHandler(peerPubKey [33]byte) (peerManager, bool) {
	s.activePeersMtx.Lock()
	defer s.activePeersMtx.Unlock()

	msgHandler, ok := s.activePeers[peerPubKey]
	if !ok {
		msgHandler = peerManager{
			msgs: make(chan lnwire.Message),
			done: make(chan struct{}),
		}
		s.activePeers[peerPubKey] = msgHandler

		s.wg.Add(1)
		go s.peerHandler(msgHandler, peerPubKey)
	}

	return msgHandler, ok
}

// peerHandler is responsible for handling our reliable message send requests
// for a given peer while also taking into account the peer's connection
// lifecycle. Any messages that are attempted to be sent while the peer is
// offline will be queued and sent once the peer reconnects.
//
// NOTE: This must be run as a goroutine.
func (s *reliableSender) peerHandler(peerMgr peerManager, peerPubKey [33]byte) {
	defer s.wg.Done()

	// We'll start by requesting a notification for when the peer
	// reconnects.
	pubKey, _ := btcec.ParsePubKey(peerPubKey[:], btcec.S256())
	peerChan := make(chan lnpeer.Peer, 1)

waitUntilOnline:
	log.Debugf("Requesting online notification for peer=%x", peerPubKey)

	s.cfg.NotifyWhenOnline(pubKey, peerChan)

	var peer lnpeer.Peer
out:
	for {
		select {
		// While we're waiting, we'll also consume any messages that
		// must be sent to prevent blocking the caller. These can be
		// ignored for now since the peer is currently offline. Once
		// they reconnect, the messages will be sent since they should
		// have been persisted to disk.
		case msg := <-peerMgr.msgs:
			// Retrieve the short channel ID for which this message
			// applies for logging purposes. The error can be
			// ignored as the store can only contain messages which
			// have a ShortChannelID field.
			shortChanID, _ := msgShortChanID(msg)
			log.Debugf("Received request to send %v message for "+
				"channel=%v while peer=%x is offline",
				msg.MsgType(), shortChanID, peerPubKey)

		case peer = <-peerChan:
			break out

		case <-s.quit:
			return
		}
	}

	log.Debugf("Peer=%x is now online, proceeding to send pending messages",
		peerPubKey)

	// Once we detect the peer has reconnected, we'll also request a
	// notification for when they disconnect. We'll use this to make sure
	// they haven't disconnected (in the case of a flappy peer, etc.) by the
	// time we attempt to send them the pending messages.
	log.Debugf("Requesting offline notification for peer=%x", peerPubKey)

	offlineChan := s.cfg.NotifyWhenOffline(peerPubKey)

	pendingMsgs, err := s.cfg.MessageStore.MessagesForPeer(peerPubKey)
	if err != nil {
		log.Errorf("Unable to retrieve pending messages for peer %x: %v",
			peerPubKey, err)
		return
	}

	// With the peer online, we can now proceed to send our pending messages
	// for them.
	for _, msg := range pendingMsgs {
		// Retrieve the short channel ID for which this message applies
		// for logging purposes. The error can be ignored as the store
		// can only contain messages which have a ShortChannelID field.
		shortChanID, _ := msgShortChanID(msg)

		if err := peer.SendMessage(false, msg); err != nil {
			log.Errorf("Unable to send %v message for channel=%v "+
				"to %x: %v", msg.MsgType(), shortChanID,
				peerPubKey, err)
			goto waitUntilOnline
		}

		log.Debugf("Successfully sent %v message for channel=%v with "+
			"peer=%x upon reconnection", msg.MsgType(), shortChanID,
			peerPubKey)

		// Now that the message has at least been sent once, we can
		// check whether it's stale. This guarantees that
		// AnnounceSignatures are sent at least once if we happen to
		// already have signatures for both parties.
		if s.cfg.IsMsgStale(msg) {
			err := s.cfg.MessageStore.DeleteMessage(msg, peerPubKey)
			if err != nil {
				log.Errorf("Unable to remove stale %v message "+
					"for channel=%v with peer %x: %v",
					msg.MsgType(), shortChanID, peerPubKey,
					err)
				continue
			}

			log.Debugf("Removed stale %v message for channel=%v "+
				"with peer=%x", msg.MsgType(), shortChanID,
				peerPubKey)
		}
	}

	// If all of our messages were stale, then there's no need for this
	// handler to continue running, so we can exit now.
	pendingMsgs, err = s.cfg.MessageStore.MessagesForPeer(peerPubKey)
	if err != nil {
		log.Errorf("Unable to retrieve pending messages for peer %x: %v",
			peerPubKey, err)
		return
	}

	if len(pendingMsgs) == 0 {
		log.Debugf("No pending messages left for peer=%x", peerPubKey)

		s.activePeersMtx.Lock()
		delete(s.activePeers, peerPubKey)
		s.activePeersMtx.Unlock()

		close(peerMgr.done)

		return
	}

	// Once the pending messages are sent, we can continue to send any
	// future messages while the peer remains connected.
	for {
		select {
		case msg := <-peerMgr.msgs:
			// Retrieve the short channel ID for which this message
			// applies for logging purposes. The error can be
			// ignored as the store can only contain messages which
			// have a ShortChannelID field.
			shortChanID, _ := msgShortChanID(msg)

			if err := peer.SendMessage(false, msg); err != nil {
				log.Errorf("Unable to send %v message for "+
					"channel=%v to %x: %v", msg.MsgType(),
					shortChanID, peerPubKey, err)
			}

			log.Debugf("Successfully sent %v message for "+
				"channel=%v with peer=%x", msg.MsgType(),
				shortChanID, peerPubKey)

		case <-offlineChan:
			goto waitUntilOnline

		case <-s.quit:
			return
		}
	}
}

// resendPendingMsgs retrieves and sends all of the messages within the message
// store that should be reliably sent to their respective peers.
func (s *reliableSender) resendPendingMsgs() error {
	// Fetch all of the peers for which we have pending messages for and
	// spawn a peerMsgHandler for each. Once the peer is seen as online, all
	// of the pending messages will be sent.
	peers, err := s.cfg.MessageStore.Peers()
	if err != nil {
		return err
	}

	for peer := range peers {
		s.spawnPeerHandler(peer)
	}

	return nil
}
<commit_msg>discovery: add missing offline peer check before sending message reliably<commit_after>package discovery

import (
	"sync"

	"github.com/btcsuite/btcd/btcec"
	"github.com/lightningnetwork/lnd/lnpeer"
	"github.com/lightningnetwork/lnd/lnwire"
)

// reliableSenderCfg contains all of necessary items for the reliableSender to
// carry out its duties.
type reliableSenderCfg struct {
	// NotifyWhenOnline is a function that allows the gossiper to be
	// notified when a certain peer comes online, allowing it to
	// retry sending a peer message.
	//
	// NOTE: The peerChan channel must be buffered.
	//
	// TODO(wilmer): use [33]byte to avoid unnecessary serializations.
	NotifyWhenOnline func(peer *btcec.PublicKey, peerChan chan<- lnpeer.Peer)

	// NotifyWhenOffline is a function that allows the gossiper to be
	// notified when a certain peer disconnects, allowing it to request a
	// notification for when it reconnects.
	NotifyWhenOffline func(peerPubKey [33]byte) <-chan struct{}

	// MessageStore is a persistent storage of gossip messages which we will
	// use to determine which messages need to be resent for a given peer.
	MessageStore GossipMessageStore

	// IsMsgStale determines whether a message retrieved from the backing
	// MessageStore is seen as stale by the current graph.
	IsMsgStale func(lnwire.Message) bool
}

// peerManager contains the set of channels required for the peerHandler to
// properly carry out its duties.
type peerManager struct {
	// msgs is the channel through which messages will be streamed to the
	// handler in order to send the message to the peer while they're
	// online.
	msgs chan lnwire.Message

	// done is a channel that will be closed to signal that the handler for
	// the given peer has been torn down for whatever reason.
	done chan struct{}
}

// reliableSender is a small subsystem of the gossiper used to reliably send
// gossip messages to peers.
type reliableSender struct {
	start sync.Once
	stop  sync.Once

	cfg reliableSenderCfg

	// activePeers keeps track of whether a peerHandler exists for a given
	// peer. A peerHandler is tasked with handling requests for messages
	// that should be reliably sent to peers while also taking into account
	// the peer's connection lifecycle.
	activePeers    map[[33]byte]peerManager
	activePeersMtx sync.Mutex

	wg   sync.WaitGroup
	quit chan struct{}
}

// newReliableSender returns a new reliableSender backed by the given config.
func newReliableSender(cfg *reliableSenderCfg) *reliableSender {
	return &reliableSender{
		cfg:         *cfg,
		activePeers: make(map[[33]byte]peerManager),
		quit:        make(chan struct{}),
	}
}

// Start spawns message handlers for any peers with pending messages.
func (s *reliableSender) Start() error {
	var err error
	s.start.Do(func() {
		err = s.resendPendingMsgs()
	})
	return err
}

// Stop halts the reliable sender from sending messages to peers.
func (s *reliableSender) Stop() {
	s.stop.Do(func() {
		close(s.quit)
		s.wg.Wait()
	})
}

// sendMessage constructs a request to send a message reliably to a peer. In the
// event that the peer is currently offline, this will only write the message to
// disk. Once the peer reconnects, this message, along with any others pending,
// will be sent to the peer.
func (s *reliableSender) sendMessage(msg lnwire.Message, peerPubKey [33]byte) error {
	// We'll start by persisting the message to disk. This allows us to
	// resend the message upon restarts and peer reconnections.
	if err := s.cfg.MessageStore.AddMessage(msg, peerPubKey); err != nil {
		return err
	}

	// Then, we'll spawn a peerHandler for this peer to handle resending its
	// pending messages while taking into account its connection lifecycle.
spawnHandler:
	msgHandler, ok := s.spawnPeerHandler(peerPubKey)

	// If the handler wasn't previously active, we can exit now as we know
	// that the message will be sent once the peer online notification is
	// received. This prevents us from potentially sending the message
	// twice.
	if !ok {
		return nil
	}

	// Otherwise, we'll attempt to stream the message to the handler.
	// There's a subtle race condition where the handler can be torn down
	// due to all of the messages sent being stale, so we'll handle this
	// gracefully by spawning another one to prevent blocking.
	select {
	case msgHandler.msgs <- msg:
	case <-msgHandler.done:
		goto spawnHandler
	case <-s.quit:
		return ErrGossiperShuttingDown
	}

	return nil
}

// spawnPeerMsgHandler spawns a peerHandler for the given peer if there isn't
// one already active. The boolean returned signals whether there was already
// one active or not.
func (s *reliableSender) spawnPeerHandler(peerPubKey [33]byte) (peerManager, bool) {
	s.activePeersMtx.Lock()
	defer s.activePeersMtx.Unlock()

	msgHandler, ok := s.activePeers[peerPubKey]
	if !ok {
		msgHandler = peerManager{
			msgs: make(chan lnwire.Message),
			done: make(chan struct{}),
		}
		s.activePeers[peerPubKey] = msgHandler

		s.wg.Add(1)
		go s.peerHandler(msgHandler, peerPubKey)
	}

	return msgHandler, ok
}

// peerHandler is responsible for handling our reliable message send requests
// for a given peer while also taking into account the peer's connection
// lifecycle. Any messages that are attempted to be sent while the peer is
// offline will be queued and sent once the peer reconnects.
//
// NOTE: This must be run as a goroutine.
func (s *reliableSender) peerHandler(peerMgr peerManager, peerPubKey [33]byte) {
	defer s.wg.Done()

	// We'll start by requesting a notification for when the peer
	// reconnects.
	pubKey, _ := btcec.ParsePubKey(peerPubKey[:], btcec.S256())
	peerChan := make(chan lnpeer.Peer, 1)

waitUntilOnline:
	log.Debugf("Requesting online notification for peer=%x", peerPubKey)

	s.cfg.NotifyWhenOnline(pubKey, peerChan)

	var peer lnpeer.Peer
out:
	for {
		select {
		// While we're waiting, we'll also consume any messages that
		// must be sent to prevent blocking the caller. These can be
		// ignored for now since the peer is currently offline. Once
		// they reconnect, the messages will be sent since they should
		// have been persisted to disk.
		case msg := <-peerMgr.msgs:
			// Retrieve the short channel ID for which this message
			// applies for logging purposes. The error can be
			// ignored as the store can only contain messages which
			// have a ShortChannelID field.
			shortChanID, _ := msgShortChanID(msg)
			log.Debugf("Received request to send %v message for "+
				"channel=%v while peer=%x is offline",
				msg.MsgType(), shortChanID, peerPubKey)

		case peer = <-peerChan:
			break out

		case <-s.quit:
			return
		}
	}

	log.Debugf("Peer=%x is now online, proceeding to send pending messages",
		peerPubKey)

	// Once we detect the peer has reconnected, we'll also request a
	// notification for when they disconnect. We'll use this to make sure
	// they haven't disconnected (in the case of a flappy peer, etc.) by the
	// time we attempt to send them the pending messages.
	log.Debugf("Requesting offline notification for peer=%x", peerPubKey)

	offlineChan := s.cfg.NotifyWhenOffline(peerPubKey)

	pendingMsgs, err := s.cfg.MessageStore.MessagesForPeer(peerPubKey)
	if err != nil {
		log.Errorf("Unable to retrieve pending messages for peer %x: %v",
			peerPubKey, err)
		return
	}

	// With the peer online, we can now proceed to send our pending messages
	// for them.
	for _, msg := range pendingMsgs {
		// Retrieve the short channel ID for which this message applies
		// for logging purposes. The error can be ignored as the store
		// can only contain messages which have a ShortChannelID field.
		shortChanID, _ := msgShortChanID(msg)

		// Ensure the peer is still online right before sending the
		// message.
		select {
		case <-offlineChan:
			goto waitUntilOnline
		default:
		}

		if err := peer.SendMessage(false, msg); err != nil {
			log.Errorf("Unable to send %v message for channel=%v "+
				"to %x: %v", msg.MsgType(), shortChanID,
				peerPubKey, err)
			goto waitUntilOnline
		}

		log.Debugf("Successfully sent %v message for channel=%v with "+
			"peer=%x upon reconnection", msg.MsgType(), shortChanID,
			peerPubKey)

		// Now that the message has at least been sent once, we can
		// check whether it's stale. This guarantees that
		// AnnounceSignatures are sent at least once if we happen to
		// already have signatures for both parties.
		if s.cfg.IsMsgStale(msg) {
			err := s.cfg.MessageStore.DeleteMessage(msg, peerPubKey)
			if err != nil {
				log.Errorf("Unable to remove stale %v message "+
					"for channel=%v with peer %x: %v",
					msg.MsgType(), shortChanID, peerPubKey,
					err)
				continue
			}

			log.Debugf("Removed stale %v message for channel=%v "+
				"with peer=%x", msg.MsgType(), shortChanID,
				peerPubKey)
		}
	}

	// If all of our messages were stale, then there's no need for this
	// handler to continue running, so we can exit now.
	pendingMsgs, err = s.cfg.MessageStore.MessagesForPeer(peerPubKey)
	if err != nil {
		log.Errorf("Unable to retrieve pending messages for peer %x: %v",
			peerPubKey, err)
		return
	}

	if len(pendingMsgs) == 0 {
		log.Debugf("No pending messages left for peer=%x", peerPubKey)

		s.activePeersMtx.Lock()
		delete(s.activePeers, peerPubKey)
		s.activePeersMtx.Unlock()

		close(peerMgr.done)

		return
	}

	// Once the pending messages are sent, we can continue to send any
	// future messages while the peer remains connected.
	for {
		select {
		case msg := <-peerMgr.msgs:
			// Retrieve the short channel ID for which this message
			// applies for logging purposes. The error can be
			// ignored as the store can only contain messages which
			// have a ShortChannelID field.
			shortChanID, _ := msgShortChanID(msg)

			if err := peer.SendMessage(false, msg); err != nil {
				log.Errorf("Unable to send %v message for "+
					"channel=%v to %x: %v", msg.MsgType(),
					shortChanID, peerPubKey, err)
			}

			log.Debugf("Successfully sent %v message for "+
				"channel=%v with peer=%x", msg.MsgType(),
				shortChanID, peerPubKey)

		case <-offlineChan:
			goto waitUntilOnline

		case <-s.quit:
			return
		}
	}
}

// resendPendingMsgs retrieves and sends all of the messages within the message
// store that should be reliably sent to their respective peers.
func (s *reliableSender) resendPendingMsgs() error {
	// Fetch all of the peers for which we have pending messages for and
	// spawn a peerMsgHandler for each. Once the peer is seen as online, all
	// of the pending messages will be sent.
	peers, err := s.cfg.MessageStore.Peers()
	if err != nil {
		return err
	}

	for peer := range peers {
		s.spawnPeerHandler(peer)
	}

	return nil
}
<|endoftext|>


<commit_before>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * 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 archive

import (
	"io"
	"os"

	"golang.org/x/crypto/openpgp"
	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
)

// Release {{{

// The file "dists/$DIST/InRelease" shall contain meta-information about the
// distribution and checksums for the indices, possibly signed with a GPG
// clearsign signature (for example created by "gpg -a -s --clearsign"). For
// older clients there can also be a "dists/$DIST/Release" file without any
// signature and the file "dists/$DIST/Release.gpg" with a detached GPG
// signature of the "Release" file, compatible with the format used by the GPG
// options "-a -b -s".
type Release struct {
	Description string

	// Optional field indicating the origin of the repository, a single line
	// of free form text.
	Origin string

	// Optional field including some kind of label, a single line of free form
	// text.
	//
	// Typically used extensively in repositories split over multiple media
	// such as repositories stored on CDs.
	Label string

	// The Version field, if specified, shall be the version of the release.
	// This is usually a sequence of integers separated by the character
	// "." (full stop).
	//
	// Example:
	//
	//   Version: 6.0
	Version string

	// The Suite field may describe the suite. A suite is a single word. In
	// Debian, this shall be one of oldstable, stable, testing, unstable,
	// or experimental; with optional suffixes such as -updates.
	//
	// Example:
	// //   Suite: stable
	Suite string

	// The Codename field shall describe the codename of the release. A
	// codename is a single word. Debian releases are codenamed after Toy
	// Story Characters, and the unstable suite has the codename sid, the
	// experimental suite has the codename experimental.
	//
	// Example:
	//
	//   Codename: squeeze
	Codename string

	// A whitespace separated list of areas.
	//
	// Example:
	//
	//   Components: main contrib non-free
	//
	// May also include be prefixed by parts of the path following the
	// directory beneath dists, if the Release file is not in a directory
	// directly beneath dists/. As an example, security updates are specified
	// in APT as:
	//
	// deb http://security.debian.org/ stable/updates main)
	//
	// The Release file would be located at
	// http://security.debian.org/dists/stable/updates/Release and look like:
	//
	//   Suite: stable
	//   Components: updates/main updates/contrib updates/non-free
	Components []string `delim:" "`

	// Whitespace separated unique single words identifying Debian machine
	// architectures as described in Architecture specification strings,
	// Section 11.1. Clients should ignore Architectures they do not know
	// about.
	Architectures []dependency.Arch

	// The Date field shall specify the time at which the Release file was
	// created. Clients updating a local on-disk cache should ignore a Release
	// file with an earlier date than the date in the already stored Release
	// file.
	//
	// The Valid-Until field may specify at which time the Release file should
	// be considered expired by the client. Client behaviour on expired Release
	// files is unspecified.
	//
	// The format of the dates is the same as for the Date field in .changes
	// files; and as used in debian/changelog files, and documented in Policy
	// 4.4 ( Debian changelog: debian/changelog)
	Date       string
	ValidUntil string `control:"Valid-Until"`

	// note the upper-case S in MD5Sum (unlike in Packages and Sources files)
	//
	// These fields are used for two purposes:
	//
	// describe what package index files are present when release signature is
	// available it certifies that listed index files and files referenced by
	// those index files are genuine Those fields shall be multi-line fields
	// containing multiple lines of whitespace separated data. Each line shall
	// contain
	//
	// The checksum of the file in the format corresponding to the field The
	// size of the file (integer >= 0) The filename relative to the directory
	// of the Release file Each datum must be separated by one or more
	// whitespace characters.
	//
	// Server requirements:
	//
	// The checksum and sizes shall match the actual existing files. If indexes
	// are compressed, checksum data must be provided for uncompressed files as
	// well, even if not present on the server.  Client behaviour:
	//
	// Any file should be checked at least once, either in compressed or
	// uncompressed form, depending on which data is available. If a file has
	// no associated data, the client shall inform the user about this under
	// possibly dangerous situations (such as installing a package from that
	// repository). If a file does not match the data specified in the release
	// file, the client shall not use any information from that file, inform
	// the user, and might use old information (such as the previous locally
	// kept information) instead.
	MD5Sum []control.MD5FileHash    `delim:"\n" strip:" \t\n\r"`
	SHA1   []control.SHA1FileHash   `delim:"\n" strip:" \t\n\r"`
	SHA256 []control.SHA256FileHash `delim:"\n" strip:" \t\n\r"`

	// The NotAutomatic and ButAutomaticUpgrades fields are optional boolean
	// fields instructing the package manager. They may contain the values
	// "yes" and "no". If one the fields is not specified, this has the same
	// meaning as a value of "no".
	//
	// If a value of "yes" is specified for the NotAutomatic field, a package
	// manager should not install packages (or upgrade to newer versions) from
	// this repository without explicit user consent (APT assigns priority 1 to
	// this) If the field ButAutomaticUpgrades is specified as well and has the
	// value "yes", the package manager should automatically install package
	// upgrades from this repository, if the installed version of the package
	// is higher than the version of the package in other sources (APT assigns
	// priority 100).
	//
	// Specifying "yes" for ButAutomaticUpgrades without specifying "yes" for
	// NotAutomatic is invalid.
	NotAutomatic         string
	ButAutomaticUpgrades string
}

// Given a file declared in the Release file, get the FileHash entries
// for that file (MD5, SHA1, SHA256). These can be used to ensure the
// integrety of files in the archive.
func (r Release) Indices() map[string]control.FileHashes {
	ret := map[string]control.FileHashes{}
	for _, el := range r.MD5Sum {
		ret[el.Filename] = append(ret[el.Filename], el.FileHash)
	}
	for _, el := range r.SHA1 {
		ret[el.Filename] = append(ret[el.Filename], el.FileHash)
	}
	for _, el := range r.SHA256 {
		ret[el.Filename] = append(ret[el.Filename], el.FileHash)
	}
	return ret
}

// }}}

// LoadInRelease {{{

// Given an InRelease io.Reader, and the OpenPGP keyring
// to validate against, return the parsed InRelease file.
func LoadInRelease(in io.Reader, keyring *openpgp.EntityList) (*Release, error) {
	ret := Release{}
	decoder, err := control.NewDecoder(in, keyring)
	if err != nil {
		return nil, err
	}
	return &ret, decoder.Decode(&ret)
}

// }}}

// LoadInReleaseFile {{{

// Given a path to the InRelease file on the filesystem, and the OpenPGP keyring
// to validate against, return the parsed InRelease file.
func LoadInReleaseFile(path string, keyring *openpgp.EntityList) (*Release, error) {
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	return LoadInRelease(fd, keyring)
}

// }}}

// vim: foldmethod=marker
<commit_msg>add sha512, yolo<commit_after>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * 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 archive

import (
	"io"
	"os"

	"golang.org/x/crypto/openpgp"
	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
)

// Release {{{

// The file "dists/$DIST/InRelease" shall contain meta-information about the
// distribution and checksums for the indices, possibly signed with a GPG
// clearsign signature (for example created by "gpg -a -s --clearsign"). For
// older clients there can also be a "dists/$DIST/Release" file without any
// signature and the file "dists/$DIST/Release.gpg" with a detached GPG
// signature of the "Release" file, compatible with the format used by the GPG
// options "-a -b -s".
type Release struct {
	Description string

	// Optional field indicating the origin of the repository, a single line
	// of free form text.
	Origin string

	// Optional field including some kind of label, a single line of free form
	// text.
	//
	// Typically used extensively in repositories split over multiple media
	// such as repositories stored on CDs.
	Label string

	// The Version field, if specified, shall be the version of the release.
	// This is usually a sequence of integers separated by the character
	// "." (full stop).
	//
	// Example:
	//
	//   Version: 6.0
	Version string

	// The Suite field may describe the suite. A suite is a single word. In
	// Debian, this shall be one of oldstable, stable, testing, unstable,
	// or experimental; with optional suffixes such as -updates.
	//
	// Example:
	// //   Suite: stable
	Suite string

	// The Codename field shall describe the codename of the release. A
	// codename is a single word. Debian releases are codenamed after Toy
	// Story Characters, and the unstable suite has the codename sid, the
	// experimental suite has the codename experimental.
	//
	// Example:
	//
	//   Codename: squeeze
	Codename string

	// A whitespace separated list of areas.
	//
	// Example:
	//
	//   Components: main contrib non-free
	//
	// May also include be prefixed by parts of the path following the
	// directory beneath dists, if the Release file is not in a directory
	// directly beneath dists/. As an example, security updates are specified
	// in APT as:
	//
	// deb http://security.debian.org/ stable/updates main)
	//
	// The Release file would be located at
	// http://security.debian.org/dists/stable/updates/Release and look like:
	//
	//   Suite: stable
	//   Components: updates/main updates/contrib updates/non-free
	Components []string `delim:" "`

	// Whitespace separated unique single words identifying Debian machine
	// architectures as described in Architecture specification strings,
	// Section 11.1. Clients should ignore Architectures they do not know
	// about.
	Architectures []dependency.Arch

	// The Date field shall specify the time at which the Release file was
	// created. Clients updating a local on-disk cache should ignore a Release
	// file with an earlier date than the date in the already stored Release
	// file.
	//
	// The Valid-Until field may specify at which time the Release file should
	// be considered expired by the client. Client behaviour on expired Release
	// files is unspecified.
	//
	// The format of the dates is the same as for the Date field in .changes
	// files; and as used in debian/changelog files, and documented in Policy
	// 4.4 ( Debian changelog: debian/changelog)
	Date       string
	ValidUntil string `control:"Valid-Until"`

	// note the upper-case S in MD5Sum (unlike in Packages and Sources files)
	//
	// These fields are used for two purposes:
	//
	// describe what package index files are present when release signature is
	// available it certifies that listed index files and files referenced by
	// those index files are genuine Those fields shall be multi-line fields
	// containing multiple lines of whitespace separated data. Each line shall
	// contain
	//
	// The checksum of the file in the format corresponding to the field The
	// size of the file (integer >= 0) The filename relative to the directory
	// of the Release file Each datum must be separated by one or more
	// whitespace characters.
	//
	// Server requirements:
	//
	// The checksum and sizes shall match the actual existing files. If indexes
	// are compressed, checksum data must be provided for uncompressed files as
	// well, even if not present on the server.  Client behaviour:
	//
	// Any file should be checked at least once, either in compressed or
	// uncompressed form, depending on which data is available. If a file has
	// no associated data, the client shall inform the user about this under
	// possibly dangerous situations (such as installing a package from that
	// repository). If a file does not match the data specified in the release
	// file, the client shall not use any information from that file, inform
	// the user, and might use old information (such as the previous locally
	// kept information) instead.
	MD5Sum []control.MD5FileHash    `delim:"\n" strip:" \t\n\r"`
	SHA1   []control.SHA1FileHash   `delim:"\n" strip:" \t\n\r"`
	SHA256 []control.SHA256FileHash `delim:"\n" strip:" \t\n\r"`
	SHA512 []control.SHA512FileHash `delim:"\n" strip:" \t\n\r"`

	// The NotAutomatic and ButAutomaticUpgrades fields are optional boolean
	// fields instructing the package manager. They may contain the values
	// "yes" and "no". If one the fields is not specified, this has the same
	// meaning as a value of "no".
	//
	// If a value of "yes" is specified for the NotAutomatic field, a package
	// manager should not install packages (or upgrade to newer versions) from
	// this repository without explicit user consent (APT assigns priority 1 to
	// this) If the field ButAutomaticUpgrades is specified as well and has the
	// value "yes", the package manager should automatically install package
	// upgrades from this repository, if the installed version of the package
	// is higher than the version of the package in other sources (APT assigns
	// priority 100).
	//
	// Specifying "yes" for ButAutomaticUpgrades without specifying "yes" for
	// NotAutomatic is invalid.
	NotAutomatic         string
	ButAutomaticUpgrades string
}

// Given a file declared in the Release file, get the FileHash entries
// for that file (MD5, SHA1, SHA256). These can be used to ensure the
// integrety of files in the archive.
func (r Release) Indices() map[string]control.FileHashes {
	ret := map[string]control.FileHashes{}
	for _, el := range r.MD5Sum {
		ret[el.Filename] = append(ret[el.Filename], el.FileHash)
	}
	for _, el := range r.SHA1 {
		ret[el.Filename] = append(ret[el.Filename], el.FileHash)
	}
	for _, el := range r.SHA256 {
		ret[el.Filename] = append(ret[el.Filename], el.FileHash)
	}
	return ret
}

// }}}

// LoadInRelease {{{

// Given an InRelease io.Reader, and the OpenPGP keyring
// to validate against, return the parsed InRelease file.
func LoadInRelease(in io.Reader, keyring *openpgp.EntityList) (*Release, error) {
	ret := Release{}
	decoder, err := control.NewDecoder(in, keyring)
	if err != nil {
		return nil, err
	}
	return &ret, decoder.Decode(&ret)
}

// }}}

// LoadInReleaseFile {{{

// Given a path to the InRelease file on the filesystem, and the OpenPGP keyring
// to validate against, return the parsed InRelease file.
func LoadInReleaseFile(path string, keyring *openpgp.EntityList) (*Release, error) {
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	return LoadInRelease(fd, keyring)
}

// }}}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>package datachannel

import (
	"sync"

	"github.com/chzyer/flow"
	"gopkg.in/logex.v1"
)

type Server struct {
	flow           *flow.Flow
	delegate       SvrDelegate
	listeners      []*Listener
	mutex          sync.Mutex
	onListenerExit chan struct{}
}

func NewServer(f *flow.Flow, d SvrDelegate) *Server {
	m := &Server{
		flow:           f,
		delegate:       d,
		onListenerExit: make(chan struct{}, 1),
	}
	return m
}

func (m *Server) GetDataChannel() int {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	for _, ln := range m.listeners {
		if ln != nil {
			return ln.GetPort()
		}
	}
	return -1
}

func (m *Server) GetAllDataChannel() []int {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	ret := make([]int, len(m.listeners))
	for idx, ln := range m.listeners {
		// BUG(chzyer): ln can be nil
		ret[idx] = ln.GetPort()
	}
	return ret
}

func (m *Server) Start(n int) {
	m.flow.Add(1)
	defer m.flow.DoneAndClose()

	started := 0
loop:
	for !m.flow.IsClosed() {
		if started < n {
			m.AddChannelListener()
			started++
		} else {
			select {
			case <-m.flow.IsClose():
				break loop
			case <-m.onListenerExit:
			}
		}
	}
}

func (m *Server) removeListener(idx int) {
	m.mutex.Lock()
	m.listeners[idx] = nil
	m.mutex.Unlock()
	select {
	case m.onListenerExit <- struct{}{}:
	default:
	}
}

func (m *Server) findNewSlot() int {
	for idx, ln := range m.listeners {
		if ln == nil {
			return idx
		}
	}
	m.listeners = append(m.listeners, nil)
	return len(m.listeners) - 1
}

func (m *Server) AddChannelListener() error {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	idx := m.findNewSlot()
	ln, err := NewListener(m.flow, m.delegate, func() {
		m.removeListener(idx)
	})
	if err != nil {
		return logex.Trace(err)
	}
	m.listeners[idx] = ln

	go ln.Serve()
	return nil
}
<commit_msg>fix crash<commit_after>package datachannel

import (
	"sync"

	"github.com/chzyer/flow"
	"gopkg.in/logex.v1"
)

type Server struct {
	flow           *flow.Flow
	delegate       SvrDelegate
	listeners      []*Listener
	mutex          sync.Mutex
	onListenerExit chan struct{}
}

func NewServer(f *flow.Flow, d SvrDelegate) *Server {
	m := &Server{
		flow:           f,
		delegate:       d,
		onListenerExit: make(chan struct{}, 1),
	}
	return m
}

func (m *Server) GetDataChannel() int {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	for _, ln := range m.listeners {
		if ln != nil {
			return ln.GetPort()
		}
	}
	return -1
}

func (m *Server) GetAllDataChannel() []int {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	ret := make([]int, 0, len(m.listeners))
	for _, ln := range m.listeners {
		// BUG(chzyer): ln can be nil
		if ln != nil {
			ret = append(ret, ln.GetPort())
		}
	}
	return ret
}

func (m *Server) Start(n int) {
	m.flow.Add(1)
	defer m.flow.DoneAndClose()

	started := 0
loop:
	for !m.flow.IsClosed() {
		if started < n {
			m.AddChannelListener()
			started++
		} else {
			select {
			case <-m.flow.IsClose():
				break loop
			case <-m.onListenerExit:
			}
		}
	}
}

func (m *Server) removeListener(idx int) {
	m.mutex.Lock()
	m.listeners[idx] = nil
	m.mutex.Unlock()
	select {
	case m.onListenerExit <- struct{}{}:
	default:
	}
}

func (m *Server) findNewSlot() int {
	for idx, ln := range m.listeners {
		if ln == nil {
			return idx
		}
	}
	m.listeners = append(m.listeners, nil)
	return len(m.listeners) - 1
}

func (m *Server) AddChannelListener() error {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	idx := m.findNewSlot()
	ln, err := NewListener(m.flow, m.delegate, func() {
		m.removeListener(idx)
	})
	if err != nil {
		return logex.Trace(err)
	}
	m.listeners[idx] = ln

	go ln.Serve()
	return nil
}
<|endoftext|>


<commit_before>package mongodb

import (
	"errors"
	"flag"
	"net/url"
	"os"

	"github.com/davecgh/go-spew/spew"
	"github.com/microservices-demo/user/users"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

var (
	name            string
	password        string
	host            string
	db              = "users"
	ErrInvalidHexID = errors.New("Invalid Id Hex")
)

func init() {
	flag.StringVar(&name, "mongo-user", os.Getenv("MONGO_USER"), "Mongo user")
	flag.StringVar(&password, "mongo-password", os.Getenv("MONGO_PASS"), "Mongo password")
	flag.StringVar(&host, "mongo-host", os.Getenv("MONGO_HOST"), "Mongo host")
}

type Mongo struct {
	Session *mgo.Session
}

type MongoUser struct {
	*users.User
	ID         bson.ObjectId   `bson:"_id"`
	AddressIDs []bson.ObjectId `bson:"addresses"`
	CardIDs    []bson.ObjectId `bson:"cards"`
}
type MongoAddress struct {
	users.Address
	ID bson.ObjectId `bson:"_id"`
}
type MongoCard struct {
	users.Card
	ID bson.ObjectId `bson:"_id"`
}

func (m Mongo) Init() error {
	u := getURL()
	var err error
	m.Session, err = mgo.Dial(u.String())
	if err != nil {
		return err
	}
	return m.EnsureIndexes()
}

func (m Mongo) Create(u *users.User) error {
	s := m.Session.Copy()
	defer s.Close()
	id := bson.NewObjectId()
	mu := MongoUser{User: u, ID: id}
	var carderr error
	var addrerr error
	mu.CardIDs, carderr = m.createCards(u.Cards)
	mu.AddressIDs, addrerr = m.createAddresses(u.Addresses)
	c := s.DB("").C("customers")
	_, err := c.UpsertId(mu.ID, mu)
	if err != nil {
		// Gonna clean up if we can, ignore error
		// because the user save error takes precedence.
		m.cleanAttributes(mu)
		return err
	}
	mu.User.UserID = mu.ID.Hex()
	spew.Dump(carderr)
	spew.Dump(addrerr)
	return nil
}

func (m Mongo) createCards(cs []users.Card) ([]bson.ObjectId, error) {
	s := m.Session.Copy()
	ids := make([]bson.ObjectId, 0)
	defer s.Close()
	for k, ca := range cs {
		id := bson.NewObjectId()
		mc := MongoCard{Card: ca, ID: id}
		c := s.DB("").C("cards")
		_, err := c.UpsertId(mc.ID, mc)
		if err != nil {
			return ids, err
		}
		ids = append(ids, id)
		cs[k].ID = id.String()
	}
	return ids, nil
}

func (m Mongo) createAddresses(as []users.Address) ([]bson.ObjectId, error) {
	ids := make([]bson.ObjectId, 0)
	s := m.Session.Copy()
	defer s.Close()
	for k, a := range as {
		id := bson.NewObjectId()
		ma := MongoAddress{Address: a, ID: id}
		c := s.DB("").C("addresses")
		_, err := c.UpsertId(ma.ID, ma)
		if err != nil {
			return ids, err
		}
		ids = append(ids, id)
		as[k].ID = id.String()
	}
	return ids, nil
}

func (m Mongo) cleanAttributes(mu MongoUser) error {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("addresses")
	_, err := c.RemoveAll(bson.M{"_id": bson.M{"$in": mu.AddressIDs}})
	return err
}

func (m Mongo) GetByName(name string) (users.User, error) {
	return users.User{}, nil
}

func (m Mongo) GetByID(name string) (users.User, error) {
	return users.User{}, nil
}

func (m Mongo) GetAttributes(u *users.User) error {
	return nil
}

func getURL() url.URL {
	u := url.UserPassword(name, password)
	return url.URL{
		Scheme: "mongodb",
		User:   u,
		Host:   host,
		Path:   db,
	}
}

func (m Mongo) EnsureIndexes() error {
	s := m.Session.Copy()
	defer s.Close()
	i := mgo.Index{
		Key:        []string{"username"},
		Unique:     true,
		DropDups:   true,
		Background: true,
		Sparse:     false,
	}
	c := s.DB("").C("users")
	return c.EnsureIndex(i)
}
<commit_msg>added attributes retrieval<commit_after>package mongodb

import (
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"

	"github.com/microservices-demo/user/users"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

var (
	name                string
	password            string
	host                string
	db                  = "users"
	ErrInvalidHexID     = errors.New("Invalid Id Hex")
	ErrorSavingCardData = errors.New("There was a problem saving some card data")
	ErrorSavingAddrData = errors.New("There was a problem saving some address data")
)

func init() {
	flag.StringVar(&name, "mongo-user", os.Getenv("MONGO_USER"), "Mongo user")
	flag.StringVar(&password, "mongo-password", os.Getenv("MONGO_PASS"), "Mongo password")
	flag.StringVar(&host, "mongo-host", os.Getenv("MONGO_HOST"), "Mongo host")
}

type Mongo struct {
	Session *mgo.Session
}

type MongoUser struct {
	*users.User
	ID         bson.ObjectId   `bson:"_id"`
	AddressIDs []bson.ObjectId `bson:"addresses"`
	CardIDs    []bson.ObjectId `bson:"cards"`
}

func New() MongoUser {
	u := users.New()
	return MongoUser{
		User:       &u,
		AddressIDs: make([]bson.ObjectId, 0),
		CardIDs:    make([]bson.ObjectId, 0),
	}
}

func (mu MongoUser) AddUserIDs() {
	u := users.New()
	if mu.User == nil {
		mu.User = &u
		return
	}
	for _, id := range mu.AddressIDs {
		mu.User.Addresses = append(mu.User.Addresses, users.Address{
			ID: id.String(),
		})
	}
	for _, id := range mu.CardIDs {
		mu.User.Cards = append(mu.User.Cards, users.Card{ID: id.String()})
	}
}

type MongoAddress struct {
	users.Address
	ID bson.ObjectId `bson:"_id"`
}
type MongoCard struct {
	users.Card
	ID bson.ObjectId `bson:"_id"`
}

func (m Mongo) Init() error {
	u := getURL()
	var err error
	m.Session, err = mgo.Dial(u.String())
	if err != nil {
		return err
	}
	return m.EnsureIndexes()
}

func (m Mongo) Create(u *users.User) error {
	s := m.Session.Copy()
	defer s.Close()
	id := bson.NewObjectId()
	mu := New()
	mu.User = u
	mu.ID = id
	var carderr error
	var addrerr error
	mu.CardIDs, carderr = m.createCards(u.Cards)
	mu.AddressIDs, addrerr = m.createAddresses(u.Addresses)
	c := s.DB("").C("customers")
	_, err := c.UpsertId(mu.ID, mu)
	if err != nil {
		// Gonna clean up if we can, ignore error
		// because the user save error takes precedence.
		m.cleanAttributes(mu)
		return err
	}
	mu.User.UserID = mu.ID.Hex()
	// Cheap err for attributes
	if carderr != nil || addrerr != nil {
		return fmt.Errorf("%v %v", carderr, addrerr)
	}
	return nil
}

func (m Mongo) createCards(cs []users.Card) ([]bson.ObjectId, error) {
	s := m.Session.Copy()
	ids := make([]bson.ObjectId, 0)
	defer s.Close()
	for k, ca := range cs {
		id := bson.NewObjectId()
		mc := MongoCard{Card: ca, ID: id}
		c := s.DB("").C("cards")
		_, err := c.UpsertId(mc.ID, mc)
		if err != nil {
			return ids, err
		}
		ids = append(ids, id)
		cs[k].ID = id.String()
	}
	return ids, nil
}

func (m Mongo) createAddresses(as []users.Address) ([]bson.ObjectId, error) {
	ids := make([]bson.ObjectId, 0)
	s := m.Session.Copy()
	defer s.Close()
	for k, a := range as {
		id := bson.NewObjectId()
		ma := MongoAddress{Address: a, ID: id}
		c := s.DB("").C("addresses")
		_, err := c.UpsertId(ma.ID, ma)
		if err != nil {
			return ids, err
		}
		ids = append(ids, id)
		as[k].ID = id.String()
	}
	return ids, nil
}

func (m Mongo) cleanAttributes(mu MongoUser) error {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("addresses")
	_, err := c.RemoveAll(bson.M{"_id": bson.M{"$in": mu.AddressIDs}})
	return err
}

func (m Mongo) GetByName(name string) (users.User, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("customers")
	mu := New()
	err := c.Find(bson.M{"username": name}).One(mu)
	mu.AddUserIDs()
	return *mu.User, err
}

func (m Mongo) GetByID(id string) (users.User, error) {
	s := m.Session.Copy()
	defer s.Close()
	if !bson.IsObjectIdHex(id) {
		return users.New(), errors.New("Invalid Id Hex")
	}
	c := s.DB("").C("customers")
	mu := New()
	err := c.FindId(bson.ObjectIdHex(id)).One(&mu)
	mu.AddUserIDs()
	return *mu.User, err
}

func (m Mongo) GetAttributes(u *users.User) error {
	s := m.Session.Copy()
	defer s.Close()
	ids := make([]bson.ObjectId, 0)
	for _, a := range u.Addresses {
		if !bson.IsObjectIdHex(a.ID) {
			return ErrInvalidHexID
		}
		ids = append(ids, bson.ObjectIdHex(a.ID))
	}
	var ma []MongoAddress
	c := s.DB("").C("addresses")
	err := c.Find(bson.M{"_id": bson.M{"$in": ids}}).All(&ma)
	if err != nil {
		return err
	}
	na := make([]users.Address, 0)
	for _, a := range ma {
		a.Address.ID = a.ID.String()
		na = append(na, a.Address)
	}
	u.Addresses = na

	ids = make([]bson.ObjectId, 0)
	for _, c := range u.Cards {
		if !bson.IsObjectIdHex(c.ID) {
			return ErrInvalidHexID
		}
		ids = append(ids, bson.ObjectIdHex(c.ID))
	}
	var mc []MongoCard
	c = s.DB("").C("cards")
	err = c.Find(bson.M{"_id": bson.M{"$in": ids}}).All(&mc)
	if err != nil {
		return err
	}

	nc := make([]users.Card, 0)
	for _, ca := range mc {
		ca.Card.ID = ca.ID.String()
		nc = append(nc, ca.Card)
	}
	u.Cards = nc
	return nil
}

func getURL() url.URL {
	u := url.UserPassword(name, password)
	return url.URL{
		Scheme: "mongodb",
		User:   u,
		Host:   host,
		Path:   db,
	}
}

func (m Mongo) EnsureIndexes() error {
	s := m.Session.Copy()
	defer s.Close()
	i := mgo.Index{
		Key:        []string{"username"},
		Unique:     true,
		DropDups:   true,
		Background: true,
		Sparse:     false,
	}
	c := s.DB("").C("users")
	return c.EnsureIndex(i)
}
<|endoftext|>


<commit_before>package restpc

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"time"
)

type Request interface {
	HTTP() *http.Request
	RemoteIP() (string, error)
	URL() *url.URL
	Host() string
	HandlerName() string

	Body() ([]byte, error)
	BodyMap() (map[string]interface{}, error)
	BodyTo(model interface{}) error

	GetHeader(string) string

	GetString(key string, sources ...FromX) (*string, error)
	GetStringList(key string, sources ...FromX) ([]string, error)
	GetInt(key string, sources ...FromX) (*int, error)
	GetFloat(key string, sources ...FromX) (*float64, error)
	GetBool(key string, sources ...FromX) (*bool, error)
	GetTime(key string, sources ...FromX) (*time.Time, error)

	FullMap() map[string]interface{}
}

type FromX interface {
	GetString(req Request, key string) (*string, error)
	GetStringList(req Request, key string) ([]string, error)
	GetInt(req Request, key string) (*int, error)
	GetFloat(req Request, key string) (*float64, error)
	GetBool(req Request, key string) (*bool, error)
	GetTime(req Request, key string) (*time.Time, error)
}

var DefaultParamSources = []FromX{
	FromBody,
	FromForm,
	// FromContext,
	FromEmpty,
}

type requestImp struct {
	r           *http.Request // must be set initially
	handlerName string        // must be set initially
	body        []byte
	bodyErr     error
	bodyMap     map[string]interface{}
	bodyMapErr  error
}

func (req *requestImp) HTTP() *http.Request {
	return req.r
}

func (req *requestImp) RemoteIP() (string, error) {
	remoteIp, _, err := net.SplitHostPort(req.r.RemoteAddr)
	if err != nil {
		return "", NewError(
			Internal, "", err,
			"r.RemoteAddr", req.r.RemoteAddr,
		)
	}
	return remoteIp, nil
}

func (req *requestImp) URL() *url.URL {
	u := *req.r.URL
	return &u
}

func (req *requestImp) Host() string {
	return req.r.Host
}

func (req *requestImp) HandlerName() string {
	return req.handlerName
}

func (req *requestImp) Body() ([]byte, error) {
	if req.body != nil {
		return req.body, nil
	}
	if req.bodyErr != nil {
		return nil, req.bodyErr
	}
	if req.r.Body == nil {
		return nil, nil
	}
	body, err := ioutil.ReadAll(req.r.Body)
	if err != nil {
		req.bodyErr = err
		log.Println(err)
	}
	req.body = body
	req.r.Body.Close()
	req.r.Body = nil
	return body, nil
}

func (req *requestImp) BodyMap() (map[string]interface{}, error) {
	if req.bodyMap != nil {
		return req.bodyMap, nil
	}
	if req.bodyMapErr != nil {
		return nil, req.bodyMapErr
	}
	data := map[string]interface{}{}
	body, err := req.Body()
	if err != nil {
		req.bodyMapErr = err
		return nil, err
	}
	if len(body) > 0 {
		err = json.Unmarshal(body, &data)
		if err != nil {
			req.bodyMapErr = err
			log.Println(err)
			// return nil, err // FIXME
		}
	}
	req.bodyMap = data
	return data, nil
}

func (req *requestImp) BodyTo(model interface{}) error {
	body, err := req.Body()
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, model)
	if err != nil {
		return NewError(InvalidArgument, "request body is not a valid json", err)
	}
	return nil
}

func (req *requestImp) GetHeader(key string) string {
	return req.r.Header.Get(key)
}

func (req *requestImp) GetString(key string, sources ...FromX) (*string, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetString(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetStringList(key string, sources ...FromX) ([]string, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetStringList(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetInt(key string, sources ...FromX) (*int, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetInt(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetFloat(key string, sources ...FromX) (*float64, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetFloat(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetBool(key string, sources ...FromX) (*bool, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetBool(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetTime(key string, sources ...FromX) (*time.Time, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetTime(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) HeaderCopy() http.Header {
	header := http.Header{}
	for key, values := range req.r.Header {
		header[key] = values
	}
	return header
}

func (req *requestImp) HeaderStrippedAuth() http.Header {
	header := req.HeaderCopy()
	authHader, ok := header["Authorization"]
	if ok {
		authHaderNew := make([]string, len(authHader))
		for i := 0; i < len(authHader); i++ {
			authHaderNew[i] = "[REMOVED]"
		}
		header["Authorization"] = authHaderNew
	}
	return header
}

func (req *requestImp) FullMap() map[string]interface{} {
	bodyMap, _ := req.BodyMap()
	urlStr := req.URL().String()
	remoteIP, _ := req.RemoteIP()
	return map[string]interface{}{
		"bodyMap":  bodyMap,
		"url":      urlStr,
		"form":     req.r.Form,
		"header":   req.HeaderStrippedAuth(),
		"remoteIP": remoteIP,
	}
}
<commit_msg>comment out FromEmpty in DefaultParamSources, makes every param optional<commit_after>package restpc

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"time"
)

type Request interface {
	HTTP() *http.Request
	RemoteIP() (string, error)
	URL() *url.URL
	Host() string
	HandlerName() string

	Body() ([]byte, error)
	BodyMap() (map[string]interface{}, error)
	BodyTo(model interface{}) error

	GetHeader(string) string

	GetString(key string, sources ...FromX) (*string, error)
	GetStringList(key string, sources ...FromX) ([]string, error)
	GetInt(key string, sources ...FromX) (*int, error)
	GetFloat(key string, sources ...FromX) (*float64, error)
	GetBool(key string, sources ...FromX) (*bool, error)
	GetTime(key string, sources ...FromX) (*time.Time, error)

	FullMap() map[string]interface{}
}

type FromX interface {
	GetString(req Request, key string) (*string, error)
	GetStringList(req Request, key string) ([]string, error)
	GetInt(req Request, key string) (*int, error)
	GetFloat(req Request, key string) (*float64, error)
	GetBool(req Request, key string) (*bool, error)
	GetTime(req Request, key string) (*time.Time, error)
}

var DefaultParamSources = []FromX{
	FromBody,
	FromForm,
	// FromContext, // I don't have any use case for it, enable if you want
	// FromEmpty, // makes every param optional, enable if you want
}

type requestImp struct {
	r           *http.Request // must be set initially
	handlerName string        // must be set initially
	body        []byte
	bodyErr     error
	bodyMap     map[string]interface{}
	bodyMapErr  error
}

func (req *requestImp) HTTP() *http.Request {
	return req.r
}

func (req *requestImp) RemoteIP() (string, error) {
	remoteIp, _, err := net.SplitHostPort(req.r.RemoteAddr)
	if err != nil {
		return "", NewError(
			Internal, "", err,
			"r.RemoteAddr", req.r.RemoteAddr,
		)
	}
	return remoteIp, nil
}

func (req *requestImp) URL() *url.URL {
	u := *req.r.URL
	return &u
}

func (req *requestImp) Host() string {
	return req.r.Host
}

func (req *requestImp) HandlerName() string {
	return req.handlerName
}

func (req *requestImp) Body() ([]byte, error) {
	if req.body != nil {
		return req.body, nil
	}
	if req.bodyErr != nil {
		return nil, req.bodyErr
	}
	if req.r.Body == nil {
		return nil, nil
	}
	body, err := ioutil.ReadAll(req.r.Body)
	if err != nil {
		req.bodyErr = err
		log.Println(err)
	}
	req.body = body
	req.r.Body.Close()
	req.r.Body = nil
	return body, nil
}

func (req *requestImp) BodyMap() (map[string]interface{}, error) {
	if req.bodyMap != nil {
		return req.bodyMap, nil
	}
	if req.bodyMapErr != nil {
		return nil, req.bodyMapErr
	}
	data := map[string]interface{}{}
	body, err := req.Body()
	if err != nil {
		req.bodyMapErr = err
		return nil, err
	}
	if len(body) > 0 {
		err = json.Unmarshal(body, &data)
		if err != nil {
			req.bodyMapErr = err
			log.Println(err)
			// return nil, err // FIXME
		}
	}
	req.bodyMap = data
	return data, nil
}

func (req *requestImp) BodyTo(model interface{}) error {
	body, err := req.Body()
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, model)
	if err != nil {
		return NewError(InvalidArgument, "request body is not a valid json", err)
	}
	return nil
}

func (req *requestImp) GetHeader(key string) string {
	return req.r.Header.Get(key)
}

func (req *requestImp) GetString(key string, sources ...FromX) (*string, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetString(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetStringList(key string, sources ...FromX) ([]string, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetStringList(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetInt(key string, sources ...FromX) (*int, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetInt(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetFloat(key string, sources ...FromX) (*float64, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetFloat(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetBool(key string, sources ...FromX) (*bool, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetBool(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) GetTime(key string, sources ...FromX) (*time.Time, error) {
	if len(sources) == 0 {
		sources = DefaultParamSources
	}
	for _, source := range sources {
		value, err := source.GetTime(req, key)
		if err != nil {
			return nil, err
		}
		if value != nil {
			return value, nil
		}
	}
	return nil, NewError(
		MissingArgument,
		fmt.Sprintf("missing '%v'", key),
		nil,
	)
}

func (req *requestImp) HeaderCopy() http.Header {
	header := http.Header{}
	for key, values := range req.r.Header {
		header[key] = values
	}
	return header
}

func (req *requestImp) HeaderStrippedAuth() http.Header {
	header := req.HeaderCopy()
	authHader, ok := header["Authorization"]
	if ok {
		authHaderNew := make([]string, len(authHader))
		for i := 0; i < len(authHader); i++ {
			authHaderNew[i] = "[REMOVED]"
		}
		header["Authorization"] = authHaderNew
	}
	return header
}

func (req *requestImp) FullMap() map[string]interface{} {
	bodyMap, _ := req.BodyMap()
	urlStr := req.URL().String()
	remoteIP, _ := req.RemoteIP()
	return map[string]interface{}{
		"bodyMap":  bodyMap,
		"url":      urlStr,
		"form":     req.r.Form,
		"header":   req.HeaderStrippedAuth(),
		"remoteIP": remoteIP,
	}
}
<|endoftext|>