<commit_before>package engine

import (
	"encoding/json"
	"errors"
	"math/rand"
)

const (
	minAreaWidth  = 10
	minAreaHeight = 10
)

type Area struct {
	width  uint8
	height uint8
}

type ErrInvalidAreaSize struct {
	Width  uint8
	Height uint8
}

func (e *ErrInvalidAreaSize) Error() string {
	return "invalid area size"
}

func NewArea(width, height uint8) (Area, error) {
	if width*height == 0 {
		return Area{}, &ErrInvalidAreaSize{
			Width:  width,
			Height: height,
		}
	}

	return Area{
		width:  width,
		height: height,
	}, nil
}

func MustArea(width, height uint8) Area {
	area, err := NewArea(width, height)
	if err != nil {
		panic(err)
	}
	return area
}

func NewUsefulArea(width, height uint8) (Area, error) {
	if width < minAreaWidth || height < minAreaHeight {
		return Area{}, errors.New("try to add useless area with extra small size")
	}

	return Area{
		width:  width,
		height: height,
	}, nil
}

// Size returns area size
func (a Area) Size() uint16 {
	return uint16(a.width) * uint16(a.height)
}

func (a Area) Width() uint8 {
	return a.width
}

func (a Area) Height() uint8 {
	return a.height
}

func (a Area) Contains(dot Dot) bool {
	// TODO: Rename method to ContainsDot.
	return a.width > dot.X && a.height > dot.Y
}

func (a Area) ContainsRect(rect Rect) bool {
	return a.width > rect.w+rect.x && a.height > rect.h+rect.y
}

func (a Area) ContainsLocation(location Location) bool {
	for _, dot := range location {
		if a.width <= dot.X || a.height <= dot.Y {
			return false
		}
	}
	return true
}

// NewRandomDot generates random dot on area with starting coordinates X and Y
func (a Area) NewRandomDot(x, y uint8) Dot {
	return Dot{
		X: x + uint8(rand.Intn(int(a.width))),
		Y: y + uint8(rand.Intn(int(a.height))),
	}
}

func (a Area) NewRandomRect(rw, rh, sx, sy uint8) (*Rect, error) {
	if rw > a.width || rh > a.height {
		return nil, errors.New("cannot get random rect on square: invalid Width or Height")
	}

	var r = &Rect{
		x: sx,
		y: sy,
		w: rw,
		h: rh,
	}

	if a.width-r.w > 0 {
		r.x = uint8(rand.Intn(int(a.width - r.w)))
	}

	if a.height-r.h > 0 {
		r.y = uint8(rand.Intn(int(a.height - r.h)))
	}

	return r, nil
}

type ErrNavigation struct {
	Err error
}

func (e *ErrNavigation) Error() string {
	return "navigation error: " + e.Err.Error()
}

type ErrAreaNotContainsDot struct {
	Dot Dot
}

func (e *ErrAreaNotContainsDot) Error() string {
	return "area does not contain dot: " + e.Dot.String()
}

// Navigate calculates and returns dot placed on distance dis dots from passed dot in direction dir
func (a Area) Navigate(dot Dot, dir Direction, dis uint8) (Dot, error) {
	// If distance is zero return passed dot
	if dis == 0 {
		return dot, nil
	}

	// Area must contain passed dot
	if !a.Contains(dot) {
		return Dot{}, &ErrNavigation{
			Err: &ErrAreaNotContainsDot{
				Dot: dot,
			},
		}
	}

	switch dir {
	case DirectionNorth, DirectionSouth:
		if dis > a.height {
			dis %= a.height
		}

		// North
		if dir == DirectionNorth {
			if dis > dot.Y {
				return Dot{
					X: dot.X,
					Y: a.height - dis + dot.Y,
				}, nil
			}
			return Dot{
				X: dot.X,
				Y: dot.Y - dis,
			}, nil
		}

		// South
		if a.height > dot.Y+dis {
			return Dot{
				X: dot.X,
				Y: dot.Y + dis,
			}, nil
		}
		return Dot{
			X: dot.X,
			Y: dis - a.height + dot.Y,
		}, nil

	case DirectionWest, DirectionEast:
		if dis > a.width {
			dis %= a.width
		}

		// East
		if dir == DirectionEast {
			if a.width > dot.X+dis {
				return Dot{
					X: dot.X + dis,
					Y: dot.Y,
				}, nil
			}
			return Dot{
				X: dis - a.width + dot.X,
				Y: dot.Y,
			}, nil
		}

		// West
		if dis > dot.X {
			return Dot{
				X: a.width - dis + dot.X,
				Y: dot.Y,
			}, nil
		}
		return Dot{
			X: dot.X - dis,
			Y: dot.Y,
		}, nil
	}

	return Dot{}, &ErrNavigation{
		Err: &ErrInvalidDirection{
			Direction: dir,
		},
	}
}

// Implementing json.Marshaler interface
func (a Area) MarshalJSON() ([]byte, error) {
	return json.Marshal([]uint8{
		a.width,
		a.height,
	})
}
<commit_msg>Create method Dots for engine.Area<commit_after>package engine

import (
	"encoding/json"
	"errors"
	"math/rand"
)

const (
	minAreaWidth  = 10
	minAreaHeight = 10
)

type Area struct {
	width  uint8
	height uint8
}

type ErrInvalidAreaSize struct {
	Width  uint8
	Height uint8
}

func (e *ErrInvalidAreaSize) Error() string {
	return "invalid area size"
}

func NewArea(width, height uint8) (Area, error) {
	if width*height == 0 {
		return Area{}, &ErrInvalidAreaSize{
			Width:  width,
			Height: height,
		}
	}

	return Area{
		width:  width,
		height: height,
	}, nil
}

func MustArea(width, height uint8) Area {
	area, err := NewArea(width, height)
	if err != nil {
		panic(err)
	}
	return area
}

func NewUsefulArea(width, height uint8) (Area, error) {
	if width < minAreaWidth || height < minAreaHeight {
		return Area{}, errors.New("try to add useless area with extra small size")
	}

	return Area{
		width:  width,
		height: height,
	}, nil
}

// Size returns area size
func (a Area) Size() uint16 {
	return uint16(a.width) * uint16(a.height)
}

func (a Area) Width() uint8 {
	return a.width
}

func (a Area) Height() uint8 {
	return a.height
}

func (a Area) Dots() []Dot {
	var x, y uint8
	var dots = make([]Dot, 0, uint16(a.width)*uint16(a.height))

	for x = 0; x < a.width; x++ {
		for y = 0; y < a.height; y++ {
			dots = append(dots, Dot{
				X: x,
				Y: y,
			})
		}
	}

	return dots
}

func (a Area) ContainsDot(dot Dot) bool {
	return a.width > dot.X && a.height > dot.Y
}

func (a Area) ContainsRect(rect Rect) bool {
	return a.width > rect.w+rect.x && a.height > rect.h+rect.y
}

func (a Area) ContainsLocation(location Location) bool {
	for _, dot := range location {
		if a.width <= dot.X || a.height <= dot.Y {
			return false
		}
	}
	return true
}

// NewRandomDot generates random dot on area with starting coordinates X and Y
func (a Area) NewRandomDot(x, y uint8) Dot {
	return Dot{
		X: x + uint8(rand.Intn(int(a.width))),
		Y: y + uint8(rand.Intn(int(a.height))),
	}
}

func (a Area) NewRandomRect(rw, rh, sx, sy uint8) (*Rect, error) {
	if rw > a.width || rh > a.height {
		return nil, errors.New("cannot get random rect on square: invalid Width or Height")
	}

	var r = &Rect{
		x: sx,
		y: sy,
		w: rw,
		h: rh,
	}

	if a.width-r.w > 0 {
		r.x = uint8(rand.Intn(int(a.width - r.w)))
	}

	if a.height-r.h > 0 {
		r.y = uint8(rand.Intn(int(a.height - r.h)))
	}

	return r, nil
}

type ErrNavigation struct {
	Err error
}

func (e *ErrNavigation) Error() string {
	return "navigation error: " + e.Err.Error()
}

type ErrAreaNotContainsDot struct {
	Dot Dot
}

func (e *ErrAreaNotContainsDot) Error() string {
	return "area does not contain dot: " + e.Dot.String()
}

// Navigate calculates and returns dot placed on distance dis dots from passed dot in direction dir
func (a Area) Navigate(dot Dot, dir Direction, dis uint8) (Dot, error) {
	// If distance is zero return passed dot
	if dis == 0 {
		return dot, nil
	}

	// Area must contain passed dot
	if !a.ContainsDot(dot) {
		return Dot{}, &ErrNavigation{
			Err: &ErrAreaNotContainsDot{
				Dot: dot,
			},
		}
	}

	switch dir {
	case DirectionNorth, DirectionSouth:
		if dis > a.height {
			dis %= a.height
		}

		// North
		if dir == DirectionNorth {
			if dis > dot.Y {
				return Dot{
					X: dot.X,
					Y: a.height - dis + dot.Y,
				}, nil
			}
			return Dot{
				X: dot.X,
				Y: dot.Y - dis,
			}, nil
		}

		// South
		if a.height > dot.Y+dis {
			return Dot{
				X: dot.X,
				Y: dot.Y + dis,
			}, nil
		}
		return Dot{
			X: dot.X,
			Y: dis - a.height + dot.Y,
		}, nil

	case DirectionWest, DirectionEast:
		if dis > a.width {
			dis %= a.width
		}

		// East
		if dir == DirectionEast {
			if a.width > dot.X+dis {
				return Dot{
					X: dot.X + dis,
					Y: dot.Y,
				}, nil
			}
			return Dot{
				X: dis - a.width + dot.X,
				Y: dot.Y,
			}, nil
		}

		// West
		if dis > dot.X {
			return Dot{
				X: a.width - dis + dot.X,
				Y: dot.Y,
			}, nil
		}
		return Dot{
			X: dot.X - dis,
			Y: dot.Y,
		}, nil
	}

	return Dot{}, &ErrNavigation{
		Err: &ErrInvalidDirection{
			Direction: dir,
		},
	}
}

// Implementing json.Marshaler interface
func (a Area) MarshalJSON() ([]byte, error) {
	return json.Marshal([]uint8{
		a.width,
		a.height,
	})
}
<|endoftext|>


<commit_before>package rsync

import (
	"fmt"
	"os"
	"testing"
	"time"

	"github.com/chrislusf/glow/util"
)

func TestNormalHttpCopy(t *testing.T) {

	util.SetupHttpClient(nil)

	files := []string{
		os.Args[0], "http_server.go", "../scheduler/scheduler.go", "fetch_url.go",
	}

	rsyncServer, err := NewRsyncServer(":0", files[0], files[1:])
	if err != nil {
		t.Fatalf("Failed to start local server: %v", err)
	}
	rsyncServer.Start()

	err = FetchFilesTo(fmt.Sprintf("localhost:%d", rsyncServer.Port), "/tmp")
	if err != nil {
		fmt.Printf("pausing localhost:%d\n", rsyncServer.Port)
		time.Sleep(time.Minute)
		t.Fatalf("Failed to download file: %v", err)
	}

	t.Logf("file downloading works ok.")
}
<commit_msg>Quickfix to make tests compile<commit_after>package rsync

import (
	"fmt"
	"os"
	"testing"
	"time"

	"github.com/chrislusf/glow/util"
)

func TestNormalHttpCopy(t *testing.T) {

	util.SetupHttpClient(nil)

	files := []string{
		os.Args[0], "http_server.go", "../scheduler/scheduler.go", "fetch_url.go",
	}

	rsyncServer, err := NewRsyncServer(files[0], files[1:])
	if err != nil {
		t.Fatalf("Failed to start local server: %v", err)
	}
	rsyncServer.StartRsyncServer(nil, ":0")

	err = FetchFilesTo(fmt.Sprintf("localhost:%d", rsyncServer.Port), "/tmp")
	if err != nil {
		fmt.Printf("pausing localhost:%d\n", rsyncServer.Port)
		time.Sleep(time.Minute)
		t.Fatalf("Failed to download file: %v", err)
	}

	t.Logf("file downloading works ok.")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"os"
	"path"
	"time"
)

