<commit_before>package gps

import (
	"encoding/json"
	"fmt"
	"go/build"
	"os"
	"path"

	"github.com/Masterminds/semver"
	"github.com/Masterminds/vcs"
)

// A SourceManager is responsible for retrieving, managing, and interrogating
// source repositories. Its primary purpose is to serve the needs of a Solver,
// but it is handy for other purposes, as well.
//
// gps's built-in SourceManager, SourceMgr, is intended to be generic and
// sufficient for any purpose. It provides some additional semantics around the
// methods defined here.
type SourceManager interface {
	// RepoExists checks if a repository exists, either upstream or in the
	// SourceManager's central repository cache.
	// TODO(sdboyer) rename to SourceExists
	RepoExists(ProjectIdentifier) (bool, error)

	// ListVersions retrieves a list of the available versions for a given
	// repository name.
	ListVersions(ProjectIdentifier) ([]Version, error)

	// RevisionPresentIn indicates whether the provided Version is present in
	// the given repository.
	RevisionPresentIn(ProjectIdentifier, Revision) (bool, error)

	// ListPackages parses the tree of the Go packages at or below root of the
	// provided ProjectIdentifier, at the provided version.
	ListPackages(ProjectIdentifier, Version) (PackageTree, error)

	// GetManifestAndLock returns manifest and lock information for the provided
	// root import path.
	//
	// gps currently requires that projects be rooted at their repository root,
	// necessitating that the ProjectIdentifier's ProjectRoot must also be a
	// repository root.
	GetManifestAndLock(ProjectIdentifier, Version) (Manifest, Lock, error)

	// ExportProject writes out the tree of the provided import path, at the
	// provided version, to the provided directory.
	ExportProject(ProjectIdentifier, Version, string) error

	// AnalyzerInfo reports the name and version of the logic used to service
	// GetManifestAndLock().
	AnalyzerInfo() (name string, version *semver.Version)
}

// A ProjectAnalyzer is responsible for analyzing a given path for Manifest and
// Lock information. Tools relying on gps must implement one.
type ProjectAnalyzer interface {
	// Perform analysis of the filesystem tree rooted at path, with the
	// root import path importRoot, to determine the project's constraints, as
	// indicated by a Manifest and Lock.
	DeriveManifestAndLock(path string, importRoot ProjectRoot) (Manifest, Lock, error)
	// Report the name and version of this ProjectAnalyzer.
	Info() (name string, version *semver.Version)
}

// SourceMgr is the default SourceManager for gps.
//
// There's no (planned) reason why it would need to be reimplemented by other
// tools; control via dependency injection is intended to be sufficient.
type SourceMgr struct {
	cachedir string
	pms      map[string]*pmState
	an       ProjectAnalyzer
	ctx      build.Context
}

var _ SourceManager = &SourceMgr{}

// Holds a projectManager, caches of the managed project's data, and information
// about the freshness of those caches
type pmState struct {
	pm   *projectManager
	cf   *os.File // handle for the cache file
	vcur bool     // indicates that we've called ListVersions()
}

// NewSourceManager produces an instance of gps's built-in SourceManager. It
// takes a cache directory (where local instances of upstream repositories are
// stored), a vendor directory for the project currently being worked on, and a
// force flag indicating whether to overwrite the global cache lock file (if
// present).
//
// The returned SourceManager aggressively caches information wherever possible.
// It is recommended that, if tools need to do preliminary, work involving
// upstream repository analysis prior to invoking a solve run, that they create
// this SourceManager as early as possible and use it to their ends. That way,
// the solver can benefit from any caches that may have already been warmed.
//
// gps's SourceManager is intended to be threadsafe (if it's not, please
// file a bug!). It should certainly be safe to reuse from one solving run to
// the next; however, the fact that it takes a basedir as an argument makes it
// much less useful for simultaneous use by separate solvers operating on
// different root projects. This architecture may change in the future.
func NewSourceManager(an ProjectAnalyzer, cachedir string, force bool) (*SourceMgr, error) {
	if an == nil {
		return nil, fmt.Errorf("a ProjectAnalyzer must be provided to the SourceManager")
	}

	err := os.MkdirAll(cachedir, 0777)
	if err != nil {
		return nil, err
	}

	glpath := path.Join(cachedir, "sm.lock")
	_, err = os.Stat(glpath)
	if err == nil && !force {
		return nil, fmt.Errorf("cache lock file %s exists - another process crashed or is still running?", glpath)
	}

	_, err = os.OpenFile(glpath, os.O_CREATE|os.O_RDONLY, 0700) // is 0700 sane for this purpose?
	if err != nil {
		return nil, fmt.Errorf("failed to create global cache lock file at %s with err %s", glpath, err)
	}

	ctx := build.Default
	// Replace GOPATH with our cache dir
	ctx.GOPATH = cachedir

	return &SourceMgr{
		cachedir: cachedir,
		pms:      make(map[string]*pmState),
		ctx:      ctx,
		an:       an,
	}, nil
}

// Release lets go of any locks held by the SourceManager.
func (sm *SourceMgr) Release() {
	os.Remove(path.Join(sm.cachedir, "sm.lock"))
}

// AnalyzerInfo reports the name and version of the injected ProjectAnalyzer.
func (sm *SourceMgr) AnalyzerInfo() (name string, version *semver.Version) {
	return sm.an.Info()
}

// GetManifestAndLock returns manifest and lock information for the provided
// import path. gps currently requires that projects be rooted at their
// repository root, necessitating that the ProjectIdentifier's ProjectRoot must
// also be a repository root.
//
// The work of producing the manifest and lock is delegated to the injected
// ProjectAnalyzer's DeriveManifestAndLock() method.
func (sm *SourceMgr) GetManifestAndLock(id ProjectIdentifier, v Version) (Manifest, Lock, error) {
	pmc, err := sm.getProjectManager(id)
	if err != nil {
		return nil, nil, err
	}

	return pmc.pm.GetInfoAt(v)
}

// ListPackages parses the tree of the Go packages at and below the ProjectRoot
// of the given ProjectIdentifier, at the given version.
func (sm *SourceMgr) ListPackages(id ProjectIdentifier, v Version) (PackageTree, error) {
	pmc, err := sm.getProjectManager(id)
	if err != nil {
		return PackageTree{}, err
	}

	return pmc.pm.ListPackages(v)
}

// ListVersions retrieves a list of the available versions for a given
// repository name.
//
// The list is not sorted; while it may be returned in the order that the
// underlying VCS reports version information, no guarantee is made. It is
// expected that the caller either not care about order, or sort the result
// themselves.
//
// This list is always retrieved from upstream on the first call. Subsequent
// calls will return a cached version of the first call's results. if upstream
// is not accessible (network outage, access issues, or the resource actually
// went away), an error will be returned.
func (sm *SourceMgr) ListVersions(id ProjectIdentifier) ([]Version, error) {
	pmc, err := sm.getProjectManager(id)
	if err != nil {
		// TODO(sdboyer) More-er proper-er errors
		return nil, err
	}

	return pmc.pm.ListVersions()
}

// RevisionPresentIn indicates whether the provided Revision is present in the given
// repository.
func (sm *SourceMgr) RevisionPresentIn(id ProjectIdentifier, r Revision) (bool, error) {
	pmc, err := sm.getProjectManager(id)
	if err != nil {
		// TODO(sdboyer) More-er proper-er errors
		return false, err
	}

	return pmc.pm.RevisionPresentIn(r)
}

// RepoExists checks if a repository exists, either upstream or in the cache,
// for the provided ProjectIdentifier.
func (sm *SourceMgr) RepoExists(id ProjectIdentifier) (bool, error) {
	pms, err := sm.getProjectManager(id)
	if err != nil {
		return false, err
	}

	return pms.pm.CheckExistence(existsInCache) || pms.pm.CheckExistence(existsUpstream), nil
}

// ExportProject writes out the tree of the provided ProjectIdentifier's
// ProjectRoot, at the provided version, to the provided directory.
func (sm *SourceMgr) ExportProject(id ProjectIdentifier, v Version, to string) error {
	pms, err := sm.getProjectManager(id)
	if err != nil {
		return err
	}

	return pms.pm.ExportVersionTo(v, to)
}

// getProjectManager gets the project manager for the given ProjectIdentifier.
//
// If no such manager yet exists, it attempts to create one.
func (sm *SourceMgr) getProjectManager(id ProjectIdentifier) (*pmState, error) {
	// Check pm cache and errcache first
	if pm, exists := sm.pms[n]; exists {
		return pm, nil
		//} else if pme, errexists := sm.pme[name]; errexists {
		//return nil, pme
	}

	repodir := filepath.Join(sm.cachedir, "src")
	// TODO(sdboyer) be more robust about this
	r, err := vcs.NewRepo("https://"+string(n), repodir)
	if err != nil {
		// TODO(sdboyer) be better
		return nil, err
	}
	if !r.CheckLocal() {
		// TODO(sdboyer) cloning the repo here puts it on a blocking, and possibly
		// unnecessary path. defer it
		err = r.Get()
		if err != nil {
			// TODO(sdboyer) be better
			return nil, err
		}
	}

	// Ensure cache dir exists
	metadir := path.Join(sm.cachedir, "metadata", string(n))
	err = os.MkdirAll(metadir, 0777)
	if err != nil {
		// TODO(sdboyer) be better
		return nil, err
	}

	pms := &pmState{}
	cpath := path.Join(metadir, "cache.json")
	fi, err := os.Stat(cpath)
	var dc *projectDataCache
	if fi != nil {
		pms.cf, err = os.OpenFile(cpath, os.O_RDWR, 0777)
		if err != nil {
			// TODO(sdboyer) be better
			return nil, fmt.Errorf("Err on opening metadata cache file: %s", err)
		}

		err = json.NewDecoder(pms.cf).Decode(dc)
		if err != nil {
			// TODO(sdboyer) be better
			return nil, fmt.Errorf("Err on JSON decoding metadata cache file: %s", err)
		}
	} else {
		// TODO(sdboyer) commented this out for now, until we manage it correctly
		//pms.cf, err = os.Create(cpath)
		//if err != nil {
		//// TODO(sdboyer) be better
		//return nil, fmt.Errorf("Err on creating metadata cache file: %s", err)
		//}

		dc = &projectDataCache{
			Infos:    make(map[Revision]projectInfo),
			Packages: make(map[Revision]PackageTree),
			VMap:     make(map[Version]Revision),
			RMap:     make(map[Revision][]Version),
		}
	}

	pm := &projectManager{
		n:   id.ProjectRoot,
		ctx: sm.ctx,
		an:  sm.an,
		dc:  dc,
		crepo: &repo{
			rpath: repodir,
			r:     r,
		},
	}

	pms.pm = pm
	sm.pms[n] = pms
	return pms, nil
}
<commit_msg>Refactor setup of projectManager instances<commit_after>package gps

import (
	"encoding/json"
	"fmt"
	"go/build"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/Masterminds/semver"
	"github.com/Masterminds/vcs"
)

// Used to compute a friendly filepath from a URL-shaped input
//
// TODO(sdboyer) this is awful. Right?
var sanitizer = strings.NewReplacer(":", "-", "/", "-", "+", "-")

// A SourceManager is responsible for retrieving, managing, and interrogating
// source repositories. Its primary purpose is to serve the needs of a Solver,
// but it is handy for other purposes, as well.
//
// gps's built-in SourceManager, SourceMgr, is intended to be generic and
// sufficient for any purpose. It provides some additional semantics around the
// methods defined here.
type SourceManager interface {
	// RepoExists checks if a repository exists, either upstream or in the
	// SourceManager's central repository cache.
	// TODO(sdboyer) rename to SourceExists
	RepoExists(ProjectIdentifier) (bool, error)

	// ListVersions retrieves a list of the available versions for a given
	// repository name.
	ListVersions(ProjectIdentifier) ([]Version, error)

	// RevisionPresentIn indicates whether the provided Version is present in
	// the given repository.
	RevisionPresentIn(ProjectIdentifier, Revision) (bool, error)

	// ListPackages parses the tree of the Go packages at or below root of the
	// provided ProjectIdentifier, at the provided version.
	ListPackages(ProjectIdentifier, Version) (PackageTree, error)

	// GetManifestAndLock returns manifest and lock information for the provided
	// root import path.
	//
	// gps currently requires that projects be rooted at their repository root,
	// necessitating that the ProjectIdentifier's ProjectRoot must also be a
	// repository root.
	GetManifestAndLock(ProjectIdentifier, Version) (Manifest, Lock, error)

	// ExportProject writes out the tree of the provided import path, at the
	// provided version, to the provided directory.
	ExportProject(ProjectIdentifier, Version, string) error

	// AnalyzerInfo reports the name and version of the logic used to service
	// GetManifestAndLock().
	AnalyzerInfo() (name string, version *semver.Version)
}

