<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/signal"

	"github.com/gabstv/sandpiper/route"
	"github.com/gabstv/sandpiper/server"
	"github.com/gabstv/sandpiper/util"
	"github.com/mattn/go-colorable"
	"github.com/mgutz/ansi"
	"gopkg.in/yaml.v2"
)

var (
	configfile string
	stdout     io.Writer
	stderr     io.Writer
)

func main() {
	stdout = colorable.NewColorableStdout()
	stderr = colorable.NewColorableStderr()
	fmt.Fprintln(stdout, ansi.Color("\nSANDPIPER 1.1.2\n", "green"))
	// init flags
	flag.Parse()
	// print help if needed
	printHelp()
	// find config
	findConfigFile()

	bs, err := ioutil.ReadFile(configfile)
	if err != nil {
		fmt.Fprintf(stderr, "ERROR: Unable to load the config file at %v!\n%v\n",
			ansi.Color(configfile, "red"),
			ansi.Color(err.Error(), "red"))
		os.Exit(1)
	}

	cfg := &Config{}
	err = yaml.Unmarshal(bs, cfg)
	if err != nil {
		fmt.Fprintf(stderr, "ERROR: Unable to unmarshal the config file at %v!\n%v\n",
			ansi.Color(configfile, "red"),
			ansi.Color(err.Error(), "red"))
		os.Exit(1)
	}

	// unpack Config
	svCfg := &server.Config{}
	svCfg.Debug = cfg.Debug
	if svCfg.Debug {
		util.DEBUG = true
	}
	svCfg.ListenAddr = cfg.ListenAddr
	svCfg.ListenAddrTLS = cfg.ListenAddrTLS
	svCfg.DisableTLS = cfg.DisableTLS
	svCfg.NumCPU = cfg.NumCPU
	svCfg.FallbackDomain = cfg.FallbackDomain
	svCfg.Graceful = cfg.Graceful
	svCfg.CachePath = cfg.CachePath
	svCfg.APIListen = cfg.APIListen
	svCfg.APIKey = cfg.APIKey
	svCfg.APIDomain = cfg.APIDomain
	svCfg.APIDomainAutocert = cfg.APIDomainAutocert

	// ENV VARS
	if vv := os.Getenv("DEBUG"); vv != "" {
		svCfg.Debug = (vv == "1")
	}
	if vv := os.Getenv("LISTEN"); vv != "" {
		svCfg.ListenAddr = vv
	}
	if vv := os.Getenv("LISTEN_TLS"); vv != "" {
		svCfg.ListenAddrTLS = vv
	}
	if vv := os.Getenv("FALLBACK_DOMAIN"); vv != "" {
		svCfg.FallbackDomain = vv
	}

	if vv := os.Getenv("API_LISTEN"); vv != "" {
		svCfg.APIListen = vv
	}
	if vv := os.Getenv("API_KEY"); vv != "" {
		svCfg.APIKey = vv
	}
	if vv := os.Getenv("API_DOMAIN"); vv != "" {
		svCfg.APIDomain = vv
	}
	if vv := os.Getenv("API_DOMAIN_AUTOCERT"); vv != "" {
		svCfg.APIDomainAutocert = (vv == "1")
	}
	if vv := os.Getenv("LETSENCRYPT_URL"); vv != "" {
		svCfg.LetsEncryptURL = vv
	}
	if vv := os.Getenv("DISABLE_TLS"); vv != "" {
		svCfg.DisableTLS = (vv == "1")
	}

	s := server.Default(svCfg)

	for _, v := range cfg.Routes {
		r := route.Route{}
		// apply Websockets config
		r.WsCFG = v.Websockets
		if ct, ok := unpackConnType(v.OutgoingServerConnType); ok {
			r.Server.OutConnType = ct
		} else {
			fmt.Fprintf(stderr, "\n CONFIGURATION ERROR\nDomain: %v\n ERR: %v\n",
				ansi.Color(v.Domain, "yellow"),
				ansi.Color("Invalid conn type "+v.OutgoingServerConnType, "red"))
			os.Exit(1)
		}
		r.Domain = v.Domain
		r.Server.OutAddress = v.OutgoingServerAddress
		r.Certificate.CertFile = v.TLSCertFile
		r.Certificate.KeyFile = v.TLSKeyFile
		r.Autocert = v.Autocert
		r.AuthMode = v.AuthMode
		r.AuthKey = v.AuthKey
		r.AuthValue = v.AuthValue
		r.ForceHTTPS = v.ForceHTTPS
		err = s.Add(r)
		if err != nil {
			fmt.Fprintf(stderr, "\nERROR: Could not add route %v\n%v\n",
				ansi.Color(v.Domain, "yellow"),
				ansi.Color(err.Error(), "red"))
			os.Exit(1)
		}
		fmt.Fprintf(stdout, "%v: %v\n",
			ansi.Color("Domain added", "green"),
			v.Domain)
	}
	//
	if cfg.Debug {
		fmt.Fprintf(stdout, "%v DEBUG MODE IS %v\n",
			ansi.Color("WARNING:", "yellow"),
			ansi.Color("ON", "green"))
	}
	//
	// Close if received signal
	go func() {
		sigchan := make(chan os.Signal, 1)
		signal.Notify(sigchan, os.Interrupt, os.Kill)
		<-sigchan
		s.Close()
	}()
	//
	err = s.Run()
	if err != nil {
		fmt.Fprintf(stderr, "ERROR: %v\n",
			ansi.Color(err.Error(), "red"))
		os.Exit(1)
	}
}

func printHelp() {
	if len(flag.Args()) < 1 {
		return
	}
	if flag.Arg(0) != "help" {
		return
	}
	fmt.Fprintln(stdout, ansi.Color("Usage:", "blue"))
	fmt.Fprintf(stdout, "sandpiper %v\n", ansi.Color("config.yml", "green"))
	fmt.Fprintf(stdout, "sandpiper %v\n", ansi.Color("help", "green"))
	os.Exit(0)
}

func findConfigFile() {
	if len(flag.Args()) > 0 {
		configfile = flag.Arg(0)
		return
	}
	if env := os.Getenv("SANDPIPER_CONFIG"); env != "" {
		configfile = env
		return
	}
	if _, err := os.Stat("config.yml"); err == nil {
		configfile = "config.yml"
		return
	}
	// no config file found!
	fmt.Fprint(stderr, ansi.Color("No configuration file found!\n\n", "red"))
	fmt.Println(`There are three different ways to fix this:
  - Pass it as a parameter:
     sandpiper /path/to/config.yml
  - Set the env var SANDPIPER_CONFIG
  - Have a config.yml in the current working directory`)
	fmt.Fprintf(stdout, "\nrun %v fo more info.\n", ansi.Color("sandpiper help", "blue"))
	os.Exit(1)
}

func unpackConnType(input string) (route.ConnType, bool) {
	if input == "HTTP" {
		return route.HTTP, true
	}
	if input == "HTTPS" || input == "HTTPS_VERIFY" {
		return route.HTTPS_VERIFY, true
	}
	if input == "HTTPS_SKIP_VERIFY" {
		return route.HTTPS_SKIP_VERIFY, true
	}
	if input == "REDIRECT" {
		return route.REDIRECT, true
	}
	return route.HTTP, false
}

// Config main config structure (yml)
type Config struct {
	Debug             bool          `yaml:"debug"`
	NumCPU            int           `yaml:"num_cpu"`
	ListenAddr        string        `yaml:"listen_addr"`
	ListenAddrTLS     string        `yaml:"listen_addr_tls"`
	DisableTLS        bool          `yaml:"disable_tls"`
	Routes            []ConfigRoute `yaml:"routes"`
	FallbackDomain    string        `yaml:"fallback_domain"`
	Graceful          bool          `yaml:"graceful"`
	CachePath         string        `yaml:"cache_path"`
	APIListen         string        `yaml:"api_listen"`
	APIKey            string        `yaml:"api_key"`
	APIDomain         string        `yaml:"api_domain"`
	APIDomainAutocert bool          `yaml:"api_domain_autocert"`
}

// ConfigRoute represents a domain route
type ConfigRoute struct {
	Domain                 string        `yaml:"domain"`
	OutgoingServerConnType string        `yaml:"out_conn_type"`
	OutgoingServerAddress  string        `yaml:"out_addr"`
	TLSCertFile            string        `yaml:"tls_cert_file"`
	TLSKeyFile             string        `yaml:"tls_key_file"`
	Autocert               bool          `yaml:"autocert"`
	Websockets             util.WsConfig `yaml:"websockets"`
	AuthMode               string        `yaml:"auth_mode"`
	AuthKey                string        `yaml:"auth_key"`
	AuthValue              string        `yaml:"auth_value"`
	ForceHTTPS             bool          `yaml:"force_https"`
}
<commit_msg>1.1.3<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/signal"

	"github.com/gabstv/sandpiper/route"
	"github.com/gabstv/sandpiper/server"
	"github.com/gabstv/sandpiper/util"
	"github.com/mattn/go-colorable"
	"github.com/mgutz/ansi"
	"gopkg.in/yaml.v2"
)

var (
	configfile string
	stdout     io.Writer
	stderr     io.Writer
)

func main() {
	stdout = colorable.NewColorableStdout()
	stderr = colorable.NewColorableStderr()
	fmt.Fprintln(stdout, ansi.Color("\nSANDPIPER 1.1.3\n", "green"))
	// init flags
	flag.Parse()
	// print help if needed
	printHelp()
	// find config
	findConfigFile()

	bs, err := ioutil.ReadFile(configfile)
	if err != nil {
		fmt.Fprintf(stderr, "ERROR: Unable to load the config file at %v!\n%v\n",
			ansi.Color(configfile, "red"),
			ansi.Color(err.Error(), "red"))
		os.Exit(1)
	}

	cfg := &Config{}
	err = yaml.Unmarshal(bs, cfg)
	if err != nil {
		fmt.Fprintf(stderr, "ERROR: Unable to unmarshal the config file at %v!\n%v\n",
			ansi.Color(configfile, "red"),
			ansi.Color(err.Error(), "red"))
		os.Exit(1)
	}

	// unpack Config
	svCfg := &server.Config{}
	svCfg.Debug = cfg.Debug
	if svCfg.Debug {
		util.DEBUG = true
	}
	svCfg.ListenAddr = cfg.ListenAddr
	svCfg.ListenAddrTLS = cfg.ListenAddrTLS
	svCfg.DisableTLS = cfg.DisableTLS
	svCfg.NumCPU = cfg.NumCPU
	svCfg.FallbackDomain = cfg.FallbackDomain
	svCfg.Graceful = cfg.Graceful
	svCfg.CachePath = cfg.CachePath
	svCfg.APIListen = cfg.APIListen
	svCfg.APIKey = cfg.APIKey
	svCfg.APIDomain = cfg.APIDomain
	svCfg.APIDomainAutocert = cfg.APIDomainAutocert

	// ENV VARS
	if vv := os.Getenv("DEBUG"); vv != "" {
		svCfg.Debug = (vv == "1")
	}
	if vv := os.Getenv("LISTEN"); vv != "" {
		svCfg.ListenAddr = vv
	}
	if vv := os.Getenv("LISTEN_TLS"); vv != "" {
		svCfg.ListenAddrTLS = vv
	}
	if vv := os.Getenv("FALLBACK_DOMAIN"); vv != "" {
		svCfg.FallbackDomain = vv
	}

	if vv := os.Getenv("API_LISTEN"); vv != "" {
		svCfg.APIListen = vv
	}
	if vv := os.Getenv("API_KEY"); vv != "" {
		svCfg.APIKey = vv
	}
	if vv := os.Getenv("API_DOMAIN"); vv != "" {
		svCfg.APIDomain = vv
	}
	if vv := os.Getenv("API_DOMAIN_AUTOCERT"); vv != "" {
		svCfg.APIDomainAutocert = (vv == "1")
	}
	if vv := os.Getenv("LETSENCRYPT_URL"); vv != "" {
		svCfg.LetsEncryptURL = vv
	}
	if vv := os.Getenv("DISABLE_TLS"); vv != "" {
		svCfg.DisableTLS = (vv == "1")
	}

	s := server.Default(svCfg)

	for _, v := range cfg.Routes {
		r := route.Route{}
		// apply Websockets config
		r.WsCFG = v.Websockets
		if ct, ok := unpackConnType(v.OutgoingServerConnType); ok {
			r.Server.OutConnType = ct
		} else {
			fmt.Fprintf(stderr, "\n CONFIGURATION ERROR\nDomain: %v\n ERR: %v\n",
				ansi.Color(v.Domain, "yellow"),
				ansi.Color("Invalid conn type "+v.OutgoingServerConnType, "red"))
			os.Exit(1)
		}
		r.Domain = v.Domain
		r.Server.OutAddress = v.OutgoingServerAddress
		r.Certificate.CertFile = v.TLSCertFile
		r.Certificate.KeyFile = v.TLSKeyFile
		r.Autocert = v.Autocert
		r.AuthMode = v.AuthMode
		r.AuthKey = v.AuthKey
		r.AuthValue = v.AuthValue
		r.ForceHTTPS = v.ForceHTTPS
		err = s.Add(r)
		if err != nil {
			fmt.Fprintf(stderr, "\nERROR: Could not add route %v\n%v\n",
				ansi.Color(v.Domain, "yellow"),
				ansi.Color(err.Error(), "red"))
			os.Exit(1)
		}
		fmt.Fprintf(stdout, "%v: %v\n",
			ansi.Color("Domain added", "green"),
			v.Domain)
	}
	//
	if cfg.Debug {
		fmt.Fprintf(stdout, "%v DEBUG MODE IS %v\n",
			ansi.Color("WARNING:", "yellow"),
			ansi.Color("ON", "green"))
	}
	//
	// Close if received signal
	go func() {
		sigchan := make(chan os.Signal, 1)
		signal.Notify(sigchan, os.Interrupt, os.Kill)
		<-sigchan
		s.Close()
	}()
	//
	err = s.Run()
	if err != nil {
		fmt.Fprintf(stderr, "ERROR: %v\n",
			ansi.Color(err.Error(), "red"))
		os.Exit(1)
	}
}

