<commit_before>package drivers

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type BaseDriver struct {
	Builtin      bool
	URL          string
	DriverHash   string
	DriverName   string
	BinaryPrefix string
}

func (d *BaseDriver) Name() string {
	return d.DriverName
}

func (d *BaseDriver) Hash() string {
	return d.DriverHash
}

func (d *BaseDriver) Checksum() string {
	return d.DriverName
}

func (d *BaseDriver) FriendlyName() string {
	return strings.TrimPrefix(d.DriverName, d.BinaryPrefix)
}

func (d *BaseDriver) Remove() error {
	cacheFilePrefix := d.cacheFile()
	content, err := ioutil.ReadFile(cacheFilePrefix)
	if os.IsNotExist(err) {
		return nil
	}

	if err != nil {
		return err
	}

	dest := path.Join(binDir(), string(content))
	os.Remove(dest)
	os.Remove(cacheFilePrefix + "-" + string(content))
	os.Remove(cacheFilePrefix)

	return nil
}

func (d *BaseDriver) Stage() error {
	if err := d.getError(); err != nil {
		return err
	}

	return d.setError(d.stage())
}

func (d *BaseDriver) setError(err error) error {
	errFile := d.cacheFile() + ".error"

	if err != nil {
		os.MkdirAll(path.Dir(errFile), 0700)
		ioutil.WriteFile(errFile, []byte(err.Error()), 0600)
	}
	return err
}

func (d *BaseDriver) getError() error {
	errFile := d.cacheFile() + ".error"

	if content, err := ioutil.ReadFile(errFile); err == nil {
		logrus.Errorf("Returning previous error: %s", content)
		d.ClearError()
		return errors.New(string(content))
	}

	return nil
}

func (d *BaseDriver) ClearError() {
	errFile := d.cacheFile() + ".error"
	os.Remove(errFile)
}

func (d *BaseDriver) stage() error {
	if d.Builtin {
		return nil
	}

	cacheFilePrefix := d.cacheFile()

	driverName, err := isInstalled(cacheFilePrefix)
	if err != nil || driverName != "" {
		d.DriverName = driverName
		return err
	}

	tempFile, err := ioutil.TempFile("", "machine-driver")
	if err != nil {
		return err
	}
	defer os.Remove(tempFile.Name())
	defer tempFile.Close()

	hasher, err := getHasher(d.DriverHash)
	if err != nil {
		return err
	}

	downloadDest := io.Writer(tempFile)
	if hasher != nil {
		downloadDest = io.MultiWriter(tempFile, hasher)
	}

	if err := d.download(downloadDest); err != nil {
		return err
	}

	if got, ok := compare(hasher, d.DriverHash); !ok {
		return fmt.Errorf("hash does not match, got %s, expected %s", got, d.DriverHash)
	}

	if err := tempFile.Close(); err != nil {
		return err
	}

	driverName, err = d.copyBinary(cacheFilePrefix, tempFile.Name())
	if err != nil {
		return err
	}

	d.DriverName = driverName
	return nil
}

func (d *BaseDriver) Exists() bool {
	if d.DriverName == "" {
		return false
	}
	if d.Builtin {
		return true
	}
	binaryPath := path.Join(binDir(), d.DriverName)
	_, err := os.Stat(binaryPath)
	return err == nil
}

func isElf(input string) bool {
	f, err := os.Open(input)
	if err != nil {
		return false
	}
	defer f.Close()

	elf := make([]byte, 4)
	if _, err := f.Read(elf); err != nil {
		return false
	}
	//support unix binary and mac-os binary mach-o
	return bytes.Compare(elf, []byte{0x7f, 0x45, 0x4c, 0x46}) == 0 || bytes.Compare(elf, []byte{0xcf, 0xfa, 0xed, 0xfe}) == 0
}

func (d *BaseDriver) copyBinary(cacheFile, input string) (string, error) {
	temp, err := ioutil.TempDir("", "machine-driver-extract")
	if err != nil {
		return "", err
	}
	defer os.RemoveAll(temp)

	file := ""
	driverName := ""

	if isElf(input) {
		file = input
		u, err := url.Parse(d.URL)
		if err != nil {
			return "", err
		}

		if !strings.HasPrefix(path.Base(u.Path), d.BinaryPrefix) {
			return "", fmt.Errorf("invalid URL %s, path should be of the format %s*", d.URL, d.BinaryPrefix)
		}

		s := strings.TrimPrefix(path.Base(u.Path), d.BinaryPrefix)
		name := strings.FieldsFunc(s, func(r rune) bool {
			return r == '-' || r == '_' || r == '.'
		})[0]

		if name == "" {
			return "", fmt.Errorf("invalid URL %s, NAME is empty, path should be of the format %sNAME", d.URL, d.BinaryPrefix)
		}
		driverName = d.BinaryPrefix + name
	} else {
		if err := exec.Command("tar", "xvf", input, "-C", temp).Run(); err != nil {
			if err := exec.Command("unzip", "-o", input, "-d", temp).Run(); err != nil {
				return "", fmt.Errorf("failed to extract")
			}
		}
	}

	filepath.Walk(temp, filepath.WalkFunc(func(p string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}

		if strings.HasPrefix(path.Base(p), d.BinaryPrefix) {
			file = p
		}

		return nil
	}))

	if file == "" {
		return "", fmt.Errorf("failed to find machine driver in archive. There must be a file of form docker-machine-driver*")
	}

	if driverName == "" {
		driverName = path.Base(file)
	}
	f, err := os.Open(file)
	if err != nil {
		return "", err
	}
	defer f.Close()

	if err := os.MkdirAll(path.Dir(cacheFile), 0755); err != nil {
		return "", err
	}

	driverName = strings.ToLower(driverName)
	dest, err := os.Create(cacheFile + "-" + driverName)
	if err != nil {
		return "", err
	}
	defer dest.Close()

	if _, err := io.Copy(dest, f); err != nil {
		return "", err
	}

	logrus.Infof("Found driver %s", driverName)
	return driverName, ioutil.WriteFile(cacheFile, []byte(driverName), 0644)
}

func (d *BaseDriver) srcBinName() string {
	return d.cacheFile() + "-" + d.DriverName
}

func binDir() string {
	dest := os.Getenv("GMS_BIN_DIR")
	if dest != "" {
		return dest
	}
	return "./management-state/bin"
}

func compare(hash hash.Hash, value string) (string, bool) {
	if hash == nil {
		return "", true
	}

	got := hex.EncodeToString(hash.Sum([]byte{}))
	expected := strings.TrimSpace(strings.ToLower(value))

	return got, got == expected
}

func getHasher(hash string) (hash.Hash, error) {
	switch len(hash) {
	case 0:
		return nil, nil
	case 32:
		return md5.New(), nil
	case 40:
		return sha1.New(), nil
	case 64:
		return sha256.New(), nil
	case 128:
		return sha512.New(), nil
	}

	return nil, fmt.Errorf("invalid hash format: %s", hash)
}

func (d *BaseDriver) download(dest io.Writer) error {
	logrus.Infof("Download %s", d.URL)
	resp, err := http.Get(d.URL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	_, err = io.Copy(dest, resp.Body)
	return err
}

func (d *BaseDriver) cacheFile() string {
	key := sha256Bytes([]byte(d.URL + d.DriverHash))

	base := os.Getenv("CATTLE_HOME")
	if base == "" {
		base = "./management-state"
	}

	return path.Join(base, "machine-drivers", key)
}

func isInstalled(file string) (string, error) {
	content, err := ioutil.ReadFile(file)
	if os.IsNotExist(err) {
		return "", nil
	}
	return strings.ToLower(strings.TrimSpace(string(content))), err
}

func sha256Bytes(content []byte) string {
	hash := sha256.New()
	io.Copy(hash, bytes.NewBuffer(content))
	return hex.EncodeToString(hash.Sum([]byte{}))
}
<commit_msg>Parameterize driver error<commit_after>package drivers

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type BaseDriver struct {
	Builtin      bool
	URL          string
	DriverHash   string
	DriverName   string
	BinaryPrefix string
}

func (d *BaseDriver) Name() string {
	return d.DriverName
}

func (d *BaseDriver) Hash() string {
	return d.DriverHash
}

func (d *BaseDriver) Checksum() string {
	return d.DriverName
}

func (d *BaseDriver) FriendlyName() string {
	return strings.TrimPrefix(d.DriverName, d.BinaryPrefix)
}

func (d *BaseDriver) Remove() error {
	cacheFilePrefix := d.cacheFile()
	content, err := ioutil.ReadFile(cacheFilePrefix)
	if os.IsNotExist(err) {
		return nil
	}

	if err != nil {
		return err
	}

	dest := path.Join(binDir(), string(content))
	os.Remove(dest)
	os.Remove(cacheFilePrefix + "-" + string(content))
	os.Remove(cacheFilePrefix)

	return nil
}

func (d *BaseDriver) Stage() error {
	if err := d.getError(); err != nil {
		return err
	}

	return d.setError(d.stage())
}

func (d *BaseDriver) setError(err error) error {
	errFile := d.cacheFile() + ".error"

	if err != nil {
		os.MkdirAll(path.Dir(errFile), 0700)
		ioutil.WriteFile(errFile, []byte(err.Error()), 0600)
	}
	return err
}

func (d *BaseDriver) getError() error {
	errFile := d.cacheFile() + ".error"

	if content, err := ioutil.ReadFile(errFile); err == nil {
		logrus.Errorf("Returning previous error: %s", content)
		d.ClearError()
		return errors.New(string(content))
	}

	return nil
}

func (d *BaseDriver) ClearError() {
	errFile := d.cacheFile() + ".error"
	os.Remove(errFile)
}

func (d *BaseDriver) stage() error {
	if d.Builtin {
		return nil
	}

	cacheFilePrefix := d.cacheFile()

	driverName, err := isInstalled(cacheFilePrefix)
	if err != nil || driverName != "" {
		d.DriverName = driverName
		return err
	}

	tempFile, err := ioutil.TempFile("", "machine-driver")
	if err != nil {
		return err
	}
	defer os.Remove(tempFile.Name())
	defer tempFile.Close()

	hasher, err := getHasher(d.DriverHash)
	if err != nil {
		return err
	}

	downloadDest := io.Writer(tempFile)
	if hasher != nil {
		downloadDest = io.MultiWriter(tempFile, hasher)
	}

	if err := d.download(downloadDest); err != nil {
		return err
	}

	if got, ok := compare(hasher, d.DriverHash); !ok {
		return fmt.Errorf("hash does not match, got %s, expected %s", got, d.DriverHash)
	}

	if err := tempFile.Close(); err != nil {
		return err
	}

	driverName, err = d.copyBinary(cacheFilePrefix, tempFile.Name())
	if err != nil {
		return err
	}

	d.DriverName = driverName
	return nil
}

func (d *BaseDriver) Exists() bool {
	if d.DriverName == "" {
		return false
	}
	if d.Builtin {
		return true
	}
	binaryPath := path.Join(binDir(), d.DriverName)
	_, err := os.Stat(binaryPath)
	return err == nil
}

func isElf(input string) bool {
	f, err := os.Open(input)
	if err != nil {
		return false
	}
	defer f.Close()

	elf := make([]byte, 4)
	if _, err := f.Read(elf); err != nil {
		return false
	}
	//support unix binary and mac-os binary mach-o
	return bytes.Compare(elf, []byte{0x7f, 0x45, 0x4c, 0x46}) == 0 || bytes.Compare(elf, []byte{0xcf, 0xfa, 0xed, 0xfe}) == 0
}

func (d *BaseDriver) copyBinary(cacheFile, input string) (string, error) {
	temp, err := ioutil.TempDir("", "machine-driver-extract")
	if err != nil {
		return "", err
	}
	defer os.RemoveAll(temp)

	file := ""
	driverName := ""

	if isElf(input) {
		file = input
		u, err := url.Parse(d.URL)
		if err != nil {
			return "", err
		}

		if !strings.HasPrefix(path.Base(u.Path), d.BinaryPrefix) {
			return "", fmt.Errorf("invalid URL %s, path should be of the format %s*", d.URL, d.BinaryPrefix)
		}

		s := strings.TrimPrefix(path.Base(u.Path), d.BinaryPrefix)
		name := strings.FieldsFunc(s, func(r rune) bool {
			return r == '-' || r == '_' || r == '.'
		})[0]

		if name == "" {
			return "", fmt.Errorf("invalid URL %s, NAME is empty, path should be of the format %sNAME", d.URL, d.BinaryPrefix)
		}
		driverName = d.BinaryPrefix + name
	} else {
		if err := exec.Command("tar", "xvf", input, "-C", temp).Run(); err != nil {
			if err := exec.Command("unzip", "-o", input, "-d", temp).Run(); err != nil {
				return "", fmt.Errorf("failed to extract")
			}
		}
	}

	filepath.Walk(temp, filepath.WalkFunc(func(p string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}

		if strings.HasPrefix(path.Base(p), d.BinaryPrefix) {
			file = p
		}

		return nil
	}))

	if file == "" {
		return "", fmt.Errorf("failed to find driver in archive. There must be a file of form %s*", d.BinaryPrefix)
	}

	if driverName == "" {
		driverName = path.Base(file)
	}
	f, err := os.Open(file)
	if err != nil {
		return "", err
	}
	defer f.Close()

	if err := os.MkdirAll(path.Dir(cacheFile), 0755); err != nil {
		return "", err
	}

	driverName = strings.ToLower(driverName)
	dest, err := os.Create(cacheFile + "-" + driverName)
	if err != nil {
		return "", err
	}
	defer dest.Close()

	if _, err := io.Copy(dest, f); err != nil {
		return "", err
	}

	logrus.Infof("Found driver %s", driverName)
	return driverName, ioutil.WriteFile(cacheFile, []byte(driverName), 0644)
}