// A ProjectAnalyzer is responsible for analyzing a given path for Manifest and
// Lock information. Tools relying on gps must implement one.
type ProjectAnalyzer interface {
	// Perform analysis of the filesystem tree rooted at path, with the
	// root import path importRoot, to determine the project's constraints, as
	// indicated by a Manifest and Lock.
	DeriveManifestAndLock(path string, importRoot ProjectRoot) (Manifest, Lock, error)
	// Report the name and version of this ProjectAnalyzer.
	Info() (name string, version *semver.Version)
}

// SourceMgr is the default SourceManager for gps.
//
// There's no (planned) reason why it would need to be reimplemented by other
// tools; control via dependency injection is intended to be sufficient.
type SourceMgr struct {
	cachedir string
	pms      map[string]*pmState
	an       ProjectAnalyzer
	ctx      build.Context
}

var _ SourceManager = &SourceMgr{}

// Holds a projectManager, caches of the managed project's data, and information
// about the freshness of those caches
type pmState struct {
	pm   *projectManager
	cf   *os.File // handle for the cache file
	vcur bool     // indicates that we've called ListVersions()
}

// NewSourceManager produces an instance of gps's built-in SourceManager. It
// takes a cache directory (where local instances of upstream repositories are
// stored), a vendor directory for the project currently being worked on, and a
// force flag indicating whether to overwrite the global cache lock file (if
// present).
//
// The returned SourceManager aggressively caches information wherever possible.
// It is recommended that, if tools need to do preliminary, work involving
// upstream repository analysis prior to invoking a solve run, that they create
// this SourceManager as early as possible and use it to their ends. That way,
// the solver can benefit from any caches that may have already been warmed.
//
// gps's SourceManager is intended to be threadsafe (if it's not, please
// file a bug!). It should certainly be safe to reuse from one solving run to
// the next; however, the fact that it takes a basedir as an argument makes it
// much less useful for simultaneous use by separate solvers operating on
// different root projects. This architecture may change in the future.
func NewSourceManager(an ProjectAnalyzer, cachedir string, force bool) (*SourceMgr, error) {
	if an == nil {
		return nil, fmt.Errorf("a ProjectAnalyzer must be provided to the SourceManager")
	}

	err := os.MkdirAll(filepath.Join(cachedir, "sources"), 0777)
	if err != nil {
		return nil, err
	}

	glpath := path.Join(cachedir, "sm.lock")
	_, err = os.Stat(glpath)
	if err == nil && !force {
		return nil, fmt.Errorf("cache lock file %s exists - another process crashed or is still running?", glpath)
	}

	_, err = os.OpenFile(glpath, os.O_CREATE|os.O_RDONLY, 0700) // is 0700 sane for this purpose?
	if err != nil {
		return nil, fmt.Errorf("failed to create global cache lock file at %s with err %s", glpath, err)
	}

	ctx := build.Default
	// Replace GOPATH with our cache dir
	ctx.GOPATH = cachedir

	return &SourceMgr{
		cachedir: cachedir,
		pms:      make(map[string]*pmState),
		ctx:      ctx,
		an:       an,
	}, nil
}

// Release lets go of any locks held by the SourceManager.
func (sm *SourceMgr) Release() {
	os.Remove(path.Join(sm.cachedir, "sm.lock"))
}

// AnalyzerInfo reports the name and version of the injected ProjectAnalyzer.
func (sm *SourceMgr) AnalyzerInfo() (name string, version *semver.Version) {
	return sm.an.Info()
}

// GetManifestAndLock returns manifest and lock information for the provided
// import path. gps currently requires that projects be rooted at their
// repository root, necessitating that the ProjectIdentifier's ProjectRoot must
// also be a repository root.
//
// The work of producing the manifest and lock is delegated to the injected
// ProjectAnalyzer's DeriveManifestAndLock() method.
func (sm *SourceMgr) GetManifestAndLock(id ProjectIdentifier, v Version) (Manifest, Lock, error) {
	pmc, err := sm.getProjectManager(id)
	if err != nil {
		return nil, nil, err
	}

	return pmc.pm.GetInfoAt(v)
}

// ListPackages parses the tree of the Go packages at and below the ProjectRoot
// of the given ProjectIdentifier, at the given version.
func (sm *SourceMgr) ListPackages(id ProjectIdentifier, v Version) (PackageTree, error) {
	pmc, err := sm.getProjectManager(id)
	if err != nil {
		return PackageTree{}, err
	}

	return pmc.pm.ListPackages(v)
}

// ListVersions retrieves a list of the available versions for a given
// repository name.
//
// The list is not sorted; while it may be returned in the order that the
// underlying VCS reports version information, no guarantee is made. It is
// expected that the caller either not care about order, or sort the result
// themselves.
//
// This list is always retrieved from upstream on the first call. Subsequent
// calls will return a cached version of the first call's results. if upstream
// is not accessible (network outage, access issues, or the resource actually
// went away), an error will be returned.
func (sm *SourceMgr) ListVersions(id ProjectIdentifier) ([]Version, error) {
	pmc, err := sm.getProjectManager(id)
	if err != nil {
		// TODO(sdboyer) More-er proper-er errors
		return nil, err
	}

	return pmc.pm.ListVersions()
}

// RevisionPresentIn indicates whether the provided Revision is present in the given
// repository.
func (sm *SourceMgr) RevisionPresentIn(id ProjectIdentifier, r Revision) (bool, error) {
	pmc, err := sm.getProjectManager(id)
	if err != nil {
		// TODO(sdboyer) More-er proper-er errors
		return false, err
	}

	return pmc.pm.RevisionPresentIn(r)
}

// RepoExists checks if a repository exists, either upstream or in the cache,
// for the provided ProjectIdentifier.
func (sm *SourceMgr) RepoExists(id ProjectIdentifier) (bool, error) {
	pms, err := sm.getProjectManager(id)
	if err != nil {
		return false, err
	}

	return pms.pm.CheckExistence(existsInCache) || pms.pm.CheckExistence(existsUpstream), nil
}

// ExportProject writes out the tree of the provided ProjectIdentifier's
// ProjectRoot, at the provided version, to the provided directory.
func (sm *SourceMgr) ExportProject(id ProjectIdentifier, v Version, to string) error {
	pms, err := sm.getProjectManager(id)
	if err != nil {
		return err
	}

	return pms.pm.ExportVersionTo(v, to)
}

// getProjectManager gets the project manager for the given ProjectIdentifier.
//
// If no such manager yet exists, it attempts to create one.
func (sm *SourceMgr) getProjectManager(id ProjectIdentifier) (*pmState, error) {
	// TODO(sdboyer) finish this, it's not sufficient (?)
	n := id.netName()
	var sn string

	// Early check to see if we already have a pm in the cache for this net name
	if pm, exists := sm.pms[n]; exists {
		return pm, nil
	}

	// Figure out the remote repo path
	rr, err := deduceRemoteRepo(n)
	if err != nil {
		// Not a valid import path, must reject
		// TODO(sdboyer) wrap error
		return nil, err
	}

	// Check the cache again, see if exact resulting clone url is in there
	if pm, exists := sm.pms[rr.CloneURL.String()]; exists {
		// Found it - re-register this PM at the original netname so that it
		// doesn't need to deduce next time
		// TODO(sdboyer) is this OK to do? are there consistency side effects?
		sm.pms[n] = pm
		return pm, nil
	}

	// No luck again. Now, walk through the scheme options the deducer returned,
	// checking if each is in the cache
	for _, scheme := range rr.Schemes {
		rr.CloneURL.Scheme = scheme
		// See if THIS scheme has a match, now
		if pm, exists := sm.pms[rr.CloneURL.String()]; exists {
			// Yep - again, re-register this PM at the original netname so that it
			// doesn't need to deduce next time
			// TODO(sdboyer) is this OK to do? are there consistency side effects?
			sm.pms[n] = pm
			return pm, nil
		}
	}

	// Definitively no match for anything in the cache, so we know we have to
	// create the entry. Next question is whether there's already a repo on disk
	// for any of the schemes, or if we need to create that, too.

	// TODO(sdboyer) this strategy kinda locks in the scheme to use over
	// multiple invocations in a way that maybe isn't the best.
	var r vcs.Repo
	for _, scheme := range rr.Schemes {
		rr.CloneURL.Scheme = scheme
		url := rr.CloneURL.String()
		sn := sanitizer.Replace(url)
		path := filepath.Join(sm.cachedir, "sources", sn)

		if fi, err := os.Stat(path); err == nil && fi.IsDir() {
			// This one exists, so set up here
			r, err = vcs.NewRepo(url, path)
			if err != nil {
				return nil, err
			}
			goto decided
		}
	}

	// Nothing on disk, either. Iterate through the schemes, trying each and
	// failing out only if none resulted in successfully setting up the local.
	for _, scheme := range rr.Schemes {
		rr.CloneURL.Scheme = scheme
		url := rr.CloneURL.String()
		sn := sanitizer.Replace(url)
		path := filepath.Join(sm.cachedir, "sources", sn)

		r, err := vcs.NewRepo(url, path)
		if err != nil {
			continue
		}

		// FIXME(sdboyer) cloning the repo here puts it on a blocking path. that
		// aspect of state management needs to be deferred into the
		// projectManager
		err = r.Get()
		if err != nil {
			continue
		}
		goto decided
	}

	// If we've gotten this far, we got some brokeass input.
	return nil, fmt.Errorf("Could not reach source repository for %s", n)

decided:
	// Ensure cache dir exists
	metadir := path.Join(sm.cachedir, "metadata", string(n))
	err = os.MkdirAll(metadir, 0777)
	if err != nil {
		// TODO(sdboyer) be better
		return nil, err
	}

	pms := &pmState{}
	cpath := path.Join(metadir, "cache.json")
	fi, err := os.Stat(cpath)
	var dc *projectDataCache
	if fi != nil {
		pms.cf, err = os.OpenFile(cpath, os.O_RDWR, 0777)
		if err != nil {
			// TODO(sdboyer) be better
			return nil, fmt.Errorf("Err on opening metadata cache file: %s", err)
		}

		err = json.NewDecoder(pms.cf).Decode(dc)
		if err != nil {
			// TODO(sdboyer) be better
			return nil, fmt.Errorf("Err on JSON decoding metadata cache file: %s", err)
		}
	} else {
		// TODO(sdboyer) commented this out for now, until we manage it correctly
		//pms.cf, err = os.Create(cpath)
		//if err != nil {
		//// TODO(sdboyer) be better
		//return nil, fmt.Errorf("Err on creating metadata cache file: %s", err)
		//}

		dc = &projectDataCache{
			Infos:    make(map[Revision]projectInfo),
			Packages: make(map[Revision]PackageTree),
			VMap:     make(map[Version]Revision),
			RMap:     make(map[Revision][]Version),
		}
	}

	pm := &projectManager{
		an: sm.an,
		dc: dc,
		crepo: &repo{
			rpath: sn,
			r:     r,
		},
	}

	pms.pm = pm
	sm.pms[n] = pms
	return pms, nil
}
<|endoftext|>


<commit_before><commit_msg>Intervals uses MessageList and return the message if interval fits<commit_after><|endoftext|>


<commit_before>// +build ncurses
// +build !windows
// +build !tcell

package tui

/*
#include <ncurses.h>
#include <locale.h>
#cgo !static LDFLAGS: -lncurses
#cgo static LDFLAGS: -l:libncursesw.a -l:libtinfo.a -l:libgpm.a -ldl
#cgo android static LDFLAGS: -l:libncurses.a -fPIE -march=armv7-a -mfpu=neon -mhard-float -Wl,--no-warn-mismatch

FILE* c_tty() {
	return fopen("/dev/tty", "r");
}

SCREEN* c_newterm(FILE* tty) {
	return newterm(NULL, stderr, tty);
}

int c_getcurx(WINDOW* win) {
	return getcurx(win);
}
*/
import "C"