func printHelp() {
	if len(flag.Args()) < 1 {
		return
	}
	if flag.Arg(0) != "help" {
		return
	}
	fmt.Fprintln(stdout, ansi.Color("Usage:", "blue"))
	fmt.Fprintf(stdout, "sandpiper %v\n", ansi.Color("config.yml", "green"))
	fmt.Fprintf(stdout, "sandpiper %v\n", ansi.Color("help", "green"))
	os.Exit(0)
}

func findConfigFile() {
	if len(flag.Args()) > 0 {
		configfile = flag.Arg(0)
		return
	}
	if env := os.Getenv("SANDPIPER_CONFIG"); env != "" {
		configfile = env
		return
	}
	if _, err := os.Stat("config.yml"); err == nil {
		configfile = "config.yml"
		return
	}
	// no config file found!
	fmt.Fprint(stderr, ansi.Color("No configuration file found!\n\n", "red"))
	fmt.Println(`There are three different ways to fix this:
  - Pass it as a parameter:
     sandpiper /path/to/config.yml
  - Set the env var SANDPIPER_CONFIG
  - Have a config.yml in the current working directory`)
	fmt.Fprintf(stdout, "\nrun %v fo more info.\n", ansi.Color("sandpiper help", "blue"))
	os.Exit(1)
}

func unpackConnType(input string) (route.ConnType, bool) {
	if input == "HTTP" {
		return route.HTTP, true
	}
	if input == "HTTPS" || input == "HTTPS_VERIFY" {
		return route.HTTPS_VERIFY, true
	}
	if input == "HTTPS_SKIP_VERIFY" {
		return route.HTTPS_SKIP_VERIFY, true
	}
	if input == "REDIRECT" {
		return route.REDIRECT, true
	}
	return route.HTTP, false
}

// Config main config structure (yml)
type Config struct {
	Debug             bool          `yaml:"debug"`
	NumCPU            int           `yaml:"num_cpu"`
	ListenAddr        string        `yaml:"listen_addr"`
	ListenAddrTLS     string        `yaml:"listen_addr_tls"`
	DisableTLS        bool          `yaml:"disable_tls"`
	Routes            []ConfigRoute `yaml:"routes"`
	FallbackDomain    string        `yaml:"fallback_domain"`
	Graceful          bool          `yaml:"graceful"`
	CachePath         string        `yaml:"cache_path"`
	APIListen         string        `yaml:"api_listen"`
	APIKey            string        `yaml:"api_key"`
	APIDomain         string        `yaml:"api_domain"`
	APIDomainAutocert bool          `yaml:"api_domain_autocert"`
}

// ConfigRoute represents a domain route
type ConfigRoute struct {
	Domain                 string        `yaml:"domain"`
	OutgoingServerConnType string        `yaml:"out_conn_type"`
	OutgoingServerAddress  string        `yaml:"out_addr"`
	TLSCertFile            string        `yaml:"tls_cert_file"`
	TLSKeyFile             string        `yaml:"tls_key_file"`
	Autocert               bool          `yaml:"autocert"`
	Websockets             util.WsConfig `yaml:"websockets"`
	AuthMode               string        `yaml:"auth_mode"`
	AuthKey                string        `yaml:"auth_key"`
	AuthValue              string        `yaml:"auth_value"`
	ForceHTTPS             bool          `yaml:"force_https"`
}
<|endoftext|>


<commit_before>package parser

import (
	"errors"
	"fmt"
	"github.com/domain-query-language/dql-server/src/server/adapter/ast"
	"github.com/domain-query-language/dql-server/src/server/adapter/parser/token"
	"github.com/domain-query-language/dql-server/src/server/adapter/parser/tokenizer"
	"strconv"
)

const (
	_ int = iota
	LOWEST
	ASSIGN	    // =
	EQUALS      // ==
	LESSGREATER // > or <
	SUM         // +
	PRODUCT     // *
	PREFIX      // -X or !X
	CALL        // myFunction(X)
	OBJECT	    // object->key
	INDEX       // array[index]

)

var precedences = map[token.TokenType]int{
	token.EQ:       EQUALS,
	token.NOTEQ:    EQUALS,
	token.LT:       LESSGREATER,
	token.GT:       LESSGREATER,
	token.PLUS:     SUM,
	token.MINUS:    SUM,
	token.SLASH:    PRODUCT,
	token.ASTERISK: PRODUCT,
	token.LPAREN:   CALL,
	token.ARROW:	OBJECT,
	token.LBRACKET: INDEX,
	token.ASSIGN: 	ASSIGN,
}

type (
	prefixParseFn func() ast.Expression
	infixParseFn  func(ast.Expression) ast.Expression
)

/** Implementation of the adapter, written using the tokenizer, parser pattern */
type statementParser struct {

	t tokenizer.Tokenizer
	error error

	curToken  *token.Token
	peekToken *token.Token

	prefixParseFns map[token.TokenType]prefixParseFn
	infixParseFns  map[token.TokenType]infixParseFn
}

func NewStatement(statements string) *statementParser {

	t := tokenizer.NewTokenizer(statements);

	p := &statementParser{
		t: t,
	};

	p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
	p.registerPrefix(token.IDENT, p.parseIdentifier)
	p.registerPrefix(token.INTEGER, p.parseIntegerLiteral)
	p.registerPrefix(token.FLOAT, p.parseFloatLiteral)
	p.registerPrefix(token.STRING, p.parseString)
	p.registerPrefix(token.BANG, p.parsePrefixExpression)
	p.registerPrefix(token.PLUS, p.parsePrefixExpression)
	p.registerPrefix(token.MINUS, p.parsePrefixExpression)
	p.registerPrefix(token.BOOLEAN, p.parseBoolean)
	p.registerPrefix(token.LPAREN, p.parseGroupedExpression)

	p.infixParseFns = make(map[token.TokenType]infixParseFn)
	p.registerInfix(token.PLUS, p.parseInfixExpression)
	p.registerInfix(token.MINUS, p.parseInfixExpression)
	p.registerInfix(token.SLASH, p.parseInfixExpression)
	p.registerInfix(token.ASTERISK, p.parseInfixExpression)
	p.registerInfix(token.EQ, p.parseInfixExpression)
	p.registerInfix(token.NOTEQ, p.parseInfixExpression)
	p.registerInfix(token.LT, p.parseInfixExpression)
	p.registerInfix(token.GT, p.parseInfixExpression)
	p.registerInfix(token.ARROW, p.parseInfixExpression)
	p.registerInfix(token.ASSIGN, p.parseInfixExpression)

	p.nextToken()
	p.nextToken()

	return p;
}

func (p *statementParser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
	p.prefixParseFns[tokenType] = fn
}

func (p *statementParser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
	p.infixParseFns[tokenType] = fn
}

func (p *statementParser) nextToken() {

	p.curToken = p.peekToken
	p.peekToken = p.t.Next();
}

func (p *statementParser) curTokenIs(t token.TokenType) bool {

	return p.curToken.Type == t
}

func (p *statementParser) peekTokenIs(t token.TokenType) bool {

	if p.peekToken == nil {
		return false
	}

	return p.peekToken.Type == t
}

func (p *statementParser) expectPeek(t token.TokenType) bool {

	if p.peekTokenIs(t) {

		p.nextToken()
		return true
	} else {

		p.peekError(t)
		return false
	}
}

func (p *statementParser) expectCurrent(t token.TokenType) bool {

	if p.curTokenIs(t) {

		p.nextToken()
		return true
	} else {

		p.peekError(t)
		return false
	}
}

func (p *statementParser) peekError(t token.TokenType) {

	if (p.peekToken == nil) {
		msg := fmt.Sprintf("Expected next token to be '%s', got EOF instead", t)
		p.error = errors.New(msg);
		return;
	}
	msg := fmt.Sprintf("Expected next token to be '%s', got '%s' instead", t, p.peekToken.Val)
	p.error = errors.New(msg);
}

func (p *statementParser) ParseBlockStatement() (*ast.BlockStatement, error) {

	if (!p.curTokenIs(token.LBRACE) ) {
		msg := fmt.Sprintf("Expected next token to be '%s', got '%s' instead", token.LBRACE, p.curToken.Val)
		p.error = errors.New(msg);
		return nil, p.error
	}

	p.nextToken()

	block := &ast.BlockStatement{Type:"blockstatement"}
	block.Statements = []ast.Node{}

	for !p.curTokenIs(token.RBRACE) && p.error == nil  {
		stmt := p.parseStatement()
		if stmt != nil {
			block.Statements = append(block.Statements, stmt)
		}
		p.nextToken()
	}

	return block, p.error
}

func (p *statementParser) parseStatement() ast.Node {

	switch p.curToken.Type {

		case token.RETURN:
			return p.parseReturnStatement()

		case token.IF:
			return p.parseIfStatement()

		case token.FOREACH:
			return p.parseForeachStatement()

		default:
			return p.parseExpressionStatement()
	}
}

func (p *statementParser) parseExpressionStatement() ast.Node {

	stmt := &ast.ExpressionStatement{Type:"expressionstatement"}

	stmt.Expression = p.parseExpression(LOWEST)

	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
	} else {
		msg := fmt.Sprintf("Expected %v, got %v", token.SEMICOLON, p.peekToken);
		p.error = errors.New(msg);
	}

	return stmt
}

func (p *statementParser) parseReturnStatement() *ast.Return {

	stmt := &ast.Return{Type:"return"}

	p.nextToken()

	stmt.Expression = p.parseExpression(LOWEST)

	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
	}

	return stmt
}

func (p *statementParser) parseForeachStatement() *ast.ForeachStatement {

	stmt := &ast.ForeachStatement{Type:"foreach"}

	p.nextToken()
	/*
	stmt.Expression = p.parseExpression(LOWEST)

	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
	}
	*/

	return stmt
}

func (p *statementParser) parseIfStatement() ast.Statement {

	stmt := &ast.IfStatement{Type:"if"}

	p.nextToken()

	stmt.Test = p.parseExpression(LOWEST)

	p.nextToken()

	if !p.curTokenIs(token.LBRACE) {
		return nil
	}

	consequent, _ := p.ParseBlockStatement()

	stmt.Consequent = consequent

	if p.peekTokenIs(token.ELSE) {

		p.nextToken()
		p.nextToken()

		stmt.Alternate, _ = p.ParseBlockStatement()
	}

	return stmt
}

func (p *statementParser) parseExpression(precedence int) ast.Expression {

	if (p.curToken == nil) {

		return nil
	}

	prefix := p.prefixParseFns[p.curToken.Type]
	if prefix == nil {

		return nil
	}
	leftExp := prefix()

	for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {

		infix := p.infixParseFns[p.peekToken.Type]
		if infix == nil {

			return leftExp
		}

		p.nextToken()

		leftExp = infix(leftExp)
	}

	return leftExp
}

func (p *statementParser) peekPrecedence() int {

	if (p.peekToken == nil) {
		return LOWEST
	}
	if p, ok := precedences[p.peekToken.Type]; ok {
		return p
	}

	return LOWEST
}

func (p *statementParser) curPrecedence() int {

	if p, ok := precedences[p.curToken.Type]; ok {
		return p
	}

	return LOWEST
}

func (p *statementParser) parsePrefixExpression() ast.Expression {

	operator := p.curToken.Val
	if (p.isIncrementOrDecrement()) {
		p.nextToken()
		operator += p.curToken.Val
	}

	expression := &ast.Prefix{
		Type: "prefix",
		Operator: operator,
	}

	p.nextToken()

	expression.Right = p.parseExpression(PREFIX)

	return expression
}

func (p *statementParser) isIncrementOrDecrement() bool {

	return (p.curToken.Type == token.PLUS || p.curToken.Type == token.MINUS) && p.curToken.Type == p.peekToken.Type
}

func (p *statementParser) parseInfixExpression(left ast.Expression) ast.Expression {

	expression := &ast.Infix{
		Type: "infix",
		Operator: p.curToken.Val,
		Left:     left,
	}

	precedence := p.curPrecedence()
	p.nextToken()
	expression.Right = p.parseExpression(precedence)

	if (expression.Right == nil) {

		p.error = errors.New("Expected expression, got nothing")
		return nil
	}

	return expression
}

func (p *statementParser) parseIdentifier() ast.Expression {

	return &ast.Identifier{Type:"identifier", Value: p.curToken.Val}
}

func (p *statementParser) parseIntegerLiteral() ast.Expression {

	lit := &ast.IntegerLiteral{Type:"integer"}

	value, err := strconv.ParseInt(p.curToken.Val, 0, 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as integer", p.curToken.Val)
		p.error = errors.New(msg)
		return nil
	}

	lit.Value = value

	return lit
}

func (p *statementParser) parseBoolean() ast.Expression {

	return &ast.Boolean{Type:"boolean", Value: p.curToken.Val == "true"}
}

func (p *statementParser) parseGroupedExpression() ast.Expression {

	p.nextToken()

	exp := p.parseExpression(LOWEST)

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	return exp
}