// Cache keeps track of last-check, also intended to cache tokens in the future
// This is *NOT* thread safe and does not use any file locks
// FIXME: Use locks for the library
type Cache struct {
	filename        string
	LastUpdateCheck time.Time `json:"last-check"`
}

func defaultCacheFilename() (string, error) {
	bd, err := CarinaCredentialsBaseDir()
	if err != nil {
		return "", err
	}
	return path.Join(bd, "cache.json"), nil
}

// Read the on disk cache
func (cache *Cache) read() error {
	f, err := os.OpenFile(cache.filename, os.O_RDONLY, 0666)
	if err != nil {
		return err
	}
	err = json.NewDecoder(f).Decode(cache)
	if err != nil {
		return err
	}
	err = f.Close()
	return err
}

// write will put the current version of the cache on disk at cache.filename
// creating it if it does not exist
func (cache *Cache) write() error {
	f, err := os.OpenFile(cache.filename, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	err = json.NewEncoder(f).Encode(cache)
	if err != nil {
		return err
	}
	err = f.Close()
	return err
}

func loadCache(filename string) (cache *Cache, err error) {
	cache = new(Cache)

	cache.filename = filename
	if err != nil {
		return nil, err
	}

	_, err = os.Stat(cache.filename)

	if os.IsNotExist(err) {
		return cache, cache.write()
	} else if err != nil {
		return nil, err
	}

	return cache, cache.read()
}

func (cache *Cache) updateLastCheck(t time.Time) error {
	err := cache.read()
	if err != nil {
		return err
	}
	cache.LastUpdateCheck = t
	return cache.write()
}
<commit_msg>Use a mutex when doing an update.<commit_after>package main

import (
	"encoding/json"
	"os"
	"path"
	"sync"
	"time"
)

// Cache keeps track of last-check, also intended to cache tokens in the future
// Not all methods are thread safe and it does not use any file locks
type Cache struct {
	sync.Mutex
	filename        string
	LastUpdateCheck time.Time `json:"last-check"`
}

func defaultCacheFilename() (string, error) {
	bd, err := CarinaCredentialsBaseDir()
	if err != nil {
		return "", err
	}
	return path.Join(bd, "cache.json"), nil
}

// Read the on disk cache
func (cache *Cache) read() error {
	f, err := os.OpenFile(cache.filename, os.O_RDONLY, 0666)
	if err != nil {
		return err
	}
	err = json.NewDecoder(f).Decode(cache)
	if err != nil {
		return err
	}
	err = f.Close()
	return err
}

// write will put the current version of the cache on disk at cache.filename
// creating it if it does not exist
func (cache *Cache) write() error {
	f, err := os.OpenFile(cache.filename, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	err = json.NewEncoder(f).Encode(cache)
	if err != nil {
		return err
	}
	err = f.Close()
	return err
}

func loadCache(filename string) (cache *Cache, err error) {
	cache = new(Cache)

	cache.filename = filename
	if err != nil {
		return nil, err
	}

	_, err = os.Stat(cache.filename)

	if os.IsNotExist(err) {
		return cache, cache.write()
	} else if err != nil {
		return nil, err
	}

	return cache, cache.read()
}

func (cache *Cache) updateLastCheck(t time.Time) error {
	cache.Lock()
	defer cache.Unlock()

	err := cache.read()
	if err != nil {
		return err
	}
	cache.LastUpdateCheck = t
	return cache.write()
}
<|endoftext|>


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

package main

import (
	"io"
	"log"
	"time"
)

type page struct {
	n      offset
	body   []byte
	expire time.Time
	cached bool
}

func newPage(n offset, body []byte) *page {
	return &page{
		n:    n,
		body: body,
	}
}

func (p *page) WriteTo(w io.Writer) (int, error) {
	n, err := w.Write(p.body)
	return n, err
}

type group string

type entry struct {
	deadline time.Time
	data     []byte
}

func newEntry(data []byte, d time.Duration) *entry {
	return &entry{
		deadline: time.Now().Add(d),
		data:     data,
	}
}

func (ce *entry) invalid(t time.Time) bool {
	return !ce.deadline.After(t)
}

func (ce *entry) asPage(n offset) *page {
	p := newPage(n, ce.data)
	p.expire = ce.deadline
	return p
}

type entries struct {
	ents map[group]map[offset]*entry
}

func newEntries() *entries {
	return &entries{
		ents: make(map[group]map[offset]*entry),
	}
}

func (e *entries) get(cg group, n offset) (*entry, bool) {
	pages, ok := e.ents[cg]
	if !ok {
		return nil, false
	}
	ce, ok := pages[n]
	return ce, ok
}

func (e *entries) put(cg group, n offset, ce *entry) {
	_, ok := e.ents[cg]
	if !ok {
		e.ents[cg] = make(map[offset]*entry)
	}
	e.ents[cg][n] = ce
}

func (e *entries) has(cg group, n offset) bool {
	_, ok := e.ents[cg]
	if !ok {
		return false
	}
	_, ok = e.ents[cg][n]
	return ok
}

// remove assumes the entry exists
func (e *entries) remove(cg group, n offset) {
	delete(e.ents[cg], n)
	if len(e.ents[cg]) == 0 {
		delete(e.ents, cg)
	}
}

func (e *entries) gc(t time.Time) {
	for cg, ents := range e.ents {
		for n := range ents {
			if ents[n].invalid(t) {
				e.remove(cg, n)
			}
		}
	}
}

type cacheFunc func() error

type cache struct {
	entries *entries
	waits   *waiters
	fetcher *fetcher
	config  *config
	events  chan cacheFunc
}

func newCache(f *fetcher, cf *config) *cache {
	c := &cache{
		fetcher: f,
		config:  cf,
		events:  make(chan cacheFunc),
		entries: newEntries(),
		waits:   newWaiters(),
	}
	go c.gc(cf.gcpause)
	go c.run()
	return c
}

func (c *cache) run() {
	for f := range c.events {
		if err := f(); err != nil {
			log.Print("cache: ", err)
		}
	}
}

func (c *cache) gc(d time.Duration) {
	done := make(chan struct{})
	for {
		time.Sleep(d)
		c.events <- func() error {
			c.entries.gc(time.Now())
			done <- struct{}{}
			return nil
		}
		<-done
	}
}

// put inserts a page into the cache (after it was fetched).
func (c *cache) put(cg group, p *page, err error) {
	c.events <- func() error {
		ce := newEntry(p.body, c.config.lifetime)
		c.entries.put(cg, p.n, ce)
		debug("added page %s/%d", cg, p.n)
		// If there were waiters, signal that the wait is over
		c.waits.done(cg, p.n)
		return err
	}
}

// prefetch requests pages (n-m, n) and (n, n+m) if not already fetched
func (c *cache) prefetch(cg group, n, m int) {
	i := n - m
	if i < 0 {
		i = 0
	}
	for ; i < n+m; i++ {
		if i == n {
			// just fetched
			continue
		}
		off := offset(i*c.config.incr)
		if c.entries.has(cg, off) || c.waits.has(cg, off) {
			// already fetched or requested
			continue
		}
		c.waits.wait(cg, off)
		res := newResource(c.config.tmpl, cg, off)
		c.fetcher.request(newJob(res, c))
	}
}

func (c *cache) request(cg group, n int) chan struct{} {
	off := offset(n*c.config.incr)
	wait := c.waits.wait(cg, off)
	res := newResource(c.config.tmpl, cg, off)
	c.fetcher.request(newJob(res, c))
	c.prefetch(cg, n, c.config.npref)
	return wait
}

func (c *cache) get(cg group, n int) *page {
	var (
		page *page
		wait chan struct{}
	)
	cached := true
	requested := make(chan struct{})
	off := offset(n*c.config.incr)
	debug("%s/%d: requesting from cache", cg, off)
	for {
		wait = nil
		c.events <- func() error {
			defer func() { requested <- struct{}{} }()
			var now time.Time
			ce, ok := c.entries.get(cg, off)
			if ok {
				now = time.Now()
			}
			if !ok || ce.invalid(now) {
				debug("%s/%d: not cached, requested", cg, off)
				wait = c.request(cg, n)
				return nil
			} else {
				debug("%s/%d: found", cg, off)
				c.prefetch(cg, n, c.config.npref)
			}
			page = ce.asPage(off)
			return nil
		}
		<-requested
		// content was already in cache, return it
		if wait == nil {
			page.cached = cached
			return page
		}
		// We needed to request the object, it was not cached
		cached = false
		// content is being fetched, wait and try to get again
		<-wait
	}
	// unreacheable
	return nil
}
<commit_msg>fix vet suggestions<commit_after>// Copyright 2017 Giulio Iotti. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"io"
	"log"
	"time"
)

type page struct {
	n      offset
	body   []byte
	expire time.Time
	cached bool
}

func newPage(n offset, body []byte) *page {
	return &page{
		n:    n,
		body: body,
	}
}

func (p *page) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write(p.body)
	return int64(n), err
}

type group string

type entry struct {
	deadline time.Time
	data     []byte
}

func newEntry(data []byte, d time.Duration) *entry {
	return &entry{
		deadline: time.Now().Add(d),
		data:     data,
	}
}

func (ce *entry) invalid(t time.Time) bool {
	return !ce.deadline.After(t)
}

func (ce *entry) asPage(n offset) *page {
	p := newPage(n, ce.data)
	p.expire = ce.deadline
	return p
}

type entries struct {
	ents map[group]map[offset]*entry
}

func newEntries() *entries {
	return &entries{
		ents: make(map[group]map[offset]*entry),
	}
}

func (e *entries) get(cg group, n offset) (*entry, bool) {
	pages, ok := e.ents[cg]
	if !ok {
		return nil, false
	}
	ce, ok := pages[n]
	return ce, ok
}

func (e *entries) put(cg group, n offset, ce *entry) {
	_, ok := e.ents[cg]
	if !ok {
		e.ents[cg] = make(map[offset]*entry)
	}
	e.ents[cg][n] = ce
}

func (e *entries) has(cg group, n offset) bool {
	_, ok := e.ents[cg]
	if !ok {
		return false
	}
	_, ok = e.ents[cg][n]
	return ok
}

// remove assumes the entry exists
func (e *entries) remove(cg group, n offset) {
	delete(e.ents[cg], n)
	if len(e.ents[cg]) == 0 {
		delete(e.ents, cg)
	}
}

func (e *entries) gc(t time.Time) {
	for cg, ents := range e.ents {
		for n := range ents {
			if ents[n].invalid(t) {
				e.remove(cg, n)
			}
		}
	}
}

type cacheFunc func() error

type cache struct {
	entries *entries
	waits   *waiters
	fetcher *fetcher
	config  *config
	events  chan cacheFunc
}

func newCache(f *fetcher, cf *config) *cache {
	c := &cache{
		fetcher: f,
		config:  cf,
		events:  make(chan cacheFunc),
		entries: newEntries(),
		waits:   newWaiters(),
	}
	go c.gc(cf.gcpause)
	go c.run()
	return c
}

func (c *cache) run() {
	for f := range c.events {
		if err := f(); err != nil {
			log.Print("cache: ", err)
		}
	}
}

func (c *cache) gc(d time.Duration) {
	done := make(chan struct{})
	for {
		time.Sleep(d)
		c.events <- func() error {
			c.entries.gc(time.Now())
			done <- struct{}{}
			return nil
		}
		<-done
	}
}

// put inserts a page into the cache (after it was fetched).
func (c *cache) put(cg group, p *page, err error) {
	c.events <- func() error {
		ce := newEntry(p.body, c.config.lifetime)
		c.entries.put(cg, p.n, ce)
		debug("added page %s/%d", cg, p.n)
		// If there were waiters, signal that the wait is over
		c.waits.done(cg, p.n)
		return err
	}
}

