<commit_before>// Ogo

package ogo

import (
	//"fmt"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
	"net/http"
	"strings"
)

type Handler func(c *HttpContext)

type Route struct {
	Pattern string
	Method  string
	Handler Handler
}

type Controller struct {
	Endpoint string
	Routes   map[string]*Route
	Request  int
}

type ControllerInterface interface {
	//Init(endpoint string, c ControllerInterface)
	Get(c *HttpContext)
	Post(c *HttpContext)
	Put(c *HttpContext)
	Delete(c *HttpContext)
	Patch(c *HttpContext)
	Head(c *HttpContext)
}

func NewRoute(p string, m string, h Handler) *Route {
	return &Route{
		Pattern: p,
		Method:  m,
		Handler: h,
	}
}

// 封装
func handlerWrap(f Handler) web.HandlerFunc { //这里封装了webC到本地的结构中
	return func(c web.C, w http.ResponseWriter, r *http.Request) {
		f(newContext(c, w, r))
	}
}

func (ctr *Controller) Init(endpoint string, c ControllerInterface) {
	ctr.Endpoint = endpoint
	ctr.Routes = make(map[string]*Route)
	//默认路由
	ctr.DefaultRoutes(c)
}

func (ctr *Controller) Get(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Post(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Put(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Delete(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Patch(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Head(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}

func (ctr *Controller) AddRoute(m string, p string, h Handler) {
	rt := NewRoute(p, m, h)
	switch strings.ToLower(m) {
	case "get":
		ctr.RouteGet(rt)
	case "post":
		ctr.RoutePost(rt)
	case "put":
		ctr.RoutePut(rt)
	case "delete":
		ctr.RouteDelete(rt)
	case "patch":
		ctr.RoutePatch(rt)
	case "head":
		ctr.RouteHead(rt)
	default:
		// unknow method
	}
}

// controller default route
// 默认路由, 如果已经定义了则忽略，没有定义则加上
//func (ctr *Controller) DefaultRoutes() {
func (ctr *Controller) DefaultRoutes(c ControllerInterface) {
	// GET /{endpoint}
	ctr.RouteGet(NewRoute("/"+ctr.Endpoint, "GET", c.Get))

	// GET /{endpoint}/{id}
	ctr.RouteGet(NewRoute("/"+ctr.Endpoint+"/:_id_", "GET", c.Get))

	// POST /{endpoint}
	ctr.RoutePost(NewRoute("/"+ctr.Endpoint, "POST", c.Post))

	// DELETE /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "DELETE", c.Delete))

	// PATCH /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "PATCH", c.Patch))

	// PUT /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "PUT", c.Put))

}

func (ctr *Controller) RouteGet(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Get(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePost(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Post(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePut(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Put(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RouteDelete(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Delete(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePatch(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Patch(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RouteHead(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Head(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
<commit_msg>update<commit_after>// Ogo

package ogo

import (
	//"fmt"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
	"net/http"
	"strings"
)

type Handler func(c *HttpContext)

type Route struct {
	Pattern string
	Method  string
	Handler Handler
}

type Controller struct {
	Endpoint string
	Routes   map[string]*Route
	Request  int
}

type ControllerInterface interface {
	//Init(endpoint string, c ControllerInterface)
	Get(c *HttpContext)
	Post(c *HttpContext)
	Put(c *HttpContext)
	Delete(c *HttpContext)
	Patch(c *HttpContext)
	Head(c *HttpContext)
}

func NewRoute(p string, m string, h Handler) *Route {
	return &Route{
		Pattern: p,
		Method:  m,
		Handler: h,
	}
}

// 封装
func handlerWrap(f Handler) web.HandlerFunc { //这里封装了webC到本地的结构中
	return func(c web.C, w http.ResponseWriter, r *http.Request) {
		f(newContext(c, w, r))
	}
}

func (ctr *Controller) Init(endpoint string, c ControllerInterface) {
	ctr.Endpoint = endpoint
	ctr.Routes = make(map[string]*Route)
	//默认路由
	ctr.DefaultRoutes(c)
}

func (ctr *Controller) Get(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Post(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Put(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Delete(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Patch(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Head(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}

func (ctr *Controller) AddRoute(m string, p string, h Handler) {
	rt := NewRoute(p, m, h)
	switch strings.ToLower(m) {
	case "get":
		ctr.RouteGet(rt)
	case "post":
		ctr.RoutePost(rt)
	case "put":
		ctr.RoutePut(rt)
	case "delete":
		ctr.RouteDelete(rt)
	case "patch":
		ctr.RoutePatch(rt)
	case "head":
		ctr.RouteHead(rt)
	default:
		// unknow method
	}
}

// controller default route
// 默认路由, 如果已经定义了则忽略，没有定义则加上
//func (ctr *Controller) DefaultRoutes() {
func (ctr *Controller) DefaultRoutes(c ControllerInterface) {
	// GET /{endpoint}
	ctr.RouteGet(NewRoute("/"+ctr.Endpoint, "GET", c.Get))

	// GET /{endpoint}/{id}
	ctr.RouteGet(NewRoute("/"+ctr.Endpoint+"/:_id_", "GET", c.Get))

	// POST /{endpoint}
	ctr.RoutePost(NewRoute("/"+ctr.Endpoint, "POST", c.Post))

	// DELETE /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "DELETE", c.Delete))

	// PATCH /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "PATCH", c.Patch))

	// PUT /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "PUT", c.Put))

}

func (ctr *Controller) RouteGet(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists, log overwrite
	}
	goji.Get(rt.Pattern, handlerWrap(rt.Handler))
	ctr.Routes[key] = rt
}
func (ctr *Controller) RoutePost(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists, log overwrite
	}
	goji.Post(rt.Pattern, handlerWrap(rt.Handler))
	ctr.Routes[key] = rt
}
func (ctr *Controller) RoutePut(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists, log overwrite
	}
	goji.Put(rt.Pattern, handlerWrap(rt.Handler))
	ctr.Routes[key] = rt
}
func (ctr *Controller) RouteDelete(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists, log overwrite
	}
	goji.Delete(rt.Pattern, handlerWrap(rt.Handler))
	ctr.Routes[key] = rt
}
func (ctr *Controller) RoutePatch(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists, log overwrite
	}
	goji.Patch(rt.Pattern, handlerWrap(rt.Handler))
	ctr.Routes[key] = rt
}
func (ctr *Controller) RouteHead(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists, log overwrite
	}
	goji.Head(rt.Pattern, handlerWrap(rt.Handler))
	ctr.Routes[key] = rt
}
<|endoftext|>


<commit_before>package httpproxy

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/HouzuoGuo/laitos/daemon/dnsd"
	"github.com/HouzuoGuo/laitos/daemon/httpd/middleware"
	"github.com/HouzuoGuo/laitos/lalog"
	"github.com/HouzuoGuo/laitos/misc"
	"github.com/HouzuoGuo/laitos/testingstub"
	"github.com/HouzuoGuo/laitos/toolbox"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	// DefaultPort is the port number the daemon will listen on in the absence of port number specified by user.
	DefaultPort = 210
	// TestPort is used exclusively by test cases to configure daemon under testing.
	TestPort = 54112
	// IOTimeout is the maximum duration of an entire proxy request. The number took inspiration from the default settings in Squid software.
	IOTimeout = time.Duration(10 * time.Minute)
	// MaxRequestBodyBytes is the maximum size accepted for the entire request body of an HTTP proxy request.
	// The size does not apply to HTTPS proxy request (HTTP CONNECT).
	MaxRequestBodyBytes = 2 * 1024 * 1024
)

// Daemon offers an HTTP proxy capable of handling both HTTP and HTTPS destinations.
type Daemon struct {
	// Address is the IP address to listen on, e.g. 0.0.0.0 to listen on all network interfaces.
	Address string `json:"Address"`
	// Port to listen on.
	Port int `json:"Port"`
	// PerIPLimit is the approximate number of requests a client (identified by its IP) can make in a second.
	PerIPLimit int `json:"PerIPLimit"`
	// AllowFromCidrs is a list of CIDRs that client address must reside in to be eligible to use this HTTP proxy daemon.
	AllowFromCidrs []string `json:"AllowFromCidrs"`
	// Processor is a toolbox command processor that collects client subject reports for its store&forward message processor app.
	// Though the HTTP proxy daemon itself is incapable of executing app commands, the daemon will however subjects of the message
	// processor (computers) to use the proxy daemon. This saves the effort of having to figure out users' Internet CIDR block
	// and placing them into AllowFromCidrs.
	// This mechanism exists in the DNS daemon in a similar form.
	CommandProcessor *toolbox.CommandProcessor `json:"-"`

	// DNSDaemon is an initialised DNS daemon that will provide protection against advertising, malware, and tracking to this web proxy.
	DNSDaemon *dnsd.Daemon `json:"-"`

	allowFromIPNets []*net.IPNet
	proxyHandler    http.HandlerFunc
	rateLimit       *misc.RateLimit
	logger          lalog.Logger
	httpServer      *http.Server
}

// Initialise validates configuration parameters and initialises the internal state of the daemon.
func (daemon *Daemon) Initialise() error {
	if daemon.Address == "" {
		daemon.Address = "0.0.0.0"
	}
	if daemon.PerIPLimit < 1 {
		daemon.PerIPLimit = 100
	}
	if daemon.Port == 0 {
		daemon.Port = DefaultPort
	}
	if daemon.CommandProcessor == nil {
		daemon.CommandProcessor = toolbox.GetEmptyCommandProcessor()
	}
	daemon.logger = lalog.Logger{ComponentName: "httpproxy", ComponentID: []lalog.LoggerIDField{{Key: "Port", Value: strconv.Itoa(daemon.Port)}}}
	daemon.rateLimit = &misc.RateLimit{
		UnitSecs: 1,
		MaxCount: daemon.PerIPLimit,
		Logger:   daemon.logger,
	}
	daemon.rateLimit.Initialise()
	// Parse allowed CIDRs into IP nets
	daemon.allowFromIPNets = make([]*net.IPNet, 0)
	for _, cidrStr := range daemon.AllowFromCidrs {
		_, cidrNet, err := net.ParseCIDR(cidrStr)
		if err != nil {
			return fmt.Errorf("httpproxy.Initialise: failed to parse string \"%s\" for a CIDR that is allowed to use this proxy server", cidrStr)
		}
		daemon.allowFromIPNets = append(daemon.allowFromIPNets, cidrNet)
	}
	// Collect proxy request and response stats in prometheus histograms
	var handlerDurationHistogram, responseTimeToFirstByteHistogram, responseSizeHistogram *prometheus.HistogramVec
	if misc.EnablePrometheusIntegration {
		metricsLabelNames := []string{middleware.PrometheusHandlerTypeLabel, middleware.PrometheusHandlerLocationLabel, middleware.PrometheusHandlerHostLabel}
		handlerDurationHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Name:    "laitos_httpproxy_handler_duration_seconds",
			Help:    "The run-duration of HTTP proxy responses in seconds",
			Buckets: []float64{0.025, 0.050, 0.1, 0.1375, 0.2125, 0.25, 0.375, 0.4375, 0.5, 0.75, 0.875, 1.25, 1.5, 2, 3, 5, 8, 12, 20},
		}, metricsLabelNames)
		responseTimeToFirstByteHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Name:    "laitos_httpproxy_response_time_to_first_byte_seconds",
			Help:    "The time-to-first-byte of HTTP proxy responses in seconds",
			Buckets: []float64{0.025, 0.050, 0.1, 0.1375, 0.2125, 0.25, 0.375, 0.4375, 0.5, 0.75, 0.875, 1.25, 1.5, 2, 3, 5, 8, 12, 20},
		}, metricsLabelNames)
		responseSizeHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Name:    "laitos_httpproxy_response_size_bytes",
			Help:    "The size of response produced by HTTP proxy responses function in bytes",
			Buckets: []float64{64, 256, 512, 1024, 2048, 4096, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 5242880, 8388608, 12582912, 16777216},
		}, metricsLabelNames)
		for _, histogram := range []*prometheus.HistogramVec{handlerDurationHistogram, responseTimeToFirstByteHistogram, responseSizeHistogram} {
			if err := prometheus.Register(histogram); err != nil {
				daemon.logger.Warning("Initialise", "", err, "failed to register prometheus metrics collectors")
			}
		}
	}
	daemon.proxyHandler = middleware.LogRequestStats(daemon.logger,
		middleware.RecordInternalStats(misc.HTTPProxyStats,
			middleware.EmergencyLockdown(
				daemon.CheckClientIPMiddleware(
					middleware.RecordPrometheusStats("httpproxy", "", handlerDurationHistogram, responseTimeToFirstByteHistogram, responseSizeHistogram,
						middleware.RateLimit(daemon.rateLimit, daemon.ProxyHandler))))))
	return nil
}

// StartAndBlock starts a web server with a specially crafted handler to serve HTTP proxy clients.
// The function will block caller until Stop is called.
func (daemon *Daemon) StartAndBlock() error {
	daemon.httpServer = &http.Server{
		Addr:         net.JoinHostPort(daemon.Address, strconv.Itoa(daemon.Port)),
		Handler:      daemon.proxyHandler,
		ReadTimeout:  IOTimeout,
		WriteTimeout: IOTimeout,
		// TODO: figure out how to handle an HTTP/2 proxy client and then reenable HTTP/2 support
		TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
	}
	daemon.logger.Info("StartAndBlock", "", nil, "starting now")
	if err := daemon.httpServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
		return fmt.Errorf("httpproxy.StartAndBlock.: failed to listen on %s:%d - %v", daemon.Address, daemon.Port, err)
	}
	return nil
}

// Stop the daemon.
func (daemon *Daemon) Stop() {
	if daemon.httpServer != nil {
		stopCtx, cancelFunc := context.WithTimeout(context.Background(), 2*time.Second)
		defer cancelFunc()
		if err := daemon.httpServer.Shutdown(stopCtx); err != nil {
			daemon.logger.Warning("Stop", daemon.Address, err, "failed to shutdown")
		}
	}
}

// TestHTTPProxyDaemon is used exclusively by test case to run a comprehensive test routine for the daemon's functions.
// The daemon must have already been completed with all of its configuration and successfully initialised.
// See httpproxy_test.go for the daemon initialisation routine.
func TestHTTPProxyDaemon(daemon *Daemon, t testingstub.T) {
	daemonStopped := make(chan struct{}, 1)
	go func() {
		if err := daemon.StartAndBlock(); err != nil {
			t.Error(err)
			return
		}
		daemonStopped <- struct{}{}
	}()
	if !misc.ProbePort(30*time.Second, daemon.Address, daemon.Port) {
		t.Fatal("daemon did not start on time")
	}
	// Use the HTTP proxy daemon as the choice of proxy in this HTTP client
	proxyURL, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", daemon.Port))
	if err != nil {
		t.Fatal(err)
	}
	proxyClient := &http.Client{Transport: &http.Transport{
		Proxy: http.ProxyURL(proxyURL),
	}}
	// Perform a regular HTTP request using the proxy server
	resp, err := proxyClient.Get("http://google.com")
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode/200 != 1 {
		t.Fatal("unexpected http response status code", resp.StatusCode)
	}
	// Perform a regular HTTPS request using the proxy server
	resp, err = proxyClient.Get("https://google.com")
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode/200 != 1 {
		t.Fatal("unexpected http response status code", resp.StatusCode)
	}
	daemon.Stop()
	<-daemonStopped
	// Repeatedly stopping the daemon should have no negative consequences
	daemon.Stop()
	daemon.Stop()
}
<commit_msg>change the domain name used in httpproxy test<commit_after>package httpproxy

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/HouzuoGuo/laitos/daemon/dnsd"
	"github.com/HouzuoGuo/laitos/daemon/httpd/middleware"
	"github.com/HouzuoGuo/laitos/lalog"
	"github.com/HouzuoGuo/laitos/misc"
	"github.com/HouzuoGuo/laitos/testingstub"
	"github.com/HouzuoGuo/laitos/toolbox"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	// DefaultPort is the port number the daemon will listen on in the absence of port number specified by user.
	DefaultPort = 210
	// TestPort is used exclusively by test cases to configure daemon under testing.
	TestPort = 54112
	// IOTimeout is the maximum duration of an entire proxy request. The number took inspiration from the default settings in Squid software.
	IOTimeout = time.Duration(10 * time.Minute)
	// MaxRequestBodyBytes is the maximum size accepted for the entire request body of an HTTP proxy request.
	// The size does not apply to HTTPS proxy request (HTTP CONNECT).
	MaxRequestBodyBytes = 2 * 1024 * 1024
)

// Daemon offers an HTTP proxy capable of handling both HTTP and HTTPS destinations.
type Daemon struct {
	// Address is the IP address to listen on, e.g. 0.0.0.0 to listen on all network interfaces.
	Address string `json:"Address"`
	// Port to listen on.
	Port int `json:"Port"`
	// PerIPLimit is the approximate number of requests a client (identified by its IP) can make in a second.
	PerIPLimit int `json:"PerIPLimit"`
	// AllowFromCidrs is a list of CIDRs that client address must reside in to be eligible to use this HTTP proxy daemon.
	AllowFromCidrs []string `json:"AllowFromCidrs"`
	// Processor is a toolbox command processor that collects client subject reports for its store&forward message processor app.
	// Though the HTTP proxy daemon itself is incapable of executing app commands, the daemon will however subjects of the message
	// processor (computers) to use the proxy daemon. This saves the effort of having to figure out users' Internet CIDR block
	// and placing them into AllowFromCidrs.
	// This mechanism exists in the DNS daemon in a similar form.
	CommandProcessor *toolbox.CommandProcessor `json:"-"`

	// DNSDaemon is an initialised DNS daemon that will provide protection against advertising, malware, and tracking to this web proxy.
	DNSDaemon *dnsd.Daemon `json:"-"`

	allowFromIPNets []*net.IPNet
	proxyHandler    http.HandlerFunc
	rateLimit       *misc.RateLimit
	logger          lalog.Logger
	httpServer      *http.Server
}

// Initialise validates configuration parameters and initialises the internal state of the daemon.
func (daemon *Daemon) Initialise() error {
	if daemon.Address == "" {
		daemon.Address = "0.0.0.0"
	}
	if daemon.PerIPLimit < 1 {
		daemon.PerIPLimit = 100
	}
	if daemon.Port == 0 {
		daemon.Port = DefaultPort
	}
	if daemon.CommandProcessor == nil {
		daemon.CommandProcessor = toolbox.GetEmptyCommandProcessor()
	}
	daemon.logger = lalog.Logger{ComponentName: "httpproxy", ComponentID: []lalog.LoggerIDField{{Key: "Port", Value: strconv.Itoa(daemon.Port)}}}
	daemon.rateLimit = &misc.RateLimit{
		UnitSecs: 1,
		MaxCount: daemon.PerIPLimit,
		Logger:   daemon.logger,
	}
	daemon.rateLimit.Initialise()
	// Parse allowed CIDRs into IP nets
	daemon.allowFromIPNets = make([]*net.IPNet, 0)
	for _, cidrStr := range daemon.AllowFromCidrs {
		_, cidrNet, err := net.ParseCIDR(cidrStr)
		if err != nil {
			return fmt.Errorf("httpproxy.Initialise: failed to parse string \"%s\" for a CIDR that is allowed to use this proxy server", cidrStr)
		}
		daemon.allowFromIPNets = append(daemon.allowFromIPNets, cidrNet)
	}
	// Collect proxy request and response stats in prometheus histograms
	var handlerDurationHistogram, responseTimeToFirstByteHistogram, responseSizeHistogram *prometheus.HistogramVec
	if misc.EnablePrometheusIntegration {
		metricsLabelNames := []string{middleware.PrometheusHandlerTypeLabel, middleware.PrometheusHandlerLocationLabel, middleware.PrometheusHandlerHostLabel}
		handlerDurationHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Name:    "laitos_httpproxy_handler_duration_seconds",
			Help:    "The run-duration of HTTP proxy responses in seconds",
			Buckets: []float64{0.025, 0.050, 0.1, 0.1375, 0.2125, 0.25, 0.375, 0.4375, 0.5, 0.75, 0.875, 1.25, 1.5, 2, 3, 5, 8, 12, 20},
		}, metricsLabelNames)
		responseTimeToFirstByteHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Name:    "laitos_httpproxy_response_time_to_first_byte_seconds",
			Help:    "The time-to-first-byte of HTTP proxy responses in seconds",
			Buckets: []float64{0.025, 0.050, 0.1, 0.1375, 0.2125, 0.25, 0.375, 0.4375, 0.5, 0.75, 0.875, 1.25, 1.5, 2, 3, 5, 8, 12, 20},
		}, metricsLabelNames)
		responseSizeHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Name:    "laitos_httpproxy_response_size_bytes",
			Help:    "The size of response produced by HTTP proxy responses function in bytes",
			Buckets: []float64{64, 256, 512, 1024, 2048, 4096, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 5242880, 8388608, 12582912, 16777216},
		}, metricsLabelNames)
		for _, histogram := range []*prometheus.HistogramVec{handlerDurationHistogram, responseTimeToFirstByteHistogram, responseSizeHistogram} {
			if err := prometheus.Register(histogram); err != nil {
				daemon.logger.Warning("Initialise", "", err, "failed to register prometheus metrics collectors")
			}
		}
	}
	daemon.proxyHandler = middleware.LogRequestStats(daemon.logger,
		middleware.RecordInternalStats(misc.HTTPProxyStats,
			middleware.EmergencyLockdown(
				daemon.CheckClientIPMiddleware(
					middleware.RecordPrometheusStats("httpproxy", "", handlerDurationHistogram, responseTimeToFirstByteHistogram, responseSizeHistogram,
						middleware.RateLimit(daemon.rateLimit, daemon.ProxyHandler))))))
	return nil
}

// StartAndBlock starts a web server with a specially crafted handler to serve HTTP proxy clients.
// The function will block caller until Stop is called.
func (daemon *Daemon) StartAndBlock() error {
	daemon.httpServer = &http.Server{
		Addr:         net.JoinHostPort(daemon.Address, strconv.Itoa(daemon.Port)),
		Handler:      daemon.proxyHandler,
		ReadTimeout:  IOTimeout,
		WriteTimeout: IOTimeout,
		// TODO: figure out how to handle an HTTP/2 proxy client and then reenable HTTP/2 support
		TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
	}
	daemon.logger.Info("StartAndBlock", "", nil, "starting now")
	if err := daemon.httpServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
		return fmt.Errorf("httpproxy.StartAndBlock.: failed to listen on %s:%d - %v", daemon.Address, daemon.Port, err)
	}
	return nil
}