func (d *BaseDriver) srcBinName() string {
	return d.cacheFile() + "-" + d.DriverName
}

func binDir() string {
	dest := os.Getenv("GMS_BIN_DIR")
	if dest != "" {
		return dest
	}
	return "./management-state/bin"
}

func compare(hash hash.Hash, value string) (string, bool) {
	if hash == nil {
		return "", true
	}

	got := hex.EncodeToString(hash.Sum([]byte{}))
	expected := strings.TrimSpace(strings.ToLower(value))

	return got, got == expected
}

func getHasher(hash string) (hash.Hash, error) {
	switch len(hash) {
	case 0:
		return nil, nil
	case 32:
		return md5.New(), nil
	case 40:
		return sha1.New(), nil
	case 64:
		return sha256.New(), nil
	case 128:
		return sha512.New(), nil
	}

	return nil, fmt.Errorf("invalid hash format: %s", hash)
}

func (d *BaseDriver) download(dest io.Writer) error {
	logrus.Infof("Download %s", d.URL)
	resp, err := http.Get(d.URL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	_, err = io.Copy(dest, resp.Body)
	return err
}

func (d *BaseDriver) cacheFile() string {
	key := sha256Bytes([]byte(d.URL + d.DriverHash))

	base := os.Getenv("CATTLE_HOME")
	if base == "" {
		base = "./management-state"
	}

	return path.Join(base, "machine-drivers", key)
}

func isInstalled(file string) (string, error) {
	content, err := ioutil.ReadFile(file)
	if os.IsNotExist(err) {
		return "", nil
	}
	return strings.ToLower(strings.TrimSpace(string(content))), err
}

func sha256Bytes(content []byte) string {
	hash := sha256.New()
	io.Copy(hash, bytes.NewBuffer(content))
	return hex.EncodeToString(hash.Sum([]byte{}))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"runtime"
	"time"

	"github.com/goamz/goamz/aws"
	lsdb "github.com/goamz/goamz/exp/sdb"
	ls3 "github.com/goamz/goamz/s3"
)

var (
	sdb    *lsdb.SDB
	domain *lsdb.Domain
	bucket *ls3.Bucket
)

func WriteJSON(w http.ResponseWriter, data interface{}) {
	js, err := json.Marshal(data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func blobsHandler(w http.ResponseWriter, r *http.Request) {
	start := r.URL.Query().Get("start")
	if start == "" {
		start = "0"
	}
	end := r.URL.Query().Get("end")
	if end == "" {
		end = time.Now().UTC().Format(time.RFC3339Nano)
	}
	query, err := sdb.Select(fmt.Sprintf("SELECT * FROM s3indextest2 where time > '%s' and time <= '%s' order by time asc", start, end), false)
	if err != nil {
		log.Printf("err:%v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	res := []map[string]string{}
	for _, i := range query.Items {
		m := map[string]string{
			"name": i.Name,
		}
		for _, attr := range i.Attrs {
			m[attr.Name] = attr.Value
		}
		res = append(res, m)
	}
	WriteJSON(w, map[string]interface{}{
		"data":  res,
		"start": start,
		"end":   end,
	})
}

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "POST":
		//parse the multipart form in the request
		mr, err := r.MultipartReader()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		for {
			part, err := mr.NextPart()
			if err == io.EOF {
				break
			}
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			hash := part.FormName()
			var data bytes.Buffer
			data.ReadFrom(part)
			if err := bucket.Put(hash, data.Bytes(), "", ls3.Private, ls3.Options{}); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			item := domain.Item(hash)
			pa := &lsdb.PutAttrs{}
			pa.Add("time", time.Now().UTC().Format(time.RFC3339Nano))
			if _, err := item.PutAttrs(pa); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func main() {
	auth, err := aws.EnvAuth()
	if err != nil {
		panic(err)
	}
	sdb = lsdb.New(auth, aws.USEast)
	s3 := ls3.New(auth, aws.USEast)
	domain = sdb.Domain("s3indextest2")
	if _, err := domain.CreateDomain(); err != nil {
		panic(err)
	}
	bucket = s3.Bucket("thomassileo.s3indextexst")
	if err := bucket.PutBucket(ls3.Private); err != nil {
		panic(err)
	}
	//r, err := sdb.Select("SELECT * FROM s3indextest2 where time > '2010' order by time asc", false)
	//log.Printf("%+v, %+v", r, err)
	//i := d.Item("ok")
	//pa := &lsdb.PutAttrs{}
	//pa.Add("a", "b")
	//r2, err := i.PutAttrs(pa)
	//r2, err := i.Attrs(nil, false)
	//log.Printf("%v, %v", r2, err)
	runtime.GOMAXPROCS(runtime.NumCPU())
	http.HandleFunc("/api/blobs", blobsHandler)
	http.HandleFunc("/api/upload", uploadHandler)
	http.ListenAndServe(":8010", nil)
}
<commit_msg>Basic content addressed blob store with working GET<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"runtime"
	"time"

	"github.com/goamz/goamz/aws"
	lsdb "github.com/goamz/goamz/exp/sdb"
	ls3 "github.com/goamz/goamz/s3"
	"github.com/gorilla/mux"
)

var (
	sdb    *lsdb.SDB
	domain *lsdb.Domain
	bucket *ls3.Bucket
)

func addSlash(s string) string {
	return s[0:2] + "/" + s[2:40]
}

func WriteJSON(w http.ResponseWriter, data interface{}) {
	js, err := json.Marshal(data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func blobHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	blob, err := bucket.Get(addSlash(vars["hash"]))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(blob)

}
func blobsHandler(w http.ResponseWriter, r *http.Request) {
	start := r.URL.Query().Get("start")
	if start == "" {
		start = "0"
	}
	end := r.URL.Query().Get("end")
	if end == "" {
		end = time.Now().UTC().Format(time.RFC3339Nano)
	}
	query, err := sdb.Select(fmt.Sprintf("SELECT * FROM s3indextest4 where time > '%s' and time <= '%s' order by time asc", start, end), false)
	if err != nil {
		log.Printf("err:%v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	res := []map[string]string{}
	for _, i := range query.Items {
		m := map[string]string{
			"name": i.Name,
		}
		for _, attr := range i.Attrs {
			m[attr.Name] = attr.Value
		}
		res = append(res, m)
	}
	WriteJSON(w, map[string]interface{}{
		"data":  res,
		"start": start,
		"end":   end,
	})
}

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "POST":
		//parse the multipart form in the request
		mr, err := r.MultipartReader()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		for {
			part, err := mr.NextPart()
			if err == io.EOF {
				break
			}
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			hash := part.FormName()
			var data bytes.Buffer
			data.ReadFrom(part)
			if err := bucket.Put(addSlash(hash), data.Bytes(), "", ls3.Private, ls3.Options{}); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			item := domain.Item(hash)
			pa := &lsdb.PutAttrs{}
			pa.Add("time", time.Now().UTC().Format(time.RFC3339Nano))
			if _, err := item.PutAttrs(pa); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func main() {
	auth, err := aws.EnvAuth()
	if err != nil {
		panic(err)
	}
	sdb = lsdb.New(auth, aws.USEast)
	s3 := ls3.New(auth, aws.USEast)
	domain = sdb.Domain("s3indextest4")
	if _, err := domain.CreateDomain(); err != nil {
		panic(err)
	}
	bucket = s3.Bucket("thomassileo.s3indextexst2")
	if err := bucket.PutBucket(ls3.Private); err != nil {
		panic(err)
	}
	runtime.GOMAXPROCS(runtime.NumCPU())
	r := mux.NewRouter()
	r.HandleFunc("/api/blobs", blobsHandler)
	r.HandleFunc("/api/blob/{hash}", blobHandler)
	r.HandleFunc("/api/upload", uploadHandler)
	http.Handle("/", r)
	http.ListenAndServe(":8010", nil)
}
<|endoftext|>


<commit_before>package gcfg

import (
	"fmt"
	"math/big"
	"reflect"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/baobabus/gcfg/types"
)

type tag struct {
	ident   string
	intMode string
}

func newTag(ts string) tag {
	t := tag{}
	s := strings.Split(ts, ",")
	t.ident = s[0]
	for _, tse := range s[1:] {
		if strings.HasPrefix(tse, "int=") {
			t.intMode = tse[len("int="):]
		}
	}
	return t
}

func fieldFold(v reflect.Value, name string) (reflect.Value, tag) {
	var n string
	r0, _ := utf8.DecodeRuneInString(name)
	if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) {
		n = "X"
	}
	n += strings.Replace(name, "-", "_", -1)
	f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool {
		if !v.FieldByName(fieldName).CanSet() {
			return false
		}
		f, _ := v.Type().FieldByName(fieldName)
		t := newTag(f.Tag.Get("gcfg"))
		if t.ident != "" {
			return strings.EqualFold(t.ident, name)
		}
		return strings.EqualFold(n, fieldName)
	})
	if !ok {
		return reflect.Value{}, tag{}
	}
	return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg"))
}

type setter func(destp interface{}, blank bool, val string, t tag) error

var errUnsupportedType = fmt.Errorf("unsupported type")
var errBlankUnsupported = fmt.Errorf("blank value not supported for type")

var setters = []setter{
	typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
}

func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error {
	dtu, ok := d.(textUnmarshaler)
	if !ok {
		return errUnsupportedType
	}
	if blank {
		return errBlankUnsupported
	}
	return dtu.UnmarshalText([]byte(val))
}

func boolSetter(d interface{}, blank bool, val string, t tag) error {
	if blank {
		reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true))
		return nil
	}
	b, err := types.ParseBool(val)
	if err == nil {
		reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b))
	}
	return err
}

func intMode(mode string) types.IntMode {
	var m types.IntMode
	if strings.ContainsAny(mode, "dD") {
		m |= types.Dec
	}
	if strings.ContainsAny(mode, "hH") {
		m |= types.Hex
	}
	if strings.ContainsAny(mode, "oO") {
		m |= types.Oct
	}
	return m
}

var typeModes = map[reflect.Type]types.IntMode{
	reflect.TypeOf(int(0)):    types.Dec | types.Hex,
	reflect.TypeOf(int8(0)):   types.Dec | types.Hex,
	reflect.TypeOf(int16(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int32(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int64(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint(0)):   types.Dec | types.Hex,
	reflect.TypeOf(uint8(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint64(0)): types.Dec | types.Hex,
	// use default mode (allow dec/hex/oct) for uintptr type
	reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
}

func intModeDefault(t reflect.Type) types.IntMode {
	m, ok := typeModes[t]
	if !ok {
		m = types.Dec | types.Hex | types.Oct
	}
	return m
}

func intSetter(d interface{}, blank bool, val string, t tag) error {
	if blank {
		return errBlankUnsupported
	}
	mode := intMode(t.intMode)
	if mode == 0 {
		mode = intModeDefault(reflect.TypeOf(d).Elem())
	}
	return types.ParseInt(d, val, mode)
}

func stringSetter(d interface{}, blank bool, val string, t tag) error {
	if blank {
		return errBlankUnsupported
	}
	dsp, ok := d.(*string)
	if !ok {
		return errUnsupportedType
	}
	*dsp = val
	return nil
}

var kindSetters = map[reflect.Kind]setter{
	reflect.String:  stringSetter,
	reflect.Bool:    boolSetter,
	reflect.Int:     intSetter,
	reflect.Int8:    intSetter,
	reflect.Int16:   intSetter,
	reflect.Int32:   intSetter,
	reflect.Int64:   intSetter,
	reflect.Uint:    intSetter,
	reflect.Uint8:   intSetter,
	reflect.Uint16:  intSetter,
	reflect.Uint32:  intSetter,
	reflect.Uint64:  intSetter,
	reflect.Uintptr: intSetter,
}

var typeSetters = map[reflect.Type]setter{
	reflect.TypeOf(big.Int{}): intSetter,
}

func typeSetter(d interface{}, blank bool, val string, tt tag) error {
	t := reflect.ValueOf(d).Type().Elem()
	setter, ok := typeSetters[t]
	if !ok {
		return errUnsupportedType
	}
	return setter(d, blank, val, tt)
}

func kindSetter(d interface{}, blank bool, val string, tt tag) error {
	k := reflect.ValueOf(d).Type().Elem().Kind()
	setter, ok := kindSetters[k]
	if !ok {
		return errUnsupportedType
	}
	return setter(d, blank, val, tt)
}

func scanSetter(d interface{}, blank bool, val string, tt tag) error {
	if blank {
		return errBlankUnsupported
	}
	return types.ScanFully(d, val, 'v')
}

func set(cfg interface{}, sect, sub, name string, blank bool, value string) error {
	vPCfg := reflect.ValueOf(cfg)
	if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct {
		panic(fmt.Errorf("config must be a pointer to a struct"))
	}
	vCfg := vPCfg.Elem()
	vSect, _ := fieldFold(vCfg, sect)
	if !vSect.IsValid() {
		return fmt.Errorf("invalid section: section %q", sect)
	}
	if vSect.Kind() == reflect.Map {
		vst := vSect.Type()
		if vst.Key().Kind() != reflect.String ||
			vst.Elem().Kind() != reflect.Ptr ||
			vst.Elem().Elem().Kind() != reflect.Struct {
			panic(fmt.Errorf("map field for section must have string keys and "+
				" pointer-to-struct values: section %q", sect))
		}
		if vSect.IsNil() {
			vSect.Set(reflect.MakeMap(vst))
		}
		k := reflect.ValueOf(sub)
		pv := vSect.MapIndex(k)
		if !pv.IsValid() {
			vType := vSect.Type().Elem().Elem()
			pv = reflect.New(vType)
			vSect.SetMapIndex(k, pv)
		}
		vSect = pv.Elem()
	} else if vSect.Kind() != reflect.Struct {
		panic(fmt.Errorf("field for section must be a map or a struct: "+
			"section %q", sect))
	} else if sub != "" {
		return fmt.Errorf("invalid subsection: "+
			"section %q subsection %q", sect, sub)
	}
	vVar, t := fieldFold(vSect, name)
	if !vVar.IsValid() {
		return fmt.Errorf("invalid variable: "+
			"section %q subsection %q variable %q", sect, sub, name)
	}
	// vVal is either single-valued var, or newly allocated value within multi-valued var
	var vVal reflect.Value
	// multi-value if unnamed slice type
	isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice
	if isMulti && blank {
		vVar.Set(reflect.Zero(vVar.Type()))
		return nil
	}
	if isMulti {
		vVal = reflect.New(vVar.Type().Elem()).Elem()
	} else {
		vVal = vVar
	}
	isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr
	isNew := isDeref && vVal.IsNil()
	// vAddr is address of value to set (dereferenced & allocated as needed)
	var vAddr reflect.Value
	switch {
	case isNew:
		vAddr = reflect.New(vVal.Type().Elem())
	case isDeref && !isNew:
		vAddr = vVal
	default:
		vAddr = vVal.Addr()
	}
	vAddrI := vAddr.Interface()
	err, ok := error(nil), false
	for _, s := range setters {
		err = s(vAddrI, blank, value, t)
		if err == nil {
			ok = true
			break
		}
		if err != errUnsupportedType {
			return err
		}
	}
	if !ok {
		// in case all setters returned errUnsupportedType
		return err
	}
	if isNew { // set reference if it was dereferenced and newly allocated
		vVal.Set(vAddr)
	}
	if isMulti { // append if multi-valued
		vVar.Set(reflect.Append(vVar, vVal))
	}
	return nil
}
<commit_msg>Support for explicit registration of custom type parsers.<commit_after>package gcfg

import (
	"fmt"
	"math/big"
	"reflect"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/baobabus/gcfg/types"
)

type tag struct {
	ident   string
	intMode string
}

func newTag(ts string) tag {
	t := tag{}
	s := strings.Split(ts, ",")
	t.ident = s[0]
	for _, tse := range s[1:] {
		if strings.HasPrefix(tse, "int=") {
			t.intMode = tse[len("int="):]
		}
	}
	return t
}

func fieldFold(v reflect.Value, name string) (reflect.Value, tag) {
	var n string
	r0, _ := utf8.DecodeRuneInString(name)
	if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) {
		n = "X"
	}
	n += strings.Replace(name, "-", "_", -1)
	f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool {
		if !v.FieldByName(fieldName).CanSet() {
			return false
		}
		f, _ := v.Type().FieldByName(fieldName)
		t := newTag(f.Tag.Get("gcfg"))
		if t.ident != "" {
			return strings.EqualFold(t.ident, name)
		}
		return strings.EqualFold(n, fieldName)
	})
	if !ok {
		return reflect.Value{}, tag{}
	}
	return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg"))
}

type setter func(destp interface{}, blank bool, val string, t tag) error

var errUnsupportedType = fmt.Errorf("unsupported type")
var errBlankUnsupported = fmt.Errorf("blank value not supported for type")

var setters = []setter{
	typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
}

func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error {
	dtu, ok := d.(textUnmarshaler)
	if !ok {
		return errUnsupportedType
	}
	if blank {
		return errBlankUnsupported
	}
	return dtu.UnmarshalText([]byte(val))
}

func boolSetter(d interface{}, blank bool, val string, t tag) error {
	if blank {
		reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true))
		return nil
	}
	b, err := types.ParseBool(val)
	if err == nil {
		reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b))
	}
	return err
}

func intMode(mode string) types.IntMode {
	var m types.IntMode
	if strings.ContainsAny(mode, "dD") {
		m |= types.Dec
	}
	if strings.ContainsAny(mode, "hH") {
		m |= types.Hex
	}
	if strings.ContainsAny(mode, "oO") {
		m |= types.Oct
	}
	return m
}

var typeModes = map[reflect.Type]types.IntMode{
	reflect.TypeOf(int(0)):    types.Dec | types.Hex,
	reflect.TypeOf(int8(0)):   types.Dec | types.Hex,
	reflect.TypeOf(int16(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int32(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int64(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint(0)):   types.Dec | types.Hex,
	reflect.TypeOf(uint8(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint64(0)): types.Dec | types.Hex,
	// use default mode (allow dec/hex/oct) for uintptr type
	reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
}

func intModeDefault(t reflect.Type) types.IntMode {
	m, ok := typeModes[t]
	if !ok {
		m = types.Dec | types.Hex | types.Oct
	}
	return m
}

func intSetter(d interface{}, blank bool, val string, t tag) error {
	if blank {
		return errBlankUnsupported
	}
	mode := intMode(t.intMode)
	if mode == 0 {
		mode = intModeDefault(reflect.TypeOf(d).Elem())
	}
	return types.ParseInt(d, val, mode)
}

func stringSetter(d interface{}, blank bool, val string, t tag) error {
	if blank {
		return errBlankUnsupported
	}
	dsp, ok := d.(*string)
	if !ok {
		return errUnsupportedType
	}
	*dsp = val
	return nil
}

var kindSetters = map[reflect.Kind]setter{
	reflect.String:  stringSetter,
	reflect.Bool:    boolSetter,
	reflect.Int:     intSetter,
	reflect.Int8:    intSetter,
	reflect.Int16:   intSetter,
	reflect.Int32:   intSetter,
	reflect.Int64:   intSetter,
	reflect.Uint:    intSetter,
	reflect.Uint8:   intSetter,
	reflect.Uint16:  intSetter,
	reflect.Uint32:  intSetter,
	reflect.Uint64:  intSetter,
	reflect.Uintptr: intSetter,
}

var typeSetters = map[reflect.Type]setter{
	reflect.TypeOf(big.Int{}): intSetter,
}

func typeSetter(d interface{}, blank bool, val string, tt tag) error {
	t := reflect.ValueOf(d).Type().Elem()
	setter, ok := typeSetters[t]
	if !ok {
		return errUnsupportedType
	}
	return setter(d, blank, val, tt)
}

func kindSetter(d interface{}, blank bool, val string, tt tag) error {
	k := reflect.ValueOf(d).Type().Elem().Kind()
	setter, ok := kindSetters[k]
	if !ok {
		return errUnsupportedType
	}
	return setter(d, blank, val, tt)
}

func scanSetter(d interface{}, blank bool, val string, tt tag) error {
	if blank {
		return errBlankUnsupported
	}
	return types.ScanFully(d, val, 'v')
}

func set(cfg interface{}, sect, sub, name string, blank bool, value string) error {
	vPCfg := reflect.ValueOf(cfg)
	if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct {
		panic(fmt.Errorf("config must be a pointer to a struct"))
	}
	vCfg := vPCfg.Elem()
	vSect, _ := fieldFold(vCfg, sect)
	if !vSect.IsValid() {
		return fmt.Errorf("invalid section: section %q", sect)
	}
	if vSect.Kind() == reflect.Map {
		vst := vSect.Type()
		if vst.Key().Kind() != reflect.String ||
			vst.Elem().Kind() != reflect.Ptr ||
			vst.Elem().Elem().Kind() != reflect.Struct {
			panic(fmt.Errorf("map field for section must have string keys and "+
				" pointer-to-struct values: section %q", sect))
		}
		if vSect.IsNil() {
			vSect.Set(reflect.MakeMap(vst))
		}
		k := reflect.ValueOf(sub)
		pv := vSect.MapIndex(k)
		if !pv.IsValid() {
			vType := vSect.Type().Elem().Elem()
			pv = reflect.New(vType)
			vSect.SetMapIndex(k, pv)
		}
		vSect = pv.Elem()
	} else if vSect.Kind() != reflect.Struct {
		panic(fmt.Errorf("field for section must be a map or a struct: "+
			"section %q", sect))
	} else if sub != "" {
		return fmt.Errorf("invalid subsection: "+
			"section %q subsection %q", sect, sub)
	}
	vVar, t := fieldFold(vSect, name)
	if !vVar.IsValid() {
		return fmt.Errorf("invalid variable: "+
			"section %q subsection %q variable %q", sect, sub, name)
	}
	// vVal is either single-valued var, or newly allocated value within multi-valued var
	var vVal reflect.Value
	// multi-value if unnamed slice type
	isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice
	if isMulti && blank {
		vVar.Set(reflect.Zero(vVar.Type()))
		return nil
	}
	if isMulti {
		vVal = reflect.New(vVar.Type().Elem()).Elem()
	} else {
		vVal = vVar
	}
	isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr
	isNew := isDeref && vVal.IsNil()
	// vAddr is address of value to set (dereferenced & allocated as needed)
	var vAddr reflect.Value
	switch {
	case isNew:
		vAddr = reflect.New(vVal.Type().Elem())
	case isDeref && !isNew:
		vAddr = vVal
	default:
		vAddr = vVal.Addr()
	}
	vAddrI := vAddr.Interface()
	err, ok := error(nil), false
	for _, s := range setters {
		err = s(vAddrI, blank, value, t)
		if err == nil {
			ok = true
			break
		}
		if err != errUnsupportedType {
			return err
		}
	}
	if !ok {
		// in case all setters returned errUnsupportedType
		return err
	}
	if isNew { // set reference if it was dereferenced and newly allocated
		vVal.Set(vAddr)
	}
	if isMulti { // append if multi-valued
		vVar.Set(reflect.Append(vVar, vVal))
	}
	return nil
}

type TypeParser func(blank bool, val string) (interface{}, error)

// Registers type parser function.
func RegisterTypeParser(tgtType reflect.Type, typeParser TypeParser) error {
	typeSetters[tgtType] = func(d interface{}, blank bool, val string, t tag) error {
		v, err := typeParser(blank, val)
		if err == nil {
			sv := reflect.ValueOf(v)
			tv := reflect.ValueOf(d)
			if tv.CanSet() {
				// TODO properly enumerate over Kind
				if sv.Kind() < reflect.Array {
					tv.Set(reflect.ValueOf(v))
				} else {
					tv.Set(reflect.ValueOf(v).Elem())
				}
			} else {
				// TODO properly enumerate over Kind
				if sv.Kind() < reflect.Array {
					tv.Elem().Set(reflect.ValueOf(v))
				} else {
					tv.Elem().Set(reflect.ValueOf(v).Elem())
				}
			}
		}
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Goset is a thread safe SET data structure implementation
package goset

import "sync"

type Set struct {
	m map[interface{}]bool
	l sync.RWMutex // not embedded because we don't expose it
}

// New creates and initialize a new Set. It's accept a variable number of
// arguments to populate the initial set. If nothing passed a Set with zero
// size is created.
func New(items ...interface{}) *Set {
	s := &Set{
		m: make(map[interface{}]bool),
	}

	for _, item := range items {
		s.Add(item)
	}

	return s
}

// Add includes the specified item to the set.
func (s *Set) Add(item interface{}) {
	s.l.Lock()
	defer s.l.Unlock()
	s.m[item] = true
}

// Remove deletes the specified item from the set
func (s *Set) Remove(item interface{}) {
	s.l.Lock()
	defer s.l.Unlock()
	delete(s.m, item)
}

// Has looks for the existence of an item
func (s *Set) Has(item interface{}) bool {
	s.l.RLock()
	defer s.l.RUnlock()
	_, ok := s.m[item]
	return ok
}

// Size returns the number of items in a set.
func (s *Set) Size() int {
	s.l.RLock()
	defer s.l.RUnlock()
	return len(s.m)
}

// Clear removes all items from the set
func (s *Set) Clear() {
	s.l.Lock()
	defer s.l.Unlock()
	s.m = make(map[interface{}]bool)
}

// IsEmpty checks for emptiness of the set
func (s *Set) IsEmpty() bool {
	if s.Size() == 0 {
		return true
	}
	return false
}

// List returns a slice of all items
func (s *Set) List() []interface{} {
	s.l.RLock()
	defer s.l.RUnlock()
	list := make([]interface{}, 0)
	for item := range s.m {
		list = append(list, item)
	}
	return list
}

// Union is the merger of two sets. It returns a new set with the element in s
// and t combined.
func (s *Set) Union(t *Set) *Set {
	u := New(t.List()...)
	for _, item := range s.List() {
		u.Add(item)
	}
	return u
}

// Merge is like Union, however it modifies the current set it's applied on
// with the given t set.
func (s *Set) Merge(t *Set) {
	for _, item := range t.List() {
		s.Add(item)
	}
}

// Seperate removes the set items containing in t from set s. PLease aware that
// it's not the opposite of Merge.
func (s *Set) Seperate(t *Set) {
	for _, item := range t.List() {
		s.Remove(item)
	}
}

// Intersection returns a new set which contains items which is in both s and t.
func (s *Set) Intersection(t *Set) *Set {
	u := New()
	for _, item := range s.List() {
		if t.Has(item) {
			u.Add(item)
		}
	}
	for _, item := range t.List() {
		if s.Has(item) {
			u.Add(item)
		}
	}
	return u
}

// Intersection returns a new set which contains items which is in both s and t.
func (s *Set) Difference(t *Set) *Set {
	u := New()
	for _, item := range s.List() {
		if !t.Has(item) {
			u.Add(item)
		}
	}
	return u
}

// Symmetric returns a new set which s is the difference of items  which are in
// one of either, but not in both.
func (s *Set) SymmetricDifference(t *Set) *Set {
	u := s.Difference(t)
	v := t.Difference(s)
	return u.Union(v)
}
<commit_msg>Fix typo<commit_after>// Goset is a thread safe SET data structure implementation
package goset

import "sync"

type Set struct {
	m map[interface{}]bool
	l sync.RWMutex // not embedded because we don't expose it
}

// New creates and initialize a new Set. It's accept a variable number of
// arguments to populate the initial set. If nothing passed a Set with zero
// size is created.
func New(items ...interface{}) *Set {
	s := &Set{
		m: make(map[interface{}]bool),
	}

	for _, item := range items {
		s.Add(item)
	}

	return s
}

// Add includes the specified item to the set.
func (s *Set) Add(item interface{}) {
	s.l.Lock()
	defer s.l.Unlock()
	s.m[item] = true
}

// Remove deletes the specified item from the set
func (s *Set) Remove(item interface{}) {
	s.l.Lock()
	defer s.l.Unlock()
	delete(s.m, item)
}

// Has looks for the existence of an item
func (s *Set) Has(item interface{}) bool {
	s.l.RLock()
	defer s.l.RUnlock()
	_, ok := s.m[item]
	return ok
}

// Size returns the number of items in a set.
func (s *Set) Size() int {
	s.l.RLock()
	defer s.l.RUnlock()
	return len(s.m)
}

// Clear removes all items from the set
func (s *Set) Clear() {
	s.l.Lock()
	defer s.l.Unlock()
	s.m = make(map[interface{}]bool)
}

// IsEmpty checks for emptiness of the set
func (s *Set) IsEmpty() bool {
	if s.Size() == 0 {
		return true
	}
	return false
}

// List returns a slice of all items
func (s *Set) List() []interface{} {
	s.l.RLock()
	defer s.l.RUnlock()
	list := make([]interface{}, 0)
	for item := range s.m {
		list = append(list, item)
	}
	return list
}

// Union is the merger of two sets. It returns a new set with the element in s
// and t combined.
func (s *Set) Union(t *Set) *Set {
	u := New(t.List()...)
	for _, item := range s.List() {
		u.Add(item)
	}
	return u
}

// Merge is like Union, however it modifies the current set it's applied on
// with the given t set.
func (s *Set) Merge(t *Set) {
	for _, item := range t.List() {
		s.Add(item)
	}
}

// Seperate removes the set items containing in t from set s. Please aware that
// it's not the opposite of Merge.
func (s *Set) Seperate(t *Set) {
	for _, item := range t.List() {
		s.Remove(item)
	}
}

// Intersection returns a new set which contains items which is in both s and t.
func (s *Set) Intersection(t *Set) *Set {
	u := New()
	for _, item := range s.List() {
		if t.Has(item) {
			u.Add(item)
		}
	}
	for _, item := range t.List() {
		if s.Has(item) {
			u.Add(item)
		}
	}
	return u
}

// Intersection returns a new set which contains items which is in both s and t.
func (s *Set) Difference(t *Set) *Set {
	u := New()
	for _, item := range s.List() {
		if !t.Has(item) {
			u.Add(item)
		}
	}
	return u
}

// Symmetric returns a new set which s is the difference of items  which are in
// one of either, but not in both.
func (s *Set) SymmetricDifference(t *Set) *Set {
	u := s.Difference(t)
	v := t.Difference(s)
	return u.Union(v)
}
<|endoftext|>


<commit_before>// Code generated by go generate. DO NOT EDIT.
// Source: vendor/istio.io/api/mixer/v1/global_dictionary.yaml

package attribute

func GlobalList() []string {
	tmp := make([]string, len(globalList))
	copy(tmp, globalList)
	return tmp
}

var (
	globalList = []string{
		"source.ip",
		"source.port",
		"source.name",
		"source.uid",
		"source.namespace",
		"source.labels",
		"source.user",
		"target.ip",
		"target.port",
		"target.service",
		"target.name",
		"target.uid",
		"target.namespace",
		"target.labels",
		"target.user",
		"request.headers",
		"request.id",
		"request.path",
		"request.host",
		"request.method",
		"request.reason",
		"request.referer",
		"request.scheme",
		"request.size",
		"request.time",
		"request.useragent",
		"response.headers",
		"response.size",
		"response.time",
		"response.duration",
		"response.code",
		":authority",
		":method",
		":path",
		":scheme",
		":status",
		"access-control-allow-origin",
		"access-control-allow-methods",
		"access-control-allow-headers",
		"access-control-max-age",
		"access-control-request-method",
		"access-control-request-headers",
		"accept-charset",
		"accept-encoding",
		"accept-language",
		"accept-ranges",
		"accept",
		"access-control-allow",
		"age",
		"allow",
		"authorization",
		"cache-control",
		"content-disposition",
		"content-encoding",
		"content-language",
		"content-length",
		"content-location",
		"content-range",
		"content-type",
		"cookie",
		"date",
		"etag",
		"expect",
		"expires",
		"from",
		"host",
		"if-match",
		"if-modified-since",
		"if-none-match",
		"if-range",
		"if-unmodified-since",
		"keep-alive",
		"last-modified",
		"link",
		"location",
		"max-forwards",
		"proxy-authenticate",
		"proxy-authorization",
		"range",
		"referer",
		"refresh",
		"retry-after",
		"server",
		"set-cookie",
		"strict-transport-sec",
		"transfer-encoding",
		"user-agent",
		"vary",
		"via",
		"www-authenticate",
		"GET",
		"POST",
		"http",
		"envoy",
		"'200'",
		"Keep-Alive",
		"chunked",
		"x-envoy-service-time",
		"x-forwarded-for",
		"x-forwarded-host",
		"x-forwarded-proto",
		"x-http-method-override",
		"x-request-id",
		"x-requested-with",
		"application/json",
		"application/xml",
		"gzip",
		"text/html",
		"text/html; charset=utf-8",
		"text/plain",
		"text/plain; charset=utf-8",
		"'0'",
		"'1'",
		"true",
		"false",
		"gzip, deflate",
		"max-age=0",
		"x-envoy-upstream-service-time",
		"x-envoy-internal",
		"x-envoy-expected-rq-timeout-ms",
		"x-ot-span-context",
		"x-b3-traceid",
		"x-b3-sampled",
		"x-b3-spanid",
		"tcp",
		"connection.id",
		"connection.received.bytes",
		"connection.received.bytes_total",
		"connection.sent.bytes",
		"connection.sent.bytes_total",
		"connection.duration",
		"context.protocol",
		"context.timestamp",
		"context.time",
		"0",
		"1",
		"200",
		"302",
		"400",
		"401",
		"403",
		"404",
		"409",
		"429",
		"499",
		"500",
		"501",
		"502",
		"503",
		"504",
		"destination.ip",
		"destination.port",
		"destination.service",
		"destination.name",
		"destination.uid",
		"destination.namespace",
		"destination.labels",
		"destination.user",
		"source.service",
		"api.service",
		"api.version",
		"api.operation",
		"api.protocol",
		"request.auth.principal",
		"request.auth.audiences",
		"request.auth.presenter",
		"request.api_key",
		"check.error_code",
		"check.error_message",
		"request.total_size",
		"response.total_size",
		"connection.event",
		"check.cache_hit",
		"quota.cache_hit",
		"source.principal",
		"request.auth.claims",
		"request.auth.raw_claims",
		"connection.mtls",
		"source.metadata",
		"source.owner",
		"source.services",
		"source.workload.uid",
		"source.workload.name",
		"source.workload.namespace",
		"destination.metadata",
		"destination.owner",
		"destination.principal",
		"destination.workload.uid",
		"destination.workload.name",
		"destination.workload.namespace",
		"destination.service.uid",
		"destination.service.name",
		"destination.service.namespace",
		"destination.service.host",
		"destination.container.name",
		"destination.container.image",
		"context.reporter.local",
		"context.reporter.uid",
		"response.grpc_status",
		"response.grpc_message",
		"context.reporter.type",
		"context.reporter.kind",
		"connection.requested_server_name",
		"request.url_path",
		"request.query_params",
		"context.proxy_error_code",
		"UH",
		"UF",
		"UO",
		"NR",
		"LH",
		"UT",
		"LR",
		"UR",
		"UC",
		"DI",
		"FI",
		"RL",
		"\"-\"",
		"inbound",
		"outbound",
	}
)
<commit_msg>Delete the obsolete service control adapter. (#11275)<commit_after>// Code generated by go generate. DO NOT EDIT.
// Source: vendor/istio.io/api/mixer/v1/global_dictionary.yaml

package attribute

func GlobalList() ([]string) {
    tmp := make([]string, len(globalList))
    copy(tmp, globalList)
    return tmp
}

var (
    globalList = []string{
		"source.ip",
		"source.port",
		"source.name",
		"source.uid",
		"source.namespace",
		"source.labels",
		"source.user",
		"target.ip",
		"target.port",
		"target.service",
		"target.name",
		"target.uid",
		"target.namespace",
		"target.labels",
		"target.user",
		"request.headers",
		"request.id",
		"request.path",
		"request.host",
		"request.method",
		"request.reason",
		"request.referer",
		"request.scheme",
		"request.size",
		"request.time",
		"request.useragent",
		"response.headers",
		"response.size",
		"response.time",
		"response.duration",
		"response.code",
		":authority",
		":method",
		":path",
		":scheme",
		":status",
		"access-control-allow-origin",
		"access-control-allow-methods",
		"access-control-allow-headers",
		"access-control-max-age",
		"access-control-request-method",
		"access-control-request-headers",
		"accept-charset",
		"accept-encoding",
		"accept-language",
		"accept-ranges",
		"accept",
		"access-control-allow",
		"age",
		"allow",
		"authorization",
		"cache-control",
		"content-disposition",
		"content-encoding",
		"content-language",
		"content-length",
		"content-location",
		"content-range",
		"content-type",
		"cookie",
		"date",
		"etag",
		"expect",
		"expires",
		"from",
		"host",
		"if-match",
		"if-modified-since",
		"if-none-match",
		"if-range",
		"if-unmodified-since",
		"keep-alive",
		"last-modified",
		"link",
		"location",
		"max-forwards",
		"proxy-authenticate",
		"proxy-authorization",
		"range",
		"referer",
		"refresh",
		"retry-after",
		"server",
		"set-cookie",
		"strict-transport-sec",
		"transfer-encoding",
		"user-agent",
		"vary",
		"via",
		"www-authenticate",
		"GET",
		"POST",
		"http",
		"envoy",
		"'200'",
		"Keep-Alive",
		"chunked",
		"x-envoy-service-time",
		"x-forwarded-for",
		"x-forwarded-host",
		"x-forwarded-proto",
		"x-http-method-override",
		"x-request-id",
		"x-requested-with",
		"application/json",
		"application/xml",
		"gzip",
		"text/html",
		"text/html; charset=utf-8",
		"text/plain",
		"text/plain; charset=utf-8",
		"'0'",
		"'1'",
		"true",
		"false",
		"gzip, deflate",
		"max-age=0",
		"x-envoy-upstream-service-time",
		"x-envoy-internal",
		"x-envoy-expected-rq-timeout-ms",
		"x-ot-span-context",
		"x-b3-traceid",
		"x-b3-sampled",
		"x-b3-spanid",
		"tcp",
		"connection.id",
		"connection.received.bytes",
		"connection.received.bytes_total",
		"connection.sent.bytes",
		"connection.sent.bytes_total",
		"connection.duration",
		"context.protocol",
		"context.timestamp",
		"context.time",
		"0",
		"1",
		"200",
		"302",
		"400",
		"401",
		"403",
		"404",
		"409",
		"429",
		"499",
		"500",
		"501",
		"502",
		"503",
		"504",
		"destination.ip",
		"destination.port",
		"destination.service",
		"destination.name",
		"destination.uid",
		"destination.namespace",
		"destination.labels",
		"destination.user",
		"source.service",
		"api.service",
		"api.version",
		"api.operation",
		"api.protocol",
		"request.auth.principal",
		"request.auth.audiences",
		"request.auth.presenter",
		"request.api_key",
		"check.error_code",
		"check.error_message",
		"request.total_size",
		"response.total_size",
		"connection.event",
		"check.cache_hit",
		"quota.cache_hit",
		"source.principal",
		"request.auth.claims",
		"request.auth.raw_claims",
		"connection.mtls",
		"source.metadata",
		"source.owner",
		"source.services",
		"source.workload.uid",
		"source.workload.name",
		"source.workload.namespace",
		"destination.metadata",
		"destination.owner",
		"destination.principal",
		"destination.workload.uid",
		"destination.workload.name",
		"destination.workload.namespace",
		"destination.service.uid",
		"destination.service.name",
		"destination.service.namespace",
		"destination.service.host",
		"destination.container.name",
		"destination.container.image",
		"context.reporter.local",
		"context.reporter.uid",
		"response.grpc_status",
		"response.grpc_message",
		"context.reporter.type",
		"context.reporter.kind",
		"connection.requested_server_name",
		"request.url_path",
		"request.query_params",
		"context.proxy_error_code",
		"UH",
		"UF",
		"UO",
		"NR",
		"LH",
		"UT",
		"LR",
		"UR",
		"UC",
		"DI",
		"FI",
		"RL",
		"\"-\"",
		"inbound",
		"outbound",
    }
)
<|endoftext|>


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

package attribute

func GlobalList() []string {
	tmp := make([]string, len(globalList))
	copy(tmp, globalList)
	return tmp
}

var (
	globalList = []string{
		"source.ip",
		"source.port",
		"source.name",
		"source.uid",
		"source.namespace",
		"source.labels",
		"source.user",
		"target.ip",
		"target.port",
		"target.service",
		"target.name",
		"target.uid",
		"target.namespace",
		"target.labels",
		"target.user",
		"request.headers",
		"request.id",
		"request.path",
		"request.host",
		"request.method",
		"request.reason",
		"request.referer",
		"request.scheme",
		"request.size",
		"request.time",
		"request.useragent",
		"response.headers",
		"response.size",
		"response.time",
		"response.duration",
		"response.code",
		":authority",
		":method",
		":path",
		":scheme",
		":status",
		"access-control-allow-origin",
		"access-control-allow-methods",
		"access-control-allow-headers",
		"access-control-max-age",
		"access-control-request-method",
		"access-control-request-headers",
		"accept-charset",
		"accept-encoding",
		"accept-language",
		"accept-ranges",
		"accept",
		"access-control-allow",
		"age",
		"allow",
		"authorization",
		"cache-control",
		"content-disposition",
		"content-encoding",
		"content-language",
		"content-length",
		"content-location",
		"content-range",
		"content-type",
		"cookie",
		"date",
		"etag",
		"expect",
		"expires",
		"from",
		"host",
		"if-match",
		"if-modified-since",
		"if-none-match",
		"if-range",
		"if-unmodified-since",
		"keep-alive",
		"last-modified",
		"link",
		"location",
		"max-forwards",
		"proxy-authenticate",
		"proxy-authorization",
		"range",
		"referer",
		"refresh",
		"retry-after",
		"server",
		"set-cookie",
		"strict-transport-sec",
		"transfer-encoding",
		"user-agent",
		"vary",
		"via",
		"www-authenticate",
		"GET",
		"POST",
		"http",
		"envoy",
		"'200'",
		"Keep-Alive",
		"chunked",
		"x-envoy-service-time",
		"x-forwarded-for",
		"x-forwarded-host",
		"x-forwarded-proto",
		"x-http-method-override",
		"x-request-id",
		"x-requested-with",
		"application/json",
		"application/xml",
		"gzip",
		"text/html",
		"text/html; charset=utf-8",
		"text/plain",
		"text/plain; charset=utf-8",
		"'0'",
		"'1'",
		"true",
		"false",
		"gzip, deflate",
		"max-age=0",
		"x-envoy-upstream-service-time",
		"x-envoy-internal",
		"x-envoy-expected-rq-timeout-ms",
		"x-ot-span-context",
		"x-b3-traceid",
		"x-b3-sampled",
		"x-b3-spanid",
		"tcp",
		"connection.id",
		"connection.received.bytes",
		"connection.received.bytes_total",
		"connection.sent.bytes",
		"connection.sent.bytes_total",
		"connection.duration",
		"context.protocol",
		"context.timestamp",
		"context.time",
		"0",
		"1",
		"200",
		"302",
		"400",
		"401",
		"403",
		"404",
		"409",
		"429",
		"499",
		"500",
		"501",
		"502",
		"503",
		"504",
		"destination.ip",
		"destination.port",
		"destination.service",
		"destination.name",
		"destination.uid",
		"destination.namespace",
		"destination.labels",
		"destination.user",
		"source.service",
		"api.service",
		"api.version",
		"api.operation",
		"api.protocol",
		"request.auth.principal",
		"request.auth.audiences",
		"request.auth.presenter",
		"request.api_key",
	}
)
<commit_msg>Add go:generate support for Mixer adapter configs (#2073)<commit_after>// Copyright 2017 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package attribute

func GlobalList() []string {
	tmp := make([]string, len(globalList))
	copy(tmp, globalList)
	return tmp
}

var (
	globalList = []string{
		"source.ip",
		"source.port",
		"source.name",
		"source.uid",
		"source.namespace",
		"source.labels",
		"source.user",
		"target.ip",
		"target.port",
		"target.service",
		"target.name",
		"target.uid",
		"target.namespace",
		"target.labels",
		"target.user",
		"request.headers",
		"request.id",
		"request.path",
		"request.host",
		"request.method",
		"request.reason",
		"request.referer",
		"request.scheme",
		"request.size",
		"request.time",
		"request.useragent",
		"response.headers",
		"response.size",
		"response.time",
		"response.duration",
		"response.code",
		":authority",
		":method",
		":path",
		":scheme",
		":status",
		"access-control-allow-origin",
		"access-control-allow-methods",
		"access-control-allow-headers",
		"access-control-max-age",
		"access-control-request-method",
		"access-control-request-headers",
		"accept-charset",
		"accept-encoding",
		"accept-language",
		"accept-ranges",
		"accept",
		"access-control-allow",
		"age",
		"allow",
		"authorization",
		"cache-control",
		"content-disposition",
		"content-encoding",
		"content-language",
		"content-length",
		"content-location",
		"content-range",
		"content-type",
		"cookie",
		"date",
		"etag",
		"expect",
		"expires",
		"from",
		"host",
		"if-match",
		"if-modified-since",
		"if-none-match",
		"if-range",
		"if-unmodified-since",
		"keep-alive",
		"last-modified",
		"link",
		"location",
		"max-forwards",
		"proxy-authenticate",
		"proxy-authorization",
		"range",
		"referer",
		"refresh",
		"retry-after",
		"server",
		"set-cookie",
		"strict-transport-sec",
		"transfer-encoding",
		"user-agent",
		"vary",
		"via",
		"www-authenticate",
		"GET",
		"POST",
		"http",
		"envoy",
		"'200'",
		"Keep-Alive",
		"chunked",
		"x-envoy-service-time",
		"x-forwarded-for",
		"x-forwarded-host",
		"x-forwarded-proto",
		"x-http-method-override",
		"x-request-id",
		"x-requested-with",
		"application/json",
		"application/xml",
		"gzip",
		"text/html",
		"text/html; charset=utf-8",
		"text/plain",
		"text/plain; charset=utf-8",
		"'0'",
		"'1'",
		"true",
		"false",
		"gzip, deflate",
		"max-age=0",
		"x-envoy-upstream-service-time",
		"x-envoy-internal",
		"x-envoy-expected-rq-timeout-ms",
		"x-ot-span-context",
		"x-b3-traceid",
		"x-b3-sampled",
		"x-b3-spanid",
		"tcp",
		"connection.id",
		"connection.received.bytes",
		"connection.received.bytes_total",
		"connection.sent.bytes",
		"connection.sent.bytes_total",
		"connection.duration",
		"context.protocol",
		"context.timestamp",
		"context.time",
		"0",
		"1",
		"200",
		"302",
		"400",
		"401",
		"403",
		"404",
		"409",
		"429",
		"499",
		"500",
		"501",
		"502",
		"503",
		"504",
		"destination.ip",
		"destination.port",
		"destination.service",
		"destination.name",
		"destination.uid",
		"destination.namespace",
		"destination.labels",
		"destination.user",
		"source.service",
		"api.service",
		"api.version",
		"api.operation",
		"api.protocol",
		"request.auth.principal",
		"request.auth.audiences",
		"request.auth.presenter",
		"request.api_key",
		"check.error_code",
		"check.error_message",
	}
)
<|endoftext|>


<commit_before>package transactionpool

import (
	"errors"
	"time"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

const (
	// The TransactionPoolSizeLimit is first checked, and then a transaction
	// set is added. The current transaction pool does not do any priority
	// ordering, so the size limit is such that the transaction pool will never
	// exceed the size of a block.
	//
	// TODO: Add a priority structure that will allow the transaction pool to
	// fill up beyond the size of a single block, without being subject to
	// manipulation.
	//
	// The first ~1/4 of the transaction pool can be filled for free. This is
	// mostly to preserve compatibility with clients that do not add fees.
	TransactionPoolSizeLimit  = 2e6 - 5e3 - modules.TransactionSetSizeLimit
	TransactionPoolSizeForFee = 500e3
)

var (
	errObjectConflict      = errors.New("transaction set conflicts with an existing transaction set")
	errFullTransactionPool = errors.New("transaction pool cannot accept more transactions")
	errLowMinerFees        = errors.New("transaction set needs more miner fees to be accepted")
	errEmptySet            = errors.New("transaction set is empty")

	TransactionMinFee = types.NewCurrency64(2).Mul(types.SiacoinPrecision)
)

// relatedObjectIDs determines all of the object ids related to a transaction.
func relatedObjectIDs(ts []types.Transaction) []ObjectID {
	oidMap := make(map[ObjectID]struct{})
	for _, t := range ts {
		for _, sci := range t.SiacoinInputs {
			oidMap[ObjectID(sci.ParentID)] = struct{}{}
		}
		for i := range t.SiacoinOutputs {
			oidMap[ObjectID(t.SiacoinOutputID(uint64(i)))] = struct{}{}
		}
		for i := range t.FileContracts {
			oidMap[ObjectID(t.FileContractID(uint64(i)))] = struct{}{}
		}
		for _, fcr := range t.FileContractRevisions {
			oidMap[ObjectID(fcr.ParentID)] = struct{}{}
		}
		for _, sp := range t.StorageProofs {
			oidMap[ObjectID(sp.ParentID)] = struct{}{}
		}
		for _, sfi := range t.SiafundInputs {
			oidMap[ObjectID(sfi.ParentID)] = struct{}{}
		}
		for i := range t.SiafundOutputs {
			oidMap[ObjectID(t.SiafundOutputID(uint64(i)))] = struct{}{}
		}
	}

	var oids []ObjectID
	for oid, _ := range oidMap {
		oids = append(oids, oid)
	}
	return oids
}

// checkMinerFees checks that the total amount of transaction fees in the
// transaction set is sufficient to earn a spot in the transaction pool.
func (tp *TransactionPool) checkMinerFees(ts []types.Transaction) error {
	// Transactions cannot be added after the TransactionPoolSizeLimit has been
	// hit.
	if tp.transactionListSize > TransactionPoolSizeLimit {
		return errFullTransactionPool
	}

	// The first TransactionPoolSizeForFee transactions do not need fees.
	if tp.transactionListSize > TransactionPoolSizeForFee {
		// Currently required fees are set on a per-transaction basis. 2 coins
		// are required per transaction if the free-fee limit has been reached,
		// adding a larger fee is not useful.
		var feeSum types.Currency
		for i := range ts {
			for _, fee := range ts[i].MinerFees {
				feeSum = feeSum.Add(fee)
			}
		}
		feeRequired := TransactionMinFee.Mul(types.NewCurrency64(uint64(len(ts))))
		if feeSum.Cmp(feeRequired) < 0 {
			return errLowMinerFees
		}
	}
	return nil
}

// checkTransactionSetComposition checks if the transaction set is valid given
// the state of the pool. It does not check that each individual transaction
// would be legal in the next block, but does check things like miner fees and
// IsStandard.
func (tp *TransactionPool) checkTransactionSetComposition(ts []types.Transaction) error {
	// Check that the transaction set is not already known.
	setID := TransactionSetID(crypto.HashObject(ts))
	_, exists := tp.transactionSets[setID]
	if exists {
		return modules.ErrDuplicateTransactionSet
	}

	// Check that the transaction set has enough fees to justify adding it to
	// the transaction list.
	err := tp.checkMinerFees(ts)
	if err != nil {
		return err
	}

	// All checks after this are expensive.
	//
	// TODO: There is no DoS prevention mechanism in place to prevent repeated
	// expensive verifications of invalid transactions that are created on the
	// fly.

	// Check that all transactions follow 'Standard.md' guidelines.
	err = tp.IsStandardTransactionSet(ts)
	if err != nil {
		return err
	}
	return nil
}

// handleConflicts detects whether the conflicts in the transaction pool are
// legal children of the new transaction pool set or not.
func (tp *TransactionPool) handleConflicts(ts []types.Transaction, conflicts []TransactionSetID) error {
	// Create a list of all the transaction ids that compose the set of
	// conflicts.
	conflictMap := make(map[types.TransactionID]TransactionSetID)
	for _, conflict := range conflicts {
		conflictSet := tp.transactionSets[conflict]
		for _, conflictTxn := range conflictSet {
			conflictMap[conflictTxn.ID()] = conflict
		}
	}

	// Discard all duplicate transactions from the input transaction set.
	var dedupSet []types.Transaction
	for _, t := range ts {
		_, exists := conflictMap[t.ID()]
		if exists {
			continue
		}
		dedupSet = append(dedupSet, t)
	}
	if len(dedupSet) == 0 {
		return modules.ErrDuplicateTransactionSet
	}
	// If transactions were pruned, it's possible that the set of
	// dependencies/conflicts has also reduced. To minimize computational load
	// on the consensus set, we want to prune out all of the conflicts that are
	// no longer relevant. As an example, consider the transaction set {A}, the
	// set {B}, and the new set {A, C}, where C is dependent on B. {A} and {B}
	// are both conflicts, but after deduplication {A} is no longer a conflict.
	// This is recursive, but it is guaranteed to run only once as the first
	// deduplication is guaranteed to be complete.
	if len(dedupSet) < len(ts) {
		oids := relatedObjectIDs(dedupSet)
		var conflicts []TransactionSetID
		for _, oid := range oids {
			conflict, exists := tp.knownObjects[oid]
			if exists {
				conflicts = append(conflicts, conflict)
			}
		}
		return tp.handleConflicts(dedupSet, conflicts)
	}

	// Merge all of the conflict sets with the input set (input set goes last
	// to preserve dependency ordering), and see if the set as a whole is both
	// small enough to be legal and valid as a set. If no, return an error. If
	// yes, add the new set to the pool, and eliminate the old set. The output
	// diff objects can be repeated, (no need to remove those). Just need to
	// remove the conflicts from tp.transactionSets.
	var superset []types.Transaction
	supersetMap := make(map[TransactionSetID]struct{})
	for _, conflict := range conflictMap {
		supersetMap[conflict] = struct{}{}
	}
	for conflict := range supersetMap {
		superset = append(superset, tp.transactionSets[conflict]...)
	}
	superset = append(superset, dedupSet...)

	// Check the composition of the transaction set, including fees and
	// IsStandard rules (this is a new set, the rules must be rechecked).
	err := tp.checkTransactionSetComposition(superset)
	if err != nil {
		return err
	}

	// Check that the transaction set is valid.
	cc, err := tp.consensusSet.TryTransactionSet(superset)
	if err != nil {
		return modules.NewConsensusConflict(err.Error())
	}

	// Remove the conflicts from the transaction pool. The diffs do not need to
	// be removed, they will be overwritten later in the function.
	for _, conflict := range conflictMap {
		conflictSet := tp.transactionSets[conflict]
		tp.transactionListSize -= len(encoding.Marshal(conflictSet))
		delete(tp.transactionSets, conflict)
		delete(tp.transactionSetDiffs, conflict)
	}

	// Add the transaction set to the pool.
	setID := TransactionSetID(crypto.HashObject(superset))
	tp.transactionSets[setID] = superset
	for _, diff := range cc.SiacoinOutputDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	for _, diff := range cc.FileContractDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	for _, diff := range cc.SiafundOutputDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	tp.transactionSetDiffs[setID] = cc
	tp.transactionListSize += len(encoding.Marshal(superset))
	return nil
}

// acceptTransactionSet verifies that a transaction set is allowed to be in the
// transaction pool, and then adds it to the transaction pool.
func (tp *TransactionPool) acceptTransactionSet(ts []types.Transaction) error {
	if len(ts) == 0 {
		return errEmptySet
	}

	// Check the composition of the transaction set, including fees and
	// IsStandard rules.
	err := tp.checkTransactionSetComposition(ts)
	if err != nil {
		return err
	}

	// Check for conflicts with other transactions, which would indicate a
	// double-spend. Legal children of a transaction set will also trigger the
	// conflict-detector.
	oids := relatedObjectIDs(ts)
	var conflicts []TransactionSetID
	for _, oid := range oids {
		conflict, exists := tp.knownObjects[oid]
		if exists {
			conflicts = append(conflicts, conflict)
		}
	}
	if len(conflicts) > 0 {
		return tp.handleConflicts(ts, conflicts)
	}
	cc, err := tp.consensusSet.TryTransactionSet(ts)
	if err != nil {
		return modules.NewConsensusConflict(err.Error())
	}

	// Add the transaction set to the pool.
	setID := TransactionSetID(crypto.HashObject(ts))
	tp.transactionSets[setID] = ts
	for _, oid := range oids {
		tp.knownObjects[oid] = setID
	}
	tp.transactionSetDiffs[setID] = cc
	tp.transactionListSize += len(encoding.Marshal(ts))
	return nil
}

// AcceptTransaction adds a transaction to the unconfirmed set of
// transactions. If the transaction is accepted, it will be relayed to
// connected peers.
func (tp *TransactionPool) AcceptTransactionSet(ts []types.Transaction) error {
	tp.mu.Lock()
	defer tp.mu.Unlock()

	err := tp.acceptTransactionSet(ts)
	if err != nil {
		return err
	}

	// Notify subscribers and broadcast the transaction set.
	go tp.gateway.Broadcast("RelayTransactionSet", ts)
	tp.updateSubscribersTransactions()

	// COMPAT v0.4.6
	//
	// Transactions must be broadcast individually as well so that they will be
	// seen by older nodes that don't support the "RelayTransactionSet" RPC.
	go func() {
		for _, t := range ts {
			tp.gateway.Broadcast("RelayTransaction", t)
			time.Sleep(time.Second * 15)
		}
	}()

	return nil
}

// relayTransactionSet is an RPC that accepts a transaction set from a peer. If
// the accept is successful, the transaction will be relayed to the gateway's
// other peers.
func (tp *TransactionPool) relayTransactionSet(conn modules.PeerConn) error {
	var ts []types.Transaction
	err := encoding.ReadObject(conn, &ts, types.BlockSizeLimit)
	if err != nil {
		return err
	}
	return tp.AcceptTransactionSet(ts)
}
<commit_msg>Remove compatibility for v0.4.6<commit_after>package transactionpool

import (
	"errors"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

const (
	// The TransactionPoolSizeLimit is first checked, and then a transaction
	// set is added. The current transaction pool does not do any priority
	// ordering, so the size limit is such that the transaction pool will never
	// exceed the size of a block.
	//
	// TODO: Add a priority structure that will allow the transaction pool to
	// fill up beyond the size of a single block, without being subject to
	// manipulation.
	//
	// The first ~1/4 of the transaction pool can be filled for free. This is
	// mostly to preserve compatibility with clients that do not add fees.
	TransactionPoolSizeLimit  = 2e6 - 5e3 - modules.TransactionSetSizeLimit
	TransactionPoolSizeForFee = 500e3
)

var (
	errObjectConflict      = errors.New("transaction set conflicts with an existing transaction set")
	errFullTransactionPool = errors.New("transaction pool cannot accept more transactions")
	errLowMinerFees        = errors.New("transaction set needs more miner fees to be accepted")
	errEmptySet            = errors.New("transaction set is empty")

	TransactionMinFee = types.NewCurrency64(2).Mul(types.SiacoinPrecision)
)

// relatedObjectIDs determines all of the object ids related to a transaction.
func relatedObjectIDs(ts []types.Transaction) []ObjectID {
	oidMap := make(map[ObjectID]struct{})
	for _, t := range ts {
		for _, sci := range t.SiacoinInputs {
			oidMap[ObjectID(sci.ParentID)] = struct{}{}
		}
		for i := range t.SiacoinOutputs {
			oidMap[ObjectID(t.SiacoinOutputID(uint64(i)))] = struct{}{}
		}
		for i := range t.FileContracts {
			oidMap[ObjectID(t.FileContractID(uint64(i)))] = struct{}{}
		}
		for _, fcr := range t.FileContractRevisions {
			oidMap[ObjectID(fcr.ParentID)] = struct{}{}
		}
		for _, sp := range t.StorageProofs {
			oidMap[ObjectID(sp.ParentID)] = struct{}{}
		}
		for _, sfi := range t.SiafundInputs {
			oidMap[ObjectID(sfi.ParentID)] = struct{}{}
		}
		for i := range t.SiafundOutputs {
			oidMap[ObjectID(t.SiafundOutputID(uint64(i)))] = struct{}{}
		}
	}

	var oids []ObjectID
	for oid, _ := range oidMap {
		oids = append(oids, oid)
	}
	return oids
}

// checkMinerFees checks that the total amount of transaction fees in the
// transaction set is sufficient to earn a spot in the transaction pool.
func (tp *TransactionPool) checkMinerFees(ts []types.Transaction) error {
	// Transactions cannot be added after the TransactionPoolSizeLimit has been
	// hit.
	if tp.transactionListSize > TransactionPoolSizeLimit {
		return errFullTransactionPool
	}

	// The first TransactionPoolSizeForFee transactions do not need fees.
	if tp.transactionListSize > TransactionPoolSizeForFee {
		// Currently required fees are set on a per-transaction basis. 2 coins
		// are required per transaction if the free-fee limit has been reached,
		// adding a larger fee is not useful.
		var feeSum types.Currency
		for i := range ts {
			for _, fee := range ts[i].MinerFees {
				feeSum = feeSum.Add(fee)
			}
		}
		feeRequired := TransactionMinFee.Mul(types.NewCurrency64(uint64(len(ts))))
		if feeSum.Cmp(feeRequired) < 0 {
			return errLowMinerFees
		}
	}
	return nil
}

// checkTransactionSetComposition checks if the transaction set is valid given
// the state of the pool. It does not check that each individual transaction
// would be legal in the next block, but does check things like miner fees and
// IsStandard.
func (tp *TransactionPool) checkTransactionSetComposition(ts []types.Transaction) error {
	// Check that the transaction set is not already known.
	setID := TransactionSetID(crypto.HashObject(ts))
	_, exists := tp.transactionSets[setID]
	if exists {
		return modules.ErrDuplicateTransactionSet
	}

	// Check that the transaction set has enough fees to justify adding it to
	// the transaction list.
	err := tp.checkMinerFees(ts)
	if err != nil {
		return err
	}

	// All checks after this are expensive.
	//
	// TODO: There is no DoS prevention mechanism in place to prevent repeated
	// expensive verifications of invalid transactions that are created on the
	// fly.

	// Check that all transactions follow 'Standard.md' guidelines.
	err = tp.IsStandardTransactionSet(ts)
	if err != nil {
		return err
	}
	return nil
}

// handleConflicts detects whether the conflicts in the transaction pool are
// legal children of the new transaction pool set or not.
func (tp *TransactionPool) handleConflicts(ts []types.Transaction, conflicts []TransactionSetID) error {
	// Create a list of all the transaction ids that compose the set of
	// conflicts.
	conflictMap := make(map[types.TransactionID]TransactionSetID)
	for _, conflict := range conflicts {
		conflictSet := tp.transactionSets[conflict]
		for _, conflictTxn := range conflictSet {
			conflictMap[conflictTxn.ID()] = conflict
		}
	}

	// Discard all duplicate transactions from the input transaction set.
	var dedupSet []types.Transaction
	for _, t := range ts {
		_, exists := conflictMap[t.ID()]
		if exists {
			continue
		}
		dedupSet = append(dedupSet, t)
	}
	if len(dedupSet) == 0 {
		return modules.ErrDuplicateTransactionSet
	}
	// If transactions were pruned, it's possible that the set of
	// dependencies/conflicts has also reduced. To minimize computational load
	// on the consensus set, we want to prune out all of the conflicts that are
	// no longer relevant. As an example, consider the transaction set {A}, the
	// set {B}, and the new set {A, C}, where C is dependent on B. {A} and {B}
	// are both conflicts, but after deduplication {A} is no longer a conflict.
	// This is recursive, but it is guaranteed to run only once as the first
	// deduplication is guaranteed to be complete.
	if len(dedupSet) < len(ts) {
		oids := relatedObjectIDs(dedupSet)
		var conflicts []TransactionSetID
		for _, oid := range oids {
			conflict, exists := tp.knownObjects[oid]
			if exists {
				conflicts = append(conflicts, conflict)
			}
		}
		return tp.handleConflicts(dedupSet, conflicts)
	}

	// Merge all of the conflict sets with the input set (input set goes last
	// to preserve dependency ordering), and see if the set as a whole is both
	// small enough to be legal and valid as a set. If no, return an error. If
	// yes, add the new set to the pool, and eliminate the old set. The output
	// diff objects can be repeated, (no need to remove those). Just need to
	// remove the conflicts from tp.transactionSets.
	var superset []types.Transaction
	supersetMap := make(map[TransactionSetID]struct{})
	for _, conflict := range conflictMap {
		supersetMap[conflict] = struct{}{}
	}
	for conflict := range supersetMap {
		superset = append(superset, tp.transactionSets[conflict]...)
	}
	superset = append(superset, dedupSet...)

	// Check the composition of the transaction set, including fees and
	// IsStandard rules (this is a new set, the rules must be rechecked).
	err := tp.checkTransactionSetComposition(superset)
	if err != nil {
		return err
	}

	// Check that the transaction set is valid.
	cc, err := tp.consensusSet.TryTransactionSet(superset)
	if err != nil {
		return modules.NewConsensusConflict(err.Error())
	}

	// Remove the conflicts from the transaction pool. The diffs do not need to
	// be removed, they will be overwritten later in the function.
	for _, conflict := range conflictMap {
		conflictSet := tp.transactionSets[conflict]
		tp.transactionListSize -= len(encoding.Marshal(conflictSet))
		delete(tp.transactionSets, conflict)
		delete(tp.transactionSetDiffs, conflict)
	}

	// Add the transaction set to the pool.
	setID := TransactionSetID(crypto.HashObject(superset))
	tp.transactionSets[setID] = superset
	for _, diff := range cc.SiacoinOutputDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	for _, diff := range cc.FileContractDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	for _, diff := range cc.SiafundOutputDiffs {
		tp.knownObjects[ObjectID(diff.ID)] = setID
	}
	tp.transactionSetDiffs[setID] = cc
	tp.transactionListSize += len(encoding.Marshal(superset))
	return nil
}

// acceptTransactionSet verifies that a transaction set is allowed to be in the
// transaction pool, and then adds it to the transaction pool.
func (tp *TransactionPool) acceptTransactionSet(ts []types.Transaction) error {
	if len(ts) == 0 {
		return errEmptySet
	}

	// Check the composition of the transaction set, including fees and
	// IsStandard rules.
	err := tp.checkTransactionSetComposition(ts)
	if err != nil {
		return err
	}

	// Check for conflicts with other transactions, which would indicate a
	// double-spend. Legal children of a transaction set will also trigger the
	// conflict-detector.
	oids := relatedObjectIDs(ts)
	var conflicts []TransactionSetID
	for _, oid := range oids {
		conflict, exists := tp.knownObjects[oid]
		if exists {
			conflicts = append(conflicts, conflict)
		}
	}
	if len(conflicts) > 0 {
		return tp.handleConflicts(ts, conflicts)
	}
	cc, err := tp.consensusSet.TryTransactionSet(ts)
	if err != nil {
		return modules.NewConsensusConflict(err.Error())
	}

	// Add the transaction set to the pool.
	setID := TransactionSetID(crypto.HashObject(ts))
	tp.transactionSets[setID] = ts
	for _, oid := range oids {
		tp.knownObjects[oid] = setID
	}
	tp.transactionSetDiffs[setID] = cc
	tp.transactionListSize += len(encoding.Marshal(ts))
	return nil
}

// AcceptTransaction adds a transaction to the unconfirmed set of
// transactions. If the transaction is accepted, it will be relayed to
// connected peers.
func (tp *TransactionPool) AcceptTransactionSet(ts []types.Transaction) error {
	tp.mu.Lock()
	defer tp.mu.Unlock()

	err := tp.acceptTransactionSet(ts)
	if err != nil {
		return err
	}

	// Notify subscribers and broadcast the transaction set.
	go tp.gateway.Broadcast("RelayTransactionSet", ts)
	tp.updateSubscribersTransactions()

	return nil
}

// relayTransactionSet is an RPC that accepts a transaction set from a peer. If
// the accept is successful, the transaction will be relayed to the gateway's
// other peers.
func (tp *TransactionPool) relayTransactionSet(conn modules.PeerConn) error {
	var ts []types.Transaction
	err := encoding.ReadObject(conn, &ts, types.BlockSizeLimit)
	if err != nil {
		return err
	}
	return tp.AcceptTransactionSet(ts)
}
<|endoftext|>


<commit_before>package transactionpool

import (
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/persist"
	"github.com/NebulousLabs/Sia/types"
)

// Consts related to the persisting structures of the transactoin pool.
const (
	dbFilename = "transactionpool.db"
	logFile    = "transactionpool.log"
)

// Constants related to the size and ease-of-entry of the transaction pool.
const (
	// maxTxnAge determines the maximum age of a transaction (in block height)
	// allowed before the transaction is pruned from the transaction pool.
	maxTxnAge = types.BlockHeight(24)

	// TransactionPoolFeeExponentiation defines the polynomial rate of growth
	// required to keep putting transactions into the transaction pool. If the
	// exponentiation is 2, then doubling the size of the transaction pool
	// requires quadrupling the fees of the transactions being added. A higher
	// number makes it harder for the transaction pool to grow beyond its
	// default size during times of congestion.
	TransactionPoolExponentiation = 3

	// TransactionPoolSizeTarget defines the target size of the pool when the
	// transactions are paying 1 SC / kb in fees.
	TransactionPoolSizeTarget = 3e6

	// TransactionPoolSizeForFee defines how large the transaction pool needs to
	// be before it starts expecting fees to be on the transaction. This initial
	// limit is to help the network grow and provide some wiggle room for
	// wallets that are not yet able to operate via a fee market.
	TransactionPoolSizeForFee = 500e3
)

// Constants related to fee estimation.
const (
	// blockFeeEstimationDepth defines how far backwards in the blockchain the
	// fee estimator looks when using blocks to figure out the appropriate fees
	// to add to transactions.
	blockFeeEstimationDepth = 6

	// maxMultiplier defines the general gap between the maximum recommended fee
	// and the minimum recommended fee.
	maxMultiplier = 3

	// minExtendMultiplier defines the amount we multiply into the minimum
	// amount required to extend the fee pool when coming up with a min fee
	// recommendation.
	minExtendMultiplier = 1.2
)

// Variables related to the persisting structures of the transaction pool.
var (
	dbMetadata = persist.Metadata{
		Header:  "Sia Transaction Pool DB",
		Version: "0.6.0",
	}
)

// Variables related to the size and ease-of-entry of the transaction pool.
var (
	// minEstimation defines a sane minimum fee per byte for transactions.  This
	// will typically be only suggested as a fee in the absense of congestion.
	minEstimation = types.SiacoinPrecision.Div64(20).Div64(1e3)
)

// Variables related to propagating transactions through the network.
var (
	// relayTransactionSetTimeout establishes the timeout for a relay
	// transaction set call.
	relayTransactionSetTimeout = build.Select(build.Var{
		Standard: 3 * time.Minute,
		Dev:      20 * time.Second,
		Testing:  3 * time.Second,
	}).(time.Duration)
)
<commit_msg>reduce the minimum fee by a factor of 5<commit_after>package transactionpool

import (
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/persist"
	"github.com/NebulousLabs/Sia/types"
)

// Consts related to the persisting structures of the transactoin pool.
const (
	dbFilename = "transactionpool.db"
	logFile    = "transactionpool.log"
)

// Constants related to the size and ease-of-entry of the transaction pool.
const (
	// maxTxnAge determines the maximum age of a transaction (in block height)
	// allowed before the transaction is pruned from the transaction pool.
	maxTxnAge = types.BlockHeight(24)

	// TransactionPoolFeeExponentiation defines the polynomial rate of growth
	// required to keep putting transactions into the transaction pool. If the
	// exponentiation is 2, then doubling the size of the transaction pool
	// requires quadrupling the fees of the transactions being added. A higher
	// number makes it harder for the transaction pool to grow beyond its
	// default size during times of congestion.
	TransactionPoolExponentiation = 3

	// TransactionPoolSizeTarget defines the target size of the pool when the
	// transactions are paying 1 SC / kb in fees.
	TransactionPoolSizeTarget = 3e6

	// TransactionPoolSizeForFee defines how large the transaction pool needs to
	// be before it starts expecting fees to be on the transaction. This initial
	// limit is to help the network grow and provide some wiggle room for
	// wallets that are not yet able to operate via a fee market.
	TransactionPoolSizeForFee = 500e3
)

// Constants related to fee estimation.
const (
	// blockFeeEstimationDepth defines how far backwards in the blockchain the
	// fee estimator looks when using blocks to figure out the appropriate fees
	// to add to transactions.
	blockFeeEstimationDepth = 6

	// maxMultiplier defines the general gap between the maximum recommended fee
	// and the minimum recommended fee.
	maxMultiplier = 3

	// minExtendMultiplier defines the amount we multiply into the minimum
	// amount required to extend the fee pool when coming up with a min fee
	// recommendation.
	minExtendMultiplier = 1.2
)

// Variables related to the persisting structures of the transaction pool.
var (
	dbMetadata = persist.Metadata{
		Header:  "Sia Transaction Pool DB",
		Version: "0.6.0",
	}
)

// Variables related to the size and ease-of-entry of the transaction pool.
var (
	// minEstimation defines a sane minimum fee per byte for transactions.  This
	// will typically be only suggested as a fee in the absense of congestion.
	minEstimation = types.SiacoinPrecision.Div64(100).Div64(1e3)
)

// Variables related to propagating transactions through the network.
var (
	// relayTransactionSetTimeout establishes the timeout for a relay
	// transaction set call.
	relayTransactionSetTimeout = build.Select(build.Var{
		Standard: 3 * time.Minute,
		Dev:      20 * time.Second,
		Testing:  3 * time.Second,
	}).(time.Duration)
)
<|endoftext|>


<commit_before>package web

import (
	"net/http"
	"path/filepath"
	"reflect"
	"time"

	"github.com/kdada/tinygo/connector"
	"github.com/kdada/tinygo/log"
	"github.com/kdada/tinygo/router"
	"github.com/kdada/tinygo/session"
	"github.com/kdada/tinygo/template"
)

// 参数类型方法
type ParamTypeFunc func(context *Context, name string, t reflect.Type) interface{}

// HttpProcessor 用于协调http连接器和路由,并管理Http应用的所有内容
type HttpProcessor struct {
	Root             router.Router                 //根路由
	Config           *HttpConfig                   //http配置
	Logger           log.Logger                    //日志记录
	SessionContainer session.SessionContainer      //Session容器
	CSRFContainer    session.SessionContainer      //Csrf容器
	Finders          map[string]ContextValueFinder //Context单类型值查找器
	MutiTypeFinders  []ContextValueFinder          //Context多类型值查找器
	Templates        *template.ViewTemplates       //视图模板信息
	Event            HttpProcessorEvent            //处理器事件
}

// NewHttpProcessor 创建Http处理器
func NewHttpProcessor(root router.Router, config *HttpConfig) (*HttpProcessor, error) {
	var processor = new(HttpProcessor)
	processor.Root = root
	processor.Config = config
	//日志
	if config.Log {
		var logger, err = log.NewLogger(config.LogType, config.LogPath)
		if err != nil {
			panic(err)
		}
		logger.SetAsync(config.LogAsync)
		processor.Logger = logger
	}
	//session
	if config.Session {
		var container, err = session.NewSessionContainer(config.SessionType, config.SessionExpire, config.SessionSource)
		if err != nil {
			panic(err)
		}
		processor.SessionContainer = container
	}
	//CSRF,过期时间与Session相同,CSRFExpire用于设置CSRF token的过期时间
	if config.CSRF {
		var container, err = session.NewSessionContainer(config.CSRFType, config.SessionExpire, config.CSRFSource)
		if err != nil {
			panic(err)
		}
		processor.CSRFContainer = container
	}

	//注册参数类型方法
	processor.Finders = make(map[string]ContextValueFinder)
	register(processor)
	//创建视图模板信息
	processor.Templates = template.NewViewTemplates(config.TemplateConfig)
	if config.Precompile {
		//预编译模板
		var err = processor.Templates.CompileAll()
		if err != nil {
			return nil, err
		}
	}
	//注册http事件
	processor.Event = new(DefaultHttpProcessorEvent)
	//注册静态文件路由
	if processor.Config.Favicon != "" {
		processor.Root.AddChild(NewFileRouter("favicon.ico", processor.Config.Favicon))
	}
	if processor.Config.Robots != "" {
		processor.Root.AddChild(NewFileRouter("robots.txt", processor.Config.Robots))
	}
	if len(processor.Config.Static) > 0 {
		for _, s := range processor.Config.Static {
			processor.Root.AddChild(NewStaticRouter(filepath.Base(s), s))
		}
	}
	//创建首页跳转
	if processor.Config.Home != "" {
		var r = NewSpaceRouter("Get")
		var excutor = NewSimpleExecutor(func(r *Context) (interface{}, error) {
			return r.Redispatch(r.Processor.Config.Home), nil
		})
		r.SetRouterExcutorGenerator(func() router.RouterExcutor {
			return excutor
		})
		processor.Root.AddChild(r)
	}
	return processor, nil
}

// createCookie 创建cookie
func (this *HttpProcessor) createCookie(name string, id string, expire int) *http.Cookie {
	var cookieValue = new(http.Cookie)
	cookieValue.Name = name
	cookieValue.Value = id
	cookieValue.Path = "/"
	cookieValue.HttpOnly = true
	if expire > 0 {
		cookieValue.MaxAge = expire
		cookieValue.Expires = time.Now().Add(time.Second * time.Duration(expire))
	}
	return cookieValue
}

// addCookie 添加cookie
func (this *HttpProcessor) addCookie(context *Context, cookie *http.Cookie) {
	context.HttpContext.ResponseWriter.Header().Add("Set-Cookie", cookie.String())
}

// RegisterFinder 注册单一类型的值查找器
func (this *HttpProcessor) RegisterFinder(t reflect.Type, finder ContextValueFinder) {
	this.Finders[t.String()] = finder
}

// RegisterMutiTypeFinder 注册多类型的值查找器
func (this *HttpProcessor) RegisterMutiTypeFinder(finder ContextValueFinder) {
	this.MutiTypeFinders = append(this.MutiTypeFinders, finder)
}

// ResolveSession 处理会话相关内容
func (this *HttpProcessor) ResolveSession(context *Context) {
	if this.SessionContainer != nil {
		//添加Session信息
		var cookieValue, exist = context.Cookie(this.Config.SessionCookieName)
		var ss session.Session
		var ok bool = false
		if exist {
			ss, ok = this.SessionContainer.Session(cookieValue.Value)
		}
		if !ok {
			ss, ok = this.SessionContainer.CreateSession()
			context.AddCookie(this.createCookie(this.Config.SessionCookieName, ss.SessionId(), this.Config.SessionCookieExpire))
		}
		if ok {
			context.Session = ss
		}
	}
	if this.CSRFContainer != nil {
		//添加CSRF Session信息,CSRF的过期时间和Session相同,使用SessionExpire设置Cookie过期时间
		var cookieValue, exist = context.Cookie(this.Config.CSRFCookieName)
		var ss session.Session
		var ok bool = false
		if exist {
			ss, ok = this.CSRFContainer.Session(cookieValue.Value)
		}
		if !ok {
			ss, ok = this.CSRFContainer.CreateSession()
			context.AddCookie(this.createCookie(this.Config.CSRFCookieName, ss.SessionId(), this.Config.CSRFCookieExpire))
		}
		if ok {
			context.CSRF = ss
		}
	}
}

// Dispatch 将接收到的请求进行分发
//  segments:用于进行分发的路径段信息
//  data:连接携带的数据
func (this *HttpProcessor) Dispatch(segments []string, data interface{}) {
	var ct = data.(*connector.HttpContext)
	var context, err = NewContext(segments, ct)
	if err == nil {
		context.Processor = this
		this.ResolveSession(context)
		if this.Event != nil {
			this.Event.Request(this, context)
		}
		var executor, ok = this.Root.Match(context)
		if ok {
			var r, err = executor.Execute()
			if err != nil {
				this.Event.Error(this, context, err)
			} else if this.Event != nil {
				var result = []interface{}{}
				if r != nil {
					var rs, ok = r.([]interface{})
					if ok {
						result = rs
					} else {
						result = []interface{}{r}
					}
				}
				this.Event.RequestFinish(this, context, result)
			}
		} else if this.Event != nil {
			this.Event.Error(this, context, ErrorRouterNotFound.Format(context.HttpContext.Request.URL.String()).Error())
		}
	} else {
		if this.Event != nil {
			this.Event.Error(this, context, err)
		}
	}
}
<commit_msg>移除无效方法<commit_after>package web

import (
	"net/http"
	"path/filepath"
	"reflect"
	"time"

	"github.com/kdada/tinygo/connector"
	"github.com/kdada/tinygo/log"
	"github.com/kdada/tinygo/router"
	"github.com/kdada/tinygo/session"
	"github.com/kdada/tinygo/template"
)

// 参数类型方法
type ParamTypeFunc func(context *Context, name string, t reflect.Type) interface{}

// HttpProcessor 用于协调http连接器和路由,并管理Http应用的所有内容
type HttpProcessor struct {
	Root             router.Router                 //根路由
	Config           *HttpConfig                   //http配置
	Logger           log.Logger                    //日志记录
	SessionContainer session.SessionContainer      //Session容器
	CSRFContainer    session.SessionContainer      //Csrf容器
	Finders          map[string]ContextValueFinder //Context单类型值查找器
	MutiTypeFinders  []ContextValueFinder          //Context多类型值查找器
	Templates        *template.ViewTemplates       //视图模板信息
	Event            HttpProcessorEvent            //处理器事件
}

// NewHttpProcessor 创建Http处理器
func NewHttpProcessor(root router.Router, config *HttpConfig) (*HttpProcessor, error) {
	var processor = new(HttpProcessor)
	processor.Root = root
	processor.Config = config
	//日志
	if config.Log {
		var logger, err = log.NewLogger(config.LogType, config.LogPath)
		if err != nil {
			panic(err)
		}
		logger.SetAsync(config.LogAsync)
		processor.Logger = logger
	}
	//session
	if config.Session {
		var container, err = session.NewSessionContainer(config.SessionType, config.SessionExpire, config.SessionSource)
		if err != nil {
			panic(err)
		}
		processor.SessionContainer = container
	}
	//CSRF,过期时间与Session相同,CSRFExpire用于设置CSRF token的过期时间
	if config.CSRF {
		var container, err = session.NewSessionContainer(config.CSRFType, config.SessionExpire, config.CSRFSource)
		if err != nil {
			panic(err)
		}
		processor.CSRFContainer = container
	}

	//注册参数类型方法
	processor.Finders = make(map[string]ContextValueFinder)
	register(processor)
	//创建视图模板信息
	processor.Templates = template.NewViewTemplates(config.TemplateConfig)
	if config.Precompile {
		//预编译模板
		var err = processor.Templates.CompileAll()
		if err != nil {
			return nil, err
		}
	}
	//注册http事件
	processor.Event = new(DefaultHttpProcessorEvent)
	//注册静态文件路由
	if processor.Config.Favicon != "" {
		processor.Root.AddChild(NewFileRouter("favicon.ico", processor.Config.Favicon))
	}
	if processor.Config.Robots != "" {
		processor.Root.AddChild(NewFileRouter("robots.txt", processor.Config.Robots))
	}
	if len(processor.Config.Static) > 0 {
		for _, s := range processor.Config.Static {
			processor.Root.AddChild(NewStaticRouter(filepath.Base(s), s))
		}
	}
	//创建首页跳转
	if processor.Config.Home != "" {
		var r = NewSpaceRouter("Get")
		var excutor = NewSimpleExecutor(func(r *Context) (interface{}, error) {
			return r.Redispatch(r.Processor.Config.Home), nil
		})
		r.SetRouterExcutorGenerator(func() router.RouterExcutor {
			return excutor
		})
		processor.Root.AddChild(r)
	}
	return processor, nil
}

// RegisterFinder 注册单一类型的值查找器
func (this *HttpProcessor) RegisterFinder(t reflect.Type, finder ContextValueFinder) {
	this.Finders[t.String()] = finder
}

// RegisterMutiTypeFinder 注册多类型的值查找器
func (this *HttpProcessor) RegisterMutiTypeFinder(finder ContextValueFinder) {
	this.MutiTypeFinders = append(this.MutiTypeFinders, finder)
}

// createCookie 创建cookie
func (this *HttpProcessor) createCookie(name string, id string, expire int) *http.Cookie {
	var cookieValue = new(http.Cookie)
	cookieValue.Name = name
	cookieValue.Value = id
	cookieValue.Path = "/"
	cookieValue.HttpOnly = true
	if expire > 0 {
		cookieValue.MaxAge = expire
		cookieValue.Expires = time.Now().Add(time.Second * time.Duration(expire))
	}
	return cookieValue
}

// ResolveSession 处理会话相关内容
func (this *HttpProcessor) ResolveSession(context *Context) {
	if this.SessionContainer != nil {
		//添加Session信息
		var cookieValue, exist = context.Cookie(this.Config.SessionCookieName)
		var ss session.Session
		var ok bool = false
		if exist {
			ss, ok = this.SessionContainer.Session(cookieValue.Value)
		}
		if !ok {
			ss, ok = this.SessionContainer.CreateSession()
			context.AddCookie(this.createCookie(this.Config.SessionCookieName, ss.SessionId(), this.Config.SessionCookieExpire))
		}
		if ok {
			context.Session = ss
		}
	}
	if this.CSRFContainer != nil {
		//添加CSRF Session信息,CSRF的过期时间和Session相同,使用SessionExpire设置Cookie过期时间
		var cookieValue, exist = context.Cookie(this.Config.CSRFCookieName)
		var ss session.Session
		var ok bool = false
		if exist {
			ss, ok = this.CSRFContainer.Session(cookieValue.Value)
		}
		if !ok {
			ss, ok = this.CSRFContainer.CreateSession()
			context.AddCookie(this.createCookie(this.Config.CSRFCookieName, ss.SessionId(), this.Config.CSRFCookieExpire))
		}
		if ok {
			context.CSRF = ss
		}
	}
}

// Dispatch 将接收到的请求进行分发
//  segments:用于进行分发的路径段信息
//  data:连接携带的数据
func (this *HttpProcessor) Dispatch(segments []string, data interface{}) {
	var ct = data.(*connector.HttpContext)
	var context, err = NewContext(segments, ct)
	if err == nil {
		context.Processor = this
		this.ResolveSession(context)
		if this.Event != nil {
			this.Event.Request(this, context)
		}
		var executor, ok = this.Root.Match(context)
		if ok {
			var r, err = executor.Execute()
			if err != nil {
				this.Event.Error(this, context, err)
			} else if this.Event != nil {
				var result = []interface{}{}
				if r != nil {
					var rs, ok = r.([]interface{})
					if ok {
						result = rs
					} else {
						result = []interface{}{r}
					}
				}
				this.Event.RequestFinish(this, context, result)
			}
		} else if this.Event != nil {
			this.Event.Error(this, context, ErrorRouterNotFound.Format(context.HttpContext.Request.URL.String()).Error())
		}
	} else {
		if this.Event != nil {
			this.Event.Error(this, context, err)
		}
	}
}
<|endoftext|>