// prefetch requests pages (n-m, n) and (n, n+m) if not already fetched
func (c *cache) prefetch(cg group, n, m int) {
	i := n - m
	if i < 0 {
		i = 0
	}
	for ; i < n+m; i++ {
		if i == n {
			// just fetched
			continue
		}
		off := offset(i*c.config.incr)
		if c.entries.has(cg, off) || c.waits.has(cg, off) {
			// already fetched or requested
			continue
		}
		c.waits.wait(cg, off)
		res := newResource(c.config.tmpl, cg, off)
		c.fetcher.request(newJob(res, c))
	}
}

func (c *cache) request(cg group, n int) chan struct{} {
	off := offset(n*c.config.incr)
	wait := c.waits.wait(cg, off)
	res := newResource(c.config.tmpl, cg, off)
	c.fetcher.request(newJob(res, c))
	c.prefetch(cg, n, c.config.npref)
	return wait
}

func (c *cache) get(cg group, n int) *page {
	var (
		page *page
		wait chan struct{}
	)
	cached := true
	requested := make(chan struct{})
	off := offset(n*c.config.incr)
	debug("%s/%d: requesting from cache", cg, off)
	for {
		wait = nil
		c.events <- func() error {
			defer func() { requested <- struct{}{} }()
			var now time.Time
			ce, ok := c.entries.get(cg, off)
			if ok {
				now = time.Now()
			}
			if !ok || ce.invalid(now) {
				debug("%s/%d: not cached, requested", cg, off)
				wait = c.request(cg, n)
				return nil
			} else {
				debug("%s/%d: found", cg, off)
				c.prefetch(cg, n, c.config.npref)
			}
			page = ce.asPage(off)
			return nil
		}
		<-requested
		// content was already in cache, return it
		if wait == nil {
			page.cached = cached
			return page
		}
		// We needed to request the object, it was not cached
		cached = false
		// content is being fetched, wait and try to get again
		<-wait
	}
}
<|endoftext|>


<commit_before>package sshvault

import (
	"crypto/md5"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/ssh-vault/ssh2pem"
)

type cache struct {
	dir string
}

// Cache creates ~/.ssh/vault
func Cache() *cache {
	usr, _ := user.Current()
	sv := filepath.Join(usr.HomeDir, ".ssh", "vault", "keys")
	if _, err := os.Stat(sv); os.IsNotExist(err) {
		os.MkdirAll(sv, os.ModePerm)
	}
	return &cache{sv}
}

// Get return ssh-key
func (c *cache) Get(s Schlosser, u, f string, k int) (string, error) {
	if k == 0 {
		k = 1
	}

	// storage format
	// ~/.ssh/vault/keys/user.key-N
	// or
	// ~/.ssh/vault/keys/<md5>.key-N
	var (
		uKey string
		hash string
	)
	if !isURL.MatchString(u) {
		uKey = fmt.Sprintf("%s/%s.%d", c.dir, u, k)
	} else {
		hash = fmt.Sprintf("%x", md5.Sum([]byte(u)))
		uKey = fmt.Sprintf("%s/%s.%d", c.dir, hash, k)
	}

	// if key not found, fetch it
	if !c.IsFile(uKey) || u == "new" {
		keys, err := s.GetKey(u)
		if err != nil {
			return "", err
		}
		if isURL.MatchString(u) {
			u = hash
		}
		for k, v := range keys {
			err = ioutil.WriteFile(fmt.Sprintf("%s/%s.%d", c.dir, u, k+1),
				[]byte(v),
				0644)
			if err != nil {
				log.Println(err)
			}
		}
		if !c.IsFile(uKey) {
			return "", fmt.Errorf("key index not found, try -k with a value between 1 and %d", len(keys))
		}
	}

	// if fingerprint, find the key that matches
	if f != "" {
		key, err := c.FindFingerprint(uKey, f)
		if err != nil {
			return "", err
		}
		return key, nil
	}

	return uKey, nil
}

// IsFile check if string is a file
func (c *cache) IsFile(path string) bool {
	f, err := os.Stat(path)
	if err != nil {
		return false
	}
	if m := f.Mode(); !m.IsDir() && m.IsRegular() && m&400 != 0 {
		return true
	}
	return false
}

// Find searches for key
func (c *cache) FindFingerprint(u, f string) (string, error) {
	files, err := ioutil.ReadDir(c.dir)
	if err != nil {
		return "", err
	}
	var user = strings.TrimSuffix(filepath.Base(u), filepath.Ext(filepath.Base(u)))
	for _, file := range files {
		if strings.HasPrefix(file.Name(), user) {
			out, err := ssh2pem.GetPem(filepath.Join(c.dir, file.Name()))
			if err != nil {
				return "", err
			}
			p, _ := pem.Decode(out)
			x := &vault{}
			fingerprint, _ := x.GenFingerprint(p)
			if f == fingerprint {
				return filepath.Join(c.dir, file.Name()), nil
			}
		}
	}
	return "", fmt.Errorf("key fingerprint: %q not found", f)
}
<commit_msg>prefer the HOME environmental variable<commit_after>package sshvault

import (
	"crypto/md5"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/ssh-vault/ssh2pem"
)

type cache struct {
	dir string
}

// Cache creates ~/.ssh/vault
func Cache() *cache {
	d := os.Getenv("SSH_VAULT_CACHE_DIR")
	if d == "" {
		home := os.Getenv("HOME")
		if home == "" {
			usr, _ := user.Current()
			home = usr.HomeDir
		}
		d = filepath.Join(home, ".ssh", "vault", "keys")
	}
	if _, err := os.Stat(d); os.IsNotExist(err) {
		if err := os.MkdirAll(d, os.ModePerm); err != nil {
			log.Fatal(err)
		}
	}
	return &cache{d}
}

// Get return ssh-key
func (c *cache) Get(s Schlosser, u, f string, k int) (string, error) {
	if k == 0 {
		k = 1
	}

	// storage format
	// ~/.ssh/vault/keys/user.key-N
	// or
	// ~/.ssh/vault/keys/<md5>.key-N
	var (
		uKey string
		hash string
	)

	if !isURL.MatchString(u) {
		uKey = fmt.Sprintf("%s/%s.%d", c.dir, u, k)
	} else {
		hash = fmt.Sprintf("%x", md5.Sum([]byte(u)))
		uKey = fmt.Sprintf("%s/%s.%d", c.dir, hash, k)
	}

	// if key not found, fetch it
	if !c.IsFile(uKey) || u == "new" {
		keys, err := s.GetKey(u)
		if err != nil {
			return "", err
		}
		if isURL.MatchString(u) {
			u = hash
		}
		for k, v := range keys {
			err = ioutil.WriteFile(fmt.Sprintf("%s/%s.%d", c.dir, u, k+1),
				[]byte(v),
				0644)
			if err != nil {
				log.Println(err)
			}
		}
		if !c.IsFile(uKey) {
			return "", fmt.Errorf("key index not found, try -k with a value between 1 and %d", len(keys))
		}
	}

	// if fingerprint, find the key that matches
	if f != "" {
		key, err := c.FindFingerprint(uKey, f)
		if err != nil {
			return "", err
		}
		return key, nil
	}

	return uKey, nil
}

// IsFile check if string is a file
func (c *cache) IsFile(path string) bool {
	f, err := os.Stat(path)
	if err != nil {
		return false
	}
	if m := f.Mode(); !m.IsDir() && m.IsRegular() && m&400 != 0 {
		return true
	}
	return false
}

// Find searches for key
func (c *cache) FindFingerprint(u, f string) (string, error) {
	files, err := ioutil.ReadDir(c.dir)
	if err != nil {
		return "", err
	}
	var user = strings.TrimSuffix(filepath.Base(u), filepath.Ext(filepath.Base(u)))
	for _, file := range files {
		if strings.HasPrefix(file.Name(), user) {
			out, err := ssh2pem.GetPem(filepath.Join(c.dir, file.Name()))
			if err != nil {
				return "", err
			}
			p, _ := pem.Decode(out)
			x := &vault{}
			fingerprint, _ := x.GenFingerprint(p)
			if f == fingerprint {
				return filepath.Join(c.dir, file.Name()), nil
			}
		}
	}
	return "", fmt.Errorf("key fingerprint: %q not found", f)
}
<|endoftext|>


<commit_before>package main

import (
	"math/rand"
	"time"
)

//A structure created for caching pages for a given amount of time. This avoids heavy traffic to the HN servers.
type PageCache struct {
	Created  time.Time        `json:"created"`
	Pages    map[string]*Page `json:"pages"`
	Articles []*Article       `json:"articles"`
	next     string
	Next     string `json:"next"`
}

func RandomString() string {
	rand.Seed(time.Now().Unix())
	b := make([80]byte)

	rand.Read(b)

	return string(b)
}

func NewPageCache() *PageCache {
	pc := PageCache{
		Next:  "news",
		Pages: make(map[string]*Page),
	}

	pc.GetNext()

	return &pc
}

func (pc *PageCache) GetNext() *Page {
	p := NewPage(pc.Next)
	pc.Pages[p.Url] = p
	pc.Next = p.NextUrl
	pc.Articles = append(pc.Articles, p.Articles...)

	return p
}
<commit_msg>Now compiling again<commit_after>package main

import (
	"math/rand"
	"time"
)

//A structure created for caching pages for a given amount of time. This avoids heavy traffic to the HN servers.
type PageCache struct {
	Created  time.Time        `json:"created"`
	Pages    map[string]*Page `json:"pages"`
	Articles []*Article       `json:"articles"`
	next     string
	Next     string `json:"next"`
}

func RandomString() string {
	rand.Seed(time.Now().Unix())
	b := make([]byte, 80)

	return string(b)
}

func NewPageCache() *PageCache {
	pc := PageCache{
		Next:  "news",
		Pages: make(map[string]*Page),
	}

	pc.GetNext()

	return &pc
}

func (pc *PageCache) GetNext() *Page {
	p := NewPage(pc.Next)
	pc.Pages[p.Url] = p
	pc.Next = p.NextUrl
	pc.Articles = append(pc.Articles, p.Articles...)

	return p
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/user"
	"strings"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
)

// Client is a wrapper over the SSH connection/sessions.
type SSHClient struct {
	Conn         *ssh.Client
	Sess         *ssh.Session
	User         string
	Host         string
	Agent        net.Conn //TODO: Signers, so we can reuse
	RemoteStdin  io.WriteCloser
	RemoteStdout io.Reader
	RemoteStderr io.Reader
	ConnOpened   bool
	SessOpened   bool
	Running      bool
	Env          string //export FOO="bar"; export BAR="baz";
}

type ErrConnect struct {
	User   string
	Host   string
	Reason string
}

func (e ErrConnect) Error() string {
	return fmt.Sprintf(`Connect("%v@%v"): %v`, e.User, e.Host, e.Reason)
}

// parseHost parses and normalizes <user>@<host:port> from a given string.
func (c *SSHClient) parseHost(host string) error {
	c.Host = host

	// Remove extra "ssh://" schema
	if len(c.Host) > 6 && c.Host[:6] == "ssh://" {
		c.Host = c.Host[6:]
	}

	if at := strings.Index(c.Host, "@"); at != -1 {
		c.User = c.Host[:at]
		c.Host = c.Host[at+1:]
	}

	// Add default user, if not set
	if c.User == "" {
		u, err := user.Current()
		if err != nil {
			return err
		}
		c.User = u.Username
	}

	if strings.Index(c.Host, "/") != -1 {
		return ErrConnect{c.User, c.Host, "unexpected slash in the host URL"}
	}

	// Add default port, if not set
	if strings.Index(c.Host, ":") == -1 {
		c.Host += ":22"
	}

	return nil
}