import (
	"os"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

func HasFullscreenRenderer() bool {
	return true
}

type Attr C.uint

type CursesWindow struct {
	impl   *C.WINDOW
	top    int
	left   int
	width  int
	height int
}

func (w *CursesWindow) Top() int {
	return w.top
}

func (w *CursesWindow) Left() int {
	return w.left
}

func (w *CursesWindow) Width() int {
	return w.width
}

func (w *CursesWindow) Height() int {
	return w.height
}

func (w *CursesWindow) Refresh() {
	C.wnoutrefresh(w.impl)
}

func (w *CursesWindow) FinishFill() {
	// NO-OP
}

const (
	Bold      Attr = C.A_BOLD
	Dim            = C.A_DIM
	Blink          = C.A_BLINK
	Reverse        = C.A_REVERSE
	Underline      = C.A_UNDERLINE
)

var Italic Attr = C.A_VERTICAL << 1 // FIXME

const (
	AttrRegular Attr = 0
)

var (
	_screen   *C.SCREEN
	_colorMap map[int]int16
	_colorFn  func(ColorPair, Attr) (C.short, C.int)
)

func init() {
	_colorMap = make(map[int]int16)
	if strings.HasPrefix(C.GoString(C.curses_version()), "ncurses 5") {
		Italic = C.A_NORMAL
	}
}

func (a Attr) Merge(b Attr) Attr {
	return a | b
}

func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
	if C.tigetnum(C.CString("colors")) >= 256 {
		return Dark256
	}
	return Default16
}

func (r *FullscreenRenderer) Init() {
	C.setlocale(C.LC_ALL, C.CString(""))
	tty := C.c_tty()
	if tty == nil {
		errorExit("Failed to open /dev/tty")
	}
	_screen = C.c_newterm(tty)
	if _screen == nil {
		errorExit("Invalid $TERM: " + os.Getenv("TERM"))
	}
	C.set_term(_screen)
	if r.mouse {
		C.mousemask(C.ALL_MOUSE_EVENTS, nil)
		C.mouseinterval(0)
	}
	C.noecho()
	C.raw() // stty dsusp undef
	C.nonl()
	C.keypad(C.stdscr, true)

	delay := 50
	delayEnv := os.Getenv("ESCDELAY")
	if len(delayEnv) > 0 {
		num, err := strconv.Atoi(delayEnv)
		if err == nil && num >= 0 {
			delay = num
		}
	}
	C.set_escdelay(C.int(delay))

	if r.theme != nil {
		C.start_color()
		initTheme(r.theme, r.defaultTheme(), r.forceBlack)
		initPairs(r.theme)
		C.bkgd(C.chtype(C.COLOR_PAIR(C.int(ColNormal.index()))))
		_colorFn = attrColored
	} else {
		initTheme(r.theme, nil, r.forceBlack)
		_colorFn = attrMono
	}

	C.nodelay(C.stdscr, true)
	ch := C.getch()
	if ch != C.ERR {
		C.ungetch(ch)
	}
	C.nodelay(C.stdscr, false)
}

func initPairs(theme *ColorTheme) {
	C.assume_default_colors(C.int(theme.Fg), C.int(theme.Bg))
	for _, pair := range []ColorPair{
		ColNormal,
		ColPrompt,
		ColMatch,
		ColCurrent,
		ColCurrentMatch,
		ColSpinner,
		ColInfo,
		ColCursor,
		ColSelected,
		ColHeader,
		ColBorder} {
		C.init_pair(C.short(pair.index()), C.short(pair.Fg()), C.short(pair.Bg()))
	}
}

func (r *FullscreenRenderer) Pause(bool) {
	C.endwin()
}

func (r *FullscreenRenderer) Resume(bool) {
}

func (r *FullscreenRenderer) Close() {
	C.endwin()
	C.delscreen(_screen)
}

func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
	win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
	if r.theme != nil {
		C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal.index()))))
	}
	// FIXME Does not implement BorderHorizontal
	if borderStyle != BorderNone {
		pair, attr := _colorFn(ColBorder, 0)
		C.wcolor_set(win, pair, nil)
		C.wattron(win, attr)
		C.box(win, 0, 0)
		C.wattroff(win, attr)
		C.wcolor_set(win, 0, nil)
	}

	return &CursesWindow{
		impl:   win,
		top:    top,
		left:   left,
		width:  width,
		height: height,
	}
}

func attrColored(color ColorPair, a Attr) (C.short, C.int) {
	return C.short(color.index()), C.int(a)
}

func attrMono(color ColorPair, a Attr) (C.short, C.int) {
	return 0, C.int(attrFor(color, a))
}

func (r *FullscreenRenderer) MaxX() int {
	return int(C.COLS)
}

func (r *FullscreenRenderer) MaxY() int {
	return int(C.LINES)
}

func (w *CursesWindow) Close() {
	C.delwin(w.impl)
}

func (w *CursesWindow) Enclose(y int, x int) bool {
	return bool(C.wenclose(w.impl, C.int(y), C.int(x)))
}

func (w *CursesWindow) Move(y int, x int) {
	C.wmove(w.impl, C.int(y), C.int(x))
}

func (w *CursesWindow) MoveAndClear(y int, x int) {
	w.Move(y, x)
	C.wclrtoeol(w.impl)
}

func (w *CursesWindow) Print(text string) {
	C.waddstr(w.impl, C.CString(strings.Map(func(r rune) rune {
		if r < 32 {
			return -1
		}
		return r
	}, text)))
}

func (w *CursesWindow) CPrint(color ColorPair, attr Attr, text string) {
	p, a := _colorFn(color, attr)
	C.wcolor_set(w.impl, p, nil)
	C.wattron(w.impl, a)
	w.Print(text)
	C.wattroff(w.impl, a)
	C.wcolor_set(w.impl, 0, nil)
}

func (r *FullscreenRenderer) Clear() {
	C.clear()
	C.endwin()
}

func (r *FullscreenRenderer) Refresh() {
	C.refresh()
}

func (w *CursesWindow) Erase() {
	C.werase(w.impl)
}

func (w *CursesWindow) X() int {
	return int(C.c_getcurx(w.impl))
}

func (r *FullscreenRenderer) DoesAutoWrap() bool {
	return true
}

func (r *FullscreenRenderer) IsOptimized() bool {
	return true
}

func (w *CursesWindow) Fill(str string) FillReturn {
	if C.waddstr(w.impl, C.CString(str)) == C.OK {
		return FillContinue
	}
	return FillSuspend
}

func (w *CursesWindow) CFill(fg Color, bg Color, attr Attr, str string) FillReturn {
	index := ColorPair{fg, bg, -1}.index()
	C.wcolor_set(w.impl, C.short(index), nil)
	C.wattron(w.impl, C.int(attr))
	ret := w.Fill(str)
	C.wattroff(w.impl, C.int(attr))
	C.wcolor_set(w.impl, 0, nil)
	return ret
}

func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
	for _, w := range windows {
		w.Refresh()
	}
	C.doupdate()
}

func (p ColorPair) index() int16 {
	if p.id >= 0 {
		return p.id
	}

	// ncurses does not support 24-bit colors
	if p.is24() {
		return ColDefault.index()
	}

	key := p.key()
	if found, prs := _colorMap[key]; prs {
		return found
	}

	id := int16(len(_colorMap)) + ColUser.id
	C.init_pair(C.short(id), C.short(p.Fg()), C.short(p.Bg()))
	_colorMap[key] = id
	return id
}

func consume(expects ...rune) bool {
	for _, r := range expects {
		if int(C.getch()) != int(r) {
			return false
		}
	}
	return true
}

func escSequence() Event {
	C.nodelay(C.stdscr, true)
	defer func() {
		C.nodelay(C.stdscr, false)
	}()
	c := C.getch()
	switch c {
	case C.ERR:
		return Event{ESC, 0, nil}
	case CtrlM:
		return Event{CtrlAltM, 0, nil}
	case '/':
		return Event{AltSlash, 0, nil}
	case ' ':
		return Event{AltSpace, 0, nil}
	case 127, C.KEY_BACKSPACE:
		return Event{AltBS, 0, nil}
	case '[':
		// Bracketed paste mode (printf "\e[?2004h")
		// \e[200~ TEXT \e[201~
		if consume('2', '0', '0', '~') {
			return Event{Invalid, 0, nil}
		}
	}
	if c >= 'a' && c <= 'z' {
		return Event{AltA + int(c) - 'a', 0, nil}
	}

	if c >= '0' && c <= '9' {
		return Event{Alt0 + int(c) - '0', 0, nil}
	}

	// Don't care. Ignore the rest.
	for ; c != C.ERR; c = C.getch() {
	}
	return Event{Invalid, 0, nil}
}

func (r *FullscreenRenderer) GetChar() Event {
	c := C.getch()
	switch c {
	case C.ERR:
		// Unexpected error from blocking read
		r.Close()
		errorExit("Failed to read /dev/tty")
	case C.KEY_UP:
		return Event{Up, 0, nil}
	case C.KEY_DOWN:
		return Event{Down, 0, nil}
	case C.KEY_LEFT:
		return Event{Left, 0, nil}
	case C.KEY_RIGHT:
		return Event{Right, 0, nil}
	case C.KEY_HOME:
		return Event{Home, 0, nil}
	case C.KEY_END:
		return Event{End, 0, nil}
	case C.KEY_BACKSPACE:
		return Event{BSpace, 0, nil}
	case C.KEY_F0 + 1:
		return Event{F1, 0, nil}
	case C.KEY_F0 + 2:
		return Event{F2, 0, nil}
	case C.KEY_F0 + 3:
		return Event{F3, 0, nil}
	case C.KEY_F0 + 4:
		return Event{F4, 0, nil}
	case C.KEY_F0 + 5:
		return Event{F5, 0, nil}
	case C.KEY_F0 + 6:
		return Event{F6, 0, nil}
	case C.KEY_F0 + 7:
		return Event{F7, 0, nil}
	case C.KEY_F0 + 8:
		return Event{F8, 0, nil}
	case C.KEY_F0 + 9:
		return Event{F9, 0, nil}
	case C.KEY_F0 + 10:
		return Event{F10, 0, nil}
	case C.KEY_F0 + 11:
		return Event{F11, 0, nil}
	case C.KEY_F0 + 12:
		return Event{F12, 0, nil}
	case C.KEY_DC:
		return Event{Del, 0, nil}
	case C.KEY_PPAGE:
		return Event{PgUp, 0, nil}
	case C.KEY_NPAGE:
		return Event{PgDn, 0, nil}
	case C.KEY_BTAB:
		return Event{BTab, 0, nil}
	case C.KEY_ENTER:
		return Event{CtrlM, 0, nil}
	case C.KEY_SLEFT:
		return Event{SLeft, 0, nil}
	case C.KEY_SRIGHT:
		return Event{SRight, 0, nil}
	case C.KEY_MOUSE:
		var me C.MEVENT
		if C.getmouse(&me) != C.ERR {
			mod := ((me.bstate & C.BUTTON_SHIFT) | (me.bstate & C.BUTTON_CTRL) | (me.bstate & C.BUTTON_ALT)) > 0
			x := int(me.x)
			y := int(me.y)
			/* Cannot use BUTTON1_DOUBLE_CLICKED due to mouseinterval(0) */
			if (me.bstate & C.BUTTON1_PRESSED) > 0 {
				now := time.Now()
				if now.Sub(r.prevDownTime) < doubleClickDuration {
					r.clickY = append(r.clickY, y)
				} else {
					r.clickY = []int{y}
					r.prevDownTime = now
				}
				return Event{Mouse, 0, &MouseEvent{y, x, 0, true, false, mod}}
			} else if (me.bstate & C.BUTTON1_RELEASED) > 0 {
				double := false
				if len(r.clickY) > 1 && r.clickY[0] == r.clickY[1] &&
					time.Now().Sub(r.prevDownTime) < doubleClickDuration {
					double = true
				}
				return Event{Mouse, 0, &MouseEvent{y, x, 0, false, double, mod}}
			} else if (me.bstate&0x8000000) > 0 || (me.bstate&0x80) > 0 {
				return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, mod}}
			} else if (me.bstate & C.BUTTON4_PRESSED) > 0 {
				return Event{Mouse, 0, &MouseEvent{y, x, 1, false, false, mod}}
			}
		}
		return Event{Invalid, 0, nil}
	case C.KEY_RESIZE:
		return Event{Resize, 0, nil}
	case ESC:
		return escSequence()
	case 127:
		return Event{BSpace, 0, nil}
	case 0:
		return Event{CtrlSpace, 0, nil}
	}
	// CTRL-A ~ CTRL-Z
	if c >= CtrlA && c <= CtrlZ {
		return Event{int(c), 0, nil}
	}

	// Multi-byte character
	buffer := []byte{byte(c)}
	for {
		r, _ := utf8.DecodeRune(buffer)
		if r != utf8.RuneError {
			return Event{Rune, r, nil}
		}

		c := C.getch()
		if c == C.ERR {
			break
		}
		if c >= C.KEY_CODE_YES {
			C.ungetch(c)
			break
		}
		buffer = append(buffer, byte(c))
	}
	return Event{Invalid, 0, nil}
}
<commit_msg>Delete ncurses implementation<commit_after><|endoftext|>


