<commit_before>package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/go-errors/errors"
	"github.com/privacybydesign/irmago/server"
	"github.com/privacybydesign/irmago/server/requestorserver"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var logger = logrus.StandardLogger()
var conf *requestorserver.Configuration

var RootCommand = &cobra.Command{
	Use:   "irmad",
	Short: "IRMA server for verifying and issuing attributes",
	Run: func(command *cobra.Command, args []string) {
		if err := configure(command); err != nil {
			die(errors.WrapPrefix(err, "Failed to read configuration", 0))
		}
		serv, err := requestorserver.New(conf)
		if err != nil {
			die(errors.WrapPrefix(err, "Failed to configure server", 0))
		}
		if err := serv.Start(conf); err != nil {
			die(errors.WrapPrefix(err, "Failed to start server", 0))
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCommand.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func die(err *errors.Error) {
	msg := err.Error()
	if logger.IsLevelEnabled(logrus.DebugLevel) {
		msg += "\nStack trace:\n" + string(err.Stack())
	}
	logger.Fatal(msg)
}

func setFlags(cmd *cobra.Command) error {
	flags := cmd.Flags()
	flags.SortFlags = false

	schemespath := server.DefaultSchemesPath()
	defaulturl, err := server.LocalIP()
	if err != nil {
		logger.Warn("Could not determine local IP address: ", err.Error())
	} else {
		defaulturl = "http://" + defaulturl + ":port"
	}

	flags.StringP("config", "c", "", "path to configuration file")
	flags.StringP("schemes-path", "i", schemespath, "path to irma_configuration")
	flags.String("schemes-assets-path", "", "if specified, copy schemes from here into schemes-path")
	flags.Int("schemes-update", 60, "update IRMA schemes every x minutes (0 to disable)")
	flags.Int("max-request-age", 300, "max age in seconds of a session request JWT")
	flags.StringP("url", "u", defaulturl, "external URL to server to which the IRMA client connects")

	flags.IntP("port", "p", 8088, "port at which to listen")
	flags.StringP("listen-addr", "l", "", "address at which to listen (default 0.0.0.0)")
	flags.Int("client-port", 0, "if specified, start a separate server for the IRMA app at this port")
	flags.String("client-listen-addr", "", "address at which server for IRMA app listens")
	flags.Lookup("port").Header = `Server address and port to listen on. If the client* configuration options are provided (see also the TLS flags)
then the endpoints at /session for the requestor and /irma for the irmaclient (i.e. IRMA app) will listen on
distinct network endpoints (e.g., localhost:1234/session and 0.0.0.0:5678/irma).`

	flags.Bool("no-auth", true, "whether or not to authenticate requestors")
	flags.String("requestors", "", "requestor configuration (in JSON)")
	flags.Lookup("no-auth").Header = `Requestor authentication. If disabled, then anyone that can reach this server can submit requests to it.
If it is enabled, then requestor specific configuration must be provided.`

	flags.StringSlice("disclose-perms", nil, "list of attributes that all requestors may verify (default *)")
	flags.StringSlice("sign-perms", nil, "list of attributes that all requestors may request in signatures (default *)")
	flags.StringSlice("issue-perms", nil, "list of attributes that all requestors may issue")
	flags.Lookup("disclose-perms").Header = `Default requestor permissions. Apply to all requestors, in addition to requestor specific permissions.
May contain wildcards. Separate multiple with comma. Example: irma-demo.*,pbdf.*. By default all requestors
may use all attributes in disclosure and signature sessions. Pass empty string to disable session type.`

	flags.StringP("privkeys", "k", "", "path to IRMA private keys")
	flags.Lookup("privkeys").Header = `Path to a folder containing IRMA private keys, with filenames scheme.issuer.xml, e.g. irma-demo.MijnOverheid.xml.
Private keys may also be stored in the scheme (e.g. irma-demo/MijnOverheid/PrivateKeys/0.xml).`

	flags.StringP("jwt-issuer", "j", "irmaserver", "JWT issuer")
	flags.String("jwt-privkey", "", "JWT private key")
	flags.String("jwt-privkeyfile", "", "path to JWT private key")
	flags.Lookup("jwt-issuer").Header = `JWT configuration. Can be omitted but then endpoints that return signed JWTs are disabled.
All of the keys and certificates below are expected in PEM. Pass it either directly, or a path to it
using the corresponding "-file" flag.`

	flags.String("tls-cert", "", "TLS certificate (chain)")
	flags.String("tls-cert-file", "", "path to TLS certificate (chain)")
	flags.String("tls-privkey", "", "TLS private key")
	flags.String("tls-privkey-file", "", "path to TLS private key")
	flags.String("client-tls-cert", "", "TLS certificate (chain) for IRMA app server")
	flags.String("client-tls-cert-file", "", "path to TLS certificate (chain) for IRMA app server")
	flags.String("client-tls-privkey", "", "TLS private key for IRMA app server")
	flags.String("client-tls-privkey-file", "", "path to TLS private key for IRMA app server")
	flags.Lookup("tls-cert").Header = "TLS configuration. Leave empty to disable TLS."

	flags.CountP("verbose", "v", "verbose (repeatable)")
	flags.BoolP("quiet", "q", false, "quiet")
	flags.Bool("log-json", false, "Log in JSON format")
	flags.Bool("production", false, "Production mode")
	flags.Lookup("verbose").Header = `Other options`

	return nil
}

func configure(cmd *cobra.Command) error {
	dashReplacer := strings.NewReplacer("-", "_")
	viper.SetEnvKeyReplacer(dashReplacer)
	viper.SetFileKeyReplacer(dashReplacer)
	viper.SetEnvPrefix("IRMASERVER")
	viper.AutomaticEnv()
	if err := viper.BindPFlags(cmd.Flags()); err != nil {
		return err
	}

	if viper.GetBool("production") {
		viper.SetDefault("no-auth", false)
		viper.SetDefault("url", "")
	}

	// Locate and read configuration file
	confpath := viper.GetString("config")
	if confpath != "" {
		dir, file := filepath.Dir(confpath), filepath.Base(confpath)
		viper.SetConfigName(strings.TrimSuffix(file, filepath.Ext(file)))
		viper.AddConfigPath(dir)
	} else {
		viper.SetConfigName("irmaserver")
		viper.AddConfigPath(".")
		viper.AddConfigPath("/etc/irmaserver/")
		viper.AddConfigPath("$HOME/.irmaserver")
	}
	err := viper.ReadInConfig() // Hold error checking until we know how much of it to log

	// Set log level
	if viper.GetBool("log-json") {
		logger.SetFormatter(&logrus.JSONFormatter{})
	}
	logger.Level = server.Verbosity(viper.GetInt("verbose"))
	if viper.GetBool("quiet") {
		logger.Out = ioutil.Discard
	}

	logger.Debug("Configuring")
	logger.Debug("Log level: ", logger.Level.String())
	if err != nil {
		if _, notfound := err.(viper.ConfigFileNotFoundError); notfound {
			logger.Info("No configuration file found")
		} else {
			die(errors.WrapPrefix(err, "Failed to unmarshal configuration file at "+viper.ConfigFileUsed(), 0))
		}
	} else {
		logger.Info("Config file: ", viper.ConfigFileUsed())
	}

	// Read configuration from flags and/or environmental variables
	conf = &requestorserver.Configuration{
		Configuration: &server.Configuration{
			DownloadDefaultSchemes: true, // If we get passed an empty schemes-path, download default schemes into it
			SchemesPath:            viper.GetString("schemes-path"),
			SchemesAssetsPath:      viper.GetString("schemes-assets-path"),
			SchemeUpdateInterval:   viper.GetInt("schemes-update"),
			IssuerPrivateKeysPath:  viper.GetString("privkeys"),
			URL:    viper.GetString("url"),
			Logger: logger,
		},
		Permissions: requestorserver.Permissions{
			Disclosing: handlePermission("disclose-perms"),
			Signing:    handlePermission("sign-perms"),
			Issuing:    handlePermission("issue-perms"),
		},
		ListenAddress:                  viper.GetString("listen-addr"),
		Port:                           viper.GetInt("port"),
		ClientListenAddress:            viper.GetString("client-listen-addr"),
		ClientPort:                     viper.GetInt("client-port"),
		DisableRequestorAuthentication: viper.GetBool("no-auth"),
		Requestors:                     make(map[string]requestorserver.Requestor),
		JwtIssuer:                      viper.GetString("jwt-issuer"),
		JwtPrivateKey:                  viper.GetString("jwt-privkey"),
		JwtPrivateKeyFile:              viper.GetString("jwt-privkey-file"),
		MaxRequestAge:                  viper.GetInt("max-request-age"),
		Verbose:                        viper.GetInt("verbose"),
		Quiet:                          viper.GetBool("quiet"),
		LogJSON:                        viper.GetBool("log-json"),

		TlsCertificate:           viper.GetString("tls-cert"),
		TlsCertificateFile:       viper.GetString("tls-cert-file"),
		TlsPrivateKey:            viper.GetString("tls-privkey"),
		TlsPrivateKeyFile:        viper.GetString("tls-privkey-file"),
		ClientTlsCertificate:     viper.GetString("client-tls-cert"),
		ClientTlsCertificateFile: viper.GetString("client-tls-cert-file"),
		ClientTlsPrivateKey:      viper.GetString("client-tls-privkey"),
		ClientTlsPrivateKeyFile:  viper.GetString("client-tls-privkey-file"),

		Production: viper.GetBool("production"),
	}

	// Handle requestors
	if len(viper.GetStringMap("requestors")) > 0 { // First read config file
		if err := viper.UnmarshalKey("requestors", &conf.Requestors); err != nil {
			return errors.WrapPrefix(err, "Failed to unmarshal requestors from config file", 0)
		}
	}
	requestors := viper.GetString("requestors") // Read flag or env var
	if len(requestors) > 0 {
		if err := json.Unmarshal([]byte(requestors), &conf.Requestors); err != nil {
			return errors.WrapPrefix(err, "Failed to unmarshal requestors from json", 0)
		}
	}

	logger.Debug("Done configuring")

	return nil
}

func handlePermission(typ string) []string {
	if !viper.IsSet(typ) && (!viper.GetBool("production") || typ != "issue-perms") {
		return []string{"*"}
	}
	perms := viper.GetStringSlice(typ)
	if perms == nil {
		return []string{}
	}
	return perms
}
<commit_msg>Fix log output and add missing flags to main irmad command<commit_after>package cmd

import (
	"encoding/json"
	"io/ioutil"
	"path/filepath"
	"strings"

	"github.com/go-errors/errors"
	"github.com/privacybydesign/irmago/server"
	"github.com/privacybydesign/irmago/server/requestorserver"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/x-cray/logrus-prefixed-formatter"
)

var logger = logrus.StandardLogger()
var conf *requestorserver.Configuration

var RootCommand = &cobra.Command{
	Use:   "irmad",
	Short: "IRMA server for verifying and issuing attributes",
	Run: func(command *cobra.Command, args []string) {
		if err := configure(command); err != nil {
			die(errors.WrapPrefix(err, "Failed to read configuration", 0))
		}
		serv, err := requestorserver.New(conf)
		if err != nil {
			die(errors.WrapPrefix(err, "Failed to configure server", 0))
		}
		if err := serv.Start(conf); err != nil {
			die(errors.WrapPrefix(err, "Failed to start server", 0))
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the RootCommand.
func Execute() {
	logger.Level = logrus.InfoLevel
	logger.SetFormatter(&prefixed.TextFormatter{
		FullTimestamp: true,
	})
	if err := setFlags(RootCommand); err != nil {
		die(errors.WrapPrefix(err, "Failed to attach flags to "+RootCommand.Name()+" command", 0))
	}
	if err := RootCommand.Execute(); err != nil {
		die(errors.Wrap(err, 0))
	}
}

func die(err *errors.Error) {
	msg := err.Error()
	if logger.IsLevelEnabled(logrus.DebugLevel) {
		msg += "\nStack trace:\n" + string(err.Stack())
	}
	logger.Fatal(msg)
}

func setFlags(cmd *cobra.Command) error {
	flags := cmd.Flags()
	flags.SortFlags = false

	schemespath := server.DefaultSchemesPath()
	defaulturl, err := server.LocalIP()
	if err != nil {
		logger.Warn("Could not determine local IP address: ", err.Error())
	} else {
		defaulturl = "http://" + defaulturl + ":port"
	}

	flags.StringP("config", "c", "", "path to configuration file")
	flags.StringP("schemes-path", "i", schemespath, "path to irma_configuration")
	flags.String("schemes-assets-path", "", "if specified, copy schemes from here into schemes-path")
	flags.Int("schemes-update", 60, "update IRMA schemes every x minutes (0 to disable)")
	flags.Int("max-request-age", 300, "max age in seconds of a session request JWT")
	flags.StringP("url", "u", defaulturl, "external URL to server to which the IRMA client connects")

	flags.IntP("port", "p", 8088, "port at which to listen")
	flags.StringP("listen-addr", "l", "", "address at which to listen (default 0.0.0.0)")
	flags.Int("client-port", 0, "if specified, start a separate server for the IRMA app at this port")
	flags.String("client-listen-addr", "", "address at which server for IRMA app listens")
	flags.Lookup("port").Header = `Server address and port to listen on. If the client* configuration options are provided (see also the TLS flags)
then the endpoints at /session for the requestor and /irma for the irmaclient (i.e. IRMA app) will listen on
distinct network endpoints (e.g., localhost:1234/session and 0.0.0.0:5678/irma).`

	flags.Bool("no-auth", true, "whether or not to authenticate requestors")
	flags.String("requestors", "", "requestor configuration (in JSON)")
	flags.Lookup("no-auth").Header = `Requestor authentication. If disabled, then anyone that can reach this server can submit requests to it.
If it is enabled, then requestor specific configuration must be provided.`

	flags.StringSlice("disclose-perms", nil, "list of attributes that all requestors may verify (default *)")
	flags.StringSlice("sign-perms", nil, "list of attributes that all requestors may request in signatures (default *)")
	flags.StringSlice("issue-perms", nil, "list of attributes that all requestors may issue")
	flags.Lookup("disclose-perms").Header = `Default requestor permissions. Apply to all requestors, in addition to requestor specific permissions.
May contain wildcards. Separate multiple with comma. Example: irma-demo.*,pbdf.*. By default all requestors
may use all attributes in disclosure and signature sessions. Pass empty string to disable session type.`

	flags.StringP("privkeys", "k", "", "path to IRMA private keys")
	flags.Lookup("privkeys").Header = `Path to a folder containing IRMA private keys, with filenames scheme.issuer.xml, e.g. irma-demo.MijnOverheid.xml.
Private keys may also be stored in the scheme (e.g. irma-demo/MijnOverheid/PrivateKeys/0.xml).`

	flags.StringP("jwt-issuer", "j", "irmaserver", "JWT issuer")
	flags.String("jwt-privkey", "", "JWT private key")
	flags.String("jwt-privkeyfile", "", "path to JWT private key")
	flags.Lookup("jwt-issuer").Header = `JWT configuration. Can be omitted but then endpoints that return signed JWTs are disabled.
All of the keys and certificates below are expected in PEM. Pass it either directly, or a path to it
using the corresponding "-file" flag.`

	flags.String("tls-cert", "", "TLS certificate (chain)")
	flags.String("tls-cert-file", "", "path to TLS certificate (chain)")
	flags.String("tls-privkey", "", "TLS private key")
	flags.String("tls-privkey-file", "", "path to TLS private key")
	flags.String("client-tls-cert", "", "TLS certificate (chain) for IRMA app server")
	flags.String("client-tls-cert-file", "", "path to TLS certificate (chain) for IRMA app server")
	flags.String("client-tls-privkey", "", "TLS private key for IRMA app server")
	flags.String("client-tls-privkey-file", "", "path to TLS private key for IRMA app server")
	flags.Lookup("tls-cert").Header = "TLS configuration. Leave empty to disable TLS."

	flags.CountP("verbose", "v", "verbose (repeatable)")
	flags.BoolP("quiet", "q", false, "quiet")
	flags.Bool("log-json", false, "Log in JSON format")
	flags.Bool("production", false, "Production mode")
	flags.Lookup("verbose").Header = `Other options`

	return nil
}

func configure(cmd *cobra.Command) error {
	dashReplacer := strings.NewReplacer("-", "_")
	viper.SetEnvKeyReplacer(dashReplacer)
	viper.SetFileKeyReplacer(dashReplacer)
	viper.SetEnvPrefix("IRMASERVER")
	viper.AutomaticEnv()
	if err := viper.BindPFlags(cmd.Flags()); err != nil {
		return err
	}

	if viper.GetBool("production") {
		viper.SetDefault("no-auth", false)
		viper.SetDefault("url", "")
	}

	// Locate and read configuration file
	confpath := viper.GetString("config")
	if confpath != "" {
		dir, file := filepath.Dir(confpath), filepath.Base(confpath)
		viper.SetConfigName(strings.TrimSuffix(file, filepath.Ext(file)))
		viper.AddConfigPath(dir)
	} else {
		viper.SetConfigName("irmaserver")
		viper.AddConfigPath(".")
		viper.AddConfigPath("/etc/irmaserver/")
		viper.AddConfigPath("$HOME/.irmaserver")
	}
	err := viper.ReadInConfig() // Hold error checking until we know how much of it to log

	// Set log level
	if viper.GetBool("log-json") {
		logger.SetFormatter(&logrus.JSONFormatter{})
	}
	logger.Level = server.Verbosity(viper.GetInt("verbose"))
	if viper.GetBool("quiet") {
		logger.Out = ioutil.Discard
	}

	logger.Debug("Configuring")
	logger.Debug("Log level: ", logger.Level.String())
	if err != nil {
		if _, notfound := err.(viper.ConfigFileNotFoundError); notfound {
			logger.Info("No configuration file found")
		} else {
			die(errors.WrapPrefix(err, "Failed to unmarshal configuration file at "+viper.ConfigFileUsed(), 0))
		}
	} else {
		logger.Info("Config file: ", viper.ConfigFileUsed())
	}

	// Read configuration from flags and/or environmental variables
	conf = &requestorserver.Configuration{
		Configuration: &server.Configuration{
			DownloadDefaultSchemes: true, // If we get passed an empty schemes-path, download default schemes into it
			SchemesPath:            viper.GetString("schemes-path"),
			SchemesAssetsPath:      viper.GetString("schemes-assets-path"),
			SchemeUpdateInterval:   viper.GetInt("schemes-update"),
			IssuerPrivateKeysPath:  viper.GetString("privkeys"),
			URL:    viper.GetString("url"),
			Logger: logger,
		},
		Permissions: requestorserver.Permissions{
			Disclosing: handlePermission("disclose-perms"),
			Signing:    handlePermission("sign-perms"),
			Issuing:    handlePermission("issue-perms"),
		},
		ListenAddress:                  viper.GetString("listen-addr"),
		Port:                           viper.GetInt("port"),
		ClientListenAddress:            viper.GetString("client-listen-addr"),
		ClientPort:                     viper.GetInt("client-port"),
		DisableRequestorAuthentication: viper.GetBool("no-auth"),
		Requestors:                     make(map[string]requestorserver.Requestor),
		JwtIssuer:                      viper.GetString("jwt-issuer"),
		JwtPrivateKey:                  viper.GetString("jwt-privkey"),
		JwtPrivateKeyFile:              viper.GetString("jwt-privkey-file"),
		MaxRequestAge:                  viper.GetInt("max-request-age"),
		Verbose:                        viper.GetInt("verbose"),
		Quiet:                          viper.GetBool("quiet"),
		LogJSON:                        viper.GetBool("log-json"),

		TlsCertificate:           viper.GetString("tls-cert"),
		TlsCertificateFile:       viper.GetString("tls-cert-file"),
		TlsPrivateKey:            viper.GetString("tls-privkey"),
		TlsPrivateKeyFile:        viper.GetString("tls-privkey-file"),
		ClientTlsCertificate:     viper.GetString("client-tls-cert"),
		ClientTlsCertificateFile: viper.GetString("client-tls-cert-file"),
		ClientTlsPrivateKey:      viper.GetString("client-tls-privkey"),
		ClientTlsPrivateKeyFile:  viper.GetString("client-tls-privkey-file"),

		Production: viper.GetBool("production"),
	}

	// Handle requestors
	if len(viper.GetStringMap("requestors")) > 0 { // First read config file
		if err := viper.UnmarshalKey("requestors", &conf.Requestors); err != nil {
			return errors.WrapPrefix(err, "Failed to unmarshal requestors from config file", 0)
		}
	}
	requestors := viper.GetString("requestors") // Read flag or env var
	if len(requestors) > 0 {
		if err := json.Unmarshal([]byte(requestors), &conf.Requestors); err != nil {
			return errors.WrapPrefix(err, "Failed to unmarshal requestors from json", 0)
		}
	}

	logger.Debug("Done configuring")

	return nil
}

func handlePermission(typ string) []string {
	if !viper.IsSet(typ) && (!viper.GetBool("production") || typ != "issue-perms") {
		return []string{"*"}
	}
	perms := viper.GetStringSlice(typ)
	if perms == nil {
		return []string{}
	}
	return perms
}
<|endoftext|>


<commit_before>package hot

import (
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"

	"gopkg.in/fsnotify.v1"
)

type Config struct {
	Watch          bool
	BaseName       string
	Dir            string
	Funcs          template.FuncMap
	LeftDelim      string
	RightDelim     string
	FilesExtension []string
	Log            io.Writer
}

type Template struct {
	tpl *template.Template
	cfg *Config
	Out io.Writer
}

func New(cfg *Config) (*Template, error) {
	leftDelim, rightDelim := getDelims(cfg)
	tmpl := template.New(cfg.BaseName).Delims(leftDelim, rightDelim)
	if cfg.Funcs != nil {
		tmpl = template.New(cfg.BaseName).Funcs(cfg.Funcs).Delims(leftDelim, rightDelim)
	}
	tpl := &Template{
		tpl: tmpl,
		cfg: cfg,
		Out: os.Stdout,
	}
	if cfg.Log != nil {
		tpl.Out = cfg.Log
	}
	tpl.Init()
	err := tpl.Load(cfg.Dir)
	if err != nil {
		return nil, err
	}
	return tpl, nil
}

func (t *Template) Init() {
	if t.cfg.Watch {
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
		watcher, err := fsnotify.NewWatcher()
		if err != nil {
			fmt.Fprintln(t.Out, err)
			return
		}
		err = filepath.Walk(t.cfg.Dir, func(fPath string, info os.FileInfo, ferr error) error {
			if ferr != nil {
				return ferr
			}
			if info.IsDir() {
				fmt.Fprintln(t.Out, "start watching ", fPath)
				return watcher.Add(fPath)
			}
			return nil
		})
		if err != nil {
			watcher.Close()
			fmt.Fprintln(t.Out, err)
			return
		}
		go func() {
			for {
				select {
				case <-c:
					watcher.Close()
					fmt.Fprintf(t.Out, "shutting down hot templates... done")
					os.Exit(0)
				case evt := <-watcher.Events:
					fmt.Fprintf(t.Out, "%s:  reloading... \n", evt.String())
					t.Reload()
				}
			}
		}()
	}
}

func (t *Template) Reload() {
	tpl := *t.tpl
	leftDelim, rightDelim := getDelims(t.cfg)
	t.tpl = template.New(t.cfg.BaseName).Delims(leftDelim, rightDelim)
	if t.cfg.Funcs != nil {
		t.tpl = template.New(t.cfg.BaseName).Funcs(t.cfg.Funcs).Delims(leftDelim, rightDelim)
	}
	err := t.Load(t.cfg.Dir)
	if err != nil {
		fmt.Fprintln(t.Out, err.Error())
		t.tpl = &tpl
	}
}

func (t *Template) Load(dir string) error {
	fmt.Fprintln(t.Out, "loading...", dir)
	return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}

		extension := filepath.Ext(path)
		found := false
		for _, ext := range t.cfg.FilesExtension {
			if ext == extension {
				found = true
				break
			}
		}
		if !found {
			return nil
		}

		data, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		// We remove the directory name from the path
		// this means if we have directory foo, with file bar.tpl
		// full path for bar file foo/bar.tpl
		// we trim the foo part and remain with /bar.tpl
		name := path[len(dir):]

		name = filepath.ToSlash(name)

		name = strings.TrimPrefix(name, "/") // case  we missed the opening slash

		tpl := t.tpl.New(name)
		_, err = tpl.Parse(string(data))
		if err != nil {
			return err
		}
		return nil
	})
}

func (t *Template) Execute(w io.Writer, name string, ctx interface{}) error {
	return t.tpl.ExecuteTemplate(w, name, ctx)
}

func getDelims(cfg *Config) (leftDelim string, rightDelim string) {
	if cfg.LeftDelim != "" {
		leftDelim = cfg.LeftDelim
	} else {
		leftDelim = "{{"
	}
	if cfg.RightDelim != "" {
		rightDelim = cfg.RightDelim
	} else {
		rightDelim = "}}"
	}
	return
}
<commit_msg>Removed os.Notify + added Close function<commit_after>package hot

import (
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/fsnotify.v1"
)

type Config struct {
	Watch          bool
	BaseName       string
	Dir            string
	Funcs          template.FuncMap
	LeftDelim      string
	RightDelim     string
	FilesExtension []string
	Log            io.Writer
}

type Template struct {
	tpl          *template.Template
	cfg          *Config
	watcher      *fsnotify.Watcher
	closeChannel chan bool
	Out          io.Writer
}

func New(cfg *Config) (*Template, error) {
	leftDelim, rightDelim := getDelims(cfg)
	tmpl := template.New(cfg.BaseName).Delims(leftDelim, rightDelim)
	if cfg.Funcs != nil {
		tmpl = template.New(cfg.BaseName).Funcs(cfg.Funcs).Delims(leftDelim, rightDelim)
	}
	tpl := &Template{
		tpl: tmpl,
		cfg: cfg,
		Out: os.Stdout,
	}
	if cfg.Log != nil {
		tpl.Out = cfg.Log
	}
	tpl.Init()
	err := tpl.Load(cfg.Dir)
	if err != nil {
		return nil, err
	}
	return tpl, nil
}

func (t *Template) Init() {
	if t.cfg.Watch {
		watcher, err := fsnotify.NewWatcher()
		t.watcher = watcher
		if err != nil {
			fmt.Fprintln(t.Out, err)
			return
		}
		t.closeChannel = make(chan bool, 1)

		err = filepath.Walk(t.cfg.Dir, func(fPath string, info os.FileInfo, ferr error) error {
			if ferr != nil {
				return ferr
			}
			if info.IsDir() {
				fmt.Fprintln(t.Out, "start watching ", fPath)
				return watcher.Add(fPath)
			}
			return nil
		})
		if err != nil {
			watcher.Close()
			fmt.Fprintln(t.Out, err)
			return
		}
		go func() {
			for {
				select {
				case <-t.closeChannel:
					return
				case evt := <-watcher.Events:
					fmt.Fprintf(t.Out, "%s:  reloading... \n", evt.String())
					t.Reload()
				}

			}
		}()
	}
}

func (t *Template) Close() {
	t.closeChannel <- true
	t.watcher.Close()
}

func (t *Template) Reload() {
	tpl := *t.tpl
	leftDelim, rightDelim := getDelims(t.cfg)
	t.tpl = template.New(t.cfg.BaseName).Delims(leftDelim, rightDelim)
	if t.cfg.Funcs != nil {
		t.tpl = template.New(t.cfg.BaseName).Funcs(t.cfg.Funcs).Delims(leftDelim, rightDelim)
	}
	err := t.Load(t.cfg.Dir)
	if err != nil {
		fmt.Fprintln(t.Out, err.Error())
		t.tpl = &tpl
	}
}

func (t *Template) Load(dir string) error {
	fmt.Fprintln(t.Out, "loading...", dir)
	return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}

		extension := filepath.Ext(path)
		found := false
		for _, ext := range t.cfg.FilesExtension {
			if ext == extension {
				found = true
				break
			}
		}
		if !found {
			return nil
		}

		data, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		// We remove the directory name from the path
		// this means if we have directory foo, with file bar.tpl
		// full path for bar file foo/bar.tpl
		// we trim the foo part and remain with /bar.tpl
		name := path[len(dir):]

		name = filepath.ToSlash(name)

		name = strings.TrimPrefix(name, "/") // case  we missed the opening slash

		tpl := t.tpl.New(name)
		_, err = tpl.Parse(string(data))
		if err != nil {
			return err
		}
		return nil
	})
}