// Connect creates SSH connection to a specified host.
// It expects the host of the form "[ssh://]host[:port]".
// TODO: Split Signers to its own method.
func (c *SSHClient) Connect(host string) error {
	if c.ConnOpened {
		return fmt.Errorf("Already connected")
	}

	if err := c.parseHost(host); err != nil {
		return err
	}

	var signers []ssh.Signer

	// If there's a running SSH Agent, try to use its Private keys.
	sock, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
	if err == nil {
		agent := agent.NewClient(sock)
		signers, _ = agent.Signers()
	}

	// Try to read user's SSH private keys form the standard paths.
	files := []string{
		os.Getenv("HOME") + "/.ssh/id_rsa",
		os.Getenv("HOME") + "/.ssh/id_dsa",
	}
	for _, file := range files {
		data, err := ioutil.ReadFile(file)
		if err != nil {
			continue
		}
		signer, err := ssh.ParsePrivateKey(data)
		if err != nil {
			continue
		}
		signers = append(signers, signer)
	}

	config := &ssh.ClientConfig{
		User: c.User,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signers...),
		},
	}

	c.Conn, err = ssh.Dial("tcp", c.Host, config)
	if err != nil {
		return ErrConnect{c.User, c.Host, err.Error()}
	}
	c.ConnOpened = true

	return nil
}

// Run runs the task.Run command remotely on cmd.Host.
func (c *SSHClient) Run(task Task) error {
	if c.Running {
		return fmt.Errorf("Session already running")
	}
	if c.SessOpened {
		return fmt.Errorf("Session already connected")
	}

	sess, err := c.Conn.NewSession()
	if err != nil {
		return err
	}

	c.RemoteStdout, err = sess.StdoutPipe()
	if err != nil {
		return err
	}

	c.RemoteStderr, err = sess.StderrPipe()
	if err != nil {
		return err
	}

	c.RemoteStdin, err = sess.StdinPipe()
	if err != nil {
		return err
	}

	c.Sess = sess
	c.SessOpened = true

	// Start the remote command.
	if err := c.Sess.Start(c.Env + "set -x;" + task.Run); err != nil {
		return ErrTask{task, err.Error()}
	}

	c.Running = true
	return nil
}

// Wait waits until the remote command finishes and exits.
// It closes the SSH session.
func (c *SSHClient) Wait() error {
	if !c.Running {
		return fmt.Errorf("Trying to wait on stopped session")
	}

	err := c.Sess.Wait()
	c.Sess.Close()
	c.Running = false
	c.SessOpened = false

	return err
}

// Close closes the underlying SSH connection and session.
func (c *SSHClient) Close() error {
	if c.SessOpened {
		c.Sess.Close()
		c.SessOpened = false
	}
	if !c.ConnOpened {
		return fmt.Errorf("Trying to close the already closed connection")
	}

	err := c.Conn.Close()
	c.ConnOpened = false

	return err
}

func (c *SSHClient) Prefix() string {
	return c.User + "@" + c.Host
}
<commit_msg>Initiate SSH authentication for all clients just once<commit_after>package client

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/user"
	"strings"
	"sync"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
)

// Client is a wrapper over the SSH connection/sessions.
type SSHClient struct {
	Conn         *ssh.Client
	Sess         *ssh.Session
	User         string
	Host         string
	Agent        net.Conn //TODO: Signers, so we can reuse
	RemoteStdin  io.WriteCloser
	RemoteStdout io.Reader
	RemoteStderr io.Reader
	ConnOpened   bool
	SessOpened   bool
	Running      bool
	Env          string //export FOO="bar"; export BAR="baz";
}

type ErrConnect struct {
	User   string
	Host   string
	Reason string
}

func (e ErrConnect) Error() string {
	return fmt.Sprintf(`Connect("%v@%v"): %v`, e.User, e.Host, e.Reason)
}

// parseHost parses and normalizes <user>@<host:port> from a given string.
func (c *SSHClient) parseHost(host string) error {
	c.Host = host

	// Remove extra "ssh://" schema
	if len(c.Host) > 6 && c.Host[:6] == "ssh://" {
		c.Host = c.Host[6:]
	}

	if at := strings.Index(c.Host, "@"); at != -1 {
		c.User = c.Host[:at]
		c.Host = c.Host[at+1:]
	}

	// Add default user, if not set
	if c.User == "" {
		u, err := user.Current()
		if err != nil {
			return err
		}
		c.User = u.Username
	}

	if strings.Index(c.Host, "/") != -1 {
		return ErrConnect{c.User, c.Host, "unexpected slash in the host URL"}
	}

	// Add default port, if not set
	if strings.Index(c.Host, ":") == -1 {
		c.Host += ":22"
	}

	return nil
}

var initAuthMethodOnce sync.Once
var authMethod ssh.AuthMethod

// initAuthMethod initiates SSH authentication method.
func initAuthMethod() {
	var signers []ssh.Signer

	// If there's a running SSH Agent, try to use its Private keys.
	sock, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
	if err == nil {
		agent := agent.NewClient(sock)
		signers, _ = agent.Signers()
	}

	// Try to read user's SSH private keys form the standard paths.
	files := []string{
		os.Getenv("HOME") + "/.ssh/id_rsa",
		os.Getenv("HOME") + "/.ssh/id_dsa",
	}
	for _, file := range files {
		data, err := ioutil.ReadFile(file)
		if err != nil {
			continue
		}
		signer, err := ssh.ParsePrivateKey(data)
		if err != nil {
			continue
		}
		signers = append(signers, signer)

	}
	authMethod = ssh.PublicKeys(signers...)
}

// Connect creates SSH connection to a specified host.
// It expects the host of the form "[ssh://]host[:port]".
// TODO: Split Signers to its own method.
func (c *SSHClient) Connect(host string) error {
	if c.ConnOpened {
		return fmt.Errorf("Already connected")
	}

	err := c.parseHost(host)
	if err != nil {
		return err
	}

	initAuthMethodOnce.Do(initAuthMethod)

	config := &ssh.ClientConfig{
		User: c.User,
		Auth: []ssh.AuthMethod{
			authMethod,
		},
	}

	c.Conn, err = ssh.Dial("tcp", c.Host, config)
	if err != nil {
		return ErrConnect{c.User, c.Host, err.Error()}
	}
	c.ConnOpened = true

	return nil
}

// Run runs the task.Run command remotely on cmd.Host.
func (c *SSHClient) Run(task Task) error {
	if c.Running {
		return fmt.Errorf("Session already running")
	}
	if c.SessOpened {
		return fmt.Errorf("Session already connected")
	}

	sess, err := c.Conn.NewSession()
	if err != nil {
		return err
	}

	c.RemoteStdout, err = sess.StdoutPipe()
	if err != nil {
		return err
	}

	c.RemoteStderr, err = sess.StderrPipe()
	if err != nil {
		return err
	}

	c.RemoteStdin, err = sess.StdinPipe()
	if err != nil {
		return err
	}

	c.Sess = sess
	c.SessOpened = true

	// Start the remote command.
	if err := c.Sess.Start(c.Env + "set -x;" + task.Run); err != nil {
		return ErrTask{task, err.Error()}
	}

	c.Running = true
	return nil
}

// Wait waits until the remote command finishes and exits.
// It closes the SSH session.
func (c *SSHClient) Wait() error {
	if !c.Running {
		return fmt.Errorf("Trying to wait on stopped session")
	}

	err := c.Sess.Wait()
	c.Sess.Close()
	c.Running = false
	c.SessOpened = false

	return err
}

// Close closes the underlying SSH connection and session.
func (c *SSHClient) Close() error {
	if c.SessOpened {
		c.Sess.Close()
		c.SessOpened = false
	}
	if !c.ConnOpened {
		return fmt.Errorf("Trying to close the already closed connection")
	}

	err := c.Conn.Close()
	c.ConnOpened = false

	return err
}

func (c *SSHClient) Prefix() string {
	return c.User + "@" + c.Host
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package grpc

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/transport"
)

var (
	// ErrUnspecTarget indicates that the target address is unspecified.
	ErrUnspecTarget = errors.New("grpc: target is unspecified")
	// ErrClientConnClosing indicates that the operation is illegal because
	// the session is closing.
	ErrClientConnClosing = errors.New("grpc: the client connection is closing")
	// ErrClientConnTimeout indicates that the connection could not be
	// established or re-established within the specified timeout.
	ErrClientConnTimeout = errors.New("grpc: timed out trying to connect")
	// minimum time to give a connection to complete
	minConnectTimeout = 20 * time.Second
)

// dialOptions configure a Dial call. dialOptions are set by the DialOption
// values passed to Dial.
type dialOptions struct {
	codec Codec
	block bool
	copts transport.ConnectOptions
}

// DialOption configures how we set up the connection.
type DialOption func(*dialOptions)

// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
func WithCodec(c Codec) DialOption {
	return func(o *dialOptions) {
		o.codec = c
	}
}

// WithBlock returns a DialOption which makes caller of Dial blocks until the underlying
// connection is up. Without this, Dial returns immediately and connecting the server
// happens in background.
func WithBlock() DialOption {
	return func(o *dialOptions) {
		o.block = true
	}
}

// WithTransportCredentials returns a DialOption which configures a
// connection level security credentials (e.g., TLS/SSL).
func WithTransportCredentials(creds credentials.TransportAuthenticator) DialOption {
	return func(o *dialOptions) {
		o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
	}
}

// WithPerRPCCredentials returns a DialOption which sets
// credentials which will place auth state on each outbound RPC.
func WithPerRPCCredentials(creds credentials.Credentials) DialOption {
	return func(o *dialOptions) {
		o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
	}
}

// WithTimeout returns a DialOption that configures a timeout for dialing a client connection.
func WithTimeout(d time.Duration) DialOption {
	return func(o *dialOptions) {
		o.copts.Timeout = d
	}
}

// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
func WithDialer(f func(addr string, timeout time.Duration) (net.Conn, error)) DialOption {
	return func(o *dialOptions) {
		o.copts.Dialer = f
	}
}

// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
func WithUserAgent(s string) DialOption {
	return func(o *dialOptions) {
		o.copts.UserAgent = s
	}
}

// Dial creates a client connection the given target.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
	if target == "" {
		return nil, ErrUnspecTarget
	}
	cc := &ClientConn{
		target:       target,
		shutdownChan: make(chan struct{}),
	}
	for _, opt := range opts {
		opt(&cc.dopts)
	}
	colonPos := strings.LastIndex(target, ":")
	if colonPos == -1 {
		colonPos = len(target)
	}
	cc.authority = target[:colonPos]
	if cc.dopts.codec == nil {
		// Set the default codec.
		cc.dopts.codec = protoCodec{}
	}
	cc.stateCV = sync.NewCond(&cc.mu)
	if cc.dopts.block {
		if err := cc.resetTransport(false); err != nil {
			cc.Close()
			return nil, err
		}
		// Start to monitor the error status of transport.
		go cc.transportMonitor()
	} else {
		// Start a goroutine connecting to the server asynchronously.
		go func() {
			if err := cc.resetTransport(false); err != nil {
				grpclog.Printf("Failed to dial %s: %v; please retry.", target, err)
				cc.Close()
				return
			}
			go cc.transportMonitor()
		}()
	}
	return cc, nil
}

// ConnectivityState indicates the state of a client connection.
type ConnectivityState int

const (
	// Idle indicates the ClientConn is idle.
	Idle ConnectivityState = iota
	// Connecting indicates the ClienConn is connecting.
	Connecting
	// Ready indicates the ClientConn is ready for work.
	Ready
	// TransientFailure indicates the ClientConn has seen a failure but expects to recover.
	TransientFailure
	// Shutdown indicates the ClientConn has stated shutting down.
	Shutdown
)

func (s ConnectivityState) String() string {
	switch s {
	case Idle:
		return "IDLE"
	case Connecting:
		return "CONNECTING"
	case Ready:
		return "READY"
	case TransientFailure:
		return "TRANSIENT_FAILURE"
	case Shutdown:
		return "SHUTDOWN"
	default:
		panic(fmt.Sprintf("unknown connectivity state: %d", s))
	}
}