<commit_before>// This file is adapted from code in the net/http/httputil
// package of the Go standard library, which is by the
// Go Authors, and bears this copyright and license info:
//
//   Copyright 2011 The Go Authors. All rights reserved.
//   Use of this source code is governed by a BSD-style
//   license that can be found in the LICENSE file.
//
// This file has been modified from the standard lib to
// meet the needs of the application.

package proxy

import (
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"net/url"
	"path"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/http2"

	"github.com/mholt/caddy/caddyhttp/httpserver"
)

var bufferPool = sync.Pool{New: createBuffer}

func createBuffer() interface{} {
	return make([]byte, 32*1024)
}

// onExitFlushLoop is a callback set by tests to detect the state of the
// flushLoop() goroutine.
var onExitFlushLoop func()

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	Director func(*http.Request)

	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper

	// FlushInterval specifies the flush interval
	// to flush to the client while copying the
	// response body.
	// If zero, no periodic flushing is done.
	FlushInterval time.Duration
}

// Though the relevant directive prefix is just "unix:", url.Parse
// will - assuming the regular URL scheme - add additional slashes
// as if "unix" was a request protocol.
// What we need is just the path, so if "unix:/var/run/www.socket"
// was the proxy directive, the parsed hostName would be
// "unix:///var/run/www.socket", hence the ambiguous trimming.
func socketDial(hostName string) func(network, addr string) (conn net.Conn, err error) {
	return func(network, addr string) (conn net.Conn, err error) {
		return net.Dial("unix", hostName[len("unix://"):])
	}
}

// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
// Without logic: target's path is "/", incoming is "/api/messages",
// without is "/api", then the target request will be for /messages.
func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int) *ReverseProxy {
	targetQuery := target.RawQuery
	director := func(req *http.Request) {
		if target.Scheme == "unix" {
			// to make Dial work with unix URL,
			// scheme and host have to be faked
			req.URL.Scheme = "http"
			req.URL.Host = "socket"
		} else {
			req.URL.Scheme = target.Scheme
			req.URL.Host = target.Host
		}

		// We should remove the `without` prefix at first.
		if without != "" {
			req.URL.Path = strings.TrimPrefix(req.URL.Path, without)
			if req.URL.Opaque != "" {
				req.URL.Opaque = strings.TrimPrefix(req.URL.Opaque, without)
			}
			if req.URL.RawPath != "" {
				req.URL.RawPath = strings.TrimPrefix(req.URL.RawPath, without)
			}
		}

		hadTrailingSlash := strings.HasSuffix(req.URL.Path, "/")
		req.URL.Path = path.Join(target.Path, req.URL.Path)
		// path.Join will strip off the last /, so put it back if it was there.
		if hadTrailingSlash && !strings.HasSuffix(req.URL.Path, "/") {
			req.URL.Path = req.URL.Path + "/"
		}

		// Trims the path of the socket from the URL path.
		// This is done because req.URL passed to your proxied service
		// will have the full path of the socket file prefixed to it.
		// Calling /test on a server that proxies requests to
		// unix:/var/run/www.socket will thus set the requested path
		// to /var/run/www.socket/test, rendering paths useless.
		if target.Scheme == "unix" {
			// See comment on socketDial for the trim
			socketPrefix := target.String()[len("unix://"):]
			req.URL.Path = strings.TrimPrefix(req.URL.Path, socketPrefix)
		}

		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}
	rp := &ReverseProxy{Director: director, FlushInterval: 250 * time.Millisecond} // flushing good for streaming & server-sent events
	if target.Scheme == "unix" {
		rp.Transport = &http.Transport{
			Dial: socketDial(target.String()),
		}
	} else if keepalive != http.DefaultMaxIdleConnsPerHost {
		// if keepalive is equal to the default,
		// just use default transport, to avoid creating
		// a brand new transport
		transport := &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		}
		if keepalive == 0 {
			transport.DisableKeepAlives = true
		} else {
			transport.MaxIdleConnsPerHost = keepalive
		}
		http2.ConfigureTransport(transport)
		rp.Transport = transport
	}
	return rp
}

// UseInsecureTransport is used to facilitate HTTPS proxying
// when it is OK for upstream to be using a bad certificate,
// since this transport skips verification.
func (rp *ReverseProxy) UseInsecureTransport() {
	if rp.Transport == nil {
		transport := &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: 10 * time.Second,
			TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
		}
		http2.ConfigureTransport(transport)
		rp.Transport = transport
	} else if transport, ok := rp.Transport.(*http.Transport); ok {
		transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}
}

// ServeHTTP serves the proxied request to the upstream by performing a roundtrip.
// It is designed to handle websocket connection upgrades as well.
func (rp *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request, respUpdateFn respUpdateFn) error {
	transport := rp.Transport
	if requestIsWebsocket(outreq) {
		transport = newConnHijackerTransport(transport)
	} else if transport == nil {
		transport = http.DefaultTransport
	}

	rp.Director(outreq)
	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	res, err := transport.RoundTrip(outreq)
	if err != nil {
		return err
	}

	if respUpdateFn != nil {
		respUpdateFn(res)
	}
	if res.StatusCode == http.StatusSwitchingProtocols && strings.ToLower(res.Header.Get("Upgrade")) == "websocket" {
		res.Body.Close()
		hj, ok := rw.(http.Hijacker)
		if !ok {
			panic(httpserver.NonHijackerError{Underlying: rw})
		}

		conn, _, err := hj.Hijack()
		if err != nil {
			return err
		}
		defer conn.Close()

		var backendConn net.Conn
		if hj, ok := transport.(*connHijackerTransport); ok {
			backendConn = hj.Conn
			if _, err := conn.Write(hj.Replay); err != nil {
				return err
			}
			bufferPool.Put(hj.Replay)
		} else {
			backendConn, err = net.Dial("tcp", outreq.URL.Host)
			if err != nil {
				return err
			}
			outreq.Write(backendConn)
		}
		defer backendConn.Close()

		go func() {
			io.Copy(backendConn, conn) // write tcp stream to backend.
		}()
		io.Copy(conn, backendConn) // read tcp stream from backend.
	} else {
		defer res.Body.Close()
		for _, h := range hopHeaders {
			res.Header.Del(h)
		}
		copyHeader(rw.Header(), res.Header)
		rw.WriteHeader(res.StatusCode)
		rp.copyResponse(rw, res.Body)
	}

	return nil
}

func (rp *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
	buf := bufferPool.Get()
	defer bufferPool.Put(buf)

	if rp.FlushInterval != 0 {
		if wf, ok := dst.(writeFlusher); ok {
			mlw := &maxLatencyWriter{
				dst:     wf,
				latency: rp.FlushInterval,
				done:    make(chan bool),
			}
			go mlw.flushLoop()
			defer mlw.stop()
			dst = mlw
		}
	}
	io.CopyBuffer(dst, src, buf.([]byte))
}

// skip these headers if they already exist.
// see https://github.com/mholt/caddy/pull/1112#discussion_r80092582
var skipHeaders = map[string]struct{}{
	"Content-Type":        {},
	"Content-Disposition": {},
	"Accept-Ranges":       {},
	"Set-Cookie":          {},
	"Cache-Control":       {},
	"Expires":             {},
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		if _, ok := dst[k]; ok {
			// skip some predefined headers
			// see https://github.com/mholt/caddy/issues/1086
			if _, shouldSkip := skipHeaders[k]; shouldSkip {
				continue
			}
			// otherwise, overwrite
			dst.Del(k)
		}
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
	"Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te", // canonicalized version of "TE"
	"Trailers",
	"Transfer-Encoding",
	"Upgrade",
	"Alternate-Protocol",
	"Alt-Svc",
}

type respUpdateFn func(resp *http.Response)

type hijackedConn struct {
	net.Conn
	hj *connHijackerTransport
}

func (c *hijackedConn) Read(b []byte) (n int, err error) {
	n, err = c.Conn.Read(b)
	c.hj.Replay = append(c.hj.Replay, b[:n]...)
	return
}

func (c *hijackedConn) Close() error {
	return nil
}

type connHijackerTransport struct {
	*http.Transport
	Conn   net.Conn
	Replay []byte
}

func newConnHijackerTransport(base http.RoundTripper) *connHijackerTransport {
	transport := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		MaxIdleConnsPerHost: -1,
	}
	if base != nil {
		if baseTransport, ok := base.(*http.Transport); ok {
			transport.Proxy = baseTransport.Proxy
			transport.TLSClientConfig = baseTransport.TLSClientConfig
			transport.TLSHandshakeTimeout = baseTransport.TLSHandshakeTimeout
			transport.Dial = baseTransport.Dial
			transport.DialTLS = baseTransport.DialTLS
			transport.MaxIdleConnsPerHost = -1
		}
	}
	hjTransport := &connHijackerTransport{transport, nil, bufferPool.Get().([]byte)[:0]}
	oldDial := transport.Dial
	oldDialTLS := transport.DialTLS
	if oldDial == nil {
		oldDial = (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial
	}
	hjTransport.Dial = func(network, addr string) (net.Conn, error) {
		c, err := oldDial(network, addr)
		hjTransport.Conn = c
		return &hijackedConn{c, hjTransport}, err
	}
	if oldDialTLS != nil {
		hjTransport.DialTLS = func(network, addr string) (net.Conn, error) {
			c, err := oldDialTLS(network, addr)
			hjTransport.Conn = c
			return &hijackedConn{c, hjTransport}, err
		}
	}
	return hjTransport
}

func requestIsWebsocket(req *http.Request) bool {
	return !(strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade"))
}

type writeFlusher interface {
	io.Writer
	http.Flusher
}

type maxLatencyWriter struct {
	dst     writeFlusher
	latency time.Duration

	lk   sync.Mutex // protects Write + Flush
	done chan bool
}

func (m *maxLatencyWriter) Write(p []byte) (int, error) {
	m.lk.Lock()
	defer m.lk.Unlock()
	return m.dst.Write(p)
}

func (m *maxLatencyWriter) flushLoop() {
	t := time.NewTicker(m.latency)
	defer t.Stop()
	for {
		select {
		case <-m.done:
			if onExitFlushLoop != nil {
				onExitFlushLoop()
			}
			return
		case <-t.C:
			m.lk.Lock()
			m.dst.Flush()
			m.lk.Unlock()
		}
	}
}

func (m *maxLatencyWriter) stop() { m.done <- true }
<commit_msg>Fixed proxy not respecting the -http2 flag<commit_after>// This file is adapted from code in the net/http/httputil
// package of the Go standard library, which is by the
// Go Authors, and bears this copyright and license info:
//
//   Copyright 2011 The Go Authors. All rights reserved.
//   Use of this source code is governed by a BSD-style
//   license that can be found in the LICENSE file.
//
// This file has been modified from the standard lib to
// meet the needs of the application.

package proxy

import (
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"net/url"
	"path"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/http2"

	"github.com/mholt/caddy/caddyhttp/httpserver"
)

var bufferPool = sync.Pool{New: createBuffer}

func createBuffer() interface{} {
	return make([]byte, 32*1024)
}

// onExitFlushLoop is a callback set by tests to detect the state of the
// flushLoop() goroutine.
var onExitFlushLoop func()

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	Director func(*http.Request)

	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper

	// FlushInterval specifies the flush interval
	// to flush to the client while copying the
	// response body.
	// If zero, no periodic flushing is done.
	FlushInterval time.Duration
}

// Though the relevant directive prefix is just "unix:", url.Parse
// will - assuming the regular URL scheme - add additional slashes
// as if "unix" was a request protocol.
// What we need is just the path, so if "unix:/var/run/www.socket"
// was the proxy directive, the parsed hostName would be
// "unix:///var/run/www.socket", hence the ambiguous trimming.
func socketDial(hostName string) func(network, addr string) (conn net.Conn, err error) {
	return func(network, addr string) (conn net.Conn, err error) {
		return net.Dial("unix", hostName[len("unix://"):])
	}
}

// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
// Without logic: target's path is "/", incoming is "/api/messages",
// without is "/api", then the target request will be for /messages.
func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int) *ReverseProxy {
	targetQuery := target.RawQuery
	director := func(req *http.Request) {
		if target.Scheme == "unix" {
			// to make Dial work with unix URL,
			// scheme and host have to be faked
			req.URL.Scheme = "http"
			req.URL.Host = "socket"
		} else {
			req.URL.Scheme = target.Scheme
			req.URL.Host = target.Host
		}

		// We should remove the `without` prefix at first.
		if without != "" {
			req.URL.Path = strings.TrimPrefix(req.URL.Path, without)
			if req.URL.Opaque != "" {
				req.URL.Opaque = strings.TrimPrefix(req.URL.Opaque, without)
			}
			if req.URL.RawPath != "" {
				req.URL.RawPath = strings.TrimPrefix(req.URL.RawPath, without)
			}
		}

		hadTrailingSlash := strings.HasSuffix(req.URL.Path, "/")
		req.URL.Path = path.Join(target.Path, req.URL.Path)
		// path.Join will strip off the last /, so put it back if it was there.
		if hadTrailingSlash && !strings.HasSuffix(req.URL.Path, "/") {
			req.URL.Path = req.URL.Path + "/"
		}

		// Trims the path of the socket from the URL path.
		// This is done because req.URL passed to your proxied service
		// will have the full path of the socket file prefixed to it.
		// Calling /test on a server that proxies requests to
		// unix:/var/run/www.socket will thus set the requested path
		// to /var/run/www.socket/test, rendering paths useless.
		if target.Scheme == "unix" {
			// See comment on socketDial for the trim
			socketPrefix := target.String()[len("unix://"):]
			req.URL.Path = strings.TrimPrefix(req.URL.Path, socketPrefix)
		}

		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}
	rp := &ReverseProxy{Director: director, FlushInterval: 250 * time.Millisecond} // flushing good for streaming & server-sent events
	if target.Scheme == "unix" {
		rp.Transport = &http.Transport{
			Dial: socketDial(target.String()),
		}
	} else if keepalive != http.DefaultMaxIdleConnsPerHost {
		// if keepalive is equal to the default,
		// just use default transport, to avoid creating
		// a brand new transport
		transport := &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		}
		if keepalive == 0 {
			transport.DisableKeepAlives = true
		} else {
			transport.MaxIdleConnsPerHost = keepalive
		}
		if httpserver.HTTP2 {
			http2.ConfigureTransport(transport)
		}
		rp.Transport = transport
	}
	return rp
}

// UseInsecureTransport is used to facilitate HTTPS proxying
// when it is OK for upstream to be using a bad certificate,
// since this transport skips verification.
func (rp *ReverseProxy) UseInsecureTransport() {
	if rp.Transport == nil {
		transport := &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: 10 * time.Second,
			TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
		}
		if httpserver.HTTP2 {
			http2.ConfigureTransport(transport)
		}
		rp.Transport = transport
	} else if transport, ok := rp.Transport.(*http.Transport); ok {
		transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
		// No http2.ConfigureTransport() here.
		// For now this is only added in places where
		// an http.Transport is actually created.
	}
}

// ServeHTTP serves the proxied request to the upstream by performing a roundtrip.
// It is designed to handle websocket connection upgrades as well.
func (rp *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request, respUpdateFn respUpdateFn) error {
	transport := rp.Transport
	if requestIsWebsocket(outreq) {
		transport = newConnHijackerTransport(transport)
	} else if transport == nil {
		transport = http.DefaultTransport
	}

	rp.Director(outreq)
	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	res, err := transport.RoundTrip(outreq)
	if err != nil {
		return err
	}

	if respUpdateFn != nil {
		respUpdateFn(res)
	}
	if res.StatusCode == http.StatusSwitchingProtocols && strings.ToLower(res.Header.Get("Upgrade")) == "websocket" {
		res.Body.Close()
		hj, ok := rw.(http.Hijacker)
		if !ok {
			panic(httpserver.NonHijackerError{Underlying: rw})
		}

		conn, _, err := hj.Hijack()
		if err != nil {
			return err
		}
		defer conn.Close()

		var backendConn net.Conn
		if hj, ok := transport.(*connHijackerTransport); ok {
			backendConn = hj.Conn
			if _, err := conn.Write(hj.Replay); err != nil {
				return err
			}
			bufferPool.Put(hj.Replay)
		} else {
			backendConn, err = net.Dial("tcp", outreq.URL.Host)
			if err != nil {
				return err
			}
			outreq.Write(backendConn)
		}
		defer backendConn.Close()

		go func() {
			io.Copy(backendConn, conn) // write tcp stream to backend.
		}()
		io.Copy(conn, backendConn) // read tcp stream from backend.
	} else {
		defer res.Body.Close()
		for _, h := range hopHeaders {
			res.Header.Del(h)
		}
		copyHeader(rw.Header(), res.Header)
		rw.WriteHeader(res.StatusCode)
		rp.copyResponse(rw, res.Body)
	}

	return nil
}

func (rp *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
	buf := bufferPool.Get()
	defer bufferPool.Put(buf)

	if rp.FlushInterval != 0 {
		if wf, ok := dst.(writeFlusher); ok {
			mlw := &maxLatencyWriter{
				dst:     wf,
				latency: rp.FlushInterval,
				done:    make(chan bool),
			}
			go mlw.flushLoop()
			defer mlw.stop()
			dst = mlw
		}
	}
	io.CopyBuffer(dst, src, buf.([]byte))
}

// skip these headers if they already exist.
// see https://github.com/mholt/caddy/pull/1112#discussion_r80092582
var skipHeaders = map[string]struct{}{
	"Content-Type":        {},
	"Content-Disposition": {},
	"Accept-Ranges":       {},
	"Set-Cookie":          {},
	"Cache-Control":       {},
	"Expires":             {},
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		if _, ok := dst[k]; ok {
			// skip some predefined headers
			// see https://github.com/mholt/caddy/issues/1086
			if _, shouldSkip := skipHeaders[k]; shouldSkip {
				continue
			}
			// otherwise, overwrite
			dst.Del(k)
		}
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
	"Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te", // canonicalized version of "TE"
	"Trailers",
	"Transfer-Encoding",
	"Upgrade",
	"Alternate-Protocol",
	"Alt-Svc",
}

type respUpdateFn func(resp *http.Response)

type hijackedConn struct {
	net.Conn
	hj *connHijackerTransport
}

func (c *hijackedConn) Read(b []byte) (n int, err error) {
	n, err = c.Conn.Read(b)
	c.hj.Replay = append(c.hj.Replay, b[:n]...)
	return
}

func (c *hijackedConn) Close() error {
	return nil
}

type connHijackerTransport struct {
	*http.Transport
	Conn   net.Conn
	Replay []byte
}

func newConnHijackerTransport(base http.RoundTripper) *connHijackerTransport {
	transport := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		MaxIdleConnsPerHost: -1,
	}
	if base != nil {
		if baseTransport, ok := base.(*http.Transport); ok {
			transport.Proxy = baseTransport.Proxy
			transport.TLSClientConfig = baseTransport.TLSClientConfig
			transport.TLSHandshakeTimeout = baseTransport.TLSHandshakeTimeout
			transport.Dial = baseTransport.Dial
			transport.DialTLS = baseTransport.DialTLS
			transport.MaxIdleConnsPerHost = -1
		}
	}
	hjTransport := &connHijackerTransport{transport, nil, bufferPool.Get().([]byte)[:0]}
	oldDial := transport.Dial
	oldDialTLS := transport.DialTLS
	if oldDial == nil {
		oldDial = (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial
	}
	hjTransport.Dial = func(network, addr string) (net.Conn, error) {
		c, err := oldDial(network, addr)
		hjTransport.Conn = c
		return &hijackedConn{c, hjTransport}, err
	}
	if oldDialTLS != nil {
		hjTransport.DialTLS = func(network, addr string) (net.Conn, error) {
			c, err := oldDialTLS(network, addr)
			hjTransport.Conn = c
			return &hijackedConn{c, hjTransport}, err
		}
	}
	return hjTransport
}