func (t *Template) Execute(w io.Writer, name string, ctx interface{}) error {
	return t.tpl.ExecuteTemplate(w, name, ctx)
}

func getDelims(cfg *Config) (leftDelim string, rightDelim string) {
	if cfg.LeftDelim != "" {
		leftDelim = cfg.LeftDelim
	} else {
		leftDelim = "{{"
	}
	if cfg.RightDelim != "" {
		rightDelim = cfg.RightDelim
	} else {
		rightDelim = "}}"
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"fmt"
	"strings"
	"sync"

	"github.com/ibelie/ruid"
	"github.com/ibelie/tygo"
)

type HubImpl struct {
	mutex     sync.Mutex
	observers map[ruid.ID]map[string]map[ruid.ID]bool
}

var HubInst = HubImpl{observers: make(map[ruid.ID]map[string]map[ruid.ID]bool)}

func HubService(_ Server) (string, Service) {
	return SYMBOL_HUB, &HubInst
}

func (s *HubImpl) Procedure(i ruid.ID, m string, param []byte) (result []byte, err error) {
	switch m {
	case SYMBOL_OBSERVE:
		if session, gate, e := DeserializeSessionGate(param); e != nil {
			err = fmt.Errorf("[Hub] Observe deserialize error: %v\n>>>> %v", i, e)
		} else {
			s.mutex.Lock()
			defer s.mutex.Unlock()
			if _, ok := s.observers[i]; !ok {
				s.observers[i] = make(map[string]map[ruid.ID]bool)
			}
			if _, ok := s.observers[i][gate]; !ok {
				s.observers[i][gate] = make(map[ruid.ID]bool)
			}
			s.observers[i][gate][session] = true
		}
	case SYMBOL_IGNORE:
		if session, gate, e := DeserializeSessionGate(param); e != nil {
			err = fmt.Errorf("[Hub] Ignore deserialize error: %v\n>>>> %v", i, e)
		} else {
			s.mutex.Lock()
			defer s.mutex.Unlock()
			if gates, ok := s.observers[i]; ok {
				if observers, ok := gates[gate]; ok {
					delete(observers, session)
					if len(observers) <= 0 {
						delete(gates, gate)
					}
				}
				if len(gates) <= 0 {
					delete(s.observers, i)
				}
			}
		}
	default:
		gates, ok := s.observers[i]
		if !ok || len(gates) <= 0 {
			err = fmt.Errorf("[Hub] Dispatch no gate found %v %v", i, gates)
			return
		}
		var errors []string
		gateGhosts := make(map[string][]ruid.ID)
		for gate, observers := range gates {
			if len(observers) <= 0 {
				errors = append(errors, fmt.Sprintf("\n>>>> [Hub] Dispatch gate no observer %v %v", i, gate))
				continue
			}
			var datas [][]byte
			var ghosts []ruid.ID
			if datas, err = server.Request(gate, i, m, SerializeDispatch(observers, param), SYMBOL_GATE); err != nil || len(datas) <= 0 {
				errors = append(errors, fmt.Sprintf("\n>>>> gate request: %v\n>>>> %v", gate, err))
			} else if ghosts, err = DeserializeGhosts(datas[0]); err != nil {
				errors = append(errors, fmt.Sprintf("\n>>>> gate ghosts: %v\n>>>> %v", gate, err))
			} else if len(ghosts) > 0 {
				gateGhosts[gate] = ghosts
			}
		}
		for gate, ghosts := range gateGhosts {
			for _, ghost := range ghosts {
				delete(gates[gate], ghost)
			}
			if len(gates[gate]) <= 0 {
				delete(gates, gate)
			}
		}
		if len(errors) > 0 {
			err = fmt.Errorf("[Hub] Dispatch %q errors %v:%s", m, i, strings.Join(errors, ""))
		}
	}
	return
}

func DeserializeSessionGate(data []byte) (session ruid.ID, gate string, err error) {
	if session, err = server.DeserializeID(&tygo.ProtoBuf{Buffer: data}); err != nil {
	} else if ring, ok := server.remote[SYMBOL_GATE]; !ok {
		err = fmt.Errorf("Cannot find gate service: %v %v %v", server.remote, server.Node, server.nodes)
	} else if gate, ok = ring.Get(session); !ok {
		err = fmt.Errorf("No gate found: %v %v", server.Node, server.nodes)
	}
	return
}
<commit_msg>warn no observer instead of error<commit_after>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"fmt"
	"log"
	"strings"
	"sync"

	"github.com/ibelie/ruid"
	"github.com/ibelie/tygo"
)

