<commit_before>package main

import "path/filepath"
import "os"
import "io/ioutil"
import "fmt"
import "time"

var count = 0
var file_count = 0
var directory_count = 0
var verbose = false
var start_time = time.Now()

func print_file_stats(path string, info os.FileInfo) {
	fmt.Println("--")
	fmt.Print(path)
	fmt.Println(info.Name())
	fmt.Print(info.Size())
	fmt.Println(" Bytes")
	fmt.Println("Not a directory")
}

func print_directory_stats(path string, info os.FileInfo) {
	fmt.Println("--")
	fmt.Print(path)
	fmt.Println(info.Name())
	files, _ := ioutil.ReadDir(path)
	fmt.Print(len(files))
	fmt.Println(" files")
	fmt.Println("A directory")
}

func print_final_stats() {
	fmt.Printf("%d items found.\n", count)
	fmt.Printf("%d files found.\n", file_count)
	fmt.Printf("%d directories found.\n", directory_count)
	fmt.Printf("Execution took: %s.\n", time.Since(start_time))
}

func walk_file_tree(dirPath string) {
	filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			if verbose {
				print_file_stats(path, info)
			}
			count++
			file_count++
		}
		if info.IsDir() {
			if verbose {
				print_directory_stats(path, info)
			}
			count++
			directory_count++
		}
		return nil
	})
}

func main() {
	path := os.Args[1]
	walk_file_tree(path)
	print_final_stats()
}
<commit_msg>/proc doesn't play nicely with this for reasons<commit_after>package main

import "path/filepath"
import "os"
import "io/ioutil"
import "fmt"
import "time"

var count = 0
var file_count = 0
var directory_count = 0
var verbose = true
var start_time = time.Now()

// Variable to store list of paths to ignore and not walk.
// Ensure /proc is in this list.

func print_file_stats(path string, info os.FileInfo) {
	fmt.Println("--")
	fmt.Print(path)
	fmt.Println(info.Name())
	fmt.Print(info.Size())
	fmt.Println(" Bytes")
	fmt.Println("Not a directory")
}

func print_directory_stats(path string, info os.FileInfo) {
	fmt.Println("--")
	fmt.Print(path)
	fmt.Println(info.Name())
	files, _ := ioutil.ReadDir(path)
	fmt.Print(len(files))
	fmt.Println(" files")
	fmt.Println("A directory")
}

func print_final_stats() {
	fmt.Printf("%d items found.\n", count)
	fmt.Printf("%d files found.\n", file_count)
	fmt.Printf("%d directories found.\n", directory_count)
	fmt.Printf("Execution took: %s.\n", time.Since(start_time))
}

func walk_file_tree(dirPath string) {
	filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		// if path is in ignored paths don't continue
		if !info.IsDir() {
			if verbose {
				print_file_stats(path, info)
			}
			count++
			file_count++
		}
		if info.IsDir() {
			if verbose {
				print_directory_stats(path, info)
			}
			count++
			directory_count++
		}
		return nil
	})
}

func main() {
	path := os.Args[1]
	walk_file_tree(path)
	print_final_stats()
}
<|endoftext|>


<commit_before>/*****************************************************************************/
/*                                                                           */
/* StatZone (c) by Frederic Cambus 2012-2015                                 */
/* http://www.statdns.com                                                    */
/*                                                                           */
/* Created: 2012/02/13                                                       */
/* Last Updated: 2015/08/10                                                  */
/*                                                                           */
/* StatZone is released under the BSD 3-Clause license.                      */
/* See LICENSE file for details.                                             */
/*                                                                           */
/*****************************************************************************/

package main

import (
	"bufio"
	"fmt"
	"github.com/miekg/dns"
	"os"
	"strconv"
	"strings"
)

type Domains struct {
	count    int
	idn      int
	previous string
	suffix   int
}

var rrParsed int

/* Return rdata */
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

