<commit_before>package jwt

import (
	"bytes"
	"crypto/rsa"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"time"
	"regexp"

	"github.com/dgrijalva/jwt-go"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// global declaration of the backend to enable caching secret key material across requests
// when keys are specified in the config file
var b = backend{
	cache: make(map[string]keycache),
}

// AuthBackend represents a backend interface that retrieves secret key material
// to validate tokens
type AuthBackend interface {
	GetHMACSecret() (b []byte)
	GetRSAPublicKey() (r *rsa.PublicKey)
	IsConfigValid() (v bool)
}

func (h Auth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// if the request path is any of the configured paths, validate JWT
	for _, p := range h.Rules {

		// TODO: this is a hack to work our CVE in Caddy dealing with parsing
		// malformed URLs.  Can be removed once upstream fix for path match
		if r.URL.EscapedPath() == "" {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}
		re := regexp.MustCompile("/+")
		cleanedPath := re.ReplaceAllString(r.URL.Path, "/")
		if !httpserver.Path(cleanedPath).Matches(p.Path) {
			continue
		}

        if r.Method == "OPTIONS" {
            continue
        }

		// strip potentially spoofed claims
		for header, _ := range r.Header {
			if strings.HasPrefix(header, "Token-Claim-") {
				r.Header.Del(header)
			}
		}

		// Check excepted paths for this rule and allow access without validating any token
		var isExceptedPath bool
		for _, e := range p.ExceptedPaths {
			if httpserver.Path(r.URL.Path).Matches(e) {
				isExceptedPath = true
			}
		}
		if isExceptedPath {
			continue
		}
		if r.URL.Path == "/" && p.AllowRoot {
			// special case for protecting children of the root path, only allow access to base directory with directive `allowbase`
			continue
		}

		// Path matches, look for unvalidated token
		uToken, err := ExtractToken(r)
		if err != nil {
			if p.Passthrough {
				continue
			}
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		var vToken *jwt.Token

		switch {
		case len(p.KeyFile) > 0:

			// Loop through all possible key files on disk, using cache
			for _, keyfile := range p.KeyFile {
				// Initialize a new caching layer if this is the first request to a protected path.
				// Cache only operates when key material is stored on disk.  When using environment variables
				// this has no effect.
				_, ok := b.cache[keyfile]
				if !ok {
					b.cache[keyfile] = keycache{
						KeyFile:     keyfile,
						KeyFileType: p.KeyFileType,
					}
				}
				b.current = b.cache[keyfile]

				// Validate token
				vToken, err = ValidateToken(uToken, b)

				if err == nil {
					// break on first correctly validated token
					break
				}
			}

			// Check last error of validating token.  If error still exists, no keyfiles matched
			if err != nil || vToken == nil {
				if p.Passthrough {
					continue
				}
				return handleUnauthorized(w, r, p, h.Realm), nil
			}
		default:
			// when no keyfiles, use environment variables, clear cache first
			b.current = keycache{}
			vToken, err = ValidateToken(uToken, b)

			if err != nil {
				if p.Passthrough {
					continue
				}
				return handleUnauthorized(w, r, p, h.Realm), nil
			}
		}

		vClaims, err := Flatten(vToken.Claims.(jwt.MapClaims), "", DotStyle)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// If token contains rules with allow or deny, evaluate
		if len(p.AccessRules) > 0 {
			var isAuthorized []bool
			for _, rule := range p.AccessRules {
				v := vClaims[rule.Claim]
				ruleMatches := contains(v, rule.Value) || v == rule.Value
				switch rule.Authorize {
				case ALLOW:
					isAuthorized = append(isAuthorized, ruleMatches)
				case DENY:
					isAuthorized = append(isAuthorized, !ruleMatches)
				default:
					return handleUnauthorized(w, r, p, h.Realm), fmt.Errorf("unknown rule type")
				}
			}
			// test all flags, if any are true then ok to pass
			ok := false
			for _, result := range isAuthorized {
				if result {
					ok = true
				}
			}
			if !ok {
				return handleForbidden(w, r, p, h.Realm), nil
			}
		}

		// set claims as separate headers for downstream to consume
		for claim, value := range vClaims {
			var headerName string
			switch p.StripHeader {
			case true:
				stripped := strings.SplitAfter(claim, "/")
				finalStrip := stripped[len(stripped)-1]
				headerName = "Token-Claim-" + modTitleCase(finalStrip)
			default:
				escaped := url.PathEscape(claim)
				headerName = "Token-Claim-" + modTitleCase(escaped)
			}

			switch v := value.(type) {
			case string:
				r.Header.Set(headerName, v)
			case int64:
				r.Header.Set(headerName, strconv.FormatInt(v, 10))
			case bool:
				r.Header.Set(headerName, strconv.FormatBool(v))
			case int32:
				r.Header.Set(headerName, strconv.FormatInt(int64(v), 10))
			case float32:
				r.Header.Set(headerName, strconv.FormatFloat(float64(v), 'f', -1, 32))
			case float64:
				r.Header.Set(headerName, strconv.FormatFloat(v, 'f', -1, 64))
			case []interface{}:
				b := bytes.NewBufferString("")
				for i, item := range v {
					if i > 0 {
						b.WriteString(",")
					}
					b.WriteString(fmt.Sprintf("%v", item))
				}
				r.Header.Set(headerName, b.String())
			default:
				// ignore, because, JWT spec says in https://tools.ietf.org/html/rfc7519#section-4
				//     all claims that are not understood
				//     by implementations MUST be ignored.
			}
		}

		return h.Next.ServeHTTP(w, r)
	}
	// pass request if no paths protected with JWT
	return h.Next.ServeHTTP(w, r)
}

// ExtractToken will find a JWT token passed one of three ways: (1) as the Authorization
// header in the form `Bearer <JWT Token>`; (2) as a cookie named `jwt_token`; (3) as
// a URL query paramter of the form https://example.com?token=<JWT token>
func ExtractToken(r *http.Request) (string, error) {
	jwtHeader := strings.Split(r.Header.Get("Authorization"), " ")
	if jwtHeader[0] == "Bearer" && len(jwtHeader) == 2 {
		return jwtHeader[1], nil
	}

	jwtCookie, err := r.Cookie("jwt_token")
	if err == nil {
		return jwtCookie.Value, nil
	}

	jwtQuery := r.URL.Query().Get("token")
	if jwtQuery != "" {
		return jwtQuery, nil
	}

	return "", fmt.Errorf("no token found")
}

// ValidateToken will return a parsed token if it passes validation, or an
// error if any part of the token fails validation.  Possible errors include
// malformed tokens, unknown/unspecified signing algorithms, missing secret key,
// tokens that are not valid yet (i.e., 'nbf' field), tokens that are expired,
// and tokens that fail signature verification (forged)
func ValidateToken(uToken string, b AuthBackend) (*jwt.Token, error) {
	if len(uToken) == 0 {
		return nil, fmt.Errorf("Token length is zero")
	}

	if !b.IsConfigValid() {
		return nil, errors.New("No valid configuration for JWT validation found")
	}

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("HMAC: Unexpected signing method: %v", t.Header["alg"])
			}
			return hmac, nil
		})

		if err != nil {
			return nil, err
		}

		return token, nil

	case rsa != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
				return nil, fmt.Errorf("RSA: Unexpected signing method: %v", t.Header["alg"])
			}
			return rsa, nil
		})
		if err != nil {
			return nil, err
		}

		return token, nil
	default:
		return nil, errors.New("No valid configuration for JWT validation found")
	}

}

type backend struct {
	current keycache
	cache   map[string]keycache
}

type keycache struct {
	KeyFile     string
	KeyFileType EncryptionType
	Key         []byte
	ModifyTime  time.Time
}

func (b backend) GetHMACSecret() []byte {
	switch b.current.KeyFileType {
	case RSA:
		return nil
	case HMAC:
		if secret, err := readKeyFromFile(&b); err == nil {
			return secret
		}
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	default:
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	}
}

func (b backend) GetRSAPublicKey() *rsa.PublicKey {

	switch b.current.KeyFileType {
	case HMAC:
		return nil
	case RSA:
		if pem, err := readKeyFromFile(&b); err == nil {
			rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
			if err != nil {
				return nil
			}
			return rsaPub
		}
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	default:
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	}
}

func (b backend) IsConfigValid() bool {

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil && rsa == nil:
		return true
	case hmac == nil && rsa != nil:
		return true
	default:
		return false
	}
}

// readKeyFromFile attempts to read key material from the path specified in the config.
// If the path is not absolute it will attempt to find it as a relative path from the
// working directory.  To prevent issues with concurrent read/write to the filesystem
// on every request, it will cache the result of the file read and only re-read the file
// when the modification time is earlier than the cached value
func readKeyFromFile(b *backend) ([]byte, error) {

	var keyfilePath string
	if path.IsAbs(b.current.KeyFile) {
		keyfilePath = b.current.KeyFile
	} else {
		wd, err := os.Getwd()
		if err != nil {
			return nil, err
		}
		keyfilePath = path.Join(wd, b.current.KeyFile)
	}

	finfo, err := os.Stat(keyfilePath)
	if os.IsNotExist(err) {
		return nil, err
	}

	cachehit, ok := b.cache[b.current.KeyFile]
	if !ok {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}

		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	if finfo.ModTime().After(cachehit.ModifyTime) {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}
		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	return cachehit.Key, nil
}

// handleUnauthorized checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleUnauthorized(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}

	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"invalid_token\"", realm))
	return http.StatusUnauthorized
}

// handleForbidden checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleForbidden(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}
	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"insufficient_scope\"", realm))
	return http.StatusForbidden
}

// contains checks weather list is a slice ans containts the
// supplied string value.
func contains(list interface{}, value string) bool {
	switch l := list.(type) {
	case []interface{}:
		for _, v := range l {
			if v == value {
				return true
			}
		}
	}
	return false
}

func modTitleCase(s string) string {
	switch {
	case len(s) == 0:
		return s
	case len(s) == 1:
		return strings.ToUpper(s)
	default:
		return strings.ToUpper(string(s[0])) + s[1:]
	}
}
<commit_msg>use path.Clean<commit_after>package jwt

