<commit_before>package main

import (
	"fmt"
	"log"
	"time"
	"strings"
	"strconv"
	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/appengine/v1beta4"
	"google.golang.org/api/storage/v1"
)

func resourceAppengine() *schema.Resource {
	return &schema.Resource{
		Create: resourceAppengineCreate,
		Read:   resourceAppengineRead,
		Delete: resourceAppengineDelete,

		Schema: map[string]*schema.Schema{
			"moduleName": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"version": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"gstorageBucket": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"gstorageKey": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"threadsafe": &schema.Schema{
				Type: 	  schema.TypeBool,
				Optional: true,
				ForceNew: true,
				Default:  true,
			},

			"runtime": &schema.Schema{
				Type:	  schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"resource_version": &schema.Schema{
				Type:     schema.TypeList,
				ForceNew: true,
				Optional: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},

			"scaling": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"minIdleInstances": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							Default:  "1",
						},

						"maxIdleInstances": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							Default: "3",
						},

						"minPendingLatency": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default: "Automatic",
						},

						"maxPendingLatency": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default: "Automatic",
						},
					},
				},
			},
			"topicName": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"scriptName": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"pythonUrlRegex": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"env_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:     schema.TypeString,
			},

			"servingStatus": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

var (
	remoteBase = "https://storage.googleapis.com/"
)

func pythonUrlHandlers(d *schema.ResourceData) ([]*appengine.UrlMap) {
	handlers := make([]*appengine.UrlMap, 0)
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:d.Get("pythonUrlRegex").(string),
		Script:&appengine.ScriptHandler{
			ScriptPath:d.Get("scriptName").(string),
		},
	})

	return handlers
}

func urlHandlers() ([]*appengine.UrlMap) {
	handlers := make([]*appengine.UrlMap, 0)
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:"/",
		Script:&appengine.ScriptHandler{
			ScriptPath:"unused",
		},
	})
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:"/.*/",
		Script:&appengine.ScriptHandler{
			ScriptPath:"unused",
		},
	})
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:"/_ah/.*",
		Script:&appengine.ScriptHandler{
			ScriptPath:"unused",
		},
	})
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:"/endpoint",
		Script:&appengine.ScriptHandler{
			ScriptPath:"unused",
		},
	})

	return handlers
}

// known issues with this function:
//   assumes "/" is delimiter in gstorage and forces that to be last char in key
//   only searches first page, if more then 1k files to load, will only grab first 1k
func generateFileList(d *schema.ResourceData, config *Config) (map[string]appengine.FileInfo, error) {
	listService := storage.NewObjectsService(config.clientStorage)
	bucket := d.Get("gstorageBucket").(string)
	listCall := listService.List(bucket)
	key := d.Get("gstorageKey").(string)
	lastChar := key[len(key)-1:]
	if lastChar != "/" {
		key = key + "/"
	}
	listCall = listCall.Prefix(key)
	objs, err := listCall.Do()
	if err != nil {
		return nil, err
	}

	files := make(map[string]appengine.FileInfo)

	//  when NextPageToken is empty string, on last page
	for objs.NextPageToken != "" {
		files = objsToFilelist(objs, files, key, bucket)
		listCall.PageToken(objs.NextPageToken)
		objs, err = listCall.Do()
		if err != nil {
			return nil, err
		}
	}

	//  read contents of last page
	files = objsToFilelist(objs, files, key, bucket)

	return files, nil
}

func objsToFilelist(objs *storage.Objects, files map[string]appengine.FileInfo, key, bucket string) (map[string]appengine.FileInfo) {
	for _, obj := range objs.Items {
		onDiskName := strings.Replace(obj.Name, key, "", 1)  // trims key from file name
		inCloudURL := remoteBase + bucket + "/" + obj.Name
		files[onDiskName] = appengine.FileInfo{SourceUrl:inCloudURL}
	}
	return files
}

func validateLatency(latency string) (string, error) {
	lastChar := latency[len(latency)-1:]
	if lastChar != "s" {
		return "", fmt.Errorf("latency values must be between 1 and 15 seconds in the form: 3s")
	}
	latency_i, err := strconv.Atoi(latency[:len(latency)-1])
	if err != nil {
		return "", err
	}
	if latency_i < 1 || latency_i > 15 {
		return "", fmt.Errorf("latency values must be between 1 and 15 seconds in the form: 3s")
	}

	return latency, nil
}

func cleanAdditionalArgs(optional_args map[string]interface{}) map[string]string {
	cleaned_opts := make(map[string]string)
	for k,v := range  optional_args {
		cleaned_opts[k] = v.(string)
	}
	return cleaned_opts
}

func resourceAppengineCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	scaling_raw := d.Get("scaling").([]interface{})
	if len(scaling_raw) > 1 {
		return fmt.Errorf("User supplied more then one scaling setting.  This is wrong")
	}

	scale := scaling_raw[0].(map[string]interface{})
	minPendingLatency, err := validateLatency(scale["minPendingLatency"].(string))
	if err != nil {
		return err
	}

	maxPendingLatency, err := validateLatency(scale["maxPendingLatency"].(string))
	if err != nil {
		return err
	}
	automaticScaling := &appengine.AutomaticScaling{
		MinIdleInstances: int64(scale["minIdleInstances"].(int)),
		MaxIdleInstances: int64(scale["maxIdleInstances"].(int)),
		MinPendingLatency: minPendingLatency,
		MaxPendingLatency: maxPendingLatency,
	}

	files, err := generateFileList(d, config)
	if err != nil {
		return err
	}
	deployment := &appengine.Deployment{Files:files}

	inbound_services := make([]string, 1)
	inbound_services[0] = "INBOUND_SERVICE_WARMUP"

	env_args := cleanAdditionalArgs(d.Get("env_args").(map[string]interface{}))
	env_vars := make(map[string]string,2)
	// the specific env vars in the config are given precendence so use the general map first
	for k, v := range env_args {
		env_vars[k] = v
	}
	if outputpubsub, ok := d.GetOk("topicName"); ok {
		env_vars["OUTPUTPUBSUB"] = outputpubsub.(string)
	}
	env_vars["RETURNMESSAGEIDS"] = "true"

	//  Version object for this module
	version := &appengine.Version{
		AutomaticScaling: automaticScaling,
		Deployment:deployment,
		Id: d.Get("version").(string),
		Runtime: d.Get("runtime").(string),
		//InstanceClass: "F2",  this is exploding.  not sure why
		InboundServices: inbound_services,
		EnvVariables: env_vars,
		Threadsafe: d.Get("threadsafe").(bool),
	}

	if d.Get("runtime").(string) == "java7" {
		version.Handlers = urlHandlers()
	} else {
		version.Handlers = pythonUrlHandlers(d)
	}

	//  create the application
	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	createCall := moduleVersionService.Create(config.Project, d.Get("moduleName").(string), version)
	operation, err := createCall.Do()
	if err != nil {
		return fmt.Errorf("failed to create mod ver: " + err.Error())
	}

	err = operationWait(operation, config)
	if err != nil {
		return err
	}

	return resourceAppengineRead(d, meta)
}

func operationWait(operation *appengine.Operation, config *Config) (error) {
	//  wait for the creation to complete
	operationService := appengine.NewAppsOperationsService(config.clientAppengine)
	operationGet := operationService.Get(config.Project, strings.Replace(operation.Name, "apps/"+config.Project+"/operations/", "", 1))
	carryon := true
	op := &appengine.Operation{}
	var err error
	for carryon {
		op, err = operationGet.Do()
		if err != nil {
			return fmt.Errorf("Wait operation has exploded: " + err.Error())
		}
		carryon = !op.Done
		time.Sleep(10*time.Second)
		log.Printf("[DEBUG] here's the whole op: %+v", op)
	}

	//   if it failed, explode
	if op.Error != nil {
		log.Printf("[DEBUG] status list from bad operation: %q", op.Error.Details)
		return fmt.Errorf("The operation has completed with errors: " + op.Error.Message)
	}

	return nil
}

func resourceAppengineRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	getCall := moduleVersionService.Get(config.Project, d.Get("moduleName").(string), d.Get("version").(string))
	version, err := getCall.Do()
	if err != nil {
		return fmt.Errorf("Couldn't find the resource: " + err.Error())
	}

	d.SetId(version.Name)
	d.Set("servingStatus", version.ServingStatus)
	return nil
}

func resourceAppengineDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	deleteCall := moduleVersionService.Delete(config.Project, d.Get("moduleName").(string), d.Get("version").(string))
	operation, err := deleteCall.Do()
	if err != nil {
		if strings.Contains(err.Error(), "Cannot delete the final version of a service (module)") {
			moduleService := appengine.NewAppsModulesService(config.clientAppengine)
			moduleDelete := moduleService.Delete(config.Project, d.Get("moduleName").(string))
			operation, err = moduleDelete.Do()
			if err != nil {
				return err
			}

			err = operationWait(operation, config)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	} else {
		err = operationWait(operation, config)
		if err != nil {
			return err
		}
	}

	d.SetId("")
	return nil
}
<commit_msg>add a check so we don't try to load illegal file names<commit_after>package main

import (
	"fmt"
	"log"
	"time"
	"regexp"
	"strings"
	"strconv"
	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/appengine/v1beta4"
	"google.golang.org/api/storage/v1"
)

func resourceAppengine() *schema.Resource {
	return &schema.Resource{
		Create: resourceAppengineCreate,
		Read:   resourceAppengineRead,
		Delete: resourceAppengineDelete,

		Schema: map[string]*schema.Schema{
			"moduleName": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"version": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"gstorageBucket": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"gstorageKey": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"threadsafe": &schema.Schema{
				Type: 	  schema.TypeBool,
				Optional: true,
				ForceNew: true,
				Default:  true,
			},

			"runtime": &schema.Schema{
				Type:	  schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"resource_version": &schema.Schema{
				Type:     schema.TypeList,
				ForceNew: true,
				Optional: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},

			"scaling": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"minIdleInstances": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							Default:  "1",
						},

						"maxIdleInstances": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							Default: "3",
						},

						"minPendingLatency": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default: "Automatic",
						},

						"maxPendingLatency": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default: "Automatic",
						},
					},
				},
			},
			"topicName": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"scriptName": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"pythonUrlRegex": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"env_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:     schema.TypeString,
			},

			"servingStatus": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

var (
	remoteBase = "https://storage.googleapis.com/"
)

func pythonUrlHandlers(d *schema.ResourceData) ([]*appengine.UrlMap) {
	handlers := make([]*appengine.UrlMap, 0)
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:d.Get("pythonUrlRegex").(string),
		Script:&appengine.ScriptHandler{
			ScriptPath:d.Get("scriptName").(string),
		},
	})

	return handlers
}

func urlHandlers() ([]*appengine.UrlMap) {
	handlers := make([]*appengine.UrlMap, 0)
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:"/",
		Script:&appengine.ScriptHandler{
			ScriptPath:"unused",
		},
	})
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:"/.*/",
		Script:&appengine.ScriptHandler{
			ScriptPath:"unused",
		},
	})
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:"/_ah/.*",
		Script:&appengine.ScriptHandler{
			ScriptPath:"unused",
		},
	})
	handlers = append(handlers, &appengine.UrlMap{
		SecurityLevel: "SECURE_OPTIONAL",
		Login: "LOGIN_OPTIONAL",
		UrlRegex:"/endpoint",
		Script:&appengine.ScriptHandler{
			ScriptPath:"unused",
		},
	})

	return handlers
}

