<commit_before>package consensus

// A DiffDirection indicates the "direction" of a diff, either applied or
// reverted. A bool is used to restrict the value to these two possibilities.
type DiffDirection bool

const (
	DiffApply  DiffDirection = true
	DiffRevert DiffDirection = false
)

// A SiacoinOutputDiff indicates the addition or removal of a SiacoinOutput in
// the consensus set.
type SiacoinOutputDiff struct {
	Direction     DiffDirection
	ID            SiacoinOutputID
	SiacoinOutput SiacoinOutput
}

// A FileContractDiff indicates the addition or removal of a FileContract in
// the consensus set.
type FileContractDiff struct {
	Direction    DiffDirection
	ID           FileContractID
	FileContract FileContract
}

// A SiafundOutputDiff indicates the addition or removal of a SiafundOutput in
// the consensus set.
type SiafundOutputDiff struct {
	Direction     DiffDirection
	ID            SiafundOutputID
	SiafundOutput SiafundOutput
}

// A SiafundPoolDiff contains the value of the siafundPool before the block
// was applied, and after the block was applied. When applying the diff, set
// siafundPool to 'Adjusted'. When reverting the diff, set siafundPool to
// 'Previous'.
type SiafundPoolDiff struct {
	Previous Currency
	Adjusted Currency
}

// commitSiacoinOutputDiff applies or reverts a SiacoinOutputDiff.
func (s *State) commitSiacoinOutputDiff(scod SiacoinOutputDiff, dir DiffDirection) {
	// Sanity check - should not be adding an output twice, or deleting an
	// output that does not exist.
	if DEBUG {
		_, exists := s.siacoinOutputs[scod.ID]
		if exists == (scod.Direction == dir) {
			panic("rogue output in commitSiacoinOutputDiff")
		}
	}

	if scod.Direction == dir {
		s.siacoinOutputs[scod.ID] = scod.SiacoinOutput
	} else {
		delete(s.siacoinOutputs, scod.ID)
	}
}

// commitFileContractDiff applies or reverts a FileContractDiff.
func (s *State) commitFileContractDiff(fcd FileContractDiff, dir DiffDirection) {
	// Sanity check - should not be adding a contract twice, or deleting a
	// contract that does not exist.
	if DEBUG {
		_, exists := s.fileContracts[fcd.ID]
		if exists == (fcd.Direction == dir) {
			panic("rogue contract in commitFileContractDiff")
		}
	}

	if fcd.Direction == dir {
		s.fileContracts[fcd.ID] = fcd.FileContract
	} else {
		delete(s.fileContracts, fcd.ID)
	}
}

// commitSiafundOutputDiff applies or reverts a SiafundOutputDiff.
func (s *State) commitSiafundOutputDiff(sfod SiafundOutputDiff, dir DiffDirection) {
	// Sanity check - should not be adding an output twice, or deleting an
	// output that does not exist.
	if DEBUG {
		_, exists := s.siafundOutputs[sfod.ID]
		if exists == (sfod.Direction == dir) {
			panic("rogue output in commitSiafundOutputDiff")
		}
	}

	if sfod.Direction == dir {
		s.siafundOutputs[sfod.ID] = sfod.SiafundOutput
	} else {
		delete(s.siafundOutputs, sfod.ID)
	}
}

// commitSiafundPoolDiff applies or reverts a SiafundPoolDiff.
func (s *State) commitSiafundPoolDiff(sfpd SiafundPoolDiff, dir DiffDirection) {
	if dir == DiffApply {
		s.siafundPool = sfpd.Adjusted
	} else {
		s.siafundPool = sfpd.Previous
	}
}

// commitDiffSet applies or reverts the diffs in a blockNode.
func (s *State) commitDiffSet(bn *blockNode, dir DiffDirection) {
	// Sanity check
	if DEBUG {
		// Diffs should have already been generated for this node.
		if !bn.diffsGenerated {
			panic("misuse of applyDiffSet - diffs have not been generated!")
		}

		// Current node must be the input node's parent if applying, and
		// current node must be the input node if reverting.
		if dir == DiffApply {
			if bn.parent.block.ID() != s.currentBlockID {
				panic("applying a block node when it's not a valid successor")
			}
		} else {
			if bn.block.ID() != s.currentBlockID {
				panic("applying a block node when it's not a valid successor")
			}
		}
	}

	// apply each of the diffs
	for _, scod := range bn.siacoinOutputDiffs {
		s.commitSiacoinOutputDiff(scod, dir)
	}
	for _, fcd := range bn.fileContractDiffs {
		s.commitFileContractDiff(fcd, dir)
	}
	for _, sfod := range bn.siafundOutputDiffs {
		s.commitSiafundOutputDiff(sfod, dir)
	}
	s.commitSiafundPoolDiff(bn.siafundPoolDiff, dir)

	// update State metadata
	if dir == DiffApply {
		s.currentBlockID = bn.block.ID()

		s.currentPath[bn.height] = bn.block.ID()
		s.delayedSiacoinOutputs[bn.height] = bn.delayedSiacoinOutputs
	} else {
		s.currentBlockID = bn.parent.block.ID()

		delete(s.currentPath, bn.height)
		delete(s.delayedSiacoinOutputs, bn.height)
	}
}

// generateAndApplyDiff will verify the block and then integrate it into the
// consensus state. These two actions must happen at the same time because
// transactions are allowed to depend on each other. We can't be sure that a
// transaction is valid unless we have applied all of the previous transactions
// in the block, which means we need to apply while we verify.
func (s *State) generateAndApplyDiff(bn *blockNode) (err error) {
	// Sanity check
	if DEBUG {
		// Generate should only be called if the diffs have not yet been
		// generated.
		if bn.diffsGenerated {
			panic("misuse of generateAndApplyDiff")
		}

		// Current node must be the input node's parent.
		if bn.parent.block.ID() != s.currentBlockID {
			panic("applying a block node when it's not a valid successor")
		}
	}

	// Update the state to point to the new block.
	s.currentBlockID = bn.block.ID()
	s.currentPath[bn.height] = bn.block.ID()
	s.delayedSiacoinOutputs[s.height()] = make(map[SiacoinOutputID]SiacoinOutput)

	// diffsGenerated is set to true as soon as we start changing the set of
	// diffs in the block node. If at any point the block is found to be
	// invalid, the diffs can be safely reversed from whatever point.
	bn.diffsGenerated = true

	// The first diff to be applied is to mark what the starting siafundPool balance
	// is.
	bn.siafundPoolDiff.Previous = s.siafundPool

	// Validate and apply each transaction in the block. They cannot be
	// validated all at once because some transactions may not be valid until
	// previous transactions have been applied.
	for _, txn := range bn.block.Transactions {
		err = s.validTransaction(txn)
		if err != nil {
			s.invalidateNode(bn)
			s.commitDiffSet(bn, DiffRevert)
			return
		}

		s.applyTransaction(bn, txn)
	}

	// After all of the transactions have been applied, 'maintenance' is
	// applied on the block. This includes adding any outputs that have reached
	// maturity, applying any contracts with missed storage proofs, and adding
	// the miner payouts to the list of delayed outputs.
	s.applyMaintenance(bn)

	// The final thing is to update the siafundPoolDiff to indicate where the
	// siafund pool ended up.
	bn.siafundPoolDiff.Adjusted = s.siafundPool

	return
}
<commit_msg>more specific panics<commit_after>package consensus

// A DiffDirection indicates the "direction" of a diff, either applied or
// reverted. A bool is used to restrict the value to these two possibilities.
type DiffDirection bool

const (
	DiffApply  DiffDirection = true
	DiffRevert DiffDirection = false
)

// A SiacoinOutputDiff indicates the addition or removal of a SiacoinOutput in
// the consensus set.
type SiacoinOutputDiff struct {
	Direction     DiffDirection
	ID            SiacoinOutputID
	SiacoinOutput SiacoinOutput
}

// A FileContractDiff indicates the addition or removal of a FileContract in
// the consensus set.
type FileContractDiff struct {
	Direction    DiffDirection
	ID           FileContractID
	FileContract FileContract
}

// A SiafundOutputDiff indicates the addition or removal of a SiafundOutput in
// the consensus set.
type SiafundOutputDiff struct {
	Direction     DiffDirection
	ID            SiafundOutputID
	SiafundOutput SiafundOutput
}

// A SiafundPoolDiff contains the value of the siafundPool before the block
// was applied, and after the block was applied. When applying the diff, set
// siafundPool to 'Adjusted'. When reverting the diff, set siafundPool to
// 'Previous'.
type SiafundPoolDiff struct {
	Previous Currency
	Adjusted Currency
}

// commitSiacoinOutputDiff applies or reverts a SiacoinOutputDiff.
func (s *State) commitSiacoinOutputDiff(scod SiacoinOutputDiff, dir DiffDirection) {
	// Sanity check - should not be adding an output twice, or deleting an
	// output that does not exist.
	if DEBUG {
		_, exists := s.siacoinOutputs[scod.ID]
		if exists == (scod.Direction == dir) {
			panic("rogue siacoin output in commitSiacoinOutputDiff")
		}
	}

	if scod.Direction == dir {
		s.siacoinOutputs[scod.ID] = scod.SiacoinOutput
	} else {
		delete(s.siacoinOutputs, scod.ID)
	}
}

// commitFileContractDiff applies or reverts a FileContractDiff.
func (s *State) commitFileContractDiff(fcd FileContractDiff, dir DiffDirection) {
	// Sanity check - should not be adding a contract twice, or deleting a
	// contract that does not exist.
	if DEBUG {
		_, exists := s.fileContracts[fcd.ID]
		if exists == (fcd.Direction == dir) {
			panic("rogue file contract in commitFileContractDiff")
		}
	}

	if fcd.Direction == dir {
		s.fileContracts[fcd.ID] = fcd.FileContract
	} else {
		delete(s.fileContracts, fcd.ID)
	}
}

// commitSiafundOutputDiff applies or reverts a SiafundOutputDiff.
func (s *State) commitSiafundOutputDiff(sfod SiafundOutputDiff, dir DiffDirection) {
	// Sanity check - should not be adding an output twice, or deleting an
	// output that does not exist.
	if DEBUG {
		_, exists := s.siafundOutputs[sfod.ID]
		if exists == (sfod.Direction == dir) {
			panic("rogue siafund output in commitSiafundOutputDiff")
		}
	}

	if sfod.Direction == dir {
		s.siafundOutputs[sfod.ID] = sfod.SiafundOutput
	} else {
		delete(s.siafundOutputs, sfod.ID)
	}
}