func main() {
	header := `-------------------------------------------------------------------------------
                   StatZone (c) by Frederic Cambus 2012-2015
-------------------------------------------------------------------------------`

	fmt.Println(header + "\n")

	/* Check input parameters and show usage */
	if len(os.Args) != 2 {
		fmt.Println("USAGE:    statzone inputfile\n")
		fmt.Println("EXAMPLES: statzone arpa.zone\n")
		os.Exit(1)
	}

	inputFile := os.Args[1]

	fmt.Println("Parsing zone :", inputFile)

	domains := new(Domains)

	ns := map[string]int{}
	signed := map[string]int{}

	zoneFile, err := os.Open(inputFile)
	if err != nil {
		fmt.Println("ERROR : Can't open zone file.")
	}

	zone := dns.ParseZone(bufio.NewReader(zoneFile), "", "")

	var rrtypes [100]int

	for parsedLine := range zone {
		if parsedLine.RR != nil {
			rrtypes[parsedLine.RR.Header().Rrtype]++

			switch parsedLine.RR.Header().Rrtype {
			case dns.TypeDS:
				/* Increment Signed Domains counter */
				signed[parsedLine.RR.Header().Name]++
			case dns.TypeNS:
				/* Increment NS counter */
				ns[rdata(parsedLine)]++

				if parsedLine.RR.Header().Name != domains.previous { // Unique domain

					/* Increment Domain counter */
					domains.count++
					domains.previous = parsedLine.RR.Header().Name

					/* Check if the domain is an IDN */

					if strings.HasPrefix(strings.ToLower(parsedLine.RR.Header().Name), "xn--") {
						domains.idn++
					}

					/* Display progression */
					if domains.count%1000000 == 0 {
						fmt.Printf("*")
					} else if domains.count%100000 == 0 {
						fmt.Printf(".")
					}
				}
			}
		} else {
			fmt.Println("ERROR : A problem occured while parsing the zone file.")
		}

		/* Increment number of resource records parsed */
		rrParsed++
	}

	/* Don't count origin */
	domains.count--

	fmt.Println("\n---[ Parsing results ]---------------------------------------------------------\n")
	fmt.Println(rrParsed, "RRs parsed.")
	for loop := 0; loop < len(rrtypes); loop++ {
		rrtype := rrtypes[loop]
		if rrtype != 0 {
			fmt.Println(dns.TypeToString[uint16(loop)], "records :", rrtype)
		}
	}

	fmt.Println("\n---[ Results ]-----------------------------------------------------------------\n")
	fmt.Println("Domains : ", domains.count)
	fmt.Println("DNSSEC Signed : ", len(signed))

	fmt.Println("IDNs : ", domains.idn)
	fmt.Println("NS : ", len(ns))

	fmt.Println("\n---[ Creating result files ]---------------------------------------------------\n")

	/* Creating name servers list + number of zones served */
	fmt.Println("Creating :", inputFile + ".csv")
	outputFile, outputError := os.OpenFile(inputFile + ".csv", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if outputError != nil {
		fmt.Printf("ERROR : Can't create output file.\n")
		return
	}

	defer outputFile.Close()

	outputWriter := bufio.NewWriter(outputFile)

	for item := range ns {
		outputWriter.WriteString(strings.ToLower(strings.TrimRight(item, ".")) + ";" + strconv.Itoa(ns[item]) + "\n")
	}

	outputWriter.Flush()

	fmt.Println("\n---[ CSV values ]--------------------------------------------------------------\n")

	fmt.Println("IPv4 Glue ; IPv6 Glue ; NS ; Unique NS ; DS ; Signed ; IDNs ; Domains")
	fmt.Println(rrtypes[dns.TypeA], ";", rrtypes[dns.TypeAAAA], ";", rrtypes[dns.TypeNS], ";", len(ns), ";", rrtypes[dns.TypeDS], ";", len(signed), ";", domains.idn, ";", domains.count)
}
<commit_msg>Gofmt -w<commit_after>/*****************************************************************************/
/*                                                                           */
/* StatZone (c) by Frederic Cambus 2012-2015                                 */
/* http://www.statdns.com                                                    */
/*                                                                           */
/* Created: 2012/02/13                                                       */
/* Last Updated: 2015/08/10                                                  */
/*                                                                           */
/* StatZone is released under the BSD 3-Clause license.                      */
/* See LICENSE file for details.                                             */
/*                                                                           */
/*****************************************************************************/

package main

import (
	"bufio"
	"fmt"
	"github.com/miekg/dns"
	"os"
	"strconv"
	"strings"
)

type Domains struct {
	count    int
	idn      int
	previous string
	suffix   int
}

var rrParsed int

/* Return rdata */
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

func main() {
	header := `-------------------------------------------------------------------------------
                   StatZone (c) by Frederic Cambus 2012-2015
-------------------------------------------------------------------------------`

	fmt.Println(header + "\n")

	/* Check input parameters and show usage */
	if len(os.Args) != 2 {
		fmt.Println("USAGE:    statzone inputfile\n")
		fmt.Println("EXAMPLES: statzone arpa.zone\n")
		os.Exit(1)
	}

	inputFile := os.Args[1]

	fmt.Println("Parsing zone :", inputFile)

	domains := new(Domains)

	ns := map[string]int{}
	signed := map[string]int{}

	zoneFile, err := os.Open(inputFile)
	if err != nil {
		fmt.Println("ERROR : Can't open zone file.")
	}

	zone := dns.ParseZone(bufio.NewReader(zoneFile), "", "")

	var rrtypes [100]int

	for parsedLine := range zone {
		if parsedLine.RR != nil {
			rrtypes[parsedLine.RR.Header().Rrtype]++

			switch parsedLine.RR.Header().Rrtype {
			case dns.TypeDS:
				/* Increment Signed Domains counter */
				signed[parsedLine.RR.Header().Name]++
			case dns.TypeNS:
				/* Increment NS counter */
				ns[rdata(parsedLine)]++

				if parsedLine.RR.Header().Name != domains.previous { // Unique domain

					/* Increment Domain counter */
					domains.count++
					domains.previous = parsedLine.RR.Header().Name

					/* Check if the domain is an IDN */

					if strings.HasPrefix(strings.ToLower(parsedLine.RR.Header().Name), "xn--") {
						domains.idn++
					}

					/* Display progression */
					if domains.count%1000000 == 0 {
						fmt.Printf("*")
					} else if domains.count%100000 == 0 {
						fmt.Printf(".")
					}
				}
			}
		} else {
			fmt.Println("ERROR : A problem occured while parsing the zone file.")
		}

		/* Increment number of resource records parsed */
		rrParsed++
	}

	/* Don't count origin */
	domains.count--

	fmt.Println("\n---[ Parsing results ]---------------------------------------------------------\n")
	fmt.Println(rrParsed, "RRs parsed.")
	for loop := 0; loop < len(rrtypes); loop++ {
		rrtype := rrtypes[loop]
		if rrtype != 0 {
			fmt.Println(dns.TypeToString[uint16(loop)], "records :", rrtype)
		}
	}

	fmt.Println("\n---[ Results ]-----------------------------------------------------------------\n")
	fmt.Println("Domains : ", domains.count)
	fmt.Println("DNSSEC Signed : ", len(signed))

	fmt.Println("IDNs : ", domains.idn)
	fmt.Println("NS : ", len(ns))

	fmt.Println("\n---[ Creating result files ]---------------------------------------------------\n")

	/* Creating name servers list + number of zones served */
	fmt.Println("Creating :", inputFile+".csv")
	outputFile, outputError := os.OpenFile(inputFile+".csv", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if outputError != nil {
		fmt.Printf("ERROR : Can't create output file.\n")
		return
	}

	defer outputFile.Close()

	outputWriter := bufio.NewWriter(outputFile)

	for item := range ns {
		outputWriter.WriteString(strings.ToLower(strings.TrimRight(item, ".")) + ";" + strconv.Itoa(ns[item]) + "\n")
	}

	outputWriter.Flush()

	fmt.Println("\n---[ CSV values ]--------------------------------------------------------------\n")

	fmt.Println("IPv4 Glue ; IPv6 Glue ; NS ; Unique NS ; DS ; Signed ; IDNs ; Domains")
	fmt.Println(rrtypes[dns.TypeA], ";", rrtypes[dns.TypeAAAA], ";", rrtypes[dns.TypeNS], ";", len(ns), ";", rrtypes[dns.TypeDS], ";", len(signed), ";", domains.idn, ";", domains.count)
}
<|endoftext|>


<commit_before>package store

// Layout of the bolt database:
//
// objects/<NODE_HASH>            => NODE_METADATA
// tree/<FULL_NODE_PATH>          => NODE_HASH
// refs/<REFNAME>                 => NODE_HASH
// checkpoints/<NODE_HASH>/<IDX>  => CHECKPOINT_DATA
// stage/objects/<NODE_HASH>      => NODE_METADATA
// stage/tree/<FULL_NODE_PATH>    => NODE_HASH
//
// NODE is either a Commit, a Directory or a File.
// FULL_NODE_PATH may contain slashes and in case of directories,
// it will contain a trailing slash.

import (
	"errors"
	"fmt"
	"path"
	"strings"

	"github.com/disorganizer/brig/store/wire"
	"github.com/disorganizer/brig/util/trie"
	"github.com/gogo/protobuf/proto"
)

/////////////// ERRORS ///////////////

var (
	ErrBadNode = errors.New("Cannot convert to concrete type. Broken input data?")
)

type ErrBadNodeType int

func (e ErrBadNodeType) Error() string {
	return fmt.Sprintf("Bad node type in db: %d", int(e))
}

type ErrNoHashFound struct {
	b58hash string
	where   string
}

func (e ErrNoHashFound) Error() string {
	return fmt.Sprintf("No such hash in `%s`: '%s'", e.where, e.b58hash)
}

type ErrNoPathFound struct {
	path  string
	where string
}

func (e ErrNoPathFound) Error() string {
	return fmt.Sprintf("No such path in `%s`: '%s'", e.where, e.path)
}

type FS struct {
	kv KV

	// Path lookup trie
	root *trie.Node

	// B58Hash to node
	index map[string]*trie.Node
}

func NewFilesystem(kv KV) *FS {
	return &FS{
		kv:    kv,
		root:  trie.NewNode(),
		index: make(map[string]*trie.Node),
	}
}

var (
	loadableBuckets = []string{"objects", "stage/objects"}
)

// LoadObject loads an individual object by its hash from the object store.
func (fs *FS) loadNode(hash *Hash) (Node, error) {
	var data []byte
	var err error

	b58hash := hash.B58String()

	for _, bucketPath := range loadableBuckets {
		var bkt Bucket
		bkt, err = fs.kv.Bucket([]string{bucketPath})
		if err != nil {
			return nil, err
		}

		data, err = bkt.Get(b58hash)
		if err != nil {
			return nil, err
		}

		if data != nil {
			break
		}
	}

	fmt.Println("lookupNode", data, b58hash)

	// Damn, no hash found:
	if data == nil {
		return nil, ErrNoHashFound{
			b58hash,
			strings.Join(loadableBuckets, " + "),
		}
	}

	node := &wire.Node{}
	if err := proto.Unmarshal(data, node); err != nil {
		return nil, err
	}
	fmt.Println("lookupNode unmarshal done")

	typ := node.GetType()
	switch typ {
	case wire.NodeType_FILE:
		// TODO
	case wire.NodeType_DIRECTORY:
		dir := &Directory{fs: fs}
		if err := dir.FromProto(node); err != nil {
			return nil, err
		}

		return dir, nil
	case wire.NodeType_COMMIT:
		// TODO
	}

	return nil, ErrBadNodeType(typ)
}

// TODO: Root() should read HEAD and return the referenced directory in there.

func (fs *FS) NodeByHash(hash *Hash) (Node, error) {
	// Check if we have this this node in the cache already:
	b58Hash := hash.B58String()
	if trieNode, ok := fs.index[b58Hash]; ok && trieNode.Data != nil {
		return trieNode.Data.(Node), nil
	}

	// Node was not in the cache, load directly from bolt.
	nd, err := fs.loadNode(hash)
	if err != nil {
		return nil, err
	}

	// NOTE: This will indirectly load parent directories (by calling
	//       Parent(), if not done yet!  We might be stuck in an endless loop if we
	//       have cycles in our DAG.
	fs.index[b58Hash] = fs.root.InsertWithData(nodePath(nd), nd)
	return nd, nil
}

func (fs *FS) ResolveNode(nodePath string) (Node, error) {
	// Check if it's cached already:
	trieNode := fs.root.Lookup(nodePath)
	if trieNode != nil && trieNode.Data != nil {
		return trieNode.Data.(Node), nil
	}

	var hash []byte
	var err error

	prefixes := []string{"tree/", "stage/tree/"}
	for _, prefix := range prefixes {
		// getPath() does a hierarchical lookup:
		hash, err = getPath(fs.kv, prefix+nodePath)
		fmt.Println("looking up path:", prefix+nodePath)

		if err != nil {
			return nil, err
		}

		if hash != nil {
			break
		}
	}

	if hash == nil {
		return nil, ErrNoPathFound{
			nodePath,
			strings.Join(prefixes, " and "),
		}
	}

	x := &Hash{hash}
	fmt.Println("Resolved to hash:", string(hash), string(x.Bytes()))

	// Delegate the actual directory loading to Directory()
	return fs.NodeByHash(&Hash{hash})
}

func (fs *FS) StageNode(nd Node) error {
	bkt, err := fs.kv.Bucket([]string{"stage/objects"})
	if err != nil {
		return err
	}

	object, err := nd.ToProto()
	if err != nil {
		return err
	}

	data, err := proto.Marshal(object)
	if err != nil {
		return err
	}

	b58Hash := nd.Hash().B58String()
	if err := bkt.Put(b58Hash, data); err != nil {
		return err
	}

	// Clean() will also remove trailing slashes:
	hashPath := path.Join("stage/tree", path.Clean(nodePath(nd)))
	switch nd.GetType() {
	case NodeTypeDirectory:
		hashPath += "/."
	}

	fmt.Println(hashPath, nodePath(nd), "insert", b58Hash)

	if err := putPath(fs.kv, hashPath, nd.Hash().Bytes()); err != nil {
		return err
	}

	// TODO: Insert to fs.index and fs.root.

	// We need to save parent directories too, in case the hash changed:
	par, err := nd.Parent()
	if err != nil {
		return err
	}

	if par != nil {
		if err := fs.StageNode(par); err != nil {
			return err
		}
	}

	return nil
}

func (fs *FS) MakeCommit() (*Commit, error) {
	// TODO: Copy everything in stage/objects and stage/tree to
	//       objects/ and tree/. Also make a commit of all current checkpoints.
	return nil, nil
}

func ResolveRef(refname string) (Node, error) {
	// TODO: Resolve refs/<refname> to objects/$HASH
	return nil, nil
}

func (fs *FS) SaveRef(refname string, nd Node) error {
	// TODO: Place refname and nd.Hash() in refs/<refname>
	return nil
}

func (fs *FS) RemoveUnreffedNodes() error {
	// TODO: This is a NO-OP currently.
	// In future this needs to be called periodically and do the following:
	// - Go through all commits and remember all hashes of all trees.
	// - Go through all hash-buckets and delete all unreffed hashes.
	return nil
}

////////////////////////////

func (fs *FS) DirectoryByHash(hash *Hash) (*Directory, error) {
	nd, err := fs.NodeByHash(hash)
	if err != nil {
		return nil, err
	}

	dir, ok := nd.(*Directory)
	if !ok {
		return nil, ErrBadNode
	}

	return dir, nil
}

func (fs *FS) ResolveDirectory(dirpath string) (*Directory, error) {
	nd, err := fs.ResolveNode(path.Clean(dirpath) + "/.")
	if err != nil {
		return nil, err
	}

	dir, ok := nd.(*Directory)
	if !ok {
		return nil, ErrBadNode
	}

	return dir, nil
}
<commit_msg>store: Fix the jacked up path handling<commit_after>package store

// Layout of the bolt database:
//
// objects/<NODE_HASH>            => NODE_METADATA
// tree/<FULL_NODE_PATH>          => NODE_HASH
// refs/<REFNAME>                 => NODE_HASH
// checkpoints/<NODE_HASH>/<IDX>  => CHECKPOINT_DATA
// stage/objects/<NODE_HASH>      => NODE_METADATA
// stage/tree/<FULL_NODE_PATH>    => NODE_HASH
//
// NODE is either a Commit, a Directory or a File.
// FULL_NODE_PATH may contain slashes and in case of directories,
// it will contain a trailing slash.

import (
	"errors"
	"fmt"
	"path"
	"strings"

	"github.com/disorganizer/brig/store/wire"
	"github.com/disorganizer/brig/util/trie"
	"github.com/gogo/protobuf/proto"
)

/////////////// ERRORS ///////////////

var (
	ErrBadNode = errors.New("Cannot convert to concrete type. Broken input data?")
)

type ErrBadNodeType int

func (e ErrBadNodeType) Error() string {
	return fmt.Sprintf("Bad node type in db: %d", int(e))
}

type ErrNoHashFound struct {
	b58hash string
	where   string
}

func (e ErrNoHashFound) Error() string {
	return fmt.Sprintf("No such hash in `%s`: '%s'", e.where, e.b58hash)
}

type ErrNoPathFound struct {
	path  string
	where string
}

func (e ErrNoPathFound) Error() string {
	return fmt.Sprintf("No such path in `%s`: '%s'", e.where, e.path)
}

type FS struct {
	kv KV

	// Path lookup trie
	root *trie.Node

	// B58Hash to node
	index map[string]*trie.Node
}

func NewFilesystem(kv KV) *FS {
	return &FS{
		kv:    kv,
		root:  trie.NewNode(),
		index: make(map[string]*trie.Node),
	}
}

var (
	loadableBuckets = []string{"objects", "stage/objects"}
)

// LoadObject loads an individual object by its hash from the object store.
func (fs *FS) loadNode(hash *Hash) (Node, error) {
	var data []byte
	var err error

	b58hash := hash.B58String()

	for _, bucketPath := range loadableBuckets {
		var bkt Bucket
		bkt, err = fs.kv.Bucket([]string{bucketPath})
		if err != nil {
			return nil, err
		}

		data, err = bkt.Get(b58hash)
		if err != nil {
			return nil, err
		}

		if data != nil {
			break
		}
	}

	fmt.Println("lookupNode", data, b58hash)

	// Damn, no hash found:
	if data == nil {
		return nil, ErrNoHashFound{
			b58hash,
			strings.Join(loadableBuckets, " and "),
		}
	}

	node := &wire.Node{}
	if err := proto.Unmarshal(data, node); err != nil {
		return nil, err
	}
	fmt.Println("lookupNode unmarshal done")

	typ := node.GetType()
	switch typ {
	case wire.NodeType_FILE:
		// TODO
	case wire.NodeType_DIRECTORY:
		dir := &Directory{fs: fs}
		if err := dir.FromProto(node); err != nil {
			return nil, err
		}

		return dir, nil
	case wire.NodeType_COMMIT:
		// TODO
	}

	return nil, ErrBadNodeType(typ)
}

// TODO: Root() should read HEAD and return the referenced directory in there.

func (fs *FS) NodeByHash(hash *Hash) (Node, error) {
	// Check if we have this this node in the cache already:
	b58Hash := hash.B58String()
	if trieNode, ok := fs.index[b58Hash]; ok && trieNode.Data != nil {
		return trieNode.Data.(Node), nil
	}

	// Node was not in the cache, load directly from bolt.
	nd, err := fs.loadNode(hash)
	if err != nil {
		return nil, err
	}

	// NOTE: This will indirectly load parent directories (by calling
	//       Parent(), if not done yet!  We might be stuck in an endless loop if we
	//       have cycles in our DAG.
	fs.index[b58Hash] = fs.root.InsertWithData(nodePath(nd), nd)
	return nd, nil
}

func appendDot(path string) string {
	// path.Join() calls path.Clean() which in turn
	// removes the '.' at the end when trying to join that.
	if strings.HasSuffix(path, "/") {
		return path + "."
	}

	return path + "/."
}

func joinButLeaveLastDot(elems ...string) string {
	if len(elems) == 0 {
		return ""
	}

	if strings.HasSuffix(elems[len(elems)-1], "/.") {
		return appendDot(path.Join(elems...))
	}

	return path.Join(elems...)
}

func (fs *FS) ResolveNode(nodePath string) (Node, error) {
	// Check if it's cached already:
	trieNode := fs.root.Lookup(nodePath)
	if trieNode != nil && trieNode.Data != nil {
		return trieNode.Data.(Node), nil
	}

	var hash []byte
	var err error

	prefixes := []string{"tree/", "stage/tree/"}
	for _, prefix := range prefixes {
		// getPath() does a hierarchical lookup:
		fmt.Println("looking up path:", joinButLeaveLastDot(prefix, nodePath), nodePath)
		hash, err = getPath(fs.kv, joinButLeaveLastDot(prefix, nodePath))

		if err != nil {
			return nil, err
		}

		if hash != nil {
			break
		}
	}

	if hash == nil {
		return nil, ErrNoPathFound{
			nodePath,
			strings.Join(prefixes, " and "),
		}
	}

	// Delegate the actual directory loading to Directory()
	return fs.NodeByHash(&Hash{hash})
}

func (fs *FS) StageNode(nd Node) error {
	bkt, err := fs.kv.Bucket([]string{"stage/objects"})
	if err != nil {
		return err
	}

	object, err := nd.ToProto()
	if err != nil {
		return err
	}

	data, err := proto.Marshal(object)
	if err != nil {
		return err
	}

	b58Hash := nd.Hash().B58String()
	if err := bkt.Put(b58Hash, data); err != nil {
		return err
	}

	// The key is the path of the
	nodePath := nodePath(nd)
	hashPath := path.Join("stage/tree", nodePath)
	switch nd.GetType() {
	case NodeTypeDirectory:
		hashPath = appendDot(hashPath)
	}

	fmt.Println("Stage:", hashPath)

	if err := putPath(fs.kv, hashPath, nd.Hash().Bytes()); err != nil {
		return err
	}

	// Remember/Update this node in the cache if it's not yet there:
	fs.index[b58Hash] = fs.root.InsertWithData(nodePath, nd)

	// We need to save parent directories too, in case the hash changed:
	par, err := nd.Parent()
	if err != nil {
		return err
	}

	if par != nil {
		if err := fs.StageNode(par); err != nil {
			return err
		}
	}

	return nil
}

func (fs *FS) MakeCommit() (*Commit, error) {
	// TODO: Copy everything in stage/objects and stage/tree to
	//       objects/ and tree/. Also make a commit of all current checkpoints.
	return nil, nil
}

func ResolveRef(refname string) (Node, error) {
	// TODO: Resolve refs/<refname> to objects/$HASH
	return nil, nil
}

func (fs *FS) SaveRef(refname string, nd Node) error {
	// TODO: Place refname and nd.Hash() in refs/<refname>
	return nil
}

func (fs *FS) RemoveUnreffedNodes() error {
	// TODO: This is a NO-OP currently.
	// In future this needs to be called periodically and do the following:
	// - Go through all commits and remember all hashes of all trees.
	// - Go through all hash-buckets and delete all unreffed hashes.
	return nil
}

////////////////////////////

func (fs *FS) DirectoryByHash(hash *Hash) (*Directory, error) {
	nd, err := fs.NodeByHash(hash)
	if err != nil {
		return nil, err
	}

	dir, ok := nd.(*Directory)
	if !ok {
		return nil, ErrBadNode
	}

	return dir, nil
}

func (fs *FS) ResolveDirectory(dirpath string) (*Directory, error) {
	nd, err := fs.ResolveNode(appendDot(path.Clean(dirpath)))
	if err != nil {
		return nil, err
	}

	dir, ok := nd.(*Directory)
	if !ok {
		return nil, ErrBadNode
	}

	return dir, nil
}
<|endoftext|>


<commit_before>package grayt

import (
	"image"
	"math/rand"
	"sync/atomic"
	"time"
)

type strategy struct {
}

func (s *strategy) traceImage(pxHigh, pxWide int, scene Scene, quality int) image.Image {

	acc := newAccumulator(pxHigh, pxWide)

	// MUST only be used atomically, since it's shared by 2 goroutines - the
	// tracing goroutine and the CLI goroutine.
	var completed uint64

	// Update the CLI (in a separate goroutine).
	cli := newCLI()
	done := make(chan struct{})
	go func() {
		total := uint64(pxWide * pxHigh * quality)
		for {
			var exit bool
			select {
			case <-done:
				exit = true
			case <-time.After(cliUpdatePeriod):
			}
			cli.update(atomic.LoadUint64(&completed), total)
			if exit {
				cli.done()
				done <- struct{}{}
				return
			}
		}
	}()

	// Trace the image.
	w := newWorld(scene.Entities)
	pxPitch := 2.0 / float64(pxWide)
	for i := 0; i < quality; i++ {
		for pxX := 0; pxX < pxWide; pxX++ {
			for pxY := 0; pxY < pxHigh; pxY++ {
				x := (float64(pxX-pxWide/2) + rand.Float64()) * pxPitch
				y := (float64(pxY-pxHigh/2) + rand.Float64()) * pxPitch * -1.0
				r := scene.Camera.MakeRay(x, y)
				r.Dir = r.Dir.Unit()
				acc.add(pxX, pxY, tracePath(w, r))
				atomic.AddUint64(&completed, 1)
			}
		}
	}

	// Tell the CLI to finish, then wait for its reply.
	done <- struct{}{}
	<-done

	return acc.toImage(1.0)
}

// TODO: Also output some kind of meta file about the image that was generated?
<commit_msg>Switch loop ordering so quality is on the inside<commit_after>package grayt

import (
	"image"
	"math/rand"
	"sync/atomic"
	"time"
)

type strategy struct {
}

func (s *strategy) traceImage(pxHigh, pxWide int, scene Scene, quality int) image.Image {

	acc := newAccumulator(pxHigh, pxWide)

	// MUST only be used atomically, since it's shared by 2 goroutines - the
	// tracing goroutine and the CLI goroutine.
	var completed uint64

	// Update the CLI (in a separate goroutine).
	cli := newCLI()
	done := make(chan struct{})
	go func() {
		total := uint64(pxWide * pxHigh * quality)
		for {
			var exit bool
			select {
			case <-done:
				exit = true
			case <-time.After(cliUpdatePeriod):
			}
			cli.update(atomic.LoadUint64(&completed), total)
			if exit {
				cli.done()
				done <- struct{}{}
				return
			}
		}
	}()

	// Trace the image.
	w := newWorld(scene.Entities)
	pxPitch := 2.0 / float64(pxWide)
	for pxX := 0; pxX < pxWide; pxX++ {
		for pxY := 0; pxY < pxHigh; pxY++ {
			for i := 0; i < quality; i++ {
				x := (float64(pxX-pxWide/2) + rand.Float64()) * pxPitch
				y := (float64(pxY-pxHigh/2) + rand.Float64()) * pxPitch * -1.0
				r := scene.Camera.MakeRay(x, y)
				r.Dir = r.Dir.Unit()
				acc.add(pxX, pxY, tracePath(w, r))
				atomic.AddUint64(&completed, 1)
			}
		}
	}

	// Tell the CLI to finish, then wait for its reply.
	done <- struct{}{}
	<-done

	return acc.toImage(1.0)
}

// TODO: Also output some kind of meta file about the image that was generated?
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"math/rand"
	"net/mail"
	"strconv"
	"time"

	"log"

	"github.com/extemporalgenome/slug"
	"github.com/joshsoftware/curem/config"
	"labix.org/v2/mgo/bson"
)

// contact type holds the fields related to a particular contact.
// omitempty tag will make sure the database doesn't contain content like:
//
//   {
//    _id: someId
//    company: ABC
//    Person: Xyz
//    Phone:
//    Skype:
//    Country:
//   }
// Instead, it will store the above data as:
//
//   {
//    _id: someId
//    company: ABC
//    Person: Xyz
//   }
type contact struct {
	Id        bson.ObjectId `bson:"_id"                  json:"id"`
	Company   string        `bson:"company,omitempty"    json:"company,omitempty"`
	Person    string        `bson:"person,omitempty"     json:"person,omitempty"`
	Slug      string        `bson:"slug,omitempty"       json:"slug,omitempty"`
	Email     string        `bson:"email,omitempty"      json:"email,omitempty"`
	Phone     string        `bson:"phone,omitempty"      json:"phone,omitempty"`
	SkypeId   string        `bson:"skypeid,omitempty"    json:"skypeid,omitempty"`
	Country   string        `bson:"country,omitempty"    json:"country,omitempty"`
	CreatedAt time.Time     `bson:"createdAt,omitempty"  json:"createdAt,omitempty"`
	UpdatedAt time.Time     `bson:"updatedAt,omitempty"  json:"updatedAt,omitempty"`
}

// incomingContact type is used for handling PATCH requests.
// To understand why we are using pointer types for fields,
// refer to http://blog.golang.org/json-and-go .
// Using pointer types, we can differentiate intentional nil value fields
// and empty fields.
// This type is used *only* for decoding json obtained from a PATCH request.
type incomingContact struct {
	Id      *bson.ObjectId `json:"id"`
	Company *string        `json:"company,omitempty"`
	Person  *string        `json:"person,omitempty"`
	Slug    *string        `json:"slug,omitempty"`
	Email   *string        `json:"email,omitempty"`
	Phone   *string        `json:"phone,omitempty"`
	SkypeId *string        `json:"skypeid,omitempty"`
	Country *string        `json:"country,omitempty"`
}

// copyIncomingFields copies the fields from an incomingContact into a
// contact object.
// Using multiple if statements provides more granularity, while allowing
// update of only some specific fields.
// TODO(Hari): Use reflect package instead of multiple if statements
func (c *contact) copyIncomingFields(i *incomingContact) error {
	if i.Id != nil {
		if *i.Id != c.Id {
			return errors.New("Id doesn't match")
		}
	}
	if i.Slug != nil {
		if *i.Slug != c.Slug {
			return errors.New("Slug can't be updated")
		}
	}
	if i.Company != nil {
		c.Company = *i.Company
	}
	if i.Person != nil {
		c.Person = *i.Person
	}
	if i.Slug != nil {
		c.Slug = *i.Slug
	}
	if i.Email != nil {
		c.Email = *i.Email
	}
	if i.Phone != nil {
		c.Phone = *i.Phone
	}
	if i.SkypeId != nil {
		c.SkypeId = *i.SkypeId
	}
	if i.Country != nil {
		c.Country = *i.Country
	}
	return nil
}

func validateContact(c *contact) error {
	if c.Person == "" {
		err := errors.New("person can't be empty")
		return err
	}
	if c.Email == "" {
		err := errors.New("email can't be empty")
		return err
	}
	if _, err := mail.ParseAddress(c.Email); err != nil {
		return err
	}

	return nil
}

// NewContact takes the fields of a contact, initializes a struct of contact type and returns
// the pointer to that struct.
func NewContact(company, person, email, phone, skypeid, country string) (*contact, error) {
	doc := contact{
		Id:      bson.NewObjectId(),
		Company: company,
		Person:  person,
		Email:   email,
		Phone:   phone,
		SkypeId: skypeid,
		Country: country,
	}
	if err := validateContact(&doc); err != nil {
		return &contact{}, err
	}
	slugifyContact(&doc)

	doc.CreatedAt = doc.Id.Time()
	doc.UpdatedAt = doc.CreatedAt

	err := config.ContactsCollection.Insert(doc)
	if err != nil {
		return &contact{}, err
	}
	return &doc, nil
}

// GetContactById takes the contact Id as an argument and returns a pointer to the contact object.
func GetContactById(i bson.ObjectId) (*contact, error) {
	var c contact
	err := config.ContactsCollection.FindId(i).One(&c)
	if err != nil {
		return &contact{}, err
	}
	return &c, nil
}

// GetContactBySlug takes the contact slug as an argument and returns a pointer to the contact object.
func GetContactBySlug(slug string) (*contact, error) {
	var c []contact
	err := config.ContactsCollection.Find(bson.M{"slug": slug}).All(&c)
	if err != nil {
		return &contact{}, err
	}

	if len(c) == 0 {
		return &contact{}, errors.New("no contact")
	}

	if len(c) > 1 {
		return &contact{}, errors.New("more than 1 contact found")
	}
	return &c[0], nil
}

// GetAllContacts fetches all the contacts from the database.
func GetAllContacts() ([]contact, error) {
	var c []contact
	err := config.ContactsCollection.Find(nil).All(&c)
	if err != nil {
		return []contact{}, err
	}
	return c, nil
}

// Update updates the contact in the database.
// First, fetch a contact from the database and change the necessary fields.
// Then call the Update method on that contact object.
func (c *contact) Update() error {
	if err := validateContact(c); err != nil {
		return err
	}
	c.UpdatedAt = bson.Now()
	err := config.ContactsCollection.UpdateId(c.Id, c)
	return err
}

// Delete deletes the contact from the database.
func (c *contact) Delete() error {
	return config.ContactsCollection.RemoveId(c.Id)
}

// slugifyContact takes a pointer to a contact as the argument and
// creates a slug and populates the slug field of the contact.
func slugifyContact(c *contact) {
	base := slug.SlugAscii(c.Person)
	temp := base
	rand.Seed(time.Now().UnixNano()) // takes the current time in nanoseconds as the seed
	i := rand.Intn(10000)
	for {
		if contactSlugExists(temp) {
			temp = base + "-" + strconv.Itoa(i)
			i = rand.Intn(10000)
		} else {
			c.Slug = temp
			return
		}

	}
}

// contactSlugExists returns true if a slug for a contact already exists in the database
// else returns false.
func contactSlugExists(slug string) bool {
	var c []contact
	err := config.ContactsCollection.Find(bson.M{"slug": slug}).All(&c)
	if err != nil {
		log.Println(err)
		return true // returns true just to be safe.
	}
	if len(c) == 0 {
		return false
	}
	return true
}
<commit_msg>Modify json tag for skype id<commit_after>package main

import (
	"errors"
	"math/rand"
	"net/mail"
	"strconv"
	"time"

	"log"

	"github.com/extemporalgenome/slug"
	"github.com/joshsoftware/curem/config"
	"labix.org/v2/mgo/bson"
)

// contact type holds the fields related to a particular contact.
// omitempty tag will make sure the database doesn't contain content like:
//
//   {
//    _id: someId
//    company: ABC
//    Person: Xyz
//    Phone:
//    Skype:
//    Country:
//   }
// Instead, it will store the above data as:
//
//   {
//    _id: someId
//    company: ABC
//    Person: Xyz
//   }
type contact struct {
	Id        bson.ObjectId `bson:"_id"                  json:"id"`
	Company   string        `bson:"company,omitempty"    json:"company,omitempty"`
	Person    string        `bson:"person,omitempty"     json:"person,omitempty"`
	Slug      string        `bson:"slug,omitempty"       json:"slug,omitempty"`
	Email     string        `bson:"email,omitempty"      json:"email,omitempty"`
	Phone     string        `bson:"phone,omitempty"      json:"phone,omitempty"`
	SkypeId   string        `bson:"skypeId,omitempty"    json:"skypeId,omitempty"`
	Country   string        `bson:"country,omitempty"    json:"country,omitempty"`
	CreatedAt time.Time     `bson:"createdAt,omitempty"  json:"createdAt,omitempty"`
	UpdatedAt time.Time     `bson:"updatedAt,omitempty"  json:"updatedAt,omitempty"`
}

// incomingContact type is used for handling PATCH requests.
// To understand why we are using pointer types for fields,
// refer to http://blog.golang.org/json-and-go .
// Using pointer types, we can differentiate intentional nil value fields
// and empty fields.
// This type is used *only* for decoding json obtained from a PATCH request.
type incomingContact struct {
	Id      *bson.ObjectId `json:"id"`
	Company *string        `json:"company,omitempty"`
	Person  *string        `json:"person,omitempty"`
	Slug    *string        `json:"slug,omitempty"`
	Email   *string        `json:"email,omitempty"`
	Phone   *string        `json:"phone,omitempty"`
	SkypeId *string        `json:"skypeId,omitempty"`
	Country *string        `json:"country,omitempty"`
}

// copyIncomingFields copies the fields from an incomingContact into a
// contact object.
// Using multiple if statements provides more granularity, while allowing
// update of only some specific fields.
// TODO(Hari): Use reflect package instead of multiple if statements
func (c *contact) copyIncomingFields(i *incomingContact) error {
	if i.Id != nil {
		if *i.Id != c.Id {
			return errors.New("Id doesn't match")
		}
	}
	if i.Slug != nil {
		if *i.Slug != c.Slug {
			return errors.New("Slug can't be updated")
		}
	}
	if i.Company != nil {
		c.Company = *i.Company
	}
	if i.Person != nil {
		c.Person = *i.Person
	}
	if i.Slug != nil {
		c.Slug = *i.Slug
	}
	if i.Email != nil {
		c.Email = *i.Email
	}
	if i.Phone != nil {
		c.Phone = *i.Phone
	}
	if i.SkypeId != nil {
		c.SkypeId = *i.SkypeId
	}
	if i.Country != nil {
		c.Country = *i.Country
	}
	return nil
}

func validateContact(c *contact) error {
	if c.Person == "" {
		err := errors.New("person can't be empty")
		return err
	}
	if c.Email == "" {
		err := errors.New("email can't be empty")
		return err
	}
	if _, err := mail.ParseAddress(c.Email); err != nil {
		return err
	}

	return nil
}

// NewContact takes the fields of a contact, initializes a struct of contact type and returns
// the pointer to that struct.
func NewContact(company, person, email, phone, skypeid, country string) (*contact, error) {
	doc := contact{
		Id:      bson.NewObjectId(),
		Company: company,
		Person:  person,
		Email:   email,
		Phone:   phone,
		SkypeId: skypeid,
		Country: country,
	}
	if err := validateContact(&doc); err != nil {
		return &contact{}, err
	}
	slugifyContact(&doc)

	doc.CreatedAt = doc.Id.Time()
	doc.UpdatedAt = doc.CreatedAt

	err := config.ContactsCollection.Insert(doc)
	if err != nil {
		return &contact{}, err
	}
	return &doc, nil
}

// GetContactById takes the contact Id as an argument and returns a pointer to the contact object.
func GetContactById(i bson.ObjectId) (*contact, error) {
	var c contact
	err := config.ContactsCollection.FindId(i).One(&c)
	if err != nil {
		return &contact{}, err
	}
	return &c, nil
}

// GetContactBySlug takes the contact slug as an argument and returns a pointer to the contact object.
func GetContactBySlug(slug string) (*contact, error) {
	var c []contact
	err := config.ContactsCollection.Find(bson.M{"slug": slug}).All(&c)
	if err != nil {
		return &contact{}, err
	}

	if len(c) == 0 {
		return &contact{}, errors.New("no contact")
	}

	if len(c) > 1 {
		return &contact{}, errors.New("more than 1 contact found")
	}
	return &c[0], nil
}

// GetAllContacts fetches all the contacts from the database.
func GetAllContacts() ([]contact, error) {
	var c []contact
	err := config.ContactsCollection.Find(nil).All(&c)
	if err != nil {
		return []contact{}, err
	}
	return c, nil
}

// Update updates the contact in the database.
// First, fetch a contact from the database and change the necessary fields.
// Then call the Update method on that contact object.
func (c *contact) Update() error {
	if err := validateContact(c); err != nil {
		return err
	}
	c.UpdatedAt = bson.Now()
	err := config.ContactsCollection.UpdateId(c.Id, c)
	return err
}

// Delete deletes the contact from the database.
func (c *contact) Delete() error {
	return config.ContactsCollection.RemoveId(c.Id)
}

// slugifyContact takes a pointer to a contact as the argument and
// creates a slug and populates the slug field of the contact.
func slugifyContact(c *contact) {
	base := slug.SlugAscii(c.Person)
	temp := base
	rand.Seed(time.Now().UnixNano()) // takes the current time in nanoseconds as the seed
	i := rand.Intn(10000)
	for {
		if contactSlugExists(temp) {
			temp = base + "-" + strconv.Itoa(i)
			i = rand.Intn(10000)
		} else {
			c.Slug = temp
			return
		}

	}
}

// contactSlugExists returns true if a slug for a contact already exists in the database
// else returns false.
func contactSlugExists(slug string) bool {
	var c []contact
	err := config.ContactsCollection.Find(bson.M{"slug": slug}).All(&c)
	if err != nil {
		log.Println(err)
		return true // returns true just to be safe.
	}
	if len(c) == 0 {
		return false
	}
	return true
}
<|endoftext|>


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

package martian

import (
	"bufio"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"net"
	"net/http"
	"sync"
)

// Context provides information and storage for a single request/response pair.
// Contexts are linked to shared session that is used for multiple requests on
// a single connection.
type Context struct {
	session *Session
	id      string

	mu            sync.RWMutex
	vals          map[string]interface{}
	skipRoundTrip bool
	skipLogging   bool
	apiRequest    bool
}

// Session provides information and storage about a connection.
type Session struct {
	mu       sync.RWMutex
	id       string
	secure   bool
	hijacked bool
	conn     net.Conn
	brw      *bufio.ReadWriter
	vals     map[string]interface{}
}

var (
	ctxmu sync.RWMutex
	ctxs  = make(map[*http.Request]*Context)
)

// NewContext returns a context for the in-flight HTTP request.
func NewContext(req *http.Request) *Context {
	ctxmu.RLock()
	defer ctxmu.RUnlock()

	return ctxs[req]
}

// TestContext builds a new session and associated context and returns the
// context and a function to remove the associated context. If it fails to
// generate either a new session or a new context it will return an error.
// Intended for tests only.
func TestContext(req *http.Request, conn net.Conn, bw *bufio.ReadWriter) (ctx *Context, remove func(), err error) {
	ctxmu.Lock()
	defer ctxmu.Unlock()

	ctx, ok := ctxs[req]
	if ok {
		return ctx, func() { unlink(req) }, nil
	}

	s, err := newSession(conn, bw)
	if err != nil {
		return nil, nil, err
	}

	ctx, err = withSession(s)
	if err != nil {
		return nil, nil, err
	}

	ctxs[req] = ctx

	return ctx, func() { unlink(req) }, nil
}

// ID returns the session ID.
func (s *Session) ID() string {
	s.mu.RLock()
	defer s.mu.RUnlock()

	return s.id
}

// IsSecure returns whether the current session is from a secure connection,
// such as when receiving requests from a TLS connection that has been MITM'd.
func (s *Session) IsSecure() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()

	return s.secure
}