// known issues with this function:
//   assumes "/" is delimiter in gstorage and forces that to be last char in key
//   only searches first page, if more then 1k files to load, will only grab first 1k
func generateFileList(d *schema.ResourceData, config *Config) (map[string]appengine.FileInfo, error) {
	listService := storage.NewObjectsService(config.clientStorage)
	bucket := d.Get("gstorageBucket").(string)
	listCall := listService.List(bucket)
	key := d.Get("gstorageKey").(string)
	lastChar := key[len(key)-1:]
	if lastChar != "/" {
		key = key + "/"
	}
	listCall = listCall.Prefix(key)
	objs, err := listCall.Do()
	if err != nil {
		return nil, err
	}

	files := make(map[string]appengine.FileInfo)

	//  when NextPageToken is empty string, on last page
	for objs.NextPageToken != "" {
		files = objsToFilelist(objs, files, key, bucket)
		listCall.PageToken(objs.NextPageToken)
		objs, err = listCall.Do()
		if err != nil {
			return nil, err
		}
	}

	//  read contents of last page
	files = objsToFilelist(objs, files, key, bucket)

	return files, nil
}

func objsToFilelist(objs *storage.Objects, files map[string]appengine.FileInfo, key, bucket string) (map[string]appengine.FileInfo) {
	for _, obj := range objs.Items {
		if matched, _ := regexp.MatchString("[(~])", obj.Name); !matched {  // both ( and ~ are illegal file name chars
			onDiskName := strings.Replace(obj.Name, key, "", 1)  // trims key from file name
			inCloudURL := remoteBase + bucket + "/" + obj.Name
			files[onDiskName] = appengine.FileInfo{SourceUrl:inCloudURL}
		}
	}
	return files
}

func validateLatency(latency string) (string, error) {
	lastChar := latency[len(latency)-1:]
	if lastChar != "s" {
		return "", fmt.Errorf("latency values must be between 1 and 15 seconds in the form: 3s")
	}
	latency_i, err := strconv.Atoi(latency[:len(latency)-1])
	if err != nil {
		return "", err
	}
	if latency_i < 1 || latency_i > 15 {
		return "", fmt.Errorf("latency values must be between 1 and 15 seconds in the form: 3s")
	}

	return latency, nil
}

func cleanAdditionalArgs(optional_args map[string]interface{}) map[string]string {
	cleaned_opts := make(map[string]string)
	for k,v := range  optional_args {
		cleaned_opts[k] = v.(string)
	}
	return cleaned_opts
}

func resourceAppengineCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	scaling_raw := d.Get("scaling").([]interface{})
	if len(scaling_raw) > 1 {
		return fmt.Errorf("User supplied more then one scaling setting.  This is wrong")
	}

	scale := scaling_raw[0].(map[string]interface{})
	minPendingLatency, err := validateLatency(scale["minPendingLatency"].(string))
	if err != nil {
		return err
	}

	maxPendingLatency, err := validateLatency(scale["maxPendingLatency"].(string))
	if err != nil {
		return err
	}
	automaticScaling := &appengine.AutomaticScaling{
		MinIdleInstances: int64(scale["minIdleInstances"].(int)),
		MaxIdleInstances: int64(scale["maxIdleInstances"].(int)),
		MinPendingLatency: minPendingLatency,
		MaxPendingLatency: maxPendingLatency,
	}

	files, err := generateFileList(d, config)
	if err != nil {
		return err
	}
	deployment := &appengine.Deployment{Files:files}

	inbound_services := make([]string, 1)
	inbound_services[0] = "INBOUND_SERVICE_WARMUP"

	env_args := cleanAdditionalArgs(d.Get("env_args").(map[string]interface{}))
	env_vars := make(map[string]string,2)
	// the specific env vars in the config are given precendence so use the general map first
	for k, v := range env_args {
		env_vars[k] = v
	}
	if outputpubsub, ok := d.GetOk("topicName"); ok {
		env_vars["OUTPUTPUBSUB"] = outputpubsub.(string)
	}
	env_vars["RETURNMESSAGEIDS"] = "true"

	//  Version object for this module
	version := &appengine.Version{
		AutomaticScaling: automaticScaling,
		Deployment:deployment,
		Id: d.Get("version").(string),
		Runtime: d.Get("runtime").(string),
		//InstanceClass: "F2",  this is exploding.  not sure why
		InboundServices: inbound_services,
		EnvVariables: env_vars,
		Threadsafe: d.Get("threadsafe").(bool),
	}

	if d.Get("runtime").(string) == "java7" {
		version.Handlers = urlHandlers()
	} else {
		version.Handlers = pythonUrlHandlers(d)
	}

	//  create the application
	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	createCall := moduleVersionService.Create(config.Project, d.Get("moduleName").(string), version)
	operation, err := createCall.Do()
	if err != nil {
		return fmt.Errorf("failed to create mod ver: " + err.Error())
	}

	err = operationWait(operation, config)
	if err != nil {
		return err
	}

	return resourceAppengineRead(d, meta)
}

func operationWait(operation *appengine.Operation, config *Config) (error) {
	//  wait for the creation to complete
	operationService := appengine.NewAppsOperationsService(config.clientAppengine)
	operationGet := operationService.Get(config.Project, strings.Replace(operation.Name, "apps/"+config.Project+"/operations/", "", 1))
	carryon := true
	op := &appengine.Operation{}
	var err error
	for carryon {
		op, err = operationGet.Do()
		if err != nil {
			return fmt.Errorf("Wait operation has exploded: " + err.Error())
		}
		carryon = !op.Done
		time.Sleep(10*time.Second)
		log.Printf("[DEBUG] here's the whole op: %+v", op)
	}

	//   if it failed, explode
	if op.Error != nil {
		log.Printf("[DEBUG] status list from bad operation: %q", op.Error.Details)
		return fmt.Errorf("The operation has completed with errors: " + op.Error.Message)
	}

	return nil
}

func resourceAppengineRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	getCall := moduleVersionService.Get(config.Project, d.Get("moduleName").(string), d.Get("version").(string))
	version, err := getCall.Do()
	if err != nil {
		return fmt.Errorf("Couldn't find the resource: " + err.Error())
	}

	d.SetId(version.Name)
	d.Set("servingStatus", version.ServingStatus)
	return nil
}

func resourceAppengineDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	deleteCall := moduleVersionService.Delete(config.Project, d.Get("moduleName").(string), d.Get("version").(string))
	operation, err := deleteCall.Do()
	if err != nil {
		if strings.Contains(err.Error(), "Cannot delete the final version of a service (module)") {
			moduleService := appengine.NewAppsModulesService(config.clientAppengine)
			moduleDelete := moduleService.Delete(config.Project, d.Get("moduleName").(string))
			operation, err = moduleDelete.Do()
			if err != nil {
				return err
			}

			err = operationWait(operation, config)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	} else {
		err = operationWait(operation, config)
		if err != nil {
			return err
		}
	}

	d.SetId("")
	return nil
}
<|endoftext|>


<commit_before>package jwt

import (
	"fmt"
	"log"

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

type token struct {
	Token string `json:"token"`
}

// HMAC is JSON Web Token authentication using HMAC.
// If successful, token context will be store with the key "userid" in session.
// If unsuccessful, connection will be closed right away.
func HMAC(password string) func(ctx *neptulon.ReqCtx) error {
	pass := []byte(password)
	var authenticated bool

	return func(ctx *neptulon.ReqCtx) error {
		if authenticated {
			return ctx.Next()
		}

		var t token
		if err := ctx.Params(&t); err != nil {
			ctx.Conn.Close()
			return err
		}

		jt, err := jwt.Parse(t.Token, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("middleware: jwt: unexpected signing method: %v", token.Header["alg"])
			}
			return pass, nil
		})

		if err != nil || !jt.Valid {
			ctx.Conn.Close()
			return fmt.Errorf("middleware: jwt: invalid JWT authentication attempt: %v: %v: %v", err, ctx.Conn.RemoteAddr(), t.Token)
		}

		userID := jt.Claims["userid"].(string)
		ctx.Conn.Session.Set("userid", userID)
		authenticated = true
		log.Printf("middleware: jwt: client authenticated. user: %v, conn: %v, ip: %v", userID, ctx.Conn.ID, ctx.Conn.RemoteAddr())
		return ctx.Next()
	}
}
<commit_msg>jwt: remove authenticated closure<commit_after>package jwt

import (
	"fmt"
	"log"

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

type token struct {
	Token string `json:"token"`
}

// HMAC is JSON Web Token authentication using HMAC.
// If successful, token context will be store with the key "userid" in session.
// If unsuccessful, connection will be closed right away.
func HMAC(password string) func(ctx *neptulon.ReqCtx) error {
	pass := []byte(password)

	return func(ctx *neptulon.ReqCtx) error {
		if _, ok := ctx.Conn.Session.GetOk("userid"); ok {
			return nil
		}

		var t token
		if err := ctx.Params(&t); err != nil {
			ctx.Conn.Close()
			return err
		}

		jt, err := jwt.Parse(t.Token, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("middleware: jwt: unexpected signing method: %v", token.Header["alg"])
			}
			return pass, nil
		})

		if err != nil || !jt.Valid {
			ctx.Conn.Close()
			return fmt.Errorf("middleware: jwt: invalid JWT authentication attempt: %v: %v: %v", err, ctx.Conn.RemoteAddr(), t.Token)
		}

		userID := jt.Claims["userid"].(string)
		ctx.Conn.Session.Set("userid", userID)
		log.Printf("middleware: jwt: client authenticated. user: %v, conn: %v, ip: %v", userID, ctx.Conn.ID, ctx.Conn.RemoteAddr())
		return ctx.Next()
	}
}
<|endoftext|>


<commit_before>package retry

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

var (
	defaultBackoff    = ExponentialJitterBackoff(100*time.Millisecond, 30*time.Second, 2)
	defaultClassifier = AnyErrorClassifier
	defaultMaxRetries = 10
	defaultMaxTime    = 3 * time.Minute
)

// NewRetryTransport returns RoundTripper that wraps around provided RoundTripper
// and adds retry logic around it. All retry parameters are read from http.Request
// context (with sane defaults).
func NewRetryTransport(next http.RoundTripper) http.RoundTripper {
	return &retryTransport{
		next: next,
	}
}

type retryTransport struct {
	next http.RoundTripper
}

func (t *retryTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	count := 0

	// configure retry settings
	classifier := getClassifier(r.Context())
	backoff := getBackoff(r.Context())
	maxRetries := getRetryTimes(r.Context())
	maxDuration := geMaxDuration(r.Context())
	if classifier == nil {
		classifier = defaultClassifier
	}
	if backoff == nil {
		backoff = defaultBackoff
	}
	if maxRetries == 0 {
		maxRetries = defaultMaxRetries
	}
	if maxDuration == time.Duration(0) {
		maxDuration = defaultMaxTime
	}

	start := time.Now().UTC()

	// buffer request body for potentially repeated requests
	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}
	r.Body.Close()

	for {
		// Copy request and sets its body to appropriate value
		reqCopy := &http.Request{}
		*reqCopy = *r
		reqCopy.Body = ioutil.NopCloser(bytes.NewBuffer(buf))

		// perform actual request
		resp, err := t.next.RoundTrip(reqCopy)

		// check if we reached any of conditions for stopping retry cycle
		currentDuration := time.Now().UTC().Sub(start)
		if !classifier(resp, err) || count > maxRetries || currentDuration.Nanoseconds() > maxDuration.Nanoseconds() {
			return resp, err
		}
		// if all else failed, increase number of retries and wait for some time
		count++
		time.Sleep(backoff(count))
	}
}
<commit_msg>Fix build<commit_after>package retry

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"time"
)

var (
	defaultBackoff    = ExponentialJitterBackoff(100*time.Millisecond, 30*time.Second, 2)
	defaultClassifier = AnyErrorClassifier
	defaultMaxRetries = 10
	defaultMaxTime    = 3 * time.Minute
)