func (p *statementParser) parseFloatLiteral() ast.Expression {

	lit := &ast.FloatLiteral{Type:"float"}

	value, err := strconv.ParseFloat(p.curToken.Val, 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as float", p.curToken.Val)
		p.error = errors.New(msg)
		return nil
	}

	lit.Value = value

	return lit
}

func (p *statementParser) parseString() ast.Expression {

	return &ast.String{Type:"string", Value:p.curToken.Val}
}<commit_msg>Adding foreach statement<commit_after>package parser

import (
	"errors"
	"fmt"
	"github.com/domain-query-language/dql-server/src/server/adapter/ast"
	"github.com/domain-query-language/dql-server/src/server/adapter/parser/token"
	"github.com/domain-query-language/dql-server/src/server/adapter/parser/tokenizer"
	"strconv"
)

const (
	_ int = iota
	LOWEST
	ASSIGN	    // =
	EQUALS      // ==
	LESSGREATER // > or <
	SUM         // +
	PRODUCT     // *
	PREFIX      // -X or !X
	CALL        // myFunction(X)
	OBJECT	    // object->key
	INDEX       // array[index]

)

var precedences = map[token.TokenType]int{
	token.EQ:       EQUALS,
	token.NOTEQ:    EQUALS,
	token.LT:       LESSGREATER,
	token.GT:       LESSGREATER,
	token.PLUS:     SUM,
	token.MINUS:    SUM,
	token.SLASH:    PRODUCT,
	token.ASTERISK: PRODUCT,
	token.LPAREN:   CALL,
	token.ARROW:	OBJECT,
	token.LBRACKET: INDEX,
	token.ASSIGN: 	ASSIGN,
}

type (
	prefixParseFn func() ast.Expression
	infixParseFn  func(ast.Expression) ast.Expression
)

/** Implementation of the adapter, written using the tokenizer, parser pattern */
type statementParser struct {

	t tokenizer.Tokenizer
	error error

	curToken  *token.Token
	peekToken *token.Token

	prefixParseFns map[token.TokenType]prefixParseFn
	infixParseFns  map[token.TokenType]infixParseFn
}

func NewStatement(statements string) *statementParser {

	t := tokenizer.NewTokenizer(statements);

	p := &statementParser{
		t: t,
	};

	p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
	p.registerPrefix(token.IDENT, p.parseIdentifier)
	p.registerPrefix(token.INTEGER, p.parseIntegerLiteral)
	p.registerPrefix(token.FLOAT, p.parseFloatLiteral)
	p.registerPrefix(token.STRING, p.parseString)
	p.registerPrefix(token.BANG, p.parsePrefixExpression)
	p.registerPrefix(token.PLUS, p.parsePrefixExpression)
	p.registerPrefix(token.MINUS, p.parsePrefixExpression)
	p.registerPrefix(token.BOOLEAN, p.parseBoolean)
	p.registerPrefix(token.LPAREN, p.parseGroupedExpression)

	p.infixParseFns = make(map[token.TokenType]infixParseFn)
	p.registerInfix(token.PLUS, p.parseInfixExpression)
	p.registerInfix(token.MINUS, p.parseInfixExpression)
	p.registerInfix(token.SLASH, p.parseInfixExpression)
	p.registerInfix(token.ASTERISK, p.parseInfixExpression)
	p.registerInfix(token.EQ, p.parseInfixExpression)
	p.registerInfix(token.NOTEQ, p.parseInfixExpression)
	p.registerInfix(token.LT, p.parseInfixExpression)
	p.registerInfix(token.GT, p.parseInfixExpression)
	p.registerInfix(token.ARROW, p.parseInfixExpression)
	p.registerInfix(token.ASSIGN, p.parseInfixExpression)

	p.nextToken()
	p.nextToken()

	return p;
}

func (p *statementParser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
	p.prefixParseFns[tokenType] = fn
}

func (p *statementParser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
	p.infixParseFns[tokenType] = fn
}

func (p *statementParser) nextToken() {

	p.curToken = p.peekToken
	p.peekToken = p.t.Next();
}

func (p *statementParser) curTokenIs(t token.TokenType) bool {

	return p.curToken.Type == t
}

func (p *statementParser) peekTokenIs(t token.TokenType) bool {

	if p.peekToken == nil {
		return false
	}

	return p.peekToken.Type == t
}

func (p *statementParser) expectPeek(t token.TokenType) bool {

	if p.peekTokenIs(t) {

		p.nextToken()
		return true
	} else {

		p.peekError(t)
		return false
	}
}

func (p *statementParser) expectCurrent(t token.TokenType) bool {

	if p.curTokenIs(t) {

		p.nextToken()
		return true
	} else {

		p.peekError(t)
		return false
	}
}

func (p *statementParser) peekError(t token.TokenType) {

	if (p.peekToken == nil) {
		msg := fmt.Sprintf("Expected next token to be '%s', got EOF instead", t)
		p.error = errors.New(msg);
		return;
	}
	msg := fmt.Sprintf("Expected next token to be '%s', got '%s' instead", t, p.peekToken.Val)
	p.error = errors.New(msg);
}

func (p *statementParser) ParseBlockStatement() (*ast.BlockStatement, error) {

	if (!p.curTokenIs(token.LBRACE) ) {
		msg := fmt.Sprintf("Expected next token to be '%s', got '%s' instead", token.LBRACE, p.curToken.Val)
		p.error = errors.New(msg);
		return nil, p.error
	}

	p.nextToken()

	block := &ast.BlockStatement{Type:"blockstatement"}
	block.Statements = []ast.Node{}

	for !p.curTokenIs(token.RBRACE) && p.error == nil  {
		stmt := p.parseStatement()
		if stmt != nil {
			block.Statements = append(block.Statements, stmt)
		}
		p.nextToken()
	}

	return block, p.error
}

func (p *statementParser) parseStatement() ast.Node {

	switch p.curToken.Type {

		case token.RETURN:
			return p.parseReturnStatement()

		case token.IF:
			return p.parseIfStatement()

		case token.FOREACH:
			return p.parseForeachStatement()

		default:
			return p.parseExpressionStatement()
	}
}

func (p *statementParser) parseExpressionStatement() ast.Node {

	stmt := &ast.ExpressionStatement{Type:"expressionstatement"}

	stmt.Expression = p.parseExpression(LOWEST)

	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
	} else {
		msg := fmt.Sprintf("Expected %v, got %v", token.SEMICOLON, p.peekToken);
		p.error = errors.New(msg);
	}

	return stmt
}

func (p *statementParser) parseReturnStatement() *ast.Return {

	stmt := &ast.Return{Type:"return"}

	p.nextToken()

	stmt.Expression = p.parseExpression(LOWEST)

	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
	}

	return stmt
}

func (p *statementParser) parseForeachStatement() *ast.ForeachStatement {

	stmt := &ast.ForeachStatement{Type:"foreach"}

	p.nextToken()

	if !p.curTokenIs(token.LPAREN) {
		return nil
	}

	stmt.Collection = p.parseExpression(LOWEST)

	panic (p.curToken.String())

	/*
	stmt.Expression = p.parseExpression(LOWEST)

	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
	}
	*/

	return stmt
}

func (p *statementParser) parseIfStatement() ast.Statement {

	stmt := &ast.IfStatement{Type:"if"}

	p.nextToken()

	stmt.Test = p.parseExpression(LOWEST)

	p.nextToken()

	if !p.curTokenIs(token.LBRACE) {
		return nil
	}

	consequent, _ := p.ParseBlockStatement()

	stmt.Consequent = consequent

	if p.peekTokenIs(token.ELSE) {

		p.nextToken()
		p.nextToken()

		stmt.Alternate, _ = p.ParseBlockStatement()
	}

	return stmt
}

func (p *statementParser) parseExpression(precedence int) ast.Expression {

	if (p.curToken == nil) {

		return nil
	}

	prefix := p.prefixParseFns[p.curToken.Type]
	if prefix == nil {

		return nil
	}
	leftExp := prefix()

	for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {

		infix := p.infixParseFns[p.peekToken.Type]
		if infix == nil {

			return leftExp
		}

		p.nextToken()

		leftExp = infix(leftExp)
	}

	return leftExp
}

func (p *statementParser) peekPrecedence() int {

	if (p.peekToken == nil) {
		return LOWEST
	}
	if p, ok := precedences[p.peekToken.Type]; ok {
		return p
	}

	return LOWEST
}

func (p *statementParser) curPrecedence() int {

	if p, ok := precedences[p.curToken.Type]; ok {
		return p
	}

	return LOWEST
}

func (p *statementParser) parsePrefixExpression() ast.Expression {

	operator := p.curToken.Val
	if (p.isIncrementOrDecrement()) {
		p.nextToken()
		operator += p.curToken.Val
	}

	expression := &ast.Prefix{
		Type: "prefix",
		Operator: operator,
	}

	p.nextToken()

	expression.Right = p.parseExpression(PREFIX)

	return expression
}

func (p *statementParser) isIncrementOrDecrement() bool {

	return (p.curToken.Type == token.PLUS || p.curToken.Type == token.MINUS) && p.curToken.Type == p.peekToken.Type
}

func (p *statementParser) parseInfixExpression(left ast.Expression) ast.Expression {

	expression := &ast.Infix{
		Type: "infix",
		Operator: p.curToken.Val,
		Left:     left,
	}

	precedence := p.curPrecedence()
	p.nextToken()
	expression.Right = p.parseExpression(precedence)

	if (expression.Right == nil) {

		p.error = errors.New("Expected expression, got nothing")
		return nil
	}

	return expression
}

func (p *statementParser) parseIdentifier() ast.Expression {

	return &ast.Identifier{Type:"identifier", Value: p.curToken.Val}
}

func (p *statementParser) parseIntegerLiteral() ast.Expression {

	lit := &ast.IntegerLiteral{Type:"integer"}

	value, err := strconv.ParseInt(p.curToken.Val, 0, 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as integer", p.curToken.Val)
		p.error = errors.New(msg)
		return nil
	}

	lit.Value = value

	return lit
}

func (p *statementParser) parseBoolean() ast.Expression {

	return &ast.Boolean{Type:"boolean", Value: p.curToken.Val == "true"}
}

func (p *statementParser) parseGroupedExpression() ast.Expression {

	p.nextToken()

	exp := p.parseExpression(LOWEST)

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	return exp
}

func (p *statementParser) parseFloatLiteral() ast.Expression {

	lit := &ast.FloatLiteral{Type:"float"}

	value, err := strconv.ParseFloat(p.curToken.Val, 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as float", p.curToken.Val)
		p.error = errors.New(msg)
		return nil
	}

	lit.Value = value

	return lit
}

func (p *statementParser) parseString() ast.Expression {

	return &ast.String{Type:"string", Value:p.curToken.Val}
}<|endoftext|>


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

package test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"regexp"
	"time"

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
	"v.io/x/devtools/internal/xunit"
)

// generateXUnitTestSuite generates an xUnit test suite that
// encapsulates the given input.
func generateXUnitTestSuite(ctx *tool.Context, failure *xunit.Failure, pkg string, duration time.Duration) *xunit.TestSuite {
	// Generate an xUnit test suite describing the result.
	s := xunit.TestSuite{Name: pkg}
	c := xunit.TestCase{
		Classname: pkg,
		Name:      "Test",
		Time:      fmt.Sprintf("%.2f", duration.Seconds()),
	}
	if failure != nil {
		fmt.Fprintf(ctx.Stdout(), "%s ... failed\n%v\n", pkg, failure.Data)
		c.Failures = append(c.Failures, *failure)
		s.Failures++
	} else {
		fmt.Fprintf(ctx.Stdout(), "%s ... ok\n", pkg)
	}
	s.Tests++
	s.Cases = append(s.Cases, c)
	return &s
}

// testSingleProdService test the given production service.
func testSingleProdService(ctx *tool.Context, vroot, principalDir string, service prodService) *xunit.TestSuite {
	bin := filepath.Join(vroot, "release", "go", "bin", "vrpc")
	var out bytes.Buffer
	opts := ctx.Run().Opts()
	opts.Stdout = &out
	opts.Stderr = &out
	start := time.Now()
	if err := ctx.Run().TimedCommandWithOpts(test.DefaultTimeout, opts, bin, "--v23.credentials", principalDir, "signature", "--show-reserved", service.objectName); err != nil {
		return generateXUnitTestSuite(ctx, &xunit.Failure{"vrpc", out.String()}, service.name, time.Now().Sub(start))
	}
	if !service.regexp.Match(out.Bytes()) {
		fmt.Fprintf(ctx.Stderr(), "couldn't match regexp %q in output:\n%v\n", service.regexp, out.String())
		return generateXUnitTestSuite(ctx, &xunit.Failure{"vrpc", "mismatching signature"}, service.name, time.Now().Sub(start))
	}
	return generateXUnitTestSuite(ctx, nil, service.name, time.Now().Sub(start))
}

type prodService struct {
	name       string         // Name to use for the test description
	objectName string         // Object name of the service to connect to
	regexp     *regexp.Regexp // Regexp that should match the signature output
}