// commitSiafundPoolDiff applies or reverts a SiafundPoolDiff.
func (s *State) commitSiafundPoolDiff(sfpd SiafundPoolDiff, dir DiffDirection) {
	if dir == DiffApply {
		s.siafundPool = sfpd.Adjusted
	} else {
		s.siafundPool = sfpd.Previous
	}
}

// commitDiffSet applies or reverts the diffs in a blockNode.
func (s *State) commitDiffSet(bn *blockNode, dir DiffDirection) {
	// Sanity check
	if DEBUG {
		// Diffs should have already been generated for this node.
		if !bn.diffsGenerated {
			panic("misuse of applyDiffSet - diffs have not been generated!")
		}

		// Current node must be the input node's parent if applying, and
		// current node must be the input node if reverting.
		if dir == DiffApply {
			if bn.parent.block.ID() != s.currentBlockID {
				panic("applying a block node when it's not a valid successor")
			}
		} else {
			if bn.block.ID() != s.currentBlockID {
				panic("applying a block node when it's not a valid successor")
			}
		}
	}

	// apply each of the diffs
	for _, scod := range bn.siacoinOutputDiffs {
		s.commitSiacoinOutputDiff(scod, dir)
	}
	for _, fcd := range bn.fileContractDiffs {
		s.commitFileContractDiff(fcd, dir)
	}
	for _, sfod := range bn.siafundOutputDiffs {
		s.commitSiafundOutputDiff(sfod, dir)
	}
	s.commitSiafundPoolDiff(bn.siafundPoolDiff, dir)

	// update State metadata
	if dir == DiffApply {
		s.currentBlockID = bn.block.ID()

		s.currentPath[bn.height] = bn.block.ID()
		s.delayedSiacoinOutputs[bn.height] = bn.delayedSiacoinOutputs
	} else {
		s.currentBlockID = bn.parent.block.ID()

		delete(s.currentPath, bn.height)
		delete(s.delayedSiacoinOutputs, bn.height)
	}
}

// generateAndApplyDiff will verify the block and then integrate it into the
// consensus state. These two actions must happen at the same time because
// transactions are allowed to depend on each other. We can't be sure that a
// transaction is valid unless we have applied all of the previous transactions
// in the block, which means we need to apply while we verify.
func (s *State) generateAndApplyDiff(bn *blockNode) (err error) {
	// Sanity check
	if DEBUG {
		// Generate should only be called if the diffs have not yet been
		// generated.
		if bn.diffsGenerated {
			panic("misuse of generateAndApplyDiff")
		}

		// Current node must be the input node's parent.
		if bn.parent.block.ID() != s.currentBlockID {
			panic("applying a block node when it's not a valid successor")
		}
	}

	// Update the state to point to the new block.
	s.currentBlockID = bn.block.ID()
	s.currentPath[bn.height] = bn.block.ID()
	s.delayedSiacoinOutputs[s.height()] = make(map[SiacoinOutputID]SiacoinOutput)

	// diffsGenerated is set to true as soon as we start changing the set of
	// diffs in the block node. If at any point the block is found to be
	// invalid, the diffs can be safely reversed from whatever point.
	bn.diffsGenerated = true

	// The first diff to be applied is to mark what the starting siafundPool balance
	// is.
	bn.siafundPoolDiff.Previous = s.siafundPool

	// Validate and apply each transaction in the block. They cannot be
	// validated all at once because some transactions may not be valid until
	// previous transactions have been applied.
	for _, txn := range bn.block.Transactions {
		err = s.validTransaction(txn)
		if err != nil {
			s.invalidateNode(bn)
			s.commitDiffSet(bn, DiffRevert)
			return
		}

		s.applyTransaction(bn, txn)
	}

	// After all of the transactions have been applied, 'maintenance' is
	// applied on the block. This includes adding any outputs that have reached
	// maturity, applying any contracts with missed storage proofs, and adding
	// the miner payouts to the list of delayed outputs.
	s.applyMaintenance(bn)

	// The final thing is to update the siafundPoolDiff to indicate where the
	// siafund pool ended up.
	bn.siafundPoolDiff.Adjusted = s.siafundPool

	return
}
<|endoftext|>


<commit_before>package couchbase

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"runtime"
	"sort"
	"strings"
)

// The HTTP Client To Use
var HttpClient = http.DefaultClient

// Auth callback gets the auth username and password for the given
// bucket.
type AuthHandler interface {
	GetCredentials() (string, string)
}

type RestPool struct {
	Name         string `json:"name"`
	StreamingURI string `json:"streamingUri"`
	URI          string `json:"uri"`
}

type Pools struct {
	ComponentsVersion     map[string]string `json:"componentsVersion,omitempty"`
	ImplementationVersion string            `json:"implementationVersion"`
	IsAdmin               bool              `json:"isAdminCreds"`
	UUID                  string            `json:"uuid"`
	Pools                 []RestPool        `json:"pools"`
}

// A computer in a cluster running the couchbase software.
type Node struct {
	ClusterCompatibility int                `json:"clusterCompatibility"`
	ClusterMembership    string             `json:"clusterMembership"`
	CouchAPIBase         string             `json:"couchApiBase"`
	Hostname             string             `json:"hostname"`
	InterestingStats     map[string]float64 `json:"interestingStats,omitempty"`
	MCDMemoryAllocated   float64            `json:"mcdMemoryAllocated"`
	MCDMemoryReserved    float64            `json:"mcdMemoryReserved"`
	MemoryFree           float64            `json:"memoryFree"`
	MemoryTotal          float64            `json:"memoryTotal"`
	OS                   string             `json:"os"`
	Ports                map[string]int     `json:"ports"`
	Status               string             `json:"status"`
	Uptime               int                `json:"uptime,string"`
	Version              string             `json:"version"`
	ThisNode             bool               `json:"thisNode,omitempty"`
}

// A pool of nodes and buckets.
type Pool struct {
	BucketMap map[string]Bucket
	Nodes     []Node

	BucketURL map[string]string `json:"buckets"`

	client Client
}

// An individual bucket.  Herein lives the most useful stuff.
type Bucket struct {
	AuthType            string             `json:"authType"`
	Capabilities        []string           `json:"bucketCapabilities"`
	CapabilitiesVersion string             `json:"bucketCapabilitiesVer"`
	Type                string             `json:"bucketType"`
	Name                string             `json:"name"`
	NodeLocator         string             `json:"nodeLocator"`
	Nodes               []Node             `json:"nodes"`
	Quota               map[string]float64 `json:"quota,omitempty"`
	Replicas            int                `json:"replicaNumber"`
	Password            string             `json:"saslPassword"`
	URI                 string             `json:"uri"`
	StreamingURI        string             `json:"streamingUri"`
	LocalRandomKeyURI   string             `json:"localRandomKeyUri,omitempty"`
	UUID                string             `json:"uuid"`
	DDocs               struct {
		URI string `json:"uri"`
	} `json:"ddocs,omitempty"`
	VBucketServerMap struct {
		HashAlgorithm string   `json:"hashAlgorithm"`
		NumReplicas   int      `json:"numReplicas"`
		ServerList    []string `json:"serverList"`
		VBucketMap    [][]int  `json:"vBucketMap"`
	} `json:"vBucketServerMap"`
	BasicStats  map[string]interface{} `json:"basicStats,omitempty"`
	Controllers map[string]interface{} `json:"controllers,omitempty"`

	pool        *Pool
	connections []*connectionPool
	commonSufix string
	auth        AuthHandler
}

// Get the (sorted) list of memcached node addresses (hostname:port).
func (b Bucket) NodeAddresses() []string {
	rv := make([]string, len(b.VBucketServerMap.ServerList))
	copy(rv, b.VBucketServerMap.ServerList)
	sort.Strings(rv)
	return rv
}

// Get the longest common suffix of all host:port strings in the node list.
func (b Bucket) CommonAddressSuffix() string {
	input := []string{}
	for _, n := range b.Nodes {
		input = append(input, n.Hostname)
	}
	return FindCommonSuffix(input)
}

// The couchbase client gives access to all the things.
type Client struct {
	BaseURL  *url.URL
	Info     Pools
	Statuses [256]uint64
}

func maybeAddAuth(req *http.Request, ah AuthHandler) {
	if ah != nil {
		user, pass := ah.GetCredentials()
		req.Header.Set("Authorization", "Basic "+
			base64.StdEncoding.EncodeToString([]byte(user+":"+pass)))
	}
}

func (c *Client) parseURLResponse(path string, ah AuthHandler,
	out interface{}) error {

	u := *c.BaseURL
	u.User = nil
	if q := strings.Index(path, "?"); q > 0 {
		u.Path = path[:q]
		u.RawQuery = path[q+1:]
	} else {
		u.Path = path
	}

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}
	maybeAddAuth(req, ah)

	res, err := HttpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		bod, _ := ioutil.ReadAll(io.LimitReader(res.Body, 512))
		return fmt.Errorf("HTTP error %v getting %q: %s",
			res.Status, u.String(), bod)
	}

	d := json.NewDecoder(res.Body)
	if err = d.Decode(&out); err != nil {
		return err
	}
	return nil
}

type basicAuth struct {
	u, p string
}

func (b basicAuth) GetCredentials() (string, string) {
	return b.u, b.p
}

// Connect to a couchbase cluster.
func Connect(baseU string) (c Client, err error) {
	c.BaseURL, err = url.Parse(baseU)
	if err != nil {
		return
	}

	return c, c.parseURLResponse("/pools", nil, &c.Info)
}

func (b *Bucket) refresh() (err error) {
	pool := b.pool
	err = pool.client.parseURLResponse(b.URI, b.auth, b)
	if err != nil {
		return err
	}
	b.pool = pool
	for i := range b.connections {
		b.connections[i] = newConnectionPool(
			b.VBucketServerMap.ServerList[i], b.auth, 4)
	}
	return nil
}

func (p *Pool) refresh() (err error) {
	p.BucketMap = make(map[string]Bucket)

	var ah AuthHandler
	if user := p.client.BaseURL.User; user != nil {
		pw, _ := user.Password()
		ah = basicAuth{user.Username(), pw}
	}

	buckets := []Bucket{}
	err = p.client.parseURLResponse(p.BucketURL["uri"], ah, &buckets)
	if err != nil {
		return err
	}
	for _, b := range buckets {
		b.pool = p
		b.auth = p.getDefaultAuth(b.Name)
		b.connections = make([]*connectionPool, len(b.VBucketServerMap.ServerList))

		p.BucketMap[b.Name] = b
	}
	return nil
}