// Stop the daemon.
func (daemon *Daemon) Stop() {
	if daemon.httpServer != nil {
		stopCtx, cancelFunc := context.WithTimeout(context.Background(), 2*time.Second)
		defer cancelFunc()
		if err := daemon.httpServer.Shutdown(stopCtx); err != nil {
			daemon.logger.Warning("Stop", daemon.Address, err, "failed to shutdown")
		}
	}
}

// TestHTTPProxyDaemon is used exclusively by test case to run a comprehensive test routine for the daemon's functions.
// The daemon must have already been completed with all of its configuration and successfully initialised.
// See httpproxy_test.go for the daemon initialisation routine.
func TestHTTPProxyDaemon(daemon *Daemon, t testingstub.T) {
	daemonStopped := make(chan struct{}, 1)
	go func() {
		if err := daemon.StartAndBlock(); err != nil {
			t.Error(err)
			return
		}
		daemonStopped <- struct{}{}
	}()
	if !misc.ProbePort(30*time.Second, daemon.Address, daemon.Port) {
		t.Fatal("daemon did not start on time")
	}
	// Use the HTTP proxy daemon as the choice of proxy in this HTTP client
	proxyURL, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", daemon.Port))
	if err != nil {
		t.Fatal(err)
	}
	proxyClient := &http.Client{Transport: &http.Transport{
		Proxy: http.ProxyURL(proxyURL),
	}}
	// Perform a regular HTTP request using the proxy server
	resp, err := proxyClient.Get("http://microsoft.com")
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode/200 != 1 {
		t.Fatal("unexpected http response status code", resp.StatusCode)
	}
	// Perform a regular HTTPS request using the proxy server
	resp, err = proxyClient.Get("https://microsoft.com")
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode/200 != 1 {
		t.Fatal("unexpected http response status code", resp.StatusCode)
	}
	daemon.Stop()
	<-daemonStopped
	// Repeatedly stopping the daemon should have no negative consequences
	daemon.Stop()
	daemon.Stop()
}
<|endoftext|>