// vanadiumProdServicesTest runs a test of vanadium production services.
func vanadiumProdServicesTest(ctx *tool.Context, testName string, opts ...Opt) (_ *test.Result, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	vroot, err := util.V23Root()
	if err != nil {
		return nil, err
	}

	// Install the vrpc tool.
	if err := ctx.Run().Command("v23", "go", "install", "v.io/x/ref/cmd/vrpc"); err != nil {
		return nil, internalTestError{err, "Install VRPC"}
	}
	// Install the principal tool.
	if err := ctx.Run().Command("v23", "go", "install", "v.io/x/ref/cmd/principal"); err != nil {
		return nil, internalTestError{err, "Install Principal"}
	}
	tmpdir, err := ctx.Run().TempDir("", "prod-services-test")
	if err != nil {
		return nil, internalTestError{err, "Create temporary directory"}
	}
	defer collect.Error(func() error { return ctx.Run().RemoveAll(tmpdir) }, &e)

	blessingRoot, namespaceRoot := getServiceOpts(opts)
	allPassed, suites := true, []xunit.TestSuite{}

	// Fetch the "root" blessing that all services are blessed by.
	suite, pubkey, blessingNames := testIdentityProviderHTTP(ctx, blessingRoot)
	suites = append(suites, *suite)

	if suite.Failures == 0 {
		// Setup a principal that will be used by testAllProdServices and will
		// recognize the blessings of the prod services.
		principalDir, err := setupPrincipal(ctx, vroot, tmpdir, pubkey, blessingNames)
		if err != nil {
			return nil, err
		}
		for _, suite := range testAllProdServices(ctx, vroot, principalDir, namespaceRoot) {
			allPassed = allPassed && (suite.Failures == 0)
			suites = append(suites, *suite)
		}
	}

	// Create the xUnit report.
	if err := xunit.CreateReport(ctx, testName, suites); err != nil {
		return nil, err
	}
	for _, suite := range suites {
		if suite.Failures > 0 {
			// At least one test failed:
			return &test.Result{Status: test.Failed}, nil
		}
	}
	return &test.Result{Status: test.Passed}, nil
}

func testAllProdServices(ctx *tool.Context, vroot, principalDir, namespaceRoot string) []*xunit.TestSuite {
	services := []prodService{
		prodService{
			name:       "mounttable",
			objectName: namespaceRoot,
			regexp:     regexp.MustCompile(`MountTable[[:space:]]+interface`),
		},
		prodService{
			name:       "application repository",
			objectName: namespaceRoot + "/applications",
			regexp:     regexp.MustCompile(`Application[[:space:]]+interface`),
		},
		prodService{
			name:       "binary repository",
			objectName: namespaceRoot + "/binaries",
			regexp:     regexp.MustCompile(`Binary[[:space:]]+interface`),
		},
		prodService{
			name:       "macaroon service",
			objectName: namespaceRoot + "/identity/dev.v.io/root/macaroon",
			regexp:     regexp.MustCompile(`MacaroonBlesser[[:space:]]+interface`),
		},
		prodService{
			name:       "google identity service",
			objectName: namespaceRoot + "/identity/dev.v.io//root/google",
			regexp:     regexp.MustCompile(`OAuthBlesser[[:space:]]+interface`),
		},
		prodService{
			objectName: namespaceRoot + "/identity/dev.v.io/root/discharger",
			name:       "binary discharger",
			regexp:     regexp.MustCompile(`Discharger[[:space:]]+interface`),
		},
		prodService{
			objectName: namespaceRoot + "/proxy-mon/__debug",
			name:       "proxy service",
			// We just check that the returned signature has the __Reserved interface since
			// proxy-mon doesn't implement any other services.
			regexp: regexp.MustCompile(`__Reserved[[:space:]]+interface`),
		},
	}

	var suites []*xunit.TestSuite
	for _, service := range services {
		suites = append(suites, testSingleProdService(ctx, vroot, principalDir, service))
	}
	return suites
}

// testIdentityProviderHTTP tests that the identity provider's HTTP server is
// up and running and also fetches the set of blessing names that the provider
// claims to be authoritative on and the public key (encoded) used by that
// identity provider to sign certificates for blessings.
//
// PARANOIA ALERT:
// This function is subject to man-in-the-middle attacks because it does not
// verify the TLS certificates presented by the server. This does open the
// door for an attack where a parallel universe of services could be setup
// and fool this production services test into thinking all services are
// up and running when they may not be.
//
// The attacker in this case will have to be able to mess with the routing
// tables on the machine running this test, or the network routes of routers
// used by the machine, or mess up DNS entries.
func testIdentityProviderHTTP(ctx *tool.Context, blessingRoot string) (suite *xunit.TestSuite, publickey string, blessingNames []string) {
	url := fmt.Sprintf("https://%s/auth/blessing-root", blessingRoot)
	start := time.Now()
	var response struct {
		Names     []string `json:"names"`
		PublicKey string   `json:"publicKey"`
	}
	resp, err := http.Get(url)
	if err == nil {
		defer resp.Body.Close()
		err = json.NewDecoder(resp.Body).Decode(&response)
	}
	var failure *xunit.Failure
	if err != nil {
		failure = &xunit.Failure{"identityd HTTP", err.Error()}
	}
	return generateXUnitTestSuite(ctx, failure, url, time.Now().Sub(start)), response.PublicKey, response.Names
}

func setupPrincipal(ctx *tool.Context, vroot, tmpdir, pubkey string, blessingNames []string) (string, error) {
	dir := filepath.Join(tmpdir, "credentials")
	bin := filepath.Join(vroot, "release", "go", "bin", "principal")
	if err := ctx.Run().TimedCommand(test.DefaultTimeout, bin, "create", dir, "prod-services-tester"); err != nil {
		fmt.Fprintf(ctx.Stderr(), "principal create failed: %v\n", err)
		return "", err
	}
	for _, name := range blessingNames {
		if err := ctx.Run().TimedCommand(test.DefaultTimeout, bin, "--v23.credentials", dir, "addtoroots", pubkey, name); err != nil {
			fmt.Fprintf(ctx.Stderr(), "principal addtoroots %v %v failed: %v\n", pubkey, name, err)
			return "", err
		}
	}
	return dir, nil
}

// getServiceOpts extracts blessing root and namespace root from the
// given Opts.
func getServiceOpts(opts []Opt) (string, string) {
	blessingRoot := "dev.v.io"
	namespaceRoot := "/ns.dev.v.io:8101"
	for _, opt := range opts {
		switch v := opt.(type) {
		case BlessingsRootOpt:
			blessingRoot = string(v)
		case NamespaceRootOpt:
			namespaceRoot = string(v)
		}
	}
	return blessingRoot, namespaceRoot
}
<commit_msg>v23/internal/test: Update corresponding to https://vanadium-review.googlesource.com/#/c/10654/<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"regexp"
	"time"

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
	"v.io/x/devtools/internal/xunit"
)

// generateXUnitTestSuite generates an xUnit test suite that
// encapsulates the given input.
func generateXUnitTestSuite(ctx *tool.Context, failure *xunit.Failure, pkg string, duration time.Duration) *xunit.TestSuite {
	// Generate an xUnit test suite describing the result.
	s := xunit.TestSuite{Name: pkg}
	c := xunit.TestCase{
		Classname: pkg,
		Name:      "Test",
		Time:      fmt.Sprintf("%.2f", duration.Seconds()),
	}
	if failure != nil {
		fmt.Fprintf(ctx.Stdout(), "%s ... failed\n%v\n", pkg, failure.Data)
		c.Failures = append(c.Failures, *failure)
		s.Failures++
	} else {
		fmt.Fprintf(ctx.Stdout(), "%s ... ok\n", pkg)
	}
	s.Tests++
	s.Cases = append(s.Cases, c)
	return &s
}

// testSingleProdService test the given production service.
func testSingleProdService(ctx *tool.Context, vroot, principalDir string, service prodService) *xunit.TestSuite {
	bin := filepath.Join(vroot, "release", "go", "bin", "vrpc")
	var out bytes.Buffer
	opts := ctx.Run().Opts()
	opts.Stdout = &out
	opts.Stderr = &out
	start := time.Now()
	if err := ctx.Run().TimedCommandWithOpts(test.DefaultTimeout, opts, bin, "--v23.credentials", principalDir, "signature", "--show-reserved", service.objectName); err != nil {
		return generateXUnitTestSuite(ctx, &xunit.Failure{"vrpc", out.String()}, service.name, time.Now().Sub(start))
	}
	if !service.regexp.Match(out.Bytes()) {
		fmt.Fprintf(ctx.Stderr(), "couldn't match regexp %q in output:\n%v\n", service.regexp, out.String())
		return generateXUnitTestSuite(ctx, &xunit.Failure{"vrpc", "mismatching signature"}, service.name, time.Now().Sub(start))
	}
	return generateXUnitTestSuite(ctx, nil, service.name, time.Now().Sub(start))
}

type prodService struct {
	name       string         // Name to use for the test description
	objectName string         // Object name of the service to connect to
	regexp     *regexp.Regexp // Regexp that should match the signature output
}

// vanadiumProdServicesTest runs a test of vanadium production services.
func vanadiumProdServicesTest(ctx *tool.Context, testName string, opts ...Opt) (_ *test.Result, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	vroot, err := util.V23Root()
	if err != nil {
		return nil, err
	}

	// Install the vrpc tool.
	if err := ctx.Run().Command("v23", "go", "install", "v.io/x/ref/cmd/vrpc"); err != nil {
		return nil, internalTestError{err, "Install VRPC"}
	}
	// Install the principal tool.
	if err := ctx.Run().Command("v23", "go", "install", "v.io/x/ref/cmd/principal"); err != nil {
		return nil, internalTestError{err, "Install Principal"}
	}
	tmpdir, err := ctx.Run().TempDir("", "prod-services-test")
	if err != nil {
		return nil, internalTestError{err, "Create temporary directory"}
	}
	defer collect.Error(func() error { return ctx.Run().RemoveAll(tmpdir) }, &e)

	blessingRoot, namespaceRoot := getServiceOpts(opts)
	allPassed, suites := true, []xunit.TestSuite{}

	// Fetch the "root" blessing that all services are blessed by.
	suite, pubkey, blessingNames := testIdentityProviderHTTP(ctx, blessingRoot)
	suites = append(suites, *suite)

	if suite.Failures == 0 {
		// Setup a principal that will be used by testAllProdServices and will
		// recognize the blessings of the prod services.
		principalDir, err := setupPrincipal(ctx, vroot, tmpdir, pubkey, blessingNames)
		if err != nil {
			return nil, err
		}
		for _, suite := range testAllProdServices(ctx, vroot, principalDir, namespaceRoot) {
			allPassed = allPassed && (suite.Failures == 0)
			suites = append(suites, *suite)
		}
	}

	// Create the xUnit report.
	if err := xunit.CreateReport(ctx, testName, suites); err != nil {
		return nil, err
	}
	for _, suite := range suites {
		if suite.Failures > 0 {
			// At least one test failed:
			return &test.Result{Status: test.Failed}, nil
		}
	}
	return &test.Result{Status: test.Passed}, nil
}

func testAllProdServices(ctx *tool.Context, vroot, principalDir, namespaceRoot string) []*xunit.TestSuite {
	services := []prodService{
		prodService{
			name:       "mounttable",
			objectName: namespaceRoot,
			regexp:     regexp.MustCompile(`MountTable[[:space:]]+interface`),
		},
		prodService{
			name:       "application repository",
			objectName: namespaceRoot + "/applications",
			regexp:     regexp.MustCompile(`Application[[:space:]]+interface`),
		},
		prodService{
			name:       "binary repository",
			objectName: namespaceRoot + "/binaries",
			regexp:     regexp.MustCompile(`Binary[[:space:]]+interface`),
		},
		prodService{
			name:       "macaroon service",
			objectName: namespaceRoot + "/identity/dev.v.io/root/macaroon",
			regexp:     regexp.MustCompile(`MacaroonBlesser[[:space:]]+interface`),
		},
		prodService{
			name:       "google identity service",
			objectName: namespaceRoot + "/identity/dev.v.io//root/google",
			regexp:     regexp.MustCompile(`OAuthBlesser[[:space:]]+interface`),
		},
		prodService{
			objectName: namespaceRoot + "/identity/dev.v.io/root/discharger",
			name:       "binary discharger",
			regexp:     regexp.MustCompile(`Discharger[[:space:]]+interface`),
		},
		prodService{
			objectName: namespaceRoot + "/proxy-mon/__debug",
			name:       "proxy service",
			// We just check that the returned signature has the __Reserved interface since
			// proxy-mon doesn't implement any other services.
			regexp: regexp.MustCompile(`__Reserved[[:space:]]+interface`),
		},
	}

	var suites []*xunit.TestSuite
	for _, service := range services {
		suites = append(suites, testSingleProdService(ctx, vroot, principalDir, service))
	}
	return suites
}

// testIdentityProviderHTTP tests that the identity provider's HTTP server is
// up and running and also fetches the set of blessing names that the provider
// claims to be authoritative on and the public key (encoded) used by that
// identity provider to sign certificates for blessings.
//
// PARANOIA ALERT:
// This function is subject to man-in-the-middle attacks because it does not
// verify the TLS certificates presented by the server. This does open the
// door for an attack where a parallel universe of services could be setup
// and fool this production services test into thinking all services are
// up and running when they may not be.
//
// The attacker in this case will have to be able to mess with the routing
// tables on the machine running this test, or the network routes of routers
// used by the machine, or mess up DNS entries.
func testIdentityProviderHTTP(ctx *tool.Context, blessingRoot string) (suite *xunit.TestSuite, publickey string, blessingNames []string) {
	url := fmt.Sprintf("https://%s/auth/blessing-root", blessingRoot)
	start := time.Now()
	var response struct {
		Names     []string `json:"names"`
		PublicKey string   `json:"publicKey"`
	}
	resp, err := http.Get(url)
	if err == nil {
		defer resp.Body.Close()
		err = json.NewDecoder(resp.Body).Decode(&response)
	}
	var failure *xunit.Failure
	if err != nil {
		failure = &xunit.Failure{"identityd HTTP", err.Error()}
	}
	return generateXUnitTestSuite(ctx, failure, url, time.Now().Sub(start)), response.PublicKey, response.Names
}