import (
	"bytes"
	"crypto/rsa"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// global declaration of the backend to enable caching secret key material across requests
// when keys are specified in the config file
var b = backend{
	cache: make(map[string]keycache),
}

// AuthBackend represents a backend interface that retrieves secret key material
// to validate tokens
type AuthBackend interface {
	GetHMACSecret() (b []byte)
	GetRSAPublicKey() (r *rsa.PublicKey)
	IsConfigValid() (v bool)
}

func (h Auth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// if the request path is any of the configured paths, validate JWT
	for _, p := range h.Rules {

		// TODO: this is a hack to work our CVE in Caddy dealing with parsing
		// malformed URLs.  Can be removed once upstream fix for path match
		if r.URL.EscapedPath() == "" {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}
		cleanedPath := path.Clean(r.URL.Path)
		if !httpserver.Path(cleanedPath).Matches(p.Path) {
			continue
		}

        if r.Method == "OPTIONS" {
            continue
        }

		// strip potentially spoofed claims
		for header, _ := range r.Header {
			if strings.HasPrefix(header, "Token-Claim-") {
				r.Header.Del(header)
			}
		}

		// Check excepted paths for this rule and allow access without validating any token
		var isExceptedPath bool
		for _, e := range p.ExceptedPaths {
			if httpserver.Path(r.URL.Path).Matches(e) {
				isExceptedPath = true
			}
		}
		if isExceptedPath {
			continue
		}
		if r.URL.Path == "/" && p.AllowRoot {
			// special case for protecting children of the root path, only allow access to base directory with directive `allowbase`
			continue
		}

		// Path matches, look for unvalidated token
		uToken, err := ExtractToken(r)
		if err != nil {
			if p.Passthrough {
				continue
			}
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		var vToken *jwt.Token

		switch {
		case len(p.KeyFile) > 0:

			// Loop through all possible key files on disk, using cache
			for _, keyfile := range p.KeyFile {
				// Initialize a new caching layer if this is the first request to a protected path.
				// Cache only operates when key material is stored on disk.  When using environment variables
				// this has no effect.
				_, ok := b.cache[keyfile]
				if !ok {
					b.cache[keyfile] = keycache{
						KeyFile:     keyfile,
						KeyFileType: p.KeyFileType,
					}
				}
				b.current = b.cache[keyfile]

				// Validate token
				vToken, err = ValidateToken(uToken, b)

				if err == nil {
					// break on first correctly validated token
					break
				}
			}

			// Check last error of validating token.  If error still exists, no keyfiles matched
			if err != nil || vToken == nil {
				if p.Passthrough {
					continue
				}
				return handleUnauthorized(w, r, p, h.Realm), nil
			}
		default:
			// when no keyfiles, use environment variables, clear cache first
			b.current = keycache{}
			vToken, err = ValidateToken(uToken, b)

			if err != nil {
				if p.Passthrough {
					continue
				}
				return handleUnauthorized(w, r, p, h.Realm), nil
			}
		}

		vClaims, err := Flatten(vToken.Claims.(jwt.MapClaims), "", DotStyle)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// If token contains rules with allow or deny, evaluate
		if len(p.AccessRules) > 0 {
			var isAuthorized []bool
			for _, rule := range p.AccessRules {
				v := vClaims[rule.Claim]
				ruleMatches := contains(v, rule.Value) || v == rule.Value
				switch rule.Authorize {
				case ALLOW:
					isAuthorized = append(isAuthorized, ruleMatches)
				case DENY:
					isAuthorized = append(isAuthorized, !ruleMatches)
				default:
					return handleUnauthorized(w, r, p, h.Realm), fmt.Errorf("unknown rule type")
				}
			}
			// test all flags, if any are true then ok to pass
			ok := false
			for _, result := range isAuthorized {
				if result {
					ok = true
				}
			}
			if !ok {
				return handleForbidden(w, r, p, h.Realm), nil
			}
		}

		// set claims as separate headers for downstream to consume
		for claim, value := range vClaims {
			var headerName string
			switch p.StripHeader {
			case true:
				stripped := strings.SplitAfter(claim, "/")
				finalStrip := stripped[len(stripped)-1]
				headerName = "Token-Claim-" + modTitleCase(finalStrip)
			default:
				escaped := url.PathEscape(claim)
				headerName = "Token-Claim-" + modTitleCase(escaped)
			}

			switch v := value.(type) {
			case string:
				r.Header.Set(headerName, v)
			case int64:
				r.Header.Set(headerName, strconv.FormatInt(v, 10))
			case bool:
				r.Header.Set(headerName, strconv.FormatBool(v))
			case int32:
				r.Header.Set(headerName, strconv.FormatInt(int64(v), 10))
			case float32:
				r.Header.Set(headerName, strconv.FormatFloat(float64(v), 'f', -1, 32))
			case float64:
				r.Header.Set(headerName, strconv.FormatFloat(v, 'f', -1, 64))
			case []interface{}:
				b := bytes.NewBufferString("")
				for i, item := range v {
					if i > 0 {
						b.WriteString(",")
					}
					b.WriteString(fmt.Sprintf("%v", item))
				}
				r.Header.Set(headerName, b.String())
			default:
				// ignore, because, JWT spec says in https://tools.ietf.org/html/rfc7519#section-4
				//     all claims that are not understood
				//     by implementations MUST be ignored.
			}
		}

		return h.Next.ServeHTTP(w, r)
	}
	// pass request if no paths protected with JWT
	return h.Next.ServeHTTP(w, r)
}

// ExtractToken will find a JWT token passed one of three ways: (1) as the Authorization
// header in the form `Bearer <JWT Token>`; (2) as a cookie named `jwt_token`; (3) as
// a URL query paramter of the form https://example.com?token=<JWT token>
func ExtractToken(r *http.Request) (string, error) {
	jwtHeader := strings.Split(r.Header.Get("Authorization"), " ")
	if jwtHeader[0] == "Bearer" && len(jwtHeader) == 2 {
		return jwtHeader[1], nil
	}

	jwtCookie, err := r.Cookie("jwt_token")
	if err == nil {
		return jwtCookie.Value, nil
	}

	jwtQuery := r.URL.Query().Get("token")
	if jwtQuery != "" {
		return jwtQuery, nil
	}

	return "", fmt.Errorf("no token found")
}

// ValidateToken will return a parsed token if it passes validation, or an
// error if any part of the token fails validation.  Possible errors include
// malformed tokens, unknown/unspecified signing algorithms, missing secret key,
// tokens that are not valid yet (i.e., 'nbf' field), tokens that are expired,
// and tokens that fail signature verification (forged)
func ValidateToken(uToken string, b AuthBackend) (*jwt.Token, error) {
	if len(uToken) == 0 {
		return nil, fmt.Errorf("Token length is zero")
	}

	if !b.IsConfigValid() {
		return nil, errors.New("No valid configuration for JWT validation found")
	}

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("HMAC: Unexpected signing method: %v", t.Header["alg"])
			}
			return hmac, nil
		})

		if err != nil {
			return nil, err
		}

		return token, nil

	case rsa != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
				return nil, fmt.Errorf("RSA: Unexpected signing method: %v", t.Header["alg"])
			}
			return rsa, nil
		})
		if err != nil {
			return nil, err
		}

		return token, nil
	default:
		return nil, errors.New("No valid configuration for JWT validation found")
	}

}

type backend struct {
	current keycache
	cache   map[string]keycache
}

type keycache struct {
	KeyFile     string
	KeyFileType EncryptionType
	Key         []byte
	ModifyTime  time.Time
}

func (b backend) GetHMACSecret() []byte {
	switch b.current.KeyFileType {
	case RSA:
		return nil
	case HMAC:
		if secret, err := readKeyFromFile(&b); err == nil {
			return secret
		}
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	default:
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	}
}

func (b backend) GetRSAPublicKey() *rsa.PublicKey {

	switch b.current.KeyFileType {
	case HMAC:
		return nil
	case RSA:
		if pem, err := readKeyFromFile(&b); err == nil {
			rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
			if err != nil {
				return nil
			}
			return rsaPub
		}
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	default:
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	}
}

func (b backend) IsConfigValid() bool {

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil && rsa == nil:
		return true
	case hmac == nil && rsa != nil:
		return true
	default:
		return false
	}
}

// readKeyFromFile attempts to read key material from the path specified in the config.
// If the path is not absolute it will attempt to find it as a relative path from the
// working directory.  To prevent issues with concurrent read/write to the filesystem
// on every request, it will cache the result of the file read and only re-read the file
// when the modification time is earlier than the cached value
func readKeyFromFile(b *backend) ([]byte, error) {

	var keyfilePath string
	if path.IsAbs(b.current.KeyFile) {
		keyfilePath = b.current.KeyFile
	} else {
		wd, err := os.Getwd()
		if err != nil {
			return nil, err
		}
		keyfilePath = path.Join(wd, b.current.KeyFile)
	}

	finfo, err := os.Stat(keyfilePath)
	if os.IsNotExist(err) {
		return nil, err
	}

	cachehit, ok := b.cache[b.current.KeyFile]
	if !ok {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}

		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	if finfo.ModTime().After(cachehit.ModifyTime) {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}
		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	return cachehit.Key, nil
}

// handleUnauthorized checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleUnauthorized(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}

	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"invalid_token\"", realm))
	return http.StatusUnauthorized
}

// handleForbidden checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleForbidden(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}
	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"insufficient_scope\"", realm))
	return http.StatusForbidden
}

// contains checks weather list is a slice ans containts the
// supplied string value.
func contains(list interface{}, value string) bool {
	switch l := list.(type) {
	case []interface{}:
		for _, v := range l {
			if v == value {
				return true
			}
		}
	}
	return false
}

func modTitleCase(s string) string {
	switch {
	case len(s) == 0:
		return s
	case len(s) == 1:
		return strings.ToUpper(s)
	default:
		return strings.ToUpper(string(s[0])) + s[1:]
	}
}
<|endoftext|>


<commit_before>package kdb

import (
  "database/sql"
  "fmt"
  "github.com/kdar/kmap"
  "strings"
)

func GetMaps(rows *sql.Rows) ([]kmap.Map, error) {
  var maps []kmap.Map

  cols, err := rows.Columns()
  if err != nil {
    return nil, err
  }

  values := make([]interface{}, len(cols))
  scanArgs := make([]interface{}, len(cols))

  for x := 0; x < len(cols); x++ {
    scanArgs[x] = &values[x]
  }

  for rows.Next() {
    m := kmap.Make()
    err := rows.Scan(scanArgs...)
    if err == nil {
      for n, c := range cols {
        m[strings.ToLower(c)] = values[n]
      }
      maps = append(maps, m)
    }
  }

  return maps, nil
}

func QueryMap(db *sql.DB, query string, args ...interface{}) (kmap.Map, error) {
  rows, err := db.Query(query, args...)
  if err != nil {
    return nil, err
  }

  ret, err := GetMaps(rows)
  if len(ret) > 0 {
    return ret[0], err
  }

  return nil, err
}

func QueryMaps(db *sql.DB, query string, args ...interface{}) ([]kmap.Map, error) {
  rows, err := db.Query(query, args...)
  if err != nil {
    return nil, err
  }

  ret, err := GetMaps(rows)
  if len(ret) > 0 {
    return ret, err
  }

  return nil, err
}

func Fields(names []string) string {
  for i, _ := range names {
    names[i] = strings.Replace(names[i], `"`, `\"`, -1)
  }
  return `("` + strings.Join(names, `", "`) + `")`
}

func InsertMap(db *sql.DB, table string, m kmap.Map) (sql.Result, error) {
  var fields []string
  var values []interface{}
  var variables []string

  for key, value := range m {
    fields = append(fields, key)
    values = append(values, value)
    variables = append(variables, "?")
  }

  fieldsSql := Fields(fields)

  res, err := db.Exec(fmt.Sprintf("INSERT INTO Messages %s VALUES (%s)", fieldsSql, strings.Join(variables, ",")), values...)
  return res, err
}
<commit_msg>Was fooling around with structs.<commit_after>package kdb

import (
  "database/sql"
  "fmt"
  "github.com/kdar/kmap"
  // "github.com/kisielk/sqlstruct"
  // "reflect"
  "strings"
)

func GetMaps(rows *sql.Rows) ([]kmap.Map, error) {
  var maps []kmap.Map

  cols, err := rows.Columns()
  if err != nil {
    return nil, err
  }

  values := make([]interface{}, len(cols))
  scanArgs := make([]interface{}, len(cols))

  for x := 0; x < len(cols); x++ {
    scanArgs[x] = &values[x]
  }

  for rows.Next() {
    m := kmap.Make()
    err := rows.Scan(scanArgs...)
    if err == nil {
      for n, c := range cols {
        m[strings.ToLower(c)] = values[n]
      }
      maps = append(maps, m)
    }
  }

  return maps, nil
}

func QueryMap(db *sql.DB, query string, args ...interface{}) (kmap.Map, error) {
  rows, err := db.Query(query, args...)
  if err != nil {
    return nil, err
  }

  ret, err := GetMaps(rows)
  if len(ret) > 0 {
    return ret[0], err
  }

  return nil, err
}

func QueryMaps(db *sql.DB, query string, args ...interface{}) ([]kmap.Map, error) {
  rows, err := db.Query(query, args...)
  if err != nil {
    return nil, err
  }

  ret, err := GetMaps(rows)
  if len(ret) > 0 {
    return ret, err
  }

  return nil, err
}

// func QueryStruct(db *sql.DB, query string, strct interface{}, args ...interface{}) error {
//   rows, err := db.Query(query, args...)
//   if err != nil {
//     return err
//   }

//   if rows.Next() {
//     err = sqlstruct.Scan(strct, rows)
//     if err != nil {
//       return err
//     }
//   }

//   return nil
// }

// // use it like:
// // var accounts []*Account
// // var account Account
// // QueryStructs(db, "select * from Accounts", &accounts, &account)
// func QueryStructs(db *sql.DB, query string, strcts interface{}, strct interface{}, args ...interface{}) error {
//   rows, err := db.Query(query, args...)
//   if err != nil {
//     return err
//   }

//   vof := reflect.ValueOf(strcts)
//   //into := reflect.New(vof.Type().Elem().Elem())

//   for rows.Next() {
//     err := sqlstruct.Scan(strct, rows)
//     if err != nil {
//       fmt.Println(err)
//       return err
//     }

//     vof.Elem().Set(reflect.Append(vof.Elem(), reflect.ValueOf(strct)))
//   }

//   return nil
// }

func Fields(names []string) string {
  for i, _ := range names {
    names[i] = strings.Replace(names[i], `"`, `\"`, -1)
  }
  return `("` + strings.Join(names, `", "`) + `")`
}