// ClientConn represents a client connection to an RPC service.
type ClientConn struct {
	target       string
	authority    string
	dopts        dialOptions
	shutdownChan chan struct{}

	mu      sync.Mutex
	state   ConnectivityState
	stateCV *sync.Cond
	// ready is closed and becomes nil when a new transport is up or failed
	// due to timeout.
	ready chan struct{}
	// Every time a new transport is created, this is incremented by 1. Used
	// to avoid trying to recreate a transport while the new one is already
	// under construction.
	transportSeq int
	transport    transport.ClientTransport
}

// State returns the connectivity state of the ClientConn
func (cc *ClientConn) State() ConnectivityState {
	cc.mu.Lock()
	defer cc.mu.Unlock()
	return cc.state
}

// WaitForStateChange returns true when the state changes to something other than the
// sourceState and false if timeout fires.
func (cc *ClientConn) WaitForStateChange(timeout time.Duration, sourceState ConnectivityState) bool {
	start := time.Now()
	cc.mu.Lock()
	defer cc.mu.Unlock()
	if sourceState != cc.state {
		return true
	}
	done := make(chan struct{})
	expired := timeout <= time.Since(start)
	go func() {
		select {
		case <-time.After(timeout-time.Since(start)):
			cc.mu.Lock()
			expired = true
			cc.stateCV.Broadcast()
			cc.mu.Unlock()
		case <-done:
		}
	}()
	defer close(done)
	for sourceState == cc.state {
		cc.stateCV.Wait()
		if expired {
			return false
		}
	}
	return true
}

func (cc *ClientConn) resetTransport(closeTransport bool) error {
	var retries int
	start := time.Now()
	for {
		cc.mu.Lock()
		cc.state = Connecting
		cc.stateCV.Broadcast()
		t := cc.transport
		ts := cc.transportSeq
		// Avoid wait() picking up a dying transport unnecessarily.
		cc.transportSeq = 0
		if cc.state == Shutdown {
			cc.mu.Unlock()
			return ErrClientConnClosing
		}
		cc.mu.Unlock()
		if closeTransport {
			t.Close()
		}
		// Adjust timeout for the current try.
		copts := cc.dopts.copts
		if copts.Timeout < 0 {
			cc.Close()
			return ErrClientConnTimeout
		}
		if copts.Timeout > 0 {
			copts.Timeout -= time.Since(start)
			if copts.Timeout <= 0 {
				cc.Close()
				return ErrClientConnTimeout
			}
		}
		sleepTime := backoff(retries)
		timeout := sleepTime
		if timeout < minConnectTimeout {
			timeout = minConnectTimeout
		}
		if copts.Timeout == 0 || copts.Timeout > timeout {
			copts.Timeout = timeout
		}
		connectTime := time.Now()
		newTransport, err := transport.NewClientTransport(cc.target, &copts)
		if err != nil {
			cc.mu.Lock()
			cc.state = TransientFailure
			cc.stateCV.Broadcast()
			cc.mu.Unlock()
			sleepTime -= time.Since(connectTime)
			if sleepTime < 0 {
				sleepTime = 0
			}
			// Fail early before falling into sleep.
			if cc.dopts.copts.Timeout > 0 && cc.dopts.copts.Timeout < sleepTime+time.Since(start) {
				cc.Close()
				return ErrClientConnTimeout
			}
			closeTransport = false
			time.Sleep(sleepTime)
			retries++
			grpclog.Printf("grpc: ClientConn.resetTransport failed to create client transport: %v; Reconnecting to %q", err, cc.target)
			continue
		}
		cc.mu.Lock()
		if cc.state == Shutdown {
			// cc.Close() has been invoked.
			cc.mu.Unlock()
			newTransport.Close()
			return ErrClientConnClosing
		}
		cc.state = Ready
		cc.stateCV.Broadcast()
		cc.transport = newTransport
		cc.transportSeq = ts + 1
		if cc.ready != nil {
			close(cc.ready)
			cc.ready = nil
		}
		cc.mu.Unlock()
		return nil
	}
}

// Run in a goroutine to track the error in transport and create the
// new transport if an error happens. It returns when the channel is closing.
func (cc *ClientConn) transportMonitor() {
	for {
		select {
		// shutdownChan is needed to detect the teardown when
		// the ClientConn is idle (i.e., no RPC in flight).
		case <-cc.shutdownChan:
			return
		case <-cc.transport.Error():
			cc.mu.Lock()
			cc.state = TransientFailure
			cc.stateCV.Broadcast()
			cc.mu.Unlock()
			if err := cc.resetTransport(true); err != nil {
				// The ClientConn is closing.
				grpclog.Printf("grpc: ClientConn.transportMonitor exits due to: %v", err)
				return
			}
			continue
		}
	}
}

// When wait returns, either the new transport is up or ClientConn is
// closing. Used to avoid working on a dying transport. It updates and
// returns the transport and its version when there is no error.
func (cc *ClientConn) wait(ctx context.Context, ts int) (transport.ClientTransport, int, error) {
	for {
		cc.mu.Lock()
		switch {
		case cc.state == Shutdown:
			cc.mu.Unlock()
			return nil, 0, ErrClientConnClosing
		case ts < cc.transportSeq:
			// Worked on a dying transport. Try the new one immediately.
			defer cc.mu.Unlock()
			return cc.transport, cc.transportSeq, nil
		default:
			ready := cc.ready
			if ready == nil {
				ready = make(chan struct{})
				cc.ready = ready
			}
			cc.mu.Unlock()
			select {
			case <-ctx.Done():
				return nil, 0, transport.ContextErr(ctx.Err())
			// Wait until the new transport is ready or failed.
			case <-ready:
			}
		}
	}
}

// Close starts to tear down the ClientConn. Returns ErrClientConnClosing if
// it has been closed (mostly due to dial time-out).
// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
// some edge cases (e.g., the caller opens and closes many ClientConn's in a
// tight loop.
func (cc *ClientConn) Close() error {
	cc.mu.Lock()
	defer cc.mu.Unlock()
	if cc.state == Shutdown {
		return ErrClientConnClosing
	}
	cc.state = Shutdown
	cc.stateCV.Broadcast()
	if cc.ready != nil {
		close(cc.ready)
		cc.ready = nil
	}
	if cc.transport != nil {
		cc.transport.Close()
	}
	if cc.shutdownChan != nil {
		close(cc.shutdownChan)
	}
	return nil
}
<commit_msg>polish the func comment<commit_after>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package grpc

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/transport"
)

var (
	// ErrUnspecTarget indicates that the target address is unspecified.
	ErrUnspecTarget = errors.New("grpc: target is unspecified")
	// ErrClientConnClosing indicates that the operation is illegal because
	// the session is closing.
	ErrClientConnClosing = errors.New("grpc: the client connection is closing")
	// ErrClientConnTimeout indicates that the connection could not be
	// established or re-established within the specified timeout.
	ErrClientConnTimeout = errors.New("grpc: timed out trying to connect")
	// minimum time to give a connection to complete
	minConnectTimeout = 20 * time.Second
)

// dialOptions configure a Dial call. dialOptions are set by the DialOption
// values passed to Dial.
type dialOptions struct {
	codec Codec
	block bool
	copts transport.ConnectOptions
}

// DialOption configures how we set up the connection.
type DialOption func(*dialOptions)

// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
func WithCodec(c Codec) DialOption {
	return func(o *dialOptions) {
		o.codec = c
	}
}

// WithBlock returns a DialOption which makes caller of Dial blocks until the underlying
// connection is up. Without this, Dial returns immediately and connecting the server
// happens in background.
func WithBlock() DialOption {
	return func(o *dialOptions) {
		o.block = true
	}
}

// WithTransportCredentials returns a DialOption which configures a
// connection level security credentials (e.g., TLS/SSL).
func WithTransportCredentials(creds credentials.TransportAuthenticator) DialOption {
	return func(o *dialOptions) {
		o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
	}
}

// WithPerRPCCredentials returns a DialOption which sets
// credentials which will place auth state on each outbound RPC.
func WithPerRPCCredentials(creds credentials.Credentials) DialOption {
	return func(o *dialOptions) {
		o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
	}
}

// WithTimeout returns a DialOption that configures a timeout for dialing a client connection.
func WithTimeout(d time.Duration) DialOption {
	return func(o *dialOptions) {
		o.copts.Timeout = d
	}
}

// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
func WithDialer(f func(addr string, timeout time.Duration) (net.Conn, error)) DialOption {
	return func(o *dialOptions) {
		o.copts.Dialer = f
	}
}

// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
func WithUserAgent(s string) DialOption {
	return func(o *dialOptions) {
		o.copts.UserAgent = s
	}
}

// Dial creates a client connection the given target.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
	if target == "" {
		return nil, ErrUnspecTarget
	}
	cc := &ClientConn{
		target:       target,
		shutdownChan: make(chan struct{}),
	}
	for _, opt := range opts {
		opt(&cc.dopts)
	}
	colonPos := strings.LastIndex(target, ":")
	if colonPos == -1 {
		colonPos = len(target)
	}
	cc.authority = target[:colonPos]
	if cc.dopts.codec == nil {
		// Set the default codec.
		cc.dopts.codec = protoCodec{}
	}
	cc.stateCV = sync.NewCond(&cc.mu)
	if cc.dopts.block {
		if err := cc.resetTransport(false); err != nil {
			cc.Close()
			return nil, err
		}
		// Start to monitor the error status of transport.
		go cc.transportMonitor()
	} else {
		// Start a goroutine connecting to the server asynchronously.
		go func() {
			if err := cc.resetTransport(false); err != nil {
				grpclog.Printf("Failed to dial %s: %v; please retry.", target, err)
				cc.Close()
				return
			}
			go cc.transportMonitor()
		}()
	}
	return cc, nil
}

// ConnectivityState indicates the state of a client connection.
type ConnectivityState int

const (
	// Idle indicates the ClientConn is idle.
	Idle ConnectivityState = iota
	// Connecting indicates the ClienConn is connecting.
	Connecting
	// Ready indicates the ClientConn is ready for work.
	Ready
	// TransientFailure indicates the ClientConn has seen a failure but expects to recover.
	TransientFailure
	// Shutdown indicates the ClientConn has stated shutting down.
	Shutdown
)

func (s ConnectivityState) String() string {
	switch s {
	case Idle:
		return "IDLE"
	case Connecting:
		return "CONNECTING"
	case Ready:
		return "READY"
	case TransientFailure:
		return "TRANSIENT_FAILURE"
	case Shutdown:
		return "SHUTDOWN"
	default:
		panic(fmt.Sprintf("unknown connectivity state: %d", s))
	}
}

// ClientConn represents a client connection to an RPC service.
type ClientConn struct {
	target       string
	authority    string
	dopts        dialOptions
	shutdownChan chan struct{}

	mu      sync.Mutex
	state   ConnectivityState
	stateCV *sync.Cond
	// ready is closed and becomes nil when a new transport is up or failed
	// due to timeout.
	ready chan struct{}
	// Every time a new transport is created, this is incremented by 1. Used
	// to avoid trying to recreate a transport while the new one is already
	// under construction.
	transportSeq int
	transport    transport.ClientTransport
}

// State returns the connectivity state of the ClientConn
func (cc *ClientConn) State() ConnectivityState {
	cc.mu.Lock()
	defer cc.mu.Unlock()
	return cc.state
}

