<commit_before>package napnap

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
)

// Param is a single URL parameter, consisting of a key and a value.
type Param struct {
	Key   string
	Value string
}

type Context struct {
	NapNap  *NapNap
	Request *http.Request
	Writer  ResponseWriter
	query   url.Values
	params  []Param
	store   map[string]interface{}
}

// NewContext returns a new context instance
func NewContext(napnap *NapNap, req *http.Request, writer ResponseWriter) *Context {
	return &Context{
		NapNap:  napnap,
		Request: req,
		Writer:  writer,
	}
}

// Render returns html format
func (c *Context) Render(code int, viewName string, data interface{}) error {
	c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
	c.Writer.WriteHeader(code)

	t, err := c.NapNap.template.Clone()
	if err != nil {
		return err
	}

	viewPath := path.Join(c.NapNap.templateRootPath, "views", viewName)
	t, err = t.ParseFiles(viewPath)
	if err != nil {
		return err
	}

	t.ExecuteTemplate(c.Writer, viewName, data)
	return nil
}

// String returns string format
func (c *Context) String(code int, s string) (err error) {
	c.Writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
	c.Writer.WriteHeader(code)
	c.Writer.Write([]byte(s))
	return
}

// JSON returns json format
func (c *Context) JSON(code int, i interface{}) (err error) {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}
	c.Writer.Header().Set("Content-Type", "application/json")
	c.Writer.WriteHeader(code)
	c.Writer.Write(b)
	return
}

// Redirect returns a HTTP redirect to the specific location.
func (c *Context) Redirect(code int, location string) error {
	if (code < 300 || code > 308) && code != 201 {
		return fmt.Errorf("Cannot redirect with status code %d", code)
	}
	http.Redirect(c.Writer, c.Request, location, code)
	return nil
}

// BindJSON binds the request body into provided type `obj`. The default binder does
// it based on Content-Type header.
func (c *Context) BindJSON(obj interface{}) error {
	req := c.Request
	decoder := json.NewDecoder(req.Body)
	err := decoder.Decode(obj)
	if err != nil {
		return err
	}
	return nil
}

// Query returns query parameter by key.
func (c *Context) Query(key string) string {
	if c.query == nil {
		c.query = c.Request.URL.Query()
	}
	return c.query.Get(key)
}

// QueryInt returns query parameter by key and cast the value to int.
func (c *Context) QueryInt(key string) (int, error) {
	return strconv.Atoi(c.Query(key))
}

// QueryIntWithDefault returns query parameter by key and cast the value to int.  If the value doesn't exist, the default value will be used.
func (c *Context) QueryIntWithDefault(key string, defaultValue int) (int, error) {
	data := c.Query(key)
	if len(data) > 0 {
		return strconv.Atoi(c.Query(key))
	}
	return defaultValue, nil
}

// Form returns form parameter by key.
func (c *Context) Form(key string) string {
	req := c.Request
	if s := req.PostFormValue(key); len(s) > 0 {
		return s
	}
	if req.MultipartForm != nil {
		if values := req.MultipartForm.Value[key]; len(values) > 0 {
			return values[0]
		}
	}
	return ""
}

// FormFile returns file.
func (c *Context) FormFile(key string) (*multipart.FileHeader, error) {
	_, fh, err := c.Request.FormFile(key)
	return fh, err
}

// SaveUploadedFile uploads the form file to specific dst.
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
	src, err := file.Open()
	if err != nil {
		return err
	}
	defer src.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	io.Copy(out, src)
	return nil
}

// Get retrieves data from the context.
func (c *Context) Get(key string) (interface{}, bool) {
	var value interface{}
	var exists bool
	if c.store != nil {
		value, exists = c.store[key]
	}
	return value, exists
}

// MustGet returns the value for the given key if it exists, otherwise it panics.
func (c *Context) MustGet(key string) interface{} {
	if value, exists := c.Get(key); exists {
		return value
	}
	panic("Key \"" + key + "\" does not exist")
}

// Set saves data in the context.
// It also lazy initializes  c.Keys if it was not used previously.
func (c *Context) Set(key string, val interface{}) {
	if c.store == nil {
		c.store = make(map[string]interface{})
	}
	c.store[key] = val
}

// Param returns form values by parameter
func (c *Context) Param(name string) string {
	for _, param := range c.params {
		if param.Key == name {
			return param.Value
		}
	}
	return ""
}

// ParamInt returns parameter by key and cast the value to int.
func (c *Context) ParamInt(key string) (int, error) {
	return strconv.Atoi(c.Param(key))
}

// ClientIP implements a best effort algorithm to return the real client IP, it parses
// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
// Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.
func (c *Context) ClientIP() string {
	if c.NapNap.ForwardedByClientIP {
		clientIP := c.RequestHeader("X-Forwarded-For")
		if index := strings.IndexByte(clientIP, ','); index >= 0 {
			clientIP = clientIP[0:index]
		}
		clientIP = strings.TrimSpace(clientIP)
		if len(clientIP) > 0 {
			return clientIP
		}
		clientIP = strings.TrimSpace(c.RequestHeader("X-Real-Ip"))
		if len(clientIP) > 0 {
			return clientIP
		}
		clientIP = strings.TrimSpace(clientIP)
		if len(clientIP) > 0 {
			return clientIP
		}
	}
	if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil {
		return ip
	}
	return ""
}

// ContentType returns the Content-Type header of the request.
func (c *Context) ContentType() string {
	return filterFlags(c.Request.Header.Get("Content-Type"))
}

// SetCookie allows us to create an cookie
func (c *Context) SetCookie(
	name string,
	value string,
	maxAge int,
	path string,
	domain string,
	secure bool,
	httpOnly bool,
) {
	if path == "" {
		path = "/"
	}
	http.SetCookie(c.Writer, &http.Cookie{
		Name:     name,
		Value:    url.QueryEscape(value),
		MaxAge:   maxAge,
		Path:     path,
		Domain:   domain,
		Secure:   secure,
		HttpOnly: httpOnly,
	})
}

// Cookie returns cookie value
func (c *Context) Cookie(name string) (string, error) {
	cookie, err := c.Request.Cookie(name)
	if err != nil {
		return "", err
	}
	val, _ := url.QueryUnescape(cookie.Value)
	return val, nil
}

// SetStatus is a intelligent shortcut for c.Writer.WriteHeader(code)
func (c *Context) SetStatus(code int) {
	c.Writer.WriteHeader(code)
}

// Status is a intelligent shortcut for c.Writer.Status()
func (c *Context) Status() int {
	return c.Writer.Status()
}

// RespHeader is a intelligent shortcut for c.Writer.Header().Set(key, value)
// It writes a header in the response.
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
func (c *Context) RespHeader(key, value string) {
	if len(value) == 0 {
		c.Writer.Header().Del(key)
	} else {
		c.Writer.Header().Set(key, value)
	}
}

// RequestHeader is a intelligent shortcut for c.Request.Header.Get(key)
func (c *Context) RequestHeader(key string) string {
	return c.Request.Header.Get(key)
}

// StdContext return golang standard context
func (c *Context) StdContext() context.Context {
	ctx := c.Request.Context()
	ctx = newGContext(ctx, c)
	return ctx
}

// SetStdContext allow us to save the golang context to request
func (c *Context) SetStdContext(ctx context.Context) {
	c.Request = c.Request.WithContext(ctx)
}

// DeviceType returns user's device type which includes web, mobile, tab, tv
func (c *Context) DeviceType() string {
	userAgent := c.RequestHeader("User-Agent")
	deviceType := "web"

	if strings.Contains(userAgent, "Android") ||
		strings.Contains(userAgent, "webOS") ||
		strings.Contains(userAgent, "iPhone") ||
		strings.Contains(userAgent, "BlackBerry") ||
		strings.Contains(userAgent, "Windows Phone") {
		deviceType = "mobile"
	} else if strings.Contains(userAgent, "iPad") ||
		strings.Contains(userAgent, "iPod") ||
		(strings.Contains(userAgent, "tablet") ||
			strings.Contains(userAgent, "RX-34") ||
			strings.Contains(userAgent, "FOLIO")) ||
		(strings.Contains(userAgent, "Kindle") ||
			strings.Contains(userAgent, "Mac OS") &&
				strings.Contains(userAgent, "Silk")) ||
		(strings.Contains(userAgent, "AppleWebKit") &&
			strings.Contains(userAgent, "Silk")) {
		deviceType = "tab"
	} else if strings.Contains(userAgent, "TV") ||
		strings.Contains(userAgent, "NetCast") ||
		strings.Contains(userAgent, "boxee") ||
		strings.Contains(userAgent, "Kylo") ||
		strings.Contains(userAgent, "Roku") ||
		strings.Contains(userAgent, "DLNADOC") {
		deviceType = "tv"
	}
	return deviceType
}

func (c *Context) reset(w http.ResponseWriter, req *http.Request) {
	c.Request = req
	c.Writer = c.Writer.reset(w)
	c.store = nil
	c.query = nil
	c.params = nil
}
<commit_msg>change json charset to charset=utf-8<commit_after>package napnap

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
)

// Param is a single URL parameter, consisting of a key and a value.
type Param struct {
	Key   string
	Value string
}

type Context struct {
	NapNap  *NapNap
	Request *http.Request
	Writer  ResponseWriter
	query   url.Values
	params  []Param
	store   map[string]interface{}
}

// NewContext returns a new context instance
func NewContext(napnap *NapNap, req *http.Request, writer ResponseWriter) *Context {
	return &Context{
		NapNap:  napnap,
		Request: req,
		Writer:  writer,
	}
}

// Render returns html format
func (c *Context) Render(code int, viewName string, data interface{}) error {
	c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
	c.Writer.WriteHeader(code)

	t, err := c.NapNap.template.Clone()
	if err != nil {
		return err
	}

	viewPath := path.Join(c.NapNap.templateRootPath, "views", viewName)
	t, err = t.ParseFiles(viewPath)
	if err != nil {
		return err
	}

	t.ExecuteTemplate(c.Writer, viewName, data)
	return nil
}

// String returns string format
func (c *Context) String(code int, s string) (err error) {
	c.Writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
	c.Writer.WriteHeader(code)
	c.Writer.Write([]byte(s))
	return
}

// JSON returns json format
func (c *Context) JSON(code int, i interface{}) (err error) {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}
	c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
	c.Writer.WriteHeader(code)
	c.Writer.Write(b)
	return
}

// Redirect returns a HTTP redirect to the specific location.
func (c *Context) Redirect(code int, location string) error {
	if (code < 300 || code > 308) && code != 201 {
		return fmt.Errorf("Cannot redirect with status code %d", code)
	}
	http.Redirect(c.Writer, c.Request, location, code)
	return nil
}

// BindJSON binds the request body into provided type `obj`. The default binder does
// it based on Content-Type header.
func (c *Context) BindJSON(obj interface{}) error {
	req := c.Request
	decoder := json.NewDecoder(req.Body)
	err := decoder.Decode(obj)
	if err != nil {
		return err
	}
	return nil
}

// Query returns query parameter by key.
func (c *Context) Query(key string) string {
	if c.query == nil {
		c.query = c.Request.URL.Query()
	}
	return c.query.Get(key)
}

// QueryInt returns query parameter by key and cast the value to int.
func (c *Context) QueryInt(key string) (int, error) {
	return strconv.Atoi(c.Query(key))
}

// QueryIntWithDefault returns query parameter by key and cast the value to int.  If the value doesn't exist, the default value will be used.
func (c *Context) QueryIntWithDefault(key string, defaultValue int) (int, error) {
	data := c.Query(key)
	if len(data) > 0 {
		return strconv.Atoi(c.Query(key))
	}
	return defaultValue, nil
}

// Form returns form parameter by key.
func (c *Context) Form(key string) string {
	req := c.Request
	if s := req.PostFormValue(key); len(s) > 0 {
		return s
	}
	if req.MultipartForm != nil {
		if values := req.MultipartForm.Value[key]; len(values) > 0 {
			return values[0]
		}
	}
	return ""
}

// FormFile returns file.
func (c *Context) FormFile(key string) (*multipart.FileHeader, error) {
	_, fh, err := c.Request.FormFile(key)
	return fh, err
}

// SaveUploadedFile uploads the form file to specific dst.
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
	src, err := file.Open()
	if err != nil {
		return err
	}
	defer src.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	io.Copy(out, src)
	return nil
}

// Get retrieves data from the context.
func (c *Context) Get(key string) (interface{}, bool) {
	var value interface{}
	var exists bool
	if c.store != nil {
		value, exists = c.store[key]
	}
	return value, exists
}