type HubImpl struct {
	mutex     sync.Mutex
	observers map[ruid.ID]map[string]map[ruid.ID]bool
}

var HubInst = HubImpl{observers: make(map[ruid.ID]map[string]map[ruid.ID]bool)}

func HubService(_ Server) (string, Service) {
	return SYMBOL_HUB, &HubInst
}

func (s *HubImpl) Procedure(i ruid.ID, m string, param []byte) (result []byte, err error) {
	switch m {
	case SYMBOL_OBSERVE:
		if session, gate, e := DeserializeSessionGate(param); e != nil {
			err = fmt.Errorf("[Hub] Observe deserialize error: %v\n>>>> %v", i, e)
		} else {
			s.mutex.Lock()
			defer s.mutex.Unlock()
			if _, ok := s.observers[i]; !ok {
				s.observers[i] = make(map[string]map[ruid.ID]bool)
			}
			if _, ok := s.observers[i][gate]; !ok {
				s.observers[i][gate] = make(map[ruid.ID]bool)
			}
			s.observers[i][gate][session] = true
		}
	case SYMBOL_IGNORE:
		if session, gate, e := DeserializeSessionGate(param); e != nil {
			err = fmt.Errorf("[Hub] Ignore deserialize error: %v\n>>>> %v", i, e)
		} else {
			s.mutex.Lock()
			defer s.mutex.Unlock()
			if gates, ok := s.observers[i]; ok {
				if observers, ok := gates[gate]; ok {
					delete(observers, session)
					if len(observers) <= 0 {
						delete(gates, gate)
					}
				}
				if len(gates) <= 0 {
					delete(s.observers, i)
				}
			}
		}
	default:
		gates, ok := s.observers[i]
		if !ok || len(gates) <= 0 {
			log.Printf("[Hub@%v] Dispatch %q no observer for %v", server.Addr, m, i)
			return
		}
		var errors []string
		gateGhosts := make(map[string][]ruid.ID)
		for gate, observers := range gates {
			if len(observers) <= 0 {
				gateGhosts[gate] = nil
				continue
			}
			var datas [][]byte
			var ghosts []ruid.ID
			if datas, err = server.Request(gate, i, m, SerializeDispatch(observers, param), SYMBOL_GATE); err != nil || len(datas) <= 0 {
				errors = append(errors, fmt.Sprintf("\n>>>> gate request: %v\n>>>> %v", gate, err))
			} else if ghosts, err = DeserializeGhosts(datas[0]); err != nil {
				errors = append(errors, fmt.Sprintf("\n>>>> gate ghosts: %v\n>>>> %v", gate, err))
			} else if len(ghosts) > 0 {
				gateGhosts[gate] = ghosts
			}
		}
		for gate, ghosts := range gateGhosts {
			for _, ghost := range ghosts {
				delete(gates[gate], ghost)
			}
			if len(gates[gate]) <= 0 {
				delete(gates, gate)
			}
		}
		if len(errors) > 0 {
			err = fmt.Errorf("[Hub] Dispatch %q errors %v:%s", m, i, strings.Join(errors, ""))
		}
	}
	return
}