// MarkSecure marks the session as secure.
func (s *Session) MarkSecure() {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.secure = true
}

// MarkInsecure marks the session as insecure.
func (s *Session) MarkInsecure() {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.secure = false
}

// Hijack takes control of the connection from the proxy. No further action
// will be taken by the proxy and the connection will be closed following the
// return of the hijacker.
func (s *Session) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.hijacked {
		return nil, nil, fmt.Errorf("martian: session has already been hijacked")
	}
	s.hijacked = true

	return s.conn, s.brw, nil
}

// Hijacked returns whether the connection has been hijacked.
func (s *Session) Hijacked() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()

	return s.hijacked
}

// setConn resets the underlying connection and bufio.ReadWriter of the
// session. Used by the proxy when the connection is upgraded to TLS.
func (s *Session) setConn(conn net.Conn, brw *bufio.ReadWriter) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.conn = conn
	s.brw = brw
}

// Get takes key and returns the associated value from the session.
func (s *Session) Get(key string) (interface{}, bool) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	val, ok := s.vals[key]

	return val, ok
}

// Set takes a key and associates it with val in the session. The value is
// persisted for the entire session across multiple requests and responses.
func (s *Session) Set(key string, val interface{}) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.vals[key] = val
}

// Session returns the session for the context.
func (ctx *Context) Session() *Session {
	return ctx.session
}