func requestIsWebsocket(req *http.Request) bool {
	return !(strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade"))
}

type writeFlusher interface {
	io.Writer
	http.Flusher
}

type maxLatencyWriter struct {
	dst     writeFlusher
	latency time.Duration

	lk   sync.Mutex // protects Write + Flush
	done chan bool
}

func (m *maxLatencyWriter) Write(p []byte) (int, error) {
	m.lk.Lock()
	defer m.lk.Unlock()
	return m.dst.Write(p)
}

func (m *maxLatencyWriter) flushLoop() {
	t := time.NewTicker(m.latency)
	defer t.Stop()
	for {
		select {
		case <-m.done:
			if onExitFlushLoop != nil {
				onExitFlushLoop()
			}
			return
		case <-t.C:
			m.lk.Lock()
			m.dst.Flush()
			m.lk.Unlock()
		}
	}
}

func (m *maxLatencyWriter) stop() { m.done <- true }
<|endoftext|>


<commit_before>package gqt_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"math/rand"
	"os/exec"
	"strings"
	"sync"
	"time"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	uuid "github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Networking Uniqueness", func() {
	var (
		client *runner.RunningGarden
	)

	BeforeEach(func() {
		config.LogLevel = "error"
		client = runner.Start(config)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	It("should not allocate duplicate subnets", func() {
		skipIfDev()

		ctrl := make(chan struct{})

		go func() {
			defer GinkgoRecover()

			for {
				out, err := exec.Command("/bin/sh", "-c", "iptables-save | grep DNAT").Output()
				if err != nil {
					continue
				}

				ipInfos := parseIPTablesOutput(out)
				if len(ipInfos) == 0 {
					continue
				}

				ipSet := map[string]struct{}{}

				for _, info := range ipInfos {
					ipSet[info.Ip] = struct{}{}
				}

				Expect(ipSet).To(HaveLen(len(ipInfos)), diagnose(ipInfos, out))

				if haveToBail(ctrl) {
					break
				}
			}
		}()

		routines := 25
		containersPerRoutine := 10
		wg := &sync.WaitGroup{}
		wg.Add(routines)
		for i := 0; i < routines; i++ {
			go func() {
				defer GinkgoRecover()
				defer wg.Done()

				createAndDestroy(client, containersPerRoutine)

				if haveToBail(ctrl) {
					return
				}
			}()
		}

		wg.Wait()
		close(ctrl)
	})
})

func createAndDestroy(client *runner.RunningGarden, n int) {
	for i := 0; i < n; i++ {
		time.Sleep(randomSleepDuration())

		id, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		randomID := "net-uniq-" + id.String()
		container, err := client.Create(garden.ContainerSpec{
			Handle: randomID,
			NetIn:  []garden.NetIn{garden.NetIn{HostPort: 8080}},
		})
		Expect(err).NotTo(HaveOccurred())

		time.Sleep(randomSleepDuration())

		err = client.Destroy(container.Handle())
		Expect(err).NotTo(HaveOccurred())
	}
}

func haveToBail(ctrl chan struct{}) bool {
	select {
	case <-ctrl:
		return true
	case <-time.After(100 * time.Millisecond):
		return false
	}
}

func parseIPTablesOutput(output []byte) []containerIPInfo {
	ips := []containerIPInfo{}

	outString := string(bytes.TrimSuffix(output, []byte{'\n'}))

	portMappings := strings.Split(outString, "\n")

	for _, portMapping := range portMappings {
		fields := strings.Split(portMapping, " ")
		ip := strings.Split(fields[17], ":")[0]
		handle := fields[13]
		ips = append(ips, containerIPInfo{handle, ip})
	}

	return ips
}

func diagnose(infos []containerIPInfo, iptablesOut []byte) string {
	containerInfo := fmt.Sprintf("%#v", infos)
	infoJson, err := json.Marshal(infos)
	if err == nil {
		containerInfo = string(infoJson)
	}
	return fmt.Sprintf("IpTables Port Mapping Ruels:\n%s\nContainers Info:\n%s\n", iptablesOut, containerInfo)
}

func randomSleepDuration() time.Duration {
	random := rand.New(rand.NewSource(time.Now().UnixNano()))
	duration := time.Duration(50+random.Intn(500)) * time.Millisecond
	return duration
}

type containerIPInfo struct {
	Handle string
	Ip     string
}
<commit_msg>Filter out irrelevant iptables entries<commit_after>package gqt_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"math/rand"
	"os/exec"
	"strings"
	"sync"
	"time"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	uuid "github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const handlePrefix = "net-uniq-"

var _ = Describe("Networking Uniqueness", func() {
	var (
		client *runner.RunningGarden
	)

	BeforeEach(func() {
		config.LogLevel = "error"
		client = runner.Start(config)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	It("should not allocate duplicate subnets", func() {
		skipIfDev()

		ctrl := make(chan struct{})

		go func() {
			defer GinkgoRecover()

			for keepGoing(ctrl) {
				out, err := exec.Command("/bin/sh", "-c", "iptables-save | grep DNAT | grep "+handlePrefix).Output()
				if err != nil {
					continue
				}

				ipInfos := parseIPTablesOutput(out)
				if len(ipInfos) == 0 {
					continue
				}

				ipSet := map[string]struct{}{}

				for _, info := range ipInfos {
					ipSet[info.Ip] = struct{}{}
				}

				Expect(ipSet).To(HaveLen(len(ipInfos)), diagnose(ipInfos, out))
			}
		}()

		routines := 25
		containersPerRoutine := 10
		wg := &sync.WaitGroup{}
		wg.Add(routines)
		for i := 0; i < routines; i++ {
			go func() {
				defer GinkgoRecover()
				defer wg.Done()

				createAndDestroy(client, containersPerRoutine)
			}()
		}

		wg.Wait()
		close(ctrl)
	})
})

func createAndDestroy(client *runner.RunningGarden, n int) {
	for i := 0; i < n; i++ {
		time.Sleep(randomSleepDuration())

		id, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		randomID := handlePrefix + id.String()
		container, err := client.Create(garden.ContainerSpec{
			Handle: randomID,
			NetIn:  []garden.NetIn{garden.NetIn{HostPort: 8080}},
		})
		Expect(err).NotTo(HaveOccurred())

		time.Sleep(randomSleepDuration())

		err = client.Destroy(container.Handle())
		Expect(err).NotTo(HaveOccurred())
	}
}

func keepGoing(ctrl chan struct{}) bool {
	select {
	case <-ctrl:
		return false
	case <-time.After(100 * time.Millisecond):
		return true
	}
}

func parseIPTablesOutput(output []byte) []containerIPInfo {
	ips := []containerIPInfo{}

	outString := string(bytes.TrimSuffix(output, []byte{'\n'}))

	portMappings := strings.Split(outString, "\n")

	for _, portMapping := range portMappings {
		fields := strings.Split(portMapping, " ")
		ip := strings.Split(fields[17], ":")[0]
		handle := fields[13]
		ips = append(ips, containerIPInfo{handle, ip})
	}

	return ips
}

func diagnose(infos []containerIPInfo, iptablesOut []byte) string {
	containerInfo := fmt.Sprintf("%#v", infos)
	infoJson, err := json.Marshal(infos)
	if err == nil {
		containerInfo = string(infoJson)
	}
	return fmt.Sprintf("IpTables Port Mapping Ruels:\n%s\nContainers Info:\n%s\n", iptablesOut, containerInfo)
}

func randomSleepDuration() time.Duration {
	random := rand.New(rand.NewSource(time.Now().UnixNano()))
	duration := time.Duration(50+random.Intn(500)) * time.Millisecond
	return duration
}

type containerIPInfo struct {
	Handle string
	Ip     string
}
<|endoftext|>


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

// Package serfin
package serfin

import (
	"github.com/ctdk/goas/v2/logger"
	"github.com/ctdk/goiardi/config"
	serfclient "github.com/hashicorp/serf/client"
	"os"
	"encoding/json"
)

var Serfer *serfclient.RPCClient

// StartSerfin sets up the serf instance and starts listening for events and
// queries from other serf instances.
func StartSerfin() error {
	var err error
	Serfer, err = serfclient.NewRPCClient(config.Config.SerfAddr)
	if err != nil {
		logger.Criticalf(err.Error())
		os.Exit(1)
	}
	err = Serfer.UserEvent("goiardi-join", []byte(config.Config.Hostname), true)
	if err != nil {
		logger.Criticalf(err.Error())
		os.Exit(1)
	}
	errch := make(chan error, 1)
	go startEventMonitor(Serfer, errch)

	err = <-errch
	if err != nil {
		logger.Errorf(err.Error())
		os.Exit(1)
	}

	return nil
}

func startEventMonitor(sc *serfclient.RPCClient, errch chan<- error) {
	ch := make(chan map[string]interface{}, 1)
	sh, err := sc.Stream("*", ch)
	if err != nil {
		errch <- err
		return
	}
	errch <- nil

	defer sc.Stop(sh)
	// watch the events and queries
	for e := range ch {
		logger.Debugf("Got an event: %v", e)
	}
	return
}

func SendEvent(eventName string, payload interface{}) {
	jsonPayload, err := json.Marshal(payload)
	if err != nil {
		logger.Errorf(err.Error())
		return
	}
	err = Serfer.UserEvent(eventName, jsonPayload, true)
	if err != nil {
		logger.Debugf(err.Error())
	}
	return
}

func SendQuery(queryName string, payload interface{}) {
	jsonPayload, err := json.Marshal(payload)
	if err != nil {
		logger.Errorf(err.Error())
		return
	}
	q := &serfclient.QueryParam{ Name: queryName, Payload: jsonPayload }
	err = Serfer.Query(q)
	if err != nil {
		logger.Debugf(err.Error())
	}
	return
}
<commit_msg>Watch serf event stream for node statuses<commit_after>/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package serfin
package serfin

import (
	"github.com/ctdk/goas/v2/logger"
	"github.com/ctdk/goiardi/config"
	"github.com/ctdk/goiardi/node"
	serfclient "github.com/hashicorp/serf/client"
	"os"
	"encoding/json"
)

var Serfer *serfclient.RPCClient

// StartSerfin sets up the serf instance and starts listening for events and
// queries from other serf instances.
func StartSerfin() error {
	var err error
	Serfer, err = serfclient.NewRPCClient(config.Config.SerfAddr)
	if err != nil {
		logger.Criticalf(err.Error())
		os.Exit(1)
	}
	err = Serfer.UserEvent("goiardi-join", []byte(config.Config.Hostname), true)
	if err != nil {
		logger.Criticalf(err.Error())
		os.Exit(1)
	}
	errch := make(chan error, 1)
	go startEventMonitor(Serfer, errch)

	err = <-errch
	if err != nil {
		logger.Errorf(err.Error())
		os.Exit(1)
	}

	return nil
}

func startEventMonitor(sc *serfclient.RPCClient, errch chan<- error) {
	ch := make(chan map[string]interface{}, 1)
	sh, err := sc.Stream("*", ch)
	if err != nil {
		errch <- err
		return
	}
	errch <- nil

	defer sc.Stop(sh)
	// watch the events and queries
	for e := range ch {
		logger.Debugf("Got an event: %v", e)
		switch e["Name"].(string) {
		case "node_status":
			jsonPayload := make(map[string]string)
			err = json.Unmarshal(e["Payload"].([]byte), &jsonPayload)
			if err != nil {
				logger.Errorf(err.Error())
				continue
			}
			n, _ := node.Get(jsonPayload["node"])
			if n == nil {
				logger.Errorf("No node %s", jsonPayload["node"])
				continue
			}
			err = n.UpdateStatus(jsonPayload["status"])
			if err != nil {
				logger.Errorf(err.Error())
				continue
			}
			r := map[string]string{ "response": "ok" }
			response, _ := json.Marshal(r)
			sc.Respond(uint64(e["ID"].(int64)), response)
		}
	}
	return
}

func SendEvent(eventName string, payload interface{}) {
	jsonPayload, err := json.Marshal(payload)
	if err != nil {
		logger.Errorf(err.Error())
		return
	}
	err = Serfer.UserEvent(eventName, jsonPayload, true)
	if err != nil {
		logger.Debugf(err.Error())
	}
	return
}

func SendQuery(queryName string, payload interface{}) {
	jsonPayload, err := json.Marshal(payload)
	if err != nil {
		logger.Errorf(err.Error())
		return
	}
	q := &serfclient.QueryParam{ Name: queryName, Payload: jsonPayload }
	err = Serfer.Query(q)
	if err != nil {
		logger.Debugf(err.Error())
	}
	return
}
<|endoftext|>


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

package r3

import "gonum.org/v1/gonum/mat"

// Mat represents a 3×3 matrix. Useful for rotation matrices and such.
// The zero value is usable as the 3×3 zero matrix.
type Mat struct {
	data *array
}

var _ mat.Matrix = (*Mat)(nil)

// NewMat returns a new 3×3 matrix Mat type and populates its elements
// with values passed as argument in row-major form. If val argument
// is nil then NewMat returns a matrix filled with zeros.
func NewMat(val []float64) *Mat {
	if len(val) == 9 {
		return &Mat{arrayFrom(val)}
	}
	if val == nil {
		return &Mat{new(array)}
	}
	panic(mat.ErrShape)
}

// Dims returns the number of rows and columns of this matrix.
// This method will always return 3×3 for a Mat.
func (m *Mat) Dims() (r, c int) { return 3, 3 }

// T returns the transpose of Mat. Changes in the receiver will be reflected in the returned matrix.
func (m *Mat) T() mat.Matrix { return mat.Transpose{Matrix: m} }

// Scale multiplies the elements of a by f, placing the result in the receiver.
//
// See the mat.Scaler interface for more information.
func (m *Mat) Scale(f float64, a mat.Matrix) {
	r, c := a.Dims()
	if r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if m.data == nil {
		m.data = new(array)
	}
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			m.Set(i, j, f*a.At(i, j))
		}
	}
}

// MulVec returns the matrix-vector product M⋅v.
func (m *Mat) MulVec(v Vec) Vec {
	if m.data == nil {
		return Vec{}
	}
	return Vec{
		X: v.X*m.At(0, 0) + v.Y*m.At(0, 1) + v.Z*m.At(0, 2),
		Y: v.X*m.At(1, 0) + v.Y*m.At(1, 1) + v.Z*m.At(1, 2),
		Z: v.X*m.At(2, 0) + v.Y*m.At(2, 1) + v.Z*m.At(2, 2),
	}
}

// MulVecTrans returns the matrix-vector product Mᵀ⋅v.
func (m *Mat) MulVecTrans(v Vec) Vec {
	if m.data == nil {
		return Vec{}
	}
	return Vec{
		X: v.X*m.At(0, 0) + v.Y*m.At(1, 0) + v.Z*m.At(2, 0),
		Y: v.X*m.At(0, 1) + v.Y*m.At(1, 1) + v.Z*m.At(2, 1),
		Z: v.X*m.At(0, 2) + v.Y*m.At(1, 2) + v.Z*m.At(2, 2),
	}
}

// CloneFrom makes a copy of a into the receiver m.
// Mat expects a 3×3 input matrix.
func (m *Mat) CloneFrom(a mat.Matrix) {
	r, c := a.Dims()
	if r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if m.data == nil {
		m.data = new(array)
	}
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			m.Set(i, j, a.At(i, j))
		}
	}
}

// Sub subtracts the matrix b from a, placing the result in the receiver.
// Sub will panic if the two matrices do not have the same shape.
func (m *Mat) Sub(a, b mat.Matrix) {
	if r, c := a.Dims(); r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if r, c := b.Dims(); r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if m.data == nil {
		m.data = new(array)
	}

	m.Set(0, 0, a.At(0, 0)-b.At(0, 0))
	m.Set(0, 1, a.At(0, 1)-b.At(0, 1))
	m.Set(0, 2, a.At(0, 2)-b.At(0, 2))
	m.Set(1, 0, a.At(1, 0)-b.At(1, 0))
	m.Set(1, 1, a.At(1, 1)-b.At(1, 1))
	m.Set(1, 2, a.At(1, 2)-b.At(1, 2))
	m.Set(2, 0, a.At(2, 0)-b.At(2, 0))
	m.Set(2, 1, a.At(2, 1)-b.At(2, 1))
	m.Set(2, 2, a.At(2, 2)-b.At(2, 2))
}

// Add adds a and b element-wise, placing the result in the receiver. Add will panic if the two matrices do not have the same shape.
func (m *Mat) Add(a, b mat.Matrix) {
	if r, c := a.Dims(); r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if r, c := b.Dims(); r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if m.data == nil {
		m.data = new(array)
	}

	m.Set(0, 0, a.At(0, 0)+b.At(0, 0))
	m.Set(0, 1, a.At(0, 1)+b.At(0, 1))
	m.Set(0, 2, a.At(0, 2)+b.At(0, 2))
	m.Set(1, 0, a.At(1, 0)+b.At(1, 0))
	m.Set(1, 1, a.At(1, 1)+b.At(1, 1))
	m.Set(1, 2, a.At(1, 2)+b.At(1, 2))
	m.Set(2, 0, a.At(2, 0)+b.At(2, 0))
	m.Set(2, 1, a.At(2, 1)+b.At(2, 1))
	m.Set(2, 2, a.At(2, 2)+b.At(2, 2))
}