func InsertMap(db *sql.DB, table string, m kmap.Map) (sql.Result, error) {
  var fields []string
  var values []interface{}
  var variables []string

  for key, value := range m {
    fields = append(fields, key)
    values = append(values, value)
    variables = append(variables, "?")
  }

  fieldsSql := Fields(fields)

  res, err := db.Exec(fmt.Sprintf("INSERT INTO Messages %s VALUES (%s)", fieldsSql, strings.Join(variables, ",")), values...)
  return res, err
}
<|endoftext|>


<commit_before>package main

import	(
	"os"
	"fmt"
	"crypto/sha512"
	"encoding/hex"
	"hash"
	"path/filepath"
	"flag"
	"net/http"
	"net/url"
	"io/ioutil"
	"log"
	"strconv"
)

type LMVFile struct {
	hash string
	size int64
	name string
}

func CalculateSHA512(str string) string {

	var hasher hash.Hash = sha512.New()

	hasher.Reset()
	hasher.Write([]byte(str))
	return hex.EncodeToString(hasher.Sum(nil))

}

func encode(fp string, token bool) {

	lmv_file := new(LMVFile)

	file, err := os.Open(fp)

    if err != nil {
    	log.Fatal(err)
    }

    defer file.Close()

    stat, err := file.Stat()

    if err != nil {
    	log.Fatal(err)
    }

    bs := make([]byte, stat.Size())
    _, err = file.Read(bs)

    if err != nil {
        log.Fatal(err)
    }

    lmv_file.hash = CalculateSHA512(string(bs))
    lmv_file.size = stat.Size()
    lmv_file.name = filepath.Base(fp)

	if token {

    	upload_address := "http://127.0.0.1:8081/upload"

		fields := make(url.Values)
        fields.Set("name", lmv_file.name)
        fields.Set("hash", lmv_file.hash)
        fields.Set("size", strconv.FormatInt(lmv_file.size, 10))

    	resp, err := http.PostForm(upload_address, fields)

    	if err != nil {
			log.Fatal(err)
		}

		defer resp.Body.Close()

		body, err := ioutil.ReadAll(resp.Body)

		if err != nil {
			log.Fatal(err)
		}

		fmt.Println("'" + lmv_file.name + "'" + " --> " + "'" + string(body) + "'")

	} else {

		os.Create(lmv_file.name + ".lmv")

		full := ""
		full += lmv_file.hash
		full += "\n"
		full += lmv_file.name
		full += "\n"
		full += strconv.FormatInt(lmv_file.size, 10)

		err = ioutil.WriteFile(lmv_file.name + ".lmv", []byte(full), 0644)

		if err != nil {
			log.Fatal(err)
		}

    }

}

func main() {

	token := flag.Bool("token", false, "Use tokens in place of .lmv files")

	flag.Parse()

	if len(os.Args) < 2 {

		fmt.Println("Use lmv -h for usage")

	} else {

		for i := 0; i < len(os.Args[1:]); i++ {

			if _, err := os.Stat(os.Args[i+1]); err == nil {

	    		encode(os.Args[i+1], *token)

			}

	    }

	}
}<commit_msg>Reduced .lmv content generation to one line<commit_after>package main

import	(
	"os"
	"fmt"
	"crypto/sha512"
	"encoding/hex"
	"hash"
	"path/filepath"
	"flag"
	"net/http"
	"net/url"
	"io/ioutil"
	"log"
	"strconv"
)

type LMVFile struct {
	hash string
	size int64
	name string
}

func CalculateSHA512(str string) string {

	var hasher hash.Hash = sha512.New()

	hasher.Reset()
	hasher.Write([]byte(str))
	return hex.EncodeToString(hasher.Sum(nil))

}

func encode(fp string, token bool) {

	lmv_file := new(LMVFile)

	file, err := os.Open(fp)

    if err != nil {
    	log.Fatal(err)
    }

    defer file.Close()

    stat, err := file.Stat()

    if err != nil {
    	log.Fatal(err)
    }

    bs := make([]byte, stat.Size())
    _, err = file.Read(bs)

    if err != nil {
        log.Fatal(err)
    }

    lmv_file.hash = CalculateSHA512(string(bs))
    lmv_file.size = stat.Size()
    lmv_file.name = filepath.Base(fp)

	if token {

    	upload_address := "http://127.0.0.1:8081/upload"

		fields := make(url.Values)
        fields.Set("name", lmv_file.name)
        fields.Set("hash", lmv_file.hash)
        fields.Set("size", strconv.FormatInt(lmv_file.size, 10))

    	resp, err := http.PostForm(upload_address, fields)

    	if err != nil {
			log.Fatal(err)
		}

		defer resp.Body.Close()

		body, err := ioutil.ReadAll(resp.Body)

		if err != nil {
			log.Fatal(err)
		}

		fmt.Println("'" + lmv_file.name + "'" + " --> " + "'" + string(body) + "'")

	} else {

		os.Create(lmv_file.name + ".lmv")

		full := lmv_file.hash + "\n" + lmv_file.name + "\n" + strconv.FormatInt(lmv_file.size, 10)

		err = ioutil.WriteFile(lmv_file.name + ".lmv", []byte(full), 0644)

		if err != nil {
			log.Fatal(err)
		}

    }

}

func main() {

	token := flag.Bool("token", false, "Use tokens in place of .lmv files")

	flag.Parse()

	if len(os.Args) < 2 {

		fmt.Println("Use lmv -h for usage")

	} else {

		for i := 0; i < len(os.Args[1:]); i++ {

			if _, err := os.Stat(os.Args[i+1]); err == nil {

	    		encode(os.Args[i+1], *token)

			}

	    }

	}
}<|endoftext|>


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

package main

import (
	"log"
	"os"
)

// An enumeration of log levels.
const (
	infoLevel = iota
	errorLevel
	silentLevel
)

// LevelLogger defines a logger which is aware of log levels and only logs
// messages when below or equal to a given log level.
type LevelLogger struct {
	*log.Logger
	level int
}

// NewLogger instantiates a new LevelLogger with a given log level.
func NewLogger(level int) *LevelLogger {
	return &LevelLogger{
		Logger: log.New(os.Stderr, "", log.LstdFlags),
		level:  level,
	}
}

// Info logs a message if the current log level is below or equal to infoLevel.
func (l *LevelLogger) Info(v ...interface{}) {
	if l.level <= infoLevel {
		log.Print(v...)
	}
}

// Infoln logs a message if the current log level is below or equal to
// infoLevel.
func (l *LevelLogger) Infoln(v ...interface{}) {
	if l.level <= infoLevel {
		log.Println(v...)
	}
}

// Infof logs a message if the current log level is below or equal to infoLevel.
func (l *LevelLogger) Infof(format string, v ...interface{}) {
	if l.level <= infoLevel {
		log.Printf(format, v...)
	}
}

// Error logs a message if the current log level is below or equal to
// errorLevel.
func (l *LevelLogger) Error(v ...interface{}) {
	if l.level <= errorLevel {
		log.Print(v...)
	}
}

// Errorln logs a message if the current log level is below or equal to
// errorLevel.
func (l *LevelLogger) Errorln(v ...interface{}) {
	if l.level <= errorLevel {
		log.Println(v...)
	}
}

// Errorf logs a message if the current log level is below or equal to
// errorLevel.
func (l *LevelLogger) Errorf(format string, v ...interface{}) {
	if l.level <= errorLevel {
		log.Printf(format, v...)
	}
}

// Fatal logs a message and exit the process.
func (l *LevelLogger) Fatal(v ...interface{}) {
	l.Error(v...)
	os.Exit(1)
}

// Fatalln logs a message and exit the process.
func (l *LevelLogger) Fatalln(v ...interface{}) {
	l.Errorln(v...)
	os.Exit(1)
}

// Fatalf logs a message and exit the process.
func (l *LevelLogger) Fatalf(format string, v ...interface{}) {
	l.Errorf(format, v...)
	os.Exit(1)
}
<commit_msg>Introduce new debug log level<commit_after>// Copyright (c) 2016 Betalo AB
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
	"log"
	"os"
)

// An enumeration of log levels.
const (
	debugLevel = iota
	infoLevel
	errorLevel
	silentLevel
)

// LevelLogger defines a logger which is aware of log levels and only logs
// messages when below or equal to a given log level.
type LevelLogger struct {
	*log.Logger
	level int
}

// NewLogger instantiates a new LevelLogger with a given log level.
func NewLogger(level int) *LevelLogger {
	return &LevelLogger{
		Logger: log.New(os.Stderr, "", log.LstdFlags),
		level:  level,
	}
}

// Debug logs a message if the current log level is below or equal to
// debugLevel.
func (l *LevelLogger) Debug(v ...interface{}) {
	if l.level <= debugLevel {
		log.Print(v...)
	}
}

// Debugln logs a message if the current log level is below or equal to
// debugLevel.
func (l *LevelLogger) Debugln(v ...interface{}) {
	if l.level <= debugLevel {
		log.Println(v...)
	}
}

// Debugf logs a message if the current log level is below or equal to
// debugLevel.
func (l *LevelLogger) Debugf(format string, v ...interface{}) {
	if l.level <= debugLevel {
		log.Printf(format, v...)
	}
}

// Info logs a message if the current log level is below or equal to infoLevel.
func (l *LevelLogger) Info(v ...interface{}) {
	if l.level <= infoLevel {
		log.Print(v...)
	}
}

// Infoln logs a message if the current log level is below or equal to
// infoLevel.
func (l *LevelLogger) Infoln(v ...interface{}) {
	if l.level <= infoLevel {
		log.Println(v...)
	}
}

// Infof logs a message if the current log level is below or equal to infoLevel.
func (l *LevelLogger) Infof(format string, v ...interface{}) {
	if l.level <= infoLevel {
		log.Printf(format, v...)
	}
}

// Error logs a message if the current log level is below or equal to
// errorLevel.
func (l *LevelLogger) Error(v ...interface{}) {
	if l.level <= errorLevel {
		log.Print(v...)
	}
}

// Errorln logs a message if the current log level is below or equal to
// errorLevel.
func (l *LevelLogger) Errorln(v ...interface{}) {
	if l.level <= errorLevel {
		log.Println(v...)
	}
}

// Errorf logs a message if the current log level is below or equal to
// errorLevel.
func (l *LevelLogger) Errorf(format string, v ...interface{}) {
	if l.level <= errorLevel {
		log.Printf(format, v...)
	}
}

// Fatal logs a message and exit the process.
func (l *LevelLogger) Fatal(v ...interface{}) {
	l.Error(v...)
	os.Exit(1)
}

// Fatalln logs a message and exit the process.
func (l *LevelLogger) Fatalln(v ...interface{}) {
	l.Errorln(v...)
	os.Exit(1)
}

// Fatalf logs a message and exit the process.
func (l *LevelLogger) Fatalf(format string, v ...interface{}) {
	l.Errorf(format, v...)
	os.Exit(1)
}
<|endoftext|>


<commit_before>// Package gogo/log controls the logging output for gb derived programs.
package gb

import (
	"fmt"
	"log"
	"os"
)

var (
	// Quiet suppresses all logging output below ERROR
	Quiet = false

	// Verbose enables logging output below INFO
	Verbose = false

	// Logger is the log.Logger object that backs this logger.
	Logger = log.New(os.Stdout, "", log.LstdFlags)
)

var Fatalf = log.Fatalf

func fatalf(format string, args ...interface{}) {
	Logger.Printf("FATAL "+format, args...)
	fmt.Scanln()
	os.Exit(1)
}

func Errorf(format string, args ...interface{}) {
	Logger.Printf("ERROR "+format, args...)
}

func Warnf(format string, args ...interface{}) {
	Logger.Printf("WARNING "+format, args...)
}

func Infof(format string, args ...interface{}) {
	if !Quiet {
		Logger.Printf("INFO "+format, args...)
	}
}

func Debugf(format string, args ...interface{}) {
	if Verbose && !Quiet {
		Logger.Printf("DEBUG "+format, args...)
	}
}
<commit_msg>Simplify logging output, drop timestamp<commit_after>// Package gogo/log controls the logging output for gb derived programs.
package gb

import (
	"fmt"
	"os"
)

var (
	// Quiet suppresses all logging output below ERROR
	Quiet = false

	// Verbose enables logging output below INFO
	Verbose = false
)

func Fatalf(format string, args ...interface{}) {
	fmt.Printf("FATAL "+format+"\n", args...)
	os.Exit(1)
}