func DeserializeSessionGate(data []byte) (session ruid.ID, gate string, err error) {
	if session, err = server.DeserializeID(&tygo.ProtoBuf{Buffer: data}); err != nil {
	} else if ring, ok := server.remote[SYMBOL_GATE]; !ok {
		err = fmt.Errorf("Cannot find gate service: %v %v %v", server.remote, server.Node, server.nodes)
	} else if gate, ok = ring.Get(session); !ok {
		err = fmt.Errorf("No gate found: %v %v", server.Node, server.nodes)
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// some code borrowed from example chat program of
// https://github.com/gorilla/websocket
package conductor

import (
	"log"
)

// hub maintains the set of active connections and broadcasts messages to the
// connections.
type hub struct {
	// The connections based on channel
	channels map[string][]*connection

	//All registered peers.
	peers map[string]*connection

	//All registered connections that aren't peers.
	clients map[string]*connection

	// Inbound messages from the connections.
	broadcast chan broadcastWriter

	// Join a new channel
	bind chan broadcastWriter

	// leave a new channel
	unbind chan broadcastWriter

	// Register requests from the connections.
	register chan *connection

	// Unregister requests from connections.
	unregister chan *connection

	// invite to a channel.
	invite chan broadcastWriter

	// send message to just peers.
	peerbroadcast chan broadcastWriter
}

// create our hub.
func createHub() hub {
	return hub{
		broadcast:     make(chan broadcastWriter),
		register:      make(chan *connection),
		unregister:    make(chan *connection),
		bind:          make(chan broadcastWriter),
		unbind:        make(chan broadcastWriter),
		invite:        make(chan broadcastWriter),
		peerbroadcast: make(chan broadcastWriter),
		channels:      make(map[string][]*connection),
		peers:         make(map[string]*connection),
		clients:       make(map[string]*connection),
	}
}

// runloop that controls broadcasting messages to other peers.
func (h *hub) run() {
	for {
		select {
		case c := <-h.register:
			h.addConnection(c)
		case c := <-h.unregister:
			h.closeConnections(c)
		case b := <-h.broadcast:
			h.broadcastMessage(b)
		case b := <-h.bind:
			h.bindChannel(b)
		case b := <-h.unbind:
			h.unbindChannel(b)
		case b := <-h.invite:
			h.inviteUser(b)
		case b := <-h.peerbroadcast:
			h.broadcastPeerMessage(b)
		}
	}
}

// closes all the channels in the connection.
func (h *hub) closeConnections(c *connection) {
	delete(h.peers, c.name)
	delete(h.clients, c.name)
	for _, name := range c.channels {
		conns := h.channels[name]
		for i, conn := range conns {
			if c == conn {
				h.channels[name] = append(conns[:i], conns[i+1:]...)
				h.processUnbindChannel(name)
			} else {
				conn.send <- Message{Name: c.name, Body: "", ChannelName: name, OpCode: UnBindOpCode}
			}
		}
	}
}

// add a connection a channel.
func (h *hub) addConnection(c *connection) {
	if c.peer {
		if h.peers[c.name] == nil {
			log.Println("adding a new peer", c.name)
			h.peers[c.name] = c
			for name, _ := range h.channels {
				h.channels[name] = append(h.channels[name], c)
			}
		} else {
			log.Println("peer already bound", c.name)
		}
	} else if h.clients[c.name] == nil {
		h.clients[c.name] = c
	}
}

// Checks if the peer has already been added
func (h *hub) ifConnectionExist(name string) bool {
	if h.peers[name] != nil {
		return true
	}
	return false
}

//bind to a connection to a channel
func (h *hub) bindChannel(b broadcastWriter) {
	//we haven't seen this channel before and need to let the peers know
	if !b.conn.peer {
		if h.channels[b.message.ChannelName] == nil {
			for _, c := range h.peers {
				//log.Printf("Binding peer: %s to channel: %s", c.name, b.message.ChannelName)
				h.channels[b.message.ChannelName] = append(h.channels[b.message.ChannelName], c)
				c.send <- Message{ChannelName: b.message.ChannelName, OpCode: BindOpCode}
			}
		}
	}
	h.channels[b.message.ChannelName] = append(h.channels[b.message.ChannelName], b.conn)
}

//unbind a connection from a channel
func (h *hub) unbindChannel(b broadcastWriter) {
	conns := h.channels[b.message.ChannelName]
	for i, conn := range conns {
		if b.conn == conn {
			h.channels[b.message.ChannelName] = append(conns[:i], conns[i+1:]...)
			h.processUnbindChannel(b.message.ChannelName)
			break
		}
	}
}

//process an unbind on a channel
func (h *hub) processUnbindChannel(channelName string) {
	//delete channel if no longer in use
	if len(h.channels[channelName]) <= len(h.peers) {
		delete(h.channels, channelName)
	}
}

//invite a user to a channel
func (h *hub) inviteUser(b broadcastWriter) {
	conn := h.clients[b.message.Body]
	if conn != nil {
		conn.send <- *b.message
	}
	//send the invite to the peers in case the user is on another server
	for _, c := range h.peers {
		c.send <- *b.message
	}
}

//broadcast a message to the peers
func (h *hub) broadcastPeerMessage(b broadcastWriter) {
	for _, c := range h.peers {
		if c != b.conn {
			select {
			case c.send <- *b.message:
			}
		}
	}
}

// broadcast message out on channel.
func (h *hub) broadcastMessage(b broadcastWriter) {
	for _, c := range h.channels[b.message.ChannelName] {
		if b.peer && c.peer {
			continue
		}
		if c != b.conn {
			log.Println("user: ", c.name)
			log.Println("message: ", *b.message)
			select {
			case c.send <- *b.message:
			default:
				h.closeConnections(c)
			}
		}
	}
}
<commit_msg>removed debug logging<commit_after>// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// some code borrowed from example chat program of
// https://github.com/gorilla/websocket
package conductor

import (
	"log"
)

// hub maintains the set of active connections and broadcasts messages to the
// connections.
type hub struct {
	// The connections based on channel
	channels map[string][]*connection

	//All registered peers.
	peers map[string]*connection

	//All registered connections that aren't peers.
	clients map[string]*connection

	// Inbound messages from the connections.
	broadcast chan broadcastWriter

	// Join a new channel
	bind chan broadcastWriter

	// leave a new channel
	unbind chan broadcastWriter

	// Register requests from the connections.
	register chan *connection

	// Unregister requests from connections.
	unregister chan *connection

	// invite to a channel.
	invite chan broadcastWriter

	// send message to just peers.
	peerbroadcast chan broadcastWriter
}

// create our hub.
func createHub() hub {
	return hub{
		broadcast:     make(chan broadcastWriter),
		register:      make(chan *connection),
		unregister:    make(chan *connection),
		bind:          make(chan broadcastWriter),
		unbind:        make(chan broadcastWriter),
		invite:        make(chan broadcastWriter),
		peerbroadcast: make(chan broadcastWriter),
		channels:      make(map[string][]*connection),
		peers:         make(map[string]*connection),
		clients:       make(map[string]*connection),
	}
}

// runloop that controls broadcasting messages to other peers.
func (h *hub) run() {
	for {
		select {
		case c := <-h.register:
			h.addConnection(c)
		case c := <-h.unregister:
			h.closeConnections(c)
		case b := <-h.broadcast:
			h.broadcastMessage(b)
		case b := <-h.bind:
			h.bindChannel(b)
		case b := <-h.unbind:
			h.unbindChannel(b)
		case b := <-h.invite:
			h.inviteUser(b)
		case b := <-h.peerbroadcast:
			h.broadcastPeerMessage(b)
		}
	}
}

// closes all the channels in the connection.
func (h *hub) closeConnections(c *connection) {
	delete(h.peers, c.name)
	delete(h.clients, c.name)
	for _, name := range c.channels {
		conns := h.channels[name]
		for i, conn := range conns {
			if c == conn {
				h.channels[name] = append(conns[:i], conns[i+1:]...)
				h.processUnbindChannel(name)
			} else {
				conn.send <- Message{Name: c.name, Body: "", ChannelName: name, OpCode: UnBindOpCode}
			}
		}
	}
}

// add a connection a channel.
func (h *hub) addConnection(c *connection) {
	if c.peer {
		if h.peers[c.name] == nil {
			log.Println("adding a new peer", c.name)
			h.peers[c.name] = c
			for name, _ := range h.channels {
				h.channels[name] = append(h.channels[name], c)
			}
		} else {
			log.Println("peer already bound", c.name)
		}
	} else if h.clients[c.name] == nil {
		h.clients[c.name] = c
	}
}

// Checks if the peer has already been added
func (h *hub) ifConnectionExist(name string) bool {
	if h.peers[name] != nil {
		return true
	}
	return false
}

//bind to a connection to a channel
func (h *hub) bindChannel(b broadcastWriter) {
	//we haven't seen this channel before and need to let the peers know
	if !b.conn.peer {
		if h.channels[b.message.ChannelName] == nil {
			for _, c := range h.peers {
				//log.Printf("Binding peer: %s to channel: %s", c.name, b.message.ChannelName)
				h.channels[b.message.ChannelName] = append(h.channels[b.message.ChannelName], c)
				c.send <- Message{ChannelName: b.message.ChannelName, OpCode: BindOpCode}
			}
		}
	}
	h.channels[b.message.ChannelName] = append(h.channels[b.message.ChannelName], b.conn)
}

//unbind a connection from a channel
func (h *hub) unbindChannel(b broadcastWriter) {
	conns := h.channels[b.message.ChannelName]
	for i, conn := range conns {
		if b.conn == conn {
			h.channels[b.message.ChannelName] = append(conns[:i], conns[i+1:]...)
			h.processUnbindChannel(b.message.ChannelName)
			break
		}
	}
}

//process an unbind on a channel
func (h *hub) processUnbindChannel(channelName string) {
	//delete channel if no longer in use
	if len(h.channels[channelName]) <= len(h.peers) {
		delete(h.channels, channelName)
	}
}

//invite a user to a channel
func (h *hub) inviteUser(b broadcastWriter) {
	conn := h.clients[b.message.Body]
	if conn != nil {
		conn.send <- *b.message
	}
	//send the invite to the peers in case the user is on another server
	for _, c := range h.peers {
		c.send <- *b.message
	}
}

//broadcast a message to the peers
func (h *hub) broadcastPeerMessage(b broadcastWriter) {
	for _, c := range h.peers {
		if c != b.conn {
			select {
			case c.send <- *b.message:
			}
		}
	}
}

// broadcast message out on channel.
func (h *hub) broadcastMessage(b broadcastWriter) {
	for _, c := range h.channels[b.message.ChannelName] {
		if b.peer && c.peer {
			continue
		}
		if c != b.conn {
			select {
			case c.send <- *b.message:
			default:
				h.closeConnections(c)
			}
		}
	}
}
<|endoftext|>


<commit_before>package eventsocket

import (
	"fmt"
	"os"
)

// hub maintains the set of active connections, and broadcasts messages
// for given events to where they need to go
type hub struct {
	// Inbound messages from the connections.
	recvClientMessage chan *ClientMessage

	// Register a client
	register chan *Client

	// Unregister requests from connections.
	unregister chan *wsConnection

	// Registered connection map from connection to clientId
	connections map[*wsConnection]string

	// subscriptions is a list of the events that have atleast one subscriber
	subscriptions hubSubscriptions

	// clientSubscriptions is a map of clientId to a slice of events
	// that the client has subscriped to
	clientSubscriptions map[string]map[string]bool
}

// hubSubscription is a list of the clients who have subscribed to a given event
type hubSubscription map[string]bool

// hubSubscriptions is a list of the events that have atleast one subscriber
type hubSubscriptions map[string]hubSubscription

var h = hub{
	recvClientMessage:   make(chan *ClientMessage),
	register:            make(chan *Client),
	unregister:          make(chan *wsConnection),
	connections:         make(map[*wsConnection]string),
	subscriptions:       make(map[string]hubSubscription),
	clientSubscriptions: make(map[string]map[string]bool),
}

func (h *hub) run() {
	for {
		select {
		case cl := <-h.register:
			h.registerClient(cl)
		case c := <-h.unregister:
			h.unregisterConnection(c)
		case m := <-h.recvClientMessage:
			h.ingest(m)
		}
	}
}

func (h *hub) registerClient(client *Client) {
	h.connections[client.ws] = client.Id
	h.clientSubscriptions[client.Id] = make(map[string]bool)

	for s := range client.subscriptions {
		h.storeSubscription(client.Id, s)
	}
}

// unsuscribe from all events for this connection..? or set to false
// also remove the connection->client reference, and close the send chan
func (h *hub) unregisterConnection(ws *wsConnection) {
	defer func() {
		ws.Close()
	}()

	// if we dont know about htis conneciton, then get out
	if _, ok := h.connections[ws]; !ok {
		return
	}

	id := h.connections[ws]
	delete(h.connections, ws)

	// if we dont have any clientSubscriptions, then leave
	if _, ok := h.clientSubscriptions[id]; !ok {
		return
	}

	// remove all client subscriptions
	for s := range h.clientSubscriptions[id] {
		h.purgeSubscription(id, s)
	}

	delete(h.clientSubscriptions, id)
}

// ingest a message and route it to the propper destination
func (h *hub) ingest(cm *ClientMessage) {
	switch cm.Message.MessageType {
	case MESSAGE_TYPE_BROADCAST:
		h.handleBroadcast(cm)
	case MESSAGE_TYPE_STANDARD:
		h.handleStandard(cm)
	case MESSAGE_TYPE_SUSCRIBE:
		h.handleSuscribe(cm)
	case MESSAGE_TYPE_UNSUSCRIBE:
		h.handleUnsuscribe(cm)
	case MESSAGE_TYPE_REQUEST:
		h.handleRequest(cm)
	case MESSAGE_TYPE_REPLY:
		h.handleReply(cm)
	default:
		fmt.Printf("ERROR: unhandled MessageType:%v [ClientId:%s]\n", cm.Message.MessageType, cm.ClientId)
		os.Exit(1)
	}
}

// broadcast a message to all active clients
func (h *hub) handleBroadcast(cm *ClientMessage) {
	for ws, _ := range h.connections {
		select {
		case ws.send <- cm.Message:
		default:
			h.unregister <- ws
		}

	}

	return
}

// handle a standard message
func (h *hub) handleStandard(cm *ClientMessage) {
	// ensure there is atleast one suscriber of this event
	subscribers, ok := h.subscriptions[cm.Message.Event]
	if !ok {
		return
	}

	fmt.Printf("%+v\n", subscribers)

	for s := range subscribers {
		clients[s].ws.send <- cm.Message
	}

	return
}

// suscribe a client to events
func (h *hub) handleSuscribe(cm *ClientMessage) {
	// sanity check
	events, ok := cm.Message.Payload["Events"]
	if !ok {
		return
	}

	for _, event := range events.([]interface{}) {
		e := event.(string)
		h.storeSubscription(cm.ClientId, e)
	}

	return
}

// unsuscribe a client from events
func (h *hub) handleUnsuscribe(cm *ClientMessage) {
	// sanity check
	events, ok := cm.Message.Payload["Events"]
	if !ok {
		return
	}

	for _, event := range events.([]interface{}) {
		e := event.(string)
		h.purgeSubscription(cm.ClientId, e)

		// remove the event from the client id subscriptions map
		if _, ok := clients[cm.ClientId].subscriptions[e]; ok {
			delete(clients[cm.ClientId].subscriptions, e)
		}
	}

	return
}

// handle a request message. this is done by storing the requestee's
// ClientId as the ReplyTo, and then forwarding the message along
// to the receiving client
func (h *hub) handleRequest(cm *ClientMessage) {
	fmt.Println("HANDLING REQUEST")

	cm.Message.ReplyClientId = cm.ClientId
	clients[cm.Message.RequestClientId].ws.send <- cm.Message
}

// handle a reply. forward the message along to the requestee
func (h *hub) handleReply(cm *ClientMessage) {
	fmt.Println("HANDLING REPLY")

	clients[cm.Message.ReplyClientId].ws.send <- cm.Message
}

// store the subscription for the given clientId and event
func (h *hub) storeSubscription(id, e string) {
	// make sure we have a subscription key
	if _, ok := h.subscriptions[e]; !ok {
		h.subscriptions[e] = make(hubSubscription)
	}

	// store the clientid in the subscription map
	h.subscriptions[e][id] = true

	h.clientSubscriptions[id][e] = true

	// store the event in the client id subscriptions map
	clients[id].subscriptions[e] = true
}

// purge the subscription
func (h *hub) purgeSubscription(id, e string) {
	// remove the clientSubscriptions map, if it exists
	if _, ok := h.clientSubscriptions[id][e]; ok {
		delete(h.clientSubscriptions[id], e)
	}

	// make sure we are actually suscribing to this event
	if _, ok := h.subscriptions[e][id]; !ok {
		return
	}

	// remove the suscription
	delete(h.subscriptions[e], id)

	// if no one else if suscribing, then remove the suscription key
	if len(h.subscriptions[e]) == 0 {
		delete(h.subscriptions, e)
	}

}
<commit_msg>hub: Remove misc debug output<commit_after>package eventsocket

import (
	"fmt"
	"os"
)

// hub maintains the set of active connections, and broadcasts messages
// for given events to where they need to go
type hub struct {
	// Inbound messages from the connections.
	recvClientMessage chan *ClientMessage

	// Register a client
	register chan *Client

	// Unregister requests from connections.
	unregister chan *wsConnection

	// Registered connection map from connection to clientId
	connections map[*wsConnection]string

	// subscriptions is a list of the events that have atleast one subscriber
	subscriptions hubSubscriptions

	// clientSubscriptions is a map of clientId to a slice of events
	// that the client has subscriped to
	clientSubscriptions map[string]map[string]bool
}

// hubSubscription is a list of the clients who have subscribed to a given event
type hubSubscription map[string]bool

// hubSubscriptions is a list of the events that have atleast one subscriber
type hubSubscriptions map[string]hubSubscription

var h = hub{
	recvClientMessage:   make(chan *ClientMessage),
	register:            make(chan *Client),
	unregister:          make(chan *wsConnection),
	connections:         make(map[*wsConnection]string),
	subscriptions:       make(map[string]hubSubscription),
	clientSubscriptions: make(map[string]map[string]bool),
}

func (h *hub) run() {
	for {
		select {
		case cl := <-h.register:
			h.registerClient(cl)
		case c := <-h.unregister:
			h.unregisterConnection(c)
		case m := <-h.recvClientMessage:
			h.ingest(m)
		}
	}
}

func (h *hub) registerClient(client *Client) {
	h.connections[client.ws] = client.Id
	h.clientSubscriptions[client.Id] = make(map[string]bool)

	for s := range client.subscriptions {
		h.storeSubscription(client.Id, s)
	}
}

// unsuscribe from all events for this connection..? or set to false
// also remove the connection->client reference, and close the send chan
func (h *hub) unregisterConnection(ws *wsConnection) {
	defer func() {
		ws.Close()
	}()

	// if we dont know about htis conneciton, then get out
	if _, ok := h.connections[ws]; !ok {
		return
	}

	id := h.connections[ws]
	delete(h.connections, ws)

	// if we dont have any clientSubscriptions, then leave
	if _, ok := h.clientSubscriptions[id]; !ok {
		return
	}

	// remove all client subscriptions
	for s := range h.clientSubscriptions[id] {
		h.purgeSubscription(id, s)
	}

	delete(h.clientSubscriptions, id)
}

// ingest a message and route it to the propper destination
func (h *hub) ingest(cm *ClientMessage) {
	switch cm.Message.MessageType {
	case MESSAGE_TYPE_BROADCAST:
		h.handleBroadcast(cm)
	case MESSAGE_TYPE_STANDARD:
		h.handleStandard(cm)
	case MESSAGE_TYPE_SUSCRIBE:
		h.handleSuscribe(cm)
	case MESSAGE_TYPE_UNSUSCRIBE:
		h.handleUnsuscribe(cm)
	case MESSAGE_TYPE_REQUEST:
		h.handleRequest(cm)
	case MESSAGE_TYPE_REPLY:
		h.handleReply(cm)
	default:
		fmt.Printf("ERROR: unhandled MessageType:%v [ClientId:%s]\n", cm.Message.MessageType, cm.ClientId)
		os.Exit(1)
	}
}

// broadcast a message to all active clients
func (h *hub) handleBroadcast(cm *ClientMessage) {
	for ws, _ := range h.connections {
		select {
		case ws.send <- cm.Message:
		default:
			h.unregister <- ws
		}

	}

	return
}

// handle a standard message
func (h *hub) handleStandard(cm *ClientMessage) {
	// ensure there is atleast one suscriber of this event
	subscribers, ok := h.subscriptions[cm.Message.Event]
	if !ok {
		return
	}

	fmt.Printf("%+v\n", subscribers)

	for s := range subscribers {
		clients[s].ws.send <- cm.Message
	}

	return
}

// suscribe a client to events
func (h *hub) handleSuscribe(cm *ClientMessage) {
	// sanity check
	events, ok := cm.Message.Payload["Events"]
	if !ok {
		return
	}

	for _, event := range events.([]interface{}) {
		e := event.(string)
		h.storeSubscription(cm.ClientId, e)
	}

	return
}

// unsuscribe a client from events
func (h *hub) handleUnsuscribe(cm *ClientMessage) {
	// sanity check
	events, ok := cm.Message.Payload["Events"]
	if !ok {
		return
	}

	for _, event := range events.([]interface{}) {
		e := event.(string)
		h.purgeSubscription(cm.ClientId, e)

		// remove the event from the client id subscriptions map
		if _, ok := clients[cm.ClientId].subscriptions[e]; ok {
			delete(clients[cm.ClientId].subscriptions, e)
		}
	}

	return
}

// handle a request message. this is done by storing the requestee's
// ClientId as the ReplyTo, and then forwarding the message along
// to the receiving client
func (h *hub) handleRequest(cm *ClientMessage) {
	cm.Message.ReplyClientId = cm.ClientId
	clients[cm.Message.RequestClientId].ws.send <- cm.Message
}

// handle a reply. forward the message along to the requestee
func (h *hub) handleReply(cm *ClientMessage) {
	clients[cm.Message.ReplyClientId].ws.send <- cm.Message
}

// store the subscription for the given clientId and event
func (h *hub) storeSubscription(id, e string) {
	// make sure we have a subscription key
	if _, ok := h.subscriptions[e]; !ok {
		h.subscriptions[e] = make(hubSubscription)
	}

	// store the clientid in the subscription map
	h.subscriptions[e][id] = true

	h.clientSubscriptions[id][e] = true

	// store the event in the client id subscriptions map
	clients[id].subscriptions[e] = true
}

// purge the subscription
func (h *hub) purgeSubscription(id, e string) {
	// remove the clientSubscriptions map, if it exists
	if _, ok := h.clientSubscriptions[id][e]; ok {
		delete(h.clientSubscriptions[id], e)
	}

	// make sure we are actually suscribing to this event
	if _, ok := h.subscriptions[e][id]; !ok {
		return
	}

	// remove the suscription
	delete(h.subscriptions[e], id)

	// if no one else if suscribing, then remove the suscription key
	if len(h.subscriptions[e]) == 0 {
		delete(h.subscriptions, e)
	}

}
<|endoftext|>


<commit_before>package forward

import (
	"net"
	"net/http"
	"strings"

	"github.com/vulcand/oxy/utils"
)

// Rewriter is responsible for removing hop-by-hop headers and setting forwarding headers
type HeaderRewriter struct {
	TrustForwardHeader bool
	Hostname           string
}

func (rw *HeaderRewriter) Rewrite(req *http.Request) {
	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		if rw.TrustForwardHeader {
			if prior, ok := req.Header[XForwardedFor]; ok {
				clientIP = strings.Join(prior, ", ") + ", " + clientIP
			}
		}
		req.Header.Set(XForwardedFor, clientIP)
	}

	if xfp := req.Header.Get(XForwardedProto); xfp != "" && rw.TrustForwardHeader {
		req.Header.Set(XForwardedProto, xfp)
	} else if req.TLS != nil {
		req.Header.Set(XForwardedProto, "https")
	} else {
		req.Header.Set(XForwardedProto, "http")
	}

	if xfp := req.Header.Get(XForwardedPort); xfp != "" && rw.TrustForwardHeader {
		req.Header.Set(XForwardedPort, xfp)
	} else if req.URL.Port() != "" {
		req.Header.Set(XForwardedPort, req.URL.Port())
	}

	if xfh := req.Header.Get(XForwardedHost); xfh != "" && rw.TrustForwardHeader {
		req.Header.Set(XForwardedHost, xfh)
	} else if req.Host != "" {
		req.Header.Set(XForwardedHost, req.Host)
	}

	if rw.Hostname != "" {
		req.Header.Set(XForwardedServer, rw.Hostname)
	}

	// Remove hop-by-hop headers to the backend.  Especially important is "Connection" because we want a persistent
	// connection, regardless of what the client sent to us.
	utils.RemoveHeaders(req.Header, HopHeaders...)
}
<commit_msg>Just forward X-Forwarded-Port.<commit_after>package forward

import (
	"net"
	"net/http"
	"strings"

	"github.com/vulcand/oxy/utils"
)

// Rewriter is responsible for removing hop-by-hop headers and setting forwarding headers
type HeaderRewriter struct {
	TrustForwardHeader bool
	Hostname           string
}

func (rw *HeaderRewriter) Rewrite(req *http.Request) {
	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		if rw.TrustForwardHeader {
			if prior, ok := req.Header[XForwardedFor]; ok {
				clientIP = strings.Join(prior, ", ") + ", " + clientIP
			}
		}
		req.Header.Set(XForwardedFor, clientIP)
	}

	if xfp := req.Header.Get(XForwardedProto); xfp != "" && rw.TrustForwardHeader {
		req.Header.Set(XForwardedProto, xfp)
	} else if req.TLS != nil {
		req.Header.Set(XForwardedProto, "https")
	} else {
		req.Header.Set(XForwardedProto, "http")
	}

	if xfp := req.Header.Get(XForwardedPort); xfp != "" && rw.TrustForwardHeader {
		req.Header.Set(XForwardedPort, xfp)
	}

	if xfh := req.Header.Get(XForwardedHost); xfh != "" && rw.TrustForwardHeader {
		req.Header.Set(XForwardedHost, xfh)
	} else if req.Host != "" {
		req.Header.Set(XForwardedHost, req.Host)
	}

	if rw.Hostname != "" {
		req.Header.Set(XForwardedServer, rw.Hostname)
	}

	// Remove hop-by-hop headers to the backend.  Especially important is "Connection" because we want a persistent
	// connection, regardless of what the client sent to us.
	utils.RemoveHeaders(req.Header, HopHeaders...)
}
<|endoftext|>