// Get a pool from within the couchbase cluster (usually "default").
func (c *Client) GetPool(name string) (p Pool, err error) {
	var poolURI string
	for _, p := range c.Info.Pools {
		if p.Name == name {
			poolURI = p.URI
		}
	}
	if poolURI == "" {
		return p, errors.New("No pool named " + name)
	}

	err = c.parseURLResponse(poolURI, nil, &p)

	p.client = *c

	p.refresh()
	return
}

// Mark this bucket as no longer needed, closing connections it may have open.
func (b *Bucket) Close() {
	if b.connections != nil {
		for _, c := range b.connections {
			if c != nil {
				c.Close()
			}
		}
		b.connections = nil
	}
}

func bucket_finalizer(b *Bucket) {
	if b.connections != nil {
		log.Printf("Warning: Finalizing a bucket with active connections.")
	}
}

func (p *Pool) getDefaultAuth(name string) AuthHandler {
	var pw string
	if p.client.BaseURL.User != nil {
		pw, _ = p.client.BaseURL.User.Password()
	}
	return &basicAuth{name, pw}
}

// Get a bucket from within this pool.
func (p *Pool) GetBucket(name string) (*Bucket, error) {
	rv, ok := p.BucketMap[name]
	if !ok {
		return nil, errors.New("No bucket named " + name)
	}
	runtime.SetFinalizer(&rv, bucket_finalizer)
	rv.refresh()
	rv.auth = p.getDefaultAuth(name)
	return &rv, nil
}

// Get the pool to which this bucket belongs.
func (b *Bucket) GetPool() *Pool {
	return b.pool
}

// Get the client from which we got this pool.
func (p *Pool) GetClient() *Client {
	return &p.client
}

// Convenience function for getting a named bucket from a URL
func GetBucket(endpoint, poolname, bucketname string) (*Bucket, error) {
	var err error
	client, err := Connect(endpoint)
	if err != nil {
		return nil, err
	}

	pool, err := client.GetPool(poolname)
	if err != nil {
		return nil, err
	}

	return pool.GetBucket(bucketname)
}
<commit_msg>Return more errors in more broken places<commit_after>package couchbase

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"runtime"
	"sort"
	"strings"
)

// The HTTP Client To Use
var HttpClient = http.DefaultClient

// Auth callback gets the auth username and password for the given
// bucket.
type AuthHandler interface {
	GetCredentials() (string, string)
}

type RestPool struct {
	Name         string `json:"name"`
	StreamingURI string `json:"streamingUri"`
	URI          string `json:"uri"`
}

type Pools struct {
	ComponentsVersion     map[string]string `json:"componentsVersion,omitempty"`
	ImplementationVersion string            `json:"implementationVersion"`
	IsAdmin               bool              `json:"isAdminCreds"`
	UUID                  string            `json:"uuid"`
	Pools                 []RestPool        `json:"pools"`
}

// A computer in a cluster running the couchbase software.
type Node struct {
	ClusterCompatibility int                `json:"clusterCompatibility"`
	ClusterMembership    string             `json:"clusterMembership"`
	CouchAPIBase         string             `json:"couchApiBase"`
	Hostname             string             `json:"hostname"`
	InterestingStats     map[string]float64 `json:"interestingStats,omitempty"`
	MCDMemoryAllocated   float64            `json:"mcdMemoryAllocated"`
	MCDMemoryReserved    float64            `json:"mcdMemoryReserved"`
	MemoryFree           float64            `json:"memoryFree"`
	MemoryTotal          float64            `json:"memoryTotal"`
	OS                   string             `json:"os"`
	Ports                map[string]int     `json:"ports"`
	Status               string             `json:"status"`
	Uptime               int                `json:"uptime,string"`
	Version              string             `json:"version"`
	ThisNode             bool               `json:"thisNode,omitempty"`
}

// A pool of nodes and buckets.
type Pool struct {
	BucketMap map[string]Bucket
	Nodes     []Node

	BucketURL map[string]string `json:"buckets"`

	client Client
}

// An individual bucket.  Herein lives the most useful stuff.
type Bucket struct {
	AuthType            string             `json:"authType"`
	Capabilities        []string           `json:"bucketCapabilities"`
	CapabilitiesVersion string             `json:"bucketCapabilitiesVer"`
	Type                string             `json:"bucketType"`
	Name                string             `json:"name"`
	NodeLocator         string             `json:"nodeLocator"`
	Nodes               []Node             `json:"nodes"`
	Quota               map[string]float64 `json:"quota,omitempty"`
	Replicas            int                `json:"replicaNumber"`
	Password            string             `json:"saslPassword"`
	URI                 string             `json:"uri"`
	StreamingURI        string             `json:"streamingUri"`
	LocalRandomKeyURI   string             `json:"localRandomKeyUri,omitempty"`
	UUID                string             `json:"uuid"`
	DDocs               struct {
		URI string `json:"uri"`
	} `json:"ddocs,omitempty"`
	VBucketServerMap struct {
		HashAlgorithm string   `json:"hashAlgorithm"`
		NumReplicas   int      `json:"numReplicas"`
		ServerList    []string `json:"serverList"`
		VBucketMap    [][]int  `json:"vBucketMap"`
	} `json:"vBucketServerMap"`
	BasicStats  map[string]interface{} `json:"basicStats,omitempty"`
	Controllers map[string]interface{} `json:"controllers,omitempty"`

	pool        *Pool
	connections []*connectionPool
	commonSufix string
	auth        AuthHandler
}

// Get the (sorted) list of memcached node addresses (hostname:port).
func (b Bucket) NodeAddresses() []string {
	rv := make([]string, len(b.VBucketServerMap.ServerList))
	copy(rv, b.VBucketServerMap.ServerList)
	sort.Strings(rv)
	return rv
}

// Get the longest common suffix of all host:port strings in the node list.
func (b Bucket) CommonAddressSuffix() string {
	input := []string{}
	for _, n := range b.Nodes {
		input = append(input, n.Hostname)
	}
	return FindCommonSuffix(input)
}

// The couchbase client gives access to all the things.
type Client struct {
	BaseURL  *url.URL
	Info     Pools
	Statuses [256]uint64
}

func maybeAddAuth(req *http.Request, ah AuthHandler) {
	if ah != nil {
		user, pass := ah.GetCredentials()
		req.Header.Set("Authorization", "Basic "+
			base64.StdEncoding.EncodeToString([]byte(user+":"+pass)))
	}
}

func (c *Client) parseURLResponse(path string, ah AuthHandler,
	out interface{}) error {

	u := *c.BaseURL
	u.User = nil
	if q := strings.Index(path, "?"); q > 0 {
		u.Path = path[:q]
		u.RawQuery = path[q+1:]
	} else {
		u.Path = path
	}

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}
	maybeAddAuth(req, ah)

	res, err := HttpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		bod, _ := ioutil.ReadAll(io.LimitReader(res.Body, 512))
		return fmt.Errorf("HTTP error %v getting %q: %s",
			res.Status, u.String(), bod)
	}

	d := json.NewDecoder(res.Body)
	if err = d.Decode(&out); err != nil {
		return err
	}
	return nil
}

type basicAuth struct {
	u, p string
}

func (b basicAuth) GetCredentials() (string, string) {
	return b.u, b.p
}

// Connect to a couchbase cluster.
func Connect(baseU string) (c Client, err error) {
	c.BaseURL, err = url.Parse(baseU)
	if err != nil {
		return
	}

	return c, c.parseURLResponse("/pools", nil, &c.Info)
}

func (b *Bucket) refresh() (err error) {
	pool := b.pool
	err = pool.client.parseURLResponse(b.URI, b.auth, b)
	if err != nil {
		return err
	}
	b.pool = pool
	for i := range b.connections {
		b.connections[i] = newConnectionPool(
			b.VBucketServerMap.ServerList[i], b.auth, 4)
	}
	return nil
}

func (p *Pool) refresh() (err error) {
	p.BucketMap = make(map[string]Bucket)

	var ah AuthHandler
	if user := p.client.BaseURL.User; user != nil {
		pw, _ := user.Password()
		ah = basicAuth{user.Username(), pw}
	}

	buckets := []Bucket{}
	err = p.client.parseURLResponse(p.BucketURL["uri"], ah, &buckets)
	if err != nil {
		return err
	}
	for _, b := range buckets {
		b.pool = p
		b.auth = p.getDefaultAuth(b.Name)
		b.connections = make([]*connectionPool, len(b.VBucketServerMap.ServerList))

		p.BucketMap[b.Name] = b
	}
	return nil
}

// Get a pool from within the couchbase cluster (usually "default").
func (c *Client) GetPool(name string) (p Pool, err error) {
	var poolURI string
	for _, p := range c.Info.Pools {
		if p.Name == name {
			poolURI = p.URI
		}
	}
	if poolURI == "" {
		return p, errors.New("No pool named " + name)
	}

	err = c.parseURLResponse(poolURI, nil, &p)

	p.client = *c

	err = p.refresh()
	return
}

// Mark this bucket as no longer needed, closing connections it may have open.
func (b *Bucket) Close() {
	if b.connections != nil {
		for _, c := range b.connections {
			if c != nil {
				c.Close()
			}
		}
		b.connections = nil
	}
}

func bucket_finalizer(b *Bucket) {
	if b.connections != nil {
		log.Printf("Warning: Finalizing a bucket with active connections.")
	}
}

func (p *Pool) getDefaultAuth(name string) AuthHandler {
	var pw string
	if p.client.BaseURL.User != nil {
		pw, _ = p.client.BaseURL.User.Password()
	}
	return &basicAuth{name, pw}
}

// Get a bucket from within this pool.
func (p *Pool) GetBucket(name string) (*Bucket, error) {
	rv, ok := p.BucketMap[name]
	if !ok {
		return nil, errors.New("No bucket named " + name)
	}
	runtime.SetFinalizer(&rv, bucket_finalizer)
	err := rv.refresh()
	if err != nil {
		return nil, err
	}
	rv.auth = p.getDefaultAuth(name)
	return &rv, nil
}

// Get the pool to which this bucket belongs.
func (b *Bucket) GetPool() *Pool {
	return b.pool
}

// Get the client from which we got this pool.
func (p *Pool) GetClient() *Client {
	return &p.client
}

// Convenience function for getting a named bucket from a URL
func GetBucket(endpoint, poolname, bucketname string) (*Bucket, error) {
	var err error
	client, err := Connect(endpoint)
	if err != nil {
		return nil, err
	}

	pool, err := client.GetPool(poolname)
	if err != nil {
		return nil, err
	}

	return pool.GetBucket(bucketname)
}
<|endoftext|>


<commit_before>package swarm

import (
	"fmt"
	peer "github.com/jbenet/go-ipfs/peer"
	"sync"
)