func Errorf(format string, args ...interface{}) {
	fmt.Printf("ERROR "+format+"\n", args...)
}

func Warnf(format string, args ...interface{}) {
	fmt.Printf("WARNING "+format+"\n", args...)
}

func Infof(format string, args ...interface{}) {
	if !Quiet {
		if Verbose {
			fmt.Printf("INFO "+format+"\n", args...)
		} else {
			fmt.Printf("# "+format+"\n", args...)
		}
	}
}

func Debugf(format string, args ...interface{}) {
	if Verbose && !Quiet {
		fmt.Printf("DEBUG "+format+"\n", args...)
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"fmt"
	"os"
	"runtime/pprof"
)

func LogAlways(f string, a ...interface{}) {
	var buf bytes.Buffer
	buf.WriteString("*kati*: ")
	buf.WriteString(f)
	buf.WriteByte('\n')
	fmt.Printf(buf.String(), a...)
}

func LogStats(f string, a ...interface{}) {
	if !katiLogFlag && !katiStatsFlag {
		return
	}
	LogAlways(f, a...)
}

func Logf(f string, a ...interface{}) {
	if !katiLogFlag {
		return
	}
	LogAlways(f, a...)
}

func Warn(filename string, lineno int, f string, a ...interface{}) {
	f = fmt.Sprintf("%s:%d: warning: %s\n", filename, lineno, f)
	fmt.Printf(f, a...)
}

func WarnNoPrefix(filename string, lineno int, f string, a ...interface{}) {
	f = fmt.Sprintf("%s:%d: %s\n", filename, lineno, f)
	fmt.Printf(f, a...)
}

func Error(filename string, lineno int, f string, a ...interface{}) {
	f = fmt.Sprintf("%s:%d: %s", filename, lineno, f)
	maybeWriteHeapProfile()
	ErrorNoLocation(f, a...)
}

func ErrorNoLocation(f string, a ...interface{}) {
	f = fmt.Sprintf("%s\n", f)
	fmt.Printf(f, a...)
	if cpuprofile != "" {
		pprof.StopCPUProfile()
	}
	maybeWriteHeapProfile()
	dumpStats()
	os.Exit(2)
}
<commit_msg>synchronize LogAlways<commit_after>// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"fmt"
	"os"
	"runtime/pprof"
	"sync"
)

var logMu sync.Mutex

func LogAlways(f string, a ...interface{}) {
	var buf bytes.Buffer
	buf.WriteString("*kati*: ")
	buf.WriteString(f)
	buf.WriteByte('\n')
	logMu.Lock()
	fmt.Printf(buf.String(), a...)
	logMu.Unlock()
}

func LogStats(f string, a ...interface{}) {
	if !katiLogFlag && !katiStatsFlag {
		return
	}
	LogAlways(f, a...)
}

func Logf(f string, a ...interface{}) {
	if !katiLogFlag {
		return
	}
	LogAlways(f, a...)
}

func Warn(filename string, lineno int, f string, a ...interface{}) {
	f = fmt.Sprintf("%s:%d: warning: %s\n", filename, lineno, f)
	fmt.Printf(f, a...)
}

func WarnNoPrefix(filename string, lineno int, f string, a ...interface{}) {
	f = fmt.Sprintf("%s:%d: %s\n", filename, lineno, f)
	fmt.Printf(f, a...)
}

func Error(filename string, lineno int, f string, a ...interface{}) {
	f = fmt.Sprintf("%s:%d: %s", filename, lineno, f)
	maybeWriteHeapProfile()
	ErrorNoLocation(f, a...)
}

func ErrorNoLocation(f string, a ...interface{}) {
	f = fmt.Sprintf("%s\n", f)
	fmt.Printf(f, a...)
	if cpuprofile != "" {
		pprof.StopCPUProfile()
	}
	maybeWriteHeapProfile()
	dumpStats()
	os.Exit(2)
}
<|endoftext|>


<commit_before>package log

import (
	"errors"
	"fmt"
	"os"
	"runtime"
	"strings"
	"sync/atomic"
)

var loggers []Logger

var pid = os.Getpid()
var currentSeverity Severity

// Severity implementation is borrowed from glog, uses sync/atomic int32
type Severity int32

const (
	SeverityInfo Severity = iota
	SeverityWarn
	SeverityError
	SeverityFatal
)

var severityName = map[Severity]string{
	SeverityInfo:  "INFO",
	SeverityWarn:  "WARN",
	SeverityError: "ERROR",
	SeverityFatal: "FATAL",
}

// get returns the value of the severity.
func (s *Severity) Get() Severity {
	return Severity(atomic.LoadInt32((*int32)(s)))
}

// set sets the value of the severity.
func (s *Severity) Set(val Severity) {
	atomic.StoreInt32((*int32)(s), int32(val))
}

// less returns if this severity is greater than passed severity
func (s *Severity) Gt(val Severity) bool {
	return s.Get() > val
}

func (s Severity) String() string {
	n, ok := severityName[s]
	if !ok {
		return "UNKNOWN SEVERITY"
	}
	return n
}

func SeverityFromString(s string) (Severity, error) {
	s = strings.ToUpper(s)
	for k, val := range severityName {
		if val == s {
			return k, nil
		}
	}
	return -1, fmt.Errorf("unsupported severity: %s", s)
}

// Logger is a unified interface for all loggers.
type Logger interface {
	Info(string)
	Warning(string)
	Error(string)
	Fatal(string)
}

type logger struct{}

// Logging configuration to be passed to all loggers during initialization.
type LogConfig struct {
	Name string
}

func (c LogConfig) String() string {
	return fmt.Sprintf("LogConfig(Name=%v)", c.Name)
}

// SetSeverity sets current logging severity. Acceptable values are SeverityInfo, SeverityWarn, SeverityError, SeverityFatal
func SetSeverity(s Severity) {
	currentSeverity.Set(s)
}

// GetSeverity returns currently set severity.
func GetSeverity() Severity {
	return currentSeverity
}

// Loggin initialization, must be called at the beginning of your cool app.
func Init(logConfigs []*LogConfig) error {
	for _, config := range logConfigs {
		logger, err := newLogger(config)
		if err != nil {
			return err
		}
		loggers = append(loggers, logger)
	}
	return nil
}

// Make a proper logger from a given configuration.
func newLogger(config *LogConfig) (Logger, error) {
	switch config.Name {
	case "console":
		return NewConsoleLogger(config)
	case "syslog":
		return NewSysLogger(config)
	}
	return nil, errors.New(fmt.Sprintf("Unknown logger: %v", config))
}

// Infof logs to the INFO log.
func Infof(format string, args ...interface{}) {
	if currentSeverity.Gt(SeverityInfo) {
		return
	}
	message := makeMessage(SeverityInfo, format, args...)
	for _, logger := range loggers {
		logger.Info(message)
	}
}

// Warningf logs to the WARNING and INFO logs.
func Warningf(format string, args ...interface{}) {
	if currentSeverity.Gt(SeverityWarn) {
		return
	}
	message := makeMessage(SeverityWarn, format, args...)
	for _, logger := range loggers {
		logger.Warning(message)
	}
}

// Errorf logs to the ERROR, WARNING, and INFO logs.
func Errorf(format string, args ...interface{}) {
	if currentSeverity.Gt(SeverityError) {
		return
	}
	message := makeMessage(SeverityError, format, args...)
	for _, logger := range loggers {
		logger.Error(message)
	}
}

// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
func Fatalf(format string, args ...interface{}) {
	if currentSeverity.Gt(SeverityFatal) {
		return
	}
	message := makeMessage(SeverityFatal, format, args...)
	stacks := stackTraces()
	for _, logger := range loggers {
		logger.Fatal(message)
		logger.Fatal(stacks)
	}

	exit()
}

func makeMessage(sev Severity, format string, args ...interface{}) string {
	file, line := callerInfo()
	return fmt.Sprintf("%s PID:%d [%s:%d] %s", sev, pid, file, line, fmt.Sprintf(format, args...))
}

// Return stack traces of all the running goroutines.
func stackTraces() string {
	trace := make([]byte, 100000)
	nbytes := runtime.Stack(trace, true)
	return string(trace[:nbytes])
}

// Return a file name and a line number.
func callerInfo() (string, int) {
	_, file, line, ok := runtimeCaller(3) // number of frames to the user's call.

	if !ok {
		file = "unknown"
		line = 0
	} else {
		slashPosition := strings.LastIndex(file, "/")
		if slashPosition >= 0 {
			file = file[slashPosition+1:]
		}
	}

	return file, line
}

// runtime functions for mocking

var runtimeCaller = runtime.Caller

var exit = func() {
	os.Exit(255)
}
<commit_msg>fix typo<commit_after>package log

import (
	"errors"
	"fmt"
	"os"
	"runtime"
	"strings"
	"sync/atomic"
)

var loggers []Logger

var pid = os.Getpid()
var currentSeverity Severity

// Severity implementation is borrowed from glog, uses sync/atomic int32
type Severity int32

const (
	SeverityInfo Severity = iota
	SeverityWarn
	SeverityError
	SeverityFatal
)

var severityName = map[Severity]string{
	SeverityInfo:  "INFO",
	SeverityWarn:  "WARN",
	SeverityError: "ERROR",
	SeverityFatal: "FATAL",
}

// get returns the value of the severity.
func (s *Severity) Get() Severity {
	return Severity(atomic.LoadInt32((*int32)(s)))
}

// set sets the value of the severity.
func (s *Severity) Set(val Severity) {
	atomic.StoreInt32((*int32)(s), int32(val))
}

// less returns if this severity is greater than passed severity
func (s *Severity) Gt(val Severity) bool {
	return s.Get() > val
}

func (s Severity) String() string {
	n, ok := severityName[s]
	if !ok {
		return "UNKNOWN SEVERITY"
	}
	return n
}

func SeverityFromString(s string) (Severity, error) {
	s = strings.ToUpper(s)
	for k, val := range severityName {
		if val == s {
			return k, nil
		}
	}
	return -1, fmt.Errorf("unsupported severity: %s", s)
}

// Logger is a unified interface for all loggers.
type Logger interface {
	Info(string)
	Warning(string)
	Error(string)
	Fatal(string)
}

type logger struct{}

// Logging configuration to be passed to all loggers during initialization.
type LogConfig struct {
	Name string
}

func (c LogConfig) String() string {
	return fmt.Sprintf("LogConfig(Name=%v)", c.Name)
}

// SetSeverity sets current logging severity. Acceptable values are SeverityInfo, SeverityWarn, SeverityError, SeverityFatal
func SetSeverity(s Severity) {
	currentSeverity.Set(s)
}

// GetSeverity returns currently set severity.
func GetSeverity() Severity {
	return currentSeverity
}

// Logging initialization, must be called at the beginning of your cool app.
func Init(logConfigs []*LogConfig) error {
	for _, config := range logConfigs {
		logger, err := newLogger(config)
		if err != nil {
			return err
		}
		loggers = append(loggers, logger)
	}
	return nil
}

// Make a proper logger from a given configuration.
func newLogger(config *LogConfig) (Logger, error) {
	switch config.Name {
	case "console":
		return NewConsoleLogger(config)
	case "syslog":
		return NewSysLogger(config)
	}
	return nil, errors.New(fmt.Sprintf("Unknown logger: %v", config))
}

// Infof logs to the INFO log.
func Infof(format string, args ...interface{}) {
	if currentSeverity.Gt(SeverityInfo) {
		return
	}
	message := makeMessage(SeverityInfo, format, args...)
	for _, logger := range loggers {
		logger.Info(message)
	}
}

// Warningf logs to the WARNING and INFO logs.
func Warningf(format string, args ...interface{}) {
	if currentSeverity.Gt(SeverityWarn) {
		return
	}
	message := makeMessage(SeverityWarn, format, args...)
	for _, logger := range loggers {
		logger.Warning(message)
	}
}

// Errorf logs to the ERROR, WARNING, and INFO logs.
func Errorf(format string, args ...interface{}) {
	if currentSeverity.Gt(SeverityError) {
		return
	}
	message := makeMessage(SeverityError, format, args...)
	for _, logger := range loggers {
		logger.Error(message)
	}
}

// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
func Fatalf(format string, args ...interface{}) {
	if currentSeverity.Gt(SeverityFatal) {
		return
	}
	message := makeMessage(SeverityFatal, format, args...)
	stacks := stackTraces()
	for _, logger := range loggers {
		logger.Fatal(message)
		logger.Fatal(stacks)
	}

	exit()
}