<commit_before>package main

import (
    "html/template"
    "math/rand"
    "net/http"
)

// these are the phrases we pick from when generating lipsum
var phrases = []string{
    "Hello, IT. Have you tried turning it off and on again?",
    "Uh... okay, well, the button on the side, is it glowing?",
    "Yeah, you need to turn it on... uh, the button turns it on.",
    "Yeah, you do know how a button works don't you? No, not on clothes.",
    "Hello, IT. Have you tried forcing an unexpected reboot?",
    "No, no there you go, no there you go. I just heard it come on.",
    "No, no, that's the music you heard when it come on.",
    "No, that's the music you hear when... I'm sorry are you from the past?",
    "See the driver hooks a function by patching the system call table, so its not safe to unload it unless another thread's about to jump in there and do its stuff, and you don't want to end up in the middle of invalid memory!",
    "Oh really? Then why don't you come down and make me then.",
    "Huh, what you think I'm afraid of you? I'm not afraid of you.",
    "You can come down here any time and I'll be waiting for you! [slams down phone] That told her!",
    "I mean, they have no respect for us up there! No respect whatsoever! We're all just drudgeons to them!",
    "Yes! If there were such a thing as a drudgeon, that is what we'd be to them.",
    "It's like they're pally-wally with us when there's a problem with their printer, but once it's fixed...",
    "They just toss us away like yesterday's jam.",
}