// ID returns the context ID.
func (ctx *Context) ID() string {
	return ctx.id
}

// Get takes key and returns the associated value from the context.
func (ctx *Context) Get(key string) (interface{}, bool) {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	val, ok := ctx.vals[key]

	return val, ok
}

// Set takes a key and associates it with val in the context. The value is
// persisted for the duration of the request and is removed on the following
// request.
func (ctx *Context) Set(key string, val interface{}) {
	ctx.mu.Lock()
	defer ctx.mu.Unlock()

	ctx.vals[key] = val
}

// SkipRoundTrip skips the round trip for the current request.
func (ctx *Context) SkipRoundTrip() {
	ctx.mu.Lock()
	defer ctx.mu.Unlock()

	ctx.skipRoundTrip = true
}

// SkippingRoundTrip returns whether the current round trip will be skipped.
func (ctx *Context) SkippingRoundTrip() bool {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	return ctx.skipRoundTrip
}

// SkipLogging skips logging by Martian loggers for the current request.
func (ctx *Context) SkipLogging() {
	ctx.mu.Lock()
	defer ctx.mu.Unlock()

	ctx.skipLogging = true
}

// SkippingLogging returns whether the current request / response pair will be logged.
func (ctx *Context) SkippingLogging() bool {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	return ctx.skipLogging
}