// NewRetryTransport returns RoundTripper that wraps around provided RoundTripper
// and adds retry logic around it. All retry parameters are read from http.Request
// context (with sane defaults).
func NewRetryTransport(next http.RoundTripper) http.RoundTripper {
	return &retryTransport{
		next: next,
	}
}

type retryTransport struct {
	next http.RoundTripper
}

func (t *retryTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	count := 0

	// configure retry settings
	classifier := getClassifier(r.Context())
	backoff := getBackoff(r.Context())
	maxRetries := getRetryTimes(r.Context())
	maxDuration := geMaxDuration(r.Context())
	if classifier == nil {
		classifier = defaultClassifier
	}
	if backoff == nil {
		backoff = defaultBackoff
	}
	if maxRetries == 0 {
		maxRetries = defaultMaxRetries
	}
	if maxDuration == time.Duration(0) {
		maxDuration = defaultMaxTime
	}

	start := time.Now().UTC()

	// buffer request body for potentially repeated requests
	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}
	r.Body.Close()

	for {
		// Copy request and sets its body to appropriate value
		reqCopy := &http.Request{}
		*reqCopy = *r
		reqCopy.Body = ioutil.NopCloser(bytes.NewBuffer(buf))

		// perform actual request
		resp, err := t.next.RoundTrip(reqCopy)

		// check if we reached any of conditions for stopping retry cycle
		currentDuration := time.Now().UTC().Sub(start)
		if !classifier(resp, err) || count > maxRetries || currentDuration.Nanoseconds() > maxDuration.Nanoseconds() {
			return resp, err
		}
		// if all else failed, increase number of retries and wait for some time
		count++
		time.Sleep(backoff(count))
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go 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 math

func isOddInt(x float64) bool {
	xi, xf := Modf(x)
	return xf == 0 && int64(xi)&1 == 1
}

// Special cases taken from FreeBSD's /usr/src/lib/msun/src/e_pow.c
// updated by IEEE Std. 754-2008 "Section 9.2.1 Special values".

// Pow returns x**y, the base-x exponential of y.
//
// Special cases are (in order):
//	Pow(x, ±0) = 1 for any x
//	Pow(1, y) = 1 for any y
//	Pow(x, 1) = x for any x
//	Pow(NaN, y) = NaN
//	Pow(x, NaN) = NaN
//	Pow(±0, y) = ±Inf for y an odd integer < 0
//	Pow(±0, -Inf) = +Inf
//	Pow(±0, +Inf) = +0
//	Pow(±0, y) = +Inf for finite y < 0 and not an odd integer
//	Pow(±0, y) = ±0 for y an odd integer > 0
//	Pow(±0, y) = +0 for finite y > 0 and not an odd integer
//	Pow(-1, ±Inf) = 1
//	Pow(x, +Inf) = +Inf for |x| > 1
//	Pow(x, -Inf) = +0 for |x| > 1
//	Pow(x, +Inf) = +0 for |x| < 1
//	Pow(x, -Inf) = +Inf for |x| < 1
//	Pow(+Inf, y) = +Inf for y > 0
//	Pow(+Inf, y) = +0 for y < 0
//	Pow(-Inf, y) = Pow(-0, -y)
//	Pow(x, y) = NaN for finite x < 0 and finite non-integer y
func Pow(x, y float64) float64

func pow(x, y float64) float64 {
	switch {
	case y == 0 || x == 1:
		return 1
	case y == 1:
		return x
	case IsNaN(x) || IsNaN(y):
		return NaN()
	case x == 0:
		switch {
		case y < 0:
			if isOddInt(y) {
				return Copysign(Inf(1), x)
			}
			return Inf(1)
		case y > 0:
			if isOddInt(y) {
				return x
			}
			return 0
		}
	case IsInf(y, 0):
		switch {
		case x == -1:
			return 1
		case (Abs(x) < 1) == IsInf(y, 1):
			return 0
		default:
			return Inf(1)
		}
	case IsInf(x, 0):
		if IsInf(x, -1) {
			return Pow(1/x, -y) // Pow(-0, -y)
		}
		switch {
		case y < 0:
			return 0
		case y > 0:
			return Inf(1)
		}
	case y == 0.5:
		return Sqrt(x)
	case y == -0.5:
		return 1 / Sqrt(x)
	}

	absy := y
	flip := false
	if absy < 0 {
		absy = -absy
		flip = true
	}
	yi, yf := Modf(absy)
	if yf != 0 && x < 0 {
		return NaN()
	}
	if yi >= 1<<63 {
		// yi is a large even int that will lead to overflow (or underflow to 0)
		// for all x except -1 (x == 1 was handled earlier)
		switch {
		case x == -1:
			return 1
		case (Abs(x) < 1) == (y > 0):
			return 0
		default:
			return Inf(1)
		}
	}

	// ans = a1 * 2**ae (= 1 for now).
	a1 := 1.0
	ae := 0

	// ans *= x**yf
	if yf != 0 {
		if yf > 0.5 {
			yf--
			yi++
		}
		a1 = Exp(yf * Log(x))
	}

	// ans *= x**yi
	// by multiplying in successive squarings
	// of x according to bits of yi.
	// accumulate powers of two into exp.
	x1, xe := Frexp(x)
	for i := int64(yi); i != 0; i >>= 1 {
		if xe < -1<<12 || 1<<12 < xe {
			// catch xe before it overflows the left shift below
			// Since i !=0 it has at least one bit still set, so ae will accumulate xe
			// on at least one more iteration, ae += xe is a lower bound on ae
			// the lower bound on ae exceeds the size of a float64 exp
			// so the final call to Ldexp will produce under/overflow (0/Inf)
			ae += xe
			break
		}
		if i&1 == 1 {
			a1 *= x1
			ae += xe
		}
		x1 *= x1
		xe <<= 1
		if x1 < .5 {
			x1 += x1
			xe--
		}
	}

	// ans = a1*2**ae
	// if flip { ans = 1 / ans }
	// but in the opposite order
	if flip {
		a1 = 1 / a1
		ae = -ae
	}
	return Ldexp(a1, ae)
}
<commit_msg>math: use Abs in Pow rather than if x < 0 { x = -x }<commit_after>// Copyright 2009 The Go 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 math

func isOddInt(x float64) bool {
	xi, xf := Modf(x)
	return xf == 0 && int64(xi)&1 == 1
}

// Special cases taken from FreeBSD's /usr/src/lib/msun/src/e_pow.c
// updated by IEEE Std. 754-2008 "Section 9.2.1 Special values".

// Pow returns x**y, the base-x exponential of y.
//
// Special cases are (in order):
//	Pow(x, ±0) = 1 for any x
//	Pow(1, y) = 1 for any y
//	Pow(x, 1) = x for any x
//	Pow(NaN, y) = NaN
//	Pow(x, NaN) = NaN
//	Pow(±0, y) = ±Inf for y an odd integer < 0
//	Pow(±0, -Inf) = +Inf
//	Pow(±0, +Inf) = +0
//	Pow(±0, y) = +Inf for finite y < 0 and not an odd integer
//	Pow(±0, y) = ±0 for y an odd integer > 0
//	Pow(±0, y) = +0 for finite y > 0 and not an odd integer
//	Pow(-1, ±Inf) = 1
//	Pow(x, +Inf) = +Inf for |x| > 1
//	Pow(x, -Inf) = +0 for |x| > 1
//	Pow(x, +Inf) = +0 for |x| < 1
//	Pow(x, -Inf) = +Inf for |x| < 1
//	Pow(+Inf, y) = +Inf for y > 0
//	Pow(+Inf, y) = +0 for y < 0
//	Pow(-Inf, y) = Pow(-0, -y)
//	Pow(x, y) = NaN for finite x < 0 and finite non-integer y
func Pow(x, y float64) float64

func pow(x, y float64) float64 {
	switch {
	case y == 0 || x == 1:
		return 1
	case y == 1:
		return x
	case IsNaN(x) || IsNaN(y):
		return NaN()
	case x == 0:
		switch {
		case y < 0:
			if isOddInt(y) {
				return Copysign(Inf(1), x)
			}
			return Inf(1)
		case y > 0:
			if isOddInt(y) {
				return x
			}
			return 0
		}
	case IsInf(y, 0):
		switch {
		case x == -1:
			return 1
		case (Abs(x) < 1) == IsInf(y, 1):
			return 0
		default:
			return Inf(1)
		}
	case IsInf(x, 0):
		if IsInf(x, -1) {
			return Pow(1/x, -y) // Pow(-0, -y)
		}
		switch {
		case y < 0:
			return 0
		case y > 0:
			return Inf(1)
		}
	case y == 0.5:
		return Sqrt(x)
	case y == -0.5:
		return 1 / Sqrt(x)
	}

	yi, yf := Modf(Abs(y))
	if yf != 0 && x < 0 {
		return NaN()
	}
	if yi >= 1<<63 {
		// yi is a large even int that will lead to overflow (or underflow to 0)
		// for all x except -1 (x == 1 was handled earlier)
		switch {
		case x == -1:
			return 1
		case (Abs(x) < 1) == (y > 0):
			return 0
		default:
			return Inf(1)
		}
	}

	// ans = a1 * 2**ae (= 1 for now).
	a1 := 1.0
	ae := 0

	// ans *= x**yf
	if yf != 0 {
		if yf > 0.5 {
			yf--
			yi++
		}
		a1 = Exp(yf * Log(x))
	}

	// ans *= x**yi
	// by multiplying in successive squarings
	// of x according to bits of yi.
	// accumulate powers of two into exp.
	x1, xe := Frexp(x)
	for i := int64(yi); i != 0; i >>= 1 {
		if xe < -1<<12 || 1<<12 < xe {
			// catch xe before it overflows the left shift below
			// Since i !=0 it has at least one bit still set, so ae will accumulate xe
			// on at least one more iteration, ae += xe is a lower bound on ae
			// the lower bound on ae exceeds the size of a float64 exp
			// so the final call to Ldexp will produce under/overflow (0/Inf)
			ae += xe
			break
		}
		if i&1 == 1 {
			a1 *= x1
			ae += xe
		}
		x1 *= x1
		xe <<= 1
		if x1 < .5 {
			x1 += x1
			xe--
		}
	}

	// ans = a1*2**ae
	// if y < 0 { ans = 1 / ans }
	// but in the opposite order
	if y < 0 {
		a1 = 1 / a1
		ae = -ae
	}
	return Ldexp(a1, ae)
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package grumpy

import (
	"fmt"
	"reflect"
)

// Property represents Python 'property' objects.
type Property struct {
	Object
	get, set, del *Object
}

func newProperty(get, set, del *Object) *Property {
	return &Property{Object{typ: PropertyType}, get, set, del}
}

func toPropertyUnsafe(o *Object) *Property {
	return (*Property)(o.toPointer())
}

// ToObject upcasts p to an Object.
func (p *Property) ToObject() *Object {
	return &p.Object
}

// PropertyType is the object representing the Python 'property' type.
var PropertyType = newBasisType("property", reflect.TypeOf(Property{}), toPropertyUnsafe, ObjectType)

func initPropertyType(map[string]*Object) {
	PropertyType.slots.Delete = &deleteSlot{propertyDelete}
	PropertyType.slots.Get = &getSlot{propertyGet}
	PropertyType.slots.Init = &initSlot{propertyInit}
	PropertyType.slots.Set = &setSlot{propertySet}
}

func propertyDelete(f *Frame, desc, inst *Object) *BaseException {
	p := toPropertyUnsafe(desc)
	if p.del == nil || p.del == None {
		return f.RaiseType(AttributeErrorType, "can't delete attribute")
	}
	_, raised := p.del.Call(f, Args{inst}, nil)
	return raised
}

func propertyGet(f *Frame, desc, instance *Object, _ *Type) (*Object, *BaseException) {
	p := toPropertyUnsafe(desc)
	if p.get == nil || p.get == None {
		return nil, f.RaiseType(AttributeErrorType, "unreadable attribute")
	}
	return p.get.Call(f, Args{instance}, nil)
}

func propertyInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) {
	expectedTypes := []*Type{ObjectType, ObjectType, ObjectType}
	argc := len(args)
	if argc < 3 {
		expectedTypes = expectedTypes[:argc]
	}
	if raised := checkFunctionArgs(f, "__init__", args, expectedTypes...); raised != nil {
		return nil, raised
	}
	p := toPropertyUnsafe(o)
	if argc > 0 {
		p.get = args[0]
	}
	if argc > 1 {
		p.set = args[1]
	}
	if argc > 2 {
		p.del = args[2]
	}
	return None, nil
}

func propertySet(f *Frame, desc, inst, value *Object) *BaseException {
	p := toPropertyUnsafe(desc)
	if p.set == nil || p.set == None {
		return f.RaiseType(AttributeErrorType, "can't set attribute")
	}
	_, raised := p.set.Call(f, Args{inst, value}, nil)
	return raised
}

// makeStructFieldDescriptor creates a descriptor with a getter that returns
// the field given by fieldName from t's basis structure.
func makeStructFieldDescriptor(t *Type, fieldName, propertyName string) *Object {
	field, ok := t.basis.FieldByName(fieldName)
	if !ok {
		logFatal(fmt.Sprintf("no such field %q for basis %s", fieldName, nativeTypeName(t.basis)))
	}
	getterFunc := reflect.MakeFunc(reflect.TypeOf(Func(nil)), func(argValues []reflect.Value) []reflect.Value {
		f := argValues[0].Interface().(*Frame)
		args := argValues[1].Interface().(Args)
		var ret *Object
		var raised *BaseException
		if raised = checkFunctionArgs(f, fieldName, args, ObjectType); raised == nil {
			o := args[0]
			if !o.isInstance(t) {
				format := "descriptor '%s' for '%s' objects doesn't apply to '%s' objects"
				raised = f.RaiseType(TypeErrorType, fmt.Sprintf(format, propertyName, t.Name(), o.typ.Name()))
			} else {
				ret, raised = WrapNative(f, t.slots.Basis.Fn(o).FieldByIndex(field.Index))
			}
		}
		return []reflect.Value{reflect.ValueOf(ret), reflect.ValueOf(raised)}
	}).Interface().(Func)
	return newProperty(newBuiltinFunction("_get"+fieldName, getterFunc).ToObject(), None, None).ToObject()
}
<commit_msg>Simplify closure in makeStructFieldDescriptor. (#131)<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package grumpy

import (
	"fmt"
	"reflect"
)

// Property represents Python 'property' objects.
type Property struct {
	Object
	get, set, del *Object
}

func newProperty(get, set, del *Object) *Property {
	return &Property{Object{typ: PropertyType}, get, set, del}
}

func toPropertyUnsafe(o *Object) *Property {
	return (*Property)(o.toPointer())
}

// ToObject upcasts p to an Object.
func (p *Property) ToObject() *Object {
	return &p.Object
}

// PropertyType is the object representing the Python 'property' type.
var PropertyType = newBasisType("property", reflect.TypeOf(Property{}), toPropertyUnsafe, ObjectType)

func initPropertyType(map[string]*Object) {
	PropertyType.slots.Delete = &deleteSlot{propertyDelete}
	PropertyType.slots.Get = &getSlot{propertyGet}
	PropertyType.slots.Init = &initSlot{propertyInit}
	PropertyType.slots.Set = &setSlot{propertySet}
}

func propertyDelete(f *Frame, desc, inst *Object) *BaseException {
	p := toPropertyUnsafe(desc)
	if p.del == nil || p.del == None {
		return f.RaiseType(AttributeErrorType, "can't delete attribute")
	}
	_, raised := p.del.Call(f, Args{inst}, nil)
	return raised
}

func propertyGet(f *Frame, desc, instance *Object, _ *Type) (*Object, *BaseException) {
	p := toPropertyUnsafe(desc)
	if p.get == nil || p.get == None {
		return nil, f.RaiseType(AttributeErrorType, "unreadable attribute")
	}
	return p.get.Call(f, Args{instance}, nil)
}

func propertyInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) {
	expectedTypes := []*Type{ObjectType, ObjectType, ObjectType}
	argc := len(args)
	if argc < 3 {
		expectedTypes = expectedTypes[:argc]
	}
	if raised := checkFunctionArgs(f, "__init__", args, expectedTypes...); raised != nil {
		return nil, raised
	}
	p := toPropertyUnsafe(o)
	if argc > 0 {
		p.get = args[0]
	}
	if argc > 1 {
		p.set = args[1]
	}
	if argc > 2 {
		p.del = args[2]
	}
	return None, nil
}

func propertySet(f *Frame, desc, inst, value *Object) *BaseException {
	p := toPropertyUnsafe(desc)
	if p.set == nil || p.set == None {
		return f.RaiseType(AttributeErrorType, "can't set attribute")
	}
	_, raised := p.set.Call(f, Args{inst, value}, nil)
	return raised
}

// makeStructFieldDescriptor creates a descriptor with a getter that returns
// the field given by fieldName from t's basis structure.
func makeStructFieldDescriptor(t *Type, fieldName, propertyName string) *Object {
	field, ok := t.basis.FieldByName(fieldName)
	if !ok {
		logFatal(fmt.Sprintf("no such field %q for basis %s", fieldName, nativeTypeName(t.basis)))
	}
	getterFunc := func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
		var ret *Object
		var raised *BaseException
		if raised = checkFunctionArgs(f, fieldName, args, ObjectType); raised == nil {
			o := args[0]
			if !o.isInstance(t) {
				format := "descriptor '%s' for '%s' objects doesn't apply to '%s' objects"
				raised = f.RaiseType(TypeErrorType, fmt.Sprintf(format, propertyName, t.Name(), o.typ.Name()))
			} else {
				ret, raised = WrapNative(f, t.slots.Basis.Fn(o).FieldByIndex(field.Index))
			}
		}
		return ret, raised
	}
	return newProperty(newBuiltinFunction("_get"+fieldName, getterFunc).ToObject(), None, None).ToObject()
}
<|endoftext|>