func makeMessage(sev Severity, format string, args ...interface{}) string {
	file, line := callerInfo()
	return fmt.Sprintf("%s PID:%d [%s:%d] %s", sev, pid, file, line, fmt.Sprintf(format, args...))
}

// Return stack traces of all the running goroutines.
func stackTraces() string {
	trace := make([]byte, 100000)
	nbytes := runtime.Stack(trace, true)
	return string(trace[:nbytes])
}

// Return a file name and a line number.
func callerInfo() (string, int) {
	_, file, line, ok := runtimeCaller(3) // number of frames to the user's call.

	if !ok {
		file = "unknown"
		line = 0
	} else {
		slashPosition := strings.LastIndex(file, "/")
		if slashPosition >= 0 {
			file = file[slashPosition+1:]
		}
	}

	return file, line
}

// runtime functions for mocking

var runtimeCaller = runtime.Caller

var exit = func() {
	os.Exit(255)
}
<|endoftext|>


<commit_before>package raft

import (
	"bytes"
	"errors"
	"fmt"
	"hash/crc32"
	"io"
	"sync"
	"time"
)

var (
	ErrTermTooSmall    = errors.New("term too small")
	ErrIndexTooSmall   = errors.New("index too small")
	ErrIndexTooBig     = errors.New("commit index too big")
	ErrInvalidChecksum = errors.New("invalid checksum")
	ErrInvalidLogLine  = errors.New("invalid log line")
	ErrNoCommand       = errors.New("no command")
	ErrBadIndex        = errors.New("bad index")
	ErrBadTerm         = errors.New("bad term")
)

type Log struct {
	sync.RWMutex
	store       io.Writer
	entries     []LogEntry
	commitIndex uint64
	apply       func([]byte) ([]byte, error)
}

func NewLog(store io.ReadWriter, apply func([]byte) ([]byte, error)) *Log {
	l := &Log{
		store:       store,
		entries:     []LogEntry{},
		commitIndex: 0,
		apply:       apply,
	}
	l.recover(store)
	return l
}

// recover reads from the log's store, to populate the log with log entries
// from persistent storage. It should be called once, at log instantiation.
func (l *Log) recover(r io.Reader) error {
	for {
		var entry LogEntry
		switch err := entry.decode(r); err {
		case io.EOF:
			return nil // successful completion
		default:
			return err // unsuccessful completion
		case nil:
			if err = l.appendEntry(entry); err != nil {
				return err
			}
		}
	}
}

// entriesAfter returns a slice of log entries after (i.e. not including) the
// passed index, and the term of the log entry specified by index, as a
// convenience to the caller. (This function is only used by a leader attempting
// to flush log entries to its followers.)
//
// This function is called to populate an AppendEntries RPC. That implies they
// are destined for a follower, which implies the application of the commands
// should have the response thrown away, which implies we shouldn't pass a
// commandResponse channel (see: commitTo implementation). In the normal case,
// the LogEntries we return here will get serialized as they pass thru their
// transport, and lose their commandResponse channel anyway. But in the case of
// a LocalPeer (or equivalent) this doesn't happen. So, we must make sure to
// proactively strip commandResponse channels.
func (l *Log) entriesAfter(index uint64) ([]LogEntry, uint64) {
	l.RLock()
	defer l.RUnlock()

	i := 0
	lastTerm := uint64(0)
	for ; i < len(l.entries); i++ {
		if l.entries[i].Index > index {
			break
		}
		lastTerm = l.entries[i].Term
	}

	a := l.entries[i:]
	if len(a) == 0 {
		return []LogEntry{}, lastTerm
	}

	return stripResponseChannels(a), lastTerm
}

func stripResponseChannels(a []LogEntry) []LogEntry {
	stripped := make([]LogEntry, len(a))
	for i, entry := range a {
		stripped[i] = LogEntry{
			Index:   entry.Index,
			Term:    entry.Term,
			Command: entry.Command,
		}
	}
	return stripped
}

// contains returns true if a log entry with the given index and term exists in
// the log.
func (l *Log) contains(index, term uint64) bool {
	l.RLock()
	defer l.RUnlock()

	// It's not necessarily true that l.entries[i] has index == i.
	for _, entry := range l.entries {
		if entry.Index == index && entry.Term == term {
			return true
		}
		if entry.Index > index || entry.Term > term {
			break
		}
	}
	return false
}

// ensureLastIs deletes all non-committed log entries after the given index and
// term. It will fail if the given index doesn't exist, has already been
// committed, or doesn't match the given term.
//
// This method satisfies the requirement that a log entry in an AppendEntries
// call precisely follows the accompanying LastLogTerm and LastLogIndex.
func (l *Log) ensureLastIs(index, term uint64) error {
	l.Lock()
	defer l.Unlock()

	// Taken loosely from benbjohnson's impl

	if index < l.commitIndex {
		return ErrIndexTooSmall
	}

	if index > uint64(len(l.entries)) {
		return ErrIndexTooBig
	}

	// It's possible that the passed index is 0. It means the leader has come to
	// decide we need a complete log rebuild. Of course, that's only valid if we
	// haven't committed anything, so this check comes after that one.
	if index == 0 {
		l.entries = []LogEntry{}
		return nil
	}

	entry := l.entries[index-1]
	if entry.Term != term {
		return ErrBadTerm
	}

	// If we blow away log entries that haven't yet sent responses to clients,
	// signal the clients to stop waiting, by closing the channel without a
	// response value.
	for _, deletedEntry := range l.entries[index:] {
		if deletedEntry.commandResponse != nil {
			close(deletedEntry.commandResponse)
			deletedEntry.commandResponse = nil
		}
	}

	l.entries = l.entries[:index]
	return nil
}

// getCommitIndex returns the commit index of the log. That is, the index of the
// last log entry which can be considered committed.
func (l *Log) getCommitIndex() uint64 {
	l.RLock()
	defer l.RUnlock()
	return l.commitIndex
}

// lastIndex returns the index of the most recent log entry.
func (l *Log) lastIndex() uint64 {
	l.RLock()
	defer l.RUnlock()
	return l.lastIndexWithLock()
}

func (l *Log) lastIndexWithLock() uint64 {
	if len(l.entries) <= 0 {
		return 0
	}
	return l.entries[len(l.entries)-1].Index
}

// lastTerm returns the term of the most recent log entry.
func (l *Log) lastTerm() uint64 {
	l.RLock()
	defer l.RUnlock()
	return l.lastTermWithLock()
}

func (l *Log) lastTermWithLock() uint64 {
	if len(l.entries) <= 0 {
		return 0
	}
	return l.entries[len(l.entries)-1].Term
}

// appendEntry appends the passed log entry to the log.
// It will return an error if any condition is violated.
func (l *Log) appendEntry(entry LogEntry) error {
	l.Lock()
	defer l.Unlock()
	if len(l.entries) > 0 {
		lastTerm := l.lastTermWithLock()
		if entry.Term < lastTerm {
			return ErrTermTooSmall
		}
		if entry.Term == lastTerm && entry.Index <= l.lastIndexWithLock() {
			return ErrIndexTooSmall
		}
	}

	l.entries = append(l.entries, entry)
	return nil
}

// commitTo commits all log entries up to the passed commitIndex. Commit means:
// synchronize the log entry to persistent storage, and call the state machine
// execute function for the log entry's command.
func (l *Log) commitTo(commitIndex uint64) error {
	l.Lock()
	defer l.Unlock()

	// Reject old commit indexes
	if commitIndex < l.commitIndex {
		return ErrIndexTooSmall
	}

	// Reject new commit indexes
	if commitIndex > uint64(len(l.entries)) {
		return ErrIndexTooBig
	}

	// Sync entries between our commit index and the passed commit index
	for i := l.commitIndex; i < commitIndex; i++ {
		entry := l.entries[i]
		if err := entry.encode(l.store); err != nil {
			return err
		}
		resp, err := l.apply(entry.Command)
		if err != nil {
			return err
		}
		if entry.commandResponse != nil {
			defer func() {
				close(entry.commandResponse)
				entry.commandResponse = nil
			}()
			select {
			case entry.commandResponse <- resp: // TODO might could `go` this
				break
			case <-time.After(BroadcastInterval()): // << ElectionInterval
				panic("uncoöperative command response receiver")
			}
		}
		l.commitIndex = entry.Index
	}

	return nil
}

// LogEntry is the atomic unit being managed by the distributed log. A log entry
// always has an index (monotonically increasing), a term in which the Raft
// network leader first sees the entry, and a command. The command is what gets
// executed against the node state machine when the log entry is successfully
// replicated.
type LogEntry struct {
	Index           uint64      `json:"index"`
	Term            uint64      `json:"term"` // when received by leader
	Command         []byte      `json:"command,omitempty"`
	commandResponse chan []byte `json:"-"` // only present on receiver's log
}

// encode serializes the log entry to the passed io.Writer.
func (e *LogEntry) encode(w io.Writer) error {
	if len(e.Command) <= 0 {
		return ErrNoCommand
	}
	if e.Index <= 0 {
		return ErrBadIndex
	}
	if e.Term <= 0 {
		return ErrBadTerm
	}

	buf := &bytes.Buffer{}
	if _, err := fmt.Fprintf(buf, "%016x %016x %s\n", e.Index, e.Term, e.Command); err != nil {
		return err
	}

	checksum := crc32.ChecksumIEEE(buf.Bytes())
	_, err := fmt.Fprintf(w, "%08x %s", checksum, buf.String())
	return err
}

// decode deserializes one log entry from the passed io.Reader.
func (e *LogEntry) decode(r io.Reader) error {
	var readChecksum uint32
	if _, err := fmt.Fscanf(r, "%08x ", &readChecksum); err != nil {
		return err
	}

	if _, err := fmt.Fscanf(r, "%016x ", &e.Index); err != nil {
		return err
	}

	if _, err := fmt.Fscanf(r, "%016x ", &e.Term); err != nil {
		return err
	}

	if err := consumeUntil(r, '\n', &e.Command); err != nil {
		return err
	}

	b := fmt.Sprintf("%016x %016x %s\n", e.Index, e.Term, e.Command)
	computedChecksum := crc32.ChecksumIEEE([]byte(b))
	if computedChecksum != readChecksum {
		return ErrInvalidChecksum
	}

	return nil
}

// consumeUntil does a series of 1-byte Reads from the passed io.Reader
// until it reaches delim, or EOF. This is pretty inefficient.
func consumeUntil(r io.Reader, delim byte, dst *[]byte) error {
	p := make([]byte, 1)
	for {
		n, err := r.Read(p)
		if err != nil {
			return err
		}
		if n != 1 {
			return io.ErrUnexpectedEOF
		}
		if p[0] == delim {
			return nil
		}
		*dst = append(*dst, p[0])
	}
}
<commit_msg>Deterministic log commandResponse closure<commit_after>package raft

import (
	"bytes"
	"errors"
	"fmt"
	"hash/crc32"
	"io"
	"sync"
	"time"
)

var (
	ErrTermTooSmall    = errors.New("term too small")
	ErrIndexTooSmall   = errors.New("index too small")
	ErrIndexTooBig     = errors.New("commit index too big")
	ErrInvalidChecksum = errors.New("invalid checksum")
	ErrInvalidLogLine  = errors.New("invalid log line")
	ErrNoCommand       = errors.New("no command")
	ErrBadIndex        = errors.New("bad index")
	ErrBadTerm         = errors.New("bad term")
)

type Log struct {
	sync.RWMutex
	store       io.Writer
	entries     []LogEntry
	commitIndex uint64
	apply       func([]byte) ([]byte, error)
}

func NewLog(store io.ReadWriter, apply func([]byte) ([]byte, error)) *Log {
	l := &Log{
		store:       store,
		entries:     []LogEntry{},
		commitIndex: 0,
		apply:       apply,
	}
	l.recover(store)
	return l
}

// recover reads from the log's store, to populate the log with log entries
// from persistent storage. It should be called once, at log instantiation.
func (l *Log) recover(r io.Reader) error {
	for {
		var entry LogEntry
		switch err := entry.decode(r); err {
		case io.EOF:
			return nil // successful completion
		default:
			return err // unsuccessful completion
		case nil:
			if err = l.appendEntry(entry); err != nil {
				return err
			}
		}
	}
}