// index is the base html string for... index
var index = `
<!DOCTYPE html>

        <meta charset="utf-8">
        <title>IT Crowd Ipsum</title>
        <link href="http://itcrowdisum.com/css/styles.css" rel="stylesheet" media="all">
        <link href="http://itcrowdisum.com/css/icons.css" rel="stylesheet" media="all">

Placeholder text taken from The IT Crowd

                {{range .Paragraphs}}
{{ . }}
{{ end }}

                <menu>
                <textarea id="text">
{{range .Paragraphs}}{{ . }}

{{ end }}
</textarea>
                    <button type="button" id="copy">Copy?</button>
                    Now press CMD + C / CTRL + C
                </menu>

                    Inspired by Bluth Ipsum. Made by Kivlor

`

// right up main street
func main() {
    http.HandleFunc("/", root)
    http.ListenAndServe(":3000", nil)
}

// root is the handler for requests to "/"
func root(w http.ResponseWriter, r *http.Request) {
    // allocate a new html template
    tmpl, err := template.New("home").Parse(index)
    if err != nil {
        panic("unable to parse index")
    }

    // build the template data
    data := struct {
        Paragraphs []string
    }{
        Paragraphs: GenerateLipsum(5),
    }

    // execute the template data
    tmpl.Execute(w, data)
}

// generateLipsum will create a number of paragraphs using randome phrases
func GenerateLipsum(count int) []string {
    var lipsum []string
    var paragraph string

    // loop the paragraph count
    for i := 0; i < count; i++ {
        paragraph = ""
        // about 6 phrases makes a goo paragrpah
        for j := 0; j < 6; j++ {
            paragraph += phrases[rand.Intn(len(phrases))]
        }

        // append our paragraph to lipsum
        lipsum = append(lipsum, paragraph)
    }

    // return lipsum
    return lipsum
}
<commit_msg>added more phrases<commit_after>package main

import (
    "html/template"
    "math/rand"
    "net/http"
)

// these are the phrases we pick from when generating lipsum
var phrases = []string{
    "Hello, IT. Have you tried turning it off and on again?",
    "Uh... okay, well, the button on the side, is it glowing?",
    "Yeah, you need to turn it on... uh, the button turns it on.",
    "Yeah, you do know how a button works don't you? No, not on clothes.",
    "Hello, IT. Have you tried forcing an unexpected reboot?",
    "No, no there you go, no there you go. I just heard it come on.",
    "No, no, that's the music you heard when it come on.",
    "No, that's the music you hear when... I'm sorry are you from the past?",
    "See the driver hooks a function by patching the system call table, so its not safe to unload it unless another thread's about to jump in there and do its stuff, and you don't want to end up in the middle of invalid memory!",
    "Oh really? Then why don't you come down and make me then.",
    "Huh, what you think I'm afraid of you? I'm not afraid of you.",
    "You can come down here any time and I'll be waiting for you! [slams down phone] That told her!",
    "I mean, they have no respect for us up there! No respect whatsoever! We're all just drudgeons to them!",
    "Yes! If there were such a thing as a drudgeon, that is what we'd be to them.",
    "It's like they're pally-wally with us when there's a problem with their printer, but once it's fixed...",
    "They just toss us away like yesterday's jam.",
    "Yes! Yesterday's jam. That is what we are to them!... Actually, that doesn't work, as a thing, because, you know, jam lasts for ages.",
    "From today, dialing 999 won't get you the Emergency Services, and that's not the only thing that's changing!",
    "Nicer ambulances, faster response times and better looking drivers mean they're not just the Emergency Services, they're your Emergency Services.",
    "So, remember the new number! 0118 999! 88199, 9119 725! ... 3!",
    "Hello? I've had a bit of a tumble.",
    "Well that's easy to remember. 0118 999 88199 9119 725! ... 3!",
    "I don't see how they couldn't just keep it as it was. How hard is it to remember 911?",
    "You mean 999. Yes, yes, I mean 999! Yeah, I know. That's the American one, you berk!",
    "I'll put this over here, with the rest of the fire.",
    "0115... no... 0118... no... 0118 999 ... 3. Hello? Is this the emergency services? Then which country am I speaking to? Hello? Hello?",
    "Dear Sir stroke Madam, I am writing to inform you of a fire which has broken out at the premises of...",
    "Dear Sir stroke Madam. Fire, exclamation mark. Fire, exclamation mark. Help me, exclamation mark. 123 Carrendon Road. Looking forward to hearing from you. All the best, Maurice Moss.",
    "I'm a 32 year old IT-man who works in a basement. Yes, I do the whole Lonely Hearts thing!",
    "Shut up, do what I tell you, I'm not interested; these are just some of the things you'll be hearing if you answer this ad. I'm an idiot and I dont care about anyone but myself. P.S. No dogs!",
    "I'm going to murder you... You bloody woman!",
    "Might want to play a bit hard to get.",
    "We don't need no education. Yes you do. You've just used a double negative.",
    "How can you two... Don't Google the question, Moss!",
    "If anyone was ever rude to me, I used to carry their food around in my trousers. Oh my God! Before you brought it to their table? No, after! Of course, before! Why would I do it after?",
    "While he was eating, did you hear anyone laughing? Like... in the kitchen area? Yes! Yes I did, actually, yes I did. That'd be trouser food!",
    "OK. Moss, what did you have for breakfast this morning? Smarties cereal.",
    "Oh my God. I didn't even know Smarties made a cereal. They don't. It's just Smarties in a bowl with milk.",
    "I am a man, he's a man, we're men! Ok, tell me how your feeling. I feel delicate... and annoyed, and... I think I'm ugly.",
    "I've got Aunt Irma visiting. Oh, do you not like Aunt Irma? I've got an aunt like that.",
    "It's my term for my time of the month. Oh. What time of the month? The weekend?",
    "You know, it's high tide. But we're not on the coast. I'm closed for maintenance! Closed for maintenance? I've fallen to the communists! Well, they do have some strong arguments.",
    "Carrie, Moss! First scene in Carrie! Oh. Okay",
    "A gay musical, called Gay. That's quite gay. Gay musical? Aren't all musicals gay? This must be, like, the gayest musical ever.",
    "A story of a young man trying to find his sexuality in the uncaring Thatcher years. Warning: Contains scenes of graphic homoeroticism.",
    "Graphic homoeroticism? Does that mean they're going to get them out?",
    "You're not comfortable with your sexuality? Oh, I'm very comfortable with my sexuality, I just don't want to be slapped in the face with their sexuality.",
    "He's had quite an evening. Someone stole his wheelchair. Did you see who it was? Red bearded man.",
    "How long have you been disabled? Ten years? Ten years, and how did it happen? If that's not a rude question. Acid?",
    "When I started Reynholm Industries, I had just two things in my possession: a dream and 6 million pounds.",
    "Today I have a business empire the like of which the world has never seen the like of which. I hope it doesn't sound arrogant when I say that I am the greatest man in the world!",
    "Unbelievable! Some idiot disabled his firewall, meaning all the computers on Seven are teeming with viruses, plus I've just had to walk all the way down the motherfudging stairs, because the lifts are broken AGAIN!",
}