<commit_before>package sfapi

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/alecthomas/units"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"time"
)

type Client struct {
	SVIP              string
	Endpoint          string
	DefaultAPIPort    int
	DefaultVolSize    int64 //bytes
	DefaultAccountID  int64
	DefaultTenantName string
	VolumeTypes       *[]VolType
	Config            *Config
}

type Config struct {
	TenantName     string
	EndPoint       string
	DefaultVolSz   int64 //Default volume size in GiB
	MountPoint     string
	SVIP           string
	InitiatorIFace string //iface to use of iSCSI initiator
	Types          *[]VolType
}

type VolType struct {
	Type string
	QOS  QoS
}

var (
	endpoint          string
	svip              string
	configFile        string
	defaultTenantName string
	defaultSizeGiB    int64
	cfg               Config
)

func ProcessConfig(fname string) (Config, error) {
	content, err := ioutil.ReadFile(fname)
	if err != nil {
		log.Fatal("Error processing config file: ", err)
	}
	var conf Config
	err = json.Unmarshal(content, &conf)
	if err != nil {
		log.Fatal("Error parsing config file: ", err)
	}
	return conf, nil
}

func NewFromConfig(configFile string) (c *Client, err error) {
	conf, err := ProcessConfig(configFile)
	if err != nil {
		log.Fatal("Error initializing client from Config file: ", configFile, "(", err, ")")
	}
	cfg = conf
	endpoint = conf.EndPoint
	svip = conf.SVIP
	configFile = os.Getenv("SF_CONFIG_FILE")
	defaultSizeGiB = conf.DefaultVolSz
	defaultTenantName = conf.TenantName
	return New()
}

func NewFromOpts(ep string, dSize int64, storageIP string, acct string) (c *Client, err error) {
	endpoint = ep
	svip = storageIP
	defaultTenantName = acct
	defaultSizeGiB = dSize
	configFile = ""
	cfg = Config{}
	return New()
}

func New() (c *Client, err error) {
	rand.Seed(time.Now().UTC().UnixNano())
	defSize := defaultSizeGiB * int64(units.GiB)
	SFClient := &Client{
		Endpoint:          endpoint,
		DefaultVolSize:    defSize,
		SVIP:              svip,
		Config:            &cfg,
		DefaultAPIPort:    443,
		VolumeTypes:       cfg.Types,
		DefaultTenantName: defaultTenantName,
	}
	return SFClient, nil
}

func (c *Client) Request(method string, params interface{}, id int) (response []byte, err error) {
	log.Debug("Issue request to SolidFire Endpoint...")
	if c.Endpoint == "" {
		log.Error("Endpoint is not set, unable to issue requests")
		err = errors.New("Unable to issue json-rpc requests without specifying Endpoint")
		return nil, err
	}
	data, err := json.Marshal(map[string]interface{}{
		"method": method,
		"id":     id,
		"params": params,
	})

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}

	log.Debugf("POST request to: %+v", c.Endpoint)
	Http := &http.Client{Transport: tr}
	resp, err := Http.Post(c.Endpoint,
		"json-rpc",
		strings.NewReader(string(data)))
	if err != nil {
		log.Errorf("Error encountered posting request: %v", err)
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return body, err
	}

	var prettyJson bytes.Buffer
	_ = json.Indent(&prettyJson, body, "", "  ")
	log.WithField("", prettyJson.String()).Debug("request:", id, " method:", method, " params:", params)

	errresp := APIError{}
	json.Unmarshal([]byte(body), &errresp)
	if errresp.Error.Code != 0 {
		err = errors.New("Received error response from API request")
		return body, err
	}
	return body, nil
}

func (c *Client) MergeQoS(theType string, rQos string) ( QoS ) {
	var qos QoS
	if rQos != "" {
		iops := strings.Split(rQos, ",")
		qos.MinIOPS, _ = strconv.ParseInt(iops[0], 10, 64)
		qos.MaxIOPS, _ = strconv.ParseInt(iops[1], 10, 64)
		qos.BurstIOPS, _ = strconv.ParseInt(iops[2], 10, 64)
		log.Infof("Received qos Options and set QoS: %+v", qos)
	}

	if theType != "" {
		for _, t := range *c.VolumeTypes {
			if strings.EqualFold(t.Type, theType) {
				qos = t.QOS
				log.Infof("Received Type Options and set QoS: %+v", qos)
				break
			}
		}
	}
	return qos
}

func newReqID() int {
	return rand.Intn(1000-1) + 1
}

func NewReqID() int {
	return rand.Intn(1000-1) + 1
}
<commit_msg>Fix missing "strconv"<commit_after>package sfapi

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/alecthomas/units"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"time"
	"strconv"
)

type Client struct {
	SVIP              string
	Endpoint          string
	DefaultAPIPort    int
	DefaultVolSize    int64 //bytes
	DefaultAccountID  int64
	DefaultTenantName string
	VolumeTypes       *[]VolType
	Config            *Config
}

type Config struct {
	TenantName     string
	EndPoint       string
	DefaultVolSz   int64 //Default volume size in GiB
	MountPoint     string
	SVIP           string
	InitiatorIFace string //iface to use of iSCSI initiator
	Types          *[]VolType
}

type VolType struct {
	Type string
	QOS  QoS
}

var (
	endpoint          string
	svip              string
	configFile        string
	defaultTenantName string
	defaultSizeGiB    int64
	cfg               Config
)

func ProcessConfig(fname string) (Config, error) {
	content, err := ioutil.ReadFile(fname)
	if err != nil {
		log.Fatal("Error processing config file: ", err)
	}
	var conf Config
	err = json.Unmarshal(content, &conf)
	if err != nil {
		log.Fatal("Error parsing config file: ", err)
	}
	return conf, nil
}