// WaitForStateChange blocks until the state changes to something other than the sourceState
// or timeout fires. It returns false if timeout fires and true otherwise.
func (cc *ClientConn) WaitForStateChange(timeout time.Duration, sourceState ConnectivityState) bool {
	start := time.Now()
	cc.mu.Lock()
	defer cc.mu.Unlock()
	if sourceState != cc.state {
		return true
	}
	done := make(chan struct{})
	expired := timeout <= time.Since(start)
	go func() {
		select {
		case <-time.After(timeout-time.Since(start)):
			cc.mu.Lock()
			expired = true
			cc.stateCV.Broadcast()
			cc.mu.Unlock()
		case <-done:
		}
	}()
	defer close(done)
	for sourceState == cc.state {
		cc.stateCV.Wait()
		if expired {
			return false
		}
	}
	return true
}

func (cc *ClientConn) resetTransport(closeTransport bool) error {
	var retries int
	start := time.Now()
	for {
		cc.mu.Lock()
		cc.state = Connecting
		cc.stateCV.Broadcast()
		t := cc.transport
		ts := cc.transportSeq
		// Avoid wait() picking up a dying transport unnecessarily.
		cc.transportSeq = 0
		if cc.state == Shutdown {
			cc.mu.Unlock()
			return ErrClientConnClosing
		}
		cc.mu.Unlock()
		if closeTransport {
			t.Close()
		}
		// Adjust timeout for the current try.
		copts := cc.dopts.copts
		if copts.Timeout < 0 {
			cc.Close()
			return ErrClientConnTimeout
		}
		if copts.Timeout > 0 {
			copts.Timeout -= time.Since(start)
			if copts.Timeout <= 0 {
				cc.Close()
				return ErrClientConnTimeout
			}
		}
		sleepTime := backoff(retries)
		timeout := sleepTime
		if timeout < minConnectTimeout {
			timeout = minConnectTimeout
		}
		if copts.Timeout == 0 || copts.Timeout > timeout {
			copts.Timeout = timeout
		}
		connectTime := time.Now()
		newTransport, err := transport.NewClientTransport(cc.target, &copts)
		if err != nil {
			cc.mu.Lock()
			cc.state = TransientFailure
			cc.stateCV.Broadcast()
			cc.mu.Unlock()
			sleepTime -= time.Since(connectTime)
			if sleepTime < 0 {
				sleepTime = 0
			}
			// Fail early before falling into sleep.
			if cc.dopts.copts.Timeout > 0 && cc.dopts.copts.Timeout < sleepTime+time.Since(start) {
				cc.Close()
				return ErrClientConnTimeout
			}
			closeTransport = false
			time.Sleep(sleepTime)
			retries++
			grpclog.Printf("grpc: ClientConn.resetTransport failed to create client transport: %v; Reconnecting to %q", err, cc.target)
			continue
		}
		cc.mu.Lock()
		if cc.state == Shutdown {
			// cc.Close() has been invoked.
			cc.mu.Unlock()
			newTransport.Close()
			return ErrClientConnClosing
		}
		cc.state = Ready
		cc.stateCV.Broadcast()
		cc.transport = newTransport
		cc.transportSeq = ts + 1
		if cc.ready != nil {
			close(cc.ready)
			cc.ready = nil
		}
		cc.mu.Unlock()
		return nil
	}
}

// Run in a goroutine to track the error in transport and create the
// new transport if an error happens. It returns when the channel is closing.
func (cc *ClientConn) transportMonitor() {
	for {
		select {
		// shutdownChan is needed to detect the teardown when
		// the ClientConn is idle (i.e., no RPC in flight).
		case <-cc.shutdownChan:
			return
		case <-cc.transport.Error():
			cc.mu.Lock()
			cc.state = TransientFailure
			cc.stateCV.Broadcast()
			cc.mu.Unlock()
			if err := cc.resetTransport(true); err != nil {
				// The ClientConn is closing.
				grpclog.Printf("grpc: ClientConn.transportMonitor exits due to: %v", err)
				return
			}
			continue
		}
	}
}

// When wait returns, either the new transport is up or ClientConn is
// closing. Used to avoid working on a dying transport. It updates and
// returns the transport and its version when there is no error.
func (cc *ClientConn) wait(ctx context.Context, ts int) (transport.ClientTransport, int, error) {
	for {
		cc.mu.Lock()
		switch {
		case cc.state == Shutdown:
			cc.mu.Unlock()
			return nil, 0, ErrClientConnClosing
		case ts < cc.transportSeq:
			// Worked on a dying transport. Try the new one immediately.
			defer cc.mu.Unlock()
			return cc.transport, cc.transportSeq, nil
		default:
			ready := cc.ready
			if ready == nil {
				ready = make(chan struct{})
				cc.ready = ready
			}
			cc.mu.Unlock()
			select {
			case <-ctx.Done():
				return nil, 0, transport.ContextErr(ctx.Err())
			// Wait until the new transport is ready or failed.
			case <-ready:
			}
		}
	}
}

// Close starts to tear down the ClientConn. Returns ErrClientConnClosing if
// it has been closed (mostly due to dial time-out).
// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
// some edge cases (e.g., the caller opens and closes many ClientConn's in a
// tight loop.
func (cc *ClientConn) Close() error {
	cc.mu.Lock()
	defer cc.mu.Unlock()
	if cc.state == Shutdown {
		return ErrClientConnClosing
	}
	cc.state = Shutdown
	cc.stateCV.Broadcast()
	if cc.ready != nil {
		close(cc.ready)
		cc.ready = nil
	}
	if cc.transport != nil {
		cc.transport.Close()
	}
	if cc.shutdownChan != nil {
		close(cc.shutdownChan)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package grpc

import (
	"errors"
	"net"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/transport"
)

var (
	// ErrUnspecTarget indicates that the target address is unspecified.
	ErrUnspecTarget = errors.New("grpc: target is unspecified")
	// ErrClientConnClosing indicates that the operation is illegal because
	// the session is closing.
	ErrClientConnClosing = errors.New("grpc: the client connection is closing")
	// ErrClientConnTimeout indicates that the connection could not be
	// established or re-established within the specified timeout.
	ErrClientConnTimeout = errors.New("grpc: timed out trying to connect")
	// minimum time to give a connection to complete
	minConnectTimeout = 20 * time.Second
)

// dialOptions configure a Dial call. dialOptions are set by the DialOption
// values passed to Dial.
type dialOptions struct {
	codec Codec
	block bool
	copts transport.ConnectOptions
}

// DialOption configures how we set up the connection.
type DialOption func(*dialOptions)

// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
func WithCodec(c Codec) DialOption {
	return func(o *dialOptions) {
		o.codec = c
	}
}

// WithBlock returns a DialOption which makes caller of Dial blocks until the underlying
// connection is up. Without this, Dial returns immediately and connecting the server
// happens in background.
func WithBlock() DialOption {
	return func(o *dialOptions) {
		o.block = true
	}
}

// WithTransportCredentials returns a DialOption which configures a
// connection level security credentials (e.g., TLS/SSL).
func WithTransportCredentials(creds credentials.TransportAuthenticator) DialOption {
	return func(o *dialOptions) {
		o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
	}
}

// WithPerRPCCredentials returns a DialOption which sets
// credentials which will place auth state on each outbound RPC.
func WithPerRPCCredentials(creds credentials.Credentials) DialOption {
	return func(o *dialOptions) {
		o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
	}
}

// WithTimeout returns a DialOption that configures a timeout for dialing a client connection.
func WithTimeout(d time.Duration) DialOption {
	return func(o *dialOptions) {
		o.copts.Timeout = d
	}
}

// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
func WithDialer(f func(addr string, timeout time.Duration) (net.Conn, error)) DialOption {
	return func(o *dialOptions) {
		o.copts.Dialer = f
	}
}

// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
func WithUserAgent(s string) DialOption {
	return func(o *dialOptions) {
		o.copts.UserAgent = s
	}
}

// Dial creates a client connection the given target.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
	if target == "" {
		return nil, ErrUnspecTarget
	}
	cc := &ClientConn{
		target:       target,
		shutdownChan: make(chan struct{}),
	}
	for _, opt := range opts {
		opt(&cc.dopts)
	}
	colonPos := strings.LastIndex(target, ":")
	if colonPos == -1 {
		colonPos = len(target)
	}
	cc.authority = target[:colonPos]
	if cc.dopts.codec == nil {
		// Set the default codec.
		cc.dopts.codec = protoCodec{}
	}
	if cc.dopts.block {
		if err := cc.resetTransport(false); err != nil {
			return nil, err
		}
		// Start to monitor the error status of transport.
		go cc.transportMonitor()
	} else {
		// Start a goroutine connecting to the server asynchronously.
		go func() {
			if err := cc.resetTransport(false); err != nil {
				grpclog.Printf("Failed to dial %s: %v; please retry.", target, err)
				return
			}
			go cc.transportMonitor()
		}()
	}
	return cc, nil
}

// ClientConn represents a client connection to an RPC service.
type ClientConn struct {
	target       string
	authority    string
	dopts        dialOptions
	shutdownChan chan struct{}

	mu sync.Mutex
	// ready is closed and becomes nil when a new transport is up or failed
	// due to timeout.
	ready chan struct{}
	// Indicates the ClientConn is under destruction.
	closing bool
	// Every time a new transport is created, this is incremented by 1. Used
	// to avoid trying to recreate a transport while the new one is already
	// under construction.
	transportSeq int
	transport    transport.ClientTransport
}

func (cc *ClientConn) resetTransport(closeTransport bool) error {
	var retries int
	start := time.Now()
	for {
		cc.mu.Lock()
		t := cc.transport
		ts := cc.transportSeq
		// Avoid wait() picking up a dying transport unnecessarily.
		cc.transportSeq = 0
		if cc.closing {
			cc.mu.Unlock()
			return ErrClientConnClosing
		}
		cc.mu.Unlock()
		if closeTransport {
			t.Close()
		}
		// Adjust timeout for the current try.
		copts := cc.dopts.copts
		if copts.Timeout < 0 {
			cc.Close()
			return ErrClientConnTimeout
		}
		if copts.Timeout > 0 {
			copts.Timeout -= time.Since(start)
			if copts.Timeout <= 0 {
				cc.Close()
				return ErrClientConnTimeout
			}
		}
		sleepTime := backoff(retries)
		timeout := sleepTime
		if timeout < minConnectTimeout {
			timeout = minConnectTimeout
		}
		if copts.Timeout > timeout {
			copts.Timeout = timeout
		}
		connectTime := time.Now()
		newTransport, err := transport.NewClientTransport(cc.target, &copts)
		if err != nil {
			sleepTime -= time.Since(connectTime)
			if sleepTime < 0 {
				sleepTime = 0
			}
			// Fail early before falling into sleep.
			if cc.dopts.copts.Timeout > 0 && cc.dopts.copts.Timeout < sleepTime+time.Since(start) {
				cc.Close()
				return ErrClientConnTimeout
			}
			closeTransport = false
			time.Sleep(sleepTime)
			retries++
			grpclog.Printf("grpc: ClientConn.resetTransport failed to create client transport: %v; Reconnecting to %q", err, cc.target)
			continue
		}
		cc.mu.Lock()
		if cc.closing {
			// cc.Close() has been invoked.
			cc.mu.Unlock()
			newTransport.Close()
			return ErrClientConnClosing
		}
		cc.transport = newTransport
		cc.transportSeq = ts + 1
		if cc.ready != nil {
			close(cc.ready)
			cc.ready = nil
		}
		cc.mu.Unlock()
		return nil
	}
}

// Run in a goroutine to track the error in transport and create the
// new transport if an error happens. It returns when the channel is closing.
func (cc *ClientConn) transportMonitor() {
	for {
		select {
		// shutdownChan is needed to detect the channel teardown when
		// the ClientConn is idle (i.e., no RPC in flight).
		case <-cc.shutdownChan:
			return
		case <-cc.transport.Error():
			if err := cc.resetTransport(true); err != nil {
				// The channel is closing.
				grpclog.Printf("grpc: ClientConn.transportMonitor exits due to: %v", err)
				return
			}
			continue
		}
	}
}

// When wait returns, either the new transport is up or ClientConn is
// closing. Used to avoid working on a dying transport. It updates and
// returns the transport and its version when there is no error.
func (cc *ClientConn) wait(ctx context.Context, ts int) (transport.ClientTransport, int, error) {
	for {
		cc.mu.Lock()
		switch {
		case cc.closing:
			cc.mu.Unlock()
			return nil, 0, ErrClientConnClosing
		case ts < cc.transportSeq:
			// Worked on a dying transport. Try the new one immediately.
			defer cc.mu.Unlock()
			return cc.transport, cc.transportSeq, nil
		default:
			ready := cc.ready
			if ready == nil {
				ready = make(chan struct{})
				cc.ready = ready
			}
			cc.mu.Unlock()
			select {
			case <-ctx.Done():
				return nil, 0, transport.ContextErr(ctx.Err())
			// Wait until the new transport is ready or failed.
			case <-ready:
			}
		}
	}
}

// Close starts to tear down the ClientConn. Returns ErrClientConnClosing if
// it has been closed (mostly due to dial time-out).
// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
// some edge cases (e.g., the caller opens and closes many ClientConn's in a
// tight loop.
func (cc *ClientConn) Close() error {
	cc.mu.Lock()
	defer cc.mu.Unlock()
	if cc.closing {
		return ErrClientConnClosing
	}
	cc.closing = true
	if cc.ready != nil {
		close(cc.ready)
		cc.ready = nil
	}
	if cc.transport != nil {
		cc.transport.Close()
	}
	if cc.shutdownChan != nil {
		close(cc.shutdownChan)
	}
	return nil
}
<commit_msg>small fix<commit_after>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package grpc

import (
	"errors"
	"net"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/transport"
)

var (
	// ErrUnspecTarget indicates that the target address is unspecified.
	ErrUnspecTarget = errors.New("grpc: target is unspecified")
	// ErrClientConnClosing indicates that the operation is illegal because
	// the session is closing.
	ErrClientConnClosing = errors.New("grpc: the client connection is closing")
	// ErrClientConnTimeout indicates that the connection could not be
	// established or re-established within the specified timeout.
	ErrClientConnTimeout = errors.New("grpc: timed out trying to connect")
	// minimum time to give a connection to complete
	minConnectTimeout = 20 * time.Second
)

// dialOptions configure a Dial call. dialOptions are set by the DialOption
// values passed to Dial.
type dialOptions struct {
	codec Codec
	block bool
	copts transport.ConnectOptions
}

// DialOption configures how we set up the connection.
type DialOption func(*dialOptions)

// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
func WithCodec(c Codec) DialOption {
	return func(o *dialOptions) {
		o.codec = c
	}
}

// WithBlock returns a DialOption which makes caller of Dial blocks until the underlying
// connection is up. Without this, Dial returns immediately and connecting the server
// happens in background.
func WithBlock() DialOption {
	return func(o *dialOptions) {
		o.block = true
	}
}

// WithTransportCredentials returns a DialOption which configures a
// connection level security credentials (e.g., TLS/SSL).
func WithTransportCredentials(creds credentials.TransportAuthenticator) DialOption {
	return func(o *dialOptions) {
		o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
	}
}

// WithPerRPCCredentials returns a DialOption which sets
// credentials which will place auth state on each outbound RPC.
func WithPerRPCCredentials(creds credentials.Credentials) DialOption {
	return func(o *dialOptions) {
		o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
	}
}

// WithTimeout returns a DialOption that configures a timeout for dialing a client connection.
func WithTimeout(d time.Duration) DialOption {
	return func(o *dialOptions) {
		o.copts.Timeout = d
	}
}

// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
func WithDialer(f func(addr string, timeout time.Duration) (net.Conn, error)) DialOption {
	return func(o *dialOptions) {
		o.copts.Dialer = f
	}
}

// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
func WithUserAgent(s string) DialOption {
	return func(o *dialOptions) {
		o.copts.UserAgent = s
	}
}

// Dial creates a client connection the given target.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
	if target == "" {
		return nil, ErrUnspecTarget
	}
	cc := &ClientConn{
		target:       target,
		shutdownChan: make(chan struct{}),
	}
	for _, opt := range opts {
		opt(&cc.dopts)
	}
	colonPos := strings.LastIndex(target, ":")
	if colonPos == -1 {
		colonPos = len(target)
	}
	cc.authority = target[:colonPos]
	if cc.dopts.codec == nil {
		// Set the default codec.
		cc.dopts.codec = protoCodec{}
	}
	if cc.dopts.block {
		if err := cc.resetTransport(false); err != nil {
			return nil, err
		}
		// Start to monitor the error status of transport.
		go cc.transportMonitor()
	} else {
		// Start a goroutine connecting to the server asynchronously.
		go func() {
			if err := cc.resetTransport(false); err != nil {
				grpclog.Printf("Failed to dial %s: %v; please retry.", target, err)
				return
			}
			go cc.transportMonitor()
		}()
	}
	return cc, nil
}