// Message represents a packet of information sent to or received from a
// particular Peer.
type Message struct {
	// To or from, depending on direction.
	Peer *peer.Peer

	// Opaque data
	Data []byte
}

// Chan is a swam channel, which provides duplex communication and errors.
type Chan struct {
	Outgoing chan Message
	Incoming chan Message
	Errors   chan error
	Close    chan bool
}

// NewChan constructs a Chan instance, with given buffer size bufsize.
func NewChan(bufsize int) *Chan {
	return &Chan{
		Outgoing: make(chan Message, bufsize),
		Incoming: make(chan Message, bufsize),
		Errors:   make(chan error),
		Close:    make(chan bool, bufsize),
	}
}

// Swarm is a connection muxer, allowing connections to other peers to
// be opened and closed, while still using the same Chan for all
// communication. The Chan sends/receives Messages, which note the
// destination or source Peer.
type Swarm struct {
	Chan      *Chan
	conns     ConnMap
	connsLock sync.RWMutex
}

// NewSwarm constructs a Swarm, with a Chan.
func NewSwarm() *Swarm {
	s := &Swarm{
		Chan:  NewChan(10),
		conns: ConnMap{},
	}
	go s.fanOut()
	return s
}

// Close closes a swam.
func (s *Swarm) Close() {
	s.connsLock.RLock()
	l := len(s.conns)
	s.connsLock.RUnlock()

	for i := 0; i < l; i++ {
		s.Chan.Close <- true // fan ins
	}
	s.Chan.Close <- true // fan out
	s.Chan.Close <- true // listener
}

// Dial connects to a peer.
//
// The idea is that the client of Swarm does not need to know what network
// the connection will happen over. Swarm can use whichever it choses.
// This allows us to use various transport protocols, do NAT traversal/relay,
// etc. to achive connection.
//
// For now, Dial uses only TCP. This will be extended.
func (s *Swarm) Dial(peer *peer.Peer) (*Conn, error) {
	k := peer.Key()

	// check if we already have an open connection first
	s.connsLock.RLock()
	conn, found := s.conns[k]
	s.connsLock.RUnlock()
	if found {
		return conn, nil
	}

	// open connection to peer
	conn, err := Dial("tcp", peer)
	if err != nil {
		return nil, err
	}

	// add to conns
	s.connsLock.Lock()
	s.conns[k] = conn
	s.connsLock.Unlock()

	// kick off reader goroutine
	go s.fanIn(conn)
	return conn, nil
}

// Handles the unwrapping + sending of messages to the right connection.
func (s *Swarm) fanOut() {
	for {
		select {
		case <-s.Chan.Close:
			return // told to close.
		case msg, ok := <-s.Chan.Outgoing:
			if !ok {
				return
			}

			s.connsLock.RLock()
			conn, found := s.conns[msg.Peer.Key()]
			s.connsLock.RUnlock()
			if !found {
				e := fmt.Errorf("Sent msg to peer without open conn: %v", msg.Peer)
				s.Chan.Errors <- e
			}

			// queue it in the connection's buffer
			conn.Outgoing.MsgChan <- msg.Data
		}
	}
}

// Handles the receiving + wrapping of messages, per conn.
// Consider using reflect.Select with one goroutine instead of n.
func (s *Swarm) fanIn(conn *Conn) {
Loop:
	for {
		select {
		case <-s.Chan.Close:
			// close Conn.
			conn.Close()
			break Loop

		case <-conn.Closed:
			break Loop

		case data, ok := <-conn.Incoming.MsgChan:
			if !ok {
				e := fmt.Errorf("Error retrieving from conn: %v", conn)
				s.Chan.Errors <- e
				break Loop
			}

			// wrap it for consumers.
			msg := Message{Peer: conn.Peer, Data: data}
			s.Chan.Incoming <- msg
		}
	}

	s.connsLock.Lock()
	delete(s.conns, conn.Peer.Key())
	s.connsLock.Unlock()
}
<commit_msg>implement listening on swarm object<commit_after>package swarm

import (
	"fmt"
	peer "github.com/jbenet/go-ipfs/peer"
	ma "github.com/jbenet/go-multiaddr"
	u "github.com/jbenet/go-ipfs/util"
	"sync"
	"net"
)

// Message represents a packet of information sent to or received from a
// particular Peer.
type Message struct {
	// To or from, depending on direction.
	Peer *peer.Peer

	// Opaque data
	Data []byte
}

// Chan is a swam channel, which provides duplex communication and errors.
type Chan struct {
	Outgoing chan Message
	Incoming chan Message
	Errors   chan error
	Close    chan bool
}

// NewChan constructs a Chan instance, with given buffer size bufsize.
func NewChan(bufsize int) *Chan {
	return &Chan{
		Outgoing: make(chan Message, bufsize),
		Incoming: make(chan Message, bufsize),
		Errors:   make(chan error),
		Close:    make(chan bool, bufsize),
	}
}

// Swarm is a connection muxer, allowing connections to other peers to
// be opened and closed, while still using the same Chan for all
// communication. The Chan sends/receives Messages, which note the
// destination or source Peer.
type Swarm struct {
	Chan      *Chan
	conns     ConnMap
	connsLock sync.RWMutex

	local	  *peer.Peer
}

// NewSwarm constructs a Swarm, with a Chan.
func NewSwarm(local *peer.Peer) *Swarm {
	s := &Swarm{
		Chan:  NewChan(10),
		conns: ConnMap{},
		local: local,
	}
	go s.fanOut()
	return s
}

// Open listeners for each network the swarm should listen on
func (s *Swarm) Listen() {
	for _,addr := range s.local.Addresses {
		err := s.connListen(addr)
		if err != nil {
			u.PErr("Failed to listen on: %s [%s]", addr, err)
		}
	}
}

// Listen for new connections on the given multiaddr
func (s *Swarm) connListen(maddr *ma.Multiaddr) error {
	netstr, addr, err := maddr.DialArgs()
	if err != nil {
		return err
	}

	list, err := net.Listen(netstr, addr)
	if err != nil {
		return err
	}

	// Accept and handle new connections on this listener until it errors
	go func() {
		for {
			nconn,err := list.Accept()
			if err != nil {
				u.PErr("Failed to accept connection: %s - %s", netstr, addr)
				return
			}
			go s.handleNewConn(nconn)
		}
	}()

	return nil
}

// Handle getting ID from this peer and adding it into the map
func (s *Swarm) handleNewConn(nconn net.Conn) {
	panic("Not yet implemented!")
}

// Close closes a swarm.
func (s *Swarm) Close() {
	s.connsLock.RLock()
	l := len(s.conns)
	s.connsLock.RUnlock()

	for i := 0; i < l; i++ {
		s.Chan.Close <- true // fan ins
	}
	s.Chan.Close <- true // fan out
	s.Chan.Close <- true // listener
}

// Dial connects to a peer.
//
// The idea is that the client of Swarm does not need to know what network
// the connection will happen over. Swarm can use whichever it choses.
// This allows us to use various transport protocols, do NAT traversal/relay,
// etc. to achive connection.
//
// For now, Dial uses only TCP. This will be extended.
func (s *Swarm) Dial(peer *peer.Peer) (*Conn, error) {
	k := peer.Key()

	// check if we already have an open connection first
	s.connsLock.RLock()
	conn, found := s.conns[k]
	s.connsLock.RUnlock()
	if found {
		return conn, nil
	}

	// open connection to peer
	conn, err := Dial("tcp", peer)
	if err != nil {
		return nil, err
	}

	// add to conns
	s.connsLock.Lock()
	s.conns[k] = conn
	s.connsLock.Unlock()

	// kick off reader goroutine
	go s.fanIn(conn)
	return conn, nil
}

// Handles the unwrapping + sending of messages to the right connection.
func (s *Swarm) fanOut() {
	for {
		select {
		case <-s.Chan.Close:
			return // told to close.
		case msg, ok := <-s.Chan.Outgoing:
			if !ok {
				return
			}

			s.connsLock.RLock()
			conn, found := s.conns[msg.Peer.Key()]
			s.connsLock.RUnlock()
			if !found {
				e := fmt.Errorf("Sent msg to peer without open conn: %v", msg.Peer)
				s.Chan.Errors <- e
			}

			// queue it in the connection's buffer
			conn.Outgoing.MsgChan <- msg.Data
		}
	}
}

// Handles the receiving + wrapping of messages, per conn.
// Consider using reflect.Select with one goroutine instead of n.
func (s *Swarm) fanIn(conn *Conn) {
Loop:
	for {
		select {
		case <-s.Chan.Close:
			// close Conn.
			conn.Close()
			break Loop

		case <-conn.Closed:
			break Loop

		case data, ok := <-conn.Incoming.MsgChan:
			if !ok {
				e := fmt.Errorf("Error retrieving from conn: %v", conn)
				s.Chan.Errors <- e
				break Loop
			}

			// wrap it for consumers.
			msg := Message{Peer: conn.Peer, Data: data}
			s.Chan.Incoming <- msg
		}
	}

	s.connsLock.Lock()
	delete(s.conns, conn.Peer.Key())
	s.connsLock.Unlock()
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"os"

	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"regexp"

	"github.com/typester/go-pit"
)

var titleRe = regexp.MustCompile(`(?i)<title[^>]*>([^<]+)</title>`)

func getTitle(url string) (string, error) {
	res, err := http.Get(url)
	if err != nil {
		return "", err
	}

	content, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}

	matches := titleRe.FindStringSubmatch(string(content))
	if matches == nil {
		return "", fmt.Errorf("can't retrieve page title\n")
	}

	return matches[1], nil
}

func postedJSON(key string, url string) string {
	return fmt.Sprintf(`{"key":"%s", "longUrl": "%s"}`, key, url)
}

const apiURL = "https://www.googleapis.com/urlshortener/v1/url"

type shortenURLResponse struct {
	ID string `json:"id"`
}

func decodeAPIResponse(r io.Reader) (string, error) {
	decoder := json.NewDecoder(r)

	var res shortenURLResponse
	if err := decoder.Decode(&res); err != nil {
		return "", err
	}

	return res.ID, nil
}

func shortURL(url string) (string, error) {
	profile, err := pit.Get("goo.gl", pit.Requires{
		"key": "API key",
	})

	if err != nil {
		return "", err
	}

	jsonStr := postedJSON((*profile)["key"], url)
	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer([]byte(jsonStr)))
	if err != nil {
		return "", err
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	return decodeAPIResponse(res.Body)
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "Usage: url_title url\n")
		return
	}

	url := os.Args[1]

	title, err := getTitle(url)
	if err != nil {
		fmt.Println(err)
		return
	}

	shorten, err := shortURL(url)
	fmt.Printf("%s %s", title, shorten)
}
<commit_msg>Use json.Marshal instead of Printf<commit_after>package main