func NewFromConfig(configFile string) (c *Client, err error) {
	conf, err := ProcessConfig(configFile)
	if err != nil {
		log.Fatal("Error initializing client from Config file: ", configFile, "(", err, ")")
	}
	cfg = conf
	endpoint = conf.EndPoint
	svip = conf.SVIP
	configFile = os.Getenv("SF_CONFIG_FILE")
	defaultSizeGiB = conf.DefaultVolSz
	defaultTenantName = conf.TenantName
	return New()
}

func NewFromOpts(ep string, dSize int64, storageIP string, acct string) (c *Client, err error) {
	endpoint = ep
	svip = storageIP
	defaultTenantName = acct
	defaultSizeGiB = dSize
	configFile = ""
	cfg = Config{}
	return New()
}

func New() (c *Client, err error) {
	rand.Seed(time.Now().UTC().UnixNano())
	defSize := defaultSizeGiB * int64(units.GiB)
	SFClient := &Client{
		Endpoint:          endpoint,
		DefaultVolSize:    defSize,
		SVIP:              svip,
		Config:            &cfg,
		DefaultAPIPort:    443,
		VolumeTypes:       cfg.Types,
		DefaultTenantName: defaultTenantName,
	}
	return SFClient, nil
}

func (c *Client) Request(method string, params interface{}, id int) (response []byte, err error) {
	log.Debug("Issue request to SolidFire Endpoint...")
	if c.Endpoint == "" {
		log.Error("Endpoint is not set, unable to issue requests")
		err = errors.New("Unable to issue json-rpc requests without specifying Endpoint")
		return nil, err
	}
	data, err := json.Marshal(map[string]interface{}{
		"method": method,
		"id":     id,
		"params": params,
	})

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}

	log.Debugf("POST request to: %+v", c.Endpoint)
	Http := &http.Client{Transport: tr}
	resp, err := Http.Post(c.Endpoint,
		"json-rpc",
		strings.NewReader(string(data)))
	if err != nil {
		log.Errorf("Error encountered posting request: %v", err)
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return body, err
	}

	var prettyJson bytes.Buffer
	_ = json.Indent(&prettyJson, body, "", "  ")
	log.WithField("", prettyJson.String()).Debug("request:", id, " method:", method, " params:", params)

	errresp := APIError{}
	json.Unmarshal([]byte(body), &errresp)
	if errresp.Error.Code != 0 {
		err = errors.New("Received error response from API request")
		return body, err
	}
	return body, nil
}

func (c *Client) MergeQoS(theType string, rQos string) ( QoS ) {
	var qos QoS
	if rQos != "" {
		iops := strings.Split(rQos, ",")
		qos.MinIOPS, _ = strconv.ParseInt(iops[0], 10, 64)
		qos.MaxIOPS, _ = strconv.ParseInt(iops[1], 10, 64)
		qos.BurstIOPS, _ = strconv.ParseInt(iops[2], 10, 64)
		log.Infof("Received qos Options and set QoS: %+v", qos)
	}

	if theType != "" {
		for _, t := range *c.VolumeTypes {
			if strings.EqualFold(t.Type, theType) {
				qos = t.QOS
				log.Infof("Received Type Options and set QoS: %+v", qos)
				break
			}
		}
	}
	return qos
}

func newReqID() int {
	return rand.Intn(1000-1) + 1
}

func NewReqID() int {
	return rand.Intn(1000-1) + 1
}
<|endoftext|>


<commit_before>package healthcheck

import (
	"fmt"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/rancherio/go-rancher/client"
	"github.com/rancherio/host-api/pkg/haproxy"
	"github.com/rancherio/host-api/util"
)

var PREFIX = "cattle-"
var SERVER_NAME = "svname"
var STATUS = "status"
var LOOPS = (60 * 60) / 2

func Poll() error {
	client, err := util.GetRancherClient()
	if err != nil {
		return err
	}
	if client == nil {
		return fmt.Errorf("Can not create RancherClient, No credentials found")
	}

	m := &Monitor{
		client:         client,
		reportedStatus: map[string]string{},
	}

	for stat := range m.getStats() {
		m.processStat(stat)
	}

	return nil
}

type Monitor struct {
	client         *client.RancherClient
	reportedStatus map[string]string
}

func (m *Monitor) getStats() <-chan haproxy.Stat {
	c := make(chan haproxy.Stat)
	go m.readStats(c)
	return c
}

func (m *Monitor) readStats(c chan<- haproxy.Stat) {
	defer close(c)

	count := 0

	h := &haproxy.Monitor{
		SocketPath: haproxy.HAPROXY_SOCK,
	}

	for i := 0; i < LOOPS; i++ {
		// Sleep up front.  This way if this program gets restarted really fast we don't spam cattle
		time.Sleep(2 * time.Second)

		stats, err := h.Stats()
		currentCount := 0
		if err != nil {
			logrus.Errorf("Failed to read stats: %v", err)
			continue
		}

		for _, stat := range stats {
			if strings.HasPrefix(stat[SERVER_NAME], PREFIX) {
				currentCount++
				c <- stat
			}
		}

		if currentCount != count {
			count = currentCount
			logrus.Infof("Monitoring %d backends", count)
		}

	}
}

func (m *Monitor) processStat(stat haproxy.Stat) {
	serverName := strings.TrimPrefix(stat[SERVER_NAME], PREFIX)
	currentStatus := stat[STATUS]

	previousStatus := m.reportedStatus[serverName]
	if strings.HasPrefix(currentStatus, "UP ") {
		// do nothing on partial UP
		return
	}

	if currentStatus == "UP" && previousStatus != "UP" && previousStatus != "INIT" {
		currentStatus = "INIT"
	}

	if previousStatus != currentStatus {
		err := m.reportStatus(serverName, currentStatus)
		if err != nil {
			logrus.Errorf("Failed to report status %s=%s: %v", serverName, currentStatus, err)
		} else {
			m.reportedStatus[serverName] = currentStatus
		}
	}
}

func (m *Monitor) reportStatus(serverName, currentStatus string) error {
	_, err := m.client.ServiceEvent.Create(&client.ServiceEvent{
		HealthcheckUuid:   serverName,
		ReportedHealth:    currentStatus,
		ExternalTimestamp: time.Now().Unix(),
	})

	if err != nil {
		return err
	}

	logrus.Infof("%s=%s", serverName, currentStatus)
	return nil
}
<commit_msg>Use cache with expiration time for the values for storing haproxy stats<commit_after>package healthcheck

import (
	"fmt"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/patrickmn/go-cache"
	"github.com/rancherio/go-rancher/client"
	"github.com/rancherio/host-api/pkg/haproxy"
	"github.com/rancherio/host-api/util"
)

var PREFIX = "cattle-"
var SERVER_NAME = "svname"
var STATUS = "status"

func Poll() error {
	client, err := util.GetRancherClient()
	if err != nil {
		return err
	}
	if client == nil {
		return fmt.Errorf("Can not create RancherClient, No credentials found")
	}
	c := cache.New(1*time.Hour, 1*time.Minute)
	m := &Monitor{
		client:         client,
		reportedStatus: c,
	}

	for stat := range m.getStats() {
		m.processStat(stat)
	}

	return nil
}

type Monitor struct {
	client         *client.RancherClient
	reportedStatus *cache.Cache
}

func (m *Monitor) getStats() <-chan haproxy.Stat {
	c := make(chan haproxy.Stat)
	go m.readStats(c)
	return c
}

func (m *Monitor) readStats(c chan<- haproxy.Stat) {
	defer close(c)

	count := 0

	h := &haproxy.Monitor{
		SocketPath: haproxy.HAPROXY_SOCK,
	}

	for {
		// Sleep up front.  This way if this program gets restarted really fast we don't spam cattle
		time.Sleep(2 * time.Second)

		stats, err := h.Stats()
		currentCount := 0
		if err != nil {
			logrus.Errorf("Failed to read stats: %v", err)
			continue
		}

		for _, stat := range stats {
			if strings.HasPrefix(stat[SERVER_NAME], PREFIX) {
				currentCount++
				c <- stat
			}
		}

		if currentCount != count {
			count = currentCount
			logrus.Infof("Monitoring %d backends", count)
		}

	}
}

func (m *Monitor) processStat(stat haproxy.Stat) {
	serverName := strings.TrimPrefix(stat[SERVER_NAME], PREFIX)
	currentStatus := stat[STATUS]

	previousStatus, _ := m.reportedStatus.Get(serverName)
	if strings.HasPrefix(currentStatus, "UP ") {
		// do nothing on partial UP
		return
	}

	if currentStatus == "UP" && previousStatus != "UP" && previousStatus != "INIT" {
		currentStatus = "INIT"
	}

	update := true
	if previousStatus != currentStatus {
		err := m.reportStatus(serverName, currentStatus)
		if err != nil {
			logrus.Errorf("Failed to report status %s=%s: %v", serverName, currentStatus, err)
			update = false
		}
	}

	if update {
		m.reportedStatus.Set(serverName, currentStatus, cache.DefaultExpiration)
	}
}

func (m *Monitor) reportStatus(serverName, currentStatus string) error {
	_, err := m.client.ServiceEvent.Create(&client.ServiceEvent{
		HealthcheckUuid:   serverName,
		ReportedHealth:    currentStatus,
		ExternalTimestamp: time.Now().Unix(),
	})

	if err != nil {
		return err
	}

	logrus.Infof("%s=%s", serverName, currentStatus)
	return nil
}
<|endoftext|>


<commit_before>package mmm

import (
	"testing"
	"unsafe"
)

// -----------------------------------------------------------------------------

func TestSize_SizeOf_bool(t *testing.T) {
	var v bool
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for bool")
	}
}

func TestSize_SizeOf_int(t *testing.T) {
	var v int
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int")
	}
}

func TestSize_SizeOf_int8(t *testing.T) {
	var v int8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int8")
	}
}

func TestSize_SizeOf_int16(t *testing.T) {
	var v int16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int16")
	}
}

func TestSize_SizeOf_int32(t *testing.T) {
	var v int32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int32")
	}
}

func TestSize_SizeOf_int64(t *testing.T) {
	var v int64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int64")
	}
}

func TestSize_SizeOf_uint(t *testing.T) {
	var v uint
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint")
	}
}

//var ui uint
//var ui8 uint8
//var ui16 uint16
//var ui32 uint32
//var ui64 uint64
//var uiptr uintptr
//var f32 float32
//var f64 float64
//var c64 complex64
//var c128 complex128
//var iarr [42]int
//var ichan chan int
//var itf interface{}
//var imap map[int]int
//var iptr *int
//var islice []int
//var str string
//var srt struct{}
<commit_msg>added TestSize_SizeOf_uint8<commit_after>package mmm

import (
	"testing"
	"unsafe"
)

// -----------------------------------------------------------------------------

func TestSize_SizeOf_bool(t *testing.T) {
	var v bool
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for bool")
	}
}

func TestSize_SizeOf_int(t *testing.T) {
	var v int
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int")
	}
}

func TestSize_SizeOf_int8(t *testing.T) {
	var v int8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int8")
	}
}

func TestSize_SizeOf_int16(t *testing.T) {
	var v int16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int16")
	}
}

func TestSize_SizeOf_int32(t *testing.T) {
	var v int32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int32")
	}
}

func TestSize_SizeOf_int64(t *testing.T) {
	var v int64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int64")
	}
}

func TestSize_SizeOf_uint(t *testing.T) {
	var v uint
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint")
	}
}

func TestSize_SizeOf_uint8(t *testing.T) {
	var v uint8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint8")
	}
}