// APIRequest marks the requests as a request to the proxy API.
func (ctx *Context) APIRequest() {
	ctx.mu.Lock()
	defer ctx.mu.Unlock()

	ctx.apiRequest = true
}

// IsAPIRequest returns true when the request patterns matches a pattern in the proxy
// mux. The mux is usually defined as a parameter to the api.Forwarder, which uses
// http.DefaultServeMux by default.
func (ctx *Context) IsAPIRequest() bool {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	return ctx.apiRequest
}

// newID creates a new 16 character random hex ID; note these are not UUIDs.
func newID() (string, error) {
	src := make([]byte, 8)
	if _, err := rand.Read(src); err != nil {
		return "", err
	}

	return hex.EncodeToString(src), nil
}

// link associates the context with request.
func link(req *http.Request, ctx *Context) {
	ctxmu.Lock()
	defer ctxmu.Unlock()

	ctxs[req] = ctx
}

// unlink removes the context for request.
func unlink(req *http.Request) {
	ctxmu.Lock()
	defer ctxmu.Unlock()

	delete(ctxs, req)
}

// newSession builds a new session.
func newSession(conn net.Conn, brw *bufio.ReadWriter) (*Session, error) {
	sid, err := newID()
	if err != nil {
		return nil, err
	}

	return &Session{
		id:   sid,
		conn: conn,
		brw:  brw,
		vals: make(map[string]interface{}),
	}, nil
}

// withSession builds a new context from an existing session. Session must be
// non-nil.
func withSession(s *Session) (*Context, error) {
	cid, err := newID()
	if err != nil {
		return nil, err
	}

	return &Context{
		session: s,
		id:      cid,
		vals:    make(map[string]interface{}),
	}, nil
}
<commit_msg>Revert "Updating a request/response Context should lock mutex for read/writing. (#180)" (#181)<commit_after>// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package martian

import (
	"bufio"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"net"
	"net/http"
	"sync"
)

// Context provides information and storage for a single request/response pair.
// Contexts are linked to shared session that is used for multiple requests on
// a single connection.
type Context struct {
	session *Session
	id      string

	mu            sync.RWMutex
	vals          map[string]interface{}
	skipRoundTrip bool
	skipLogging   bool
	apiRequest    bool
}

// Session provides information and storage about a connection.
type Session struct {
	mu       sync.RWMutex
	id       string
	secure   bool
	hijacked bool
	conn     net.Conn
	brw      *bufio.ReadWriter
	vals     map[string]interface{}
}

var (
	ctxmu sync.RWMutex
	ctxs  = make(map[*http.Request]*Context)
)

// NewContext returns a context for the in-flight HTTP request.
func NewContext(req *http.Request) *Context {
	ctxmu.RLock()
	defer ctxmu.RUnlock()

	return ctxs[req]
}

// TestContext builds a new session and associated context and returns the
// context and a function to remove the associated context. If it fails to
// generate either a new session or a new context it will return an error.
// Intended for tests only.
func TestContext(req *http.Request, conn net.Conn, bw *bufio.ReadWriter) (ctx *Context, remove func(), err error) {
	ctxmu.Lock()
	defer ctxmu.Unlock()

	ctx, ok := ctxs[req]
	if ok {
		return ctx, func() { unlink(req) }, nil
	}

	s, err := newSession(conn, bw)
	if err != nil {
		return nil, nil, err
	}

	ctx, err = withSession(s)
	if err != nil {
		return nil, nil, err
	}

	ctxs[req] = ctx

	return ctx, func() { unlink(req) }, nil
}

// ID returns the session ID.
func (s *Session) ID() string {
	s.mu.RLock()
	defer s.mu.RUnlock()

	return s.id
}

// IsSecure returns whether the current session is from a secure connection,
// such as when receiving requests from a TLS connection that has been MITM'd.
func (s *Session) IsSecure() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()

	return s.secure
}

// MarkSecure marks the session as secure.
func (s *Session) MarkSecure() {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.secure = true
}

// MarkInsecure marks the session as insecure.
func (s *Session) MarkInsecure() {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.secure = false
}

// Hijack takes control of the connection from the proxy. No further action
// will be taken by the proxy and the connection will be closed following the
// return of the hijacker.
func (s *Session) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.hijacked {
		return nil, nil, fmt.Errorf("martian: session has already been hijacked")
	}
	s.hijacked = true

	return s.conn, s.brw, nil
}

// Hijacked returns whether the connection has been hijacked.
func (s *Session) Hijacked() bool {
	s.mu.Lock()
	defer s.mu.Unlock()

	return s.hijacked
}

// setConn resets the underlying connection and bufio.ReadWriter of the
// session. Used by the proxy when the connection is upgraded to TLS.
func (s *Session) setConn(conn net.Conn, brw *bufio.ReadWriter) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.conn = conn
	s.brw = brw
}

// Get takes key and returns the associated value from the session.
func (s *Session) Get(key string) (interface{}, bool) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	val, ok := s.vals[key]

	return val, ok
}

// Set takes a key and associates it with val in the session. The value is
// persisted for the entire session across multiple requests and responses.
func (s *Session) Set(key string, val interface{}) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.vals[key] = val
}

// Session returns the session for the context.
func (ctx *Context) Session() *Session {
	return ctx.session
}

// ID returns the context ID.
func (ctx *Context) ID() string {
	return ctx.id
}

// Get takes key and returns the associated value from the context.
func (ctx *Context) Get(key string) (interface{}, bool) {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	val, ok := ctx.vals[key]

	return val, ok
}

// Set takes a key and associates it with val in the context. The value is
// persisted for the duration of the request and is removed on the following
// request.
func (ctx *Context) Set(key string, val interface{}) {
	ctx.mu.Lock()
	defer ctx.mu.Unlock()

	ctx.vals[key] = val
}

// SkipRoundTrip skips the round trip for the current request.
func (ctx *Context) SkipRoundTrip() {
	ctx.mu.Lock()
	defer ctx.mu.Unlock()

	ctx.skipRoundTrip = true
}

// SkippingRoundTrip returns whether the current round trip will be skipped.
func (ctx *Context) SkippingRoundTrip() bool {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	return ctx.skipRoundTrip
}

// SkipLogging skips logging by Martian loggers for the current request.
func (ctx *Context) SkipLogging() {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	ctx.skipLogging = true
}

// SkippingLogging returns whether the current request / response pair will be logged.
func (ctx *Context) SkippingLogging() bool {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	return ctx.skipLogging
}