<commit_before>package rethinkdb

import (
	r "gopkg.in/gorethink/gorethink.v2"

	"github.com/Sirbu/golang-common/dao/api"
)

// Default contains the basic implementation of the EntityCRUD interface
type Default struct {
	table   string
	db      string
	session *r.Session
}

// NewCRUDTable sets up a new Default struct
func NewCRUDTable(session *r.Session, db, table string) *Default {
	return &Default{
		db:      db,
		table:   table,
		session: session,
	}
}

// GetTableName returns table's name
func (d *Default) GetTableName() string {
	return d.table
}

// GetDBName returns database's name
func (d *Default) GetDBName() string {
	return d.db
}

// GetTable returns no table
func (d *Default) GetTable() interface{} {
	return r.Table(d.table)
}

// GetSession returns the current session
func (d *Default) GetSession() interface{} {
	return d.session
}

// Insert inserts a document into the database
func (d *Default) Insert(data interface{}) error {
	_, err := r.Table(d.table).Insert(data).RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// InsertOrUpdate a document occording to ID presence in database
func (d *Default) InsertOrUpdate(id interface{}, data interface{}) (r.WriteResponse, error) {
	_, err := r.Table(d.table).Insert(data, r.InsertOpts{Conflict: "update"}).RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}

		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// Find a document match given id
func (d *Default) Find(id interface{}, value interface{}) error {
	cursor, err := r.Table(d.table).Get(id).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.One(value); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// FindOneBy a couple (k = v) in the database
func (d *Default) FindOneBy(key string, value interface{}, result interface{}) error {

	cursor, err := r.Table(d.table).GetAllByIndex(key, value).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.One(result); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// FindBy all couples (k = v) in the database
func (d *Default) FindBy(key string, value interface{}, results interface{}) error {
	cursor, err := r.Table(d.table).Filter(func(row r.Term) r.Term {
		return row.Field(key).Eq(value)
	}).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.All(results); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// FindByAndCount is used to count object that matchs the (key = value) predicate
func (d *Default) FindByAndCount(key string, value interface{}) (int, error) {
	cursor, err := r.Table(d.table).Filter(func(row r.Term) r.Term {
		return row.Field(key).Eq(value)
	}).Count().Run(d.session)
	if err != nil {
		return 0, err
	}

	var count int
	if err := cursor.One(&count); err != nil {
		if err == r.ErrEmptyResult {
			return 0, api.ErrNoResult
		}
		return 0, api.NewDatabaseError(d, err, "")
	}

	return count, nil
}

// Where is used to fetch documents that match th filter from the database
func (d *Default) Where(filter interface{}, results interface{}) error {
	cursor, err := r.Table(d.table).Filter(filter).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.All(results); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// WhereCount returns the document count that match the filter
func (d *Default) WhereCount(filter interface{}) (int, error) {
	cursor, err := r.Table(d.table).Filter(filter).Count().Run(d.session)
	if err != nil {
		return 0, err
	}

	var count int
	if err := cursor.One(&count); err != nil {
		if err == r.ErrEmptyResult {
			return 0, api.ErrNoResult
		}
		return 0, api.NewDatabaseError(d, err, "")
	}

	return count, nil
}

// WhereAndFetchOne returns one document that match the filter
func (d *Default) WhereAndFetchOne(filter interface{}, result interface{}) error {
	cursor, err := r.Table(d.table).Filter(filter).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.One(result); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// WhereAndFetchLimit returns paginated list of document
func (d *Default) WhereAndFetchLimit(filter interface{}, paginator *api.Pagination, results interface{}) error {
	cursor, err := r.Table(d.table).Filter(filter).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.All(results); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// Update a document that match the selector
func (d *Default) Update(selector interface{}, data interface{}) error {
	_, err := r.Table(d.table).Filter(selector).Update(data).RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// UpdateID updates a document using his id
func (d *Default) UpdateID(id interface{}, data interface{}) error {
	_, err := r.Table(d.table).Get(id).Update(data).RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// DeleteAll documents from the database
func (d *Default) DeleteAll(pred interface{}) error {
	_, err := r.Table(d.table).Filter(pred).Delete().RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// Delete a document from the database
func (d *Default) Delete(id interface{}) error {
	_, err := r.Table(d.table).Get(id).Delete().RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// List all entities from the database
func (d *Default) List(results interface{}, sortParams *api.SortParameters, pagination *api.Pagination) error {
	return d.Search(results, nil, sortParams, pagination)
}

// Search all entities in the database
func (d *Default) Search(results interface{}, filter interface{}, sortParams *api.SortParameters, pagination *api.Pagination) error {
	term := r.Table(d.table)

	// Filter
	if filter != nil {
		term = term.Filter(filter)
	}

	// Get total
	if pagination != nil {
		total, err := d.WhereCount(filter)
		if err != nil {
			return api.NewDatabaseError(d, err, "")
		}
		pagination.SetTotal(uint(total))
	}

	// Sort
	if sortParams != nil {
		term = term.OrderBy(ConvertSortParameters(*sortParams)...)
	}

	// Slice result
	if pagination != nil {
		term = term.Slice(pagination.Offset(), pagination.Offset()+pagination.PerPage)
	}

	// Run the query
	cursor, err := term.Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	// Fetch cursor
	err = cursor.All(results)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return err
	}

	return nil
}
<commit_msg>Damn it I really didn't pay attention...<commit_after>package rethinkdb

import (
	r "gopkg.in/gorethink/gorethink.v2"

	"github.com/Sirbu/golang-common/dao/api"
)

// Default contains the basic implementation of the EntityCRUD interface
type Default struct {
	table   string
	db      string
	session *r.Session
}

// NewCRUDTable sets up a new Default struct
func NewCRUDTable(session *r.Session, db, table string) *Default {
	return &Default{
		db:      db,
		table:   table,
		session: session,
	}
}

// GetTableName returns table's name
func (d *Default) GetTableName() string {
	return d.table
}

// GetDBName returns database's name
func (d *Default) GetDBName() string {
	return d.db
}

// GetTable returns no table
func (d *Default) GetTable() interface{} {
	return r.Table(d.table)
}

// GetSession returns the current session
func (d *Default) GetSession() interface{} {
	return d.session
}

// Insert inserts a document into the database
func (d *Default) Insert(data interface{}) error {
	_, err := r.Table(d.table).Insert(data).RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// InsertOrUpdate a document occording to ID presence in database
func (d *Default) InsertOrUpdate(id interface{}, data interface{}) (r.WriteResponse, error) {
	resp, err := r.Table(d.table).Insert(data, r.InsertOpts{Conflict: "update"}).RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return resp, api.ErrNoResult
		}

		return resp, api.NewDatabaseError(d, err, "")
	}

	return resp, nil
}

// Find a document match given id
func (d *Default) Find(id interface{}, value interface{}) error {
	cursor, err := r.Table(d.table).Get(id).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.One(value); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// FindOneBy a couple (k = v) in the database
func (d *Default) FindOneBy(key string, value interface{}, result interface{}) error {

	cursor, err := r.Table(d.table).GetAllByIndex(key, value).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.One(result); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// FindBy all couples (k = v) in the database
func (d *Default) FindBy(key string, value interface{}, results interface{}) error {
	cursor, err := r.Table(d.table).Filter(func(row r.Term) r.Term {
		return row.Field(key).Eq(value)
	}).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.All(results); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// FindByAndCount is used to count object that matchs the (key = value) predicate
func (d *Default) FindByAndCount(key string, value interface{}) (int, error) {
	cursor, err := r.Table(d.table).Filter(func(row r.Term) r.Term {
		return row.Field(key).Eq(value)
	}).Count().Run(d.session)
	if err != nil {
		return 0, err
	}

	var count int
	if err := cursor.One(&count); err != nil {
		if err == r.ErrEmptyResult {
			return 0, api.ErrNoResult
		}
		return 0, api.NewDatabaseError(d, err, "")
	}

	return count, nil
}

// Where is used to fetch documents that match th filter from the database
func (d *Default) Where(filter interface{}, results interface{}) error {
	cursor, err := r.Table(d.table).Filter(filter).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.All(results); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// WhereCount returns the document count that match the filter
func (d *Default) WhereCount(filter interface{}) (int, error) {
	cursor, err := r.Table(d.table).Filter(filter).Count().Run(d.session)
	if err != nil {
		return 0, err
	}

	var count int
	if err := cursor.One(&count); err != nil {
		if err == r.ErrEmptyResult {
			return 0, api.ErrNoResult
		}
		return 0, api.NewDatabaseError(d, err, "")
	}

	return count, nil
}

// WhereAndFetchOne returns one document that match the filter
func (d *Default) WhereAndFetchOne(filter interface{}, result interface{}) error {
	cursor, err := r.Table(d.table).Filter(filter).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.One(result); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// WhereAndFetchLimit returns paginated list of document
func (d *Default) WhereAndFetchLimit(filter interface{}, paginator *api.Pagination, results interface{}) error {
	cursor, err := r.Table(d.table).Filter(filter).Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	if err := cursor.All(results); err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// Update a document that match the selector
func (d *Default) Update(selector interface{}, data interface{}) error {
	_, err := r.Table(d.table).Filter(selector).Update(data).RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// UpdateID updates a document using his id
func (d *Default) UpdateID(id interface{}, data interface{}) error {
	_, err := r.Table(d.table).Get(id).Update(data).RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// DeleteAll documents from the database
func (d *Default) DeleteAll(pred interface{}) error {
	_, err := r.Table(d.table).Filter(pred).Delete().RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// Delete a document from the database
func (d *Default) Delete(id interface{}) error {
	_, err := r.Table(d.table).Get(id).Delete().RunWrite(d.session)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return api.NewDatabaseError(d, err, "")
	}

	return nil
}

// List all entities from the database
func (d *Default) List(results interface{}, sortParams *api.SortParameters, pagination *api.Pagination) error {
	return d.Search(results, nil, sortParams, pagination)
}

// Search all entities in the database
func (d *Default) Search(results interface{}, filter interface{}, sortParams *api.SortParameters, pagination *api.Pagination) error {
	term := r.Table(d.table)

	// Filter
	if filter != nil {
		term = term.Filter(filter)
	}

	// Get total
	if pagination != nil {
		total, err := d.WhereCount(filter)
		if err != nil {
			return api.NewDatabaseError(d, err, "")
		}
		pagination.SetTotal(uint(total))
	}

	// Sort
	if sortParams != nil {
		term = term.OrderBy(ConvertSortParameters(*sortParams)...)
	}

	// Slice result
	if pagination != nil {
		term = term.Slice(pagination.Offset(), pagination.Offset()+pagination.PerPage)
	}

	// Run the query
	cursor, err := term.Run(d.session)
	if err != nil {
		return api.NewDatabaseError(d, err, "")
	}

	// Fetch cursor
	err = cursor.All(results)
	if err != nil {
		if err == r.ErrEmptyResult {
			return api.ErrNoResult
		}
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package ptgen

import (
	"bytes"
	"encoding/json"
	"math/rand"
	"time"

	"golang.org/x/tools/container/intsets"

	"github.com/intervention-engine/fhir/models"
)

type ConditionMetadata struct {
	ID                   int    `json:"condition_id"`
	ICD9                 string `json:"icd9code"`
	Display              string `json:"display"`
	MedicationID         int    `json:"medication_id"`
	Overnights           string `json:"overnights"`
	AbatementChance      int    `json:"abatementChance"`
	Fatal                bool   `json:"healOrDeath"`
	MortalityChance      int    `json:"mortalityChance"`
	MortalityTime        string `json:"mortalityTime"`
	RecoveryEstimate     string `json:"recoveryEstimate"`
	ProcedureChance      int    `json:"procedureChance"`
	ProcedureSuccess     int    `json:"procedureSuccess"`
	CheckUp              string `json:"checkUp"`
	ProcedureDescription string `json:"procedureDescription"`
	ProcedureCode        string `json:"procedureCode"`
	ProcedureName        string `json:"procedureCodeName"`
}

func LoadConditions() []ConditionMetadata {
	j, err := Asset("data/conditions.json")
	if err != nil {
		panic("Can't get the condition data")
	}
	decoder := json.NewDecoder(bytes.NewReader(j))
	md := []ConditionMetadata{}
	decoder.Decode(&md)
	return md
}

func GenerateConditions(ctx Context, md []ConditionMetadata) []models.Condition {
	conditions := []models.Condition{}
	if ctx.Hypertention == "Hypertension" {
		ht := generateCondition("Hypertension", 2, md)
		conditions = append(conditions, ht)
		complication := rand.Intn(5)
		if complication == 1 {
			chf := models.Condition{}
			chfmd := conditionByName("Congestive Heart Failure", md)
			chf.Code = &models.CodeableConcept{Coding: []models.Coding{{Code: chfmd.ICD9, System: "http://hl7.org/fhir/sid/icd-9"}}, Text: chfmd.Display}
			chf.OnsetDateTime = &models.FHIRDateTime{Time: ht.OnsetDateTime.Time.AddDate(rand.Intn(2), rand.Intn(10), rand.Intn(28)), Precision: models.Date}
			conditions = append(conditions, chf)
		}
		if complication == 2 {
			phd := models.Condition{}
			phdmd := conditionByName("Pulmonary Heart Disease", md)
			phd.Code = &models.CodeableConcept{Coding: []models.Coding{{Code: phdmd.ICD9, System: "http://hl7.org/fhir/sid/icd-9"}}, Text: phdmd.Display}
			phd.OnsetDateTime = &models.FHIRDateTime{Time: ht.OnsetDateTime.Time.AddDate(rand.Intn(2), rand.Intn(10), rand.Intn(28)), Precision: models.Date}
			conditions = append(conditions, phd)
		}
	}
	if ctx.Diabetes == "Diabetes" {
		dia := generateCondition("Diabetes", 2, md)
		conditions = append(conditions, dia)
	}

	if ctx.Smoker == "Smoker" {
		complication := rand.Intn(5)
		if complication == 1 {
			e := generateCondition("Emphysema", 2, md)
			conditions = append(conditions, e)
		}
		if complication == 2 {
			lc := generateCondition("Lung Cancer", 2, md)
			conditions = append(conditions, lc)
		}
	}
	// per http://www.cdc.gov/dhdsp/data_statistics/fact_sheets/fs_atrial_fibrillation.htm
	var afibChance int
	if time.Now().Sub(ctx.BirthDate) > (65 * 365 * 24 * time.Hour) {
		afibChance = 9
	} else {
		afibChance = 2
	}

	afibDiceRoll := rand.Intn(100)
	if afibDiceRoll <= afibChance {
		afib := generateCondition("Atrial Fibrillation", 3, md)
		conditions = append(conditions, afib)
	}

	otherConditions := rand.Intn(3)
	previouslySelected := &intsets.Sparse{}
	for index := 0; index < otherConditions; index++ {
		randomCondition := 2 + rand.Intn(76)
		if !previouslySelected.Has(randomCondition) {
			rmd := conditionByID(randomCondition, md)
			rc := generateCondition(rmd.Display, index, md)
			previouslySelected.Insert(randomCondition)
			conditions = append(conditions, rc)
		}
	}

	return conditions
}

func generateCondition(name string, yearOffset int, md []ConditionMetadata) models.Condition {
	c := models.Condition{}
	cmd := conditionByName(name, md)
	c.Code = &models.CodeableConcept{Coding: []models.Coding{{Code: cmd.ICD9, System: "http://hl7.org/fhir/sid/icd-9"}}, Text: cmd.Display}
	c.OnsetDateTime = &models.FHIRDateTime{Time: randomOnset(yearOffset), Precision: models.Date}
	recoveryDiceRoll := rand.Intn(100)
	if recoveryDiceRoll <= cmd.AbatementChance {
		switch cmd.RecoveryEstimate {
		case "week":
			c.AbatementDateTime = &models.FHIRDateTime{Time: c.OnsetDateTime.Time.AddDate(0, 0, 7), Precision: models.Timestamp}
		case "threeMonths":
			c.AbatementDateTime = &models.FHIRDateTime{Time: c.OnsetDateTime.Time.AddDate(0, 3, 0), Precision: models.Timestamp}
		case "sixMonths":
			c.AbatementDateTime = &models.FHIRDateTime{Time: c.OnsetDateTime.Time.AddDate(0, 6, 0), Precision: models.Timestamp}
		case "threeYears":
			c.AbatementDateTime = &models.FHIRDateTime{Time: c.OnsetDateTime.Time.AddDate(3, 0, 0), Precision: models.Timestamp}
		}
	}

	return c
}

func conditionByName(name string, md []ConditionMetadata) *ConditionMetadata {
	for _, c := range md {
		if c.Display == name {
			return &c
		}
	}
	return nil
}

func conditionByID(id int, md []ConditionMetadata) *ConditionMetadata {
	for _, c := range md {
		if c.ID == id {
			return &c
		}
	}
	return nil
}

func randomOnset(minYearsAgo int) time.Time {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	randomYears := minYearsAgo + r.Intn(3)
	randomMonth := r.Intn(11)
	randomDay := r.Intn(28)
	t := time.Now()
	return t.AddDate(-randomYears, -randomMonth, -randomDay).Truncate(time.Hour * 24)
}
<commit_msg>More accurate calculation of whether the person is over 65<commit_after>package ptgen

import (
	"bytes"
	"encoding/json"
	"math/rand"
	"time"

	"golang.org/x/tools/container/intsets"

	"github.com/intervention-engine/fhir/models"
)

type ConditionMetadata struct {
	ID                   int    `json:"condition_id"`
	ICD9                 string `json:"icd9code"`
	Display              string `json:"display"`
	MedicationID         int    `json:"medication_id"`
	Overnights           string `json:"overnights"`
	AbatementChance      int    `json:"abatementChance"`
	Fatal                bool   `json:"healOrDeath"`
	MortalityChance      int    `json:"mortalityChance"`
	MortalityTime        string `json:"mortalityTime"`
	RecoveryEstimate     string `json:"recoveryEstimate"`
	ProcedureChance      int    `json:"procedureChance"`
	ProcedureSuccess     int    `json:"procedureSuccess"`
	CheckUp              string `json:"checkUp"`
	ProcedureDescription string `json:"procedureDescription"`
	ProcedureCode        string `json:"procedureCode"`
	ProcedureName        string `json:"procedureCodeName"`
}

func LoadConditions() []ConditionMetadata {
	j, err := Asset("data/conditions.json")
	if err != nil {
		panic("Can't get the condition data")
	}
	decoder := json.NewDecoder(bytes.NewReader(j))
	md := []ConditionMetadata{}
	decoder.Decode(&md)
	return md
}

func GenerateConditions(ctx Context, md []ConditionMetadata) []models.Condition {
	conditions := []models.Condition{}
	if ctx.Hypertention == "Hypertension" {
		ht := generateCondition("Hypertension", 2, md)
		conditions = append(conditions, ht)
		complication := rand.Intn(5)
		if complication == 1 {
			chf := models.Condition{}
			chfmd := conditionByName("Congestive Heart Failure", md)
			chf.Code = &models.CodeableConcept{Coding: []models.Coding{{Code: chfmd.ICD9, System: "http://hl7.org/fhir/sid/icd-9"}}, Text: chfmd.Display}
			chf.OnsetDateTime = &models.FHIRDateTime{Time: ht.OnsetDateTime.Time.AddDate(rand.Intn(2), rand.Intn(10), rand.Intn(28)), Precision: models.Date}
			conditions = append(conditions, chf)
		}
		if complication == 2 {
			phd := models.Condition{}
			phdmd := conditionByName("Pulmonary Heart Disease", md)
			phd.Code = &models.CodeableConcept{Coding: []models.Coding{{Code: phdmd.ICD9, System: "http://hl7.org/fhir/sid/icd-9"}}, Text: phdmd.Display}
			phd.OnsetDateTime = &models.FHIRDateTime{Time: ht.OnsetDateTime.Time.AddDate(rand.Intn(2), rand.Intn(10), rand.Intn(28)), Precision: models.Date}
			conditions = append(conditions, phd)
		}
	}
	if ctx.Diabetes == "Diabetes" {
		dia := generateCondition("Diabetes", 2, md)
		conditions = append(conditions, dia)
	}

	if ctx.Smoker == "Smoker" {
		complication := rand.Intn(5)
		if complication == 1 {
			e := generateCondition("Emphysema", 2, md)
			conditions = append(conditions, e)
		}
		if complication == 2 {
			lc := generateCondition("Lung Cancer", 2, md)
			conditions = append(conditions, lc)
		}
	}
	// per http://www.cdc.gov/dhdsp/data_statistics/fact_sheets/fs_atrial_fibrillation.htm
	var afibChance int
	if time.Now().AddDate(-65, 0, 0).After(ctx.BirthDate) {
		afibChance = 9
	} else {
		afibChance = 2
	}

	afibDiceRoll := rand.Intn(100)
	if afibDiceRoll <= afibChance {
		afib := generateCondition("Atrial Fibrillation", 3, md)
		conditions = append(conditions, afib)
	}

	otherConditions := rand.Intn(3)
	previouslySelected := &intsets.Sparse{}
	for index := 0; index < otherConditions; index++ {
		randomCondition := 2 + rand.Intn(76)
		if !previouslySelected.Has(randomCondition) {
			rmd := conditionByID(randomCondition, md)
			rc := generateCondition(rmd.Display, index, md)
			previouslySelected.Insert(randomCondition)
			conditions = append(conditions, rc)
		}
	}

	return conditions
}

func generateCondition(name string, yearOffset int, md []ConditionMetadata) models.Condition {
	c := models.Condition{}
	cmd := conditionByName(name, md)
	c.Code = &models.CodeableConcept{Coding: []models.Coding{{Code: cmd.ICD9, System: "http://hl7.org/fhir/sid/icd-9"}}, Text: cmd.Display}
	c.OnsetDateTime = &models.FHIRDateTime{Time: randomOnset(yearOffset), Precision: models.Date}
	recoveryDiceRoll := rand.Intn(100)
	if recoveryDiceRoll <= cmd.AbatementChance {
		switch cmd.RecoveryEstimate {
		case "week":
			c.AbatementDateTime = &models.FHIRDateTime{Time: c.OnsetDateTime.Time.AddDate(0, 0, 7), Precision: models.Timestamp}
		case "threeMonths":
			c.AbatementDateTime = &models.FHIRDateTime{Time: c.OnsetDateTime.Time.AddDate(0, 3, 0), Precision: models.Timestamp}
		case "sixMonths":
			c.AbatementDateTime = &models.FHIRDateTime{Time: c.OnsetDateTime.Time.AddDate(0, 6, 0), Precision: models.Timestamp}
		case "threeYears":
			c.AbatementDateTime = &models.FHIRDateTime{Time: c.OnsetDateTime.Time.AddDate(3, 0, 0), Precision: models.Timestamp}
		}
	}

	return c
}

func conditionByName(name string, md []ConditionMetadata) *ConditionMetadata {
	for _, c := range md {
		if c.Display == name {
			return &c
		}
	}
	return nil
}

func conditionByID(id int, md []ConditionMetadata) *ConditionMetadata {
	for _, c := range md {
		if c.ID == id {
			return &c
		}
	}
	return nil
}

func randomOnset(minYearsAgo int) time.Time {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	randomYears := minYearsAgo + r.Intn(3)
	randomMonth := r.Intn(11)
	randomDay := r.Intn(28)
	t := time.Now()
	return t.AddDate(-randomYears, -randomMonth, -randomDay).Truncate(time.Hour * 24)
}
<|endoftext|>


<commit_before>// +build linux

package net

import (
	"net"

	"veyron2"

	"veyron/profiles/internal/gce"
)

func handleGCE(rt veyron2.Runtime, publisher *config.Publisher) bool {
	log := rt.Logger()
	if gce.RunningOnGCE() {
		var pub net.IP
		pub = publish_addr.IP
		if pub == nil {
			// Determine the IP address from GCE's metadata
			if ip, err := gce.ExternalIPAddress(); err != nil {
				log.Infof("failed to query GCE metadata: %s", err)
			} else {
				// 1:1 NAT case, our network config will not change.
				pub = ip
			}
		}
		if pub == nil {
			log.Infof("failed to determine public IP address to publish with")
		}

		ch := make(chan config.Setting)
		defer close(ch)
		if _, err := publisher.CreateStream(StreamName, "network configuration", ch); err != nil {
			return nil, nil, err
		}
		publishInitialSettings(ch, listenProtocol, listenSpec, prevAddr)
		return true
	}
	return false
}
<commit_msg>TBR: veyron/profiles/net: fix the build.<commit_after>// +build linux

package net

import (
	"net"

	"veyron2"
	"veyron2/config"

	"veyron/profiles/internal/gce"
)

func handleGCE(rt veyron2.Runtime, publisher *config.Publisher) bool {
	log := rt.Logger()
	if gce.RunningOnGCE() {
		var pub net.IP
		// Determine the IP address from GCE's metadata
		if ip, err := gce.ExternalIPAddress(); err != nil {
			log.Infof("failed to query GCE metadata: %s", err)
		} else {
			// 1:1 NAT case, our network config will not change.
			pub = ip
		}
		if pub == nil {
			log.Infof("failed to determine public IP address to publish with")
		}

		ch := make(chan config.Setting)
		defer close(ch)
		if _, err := publisher.CreateStream(StreamName, "network configuration", ch); err != nil {
			return false
		}
		publishInitialSettings(ch, listen_protocol, listen_addr.IP.String(), pub)
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru 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 docker

import (
	"fmt"
	"strings"

	"github.com/fsouza/go-dockerclient"
	"github.com/tsuru/config"
	"github.com/tsuru/docker-cluster/cluster"
	"github.com/tsuru/docker-cluster/storage"
	"github.com/tsuru/tsuru/app"
	"github.com/tsuru/tsuru/db"
	dbStorage "github.com/tsuru/tsuru/db/storage"
	"github.com/tsuru/tsuru/log"
	"github.com/tsuru/tsuru/provision"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type appImages struct {
	AppName string `bson:"_id"`
	Images  []string
	Count   int
}

func migrateImages() error {
	registry, _ := config.GetString("docker:registry")
	if registry != "" {
		registry += "/"
	}
	repoNamespace, err := config.GetString("docker:repository-namespace")
	if err != nil {
		return err
	}
	apps, err := app.List(nil)
	if err != nil {
		return err
	}
	dcluster := dockerCluster()
	for _, app := range apps {
		oldImage := registry + repoNamespace + "/" + app.Name
		newImage := registry + repoNamespace + "/app-" + app.Name
		opts := docker.TagImageOptions{Repo: newImage, Force: true}
		err = dcluster.TagImage(oldImage, opts)
		var baseErr error
		if nodeErr, ok := err.(cluster.DockerNodeError); ok {
			baseErr = nodeErr.BaseError()
		}
		if err != nil && err != storage.ErrNoSuchImage && baseErr != docker.ErrNoSuchImage {
			return err
		}
		if registry != "" {
			pushOpts := docker.PushImageOptions{Name: newImage}
			err = dcluster.PushImage(pushOpts, docker.AuthConfiguration{})
			if err != nil {
				return err
			}
		}
		err = updateContainers(bson.M{"appname": app.Name}, bson.M{"$set": bson.M{"image": newImage}})
		if err != nil {
			return err
		}
	}
	return nil
}

// getBuildImage returns the image name from app or plaftorm.
// the platform image will be returned if:
// * there are no containers;
// * the container have an empty image name;
// * the deploy number is multiple of 10.
// in all other cases the app image name will be returne.
func getBuildImage(app provision.App) string {
	if usePlatformImage(app) {
		return platformImageName(app.GetPlatform())
	}
	appImageName, err := appCurrentImageName(app.GetName())
	if err != nil {
		return platformImageName(app.GetPlatform())
	}
	return appImageName
}

func appImagesColl() (*dbStorage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, err
	}
	name, err := config.GetString("docker:collection")
	if err != nil {
		return nil, err
	}
	return conn.Collection(fmt.Sprintf("%s_app_image", name)), nil
}

func imageCustomDataColl() (*dbStorage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, err
	}
	name, err := config.GetString("docker:collection")
	if err != nil {
		return nil, err
	}
	return conn.Collection(fmt.Sprintf("%s_image_custom_data", name)), nil
}

func saveImageCustomData(imageName string, customData map[string]interface{}) error {
	coll, err := imageCustomDataColl()
	if err != nil {
		return err
	}
	defer coll.Close()
	return coll.Insert(bson.M{"_id": imageName, "customdata": customData})
}

func getImageTsuruYamlData(imageName string) (provision.TsuruYamlData, error) {
	var customData struct {
		Customdata provision.TsuruYamlData
	}
	coll, err := imageCustomDataColl()
	if err != nil {
		return customData.Customdata, err
	}
	defer coll.Close()
	err = coll.FindId(imageName).One(&customData)
	return customData.Customdata, err
}

// TODO(cezarsa): This method only exist to keep tsuru compatible with older
// platforms. It should be deprecated in the next major after 0.10.0.
func getImageTsuruYamlDataWithFallback(imageName, appName string) (provision.TsuruYamlData, error) {
	yamlData, err := getImageTsuruYamlData(imageName)
	if err != nil {
		a, err := app.GetByName(appName)
		if err != nil {
			// Ignored error if app not found
			return yamlData, nil
		}
		return a.GetTsuruYamlData()
	}
	return yamlData, nil
}

func appNewImageName(appName string) (string, error) {
	coll, err := appImagesColl()
	if err != nil {
		return "", err
	}
	defer coll.Close()
	var imgs appImages
	dbChange := mgo.Change{
		Update:    bson.M{"$inc": bson.M{"count": 1}},
		ReturnNew: true,
		Upsert:    true,
	}
	_, err = coll.FindId(appName).Apply(dbChange, &imgs)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s/app-%s:v%d", basicImageName(), appName, imgs.Count), nil
}

func appCurrentImageName(appName string) (string, error) {
	coll, err := appImagesColl()
	if err != nil {
		return "", err
	}
	defer coll.Close()
	var imgs appImages
	err = coll.FindId(appName).One(&imgs)
	if err != nil {
		log.Errorf("Couldn't find images for app %q, fallback to old image names. Error: %s", appName, err.Error())
		return fmt.Sprintf("%s/app-%s", basicImageName(), appName), nil
	}
	if len(imgs.Images) == 0 {
		return "", fmt.Errorf("no images available for app %q", appName)
	}
	return imgs.Images[len(imgs.Images)-1], nil
}

func appendAppImageName(appName, imageId string) error {
	coll, err := appImagesColl()
	if err != nil {
		return err
	}
	defer coll.Close()
	_, err = coll.UpsertId(appName, bson.M{"$pull": bson.M{"images": imageId}})
	if err != nil {
		return err
	}
	_, err = coll.UpsertId(appName, bson.M{"$push": bson.M{"images": imageId}})
	return err
}

func listAppImages(appName string) ([]string, error) {
	coll, err := appImagesColl()
	if err != nil {
		return nil, err
	}
	defer coll.Close()
	var imgs appImages
	err = coll.FindId(appName).One(&imgs)
	if err != nil {
		return nil, err
	}
	return imgs.Images, nil
}

func listValidAppImages(appName string) ([]string, error) {
	coll, err := appImagesColl()
	if err != nil {
		return nil, err
	}
	defer coll.Close()
	var img appImages
	err = coll.FindId(appName).One(&img)
	if err != nil {
		if err == mgo.ErrNotFound {
			return []string{}, nil
		}
		return nil, err
	}
	historySize := imageHistorySize()
	if len(img.Images) > historySize {
		img.Images = img.Images[len(img.Images)-historySize:]
	}
	return img.Images, nil
}

func isValidAppImage(appName, imageId string) (bool, error) {
	images, err := listValidAppImages(appName)
	if err != nil && err != mgo.ErrNotFound {
		return false, err
	}
	for _, img := range images {
		if img == imageId {
			return true, nil
		}
	}
	return false, nil
}

func imageHistorySize() int {
	imgHistorySize, _ := config.GetInt("docker:image-history-size")
	if imgHistorySize == 0 {
		imgHistorySize = 10
	}
	return imgHistorySize
}

func deleteAllAppImageNames(appName string) error {
	images, err := listAppImages(appName)
	if err != nil {
		return err
	}
	dataColl, err := imageCustomDataColl()
	if err != nil {
		return err
	}
	defer dataColl.Close()
	_, err = dataColl.RemoveAll(bson.M{"_id": bson.M{"$in": images}})
	if err != nil {
		return err
	}
	coll, err := appImagesColl()
	if err != nil {
		return err
	}
	defer coll.Close()
	return coll.RemoveId(appName)
}

func pullAppImageNames(appName string, images []string) error {
	dataColl, err := imageCustomDataColl()
	if err != nil {
		return err
	}
	defer dataColl.Close()
	_, err = dataColl.RemoveAll(bson.M{"_id": bson.M{"$in": images}})
	if err != nil {
		return err
	}
	coll, err := appImagesColl()
	if err != nil {
		return err
	}
	defer coll.Close()
	return coll.UpdateId(appName, bson.M{"$pullAll": bson.M{"images": images}})
}

func platformImageName(platformName string) string {
	return fmt.Sprintf("%s/%s", basicImageName(), platformName)
}

func basicImageName() string {
	parts := make([]string, 0, 2)
	registry, _ := config.GetString("docker:registry")
	if registry != "" {
		parts = append(parts, registry)
	}
	repoNamespace, _ := config.GetString("docker:repository-namespace")
	parts = append(parts, repoNamespace)
	return strings.Join(parts, "/")
}

func usePlatformImage(app provision.App) bool {
	deploys := app.GetDeploys()
	if (deploys != 0 && deploys%10 == 0) || app.GetUpdatePlatform() {
		return true
	}
	c, err := getOneContainerByAppName(app.GetName())
	if err != nil || c.Image == "" {
		return true
	}
	return false
}
<commit_msg>provision/docker: if image is not available we shouldnt try to push it<commit_after>// Copyright 2015 tsuru 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 docker

import (
	"fmt"
	"strings"

	"github.com/fsouza/go-dockerclient"
	"github.com/tsuru/config"
	"github.com/tsuru/docker-cluster/cluster"
	"github.com/tsuru/docker-cluster/storage"
	"github.com/tsuru/tsuru/app"
	"github.com/tsuru/tsuru/db"
	dbStorage "github.com/tsuru/tsuru/db/storage"
	"github.com/tsuru/tsuru/log"
	"github.com/tsuru/tsuru/provision"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type appImages struct {
	AppName string `bson:"_id"`
	Images  []string
	Count   int
}

func migrateImages() error {
	registry, _ := config.GetString("docker:registry")
	if registry != "" {
		registry += "/"
	}
	repoNamespace, err := config.GetString("docker:repository-namespace")
	if err != nil {
		return err
	}
	apps, err := app.List(nil)
	if err != nil {
		return err
	}
	dcluster := dockerCluster()
	for _, app := range apps {
		oldImage := registry + repoNamespace + "/" + app.Name
		newImage := registry + repoNamespace + "/app-" + app.Name
		opts := docker.TagImageOptions{Repo: newImage, Force: true}
		err = dcluster.TagImage(oldImage, opts)
		var baseErr error
		if nodeErr, ok := err.(cluster.DockerNodeError); ok {
			baseErr = nodeErr.BaseError()
		}
		if err != nil {
			if err == storage.ErrNoSuchImage || baseErr == docker.ErrNoSuchImage {
				continue
			}
			return err
		}
		if registry != "" {
			pushOpts := docker.PushImageOptions{Name: newImage}
			err = dcluster.PushImage(pushOpts, docker.AuthConfiguration{})
			if err != nil {
				return err
			}
		}
		err = updateContainers(bson.M{"appname": app.Name}, bson.M{"$set": bson.M{"image": newImage}})
		if err != nil {
			return err
		}
	}
	return nil
}

// getBuildImage returns the image name from app or plaftorm.
// the platform image will be returned if:
// * there are no containers;
// * the container have an empty image name;
// * the deploy number is multiple of 10.
// in all other cases the app image name will be returne.
func getBuildImage(app provision.App) string {
	if usePlatformImage(app) {
		return platformImageName(app.GetPlatform())
	}
	appImageName, err := appCurrentImageName(app.GetName())
	if err != nil {
		return platformImageName(app.GetPlatform())
	}
	return appImageName
}

func appImagesColl() (*dbStorage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, err
	}
	name, err := config.GetString("docker:collection")
	if err != nil {
		return nil, err
	}
	return conn.Collection(fmt.Sprintf("%s_app_image", name)), nil
}

func imageCustomDataColl() (*dbStorage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, err
	}
	name, err := config.GetString("docker:collection")
	if err != nil {
		return nil, err
	}
	return conn.Collection(fmt.Sprintf("%s_image_custom_data", name)), nil
}

func saveImageCustomData(imageName string, customData map[string]interface{}) error {
	coll, err := imageCustomDataColl()
	if err != nil {
		return err
	}
	defer coll.Close()
	return coll.Insert(bson.M{"_id": imageName, "customdata": customData})
}

func getImageTsuruYamlData(imageName string) (provision.TsuruYamlData, error) {
	var customData struct {
		Customdata provision.TsuruYamlData
	}
	coll, err := imageCustomDataColl()
	if err != nil {
		return customData.Customdata, err
	}
	defer coll.Close()
	err = coll.FindId(imageName).One(&customData)
	return customData.Customdata, err
}

// TODO(cezarsa): This method only exist to keep tsuru compatible with older
// platforms. It should be deprecated in the next major after 0.10.0.
func getImageTsuruYamlDataWithFallback(imageName, appName string) (provision.TsuruYamlData, error) {
	yamlData, err := getImageTsuruYamlData(imageName)
	if err != nil {
		a, err := app.GetByName(appName)
		if err != nil {
			// Ignored error if app not found
			return yamlData, nil
		}
		return a.GetTsuruYamlData()
	}
	return yamlData, nil
}

func appNewImageName(appName string) (string, error) {
	coll, err := appImagesColl()
	if err != nil {
		return "", err
	}
	defer coll.Close()
	var imgs appImages
	dbChange := mgo.Change{
		Update:    bson.M{"$inc": bson.M{"count": 1}},
		ReturnNew: true,
		Upsert:    true,
	}
	_, err = coll.FindId(appName).Apply(dbChange, &imgs)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s/app-%s:v%d", basicImageName(), appName, imgs.Count), nil
}

func appCurrentImageName(appName string) (string, error) {
	coll, err := appImagesColl()
	if err != nil {
		return "", err
	}
	defer coll.Close()
	var imgs appImages
	err = coll.FindId(appName).One(&imgs)
	if err != nil {
		log.Errorf("Couldn't find images for app %q, fallback to old image names. Error: %s", appName, err.Error())
		return fmt.Sprintf("%s/app-%s", basicImageName(), appName), nil
	}
	if len(imgs.Images) == 0 {
		return "", fmt.Errorf("no images available for app %q", appName)
	}
	return imgs.Images[len(imgs.Images)-1], nil
}

func appendAppImageName(appName, imageId string) error {
	coll, err := appImagesColl()
	if err != nil {
		return err
	}
	defer coll.Close()
	_, err = coll.UpsertId(appName, bson.M{"$pull": bson.M{"images": imageId}})
	if err != nil {
		return err
	}
	_, err = coll.UpsertId(appName, bson.M{"$push": bson.M{"images": imageId}})
	return err
}

func listAppImages(appName string) ([]string, error) {
	coll, err := appImagesColl()
	if err != nil {
		return nil, err
	}
	defer coll.Close()
	var imgs appImages
	err = coll.FindId(appName).One(&imgs)
	if err != nil {
		return nil, err
	}
	return imgs.Images, nil
}

func listValidAppImages(appName string) ([]string, error) {
	coll, err := appImagesColl()
	if err != nil {
		return nil, err
	}
	defer coll.Close()
	var img appImages
	err = coll.FindId(appName).One(&img)
	if err != nil {
		if err == mgo.ErrNotFound {
			return []string{}, nil
		}
		return nil, err
	}
	historySize := imageHistorySize()
	if len(img.Images) > historySize {
		img.Images = img.Images[len(img.Images)-historySize:]
	}
	return img.Images, nil
}

func isValidAppImage(appName, imageId string) (bool, error) {
	images, err := listValidAppImages(appName)
	if err != nil && err != mgo.ErrNotFound {
		return false, err
	}
	for _, img := range images {
		if img == imageId {
			return true, nil
		}
	}
	return false, nil
}

func imageHistorySize() int {
	imgHistorySize, _ := config.GetInt("docker:image-history-size")
	if imgHistorySize == 0 {
		imgHistorySize = 10
	}
	return imgHistorySize
}

func deleteAllAppImageNames(appName string) error {
	images, err := listAppImages(appName)
	if err != nil {
		return err
	}
	dataColl, err := imageCustomDataColl()
	if err != nil {
		return err
	}
	defer dataColl.Close()
	_, err = dataColl.RemoveAll(bson.M{"_id": bson.M{"$in": images}})
	if err != nil {
		return err
	}
	coll, err := appImagesColl()
	if err != nil {
		return err
	}
	defer coll.Close()
	return coll.RemoveId(appName)
}

func pullAppImageNames(appName string, images []string) error {
	dataColl, err := imageCustomDataColl()
	if err != nil {
		return err
	}
	defer dataColl.Close()
	_, err = dataColl.RemoveAll(bson.M{"_id": bson.M{"$in": images}})
	if err != nil {
		return err
	}
	coll, err := appImagesColl()
	if err != nil {
		return err
	}
	defer coll.Close()
	return coll.UpdateId(appName, bson.M{"$pullAll": bson.M{"images": images}})
}

func platformImageName(platformName string) string {
	return fmt.Sprintf("%s/%s", basicImageName(), platformName)
}

func basicImageName() string {
	parts := make([]string, 0, 2)
	registry, _ := config.GetString("docker:registry")
	if registry != "" {
		parts = append(parts, registry)
	}
	repoNamespace, _ := config.GetString("docker:repository-namespace")
	parts = append(parts, repoNamespace)
	return strings.Join(parts, "/")
}

func usePlatformImage(app provision.App) bool {
	deploys := app.GetDeploys()
	if (deploys != 0 && deploys%10 == 0) || app.GetUpdatePlatform() {
		return true
	}
	c, err := getOneContainerByAppName(app.GetName())
	if err != nil || c.Image == "" {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package agi

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
)

// State describes the Asterisk channel state.  There are mapped
// directly to the Asterisk enumerations.
type State int

const (
	// StateDown indicates the channel is down and available
	StateDown State = iota

	// StateReserved indicates the channel is down but reserved
	StateReserved

	// StateOffhook indicates that the channel is offhook
	StateOffhook

	// StateDialing indicates that digits have been dialed
	StateDialing

	// StateRing indicates the channel is ringing
	StateRing

	// StateRinging indicates the channel's remote end is rining (the channel is receiving ringback)
	StateRinging

	// StateUp indicates the channel is up
	StateUp

	// StateBusy indicates the line is busy
	StateBusy

	// StateDialingOffHook indicates digits have been dialed while offhook
	StateDialingOffHook

	// StatePreRing indicates the channel has detected an incoming call and is waiting for ring
	StatePreRing
)

// AGI represents an AGI session
type AGI struct {
	// Variables stored the initial variables
	// transmitted from Asterisk at the start
	// of the AGI session.
	Variables map[string]string

	r    io.Reader
	eagi io.Reader
	w    io.Writer

	conn net.Conn

	mu sync.Mutex
}

// Response represents a response to an AGI
// request.
type Response struct {
	Error        error  // Error received, if any
	Status       int    // HTTP-style status code received
	Result       int    // Result is the numerical return (if parseable)
	ResultString string // Result value as a string
	Value        string // Value is the (optional) string value returned
}

// Err returns the error value from the response
func (r *Response) Err() error {
	return r.Error
}

// Val returns the response value and error
func (r *Response) Val() (string, error) {
	return r.Value, r.Error
}

// Regex for AGI response result code and value
var responseRegex = regexp.MustCompile(`^([\d]{3})\sresult=(\-?[[:alnum:]]*)(\s.*)?$`)

const (
	// StatusOK indicates the AGI command was
	// accepted.
	StatusOK = 200

	// StatusInvalid indicates Asterisk did not
	// understand the command.
	StatusInvalid = 510

	// StatusDeadChannel indicates that the command
	// cannot be performed on a dead (hungup) channel.
	StatusDeadChannel = 511

	// StatusEndUsage indicates...TODO
	StatusEndUsage = 520
)

// HandlerFunc is a function which accepts an AGI instance
type HandlerFunc func(*AGI)

// New creates an AGI session from the given reader and writer.
func New(r io.Reader, w io.Writer) *AGI {
	return NewWithEAGI(r, w, nil)
}

// NewWithEAGI returns a new AGI session to the given `os.Stdin` `io.Reader`,
// EAGI `io.Reader`, and `os.Stdout` `io.Writer`. The initial variables will
// be read in.
func NewWithEAGI(r io.Reader, w io.Writer, eagi io.Reader) *AGI {
	a := AGI{
		Variables: make(map[string]string),
		r:         r,
		w:         w,
		eagi:      eagi,
	}

	s := bufio.NewScanner(a.r)
	for s.Scan() {
		if s.Text() == "" {
			break
		}

		terms := strings.Split(s.Text(), ":")
		if len(terms) == 2 {
			a.Variables[strings.TrimSpace(terms[0])] = strings.TrimSpace(terms[1])
		}
	}

	return &a
}

// NewConn returns a new AGI session bound to the given net.Conn interface
func NewConn(conn net.Conn) *AGI {
	a := New(conn, conn)
	a.conn = conn
	return a
}

// NewStdio returns a new AGI session to stdin and stdout.
func NewStdio() *AGI {
	return New(os.Stdin, os.Stdout)
}

// NewEAGI returns a new AGI session to stdin, the EAGI stream (FD=3), and stdout.
func NewEAGI() *AGI {
	return NewWithEAGI(os.Stdin, os.Stdout, os.NewFile(uintptr(3), "/dev/stdeagi"))
}

// Listen binds an AGI HandlerFunc to the given TCP `host:port` address, creating a FastAGI service.
func Listen(addr string, handler HandlerFunc) error {
	if addr == "" {
		addr = "localhost:4573"
	}

	l, err := net.Listen("tcp", addr)
	if err != nil {
		return errors.Wrap(err, "failed to bind server")
	}
	defer l.Close() // nolint: errcheck

	for {
		conn, err := l.Accept()
		if err != nil {
			return errors.Wrap(err, "failed to accept TCP connection")
		}

		go handler(NewConn(conn))
	}
}

// Close closes any network connection associated with the AGI instance
func (a *AGI) Close() (err error) {
	if a.conn != nil {
		err = a.conn.Close()
		a.conn = nil
	}
	return
}

// EAGI enables access to the EAGI incoming stream (if available).
func (a *AGI) EAGI() io.Reader {
	return a.eagi
}

// Command sends the given command line to stdout
// and returns the response.
// TODO: this does not handle multi-line responses properly
func (a *AGI) Command(cmd ...string) (resp *Response) {
	resp = &Response{}

	a.mu.Lock()
	defer a.mu.Unlock()

	cmdString := strings.Join(cmd, " ") + "\n"
	_, err := a.w.Write([]byte(cmdString))
	if err != nil {
		resp.Error = errors.Wrap(err, "failed to send command")
		return
	}

	s := bufio.NewScanner(a.r)
	for s.Scan() {
		raw := s.Text()
		if raw == "" {
			break
		}

		// Parse and store the result code
		pieces := responseRegex.FindStringSubmatch(raw)
		if pieces == nil {
			resp.Error = fmt.Errorf("failed to parse result: %s", raw)
			return
		}

		// Status code is the first substring
		resp.Status, err = strconv.Atoi(pieces[1])
		if err != nil {
			resp.Error = errors.Wrap(err, "failed to get status code")
			return
		}

		// Result code is the second substring
		resp.ResultString = pieces[2]
		resp.Result, err = strconv.Atoi(pieces[2])
		if err != nil {
			resp.Error = errors.Wrap(err, "failed to parse status code as an integer")
			return
		}

		// Value is the third (and optional) substring
		wrappedVal := strings.TrimSpace(pieces[3])
		resp.Value = strings.TrimSuffix(strings.TrimPrefix(wrappedVal, "("), ")")

		break
	}

	// If the Status code is not 200, return an error
	if resp.Status != 200 {
		resp.Error = fmt.Errorf("Non-200 status code")
	}
	return
}

// Answer answers the channel
func (a *AGI) Answer() error {
	return a.Command("ANSWER").Err()
}

// Status returns the channel status
func (a *AGI) Status() (State, error) {
	r, err := a.Command("CHANNEL STATUS").Val()
	if err != nil {
		return StateDown, err
	}
	state, err := strconv.Atoi(r)
	if err != nil {
		return StateDown, fmt.Errorf("Failed to parse state %s", r)
	}
	return State(state), nil
}

// Exec runs a dialplan application
func (a *AGI) Exec(cmd ...string) (string, error) {
	cmd = append([]string{"EXEC"}, cmd...)
	return a.Command(cmd...).Val()
}

// Get gets the value of the given channel variable
func (a *AGI) Get(key string) (string, error) {
	return a.Command("GET FULL VARIABLE", key).Val()
}

// GetData plays a file and receives DTMF, returning the received digits
func (a *AGI) GetData(name string, timeout int, maxdigits int) (digit string, err error) {
	return a.Command("GET DATA", name, strconv.Itoa(timeout), strconv.Itoa(maxdigits)).Val()
}

// Hangup terminates the call
func (a *AGI) Hangup() error {
	return a.Command("HANGUP").Err()
}

// RecordOptions describes the options available when recording
type RecordOptions struct {
	// Format is the format of the audio file to record; defaults to "wav".
	Format string

	// EscapeDigits is the set of digits on receipt of which will terminate the recording. Default is "#".  This may not be blank.
	EscapeDigits string

	// Timeout is the maximum time to allow for the recording.  Defaults to 5 minutes.
	Timeout time.Duration

	// Silence is the maximum amount of silence to allow before ending the recording.  The finest resolution is to the second.   0=disabled, which is the default.
	Silence time.Duration

	// Beep controls whether a beep is played before starting the recording.  Defaults to false.
	Beep bool

	// Offset is the number of samples in the recording to advance before storing to the file.  This is means of clipping the beginning of a recording.  Defaults to 0.
	Offset int
}

// Record records audio to a file
func (a *AGI) Record(name string, opts *RecordOptions) error {
	if opts == nil {
		opts = &RecordOptions{}
	}
	if opts.Format == "" {
		opts.Format = "wav"
	}
	if opts.EscapeDigits == "" {
		opts.EscapeDigits = "#"
	}
	if opts.Timeout == 0 {
		opts.Timeout = 5 * time.Minute
	}

	cmd := strings.Join([]string{
		"RECORD FILE ",
		name,
		opts.Format,
		opts.EscapeDigits,
		toMSec(opts.Timeout),
	}, " ")

	if opts.Offset > 0 {
		cmd += " " + strconv.Itoa(opts.Offset)
	}

	if opts.Beep {
		cmd += " BEEP"
	}

	if opts.Silence > 0 {
		cmd += " s=" + toSec(opts.Silence)
	}

	return a.Command(cmd).Err()
}

// SayAlpha plays a character string, annunciating each character.
func (a *AGI) SayAlpha(label string, escapeDigits string) (digit string, err error) {
	return a.Command("SAY ALPHA", label, escapeDigits).Val()
}

// SayDigits plays a digit string, annunciating each digit.
func (a *AGI) SayDigits(number string, escapeDigits string) (digit string, err error) {
	return a.Command("SAY DIGITS", number, escapeDigits).Val()
}

// SayDate plays a date
func (a *AGI) SayDate(when time.Time, escapeDigits string) (digit string, err error) {
	return a.Command("SAY DATE", toEpoch(when), escapeDigits).Val()
}

// SayDateTime plays a date using the given format.  See `voicemail.conf` for the format syntax; defaults to `ABdY 'digits/at' IMp`.
func (a *AGI) SayDateTime(when time.Time, escapeDigits string, format string) (digit string, err error) {
	// Extract the timezone from the time
	zone, _ := when.Zone()

	// Use the Asterisk default format if we are not given one
	if format == "" {
		format = "ABdY 'digits/at' IMp"
	}

	return a.Command("SAY DATETIME", toEpoch(when), escapeDigits, format, zone).Val()
}

// SayNumber plays the given number.
func (a *AGI) SayNumber(number string, escapeDigits string) (digit string, err error) {
	return a.Command("SAY NUMBER", number, escapeDigits).Val()
}

// SayPhonetic plays the given phrase phonetically
func (a *AGI) SayPhonetic(phrase string, escapeDigits string) (digit string, err error) {
	return a.Command("SAY PHOENTIC", phrase, escapeDigits).Val()
}

// SayTime plays the time part of the given timestamp
func (a *AGI) SayTime(when time.Time, escapeDigits string) (digit string, err error) {
	return a.Command("SAY TIME", toEpoch(when), escapeDigits).Val()
}

// Set sets the given channel variable to
// the provided value.
func (a *AGI) Set(key, val string) error {
	return a.Command("SET VARIABLE", key, val).Err()
}

// StreamFile plays the given file to the channel
func (a *AGI) StreamFile(name string, escapeDigits string, offset int) (digit string, err error) {
	return a.Command("STREAM FILE", name, escapeDigits, strconv.Itoa(offset)).Val()
}

// Verbose logs the given message to the verbose message system
func (a *AGI) Verbose(msg string, level int) error {
	return a.Command("VERBOSE", msg, strconv.Itoa(level)).Err()
}

// WaitForDigit waits for a DTMF digit and returns what is received
func (a *AGI) WaitForDigit(timeout time.Duration) (digit string, err error) {
	return a.Command("WAIT FOR DIGIT", toMSec(timeout)).Val()
}
<commit_msg>GET FULL VARIABLE appears to be broken with modern asterisk; use GET VARIABLE instead<commit_after>package agi

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
)

// State describes the Asterisk channel state.  There are mapped
// directly to the Asterisk enumerations.
type State int

const (
	// StateDown indicates the channel is down and available
	StateDown State = iota

	// StateReserved indicates the channel is down but reserved
	StateReserved

	// StateOffhook indicates that the channel is offhook
	StateOffhook

	// StateDialing indicates that digits have been dialed
	StateDialing

	// StateRing indicates the channel is ringing
	StateRing

	// StateRinging indicates the channel's remote end is rining (the channel is receiving ringback)
	StateRinging

	// StateUp indicates the channel is up
	StateUp

	// StateBusy indicates the line is busy
	StateBusy

	// StateDialingOffHook indicates digits have been dialed while offhook
	StateDialingOffHook

	// StatePreRing indicates the channel has detected an incoming call and is waiting for ring
	StatePreRing
)

// AGI represents an AGI session
type AGI struct {
	// Variables stored the initial variables
	// transmitted from Asterisk at the start
	// of the AGI session.
	Variables map[string]string

	r    io.Reader
	eagi io.Reader
	w    io.Writer

	conn net.Conn

	mu sync.Mutex
}

// Response represents a response to an AGI
// request.
type Response struct {
	Error        error  // Error received, if any
	Status       int    // HTTP-style status code received
	Result       int    // Result is the numerical return (if parseable)
	ResultString string // Result value as a string
	Value        string // Value is the (optional) string value returned
}

// Err returns the error value from the response
func (r *Response) Err() error {
	return r.Error
}

// Val returns the response value and error
func (r *Response) Val() (string, error) {
	return r.Value, r.Error
}

// Regex for AGI response result code and value
var responseRegex = regexp.MustCompile(`^([\d]{3})\sresult=(\-?[[:alnum:]]*)(\s.*)?$`)

const (
	// StatusOK indicates the AGI command was
	// accepted.
	StatusOK = 200

	// StatusInvalid indicates Asterisk did not
	// understand the command.
	StatusInvalid = 510

	// StatusDeadChannel indicates that the command
	// cannot be performed on a dead (hungup) channel.
	StatusDeadChannel = 511

	// StatusEndUsage indicates...TODO
	StatusEndUsage = 520
)

// HandlerFunc is a function which accepts an AGI instance
type HandlerFunc func(*AGI)

// New creates an AGI session from the given reader and writer.
func New(r io.Reader, w io.Writer) *AGI {
	return NewWithEAGI(r, w, nil)
}

// NewWithEAGI returns a new AGI session to the given `os.Stdin` `io.Reader`,
// EAGI `io.Reader`, and `os.Stdout` `io.Writer`. The initial variables will
// be read in.
func NewWithEAGI(r io.Reader, w io.Writer, eagi io.Reader) *AGI {
	a := AGI{
		Variables: make(map[string]string),
		r:         r,
		w:         w,
		eagi:      eagi,
	}

	s := bufio.NewScanner(a.r)
	for s.Scan() {
		if s.Text() == "" {
			break
		}

		terms := strings.Split(s.Text(), ":")
		if len(terms) == 2 {
			a.Variables[strings.TrimSpace(terms[0])] = strings.TrimSpace(terms[1])
		}
	}

	return &a
}

// NewConn returns a new AGI session bound to the given net.Conn interface
func NewConn(conn net.Conn) *AGI {
	a := New(conn, conn)
	a.conn = conn
	return a
}

// NewStdio returns a new AGI session to stdin and stdout.
func NewStdio() *AGI {
	return New(os.Stdin, os.Stdout)
}

// NewEAGI returns a new AGI session to stdin, the EAGI stream (FD=3), and stdout.
func NewEAGI() *AGI {
	return NewWithEAGI(os.Stdin, os.Stdout, os.NewFile(uintptr(3), "/dev/stdeagi"))
}

// Listen binds an AGI HandlerFunc to the given TCP `host:port` address, creating a FastAGI service.
func Listen(addr string, handler HandlerFunc) error {
	if addr == "" {
		addr = "localhost:4573"
	}

	l, err := net.Listen("tcp", addr)
	if err != nil {
		return errors.Wrap(err, "failed to bind server")
	}
	defer l.Close() // nolint: errcheck

	for {
		conn, err := l.Accept()
		if err != nil {
			return errors.Wrap(err, "failed to accept TCP connection")
		}

		go handler(NewConn(conn))
	}
}

// Close closes any network connection associated with the AGI instance
func (a *AGI) Close() (err error) {
	if a.conn != nil {
		err = a.conn.Close()
		a.conn = nil
	}
	return
}

// EAGI enables access to the EAGI incoming stream (if available).
func (a *AGI) EAGI() io.Reader {
	return a.eagi
}

// Command sends the given command line to stdout
// and returns the response.
// TODO: this does not handle multi-line responses properly
func (a *AGI) Command(cmd ...string) (resp *Response) {
	resp = &Response{}

	a.mu.Lock()
	defer a.mu.Unlock()

	cmdString := strings.Join(cmd, " ") + "\n"
	_, err := a.w.Write([]byte(cmdString))
	if err != nil {
		resp.Error = errors.Wrap(err, "failed to send command")
		return
	}

	s := bufio.NewScanner(a.r)
	for s.Scan() {
		raw := s.Text()
		if raw == "" {
			break
		}

		// Parse and store the result code
		pieces := responseRegex.FindStringSubmatch(raw)
		if pieces == nil {
			resp.Error = fmt.Errorf("failed to parse result: %s", raw)
			return
		}

		// Status code is the first substring
		resp.Status, err = strconv.Atoi(pieces[1])
		if err != nil {
			resp.Error = errors.Wrap(err, "failed to get status code")
			return
		}

		// Result code is the second substring
		resp.ResultString = pieces[2]
		resp.Result, err = strconv.Atoi(pieces[2])
		if err != nil {
			resp.Error = errors.Wrap(err, "failed to parse status code as an integer")
			return
		}

		// Value is the third (and optional) substring
		wrappedVal := strings.TrimSpace(pieces[3])
		resp.Value = strings.TrimSuffix(strings.TrimPrefix(wrappedVal, "("), ")")

		break
	}

	// If the Status code is not 200, return an error
	if resp.Status != 200 {
		resp.Error = fmt.Errorf("Non-200 status code")
	}
	return
}

// Answer answers the channel
func (a *AGI) Answer() error {
	return a.Command("ANSWER").Err()
}

// Status returns the channel status
func (a *AGI) Status() (State, error) {
	r, err := a.Command("CHANNEL STATUS").Val()
	if err != nil {
		return StateDown, err
	}
	state, err := strconv.Atoi(r)
	if err != nil {
		return StateDown, fmt.Errorf("Failed to parse state %s", r)
	}
	return State(state), nil
}

// Exec runs a dialplan application
func (a *AGI) Exec(cmd ...string) (string, error) {
	cmd = append([]string{"EXEC"}, cmd...)
	return a.Command(cmd...).Val()
}

// Get gets the value of the given channel variable
func (a *AGI) Get(key string) (string, error) {
	return a.Command("GET VARIABLE", key).Val()
}

// GetData plays a file and receives DTMF, returning the received digits
func (a *AGI) GetData(name string, timeout int, maxdigits int) (digit string, err error) {
	return a.Command("GET DATA", name, strconv.Itoa(timeout), strconv.Itoa(maxdigits)).Val()
}

// Hangup terminates the call
func (a *AGI) Hangup() error {
	return a.Command("HANGUP").Err()
}

// RecordOptions describes the options available when recording
type RecordOptions struct {
	// Format is the format of the audio file to record; defaults to "wav".
	Format string

	// EscapeDigits is the set of digits on receipt of which will terminate the recording. Default is "#".  This may not be blank.
	EscapeDigits string

	// Timeout is the maximum time to allow for the recording.  Defaults to 5 minutes.
	Timeout time.Duration

	// Silence is the maximum amount of silence to allow before ending the recording.  The finest resolution is to the second.   0=disabled, which is the default.
	Silence time.Duration

	// Beep controls whether a beep is played before starting the recording.  Defaults to false.
	Beep bool

	// Offset is the number of samples in the recording to advance before storing to the file.  This is means of clipping the beginning of a recording.  Defaults to 0.
	Offset int
}

// Record records audio to a file
func (a *AGI) Record(name string, opts *RecordOptions) error {
	if opts == nil {
		opts = &RecordOptions{}
	}
	if opts.Format == "" {
		opts.Format = "wav"
	}
	if opts.EscapeDigits == "" {
		opts.EscapeDigits = "#"
	}
	if opts.Timeout == 0 {
		opts.Timeout = 5 * time.Minute
	}

	cmd := strings.Join([]string{
		"RECORD FILE ",
		name,
		opts.Format,
		opts.EscapeDigits,
		toMSec(opts.Timeout),
	}, " ")

	if opts.Offset > 0 {
		cmd += " " + strconv.Itoa(opts.Offset)
	}

	if opts.Beep {
		cmd += " BEEP"
	}

	if opts.Silence > 0 {
		cmd += " s=" + toSec(opts.Silence)
	}

	return a.Command(cmd).Err()
}

// SayAlpha plays a character string, annunciating each character.
func (a *AGI) SayAlpha(label string, escapeDigits string) (digit string, err error) {
	return a.Command("SAY ALPHA", label, escapeDigits).Val()
}

// SayDigits plays a digit string, annunciating each digit.
func (a *AGI) SayDigits(number string, escapeDigits string) (digit string, err error) {
	return a.Command("SAY DIGITS", number, escapeDigits).Val()
}

// SayDate plays a date
func (a *AGI) SayDate(when time.Time, escapeDigits string) (digit string, err error) {
	return a.Command("SAY DATE", toEpoch(when), escapeDigits).Val()
}

// SayDateTime plays a date using the given format.  See `voicemail.conf` for the format syntax; defaults to `ABdY 'digits/at' IMp`.
func (a *AGI) SayDateTime(when time.Time, escapeDigits string, format string) (digit string, err error) {
	// Extract the timezone from the time
	zone, _ := when.Zone()

	// Use the Asterisk default format if we are not given one
	if format == "" {
		format = "ABdY 'digits/at' IMp"
	}

	return a.Command("SAY DATETIME", toEpoch(when), escapeDigits, format, zone).Val()
}

// SayNumber plays the given number.
func (a *AGI) SayNumber(number string, escapeDigits string) (digit string, err error) {
	return a.Command("SAY NUMBER", number, escapeDigits).Val()
}

// SayPhonetic plays the given phrase phonetically
func (a *AGI) SayPhonetic(phrase string, escapeDigits string) (digit string, err error) {
	return a.Command("SAY PHOENTIC", phrase, escapeDigits).Val()
}

// SayTime plays the time part of the given timestamp
func (a *AGI) SayTime(when time.Time, escapeDigits string) (digit string, err error) {
	return a.Command("SAY TIME", toEpoch(when), escapeDigits).Val()
}

// Set sets the given channel variable to
// the provided value.
func (a *AGI) Set(key, val string) error {
	return a.Command("SET VARIABLE", key, val).Err()
}

// StreamFile plays the given file to the channel
func (a *AGI) StreamFile(name string, escapeDigits string, offset int) (digit string, err error) {
	return a.Command("STREAM FILE", name, escapeDigits, strconv.Itoa(offset)).Val()
}

// Verbose logs the given message to the verbose message system
func (a *AGI) Verbose(msg string, level int) error {
	return a.Command("VERBOSE", msg, strconv.Itoa(level)).Err()
}

// WaitForDigit waits for a DTMF digit and returns what is received
func (a *AGI) WaitForDigit(timeout time.Duration) (digit string, err error) {
	return a.Command("WAIT FOR DIGIT", toMSec(timeout)).Val()
}
<|endoftext|>


<commit_before>package main

import (
	"./helpers"
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"github.com/googollee/go-socket.io"
	"strings"
)

type Space struct {
	SpaceID string
	Space   []Player
}

type Player struct {
	LocalIP  string
	UserName string
}

func Adduser(msg string) {
	helpers.TRACE.Println("socket.io: adduser", msg)

}

func Logon(msg string) {

	var space Space
	var known bool = false

	ipNumbers := strings.Split(msg, " ")
	helpers.TRACE.Println("socket.io->Logon: IP", ipNumbers)

	localIP := ipNumbers[0]
	spaceID := ipNumbers[1]
	helpers.TRACE.Println("socket.io->Logon: SpaceID", spaceID)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err := redis.Bytes(redisDB.Do("GET", spaceID))
	if err != nil {
		// so the user is in a new space we add him
		known = true
		TRACE.Println("socket.io->Logon: newSpace", err)
		space = Space{
			SpaceID: spaceID,
			Space: []Player{
				{
					LocalIP:  localIP,
					UserName: "JonDoe",
				},
			},
		}
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceID, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

	} else {
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Unmarshal error: ", err)
		}
	}

	// check if the user is known
	for _, element := range space.Space {
		if element.LocalIP == localIP {
			known = true
			TRACE.Println("socket.io->Logon known LocalIP", element.LocalIP, "in Space", spaceID)
		}
	}

	// if ip is unknow add it to the space
	if !known {
		TRACE.Println("socket.io->Logon unknown LocalIP", localIP, "is added")

		player := Player{
			LocalIP:  localIP,
			UserName: "JonDoe",
		}

		space.Space = append(space.Space, player)

		TRACE.Println("socket.io->Logon added", space)
	}
}

func JoinGame(so socketio.Socket, msg string) {

	helpers.TRACE.Println("socket.io: Join", msg)

	so.Emit("channel", "abcde")
}
<commit_msg>Logon test<commit_after>package main

import (
	"./helpers"
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"github.com/googollee/go-socket.io"
	"strings"
)

type Space struct {
	SpaceID string
	Space   []Player
}

type Player struct {
	LocalIP  string
	UserName string
}

func Adduser(msg string) {
	helpers.TRACE.Println("socket.io: adduser", msg)

}

func Logon(msg string) {

	var space Space
	var known bool = false

	ipNumbers := strings.Split(msg, " ")
	helpers.TRACE.Println("socket.io->Logon: IP", ipNumbers)

	localIP := ipNumbers[0]
	spaceID := ipNumbers[1]
	helpers.TRACE.Println("socket.io->Logon: SpaceID", spaceID)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err := redis.Bytes(redisDB.Do("GET", spaceID))
	if err != nil {
		// so the user is in a new space we add him
		known = true
		TRACE.Println("socket.io->Logon: newSpace", err)
		space = Space{
			SpaceID: spaceID,
			Space: []Player{
				{
					LocalIP:  localIP,
					UserName: "JonDoe",
				},
			},
		}
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceID, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

	} else {
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Unmarshal error: ", err)
		}
	}

	// check if the user is known
	for _, element := range space.Space {
		if element.LocalIP == localIP {
			known = true
			TRACE.Println("socket.io->Logon known LocalIP", element.LocalIP, "in Space", spaceID)
		}
	}

	// if ip is unknow add it to the space
	if !known {
		TRACE.Println("socket.io->Logon unknown LocalIP", localIP, "is added")

		player := Player{
			LocalIP:  localIP,
			UserName: "JonDoe",
		}

		space.Space = append(space.Space, player)
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceID, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

		TRACE.Println("socket.io->Logon added", space)
	}
}

func JoinGame(so socketio.Socket, msg string) {

	helpers.TRACE.Println("socket.io: Join", msg)

	so.Emit("channel", "abcde")
}
<|endoftext|>