//var ui16 uint16
//var ui32 uint32
//var ui64 uint64
//var uiptr uintptr
//var f32 float32
//var f64 float64
//var c64 complex64
//var c128 complex128
//var iarr [42]int
//var ichan chan int
//var itf interface{}
//var imap map[int]int
//var iptr *int
//var islice []int
//var str string
//var srt struct{}
<|endoftext|>


<commit_before>// generated by stringer -type FileFormat; DO NOT EDIT

package hydra

import "fmt"

const _FileFormat_name = "NoneLTSVJSON"

var _FileFormat_index = [...]uint8{4, 8, 12}

func (i FileFormat) String() string {
	if i < 0 || i >= FileFormat(len(_FileFormat_index)) {
		return fmt.Sprintf("FileFormat(%d)", i)
	}
	hi := _FileFormat_index[i]
	lo := uint8(0)
	if i > 0 {
		lo = _FileFormat_index[i-1]
	}
	return _FileFormat_name[lo:hi]
}
<commit_msg>update fileformat_string.go<commit_after>// generated by stringer -type FileFormat; DO NOT EDIT

package hydra

import "fmt"

const _FileFormat_name = "NoneLTSVJSON"

var _FileFormat_index = [...]uint8{0, 4, 8, 12}

func (i FileFormat) String() string {
	if i < 0 || i >= FileFormat(len(_FileFormat_index)-1) {
		return fmt.Sprintf("FileFormat(%d)", i)
	}
	return _FileFormat_name[_FileFormat_index[i]:_FileFormat_index[i+1]]
}
<|endoftext|>


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

import (
	"flag"
	"fmt"
	kafka "github.com/jamiealquiza/sangrenel/vendor/github.com/Shopify/sarama"
	"math/rand"
	"os"
	"os/signal"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"time"
)

var (
	// Control chans.
	sig_chan        = make(chan os.Signal)
	clientKill_chan = make(chan bool, 24)
	// Init config vars
	brokers       []string
	topic         *string
	msgSize       *int
	msgRate       *int64
	clientWorkers *int
	noop          *bool
	// Character selection from which random messages are generated.
	chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}][:<>.")
	// Counters / misc.
	sentCntr      = make(chan int64, 1)
	latency       []float64
	latency_chan  = make(chan float64, 1)
	resetLat_chan = make(chan bool, 1)
)

func init() {
	flag_brokers := flag.String("brokers", "localhost:9092", "Comma delimited list of Kafka brokers")
	topic = flag.String("topic", "sangrenel", "Topic to publish to")
	msgSize = flag.Int("size", 300, "Message size in bytes")
	msgRate = flag.Int64("rate", 100000000, "Apply a global message rate limit")
	noop = flag.Bool("noop", false, "Test message generation performance, do not transmit messages")
	clientWorkers = flag.Int("workers", 1, "Number of Kafka client workers")
	flag.Parse()

	brokers = strings.Split(*flag_brokers, ",")
	// Init sent count at 0
	sentCntr <- 0
	runtime.GOMAXPROCS(runtime.NumCPU())
}

// Returns a random message generated from
// the 'chars' rune set, of 'm' length in bytes as defined
// by ''*msgSize'.
func randMsg(m []rune, generator *rand.Rand) string {
	for i := range m {
		m[i] = chars[generator.Intn(len(chars))]
	}
	return string(m)
}

// Thread-safe global counter function.
func incrSent() {
	i := <-sentCntr
	sentCntr <- i + 1
}

// Fetch counter val.
func fetchSent() int64 {
	i := <-sentCntr
	sentCntr <- i
	return i
}

func genMsgs() {
	// Instantiate 'rand' per producer to avoid
	// mutex contention on a globally shared object.
	source := rand.NewSource(time.Now().UnixNano())
	generator := rand.New(source)
	// 'msg' object reuse
	msg := make([]rune, *msgSize)
	for {
		randMsg(msg, generator)
		incrSent()
	}
}

// A producer instance of a parent Kafka client connection.
func sendWorker(c kafka.Client) {
	producer, err := kafka.NewSimpleProducer(&c, nil)
	if err != nil {
		fmt.Println(err.Error())
	}
	defer producer.Close()

	// Instantiate 'rand' per producer to avoid
	// mutex contention of a globally shared object.
	source := rand.NewSource(time.Now().UnixNano())
	generator := rand.New(source)
	// 'msg' object reuse.
	msg := make([]rune, *msgSize)

	for {
		// Message rate limit works by having
		// all producer worker loops incrementing
		// a global counter and tracking
		// the aggregate per-second progress.
		// If the configured rate is met, the worker will sleep
		// for the remainder of the 1 second window.
		rateStart := time.Now().Add(time.Second)
		countStart := fetchSent()
		// 'start' is a time marker to track ack latency.
		var start time.Time
		for fetchSent()-countStart < *msgRate {
			// Gen a random message first,
			data := randMsg(msg, generator)
			// then start the latency clock to ensure
			// transmit -> broker ack time is metered.
			start = time.Now()
			err = producer.SendMessage(*topic, nil, kafka.StringEncoder(data))
			if err != nil {
				fmt.Println(err)
			} else {
				// Increment global sent count and fire off
				// time delta since 'start' into the latency chan.
				incrSent()
				latency_chan <- time.Since(start).Seconds() * 1000
			}
		}
		// If the global per-second rate limit was met, the inner loop
		// breaks and the outer loop sleeps for the second remainder.
		time.Sleep(rateStart.Sub(time.Now()) + time.Since(start))
	}
}

// A connection to a Kafka cluster, manages 5
// 'sendworker()' (producer instances); fixed value since
// these tend to flatline throughput at ~5 producers.
func createClient(n int) {
	switch *noop {
	case false:
		cId := "client_" + strconv.Itoa(n)
		client, err := kafka.NewClient(cId, brokers, kafka.NewClientConfig())
		if err != nil {
			panic(err)
		} else {
			fmt.Printf("%s connected\n", cId)
		}
		for i := 0; i < 5; i++ {
			go sendWorker(*client)
		}
	default:
		for i := 0; i < 5; i++ {
			go genMsgs()
		}
	}
	<-clientKill_chan
}

// Calculates raw message output in
// networking friendly units; gives an idea of
// minimum network traffic being generated.
func calcOutput(n int64) string {
	m := (float64(n) / 5) * float64(*msgSize)
	var o string
	switch {
	case m >= 131072:
		o = strconv.FormatFloat(m/131072, 'f', 0, 64) + "Mb/sec"
	case m < 131072:
		o = strconv.FormatFloat(m/1024, 'f', 0, 64) + "KB/sec"
	}
	return o
}

// Thread-safe receiver for latency values
// captured by all producer goroutines.
// May want to do something smart about this
// (e.g. sampling) to limit the time-complexity on
// sorting huge slices in high-throughput configurations.
func latencyAggregator() {
	for {
		select {
		case i := <-latency_chan:
			latency = append(latency, i)
		case <-resetLat_chan:
			latency = latency[:0]
		}
	}
}

// Fetches & resets current latencies set
// populated by 'latencyAggregator()'.
// Sorts then averages the 90th percentile
// worse latencies.
func calcLatency() float64 {
	var avg float64
	switch *noop {
	case true:
		break
	default:
		lat := latency
		resetLat_chan <- true
		sort.Float64s(lat)
		var sum float64
		topn := int(float64(len(lat)) * 0.90)
		for i := topn; i < len(lat); i++ {
			sum += lat[i]
		}
		avg = sum / float64(len(lat)-topn)
	}
	return avg
}

func main() {
	// Listens for signals.
	signal.Notify(sig_chan, syscall.SIGINT, syscall.SIGTERM)
	// Fires up 'latencyAggregator()' to background.
	go latencyAggregator()

	// Warns you stuff is happening.
	fmt.Printf("\n::: Sangrenel :::\nStarting %s workers\nMessage size %s bytes\n\n",
		strconv.Itoa(*clientWorkers),
		strconv.Itoa(*msgSize))
	// Fires up clients.
	for i := 0; i < *clientWorkers; i++ {
		go createClient(i + 1)
	}

	// Info output ticker.
	tick := time.Tick(5 * time.Second)
	// Markers for tracking message rates.
	var currCnt, lastCnt int64
	for {
		select {
		case <-tick:
			lastCnt = currCnt
			currCnt = fetchSent()
			deltaCnt := currCnt - lastCnt
			fmt.Printf("%s Generating %s @ %d messages/sec | topic: %s | %.2fms avg latency\n",
				// Always be RFC'ing
				time.Now().Format(time.RFC3339),
				calcOutput(deltaCnt),
				deltaCnt/5,
				*topic,
				// Well, this technically appends a
				// latency to the 5s interval.
				calcLatency())
		// The Chuck Norris of signals; it doesn't sleep, it waits.
		case <-sig_chan:
			fmt.Println("Killing Connections")
			for i := 0; i < *clientWorkers; i++ {
				clientKill_chan <- true
			}
			close(clientKill_chan)
			time.Sleep(2 * time.Second)
			os.Exit(0)
		}
	}
}
<commit_msg>cleanup, comments<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 Jamie Alquiza
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package main

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"time"

	kafka "github.com/jamiealquiza/sangrenel/vendor/github.com/Shopify/sarama"
)

var (
	// Configs.
	brokers       []string
	topic         string
	msgSize       int
	msgRate       int64
	clientWorkers int
	noop          bool

	// Character selection from which random messages are generated.
	chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}][:<>.")

	// Counters / misc.
	sig_chan        = make(chan os.Signal)
	clientKill_chan = make(chan bool, 24)
	sentCntr        = make(chan int64, 1)
	latency         []float64
	latency_chan    = make(chan float64, 1)
	resetLat_chan   = make(chan bool, 1)
)

func init() {
	flag.StringVar(&topic, "topic", "sangrenel", "Topic to publish to")
	flag.IntVar(&msgSize, "size", 300, "Message size in bytes")
	flag.Int64Var(&msgRate, "rate", 100000000, "Apply a global message rate limit")
	flag.BoolVar(&noop, "noop", false, "Test message generation performance, do not transmit messages")
	flag.IntVar(&clientWorkers, "workers", 1, "Number of Kafka client workers")
	brokerString := flag.String("brokers", "localhost:9092", "Comma delimited list of Kafka brokers")
	flag.Parse()

	brokers = strings.Split(*brokerString, ",")

	sentCntr <- 0
	runtime.GOMAXPROCS(runtime.NumCPU())
}

// clientWorker generates random messages and writes to Kafka.
// Workers track and limit message rates using incrSent() and fetchSent().
// 5 instances of clientWorker are created under each Kafka client.
func clientWorker(c kafka.Client) {
	producer, err := kafka.NewSimpleProducer(&c, nil)
	if err != nil {
		fmt.Println(err.Error())
	}
	defer producer.Close()

	// Instantiate rand per producer to avoid mutex contention.
	source := rand.NewSource(time.Now().UnixNano())
	generator := rand.New(source)
	// Create msg object once, reuse.
	msg := make([]rune, msgSize)

	for {
		// Message rate limit works by having all clientWorker loops incrementing
		// a global counter and tracking the aggregate per-second progress.
		// If the configured rate is met, the worker will sleep
		// for the remainder of the 1 second window.
		rateEnd := time.Now().Add(time.Second)
		countStart := fetchSent()
		var start time.Time
		for fetchSent()-countStart < msgRate {
			data := randMsg(msg, *generator)
			// We start timing after the message is created.
			// This ensures latency metering from the time between message sent and receiving an ack.
			start = time.Now()
			err = producer.SendMessage(topic, nil, kafka.StringEncoder(data))
			if err != nil {
				fmt.Println(err)
			} else {
				// Increment global sent count and fire off time since start value into the latency channel.
				incrSent()
				latency_chan <- time.Since(start).Seconds() * 1000
			}
		}
		// If the global per-second rate limit was met,
		// the inner loop breaks and the outer loop sleeps for the second remainder.
		time.Sleep(rateEnd.Sub(time.Now()) + time.Since(start))
	}
}