import (
	"net/http"
	"os"

	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"regexp"

	"github.com/typester/go-pit"
)

var titleRe = regexp.MustCompile(`(?i)<title[^>]*>([^<]+)</title>`)

func getTitle(url string) (string, error) {
	res, err := http.Get(url)
	if err != nil {
		return "", err
	}

	content, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}

	matches := titleRe.FindStringSubmatch(string(content))
	if matches == nil {
		return "", fmt.Errorf("can't retrieve page title\n")
	}

	return matches[1], nil
}

func postedJSON(key string, url string) (string, error) {
	type Request struct {
		Key     string `json:"key"`
		LongURL string `json:"longUrl"`
	}

	req := Request{
		Key:     key,
		LongURL: url,
	}

	bytes, err := json.Marshal(&req)
	if err != nil {
		return "", err
	}

	return string(bytes), nil
}

const apiURL = "https://www.googleapis.com/urlshortener/v1/url"

type shortenURLResponse struct {
	ID string `json:"id"`
}

func decodeAPIResponse(r io.Reader) (string, error) {
	decoder := json.NewDecoder(r)

	var res shortenURLResponse
	if err := decoder.Decode(&res); err != nil {
		return "", err
	}

	return res.ID, nil
}

func shortURL(url string) (string, error) {
	profile, err := pit.Get("goo.gl", pit.Requires{
		"key": "API key",
	})

	if err != nil {
		return "", err
	}

	jsonStr, err := postedJSON((*profile)["key"], url)
	if err != nil {
		return "", err
	}

	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer([]byte(jsonStr)))
	if err != nil {
		return "", err
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	return decodeAPIResponse(res.Body)
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "Usage: url_title url\n")
		return
	}

	url := os.Args[1]

	title, err := getTitle(url)
	if err != nil {
		fmt.Println(err)
		return
	}

	shorten, err := shortURL(url)
	fmt.Printf("%s %s", title, shorten)
}
<|endoftext|>


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

// Package context defines the Context type, which carries deadlines,
// cancelation signals, and other request-scoped values across API boundaries
// and between processes.
//
// Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context.  The chain of function calls between must
// propagate the Context, optionally replacing it with a modified copy created
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
// propagation:
//
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it.  The Context should be the first
// parameter, typically named ctx:
//
// 	func DoSomething(ctx context.Context, arg Arg) error {
// 		// ... use ctx ...
// 	}
//
// Do not pass a nil Context, even if a function permits it.  Pass context.TODO
// if you are unsure about which Context to use.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The same Context may be passed to functions running in different goroutines;
// Contexts are safe for simultaneous use by multiple goroutines.
//
// See http://blog.golang.org/context for example code for a server that uses
// Contexts.
package context

import (
	"errors"
	"fmt"
	"sync"
	"time"
)

// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
	// Deadline returns the time when work done on behalf of this context
	// should be canceled.  Deadline returns ok==false when no deadline is
	// set.  Successive calls to Deadline return the same results.
	Deadline() (deadline time.Time, ok bool)

	// Done returns a channel that's closed when work done on behalf of this
	// context should be canceled.  Done may return nil if this context can
	// never be canceled.  Successive calls to Done return the same value.
	//
	// WithCancel arranges for Done to be closed when cancel is called;
	// WithDeadline arranges for Done to be closed when the deadline
	// expires; WithTimeout arranges for Done to be closed when the timeout
	// elapses.
	//
	// Done is provided for use in select statements:
	//
	// 	// DoSomething calls DoSomethingSlow and returns as soon as
	// 	// it returns or ctx.Done is closed.
	// 	func DoSomething(ctx context.Context) (Result, error) {
	// 		c := make(chan Result, 1)
	// 		go func() { c <- DoSomethingSlow(ctx) }()
	// 		select {
	// 		case res := <-c:
	// 			return res, nil
	// 		case <-ctx.Done():
	// 			return nil, ctx.Err()
	// 		}
	// 	}
	//
	// See http://blog.golang.org/pipelines for more examples of how to use
	// a Done channel for cancelation.
	Done() <-chan struct{}

	// Err returns a non-nil error value after Done is closed.  Err returns
	// Canceled if the context was canceled or DeadlineExceeded if the
	// context's deadline passed.  No other values for Err are defined.
	// After Done is closed, successive calls to Err return the same value.
	Err() error

	// Value returns the value associated with this context for key, or nil
	// if no value is associated with key.  Successive calls to Value with
	// the same key returns the same result.
	//
	// Use context values only for request-scoped data that transits
	// processes and API boundaries, not for passing optional parameters to
	// functions.
	//
	// A key identifies a specific value in a Context.  Functions that wish
	// to store values in Context typically allocate a key in a global
	// variable then use that key as the argument to context.WithValue and
	// Context.Value.  A key can be any type that supports equality;
	// packages should define keys as an unexported type to avoid
	// collisions.
	//
	// Packages that define a Context key should provide type-safe accessors
	// for the values stores using that key:
	//
	// 	// Package user defines a User type that's stored in Contexts.
	// 	package user
	//
	// 	import "golang.org/x/net/context"
	//
	// 	// User is the type of value stored in the Contexts.
	// 	type User struct {...}
	//
	// 	// key is an unexported type for keys defined in this package.
	// 	// This prevents collisions with keys defined in other packages.
	// 	type key int
	//
	// 	// userKey is the key for user.User values in Contexts.  It is
	// 	// unexported; clients use user.NewContext and user.FromContext
	// 	// instead of using this key directly.
	// 	var userKey key = 0
	//
	// 	// NewContext returns a new Context that carries value u.
	// 	func NewContext(ctx context.Context, u *User) context.Context {
	// 		return context.WithValue(userKey, u)
	// 	}
	//
	// 	// FromContext returns the User value stored in ctx, if any.
	// 	func FromContext(ctx context.Context) (*User, bool) {
	// 		u, ok := ctx.Value(userKey).(*User)
	// 		return u, ok
	// 	}
	Value(key interface{}) interface{}
}

// Canceled is the error returned by Context.Err when the context is canceled.
var Canceled = errors.New("context canceled")

// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var DeadlineExceeded = errors.New("context deadline exceeded")

// An emptyCtx is never canceled, has no values, and has no deadline.  It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
	return
}

func (*emptyCtx) Done() <-chan struct{} {
	return nil
}

func (*emptyCtx) Err() error {
	return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
	return nil
}

func (e *emptyCtx) String() string {
	switch e {
	case background:
		return "context.Background"
	case todo:
		return "context.TODO"
	}
	return "unknown empty Context"
}

var (
	background = new(emptyCtx)
	todo       = new(emptyCtx)
)

// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline.  It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
	return background
}

// TODO returns a non-nil, empty Context.  Code should use context.TODO when
// it's unclear which Context to use or it's is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter).  TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
	return todo
}

// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := newCancelCtx(parent)
	propagateCancel(parent, &c)
	return &c, func() { c.cancel(true, Canceled) }
}

// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
	return cancelCtx{
		Context: parent,
		done:    make(chan struct{}),
	}
}

// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
	if parent.Done() == nil {
		return // parent is never canceled
	}
	if p, ok := parentCancelCtx(parent); ok {
		p.mu.Lock()
		if p.err != nil {
			// parent has already been canceled
			child.cancel(false, p.err)
		} else {
			if p.children == nil {
				p.children = make(map[canceler]bool)
			}
			p.children[child] = true
		}
		p.mu.Unlock()
	} else {
		go func() {
			select {
			case <-parent.Done():
				child.cancel(false, parent.Err())
			case <-child.Done():
			}
		}()
	}
}

// parentCancelCtx follows a chain of parent references until it finds a
// *cancelCtx.  This function understands how each of the concrete types in this
// package represents its parent.
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
	for {
		switch c := parent.(type) {
		case *cancelCtx:
			return c, true
		case *timerCtx:
			return &c.cancelCtx, true
		case *valueCtx:
			parent = c.Context
		default:
			return nil, false
		}
	}
}

// A canceler is a context type that can be canceled directly.  The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
	cancel(removeFromParent bool, err error)
	Done() <-chan struct{}
}

// A cancelCtx can be canceled.  When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
	Context

	done chan struct{} // closed by the first cancel call.

	mu       sync.Mutex
	children map[canceler]bool // set to nil by the first cancel call
	err      error             // set to non-nil by the first cancel call
}

func (c *cancelCtx) Done() <-chan struct{} {
	return c.done
}

func (c *cancelCtx) Err() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.err
}

func (c *cancelCtx) String() string {
	return fmt.Sprintf("%v.WithCancel", c.Context)
}

// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
	if err == nil {
		panic("context: internal error: missing cancel error")
	}
	c.mu.Lock()
	if c.err != nil {
		c.mu.Unlock()
		return // already canceled
	}
	c.err = err
	close(c.done)
	for child := range c.children {
		// NOTE: acquiring the child's lock while holding parent's lock.
		child.cancel(false, err)
	}
	c.children = nil
	c.mu.Unlock()

	if removeFromParent {
		if p, ok := parentCancelCtx(c.Context); ok {
			p.mu.Lock()
			if p.children != nil {
				delete(p.children, c)
			}
			p.mu.Unlock()
		}
	}
}

// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d.  If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent.  The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with the deadline
// timer, so code should call cancel as soon as the operations running in this
// Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
	if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
		// The current deadline is already sooner than the new one.
		return WithCancel(parent)
	}
	c := &timerCtx{
		cancelCtx: newCancelCtx(parent),
		deadline:  deadline,
	}
	propagateCancel(parent, c)
	d := deadline.Sub(time.Now())
	if d <= 0 {
		c.cancel(true, DeadlineExceeded) // deadline has already passed
		return c, func() { c.cancel(true, Canceled) }
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err == nil {
		c.timer = time.AfterFunc(d, func() {
			c.cancel(true, DeadlineExceeded)
		})
	}
	return c, func() { c.cancel(true, Canceled) }
}

// A timerCtx carries a timer and a deadline.  It embeds a cancelCtx to
// implement Done and Err.  It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
	cancelCtx
	timer *time.Timer // Under cancelCtx.mu.

	deadline time.Time
}

func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
	return c.deadline, true
}

func (c *timerCtx) String() string {
	return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
}

func (c *timerCtx) cancel(removeFromParent bool, err error) {
	c.cancelCtx.cancel(removeFromParent, err)
	c.mu.Lock()
	if c.timer != nil {
		c.timer.Stop()
		c.timer = nil
	}
	c.mu.Unlock()
}

// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with the deadline
// timer, so code should call cancel as soon as the operations running in this
// Context complete:
//
// 	func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// 		ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// 		defer cancel()  // releases resources if slowOperation completes before timeout elapses
// 		return slowOperation(ctx)
// 	}
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func WithValue(parent Context, key interface{}, val interface{}) Context {
	return &valueCtx{parent, key, val}
}

// A valueCtx carries a key-value pair.  It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
	Context
	key, val interface{}
}

func (c *valueCtx) String() string {
	return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
}

func (c *valueCtx) Value(key interface{}) interface{} {
	if c.key == key {
		return c.val
	}
	return c.Context.Value(key)
}
<commit_msg>context: Fix WithValue example code.<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package context defines the Context type, which carries deadlines,
// cancelation signals, and other request-scoped values across API boundaries
// and between processes.
//
// Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context.  The chain of function calls between must
// propagate the Context, optionally replacing it with a modified copy created
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
// propagation:
//
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it.  The Context should be the first
// parameter, typically named ctx:
//
// 	func DoSomething(ctx context.Context, arg Arg) error {
// 		// ... use ctx ...
// 	}
//
// Do not pass a nil Context, even if a function permits it.  Pass context.TODO
// if you are unsure about which Context to use.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The same Context may be passed to functions running in different goroutines;
// Contexts are safe for simultaneous use by multiple goroutines.
//
// See http://blog.golang.org/context for example code for a server that uses
// Contexts.
package context

import (
	"errors"
	"fmt"
	"sync"
	"time"
)

// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
	// Deadline returns the time when work done on behalf of this context
	// should be canceled.  Deadline returns ok==false when no deadline is
	// set.  Successive calls to Deadline return the same results.
	Deadline() (deadline time.Time, ok bool)

	// Done returns a channel that's closed when work done on behalf of this
	// context should be canceled.  Done may return nil if this context can
	// never be canceled.  Successive calls to Done return the same value.
	//
	// WithCancel arranges for Done to be closed when cancel is called;
	// WithDeadline arranges for Done to be closed when the deadline
	// expires; WithTimeout arranges for Done to be closed when the timeout
	// elapses.
	//
	// Done is provided for use in select statements:
	//
	// 	// DoSomething calls DoSomethingSlow and returns as soon as
	// 	// it returns or ctx.Done is closed.
	// 	func DoSomething(ctx context.Context) (Result, error) {
	// 		c := make(chan Result, 1)
	// 		go func() { c <- DoSomethingSlow(ctx) }()
	// 		select {
	// 		case res := <-c:
	// 			return res, nil
	// 		case <-ctx.Done():
	// 			return nil, ctx.Err()
	// 		}
	// 	}
	//
	// See http://blog.golang.org/pipelines for more examples of how to use
	// a Done channel for cancelation.
	Done() <-chan struct{}

	// Err returns a non-nil error value after Done is closed.  Err returns
	// Canceled if the context was canceled or DeadlineExceeded if the
	// context's deadline passed.  No other values for Err are defined.
	// After Done is closed, successive calls to Err return the same value.
	Err() error

	// Value returns the value associated with this context for key, or nil
	// if no value is associated with key.  Successive calls to Value with
	// the same key returns the same result.
	//
	// Use context values only for request-scoped data that transits
	// processes and API boundaries, not for passing optional parameters to
	// functions.
	//
	// A key identifies a specific value in a Context.  Functions that wish
	// to store values in Context typically allocate a key in a global
	// variable then use that key as the argument to context.WithValue and
	// Context.Value.  A key can be any type that supports equality;
	// packages should define keys as an unexported type to avoid
	// collisions.
	//
	// Packages that define a Context key should provide type-safe accessors
	// for the values stores using that key:
	//
	// 	// Package user defines a User type that's stored in Contexts.
	// 	package user
	//
	// 	import "golang.org/x/net/context"
	//
	// 	// User is the type of value stored in the Contexts.
	// 	type User struct {...}
	//
	// 	// key is an unexported type for keys defined in this package.
	// 	// This prevents collisions with keys defined in other packages.
	// 	type key int
	//
	// 	// userKey is the key for user.User values in Contexts.  It is
	// 	// unexported; clients use user.NewContext and user.FromContext
	// 	// instead of using this key directly.
	// 	var userKey key = 0
	//
	// 	// NewContext returns a new Context that carries value u.
	// 	func NewContext(ctx context.Context, u *User) context.Context {
	// 		return context.WithValue(ctx, userKey, u)
	// 	}
	//
	// 	// FromContext returns the User value stored in ctx, if any.
	// 	func FromContext(ctx context.Context) (*User, bool) {
	// 		u, ok := ctx.Value(userKey).(*User)
	// 		return u, ok
	// 	}
	Value(key interface{}) interface{}
}

// Canceled is the error returned by Context.Err when the context is canceled.
var Canceled = errors.New("context canceled")

// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var DeadlineExceeded = errors.New("context deadline exceeded")

// An emptyCtx is never canceled, has no values, and has no deadline.  It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
	return
}

func (*emptyCtx) Done() <-chan struct{} {
	return nil
}

func (*emptyCtx) Err() error {
	return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
	return nil
}

func (e *emptyCtx) String() string {
	switch e {
	case background:
		return "context.Background"
	case todo:
		return "context.TODO"
	}
	return "unknown empty Context"
}

var (
	background = new(emptyCtx)
	todo       = new(emptyCtx)
)

// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline.  It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
	return background
}

// TODO returns a non-nil, empty Context.  Code should use context.TODO when
// it's unclear which Context to use or it's is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter).  TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
	return todo
}

// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := newCancelCtx(parent)
	propagateCancel(parent, &c)
	return &c, func() { c.cancel(true, Canceled) }
}

// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
	return cancelCtx{
		Context: parent,
		done:    make(chan struct{}),
	}
}

// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
	if parent.Done() == nil {
		return // parent is never canceled
	}
	if p, ok := parentCancelCtx(parent); ok {
		p.mu.Lock()
		if p.err != nil {
			// parent has already been canceled
			child.cancel(false, p.err)
		} else {
			if p.children == nil {
				p.children = make(map[canceler]bool)
			}
			p.children[child] = true
		}
		p.mu.Unlock()
	} else {
		go func() {
			select {
			case <-parent.Done():
				child.cancel(false, parent.Err())
			case <-child.Done():
			}
		}()
	}
}

// parentCancelCtx follows a chain of parent references until it finds a
// *cancelCtx.  This function understands how each of the concrete types in this
// package represents its parent.
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
	for {
		switch c := parent.(type) {
		case *cancelCtx:
			return c, true
		case *timerCtx:
			return &c.cancelCtx, true
		case *valueCtx:
			parent = c.Context
		default:
			return nil, false
		}
	}
}

// A canceler is a context type that can be canceled directly.  The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
	cancel(removeFromParent bool, err error)
	Done() <-chan struct{}
}

// A cancelCtx can be canceled.  When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
	Context

	done chan struct{} // closed by the first cancel call.

	mu       sync.Mutex
	children map[canceler]bool // set to nil by the first cancel call
	err      error             // set to non-nil by the first cancel call
}

func (c *cancelCtx) Done() <-chan struct{} {
	return c.done
}

func (c *cancelCtx) Err() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.err
}

func (c *cancelCtx) String() string {
	return fmt.Sprintf("%v.WithCancel", c.Context)
}

// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
	if err == nil {
		panic("context: internal error: missing cancel error")
	}
	c.mu.Lock()
	if c.err != nil {
		c.mu.Unlock()
		return // already canceled
	}
	c.err = err
	close(c.done)
	for child := range c.children {
		// NOTE: acquiring the child's lock while holding parent's lock.
		child.cancel(false, err)
	}
	c.children = nil
	c.mu.Unlock()

	if removeFromParent {
		if p, ok := parentCancelCtx(c.Context); ok {
			p.mu.Lock()
			if p.children != nil {
				delete(p.children, c)
			}
			p.mu.Unlock()
		}
	}
}

// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d.  If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent.  The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with the deadline
// timer, so code should call cancel as soon as the operations running in this
// Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
	if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
		// The current deadline is already sooner than the new one.
		return WithCancel(parent)
	}
	c := &timerCtx{
		cancelCtx: newCancelCtx(parent),
		deadline:  deadline,
	}
	propagateCancel(parent, c)
	d := deadline.Sub(time.Now())
	if d <= 0 {
		c.cancel(true, DeadlineExceeded) // deadline has already passed
		return c, func() { c.cancel(true, Canceled) }
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err == nil {
		c.timer = time.AfterFunc(d, func() {
			c.cancel(true, DeadlineExceeded)
		})
	}
	return c, func() { c.cancel(true, Canceled) }
}

// A timerCtx carries a timer and a deadline.  It embeds a cancelCtx to
// implement Done and Err.  It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
	cancelCtx
	timer *time.Timer // Under cancelCtx.mu.

	deadline time.Time
}

func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
	return c.deadline, true
}

func (c *timerCtx) String() string {
	return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
}

func (c *timerCtx) cancel(removeFromParent bool, err error) {
	c.cancelCtx.cancel(removeFromParent, err)
	c.mu.Lock()
	if c.timer != nil {
		c.timer.Stop()
		c.timer = nil
	}
	c.mu.Unlock()
}

// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with the deadline
// timer, so code should call cancel as soon as the operations running in this
// Context complete:
//
// 	func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// 		ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// 		defer cancel()  // releases resources if slowOperation completes before timeout elapses
// 		return slowOperation(ctx)
// 	}
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func WithValue(parent Context, key interface{}, val interface{}) Context {
	return &valueCtx{parent, key, val}
}

// A valueCtx carries a key-value pair.  It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
	Context
	key, val interface{}
}

func (c *valueCtx) String() string {
	return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
}

func (c *valueCtx) Value(key interface{}) interface{} {
	if c.key == key {
		return c.val
	}
	return c.Context.Value(key)
}
<|endoftext|>


<commit_before>package context

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/urandom/webfw/util"
)

var (
	ErrExpired        = errors.New("Session expired")
	ErrNotExist       = errors.New("Session does not exist")
	ErrCookieNotExist = errors.New("Session cookie does not exist")
)