// ClientConn represents a client connection to an RPC service.
type ClientConn struct {
	target       string
	authority    string
	dopts        dialOptions
	shutdownChan chan struct{}

	mu sync.Mutex
	// ready is closed and becomes nil when a new transport is up or failed
	// due to timeout.
	ready chan struct{}
	// Indicates the ClientConn is under destruction.
	closing bool
	// Every time a new transport is created, this is incremented by 1. Used
	// to avoid trying to recreate a transport while the new one is already
	// under construction.
	transportSeq int
	transport    transport.ClientTransport
}

func (cc *ClientConn) resetTransport(closeTransport bool) error {
	var retries int
	start := time.Now()
	for {
		cc.mu.Lock()
		t := cc.transport
		ts := cc.transportSeq
		// Avoid wait() picking up a dying transport unnecessarily.
		cc.transportSeq = 0
		if cc.closing {
			cc.mu.Unlock()
			return ErrClientConnClosing
		}
		cc.mu.Unlock()
		if closeTransport {
			t.Close()
		}
		// Adjust timeout for the current try.
		copts := cc.dopts.copts
		if copts.Timeout < 0 {
			cc.Close()
			return ErrClientConnTimeout
		}
		if copts.Timeout > 0 {
			copts.Timeout -= time.Since(start)
			if copts.Timeout <= 0 {
				cc.Close()
				return ErrClientConnTimeout
			}
		}
		sleepTime := backoff(retries)
		timeout := sleepTime
		if timeout < minConnectTimeout {
			timeout = minConnectTimeout
		}
		if copts.Timeout == 0 || copts.Timeout > timeout {
			copts.Timeout = timeout
		}
		connectTime := time.Now()
		newTransport, err := transport.NewClientTransport(cc.target, &copts)
		if err != nil {
			sleepTime -= time.Since(connectTime)
			if sleepTime < 0 {
				sleepTime = 0
			}
			// Fail early before falling into sleep.
			if cc.dopts.copts.Timeout > 0 && cc.dopts.copts.Timeout < sleepTime+time.Since(start) {
				cc.Close()
				return ErrClientConnTimeout
			}
			closeTransport = false
			time.Sleep(sleepTime)
			retries++
			grpclog.Printf("grpc: ClientConn.resetTransport failed to create client transport: %v; Reconnecting to %q", err, cc.target)
			continue
		}
		cc.mu.Lock()
		if cc.closing {
			// cc.Close() has been invoked.
			cc.mu.Unlock()
			newTransport.Close()
			return ErrClientConnClosing
		}
		cc.transport = newTransport
		cc.transportSeq = ts + 1
		if cc.ready != nil {
			close(cc.ready)
			cc.ready = nil
		}
		cc.mu.Unlock()
		return nil
	}
}

// Run in a goroutine to track the error in transport and create the
// new transport if an error happens. It returns when the channel is closing.
func (cc *ClientConn) transportMonitor() {
	for {
		select {
		// shutdownChan is needed to detect the channel teardown when
		// the ClientConn is idle (i.e., no RPC in flight).
		case <-cc.shutdownChan:
			return
		case <-cc.transport.Error():
			if err := cc.resetTransport(true); err != nil {
				// The channel is closing.
				grpclog.Printf("grpc: ClientConn.transportMonitor exits due to: %v", err)
				return
			}
			continue
		}
	}
}

// When wait returns, either the new transport is up or ClientConn is
// closing. Used to avoid working on a dying transport. It updates and
// returns the transport and its version when there is no error.
func (cc *ClientConn) wait(ctx context.Context, ts int) (transport.ClientTransport, int, error) {
	for {
		cc.mu.Lock()
		switch {
		case cc.closing:
			cc.mu.Unlock()
			return nil, 0, ErrClientConnClosing
		case ts < cc.transportSeq:
			// Worked on a dying transport. Try the new one immediately.
			defer cc.mu.Unlock()
			return cc.transport, cc.transportSeq, nil
		default:
			ready := cc.ready
			if ready == nil {
				ready = make(chan struct{})
				cc.ready = ready
			}
			cc.mu.Unlock()
			select {
			case <-ctx.Done():
				return nil, 0, transport.ContextErr(ctx.Err())
			// Wait until the new transport is ready or failed.
			case <-ready:
			}
		}
	}
}

// Close starts to tear down the ClientConn. Returns ErrClientConnClosing if
// it has been closed (mostly due to dial time-out).
// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
// some edge cases (e.g., the caller opens and closes many ClientConn's in a
// tight loop.
func (cc *ClientConn) Close() error {
	cc.mu.Lock()
	defer cc.mu.Unlock()
	if cc.closing {
		return ErrClientConnClosing
	}
	cc.closing = true
	if cc.ready != nil {
		close(cc.ready)
		cc.ready = nil
	}
	if cc.transport != nil {
		cc.transport.Close()
	}
	if cc.shutdownChan != nil {
		close(cc.shutdownChan)
	}
	return nil
}
<|endoftext|>


<commit_before>package opentracing

import (
	"errors"
	"net/http"
	"net/url"
	"strings"
)

///////////////////////////////////////////////////////////////////////////////
// CORE PROPAGATION INTERFACES:
///////////////////////////////////////////////////////////////////////////////

var (
	// ErrUnsupportedFormat occurs when the `format` passed to Tracer.Inject() or
	// Tracer.Join() is not recognized by the Tracer implementation.
	ErrUnsupportedFormat = errors.New("opentracing: Unknown or unsupported Inject/Join format")

	// ErrTraceNotFound occurs when the `carrier` passed to Tracer.Join() is
	// valid and uncorrupted but has insufficient information to join or resume
	// a trace.
	ErrTraceNotFound = errors.New("opentracing: Trace not found in Join carrier")

	// ErrInvalidSpan errors occur when Tracer.Inject() is asked to operate on
	// a Span which it is not prepared to handle (for example, since it was
	// created by a different tracer implementation).
	ErrInvalidSpan = errors.New("opentracing: Span type incompatible with tracer")

	// ErrInvalidCarrier errors occur when Tracer.Inject() or Tracer.Join()
	// implementations expect a different type of `carrier` than they are
	// given.
	ErrInvalidCarrier = errors.New("opentracing: Invalid Inject/Join carrier")

	// ErrTraceCorrupted occurs when the `carrier` passed to Tracer.Join() is
	// of the expected type but is corrupted.
	ErrTraceCorrupted = errors.New("opentracing: Trace data corrupted in Join carrier")
)

///////////////////////////////////////////////////////////////////////////////
// BUILTIN PROPAGATION FORMATS:
///////////////////////////////////////////////////////////////////////////////

// BuiltinFormat is used to demarcate the values within package `opentracing`
// that are intended for use with the Tracer.Inject() and Tracer.Join()
// methods.
type BuiltinFormat byte