// kafkaClient initializes a connection to a Kafka cluster.
// Each client manages 5 clientWorker() (producer instances).
// Fixed value since this seem to flatline throughput at ~5.
func kafkaClient(n int) {
	switch noop {
	// If not noop, actually fire up Kafka connections and send messages.
	case false:
		cId := "client_" + strconv.Itoa(n)
		client, err := kafka.NewClient(cId, brokers, kafka.NewClientConfig())
		if err != nil {
			panic(err)
		} else {
			fmt.Printf("%s connected\n", cId)
		}
		for i := 0; i < 5; i++ {
			go clientWorker(*client)
		}
	// If noop, we're not creating connections at all.
	// Just generate messages and burn CPU.
	default:
		for i := 0; i < 5; i++ {
			go genMsgs()
		}
	}
	<-clientKill_chan
}

// genMsgs is a dummy function that kafkaClient calls if noop is True.
// It is used in place of starting actual Kafka client connections to test message creation performance.
func genMsgs() {
	// Instantiate 'rand' per producer to avoid mutex contention.
	source := rand.NewSource(time.Now().UnixNano())
	generator := rand.New(source)
	// Define msg objet once, reuse.
	msg := make([]rune, msgSize)
	for {
		// Generate a random message and increment the global counter.
		randMsg(msg, *generator)
		incrSent()
	}
}

// Returns a random message generated from the chars rune slice.
// Message length of m bytes as defined by msgSize.
func randMsg(m []rune, generator rand.Rand) string {
	for i := range m {
		m[i] = chars[generator.Intn(len(chars))]
	}
	return string(m)
}

// Thread-safe global counter functions.
func incrSent() {
	i := <-sentCntr
	sentCntr <- i + 1
}
func fetchSent() int64 {
	i := <-sentCntr
	sentCntr <- i
	return i
}

// Thread-safe receiver for latency values captured by all producer goroutines.
// May want to do something smart about this to limit time to sort
// huge slices in high-throughput configurations where lots of latency values are received.
func latencyAggregator() {
	for {
		select {
		case i := <-latency_chan:
			latency = append(latency, i)
		case <-resetLat_chan:
			latency = latency[:0]
		}
	}
}

// Calculates aggregate raw message output in networking friendly units.
// Gives an idea of minimum network traffic being generated.
func calcOutput(n int64) string {
	m := (float64(n) / 5) * float64(msgSize)
	var o string
	switch {
	case m >= 131072:
		o = strconv.FormatFloat(m/131072, 'f', 0, 64) + "Mb/sec"
	case m < 131072:
		o = strconv.FormatFloat(m/1024, 'f', 0, 64) + "KB/sec"
	}
	return o
}

// Fetches & resets current latencies set held by 'latencyAggregator()'.
// Sorts then averages the 90th percentile worse latencies.
func calcLatency() float64 {
	var avg float64
	// With 'noop', we don't have latencies to operate on.
	switch noop {
	case true:
		break
	default:
		// Fetch values.
		lat := latency
		// Issue the current values to be cleared.
		resetLat_chan <- true
		// Sort and sum values.
		sort.Float64s(lat)
		var sum float64
		// Get percentile count and values, sum values.
		topn := int(float64(len(lat)) * 0.90)
		for i := topn; i < len(lat); i++ {
			sum += lat[i]
		}
		// Calc average.
		avg = sum / float64(len(lat)-topn)
	}
	return avg
}

func main() {
	// Listens for signals.
	signal.Notify(sig_chan, syscall.SIGINT, syscall.SIGTERM)
	// Fires up 'latencyAggregator()' to background.
	go latencyAggregator()

	// Print Sangrenel startup info.
	fmt.Printf("\n::: Sangrenel :::\nStarting %s workers\nMessage size %s bytes\n\n",
		strconv.Itoa(clientWorkers),
		strconv.Itoa(msgSize))

	// Start client workers.
	for i := 0; i < clientWorkers; i++ {
		go kafkaClient(i + 1)
	}

	// Start Sangrenel periodic info output.
	tick := time.Tick(5 * time.Second)
	// Count mile-markers for tracking message rates.
	var currCnt, lastCnt int64
	for {
		select {
		case <-tick:
			// Set last and current to last read sent count.
			lastCnt = currCnt
			// Get actual current sent count, then delta from last count.
			// Delta is divided by update interval (5s) for per-second rate over output updates.
			currCnt = fetchSent()
			deltaCnt := currCnt - lastCnt
			fmt.Printf("%s Generating %s @ %d messages/sec | topic: %s | %.2fms avg latency\n",
				// Always be RFC'ing.
				time.Now().Format(time.RFC3339),
				calcOutput(deltaCnt),
				deltaCnt/5,
				topic,
				// Well, this technically appends a small latency to the 5s interval.
				calcLatency())
		// Waits for signals. Currently just brutally kills Sangrenel.
		case <-sig_chan:
			fmt.Println("Killing Connections")
			for i := 0; i < clientWorkers; i++ {
				clientKill_chan <- true
			}
			close(clientKill_chan)
			time.Sleep(2 * time.Second)
			os.Exit(0)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
    "database/sql"
    "github.com/coopernurse/gorp"
    "github.com/Narsil/xmpp"
    _ "github.com/mattn/go-sqlite3"
    "log"
    "io"
    "time"
    "net/http"
    "encoding/json"
    "fmt"
    "strings"
    "html"
    "html/template"
)

type Message struct {
    Id      int64
    Created int64
    Message    string
    From         string
    To    string
    Sent    bool
    Incoming bool
}

func authenticate(username, password string) bool{
    return true
}

func main(){
    dbmap := initDb()
    defer dbmap.Db.Close()

    // create two posts
    // dbmap.TruncateTables()
    // p1 := newMessage("+33672317534", "Hey from go")

    // // // insert rows - auto increment PKs will be set properly after the insert
    // dbmap.Insert(&p1)

    http.HandleFunc("/pending/", func(w http.ResponseWriter, r *http.Request) {
        var messages []Message
        _, err := dbmap.Select(&messages, "SELECT * FROM messages WHERE Sent=? AND Incoming=?", false, false)
        checkErr(err, "Select failed")

        str, err := json.Marshal(messages)
        checkErr(err, "Marshalling failed")

        fmt.Fprintf(w, "%s", str)
    })
    http.HandleFunc("/send/", func(w http.ResponseWriter, r *http.Request) {
        err := r.ParseForm()
        checkErr(err, "Parsing form failed")

        if (r.Form.Get("To") != "" && r.Form.Get("Message") != ""){
            message := newMessage(r.Form.Get("To"), r.Form.Get("Message"))
            err = dbmap.Insert(&message)
            checkErr(err, "Could not insert new message")
            http.Redirect(w, r, "/send/", 302)
            return
        }

        var messages []Message
        _, err = dbmap.Select(&messages, "SELECT * FROM messages ORDER BY Created DESC")
        checkErr(err, "Select failed")

        tmpl, _ := template.New("send").Parse(`

                    <form action="." method="POST">
                        <input type="text" name="To" placeholder="To"/>
                        <input type="text" name="Message" placeholder="Message"/>
                        <input type="submit" value="Envoyer"/>
                    </form>
                    {{range .Messages}}

                            From: {{.From}} to {{.To}} {{.Message}}

                                Sent: {{.Sent}}
                                Created: {{.Created}}
                                Incoming: {{.Incoming}}

                    {{end}}

        `)
        tmpl.Execute(w, map[string]interface{} {
            "Messages": messages,
        })

    })
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        err := r.ParseForm()
        checkErr(err, "Parsing form failed")

        if (r.Form.Get("To") != "" && r.Form.Get("Message") != ""){
            message := newMessage(r.Form.Get("To"), r.Form.Get("Message"))
            err = dbmap.Insert(&message)
            checkErr(err, "Could not insert new message")
            http.Redirect(w, r, "/", 302)
            return
        }

        tmpl, _ := template.New("homepage").Parse(`
            <!DOCTYPE html>

                    <link href="/static/bootstrap/css/bootstrap.min.css" type="text/css" rel="stylesheet">
                    <link href="/static/bootstrap/css/bootstrap-responsive.min.css" type="text/css" rel="stylesheet">

                    <meta name="viewport" content="width=device-width, initial-scale=1.0">

        `)
        tmpl.Execute(w, nil)

    })
    http.HandleFunc("/history/", func(w http.ResponseWriter, r *http.Request) {
        var messages []Message
        _, err := dbmap.Select(&messages, "SELECT * FROM messages ORDER BY Created DESC")
        checkErr(err, "Select failed")
        b, err := json.Marshal(messages)
        checkErr(err, "Encoding failed")
        fmt.Fprintf(w, "%s", b)
    })
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
    http.HandleFunc("/sent/", func(w http.ResponseWriter, r *http.Request) {
        err := r.ParseForm()
        checkErr(err, "Parsing form failed")

        ids_string := r.Form.Get("Id")

        ids := strings.Split(ids_string, ",")
        new_ids_string := "\"" + strings.Join(ids, "\", \"") + "\""

        query := fmt.Sprintf("UPDATE messages SET Sent='true' WHERE Id IN (%s)", new_ids_string)

        _, err = dbmap.Exec(query)

        if err == nil{
            fmt.Fprintf(w, "Ok")
        }else{
            fmt.Fprintf(w, "Error", err)
        }

    })
    srv := xmpp.NewServer()
    http.HandleFunc("/received/", func(w http.ResponseWriter, r *http.Request) {
        err := r.ParseForm()
        checkErr(err, "Parsing form failed")

        from := r.Form.Get("From")
        message := r.Form.Get("Message")

        if (from != "" && message != ""){
            m := newIncomingMessage(from, message, srv)
            err = dbmap.Insert(&m)
        }
        if err == nil{
            fmt.Fprintf(w, "Ok")
        }else{
            fmt.Fprintf(w, "Error", err)
        }

    })
    err := srv.SetKeyPair("private.cert", "private.key")
    if (err != nil){
    }
    srv.SetAuthFunc(authenticate)
    srv.SetHandleIncomingMessage(
        func (msg xmpp.XmppMessage){
            for _, body := range(msg.Bodies){
                number := strings.Split(msg.To, "@")[0]
                message := newMessage(number, html.UnescapeString(body.Body))
                err := dbmap.Insert(&message)
                checkErr(err, "Could not insert new message")
            }
    })
    srv.HandleQuery("http://jabber.org/protocol/disco#info", func (w io.Writer, iq xmpp.IQ){
            w.Write([]byte(`<iq type='result' from='sms.nicolas.kwyk.fr' to='nicolas@kwyk.fr/YY9R80gi' id='` + iq.Id + `'>
                        <query xmlns='http://jabber.org/protocol/disco#info'>
                            <feature var='http://jabber.org/protocol/disco#info'/>
                            <feature var='http://jabber.org/protocol/disco#items'/>
                        </query>
                    </iq>
            `))
    })
    srv.HandleQuery("http://jabber.org/protocol/disco#items", func (w io.Writer, iq xmpp.IQ){
        w.Write([]byte(`<iq type='result' from='sms.nicolas.kwyk.fr' to='nicolas@kwyk.fr/YY9R80gi' id='` + iq.Id + `'>
                <error code="501" type="cancel"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>
                </iq>
        `))
    })
    srv.HandleQuery("jabber:iq:roster", func(w io.Writer, iq xmpp.IQ){
            w.Write([]byte(`<iq type='result' to='nicolas@kwyk.fr/YY9R80gi' id='` + iq.Id + `'>
                    <query xmlns="jabber:iq:roster">
                        <item jid="+33609781500@sms.nicolas.kwyk.fr" subscription="both" name="Julie Famery Phone"><group>Buddies</group></item>
                        <item jid="+33672317534@sms.nicolas.kwyk.fr" subscription="both" name="Nicolas Patry Phone"><group>Buddies</group></item>
                    </query>
                </iq>
            `))
            w.Write([]byte(`<presence from="+33609781500@sms.nicolas.kwyk.fr" to="nicolas@kwyk.fr/YY9R80gi"><caps:c node="http://www.android.com/gtalk/client/caps" ver="1.1" xmlns:caps="http://jabber.org/protocol/caps"/></presence>`))
            w.Write([]byte(`<presence from="+33672317534@sms.nicolas.kwyk.fr" to="nicolas@kwyk.fr/YY9R80gi"><caps:c node="http://www.android.com/gtalk/client/caps" ver="1.1" xmlns:caps="http://jabber.org/protocol/caps"/></presence>`))
    })

    go func(){
        srv.ListenAndServe("tcp", ":5222")
    }()
    log.Fatal(http.ListenAndServe(":8080", nil))

}