// VecRow returns the elements in the ith row of the receiver.
func (m *Mat) VecRow(i int) Vec {
	if i > 2 {
		panic(mat.ErrRowAccess)
	}
	if m.data == nil {
		return Vec{}
	}
	return Vec{X: m.At(i, 0), Y: m.At(i, 1), Z: m.At(i, 2)}
}

// VecCol returns the elements in the jth column of the receiver.
func (m *Mat) VecCol(j int) Vec {
	if j > 2 {
		panic(mat.ErrColAccess)
	}
	if m.data == nil {
		return Vec{}
	}
	return Vec{X: m.At(0, j), Y: m.At(1, j), Z: m.At(2, j)}
}

// Outer calculates the outer product of the vectors x and y,
// where x and y are treated as column vectors, and stores the result in the receiver.
//
//	m = alpha * x * yᵀ
func (m *Mat) Outer(alpha float64, x, y Vec) {
	ax := alpha * x.X
	ay := alpha * x.Y
	az := alpha * x.Z
	m.Set(0, 0, ax*y.X)
	m.Set(0, 1, ax*y.Y)
	m.Set(0, 2, ax*y.Z)

	m.Set(1, 0, ay*y.X)
	m.Set(1, 1, ay*y.Y)
	m.Set(1, 2, ay*y.Z)

	m.Set(2, 0, az*y.X)
	m.Set(2, 1, az*y.Y)
	m.Set(2, 2, az*y.Z)
}

// Det calculates the determinant of the receiver using the following formula
//
//	    ⎡a b c⎤
//	m = ⎢d e f⎥
//	    ⎣g h i⎦
//	det(m) = a(ei − fh) − b(di − fg) + c(dh − eg)
func (m *Mat) Det() float64 {
	a := m.At(0, 0)
	b := m.At(0, 1)
	c := m.At(0, 2)

	deta := m.At(1, 1)*m.At(2, 2) - m.At(1, 2)*m.At(2, 1)
	detb := m.At(1, 0)*m.At(2, 2) - m.At(1, 2)*m.At(2, 0)
	detc := m.At(1, 0)*m.At(2, 1) - m.At(1, 1)*m.At(2, 0)
	return a*deta - b*detb + c*detc
}

// Skew sets the receiver to the 3×3 skew symmetric matrix
// (right hand system) of v.
//
//	                ⎡ 0 -z  y⎤
//	Skew({x,y,z}) = ⎢ z  0 -x⎥
//	                ⎣-y  x  0⎦
func (m *Mat) Skew(v Vec) {
	m.Set(0, 0, 0)
	m.Set(0, 1, -v.Z)
	m.Set(0, 2, v.Y)
	m.Set(1, 0, v.Z)
	m.Set(1, 1, 0)
	m.Set(1, 2, -v.X)
	m.Set(2, 0, -v.Y)
	m.Set(2, 1, v.X)
	m.Set(2, 2, 0)
}

// Hessian sets the receiver to the Hessian matrix of the scalar field at the point p,
// approximated using finite differences with the given step sizes.
// The field is evaluated at points in the area surrounding p by adding
// at most 2 components of step to p. Hessian expects the field's second partial
// derivatives are all continuous for correct results.
func (m *Mat) Hessian(p, step Vec, field func(Vec) float64) {
	dx := Vec{X: step.X}
	dy := Vec{Y: step.Y}
	dz := Vec{Z: step.Z}
	fp := field(p)
	fxp := field(Add(p, dx))
	fxm := field(Sub(p, dx))
	fxx := (fxp - 2*fp + fxm) / (step.X * step.X)

	fyp := field(Add(p, dy))
	fym := field(Sub(p, dy))
	fyy := (fyp - 2*fp + fym) / (step.Y * step.Y)

	aux := Add(dx, dy)
	fxyp := field(Add(p, aux))
	fxym := field(Sub(p, aux))
	fxy := (fxyp - fxp - fyp + 2*fp - fxm - fym + fxym) / (2 * step.X * step.Y)

	fzp := field(Add(p, dz))
	fzm := field(Sub(p, dz))
	fzz := (fzp - 2*fp + fzm) / (step.Z * step.Z)

	aux = Add(dx, dz)
	fxzp := field(Add(p, aux))
	fxzm := field(Sub(p, aux))
	fxz := (fxzp - fxp - fzp + 2*fp - fxm - fzm + fxzm) / (2 * step.X * step.Z)

	aux = Add(dy, dz)
	fyzp := field(Add(p, aux))
	fyzm := field(Sub(p, aux))
	fyz := (fyzp - fyp - fzp + 2*fp - fym - fzm + fyzm) / (2 * step.Y * step.Z)

	m.Set(0, 0, fxx)
	m.Set(0, 1, fxy)
	m.Set(0, 2, fxz)

	m.Set(1, 0, fxy)
	m.Set(1, 1, fyy)
	m.Set(1, 2, fyz)

	m.Set(2, 0, fxz)
	m.Set(2, 1, fyz)
	m.Set(2, 2, fzz)
}

// Jacobian sets the receiver to the Jacobian matrix of the vector field at the point p,
// approximated using finite differences with the given step sizes.
// Jacobian expects the field's first order partial
// derivatives are all continuous for correct results.
func (m *Mat) Jacobian(p, step Vec, field func(Vec) Vec) {
	dx := Vec{X: step.X}
	dy := Vec{Y: step.Y}
	dz := Vec{Z: step.Z}

	dfdx := Scale(0.5/step.X, Sub(field(Add(p, dx)), field(Sub(p, dx))))
	dfdy := Scale(0.5/step.Y, Sub(field(Add(p, dy)), field(Sub(p, dy))))
	dfdz := Scale(0.5/step.Z, Sub(field(Add(p, dz)), field(Sub(p, dz))))
	m.Set(0, 0, dfdx.X)
	m.Set(0, 1, dfdy.X)
	m.Set(0, 2, dfdz.X)

	m.Set(1, 0, dfdx.Y)
	m.Set(1, 1, dfdy.Y)
	m.Set(1, 2, dfdz.Y)

	m.Set(2, 0, dfdx.Z)
	m.Set(2, 1, dfdy.Z)
	m.Set(2, 2, dfdz.Z)
}
<commit_msg>spatial/r3: add Jacobian layout description to doc comment<commit_after>// Copyright ©2021 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package r3

import "gonum.org/v1/gonum/mat"

// Mat represents a 3×3 matrix. Useful for rotation matrices and such.
// The zero value is usable as the 3×3 zero matrix.
type Mat struct {
	data *array
}

var _ mat.Matrix = (*Mat)(nil)

// NewMat returns a new 3×3 matrix Mat type and populates its elements
// with values passed as argument in row-major form. If val argument
// is nil then NewMat returns a matrix filled with zeros.
func NewMat(val []float64) *Mat {
	if len(val) == 9 {
		return &Mat{arrayFrom(val)}
	}
	if val == nil {
		return &Mat{new(array)}
	}
	panic(mat.ErrShape)
}

// Dims returns the number of rows and columns of this matrix.
// This method will always return 3×3 for a Mat.
func (m *Mat) Dims() (r, c int) { return 3, 3 }

// T returns the transpose of Mat. Changes in the receiver will be reflected in the returned matrix.
func (m *Mat) T() mat.Matrix { return mat.Transpose{Matrix: m} }

// Scale multiplies the elements of a by f, placing the result in the receiver.
//
// See the mat.Scaler interface for more information.
func (m *Mat) Scale(f float64, a mat.Matrix) {
	r, c := a.Dims()
	if r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if m.data == nil {
		m.data = new(array)
	}
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			m.Set(i, j, f*a.At(i, j))
		}
	}
}

// MulVec returns the matrix-vector product M⋅v.
func (m *Mat) MulVec(v Vec) Vec {
	if m.data == nil {
		return Vec{}
	}
	return Vec{
		X: v.X*m.At(0, 0) + v.Y*m.At(0, 1) + v.Z*m.At(0, 2),
		Y: v.X*m.At(1, 0) + v.Y*m.At(1, 1) + v.Z*m.At(1, 2),
		Z: v.X*m.At(2, 0) + v.Y*m.At(2, 1) + v.Z*m.At(2, 2),
	}
}

// MulVecTrans returns the matrix-vector product Mᵀ⋅v.
func (m *Mat) MulVecTrans(v Vec) Vec {
	if m.data == nil {
		return Vec{}
	}
	return Vec{
		X: v.X*m.At(0, 0) + v.Y*m.At(1, 0) + v.Z*m.At(2, 0),
		Y: v.X*m.At(0, 1) + v.Y*m.At(1, 1) + v.Z*m.At(2, 1),
		Z: v.X*m.At(0, 2) + v.Y*m.At(1, 2) + v.Z*m.At(2, 2),
	}
}

// CloneFrom makes a copy of a into the receiver m.
// Mat expects a 3×3 input matrix.
func (m *Mat) CloneFrom(a mat.Matrix) {
	r, c := a.Dims()
	if r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if m.data == nil {
		m.data = new(array)
	}
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			m.Set(i, j, a.At(i, j))
		}
	}
}

// Sub subtracts the matrix b from a, placing the result in the receiver.
// Sub will panic if the two matrices do not have the same shape.
func (m *Mat) Sub(a, b mat.Matrix) {
	if r, c := a.Dims(); r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if r, c := b.Dims(); r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if m.data == nil {
		m.data = new(array)
	}

	m.Set(0, 0, a.At(0, 0)-b.At(0, 0))
	m.Set(0, 1, a.At(0, 1)-b.At(0, 1))
	m.Set(0, 2, a.At(0, 2)-b.At(0, 2))
	m.Set(1, 0, a.At(1, 0)-b.At(1, 0))
	m.Set(1, 1, a.At(1, 1)-b.At(1, 1))
	m.Set(1, 2, a.At(1, 2)-b.At(1, 2))
	m.Set(2, 0, a.At(2, 0)-b.At(2, 0))
	m.Set(2, 1, a.At(2, 1)-b.At(2, 1))
	m.Set(2, 2, a.At(2, 2)-b.At(2, 2))
}

// Add adds a and b element-wise, placing the result in the receiver. Add will panic if the two matrices do not have the same shape.
func (m *Mat) Add(a, b mat.Matrix) {
	if r, c := a.Dims(); r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if r, c := b.Dims(); r != 3 || c != 3 {
		panic(mat.ErrShape)
	}
	if m.data == nil {
		m.data = new(array)
	}

	m.Set(0, 0, a.At(0, 0)+b.At(0, 0))
	m.Set(0, 1, a.At(0, 1)+b.At(0, 1))
	m.Set(0, 2, a.At(0, 2)+b.At(0, 2))
	m.Set(1, 0, a.At(1, 0)+b.At(1, 0))
	m.Set(1, 1, a.At(1, 1)+b.At(1, 1))
	m.Set(1, 2, a.At(1, 2)+b.At(1, 2))
	m.Set(2, 0, a.At(2, 0)+b.At(2, 0))
	m.Set(2, 1, a.At(2, 1)+b.At(2, 1))
	m.Set(2, 2, a.At(2, 2)+b.At(2, 2))
}

// VecRow returns the elements in the ith row of the receiver.
func (m *Mat) VecRow(i int) Vec {
	if i > 2 {
		panic(mat.ErrRowAccess)
	}
	if m.data == nil {
		return Vec{}
	}
	return Vec{X: m.At(i, 0), Y: m.At(i, 1), Z: m.At(i, 2)}
}

// VecCol returns the elements in the jth column of the receiver.
func (m *Mat) VecCol(j int) Vec {
	if j > 2 {
		panic(mat.ErrColAccess)
	}
	if m.data == nil {
		return Vec{}
	}
	return Vec{X: m.At(0, j), Y: m.At(1, j), Z: m.At(2, j)}
}

// Outer calculates the outer product of the vectors x and y,
// where x and y are treated as column vectors, and stores the result in the receiver.
//
//	m = alpha * x * yᵀ
func (m *Mat) Outer(alpha float64, x, y Vec) {
	ax := alpha * x.X
	ay := alpha * x.Y
	az := alpha * x.Z
	m.Set(0, 0, ax*y.X)
	m.Set(0, 1, ax*y.Y)
	m.Set(0, 2, ax*y.Z)

	m.Set(1, 0, ay*y.X)
	m.Set(1, 1, ay*y.Y)
	m.Set(1, 2, ay*y.Z)

	m.Set(2, 0, az*y.X)
	m.Set(2, 1, az*y.Y)
	m.Set(2, 2, az*y.Z)
}