func setupPrincipal(ctx *tool.Context, vroot, tmpdir, pubkey string, blessingNames []string) (string, error) {
	dir := filepath.Join(tmpdir, "credentials")
	bin := filepath.Join(vroot, "release", "go", "bin", "principal")
	if err := ctx.Run().TimedCommand(test.DefaultTimeout, bin, "create", dir, "prod-services-tester"); err != nil {
		fmt.Fprintf(ctx.Stderr(), "principal create failed: %v\n", err)
		return "", err
	}
	for _, name := range blessingNames {
		if err := ctx.Run().TimedCommand(test.DefaultTimeout, bin, "--v23.credentials", dir, "recognize", name, pubkey); err != nil {
			fmt.Fprintf(ctx.Stderr(), "principal recognize %v %v failed: %v\n", name, pubkey, err)
			return "", err
		}
	}
	return dir, nil
}

// getServiceOpts extracts blessing root and namespace root from the
// given Opts.
func getServiceOpts(opts []Opt) (string, string) {
	blessingRoot := "dev.v.io"
	namespaceRoot := "/ns.dev.v.io:8101"
	for _, opt := range opts {
		switch v := opt.(type) {
		case BlessingsRootOpt:
			blessingRoot = string(v)
		case NamespaceRootOpt:
			namespaceRoot = string(v)
		}
	}
	return blessingRoot, namespaceRoot
}
<|endoftext|>


<commit_before>package daemon

import (
	"fmt"
	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
	"log"
	"os"
	"strings"
	"time"
)

type Daemon struct {
	etcd     client.KeysAPI
	index    uint64
	firstRun bool
}

func (daemon *Daemon) register() error {
	hostname, err := os.Hostname()
	if err != nil {
		log.Fatalf(err.Error())
	}
	options := client.SetOptions{
		TTL: 60 * time.Second,
	}
	response, err := daemon.etcd.Set(context.Background(), fmt.Sprintf("/hosts/%s/host", hostname), hostname, &options)
	if err != nil {
		return err
	}

	if daemon.firstRun {
		daemon.index = response.Index
		daemon.firstRun = false
	} else {
		if daemon.index+1 != response.Index {
			return fmt.Errorf("Another host is already registered with this hostname")
		} else {
			daemon.index = response.Index
		}
	}

	return nil
}

func (daemon *Daemon) synchronize() {
	err := daemon.register()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	time.Sleep(15 * time.Second)
	daemon.Run()
}

func (daemon *Daemon) maintainState() {
	hostname, err := os.Hostname()
	if err != nil {
		log.Fatal(err)
	}
	response, err := daemon.etcd.Get(context.Background(), fmt.Sprintf("/hosts/%s/state", hostname), nil)
	if err != nil {
		log.Println("Etcd error: ", err.Error())
	} else {
		fmt.Println(response.Node.Value)
	}
}

func (daemon *Daemon) LoadState(state string) error {

	hosts, err := daemon.ListHosts()

	if err != nil {
		return err
	}

	for _, host := range hosts {
		response, err := daemon.etcd.Set(context.Background(), fmt.Sprintf("/hosts/%s/state", host), state, &client.SetOptions{})
		if err != nil {
			return err
		}
		log.Printf("Set host: %s %s", response.Action, host)
	}

	return nil
}

func (daemon *Daemon) ListHosts() ([]string, error) {

	var hosts []string

	response, err := daemon.etcd.Get(context.Background(), "/hosts", &client.GetOptions{Recursive: true})

	if err != nil {
		return hosts, err
	}

	for _, node := range response.Node.Nodes {
		hosts = append(hosts, strings.Split(node.Key, "/")[2])
	}

	return hosts, nil
}

func (daemon *Daemon) Run() {
	go daemon.synchronize()
	go daemon.maintainState()
	select {}
}

func NewDaemon(servers []string) (*Daemon, error) {
	daemon := Daemon{
		firstRun: true,
	}

	cfg := client.Config{
		Endpoints: servers,
		Transport: client.DefaultTransport,
	}

	c, err := client.New(cfg)

	if err != nil {
		return nil, err
	}

	daemon.etcd = client.NewKeysAPI(c)

	return &daemon, nil
}
<commit_msg>Log fatal on bad registration<commit_after>package daemon

import (
	"fmt"
	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
	"log"
	"os"
	"strings"
	"time"
)

type Daemon struct {
	etcd     client.KeysAPI
	index    uint64
	firstRun bool
}

func (daemon *Daemon) register() error {
	hostname, err := os.Hostname()
	if err != nil {
		log.Fatalf(err.Error())
	}
	options := client.SetOptions{
		TTL: 60 * time.Second,
	}
	response, err := daemon.etcd.Set(context.Background(), fmt.Sprintf("/hosts/%s/host", hostname), hostname, &options)
	if err != nil {
		return err
	}

	if daemon.firstRun {
		daemon.index = response.Index
		daemon.firstRun = false
	} else {
		if daemon.index+1 != response.Index {
			return fmt.Errorf("Another host is already registered with this hostname")
		} else {
			daemon.index = response.Index
		}
	}
	return nil
}

func (daemon *Daemon) synchronize() {
	err := daemon.register()
	if err != nil {
		log.Fatalf(err.Error())
	}
	time.Sleep(15 * time.Second)
	daemon.Run()
}

func (daemon *Daemon) maintainState() {
	hostname, err := os.Hostname()
	if err != nil {
		log.Fatal(err)
	}
	response, err := daemon.etcd.Get(context.Background(), fmt.Sprintf("/hosts/%s/state", hostname), nil)
	if err != nil {
		log.Println("Etcd error: ", err.Error())
	} else {
		fmt.Println(response.Node.Value)
	}
}

func (daemon *Daemon) LoadState(state string) error {

	hosts, err := daemon.ListHosts()

	if err != nil {
		return err
	}

	for _, host := range hosts {
		response, err := daemon.etcd.Set(context.Background(), fmt.Sprintf("/hosts/%s/state", host), state, &client.SetOptions{})
		if err != nil {
			return err
		}
		log.Printf("Set host: %s %s", response.Action, host)
	}

	return nil
}

func (daemon *Daemon) ListHosts() ([]string, error) {

	var hosts []string

	response, err := daemon.etcd.Get(context.Background(), "/hosts", &client.GetOptions{Recursive: true})

	if err != nil {
		return hosts, err
	}

	for _, node := range response.Node.Nodes {
		hosts = append(hosts, strings.Split(node.Key, "/")[2])
	}

	return hosts, nil
}

func (daemon *Daemon) Run() {
	go daemon.synchronize()
	go daemon.maintainState()
	select {}
}

func NewDaemon(servers []string) (*Daemon, error) {
	daemon := Daemon{
		firstRun: true,
	}

	cfg := client.Config{
		Endpoints: servers,
		Transport: client.DefaultTransport,
	}

	c, err := client.New(cfg)

	if err != nil {
		return nil, err
	}

	daemon.etcd = client.NewKeysAPI(c)

	return &daemon, nil
}
<|endoftext|>


<commit_before>package daemon

import (
    "os"
    "time"
    "fmt"
    "github.com/sevlyar/go-daemon"
    "github.com/Sirupsen/logrus"
    "github.com/BluePecker/JwtAuth/server/types/token"
    "github.com/BluePecker/JwtAuth/server"
    "github.com/BluePecker/JwtAuth/storage"
    //"github.com/BluePecker/JwtAuth/server/router"
    _ "github.com/BluePecker/JwtAuth/storage/redis"
    //_ "github.com/BluePecker/JwtAuth/storage/ram"
    "github.com/dgrijalva/jwt-go"
    RouteToken "github.com/BluePecker/JwtAuth/server/router/token"
    "github.com/kataras/iris"
    "github.com/kataras/iris/core/netutil"
)

const (
    TOKEN_TTL = 2 * 3600

    VERSION = "1.0.0"

    ALLOW_LOGIN_NUM = 3
)

type Storage struct {
    Driver string
    Opts   string
}

type Security struct {
    Verify bool
    TLS    bool
    Key    string
    Cert   string
}

type Options struct {
    PidFile  string
    LogFile  string
    LogLevel string
    Port     int
    Host     string
    Daemon   bool
    Version  bool
    Security Security
    Storage  Storage
    Secret   string
}

type Daemon struct {
    Options *Options
    Front   *server.Server
    Backend *server.Server
    Storage *storage.Driver
}

type (
    CustomClaims struct {
        Device    string `json:"device"`
        Unique    string `json:"unique"`
        Timestamp int64  `json:"timestamp"`
        Addr      string `json:"addr"`
        jwt.StandardClaims
    }
)

func (d *Daemon) NewStorage() (err error) {
    conf := d.Options.Storage
    d.Storage, err = storage.New(conf.Driver, conf.Opts)
    return err
}

func (d *Daemon) NewFront() (err error) {
    d.Front = &server.Server{}
    Addr := fmt.Sprintf("%s:%d", d.Options.Host, d.Options.Port)
    if !d.Options.Security.TLS && !d.Options.Security.Verify {
        err = d.Front.Run(iris.Addr(Addr))
    } else {
        runner := iris.TLS(Addr, d.Options.Security.Cert, d.Options.Security.Key)
        err = d.Front.Run(runner)
    }
    if err == nil {
        d.Front.AddRouter(RouteToken.NewRouter(d))
    }
    return err
}

func (d *Daemon) NewBackend() (err error) {
    d.Backend = &server.Server{}
    l, err := netutil.UNIX("/tmpl/srv.sock", 0666)
    if err != nil {
        return err
    }
    err = d.Backend.Run(iris.Listener(l))
    if err == nil {
        // todo add backend router
    }
    return err
}

func (d *Daemon) Generate(req token.GenerateRequest) (string, error) {
    Claims := CustomClaims{
        req.Device,
        req.Unique,
        time.Now().Unix(),
        req.Addr,
        jwt.StandardClaims{
            ExpiresAt: time.Now().Add(time.Second * TOKEN_TTL).Unix(),
            Issuer: "shuc324@gmail.com",
        },
    }
    Token := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims)
    if Signed, err := Token.SignedString([]byte(d.Options.Secret)); err != nil {
        return "", err
    } else {
        err := (*d.Storage).LKeep(req.Unique, Signed, ALLOW_LOGIN_NUM, TOKEN_TTL)
        if err != nil {
            return "", err
        }
        return Signed, err
    }
}

func (d *Daemon) Auth(req token.AuthRequest) (interface{}, error) {
    Token, err := jwt.ParseWithClaims(
        req.JsonWebToken,
        &CustomClaims{},
        func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("Unexpected signing method %v", token.Header["alg"])
            }
            return []byte(d.Options.Secret), nil
        })
    if err == nil && Token.Valid {
        if Claims, ok := Token.Claims.(*CustomClaims); ok {
            if (*d.Storage).LExist(Claims.Unique, req.JsonWebToken) {
                return Claims, nil
            }
        }
    }
    return nil, err
}

func NewDaemon(background bool, args Options) *Daemon {
    if background {
        ctx := daemon.Context{
            PidFileName: args.PidFile,
            PidFilePerm: 0644,
            LogFilePerm: 0640,
            Umask:       027,
            WorkDir:     "/",
            LogFileName: args.LogFile,
        }
        if rank, err := logrus.ParseLevel(args.LogLevel); err != nil {
            fmt.Println(err)
            os.Exit(0)
        } else {
            logrus.SetFormatter(&logrus.TextFormatter{
                TimestampFormat: "2006-01-02 15:04:05",
            })
            logrus.SetLevel(rank)
        }
        if process, err := ctx.Reborn(); err == nil {
            defer ctx.Release()
            if process != nil {
                return nil
            }
        } else {
            if err == daemon.ErrWouldBlock {
                fmt.Println("daemon already exists.")
            } else {
                fmt.Println("Unable to run: ", err)
            }
            os.Exit(0)
        }
    }
    return &Daemon{Options: &args}
}

func NewStart(args Options) {
    var err error;

    if args.Version == true {
        fmt.Printf("JwtAuth version %s.\n", VERSION)
        os.Exit(0)
    }

    Process := NewDaemon(args.Daemon, args)

    if Process == nil {
        return
    }

    if Process.Options.Secret == "" {
        fmt.Println("please specify the key.")
        os.Exit(0)
    }

    if err = Process.NewStorage(); err != nil {
        fmt.Println(err)
        os.Exit(0)
    }

    if err = Process.NewFront(); err != nil {
        fmt.Printf("front server listen error: %s", err)
        os.Exit(0)
    }

    if err = Process.NewBackend(); err != nil {
        fmt.Printf("backend server listen error: %s", err)
        os.Exit(0)
    }
}<commit_msg>fix bug<commit_after>package daemon

import (
    "os"
    "time"
    "fmt"
    "github.com/sevlyar/go-daemon"
    "github.com/Sirupsen/logrus"
    "github.com/BluePecker/JwtAuth/server/types/token"
    "github.com/BluePecker/JwtAuth/server"
    "github.com/BluePecker/JwtAuth/storage"
    //"github.com/BluePecker/JwtAuth/server/router"
    _ "github.com/BluePecker/JwtAuth/storage/redis"
    //_ "github.com/BluePecker/JwtAuth/storage/ram"
    "github.com/dgrijalva/jwt-go"
    RouteToken "github.com/BluePecker/JwtAuth/server/router/token"
    "github.com/kataras/iris"
    "github.com/kataras/iris/core/netutil"
)