// MustGet returns the value for the given key if it exists, otherwise it panics.
func (c *Context) MustGet(key string) interface{} {
	if value, exists := c.Get(key); exists {
		return value
	}
	panic("Key \"" + key + "\" does not exist")
}

// Set saves data in the context.
// It also lazy initializes  c.Keys if it was not used previously.
func (c *Context) Set(key string, val interface{}) {
	if c.store == nil {
		c.store = make(map[string]interface{})
	}
	c.store[key] = val
}

// Param returns form values by parameter
func (c *Context) Param(name string) string {
	for _, param := range c.params {
		if param.Key == name {
			return param.Value
		}
	}
	return ""
}

// ParamInt returns parameter by key and cast the value to int.
func (c *Context) ParamInt(key string) (int, error) {
	return strconv.Atoi(c.Param(key))
}

// ClientIP implements a best effort algorithm to return the real client IP, it parses
// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
// Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.
func (c *Context) ClientIP() string {
	if c.NapNap.ForwardedByClientIP {
		clientIP := c.RequestHeader("X-Forwarded-For")
		if index := strings.IndexByte(clientIP, ','); index >= 0 {
			clientIP = clientIP[0:index]
		}
		clientIP = strings.TrimSpace(clientIP)
		if len(clientIP) > 0 {
			return clientIP
		}
		clientIP = strings.TrimSpace(c.RequestHeader("X-Real-Ip"))
		if len(clientIP) > 0 {
			return clientIP
		}
		clientIP = strings.TrimSpace(clientIP)
		if len(clientIP) > 0 {
			return clientIP
		}
	}
	if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil {
		return ip
	}
	return ""
}

// ContentType returns the Content-Type header of the request.
func (c *Context) ContentType() string {
	return filterFlags(c.Request.Header.Get("Content-Type"))
}

// SetCookie allows us to create an cookie
func (c *Context) SetCookie(
	name string,
	value string,
	maxAge int,
	path string,
	domain string,
	secure bool,
	httpOnly bool,
) {
	if path == "" {
		path = "/"
	}
	http.SetCookie(c.Writer, &http.Cookie{
		Name:     name,
		Value:    url.QueryEscape(value),
		MaxAge:   maxAge,
		Path:     path,
		Domain:   domain,
		Secure:   secure,
		HttpOnly: httpOnly,
	})
}

// Cookie returns cookie value
func (c *Context) Cookie(name string) (string, error) {
	cookie, err := c.Request.Cookie(name)
	if err != nil {
		return "", err
	}
	val, _ := url.QueryUnescape(cookie.Value)
	return val, nil
}

// SetStatus is a intelligent shortcut for c.Writer.WriteHeader(code)
func (c *Context) SetStatus(code int) {
	c.Writer.WriteHeader(code)
}

// Status is a intelligent shortcut for c.Writer.Status()
func (c *Context) Status() int {
	return c.Writer.Status()
}

// RespHeader is a intelligent shortcut for c.Writer.Header().Set(key, value)
// It writes a header in the response.
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
func (c *Context) RespHeader(key, value string) {
	if len(value) == 0 {
		c.Writer.Header().Del(key)
	} else {
		c.Writer.Header().Set(key, value)
	}
}

// RequestHeader is a intelligent shortcut for c.Request.Header.Get(key)
func (c *Context) RequestHeader(key string) string {
	return c.Request.Header.Get(key)
}

// StdContext return golang standard context
func (c *Context) StdContext() context.Context {
	ctx := c.Request.Context()
	ctx = newGContext(ctx, c)
	return ctx
}

// SetStdContext allow us to save the golang context to request
func (c *Context) SetStdContext(ctx context.Context) {
	c.Request = c.Request.WithContext(ctx)
}

// DeviceType returns user's device type which includes web, mobile, tab, tv
func (c *Context) DeviceType() string {
	userAgent := c.RequestHeader("User-Agent")
	deviceType := "web"

	if strings.Contains(userAgent, "Android") ||
		strings.Contains(userAgent, "webOS") ||
		strings.Contains(userAgent, "iPhone") ||
		strings.Contains(userAgent, "BlackBerry") ||
		strings.Contains(userAgent, "Windows Phone") {
		deviceType = "mobile"
	} else if strings.Contains(userAgent, "iPad") ||
		strings.Contains(userAgent, "iPod") ||
		(strings.Contains(userAgent, "tablet") ||
			strings.Contains(userAgent, "RX-34") ||
			strings.Contains(userAgent, "FOLIO")) ||
		(strings.Contains(userAgent, "Kindle") ||
			strings.Contains(userAgent, "Mac OS") &&
				strings.Contains(userAgent, "Silk")) ||
		(strings.Contains(userAgent, "AppleWebKit") &&
			strings.Contains(userAgent, "Silk")) {
		deviceType = "tab"
	} else if strings.Contains(userAgent, "TV") ||
		strings.Contains(userAgent, "NetCast") ||
		strings.Contains(userAgent, "boxee") ||
		strings.Contains(userAgent, "Kylo") ||
		strings.Contains(userAgent, "Roku") ||
		strings.Contains(userAgent, "DLNADOC") {
		deviceType = "tv"
	}
	return deviceType
}

func (c *Context) reset(w http.ResponseWriter, req *http.Request) {
	c.Request = req
	c.Writer = c.Writer.reset(w)
	c.store = nil
	c.query = nil
	c.params = nil
}
<|endoftext|>


<commit_before>package srtp

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha1" // #nosec
	"encoding/binary"

	"github.com/pkg/errors"
)

// ProtectionProfile specifies Cipher and AuthTag details, similar to TLS cipher suite
type ProtectionProfile uint16

// Supported protection profiles
const (
	ProtectionProfileAes128CmHmacSha1_80 ProtectionProfile = 0x0001
)

const (
	labelSRTPEncryption        = 0x00
	labelSRTPAuthenticationTag = 0x01
	labelSRTPSalt              = 0x02

	labelSRTCPEncryption        = 0x03
	labelSRTCPAuthenticationTag = 0x04
	labelSRTCPSalt              = 0x05

	keyLen  = 16
	saltLen = 14

	maxROCDisorder    = 100
	maxSequenceNumber = 65535

	authTagSize    = 10
	srtcpIndexSize = 4
)

// Encode/Decode state for a single SSRC
type ssrcState struct {
	ssrc                 uint32
	rolloverCounter      uint32
	rolloverHasProcessed bool
	lastSequenceNumber   uint16
}

// Context represents a SRTP cryptographic context
// Context can only be used for one-way operations
// it must either used ONLY for encryption or ONLY for decryption
type Context struct {
	masterKey  []byte
	masterSalt []byte

	ssrcStates         map[uint32]*ssrcState
	srtpSessionKey     []byte
	srtpSessionSalt    []byte
	srtpSessionAuthTag []byte
	srtpBlock          cipher.Block

	srtcpSessionKey     []byte
	srtcpSessionSalt    []byte
	srtcpSessionAuthTag []byte
	srtcpIndex          uint32
	srtcpBlock          cipher.Block
}

// CreateContext creates a new SRTP Context
func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile) (c *Context, err error) {
	if masterKeyLen := len(masterKey); masterKeyLen != keyLen {
		return c, errors.Errorf("SRTP Master Key must be len %d, got %d", masterKey, keyLen)
	} else if masterSaltLen := len(masterSalt); masterSaltLen != saltLen {
		return c, errors.Errorf("SRTP Salt must be len %d, got %d", saltLen, masterSaltLen)
	}

	c = &Context{
		masterKey:  masterKey,
		masterSalt: masterSalt,
		ssrcStates: map[uint32]*ssrcState{},
	}

	if c.srtpSessionKey, err = c.generateSessionKey(labelSRTPEncryption); err != nil {
		return nil, err
	} else if c.srtpSessionSalt, err = c.generateSessionSalt(labelSRTPSalt); err != nil {
		return nil, err
	} else if c.srtpSessionAuthTag, err = c.generateSessionAuthTag(labelSRTPAuthenticationTag); err != nil {
		return nil, err
	} else if c.srtpBlock, err = aes.NewCipher(c.srtpSessionKey); err != nil {
		return nil, err
	}

	if c.srtcpSessionKey, err = c.generateSessionKey(labelSRTCPEncryption); err != nil {
		return nil, err
	} else if c.srtcpSessionSalt, err = c.generateSessionSalt(labelSRTCPSalt); err != nil {
		return nil, err
	} else if c.srtcpSessionAuthTag, err = c.generateSessionAuthTag(labelSRTCPAuthenticationTag); err != nil {
		return nil, err
	} else if c.srtcpBlock, err = aes.NewCipher(c.srtcpSessionKey); err != nil {
		return nil, err
	}

	return c, nil
}