// entriesAfter returns a slice of log entries after (i.e. not including) the
// passed index, and the term of the log entry specified by index, as a
// convenience to the caller. (This function is only used by a leader attempting
// to flush log entries to its followers.)
//
// This function is called to populate an AppendEntries RPC. That implies they
// are destined for a follower, which implies the application of the commands
// should have the response thrown away, which implies we shouldn't pass a
// commandResponse channel (see: commitTo implementation). In the normal case,
// the LogEntries we return here will get serialized as they pass thru their
// transport, and lose their commandResponse channel anyway. But in the case of
// a LocalPeer (or equivalent) this doesn't happen. So, we must make sure to
// proactively strip commandResponse channels.
func (l *Log) entriesAfter(index uint64) ([]LogEntry, uint64) {
	l.RLock()
	defer l.RUnlock()

	i := 0
	lastTerm := uint64(0)
	for ; i < len(l.entries); i++ {
		if l.entries[i].Index > index {
			break
		}
		lastTerm = l.entries[i].Term
	}

	a := l.entries[i:]
	if len(a) == 0 {
		return []LogEntry{}, lastTerm
	}

	return stripResponseChannels(a), lastTerm
}

func stripResponseChannels(a []LogEntry) []LogEntry {
	stripped := make([]LogEntry, len(a))
	for i, entry := range a {
		stripped[i] = LogEntry{
			Index:   entry.Index,
			Term:    entry.Term,
			Command: entry.Command,
		}
	}
	return stripped
}

// contains returns true if a log entry with the given index and term exists in
// the log.
func (l *Log) contains(index, term uint64) bool {
	l.RLock()
	defer l.RUnlock()

	// It's not necessarily true that l.entries[i] has index == i.
	for _, entry := range l.entries {
		if entry.Index == index && entry.Term == term {
			return true
		}
		if entry.Index > index || entry.Term > term {
			break
		}
	}
	return false
}

// ensureLastIs deletes all non-committed log entries after the given index and
// term. It will fail if the given index doesn't exist, has already been
// committed, or doesn't match the given term.
//
// This method satisfies the requirement that a log entry in an AppendEntries
// call precisely follows the accompanying LastLogTerm and LastLogIndex.
func (l *Log) ensureLastIs(index, term uint64) error {
	l.Lock()
	defer l.Unlock()

	// Taken loosely from benbjohnson's impl

	if index < l.commitIndex {
		return ErrIndexTooSmall
	}

	if index > uint64(len(l.entries)) {
		return ErrIndexTooBig
	}

	// It's possible that the passed index is 0. It means the leader has come to
	// decide we need a complete log rebuild. Of course, that's only valid if we
	// haven't committed anything, so this check comes after that one.
	if index == 0 {
		l.entries = []LogEntry{}
		return nil
	}

	entry := l.entries[index-1]
	if entry.Term != term {
		return ErrBadTerm
	}

	// If we blow away log entries that haven't yet sent responses to clients,
	// signal the clients to stop waiting, by closing the channel without a
	// response value.
	for _, deletedEntry := range l.entries[index:] {
		if deletedEntry.commandResponse != nil {
			close(deletedEntry.commandResponse)
			deletedEntry.commandResponse = nil
		}
	}

	l.entries = l.entries[:index]
	return nil
}

// getCommitIndex returns the commit index of the log. That is, the index of the
// last log entry which can be considered committed.
func (l *Log) getCommitIndex() uint64 {
	l.RLock()
	defer l.RUnlock()
	return l.commitIndex
}

// lastIndex returns the index of the most recent log entry.
func (l *Log) lastIndex() uint64 {
	l.RLock()
	defer l.RUnlock()
	return l.lastIndexWithLock()
}

func (l *Log) lastIndexWithLock() uint64 {
	if len(l.entries) <= 0 {
		return 0
	}
	return l.entries[len(l.entries)-1].Index
}

// lastTerm returns the term of the most recent log entry.
func (l *Log) lastTerm() uint64 {
	l.RLock()
	defer l.RUnlock()
	return l.lastTermWithLock()
}

func (l *Log) lastTermWithLock() uint64 {
	if len(l.entries) <= 0 {
		return 0
	}
	return l.entries[len(l.entries)-1].Term
}

// appendEntry appends the passed log entry to the log.
// It will return an error if any condition is violated.
func (l *Log) appendEntry(entry LogEntry) error {
	l.Lock()
	defer l.Unlock()
	if len(l.entries) > 0 {
		lastTerm := l.lastTermWithLock()
		if entry.Term < lastTerm {
			return ErrTermTooSmall
		}
		if entry.Term == lastTerm && entry.Index <= l.lastIndexWithLock() {
			return ErrIndexTooSmall
		}
	}

	l.entries = append(l.entries, entry)
	return nil
}

// commitTo commits all log entries up to the passed commitIndex. Commit means:
// synchronize the log entry to persistent storage, and call the state machine
// execute function for the log entry's command.
func (l *Log) commitTo(commitIndex uint64) error {
	l.Lock()
	defer l.Unlock()

	// Reject old commit indexes
	if commitIndex < l.commitIndex {
		return ErrIndexTooSmall
	}

	// Reject new commit indexes
	if commitIndex > uint64(len(l.entries)) {
		return ErrIndexTooBig
	}

	// Sync entries between our commit index and the passed commit index
	for i := l.commitIndex; i < commitIndex; i++ {
		entry := l.entries[i]
		if err := entry.encode(l.store); err != nil {
			return err
		}
		resp, err := l.apply(entry.Command)
		if err != nil {
			return err
		}
		if entry.commandResponse != nil {
			select {
			case entry.commandResponse <- resp: // TODO might could `go` this
				break
			case <-time.After(BroadcastInterval()): // << ElectionInterval
				panic("uncoöperative command response receiver")
			}
			close(entry.commandResponse)
			entry.commandResponse = nil
		}
		l.commitIndex = entry.Index
	}

	return nil
}

// LogEntry is the atomic unit being managed by the distributed log. A log entry
// always has an index (monotonically increasing), a term in which the Raft
// network leader first sees the entry, and a command. The command is what gets
// executed against the node state machine when the log entry is successfully
// replicated.
type LogEntry struct {
	Index           uint64      `json:"index"`
	Term            uint64      `json:"term"` // when received by leader
	Command         []byte      `json:"command,omitempty"`
	commandResponse chan []byte `json:"-"` // only present on receiver's log
}

// encode serializes the log entry to the passed io.Writer.
func (e *LogEntry) encode(w io.Writer) error {
	if len(e.Command) <= 0 {
		return ErrNoCommand
	}
	if e.Index <= 0 {
		return ErrBadIndex
	}
	if e.Term <= 0 {
		return ErrBadTerm
	}

	buf := &bytes.Buffer{}
	if _, err := fmt.Fprintf(buf, "%016x %016x %s\n", e.Index, e.Term, e.Command); err != nil {
		return err
	}

	checksum := crc32.ChecksumIEEE(buf.Bytes())
	_, err := fmt.Fprintf(w, "%08x %s", checksum, buf.String())
	return err
}

// decode deserializes one log entry from the passed io.Reader.
func (e *LogEntry) decode(r io.Reader) error {
	var readChecksum uint32
	if _, err := fmt.Fscanf(r, "%08x ", &readChecksum); err != nil {
		return err
	}

	if _, err := fmt.Fscanf(r, "%016x ", &e.Index); err != nil {
		return err
	}

	if _, err := fmt.Fscanf(r, "%016x ", &e.Term); err != nil {
		return err
	}

	if err := consumeUntil(r, '\n', &e.Command); err != nil {
		return err
	}

	b := fmt.Sprintf("%016x %016x %s\n", e.Index, e.Term, e.Command)
	computedChecksum := crc32.ChecksumIEEE([]byte(b))
	if computedChecksum != readChecksum {
		return ErrInvalidChecksum
	}

	return nil
}

// consumeUntil does a series of 1-byte Reads from the passed io.Reader
// until it reaches delim, or EOF. This is pretty inefficient.
func consumeUntil(r io.Reader, delim byte, dst *[]byte) error {
	p := make([]byte, 1)
	for {
		n, err := r.Read(p)
		if err != nil {
			return err
		}
		if n != 1 {
			return io.ErrUnexpectedEOF
		}
		if p[0] == delim {
			return nil
		}
		*dst = append(*dst, p[0])
	}
}
<|endoftext|>


<commit_before>package anaconda

import (
	"log"
	"os"
)

// The Logger interface provides optional logging ability for the streaming API.
// It can also be used to log the rate limiting headers if desired.
type Logger interface {
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})

	Panic(args ...interface{})
	Panicf(format string, args ...interface{})

	// Log functions
	Critical(args ...interface{})
	Criticalf(format string, args ...interface{})

	Error(args ...interface{})
	Errorf(format string, args ...interface{})

	Warning(args ...interface{})
	Warningf(format string, args ...interface{})

	Notice(args ...interface{})
	Noticef(format string, args ...interface{})

	Info(args ...interface{})
	Infof(format string, args ...interface{})

	Debug(args ...interface{})
	Debugf(format string, args ...interface{})
}

// SetLogger sets the Logger used by the API client.
// The default logger is silent. BasicLogger will log to STDERR
// using the log package from the standard library.
func (c *TwitterApi) SetLogger(l Logger) {
	c.Log = l
}

type silentLogger struct {
}

func (_ silentLogger) Fatal(_ ...interface{})                 {}
func (_ silentLogger) Fatalf(_ string, _ ...interface{})      {}
func (_ silentLogger) Panic(_ ...interface{})                 {}
func (_ silentLogger) Panicf(_ string, _ ...interface{})      {}
func (_ silentLogger) Critical(_ ...interface{})              {}
func (_ silentLogger) Criticalf(_ string, _ ...interface{})   {}
func (_ silentLogger) Error(_ ...interface{})                 {}
func (_ silentLogger) Errorf(_ string, _ ...interface{})      {}
func (_ silentLogger) Warning(_ ...interface{})               {}
func (_ silentLogger) Warningf(_ string, _ ...interface{})    {}
func (_ silentLogger) Notice(_ ...interface{})                {}
func (_ silentLogger) Noticef(_ string, _ ...interface{})     {}
func (_ silentLogger) Info(_ ...interface{})                  {}
func (_ silentLogger) Infof(_ string, _ ...interface{})       {}
func (_ silentLogger) Debug(_ ...interface{})                 {}
func (_ silentLogger) Debugf(format string, _ ...interface{}) {}

// BasicLogger is the equivalent of using log from the standard
// library to print to STDERR.
var BasicLogger Logger

type basicLogger struct {
	log *log.Logger //func New(out io.Writer, prefix string, flag int) *Logger
}

func init() {
	BasicLogger = &basicLogger{log: log.New(os.Stderr, log.Prefix(), log.LstdFlags)}
}

func (l basicLogger) Fatal(items ...interface{})               { l.log.Fatal(items) }
func (l basicLogger) Fatalf(s string, items ...interface{})    { l.log.Fatalf(s, items) }
func (l basicLogger) Panic(items ...interface{})               { l.log.Panic(items) }
func (l basicLogger) Panicf(s string, items ...interface{})    { l.log.Panicf(s, items) }
func (l basicLogger) Critical(items ...interface{})            { l.log.Print(items) }
func (l basicLogger) Criticalf(s string, items ...interface{}) { l.log.Printf(s, items) }
func (l basicLogger) Error(items ...interface{})               { l.log.Print(items) }
func (l basicLogger) Errorf(s string, items ...interface{})    { l.log.Printf(s, items) }
func (l basicLogger) Warning(items ...interface{})             { l.log.Print(items) }
func (l basicLogger) Warningf(s string, items ...interface{})  { l.log.Printf(s, items) }
func (l basicLogger) Notice(items ...interface{})              { l.log.Print(items) }
func (l basicLogger) Noticef(s string, items ...interface{})   { l.log.Printf(s, items) }
func (l basicLogger) Info(items ...interface{})                { l.log.Print(items) }
func (l basicLogger) Infof(s string, items ...interface{})     { l.log.Printf(s, items) }
func (l basicLogger) Debug(items ...interface{})               { l.log.Print(items) }
func (l basicLogger) Debugf(s string, items ...interface{})    { l.log.Printf(s, items) }
<commit_msg>Added setOutput method<commit_after>package anaconda

import (
	"log"
	"os"
	"io"
)