const (
    TOKEN_TTL = 2 * 3600

    VERSION = "1.0.0"

    ALLOW_LOGIN_NUM = 3
)

type Storage struct {
    Driver string
    Opts   string
}

type Security struct {
    Verify bool
    TLS    bool
    Key    string
    Cert   string
}

type Options struct {
    PidFile  string
    LogFile  string
    LogLevel string
    Port     int
    Host     string
    Daemon   bool
    Version  bool
    Security Security
    Storage  Storage
    Secret   string
}

type Daemon struct {
    Options *Options
    Front   *server.Server
    Backend *server.Server
    Storage *storage.Driver
}

type (
    CustomClaims struct {
        Device    string `json:"device"`
        Unique    string `json:"unique"`
        Timestamp int64  `json:"timestamp"`
        Addr      string `json:"addr"`
        jwt.StandardClaims
    }
)

func (d *Daemon) NewStorage() (err error) {
    conf := d.Options.Storage
    d.Storage, err = storage.New(conf.Driver, conf.Opts)
    return err
}

func (d *Daemon) NewFront() (err error) {
    d.Front = &server.Server{}
    Addr := fmt.Sprintf("%s:%d", d.Options.Host, d.Options.Port)
    if !d.Options.Security.TLS && !d.Options.Security.Verify {
        err = d.Front.Run(iris.Addr(Addr))
    } else {
        runner := iris.TLS(Addr, d.Options.Security.Cert, d.Options.Security.Key)
        err = d.Front.Run(runner)
    }
    if err == nil {
        d.Front.AddRouter(RouteToken.NewRouter(d))
    }
    return err
}

func (d *Daemon) NewBackend() (err error) {
    d.Backend = &server.Server{}
    l, err := netutil.UNIX("/tmpl/srv.sock", 0666)
    if err != nil {
        return err
    }
    err = d.Backend.Run(iris.Listener(l))
    if err == nil {
        // todo add backend router
    }
    return err
}

func (d *Daemon) Generate(req token.GenerateRequest) (string, error) {
    Claims := CustomClaims{
        req.Device,
        req.Unique,
        time.Now().Unix(),
        req.Addr,
        jwt.StandardClaims{
            ExpiresAt: time.Now().Add(time.Second * TOKEN_TTL).Unix(),
            Issuer: "shuc324@gmail.com",
        },
    }
    Token := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims)
    if Signed, err := Token.SignedString([]byte(d.Options.Secret)); err != nil {
        return "", err
    } else {
        err := (*d.Storage).LKeep(req.Unique, Signed, ALLOW_LOGIN_NUM, TOKEN_TTL)
        if err != nil {
            return "", err
        }
        return Signed, err
    }
}

func (d *Daemon) Auth(req token.AuthRequest) (interface{}, error) {
    Token, err := jwt.ParseWithClaims(
        req.JsonWebToken,
        &CustomClaims{},
        func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("Unexpected signing method %v", token.Header["alg"])
            }
            return []byte(d.Options.Secret), nil
        })
    if err == nil && Token.Valid {
        if Claims, ok := Token.Claims.(*CustomClaims); ok {
            if (*d.Storage).LExist(Claims.Unique, req.JsonWebToken) {
                return Claims, nil
            }
        }
    }
    return nil, err
}

func NewDaemon(background bool, args Options) *Daemon {
    if background {
        ctx := daemon.Context{
            PidFileName: args.PidFile,
            PidFilePerm: 0644,
            LogFilePerm: 0640,
            Umask:       027,
            WorkDir:     "/",
            LogFileName: args.LogFile,
        }
        if rank, err := logrus.ParseLevel(args.LogLevel); err != nil {
            fmt.Println(err)
            os.Exit(0)
        } else {
            logrus.SetFormatter(&logrus.TextFormatter{
                TimestampFormat: "2006-01-02 15:04:05",
            })
            logrus.SetLevel(rank)
        }
        if process, err := ctx.Reborn(); err == nil {
            defer ctx.Release()
            if process != nil {
                return nil
            }
        } else {
            if err == daemon.ErrWouldBlock {
                fmt.Println("daemon already exists.")
            } else {
                fmt.Println("Unable to run: ", err)
            }
            os.Exit(0)
        }
    }
    return &Daemon{Options: &args}
}

func NewStart(args Options) {
    var err error;

    if args.Version == true {
        fmt.Printf("JwtAuth version %s.\n", VERSION)
        os.Exit(0)
    }

    Process := NewDaemon(args.Daemon, args)

    if Process == nil {
        return
    }

    if Process.Options.Secret == "" {
        fmt.Println("please specify the key.")
        os.Exit(0)
    }

    if err = Process.NewStorage(); err != nil {
        fmt.Println(err)
        os.Exit(0)
    }

    go func() {
        if err = Process.NewBackend(); err != nil {
            fmt.Printf("backend server listen error: %s", err)
            os.Exit(0)
        }
    }()

    if err = Process.NewFront(); err != nil {
        fmt.Printf("front server listen error: %s", err)
        os.Exit(0)
    }
}<|endoftext|>


<commit_before>package game

import (
	"context"
	"github.com/Sirupsen/logrus"
	"github.com/pkg/errors"
	"github.com/verath/archipelago/lib/common"
	"github.com/verath/archipelago/lib/game/model"
)

// The game controller represents a single game. It starts and handles
// communication between the game loop and the player connections.
type controller struct {
	logEntry *logrus.Entry

	gameLoop *gameLoop
	p1Proxy  *playerProxy
	p2Proxy  *playerProxy
}

// newController creates a new game controller.
func newController(log *logrus.Logger, game *model.Game, p1Client, p2Client Client) (*controller, error) {
	logEntry := common.ModuleLogEntryWithID(log, "game/controller")

	gameLoop, err := newGameLoop(log, game)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating gameLoop")
	}
	p1Proxy, err := newPlayerProxy(game.Player1(), p1Client)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating player1 proxy")
	}
	p2Proxy, err := newPlayerProxy(game.Player2(), p2Client)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating player2 proxy")
	}

	return &controller{
		logEntry: logEntry,
		gameLoop: gameLoop,
		p1Proxy:  p1Proxy,
		p2Proxy:  p2Proxy,
	}, nil
}

// run starts the game controller, in turn starting the gameLoop and makes
// the game controller start listening for player actions. Blocks until an
// error occurs, the context is canceled, or the game is successfully finished.
func (ctrl *controller) run(ctx context.Context) error {
	ctrl.logEntry.Debug("Starting")
	defer ctrl.logEntry.Debug("Stopped")

	// Notify the players that the game is starting
	startEvt := &model.EventGameStart{}
	if err := ctrl.broadcastEvent(ctx, startEvt); err != nil {
		return errors.Wrap(err, "Could not broadcast game starting event")
	}
	// Register ourselves as the eventHandler of the game loop
	ctrl.gameLoop.SetEventHandler(ctrl)
	defer ctrl.gameLoop.SetEventHandler(nil)

	// Start the action forwarding loop
	ctx, cancel := context.WithCancel(ctx)
	actionErrCh := make(chan error)
	go func() { actionErrCh <- ctrl.actionLoop(ctx) }()
	// Before returning control, make sure the action loop has finished
	defer func() {
		ctrl.logEntry.Debug("Stopping the action loop")
		cancel()
		<-actionErrCh
	}()
	// Run the game loop until it finishes
	err := ctrl.gameLoop.Run(ctx)
	return errors.Wrap(err, "Error while running game loop")
}

// Broadcast an event to both the player proxies simultaneously. Blocks until both
// events have been sent.
func (ctrl *controller) broadcastEvent(ctx context.Context, evt model.Event) error {
	errCh := make(chan error)
	go func() { errCh <- ctrl.p1Proxy.WriteEvent(ctx, evt) }()
	go func() { errCh <- ctrl.p2Proxy.WriteEvent(ctx, evt) }()
	err, err2 := <-errCh, <-errCh
	if err == nil {
		err = err2
	}
	return errors.Wrapf(err, "Failed broadcasting event: %v", evt)
}

// handleEvent forwards the event to both players, and blocks until the event
// has been successfully sent. Called by the gameLoop for each event produced.
func (ctrl *controller) handleEvent(ctx context.Context, evt model.Event) {
	if err := ctrl.broadcastEvent(ctx, evt); err != nil {
		ctrl.logEntry.Debugf("Error in handleEvent: %+v", err)
	}
}

// playerActionLoop is a helper method for reading actions from one player
// and sending them to the actionCh. Blocks until an error occurs or the context
// is canceled. Always returns a non-nil error.
func (ctrl *controller) playerActionLoop(ctx context.Context, proxy *playerProxy, actionCh chan<- model.Action) error {
	for {
		act, err := proxy.ReadAction(ctx)
		if err != nil {
			return errors.Wrap(err, "Could not get action from proxy")
		}
		select {
		case actionCh <- act:
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// The actionLoop takes actions from both players and forwards them
// to the gameLoop. Blocks until an error occurs or the context
// is canceled. Always returns a non-nil error.
func (ctrl *controller) actionLoop(ctx context.Context) error {
	// A struct holding both an error and the proxy in which the error occurred,
	// used so we can share the handling of the error
	type actionLoopError struct {
		error error
		proxy *playerProxy
	}
	ctx, cancel := context.WithCancel(ctx)
	actionCh := make(chan model.Action)
	errCh := make(chan actionLoopError)

	// Spawn a actions reading loop for each player, both broadcasting
	// new actions to a shared actionCh channel.
	go func() {
		err := ctrl.playerActionLoop(ctx, ctrl.p1Proxy, actionCh)
		errCh <- actionLoopError{err, ctrl.p1Proxy}
	}()
	go func() {
		err := ctrl.playerActionLoop(ctx, ctrl.p2Proxy, actionCh)
		errCh <- actionLoopError{err, ctrl.p2Proxy}
	}()

	// Read actions from the actionCh channel and forward them to the
	// gameLoop, applying them to the game. If an error is posted on the
	// errCh, then a player leave action is created for that player,
	// resulting in a game over with the remaining player as the winner.
	for {
		select {
		case act := <-actionCh:
			ctrl.gameLoop.AddAction(act)
		case err := <-errCh:
			leavePlayerAct := &model.PlayerActionLeave{}
			leaveAct := leavePlayerAct.ToAction(err.proxy.playerID)
			ctrl.gameLoop.AddAction(leaveAct)
			cancel()
			<-errCh
			ctrl.logEntry.WithFields(logrus.Fields{
				logrus.ErrorKey: err.error,
				"PlayerID":      err.proxy.playerID,
			}).Debug("Error in action loop")
			return err.error
		}
	}
}
<commit_msg>Display type of event when failed to broadcast<commit_after>package game

import (
	"context"
	"github.com/Sirupsen/logrus"
	"github.com/pkg/errors"
	"github.com/verath/archipelago/lib/common"
	"github.com/verath/archipelago/lib/game/model"
)

// The game controller represents a single game. It starts and handles
// communication between the game loop and the player connections.
type controller struct {
	logEntry *logrus.Entry

	gameLoop *gameLoop
	p1Proxy  *playerProxy
	p2Proxy  *playerProxy
}

// newController creates a new game controller.
func newController(log *logrus.Logger, game *model.Game, p1Client, p2Client Client) (*controller, error) {
	logEntry := common.ModuleLogEntryWithID(log, "game/controller")

	gameLoop, err := newGameLoop(log, game)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating gameLoop")
	}
	p1Proxy, err := newPlayerProxy(game.Player1(), p1Client)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating player1 proxy")
	}
	p2Proxy, err := newPlayerProxy(game.Player2(), p2Client)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating player2 proxy")
	}

	return &controller{
		logEntry: logEntry,
		gameLoop: gameLoop,
		p1Proxy:  p1Proxy,
		p2Proxy:  p2Proxy,
	}, nil
}

// run starts the game controller, in turn starting the gameLoop and makes
// the game controller start listening for player actions. Blocks until an
// error occurs, the context is canceled, or the game is successfully finished.
func (ctrl *controller) run(ctx context.Context) error {
	ctrl.logEntry.Debug("Starting")
	defer ctrl.logEntry.Debug("Stopped")

	// Notify the players that the game is starting
	startEvt := &model.EventGameStart{}
	if err := ctrl.broadcastEvent(ctx, startEvt); err != nil {
		return errors.Wrap(err, "Could not broadcast game starting event")
	}
	// Register ourselves as the eventHandler of the game loop
	ctrl.gameLoop.SetEventHandler(ctrl)
	defer ctrl.gameLoop.SetEventHandler(nil)

	// Start the action forwarding loop
	ctx, cancel := context.WithCancel(ctx)
	actionErrCh := make(chan error)
	go func() { actionErrCh <- ctrl.actionLoop(ctx) }()
	// Before returning control, make sure the action loop has finished
	defer func() {
		ctrl.logEntry.Debug("Stopping the action loop")
		cancel()
		<-actionErrCh
	}()
	// Run the game loop until it finishes
	err := ctrl.gameLoop.Run(ctx)
	return errors.Wrap(err, "Error while running game loop")
}

// Broadcast an event to both the player proxies simultaneously. Blocks until both
// events have been sent.
func (ctrl *controller) broadcastEvent(ctx context.Context, evt model.Event) error {
	errCh := make(chan error)
	go func() { errCh <- ctrl.p1Proxy.WriteEvent(ctx, evt) }()
	go func() { errCh <- ctrl.p2Proxy.WriteEvent(ctx, evt) }()
	err, err2 := <-errCh, <-errCh
	if err == nil {
		err = err2
	}
	return errors.Wrapf(err, "Failed broadcasting event of type: %T", evt)
}