func (c *Context) generateSessionKey(label byte) ([]byte, error) {
	// https://tools.ietf.org/html/rfc3711#appendix-B.3
	// The input block for AES-CM is generated by exclusive-oring the master salt with the
	// concatenation of the encryption key label 0x00 with (index DIV kdr),
	// - index is 'rollover count' and DIV is 'divided by'
	sessionKey := make([]byte, len(c.masterSalt))
	copy(sessionKey, c.masterSalt)

	labelAndIndexOverKdr := []byte{label, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	for i, j := len(labelAndIndexOverKdr)-1, len(sessionKey)-1; i >= 0; i, j = i-1, j-1 {
		sessionKey[j] = sessionKey[j] ^ labelAndIndexOverKdr[i]
	}

	// then padding on the right with two null octets (which implements the multiply-by-2^16 operation, see Section 4.3.3).
	sessionKey = append(sessionKey, []byte{0x00, 0x00}...)

	//The resulting value is then AES-CM- encrypted using the master key to get the cipher key.
	block, err := aes.NewCipher(c.masterKey)
	if err != nil {
		return nil, err
	}

	block.Encrypt(sessionKey, sessionKey)
	return sessionKey, nil
}

func (c *Context) generateSessionSalt(label byte) ([]byte, error) {
	// https://tools.ietf.org/html/rfc3711#appendix-B.3
	// The input block for AES-CM is generated by exclusive-oring the master salt with
	// the concatenation of the encryption salt label
	sessionSalt := make([]byte, len(c.masterSalt))
	copy(sessionSalt, c.masterSalt)

	labelAndIndexOverKdr := []byte{label, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	for i, j := len(labelAndIndexOverKdr)-1, len(sessionSalt)-1; i >= 0; i, j = i-1, j-1 {
		sessionSalt[j] = sessionSalt[j] ^ labelAndIndexOverKdr[i]
	}

	// That value is padded and encrypted as above.
	sessionSalt = append(sessionSalt, []byte{0x00, 0x00}...)
	block, err := aes.NewCipher(c.masterKey)
	if err != nil {
		return nil, err
	}

	block.Encrypt(sessionSalt, sessionSalt)
	return sessionSalt[0:saltLen], nil
}
func (c *Context) generateSessionAuthTag(label byte) ([]byte, error) {
	// https://tools.ietf.org/html/rfc3711#appendix-B.3
	// We now show how the auth key is generated.  The input block for AES-
	// CM is generated as above, but using the authentication key label.
	sessionAuthTag := make([]byte, len(c.masterSalt))
	copy(sessionAuthTag, c.masterSalt)

	labelAndIndexOverKdr := []byte{label, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	for i, j := len(labelAndIndexOverKdr)-1, len(sessionAuthTag)-1; i >= 0; i, j = i-1, j-1 {
		sessionAuthTag[j] = sessionAuthTag[j] ^ labelAndIndexOverKdr[i]
	}

	// That value is padded and encrypted as above.
	// - We need to do multiple runs at key size (20) is larger then source
	firstRun := append(sessionAuthTag, []byte{0x00, 0x00}...)
	secondRun := append(sessionAuthTag, []byte{0x00, 0x01}...)
	block, err := aes.NewCipher(c.masterKey)
	if err != nil {
		return nil, err
	}

	block.Encrypt(firstRun, firstRun)
	block.Encrypt(secondRun, secondRun)
	return append(firstRun, secondRun[:4]...), nil
}

// Generate IV https://tools.ietf.org/html/rfc3711#section-4.1.1
// where the 128-bit integer value IV SHALL be defined by the SSRC, the
// SRTP packet index i, and the SRTP session salting key k_s, as below.
// - ROC = a 32-bit unsigned rollover counter (ROC), which records how many
// -       times the 16-bit RTP sequence number has been reset to zero after
// -       passing through 65,535
// i = 2^16 * ROC + SEQ
// IV = (salt*2 ^ 16) | (ssrc*2 ^ 64) | (i*2 ^ 16)
func (c *Context) generateCounter(sequenceNumber uint16, rolloverCounter uint32, ssrc uint32, sessionSalt []byte) []byte {
	counter := make([]byte, 16)

	binary.BigEndian.PutUint32(counter[4:], ssrc)
	binary.BigEndian.PutUint32(counter[8:], rolloverCounter)
	binary.BigEndian.PutUint32(counter[12:], uint32(sequenceNumber)<<16)

	for i := range sessionSalt {
		counter[i] = counter[i] ^ sessionSalt[i]
	}

	return counter
}

func (c *Context) generateAuthTag(buf, sessionAuthTag []byte) ([]byte, error) {
	// https://tools.ietf.org/html/rfc3711#section-4.2
	// In the case of SRTP, M SHALL consist of the Authenticated
	// Portion of the packet (as specified in Figure 1) concatenated with
	// the ROC, M = Authenticated Portion || ROC;
	//
	// The pre-defined authentication transform for SRTP is HMAC-SHA1
	// [RFC2104].  With HMAC-SHA1, the SRTP_PREFIX_LENGTH (Figure 3) SHALL
	// be 0.  For SRTP (respectively SRTCP), the HMAC SHALL be applied to
	// the session authentication key and M as specified above, i.e.,
	// HMAC(k_a, M).  The HMAC output SHALL then be truncated to the n_tag
	// left-most bits.
	// - Authenticated portion of the packet is everything BEFORE MKI
	// - k_a is the session message authentication key
	// - n_tag is the bit-length of the output authentication tag
	// - ROC is already added by caller (to allow RTP + RTCP support)
	mac := hmac.New(sha1.New, sessionAuthTag)

	if _, err := mac.Write(buf); err != nil {
		return nil, err
	}

	return mac.Sum(nil)[0:10], nil
}

func (c *Context) verifyAuthTag(buf, actualAuthTag []byte) (bool, error) {
	expectedAuthTag, err := c.generateAuthTag(buf, c.srtpSessionAuthTag)
	if err != nil {
		return false, err
	}
	return bytes.Equal(actualAuthTag, expectedAuthTag), nil
}

// https://tools.ietf.org/html/rfc3550#appendix-A.1
func (c *Context) updateRolloverCount(sequenceNumber uint16, s *ssrcState) {
	if !s.rolloverHasProcessed {
		s.rolloverHasProcessed = true
	} else if sequenceNumber == 0 { // We exactly hit the rollover count

		// Only update rolloverCounter if lastSequenceNumber is greater then maxROCDisorder
		// otherwise we already incremented for disorder
		if s.lastSequenceNumber > maxROCDisorder {
			s.rolloverCounter++
		}
	} else if s.lastSequenceNumber < maxROCDisorder && sequenceNumber > (maxSequenceNumber-maxROCDisorder) {
		// Our last sequence number incremented because we crossed 0, but then our current number was within maxROCDisorder of the max
		// So we fell behind, drop to account for jitter
		s.rolloverCounter--
	} else if sequenceNumber < maxROCDisorder && s.lastSequenceNumber > (maxSequenceNumber-maxROCDisorder) {
		// our current is within a maxROCDisorder of 0
		// and our last sequence number was a high sequence number, increment to account for jitter
		s.rolloverCounter++
	}
	s.lastSequenceNumber = sequenceNumber
}

func (c *Context) getSSRCState(ssrc uint32) *ssrcState {
	s, ok := c.ssrcStates[ssrc]
	if ok {
		return s
	}

	s = &ssrcState{ssrc: ssrc}
	c.ssrcStates[ssrc] = s
	return s
}
<commit_msg>Constant time comparison for auth verification<commit_after>package srtp

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha1" // #nosec
	"crypto/subtle"
	"encoding/binary"

	"github.com/pkg/errors"
)

// ProtectionProfile specifies Cipher and AuthTag details, similar to TLS cipher suite
type ProtectionProfile uint16

// Supported protection profiles
const (
	ProtectionProfileAes128CmHmacSha1_80 ProtectionProfile = 0x0001
)

const (
	labelSRTPEncryption        = 0x00
	labelSRTPAuthenticationTag = 0x01
	labelSRTPSalt              = 0x02

	labelSRTCPEncryption        = 0x03
	labelSRTCPAuthenticationTag = 0x04
	labelSRTCPSalt              = 0x05

	keyLen  = 16
	saltLen = 14

	maxROCDisorder    = 100
	maxSequenceNumber = 65535

	authTagSize    = 10
	srtcpIndexSize = 4
)

// Encode/Decode state for a single SSRC
type ssrcState struct {
	ssrc                 uint32
	rolloverCounter      uint32
	rolloverHasProcessed bool
	lastSequenceNumber   uint16
}

// Context represents a SRTP cryptographic context
// Context can only be used for one-way operations
// it must either used ONLY for encryption or ONLY for decryption
type Context struct {
	masterKey  []byte
	masterSalt []byte

	ssrcStates         map[uint32]*ssrcState
	srtpSessionKey     []byte
	srtpSessionSalt    []byte
	srtpSessionAuthTag []byte
	srtpBlock          cipher.Block

	srtcpSessionKey     []byte
	srtcpSessionSalt    []byte
	srtcpSessionAuthTag []byte
	srtcpIndex          uint32
	srtcpBlock          cipher.Block
}

// CreateContext creates a new SRTP Context
func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile) (c *Context, err error) {
	if masterKeyLen := len(masterKey); masterKeyLen != keyLen {
		return c, errors.Errorf("SRTP Master Key must be len %d, got %d", masterKey, keyLen)
	} else if masterSaltLen := len(masterSalt); masterSaltLen != saltLen {
		return c, errors.Errorf("SRTP Salt must be len %d, got %d", saltLen, masterSaltLen)
	}

	c = &Context{
		masterKey:  masterKey,
		masterSalt: masterSalt,
		ssrcStates: map[uint32]*ssrcState{},
	}

	if c.srtpSessionKey, err = c.generateSessionKey(labelSRTPEncryption); err != nil {
		return nil, err
	} else if c.srtpSessionSalt, err = c.generateSessionSalt(labelSRTPSalt); err != nil {
		return nil, err
	} else if c.srtpSessionAuthTag, err = c.generateSessionAuthTag(labelSRTPAuthenticationTag); err != nil {
		return nil, err
	} else if c.srtpBlock, err = aes.NewCipher(c.srtpSessionKey); err != nil {
		return nil, err
	}

	if c.srtcpSessionKey, err = c.generateSessionKey(labelSRTCPEncryption); err != nil {
		return nil, err
	} else if c.srtcpSessionSalt, err = c.generateSessionSalt(labelSRTCPSalt); err != nil {
		return nil, err
	} else if c.srtcpSessionAuthTag, err = c.generateSessionAuthTag(labelSRTCPAuthenticationTag); err != nil {
		return nil, err
	} else if c.srtcpBlock, err = aes.NewCipher(c.srtcpSessionKey); err != nil {
		return nil, err
	}

	return c, nil
}

func (c *Context) generateSessionKey(label byte) ([]byte, error) {
	// https://tools.ietf.org/html/rfc3711#appendix-B.3
	// The input block for AES-CM is generated by exclusive-oring the master salt with the
	// concatenation of the encryption key label 0x00 with (index DIV kdr),
	// - index is 'rollover count' and DIV is 'divided by'
	sessionKey := make([]byte, len(c.masterSalt))
	copy(sessionKey, c.masterSalt)

	labelAndIndexOverKdr := []byte{label, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	for i, j := len(labelAndIndexOverKdr)-1, len(sessionKey)-1; i >= 0; i, j = i-1, j-1 {
		sessionKey[j] = sessionKey[j] ^ labelAndIndexOverKdr[i]
	}

	// then padding on the right with two null octets (which implements the multiply-by-2^16 operation, see Section 4.3.3).
	sessionKey = append(sessionKey, []byte{0x00, 0x00}...)

	//The resulting value is then AES-CM- encrypted using the master key to get the cipher key.
	block, err := aes.NewCipher(c.masterKey)
	if err != nil {
		return nil, err
	}

	block.Encrypt(sessionKey, sessionKey)
	return sessionKey, nil
}

func (c *Context) generateSessionSalt(label byte) ([]byte, error) {
	// https://tools.ietf.org/html/rfc3711#appendix-B.3
	// The input block for AES-CM is generated by exclusive-oring the master salt with
	// the concatenation of the encryption salt label
	sessionSalt := make([]byte, len(c.masterSalt))
	copy(sessionSalt, c.masterSalt)

	labelAndIndexOverKdr := []byte{label, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	for i, j := len(labelAndIndexOverKdr)-1, len(sessionSalt)-1; i >= 0; i, j = i-1, j-1 {
		sessionSalt[j] = sessionSalt[j] ^ labelAndIndexOverKdr[i]
	}

	// That value is padded and encrypted as above.
	sessionSalt = append(sessionSalt, []byte{0x00, 0x00}...)
	block, err := aes.NewCipher(c.masterKey)
	if err != nil {
		return nil, err
	}

	block.Encrypt(sessionSalt, sessionSalt)
	return sessionSalt[0:saltLen], nil
}
func (c *Context) generateSessionAuthTag(label byte) ([]byte, error) {
	// https://tools.ietf.org/html/rfc3711#appendix-B.3
	// We now show how the auth key is generated.  The input block for AES-
	// CM is generated as above, but using the authentication key label.
	sessionAuthTag := make([]byte, len(c.masterSalt))
	copy(sessionAuthTag, c.masterSalt)

	labelAndIndexOverKdr := []byte{label, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	for i, j := len(labelAndIndexOverKdr)-1, len(sessionAuthTag)-1; i >= 0; i, j = i-1, j-1 {
		sessionAuthTag[j] = sessionAuthTag[j] ^ labelAndIndexOverKdr[i]
	}

	// That value is padded and encrypted as above.
	// - We need to do multiple runs at key size (20) is larger then source
	firstRun := append(sessionAuthTag, []byte{0x00, 0x00}...)
	secondRun := append(sessionAuthTag, []byte{0x00, 0x01}...)
	block, err := aes.NewCipher(c.masterKey)
	if err != nil {
		return nil, err
	}

	block.Encrypt(firstRun, firstRun)
	block.Encrypt(secondRun, secondRun)
	return append(firstRun, secondRun[:4]...), nil
}

// Generate IV https://tools.ietf.org/html/rfc3711#section-4.1.1
// where the 128-bit integer value IV SHALL be defined by the SSRC, the
// SRTP packet index i, and the SRTP session salting key k_s, as below.
// - ROC = a 32-bit unsigned rollover counter (ROC), which records how many
// -       times the 16-bit RTP sequence number has been reset to zero after
// -       passing through 65,535
// i = 2^16 * ROC + SEQ
// IV = (salt*2 ^ 16) | (ssrc*2 ^ 64) | (i*2 ^ 16)
func (c *Context) generateCounter(sequenceNumber uint16, rolloverCounter uint32, ssrc uint32, sessionSalt []byte) []byte {
	counter := make([]byte, 16)

	binary.BigEndian.PutUint32(counter[4:], ssrc)
	binary.BigEndian.PutUint32(counter[8:], rolloverCounter)
	binary.BigEndian.PutUint32(counter[12:], uint32(sequenceNumber)<<16)

	for i := range sessionSalt {
		counter[i] = counter[i] ^ sessionSalt[i]
	}

	return counter
}

func (c *Context) generateAuthTag(buf, sessionAuthTag []byte) ([]byte, error) {
	// https://tools.ietf.org/html/rfc3711#section-4.2
	// In the case of SRTP, M SHALL consist of the Authenticated
	// Portion of the packet (as specified in Figure 1) concatenated with
	// the ROC, M = Authenticated Portion || ROC;
	//
	// The pre-defined authentication transform for SRTP is HMAC-SHA1
	// [RFC2104].  With HMAC-SHA1, the SRTP_PREFIX_LENGTH (Figure 3) SHALL
	// be 0.  For SRTP (respectively SRTCP), the HMAC SHALL be applied to
	// the session authentication key and M as specified above, i.e.,
	// HMAC(k_a, M).  The HMAC output SHALL then be truncated to the n_tag
	// left-most bits.
	// - Authenticated portion of the packet is everything BEFORE MKI
	// - k_a is the session message authentication key
	// - n_tag is the bit-length of the output authentication tag
	// - ROC is already added by caller (to allow RTP + RTCP support)
	mac := hmac.New(sha1.New, sessionAuthTag)

	if _, err := mac.Write(buf); err != nil {
		return nil, err
	}

	return mac.Sum(nil)[0:10], nil
}

func (c *Context) verifyAuthTag(buf, actualAuthTag []byte) (bool, error) {
	expectedAuthTag, err := c.generateAuthTag(buf, c.srtpSessionAuthTag)
	if err != nil {
		return false, err
	}

	// We use a constant time comparison to prevent timing attacks.
	// If we compared each byte and broke out early, like bytes.Equal,
	// then an attacker could measure the time it takes for the auth comparison to fail.
	// The longer it takes, the more correct the forged auth tag.
	return subtle.ConstantTimeCompare(actualAuthTag, expectedAuthTag) == 1, nil
}

// https://tools.ietf.org/html/rfc3550#appendix-A.1
func (c *Context) updateRolloverCount(sequenceNumber uint16, s *ssrcState) {
	if !s.rolloverHasProcessed {
		s.rolloverHasProcessed = true
	} else if sequenceNumber == 0 { // We exactly hit the rollover count

		// Only update rolloverCounter if lastSequenceNumber is greater then maxROCDisorder
		// otherwise we already incremented for disorder
		if s.lastSequenceNumber > maxROCDisorder {
			s.rolloverCounter++
		}
	} else if s.lastSequenceNumber < maxROCDisorder && sequenceNumber > (maxSequenceNumber-maxROCDisorder) {
		// Our last sequence number incremented because we crossed 0, but then our current number was within maxROCDisorder of the max
		// So we fell behind, drop to account for jitter
		s.rolloverCounter--
	} else if sequenceNumber < maxROCDisorder && s.lastSequenceNumber > (maxSequenceNumber-maxROCDisorder) {
		// our current is within a maxROCDisorder of 0
		// and our last sequence number was a high sequence number, increment to account for jitter
		s.rolloverCounter++
	}
	s.lastSequenceNumber = sequenceNumber
}

func (c *Context) getSSRCState(ssrc uint32) *ssrcState {
	s, ok := c.ssrcStates[ssrc]
	if ok {
		return s
	}

	s = &ssrcState{ssrc: ssrc}
	c.ssrcStates[ssrc] = s
	return s
}
<|endoftext|>


<commit_before>package ulog

import "golang.org/x/net/context"

type (
	fieldsKeyType    struct{}
	adapterKeyType   struct{}
	callDepthKeyType struct{}
	fieldKey         string
)

var (
	fieldsKey    fieldsKeyType
	adapterKey   adapterKeyType
	callDepthKey callDepthKeyType
)

// withField creates a context that holds key-value pair to log.
func withField(ctx context.Context, key string, value interface{}) context.Context {
	fk := fieldKey(key)
	ctx = context.WithValue(ctx, fk, value)
	fks, _ := ctx.Value(fieldsKey).([]fieldKey)
	for _, fk := range fks {
		if string(fk) == key {
			// fields already contains key
			return ctx
		}
	}
	ctx = context.WithValue(ctx, fieldsKey, append(fks, fk))
	return ctx
}

func fieldsFromContext(ctx context.Context) []Field {
	keys, _ := ctx.Value(fieldsKey).([]fieldKey)
	if len(keys) == 0 {
		return nil
	}
	fs := make([]Field, len(keys))
	for i := range keys {
		fs[i] = Field{
			Key:   string(keys[i]),
			Value: ctx.Value(keys[i]),
		}
	}
	return fs
}

// withAdapter returns a new context that holds LoggerAdapter that is used to logging.
func withAdapter(ctx context.Context, lc Adapter) context.Context {
	return context.WithValue(ctx, adapterKey, lc)
}

func adapterFromContext(ctx context.Context) Adapter {
	lc, _ := ctx.Value(adapterKey).(Adapter)
	return lc
}

func callDepthFromContext(ctx context.Context) int {
	if ctx == nil {
		return 0
	}
	cd, _ := ctx.Value(callDepthKey).(int)
	return cd
}

// withAddingCallDepth returns a new context that has incremented call depth to log. Used with wrapped or utilized logger functions.
func withAddingCallDepth(ctx context.Context, depth int) context.Context {
	return context.WithValue(ctx, callDepthKey, callDepthFromContext(ctx)+depth)
}
<commit_msg>publish CallerDepthFromContext, this is needed for creating another adapter<commit_after>package ulog

import "golang.org/x/net/context"

type (
	fieldsKeyType    struct{}
	adapterKeyType   struct{}
	callDepthKeyType struct{}
	fieldKey         string
)

var (
	fieldsKey    fieldsKeyType
	adapterKey   adapterKeyType
	callDepthKey callDepthKeyType
)

// withField creates a context that holds key-value pair to log.
func withField(ctx context.Context, key string, value interface{}) context.Context {
	fk := fieldKey(key)
	ctx = context.WithValue(ctx, fk, value)
	fks, _ := ctx.Value(fieldsKey).([]fieldKey)
	for _, fk := range fks {
		if string(fk) == key {
			// fields already contains key
			return ctx
		}
	}
	ctx = context.WithValue(ctx, fieldsKey, append(fks, fk))
	return ctx
}

func fieldsFromContext(ctx context.Context) []Field {
	keys, _ := ctx.Value(fieldsKey).([]fieldKey)
	if len(keys) == 0 {
		return nil
	}
	fs := make([]Field, len(keys))
	for i := range keys {
		fs[i] = Field{
			Key:   string(keys[i]),
			Value: ctx.Value(keys[i]),
		}
	}
	return fs
}

// withAdapter returns a new context that holds LoggerAdapter that is used to logging.
func withAdapter(ctx context.Context, lc Adapter) context.Context {
	return context.WithValue(ctx, adapterKey, lc)
}

func adapterFromContext(ctx context.Context) Adapter {
	lc, _ := ctx.Value(adapterKey).(Adapter)
	return lc
}

// CallerDepthFromContext return callerDepth int value, this value is used by finding caller position, usually dones't have to remember it.
func CallDepthFromContext(ctx context.Context) int {
	if ctx == nil {
		return 0
	}
	cd, _ := ctx.Value(callDepthKey).(int)
	return cd
}

// withAddingCallDepth returns a new context that has incremented call depth to log. Used with wrapped or utilized logger functions.
func withAddingCallDepth(ctx context.Context, depth int) context.Context {
	return context.WithValue(ctx, callDepthKey, callDepthFromContext(ctx)+depth)
}
<|endoftext|>


<commit_before>package gofr

import (
	"image"
	"image/color"
	"log"
)

type Parameters struct {
	ImageWidth   int
	ImageHeight  int
	Min          complex128
	Max          complex128
	MaxI         int
	ColorFunc    ColorFunc
	Scaling      int
	Logger       *log.Logger
	MemberColor  color.NRGBA64
	EscapeRadius float64
}

type Context struct {
	Id         int
	Image      *image.NRGBA64
	Parameters *Parameters
}

func MakeContexts(im *image.NRGBA64, n int, p *Parameters) (c []*Context) {
	r := im.Bounds()
	x := 0
	y := 0
	dx := r.Max.X / n
	dy := r.Max.Y / n
	rx := dx + (r.Max.X % n)
	ry := dy + (r.Max.Y % n)
	w := dx
	h := dy

	if n <= 0 {
		panic("I refuse to make zero or fewer contexts of an image.")
	}

	if n > r.Max.X || n > r.Max.Y {
		panic("I refuse to make more contexts than I have pixels.")
	}

	for i := 0; i < n; i++ {
		x = i * dx
		if i == n-1 {
			w = rx
		}

		for j := 0; j < n; j++ {
			y = j * dy
			if j == n-1 {
				h = ry
			}

			sub := im.SubImage(image.Rect(x, y, x+w, y+h)).(*image.NRGBA64)
			nc := Context{
				Id:         (i * n) + j,
				Image:      sub,
				Parameters: p,
			}

			c = append(c, &nc)
		}
	}

	return
}

func (self *Context) Delta() (dx, dy float64) {
	rw := self.Parameters.ImageWidth
	rh := self.Parameters.ImageHeight

	cw := real(self.Parameters.Max) - real(self.Parameters.Min)
	ch := imag(self.Parameters.Max) - imag(self.Parameters.Min)

	dx = cw / float64(rw)
	dy = ch / float64(rh)
	return
}

/*
 * Use this with EachPoint to iterate over the map of pixel coordinates
 * and mapped complex points.
 */
type ContextFunc func(int, int, complex128)

/*
 * Iterate over the map of pixel coordinates and complex points. Pass in
 */
func (self *Context) EachPoint(fn ContextFunc) {
	rmin := self.Image.Bounds().Min
	rmax := self.Image.Bounds().Max
	cmin := self.Parameters.Min
	dx, dy := self.Delta()
	var z complex128

	for x := rmin.X; x < rmax.X; x++ {
		for y := rmin.Y; y < rmax.Y; y++ {
			z = complex(real(cmin)+float64(x)*dx, imag(cmin)+float64(y)*dy)
			fn(x, y, z)
		}
	}
}
<commit_msg>Don't export Context.Delta<commit_after>package gofr

import (
	"image"
	"image/color"
	"log"
)

type Parameters struct {
	ImageWidth   int
	ImageHeight  int
	Min          complex128
	Max          complex128
	MaxI         int
	ColorFunc    ColorFunc
	Scaling      int
	Logger       *log.Logger
	MemberColor  color.NRGBA64
	EscapeRadius float64
}

type Context struct {
	Id         int
	Image      *image.NRGBA64
	Parameters *Parameters
}

func MakeContexts(im *image.NRGBA64, n int, p *Parameters) (c []*Context) {
	r := im.Bounds()
	x := 0
	y := 0
	dx := r.Max.X / n
	dy := r.Max.Y / n
	rx := dx + (r.Max.X % n)
	ry := dy + (r.Max.Y % n)
	w := dx
	h := dy

	if n <= 0 {
		panic("I refuse to make zero or fewer contexts of an image.")
	}

	if n > r.Max.X || n > r.Max.Y {
		panic("I refuse to make more contexts than I have pixels.")
	}

	for i := 0; i < n; i++ {
		x = i * dx
		if i == n-1 {
			w = rx
		}

		for j := 0; j < n; j++ {
			y = j * dy
			if j == n-1 {
				h = ry
			}

			sub := im.SubImage(image.Rect(x, y, x+w, y+h)).(*image.NRGBA64)
			nc := Context{
				Id:         (i * n) + j,
				Image:      sub,
				Parameters: p,
			}

			c = append(c, &nc)
		}
	}

	return
}

func (self *Context) delta() (dx, dy float64) {
	rw := self.Parameters.ImageWidth
	rh := self.Parameters.ImageHeight

	cw := real(self.Parameters.Max) - real(self.Parameters.Min)
	ch := imag(self.Parameters.Max) - imag(self.Parameters.Min)

	dx = cw / float64(rw)
	dy = ch / float64(rh)
	return
}

/*
 * Use this with EachPoint to iterate over the map of pixel coordinates
 * and mapped complex points.
 */
type ContextFunc func(int, int, complex128)

/*
 * Iterate over the map of pixel coordinates and complex points.
 */
func (self *Context) EachPoint(fn ContextFunc) {
	rmin := self.Image.Bounds().Min
	rmax := self.Image.Bounds().Max
	cmin := self.Parameters.Min
	dx, dy := self.delta()
	var z complex128

	for x := rmin.X; x < rmax.X; x++ {
		for y := rmin.Y; y < rmax.Y; y++ {
			z = complex(real(cmin)+float64(x)*dx, imag(cmin)+float64(y)*dy)
			fn(x, y, z)
		}
	}
}
<|endoftext|>


<commit_before>//
package contour

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/BurntSushi/toml"
	utils "github.com/mohae/utilitybelt"
)

// Environment Variable constants for common environment variables.
// Or you can supply your own values. Contour automatically downcases
// environment variables for consistency across formats.
var (
	EnvConfigFilename string = "configfilename"
	EnvConfigFormat string = "configformat"
	EnvLogFilename string = "logfilename"
	EnvLogging string = "logging"
)

// settingAlias are aliases to settings, each setting is its own alias.
var settingAlias map[string]string = make(map[string]string)

// commandAlias are aliases to commands, each command is its own alias.
var commandAlias map[string]string = make(map[string]string)

// configFile holds the contents of the configuration file
var configFile map[string]interface{} = make(map[string]interface{})

// config holds the current application configuration
var AppConfig *Config = &Config{Settings: map[string]*setting{}}

// Config is a group of settings and holds all of the application setting
// information. Even though contour automatically uses environment variables,
// unless its told to ignore them, it still needs to maintain state
// information about each setting so it knows how to handle attempst to update.
// TODO:
//	* support ignoring environment variables
//
type Config struct {

	// code is the shortcode for this configuration. It is mostly used to
	// prefix environment variables, when used.
	code 	string
	Settings 	map[string]*setting
}

// GetAppConfig returns the AppConfig to the caller. Any contour function
// called uses this config.
func GetAppConfig() *Config {
	return AppConfig
}

// NewConfig returns a *Config to the caller. Any config created by NewConfig()
// is independent of the AppConfig
func NewConfig() *Config {
	return &Config{Settings: map[string]*setting{}}
}

func (c *Config) GetCode() string {
	return c.code
}

// SetAppCode set's the appcode. This can only be done once. If it is already
// set, it will return an error.
func (c *Config) SetCode(s string) error {
	if c.code != "" {
		return errors.New("appCode is already set. AppCode is idempotent. Once set, it cannot be altered")
	}

	c.code = s

	return nil
}

// setting holds the information for a configuration setting.
type setting struct {
	// Code of the setting
	Code string

	// Type is the datatype for the setting
	Type string

	// The current value of the setting
	Value interface{}

	// IsFlag:  whether or not this is a flag.
	IsFlag bool

	// IsIdempotent: whether or not this value can be overwritten once set.
	IsIdempotent bool

	// SourceIsEnv: whether or not the original source of this setting was
	// its environment variables, vs. flags or config, etc. This is tracked
	// because it has implications on override behavior.
	SourceIsEnv bool

	// IsCore: whether or not this is considered a core setting. Core
	// settings if for things like application name, where you don't want
	// anything else overwriting that value, once set, and you want to be
	// able to overwrite any existing ENV value if contour hasn't already
	// set it. Once set, IsIdempotent is also true.
	IsCore bool
}

// getConfigFormat gets the configured config filename and returns the format
// it is in, if it is a supported format; otherwise an error.
func getConfigFormat() (string, error) {
	// If the format is already set, we don't override the setting.
	// A nil is returned because this is not an error.
	format := os.Getenv(EnvConfigFormat)
	if format != "" {
		// See if the set format is supported, if it is,
		if isSupportedFormat(format) {
			return format, nil
		}
	}

	// Format is either unknown, or not supported; try to resolve it.
	fname := os.Getenv(EnvConfigFilename)
	if fname == "" {
		return "", errors.New("unable to determine config format, filename not set")
	}

	parts := strings.Split(fname, ".")

	// case 0 has already been evaluated
	switch len(parts) {
	case 1:
		return "", errors.New("unable to determine config format, the configuration file " + fname + " doesn't have an extension")
	case 2:
		format = parts[1]
	default:
		// assume its the last part
		format = parts[len(parts) - 1]
	}

	if isSupportedFormat(format) {
		return "", errors.New(format + " is an unsupported format for configuration files")
	}

	return format, nil

}

// isSupportedFormat checks to see if the passed string represents a supported
// config format.
func isSupportedFormat(s string) bool {
        switch s {
        case "json":
                return true
	case "toml":
                return true
        default:
                return  false
        }

        return false
}

// SetFromEnv updates the configuration from the environment variable values.
// A setting is only updated if it IsUpdateable.
func SetFromEnv() {
	for k, _ := range AppConfig.Settings {
		// See if k exists as an env variable
		v := os.Getenv(k)
		if v == "" {
			continue
		}

		// Only core and idempotent are updateable
		if !CanSetUsingEnv(k) {
			continue
		}

		// Gotten this far, set it
		AppConfig.Settings[k].Value = v
		AppConfig.Settings[k].SourceIsEnv = true
	}

}

// SetFromConfigFile populates configFile from the configured config file.
// The config file entries are then processed, updating their associated
// settings. A setting is only updated if it IsUpdateable.
func SetFromConfigFile() error {
	// ConfigFile should be set and its format type should be known.
	err := LoadConfigFile()
	if err != nil {
		return err
	}

	// ProcessConfigFile, setting what's appropriate.
	for k, v := range configFile {
		// Find the key in the settings
		_, ok := AppConfig.Settings[k]
		if !ok {
			// skip settings that don't already exist
			continue
		}

		// Skip if IsIdempotent, IsCore, SourceIsEnv since they aren't
		// overridable by ConfigFile.
		if !IsUpdateable(k) {
			continue
		}

		// Update the setting with file's
		Update(k, v)
	}

	return nil
}

// LoadConfigFile() is the entry point for reading the configuration file.
func LoadConfigFile() error {
	n := os.Getenv(EnvConfigFilename)
	if n == "" {
		// This isn't an error as config file is allowed to not exist
		// TODO:
		//	Possible add a ConfigFileRequired flag
		//	Add logging support
		return nil
	}

	fBytes, err := readConfigFile(n)
	if err != nil {
		return err
	}

	err = MarshalFormatReader(os.Getenv(EnvConfigFormat),bytes.NewReader(fBytes))
	if err != nil {
		return err
	}

	return nil
}

// readConfigFile reads the configFile
func readConfigFile(n string) ([]byte, error) {
	cfg, err := ioutil.ReadFile(n)
	if err != nil {
		return nil, err
	}

	return cfg, nil
}

// MarshalFormatReader
func MarshalFormatReader(t string, r io.Reader) error {
	b := new(bytes.Buffer)
	b.ReadFrom(r)

	switch t{
	case "json":
		err := json.Unmarshal(b.Bytes(), &configFile)
		if err != nil {
			return err
		}

	case "toml":
		_, err := toml.Decode(b.String(), &configFile)
		if err != nil {
			return err
		}

	}
	return nil
}

// SetIdempotentString sets the value of idempotent configuration settings
// that are strings.
func SetIdempotentString(k, v string) error {
	// see if the key already exists, if it does, it can't be set
	_, ok := AppConfig.Settings[k]
	if ok {
		return nil
	}

	// Set the environment variable for it
	err := os.Setenv(k, v)
	if err != nil {
		return err
	}

	AppConfig.Settings[k] = &setting{Type: "string",  Value: v, IsIdempotent: true}

	return nil
}

// SetIdemString is a convenience function that wraps SetIdempotentString()
func SetIdemString(k, v string) {
	SetIdempotentString(k, v)
}

// SetBoolFlag sets the value of a configuration setting that is also a flag.
func SetBoolFlag(k, f string, v bool) {
	// see if the key exists
	_, ok := AppConfig.Settings[k]
	if ok {
		// see if it can be set
		if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].SourceIsEnv {
			return
		}

		// replace current settings with new
		AppConfig.Settings[k].Value = v
		AppConfig.Settings[k].IsFlag = true
		AppConfig.Settings[k].Code = f
		return
	}

	// otherwise add it
	s := utils.BoolToString(v)
	os.Setenv(AppConfig.GetCode() + k, s)
	AppConfig.Settings[k] = &setting{Value: v, Code: f, IsFlag: true}
}

// SetStringFlag sets the value of a configuration setting that is also a flag.
func SetStringFlag(k, f string, v string) {
	// see if the key exists
	_, ok := AppConfig.Settings[k]
	if ok {
		// see if it can be set
		if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].SourceIsEnv || AppConfig.Settings[k].IsCore {
			return
		}

		// replace current settings with new
		AppConfig.Settings[k].Value = v
		AppConfig.Settings[k].IsFlag = true
		AppConfig.Settings[k].Code = f
		return
	}

	// otherwise add it
	os.Setenv(AppConfig.GetCode() + k, v)
	AppConfig.Settings[k] = &setting{Value: v, Code: f, IsFlag: true}
}

// Update updates the passed key with the passed value.
func Update(k string, v interface{}) error {
	if !IsUpdateable(k) {
		return nil
	}

	err := os.Setenv(AppConfig.GetCode() + k, v.(string))
	if err != nil {
		return err
	}

	set(k, v)

	return nil
}

func set(k string, v interface{}) {
	// Cast according to type for this key
	switch AppConfig.Settings[k].Type {
	case "string":
		AppConfig.Settings[k].Value = v.(string)

	case "bool":
		AppConfig.Settings[k].Value = v.(bool)

	case "int", "int8", "int16", "int32", "int64":
		AppConfig.Settings[k].Value = v.(int)

	default:
		AppConfig.Settings[k].Value = v
	}
}

// IsUpdateable checks to see if the passed setting key is updateable.
func IsUpdateable(k string) bool {
	// See if the key exists, if it doesn't already exist, it can't be
	// updated.
	_, ok := AppConfig.Settings[k]
	if !ok {
		return false
	}

	// See if there are any settings that prevent it from being updated.
	if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].SourceIsEnv || AppConfig.Settings[k].IsCore {
		return false
	}

	return true
}