const (
	// Binary encodes the Span for propagation as opaque binary data.
	//
	// For Tracer.Inject(): the carrier must be an `io.Writer`.
	//
	// For Tracer.Join(): the carrier must be an `io.Reader`.
	Binary BuiltinFormat = iota

	// TextMap encodes the Span in a TextMapCarrier instance.
	//
	// For Tracer.Inject(): the carrier must be a `TextMapWriter`.
	//
	// For Tracer.Join(): the carrier must be a `TextMapReader`.
	//
	// See HTTPHeaderTextMapCarrier for an implementation of both TextMapWriter
	// and TextMapReader that defers to an http.Header instance for storage.
	// For example, Inject():
	//
	//    carrier := HTTPHeaderTextMapCarrier{
	//    	HeaderPrefix: "opentracing-",
	//    	Header:       h,
	//    }
	//    err := span.Tracer().Inject(span, TextMap, carrier)
	//
	// Or Join():
	//
	//    carrier := HTTPHeaderTextMapCarrier{
	//    	HeaderPrefix: "opentracing-",
	//    	Header:       h,
	//    }
	//    span, err := tracer.Join("opName", TextMap, carrier)
	//
	TextMap

	// SplitBinary is DEPRECATED
	SplitBinary
	// SplitText is DEPRECATED
	SplitText
)

// TextMapWriter is the Inject() carrier for the TextMap builtin format. With
// it, the caller can encode a Span for propagation as entries in a multimap of
// unicode strings.
type TextMapWriter interface {
	// Add a key:value pair to the carrier.
	Add(key, val string)
}

// TextMapWriter is the Join() carrier for the TextMap builtin format. With it,
// the caller can decode a propagated Span as entries in a multimap of unicode
// strings.
type TextMapReader interface {
	// GetAll returns TextMap contents via repeated calls to the `handler`
	// function. If any call to `handler` returns a non-nil error, GetAll
	// terminates and returns that error.
	//
	// NOTE: A single `key` may appear in multiple calls to `handler` for a
	// single `GetAll` invocation.
	ReadAllEntries(handler func(key, val string) error) error
}

// HTTPHeaderTextMapCarrier satisfies both TextMapWriter and TextMapReader.
//
// NOTE: All `key` parameters to Add() and the ReadAllEntries() handler func
// are lowercased since http.Header doesn't respect character casing for keys.
type HTTPHeaderTextMapCarrier struct {
	// The prefix used to distinguish the TextMap entries within the
	// http.Header map.
	HeaderPrefix string

	http.Header
}

var ErrNoHeaderPrefix = errors.New("HTTPHeaderTextMapCarrier.HeaderPrefix is empty")

func (c HTTPHeaderTextMapCarrier) Add(key, val string) {
	c.Header.Add(strings.ToLower(c.HeaderPrefix+key), url.QueryEscape(val))
}
func (c HTTPHeaderTextMapCarrier) ReadAllEntries(handler func(key, val string) error) error {
	if len(c.HeaderPrefix) == 0 {
		return ErrNoHeaderPrefix
	}
	for k, vals := range c.Header {
		k = strings.ToLower(k)
		if !strings.HasPrefix(k, c.HeaderPrefix) {
			continue
		}
		kSuffix := k[len(c.HeaderPrefix):]
		for _, v := range vals {
			rawV, err := url.QueryUnescape(v)
			if err != nil {
				continue
			}
			if err = handler(kSuffix, rawV); err != nil {
				return err
			}
		}
	}
	return nil
}

// SplitTextCarrier is DEPRECATED
type SplitTextCarrier struct {
	TracerState map[string]string
	Baggage     map[string]string
}

// NewSplitTextCarrier is DEPRECATED
func NewSplitTextCarrier() *SplitTextCarrier {
	return &SplitTextCarrier{}
}

// SplitBinaryCarrier is DEPRECATED
type SplitBinaryCarrier struct {
	TracerState []byte
	Baggage     []byte
}

// NewSplitBinaryCarrier is DEPRECATED
func NewSplitBinaryCarrier() *SplitBinaryCarrier {
	return &SplitBinaryCarrier{}
}
<commit_msg>Respond to review comment<commit_after>package opentracing

import (
	"errors"
	"net/http"
	"net/url"
	"strings"
)

///////////////////////////////////////////////////////////////////////////////
// CORE PROPAGATION INTERFACES:
///////////////////////////////////////////////////////////////////////////////

var (
	// ErrUnsupportedFormat occurs when the `format` passed to Tracer.Inject() or
	// Tracer.Join() is not recognized by the Tracer implementation.
	ErrUnsupportedFormat = errors.New("opentracing: Unknown or unsupported Inject/Join format")

	// ErrTraceNotFound occurs when the `carrier` passed to Tracer.Join() is
	// valid and uncorrupted but has insufficient information to join or resume
	// a trace.
	ErrTraceNotFound = errors.New("opentracing: Trace not found in Join carrier")

	// ErrInvalidSpan errors occur when Tracer.Inject() is asked to operate on
	// a Span which it is not prepared to handle (for example, since it was
	// created by a different tracer implementation).
	ErrInvalidSpan = errors.New("opentracing: Span type incompatible with tracer")

	// ErrInvalidCarrier errors occur when Tracer.Inject() or Tracer.Join()
	// implementations expect a different type of `carrier` than they are
	// given.
	ErrInvalidCarrier = errors.New("opentracing: Invalid Inject/Join carrier")

	// ErrTraceCorrupted occurs when the `carrier` passed to Tracer.Join() is
	// of the expected type but is corrupted.
	ErrTraceCorrupted = errors.New("opentracing: Trace data corrupted in Join carrier")
)

///////////////////////////////////////////////////////////////////////////////
// BUILTIN PROPAGATION FORMATS:
///////////////////////////////////////////////////////////////////////////////

// BuiltinFormat is used to demarcate the values within package `opentracing`
// that are intended for use with the Tracer.Inject() and Tracer.Join()
// methods.
type BuiltinFormat byte

const (
	// Binary encodes the Span for propagation as opaque binary data.
	//
	// For Tracer.Inject(): the carrier must be an `io.Writer`.
	//
	// For Tracer.Join(): the carrier must be an `io.Reader`.
	Binary BuiltinFormat = iota

	// TextMap encodes the Span in a TextMapCarrier instance.
	//
	// For Tracer.Inject(): the carrier must be a `TextMapWriter`.
	//
	// For Tracer.Join(): the carrier must be a `TextMapReader`.
	//
	// See HTTPHeaderTextMapCarrier for an implementation of both TextMapWriter
	// and TextMapReader that defers to an http.Header instance for storage.
	// For example, Inject():
	//
	//    carrier := HTTPHeaderTextMapCarrier{
	//    	HeaderPrefix: "opentracing-",
	//    	Header:       h,
	//    }
	//    err := span.Tracer().Inject(span, TextMap, carrier)
	//
	// Or Join():
	//
	//    carrier := HTTPHeaderTextMapCarrier{
	//    	HeaderPrefix: "opentracing-",
	//    	Header:       h,
	//    }
	//    span, err := tracer.Join("opName", TextMap, carrier)
	//
	TextMap

	// SplitBinary is DEPRECATED
	SplitBinary
	// SplitText is DEPRECATED
	SplitText
)

// TextMapWriter is the Inject() carrier for the TextMap builtin format. With
// it, the caller can encode a Span for propagation as entries in a multimap of
// unicode strings.
type TextMapWriter interface {
	// Add a key:value pair to the carrier. Multiple values may be added for a
	// single (repeated) key.
	Add(key, val string)
}

// TextMapWriter is the Join() carrier for the TextMap builtin format. With it,
// the caller can decode a propagated Span as entries in a multimap of unicode
// strings.
type TextMapReader interface {
	// ReadAllEntries returns TextMap contents via repeated calls to the
	// `handler` function. If any call to `handler` returns a non-nil error,
	// ReadAllEntries terminates and returns that error.
	//
	// NOTE: A single `key` may appear in multiple calls to `handler` for a
	// single `ReadAllEntries` invocation.
	ReadAllEntries(handler func(key, val string) error) error
}

// HTTPHeaderTextMapCarrier satisfies both TextMapWriter and TextMapReader.
//
// NOTE: All `key` parameters to Add() and the ReadAllEntries() handler func
// are lowercased since http.Header doesn't respect character casing for keys.
type HTTPHeaderTextMapCarrier struct {
	// The prefix used to distinguish the TextMap entries within the
	// http.Header map.
	HeaderPrefix string

	http.Header
}

var ErrNoHeaderPrefix = errors.New("HTTPHeaderTextMapCarrier.HeaderPrefix is empty")

func (c HTTPHeaderTextMapCarrier) Add(key, val string) {
	c.Header.Add(strings.ToLower(c.HeaderPrefix+key), url.QueryEscape(val))
}
func (c HTTPHeaderTextMapCarrier) ReadAllEntries(handler func(key, val string) error) error {
	if len(c.HeaderPrefix) == 0 {
		return ErrNoHeaderPrefix
	}
	for k, vals := range c.Header {
		k = strings.ToLower(k)
		if !strings.HasPrefix(k, c.HeaderPrefix) {
			continue
		}
		kSuffix := k[len(c.HeaderPrefix):]
		for _, v := range vals {
			rawV, err := url.QueryUnescape(v)
			if err != nil {
				continue
			}
			if err = handler(kSuffix, rawV); err != nil {
				return err
			}
		}
	}
	return nil
}

// SplitTextCarrier is DEPRECATED
type SplitTextCarrier struct {
	TracerState map[string]string
	Baggage     map[string]string
}

// NewSplitTextCarrier is DEPRECATED
func NewSplitTextCarrier() *SplitTextCarrier {
	return &SplitTextCarrier{}
}

// SplitBinaryCarrier is DEPRECATED
type SplitBinaryCarrier struct {
	TracerState []byte
	Baggage     []byte
}

// NewSplitBinaryCarrier is DEPRECATED
func NewSplitBinaryCarrier() *SplitBinaryCarrier {
	return &SplitBinaryCarrier{}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"path/filepath"
	"strings"
)

/* Import Command */
type importCommand struct{}

/* Import Profile Command */
type importProfileCommand struct{}

func (cmd *importProfileCommand) Execute(args []string) error {
	if len(args) < 1 {
		return usageError{}
	}

	return runInContext(func(current *executionContext) error {
		profilePath := args[0]
		profile, err := loadProfile(profilePath)
		if err != nil {
			return appError{err, "Could not load profile import"}
		}

		if len(args) > 1 {
			profile.Id = strings.ToLower(args[1])
			profile.Name = strings.Title(args[1])
		}

		newProfilePath := filepath.Join(current.config.ProfilesPath, profile.Id+".json")
		if err := profile.saveTo(newProfilePath); err != nil {
			return err
		}

		log.Printf("imported \"%s\" to: %s\n", profile.Id, newProfilePath)
		return nil
	})
}

func init() {
	imp, _ := commandParser.AddCommand("import", "", "", &importCommand{})
	imp.AddCommand("profile", "", "", &importProfileCommand{})
}
<commit_msg>remove import command for now<commit_after>// -build

package main

import (
	"log"
	"path/filepath"
	"strings"
)

/* Import Command */
type importCommand struct{}

/* Import Profile Command */
type importProfileCommand struct{}

func (cmd *importProfileCommand) Execute(args []string) error {
	if len(args) < 1 {
		return usageError{}
	}

	return runInContext(func(current *executionContext) error {
		profilePath := args[0]
		profile, err := loadProfile(profilePath)
		if err != nil {
			return appError{err, "Could not load profile import"}
		}

		if len(args) > 1 {
			profile.Id = strings.ToLower(args[1])
			profile.Name = strings.Title(args[1])
		}

		newProfilePath := filepath.Join(current.config.ProfilesPath, profile.Id+".json")
		if err := profile.saveTo(newProfilePath); err != nil {
			return err
		}

		log.Printf("imported \"%s\" to: %s\n", profile.Id, newProfilePath)
		return nil
	})
}

func init() {
	imp, _ := commandParser.AddCommand("import", "", "", &importCommand{})
	imp.AddCommand("profile", "", "", &importProfileCommand{})
}
<|endoftext|>