func newMessage(destination, message string) Message {
    fmt.Println("Sending SMS to: ", destination)
    return Message{
        Created: time.Now().UnixNano(),
        From: "+33672317534",
        Message:   message,
        To:    destination,
        Sent: false,
        Incoming: false,
    }
}
func newIncomingMessage(author, message string, srv xmpp.Server) Message {
    fmt.Println("Received SMS from: ", author)
    go func(){
        srv.MessageChannel<-xmpp.Message{From: author, Message:message, To: "nicolas"}
    }()

    return Message{
        Created: time.Now().UnixNano(),
        From: author,
        Message:   message,
        To:    "+33672317534",
        Sent: true,
        Incoming: true,
    }
}

func initDb() *gorp.DbMap {
    // connect to db using standard Go database/sql API
    // use whatever database/sql driver you wish
    db, err := sql.Open("sqlite3", "db.bin")
    checkErr(err, "sql.Open failed")

    // construct a gorp DbMap
    dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}

    // add a table, setting the table name to 'posts' and
    // specifying that the Id property is an auto incrementing PK
    dbmap.AddTableWithName(Message{}, "messages").SetKeys(true, "Id")

    // create the table. in a production system you'd generally
    // use a migration tool, or create the tables via scripts
    err = dbmap.CreateTablesIfNotExists()
    checkErr(err, "Create tables failed")

    return dbmap
}

func checkErr(err error, msg string) {
    if err != nil {
        log.Fatalln(msg, err)
    }
}

<commit_msg>Using the new xmpp dependency.<commit_after>package main

import (
    "database/sql"
    "github.com/coopernurse/gorp"
    "github.com/Narsil/xmpp"
    _ "github.com/mattn/go-sqlite3"
    "log"
    "io"
    "time"
    "net/http"
    "encoding/json"
    "fmt"
    "strings"
    "html"
    "html/template"
)

type Message struct {
    Id      int64
    Created int64
    Message    string
    From         string
    To    string
    Sent    bool
    Incoming bool
}

func authenticate(username, password string) bool{
    return true
}

func main(){
    dbmap := initDb()
    defer dbmap.Db.Close()

    // create two posts
    // dbmap.TruncateTables()
    // p1 := newMessage("+33672317534", "Hey from go")

    // // // insert rows - auto increment PKs will be set properly after the insert
    // dbmap.Insert(&p1)

    http.HandleFunc("/pending/", func(w http.ResponseWriter, r *http.Request) {
        var messages []Message
        _, err := dbmap.Select(&messages, "SELECT * FROM messages WHERE Sent=? AND Incoming=?", false, false)
        checkErr(err, "Select failed")

        str, err := json.Marshal(messages)
        checkErr(err, "Marshalling failed")

        fmt.Fprintf(w, "%s", str)
    })
    http.HandleFunc("/send/", func(w http.ResponseWriter, r *http.Request) {
        err := r.ParseForm()
        checkErr(err, "Parsing form failed")

        if (r.Form.Get("To") != "" && r.Form.Get("Message") != ""){
            message := newMessage(r.Form.Get("To"), r.Form.Get("Message"))
            err = dbmap.Insert(&message)
            checkErr(err, "Could not insert new message")
            http.Redirect(w, r, "/send/", 302)
            return
        }

        var messages []Message
        _, err = dbmap.Select(&messages, "SELECT * FROM messages ORDER BY Created DESC")
        checkErr(err, "Select failed")

        tmpl, _ := template.New("send").Parse(`

                    <form action="." method="POST">
                        <input type="text" name="To" placeholder="To"/>
                        <input type="text" name="Message" placeholder="Message"/>
                        <input type="submit" value="Envoyer"/>
                    </form>
                    {{range .Messages}}

                            From: {{.From}} to {{.To}} {{.Message}}

                                Sent: {{.Sent}}
                                Created: {{.Created}}
                                Incoming: {{.Incoming}}

                    {{end}}

        `)
        tmpl.Execute(w, map[string]interface{} {
            "Messages": messages,
        })

    })
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        err := r.ParseForm()
        checkErr(err, "Parsing form failed")

        if (r.Form.Get("To") != "" && r.Form.Get("Message") != ""){
            message := newMessage(r.Form.Get("To"), r.Form.Get("Message"))
            err = dbmap.Insert(&message)
            checkErr(err, "Could not insert new message")
            http.Redirect(w, r, "/", 302)
            return
        }

        tmpl, _ := template.New("homepage").Parse(`
            <!DOCTYPE html>

                    <link href="/static/bootstrap/css/bootstrap.min.css" type="text/css" rel="stylesheet">
                    <link href="/static/bootstrap/css/bootstrap-responsive.min.css" type="text/css" rel="stylesheet">

                    <meta name="viewport" content="width=device-width, initial-scale=1.0">

        `)
        tmpl.Execute(w, nil)

    })
    http.HandleFunc("/history/", func(w http.ResponseWriter, r *http.Request) {
        var messages []Message
        _, err := dbmap.Select(&messages, "SELECT * FROM messages ORDER BY Created DESC")
        checkErr(err, "Select failed")
        b, err := json.Marshal(messages)
        checkErr(err, "Encoding failed")
        fmt.Fprintf(w, "%s", b)
    })
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
    http.HandleFunc("/sent/", func(w http.ResponseWriter, r *http.Request) {
        err := r.ParseForm()
        checkErr(err, "Parsing form failed")

        ids_string := r.Form.Get("Id")

        ids := strings.Split(ids_string, ",")
        new_ids_string := "\"" + strings.Join(ids, "\", \"") + "\""

        query := fmt.Sprintf("UPDATE messages SET Sent='true' WHERE Id IN (%s)", new_ids_string)

        _, err = dbmap.Exec(query)

        if err == nil{
            fmt.Fprintf(w, "Ok")
        }else{
            fmt.Fprintf(w, "Error", err)
        }

    })
    srv := xmpp.NewServer("sms.nicolas.kwyk.fr")
    http.HandleFunc("/received/", func(w http.ResponseWriter, r *http.Request) {
        err := r.ParseForm()
        checkErr(err, "Parsing form failed")

        from := r.Form.Get("From")
        message := r.Form.Get("Message")

        if (from != "" && message != ""){
            m := newIncomingMessage(from, message, srv)
            err = dbmap.Insert(&m)
        }
        if err == nil{
            fmt.Fprintf(w, "Ok")
        }else{
            fmt.Fprintf(w, "Error", err)
        }

    })
    err := srv.SetKeyPair("private.cert", "private.key")
    if (err != nil){
    }
    srv.SetAuthFunc(authenticate)
    srv.SetHandleIncomingMessage(
        func (msg xmpp.XmppMessage){
            for _, body := range(msg.Bodies){
                number := strings.Split(msg.To, "@")[0]
                message := newMessage(number, html.UnescapeString(body.Body))
                err := dbmap.Insert(&message)
                checkErr(err, "Could not insert new message")
            }
    })
    srv.HandleQuery("http://jabber.org/protocol/disco#info", func (w io.Writer, iq xmpp.IQ){
            w.Write([]byte(`<iq type='result' from='sms.nicolas.kwyk.fr' to='nicolas@kwyk.fr/YY9R80gi' id='` + iq.Id + `'>
                        <query xmlns='http://jabber.org/protocol/disco#info'>
                            <feature var='http://jabber.org/protocol/disco#info'/>
                            <feature var='http://jabber.org/protocol/disco#items'/>
                        </query>
                    </iq>
            `))
    })
    srv.HandleQuery("http://jabber.org/protocol/disco#items", func (w io.Writer, iq xmpp.IQ){
        w.Write([]byte(`<iq type='result' from='sms.nicolas.kwyk.fr' to='nicolas@kwyk.fr/YY9R80gi' id='` + iq.Id + `'>
                <error code="501" type="cancel"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>
                </iq>
        `))
    })
    srv.HandleQuery("jabber:iq:roster", func(w io.Writer, iq xmpp.IQ){
            w.Write([]byte(`<iq type='result' to='nicolas@kwyk.fr/YY9R80gi' id='` + iq.Id + `'>
                    <query xmlns="jabber:iq:roster">
                        <item jid="+33609781500@sms.nicolas.kwyk.fr" subscription="both" name="Julie Famery Phone"><group>Buddies</group></item>
                        <item jid="+33672317534@sms.nicolas.kwyk.fr" subscription="both" name="Nicolas Patry Phone"><group>Buddies</group></item>
                    </query>
                </iq>
            `))
            w.Write([]byte(`<presence from="+33609781500@sms.nicolas.kwyk.fr" to="nicolas@kwyk.fr/YY9R80gi"><caps:c node="http://www.android.com/gtalk/client/caps" ver="1.1" xmlns:caps="http://jabber.org/protocol/caps"/></presence>`))
            w.Write([]byte(`<presence from="+33672317534@sms.nicolas.kwyk.fr" to="nicolas@kwyk.fr/YY9R80gi"><caps:c node="http://www.android.com/gtalk/client/caps" ver="1.1" xmlns:caps="http://jabber.org/protocol/caps"/></presence>`))
    })

    go func(){
        srv.ListenAndServe("tcp", ":5222")
    }()
    log.Fatal(http.ListenAndServe(":8080", nil))

}

func newMessage(destination, message string) Message {
    fmt.Println("Sending SMS to: ", destination)
    return Message{
        Created: time.Now().UnixNano(),
        From: "+33672317534",
        Message:   message,
        To:    destination,
        Sent: false,
        Incoming: false,
    }
}
func newIncomingMessage(author, message string, srv xmpp.Server) Message {
    fmt.Println("Received SMS from: ", author)
    go func(){
        srv.MessageChannel<-xmpp.Message{From: author, Message:message, To: "nicolas"}
    }()

    return Message{
        Created: time.Now().UnixNano(),
        From: author,
        Message:   message,
        To:    "+33672317534",
        Sent: true,
        Incoming: true,
    }
}

func initDb() *gorp.DbMap {
    // connect to db using standard Go database/sql API
    // use whatever database/sql driver you wish
    db, err := sql.Open("sqlite3", "db.bin")
    checkErr(err, "sql.Open failed")

    // construct a gorp DbMap
    dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}

    // add a table, setting the table name to 'posts' and
    // specifying that the Id property is an auto incrementing PK
    dbmap.AddTableWithName(Message{}, "messages").SetKeys(true, "Id")

    // create the table. in a production system you'd generally
    // use a migration tool, or create the tables via scripts
    err = dbmap.CreateTablesIfNotExists()
    checkErr(err, "Create tables failed")

    return dbmap
}

func checkErr(err error, msg string) {
    if err != nil {
        log.Fatalln(msg, err)
    }
}

<|endoftext|>