// Override overrides the setting, if it is overrideable. This is used to
// override any environment variable that had pre-existing values.
func Override(k string, v interface{}) error {
	if !IsOverrideable(k) {
		return nil
	}

	err := os.Setenv(AppConfig.GetCode() + k, v.(string))
	if err != nil {
		return err
	}

	// Set the new value
	set(k, v)

	return nil
}

// IsOverrideable() checks to see if the setting can be overridden. Overrides
// only come from args and flags. ConfigFile settings must be updated instead.
func IsOverrideable(k string) bool {
	// See if the key exists, if it doesn't already exist, it can't be
	// overridden
	_, ok := AppConfig.Settings[k]
	if !ok {
		return false
	}

	// See if there are any settings that prevent it from being overridden.
	if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].IsCore {
		return false
	}

	return true
}

// CanSetUsingEnv checks to see if the setting is settable using an env
// variable.
func CanSetUsingEnv(k string) bool {
	// If something is flagged as idempotent, it can't be set be changed.
	// Same with core settings.
	if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].IsCore {
		return false
	}

	return true
}

// AddCommandAlias adds an alias for a command. The first time a command is
// added, it's added as an alias of itself too.
func AddCommandAlias(command, alias string) error {
	// see if an alias already exists
	v, ok := commandAlias[alias]
	if ok {
		err := errors.New(alias + " is an alias of the command " + v + " cannot make it an alias of " + command)
		return err
	}

	// see if the command already has aliases
	v, ok = commandAlias[command]
	if !ok {
		// Add it as an alias of itself first
		commandAlias[command] = command
	}

	commandAlias[alias] = command

	return nil
}