// handleEvent forwards the event to both players, and blocks until the event
// has been successfully sent. Called by the gameLoop for each event produced.
func (ctrl *controller) handleEvent(ctx context.Context, evt model.Event) {
	if err := ctrl.broadcastEvent(ctx, evt); err != nil {
		ctrl.logEntry.Debugf("Error in handleEvent: %+v", err)
	}
}

// playerActionLoop is a helper method for reading actions from one player
// and sending them to the actionCh. Blocks until an error occurs or the context
// is canceled. Always returns a non-nil error.
func (ctrl *controller) playerActionLoop(ctx context.Context, proxy *playerProxy, actionCh chan<- model.Action) error {
	for {
		act, err := proxy.ReadAction(ctx)
		if err != nil {
			return errors.Wrap(err, "Could not get action from proxy")
		}
		select {
		case actionCh <- act:
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// The actionLoop takes actions from both players and forwards them
// to the gameLoop. Blocks until an error occurs or the context
// is canceled. Always returns a non-nil error.
func (ctrl *controller) actionLoop(ctx context.Context) error {
	// A struct holding both an error and the proxy in which the error occurred,
	// used so we can share the handling of the error
	type actionLoopError struct {
		error error
		proxy *playerProxy
	}
	ctx, cancel := context.WithCancel(ctx)
	actionCh := make(chan model.Action)
	errCh := make(chan actionLoopError)

	// Spawn a actions reading loop for each player, both broadcasting
	// new actions to a shared actionCh channel.
	go func() {
		err := ctrl.playerActionLoop(ctx, ctrl.p1Proxy, actionCh)
		errCh <- actionLoopError{err, ctrl.p1Proxy}
	}()
	go func() {
		err := ctrl.playerActionLoop(ctx, ctrl.p2Proxy, actionCh)
		errCh <- actionLoopError{err, ctrl.p2Proxy}
	}()

	// Read actions from the actionCh channel and forward them to the
	// gameLoop, applying them to the game. If an error is posted on the
	// errCh, then a player leave action is created for that player,
	// resulting in a game over with the remaining player as the winner.
	for {
		select {
		case act := <-actionCh:
			ctrl.gameLoop.AddAction(act)
		case err := <-errCh:
			leavePlayerAct := &model.PlayerActionLeave{}
			leaveAct := leavePlayerAct.ToAction(err.proxy.playerID)
			ctrl.gameLoop.AddAction(leaveAct)
			cancel()
			<-errCh
			ctrl.logEntry.WithFields(logrus.Fields{
				logrus.ErrorKey: err.error,
				"PlayerID":      err.proxy.playerID,
			}).Debug("Error in action loop")
			return err.error
		}
	}
}
<|endoftext|>


<commit_before>package lib

import (
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"sync"
	"strconv"
	"io/ioutil"
	"regexp"

	"github.com/matryer/runner"
	"github.com/xiwenc/cf-fastpush-controller/utils"
)

type FileEntry struct {
	Path     string
	Checksum string
	Content	 []byte
}

type Status struct {
	Health	 string
}

var task *runner.Task
var cmd *exec.Cmd
var lock = sync.RWMutex{}
var cmdRaw = ""

const (
	ENV_RESTART_REGEX = "FASTPUSH_RESTART_REGEX"
	ENV_IGNORE_REGEX = "FASTPUSH_IGNORE_REGEX"
)

func RestartApp(backendRunCommand string) Status {
	log.Println("Restarting backend")

	if len(backendRunCommand) > 0 {
		cmdRaw = backendRunCommand
	} else {
		backendRunCommand = cmdRaw
	}

	parts := strings.Fields(backendRunCommand)
	head := parts[0]
	parts = parts[1:len(parts)]
	lock.RLock()

	if task != nil {
		task.Stop()
		cmd.Wait()
	}
	cmd = exec.Command(head, parts...)

	task = runner.Go(func(shouldStop runner.S) error {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Start()

		for {
			if shouldStop() {
				cmd.Process.Signal(syscall.SIGTERM)
				break
			}
		}
		return nil
	})
	lock.RUnlock()
	return Status{Health: "Restarting"}
}

func ListFiles() []*FileEntry {
	var store = []*FileEntry{}
	dir := "./"

	err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
		if f.IsDir() {
			return nil
		}
		fileEntry := FileEntry{}
		fileEntry.Path = path
		checksum, _ := utils.ChecksumsForFile(path)
		fileEntry.Checksum = checksum.SHA256
		store = append(store, &fileEntry)
		return nil
	})
	if err != nil {
		log.Println(err)
	}
	return store
}

func GetStatus() Status {
	status := Status{}

	if cmd.Process.Pid > 0 {
		status.Health = "Running"
	} else {
		status.Health = "Not-Running"
	}
	return status
}

func UploadFiles(files []FileEntry) Status {
	status := Status{}
	failed := 0
	updated := 0
	restart := false
	for _, fileEntry := range files {
		log.Println("Updating file: " + fileEntry.Path)
		err := ioutil.WriteFile(fileEntry.Path, fileEntry.Content, 0644)
		if err != nil {
			log.Println(err)
			failed++
		} else {
			if NeedsRestart(fileEntry) {
				restart = true
			}
			updated++
		}
	}

	if failed > 0 {
		status.Health = "Failed to update " + strconv.Itoa(failed) + " files"
	} else {
		status.Health = "Updated " + strconv.Itoa(updated) + " files"
	}

	if restart {
		RestartApp("")
		status.Health = "Restarting after updating " + strconv.Itoa(updated) + " files"
	}
	return status
}

func NeedsRestart(file FileEntry) bool {
	ignoreRegex := os.Getenv(ENV_IGNORE_REGEX)
	if len(ignoreRegex) > 0 {
		match, _ := regexp.MatchString(ignoreRegex, file.Path)
		if match {
			log.Println("Skipping restart for: " + file.Path)
			return false
		}
	}
	restartRegex := os.Getenv(ENV_RESTART_REGEX)
	if len(restartRegex) > 0 {
		match, _ := regexp.MatchString(restartRegex, file.Path)
		if match {
			log.Println("Requires restart for: " + file.Path)
			return true
		}
	}
	return false
}<commit_msg>goroutine for backend uses time.Sleep to give up CPU usage<commit_after>package lib

import (
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"sync"
	"strconv"
	"io/ioutil"
	"regexp"
	"time"

	"github.com/matryer/runner"
	"github.com/xiwenc/cf-fastpush-controller/utils"
)

type FileEntry struct {
	Path     string
	Checksum string
	Content	 []byte
}

type Status struct {
	Health	 string
}

var task *runner.Task
var cmd *exec.Cmd
var lock = sync.RWMutex{}
var cmdRaw = ""

const (
	ENV_RESTART_REGEX = "FASTPUSH_RESTART_REGEX"
	ENV_IGNORE_REGEX = "FASTPUSH_IGNORE_REGEX"
)

func RestartApp(backendRunCommand string) Status {
	log.Println("Restarting backend")

	if len(backendRunCommand) > 0 {
		cmdRaw = backendRunCommand
	} else {
		backendRunCommand = cmdRaw
	}

	parts := strings.Fields(backendRunCommand)
	head := parts[0]
	parts = parts[1:len(parts)]
	lock.RLock()

	if task != nil {
		task.Stop()
		cmd.Wait()
	}
	cmd = exec.Command(head, parts...)

	task = runner.Go(func(shouldStop runner.S) error {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Start()

		for {
			if shouldStop() {
				cmd.Process.Signal(syscall.SIGTERM)
				break
			}
			time.Sleep(1000 * time.Millisecond)
		}
		return nil
	})
	lock.RUnlock()
	return Status{Health: "Restarting"}
}

func ListFiles() []*FileEntry {
	var store = []*FileEntry{}
	dir := "./"

	err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
		if f.IsDir() {
			return nil
		}
		fileEntry := FileEntry{}
		fileEntry.Path = path
		checksum, _ := utils.ChecksumsForFile(path)
		fileEntry.Checksum = checksum.SHA256
		store = append(store, &fileEntry)
		return nil
	})
	if err != nil {
		log.Println(err)
	}
	return store
}

func GetStatus() Status {
	status := Status{}

	if cmd.Process.Pid > 0 {
		status.Health = "Running"
	} else {
		status.Health = "Not-Running"
	}
	return status
}

func UploadFiles(files []FileEntry) Status {
	status := Status{}
	failed := 0
	updated := 0
	restart := false
	for _, fileEntry := range files {
		log.Println("Updating file: " + fileEntry.Path)
		err := ioutil.WriteFile(fileEntry.Path, fileEntry.Content, 0644)
		if err != nil {
			log.Println(err)
			failed++
		} else {
			if NeedsRestart(fileEntry) {
				restart = true
			}
			updated++
		}
	}

	if failed > 0 {
		status.Health = "Failed to update " + strconv.Itoa(failed) + " files"
	} else {
		status.Health = "Updated " + strconv.Itoa(updated) + " files"
	}

	if restart {
		RestartApp("")
		status.Health = "Restarting after updating " + strconv.Itoa(updated) + " files"
	}
	return status
}

func NeedsRestart(file FileEntry) bool {
	ignoreRegex := os.Getenv(ENV_IGNORE_REGEX)
	if len(ignoreRegex) > 0 {
		match, _ := regexp.MatchString(ignoreRegex, file.Path)
		if match {
			log.Println("Skipping restart for: " + file.Path)
			return false
		}
	}
	restartRegex := os.Getenv(ENV_RESTART_REGEX)
	if len(restartRegex) > 0 {
		match, _ := regexp.MatchString(restartRegex, file.Path)
		if match {
			log.Println("Requires restart for: " + file.Path)
			return true
		}
	}
	return false
}<|endoftext|>


<commit_before>package core

import (
	"testing"
	"time"

	"github.com/af83/edwig/model"
)

func Test_StopMonitoringBroadcaster_Create_Events(t *testing.T) {
	model.SetDefaultClock(model.NewFakeClock())

	referentials := NewMemoryReferentials()
	referential := referentials.New("Un Referential Plutot Cool")
	referential.model = model.NewMemoryModel()

	referential.brocasterManager = NewBroadcasterManager(referential)
	referential.brocasterManager.Run()
	referential.Model().StopVisits().(*model.MemoryStopVisits).BroadcastEventChan = referential.brocasterManager.StopVisitBroadcastEvent()

	partner := referential.Partners().New("Un Partner tout autant cool")
	partner.Settings["remote_objectid_kind"] = "internal"
	partner.ConnectorTypes = []string{TEST_STOP_MONITORING_SUBSCRIPTION_BROADCASTER}
	partner.RefreshConnectors()
	referential.Partners().Save(partner)

	connector, _ := partner.Connector(TEST_STOP_MONITORING_SUBSCRIPTION_BROADCASTER)

	stopArea := referential.Model().StopAreas().New()
	stopArea.Save()

	objectid := model.NewObjectID("internal", string(stopArea.Id()))
	stopArea.SetObjectID(objectid)

	reference := model.Reference{
		ObjectId: &objectid,
		Id:       string(stopArea.Id()),
		Type:     "StopArea",
	}

	subscriptionRessource, _ := partner.Subscriptions().FindOrCreateByKind("stopMonitoring")
	subscriptionRessource.CreateAddNewResource(reference)

	stopVisit := referential.Model().StopVisits().New()
	stopVisit.StopAreaId = stopArea.Id()
	time.Sleep(5 * time.Millisecond) // Wait for the goRoutine to start ...
	stopVisit.Save()

	time.Sleep(5 * time.Millisecond) // Wait for the Broadcaster and Connector to finish their work
	if len(connector.(*TestStopMonitoringSubscriptionBroadcaster).events) != 1 {
		t.Error("1 event should have been generated got: ", len(connector.(*TestStopMonitoringSubscriptionBroadcaster).events))
	}
}

// func Test_StopMonitoringBroadcaster_Receive_Notify(t *testing.T) {
// 	//fakeClock := model.NewFakeClock()
//
// 	// Create a test http server
// 	response := []byte{}
// 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 		if r.ContentLength <= 0 {
// 			t.Errorf("Notify ContentLength should be zero")
// 		}
// 		r.Body.Read(response)
// 	}))
// 	defer ts.Close()
//
// 	// Create a test http server
// 	referentials := NewMemoryReferentials()
// 	referential := referentials.New("Un Referential Plutot Cool")
// 	referential.model = model.NewMemoryModel()
//
// 	referential.brocasterManager = NewBroadcasterManager(referential)
// 	referential.brocasterManager.Run()
// 	referential.Model().StopVisits().(*model.MemoryStopVisits).BroadcastEventChan = referential.brocasterManager.StopVisitBroadcastEvent()
//
// 	partner := referential.Partners().New("Un Partner tout autant cool")
// 	partner.Settings["remote_objectid_kind"] = "internal"
// 	partner.Settings["remote_url"] = ts.URL
//
// 	partner.ConnectorTypes = []string{SIRI_STOP_MONITORING_SUBSCRIPTION_BROADCASTER}
// 	partner.RefreshConnectors()
// 	referential.Partners().Save(partner)
//
// 	connector, _ := partner.Connector(SIRI_STOP_MONITORING_SUBSCRIPTION_BROADCASTER)
// 	connector.(*SIRIStopMonitoringSubscriptionBroadcaster).stopMonitoringBroadcaster = NewFakeStopMonitoringBroadcaster(connector.(*SIRIStopMonitoringSubscriptionBroadcaster))
//
// 	stopArea := referential.Model().StopAreas().New()
// 	stopArea.Save()
//
// 	objectid := model.NewObjectID("internal", string(stopArea.Id()))
// 	stopArea.SetObjectID(objectid)
//
// 	reference := model.Reference{
// 		ObjectId: &objectid,
// 		Id:       string(stopArea.Id()),
// 		Type:     "StopArea",
// 	}
//
// 	subscriptionRessource, _ := partner.Subscriptions().FindOrCreateByKind("stopMonitoring")
// 	subscriptionRessource.CreateAddNewResource(reference)
//
// 	stopVisit := referential.Model().StopVisits().New()
// 	stopVisit.StopAreaId = stopArea.Id()
// 	time.Sleep(10 * time.Millisecond) // Wait for the goRoutine to start ...
// 	stopVisit.Save()
//
// 	time.Sleep(100 * time.Millisecond) // Wait for the Broadcaster and Connector to finish their work
// 	connector.(*SIRIStopMonitoringSubscriptionBroadcaster).stopMonitoringBroadcaster.Run()
// 	time.Sleep(100 * time.Millisecond) // Wait for the Broadcaster and Connector to finish their work
//
// 	if len(response) == 0 {
// 		t.Errorf("reponse == %v", response)
// 	}
// }
<commit_msg>Testing the NotifyStopMonitoring response from event to the request<commit_after>package core

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/af83/edwig/model"
	"github.com/af83/edwig/siri"
)