// APIRequest marks the requests as a request to the proxy API.
func (ctx *Context) APIRequest() {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	ctx.apiRequest = true
}

// IsAPIRequest returns true when the request patterns matches a pattern in the proxy
// mux. The mux is usually defined as a parameter to the api.Forwarder, which uses
// http.DefaultServeMux by default.
func (ctx *Context) IsAPIRequest() bool {
	ctx.mu.RLock()
	defer ctx.mu.RUnlock()

	return ctx.apiRequest
}

// newID creates a new 16 character random hex ID; note these are not UUIDs.
func newID() (string, error) {
	src := make([]byte, 8)
	if _, err := rand.Read(src); err != nil {
		return "", err
	}

	return hex.EncodeToString(src), nil
}

// link associates the context with request.
func link(req *http.Request, ctx *Context) {
	ctxmu.Lock()
	defer ctxmu.Unlock()

	ctxs[req] = ctx
}

// unlink removes the context for request.
func unlink(req *http.Request) {
	ctxmu.Lock()
	defer ctxmu.Unlock()

	delete(ctxs, req)
}

// newSession builds a new session.
func newSession(conn net.Conn, brw *bufio.ReadWriter) (*Session, error) {
	sid, err := newID()
	if err != nil {
		return nil, err
	}

	return &Session{
		id:   sid,
		conn: conn,
		brw:  brw,
		vals: make(map[string]interface{}),
	}, nil
}

// withSession builds a new context from an existing session. Session must be
// non-nil.
func withSession(s *Session) (*Context, error) {
	cid, err := newID()
	if err != nil {
		return nil, err
	}

	return &Context{
		session: s,
		id:      cid,
		vals:    make(map[string]interface{}),
	}, nil
}
<|endoftext|>


<commit_before>package appkit

import (
	"errors"
	"fmt"
	"strconv"
)

type Context struct {
	Data map[string]interface{}
}

func NewContext() Context {
	c := Context{}
	c.Data = make(map[string]interface{})

	return c
}

func (c Context) Has(key string) bool {
	_, ok := c.Data[key]
	return ok
}

func (c Context) Get(key string) (interface{}, bool) {
	x, ok := c.Data[key]
	return x, ok
}

func (c Context) MustGet(key string) interface{} {
	x, ok := c.Data[key]
	if !ok {
		panic("Context does not have key " + key)
	}

	return x
}

func (c *Context) Set(key string, data interface{}) {
	c.Data[key] = data
}

func (c Context) String(key string) string {
	x, ok := c.Data[key]
	if !ok {
		return ""
	}

	str, ok := x.(string)
	if !ok {
		return ""
	}
	return str
}

func (c Context) MustString(key string) string {
	val := c.MustGet(key)
	str, ok := val.(string)
	if !ok {
		panic(fmt.Sprintf("Context key %v is not a string", key))
	}

	return str
}

func (c *Context) SetString(key, val string) {
	c.Data[key] = val
}

func (c Context) Bool(key string) (bool, error) {
	val, ok := c.Get(key)
	if !ok {
		return false, errors.New("inexistant_key")
	}

	if boolVal, ok := val.(bool); ok {
		return boolVal, nil
	} else {
		return false, errors.New("type_mismatch")
	}
}

func (c Context) Map(key string) (map[string]interface{}, error) {
	val, ok := c.Get(key)
	if !ok {
		return nil, errors.New("inexistant_key")
	}

	if mapVal, ok := val.(map[string]interface{}); ok {
		return mapVal, nil
	} else {
		return nil, errors.New("type_mismatch")
	}
}

// Retrieve an int value from the context.
// Will auto-convert string values.
func (c *Context) Int(key string) (int, error) {
	val, ok := c.Get(key)
	if !ok {
		return 0, errors.New("inexistant_key")
	}

	if intVal, ok := val.(int); ok {
		return intVal, nil
	}

	if strVal, ok := val.(string); ok {
		intVal, err := strconv.ParseInt(strVal, 10, 64)
		if err != nil {
			return 0, err
		}

		return int(intVal), nil
	}

	return 0, errors.New("cant_convert_to_int")
}
<commit_msg>NewContext() now accepts optional data<commit_after>package appkit

import (
	"errors"
	"fmt"
	"strconv"
)

type Context struct {
	Data map[string]interface{}
}

func NewContext(data ...map[string]interface{}) *Context {
	c := &Context{}

	if len(data) > 0 && data[0] != nil {
		c.Data = data[0]
	} else {
		c.Data = make(map[string]interface{})
	}

	return c
}

func (c Context) Has(key string) bool {
	_, ok := c.Data[key]
	return ok
}

func (c Context) Get(key string) (interface{}, bool) {
	x, ok := c.Data[key]
	return x, ok
}

func (c Context) MustGet(key string) interface{} {
	x, ok := c.Data[key]
	if !ok {
		panic("Context does not have key " + key)
	}

	return x
}

func (c *Context) Set(key string, data interface{}) {
	c.Data[key] = data
}

func (c Context) String(key string) string {
	x, ok := c.Data[key]
	if !ok {
		return ""
	}

	str, ok := x.(string)
	if !ok {
		return ""
	}
	return str
}

func (c Context) MustString(key string) string {
	val := c.MustGet(key)
	str, ok := val.(string)
	if !ok {
		panic(fmt.Sprintf("Context key %v is not a string", key))
	}

	return str
}

func (c *Context) SetString(key, val string) {
	c.Data[key] = val
}

func (c Context) Bool(key string) (bool, error) {
	val, ok := c.Get(key)
	if !ok {
		return false, errors.New("inexistant_key")
	}

	if boolVal, ok := val.(bool); ok {
		return boolVal, nil
	} else {
		return false, errors.New("type_mismatch")
	}
}

func (c Context) Map(key string) (map[string]interface{}, error) {
	val, ok := c.Get(key)
	if !ok {
		return nil, errors.New("inexistant_key")
	}

	if mapVal, ok := val.(map[string]interface{}); ok {
		return mapVal, nil
	} else {
		return nil, errors.New("type_mismatch")
	}
}

// Retrieve an int value from the context.
// Will auto-convert string values.
func (c *Context) Int(key string) (int, error) {
	val, ok := c.Get(key)
	if !ok {
		return 0, errors.New("inexistant_key")
	}

	if intVal, ok := val.(int); ok {
		return intVal, nil
	}

	if strVal, ok := val.(string); ok {
		intVal, err := strconv.ParseInt(strVal, 10, 64)
		if err != nil {
			return 0, err
		}

		return int(intVal), nil
	}

	return 0, errors.New("cant_convert_to_int")
}
<|endoftext|>


<commit_before>package goblet

import (
	"bufio"
	"fmt"
	"github.com/extrame/goblet/render"
	"html/template"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
)

var USERCOOKIENAME = "user"

type Context struct {
	Server  *Server
	request *http.Request
	writer  http.ResponseWriter
	option  BlockOption
	//默认请求类型：HTML
	suffix         string
	format         string
	forceFormat    string
	tempRenders    []string
	method         string
	renderInstance render.RenderInstance
	response       interface{}
	responseMap    map[string]interface{}
	layout         string
	status_code    int
	already_writed bool
	bower_stack    map[string]bool
}

func (c *Context) handleData() {

}

func (c *Context) Writer() http.ResponseWriter {
	c.already_writed = true
	//TODO
	// c.writer.WriteHeader(c.status_code)
	return c.writer
}

func (c *Context) Callback() string {
	return c.request.FormValue("callback")
}

func (c *Context) Suffix() string {
	return c.suffix
}

func (c *Context) SetHeader(key, value string) {
	c.writer.Header().Add(key, value)
}

func (c *Context) IntFormValue(key string) int64 {
	str := c.request.FormValue(key)
	val, _ := strconv.ParseInt(str, 10, 64)
	return val
}

func (c *Context) StrFormValue(key string) string {
	return c.request.FormValue(key)
}

func (c *Context) render() (err error) {
	if !c.already_writed {
		if c.renderInstance != nil {
			funcMap := make(template.FuncMap)
			funcMap["bower"] = func(name string, version ...string) (template.HTML, error) {
				if c.bower_stack == nil {
					c.bower_stack = make(map[string]bool)
				}
				maps, err := c.Server.Bower(name, version...)
				res := ""
				for _, strs := range maps {
					if _, loaded := c.bower_stack[strs[0]]; !loaded {
						res += strs[1]
						c.bower_stack[strs[0]] = true
					}
				}
				return template.HTML(res), err
			}
			for i := 0; i < len(c.Server.funcs); i++ {
				var fn = c.Server.funcs[i].Fn
				funcMap[c.Server.funcs[i].Name] = func() interface{} {
					return fn(c)
				}
			}
			return c.renderInstance.Render(c.writer, c.response, c.status_code, funcMap)
		} else {
			c.writer.WriteHeader(500)
			c.writer.Write([]byte("Internal Error: No Render Allowed, please contact the admin"))
		}
	}
	return nil
}

//Respond with multi data, data will tread as a key-value map
//for example:
//AddRespond("key1","value1",key2","value2")
//You can use AddRespond multi time in controller
func (c *Context) AddRespond(datas ...interface{}) {
	if len(datas) > 1 {
		if c.responseMap == nil {
			c.responseMap = make(map[string]interface{})
		}
		for i := 0; i < len(datas)/2; i++ {
			k := fmt.Sprintf("%s", datas[i])
			v := autoHide(datas[i+1])
			c.responseMap[k] = v
		}
	}
}

func (c *Context) RespondReader(reader io.Reader) {
	bufio.NewWriter(c.writer).ReadFrom(reader)
	c.already_writed = true
}

func (c *Context) Respond(data interface{}) {
	switch td := data.(type) {
	case error:
		c.RespondWithStatus(data, http.StatusInternalServerError)
	case []byte:
		c.format = "raw"
		c.Writer().Write(td)
	case io.Reader:
		c.format = "raw"
		io.Copy(c.Writer(), td)
	default:
		c.RespondWithStatus(data, http.StatusOK)
	}
}

func (c *Context) RespondOK() {
	c.status_code = http.StatusOK
}

func (c *Context) RespondError(err error) {
	c.RespondWithStatus(err.Error(), http.StatusBadRequest)
}

//Reset the context renders
func (c *Context) UseRender(render string) {
	c.forceFormat = render
}

//Allow some temporary render
func (c *Context) AllowRender(renders ...string) {
	c.tempRenders = renders
}

func (c *Context) RespondStatus(status int) {
	c.status_code = status
}

func (c *Context) RespondWithStatus(data interface{}, status int) {
	c.response = autoHide(data)
	c.status_code = status
}

func (c *Context) RespondWithRender(data interface{}, render string) {
	c.response = autoHide(data)
	c.method = render
}

func (c *Context) prepareRender() (err error) {
	if !c.already_writed {
		//test required format in allow list or not
		var format string
		if format, err = c.option.GetRender(c); err == nil {
			re := c.Server.Renders[format]
			if re != nil {
				c.renderInstance, err = re.PrepareInstance(c)
			}
		}
	}
	return
}

func (c *Context) checkResponse() {
	if c.responseMap != nil {
		c.response = c.responseMap
	}
}

func (c *Context) SetLayout(l string) {
	c.layout = l
}