// AddSettingAlias adds an alias for a setting. The first time a setting is
// added, it's added as an alias of itself too.
func AddSettingAlias(setting, alias string) error {
	// see if an alias already exists
	v, ok := settingAlias[alias]
	if ok {
		err := errors.New(alias + " is an alias of the setting " + v + " cannot make it an alias of " + setting)
		return err
	}

	// see if the setting already has aliases
	v, ok = settingAlias[setting]
	if !ok {
		// Add it as an alias of itself first
		settingAlias[setting] = setting
	}

	settingAlias[alias] = setting

	return nil
}

// Set
// resetAppConfig resets the application's configuration struct to empty.
// This does not affect their respective environment variables
func resetAppConfig() {
	AppConfig = &Config{Settings: map[string]*setting{}}
}

/*
func Get(k string) interface{} {

}

func GetBool(k string) bool {

}

func GetInt(k string) int {

}

// GetInterface is a convenience wrapper function to Get
func GetInterface(k string) interface{} {
	return Get(k)
}

*/
<commit_msg>updated code to make gets more safe, have a bool related bug though, I think, otherwise its an interface related one<commit_after>//
package contour

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/BurntSushi/toml"
	utils "github.com/mohae/utilitybelt"
)

// Environment Variable constants for common environment variables.
// Or you can supply your own values. Contour automatically downcases
// environment variables for consistency across formats.
var (
	EnvConfigFilename string = "configfilename"
	EnvConfigFormat string = "configformat"
	EnvLogFilename string = "logfilename"
	EnvLogging string = "logging"
)

// settingAlias are aliases to settings, each setting is its own alias.
var settingAlias map[string]string = make(map[string]string)

// commandAlias are aliases to commands, each command is its own alias.
var commandAlias map[string]string = make(map[string]string)

// configFile holds the contents of the configuration file
var configFile map[string]interface{} = make(map[string]interface{})

// config holds the current application configuration
var AppConfig *Config = &Config{Settings: map[string]*setting{}}

const (
	SettingNotFoundErr = " setting was not found"
)

// Config is a group of settings and holds all of the application setting
// information. Even though contour automatically uses environment variables,
// unless its told to ignore them, it still needs to maintain state
// information about each setting so it knows how to handle attempst to update.
// TODO:
//	* support ignoring environment variables
//
type Config struct {

	// code is the shortcode for this configuration. It is mostly used to
	// prefix environment variables, when used.
	code 	string
	Settings 	map[string]*setting
}