func Test_StopMonitoringBroadcaster_Create_Events(t *testing.T) {
	model.SetDefaultClock(model.NewFakeClock())

	referentials := NewMemoryReferentials()
	referential := referentials.New("Un Referential Plutot Cool")
	referential.model = model.NewMemoryModel()

	referential.brocasterManager = NewBroadcasterManager(referential)
	referential.brocasterManager.Run()
	referential.Model().StopVisits().(*model.MemoryStopVisits).BroadcastEventChan = referential.brocasterManager.StopVisitBroadcastEvent()

	partner := referential.Partners().New("Un Partner tout autant cool")
	partner.Settings["remote_objectid_kind"] = "internal"
	partner.ConnectorTypes = []string{TEST_STOP_MONITORING_SUBSCRIPTION_BROADCASTER}
	partner.RefreshConnectors()
	referential.Partners().Save(partner)

	connector, _ := partner.Connector(TEST_STOP_MONITORING_SUBSCRIPTION_BROADCASTER)

	stopArea := referential.Model().StopAreas().New()
	stopArea.Save()

	objectid := model.NewObjectID("internal", string(stopArea.Id()))
	stopArea.SetObjectID(objectid)

	reference := model.Reference{
		ObjectId: &objectid,
		Id:       string(stopArea.Id()),
		Type:     "StopArea",
	}

	subscriptionRessource, _ := partner.Subscriptions().FindOrCreateByKind("stopMonitoring")
	subscriptionRessource.CreateAddNewResource(reference)

	stopVisit := referential.Model().StopVisits().New()
	stopVisit.StopAreaId = stopArea.Id()
	time.Sleep(5 * time.Millisecond) // Wait for the goRoutine to start ...
	stopVisit.Save()

	time.Sleep(5 * time.Millisecond) // Wait for the Broadcaster and Connector to finish their work
	if len(connector.(*TestStopMonitoringSubscriptionBroadcaster).events) != 1 {
		t.Error("1 event should have been generated got: ", len(connector.(*TestStopMonitoringSubscriptionBroadcaster).events))
	}
}

func Test_StopMonitoringBroadcaster_Receive_Notify(t *testing.T) {
	//fakeClock := model.NewFakeClock()

	// Create a test http server
	response := []byte{}
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		response, _ = ioutil.ReadAll(r.Body)
		w.Header().Add("Content-Type", "text/xml")
	}))
	defer ts.Close()

	// Create a test http server
	referentials := NewMemoryReferentials()
	referential := referentials.New("Un Referential Plutot Cool")
	referential.model = model.NewMemoryModel()

	referential.brocasterManager = NewBroadcasterManager(referential)
	referential.brocasterManager.Run()
	referential.Model().StopVisits().(*model.MemoryStopVisits).BroadcastEventChan = referential.brocasterManager.StopVisitBroadcastEvent()

	partner := referential.Partners().New("Un Partner tout autant cool")
	partner.Settings["remote_objectid_kind"] = "internal"
	partner.Settings["remote_credential"] = "external"
	partner.Settings["remote_url"] = ts.URL

	partner.ConnectorTypes = []string{SIRI_STOP_MONITORING_SUBSCRIPTION_BROADCASTER}
	partner.RefreshConnectors()
	referential.Partners().Save(partner)

	connector, _ := partner.Connector(SIRI_STOP_MONITORING_SUBSCRIPTION_BROADCASTER)
	connector.(*SIRIStopMonitoringSubscriptionBroadcaster).stopMonitoringBroadcaster = NewFakeStopMonitoringBroadcaster(connector.(*SIRIStopMonitoringSubscriptionBroadcaster))

	stopArea := referential.Model().StopAreas().New()
	stopArea.Save()

	objectid := model.NewObjectID("internal", string(stopArea.Id()))
	stopArea.SetObjectID(objectid)

	reference := model.Reference{
		ObjectId: &objectid,
		Id:       string(stopArea.Id()),
		Type:     "StopArea",
	}

	subscriptionRessource, _ := partner.Subscriptions().FindOrCreateByKind("stopMonitoring")
	subscriptionRessource.CreateAddNewResource(reference)

	line := referential.Model().Lines().New()
	line.SetObjectID(objectid)
	line.Save()

	vehicleJourney := referential.Model().VehicleJourneys().New()
	vehicleJourney.LineId = line.Id()
	vehicleJourney.SetObjectID(objectid)
	vehicleJourney.Save()

	stopVisit := referential.Model().StopVisits().New()
	stopVisit.StopAreaId = stopArea.Id()
	stopVisit.VehicleJourneyId = vehicleJourney.Id()
	stopVisit.SetObjectID(objectid)

	time.Sleep(10 * time.Millisecond) // Wait for the goRoutine to start ...
	stopVisit.Save()

	time.Sleep(10 * time.Millisecond) // Wait for the Broadcaster and Connector to finish their work
	connector.(*SIRIStopMonitoringSubscriptionBroadcaster).stopMonitoringBroadcaster.Run()

	notify, _ := siri.NewXMLNotifyStopMonitoringFromContent(response)
	delivery := notify.StopMonitoringDeliveries()

	if len(delivery) != 1 {
		t.Errorf("Should have received 1 delivery but got == %v", len(delivery))
	}

	if delivery[0].SubscriberRef() != "external" {
		t.Errorf("SubscriberRef should be external but got == %v", delivery[0].SubscriberRef())
	}

	sv := delivery[0].XMLMonitoredStopVisits()

	if len(sv) != 1 {
		t.Errorf("Should have received 1 StopVisit but got == %v", len(sv))
	}

}
<|endoftext|>


<commit_before>/**
 * go-mapbox Geocoding Module
 * Wraps the mapbox geocoding API for server side use
 * See https://www.mapbox.com/api-documentation/#geocoding for API information
 *
 * https://github.com/ryankurte/go-mapbox
 * Copyright 2017 Ryan Kurte
 */

package geocode

import (
	"fmt"
	"strings"

	"github.com/google/go-querystring/query"
	"github.com/ryankurte/go-mapbox/lib/base"
)

const (
	apiName    = "geocoding"
	apiVersion = "v5"
	apiMode    = "mapbox.places"
)

// Type defines geocode location response types
type Type string

const (
	// Country level
	Country Type = "country"
	// Region level
	Region Type = "region"
	// Postcode level
	Postcode Type = "postcode"
	// District level
	District Type = "district"
	// Place level
	Place Type = "place"
	// Locality level
	Locality Type = "locality"
	// Neighborhood level
	Neighborhood Type = "neighborhood"
	// Address level
	Address Type = "address"
	// POI (Point of Interest) level
	POI Type = "poi"
)

// Geocode api wrapper instance
type Geocode struct {
	base *base.Base
}

// NewGeocode Create a new Geocode API wrapper
func NewGeocode(base *base.Base) *Geocode {
	return &Geocode{base}
}

// ForwardRequestOpts request options fo forward geocoding
type ForwardRequestOpts struct {
	Country      string           `url:"country,omitempty"`
	Proximity    []float64        `url:"proximity,omitempty"`
	Types        []Type           `url:"types,omitempty"`
	Autocomplete bool             `url:"autocomplete,omitempty"`
	BBox         base.BoundingBox `url:"bbox,omitempty"`
	Limit        uint             `url:"limit,omitempty"`
}

// ForwardResponse is the response from a forward geocode lookup
type ForwardResponse struct {
	*base.FeatureCollection
	Query []string
}

// Forward geocode lookup
// Finds locations from a place name
func (g *Geocode) Forward(place string, req *ForwardRequestOpts) (*ForwardResponse, error) {

	v, err := query.Values(req)
	if err != nil {
		return nil, err
	}

	resp := ForwardResponse{}

	queryString := strings.Replace(place, " ", "+", -1)

	err = g.base.Query(apiName, apiVersion, apiMode, fmt.Sprintf("%s.json", queryString), &v, &resp)

	return &resp, err
}

// ReverseRequestOpts request options fo reverse geocoding
type ReverseRequestOpts struct {
	Types []Type
	Limit uint
}

// ReverseResponse is the response to a reverse geocode request
type ReverseResponse struct {
	*base.FeatureCollection
	Query []float64
}

// Reverse geocode lookup
// Finds place names from a location
func (g *Geocode) Reverse(loc *base.Location, req *ReverseRequestOpts) (*ReverseResponse, error) {

	v, err := query.Values(req)
	if err != nil {
		return nil, err
	}

	resp := ReverseResponse{}

	queryString := fmt.Sprintf("%f,%f.json", loc.Longitude, loc.Latitude)

	err = g.base.Query(apiName, apiVersion, apiMode, queryString, &v, &resp)

	return &resp, err
}
<commit_msg>Adding Places Permanent As an additional option & Addition of additional options in the Geocode API<commit_after>/**
 * go-mapbox Geocoding Module
 * Wraps the mapbox geocoding API for server side use
 * See https://www.mapbox.com/api-documentation/#geocoding for API information
 *
 * https://github.com/ryankurte/go-mapbox
 * Copyright 2017 Ryan Kurte
 */

package geocode

import (
	"fmt"
	"strings"

	"github.com/google/go-querystring/query"
	"github.com/ryankurte/go-mapbox/lib/base"
)

const (
	apiName          = "geocoding"
	apiVersion       = "v5"
	apiMode          = "mapbox.places"
	apiModePermanent = "mapbox.places-permanent"
)

// Type defines geocode location response types
type Type string

const (
	// Country level
	Country Type = "country"
	// Region level
	Region Type = "region"
	// Postcode level
	Postcode Type = "postcode"
	// District level
	District Type = "district"
	// Place level
	Place Type = "place"
	// Locality level
	Locality Type = "locality"
	// Neighborhood level
	Neighborhood Type = "neighborhood"
	// Address level
	Address Type = "address"
	// POI (Point of Interest) level
	POI Type = "poi"
)

// Geocode api wrapper instance
type Geocode struct {
	base *base.Base
}

// NewGeocode Create a new Geocode API wrapper
func NewGeocode(base *base.Base) *Geocode {
	return &Geocode{base}
}

// ForwardRequestOpts request options fo forward geocoding
type ForwardRequestOpts struct {
	Country      string           `url:"country,omitempty"`
	Proximity    []float64        `url:"proximity,omitempty"`
	Types        []Type           `url:"types,omitempty"`
	Autocomplete bool             `url:"autocomplete,omitempty"`
	BBox         base.BoundingBox `url:"bbox,omitempty"`
	Limit        uint             `url:"limit,omitempty"`
	FuzzyMatch   bool             `url:"fuzzyMatch,omitempty"`
	Routing      bool             `url:"routing,omitempty"`
}

// ForwardResponse is the response from a forward geocode lookup
type ForwardResponse struct {
	*base.FeatureCollection
	Query []string
}

// Forward geocode lookup
// Finds locations from a place name
func (g *Geocode) Forward(place string, req *ForwardRequestOpts, permanent ...bool) (*ForwardResponse, error) {

	v, err := query.Values(req)
	if err != nil {
		return nil, err
	}

	resp := ForwardResponse{}

	queryString := strings.Replace(place, " ", "+", -1)
	if len(permanent) > 0 && permanent[0] {
		err = g.base.Query(apiName, apiVersion, apiModePermanent, fmt.Sprintf("%s.json", queryString), &v, &resp)
	} else {
		err = g.base.Query(apiName, apiVersion, apiMode, fmt.Sprintf("%s.json", queryString), &v, &resp)
	}

	return &resp, err
}

// ReverseRequestOpts request options fo reverse geocoding
type ReverseRequestOpts struct {
	Types []Type
	Limit uint
}

// ReverseResponse is the response to a reverse geocode request
type ReverseResponse struct {
	*base.FeatureCollection
	Query []float64
}

// Reverse geocode lookup
// Finds place names from a location
func (g *Geocode) Reverse(loc *base.Location, req *ReverseRequestOpts) (*ReverseResponse, error) {

	v, err := query.Values(req)
	if err != nil {
		return nil, err
	}

	resp := ReverseResponse{}

	queryString := fmt.Sprintf("%f,%f.json", loc.Longitude, loc.Latitude)

	err = g.base.Query(apiName, apiVersion, apiMode, queryString, &v, &resp)

	return &resp, err
}
<|endoftext|>