<commit_before>package goStrongswanVici

import (
	"fmt"
	"strconv"
)

//from list-sa event
type IkeSa struct {
	Uniqueid        string               `json:"uniqueid"` //called ike_id in terminate() argument.
	Version         string               `json:"version"`
	State           string               `json:"state"` //had saw: ESTABLISHED
	Local_host      string               `json:"local-host"`
	Local_port      string               `json:"local-port"`
	Local_id        string               `json:"local-id"`
	Remote_host     string               `json:"remote-host"`
	Remote_port     string               `json:"remote-port"`
	Remote_id       string               `json:"remote-id"`
	Remote_xauth_id string               `json:"remote-xauth-id"` //client username
	Initiator       string               `json:"initiator"`
	Initiator_spi   string               `json:"initiator-spi"`
	Responder_spi   string               `json:"responder-spi"`
	Encr_alg        string               `json:"encr-alg"`
	Encr_keysize    string               `json:"encr-keysize"`
	Integ_alg       string               `json:"integ-alg"`
	Integ_keysize   string               `json:"integ-keysize"`
	Prf_alg         string               `json:"prf-alg"`
	Dh_group        string               `json:"dh-group"`
	Established     string               `json:"established"`
	Rekey_time      string               `json:"rekey-time"`
	Reauth_time     string               `json:"reauth-time"`
	Remote_vips     []string             `json:"remote-vips"`
	Child_sas       map[string]Child_sas `json:"child-sas"` //key means child-sa-name(conn name in ipsec.conf)
	Tasks_active    []string             `json:"tasks-active"`
}

type Child_sas struct {
	Reqid         string   `json:"reqid"`
	State         string   `json:"state"` //had saw: INSTALLED
	Mode          string   `json:"mode"`  //had saw: TUNNEL
	Protocol      string   `json:"protocol"`
	Encap         string   `json:"encap"`
	Spi_in        string   `json:"spi-in"`
	Spi_out       string   `json:"spi-out"`
	Cpi_in        string   `json:"cpi-in"`
	Cpi_out       string   `json:"cpi-out"`
	Encr_alg      string   `json:"encr-alg"`
	Encr_keysize  string   `json:"encr-keysize"`
	Integ_alg     string   `json:"integ-alg"`
	Integ_keysize string   `json:"integ-keysize"`
	Prf_alg       string   `json:"prf-alg"`
	Dh_group      string   `json:"dh-group"`
	Esn           string   `json:"esn"`
	Bytes_in      string   `json:"bytes-in"` //bytes into this machine
	Packets_in    string   `json:"packets-in"`
	Use_in        string   `json:"use-in"`
	Bytes_out     string   `json:"bytes-out"` // bytes out of this machine
	Packets_out   string   `json:"packets-out"`
	Use_out       string   `json:"use-out"`
	Rekey_time    string   `json:"rekey-time"`
	Life_time     string   `json:"life-time"`
	Install_time  string   `json:"install-time"`
	Local_ts      []string `json:"local-ts"`
	Remote_ts     []string `json:"remote-ts"`
}