// Det calculates the determinant of the receiver using the following formula
//
//	    ⎡a b c⎤
//	m = ⎢d e f⎥
//	    ⎣g h i⎦
//	det(m) = a(ei − fh) − b(di − fg) + c(dh − eg)
func (m *Mat) Det() float64 {
	a := m.At(0, 0)
	b := m.At(0, 1)
	c := m.At(0, 2)

	deta := m.At(1, 1)*m.At(2, 2) - m.At(1, 2)*m.At(2, 1)
	detb := m.At(1, 0)*m.At(2, 2) - m.At(1, 2)*m.At(2, 0)
	detc := m.At(1, 0)*m.At(2, 1) - m.At(1, 1)*m.At(2, 0)
	return a*deta - b*detb + c*detc
}

// Skew sets the receiver to the 3×3 skew symmetric matrix
// (right hand system) of v.
//
//	                ⎡ 0 -z  y⎤
//	Skew({x,y,z}) = ⎢ z  0 -x⎥
//	                ⎣-y  x  0⎦
func (m *Mat) Skew(v Vec) {
	m.Set(0, 0, 0)
	m.Set(0, 1, -v.Z)
	m.Set(0, 2, v.Y)
	m.Set(1, 0, v.Z)
	m.Set(1, 1, 0)
	m.Set(1, 2, -v.X)
	m.Set(2, 0, -v.Y)
	m.Set(2, 1, v.X)
	m.Set(2, 2, 0)
}

// Hessian sets the receiver to the Hessian matrix of the scalar field at the point p,
// approximated using finite differences with the given step sizes.
// The field is evaluated at points in the area surrounding p by adding
// at most 2 components of step to p. Hessian expects the field's second partial
// derivatives are all continuous for correct results.
func (m *Mat) Hessian(p, step Vec, field func(Vec) float64) {
	dx := Vec{X: step.X}
	dy := Vec{Y: step.Y}
	dz := Vec{Z: step.Z}
	fp := field(p)
	fxp := field(Add(p, dx))
	fxm := field(Sub(p, dx))
	fxx := (fxp - 2*fp + fxm) / (step.X * step.X)

	fyp := field(Add(p, dy))
	fym := field(Sub(p, dy))
	fyy := (fyp - 2*fp + fym) / (step.Y * step.Y)

	aux := Add(dx, dy)
	fxyp := field(Add(p, aux))
	fxym := field(Sub(p, aux))
	fxy := (fxyp - fxp - fyp + 2*fp - fxm - fym + fxym) / (2 * step.X * step.Y)

	fzp := field(Add(p, dz))
	fzm := field(Sub(p, dz))
	fzz := (fzp - 2*fp + fzm) / (step.Z * step.Z)

	aux = Add(dx, dz)
	fxzp := field(Add(p, aux))
	fxzm := field(Sub(p, aux))
	fxz := (fxzp - fxp - fzp + 2*fp - fxm - fzm + fxzm) / (2 * step.X * step.Z)

	aux = Add(dy, dz)
	fyzp := field(Add(p, aux))
	fyzm := field(Sub(p, aux))
	fyz := (fyzp - fyp - fzp + 2*fp - fym - fzm + fyzm) / (2 * step.Y * step.Z)

	m.Set(0, 0, fxx)
	m.Set(0, 1, fxy)
	m.Set(0, 2, fxz)

	m.Set(1, 0, fxy)
	m.Set(1, 1, fyy)
	m.Set(1, 2, fyz)

	m.Set(2, 0, fxz)
	m.Set(2, 1, fyz)
	m.Set(2, 2, fzz)
}

// Jacobian sets the receiver to the Jacobian matrix of the vector field at
// the point p, approximated using finite differences with the given step sizes.
//
// The Jacobian matrix J is the matrix of all first-order partial derivatives of f.
// If f maps an 3-dimensional vector x to an 3-dimensional vector y = f(x), J is
// a 3×3 matrix whose elements are given as
//
//	J_{i,j} = ∂f_i/∂x_j,
//
// or expanded out
//
//	    [ ∂f_1/∂x_1 ∂f_1/∂x_2 ∂f_1/∂x_3 ]
//	J = [ ∂f_2/∂x_1 ∂f_2/∂x_2 ∂f_2/∂x_3 ]
//	    [ ∂f_3/∂x_1 ∂f_3/∂x_2 ∂f_3/∂x_3 ]
//
// Jacobian expects the field's first order partial derivatives are all
// continuous for correct results.
func (m *Mat) Jacobian(p, step Vec, field func(Vec) Vec) {
	dx := Vec{X: step.X}
	dy := Vec{Y: step.Y}
	dz := Vec{Z: step.Z}

	dfdx := Scale(0.5/step.X, Sub(field(Add(p, dx)), field(Sub(p, dx))))
	dfdy := Scale(0.5/step.Y, Sub(field(Add(p, dy)), field(Sub(p, dy))))
	dfdz := Scale(0.5/step.Z, Sub(field(Add(p, dz)), field(Sub(p, dz))))
	m.Set(0, 0, dfdx.X)
	m.Set(0, 1, dfdy.X)
	m.Set(0, 2, dfdz.X)

	m.Set(1, 0, dfdx.Y)
	m.Set(1, 1, dfdy.Y)
	m.Set(1, 2, dfdz.Y)

	m.Set(2, 0, dfdx.Z)
	m.Set(2, 1, dfdy.Z)
	m.Set(2, 2, dfdz.Z)
}
<|endoftext|>


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

package ssh

import (
	"bytes"
	"crypto"
	"crypto/aes"
	"crypto/rand"
	"testing"
)

func TestDefaultCiphersExist(t *testing.T) {
	for _, cipherAlgo := range supportedCiphers {
		if _, ok := cipherModes[cipherAlgo]; !ok {
			t.Errorf("default cipher %q is unknown", cipherAlgo)
		}
	}
}

func TestPacketCiphers(t *testing.T) {
	// Still test aes128cbc cipher althought it's commented out.
	cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil}
	defer delete(cipherModes, aes128cbcID)

	for cipher := range cipherModes {
		kr := &kexResult{Hash: crypto.SHA1}
		algs := directionAlgorithms{
			Cipher:      cipher,
			MAC:         "hmac-sha1",
			Compression: "none",
		}
		client, err := newPacketCipher(clientKeys, algs, kr)
		if err != nil {
			t.Errorf("newPacketCipher(client, %q): %v", cipher, err)
			continue
		}
		server, err := newPacketCipher(clientKeys, algs, kr)
		if err != nil {
			t.Errorf("newPacketCipher(client, %q): %v", cipher, err)
			continue
		}

		want := "bla bla"
		input := []byte(want)
		buf := &bytes.Buffer{}
		if err := client.writePacket(0, buf, rand.Reader, input); err != nil {
			t.Errorf("writePacket(%q): %v", cipher, err)
			continue
		}

		packet, err := server.readPacket(0, buf)
		if err != nil {
			t.Errorf("readPacket(%q): %v", cipher, err)
			continue
		}

		if string(packet) != want {
			t.Errorf("roundtrip(%q): got %q, want %q", cipher, packet, want)
		}
	}
}

func TestCBCOracleCounterMeasure(t *testing.T) {
	cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil}
	defer delete(cipherModes, aes128cbcID)

	kr := &kexResult{Hash: crypto.SHA1}
	algs := directionAlgorithms{
		Cipher:      aes128cbcID,
		MAC:         "hmac-sha1",
		Compression: "none",
	}
	client, err := newPacketCipher(clientKeys, algs, kr)
	if err != nil {
		t.Fatalf("newPacketCipher(client): %v", err)
	}

	want := "bla bla"
	input := []byte(want)
	buf := &bytes.Buffer{}
	if err := client.writePacket(0, buf, rand.Reader, input); err != nil {
		t.Errorf("writePacket: %v", err)
	}

	packetSize := buf.Len()
	buf.Write(make([]byte, 2*maxPacket))

	// We corrupt each byte, but this usually will only test the
	// 'packet too large' or 'MAC failure' cases.
	lastRead := -1
	for i := 0; i < packetSize; i++ {
		server, err := newPacketCipher(clientKeys, algs, kr)
		if err != nil {
			t.Fatalf("newPacketCipher(client): %v", err)
		}

		fresh := &bytes.Buffer{}
		fresh.Write(buf.Bytes())
		fresh.Bytes()[i] ^= 0x01

		before := fresh.Len()
		_, err = server.readPacket(0, fresh)
		if err == nil {
			t.Errorf("corrupt byte %d: readPacket succeeded ", i)
			continue
		}
		if _, ok := err.(cbcError); !ok {
			t.Errorf("corrupt byte %d: got %v (%T), want cbcError", i, err, err)
			continue
		}

		after := fresh.Len()
		bytesRead := before - after
		if bytesRead < maxPacket {
			t.Errorf("corrupt byte %d: read %d bytes, want more than %d", i, bytesRead, maxPacket)
			continue
		}

		if i > 0 && bytesRead != lastRead {
			t.Errorf("corrupt byte %d: want %d, got %d bytes read", bytesRead, lastRead)
		}
		lastRead = bytesRead
	}
}
<commit_msg>crypto/ssh: fix format string error in test.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ssh

import (
	"bytes"
	"crypto"
	"crypto/aes"
	"crypto/rand"
	"testing"
)

func TestDefaultCiphersExist(t *testing.T) {
	for _, cipherAlgo := range supportedCiphers {
		if _, ok := cipherModes[cipherAlgo]; !ok {
			t.Errorf("default cipher %q is unknown", cipherAlgo)
		}
	}
}

func TestPacketCiphers(t *testing.T) {
	// Still test aes128cbc cipher althought it's commented out.
	cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil}
	defer delete(cipherModes, aes128cbcID)

	for cipher := range cipherModes {
		kr := &kexResult{Hash: crypto.SHA1}
		algs := directionAlgorithms{
			Cipher:      cipher,
			MAC:         "hmac-sha1",
			Compression: "none",
		}
		client, err := newPacketCipher(clientKeys, algs, kr)
		if err != nil {
			t.Errorf("newPacketCipher(client, %q): %v", cipher, err)
			continue
		}
		server, err := newPacketCipher(clientKeys, algs, kr)
		if err != nil {
			t.Errorf("newPacketCipher(client, %q): %v", cipher, err)
			continue
		}

		want := "bla bla"
		input := []byte(want)
		buf := &bytes.Buffer{}
		if err := client.writePacket(0, buf, rand.Reader, input); err != nil {
			t.Errorf("writePacket(%q): %v", cipher, err)
			continue
		}

		packet, err := server.readPacket(0, buf)
		if err != nil {
			t.Errorf("readPacket(%q): %v", cipher, err)
			continue
		}

		if string(packet) != want {
			t.Errorf("roundtrip(%q): got %q, want %q", cipher, packet, want)
		}
	}
}

func TestCBCOracleCounterMeasure(t *testing.T) {
	cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil}
	defer delete(cipherModes, aes128cbcID)

	kr := &kexResult{Hash: crypto.SHA1}
	algs := directionAlgorithms{
		Cipher:      aes128cbcID,
		MAC:         "hmac-sha1",
		Compression: "none",
	}
	client, err := newPacketCipher(clientKeys, algs, kr)
	if err != nil {
		t.Fatalf("newPacketCipher(client): %v", err)
	}

	want := "bla bla"
	input := []byte(want)
	buf := &bytes.Buffer{}
	if err := client.writePacket(0, buf, rand.Reader, input); err != nil {
		t.Errorf("writePacket: %v", err)
	}

	packetSize := buf.Len()
	buf.Write(make([]byte, 2*maxPacket))

	// We corrupt each byte, but this usually will only test the
	// 'packet too large' or 'MAC failure' cases.
	lastRead := -1
	for i := 0; i < packetSize; i++ {
		server, err := newPacketCipher(clientKeys, algs, kr)
		if err != nil {
			t.Fatalf("newPacketCipher(client): %v", err)
		}

		fresh := &bytes.Buffer{}
		fresh.Write(buf.Bytes())
		fresh.Bytes()[i] ^= 0x01

		before := fresh.Len()
		_, err = server.readPacket(0, fresh)
		if err == nil {
			t.Errorf("corrupt byte %d: readPacket succeeded ", i)
			continue
		}
		if _, ok := err.(cbcError); !ok {
			t.Errorf("corrupt byte %d: got %v (%T), want cbcError", i, err, err)
			continue
		}

		after := fresh.Len()
		bytesRead := before - after
		if bytesRead < maxPacket {
			t.Errorf("corrupt byte %d: read %d bytes, want more than %d", i, bytesRead, maxPacket)
			continue
		}

		if i > 0 && bytesRead != lastRead {
			t.Errorf("corrupt byte %d: read %d bytes, want %d bytes read", i, bytesRead, lastRead)
		}
		lastRead = bytesRead
	}
}
<|endoftext|>