// GetAppConfig returns the AppConfig to the caller. Any contour function
// called uses this config.
func GetAppConfig() *Config {
	return AppConfig
}

// NewConfig returns a *Config to the caller. Any config created by NewConfig()
// is independent of the AppConfig
func NewConfig() *Config {
	return &Config{Settings: map[string]*setting{}}
}

func (c *Config) GetCode() string {
	return c.code
}

// SetAppCode set's the appcode. This can only be done once. If it is already
// set, it will return an error.
func (c *Config) SetCode(s string) error {
	if c.code != "" {
		return errors.New("appCode is already set. AppCode is idempotent. Once set, it cannot be altered")
	}

	c.code = s

	return nil
}

// setting holds the information for a configuration setting.
type setting struct {
	// Code of the setting
	Code string

	// Type is the datatype for the setting
	Type string

	// The current value of the setting
	Value interface{}

	// IsFlag:  whether or not this is a flag.
	IsFlag bool

	// IsIdempotent: whether or not this value can be overwritten once set.
	IsIdempotent bool

	// SourceIsEnv: whether or not the original source of this setting was
	// its environment variables, vs. flags or config, etc. This is tracked
	// because it has implications on override behavior.
	SourceIsEnv bool

	// IsCore: whether or not this is considered a core setting. Core
	// settings if for things like application name, where you don't want
	// anything else overwriting that value, once set, and you want to be
	// able to overwrite any existing ENV value if contour hasn't already
	// set it. Once set, IsIdempotent is also true.
	IsCore bool
}

// getConfigFormat gets the configured config filename and returns the format
// it is in, if it is a supported format; otherwise an error.
func getConfigFormat() (string, error) {
	// If the format is already set, we don't override the setting.
	// A nil is returned because this is not an error.
	format := os.Getenv(EnvConfigFormat)
	if format != "" {
		// See if the set format is supported, if it is,
		if isSupportedFormat(format) {
			return format, nil
		}
	}

	// Format is either unknown, or not supported; try to resolve it.
	fname := os.Getenv(EnvConfigFilename)
	if fname == "" {
		return "", errors.New("unable to determine config format, filename not set")
	}

	parts := strings.Split(fname, ".")

	// case 0 has already been evaluated
	switch len(parts) {
	case 1:
		return "", errors.New("unable to determine config format, the configuration file " + fname + " doesn't have an extension")
	case 2:
		format = parts[1]
	default:
		// assume its the last part
		format = parts[len(parts) - 1]
	}

	if isSupportedFormat(format) {
		return "", errors.New(format + " is an unsupported format for configuration files")
	}

	return format, nil

}

// isSupportedFormat checks to see if the passed string represents a supported
// config format.
func isSupportedFormat(s string) bool {
        switch s {
        case "json":
                return true
	case "toml":
                return true
        default:
                return  false
        }

        return false
}

// SetFromEnv updates the configuration from the environment variable values.
// A setting is only updated if it IsUpdateable.
func SetFromEnv() {
	for k, _ := range AppConfig.Settings {
		// See if k exists as an env variable
		v := os.Getenv(k)
		if v == "" {
			continue
		}

		// Only core and idempotent are updateable
		if !CanSetUsingEnv(k) {
			continue
		}

		// Gotten this far, set it
		AppConfig.Settings[k].Value = v
		AppConfig.Settings[k].SourceIsEnv = true
	}

}

// SetFromConfigFile populates configFile from the configured config file.
// The config file entries are then processed, updating their associated
// settings. A setting is only updated if it IsUpdateable.
func SetFromConfigFile() error {
	// ConfigFile should be set and its format type should be known.
	err := LoadConfigFile()
	if err != nil {
		return err
	}

	// ProcessConfigFile, setting what's appropriate.
	for k, v := range configFile {
		// Find the key in the settings
		_, ok := AppConfig.Settings[k]
		if !ok {
			// skip settings that don't already exist
			continue
		}

		// Skip if IsIdempotent, IsCore, SourceIsEnv since they aren't
		// overridable by ConfigFile.
		if !IsUpdateable(k) {
			continue
		}

		// Update the setting with file's
		Update(k, v)
	}

	return nil
}

// LoadConfigFile() is the entry point for reading the configuration file.
func LoadConfigFile() error {
	n := os.Getenv(EnvConfigFilename)
	if n == "" {
		// This isn't an error as config file is allowed to not exist
		// TODO:
		//	Possible add a ConfigFileRequired flag
		//	Add logging support
		return nil
	}

	fBytes, err := readConfigFile(n)
	if err != nil {
		return err
	}

	err = MarshalFormatReader(os.Getenv(EnvConfigFormat),bytes.NewReader(fBytes))
	if err != nil {
		return err
	}

	return nil
}

// readConfigFile reads the configFile
func readConfigFile(n string) ([]byte, error) {
	cfg, err := ioutil.ReadFile(n)
	if err != nil {
		return nil, err
	}

	return cfg, nil
}

// MarshalFormatReader
func MarshalFormatReader(t string, r io.Reader) error {
	b := new(bytes.Buffer)
	b.ReadFrom(r)

	switch t{
	case "json":
		err := json.Unmarshal(b.Bytes(), &configFile)
		if err != nil {
			return err
		}

	case "toml":
		_, err := toml.Decode(b.String(), &configFile)
		if err != nil {
			return err
		}

	}
	return nil
}

// SetIdempotentString sets the value of idempotent configuration settings
// that are strings.
func SetIdempotentString(k, v string) error {
	// see if the key already exists, if it does, it can't be set
	_, ok := AppConfig.Settings[k]
	if ok {
		return nil
	}

	// Set the environment variable for it
	err := os.Setenv(k, v)
	if err != nil {
		return err
	}

	AppConfig.Settings[k] = &setting{Type: "string",  Value: v, IsIdempotent: true}

	return nil
}

// SetIdemString is a convenience function that wraps SetIdempotentString()
func SetIdemString(k, v string) {
	SetIdempotentString(k, v)
}

// SetBoolFlag sets the value of a configuration setting that is also a flag.
func SetBoolFlag(k, f string, v bool) {
	// see if the key exists
	_, ok := AppConfig.Settings[k]
	if ok {
		// see if it can be set
		if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].SourceIsEnv {
			return
		}

		// replace current settings with new
		AppConfig.Settings[k].Value = v
		AppConfig.Settings[k].IsFlag = true
		AppConfig.Settings[k].Code = f
		return
	}

	// otherwise add it
	s := utils.BoolToString(v)
	os.Setenv(AppConfig.GetCode() + k, s)
	AppConfig.Settings[k] = &setting{Value: v, Code: f, IsFlag: true}
}

// SetStringFlag sets the value of a configuration setting that is also a flag.
func SetStringFlag(k, f string, v string) {
	// see if the key exists
	_, ok := AppConfig.Settings[k]
	if ok {
		// see if it can be set
		if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].SourceIsEnv || AppConfig.Settings[k].IsCore {
			return
		}

		// replace current settings with new
		AppConfig.Settings[k].Value = v
		AppConfig.Settings[k].IsFlag = true
		AppConfig.Settings[k].Code = f
		return
	}

	// otherwise add it
	os.Setenv(AppConfig.GetCode() + k, v)
	AppConfig.Settings[k] = &setting{Value: v, Code: f, IsFlag: true}
}

// Update updates the passed key with the passed value.
func Update(k string, v interface{}) error {
	if !IsUpdateable(k) {
		return nil
	}

	err := os.Setenv(AppConfig.GetCode() + k, v.(string))
	if err != nil {
		return err
	}

	set(k, v)

	return nil
}

func set(k string, v interface{}) {
	// Cast according to type for this key
	switch AppConfig.Settings[k].Type {
	case "string":
		AppConfig.Settings[k].Value = v.(string)

	case "bool":
		AppConfig.Settings[k].Value = v.(bool)

	case "int", "int8", "int16", "int32", "int64":
		AppConfig.Settings[k].Value = v.(int)

	default:
		AppConfig.Settings[k].Value = v
	}
}

// IsUpdateable checks to see if the passed setting key is updateable.
func IsUpdateable(k string) bool {
	// See if the key exists, if it doesn't already exist, it can't be
	// updated.
	_, ok := AppConfig.Settings[k]
	if !ok {
		return false
	}

	// See if there are any settings that prevent it from being updated.
	if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].SourceIsEnv || AppConfig.Settings[k].IsCore {
		return false
	}

	return true
}

// Override overrides the setting, if it is overrideable. This is used to
// override any environment variable that had pre-existing values.
func Override(k string, v interface{}) error {
	if !IsOverrideable(k) {
		return nil
	}

	err := os.Setenv(AppConfig.GetCode() + k, v.(string))
	if err != nil {
		return err
	}

	// Set the new value
	set(k, v)

	return nil
}

// IsOverrideable() checks to see if the setting can be overridden. Overrides
// only come from args and flags. ConfigFile settings must be updated instead.
func IsOverrideable(k string) bool {
	// See if the key exists, if it doesn't already exist, it can't be
	// overridden
	_, ok := AppConfig.Settings[k]
	if !ok {
		return false
	}

	// See if there are any settings that prevent it from being overridden.
	if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].IsCore {
		return false
	}

	return true
}

// CanSetUsingEnv checks to see if the setting is settable using an env
// variable.
func CanSetUsingEnv(k string) bool {
	// If something is flagged as idempotent, it can't be set be changed.
	// Same with core settings.
	if AppConfig.Settings[k].IsIdempotent || AppConfig.Settings[k].IsCore {
		return false
	}

	return true
}

// AddCommandAlias adds an alias for a command. The first time a command is
// added, it's added as an alias of itself too.
func AddCommandAlias(command, alias string) error {
	// see if an alias already exists
	v, ok := commandAlias[alias]
	if ok {
		err := errors.New(alias + " is an alias of the command " + v + " cannot make it an alias of " + command)
		return err
	}

	// see if the command already has aliases
	v, ok = commandAlias[command]
	if !ok {
		// Add it as an alias of itself first
		commandAlias[command] = command
	}

	commandAlias[alias] = command

	return nil
}

// AddSettingAlias adds an alias for a setting. The first time a setting is
// added, it's added as an alias of itself too.
func AddSettingAlias(setting, alias string) error {
	// see if an alias already exists
	v, ok := settingAlias[alias]
	if ok {
		err := errors.New(alias + " is an alias of the setting " + v + " cannot make it an alias of " + setting)
		return err
	}

	// see if the setting already has aliases
	v, ok = settingAlias[setting]
	if !ok {
		// Add it as an alias of itself first
		settingAlias[setting] = setting
	}

	settingAlias[alias] = setting

	return nil
}

// Set
// resetAppConfig resets the application's configuration struct to empty.
// This does not affect their respective environment variables
func resetAppConfig() {
	AppConfig = &Config{Settings: map[string]*setting{}}
}

func Get(k string) (interface{}, error) {
	_, ok := AppConfig.Settings[k]
	if !ok {
		return nil, notFoundErr(k)
	}

	return AppConfig.Settings[k].Value, nil
}

func GetBool(k string) (bool, error) {
	_, ok := AppConfig.Settings[k]
	if !ok {
		return false, notFoundErr(k)
	}

	return AppConfig.Settings[k].Value.(bool), nil
}

func GetInt(k string) (int, error) {
	_, ok := AppConfig.Settings[k]
	if !ok {
		return 0, notFoundErr(k)
	}

	return AppConfig.Settings[k].Value.(int), nil
}

func GetString(k string) (string, error) {
	_, ok := AppConfig.Settings[k]
	if !ok {
		return "", notFoundErr(k)
	}

	return AppConfig.Settings[k].Value.(string), nil
}

// GetInterface is a convenience wrapper function to Get
func GetInterface(k string) (interface{}, error) {
	return Get(k)
}

func notFoundErr(k string) error {
	return errors.New(k + " not found")
}
<|endoftext|>


<commit_before>package gocql