/*
The Session represents persistent user data. Its Name field is how it is
identified.  The secret is used to salt the hmac, sent along the data
to the client as a cookie. The data is a base64 encoded string, containing
the session name and date showing when it was last used. The actual session
data is stored in the filesystem, in a directory specified by the Path
field. The data is serialized using encoding/gob, therefore any custom
data type should be registered with it. The MaxAge field specifies a duration,
after which an unused session will get cleared of its data and marked as
expired. It is also used as the max-age and expires fields of the session
cookie.
*/
type Session interface {
	Read(*http.Request, Context) error
	Write(http.ResponseWriter) error
	Name() string
	SetName(string)
	MaxAge() time.Duration
	SetMaxAge(time.Duration)
	CookieName() string
	SetCookieName(string)

	Set(interface{}, interface{})
	Get(interface{}) (interface{}, bool)
	GetAll() SessionValues
	DeleteAll()
	Delete(interface{})
	Flash(interface{}) (interface{}, bool)
	SetFlash(interface{}, interface{})
}

type SessionGenerator func(secret, cipher []byte, path string) Session

type SessionValues map[interface{}]interface{}
type FlashValues map[interface{}]interface{}

type session struct {
	Path string

	name       string
	maxAge     time.Duration
	values     SessionValues
	secret     []byte
	block      cipher.Block
	cookieName string
	mutex      sync.RWMutex
}

type fileData struct {
	Name       string
	MaxAge     time.Duration
	Values     SessionValues
	CookieName string
}

type contextKey string

var fsMutex sync.RWMutex

// NewSession creates a new session object.
func NewSession(secret, cipher []byte, path string) Session {
	s := &session{
		Path:       path,
		maxAge:     time.Hour,
		values:     SessionValues{},
		secret:     secret,
		cookieName: "session",
	}

	if cipher != nil && len(cipher) > 0 {
		if b, err := aes.NewCipher(cipher); err == nil {
			s.block = b
		} else {
			panic(err)
		}
	}

	return s
}

// CleanupSessions is a helper function for clearing all session data
// from the filesystem older than a given age. If the age is 0, all
// session data is removed.
func CleanupSessions(path string, age time.Duration) error {
	fsMutex.Lock()
	defer fsMutex.Unlock()

	files, err := ioutil.ReadDir(path)

	if err != nil {
		return err
	}

	for _, fi := range files {
		if age > 0 {
			min := time.Now().Add(-age).UnixNano()
			if fi.ModTime().UnixNano() >= min {
				continue
			}
		}

		if err := os.Remove(filepath.Join(path, fi.Name())); err != nil {
			return err
		}
	}

	return nil
}

// Read fetches the session from the cookie, and loads the session data from
// the filesystem. It may return a generic error due to the various read
// operations, or one of the following:
//  - ErrExpired - if its older than the set max-age. The session data
//    is removed
//  - ErrNotExist - if session data hasn't been found for this session
//  - ErrCookieNotExist - if a session cookie doesn't exist
func (s *session) Read(r *http.Request, c Context) error {
	d := http.Dir(s.Path)

	if cookie, err := r.Cookie(s.cookieName); err == nil {
		name, date, err := s.decodeName(cookie.Value)

		if err != nil {
			return err
		}

		var data *fileData
		var ok bool

		if data, ok = getSessionData(s.name, r, c); ok {
			s.fromData(data)
		} else {
			fsMutex.RLock()
			defer fsMutex.RUnlock()

			if file, err := d.Open(name); err == nil {
				defer file.Close()

				data = &fileData{}

				dec := gob.NewDecoder(file)

				if err := dec.Decode(data); err == nil {
					s.fromData(data)
				} else {
					return err
				}
			}
		}

		if data == nil {
			s.name = name

			return ErrNotExist
		} else {
			if s.maxAge != 0 {
				maxAge := time.Now().Add(-s.maxAge).Unix()

				if date < maxAge {
					s.DeleteAll()
					return ErrExpired
				}
			}
		}

		if c != nil {
			c.Set(r, contextKey(name), s.toData())
		}

		return nil
	} else {
		return ErrCookieNotExist
	}
}

// Write stores the session name in the session cookie along with the current
// date, and writes the session data to the filesystem, under the path which
// is stored in the session itself.
func (s *session) Write(w http.ResponseWriter) error {
	buf := util.BufferPool.GetBuffer()
	defer util.BufferPool.Put(buf)

	enc := gob.NewEncoder(buf)

	if err := enc.Encode(s.toData()); err != nil {
		return err
	}

	if filepath.Separator != '/' && strings.IndexRune(s.name, filepath.Separator) >= 0 ||
		strings.Contains(s.name, "\x00") {
		return errors.New("http: invalid character in file path")
	}

	val, date, err := s.encodeName()

	if err != nil {
		return err
	}

	var cookie *http.Cookie

	if s.maxAge > 0 {
		t := time.Unix(date, 0).Add(s.maxAge)
		cookie = &http.Cookie{Name: s.cookieName, Value: val, Path: "/", MaxAge: int(s.maxAge.Seconds()), Expires: t}
	} else {
		cookie = &http.Cookie{Name: s.cookieName, Value: val, Path: "/"}
	}
	http.SetCookie(w, cookie)

	fsMutex.Lock()
	defer fsMutex.Unlock()

	if err := os.MkdirAll(s.Path, os.FileMode(0700)); err != nil {
		return err
	}

	f, err := os.OpenFile(filepath.Join(s.Path, filepath.FromSlash(path.Clean("/"+s.name))),
		os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)

	if err != nil {
		return err
	}

	f.Write(buf.Bytes())
	f.Close()

	return nil
}

// Name returns the name of the session
func (s *session) Name() string {
	return s.name
}

// SetName sets a new name for the session
func (s *session) SetName(name string) {
	s.name = name
}

// MaxAge returns the max-age of the session
func (s *session) MaxAge() time.Duration {
	return s.maxAge
}

// SetMaxAge sets a new max age for the session
func (s *session) SetMaxAge(maxAge time.Duration) {
	s.maxAge = maxAge
}

// CookieName returns the cookie name, used to store the session name in
// the client
func (s *session) CookieName() string {
	return s.cookieName
}

// SetCookieName sets a new cookie name for the session
func (s *session) SetCookieName(name string) {
	s.cookieName = name
}

// Set stores a key-value pair in the session.
func (s *session) Set(key interface{}, val interface{}) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	s.values[key] = val
}

// Get fetches a value for a given key.
func (s *session) Get(key interface{}) (interface{}, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	if val, ok := s.values[key]; ok {
		return val, ok
	}
	return nil, false
}

// GetAll returns all values stored in the session
func (s *session) GetAll() SessionValues {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	return s.values
}

// DeleteAll removes all key-value pairs from the session.
func (s *session) DeleteAll() {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	s.values = SessionValues{}
}

// Delete removes a value for a given key.
func (s *session) Delete(key interface{}) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	delete(s.values, key)
}

// Flash gets a flash value for a given key from the session.  Flash values
// are temporary values that are removed when they are fetched.
func (s *session) Flash(key interface{}) (interface{}, bool) {
	var val interface{}

	if flash, ok := s.Get(contextKey("flashValues")); ok {
		flashValues := flash.(FlashValues)

		if val, ok = flashValues[key]; ok {
			delete(flashValues, key)

			return val, ok
		}
	}

	return val, false
}

// SetFlash stores a flash value under a given key.
func (s *session) SetFlash(key interface{}, value interface{}) {
	var flashValues FlashValues

	if val, ok := s.Get(contextKey("flashValues")); ok {
		flashValues = val.(FlashValues)
	} else {
		flashValues = FlashValues{}
	}

	flashValues[key] = value

	s.Set(contextKey("flashValues"), flashValues)
}

func (s *session) toData() *fileData {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	return &fileData{
		Name:       s.name,
		MaxAge:     s.maxAge,
		Values:     s.values,
		CookieName: s.cookieName,
	}
}

func (s *session) fromData(data *fileData) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	s.name = data.Name
	s.maxAge = data.MaxAge
	s.values = data.Values
	s.cookieName = data.CookieName
}

func (s *session) decodeName(data string) (string, int64, error) {
	decoded, err := base64.URLEncoding.DecodeString(data)

	if err != nil {
		return "", 0, err
	}

	parts := bytes.SplitN(decoded, []byte("|"), 3)
	if len(parts) != 3 {
		return "", 0, errors.New("Not enough cookie parts")
	}

	t1, err := strconv.ParseInt(string(parts[1]), 10, 64)

	if err != nil {
		return "", 0, err
	}

	sig := parts[2]

	if s.block != nil {
		size := s.block.BlockSize()
		if len(sig) > size {
			key := sig[:size]
			sig = sig[size:]

			ctr := cipher.NewCTR(s.block, key)
			ctr.XORKeyStream(sig, sig)
		} else {
			return "", 0, errors.New("Invalid cookie encryption part")
		}
	}

	if !s.checkSignature(sig, parts[0], t1) {
		return "", 0, errors.New("Signatures don't match")
	}

	return string(parts[0]), t1, nil
}

func (s *session) encodeName() (string, int64, error) {
	now := time.Now().Unix()

	sig, err := createSignature(s.cookieName, []byte(s.name), s.secret, now)

	if err != nil {
		return "", 0, err
	}

	if s.block != nil {
		if key, err := randomData(s.block.BlockSize()); err == nil {
			ctr := cipher.NewCTR(s.block, key)
			ctr.XORKeyStream(sig, sig)

			sig = append(key, sig...)
		} else {
			return "", 0, err
		}
	}

	message := []byte(fmt.Sprintf("%s|%d|%s", s.name, now, sig))

	encoded := base64.URLEncoding.EncodeToString(message)

	return string(encoded), now, nil
}

func (s *session) checkSignature(signature, name []byte, date int64) bool {
	expected, err := createSignature(s.cookieName, name, s.secret, date)
	if err != nil {
		return false
	}

	return hmac.Equal(signature, expected)
}

func createSignature(cookieName string, name, secret []byte, date int64) ([]byte, error) {
	hm := hmac.New(sha256.New, secret)

	message := []byte(fmt.Sprintf("%s|%s|%d", cookieName, name, date))

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

	mac := hm.Sum(nil)

	return mac, nil
}

func getSessionData(name string, r *http.Request, c Context) (*fileData, bool) {
	if c != nil {
		if sd, ok := c.Get(r, contextKey(name)); ok {
			if s, ok := sd.(*fileData); ok {
				return s, true
			}
		}
	}

	return nil, false
}

func randomData(size int) ([]byte, error) {
	data := make([]byte, size)
	if _, err := rand.Read(data); err != nil {
		return nil, err
	}
	return data, nil
}
<commit_msg>do not set the cookie if a response writer is not provided<commit_after>package context

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/urandom/webfw/util"
)

var (
	ErrExpired        = errors.New("Session expired")
	ErrNotExist       = errors.New("Session does not exist")
	ErrCookieNotExist = errors.New("Session cookie does not exist")
)