// The Logger interface provides optional logging ability for the streaming API.
// It can also be used to log the rate limiting headers if desired.
type Logger interface {
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})

	Panic(args ...interface{})
	Panicf(format string, args ...interface{})

	// Log functions
	Critical(args ...interface{})
	Criticalf(format string, args ...interface{})

	Error(args ...interface{})
	Errorf(format string, args ...interface{})

	Warning(args ...interface{})
	Warningf(format string, args ...interface{})

	Notice(args ...interface{})
	Noticef(format string, args ...interface{})

	Info(args ...interface{})
	Infof(format string, args ...interface{})

	Debug(args ...interface{})
	Debugf(format string, args ...interface{})
}

// SetLogger sets the Logger used by the API client.
// The default logger is silent. BasicLogger will log to STDERR
// using the log package from the standard library.
func (c *TwitterApi) SetLogger(l Logger) {
	c.Log = l
}

func (c *TwitterApi) SetOutput(w io.Writer) {
	BasicLogger = &basicLogger{log: log.New(w, log.Prefix(), log.LstdFlags)}
}

type silentLogger struct {
}

func (_ silentLogger) Fatal(_ ...interface{})                 {}
func (_ silentLogger) Fatalf(_ string, _ ...interface{})      {}
func (_ silentLogger) Panic(_ ...interface{})                 {}
func (_ silentLogger) Panicf(_ string, _ ...interface{})      {}
func (_ silentLogger) Critical(_ ...interface{})              {}
func (_ silentLogger) Criticalf(_ string, _ ...interface{})   {}
func (_ silentLogger) Error(_ ...interface{})                 {}
func (_ silentLogger) Errorf(_ string, _ ...interface{})      {}
func (_ silentLogger) Warning(_ ...interface{})               {}
func (_ silentLogger) Warningf(_ string, _ ...interface{})    {}
func (_ silentLogger) Notice(_ ...interface{})                {}
func (_ silentLogger) Noticef(_ string, _ ...interface{})     {}
func (_ silentLogger) Info(_ ...interface{})                  {}
func (_ silentLogger) Infof(_ string, _ ...interface{})       {}
func (_ silentLogger) Debug(_ ...interface{})                 {}
func (_ silentLogger) Debugf(format string, _ ...interface{}) {}

// BasicLogger is the equivalent of using log from the standard
// library to print to STDERR.
var BasicLogger Logger

type basicLogger struct {
	log *log.Logger //func New(out io.Writer, prefix string, flag int) *Logger
}

func init() {
	BasicLogger = &basicLogger{log: log.New(os.Stderr, log.Prefix(), log.LstdFlags)}
}

func (l basicLogger) Fatal(items ...interface{})               { l.log.Fatal(items) }
func (l basicLogger) Fatalf(s string, items ...interface{})    { l.log.Fatalf(s, items) }
func (l basicLogger) Panic(items ...interface{})               { l.log.Panic(items) }
func (l basicLogger) Panicf(s string, items ...interface{})    { l.log.Panicf(s, items) }
func (l basicLogger) Critical(items ...interface{})            { l.log.Print(items) }
func (l basicLogger) Criticalf(s string, items ...interface{}) { l.log.Printf(s, items) }
func (l basicLogger) Error(items ...interface{})               { l.log.Print(items) }
func (l basicLogger) Errorf(s string, items ...interface{})    { l.log.Printf(s, items) }
func (l basicLogger) Warning(items ...interface{})             { l.log.Print(items) }
func (l basicLogger) Warningf(s string, items ...interface{})  { l.log.Printf(s, items) }
func (l basicLogger) Notice(items ...interface{})              { l.log.Print(items) }
func (l basicLogger) Noticef(s string, items ...interface{})   { l.log.Printf(s, items) }
func (l basicLogger) Info(items ...interface{})                { l.log.Print(items) }
func (l basicLogger) Infof(s string, items ...interface{})     { l.log.Printf(s, items) }
func (l basicLogger) Debug(items ...interface{})               { l.log.Print(items) }
func (l basicLogger) Debugf(s string, items ...interface{})    { l.log.Printf(s, items) }
<|endoftext|>


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

// Package log provides an interface for logging diagnostic messages
// independent of output medium.
package log // import "zombiezen.com/go/log"

import (
	"context"
	"time"
)

// Logger is the interface that wraps the Log method.
//
// Log sends an Entry to the underlying log sink.  Each call will be sent
// to the sink in the order that Log was called, but Log may not wait for
// delivery to be acknowledged.  As such, Log should ignore any deadline
// set on the Context.  Log must be safe to call from multiple goroutines.
//
// LogEnabled returns false if Log will no-op for a particular Entry, which
// may or may not have Msg filled in.
type Logger interface {
	Log(context.Context, Entry)
	LogEnabled(Entry) bool
}

// Entry is a single log record.
type Entry struct {
	Msg string

	// Time is the timestamp that the entry was created.
	Time time.Time

	// Level is the verbosity level for the entry. A Logger may skip
	// writing an Entry based on its level.
	Level Level

	// File name and line number of the code that initiated the log as
	// reported by runtime.Caller.
	File string
	Line int
}

// Append appends a formatted entry to a buffer.
// flag controls the formatting of the entry.
// Even if ent.Msg ends in a newline, the last byte appended to buf will
// never be a newline.
func (ent Entry) Append(buf []byte, flag Flags) []byte {
	file, line := ent.File, ent.Line
	if file == "" {
		file, line = "???", 0
	}
	buf = formatHeader(buf, flag, ent.Time, ent.Level, file, line)
	s := ent.Msg
	if n := len(s); n == 0 || s[n-1] == '\n' {
		s = s[:n-1]
	}
	buf = append(buf, s...)
	return buf
}

// String returns a formatted entry string with the default options.
func (ent Entry) String() string {
	return string(ent.Append(nil, StdFlags))
}

func formatHeader(buf []byte, flag Flags, t time.Time, level Level, file string, line int) []byte {
	if flag&UTC != 0 {
		t = t.UTC()
	}
	if flag&(ShowDate|ShowTime|Microseconds) != 0 {
		if flag&ShowDate != 0 {
			year, month, day := t.Date()
			buf = itoa(buf, year, 4)
			buf = append(buf, '/')
			buf = itoa(buf, int(month), 2)
			buf = append(buf, '/')
			buf = itoa(buf, day, 2)
			buf = append(buf, ' ')
		}
		if flag&(ShowTime|Microseconds) != 0 {
			hour, min, sec := t.Clock()
			buf = itoa(buf, hour, 2)
			buf = append(buf, ':')
			buf = itoa(buf, min, 2)
			buf = append(buf, ':')
			buf = itoa(buf, sec, 2)
			if flag&Microseconds != 0 {
				buf = append(buf, '.')
				buf = itoa(buf, t.Nanosecond()/1e3, 6)
			}
			buf = append(buf, ' ')
		}
	}
	if flag&(ShowLevel|ShortFile|ShowFile) != 0 {
		if flag&ShortFile != 0 {
			short := file
			for i := len(file) - 1; i > 0; i-- {
				if file[i] == '/' {
					short = file[i+1:]
					break
				}
			}
			file = short
		}
		switch {
		case flag&ShowLevel != 0 && flag&(ShortFile|ShowFile) == 0:
			buf = append(buf, entryLevel(level)...)
		case flag&ShowLevel == 0 && flag&(ShortFile|ShowFile) != 0:
			buf = append(buf, file...)
			buf = append(buf, ':')
			buf = itoa(buf, line, -1)
		default:
			buf = append(buf, entryLevel(level)...)
			buf = append(buf, ' ')
			buf = append(buf, file...)
			buf = append(buf, ':')
			buf = itoa(buf, line, -1)
		}
		buf = append(buf, ": "...)
	}
	return buf
}

// Cheap integer to fixed-width decimal ASCII.  Give a negative width to avoid zero-padding.
func itoa(buf []byte, i int, wid int) []byte {
	// Assemble decimal in reverse order.
	var b [20]byte
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	// i < 10
	b[bp] = byte('0' + i)
	return append(buf, b[bp:]...)
}

type discard struct{}

func (discard) Log(ctx context.Context, ent Entry) {}

func (discard) LogEnabled(Entry) bool { return false }

// Discard is the no-op Logger.
var Discard Logger = discard{}

// Level is a hint at the audience of the log.
// Lower values of Level mean more verbose.
type Level int

// Predefined logging levels.
const (
	Debug Level = -10 // messages for developers
	Info  Level = 0   // messages for users
	Warn  Level = 10  // warnings for users
	Error Level = 20  // failures
)

// String returns the constant name of the level.
func (l Level) String() string {
	switch l {
	case Debug:
		return "Debug"
	case Info:
		return "Info"
	case Warn:
		return "Warn"
	case Error:
		return "Error"
	}
	var buf []byte
	buf = append(buf, "Level("...)
	buf = itoa(buf, int(l), -1)
	buf = append(buf, ')')
	return string(buf)
}

func entryLevel(l Level) string {
	switch l {
	case Debug:
		return "DEBUG"
	case Info:
		return "INFO"
	case Warn:
		return "WARN"
	case Error:
		return "ERROR"
	default:
		return "???"
	}
}
<commit_msg>log: clarify docs around Logger<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package log provides an interface for logging diagnostic messages
// independent of output medium.
package log // import "zombiezen.com/go/log"

import (
	"context"
	"time"
)

// Logger is the interface that wraps the Log and LogEnabled methods.
// Most people should use Debugf, Infof, Warnf, or Errorf instead of
// interacting with a Logger directly.
//
// Log sends an Entry to the underlying log sink.  Each call will be sent
// to the sink in the order that Log was called, but Log may not wait for
// delivery to be acknowledged.  Further, logs may be sent when a request
// is being shut down.  As such, Log must ignore any deadline set on the
// Context.  Log must be safe to call from multiple goroutines.
//
// LogEnabled returns false if Log will no-op for a particular Entry, which
// may or may not have Msg filled in.
type Logger interface {
	Log(context.Context, Entry)
	LogEnabled(Entry) bool
}

// Entry is a single log record.
type Entry struct {
	Msg string

	// Time is the timestamp that the entry was created.
	Time time.Time

	// Level is the verbosity level for the entry. A Logger may skip
	// writing an Entry based on its level.
	Level Level

	// File name and line number of the code that initiated the log as
	// reported by runtime.Caller.
	File string
	Line int
}

// Append appends a formatted entry to a buffer.
// flag controls the formatting of the entry.
// Even if ent.Msg ends in a newline, the last byte appended to buf will
// never be a newline.
func (ent Entry) Append(buf []byte, flag Flags) []byte {
	file, line := ent.File, ent.Line
	if file == "" {
		file, line = "???", 0
	}
	buf = formatHeader(buf, flag, ent.Time, ent.Level, file, line)
	s := ent.Msg
	if n := len(s); n == 0 || s[n-1] == '\n' {
		s = s[:n-1]
	}
	buf = append(buf, s...)
	return buf
}

// String returns a formatted entry string with the default options.
func (ent Entry) String() string {
	return string(ent.Append(nil, StdFlags))
}

func formatHeader(buf []byte, flag Flags, t time.Time, level Level, file string, line int) []byte {
	if flag&UTC != 0 {
		t = t.UTC()
	}
	if flag&(ShowDate|ShowTime|Microseconds) != 0 {
		if flag&ShowDate != 0 {
			year, month, day := t.Date()
			buf = itoa(buf, year, 4)
			buf = append(buf, '/')
			buf = itoa(buf, int(month), 2)
			buf = append(buf, '/')
			buf = itoa(buf, day, 2)
			buf = append(buf, ' ')
		}
		if flag&(ShowTime|Microseconds) != 0 {
			hour, min, sec := t.Clock()
			buf = itoa(buf, hour, 2)
			buf = append(buf, ':')
			buf = itoa(buf, min, 2)
			buf = append(buf, ':')
			buf = itoa(buf, sec, 2)
			if flag&Microseconds != 0 {
				buf = append(buf, '.')
				buf = itoa(buf, t.Nanosecond()/1e3, 6)
			}
			buf = append(buf, ' ')
		}
	}
	if flag&(ShowLevel|ShortFile|ShowFile) != 0 {
		if flag&ShortFile != 0 {
			short := file
			for i := len(file) - 1; i > 0; i-- {
				if file[i] == '/' {
					short = file[i+1:]
					break
				}
			}
			file = short
		}
		switch {
		case flag&ShowLevel != 0 && flag&(ShortFile|ShowFile) == 0:
			buf = append(buf, entryLevel(level)...)
		case flag&ShowLevel == 0 && flag&(ShortFile|ShowFile) != 0:
			buf = append(buf, file...)
			buf = append(buf, ':')
			buf = itoa(buf, line, -1)
		default:
			buf = append(buf, entryLevel(level)...)
			buf = append(buf, ' ')
			buf = append(buf, file...)
			buf = append(buf, ':')
			buf = itoa(buf, line, -1)
		}
		buf = append(buf, ": "...)
	}
	return buf
}