func (c *Context) Layout() string {
	if c.layout != "" {
		return c.layout
	} else {
		return c.option.Layout()
	}
}

func (c *Context) ResetDB() error {
	return c.Server.connectDB()
}

func (c *Context) RenderAs(name string) {
	c.method = name
}

func (c *Context) RestRedirectToRead(id interface{}) {
	switch rid := id.(type) {
	case string:
		c.option.(*RestBlockOption).renderAsRead(rid, c)
	case int64:
		c.option.(*RestBlockOption).renderAsRead(strconv.FormatInt(rid, 10), c)
	}
}

func (c *Context) RedirectTo(url string) {
	c.writer.Header().Set("Location", url)
	c.writer.WriteHeader(302)
	c.format = "raw"
}

///////////for renders/////////////
func (c *Context) BlockOptionType() string {
	switch c.option.(type) {
	case *RestBlockOption:
		return "Rest"
	case *GroupBlockOption:
		return "Group"
	case *_staticBlockOption:
		return "Static"
	case *HtmlBlockOption:
		return "Html"
	}
	log.Panic("err of block option type!!!")
	return ""
}

func (c *Context) Method() string {
	return c.method
}

func (c *Context) Format() string {
	return c.format
}

func (c *Context) StatusCode() int {
	return c.status_code
}

func (c *Context) TemplatePath() string {
	return c.option.TemplatePath()
}

type RemoteAddr struct {
	str string
}

func (r *RemoteAddr) String() string {
	return r.str
}

func (r *RemoteAddr) Network() string {
	return "tcp"
}

func (c *Context) RemoteAddr() net.Addr {
	addr := new(RemoteAddr)
	addr.str = c.request.RemoteAddr
	return addr
}

func (c *Context) FormValue(key string) string {
	return c.request.FormValue(key)
}

func (c *Context) QueryString(key string) string {
	return c.request.URL.Query().Get(key)
}

func (c *Context) Referer() string {
	return c.request.Referer()
}

func (c *Context) PathToURL(path string) (*url.URL, error) {
	if abs, err := filepath.Abs(path); err == nil {
		if rel, err := filepath.Rel(c.Server.WwwRoot(), abs); err == nil {
			return url.Parse(rel)
		} else {
			return nil, err
		}
	} else {
		return nil, err
	}
}
<commit_msg>fix public<commit_after>package goblet

import (
	"bufio"
	"fmt"
	"github.com/extrame/goblet/render"
	"html/template"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
)

var USERCOOKIENAME = "user"

type Context struct {
	Server  *Server
	request *http.Request
	writer  http.ResponseWriter
	option  BlockOption
	//默认请求类型：HTML
	suffix         string
	format         string
	forceFormat    string
	tempRenders    []string
	method         string
	renderInstance render.RenderInstance
	response       interface{}
	responseMap    map[string]interface{}
	layout         string
	status_code    int
	already_writed bool
	bower_stack    map[string]bool
}

func (c *Context) handleData() {

}

func (c *Context) Writer() http.ResponseWriter {
	c.already_writed = true
	//TODO
	// c.writer.WriteHeader(c.status_code)
	return c.writer
}

func (c *Context) Callback() string {
	return c.request.FormValue("callback")
}

func (c *Context) Suffix() string {
	return c.suffix
}

func (c *Context) SetHeader(key, value string) {
	c.writer.Header().Add(key, value)
}

func (c *Context) IntFormValue(key string) int64 {
	str := c.request.FormValue(key)
	val, _ := strconv.ParseInt(str, 10, 64)
	return val
}

func (c *Context) StrFormValue(key string) string {
	return c.request.FormValue(key)
}

func (c *Context) render() (err error) {
	if !c.already_writed {
		if c.renderInstance != nil {
			funcMap := make(template.FuncMap)
			funcMap["bower"] = func(name string, version ...string) (template.HTML, error) {
				if c.bower_stack == nil {
					c.bower_stack = make(map[string]bool)
				}
				maps, err := c.Server.Bower(name, version...)
				res := ""
				for _, strs := range maps {
					if _, loaded := c.bower_stack[strs[0]]; !loaded {
						res += strs[1]
						c.bower_stack[strs[0]] = true
					}
				}
				return template.HTML(res), err
			}
			for i := 0; i < len(c.Server.funcs); i++ {
				var fn = c.Server.funcs[i].Fn
				funcMap[c.Server.funcs[i].Name] = func() interface{} {
					return fn(c)
				}
			}
			return c.renderInstance.Render(c.writer, c.response, c.status_code, funcMap)
		} else {
			c.writer.WriteHeader(500)
			c.writer.Write([]byte("Internal Error: No Render Allowed, please contact the admin"))
		}
	}
	return nil
}

//Respond with multi data, data will tread as a key-value map
//for example:
//AddRespond("key1","value1",key2","value2")
//You can use AddRespond multi time in controller
func (c *Context) AddRespond(datas ...interface{}) {
	if len(datas) > 1 {
		if c.responseMap == nil {
			c.responseMap = make(map[string]interface{})
		}
		for i := 0; i < len(datas)/2; i++ {
			k := fmt.Sprintf("%s", datas[i])
			v := autoHide(datas[i+1])
			c.responseMap[k] = v
		}
	}
}

func (c *Context) RespondReader(reader io.Reader) {
	bufio.NewWriter(c.writer).ReadFrom(reader)
	c.already_writed = true
}

func (c *Context) Respond(data interface{}) {
	switch td := data.(type) {
	case error:
		c.RespondWithStatus(data, http.StatusInternalServerError)
	case []byte:
		c.format = "raw"
		c.Writer().Write(td)
	case io.Reader:
		c.format = "raw"
		io.Copy(c.Writer(), td)
	default:
		c.RespondWithStatus(data, http.StatusOK)
	}
}

func (c *Context) RespondOK() {
	c.status_code = http.StatusOK
}

func (c *Context) RespondError(err error) {
	c.RespondWithStatus(err.Error(), http.StatusBadRequest)
}

//Reset the context renders
func (c *Context) UseRender(render string) {
	c.forceFormat = render
}

//Allow some temporary render
func (c *Context) AllowRender(renders ...string) {
	c.tempRenders = renders
}

func (c *Context) RespondStatus(status int) {
	c.status_code = status
}

func (c *Context) RespondWithStatus(data interface{}, status int) {
	c.response = autoHide(data)
	c.status_code = status
}

func (c *Context) RespondWithRender(data interface{}, render string) {
	c.response = autoHide(data)
	c.method = render
}

func (c *Context) prepareRender() (err error) {
	if !c.already_writed {
		//test required format in allow list or not
		var format string
		if format, err = c.option.GetRender(c); err == nil {
			re := c.Server.Renders[format]
			if re != nil {
				c.renderInstance, err = re.PrepareInstance(c)
			}
		}
	}
	return
}

func (c *Context) checkResponse() {
	if c.responseMap != nil {
		c.response = c.responseMap
	}
}

func (c *Context) SetLayout(l string) {
	c.layout = l
}

func (c *Context) Layout() string {
	if c.layout != "" {
		return c.layout
	} else {
		return c.option.Layout()
	}
}

func (c *Context) ResetDB() error {
	return c.Server.connectDB()
}

func (c *Context) RenderAs(name string) {
	c.method = name
}

func (c *Context) RestRedirectToRead(id interface{}) {
	switch rid := id.(type) {
	case string:
		c.option.(*RestBlockOption).renderAsRead(rid, c)
	case int64:
		c.option.(*RestBlockOption).renderAsRead(strconv.FormatInt(rid, 10), c)
	}
}

func (c *Context) RedirectTo(url string) {
	c.writer.Header().Set("Location", url)
	c.writer.WriteHeader(302)
	c.format = "raw"
}

///////////for renders/////////////
func (c *Context) BlockOptionType() string {
	switch c.option.(type) {
	case *RestBlockOption:
		return "Rest"
	case *GroupBlockOption:
		return "Group"
	case *_staticBlockOption:
		return "Static"
	case *HtmlBlockOption:
		return "Html"
	}
	log.Panic("err of block option type!!!")
	return ""
}

func (c *Context) Method() string {
	return c.method
}

func (c *Context) Format() string {
	return c.format
}

func (c *Context) StatusCode() int {
	return c.status_code
}

func (c *Context) TemplatePath() string {
	return c.option.TemplatePath()
}

type RemoteAddr struct {
	str string
}

func (r *RemoteAddr) String() string {
	return r.str
}

func (r *RemoteAddr) Network() string {
	return "tcp"
}

func (c *Context) RemoteAddr() net.Addr {
	addr := new(RemoteAddr)
	addr.str = c.request.RemoteAddr
	return addr
}

func (c *Context) FormValue(key string) string {
	return c.request.FormValue(key)
}

func (c *Context) QueryString(key string) string {
	return c.request.URL.Query().Get(key)
}

func (c *Context) Referer() string {
	return c.request.Referer()
}

func (c *Context) PathToURL(path string) (*url.URL, error) {
	if abs, err := filepath.Abs(path); err == nil {
		if rel, err := filepath.Rel(c.Server.WwwRoot(), abs); err == nil {
			rel = strings.TrimPrefix(rel, "public/")
			return url.Parse(rel)
		} else {
			return nil, err
		}
	} else {
		return nil, err
	}
}
<|endoftext|>


<commit_before>package rest

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

// CompressData разрешает поддержку сжатия данных, если это поддерживается браузером.
// Если сжатие данных отдельно обрабатывается специальными средствами в другом месте, то чтобы
// избежать двойного сжатия, ее лучше отключить, установив значение данного флага в false.
var CompressData = true

// Context содержит контекстную информацию HTTP-запроса и методы удобного формирования ответа
// на них.
type Context struct {
	// параметры запроса
	*http.Request            // HTTP запрос в разобранном виде
	Params        Params     // именованные параметры из пути запроса
	urlQuery      url.Values // параметры запроса
	// параметры ответа на запрос
	Response    http.ResponseWriter // интерфейс для публикации ответа на запрос
	ContentType string              // тип информации в ответе
	status      int                 // код HTTP-ответа
}

// NewContext возвращает новый инициализированный контекст. В отличии от просто создания нового
// контекста, вызов данного метода использует пул контекстов.
func NewContext(w http.ResponseWriter, r *http.Request, params Params) *Context {
	context := contexts.Get().(*Context)

	context.Request = r
	context.Params = params
	context.urlQuery = nil

	context.Response = w
	context.ContentType = ""
	context.status = 0

	return context
}

// Free возвращает контекст в пул используемых контекстов для дальнейшего использования.
// Вызывается автоматически после того, как контекст перестает использоваться.
func (c *Context) Free() {
	contexts.Put(c)
}

// Get возвращает значение именованного параметра. Если параметр с таким именем не найден,
// то возвращается значение параметра из URL с тем же именем. Разбор параметров запроса сохраняется
// внутри Context и повторного его разбора уже не требует. Но это происходит только при первом
// к ним обращении.
func (c *Context) Get(key string) string {
	for _, param := range c.Params {
		if param.Key == key {
			return param.Value
		}
	}
	if c.urlQuery == nil {
		c.urlQuery = c.Request.URL.Query()
	}
	return c.urlQuery.Get(key)
}