import (
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

// Ensure that the atomic variable is aligned to a 64bit boundary
// so that atomic operations can be applied on 32bit architectures.
type controlConn struct {
	connecting uint64

	session *Session

	conn atomic.Value

	session *Session
	conn    atomic.Value

	retry RetryPolicy

	closeWg sync.WaitGroup
	quit    chan struct{}
}

func createControlConn(session *Session) *controlConn {
	control := &controlConn{
		session: session,
		quit:    make(chan struct{}),
		retry:   &SimpleRetryPolicy{NumRetries: 3},
	}

	control.conn.Store((*Conn)(nil))

	return control
}

func (c *controlConn) heartBeat() {
	defer c.closeWg.Done()

	for {
		select {
		case <-c.quit:
			return
		case <-time.After(5 * time.Second):
		}

		resp, err := c.writeFrame(&writeOptionsFrame{})
		if err != nil {
			goto reconn
		}

		switch resp.(type) {
		case *supportedFrame:
			continue
		case error:
			goto reconn
		default:
			panic(fmt.Sprintf("gocql: unknown frame in response to options: %T", resp))
		}

	reconn:
		c.reconnect(true)
		// time.Sleep(5 * time.Second)
		continue
	}
}

func (c *controlConn) connect(endpoints []string) error {
	// intial connection attmept, try to connect to each endpoint to get an initial
	// list of nodes.

	// shuffle endpoints so not all drivers will connect to the same initial
	// node.
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	perm := r.Perm(len(endpoints))
	shuffled := make([]string, len(endpoints))

	for i, endpoint := range endpoints {
		shuffled[perm[i]] = endpoint
	}

	// store that we are not connected so that reconnect wont happen if we error
	atomic.StoreInt64(&c.connecting, -1)

	var (
		conn *Conn
		err  error
	)

	for _, addr := range shuffled {
		conn, err = c.session.connect(JoinHostPort(addr, c.session.cfg.Port), c)
		if err != nil {
			log.Printf("gocql: unable to control conn dial %v: %v\n", addr, err)
			continue
		}

		if err = c.registerEvents(conn); err != nil {
			conn.Close()
			continue
		}

		// we should fetch the initial ring here and update initial host data. So that
		// when we return from here we have a ring topology ready to go.
		break
	}

	if conn == nil {
		// this is fatal, not going to connect a session
		return err
	}

	c.conn.Store(conn)
	atomic.StoreInt64(&c.connecting, 0)

	c.closeWg.Add(1)
	go c.heartBeat()

	return nil
}

func (c *controlConn) registerEvents(conn *Conn) error {
	framer, err := conn.exec(&writeRegisterFrame{
		events: []string{"TOPOLOGY_CHANGE", "STATUS_CHANGE", "STATUS_CHANGE"},
	}, nil)
	if err != nil {
		return err
	}

	frame, err := framer.parseFrame()
	if err != nil {
		return err
	} else if _, ok := frame.(*readyFrame); !ok {
		return fmt.Errorf("unexpected frame in response to register: got %T: %v\n", frame, frame)
	}

	return nil
}

func (c *controlConn) reconnect(refreshring bool) {
	// TODO: simplify this function, use session.ring to get hosts instead of the
	// connection pool
	if !atomic.CompareAndSwapInt64(&c.connecting, 0, 1) {
		return
	}

	success := false
	defer func() {
		// debounce reconnect a little
		if success {
			go func() {
				time.Sleep(500 * time.Millisecond)
				atomic.StoreInt64(&c.connecting, 0)
			}()
		} else {
			atomic.StoreInt64(&c.connecting, 0)
		}
	}()

	addr := c.addr()
	oldConn := c.conn.Load().(*Conn)
	if oldConn != nil {
		oldConn.Close()
	}

	var newConn *Conn
	if addr != "" {
		// try to connect to the old host
		conn, err := c.session.connect(addr, c)
		if err != nil {
			// host is dead
			// TODO: this is replicated in a few places
			ip, portStr, _ := net.SplitHostPort(addr)
			port, _ := strconv.Atoi(portStr)
			c.session.handleNodeDown(net.ParseIP(ip), port)
		} else {
			newConn = conn
		}
	}

	// TODO: should have our own roundrobbin for hosts so that we can try each
	// in succession and guantee that we get a different host each time.
	if newConn == nil {
		_, conn := c.session.pool.Pick(nil)
		if conn == nil {
			return
		}

		if conn == nil {
			return
		}

		var err error
		newConn, err = c.session.connect(conn.addr, c)
		if err != nil {
			// TODO: add log handler for things like this
			return
		}
	}

	if err := c.registerEvents(newConn); err != nil {
		// TODO: handle this case better
		newConn.Close()
		log.Printf("gocql: control unable to register events: %v\n", err)
		return
	}

	c.conn.Store(newConn)
	success = true

	if refreshring {
		c.session.hostSource.refreshRing()
	}
}

func (c *controlConn) HandleError(conn *Conn, err error, closed bool) {
	if !closed {
		return
	}

	oldConn := c.conn.Load().(*Conn)
	if oldConn != conn {
		return
	}

	c.reconnect(true)
}

func (c *controlConn) writeFrame(w frameWriter) (frame, error) {
	conn := c.conn.Load().(*Conn)
	if conn == nil {
		return nil, errNoControl
	}

	framer, err := conn.exec(w, nil)
	if err != nil {
		return nil, err
	}

	return framer.parseFrame()
}

func (c *controlConn) withConn(fn func(*Conn) *Iter) *Iter {
	const maxConnectAttempts = 5
	connectAttempts := 0

	for i := 0; i < maxConnectAttempts; i++ {
		conn := c.conn.Load().(*Conn)
		if conn == nil {
			if connectAttempts > maxConnectAttempts {
				break
			}

			connectAttempts++

			c.reconnect(false)
			continue
		}

		return fn(conn)
	}

	return &Iter{err: errNoControl}
}

// query will return nil if the connection is closed or nil
func (c *controlConn) query(statement string, values ...interface{}) (iter *Iter) {
	q := c.session.Query(statement, values...).Consistency(One)

	for {
		iter = c.withConn(func(conn *Conn) *Iter {
			return conn.executeQuery(q)
		})

		q.attempts++
		if iter.err == nil || !c.retry.Attempt(q) {
			break
		}
	}

	return
}

func (c *controlConn) fetchHostInfo(addr net.IP, port int) (*HostInfo, error) {
	// TODO(zariel): we should probably move this into host_source or atleast
	// share code with it.
	isLocal := c.addr() == addr.String()

	var fn func(*HostInfo) error

	if isLocal {
		fn = func(host *HostInfo) error {
			// TODO(zariel): should we fetch rpc_address from here?
			iter := c.query("SELECT data_center, rack, host_id, tokens, release_version FROM system.local WHERE key='local'")
			iter.Scan(&host.dataCenter, &host.rack, &host.hostId, &host.tokens, &host.version)
			return iter.Close()
		}
	} else {
		fn = func(host *HostInfo) error {
			// TODO(zariel): should we fetch rpc_address from here?
			iter := c.query("SELECT data_center, rack, host_id, tokens, release_version FROM system.peers WHERE peer=?", addr)
			iter.Scan(&host.dataCenter, &host.rack, &host.hostId, &host.tokens, &host.version)
			return iter.Close()
		}
	}

	host := &HostInfo{
		port: port,
	}

	if err := fn(host); err != nil {
		return nil, err
	}
	host.peer = addr.String()

	return host, nil
}

func (c *controlConn) awaitSchemaAgreement() error {
	return c.withConn(func(conn *Conn) *Iter {
		return &Iter{err: conn.awaitSchemaAgreement()}
	}).err
}

func (c *controlConn) addr() string {
	conn := c.conn.Load().(*Conn)
	if conn == nil {
		return ""
	}
	return conn.addr
}

func (c *controlConn) close() {
	// TODO: handle more gracefully
	close(c.quit)
	c.closeWg.Wait()
	conn := c.conn.Load().(*Conn)
	if conn != nil {
		conn.Close()
	}
}

var errNoControl = errors.New("gocql: no control connection available")
<commit_msg>improve control connection isLocal detection<commit_after>package gocql

import (
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

// Ensure that the atomic variable is aligned to a 64bit boundary
// so that atomic operations can be applied on 32bit architectures.
type controlConn struct {
	connecting uint64

	session *Session

	conn atomic.Value

	session *Session
	conn    atomic.Value

	retry RetryPolicy

	closeWg sync.WaitGroup
	quit    chan struct{}
}

func createControlConn(session *Session) *controlConn {
	control := &controlConn{
		session: session,
		quit:    make(chan struct{}),
		retry:   &SimpleRetryPolicy{NumRetries: 3},
	}

	control.conn.Store((*Conn)(nil))

	return control
}

func (c *controlConn) heartBeat() {
	defer c.closeWg.Done()

	for {
		select {
		case <-c.quit:
			return
		case <-time.After(5 * time.Second):
		}

		resp, err := c.writeFrame(&writeOptionsFrame{})
		if err != nil {
			goto reconn
		}

		switch resp.(type) {
		case *supportedFrame:
			continue
		case error:
			goto reconn
		default:
			panic(fmt.Sprintf("gocql: unknown frame in response to options: %T", resp))
		}

	reconn:
		c.reconnect(true)
		// time.Sleep(5 * time.Second)
		continue
	}
}

func (c *controlConn) connect(endpoints []string) error {
	// intial connection attmept, try to connect to each endpoint to get an initial
	// list of nodes.

	// shuffle endpoints so not all drivers will connect to the same initial
	// node.
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	perm := r.Perm(len(endpoints))
	shuffled := make([]string, len(endpoints))

	for i, endpoint := range endpoints {
		shuffled[perm[i]] = endpoint
	}

	// store that we are not connected so that reconnect wont happen if we error
	atomic.StoreInt64(&c.connecting, -1)

	var (
		conn *Conn
		err  error
	)

	for _, addr := range shuffled {
		conn, err = c.session.connect(JoinHostPort(addr, c.session.cfg.Port), c)
		if err != nil {
			log.Printf("gocql: unable to control conn dial %v: %v\n", addr, err)
			continue
		}

		if err = c.registerEvents(conn); err != nil {
			conn.Close()
			continue
		}

		// we should fetch the initial ring here and update initial host data. So that
		// when we return from here we have a ring topology ready to go.
		break
	}

	if conn == nil {
		// this is fatal, not going to connect a session
		return err
	}

	c.conn.Store(conn)
	atomic.StoreInt64(&c.connecting, 0)

	c.closeWg.Add(1)
	go c.heartBeat()

	return nil
}

func (c *controlConn) registerEvents(conn *Conn) error {
	framer, err := conn.exec(&writeRegisterFrame{
		events: []string{"TOPOLOGY_CHANGE", "STATUS_CHANGE", "STATUS_CHANGE"},
	}, nil)
	if err != nil {
		return err
	}

	frame, err := framer.parseFrame()
	if err != nil {
		return err
	} else if _, ok := frame.(*readyFrame); !ok {
		return fmt.Errorf("unexpected frame in response to register: got %T: %v\n", frame, frame)
	}

	return nil
}

func (c *controlConn) reconnect(refreshring bool) {
	// TODO: simplify this function, use session.ring to get hosts instead of the
	// connection pool
	if !atomic.CompareAndSwapInt64(&c.connecting, 0, 1) {
		return
	}

	success := false
	defer func() {
		// debounce reconnect a little
		if success {
			go func() {
				time.Sleep(500 * time.Millisecond)
				atomic.StoreInt64(&c.connecting, 0)
			}()
		} else {
			atomic.StoreInt64(&c.connecting, 0)
		}
	}()

	addr := c.addr()
	oldConn := c.conn.Load().(*Conn)
	if oldConn != nil {
		oldConn.Close()
	}

	var newConn *Conn
	if addr != "" {
		// try to connect to the old host
		conn, err := c.session.connect(addr, c)
		if err != nil {
			// host is dead
			// TODO: this is replicated in a few places
			ip, portStr, _ := net.SplitHostPort(addr)
			port, _ := strconv.Atoi(portStr)
			c.session.handleNodeDown(net.ParseIP(ip), port)
		} else {
			newConn = conn
		}
	}

	// TODO: should have our own roundrobbin for hosts so that we can try each
	// in succession and guantee that we get a different host each time.
	if newConn == nil {
		_, conn := c.session.pool.Pick(nil)
		if conn == nil {
			return
		}

		if conn == nil {
			return
		}

		var err error
		newConn, err = c.session.connect(conn.addr, c)
		if err != nil {
			// TODO: add log handler for things like this
			return
		}
	}

	if err := c.registerEvents(newConn); err != nil {
		// TODO: handle this case better
		newConn.Close()
		log.Printf("gocql: control unable to register events: %v\n", err)
		return
	}

	c.conn.Store(newConn)
	success = true

	if refreshring {
		c.session.hostSource.refreshRing()
	}
}

func (c *controlConn) HandleError(conn *Conn, err error, closed bool) {
	if !closed {
		return
	}

	oldConn := c.conn.Load().(*Conn)
	if oldConn != conn {
		return
	}

	c.reconnect(true)
}

func (c *controlConn) writeFrame(w frameWriter) (frame, error) {
	conn := c.conn.Load().(*Conn)
	if conn == nil {
		return nil, errNoControl
	}

	framer, err := conn.exec(w, nil)
	if err != nil {
		return nil, err
	}

	return framer.parseFrame()
}

func (c *controlConn) withConn(fn func(*Conn) *Iter) *Iter {
	const maxConnectAttempts = 5
	connectAttempts := 0

	for i := 0; i < maxConnectAttempts; i++ {
		conn := c.conn.Load().(*Conn)
		if conn == nil {
			if connectAttempts > maxConnectAttempts {
				break
			}

			connectAttempts++

			c.reconnect(false)
			continue
		}

		return fn(conn)
	}

	return &Iter{err: errNoControl}
}

// query will return nil if the connection is closed or nil
func (c *controlConn) query(statement string, values ...interface{}) (iter *Iter) {
	q := c.session.Query(statement, values...).Consistency(One)

	for {
		iter = c.withConn(func(conn *Conn) *Iter {
			return conn.executeQuery(q)
		})

		q.attempts++
		if iter.err == nil || !c.retry.Attempt(q) {
			break
		}
	}

	return
}

func (c *controlConn) fetchHostInfo(addr net.IP, port int) (*HostInfo, error) {
	// TODO(zariel): we should probably move this into host_source or atleast
	// share code with it.
	hostname, _, err := net.SplitHostPort(c.addr())
	if err != nil {
		return nil, fmt.Errorf("unable to fetch host info, invalid conn addr: %q: %v", c.addr(), err)
	}

	isLocal := hostname == addr.String()

	var fn func(*HostInfo) error

	if isLocal {
		fn = func(host *HostInfo) error {
			// TODO(zariel): should we fetch rpc_address from here?
			iter := c.query("SELECT data_center, rack, host_id, tokens, release_version FROM system.local WHERE key='local'")
			iter.Scan(&host.dataCenter, &host.rack, &host.hostId, &host.tokens, &host.version)
			return iter.Close()
		}
	} else {
		fn = func(host *HostInfo) error {
			// TODO(zariel): should we fetch rpc_address from here?
			iter := c.query("SELECT data_center, rack, host_id, tokens, release_version FROM system.peers WHERE peer=?", addr)
			iter.Scan(&host.dataCenter, &host.rack, &host.hostId, &host.tokens, &host.version)
			return iter.Close()
		}
	}

	host := &HostInfo{
		port: port,
	}

	if err := fn(host); err != nil {
		return nil, err
	}
	host.peer = addr.String()

	return host, nil
}

func (c *controlConn) awaitSchemaAgreement() error {
	return c.withConn(func(conn *Conn) *Iter {
		return &Iter{err: conn.awaitSchemaAgreement()}
	}).err
}

func (c *controlConn) addr() string {
	conn := c.conn.Load().(*Conn)
	if conn == nil {
		return ""
	}
	return conn.addr
}

func (c *controlConn) close() {
	// TODO: handle more gracefully
	close(c.quit)
	c.closeWg.Wait()
	conn := c.conn.Load().(*Conn)
	if conn != nil {
		conn.Close()
	}
}

var errNoControl = errors.New("gocql: no control connection available")
<|endoftext|>


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

package router

import (
	"compress/gzip"
	"encoding/json"
	"net/http"
	"strings"
	"time"
)

// Default content types
const (
	// MIMEJSON - "Content-type" for JSON
	MIMEJSON = "application/json"
	// MIMETEXT - "Content-type" for TEXT
	MIMETEXT = "text/plain"
)

// Control allows us to pass variables between middleware,
// assign Http codes and render a Body.
type Control struct {

	// Request is an adapter which allows the usage of a http.Request as standard request
	Request *http.Request

	// Writer is an adapter which allows the usage of a http.ResponseWriter as standard writer
	Writer http.ResponseWriter

	// User content type
	ContentType string

	// Code of HTTP status
	code int

	// CompactJSON propery defines JSON output format (default is not compact)
	compactJSON bool

	// Params is set of parameters
	Params []Param

	// timer used to calculate a elapsed time for handler and writing it in a response
	timer time.Time
}

// Param is a URL parameter which represents as key and value.
type Param struct {
	Key   string
	Value string
}

// Header is used to prepare a JSON header with duration triggered by UserTimer() method
type Header struct {
	Duration time.Duration `json:"duration"`
	Took     string        `json:"took"`
	Data     interface{}   `json:"data"`
}

// Get returns the first value associated with the given name.
// If there are no values associated with the key, an empty string is returned.
func (c *Control) Get(name string) string {
	for idx := range c.Params {
		if c.Params[idx].Key == name {
			return c.Params[idx].Value
		}
	}

	return c.Request.URL.Query().Get(name)
}

// Set adds new parameters which represents as set of key/value.
func (c *Control) Set(params []Param) {
	c.Params = append(c.Params, params...)
}

// Code assigns http status code, which returns on http request
func (c *Control) Code(code int) *Control {
	if code >= 200 && code < 600 {
		c.code = code
	}
	return c
}

// CompactJSON change JSON output format (default mode is false)
func (c *Control) CompactJSON(mode bool) {
	c.compactJSON = mode
}

// UseTimer allow caalculate elapsed time of request handling
func (c *Control) UseTimer() {
	c.timer = time.Now()
}

// Body renders the given data into the response body
func (c *Control) Body(data interface{}) {
	var content []byte

	if str, ok := data.(string); ok {
		content = []byte(str)
		if c.ContentType != "" {
			c.Writer.Header().Add("Content-type", c.ContentType)
		} else {
			c.Writer.Header().Add("Content-type", MIMETEXT)
		}
	} else {
		if !c.timer.IsZero() {
			took := time.Now()
			data = &Header{Duration: took.Sub(c.timer), Took: took.Sub(c.timer).String(), Data: data}
		}
		var err error
		if c.compactJSON {
			content, err = json.Marshal(data)
		} else {
			content, err = json.MarshalIndent(data, "", "  ")
		}
		if err != nil {
			c.Writer.WriteHeader(http.StatusInternalServerError)
			return
		}
		c.Writer.Header().Add("Content-type", MIMEJSON)
	}
	if strings.Contains(c.Request.Header.Get("Accept-Encoding"), "gzip") {
		c.Writer.Header().Add("Content-Encoding", "gzip")
		if c.code > 0 {
			c.Writer.WriteHeader(c.code)
		}
		gz := gzip.NewWriter(c.Writer)
		gz.Write(content)
		gz.Close()
	} else {
		if c.code > 0 {
			c.Writer.WriteHeader(c.code)
		}
		c.Writer.Write(content)
	}
}
<commit_msg>Google json structure<commit_after>// Copyright 2015 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package router

import (
	"compress/gzip"
	"encoding/json"
	"net/http"
	"strings"
	"time"
)

// Default content types
const (
	// MIMEJSON - "Content-type" for JSON
	MIMEJSON = "application/json"
	// MIMETEXT - "Content-type" for TEXT
	MIMETEXT = "text/plain"
)

// Control allows us to pass variables between middleware,
// assign Http codes and render a Body.
type Control struct {

	// Request is an adapter which allows the usage of a http.Request as standard request
	Request *http.Request

	// Writer is an adapter which allows the usage of a http.ResponseWriter as standard writer
	Writer http.ResponseWriter

	// User content type
	ContentType string

	// Code of HTTP status
	code int

	// compactJSON propery defines JSON output format (default is not compact)
	compactJSON bool

	// if used, json header shows meta data
	useMetaData bool

	// header with metadata
	header Header

	// errors
	errorHeader ErrorHeader
	errors      []Error

	// Params is set of parameters
	Params []Param

	// timer used to calculate a elapsed time for handler and writing it in a response
	timer time.Time
}

// Param is a URL parameter which represents as key and value.
type Param struct {
	Key   string
	Value string
}

// Header is used to prepare a JSON header with meta data
type Header struct {
	Duration   time.Duration `json:"duration,omitempty"`
	Took       string        `json:"took,omitempty"`
	APIVersion string        `json:"apiVersion,omitempty"`
	Context    string        `json:"context,omitempty"`
	ID         string        `json:"id,omitempty"`
	Method     string        `json:"method,omitempty"`
	Params     interface{}   `json:"params,omitempty"`
	Data       interface{}   `json:"data,omitempty"`
	Error      interface{}   `json:"error,omitempty"`
}

type ErrorHeader struct {
	Code    uint16      `json:"code,omitempty"`
	Message string      `json:"message,omitempty"`
	Errors  interface{} `json:"errors,omitempty"`
}

type Error struct {
	Domain       string `json:"domain,omitempty"`
	Reason       string `json:"reason,omitempty"`
	Location     string `json:"location,omitempty"`
	LocationType string `json:"locationType,omitempty"`
	ExtendedHelp string `json:"extendedHelp,omitempty"`
	SendReport   string `json:"sendReport,omitempty"`
}

// Get returns the first value associated with the given name.
// If there are no values associated with the key, an empty string is returned.
func (c *Control) Get(name string) string {
	for idx := range c.Params {
		if c.Params[idx].Key == name {
			return c.Params[idx].Value
		}
	}

	return c.Request.URL.Query().Get(name)
}

// Set adds new parameters which represents as set of key/value.
func (c *Control) Set(params []Param) {
	c.Params = append(c.Params, params...)
}

// Code assigns http status code, which returns on http request
func (c *Control) Code(code int) *Control {
	if code >= 200 && code < 600 {
		c.code = code
	}
	return c
}

// CompactJSON change JSON output format (default mode is false)
func (c *Control) CompactJSON(mode bool) *Control {
	c.compactJSON = mode
	return c
}

// UseMetaData shows meta data in JSON Header
func (c *Control) UseMetaData() *Control {
	c.useMetaData = true
	return c
}

// APIVersion add API version meta data
func (c *Control) APIVersion(version string) *Control {
	c.useMetaData = true
	c.header.APIVersion = version
	return c
}

// Context add context meta data
func (c *Control) Context(context string) *Control {
	c.useMetaData = true
	c.header.Context = context
	return c
}

// ID add id meta data
func (c *Control) ID(id string) *Control {
	c.useMetaData = true
	c.header.ID = id
	return c
}

// Method add method meta data
func (c *Control) Method(method string) *Control {
	c.useMetaData = true
	c.header.Method = method
	return c
}

func (c *Control) produceError(code uint16, message string) *Control {
	c.useMetaData = true
	c.errorHeader.Code = code
	c.errorHeader.Message = message
	return c
}

func (c *Control) AddError(errors ...Error) *Control {
	c.useMetaData = true
	c.errors = append(c.errors, errors...)
	return c
}

// UseTimer allow caalculate elapsed time of request handling
func (c *Control) UseTimer() {
	c.useMetaData = true
	c.timer = time.Now()
}

// Body renders the given data into the response body
func (c *Control) Body(data interface{}) {
	var content []byte

	if str, ok := data.(string); ok {
		content = []byte(str)
		if c.ContentType != "" {
			c.Writer.Header().Add("Content-type", c.ContentType)
		} else {
			c.Writer.Header().Add("Content-type", MIMETEXT)
		}
	} else {
		if c.useMetaData {
			c.header.Data = data
			if !c.timer.IsZero() {
				took := time.Now()
				c.header.Duration = took.Sub(c.timer)
				c.header.Took = took.Sub(c.timer).String()
			}
			if len(c.Params) > 0 {
				c.header.Params = c.Params
			}
			if c.errorHeader.Code != 0 || c.errorHeader.Message != "" || len(c.errors) > 0 {
				if len(c.errors) > 0 {
					c.errorHeader.Errors = c.errors
				}
				c.header.Error = c.errorHeader
			}
			data = c.header
		}
		var err error
		if c.compactJSON {
			content, err = json.Marshal(data)
		} else {
			content, err = json.MarshalIndent(data, "", "  ")
		}
		if err != nil {
			c.Writer.WriteHeader(http.StatusInternalServerError)
			return
		}
		c.Writer.Header().Add("Content-type", MIMEJSON)
	}
	if strings.Contains(c.Request.Header.Get("Accept-Encoding"), "gzip") {
		c.Writer.Header().Add("Content-Encoding", "gzip")
		if c.code > 0 {
			c.Writer.WriteHeader(c.code)
		}
		gz := gzip.NewWriter(c.Writer)
		gz.Write(content)
		gz.Close()
	} else {
		if c.code > 0 {
			c.Writer.WriteHeader(c.code)
		}
		c.Writer.Write(content)
	}
}
<|endoftext|>