// Cheap integer to fixed-width decimal ASCII.  Give a negative width to avoid zero-padding.
func itoa(buf []byte, i int, wid int) []byte {
	// Assemble decimal in reverse order.
	var b [20]byte
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	// i < 10
	b[bp] = byte('0' + i)
	return append(buf, b[bp:]...)
}

type discard struct{}

func (discard) Log(ctx context.Context, ent Entry) {}

func (discard) LogEnabled(Entry) bool { return false }

// Discard is the no-op Logger.
var Discard Logger = discard{}

// Level is a hint at the audience of the log.
// Lower values of Level mean more verbose.
type Level int

// Predefined logging levels.
const (
	Debug Level = -10 // messages for developers
	Info  Level = 0   // messages for users
	Warn  Level = 10  // warnings for users
	Error Level = 20  // failures
)

// String returns the constant name of the level.
func (l Level) String() string {
	switch l {
	case Debug:
		return "Debug"
	case Info:
		return "Info"
	case Warn:
		return "Warn"
	case Error:
		return "Error"
	}
	var buf []byte
	buf = append(buf, "Level("...)
	buf = itoa(buf, int(l), -1)
	buf = append(buf, ')')
	return string(buf)
}

func entryLevel(l Level) string {
	switch l {
	case Debug:
		return "DEBUG"
	case Info:
		return "INFO"
	case Warn:
		return "WARN"
	case Error:
		return "ERROR"
	default:
		return "???"
	}
}
<|endoftext|>


<commit_before>package main

type raynode struct {
	Cost int
}

type rayMap map[position]*raynode

func (rm rayMap) get(p position) *raynode {
	r, ok := rm[p]
	if !ok {
		r = &raynode{}
		rm[p] = r
	}
	return r
}

func (g *game) bestParent(rm rayMap, from, pos position) (position, int) {
	p := pos.Parents(from)
	b := p[0]
	if len(p) > 1 && rm[p[1]].Cost+g.losCost(p[1]) < rm[b].Cost+g.losCost(b) {
		b = p[1]
	}
	return b, rm[b].Cost + g.losCost(b)
}

func (g *game) losCost(pos position) int {
	cost := 1
	c := g.Dungeon.Cell(pos)
	if c.T == WallCell {
		cost += 100
	}
	if _, ok := g.Clouds[pos]; ok {
		cost += 25
	}
	if _, ok := g.Fungus[pos]; ok {
		cost += 23
	}
	if _, ok := g.Doors[pos]; ok {
		if pos != g.Player.Pos {
			mons, _ := g.MonsterAt(pos)
			if !mons.Exists() {
				cost += 100
			}
		}
	}
	return cost
}

func (g *game) buildRayMap(from position, distance int) rayMap {
	dungeon := g.Dungeon
	rm := rayMap{}
	rm[from] = &raynode{Cost: 0}
	for d := 1; d <= distance; d++ {
		for x := -d + from.X; x <= d+from.X; x++ {
			for _, pos := range []position{{x, from.Y + d}, {x, from.Y - d}} {
				if !dungeon.Valid(pos) {
					continue
				}
				_, c := g.bestParent(rm, from, pos)
				rm[pos] = &raynode{Cost: c}
			}
		}
		for y := -d + 1 + from.Y; y <= d-1+from.Y; y++ {
			for _, pos := range []position{{from.X + d, y}, {from.X - d, y}} {
				if !dungeon.Valid(pos) {
					continue
				}
				_, c := g.bestParent(rm, from, pos)
				rm[pos] = &raynode{Cost: c}
			}
		}
	}
	return rm
}

func (g *game) LosRange() int {
	losRange := 6
	if g.Player.Aptitudes[AptStealthyLOS] {
		losRange -= 2
	}
	return losRange
}

func (g *game) StopAuto() {
	g.AutoHalt = true
	g.AutoDir = nil
	g.AutoTarget = nil
}

func (g *game) ComputeLOS() {
	m := map[position]bool{}
	losRange := g.LosRange()
	g.Player.Rays = g.buildRayMap(g.Player.Pos, losRange)
	for pos, n := range g.Player.Rays {
		if n.Cost < 50 {
			m[pos] = true
			if !g.Dungeon.Cell(pos).Explored {
				if c, ok := g.Collectables[pos]; ok {
					g.StopAuto()
					if c.Quantity > 1 {
						g.Printf("You see %d %s.", c.Quantity, c.Consumable.Plural())
					} else {
						g.Printf("You see %s.", Indefinite(c.Consumable.String(), false))
					}
				} else if _, ok := g.Stairs[pos]; ok {
					g.StopAuto()
					g.Printf("You see stairs.")
				} else if eq, ok := g.Equipables[pos]; ok {
					g.StopAuto()
					g.Printf("You see %s.", Indefinite(eq.String(), false))
				} else if rod, ok := g.Rods[pos]; ok {
					g.StopAuto()
					g.Printf("You see %s.", Indefinite(rod.String(), false))
				}
				g.FairAction()
			}
			if g.UnknownDig[pos] {
				delete(g.UnknownDig, pos)
				delete(g.TemporalWalls, pos)
			}
			g.Dungeon.SetExplored(pos)
		}
	}
	g.Player.LOS = m
}

func (g *game) ComputeExclusion(pos position, toggle bool) {
	exclusionRange := g.LosRange()
	rays := g.buildRayMap(pos, exclusionRange)
	for pos, n := range rays {
		if n.Cost < 50 {
			g.ExclusionsMap[pos] = toggle
		}
	}
	for d := 1; d <= exclusionRange; d++ {
		for x := -d + pos.X; x <= d+pos.X; x++ {
			for _, pos := range []position{{x, pos.Y + d}, {x, pos.Y - d}} {
				if !g.Dungeon.Valid(pos) {
					continue
				}
				if !g.Player.LOS[pos] {
					g.ExclusionsMap[pos] = toggle
				}
			}
		}
		for y := -d + 1 + pos.Y; y <= d-1+pos.Y; y++ {
			for _, pos := range []position{{pos.X + d, y}, {pos.X - d, y}} {
				if !g.Dungeon.Valid(pos) {
					continue
				}
				if !g.Player.LOS[pos] {
					g.ExclusionsMap[pos] = toggle
				}
			}
		}
	}
}

func (g *game) Ray(pos position) []position {
	if !g.Player.LOS[pos] {
		return nil
	}
	ray := []position{}
	for pos != g.Player.Pos {
		ray = append(ray, pos)
		pos, _ = g.bestParent(g.Player.Rays, g.Player.Pos, pos)
	}
	return ray
}

func (g *game) ComputeRayHighlight(pos position) {
	g.Highlight = map[position]bool{}
	ray := g.Ray(pos)
	for _, p := range ray {
		g.Highlight[p] = true
	}
}
<commit_msg>rework LOS cost function to be perfectly symmetrical<commit_after>package main

type raynode struct {
	Cost int
}

type rayMap map[position]*raynode

func (rm rayMap) get(p position) *raynode {
	r, ok := rm[p]
	if !ok {
		r = &raynode{}
		rm[p] = r
	}
	return r
}

func (g *game) bestParent(rm rayMap, from, pos position) (position, int) {
	p := pos.Parents(from)
	b := p[0]
	if len(p) > 1 && rm[p[1]].Cost+g.losCost(p[1]) < rm[b].Cost+g.losCost(b) {
		b = p[1]
	}
	return b, rm[b].Cost + g.losCost(b)
}

func (g *game) losCost(pos position) int {
	if g.Player.Pos == pos {
		return 0
	}
	c := g.Dungeon.Cell(pos)
	if c.T == WallCell {
		return g.LosRange()
	}
	if _, ok := g.Clouds[pos]; ok {
		return g.LosRange()
	}
	if _, ok := g.Doors[pos]; ok {
		if pos != g.Player.Pos {
			mons, _ := g.MonsterAt(pos)
			if !mons.Exists() {
				return g.LosRange()
			}
		}
	}
	if _, ok := g.Fungus[pos]; ok {
		return g.LosRange() - 1
	}
	return 1
}

func (g *game) buildRayMap(from position, distance int) rayMap {
	dungeon := g.Dungeon
	rm := rayMap{}
	rm[from] = &raynode{Cost: 0}
	for d := 1; d <= distance; d++ {
		for x := -d + from.X; x <= d+from.X; x++ {
			for _, pos := range []position{{x, from.Y + d}, {x, from.Y - d}} {
				if !dungeon.Valid(pos) {
					continue
				}
				_, c := g.bestParent(rm, from, pos)
				rm[pos] = &raynode{Cost: c}
			}
		}
		for y := -d + 1 + from.Y; y <= d-1+from.Y; y++ {
			for _, pos := range []position{{from.X + d, y}, {from.X - d, y}} {
				if !dungeon.Valid(pos) {
					continue
				}
				_, c := g.bestParent(rm, from, pos)
				rm[pos] = &raynode{Cost: c}
			}
		}
	}
	return rm
}

func (g *game) LosRange() int {
	losRange := 6
	if g.Player.Aptitudes[AptStealthyLOS] {
		losRange -= 2
	}
	return losRange
}

func (g *game) StopAuto() {
	g.AutoHalt = true
	g.AutoDir = nil
	g.AutoTarget = nil
}

func (g *game) ComputeLOS() {
	m := map[position]bool{}
	losRange := g.LosRange()
	g.Player.Rays = g.buildRayMap(g.Player.Pos, losRange)
	for pos, n := range g.Player.Rays {
		if n.Cost < g.LosRange() {
			m[pos] = true
			if !g.Dungeon.Cell(pos).Explored {
				if c, ok := g.Collectables[pos]; ok {
					g.StopAuto()
					if c.Quantity > 1 {
						g.Printf("You see %d %s.", c.Quantity, c.Consumable.Plural())
					} else {
						g.Printf("You see %s.", Indefinite(c.Consumable.String(), false))
					}
				} else if _, ok := g.Stairs[pos]; ok {
					g.StopAuto()
					g.Printf("You see stairs.")
				} else if eq, ok := g.Equipables[pos]; ok {
					g.StopAuto()
					g.Printf("You see %s.", Indefinite(eq.String(), false))
				} else if rod, ok := g.Rods[pos]; ok {
					g.StopAuto()
					g.Printf("You see %s.", Indefinite(rod.String(), false))
				}
				g.FairAction()
			}
			if g.UnknownDig[pos] {
				delete(g.UnknownDig, pos)
				delete(g.TemporalWalls, pos)
			}
			g.Dungeon.SetExplored(pos)
		}
	}
	g.Player.LOS = m
}

func (g *game) ComputeExclusion(pos position, toggle bool) {
	exclusionRange := g.LosRange()
	rays := g.buildRayMap(pos, exclusionRange)
	for pos, n := range rays {
		if n.Cost < 50 {
			g.ExclusionsMap[pos] = toggle
		}
	}
	for d := 1; d <= exclusionRange; d++ {
		for x := -d + pos.X; x <= d+pos.X; x++ {
			for _, pos := range []position{{x, pos.Y + d}, {x, pos.Y - d}} {
				if !g.Dungeon.Valid(pos) {
					continue
				}
				if !g.Player.LOS[pos] {
					g.ExclusionsMap[pos] = toggle
				}
			}
		}
		for y := -d + 1 + pos.Y; y <= d-1+pos.Y; y++ {
			for _, pos := range []position{{pos.X + d, y}, {pos.X - d, y}} {
				if !g.Dungeon.Valid(pos) {
					continue
				}
				if !g.Player.LOS[pos] {
					g.ExclusionsMap[pos] = toggle
				}
			}
		}
	}
}

func (g *game) Ray(pos position) []position {
	if !g.Player.LOS[pos] {
		return nil
	}
	ray := []position{}
	for pos != g.Player.Pos {
		ray = append(ray, pos)
		pos, _ = g.bestParent(g.Player.Rays, g.Player.Pos, pos)
	}
	return ray
}

func (g *game) ComputeRayHighlight(pos position) {
	g.Highlight = map[position]bool{}
	ray := g.Ray(pos)
	for _, p := range ray {
		g.Highlight[p] = true
	}
}
<|endoftext|>