// index is the base html string for... index
var index = `
<!DOCTYPE html>

        <meta charset="utf-8">
        <title>IT Crowd Ipsum</title>
        <link href="http://itcrowdisum.com/css/styles.css" rel="stylesheet" media="all">
        <link href="http://itcrowdisum.com/css/icons.css" rel="stylesheet" media="all">

Placeholder text taken from The IT Crowd

                {{range .Paragraphs}}
{{ . }}
{{ end }}

                <menu>
                <textarea id="text">
{{range .Paragraphs}}{{ . }}

{{ end }}
</textarea>
                    <button type="button" id="copy">Copy?</button>
                    Now press CMD + C / CTRL + C
                </menu>

                    Inspired by Bluth Ipsum. Made by Kivlor

`

// right up main street
func main() {
    http.HandleFunc("/", root)
    http.ListenAndServe(":3000", nil)
}

// root is the handler for requests to "/"
func root(w http.ResponseWriter, r *http.Request) {
    // allocate a new html template
    tmpl, err := template.New("home").Parse(index)
    if err != nil {
        panic("unable to parse index")
    }

    // build the template data
    data := struct {
        Paragraphs []string
    }{
        Paragraphs: GenerateLipsum(5),
    }

    // execute the template data
    tmpl.Execute(w, data)
}

// generateLipsum will create a number of paragraphs using randome phrases
func GenerateLipsum(count int) []string {
    var lipsum []string
    var paragraph string

    // loop the paragraph count
    for i := 0; i < count; i++ {
        paragraph = ""
        // about 6 phrases makes a goo paragrpah
        for j := 0; j < 6; j++ {
            paragraph += phrases[rand.Intn(len(phrases))]
        }

        // append our paragraph to lipsum
        lipsum = append(lipsum, paragraph)
    }

    // return lipsum
    return lipsum
}
<|endoftext|>


<commit_before>package guage

import (
	"fmt"
	ui "github.com/gizak/termui"
	DB "github.com/vrecan/FluxDash/influx"
	"github.com/vrecan/FluxDash/merge"
	"github.com/vrecan/FluxDash/query"
	TS "github.com/vrecan/FluxDash/timeselect"
)

type GaugeInfo struct {
}

type Gauge struct {
	From                    string       `json:"from"`
	Where                   string       `json:"where"`
	BorderLabel             string       `json:"borderlabel"`
	Border                  bool         `json:"border"`
	BorderFg                ui.Attribute `json:"borderfg"`
	BorderBg                ui.Attribute `json:"borderbg"`
	BorderLeft              bool         `json:borderleft"`
	BorderRight             bool         `json:"borderright"`
	BorderTop               bool         `json:"bordertop"`
	BorderBottom            bool         `json:"borderbottom"`
	BorderLabelFg           ui.Attribute `json:"borderlabelfg"`
	BorderLabelBg           ui.Attribute `json:"borderlabelbg"`
	Display                 bool         `json:"display"`
	Bg                      ui.Attribute `json:"bg"`
	Width                   int          `json:"width"`
	Height                  int          `json:"height"`
	PaddingTop              int          `json:"paddingtop"`
	PaddingBottom           int          `json:"paddingbottom"`
	PaddingLeft             int          `json:"paddingleft"`
	PaddingRight            int          `json:"paddingright"`
	BarColor                ui.Attribute `json:"barcolor"`
	PercentColor            ui.Attribute `json:"percentcolor"`
	PercentColorHighlighted ui.Attribute `json:"percentcolorhighlighted"`
	Label                   string       `json:"label"`
	LabelAlign              ui.Align     `json:"labelalign"`
	G                       *ui.Gauge
	db                      *DB.Influx
}

func NewGauge(db *DB.Influx, g *Gauge) *Gauge {
	g.db = db
	g.G = ui.NewGauge()
	merge.Merge(g, g.G, "G", "db")
	return g
}

func (s *Gauge) Gauges() *ui.Gauge {
	return s.G
}

func (s *Gauge) Update(time TS.TimeSelect) {
	t, _, _ := time.CurTime()
	s.SetData(t)
	s.SetTitle(t)
}

func (s *Gauge) SetData(time string) {
	meanTotal := query.GetIntData(s.db, query.Build("mean(value)", s.From, s.Where, time, ""))
	s.G.Percent = meanTotal[0]
}

func (s *Gauge) SetTitle(time string) {
	meanTotal := query.GetIntData(s.db, query.Build("mean(value)", s.From, s.Where, time, ""))
	if len(meanTotal) > 0 {
		s.G.Percent = meanTotal[0]
	} else {
		s.G.Percent = 0
	}
	maxTotal := query.GetIntData(s.db, query.Build("max(value)", s.From, s.Where, time, ""))
	s.G.Label = fmt.Sprintf("%s mean:%v%% max:%v%%", s.Label, s.G.Percent, maxTotal[0])
}
<commit_msg>ignore internal structs<commit_after>package guage

import (
	"fmt"
	ui "github.com/gizak/termui"
	DB "github.com/vrecan/FluxDash/influx"
	"github.com/vrecan/FluxDash/merge"
	"github.com/vrecan/FluxDash/query"
	TS "github.com/vrecan/FluxDash/timeselect"
)

type GaugeInfo struct {
}

type Gauge struct {
	From                    string       `json:"from"`
	Where                   string       `json:"where"`
	BorderLabel             string       `json:"borderlabel"`
	Border                  bool         `json:"border"`
	BorderFg                ui.Attribute `json:"borderfg"`
	BorderBg                ui.Attribute `json:"borderbg"`
	BorderLeft              bool         `json:borderleft"`
	BorderRight             bool         `json:"borderright"`
	BorderTop               bool         `json:"bordertop"`
	BorderBottom            bool         `json:"borderbottom"`
	BorderLabelFg           ui.Attribute `json:"borderlabelfg"`
	BorderLabelBg           ui.Attribute `json:"borderlabelbg"`
	Display                 bool         `json:"display"`
	Bg                      ui.Attribute `json:"bg"`
	Width                   int          `json:"width"`
	Height                  int          `json:"height"`
	PaddingTop              int          `json:"paddingtop"`
	PaddingBottom           int          `json:"paddingbottom"`
	PaddingLeft             int          `json:"paddingleft"`
	PaddingRight            int          `json:"paddingright"`
	BarColor                ui.Attribute `json:"barcolor"`
	PercentColor            ui.Attribute `json:"percentcolor"`
	PercentColorHighlighted ui.Attribute `json:"percentcolorhighlighted"`
	Label                   string       `json:"label"`
	LabelAlign              ui.Align     `json:"labelalign"`
	G                       *ui.Gauge    `json:"-"`
	db                      *DB.Influx   `json:"-"`
}

func NewGauge(db *DB.Influx, g *Gauge) *Gauge {
	g.db = db
	g.G = ui.NewGauge()
	merge.Merge(g, g.G, "G", "db")
	return g
}

func (s *Gauge) Gauges() *ui.Gauge {
	return s.G
}

func (s *Gauge) Update(time TS.TimeSelect) {
	t, _, _ := time.CurTime()
	s.SetData(t)
	s.SetTitle(t)
}

func (s *Gauge) SetData(time string) {
	meanTotal := query.GetIntData(s.db, query.Build("mean(value)", s.From, s.Where, time, ""))
	s.G.Percent = meanTotal[0]
}

func (s *Gauge) SetTitle(time string) {
	meanTotal := query.GetIntData(s.db, query.Build("mean(value)", s.From, s.Where, time, ""))
	if len(meanTotal) > 0 {
		s.G.Percent = meanTotal[0]
	} else {
		s.G.Percent = 0
	}
	maxTotal := query.GetIntData(s.db, query.Build("max(value)", s.From, s.Where, time, ""))
	s.G.Label = fmt.Sprintf("%s mean:%v%% max:%v%%", s.Label, s.G.Percent, maxTotal[0])
}
<|endoftext|>


<commit_before>package webservice

import (
	ae "appengine"
	"data"
	"encoding/json"
	"errors"
	"googlebooks"
	"isbn13"
	"net/http"
	"persistence"
)

type Call struct {
	Context  ae.Context
	Request  *http.Request
	Response http.ResponseWriter
}

func (call *Call) writeError(err error, strings []string) []string {
	if me, ok := err.(ae.MultiError); ok {
		for _, next := range me {
			strings = call.writeError(next, strings)
		}
	} else {
		str := err.Error()
		call.Context.Errorf("Error reported: %s", str)
		strings = append(strings, str)
	}

	return strings
}

func (call *Call) DetermineCountry() string {
	header := call.Request.Header["X-Appengine-Country"]
	if len(header) > 0 {
		country := header[0]

		if country != "ZZ" {
			return country
		}
		call.Context.Debugf("Falling back to user-supplied country in dev server")
	} else {
		call.Context.Warningf("Could not auto-detect country (available headers: %v), falling back to query parameter", call.Request.Header)
	}

	query := call.Request.URL.Query()["country"]

	if len(query) > 0 {
		return query[0]
	}

	return "unknown"
}

type CallHandler func(*Call) (interface{}, error)

func (function CallHandler) ServeHTTP(w http.ResponseWriter, rq *http.Request) {
	call := Call{
		Context:  ae.NewContext(rq),
		Request:  rq,
		Response: w,
	}

	call.Response.Header().Add("Content-Type", "application/json;charset=UTF-8")
	result, err := function(&call)
	encoder := json.NewEncoder(w)

	if err != nil {
		errors := call.writeError(err, nil)
		call.Response.WriteHeader(http.StatusInternalServerError)
		encoder.Encode(map[string][]string{
			"errors": errors,
		})
	} else {
		encoder.Encode(result)
	}
}

func init() {
	http.Handle("/volumes/", CallHandler(serveVolumeSingle))
	http.Handle("/volumes", CallHandler(serveVolumeBulk))
}

func serveVolumeBulk(call *Call) (interface{}, error) {
	var shelf *data.LookupReply
	var err error

	switch call.Request.Method {
	case "GET":
		shelf, err = serveVolumeBulk(call)
	case "PUT":
		shelf, err = putVolumeBulk(call)
	default:
		err = errors.New("Unsupported operation. Only GET, PUT and DELETE methods are allowed")
	}

	return shelf, err
}

func serveVolumeBulk(call *Call) (reply *data.LookupReply, err error) {
	var shelf *data.Bookshelf

	if shelf, err = persistence.LookupBookshelf(call.Context); err == nil {
		for _, str := range call.Request.URL.Query()["search"] {
			matches := shelf.Search(str)

			shelf := &data.Bookshelf{make([]data.BookMetaData, len(matches))}
			for i, ptr := range matches {
				shelf.Books[i] = *ptr
			}
		}

		reply = &data.LookupReply{
			Count:     len(shelf.Books),
			BookInfos: shelf.Books,
		}
	}
}

func putVolumeBulk(call *Call) (reply *data.LookupReply, err error) {
	decode := json.NewDecoder(call.Request.Body)
	shelf := new(data.Bookshelf)
	if err = decode.Decode(shelf); err == nil {
		for i := range shelf.Books {
			book := &shelf.Books[i]
			normalize(call, book)
		}

		if err = persistence.StoreBookshelf(call.Context, shelf); err == nil {
			reply = &data.LookupReply{
				Count:     len(shelf.Books),
				BookInfos: shelf.Books,
			}
		}
	}

	return
}

func serveVolumeSingle(call *Call) (interface{}, error) {
	isbn, err := isbn13.New(call.Request.URL.Path[9:])
	var book *data.BookMetaData

	if err == nil {
		switch call.Request.Method {
		case "GET":
			book, err = compositeISBNLookup(call.Context, call.DetermineCountry(), isbn)
		case "PUT":
			book, err = putVolumeSingle(call, isbn)
		default:
			err = errors.New("Unsupported operation. Only GET, PUT and DELETE methods are allowed")
		}
	}

	return book, err
}

func putVolumeSingle(call *Call, isbn isbn13.ISBN13) (info *data.BookMetaData, err error) {
	decode := json.NewDecoder(call.Request.Body)
	info = new(data.BookMetaData)
	if err = decode.Decode(info); err == nil {
		info.ISBN = isbn.String()
		normalize(call, info)

		persistence.UpdateBookshelf(call.Context, func(_ ae.Context, shelf *data.Bookshelf) error {

			if ptr := shelf.LookupInfo(isbn); ptr != nil {
				*ptr = *info
			} else {
				shelf.Books = append(shelf.Books, *info)
			}

			return nil
		})
	}
	return info, err
}