func (s *Child_sas) GetBytesIn() uint64 {
	num, err := strconv.ParseUint(s.Bytes_in, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func (s *Child_sas) GetBytesOut() uint64 {
	num, err := strconv.ParseUint(s.Bytes_out, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func (s *Child_sas) GetPacketsIn() uint64 {
	num, err := strconv.ParseUint(s.Packets_in, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func (s *Child_sas) GetPacketsOut() uint64 {
	num, err := strconv.ParseUint(s.Packets_out, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

// To be simple, list all clients that are connecting to this server .
// A client is a sa.
// Lists currently active IKE_SAs
func (c *ClientConn) ListSas(ike string, ike_id string) (sas []map[string]IkeSa, err error) {
	sas = []map[string]IkeSa{}
	var eventErr error
	//register event
	err = c.RegisterEvent("list-sa", func(response map[string]interface{}) {
		sa := &map[string]IkeSa{}
		err = ConvertFromGeneral(response, sa)
		if err != nil {
			fmt.Printf("list-sa event error: %s\n", err)
			eventErr = err
			return
		}
		sas = append(sas, *sa)
		//fmt.Printf("event %#v\n", response)
	})
	if err != nil {
		return
	}
	if eventErr != nil {
		return
	}

	inMap := map[string]interface{}{}
	if ike != "" {
		inMap["ike"] = ike
	}
	if ike_id != "" {
		inMap["ike_id"] = ike_id
	}
	_, err = c.Request("list-sas", inMap)
	if err != nil {
		return
	}
	//fmt.Printf("request finish %#v\n", sas)
	err = c.UnregisterEvent("list-sa")
	if err != nil {
		return
	}
	return
}

//a vpn conn in the strongswan server
type VpnConnInfo struct {
	IkeSa
	Child_sas
	IkeSaName   string //looks like conn name in ipsec.conf, content is same as ChildSaName
	ChildSaName string //looks like conn name in ipsec.conf
}

func (c *VpnConnInfo) GuessUserName() string {
	if c.Remote_xauth_id != "" {
		return c.Remote_xauth_id
	}
	if c.Remote_id != "" {
		return c.Remote_id
	}
	return ""
}

// a helper method to avoid complex data struct in ListSas
// if it only have one child_sas ,it will put it into info.Child_sas
func (c *ClientConn) ListAllVpnConnInfo() (list []VpnConnInfo, err error) {
	sasList, err := c.ListSas("", "")
	if err != nil {
		return
	}
	list = make([]VpnConnInfo, len(sasList))
	for i, sa := range sasList {
		info := VpnConnInfo{}
		if len(sa) != 1 {
			fmt.Printf("[vici.ListAllVpnConnInfo] warning: len(sa)[%d]!=1\n", len(sa))
		}
		for ikeSaName, ikeSa := range sa {
			info.IkeSaName = ikeSaName
			info.IkeSa = ikeSa
			//if len(ikeSa.Child_sas) != 1 {
			//	fmt.Println("[vici.ListAllVpnConnInfo] warning: len(ikeSa.Child_sas)[%d]!=1", len(ikeSa.Child_sas))
			//}
			for childSaName, childSa := range ikeSa.Child_sas {
				info.ChildSaName = childSaName
				info.Child_sas = childSa
				break
			}
			break
		}
		if len(info.IkeSa.Child_sas) == 1 {
			info.IkeSa.Child_sas = nil
		}
		list[i] = info
	}
	return
}
<commit_msg>Add queued task information to IkeSa struct<commit_after>package goStrongswanVici

import (
	"fmt"
	"strconv"
)

//from list-sa event
type IkeSa struct {
	Uniqueid        string               `json:"uniqueid"` //called ike_id in terminate() argument.
	Version         string               `json:"version"`
	State           string               `json:"state"` //had saw: ESTABLISHED
	Local_host      string               `json:"local-host"`
	Local_port      string               `json:"local-port"`
	Local_id        string               `json:"local-id"`
	Remote_host     string               `json:"remote-host"`
	Remote_port     string               `json:"remote-port"`
	Remote_id       string               `json:"remote-id"`
	Remote_xauth_id string               `json:"remote-xauth-id"` //client username
	Initiator       string               `json:"initiator"`
	Initiator_spi   string               `json:"initiator-spi"`
	Responder_spi   string               `json:"responder-spi"`
	Encr_alg        string               `json:"encr-alg"`
	Encr_keysize    string               `json:"encr-keysize"`
	Integ_alg       string               `json:"integ-alg"`
	Integ_keysize   string               `json:"integ-keysize"`
	Prf_alg         string               `json:"prf-alg"`
	Dh_group        string               `json:"dh-group"`
	Established     string               `json:"established"`
	Rekey_time      string               `json:"rekey-time"`
	Reauth_time     string               `json:"reauth-time"`
	Remote_vips     []string             `json:"remote-vips"`
	Child_sas       map[string]Child_sas `json:"child-sas"` //key means child-sa-name(conn name in ipsec.conf)
	Tasks_active    []string             `json:"tasks-active"`
	Tasks_queued    []string             `json:"tasks-queued"`
}

type Child_sas struct {
	Reqid         string   `json:"reqid"`
	State         string   `json:"state"` //had saw: INSTALLED
	Mode          string   `json:"mode"`  //had saw: TUNNEL
	Protocol      string   `json:"protocol"`
	Encap         string   `json:"encap"`
	Spi_in        string   `json:"spi-in"`
	Spi_out       string   `json:"spi-out"`
	Cpi_in        string   `json:"cpi-in"`
	Cpi_out       string   `json:"cpi-out"`
	Encr_alg      string   `json:"encr-alg"`
	Encr_keysize  string   `json:"encr-keysize"`
	Integ_alg     string   `json:"integ-alg"`
	Integ_keysize string   `json:"integ-keysize"`
	Prf_alg       string   `json:"prf-alg"`
	Dh_group      string   `json:"dh-group"`
	Esn           string   `json:"esn"`
	Bytes_in      string   `json:"bytes-in"` //bytes into this machine
	Packets_in    string   `json:"packets-in"`
	Use_in        string   `json:"use-in"`
	Bytes_out     string   `json:"bytes-out"` // bytes out of this machine
	Packets_out   string   `json:"packets-out"`
	Use_out       string   `json:"use-out"`
	Rekey_time    string   `json:"rekey-time"`
	Life_time     string   `json:"life-time"`
	Install_time  string   `json:"install-time"`
	Local_ts      []string `json:"local-ts"`
	Remote_ts     []string `json:"remote-ts"`
}

func (s *Child_sas) GetBytesIn() uint64 {
	num, err := strconv.ParseUint(s.Bytes_in, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func (s *Child_sas) GetBytesOut() uint64 {
	num, err := strconv.ParseUint(s.Bytes_out, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func (s *Child_sas) GetPacketsIn() uint64 {
	num, err := strconv.ParseUint(s.Packets_in, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

func (s *Child_sas) GetPacketsOut() uint64 {
	num, err := strconv.ParseUint(s.Packets_out, 10, 64)
	if err != nil {
		return 0
	}
	return num
}

// To be simple, list all clients that are connecting to this server .
// A client is a sa.
// Lists currently active IKE_SAs
func (c *ClientConn) ListSas(ike string, ike_id string) (sas []map[string]IkeSa, err error) {
	sas = []map[string]IkeSa{}
	var eventErr error
	//register event
	err = c.RegisterEvent("list-sa", func(response map[string]interface{}) {
		sa := &map[string]IkeSa{}
		err = ConvertFromGeneral(response, sa)
		if err != nil {
			fmt.Printf("list-sa event error: %s\n", err)
			eventErr = err
			return
		}
		sas = append(sas, *sa)
		//fmt.Printf("event %#v\n", response)
	})
	if err != nil {
		return
	}
	if eventErr != nil {
		return
	}

	inMap := map[string]interface{}{}
	if ike != "" {
		inMap["ike"] = ike
	}
	if ike_id != "" {
		inMap["ike_id"] = ike_id
	}
	_, err = c.Request("list-sas", inMap)
	if err != nil {
		return
	}
	//fmt.Printf("request finish %#v\n", sas)
	err = c.UnregisterEvent("list-sa")
	if err != nil {
		return
	}
	return
}

//a vpn conn in the strongswan server
type VpnConnInfo struct {
	IkeSa
	Child_sas
	IkeSaName   string //looks like conn name in ipsec.conf, content is same as ChildSaName
	ChildSaName string //looks like conn name in ipsec.conf
}

func (c *VpnConnInfo) GuessUserName() string {
	if c.Remote_xauth_id != "" {
		return c.Remote_xauth_id
	}
	if c.Remote_id != "" {
		return c.Remote_id
	}
	return ""
}

// a helper method to avoid complex data struct in ListSas
// if it only have one child_sas ,it will put it into info.Child_sas
func (c *ClientConn) ListAllVpnConnInfo() (list []VpnConnInfo, err error) {
	sasList, err := c.ListSas("", "")
	if err != nil {
		return
	}
	list = make([]VpnConnInfo, len(sasList))
	for i, sa := range sasList {
		info := VpnConnInfo{}
		if len(sa) != 1 {
			fmt.Printf("[vici.ListAllVpnConnInfo] warning: len(sa)[%d]!=1\n", len(sa))
		}
		for ikeSaName, ikeSa := range sa {
			info.IkeSaName = ikeSaName
			info.IkeSa = ikeSa
			//if len(ikeSa.Child_sas) != 1 {
			//	fmt.Println("[vici.ListAllVpnConnInfo] warning: len(ikeSa.Child_sas)[%d]!=1", len(ikeSa.Child_sas))
			//}
			for childSaName, childSa := range ikeSa.Child_sas {
				info.ChildSaName = childSaName
				info.Child_sas = childSa
				break
			}
			break
		}
		if len(info.IkeSa.Child_sas) == 1 {
			info.IkeSa.Child_sas = nil
		}
		list[i] = info
	}
	return
}
<|endoftext|>


<commit_before>package log

import(
	"fmt"
	"time"
)

type Logger struct {
	Level LogLevel
}

type LogLevel int
const (
	TRACE = iota
	DEBUG
	WARN
	INFO
	ERROR
	FATAL
)
var levels = map[LogLevel]string{
	TRACE: "TRACE",
	DEBUG: "DEBUG",
	WARN: "WARN",
	INFO: "INFO",
	ERROR: "ERROR",
	FATAL: "FATAL",
}

var logger *Logger

func New(level LogLevel) *Logger {
	return &Logger{
		Level: level,
	}
}

func Write(level LogLevel, message string, params ...interface{}) {
	p := append([]interface{}{time.Now(), levels[level]}, params...)
	fmt.Printf("[%s] [%s] " + message + "\n", p...)
}

func Unwrap(args ...interface{}) []interface{} {
	head := args[0]
	switch head.(type) {
	case func(...interface{})[]interface{}:
		args = head.(func(...interface{})[]interface{})(args[1:]...)
	}
	return args
}

func Log(level LogLevel, params ...interface{}) {
	if logger == nil { logger = New(DEBUG) }
	logger.Log(level, params...)
}
func Debug(params ...interface{}) {	Log(DEBUG, params...) }
func Info(params ...interface{}) { Log(INFO, params...) }
func Warn(params ...interface{}) { Log(WARN, params...) }
func Error(params ...interface{}) {	Log(ERROR, params...) }
func Trace(params ...interface{}) {	Log(TRACE, params...) }
func Printf(params ...interface{}) { Log(INFO, params...) }
func Println(params ...interface{}) { Log(INFO, params...) }
func Fatalf(params ...interface{}) { Log(FATAL, params...) }

func (l *Logger) Write(level LogLevel, message string, params ...interface{}) {
	Write(level, message, params...)
}
func (l *Logger) Log(level LogLevel, params ...interface{}) {
	l.Write(level, params[0].(string), params[1:]...)
}
func (l *Logger) Debug(params ...interface{}) { l.Log(DEBUG, Unwrap(params...)...) }
func (l *Logger) Info(params ...interface{}) { l.Log(INFO, Unwrap(params...)...) }
func (l *Logger) Warn(params ...interface{}) { l.Log(WARN, Unwrap(params...)...) }
func (l *Logger) Error(params ...interface{}) { l.Log(ERROR, Unwrap(params...)...) }
func (l *Logger) Trace(params ...interface{}) { l.Log(TRACE, Unwrap(params...)...) }
func (l *Logger) Printf(params ...interface{}) { l.Log(INFO, Unwrap(params...)...) }
func (l *Logger) Println(params ...interface{}) { l.Log(INFO, Unwrap(params...)...) }
func (l *Logger) Fatalf(params ...interface{}) { l.Log(FATAL, Unwrap(params...)...) }<commit_msg>Fix unwrapping bug<commit_after>package log

import(
	"fmt"
	"time"
)

type Logger struct {
	Level LogLevel
}

type LogLevel int
const (
	TRACE = iota
	DEBUG
	WARN
	INFO
	ERROR
	FATAL
)
var levels = map[LogLevel]string{
	TRACE: "TRACE",
	DEBUG: "DEBUG",
	WARN: "WARN",
	INFO: "INFO",
	ERROR: "ERROR",
	FATAL: "FATAL",
}

var logger *Logger

func New(level LogLevel) *Logger {
	return &Logger{
		Level: level,
	}
}

func Write(level LogLevel, message string, params ...interface{}) {
	p := append([]interface{}{time.Now(), levels[level]}, params...)
	fmt.Printf("[%s] [%s] " + message + "\n", p...)
}

func Unwrap(args ...interface{}) []interface{} {
	head := args[0]
	switch head.(type) {
	case func(...interface{})[]interface{}:
		args = head.(func(...interface{})[]interface{})(args[1:]...)
	}
	return args
}

func Log(level LogLevel, params ...interface{}) {
	if logger == nil { logger = New(DEBUG) }
	logger.Log(level, params...)
}
func Debug(params ...interface{}) {	Log(DEBUG, Unwrap(params...)...) }
func Info(params ...interface{}) { Log(INFO, Unwrap(params...)...) }
func Warn(params ...interface{}) { Log(WARN, Unwrap(params...)...) }
func Error(params ...interface{}) {	Log(ERROR, Unwrap(params...)...) }
func Trace(params ...interface{}) {	Log(TRACE, Unwrap(params...)...) }
func Printf(params ...interface{}) { Log(INFO, Unwrap(params...)...) }
func Println(params ...interface{}) { Log(INFO, Unwrap(params...)...) }
func Fatalf(params ...interface{}) { Log(FATAL, Unwrap(params...)...) }

func (l *Logger) Write(level LogLevel, message string, params ...interface{}) {
	Write(level, message, params...)
}
func (l *Logger) Log(level LogLevel, params ...interface{}) {
	l.Write(level, params[0].(string), params[1:]...)
}
func (l *Logger) Debug(params ...interface{}) { l.Log(DEBUG, Unwrap(params...)...) }
func (l *Logger) Info(params ...interface{}) { l.Log(INFO, Unwrap(params...)...) }
func (l *Logger) Warn(params ...interface{}) { l.Log(WARN, Unwrap(params...)...) }
func (l *Logger) Error(params ...interface{}) { l.Log(ERROR, Unwrap(params...)...) }
func (l *Logger) Trace(params ...interface{}) { l.Log(TRACE, Unwrap(params...)...) }
func (l *Logger) Printf(params ...interface{}) { l.Log(INFO, Unwrap(params...)...) }
func (l *Logger) Println(params ...interface{}) { l.Log(INFO, Unwrap(params...)...) }
func (l *Logger) Fatalf(params ...interface{}) { l.Log(FATAL, Unwrap(params...)...) }<|endoftext|>


<commit_before>package logging

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// L is the global Logger instance.
var L *Logger

func init() {
	L = New("")
}

// New creates a new private Logger. If fileName is an empty string, the Logger will write to stdout. New will return nil if it can't create/open fileName.
func New(fileName string) *Logger {
	hostname, _ := os.Hostname()
	l := Logger{
		appName:    path.Base(os.Args[0]),
		hostName:   hostname,
		pid:        strconv.Itoa(os.Getpid()),
		doDebug:    false,
		doInfo:     true,
		doWarning:  true,
		doError:    true,
		doCritical: true,
		doFatal:    true,
	}
	err := l.SetLogFile(fileName)
	if err != nil {
		return nil
	}
	return &l
}

// Logger is a type passed to the logging functions. It stores the log settings.
type Logger struct {
	appName     string
	hostName    string
	pid         string
	jsonWriter  io.WriteCloser
	textWriter  io.WriteCloser
	jsonChannel chan<- string
	doDebug     bool
	doInfo      bool
	doWarning   bool
	doError     bool
	doCritical  bool
	doFatal     bool
}

// SetLogFile sets fileName as the log file target. An empty string sets text file logging to stdout
// and json logging to null (ie, no json output); this is the default.
// Normally, there are two log files, one for text and one for json. The text file will be written to
// filename, while the json content will be written to filename.json
// If filename.json cannot be opened for write (eg, filename = "/dev/null"), then
// both text and json will be written to filename.
func (l *Logger) SetLogFile(fileName string) error {
	var textWriter, jsonWriter *os.File
	var err error
	if len(fileName) == 0 {
		textWriter = os.Stdout
		jsonWriter, _ = os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	} else {
		textWriter, err = os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
		if err == nil {
			jsonWriter, err = os.OpenFile(fmt.Sprintf("%s.json", fileName), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
			if err != nil {
				jsonWriter = textWriter
				err = nil
			}
		}
	}
	if err == nil {
		if l.textWriter != nil && l.textWriter != os.Stdout {
			l.textWriter.Close()
		}
		if l.jsonWriter != nil && l.jsonWriter != os.Stdout && l.jsonWriter != l.textWriter {
			l.jsonWriter.Close()
		}
		l.textWriter = textWriter
		l.jsonWriter = jsonWriter
	}
	return err
}

// WriteJSONToChannel changes the destination of the json entries from a local file to a channel. Note that
// SetLogFile will change it back to using a local file, so keep the call order in mind.
func (l *Logger) WriteJSONToChannel(c chan<- string) {
	l.jsonChannel = c
}

// SetOutput controls which levels of logging are enabled/disabled
func (l *Logger) SetOutput(debug, info, warning, err, critical, fatal bool) {
	l.doDebug = debug
	l.doInfo = info
	l.doWarning = warning
	l.doError = err
	l.doCritical = critical
	l.doFatal = fatal
}

// Error is like Debug for ERROR log entries.
func (l *Logger) Error(values map[string]string, format string, args ...interface{}) error {
	if !l.doError {
		return nil
	}
	return l.writeEntry("ERROR", values, format, args...)
}

// Warning is like Debug for WARNING log entries.
func (l *Logger) Warning(values map[string]string, format string, args ...interface{}) error {
	if !l.doWarning {
		return nil
	}
	return l.writeEntry("WARNING", values, format, args...)
}

// Info is like Debug for INFO log entries.
func (l *Logger) Info(values map[string]string, format string, args ...interface{}) error {
	if !l.doInfo {
		return nil
	}
	return l.writeEntry("INFO", values, format, args...)
}

// Debug writes a DEBUG log entry. The optional values map contains
// user-supplied key-value pairs. format and args are passed to fmt.Printf
// to generate the message entry.
func (l *Logger) Debug(values map[string]string, format string, args ...interface{}) error {
	if !l.doDebug {
		return nil
	}
	return l.writeEntry("DEBUG", values, format, args...)
}

// Critical is like Debug for CRITICAL log entries.
func (l *Logger) Critical(values map[string]string, format string, args ...interface{}) error {
	if !l.doCritical {
		return nil
	}
	return l.writeEntry("CRITICAL", values, format, args...)
}

// Fatal is like Debug for FATAL log entries, but it also calls os.Exit(1).
func (l *Logger) Fatal(values map[string]string, format string, args ...interface{}) error {
	if !l.doFatal {
		return nil
	}
	err := l.writeEntry("FATAL", values, format, args...)
	os.Exit(1)
	return err // won't actually get here
}

func (l *Logger) writeEntry(severity string, values map[string]string, format string, args ...interface{}) error {
	kv := l.getHeaderValues(severity)
	headerStr := makeHeaderString(kv)
	messageStr := fmt.Sprintf(format, args...)
	if strings.ContainsAny(messageStr, "{}\t") {
		messageStr = strings.Replace(messageStr, "\t", " ", -1)
		messageStr = strings.Replace(messageStr, "{", "[", -1)
		messageStr = strings.Replace(messageStr, "}", "]", -1)
	}
	_, err := fmt.Fprintf(l.textWriter, "%s\t%s\n", headerStr, messageStr)
	if err != nil {
		return err
	}
	jsonStr, err := makeJSONString(kv, values, messageStr)
	if err != nil {
		return err
	}
	if l.jsonChannel != nil {
		l.jsonChannel <- jsonStr
	} else {
		_, err = fmt.Fprintln(l.jsonWriter, jsonStr)
	}
	return err
}

func (l *Logger) writeLocalEntry(severity string, values map[string]string, format string, args ...interface{}) error {
	// Note that this duplicates nearly all of the code from writeEntry. We use this function internally to log
	// errors from the catcherWriter, where we don't want to output the json
	kv := l.getHeaderValues(severity)
	headerStr := makeHeaderString(kv)
	messageStr := fmt.Sprintf(format, args...)
	if strings.ContainsAny(messageStr, "{}\t") {
		messageStr = strings.Replace(messageStr, "\t", " ", -1)
		messageStr = strings.Replace(messageStr, "{", "[", -1)
		messageStr = strings.Replace(messageStr, "}", "]", -1)
	}
	_, err := fmt.Fprintf(l.textWriter, "%s\t%s\n", headerStr, messageStr)
	return err
}

func (l *Logger) getHeaderValues(severity string) map[string]string {
	pc, file, line, _ := runtime.Caller(3)
	f := runtime.FuncForPC(pc)
	caller := f.Name()
	m := map[string]string{
		"timestamp": time.Now().UTC().Format(time.RFC3339Nano),
		"severity":  severity,
		"pid":       l.pid,
		"app":       l.appName,
		"host":      l.hostName,
		"line":      strconv.Itoa(line),
		"file":      path.Base(file),
		"function":  path.Base(caller),
	}
	return m
}

func makeHeaderString(m map[string]string) string {
	return strings.Join([]string{m["timestamp"], m["severity"]}, "\t")
}

func makeJSONString(header map[string]string, kv map[string]string, message string) (string, error) {
	merged := make(map[string]string)
	for k, v := range kv {
		merged[k] = v
	}
	for k, v := range header {
		merged[k] = v
	}
	merged["message"] = message
	b, err := json.Marshal(merged)
	if err != nil {
		return "", err
	}
	return string(b), nil
}
<commit_msg>remove unused function<commit_after>package logging

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// L is the global Logger instance.
var L *Logger

func init() {
	L = New("")
}

// New creates a new private Logger. If fileName is an empty string, the Logger will write to stdout. New will return nil if it can't create/open fileName.
func New(fileName string) *Logger {
	hostname, _ := os.Hostname()
	l := Logger{
		appName:    path.Base(os.Args[0]),
		hostName:   hostname,
		pid:        strconv.Itoa(os.Getpid()),
		doDebug:    false,
		doInfo:     true,
		doWarning:  true,
		doError:    true,
		doCritical: true,
		doFatal:    true,
	}
	err := l.SetLogFile(fileName)
	if err != nil {
		return nil
	}
	return &l
}

// Logger is a type passed to the logging functions. It stores the log settings.
type Logger struct {
	appName     string
	hostName    string
	pid         string
	jsonWriter  io.WriteCloser
	textWriter  io.WriteCloser
	jsonChannel chan<- string
	doDebug     bool
	doInfo      bool
	doWarning   bool
	doError     bool
	doCritical  bool
	doFatal     bool
}

// SetLogFile sets fileName as the log file target. An empty string sets text file logging to stdout
// and json logging to null (ie, no json output); this is the default.
// Normally, there are two log files, one for text and one for json. The text file will be written to
// filename, while the json content will be written to filename.json
// If filename.json cannot be opened for write (eg, filename = "/dev/null"), then
// both text and json will be written to filename.
func (l *Logger) SetLogFile(fileName string) error {
	var textWriter, jsonWriter *os.File
	var err error
	if len(fileName) == 0 {
		textWriter = os.Stdout
		jsonWriter, _ = os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	} else {
		textWriter, err = os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
		if err == nil {
			jsonWriter, err = os.OpenFile(fmt.Sprintf("%s.json", fileName), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
			if err != nil {
				jsonWriter = textWriter
				err = nil
			}
		}
	}
	if err == nil {
		if l.textWriter != nil && l.textWriter != os.Stdout {
			l.textWriter.Close()
		}
		if l.jsonWriter != nil && l.jsonWriter != os.Stdout && l.jsonWriter != l.textWriter {
			l.jsonWriter.Close()
		}
		l.textWriter = textWriter
		l.jsonWriter = jsonWriter
	}
	return err
}

// WriteJSONToChannel changes the destination of the json entries from a local file to a channel. Note that
// SetLogFile will change it back to using a local file, so keep the call order in mind.
func (l *Logger) WriteJSONToChannel(c chan<- string) {
	l.jsonChannel = c
}

// SetOutput controls which levels of logging are enabled/disabled
func (l *Logger) SetOutput(debug, info, warning, err, critical, fatal bool) {
	l.doDebug = debug
	l.doInfo = info
	l.doWarning = warning
	l.doError = err
	l.doCritical = critical
	l.doFatal = fatal
}

// Error is like Debug for ERROR log entries.
func (l *Logger) Error(values map[string]string, format string, args ...interface{}) error {
	if !l.doError {
		return nil
	}
	return l.writeEntry("ERROR", values, format, args...)
}

// Warning is like Debug for WARNING log entries.
func (l *Logger) Warning(values map[string]string, format string, args ...interface{}) error {
	if !l.doWarning {
		return nil
	}
	return l.writeEntry("WARNING", values, format, args...)
}

// Info is like Debug for INFO log entries.
func (l *Logger) Info(values map[string]string, format string, args ...interface{}) error {
	if !l.doInfo {
		return nil
	}
	return l.writeEntry("INFO", values, format, args...)
}

// Debug writes a DEBUG log entry. The optional values map contains
// user-supplied key-value pairs. format and args are passed to fmt.Printf
// to generate the message entry.
func (l *Logger) Debug(values map[string]string, format string, args ...interface{}) error {
	if !l.doDebug {
		return nil
	}
	return l.writeEntry("DEBUG", values, format, args...)
}

// Critical is like Debug for CRITICAL log entries.
func (l *Logger) Critical(values map[string]string, format string, args ...interface{}) error {
	if !l.doCritical {
		return nil
	}
	return l.writeEntry("CRITICAL", values, format, args...)
}

// Fatal is like Debug for FATAL log entries, but it also calls os.Exit(1).
func (l *Logger) Fatal(values map[string]string, format string, args ...interface{}) error {
	if !l.doFatal {
		return nil
	}
	err := l.writeEntry("FATAL", values, format, args...)
	os.Exit(1)
	return err // won't actually get here
}

func (l *Logger) writeEntry(severity string, values map[string]string, format string, args ...interface{}) error {
	kv := l.getHeaderValues(severity)
	headerStr := makeHeaderString(kv)
	messageStr := fmt.Sprintf(format, args...)
	if strings.ContainsAny(messageStr, "{}\t") {
		messageStr = strings.Replace(messageStr, "\t", " ", -1)
		messageStr = strings.Replace(messageStr, "{", "[", -1)
		messageStr = strings.Replace(messageStr, "}", "]", -1)
	}
	_, err := fmt.Fprintf(l.textWriter, "%s\t%s\n", headerStr, messageStr)
	if err != nil {
		return err
	}
	jsonStr, err := makeJSONString(kv, values, messageStr)
	if err != nil {
		return err
	}
	if l.jsonChannel != nil {
		l.jsonChannel <- jsonStr
	} else {
		_, err = fmt.Fprintln(l.jsonWriter, jsonStr)
	}
	return err
}

func (l *Logger) getHeaderValues(severity string) map[string]string {
	pc, file, line, _ := runtime.Caller(3)
	f := runtime.FuncForPC(pc)
	caller := f.Name()
	m := map[string]string{
		"timestamp": time.Now().UTC().Format(time.RFC3339Nano),
		"severity":  severity,
		"pid":       l.pid,
		"app":       l.appName,
		"host":      l.hostName,
		"line":      strconv.Itoa(line),
		"file":      path.Base(file),
		"function":  path.Base(caller),
	}
	return m
}

func makeHeaderString(m map[string]string) string {
	return strings.Join([]string{m["timestamp"], m["severity"]}, "\t")
}

func makeJSONString(header map[string]string, kv map[string]string, message string) (string, error) {
	merged := make(map[string]string)
	for k, v := range kv {
		merged[k] = v
	}
	for k, v := range header {
		merged[k] = v
	}
	merged["message"] = message
	b, err := json.Marshal(merged)
	if err != nil {
		return "", err
	}
	return string(b), nil
}
<|endoftext|>


<commit_before>package main

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

	log "github.com/lxc/lxd/shared/log15"

	"github.com/gorilla/mux"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/shared/logger"
)

// RestServer creates an http.Server capable of handling requests against the LXD REST
// API endpoint.
func RestServer(d *Daemon) *http.Server {
	/* Setup the web server */
	mux := mux.NewRouter()
	mux.StrictSlash(false)

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		SyncResponse(true, []string{"/1.0"}).Render(w)
	})

	for endpoint, f := range d.gateway.HandlerFuncs(d.NodeRefreshTask) {
		mux.HandleFunc(endpoint, f)
	}

	for _, c := range api10 {
		d.createCmd(mux, "1.0", c)
	}

	for _, c := range apiInternal {
		d.createCmd(mux, "internal", c)
	}

	mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger.Info("Sending top level 404", log.Ctx{"url": r.URL})
		w.Header().Set("Content-Type", "application/json")
		NotFound(nil).Render(w)
	})

	return &http.Server{Handler: &lxdHttpServer{r: mux, d: d}}
}

type lxdHttpServer struct {
	r *mux.Router
	d *Daemon
}

func (s *lxdHttpServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	// Set CORS headers, unless this is an internal or gRPC request.
	if !strings.HasPrefix(req.URL.Path, "/internal") && !strings.HasPrefix(req.URL.Path, "/protocol.SQL") {
		<-s.d.setupChan
		err := s.d.cluster.Transaction(func(tx *db.ClusterTx) error {
			config, err := cluster.ConfigLoad(tx)
			if err != nil {
				return err
			}
			setCORSHeaders(rw, req, config)
			return nil
		})
		if err != nil {
			response := SmartError(err)
			response.Render(rw)
			return
		}
	}

	// OPTIONS request don't need any further processing
	if req.Method == "OPTIONS" {
		return
	}

	// Call the original server
	s.r.ServeHTTP(rw, req)
}

func setCORSHeaders(rw http.ResponseWriter, req *http.Request, config *cluster.Config) {
	allowedOrigin := config.HTTPSAllowedOrigin()
	origin := req.Header.Get("Origin")
	if allowedOrigin != "" && origin != "" {
		rw.Header().Set("Access-Control-Allow-Origin", allowedOrigin)
	}

	allowedMethods := config.HTTPSAllowedMethods()
	if allowedMethods != "" && origin != "" {
		rw.Header().Set("Access-Control-Allow-Methods", allowedMethods)
	}

	allowedHeaders := config.HTTPSAllowedHeaders()
	if allowedHeaders != "" && origin != "" {
		rw.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
	}

	allowedCredentials := config.HTTPSAllowedCredentials()
	if allowedCredentials {
		rw.Header().Set("Access-Control-Allow-Credentials", "true")
	}
}

// Return true if this an API request coming from a cluster node that is
// notifying us of some user-initiated API request that needs some action to be
// taken on this node as well.
func isClusterNotification(r *http.Request) bool {
	return r.Header.Get("User-Agent") == "lxd-cluster-notifier"
}

// Extract the project query parameter from the given request.
func projectParam(request *http.Request) string {
	project := queryParam(request, "project")
	if project == "" {
		project = "default"
	}
	return project
}

// Extract the given query parameter directly from the URL, never from an
// encoded body.
func queryParam(request *http.Request, key string) string {
	var values url.Values
	var err error

	if request.URL != nil {
		values, err = url.ParseQuery(request.URL.RawQuery)
		if err != nil {
			logger.Warnf("Failed to parse query string %q: %v", request.URL.RawQuery, err)
			return ""
		}
	}

	if values == nil {
		values = make(url.Values)
	}

	return values.Get(key)
}
<commit_msg>lxd/api: Contructs endpoint alias routes<commit_after>package main

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

	log "github.com/lxc/lxd/shared/log15"

	"github.com/gorilla/mux"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/shared/logger"
)

// RestServer creates an http.Server capable of handling requests against the LXD REST
// API endpoint.
func RestServer(d *Daemon) *http.Server {
	/* Setup the web server */
	mux := mux.NewRouter()
	mux.StrictSlash(false)

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		SyncResponse(true, []string{"/1.0"}).Render(w)
	})

	for endpoint, f := range d.gateway.HandlerFuncs(d.NodeRefreshTask) {
		mux.HandleFunc(endpoint, f)
	}

	for _, c := range api10 {
		d.createCmd(mux, "1.0", c)

		// Create any alias endpoints using the same handlers as the parent endpoint but
		// with a different path and name (so the handler can differentiate being called via
		// a different endpoint) if it wants to.
		for _, alias := range c.Aliases {
			ac := c
			ac.Name = alias.Name
			ac.Path = alias.Path
			d.createCmd(mux, "1.0", ac)
		}
	}

	for _, c := range apiInternal {
		d.createCmd(mux, "internal", c)
	}

	mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger.Info("Sending top level 404", log.Ctx{"url": r.URL})
		w.Header().Set("Content-Type", "application/json")
		NotFound(nil).Render(w)
	})

	return &http.Server{Handler: &lxdHttpServer{r: mux, d: d}}
}

type lxdHttpServer struct {
	r *mux.Router
	d *Daemon
}

func (s *lxdHttpServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	// Set CORS headers, unless this is an internal or gRPC request.
	if !strings.HasPrefix(req.URL.Path, "/internal") && !strings.HasPrefix(req.URL.Path, "/protocol.SQL") {
		<-s.d.setupChan
		err := s.d.cluster.Transaction(func(tx *db.ClusterTx) error {
			config, err := cluster.ConfigLoad(tx)
			if err != nil {
				return err
			}
			setCORSHeaders(rw, req, config)
			return nil
		})
		if err != nil {
			response := SmartError(err)
			response.Render(rw)
			return
		}
	}

	// OPTIONS request don't need any further processing
	if req.Method == "OPTIONS" {
		return
	}

	// Call the original server
	s.r.ServeHTTP(rw, req)
}

func setCORSHeaders(rw http.ResponseWriter, req *http.Request, config *cluster.Config) {
	allowedOrigin := config.HTTPSAllowedOrigin()
	origin := req.Header.Get("Origin")
	if allowedOrigin != "" && origin != "" {
		rw.Header().Set("Access-Control-Allow-Origin", allowedOrigin)
	}

	allowedMethods := config.HTTPSAllowedMethods()
	if allowedMethods != "" && origin != "" {
		rw.Header().Set("Access-Control-Allow-Methods", allowedMethods)
	}

	allowedHeaders := config.HTTPSAllowedHeaders()
	if allowedHeaders != "" && origin != "" {
		rw.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
	}

	allowedCredentials := config.HTTPSAllowedCredentials()
	if allowedCredentials {
		rw.Header().Set("Access-Control-Allow-Credentials", "true")
	}
}

// Return true if this an API request coming from a cluster node that is
// notifying us of some user-initiated API request that needs some action to be
// taken on this node as well.
func isClusterNotification(r *http.Request) bool {
	return r.Header.Get("User-Agent") == "lxd-cluster-notifier"
}

// Extract the project query parameter from the given request.
func projectParam(request *http.Request) string {
	project := queryParam(request, "project")
	if project == "" {
		project = "default"
	}
	return project
}

// Extract the given query parameter directly from the URL, never from an
// encoded body.
func queryParam(request *http.Request, key string) string {
	var values url.Values
	var err error

	if request.URL != nil {
		values, err = url.ParseQuery(request.URL.RawQuery)
		if err != nil {
			logger.Warnf("Failed to parse query string %q: %v", request.URL.RawQuery, err)
			return ""
		}
	}

	if values == nil {
		values = make(url.Values)
	}

	return values.Get(key)
}
<|endoftext|>


<commit_before>package xml

/*
#cgo pkg-config: libxml-2.0

#include "helper.h"
*/
import "C"

import (
	"errors"
	"github.com/moovweb/gokogiri/help"
	. "github.com/moovweb/gokogiri/util"
	"github.com/moovweb/gokogiri/xpath"
	//"runtime"
	"unsafe"
)

type Document interface {
	/* Nokogiri APIs */
	CreateElementNode(string) *ElementNode
	CreateCDataNode(string) *CDataNode
	CreateTextNode(string) *TextNode
	CreateCommentNode(string) *CommentNode
	CreatePINode(string, string) *ProcessingInstructionNode
	ParseFragment([]byte, []byte, ParseOption) (*DocumentFragment, error)

	DocPtr() unsafe.Pointer
	DocType() NodeType
	DocRef() Document
	InputEncoding() []byte
	OutputEncoding() []byte
	DocXPathCtx() *xpath.XPath
	AddUnlinkedNode(unsafe.Pointer)
	RemoveUnlinkedNode(unsafe.Pointer) bool
	Free()
	String() string
	Root() *ElementNode
	NodeById(string) *ElementNode
	BookkeepFragment(*DocumentFragment)

	RecursivelyRemoveNamespaces() error
	UnparsedEntityURI(string) string
}

type ParseOption int

const (
	XML_PARSE_RECOVER    ParseOption = 1 << iota // recover on errors
	XML_PARSE_NOENT                              // substitute entities
	XML_PARSE_DTDLOAD                            // load the external subset
	XML_PARSE_DTDATTR                            // default DTD attributes
	XML_PARSE_DTDVALID                           // validate with the DTD
	XML_PARSE_NOERROR                            // suppress error reports
	XML_PARSE_NOWARNING                          // suppress warning reports
	XML_PARSE_PEDANTIC                           // pedantic error reporting
	XML_PARSE_NOBLANKS                           // remove blank nodes
	XML_PARSE_SAX1                               // use the SAX1 interface internally
	XML_PARSE_XINCLUDE                           // Implement XInclude substitition
	XML_PARSE_NONET                              // Forbid network access
	XML_PARSE_NODICT                             // Do not reuse the context dictionnary
	XML_PARSE_NSCLEAN                            // remove redundant namespaces declarations
	XML_PARSE_NOCDATA                            // merge CDATA as text nodes
	XML_PARSE_NOXINCNODE                         // do not generate XINCLUDE START/END nodes
	XML_PARSE_COMPACT                            // compact small text nodes; makes tree read-only
	XML_PARSE_OLD10                              // parse using XML-1.0 before update 5
	XML_PARSE_NOBASEFIX                          // do not fixup XINCLUDE xml//base uris
	XML_PARSE_HUGE                               // relax any hardcoded limit from the parser
	XML_PARSE_OLDSAX                             // parse using SAX2 interface before 2.7.0
	XML_PARSE_IGNORE_ENC                         // ignore internal document encoding hint
	XML_PARSE_BIG_LINES                          // Store big lines numbers in text PSVI field
)

//default parsing option: relax parsing
const DefaultParseOption ParseOption = XML_PARSE_RECOVER |
	XML_PARSE_NONET |
	XML_PARSE_NOERROR |
	XML_PARSE_NOWARNING

//Stricter parsing option: load the DTD and report errors
const StrictParseOption ParseOption = XML_PARSE_NOENT |
	XML_PARSE_DTDLOAD |
	XML_PARSE_DTDATTR |
	XML_PARSE_NOCDATA

//libxml2 use "utf-8" by default, and so do we
const DefaultEncoding = "utf-8"

var ERR_FAILED_TO_PARSE_XML = errors.New("failed to parse xml input")

type XmlDocument struct {
	Ptr *C.xmlDoc
	Me  Document
	Node
	InEncoding    []byte
	OutEncoding   []byte
	UnlinkedNodes map[*C.xmlNode]bool
	XPathCtx      *xpath.XPath
	Type          NodeType
	InputLen      int

	fragments []*DocumentFragment //save the pointers to free them when the doc is freed
}

//default encoding in byte slice
var DefaultEncodingBytes = []byte(DefaultEncoding)

const initialFragments = 2

//create a document
func NewDocument(p unsafe.Pointer, contentLen int, inEncoding, outEncoding []byte) (doc *XmlDocument) {
	inEncoding = AppendCStringTerminator(inEncoding)
	outEncoding = AppendCStringTerminator(outEncoding)

	xmlNode := &XmlNode{Ptr: (*C.xmlNode)(p)}
	docPtr := (*C.xmlDoc)(p)
	doc = &XmlDocument{Ptr: docPtr, Node: xmlNode, InEncoding: inEncoding, OutEncoding: outEncoding, InputLen: contentLen}
	doc.UnlinkedNodes = make(map[*C.xmlNode]bool)
	doc.XPathCtx = xpath.NewXPath(p)
	doc.Type = xmlNode.NodeType()
	doc.fragments = make([]*DocumentFragment, 0, initialFragments)
	doc.Me = doc
	xmlNode.Document = doc
	//runtime.SetFinalizer(doc, (*XmlDocument).Free)
	return
}

func Parse(content, inEncoding, url []byte, options ParseOption, outEncoding []byte) (doc *XmlDocument, err error) {
	inEncoding = AppendCStringTerminator(inEncoding)
	outEncoding = AppendCStringTerminator(outEncoding)

	var docPtr *C.xmlDoc
	contentLen := len(content)

	if contentLen > 0 {
		var contentPtr, urlPtr, encodingPtr unsafe.Pointer
		contentPtr = unsafe.Pointer(&content[0])

		if len(url) > 0 {
			url = AppendCStringTerminator(url)
			urlPtr = unsafe.Pointer(&url[0])
		}
		if len(inEncoding) > 0 {
			encodingPtr = unsafe.Pointer(&inEncoding[0])
		}

		docPtr = C.xmlParse(contentPtr, C.int(contentLen), urlPtr, encodingPtr, C.int(options), nil, 0)

		if docPtr == nil {
			err = ERR_FAILED_TO_PARSE_XML
		} else {
			doc = NewDocument(unsafe.Pointer(docPtr), contentLen, inEncoding, outEncoding)
		}

	} else {
		doc = CreateEmptyDocument(inEncoding, outEncoding)
	}
	return
}

func CreateEmptyDocument(inEncoding, outEncoding []byte) (doc *XmlDocument) {
	help.LibxmlInitParser()
	docPtr := C.newEmptyXmlDoc()
	doc = NewDocument(unsafe.Pointer(docPtr), 0, inEncoding, outEncoding)
	return
}

func (document *XmlDocument) DocPtr() (ptr unsafe.Pointer) {
	ptr = unsafe.Pointer(document.Ptr)
	return
}

func (document *XmlDocument) DocType() (t NodeType) {
	t = document.Type
	return
}

func (document *XmlDocument) DocRef() (d Document) {
	d = document.Me
	return
}

func (document *XmlDocument) InputEncoding() (encoding []byte) {
	encoding = document.InEncoding
	return
}

func (document *XmlDocument) OutputEncoding() (encoding []byte) {
	encoding = document.OutEncoding
	return
}

func (document *XmlDocument) DocXPathCtx() (ctx *xpath.XPath) {
	ctx = document.XPathCtx
	return
}

func (document *XmlDocument) AddUnlinkedNode(nodePtr unsafe.Pointer) {
	p := (*C.xmlNode)(nodePtr)
	document.UnlinkedNodes[p] = true
}

func (document *XmlDocument) RemoveUnlinkedNode(nodePtr unsafe.Pointer) bool {
	p := (*C.xmlNode)(nodePtr)
	if document.UnlinkedNodes[p] {
		delete(document.UnlinkedNodes, p)
		return true
	}
	return false
}

func (document *XmlDocument) BookkeepFragment(fragment *DocumentFragment) {
	document.fragments = append(document.fragments, fragment)
}

func (document *XmlDocument) Root() (element *ElementNode) {
	nodePtr := C.xmlDocGetRootElement(document.Ptr)
	if nodePtr != nil {
		element = NewNode(unsafe.Pointer(nodePtr), document).(*ElementNode)
	}
	return
}

// Get an element node by the value of its ID attribute. By convention this attribute
// is named id, but the actual name of the attribute is set by the document's DTD or schema.

// The value for an ID attribute is guaranteed to be unique within a valid document.
func (document *XmlDocument) NodeById(id string) (element *ElementNode) {
	dataBytes := GetCString([]byte(id))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlGetID(document.Ptr, (*C.xmlChar)(dataPtr))
	if nodePtr != nil {
		idattr := NewNode(unsafe.Pointer(nodePtr), document).(*AttributeNode)
		element = idattr.Parent().(*ElementNode)
	}
	return
}

func (document *XmlDocument) CreateElementNode(tag string) (element *ElementNode) {
	tagBytes := GetCString([]byte(tag))
	tagPtr := unsafe.Pointer(&tagBytes[0])
	newNodePtr := C.xmlNewNode(nil, (*C.xmlChar)(tagPtr))
	newNode := NewNode(unsafe.Pointer(newNodePtr), document)
	element = newNode.(*ElementNode)
	return
}

func (document *XmlDocument) CreateTextNode(data string) (text *TextNode) {
	dataBytes := GetCString([]byte(data))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlNewText((*C.xmlChar)(dataPtr))
	if nodePtr != nil {
		nodePtr.doc = (*_Ctype_struct__xmlDoc)(document.DocPtr())
		text = NewNode(unsafe.Pointer(nodePtr), document).(*TextNode)
	}
	return
}

func (document *XmlDocument) CreateCDataNode(data string) (cdata *CDataNode) {
	dataLen := len(data)
	dataBytes := GetCString([]byte(data))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlNewCDataBlock(document.Ptr, (*C.xmlChar)(dataPtr), C.int(dataLen))
	if nodePtr != nil {
		cdata = NewNode(unsafe.Pointer(nodePtr), document).(*CDataNode)
	}
	return
}

func (document *XmlDocument) CreateCommentNode(data string) (comment *CommentNode) {
	dataBytes := GetCString([]byte(data))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlNewComment((*C.xmlChar)(dataPtr))
	if nodePtr != nil {
		comment = NewNode(unsafe.Pointer(nodePtr), document).(*CommentNode)
	}
	return
}

func (document *XmlDocument) CreatePINode(name, data string) (pi *ProcessingInstructionNode) {
	nameBytes := GetCString([]byte(name))
	namePtr := unsafe.Pointer(&nameBytes[0])
	dataBytes := GetCString([]byte(data))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlNewDocPI(document.Ptr, (*C.xmlChar)(namePtr), (*C.xmlChar)(dataPtr))
	if nodePtr != nil {
		pi = NewNode(unsafe.Pointer(nodePtr), document).(*ProcessingInstructionNode)
	}
	return
}

func (document *XmlDocument) ParseFragment(input, url []byte, options ParseOption) (fragment *DocumentFragment, err error) {
	root := document.Root()
	if root == nil {
		fragment, err = parsefragment(document, nil, input, url, options)
	} else {
		fragment, err = parsefragment(document, root.XmlNode, input, url, options)
	}
	return
}

// Return the value of an NDATA entity declared in the DTD. If there is no such entity or
// the value cannot be encoded as a valid URI, an empty string is returned.
//
// Note that this library assumes you already know the name of entity and does not
// expose any way of getting the list of entities.
func (document *XmlDocument) UnparsedEntityURI(name string) (val string) {
	if name == "" {
		return
	}

	nameBytes := GetCString([]byte(name))
	namePtr := unsafe.Pointer(&nameBytes[0])
	entity := C.xmlGetDocEntity(document.Ptr, (*C.xmlChar)(namePtr))
	if entity == nil {
		return
	}

	// unlike entity.content (which returns the raw, unprocessed string value of the entity),
	// it looks like entity.URI includes any escaping needed to treat the value as a URI.
	valPtr := unsafe.Pointer(entity.URI)
	if valPtr == nil {
		return
	}

	val = C.GoString((*C.char)(valPtr))
	return
}

// Free the C structures associated with this document.
func (document *XmlDocument) Free() {
	//must free the xpath context before freeing the fragments or unlinked nodes
	//otherwise, it causes memory leaks and crashes when dealing with very large documents (a few MB)
	if document.XPathCtx != nil {
		document.XPathCtx.Free()
		document.XPathCtx = nil
	}
	//must clear the fragments first
	//because the nodes are put in the unlinked list
	if document.fragments != nil {
		for _, fragment := range document.fragments {
			fragment.Remove()
		}
	}
	document.fragments = nil
	var p *C.xmlNode
	if document.UnlinkedNodes != nil {
		for p, _ = range document.UnlinkedNodes {
			C.xmlFreeNode(p)
		}
	}
	document.UnlinkedNodes = nil
	if document.Ptr != nil {
		C.xmlFreeDoc(document.Ptr)
		document.Ptr = nil
	}
}
<commit_msg>Add xml.ReadFile API as an alternative to calling xml.Parse<commit_after>package xml

/*
#cgo pkg-config: libxml-2.0

#include "helper.h"
*/
import "C"

import (
	"errors"
	"github.com/moovweb/gokogiri/help"
	. "github.com/moovweb/gokogiri/util"
	"github.com/moovweb/gokogiri/xpath"
	"os"
	"unsafe"
)

type Document interface {
	/* Nokogiri APIs */
	CreateElementNode(string) *ElementNode
	CreateCDataNode(string) *CDataNode
	CreateTextNode(string) *TextNode
	CreateCommentNode(string) *CommentNode
	CreatePINode(string, string) *ProcessingInstructionNode
	ParseFragment([]byte, []byte, ParseOption) (*DocumentFragment, error)

	DocPtr() unsafe.Pointer
	DocType() NodeType
	DocRef() Document
	InputEncoding() []byte
	OutputEncoding() []byte
	DocXPathCtx() *xpath.XPath
	AddUnlinkedNode(unsafe.Pointer)
	RemoveUnlinkedNode(unsafe.Pointer) bool
	Free()
	String() string
	Root() *ElementNode
	NodeById(string) *ElementNode
	BookkeepFragment(*DocumentFragment)

	RecursivelyRemoveNamespaces() error
	UnparsedEntityURI(string) string
}

type ParseOption int

const (
	XML_PARSE_RECOVER    ParseOption = 1 << iota // recover on errors
	XML_PARSE_NOENT                              // substitute entities
	XML_PARSE_DTDLOAD                            // load the external subset
	XML_PARSE_DTDATTR                            // default DTD attributes
	XML_PARSE_DTDVALID                           // validate with the DTD
	XML_PARSE_NOERROR                            // suppress error reports
	XML_PARSE_NOWARNING                          // suppress warning reports
	XML_PARSE_PEDANTIC                           // pedantic error reporting
	XML_PARSE_NOBLANKS                           // remove blank nodes
	XML_PARSE_SAX1                               // use the SAX1 interface internally
	XML_PARSE_XINCLUDE                           // Implement XInclude substitition
	XML_PARSE_NONET                              // Forbid network access
	XML_PARSE_NODICT                             // Do not reuse the context dictionnary
	XML_PARSE_NSCLEAN                            // remove redundant namespaces declarations
	XML_PARSE_NOCDATA                            // merge CDATA as text nodes
	XML_PARSE_NOXINCNODE                         // do not generate XINCLUDE START/END nodes
	XML_PARSE_COMPACT                            // compact small text nodes; makes tree read-only
	XML_PARSE_OLD10                              // parse using XML-1.0 before update 5
	XML_PARSE_NOBASEFIX                          // do not fixup XINCLUDE xml//base uris
	XML_PARSE_HUGE                               // relax any hardcoded limit from the parser
	XML_PARSE_OLDSAX                             // parse using SAX2 interface before 2.7.0
	XML_PARSE_IGNORE_ENC                         // ignore internal document encoding hint
	XML_PARSE_BIG_LINES                          // Store big lines numbers in text PSVI field
)

//default parsing option: relax parsing
const DefaultParseOption ParseOption = XML_PARSE_RECOVER |
	XML_PARSE_NONET |
	XML_PARSE_NOERROR |
	XML_PARSE_NOWARNING

//Stricter parsing option: load the DTD and report errors
const StrictParseOption ParseOption = XML_PARSE_NOENT |
	XML_PARSE_DTDLOAD |
	XML_PARSE_DTDATTR |
	XML_PARSE_NOCDATA

//libxml2 use "utf-8" by default, and so do we
const DefaultEncoding = "utf-8"

var ERR_FAILED_TO_PARSE_XML = errors.New("failed to parse xml input")

type XmlDocument struct {
	Ptr *C.xmlDoc
	Me  Document
	Node
	InEncoding    []byte
	OutEncoding   []byte
	UnlinkedNodes map[*C.xmlNode]bool
	XPathCtx      *xpath.XPath
	Type          NodeType
	InputLen      int

	fragments []*DocumentFragment //save the pointers to free them when the doc is freed
}

//default encoding in byte slice
var DefaultEncodingBytes = []byte(DefaultEncoding)

const initialFragments = 2

//create a document
func NewDocument(p unsafe.Pointer, contentLen int, inEncoding, outEncoding []byte) (doc *XmlDocument) {
	inEncoding = AppendCStringTerminator(inEncoding)
	outEncoding = AppendCStringTerminator(outEncoding)

	xmlNode := &XmlNode{Ptr: (*C.xmlNode)(p)}
	docPtr := (*C.xmlDoc)(p)
	doc = &XmlDocument{Ptr: docPtr, Node: xmlNode, InEncoding: inEncoding, OutEncoding: outEncoding, InputLen: contentLen}
	doc.UnlinkedNodes = make(map[*C.xmlNode]bool)
	doc.XPathCtx = xpath.NewXPath(p)
	doc.Type = xmlNode.NodeType()
	doc.fragments = make([]*DocumentFragment, 0, initialFragments)
	doc.Me = doc
	xmlNode.Document = doc
	//runtime.SetFinalizer(doc, (*XmlDocument).Free)
	return
}

// Parse creates an XmlDcument from some pre-existing content where the input encoding is known. Byte arrays created from
// a Go string are utf-8 encoded (you can pass DefaultEncodingBytes in this scenario).

// If you want to build up a document programatically, calling CreateEmptyDocument and building it up using the xml.Node
// interface is a better approach than building a string and calling Parse.

// If you have an XML file, then ReadFile will automatically determine the encoding according to the XML specification.
func Parse(content, inEncoding, url []byte, options ParseOption, outEncoding []byte) (doc *XmlDocument, err error) {
	inEncoding = AppendCStringTerminator(inEncoding)
	outEncoding = AppendCStringTerminator(outEncoding)

	var docPtr *C.xmlDoc
	contentLen := len(content)

	if contentLen > 0 {
		var contentPtr, urlPtr, encodingPtr unsafe.Pointer
		contentPtr = unsafe.Pointer(&content[0])

		if len(url) > 0 {
			url = AppendCStringTerminator(url)
			urlPtr = unsafe.Pointer(&url[0])
		}
		if len(inEncoding) > 0 {
			encodingPtr = unsafe.Pointer(&inEncoding[0])
		}

		docPtr = C.xmlParse(contentPtr, C.int(contentLen), urlPtr, encodingPtr, C.int(options), nil, 0)

		if docPtr == nil {
			err = ERR_FAILED_TO_PARSE_XML
		} else {
			doc = NewDocument(unsafe.Pointer(docPtr), contentLen, inEncoding, outEncoding)
		}

	} else {
		doc = CreateEmptyDocument(inEncoding, outEncoding)
	}
	return
}

// ReadFile loads an XmlDocument from a filename. The encoding declared in the document will be
// used as the input encoding. If no encoding is declared, the library will use the alogrithm
// in the XML standard to determine if the document is encoded with UTF-8 or UTF-16.
func ReadFile(filename string, options ParseOption) (doc *XmlDocument, err error) {
	// verify the file exists and can be read before we invoke C API
	_, err = os.Stat(filename)
	if err != nil {
		return
	}

	dataBytes := GetCString([]byte(filename))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	var docPtr *C.xmlDoc
	docPtr = C.xmlReadFile((*C.char)(dataPtr), nil, C.int(options))
	if docPtr == nil {
		err = ERR_FAILED_TO_PARSE_XML
	} else {
		var encoding []byte
		// capture the detected input encoding
		p := docPtr.encoding
		if p != nil {
			encoding = []byte(C.GoString((*C.char)(unsafe.Pointer(p))))
		}
		doc = NewDocument(unsafe.Pointer(docPtr), 0, encoding, DefaultEncodingBytes)
	}
	return
}

// Create an empty XML document and return an XmlDocument. The root element, along with
// any top-level comments or processing instructions, can be added by calling
// AddChild() on the document itself.
func CreateEmptyDocument(inEncoding, outEncoding []byte) (doc *XmlDocument) {
	help.LibxmlInitParser()
	docPtr := C.newEmptyXmlDoc()
	doc = NewDocument(unsafe.Pointer(docPtr), 0, inEncoding, outEncoding)
	return
}

func (document *XmlDocument) DocPtr() (ptr unsafe.Pointer) {
	ptr = unsafe.Pointer(document.Ptr)
	return
}

func (document *XmlDocument) DocType() (t NodeType) {
	t = document.Type
	return
}

func (document *XmlDocument) DocRef() (d Document) {
	d = document.Me
	return
}

func (document *XmlDocument) InputEncoding() (encoding []byte) {
	encoding = document.InEncoding
	return
}

func (document *XmlDocument) OutputEncoding() (encoding []byte) {
	encoding = document.OutEncoding
	return
}

func (document *XmlDocument) DocXPathCtx() (ctx *xpath.XPath) {
	ctx = document.XPathCtx
	return
}

func (document *XmlDocument) AddUnlinkedNode(nodePtr unsafe.Pointer) {
	p := (*C.xmlNode)(nodePtr)
	document.UnlinkedNodes[p] = true
}

func (document *XmlDocument) RemoveUnlinkedNode(nodePtr unsafe.Pointer) bool {
	p := (*C.xmlNode)(nodePtr)
	if document.UnlinkedNodes[p] {
		delete(document.UnlinkedNodes, p)
		return true
	}
	return false
}

func (document *XmlDocument) BookkeepFragment(fragment *DocumentFragment) {
	document.fragments = append(document.fragments, fragment)
}

func (document *XmlDocument) Root() (element *ElementNode) {
	nodePtr := C.xmlDocGetRootElement(document.Ptr)
	if nodePtr != nil {
		element = NewNode(unsafe.Pointer(nodePtr), document).(*ElementNode)
	}
	return
}

// Get an element node by the value of its ID attribute. By convention this attribute
// is named id, but the actual name of the attribute is set by the document's DTD or schema.

// The value for an ID attribute is guaranteed to be unique within a valid document.
func (document *XmlDocument) NodeById(id string) (element *ElementNode) {
	dataBytes := GetCString([]byte(id))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlGetID(document.Ptr, (*C.xmlChar)(dataPtr))
	if nodePtr != nil {
		idattr := NewNode(unsafe.Pointer(nodePtr), document).(*AttributeNode)
		element = idattr.Parent().(*ElementNode)
	}
	return
}

func (document *XmlDocument) CreateElementNode(tag string) (element *ElementNode) {
	tagBytes := GetCString([]byte(tag))
	tagPtr := unsafe.Pointer(&tagBytes[0])
	newNodePtr := C.xmlNewNode(nil, (*C.xmlChar)(tagPtr))
	newNode := NewNode(unsafe.Pointer(newNodePtr), document)
	element = newNode.(*ElementNode)
	return
}

func (document *XmlDocument) CreateTextNode(data string) (text *TextNode) {
	dataBytes := GetCString([]byte(data))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlNewText((*C.xmlChar)(dataPtr))
	if nodePtr != nil {
		nodePtr.doc = (*_Ctype_struct__xmlDoc)(document.DocPtr())
		text = NewNode(unsafe.Pointer(nodePtr), document).(*TextNode)
	}
	return
}

func (document *XmlDocument) CreateCDataNode(data string) (cdata *CDataNode) {
	dataLen := len(data)
	dataBytes := GetCString([]byte(data))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlNewCDataBlock(document.Ptr, (*C.xmlChar)(dataPtr), C.int(dataLen))
	if nodePtr != nil {
		cdata = NewNode(unsafe.Pointer(nodePtr), document).(*CDataNode)
	}
	return
}

func (document *XmlDocument) CreateCommentNode(data string) (comment *CommentNode) {
	dataBytes := GetCString([]byte(data))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlNewComment((*C.xmlChar)(dataPtr))
	if nodePtr != nil {
		comment = NewNode(unsafe.Pointer(nodePtr), document).(*CommentNode)
	}
	return
}

func (document *XmlDocument) CreatePINode(name, data string) (pi *ProcessingInstructionNode) {
	nameBytes := GetCString([]byte(name))
	namePtr := unsafe.Pointer(&nameBytes[0])
	dataBytes := GetCString([]byte(data))
	dataPtr := unsafe.Pointer(&dataBytes[0])
	nodePtr := C.xmlNewDocPI(document.Ptr, (*C.xmlChar)(namePtr), (*C.xmlChar)(dataPtr))
	if nodePtr != nil {
		pi = NewNode(unsafe.Pointer(nodePtr), document).(*ProcessingInstructionNode)
	}
	return
}

func (document *XmlDocument) ParseFragment(input, url []byte, options ParseOption) (fragment *DocumentFragment, err error) {
	root := document.Root()
	if root == nil {
		fragment, err = parsefragment(document, nil, input, url, options)
	} else {
		fragment, err = parsefragment(document, root.XmlNode, input, url, options)
	}
	return
}

// Return the value of an NDATA entity declared in the DTD. If there is no such entity or
// the value cannot be encoded as a valid URI, an empty string is returned.
//
// Note that this library assumes you already know the name of entity and does not
// expose any way of getting the list of entities.
func (document *XmlDocument) UnparsedEntityURI(name string) (val string) {
	if name == "" {
		return
	}

	nameBytes := GetCString([]byte(name))
	namePtr := unsafe.Pointer(&nameBytes[0])
	entity := C.xmlGetDocEntity(document.Ptr, (*C.xmlChar)(namePtr))
	if entity == nil {
		return
	}

	// unlike entity.content (which returns the raw, unprocessed string value of the entity),
	// it looks like entity.URI includes any escaping needed to treat the value as a URI.
	valPtr := unsafe.Pointer(entity.URI)
	if valPtr == nil {
		return
	}

	val = C.GoString((*C.char)(valPtr))
	return
}

// Free the C structures associated with this document.
func (document *XmlDocument) Free() {
	//must free the xpath context before freeing the fragments or unlinked nodes
	//otherwise, it causes memory leaks and crashes when dealing with very large documents (a few MB)
	if document.XPathCtx != nil {
		document.XPathCtx.Free()
		document.XPathCtx = nil
	}
	//must clear the fragments first
	//because the nodes are put in the unlinked list
	if document.fragments != nil {
		for _, fragment := range document.fragments {
			fragment.Remove()
		}
	}
	document.fragments = nil
	var p *C.xmlNode
	if document.UnlinkedNodes != nil {
		for p, _ = range document.UnlinkedNodes {
			C.xmlFreeNode(p)
		}
	}
	document.UnlinkedNodes = nil
	if document.Ptr != nil {
		C.xmlFreeDoc(document.Ptr)
		document.Ptr = nil
	}
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package newton

import (
	"github.com/szabba/md/src/vect"
)

// An integrator algorithm
type Integrator interface {
	// The total number of states kept at once by the algorithm
	StateLen() int
	// The location of the current state in the sequence of states kept track
	// of
	CurrentAt() int
	// Performs the integration for a single body
	Integrate(b *Body, a vect.Vector, dt float64)
}

// Performs a step of an Euler integration
func Euler(xs, vs []vect.Vector, a vect.Vector, dt float64) {

	v := vs[0].Plus(a.Scale(dt))
	x := xs[0].Plus(vs[0].Scale(dt))

	Shift(vs, v)
	Shift(xs, x)
}

// Performs a step of a Verlet integrator
//
// Note that v[0] will not be calculated until the next step
func Verlet(xs, vs []vect.Vector, a vect.Vector, dt float64) {

	xNext := xs[0].Scale(2).Minus(xs[1]).Plus(a.Scale(dt * dt))
	vs[0] = xNext.Minus(xs[1]).Scale(1 / (2 * dt))

	Shift(vs, vect.Zero)
	Shift(xs, xNext)
}

func Step(algo Integrator, bs []Body, f Force, dt float64) {

	as := make([]vect.Vector, len(bs))

	for i, _ := range bs {

		as[i] = f.Accel(bs, i)
	}

	for i, body := range bs {

		algo.Integrate(body, as[i], dt)
	}
}
<commit_msg>Reimplement newton.Euler<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package newton

import (
	"github.com/szabba/md/src/vect"
)

// An integrator algorithm
type Integrator interface {
	// The total number of states kept at once by the algorithm
	StateLen() int
	// The location of the current state in the sequence of states kept track
	// of
	CurrentAt() int
	// Performs the integration for a single body
	Integrate(b *Body, a vect.Vector, dt float64)
}

var (
	Euler Integrator = euler{}
)

type euler struct{}

func (_ euler) StateLen() int {
	return 1
}

func (_ euler) CurrentAt() int {
	return 0
}

func (_ euler) Integrate(b *Body, a vect.Vector, dt float64) {

	x0, v0 := b.Now()

	v := v0.Plus(a.Scale(dt))
	x := x0.Plus(v0.Scale(dt))

	b.Shift(x, v)
}

// Performs a step of a Verlet integrator
//
// Note that v[0] will not be calculated until the next step
func Verlet(xs, vs []vect.Vector, a vect.Vector, dt float64) {

	xNext := xs[0].Scale(2).Minus(xs[1]).Plus(a.Scale(dt * dt))
	vs[0] = xNext.Minus(xs[1]).Scale(1 / (2 * dt))

	Shift(vs, vect.Zero)
	Shift(xs, xNext)
}

func Step(algo Integrator, bs []Body, f Force, dt float64) {

	as := make([]vect.Vector, len(bs))

	for i, _ := range bs {

		as[i] = f.Accel(bs, i)
	}

	for i, body := range bs {

		algo.Integrate(body, as[i], dt)
	}
}
<|endoftext|>


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

// Package macaron is a high productive and modular design web framework in Go.
package macaron

import (
	"io"
	"log"
	"net/http"
	"os"
	"reflect"
	"strings"

	"github.com/Unknwon/com"
	"gopkg.in/ini.v0"

	"github.com/Unknwon/macaron/inject"
)

const _VERSION = "0.4.9.1229"

func Version() string {
	return _VERSION
}

// Handler can be any callable function.
// Macaron attempts to inject services into the handler's argument list,
// and panics if an argument could not be fullfilled via dependency injection.
type Handler interface{}

// validateHandler makes sure a handler is a callable function,
// and panics if it is not.
func validateHandler(h Handler) {
	if reflect.TypeOf(h).Kind() != reflect.Func {
		panic("Macaron handler must be a callable function")
	}
}

// validateHandlers makes sure handlers are callable functions,
// and panics if any of them is not.
func validateHandlers(handlers []Handler) {
	for _, h := range handlers {
		validateHandler(h)
	}
}

// Macaron represents the top level web application.
// inject.Injector methods can be invoked to map services on a global level.
type Macaron struct {
	inject.Injector
	befores  []BeforeHandler
	handlers []Handler
	action   Handler

	urlPrefix string // For suburl support.
	*Router

	logger *log.Logger
}

// NewWithLogger creates a bare bones Macaron instance.
// Use this method if you want to have full control over the middleware that is used.
// You can specify logger output writer with this function.
func NewWithLogger(out io.Writer) *Macaron {
	m := &Macaron{
		Injector: inject.New(),
		action:   func() {},
		Router:   NewRouter(),
		logger:   log.New(out, "[Macaron] ", 0),
	}
	m.Router.m = m
	m.Map(m.logger)
	m.Map(defaultReturnHandler())
	m.notFound = func(resp http.ResponseWriter, req *http.Request) {
		c := m.createContext(resp, req)
		c.handlers = append(c.handlers, http.NotFound)
		c.run()
	}
	return m
}

// New creates a bare bones Macaron instance.
// Use this method if you want to have full control over the middleware that is used.
func New() *Macaron {
	return NewWithLogger(os.Stdout)
}

// Classic creates a classic Macaron with some basic default middleware:
// mocaron.Logger, mocaron.Recovery and mocaron.Static.
func Classic() *Macaron {
	m := New()
	m.Use(Logger())
	m.Use(Recovery())
	m.Use(Static("public"))
	return m
}

// Handlers sets the entire middleware stack with the given Handlers.
// This will clear any current middleware handlers,
// and panics if any of the handlers is not a callable function
func (m *Macaron) Handlers(handlers ...Handler) {
	m.handlers = make([]Handler, 0)
	for _, handler := range handlers {
		m.Use(handler)
	}
}

// Action sets the handler that will be called after all the middleware has been invoked.
// This is set to macaron.Router in a macaron.Classic().
func (m *Macaron) Action(handler Handler) {
	validateHandler(handler)
	m.action = handler
}

// BeforeHandler represents a handler executes at beginning of every request.
// Macaron stops future process when it returns true.
type BeforeHandler func(rw http.ResponseWriter, req *http.Request) bool

func (m *Macaron) Before(handler BeforeHandler) {
	m.befores = append(m.befores, handler)
}

// Use adds a middleware Handler to the stack,
// and panics if the handler is not a callable func.
// Middleware Handlers are invoked in the order that they are added.
func (m *Macaron) Use(handler Handler) {
	validateHandler(handler)
	m.handlers = append(m.handlers, handler)
}

func (m *Macaron) createContext(rw http.ResponseWriter, req *http.Request) *Context {
	c := &Context{
		Injector: inject.New(),
		handlers: m.handlers,
		action:   m.action,
		index:    0,
		Router:   m.Router,
		Req:      Request{req},
		Resp:     NewResponseWriter(rw),
		Data:     make(map[string]interface{}),
	}
	c.SetParent(m)
	c.Map(c)
	c.MapTo(c.Resp, (*http.ResponseWriter)(nil))
	c.Map(req)
	return c
}

// ServeHTTP is the HTTP Entry point for a Macaron instance.
// Useful if you want to control your own HTTP server.
// Be aware that none of middleware will run without registering any router.
func (m *Macaron) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	req.URL.Path = strings.TrimPrefix(req.URL.Path, m.urlPrefix)
	for _, h := range m.befores {
		if h(rw, req) {
			return
		}
	}
	m.Router.ServeHTTP(rw, req)
}

func GetDefaultListenInfo() (string, int) {
	host := os.Getenv("HOST")
	if len(host) == 0 {
		host = "0.0.0.0"
	}
	port := com.StrTo(os.Getenv("PORT")).MustInt()
	if port == 0 {
		port = 4000
	}
	return host, port
}

// Run the http server. Listening on os.GetEnv("PORT") or 4000 by default.
func (m *Macaron) Run(args ...interface{}) {
	host, port := GetDefaultListenInfo()
	if len(args) == 1 {
		switch arg := args[0].(type) {
		case string:
			host = arg
		case int:
			port = arg
		}
	} else if len(args) >= 2 {
		if arg, ok := args[0].(string); ok {
			host = arg
		}
		if arg, ok := args[1].(int); ok {
			port = arg
		}
	}

	addr := host + ":" + com.ToStr(port)
	logger := m.Injector.GetVal(reflect.TypeOf(m.logger)).Interface().(*log.Logger)
	logger.Printf("listening on %s (%s)\n", addr, Env)
	logger.Fatalln(http.ListenAndServe(addr, m))
}

// SetURLPrefix sets URL prefix of router layer, so that it support suburl.
func (m *Macaron) SetURLPrefix(prefix string) {
	m.urlPrefix = prefix
}

// ____   ____            .__      ___.   .__
// \   \ /   /____ _______|__|____ \_ |__ |  |   ____   ______
//  \   Y   /\__  \\_  __ \  \__  \ | __ \|  | _/ __ \ /  ___/
//   \     /  / __ \|  | \/  |/ __ \| \_\ \  |_\  ___/ \___ \
//    \___/  (____  /__|  |__(____  /___  /____/\___  >____  >
//                \/              \/    \/          \/     \/

const (
	DEV  = "development"
	PROD = "production"
	TEST = "test"
)

var (
	// Env is the environment that Macaron is executing in.
	// The MACARON_ENV is read on initialization to set this variable.
	Env = DEV

	// Path of work directory.
	Root string

	// Flash applies to current request.
	FlashNow bool

	// Configuration convention object.
	cfg *ini.File
)

func setENV(e string) {
	if len(e) > 0 {
		Env = e
	}
}

func init() {
	setENV(os.Getenv("MACARON_ENV"))

	var err error
	Root, err = os.Getwd()
	if err != nil {
		panic("error getting work directory: " + err.Error())
	}
}

// SetConfig sets data sources for configuration.
func SetConfig(source interface{}, others ...interface{}) (err error) {
	cfg, err = ini.Load(source, others...)
	return err
}

// Config returns configuration convention object.
// It returns an empty object if there is no one available.
func Config() *ini.File {
	if cfg == nil {
		return &ini.File{}
	}
	return cfg
}
<commit_msg>update dep version<commit_after>// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Package macaron is a high productive and modular design web framework in Go.
package macaron

import (
	"io"
	"log"
	"net/http"
	"os"
	"reflect"
	"strings"

	"github.com/Unknwon/com"
	"gopkg.in/ini.v1"

	"github.com/Unknwon/macaron/inject"
)

const _VERSION = "0.4.9.1229"

func Version() string {
	return _VERSION
}

// Handler can be any callable function.
// Macaron attempts to inject services into the handler's argument list,
// and panics if an argument could not be fullfilled via dependency injection.
type Handler interface{}

// validateHandler makes sure a handler is a callable function,
// and panics if it is not.
func validateHandler(h Handler) {
	if reflect.TypeOf(h).Kind() != reflect.Func {
		panic("Macaron handler must be a callable function")
	}
}

// validateHandlers makes sure handlers are callable functions,
// and panics if any of them is not.
func validateHandlers(handlers []Handler) {
	for _, h := range handlers {
		validateHandler(h)
	}
}

// Macaron represents the top level web application.
// inject.Injector methods can be invoked to map services on a global level.
type Macaron struct {
	inject.Injector
	befores  []BeforeHandler
	handlers []Handler
	action   Handler

	urlPrefix string // For suburl support.
	*Router

	logger *log.Logger
}

// NewWithLogger creates a bare bones Macaron instance.
// Use this method if you want to have full control over the middleware that is used.
// You can specify logger output writer with this function.
func NewWithLogger(out io.Writer) *Macaron {
	m := &Macaron{
		Injector: inject.New(),
		action:   func() {},
		Router:   NewRouter(),
		logger:   log.New(out, "[Macaron] ", 0),
	}
	m.Router.m = m
	m.Map(m.logger)
	m.Map(defaultReturnHandler())
	m.notFound = func(resp http.ResponseWriter, req *http.Request) {
		c := m.createContext(resp, req)
		c.handlers = append(c.handlers, http.NotFound)
		c.run()
	}
	return m
}

// New creates a bare bones Macaron instance.
// Use this method if you want to have full control over the middleware that is used.
func New() *Macaron {
	return NewWithLogger(os.Stdout)
}

// Classic creates a classic Macaron with some basic default middleware:
// mocaron.Logger, mocaron.Recovery and mocaron.Static.
func Classic() *Macaron {
	m := New()
	m.Use(Logger())
	m.Use(Recovery())
	m.Use(Static("public"))
	return m
}

// Handlers sets the entire middleware stack with the given Handlers.
// This will clear any current middleware handlers,
// and panics if any of the handlers is not a callable function
func (m *Macaron) Handlers(handlers ...Handler) {
	m.handlers = make([]Handler, 0)
	for _, handler := range handlers {
		m.Use(handler)
	}
}

// Action sets the handler that will be called after all the middleware has been invoked.
// This is set to macaron.Router in a macaron.Classic().
func (m *Macaron) Action(handler Handler) {
	validateHandler(handler)
	m.action = handler
}

// BeforeHandler represents a handler executes at beginning of every request.
// Macaron stops future process when it returns true.
type BeforeHandler func(rw http.ResponseWriter, req *http.Request) bool

func (m *Macaron) Before(handler BeforeHandler) {
	m.befores = append(m.befores, handler)
}

// Use adds a middleware Handler to the stack,
// and panics if the handler is not a callable func.
// Middleware Handlers are invoked in the order that they are added.
func (m *Macaron) Use(handler Handler) {
	validateHandler(handler)
	m.handlers = append(m.handlers, handler)
}

func (m *Macaron) createContext(rw http.ResponseWriter, req *http.Request) *Context {
	c := &Context{
		Injector: inject.New(),
		handlers: m.handlers,
		action:   m.action,
		index:    0,
		Router:   m.Router,
		Req:      Request{req},
		Resp:     NewResponseWriter(rw),
		Data:     make(map[string]interface{}),
	}
	c.SetParent(m)
	c.Map(c)
	c.MapTo(c.Resp, (*http.ResponseWriter)(nil))
	c.Map(req)
	return c
}

// ServeHTTP is the HTTP Entry point for a Macaron instance.
// Useful if you want to control your own HTTP server.
// Be aware that none of middleware will run without registering any router.
func (m *Macaron) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	req.URL.Path = strings.TrimPrefix(req.URL.Path, m.urlPrefix)
	for _, h := range m.befores {
		if h(rw, req) {
			return
		}
	}
	m.Router.ServeHTTP(rw, req)
}

func GetDefaultListenInfo() (string, int) {
	host := os.Getenv("HOST")
	if len(host) == 0 {
		host = "0.0.0.0"
	}
	port := com.StrTo(os.Getenv("PORT")).MustInt()
	if port == 0 {
		port = 4000
	}
	return host, port
}

// Run the http server. Listening on os.GetEnv("PORT") or 4000 by default.
func (m *Macaron) Run(args ...interface{}) {
	host, port := GetDefaultListenInfo()
	if len(args) == 1 {
		switch arg := args[0].(type) {
		case string:
			host = arg
		case int:
			port = arg
		}
	} else if len(args) >= 2 {
		if arg, ok := args[0].(string); ok {
			host = arg
		}
		if arg, ok := args[1].(int); ok {
			port = arg
		}
	}

	addr := host + ":" + com.ToStr(port)
	logger := m.Injector.GetVal(reflect.TypeOf(m.logger)).Interface().(*log.Logger)
	logger.Printf("listening on %s (%s)\n", addr, Env)
	logger.Fatalln(http.ListenAndServe(addr, m))
}

// SetURLPrefix sets URL prefix of router layer, so that it support suburl.
func (m *Macaron) SetURLPrefix(prefix string) {
	m.urlPrefix = prefix
}

// ____   ____            .__      ___.   .__
// \   \ /   /____ _______|__|____ \_ |__ |  |   ____   ______
//  \   Y   /\__  \\_  __ \  \__  \ | __ \|  | _/ __ \ /  ___/
//   \     /  / __ \|  | \/  |/ __ \| \_\ \  |_\  ___/ \___ \
//    \___/  (____  /__|  |__(____  /___  /____/\___  >____  >
//                \/              \/    \/          \/     \/

const (
	DEV  = "development"
	PROD = "production"
	TEST = "test"
)

var (
	// Env is the environment that Macaron is executing in.
	// The MACARON_ENV is read on initialization to set this variable.
	Env = DEV

	// Path of work directory.
	Root string

	// Flash applies to current request.
	FlashNow bool

	// Configuration convention object.
	cfg *ini.File
)

func setENV(e string) {
	if len(e) > 0 {
		Env = e
	}
}

func init() {
	setENV(os.Getenv("MACARON_ENV"))

	var err error
	Root, err = os.Getwd()
	if err != nil {
		panic("error getting work directory: " + err.Error())
	}
}

// SetConfig sets data sources for configuration.
func SetConfig(source interface{}, others ...interface{}) (err error) {
	cfg, err = ini.Load(source, others...)
	return err
}

// Config returns configuration convention object.
// It returns an empty object if there is no one available.
func Config() *ini.File {
	if cfg == nil {
		return &ini.File{}
	}
	return cfg
}
<|endoftext|>


<commit_before>package messages

import (
	"fmt"
	"github.com/crosbymichael/messages/stats"
	"github.com/garyburd/redigo/redis"
)

type Mailbox interface {
	Send(*Message) error
	Wait() (*Message, error)
	Destroy(*Message) error
	DestroyAfter(*Message, int) error
	Close() error
}

// Place to send and receive messages
type mailbox struct {
	name               string // name of the mailbox
	defaultWaitTimeout int    // How long to wait
	pool               *redis.Pool
}

// Create a new named mailbox to send and receive
// messages on.
func NewMailbox(name, proto, addr, password string) Mailbox {
	return &mailbox{
		name:               name,
		defaultWaitTimeout: 0, // Default to 0 so that the mailbox blocks forever
		pool:               newPool(proto, addr, password),
	}
}

func (mbox *mailbox) Close() error {
	return mbox.pool.Close()
}

// Send the message
func (mbox *mailbox) Send(m *Message) error {
	stats.MessageCount.Inc(1)

	conn := mbox.pool.Get()
	defer conn.Close()

	args := []interface{}{
		fmt.Sprintf("messages:%s", m.ID),
		"created", m.Created,
		"body", m.Body,
	}

	if err := conn.Send("MULTI"); err != nil {
		return err
	}
	if err := conn.Send("HMSET", args...); err != nil {
		return err
	}
	if err := conn.Send("RPUSH", fmt.Sprintf("mailbox:%s", mbox.name), m.ID); err != nil {
		return err
	}
	if _, err := conn.Do("EXEC"); err != nil {
		return err
	}
	return nil
}

// Wait for a new message to be delivered using the
// timeout specified for the mailbox
func (mbox *mailbox) Wait() (*Message, error) {
	reply, err := redis.MultiBulk(mbox.send("BLPOP", fmt.Sprintf("mailbox:%s", mbox.name), mbox.defaultWaitTimeout))
	if err != nil {
		return nil, err
	}
	return mbox.messageFromId(string(reply[1].([]byte)))
}

// Delete the message from the transport NOW
func (mbox *mailbox) Destroy(m *Message) error {
	return mbox.DestroyAfter(m, 0)
}

// Delete the message after n seconds
func (mbox *mailbox) DestroyAfter(m *Message, seconds int) error {
	if seconds < 1 {
		if _, err := mbox.send("DEL", fmt.Sprintf("messages:%s", m.ID)); err != nil {
			return err
		}
		return nil
	}
	if _, err := mbox.send("EXPIRE", fmt.Sprintf("messages:%s", m.ID), seconds); err != nil {
		return err
	}
	return nil
}

func (mbox *mailbox) send(cmd string, args ...interface{}) (interface{}, error) {
	conn := mbox.pool.Get()
	defer conn.Close()
	return conn.Do(cmd, args...)
}

func (mbox *mailbox) messageFromId(id string) (*Message, error) {
	result, err := redis.MultiBulk(mbox.send("HGETALL", fmt.Sprintf("messages:%s", id)))
	if err != nil {
		return nil, err
	}

	args := make([][]byte, len(result))
	for i := 0; i < len(result); i++ {
		data := result[i].([]byte)
		args[i] = data
	}

	hash := argsToMap(args)
	return &Message{
		ID:      id,
		Created: string(hash["created"]),
		Body:    hash["body"],
	}, nil
}
<commit_msg>Reuse conn where possible<commit_after>package messages

import (
	"fmt"
	"github.com/crosbymichael/messages/stats"
	"github.com/garyburd/redigo/redis"
)

type Mailbox interface {
	Send(*Message) error
	Wait() (*Message, error)
	Destroy(*Message) error
	DestroyAfter(*Message, int) error
	Close() error
}

// Place to send and receive messages
type mailbox struct {
	name               string // name of the mailbox
	defaultWaitTimeout int    // How long to wait
	pool               *redis.Pool
}

// Create a new named mailbox to send and receive
// messages on.
func NewMailbox(name, proto, addr, password string) Mailbox {
	return &mailbox{
		name:               name,
		defaultWaitTimeout: 0, // Default to 0 so that the mailbox blocks forever
		pool:               newPool(proto, addr, password),
	}
}

func (mbox *mailbox) Close() error {
	return mbox.pool.Close()
}

// Send the message
func (mbox *mailbox) Send(m *Message) error {
	stats.MessageCount.Inc(1)

	conn := mbox.pool.Get()
	defer conn.Close()

	args := []interface{}{
		fmt.Sprintf("messages:%s", m.ID),
		"created", m.Created,
		"body", m.Body,
	}

	if err := conn.Send("MULTI"); err != nil {
		return err
	}
	if err := conn.Send("HMSET", args...); err != nil {
		return err
	}
	if err := conn.Send("RPUSH", fmt.Sprintf("mailbox:%s", mbox.name), m.ID); err != nil {
		return err
	}
	if _, err := conn.Do("EXEC"); err != nil {
		return err
	}
	return nil
}

// Wait for a new message to be delivered using the
// timeout specified for the mailbox
func (mbox *mailbox) Wait() (*Message, error) {
	conn := mbox.pool.Get()
	defer conn.Close()

	reply, err := redis.MultiBulk(conn.Do("BLPOP", fmt.Sprintf("mailbox:%s", mbox.name), mbox.defaultWaitTimeout))
	if err != nil {
		return nil, err
	}

	id := string(reply[1].([]byte))
	result, err := redis.MultiBulk(conn.Do("HGETALL", fmt.Sprintf("messages:%s", id)))
	if err != nil {
		return nil, err
	}

	args := make([][]byte, len(result))
	for i := 0; i < len(result); i++ {
		args[i] = result[i].([]byte)
	}

	hash := argsToMap(args)
	return &Message{
		ID:      id,
		Created: string(hash["created"]),
		Body:    hash["body"],
	}, nil
}

// Delete the message from the transport NOW
func (mbox *mailbox) Destroy(m *Message) error {
	return mbox.DestroyAfter(m, 0)
}

// Delete the message after n seconds
func (mbox *mailbox) DestroyAfter(m *Message, seconds int) error {
	var (
		key  = fmt.Sprintf("messages:%s", m.ID)
		conn = mbox.pool.Get()
	)
	defer conn.Close()

	if seconds < 1 {
		if _, err := conn.Do("DEL", key); err != nil {
			return err
		}
		return nil
	}
	if _, err := conn.Do("EXPIRE", key, seconds); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package logyard

import (
	"fmt"
	"github.com/ActiveState/doozerconfig"
	"github.com/ActiveState/log"
	"logyard/retry"
	"strings"
	"sync"
	"time"
)

// DrainConstructor is a function that returns a new drain instance
type DrainConstructor func(*log.Logger) Drain

// DRAINS is a map of drain type (string) to its constructur function
var DRAINS = map[string]DrainConstructor{
	"redis": NewRedisDrain,
	"tcp":   NewIPConnDrain,
	"udp":   NewIPConnDrain,
	"file":  NewFileDrain,
}

type Drain interface {
	Start(*DrainConfig)
	Stop() error
	Wait() error
}

const configKey = "/proc/logyard/config/"

type DrainManager struct {
	mux       sync.Mutex       // mutex to protect Start/Stop
	running   map[string]Drain // map of drain instance name to drain
	doozerCfg *doozerconfig.DoozerConfig
	doozerRev int64
}

func NewDrainManager() *DrainManager {
	manager := new(DrainManager)
	manager.running = make(map[string]Drain)
	return manager
}

// XXX: use tomb and channels to properly process start/stop events.

// StopDrain starts the drain if it is running
func (manager *DrainManager) StopDrain(drainName string) {
	manager.mux.Lock()
	defer manager.mux.Unlock()
	if drain, ok := manager.running[drainName]; ok {
		log.Infof("Stopping drain %s ...\n", drainName)

		// drain.Stop is expected to stop in 1s, but a known bug
		// (#96008) causes certain drains to hang. workaround it using
		// timeouts.
		done := make(chan error)
		go func() {
			done <- drain.Stop()
		}()
		var err error
		select {
		case err = <-done:
			break
		case <-time.After(5 * time.Second):
			log.Fatalf("Error: expecting drain %s to stop in 1s, "+
				"but it takes more than 5s; exiting..", drainName)
		}

		if err != nil {
			log.Errorf("failed to stop drain %s: %s\n", drainName, err)
		} else {
			delete(manager.running, drainName)
			log.Infof("Removed drain %s\n", drainName)
		}
	} else {
		log.Infof("Drain %s cannot be stopped; it is not running.\n", drainName)
	}
}

// StartDrain starts the drain and waits for it exit.
func (manager *DrainManager) StartDrain(name, uri string, retry retry.Retryer) {
	manager.mux.Lock()
	defer manager.mux.Unlock()

	if _, ok := manager.running[name]; ok {
		log.Errorf("drain %s is already running", name)
		return
	}

	config, err := DrainConfigFromUri(name, uri)
	if err != nil {
		log.Errorf("invalid drain URI (%s): %s\n", uri, err)
		return
	}

	drainLog := NewDrainLogger(config)
	var drain Drain

	if constructor, ok := DRAINS[config.Type]; ok && constructor != nil {
		drain = constructor(drainLog)
	} else {
		log.Info("unsupported drain")
		return
	}

	manager.running[config.Name] = drain
	drainLog.Infof("Starting drain: %+v", config)
	go drain.Start(config)

	go func() {
		err = drain.Wait()
		delete(manager.running, name)
		if err != nil {
			// HACK: apptail.* drains should not log WARN or ERROR
			// records. ideally, make this configurable in drain URI
			// arguments (eg: tcp://google.com:12345?warn=false);
			// doing so will require changes to cloud_controller/kato
			// (the ruby code).
			shouldWarn := !strings.HasPrefix(name, "appdrain.")

			proceed := retry.Wait(
				fmt.Sprintf("Drain '%s' exited abruptly -- %s", name, err),
				shouldWarn)
			if !proceed {
				return
			}
			if _, ok := Config.Drains[name]; ok {
				manager.StartDrain(name, uri, retry)
			} else {
				log.Infof("Not restarting crashed drain %s, becase it was deleted recently", name)
			}
		}
	}()
}

func NewDrainLogger(c *DrainConfig) *log.Logger {
	l := log.New()
	prefix := c.Name + "--" + c.Type
	l.SetPrefix(fmt.Sprintf("[%30s] ", prefix))
	return l
}

// chooseRetryer chooses an appropriate retryer for the given drain
// name.
func chooseRetryer(name string) retry.Retryer {
	if strings.HasPrefix(name, "tmp.") {
		// "tmp" drains -- such as 'kato tail' -- need not be retried
		// infinitely.
		return retry.NewFiniteRetryer()
	}
	return retry.NewInfiniteRetryer()
}

func (manager *DrainManager) Run() {
	log.Infof("Found %d drains to start\n", len(Config.Drains))
	for name, uri := range Config.Drains {
		manager.StartDrain(name, uri, chooseRetryer(name))
	}

	// Watch for config changes in doozer
	for change := range Config.Ch {
		switch change.Type {
		case doozerconfig.DELETE:
			manager.StopDrain(change.Key)
		case doozerconfig.SET:
			manager.StopDrain(change.Key)
			manager.StartDrain(
				change.Key, Config.Drains[change.Key], retry.NewInfiniteRetryer())
		}
	}
}
<commit_msg>complete 71a1ad52 (Bug #97522) - fix retryer to work<commit_after>package logyard

import (
	"fmt"
	"github.com/ActiveState/doozerconfig"
	"github.com/ActiveState/log"
	"logyard/retry"
	"strings"
	"sync"
	"time"
)

// DrainConstructor is a function that returns a new drain instance
type DrainConstructor func(*log.Logger) Drain

// DRAINS is a map of drain type (string) to its constructur function
var DRAINS = map[string]DrainConstructor{
	"redis": NewRedisDrain,
	"tcp":   NewIPConnDrain,
	"udp":   NewIPConnDrain,
	"file":  NewFileDrain,
}

type Drain interface {
	Start(*DrainConfig)
	Stop() error
	Wait() error
}

const configKey = "/proc/logyard/config/"

type DrainManager struct {
	mux       sync.Mutex       // mutex to protect Start/Stop
	running   map[string]Drain // map of drain instance name to drain
	doozerCfg *doozerconfig.DoozerConfig
	doozerRev int64
}

func NewDrainManager() *DrainManager {
	manager := new(DrainManager)
	manager.running = make(map[string]Drain)
	return manager
}

// XXX: use tomb and channels to properly process start/stop events.

// StopDrain starts the drain if it is running
func (manager *DrainManager) StopDrain(drainName string) {
	manager.mux.Lock()
	defer manager.mux.Unlock()
	if drain, ok := manager.running[drainName]; ok {
		log.Infof("Stopping drain %s ...\n", drainName)

		// drain.Stop is expected to stop in 1s, but a known bug
		// (#96008) causes certain drains to hang. workaround it using
		// timeouts.
		done := make(chan error)
		go func() {
			done <- drain.Stop()
		}()
		var err error
		select {
		case err = <-done:
			break
		case <-time.After(5 * time.Second):
			log.Fatalf("Error: expecting drain %s to stop in 1s, "+
				"but it takes more than 5s; exiting..", drainName)
		}

		if err != nil {
			log.Errorf("failed to stop drain %s: %s\n", drainName, err)
		} else {
			delete(manager.running, drainName)
			log.Infof("Removed drain %s\n", drainName)
		}
	} else {
		log.Infof("Drain %s cannot be stopped; it is not running.\n", drainName)
	}
}

// StartDrain starts the drain and waits for it exit.
func (manager *DrainManager) StartDrain(name, uri string, retry retry.Retryer) {
	manager.mux.Lock()
	defer manager.mux.Unlock()

	if _, ok := manager.running[name]; ok {
		log.Errorf("drain %s is already running", name)
		return
	}

	config, err := DrainConfigFromUri(name, uri)
	if err != nil {
		log.Errorf("invalid drain URI (%s): %s\n", uri, err)
		return
	}

	drainLog := NewDrainLogger(config)
	var drain Drain

	if constructor, ok := DRAINS[config.Type]; ok && constructor != nil {
		drain = constructor(drainLog)
	} else {
		log.Info("unsupported drain")
		return
	}

	manager.running[config.Name] = drain
	drainLog.Infof("Starting drain: %+v", config)
	go drain.Start(config)

	go func() {
		err = drain.Wait()
		delete(manager.running, name)
		if err != nil {
			// HACK: apptail.* drains should not log WARN or ERROR
			// records. ideally, make this configurable in drain URI
			// arguments (eg: tcp://google.com:12345?warn=false);
			// doing so will require changes to cloud_controller/kato
			// (the ruby code).
			shouldWarn := !strings.HasPrefix(name, "appdrain.")

			proceed := retry.Wait(
				fmt.Sprintf("Drain '%s' exited abruptly -- %s", name, err),
				shouldWarn)
			if !proceed {
				return
			}
			if _, ok := Config.Drains[name]; ok {
				manager.StartDrain(name, uri, retry)
			} else {
				log.Infof("Not restarting crashed drain %s, becase it was deleted recently", name)
			}
		}
	}()
}

func NewDrainLogger(c *DrainConfig) *log.Logger {
	l := log.New()
	prefix := c.Name + "--" + c.Type
	l.SetPrefix(fmt.Sprintf("[%30s] ", prefix))
	return l
}

// chooseRetryer chooses an appropriate retryer for the given drain
// name.
func chooseRetryer(name string) retry.Retryer {
	if strings.HasPrefix(name, "tmp.") {
		// "tmp" drains -- such as 'kato tail' -- need not be retried
		// infinitely.
		return retry.NewFiniteRetryer()
	}
	return retry.NewInfiniteRetryer()
}

func (manager *DrainManager) Run() {
	log.Infof("Found %d drains to start\n", len(Config.Drains))
	for name, uri := range Config.Drains {
		manager.StartDrain(name, uri, chooseRetryer(name))
	}

	// Watch for config changes in doozer
	for change := range Config.Ch {
		switch change.Type {
		case doozerconfig.DELETE:
			manager.StopDrain(change.Key)
		case doozerconfig.SET:
			manager.StopDrain(change.Key)
			manager.StartDrain(
				change.Key, Config.Drains[change.Key], chooseRetryer(change.Key))
		}
	}
}
<|endoftext|>