/*
The Session represents persistent user data. Its Name field is how it is
identified.  The secret is used to salt the hmac, sent along the data
to the client as a cookie. The data is a base64 encoded string, containing
the session name and date showing when it was last used. The actual session
data is stored in the filesystem, in a directory specified by the Path
field. The data is serialized using encoding/gob, therefore any custom
data type should be registered with it. The MaxAge field specifies a duration,
after which an unused session will get cleared of its data and marked as
expired. It is also used as the max-age and expires fields of the session
cookie.
*/
type Session interface {
	Read(*http.Request, Context) error
	Write(http.ResponseWriter) error
	Name() string
	SetName(string)
	MaxAge() time.Duration
	SetMaxAge(time.Duration)
	CookieName() string
	SetCookieName(string)

	Set(interface{}, interface{})
	Get(interface{}) (interface{}, bool)
	GetAll() SessionValues
	DeleteAll()
	Delete(interface{})
	Flash(interface{}) (interface{}, bool)
	SetFlash(interface{}, interface{})
}

type SessionGenerator func(secret, cipher []byte, path string) Session

type SessionValues map[interface{}]interface{}
type FlashValues map[interface{}]interface{}

type session struct {
	Path string

	name       string
	maxAge     time.Duration
	values     SessionValues
	secret     []byte
	block      cipher.Block
	cookieName string
	mutex      sync.RWMutex
}

type fileData struct {
	Name       string
	MaxAge     time.Duration
	Values     SessionValues
	CookieName string
}

type contextKey string

var fsMutex sync.RWMutex

// NewSession creates a new session object.
func NewSession(secret, cipher []byte, path string) Session {
	s := &session{
		Path:       path,
		maxAge:     time.Hour,
		values:     SessionValues{},
		secret:     secret,
		cookieName: "session",
	}

	if cipher != nil && len(cipher) > 0 {
		if b, err := aes.NewCipher(cipher); err == nil {
			s.block = b
		} else {
			panic(err)
		}
	}

	return s
}

// CleanupSessions is a helper function for clearing all session data
// from the filesystem older than a given age. If the age is 0, all
// session data is removed.
func CleanupSessions(path string, age time.Duration) error {
	fsMutex.Lock()
	defer fsMutex.Unlock()

	files, err := ioutil.ReadDir(path)

	if err != nil {
		return err
	}

	for _, fi := range files {
		if age > 0 {
			min := time.Now().Add(-age).UnixNano()
			if fi.ModTime().UnixNano() >= min {
				continue
			}
		}

		if err := os.Remove(filepath.Join(path, fi.Name())); err != nil {
			return err
		}
	}

	return nil
}

// Read fetches the session from the cookie, and loads the session data from
// the filesystem. It may return a generic error due to the various read
// operations, or one of the following:
//  - ErrExpired - if its older than the set max-age. The session data
//    is removed
//  - ErrNotExist - if session data hasn't been found for this session
//  - ErrCookieNotExist - if a session cookie doesn't exist
func (s *session) Read(r *http.Request, c Context) error {
	d := http.Dir(s.Path)

	if cookie, err := r.Cookie(s.cookieName); err == nil {
		name, date, err := s.decodeName(cookie.Value)

		if err != nil {
			return err
		}

		var data *fileData
		var ok bool

		if data, ok = getSessionData(s.name, r, c); ok {
			s.fromData(data)
		} else {
			fsMutex.RLock()
			defer fsMutex.RUnlock()

			if file, err := d.Open(name); err == nil {
				defer file.Close()

				data = &fileData{}

				dec := gob.NewDecoder(file)

				if err := dec.Decode(data); err == nil {
					s.fromData(data)
				} else {
					return err
				}
			}
		}

		if data == nil {
			s.name = name

			return ErrNotExist
		} else {
			if s.maxAge != 0 {
				maxAge := time.Now().Add(-s.maxAge).Unix()

				if date < maxAge {
					s.DeleteAll()
					return ErrExpired
				}
			}
		}

		if c != nil {
			c.Set(r, contextKey(name), s.toData())
		}

		return nil
	} else {
		return ErrCookieNotExist
	}
}

// Write stores the session name in the session cookie along with the current
// date, and writes the session data to the filesystem, under the path which
// is stored in the session itself.
func (s *session) Write(w http.ResponseWriter) error {
	buf := util.BufferPool.GetBuffer()
	defer util.BufferPool.Put(buf)

	enc := gob.NewEncoder(buf)

	if err := enc.Encode(s.toData()); err != nil {
		return err
	}

	if filepath.Separator != '/' && strings.IndexRune(s.name, filepath.Separator) >= 0 ||
		strings.Contains(s.name, "\x00") {
		return errors.New("http: invalid character in file path")
	}

	val, date, err := s.encodeName()

	if err != nil {
		return err
	}

	if w != nil {
		var cookie *http.Cookie

		if s.maxAge > 0 {
			t := time.Unix(date, 0).Add(s.maxAge)
			cookie = &http.Cookie{Name: s.cookieName, Value: val, Path: "/", MaxAge: int(s.maxAge.Seconds()), Expires: t}
		} else {
			cookie = &http.Cookie{Name: s.cookieName, Value: val, Path: "/"}
		}
		http.SetCookie(w, cookie)
	}

	fsMutex.Lock()
	defer fsMutex.Unlock()

	if err := os.MkdirAll(s.Path, os.FileMode(0700)); err != nil {
		return err
	}

	f, err := os.OpenFile(filepath.Join(s.Path, filepath.FromSlash(path.Clean("/"+s.name))),
		os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)

	if err != nil {
		return err
	}

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

	f.Close()

	return nil
}

// Name returns the name of the session
func (s *session) Name() string {
	return s.name
}

// SetName sets a new name for the session
func (s *session) SetName(name string) {
	s.name = name
}

// MaxAge returns the max-age of the session
func (s *session) MaxAge() time.Duration {
	return s.maxAge
}

// SetMaxAge sets a new max age for the session
func (s *session) SetMaxAge(maxAge time.Duration) {
	s.maxAge = maxAge
}

// CookieName returns the cookie name, used to store the session name in
// the client
func (s *session) CookieName() string {
	return s.cookieName
}

// SetCookieName sets a new cookie name for the session
func (s *session) SetCookieName(name string) {
	s.cookieName = name
}

// Set stores a key-value pair in the session.
func (s *session) Set(key interface{}, val interface{}) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	s.values[key] = val
}

// Get fetches a value for a given key.
func (s *session) Get(key interface{}) (interface{}, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	if val, ok := s.values[key]; ok {
		return val, ok
	}
	return nil, false
}

// GetAll returns all values stored in the session
func (s *session) GetAll() SessionValues {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	return s.values
}

// DeleteAll removes all key-value pairs from the session.
func (s *session) DeleteAll() {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	s.values = SessionValues{}
}

// Delete removes a value for a given key.
func (s *session) Delete(key interface{}) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	delete(s.values, key)
}

// Flash gets a flash value for a given key from the session.  Flash values
// are temporary values that are removed when they are fetched.
func (s *session) Flash(key interface{}) (interface{}, bool) {
	var val interface{}

	if flash, ok := s.Get(contextKey("flashValues")); ok {
		flashValues := flash.(FlashValues)

		if val, ok = flashValues[key]; ok {
			delete(flashValues, key)

			return val, ok
		}
	}

	return val, false
}

// SetFlash stores a flash value under a given key.
func (s *session) SetFlash(key interface{}, value interface{}) {
	var flashValues FlashValues

	if val, ok := s.Get(contextKey("flashValues")); ok {
		flashValues = val.(FlashValues)
	} else {
		flashValues = FlashValues{}
	}

	flashValues[key] = value

	s.Set(contextKey("flashValues"), flashValues)
}

func (s *session) toData() *fileData {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	return &fileData{
		Name:       s.name,
		MaxAge:     s.maxAge,
		Values:     s.values,
		CookieName: s.cookieName,
	}
}

func (s *session) fromData(data *fileData) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	s.name = data.Name
	s.maxAge = data.MaxAge
	s.values = data.Values
	s.cookieName = data.CookieName
}

func (s *session) decodeName(data string) (string, int64, error) {
	decoded, err := base64.URLEncoding.DecodeString(data)

	if err != nil {
		return "", 0, err
	}

	parts := bytes.SplitN(decoded, []byte("|"), 3)
	if len(parts) != 3 {
		return "", 0, errors.New("Not enough cookie parts")
	}

	t1, err := strconv.ParseInt(string(parts[1]), 10, 64)

	if err != nil {
		return "", 0, err
	}

	sig := parts[2]

	if s.block != nil {
		size := s.block.BlockSize()
		if len(sig) > size {
			key := sig[:size]
			sig = sig[size:]

			ctr := cipher.NewCTR(s.block, key)
			ctr.XORKeyStream(sig, sig)
		} else {
			return "", 0, errors.New("Invalid cookie encryption part")
		}
	}

	if !s.checkSignature(sig, parts[0], t1) {
		return "", 0, errors.New("Signatures don't match")
	}

	return string(parts[0]), t1, nil
}

func (s *session) encodeName() (string, int64, error) {
	now := time.Now().Unix()

	sig, err := createSignature(s.cookieName, []byte(s.name), s.secret, now)

	if err != nil {
		return "", 0, err
	}

	if s.block != nil {
		if key, err := randomData(s.block.BlockSize()); err == nil {
			ctr := cipher.NewCTR(s.block, key)
			ctr.XORKeyStream(sig, sig)

			sig = append(key, sig...)
		} else {
			return "", 0, err
		}
	}

	message := []byte(fmt.Sprintf("%s|%d|%s", s.name, now, sig))

	encoded := base64.URLEncoding.EncodeToString(message)

	return string(encoded), now, nil
}

func (s *session) checkSignature(signature, name []byte, date int64) bool {
	expected, err := createSignature(s.cookieName, name, s.secret, date)
	if err != nil {
		return false
	}

	return hmac.Equal(signature, expected)
}

func createSignature(cookieName string, name, secret []byte, date int64) ([]byte, error) {
	hm := hmac.New(sha256.New, secret)

	message := []byte(fmt.Sprintf("%s|%s|%d", cookieName, name, date))

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

	mac := hm.Sum(nil)

	return mac, nil
}

func getSessionData(name string, r *http.Request, c Context) (*fileData, bool) {
	if c != nil {
		if sd, ok := c.Get(r, contextKey(name)); ok {
			if s, ok := sd.(*fileData); ok {
				return s, true
			}
		}
	}

	return nil, false
}

func randomData(size int) ([]byte, error) {
	data := make([]byte, size)
	if _, err := rand.Read(data); err != nil {
		return nil, err
	}
	return data, nil
}
<|endoftext|>