func normalize(call *Call, info *data.BookMetaData) {
	var isbn isbn13.ISBN13
	var err error
	if isbn, err = isbn13.New(info.ISBN); err == nil {
		var base *data.BookMetaData
		if base, err = compositeISBNLookup(call.Context, call.DetermineCountry(), isbn); err == nil {
			info.Volume.Images = base.Volume.Images
			return
		} else {
			info.Volume.Images = data.ImageLinks{}
		}
	}
	call.Context.Errorf("Could not normalize data for %s: %s", info.ISBN, err.Error())
}

func compositeISBNLookup(ctx ae.Context, country string, isbn isbn13.ISBN13) (resp *data.BookMetaData, err error) {
	// look in my own library first - if this fails badly, abort anything further
	if shelf, err := persistence.LookupBookshelf(ctx); err == nil {
		if book := shelf.LookupInfo(isbn); book != nil {
			return book, nil
		}
	} else {
		return nil, err
	}

	var errors ae.MultiError
	if book, err := persistence.LookupISBN(ctx, country, isbn); err == nil {
		return book, nil
	} else {
		errors = append(errors, err)
	}

	if r, err := googlebooks.LookupISBN(ctx, country, isbn); err == nil {
		persistence.StoreISBNResult(ctx, country, isbn, r)
		return r, nil
	} else {
		errors = append(errors, err)
	}

	return nil, errors
}
<commit_msg>Explicit return<commit_after>package webservice

import (
	ae "appengine"
	"data"
	"encoding/json"
	"errors"
	"googlebooks"
	"isbn13"
	"net/http"
	"persistence"
)

type Call struct {
	Context  ae.Context
	Request  *http.Request
	Response http.ResponseWriter
}

func (call *Call) writeError(err error, strings []string) []string {
	if me, ok := err.(ae.MultiError); ok {
		for _, next := range me {
			strings = call.writeError(next, strings)
		}
	} else {
		str := err.Error()
		call.Context.Errorf("Error reported: %s", str)
		strings = append(strings, str)
	}

	return strings
}

func (call *Call) DetermineCountry() string {
	header := call.Request.Header["X-Appengine-Country"]
	if len(header) > 0 {
		country := header[0]

		if country != "ZZ" {
			return country
		}
		call.Context.Debugf("Falling back to user-supplied country in dev server")
	} else {
		call.Context.Warningf("Could not auto-detect country (available headers: %v), falling back to query parameter", call.Request.Header)
	}

	query := call.Request.URL.Query()["country"]

	if len(query) > 0 {
		return query[0]
	}

	return "unknown"
}

type CallHandler func(*Call) (interface{}, error)

func (function CallHandler) ServeHTTP(w http.ResponseWriter, rq *http.Request) {
	call := Call{
		Context:  ae.NewContext(rq),
		Request:  rq,
		Response: w,
	}

	call.Response.Header().Add("Content-Type", "application/json;charset=UTF-8")
	result, err := function(&call)
	encoder := json.NewEncoder(w)

	if err != nil {
		errors := call.writeError(err, nil)
		call.Response.WriteHeader(http.StatusInternalServerError)
		encoder.Encode(map[string][]string{
			"errors": errors,
		})
	} else {
		encoder.Encode(result)
	}
}

func init() {
	http.Handle("/volumes/", CallHandler(serveVolumeSingle))
	http.Handle("/volumes", CallHandler(serveVolumeBulk))
}

func serveVolumeBulk(call *Call) (interface{}, error) {
	var shelf *data.LookupReply
	var err error

	switch call.Request.Method {
	case "GET":
		shelf, err = serveVolumeBulk(call)
	case "PUT":
		shelf, err = putVolumeBulk(call)
	default:
		err = errors.New("Unsupported operation. Only GET, PUT and DELETE methods are allowed")
	}

	return shelf, err
}

func serveVolumeBulk(call *Call) (reply *data.LookupReply, err error) {
	var shelf *data.Bookshelf

	if shelf, err = persistence.LookupBookshelf(call.Context); err == nil {
		for _, str := range call.Request.URL.Query()["search"] {
			matches := shelf.Search(str)

			shelf := &data.Bookshelf{make([]data.BookMetaData, len(matches))}
			for i, ptr := range matches {
				shelf.Books[i] = *ptr
			}
		}

		reply = &data.LookupReply{
			Count:     len(shelf.Books),
			BookInfos: shelf.Books,
		}
	}

	return
}

func putVolumeBulk(call *Call) (reply *data.LookupReply, err error) {
	decode := json.NewDecoder(call.Request.Body)
	shelf := new(data.Bookshelf)
	if err = decode.Decode(shelf); err == nil {
		for i := range shelf.Books {
			book := &shelf.Books[i]
			normalize(call, book)
		}

		if err = persistence.StoreBookshelf(call.Context, shelf); err == nil {
			reply = &data.LookupReply{
				Count:     len(shelf.Books),
				BookInfos: shelf.Books,
			}
		}
	}

	return
}

func serveVolumeSingle(call *Call) (interface{}, error) {
	isbn, err := isbn13.New(call.Request.URL.Path[9:])
	var book *data.BookMetaData

	if err == nil {
		switch call.Request.Method {
		case "GET":
			book, err = compositeISBNLookup(call.Context, call.DetermineCountry(), isbn)
		case "PUT":
			book, err = putVolumeSingle(call, isbn)
		default:
			err = errors.New("Unsupported operation. Only GET, PUT and DELETE methods are allowed")
		}
	}

	return book, err
}

func putVolumeSingle(call *Call, isbn isbn13.ISBN13) (info *data.BookMetaData, err error) {
	decode := json.NewDecoder(call.Request.Body)
	info = new(data.BookMetaData)
	if err = decode.Decode(info); err == nil {
		info.ISBN = isbn.String()
		normalize(call, info)

		persistence.UpdateBookshelf(call.Context, func(_ ae.Context, shelf *data.Bookshelf) error {

			if ptr := shelf.LookupInfo(isbn); ptr != nil {
				*ptr = *info
			} else {
				shelf.Books = append(shelf.Books, *info)
			}

			return nil
		})
	}
	return info, err
}

func normalize(call *Call, info *data.BookMetaData) {
	var isbn isbn13.ISBN13
	var err error
	if isbn, err = isbn13.New(info.ISBN); err == nil {
		var base *data.BookMetaData
		if base, err = compositeISBNLookup(call.Context, call.DetermineCountry(), isbn); err == nil {
			info.Volume.Images = base.Volume.Images
			return
		} else {
			info.Volume.Images = data.ImageLinks{}
		}
	}
	call.Context.Errorf("Could not normalize data for %s: %s", info.ISBN, err.Error())
}

func compositeISBNLookup(ctx ae.Context, country string, isbn isbn13.ISBN13) (resp *data.BookMetaData, err error) {
	// look in my own library first - if this fails badly, abort anything further
	if shelf, err := persistence.LookupBookshelf(ctx); err == nil {
		if book := shelf.LookupInfo(isbn); book != nil {
			return book, nil
		}
	} else {
		return nil, err
	}

	var errors ae.MultiError
	if book, err := persistence.LookupISBN(ctx, country, isbn); err == nil {
		return book, nil
	} else {
		errors = append(errors, err)
	}

	if r, err := googlebooks.LookupISBN(ctx, country, isbn); err == nil {
		persistence.StoreISBNResult(ctx, country, isbn, r)
		return r, nil
	} else {
		errors = append(errors, err)
	}

	return nil, errors
}
<|endoftext|>


<commit_before>// package fromenv provides utilities to create lile options from environment
// variables. fromenv will error with fatal if it cannot resolve or errors
package fromenv

import (
	"fmt"
	"os"

	"github.com/lileio/lile/pubsub"
	"github.com/lileio/lile/pubsub/google"
	opentracing "github.com/opentracing/opentracing-go"
	zipkin "github.com/openzipkin/zipkin-go-opentracing"
	"github.com/sirupsen/logrus"
)

func Tracer(name string) opentracing.Tracer {
	var collector zipkin.Collector
	var err error

	zipkin_host := os.Getenv("ZIPKIN_SERVICE_HOST")
	if zipkin_host != "" {
		addr := fmt.Sprintf("http://%s:%s/api/v1/spans",
			os.Getenv("ZIPKIN_SERVICE_HOST"),
			os.Getenv("ZIPKIN_SERVICE_PORT"))
		collector, err = zipkin.NewHTTPCollector(addr)
		if err != nil {
			logrus.Fatalf("unable to create Zipkin HTTP collector: %+v", err)
		}

		logrus.Infof("Using Zipkin HTTP tracer: %s", addr)
	}

	if collector == nil {
		logrus.Infof("Using Zipkin Global tracer")
		return opentracing.GlobalTracer()
	}

	// create recorder.
	recorder := zipkin.NewRecorder(collector, false, "", name)

	// create tracer.
	tracer, err := zipkin.NewTracer(
		recorder,
	)
	if err != nil {
		logrus.Fatalf("unable to create Zipkin tracer: %+v", err)
	}

	// explicitly set our tracer to be the default tracer.
	opentracing.InitGlobalTracer(tracer)

	return tracer
}

func PubSubProvider() pubsub.Provider {
	gpid := os.Getenv("GOOGLE_PUBSUB_PROJECT_ID")
	if gpid != "" {
		gc, err := google.NewGoogleCloud(gpid)
		if err != nil {
			logrus.Fatalf("fronenv: Google Cloud pubsub err: %s", err)
			return nil
		}

		logrus.Infof("Using Google Cloud pubsub: %s", gpid)
		return gc
	}

	logrus.Warn("Using noop pubsub provider")
	return pubsub.NoopProvider{}
}
<commit_msg>Zipkin traces for client and server should be seperate<commit_after>// package fromenv provides utilities to create lile options from environment
// variables. fromenv will error with fatal if it cannot resolve or errors
package fromenv

import (
	"fmt"
	"os"

	"github.com/lileio/lile/pubsub"
	"github.com/lileio/lile/pubsub/google"
	opentracing "github.com/opentracing/opentracing-go"
	zipkin "github.com/openzipkin/zipkin-go-opentracing"
	"github.com/sirupsen/logrus"
)

func Tracer(name string) opentracing.Tracer {
	var collector zipkin.Collector
	var err error

	zipkin_host := os.Getenv("ZIPKIN_SERVICE_HOST")
	if zipkin_host != "" {
		addr := fmt.Sprintf("http://%s:%s/api/v1/spans",
			os.Getenv("ZIPKIN_SERVICE_HOST"),
			os.Getenv("ZIPKIN_SERVICE_PORT"))
		collector, err = zipkin.NewHTTPCollector(addr)
		if err != nil {
			logrus.Fatalf("unable to create Zipkin HTTP collector: %+v", err)
		}

		logrus.Infof("Using Zipkin HTTP tracer: %s", addr)
	}

	if collector == nil {
		logrus.Infof("Using Zipkin Global tracer")
		return opentracing.GlobalTracer()
	}

	// create recorder.
	recorder := zipkin.NewRecorder(collector, false, "", name)

	// create tracer.
	tracer, err := zipkin.NewTracer(
		recorder,
		zipkin.ClientServerSameSpan(false),
	)
	if err != nil {
		logrus.Fatalf("unable to create Zipkin tracer: %+v", err)
	}

	// explicitly set our tracer to be the default tracer.
	opentracing.InitGlobalTracer(tracer)

	return tracer
}

func PubSubProvider() pubsub.Provider {
	gpid := os.Getenv("GOOGLE_PUBSUB_PROJECT_ID")
	if gpid != "" {
		gc, err := google.NewGoogleCloud(gpid)
		if err != nil {
			logrus.Fatalf("fronenv: Google Cloud pubsub err: %s", err)
			return nil
		}

		logrus.Infof("Using Google Cloud pubsub: %s", gpid)
		return gc
	}

	logrus.Warn("Using noop pubsub provider")
	return pubsub.NoopProvider{}
}
<|endoftext|>