// Set позволяет добавить новый параметр с заданным именем и значением. Добавление нового параметра
// с таким же именем не изменяет и не удаляет предыдущего значения, а именно добавляет его в список.
func (c *Context) Set(key, value string) {
	c.Params = append(c.Params, Param{key, value})
}

// Code код HTTP-ответа, который будет отправлен сервером. Данный метод возвращает ссылку
// на основной контекст, чтобы можно было использовать его в последовательности выполнения
// команд. Например, можно сразу установить код ответа и тут же опубликовать данные.
func (c *Context) Code(code int) *Context {
	if code >= 200 && code < 600 {
		c.status = code
	}
	return c
}

// SetHeader устанавливает новое значение для указанного HTTP-заголовка. Если передаваемое
// значение заголовка пустое, то данный заголовок будет удален.
func (c *Context) SetHeader(key, value string) {
	if value == "" {
		c.Response.Header().Del(key)
	} else {
		c.Response.Header().Set(key, value)
	}
}

// SetLocation устанавливает в заголовке HTTP-ответа значение для Lacation.
func (c *Context) SetLocation(value string) {
	c.SetHeader("Location", value)
}

// ParseBody декодирует содержимое запроса в объект. После чтения из запроса
// http.Request.Body автоматически закрывается и дополнительного закрытия не требуется.
//
// На данный момент поддерживается только разбор объектов в формате JSON.
func (c *Context) ParseBody(obj interface{}) error {
	defer c.Request.Body.Close()
	return json.NewDecoder(c.Request.Body).Decode(obj)
}

// Body публикует данные, переданные в параметре, в качестве ответа.
func (c *Context) Body(data interface{}) {
	var headers = c.Response.Header() // быстрый доступ к заголовкам ответа
	if c.ContentType == "" {
		c.ContentType = "application/json; charset=utf-8"
	}
	headers.Set("Content-Type", c.ContentType)
	// поддерживаем компрессию, если оно поддерживается клиентом и не запрещена в библиотеке
	var writer io.Writer = c.Response
	if CompressData {
		switch accept := c.Request.Header.Get("Accept-Encoding"); {
		case strings.Contains(accept, "gzip"):
			// Поддерживается gzip-сжатие
			headers.Set("Content-Encoding", "gzip")
			headers.Add("Vary", "Accept-Encoding")
			writer = gzipGet(writer)
			defer gzipPut(writer.(io.Closer))
		case strings.Contains(accept, "deflate"):
			// Поддерживается deflate-сжатие
			headers.Set("Content-Encoding", "deflate")
			headers.Add("Vary", "Accept-Encoding")
			writer = deflateGet(writer)
			defer deflatePut(writer.(io.Closer))
		}
	}
	// обрабатываем статус выполнения запроса
	if c.status == 0 {
		c.status = http.StatusOK
	}
	c.Response.WriteHeader(c.status)
	// в зависимости от типа данных поддерживаются разные методы вывода
	var err error
	switch data := data.(type) {
	// case nil: // нечего отдавать
	case io.Reader: // поток данных отдаем как есть
		_, err = io.Copy(writer, data)
		if data, ok := data.(io.Closer); ok {
			data.Close() // закрываем по окончании, раз поддерживается
		}
	case []byte: // уже готовый к отдаче набор данных
		_, err = writer.Write(data) // тоже отдаем как есть
	case error: // ошибки возвращаем в виде специального JSON
		err = json.NewEncoder(writer).Encode(map[string]interface{}{
			"code":  c.status,
			"error": data.Error(),
		})
	case string: // строки тоже возвращаем в виде специального JSON
		err = json.NewEncoder(writer).Encode(map[string]interface{}{
			"code":    c.status,
			"message": data,
		})
	default: // во всех остальных случаях сериализуем в JSON-представление
		err = json.NewEncoder(writer).Encode(data)
	}
	// если возникла ошибка, то пытаемся ее вернуть
	if err != nil {
		// почти http.Error
		headers.Set("Content-Type", "text/plain; charset=utf-8")
		headers.Set("X-Content-Type-Options", "nosniff")
		c.Response.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(writer, err)
	}
}

// contexts содержит пул контекстов
var contexts = sync.Pool{New: func() interface{} { return new(Context) }}
<commit_msg>Пользовательские данные<commit_after>package rest

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

// CompressData разрешает поддержку сжатия данных, если это поддерживается браузером.
// Если сжатие данных отдельно обрабатывается специальными средствами в другом месте, то чтобы
// избежать двойного сжатия, ее лучше отключить, установив значение данного флага в false.
var CompressData = true

// Context содержит контекстную информацию HTTP-запроса и методы удобного формирования ответа
// на них.
type Context struct {
	// HTTP запрос в разобранном виде
	*http.Request
	// именованные параметры из пути запроса
	Params Params
	// разобранные параметры запроса в URL (кеш)
	urlQuery url.Values
	// дополнительные данные, устанавливаемые пользователем
	// в качестве ключа рекомендуется использовать приватный тип
	// и какое-нибудь его значение, что позволит застраховаться от
	// случайной перезаписи этих данных
	data map[interface{}]interface{}
	// интерфейс для публикации ответа на запрос
	Response http.ResponseWriter
	// тип информации в ответе
	ContentType string
	// код HTTP-ответа
	status int
}

// newContext возвращает новый инициализированный контекст. В отличии от просто создания нового
// контекста, вызов данного метода использует пул контекстов.
func newContext(w http.ResponseWriter, r *http.Request, params Params) *Context {
	context := contexts.Get().(*Context)
	context.Request = r
	context.Params = params
	context.urlQuery = nil
	context.data = nil
	context.Response = w
	context.ContentType = ""
	context.status = 0
	return context
}

// free возвращает контекст в пул используемых контекстов для дальнейшего использования.
// Вызывается автоматически после того, как контекст перестает использоваться.
func (c *Context) free() {
	contexts.Put(c)
}

// Get возвращает значение именованного параметра. Если параметр с таким именем не найден,
// то возвращается значение параметра из URL с тем же именем. Разбор параметров запроса сохраняется
// внутри Context и повторного его разбора уже не требует. Но это происходит только при первом
// к ним обращении.
func (c *Context) Get(key string) string {
	for _, param := range c.Params {
		if param.Key == key {
			return param.Value
		}
	}
	if c.urlQuery == nil {
		c.urlQuery = c.Request.URL.Query()
	}
	return c.urlQuery.Get(key)
}

// Set позволяет добавить новый параметр с заданным именем и значением. Добавление нового параметра
// с таким же именем не изменяет и не удаляет предыдущего значения, а именно добавляет его в список.
func (c *Context) Set(key, value string) {
	c.Params = append(c.Params, Param{key, value})
}

// GetData возвращает пользовательские данные, сохраненные в контексте запроса с указанным ключем.
func (c *Context) GetData(key interface{}) interface{} {
	if c.data == nil {
		return nil
	}
	return c.data[key]
}

// SetData сохраняет пользовательские данные в контексте запроса с указанным ключем.
// Рекомендуется в качестве ключа использовать какой-нибудь приватный тип и его значение,
// чтобы избежать случайного затирания данных другими обработчиками. Это гарантированно обезопасит
// их от случайного доступа к ним.
func (c *Context) SetData(key, value interface{}) {
	if c.data == nil {
		c.data = make(map[interface{}]interface{})
	}
	c.data[key] = value
}

// Code код HTTP-ответа, который будет отправлен сервером. Данный метод возвращает ссылку
// на основной контекст, чтобы можно было использовать его в последовательности выполнения
// команд. Например, можно сразу установить код ответа и тут же опубликовать данные.
func (c *Context) Code(code int) *Context {
	if code >= 200 && code < 600 {
		c.status = code
	}
	return c
}

// SetHeader устанавливает новое значение для указанного HTTP-заголовка. Если передаваемое
// значение заголовка пустое, то данный заголовок будет удален.
func (c *Context) SetHeader(key, value string) {
	if value == "" {
		c.Response.Header().Del(key)
	} else {
		c.Response.Header().Set(key, value)
	}
}

// SetLocation устанавливает в заголовке HTTP-ответа значение для Lacation.
func (c *Context) SetLocation(value string) {
	c.SetHeader("Location", value)
}

// ParseBody декодирует содержимое запроса в объект. После чтения из запроса
// http.Request.Body автоматически закрывается и дополнительного закрытия не требуется.
//
// На данный момент поддерживается только разбор объектов в формате JSON.
func (c *Context) ParseBody(obj interface{}) error {
	defer c.Request.Body.Close()
	return json.NewDecoder(c.Request.Body).Decode(obj)
}

// Body публикует данные, переданные в параметре, в качестве ответа.
func (c *Context) Body(data interface{}) {
	var headers = c.Response.Header() // быстрый доступ к заголовкам ответа
	if c.ContentType == "" {
		c.ContentType = "application/json; charset=utf-8"
	}
	headers.Set("Content-Type", c.ContentType)
	// поддерживаем компрессию, если оно поддерживается клиентом и не запрещена в библиотеке
	var writer io.Writer = c.Response
	if CompressData {
		switch accept := c.Request.Header.Get("Accept-Encoding"); {
		case strings.Contains(accept, "gzip"):
			// Поддерживается gzip-сжатие
			headers.Set("Content-Encoding", "gzip")
			headers.Add("Vary", "Accept-Encoding")
			writer = gzipGet(writer)
			defer gzipPut(writer.(io.Closer))
		case strings.Contains(accept, "deflate"):
			// Поддерживается deflate-сжатие
			headers.Set("Content-Encoding", "deflate")
			headers.Add("Vary", "Accept-Encoding")
			writer = deflateGet(writer)
			defer deflatePut(writer.(io.Closer))
		}
	}
	// обрабатываем статус выполнения запроса
	if c.status == 0 {
		c.status = http.StatusOK
	}
	c.Response.WriteHeader(c.status)
	// в зависимости от типа данных поддерживаются разные методы вывода
	var err error
	switch data := data.(type) {
	// case nil: // нечего отдавать
	case io.Reader: // поток данных отдаем как есть
		_, err = io.Copy(writer, data)
		if data, ok := data.(io.Closer); ok {
			data.Close() // закрываем по окончании, раз поддерживается
		}
	case []byte: // уже готовый к отдаче набор данных
		_, err = writer.Write(data) // тоже отдаем как есть
	case error: // ошибки возвращаем в виде специального JSON
		err = json.NewEncoder(writer).Encode(map[string]interface{}{
			"code":  c.status,
			"error": data.Error(),
		})
	case string: // строки тоже возвращаем в виде специального JSON
		err = json.NewEncoder(writer).Encode(map[string]interface{}{
			"code":    c.status,
			"message": data,
		})
	default: // во всех остальных случаях сериализуем в JSON-представление
		err = json.NewEncoder(writer).Encode(data)
	}
	// если возникла ошибка, то пытаемся ее вернуть
	if err != nil {
		// почти http.Error
		headers.Set("Content-Type", "text/plain; charset=utf-8")
		headers.Set("X-Content-Type-Options", "nosniff")
		c.Response.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(writer, err)
	}
}

// contexts содержит пул контекстов
var contexts = sync.Pool{New: func() interface{} { return new(Context) }}
<|endoftext|>