<commit_before>// Copyright 2019 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 cache

import (
	"bytes"
	"context"
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"path"
	"sort"
	"strings"
	"sync"

	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/lsp/telemetry"
	"golang.org/x/tools/internal/memoize"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/telemetry/log"
	"golang.org/x/tools/internal/telemetry/trace"
	errors "golang.org/x/xerrors"
)

type packageHandleKey string

// packageHandle implements source.PackageHandle.
type packageHandle struct {
	handle *memoize.Handle

	goFiles []source.ParseGoHandle

	// compiledGoFiles are the ParseGoHandles that compose the package.
	compiledGoFiles []source.ParseGoHandle

	// mode is the mode the the files were parsed in.
	mode source.ParseMode

	// m is the metadata associated with the package.
	m *metadata

	// key is the hashed key for the package.
	key packageHandleKey
}

func (ph *packageHandle) packageKey() packageKey {
	return packageKey{
		id:   ph.m.id,
		mode: ph.mode,
	}
}

// packageData contains the data produced by type-checking a package.
type packageData struct {
	memoize.NoCopy

	pkg *pkg
	err error
}

// buildPackageHandle returns a source.PackageHandle for a given package and config.
func (s *snapshot) buildPackageHandle(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, error) {
	if ph := s.getPackage(id, mode); ph != nil {
		return ph, nil
	}

	// Build the PackageHandle for this ID and its dependencies.
	ph, deps, err := s.buildKey(ctx, id, mode)
	if err != nil {
		return nil, err
	}

	// Do not close over the packageHandle or the snapshot in the Bind function.
	// This creates a cycle, which causes the finalizers to never run on the handles.
	// The possible cycles are:
	//
	//     packageHandle.h.function -> packageHandle
	//     packageHandle.h.function -> snapshot -> packageHandle
	//

	m := ph.m
	goFiles := ph.goFiles
	compiledGoFiles := ph.compiledGoFiles
	key := ph.key
	fset := s.view.session.cache.fset

	h := s.view.session.cache.store.Bind(key, func(ctx context.Context) interface{} {
		// Begin loading the direct dependencies, in parallel.
		for _, dep := range deps {
			go func(dep *packageHandle) {
				dep.check(ctx)
			}(dep)
		}
		data := &packageData{}
		data.pkg, data.err = typeCheck(ctx, fset, m, mode, goFiles, compiledGoFiles, deps)
		return data
	})
	ph.handle = h

	// Cache the PackageHandle in the snapshot.
	s.addPackage(ph)

	return ph, nil
}

// buildKey computes the key for a given packageHandle.
func (s *snapshot) buildKey(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, map[packagePath]*packageHandle, error) {
	m := s.getMetadata(id)
	if m == nil {
		return nil, nil, errors.Errorf("no metadata for %s", id)
	}
	goFiles, err := s.parseGoHandles(ctx, m.goFiles, mode)
	if err != nil {
		return nil, nil, err
	}
	compiledGoFiles, err := s.parseGoHandles(ctx, m.compiledGoFiles, mode)
	if err != nil {
		return nil, nil, err
	}
	ph := &packageHandle{
		m:               m,
		goFiles:         goFiles,
		compiledGoFiles: compiledGoFiles,
		mode:            mode,
	}
	// Make sure all of the depList are sorted.
	depList := append([]packageID{}, m.deps...)
	sort.Slice(depList, func(i, j int) bool {
		return depList[i] < depList[j]
	})

	deps := make(map[packagePath]*packageHandle)

	// Begin computing the key by getting the depKeys for all dependencies.
	var depKeys []packageHandleKey
	for _, depID := range depList {
		mode := source.ParseExported
		if _, ok := s.isWorkspacePackage(depID); ok {
			mode = source.ParseFull
		}
		depHandle, err := s.buildPackageHandle(ctx, depID, mode)
		if err != nil {
			log.Error(ctx, "no dep handle", err, telemetry.Package.Of(depID))

			// One bad dependency should not prevent us from checking the entire package.
			// Add a special key to mark a bad dependency.
			depKeys = append(depKeys, packageHandleKey(fmt.Sprintf("%s import not found", id)))
			continue
		}
		deps[depHandle.m.pkgPath] = depHandle
		depKeys = append(depKeys, depHandle.key)
	}
	ph.key = checkPackageKey(ph.m.id, ph.compiledGoFiles, m.config, depKeys)
	return ph, deps, nil
}

func checkPackageKey(id packageID, pghs []source.ParseGoHandle, cfg *packages.Config, deps []packageHandleKey) packageHandleKey {
	var depBytes []byte
	for _, dep := range deps {
		depBytes = append(depBytes, []byte(dep)...)
	}
	return packageHandleKey(hashContents([]byte(fmt.Sprintf("%s%s%s%s", id, hashParseKeys(pghs), hashConfig(cfg), hashContents(depBytes)))))
}

// hashConfig returns the hash for the *packages.Config.
func hashConfig(config *packages.Config) string {
	b := bytes.NewBuffer(nil)

	// Dir, Mode, Env, BuildFlags are the parts of the config that can change.
	b.WriteString(config.Dir)
	b.WriteString(string(config.Mode))

	for _, e := range config.Env {
		b.WriteString(e)
	}
	for _, f := range config.BuildFlags {
		b.WriteString(f)
	}
	return hashContents(b.Bytes())
}

func (ph *packageHandle) Check(ctx context.Context) (source.Package, error) {
	return ph.check(ctx)
}

func (ph *packageHandle) check(ctx context.Context) (*pkg, error) {
	v := ph.handle.Get(ctx)
	if v == nil {
		return nil, ctx.Err()
	}
	data := v.(*packageData)
	return data.pkg, data.err
}

func (ph *packageHandle) CompiledGoFiles() []source.ParseGoHandle {
	return ph.compiledGoFiles
}

func (ph *packageHandle) ID() string {
	return string(ph.m.id)
}

func (ph *packageHandle) MissingDependencies() []string {
	var md []string
	for i := range ph.m.missingDeps {
		md = append(md, string(i))
	}
	return md
}

func hashImports(ctx context.Context, wsPackages []source.PackageHandle) (string, error) {
	results := make(map[string]bool)
	var imports []string
	for _, ph := range wsPackages {
		// Check package since we do not always invalidate the metadata.
		pkg, err := ph.Check(ctx)
		if err != nil {
			return "", err
		}
		for _, path := range pkg.Imports() {
			imp := path.PkgPath()
			if _, ok := results[imp]; !ok {
				results[imp] = true
				imports = append(imports, imp)
			}
		}
	}
	sort.Strings(imports)
	hashed := strings.Join(imports, ",")
	return hashContents([]byte(hashed)), nil
}

func (ph *packageHandle) Cached() (source.Package, error) {
	return ph.cached()
}

func (ph *packageHandle) cached() (*pkg, error) {
	v := ph.handle.Cached()
	if v == nil {
		return nil, errors.Errorf("no cached type information for %s", ph.m.pkgPath)
	}
	data := v.(*packageData)
	return data.pkg, data.err
}

func (s *snapshot) parseGoHandles(ctx context.Context, files []span.URI, mode source.ParseMode) ([]source.ParseGoHandle, error) {
	phs := make([]source.ParseGoHandle, 0, len(files))
	for _, uri := range files {
		fh, err := s.GetFile(uri)
		if err != nil {
			return nil, err
		}
		phs = append(phs, s.view.session.cache.ParseGoHandle(fh, mode))
	}
	return phs, nil
}

func typeCheck(ctx context.Context, fset *token.FileSet, m *metadata, mode source.ParseMode, goFiles []source.ParseGoHandle, compiledGoFiles []source.ParseGoHandle, deps map[packagePath]*packageHandle) (*pkg, error) {
	ctx, done := trace.StartSpan(ctx, "cache.importer.typeCheck", telemetry.Package.Of(m.id))
	defer done()

	var rawErrors []error
	for _, err := range m.errors {
		rawErrors = append(rawErrors, err)
	}

	pkg := &pkg{
		id:              m.id,
		pkgPath:         m.pkgPath,
		mode:            mode,
		goFiles:         goFiles,
		compiledGoFiles: compiledGoFiles,
		module:          m.module,
		imports:         make(map[packagePath]*pkg),
		typesSizes:      m.typesSizes,
		typesInfo: &types.Info{
			Types:      make(map[ast.Expr]types.TypeAndValue),
			Defs:       make(map[*ast.Ident]types.Object),
			Uses:       make(map[*ast.Ident]types.Object),
			Implicits:  make(map[ast.Node]types.Object),
			Selections: make(map[*ast.SelectorExpr]*types.Selection),
			Scopes:     make(map[ast.Node]*types.Scope),
		},
		forTest: m.forTest,
	}
	var (
		files        = make([]*ast.File, len(pkg.compiledGoFiles))
		parseErrors  = make([]error, len(pkg.compiledGoFiles))
		actualErrors = make([]error, len(pkg.compiledGoFiles))
		wg           sync.WaitGroup
	)
	for i, ph := range pkg.compiledGoFiles {
		wg.Add(1)
		go func(i int, ph source.ParseGoHandle) {
			files[i], _, _, parseErrors[i], actualErrors[i] = ph.Parse(ctx)
			wg.Done()
		}(i, ph)
	}
	for _, ph := range pkg.goFiles {
		wg.Add(1)
		// We need to parse the non-compiled go files, but we don't care about their errors.
		go func(ph source.ParseGoHandle) {
			ph.Parse(ctx)
			wg.Done()
		}(ph)
	}
	wg.Wait()

	for _, e := range parseErrors {
		if e != nil {
			rawErrors = append(rawErrors, e)
		}
	}

	var i int
	for _, f := range files {
		if f != nil {
			files[i] = f
			i++
		}
	}
	files = files[:i]

	// Use the default type information for the unsafe package.
	if pkg.pkgPath == "unsafe" {
		pkg.types = types.Unsafe
		// Don't type check Unsafe: it's unnecessary, and doing so exposes a data
		// race to Unsafe.completed.
		return pkg, nil
	} else if len(files) == 0 { // not the unsafe package, no parsed files
		return nil, errors.Errorf("no parsed files for package %s, expected: %s, errors: %v, list errors: %v", pkg.pkgPath, pkg.compiledGoFiles, actualErrors, rawErrors)
	} else {
		pkg.types = types.NewPackage(string(m.pkgPath), m.name)
	}

	cfg := &types.Config{
		Error: func(e error) {
			rawErrors = append(rawErrors, e)
		},
		Importer: importerFunc(func(pkgPath string) (*types.Package, error) {
			// If the context was cancelled, we should abort.
			if ctx.Err() != nil {
				return nil, ctx.Err()
			}
			dep := deps[packagePath(pkgPath)]
			if dep == nil {
				// We may be in GOPATH mode, in which case we need to check vendor dirs.
				searchDir := path.Dir(pkg.PkgPath())
				for {
					vdir := packagePath(path.Join(searchDir, "vendor", pkgPath))
					if vdep := deps[vdir]; vdep != nil {
						dep = vdep
						break
					}

					// Search until Dir doesn't take us anywhere new, e.g. "." or "/".
					next := path.Dir(searchDir)
					if searchDir == next {
						break
					}
					searchDir = next
				}
			}
			if dep == nil {
				return nil, errors.Errorf("no package for import %s", pkgPath)
			}
			depPkg, err := dep.check(ctx)
			if err != nil {
				return nil, err
			}
			pkg.imports[depPkg.pkgPath] = depPkg
			return depPkg.types, nil
		}),
	}
	check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo)

	// Type checking errors are handled via the config, so ignore them here.
	_ = check.Files(files)
	// If the context was cancelled, we may have returned a ton of transient
	// errors to the type checker. Swallow them.
	if ctx.Err() != nil {
		return nil, ctx.Err()
	}

	// We don't care about a package's errors unless we have parsed it in full.
	if mode == source.ParseFull {
		for _, e := range rawErrors {
			srcErr, err := sourceError(ctx, fset, pkg, e)
			if err != nil {
				log.Error(ctx, "unable to compute error positions", err, telemetry.Package.Of(pkg.ID()))
				continue
			}
			pkg.errors = append(pkg.errors, srcErr)
		}
	}
	return pkg, nil
}

// An importFunc is an implementation of the single-method
// types.Importer interface based on a function value.
type importerFunc func(path string) (*types.Package, error)

func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
<commit_msg>internal/lsp: report use of disallowed internal packages<commit_after>// Copyright 2019 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 cache

import (
	"bytes"
	"context"
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"path"
	"sort"
	"strings"
	"sync"

	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/lsp/telemetry"
	"golang.org/x/tools/internal/memoize"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/telemetry/log"
	"golang.org/x/tools/internal/telemetry/trace"
	errors "golang.org/x/xerrors"
)

type packageHandleKey string

// packageHandle implements source.PackageHandle.
type packageHandle struct {
	handle *memoize.Handle

	goFiles []source.ParseGoHandle

	// compiledGoFiles are the ParseGoHandles that compose the package.
	compiledGoFiles []source.ParseGoHandle

	// mode is the mode the the files were parsed in.
	mode source.ParseMode

	// m is the metadata associated with the package.
	m *metadata

	// key is the hashed key for the package.
	key packageHandleKey
}

func (ph *packageHandle) packageKey() packageKey {
	return packageKey{
		id:   ph.m.id,
		mode: ph.mode,
	}
}

func (ph *packageHandle) isValidImportFor(parentPkgPath string) bool {
	importPath := string(ph.m.pkgPath)

	pkgRootIndex := strings.Index(importPath, "/internal/")
	if pkgRootIndex != -1 && parentPkgPath != "command-line-arguments" {
		if !strings.HasPrefix(parentPkgPath, importPath[0:pkgRootIndex]) {
			return false
		}
	}

	return true
}

// packageData contains the data produced by type-checking a package.
type packageData struct {
	memoize.NoCopy

	pkg *pkg
	err error
}

// buildPackageHandle returns a source.PackageHandle for a given package and config.
func (s *snapshot) buildPackageHandle(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, error) {
	if ph := s.getPackage(id, mode); ph != nil {
		return ph, nil
	}

	// Build the PackageHandle for this ID and its dependencies.
	ph, deps, err := s.buildKey(ctx, id, mode)
	if err != nil {
		return nil, err
	}

	// Do not close over the packageHandle or the snapshot in the Bind function.
	// This creates a cycle, which causes the finalizers to never run on the handles.
	// The possible cycles are:
	//
	//     packageHandle.h.function -> packageHandle
	//     packageHandle.h.function -> snapshot -> packageHandle
	//

	m := ph.m
	goFiles := ph.goFiles
	compiledGoFiles := ph.compiledGoFiles
	key := ph.key
	fset := s.view.session.cache.fset

	h := s.view.session.cache.store.Bind(key, func(ctx context.Context) interface{} {
		// Begin loading the direct dependencies, in parallel.
		for _, dep := range deps {
			go func(dep *packageHandle) {
				dep.check(ctx)
			}(dep)
		}
		data := &packageData{}
		data.pkg, data.err = typeCheck(ctx, fset, m, mode, goFiles, compiledGoFiles, deps)
		return data
	})
	ph.handle = h

	// Cache the PackageHandle in the snapshot.
	s.addPackage(ph)

	return ph, nil
}

// buildKey computes the key for a given packageHandle.
func (s *snapshot) buildKey(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, map[packagePath]*packageHandle, error) {
	m := s.getMetadata(id)
	if m == nil {
		return nil, nil, errors.Errorf("no metadata for %s", id)
	}
	goFiles, err := s.parseGoHandles(ctx, m.goFiles, mode)
	if err != nil {
		return nil, nil, err
	}
	compiledGoFiles, err := s.parseGoHandles(ctx, m.compiledGoFiles, mode)
	if err != nil {
		return nil, nil, err
	}
	ph := &packageHandle{
		m:               m,
		goFiles:         goFiles,
		compiledGoFiles: compiledGoFiles,
		mode:            mode,
	}
	// Make sure all of the depList are sorted.
	depList := append([]packageID{}, m.deps...)
	sort.Slice(depList, func(i, j int) bool {
		return depList[i] < depList[j]
	})

	deps := make(map[packagePath]*packageHandle)

	// Begin computing the key by getting the depKeys for all dependencies.
	var depKeys []packageHandleKey
	for _, depID := range depList {
		mode := source.ParseExported
		if _, ok := s.isWorkspacePackage(depID); ok {
			mode = source.ParseFull
		}
		depHandle, err := s.buildPackageHandle(ctx, depID, mode)
		if err != nil {
			log.Error(ctx, "no dep handle", err, telemetry.Package.Of(depID))

			// One bad dependency should not prevent us from checking the entire package.
			// Add a special key to mark a bad dependency.
			depKeys = append(depKeys, packageHandleKey(fmt.Sprintf("%s import not found", id)))
			continue
		}
		deps[depHandle.m.pkgPath] = depHandle
		depKeys = append(depKeys, depHandle.key)
	}
	ph.key = checkPackageKey(ph.m.id, ph.compiledGoFiles, m.config, depKeys)
	return ph, deps, nil
}

func checkPackageKey(id packageID, pghs []source.ParseGoHandle, cfg *packages.Config, deps []packageHandleKey) packageHandleKey {
	var depBytes []byte
	for _, dep := range deps {
		depBytes = append(depBytes, []byte(dep)...)
	}
	return packageHandleKey(hashContents([]byte(fmt.Sprintf("%s%s%s%s", id, hashParseKeys(pghs), hashConfig(cfg), hashContents(depBytes)))))
}

// hashConfig returns the hash for the *packages.Config.
func hashConfig(config *packages.Config) string {
	b := bytes.NewBuffer(nil)

	// Dir, Mode, Env, BuildFlags are the parts of the config that can change.
	b.WriteString(config.Dir)
	b.WriteString(string(config.Mode))

	for _, e := range config.Env {
		b.WriteString(e)
	}
	for _, f := range config.BuildFlags {
		b.WriteString(f)
	}
	return hashContents(b.Bytes())
}

func (ph *packageHandle) Check(ctx context.Context) (source.Package, error) {
	return ph.check(ctx)
}

func (ph *packageHandle) check(ctx context.Context) (*pkg, error) {
	v := ph.handle.Get(ctx)
	if v == nil {
		return nil, ctx.Err()
	}
	data := v.(*packageData)
	return data.pkg, data.err
}

func (ph *packageHandle) CompiledGoFiles() []source.ParseGoHandle {
	return ph.compiledGoFiles
}

func (ph *packageHandle) ID() string {
	return string(ph.m.id)
}

func (ph *packageHandle) MissingDependencies() []string {
	var md []string
	for i := range ph.m.missingDeps {
		md = append(md, string(i))
	}
	return md
}

func hashImports(ctx context.Context, wsPackages []source.PackageHandle) (string, error) {
	results := make(map[string]bool)
	var imports []string
	for _, ph := range wsPackages {
		// Check package since we do not always invalidate the metadata.
		pkg, err := ph.Check(ctx)
		if err != nil {
			return "", err
		}
		for _, path := range pkg.Imports() {
			imp := path.PkgPath()
			if _, ok := results[imp]; !ok {
				results[imp] = true
				imports = append(imports, imp)
			}
		}
	}
	sort.Strings(imports)
	hashed := strings.Join(imports, ",")
	return hashContents([]byte(hashed)), nil
}

func (ph *packageHandle) Cached() (source.Package, error) {
	return ph.cached()
}

func (ph *packageHandle) cached() (*pkg, error) {
	v := ph.handle.Cached()
	if v == nil {
		return nil, errors.Errorf("no cached type information for %s", ph.m.pkgPath)
	}
	data := v.(*packageData)
	return data.pkg, data.err
}

func (s *snapshot) parseGoHandles(ctx context.Context, files []span.URI, mode source.ParseMode) ([]source.ParseGoHandle, error) {
	phs := make([]source.ParseGoHandle, 0, len(files))
	for _, uri := range files {
		fh, err := s.GetFile(uri)
		if err != nil {
			return nil, err
		}
		phs = append(phs, s.view.session.cache.ParseGoHandle(fh, mode))
	}
	return phs, nil
}

func typeCheck(ctx context.Context, fset *token.FileSet, m *metadata, mode source.ParseMode, goFiles []source.ParseGoHandle, compiledGoFiles []source.ParseGoHandle, deps map[packagePath]*packageHandle) (*pkg, error) {
	ctx, done := trace.StartSpan(ctx, "cache.importer.typeCheck", telemetry.Package.Of(m.id))
	defer done()

	var rawErrors []error
	for _, err := range m.errors {
		rawErrors = append(rawErrors, err)
	}

	pkg := &pkg{
		id:              m.id,
		pkgPath:         m.pkgPath,
		mode:            mode,
		goFiles:         goFiles,
		compiledGoFiles: compiledGoFiles,
		module:          m.module,
		imports:         make(map[packagePath]*pkg),
		typesSizes:      m.typesSizes,
		typesInfo: &types.Info{
			Types:      make(map[ast.Expr]types.TypeAndValue),
			Defs:       make(map[*ast.Ident]types.Object),
			Uses:       make(map[*ast.Ident]types.Object),
			Implicits:  make(map[ast.Node]types.Object),
			Selections: make(map[*ast.SelectorExpr]*types.Selection),
			Scopes:     make(map[ast.Node]*types.Scope),
		},
		forTest: m.forTest,
	}
	var (
		files        = make([]*ast.File, len(pkg.compiledGoFiles))
		parseErrors  = make([]error, len(pkg.compiledGoFiles))
		actualErrors = make([]error, len(pkg.compiledGoFiles))
		wg           sync.WaitGroup
	)
	for i, ph := range pkg.compiledGoFiles {
		wg.Add(1)
		go func(i int, ph source.ParseGoHandle) {
			files[i], _, _, parseErrors[i], actualErrors[i] = ph.Parse(ctx)
			wg.Done()
		}(i, ph)
	}
	for _, ph := range pkg.goFiles {
		wg.Add(1)
		// We need to parse the non-compiled go files, but we don't care about their errors.
		go func(ph source.ParseGoHandle) {
			ph.Parse(ctx)
			wg.Done()
		}(ph)
	}
	wg.Wait()

	for _, e := range parseErrors {
		if e != nil {
			rawErrors = append(rawErrors, e)
		}
	}

	var i int
	for _, f := range files {
		if f != nil {
			files[i] = f
			i++
		}
	}
	files = files[:i]

	// Use the default type information for the unsafe package.
	if pkg.pkgPath == "unsafe" {
		pkg.types = types.Unsafe
		// Don't type check Unsafe: it's unnecessary, and doing so exposes a data
		// race to Unsafe.completed.
		return pkg, nil
	} else if len(files) == 0 { // not the unsafe package, no parsed files
		return nil, errors.Errorf("no parsed files for package %s, expected: %s, errors: %v, list errors: %v", pkg.pkgPath, pkg.compiledGoFiles, actualErrors, rawErrors)
	} else {
		pkg.types = types.NewPackage(string(m.pkgPath), m.name)
	}

	cfg := &types.Config{
		Error: func(e error) {
			rawErrors = append(rawErrors, e)
		},
		Importer: importerFunc(func(pkgPath string) (*types.Package, error) {
			// If the context was cancelled, we should abort.
			if ctx.Err() != nil {
				return nil, ctx.Err()
			}
			dep := deps[packagePath(pkgPath)]
			if dep == nil {
				// We may be in GOPATH mode, in which case we need to check vendor dirs.
				searchDir := path.Dir(pkg.PkgPath())
				for {
					vdir := packagePath(path.Join(searchDir, "vendor", pkgPath))
					if vdep := deps[vdir]; vdep != nil {
						dep = vdep
						break
					}

					// Search until Dir doesn't take us anywhere new, e.g. "." or "/".
					next := path.Dir(searchDir)
					if searchDir == next {
						break
					}
					searchDir = next
				}
			}
			if dep == nil {
				return nil, errors.Errorf("no package for import %s", pkgPath)
			}
			if !dep.isValidImportFor(pkg.PkgPath()) {
				return nil, errors.Errorf("invalid use of internal package %s", pkgPath)
			}
			depPkg, err := dep.check(ctx)
			if err != nil {
				return nil, err
			}
			pkg.imports[depPkg.pkgPath] = depPkg
			return depPkg.types, nil
		}),
	}
	check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo)

	// Type checking errors are handled via the config, so ignore them here.
	_ = check.Files(files)
	// If the context was cancelled, we may have returned a ton of transient
	// errors to the type checker. Swallow them.
	if ctx.Err() != nil {
		return nil, ctx.Err()
	}

	// We don't care about a package's errors unless we have parsed it in full.
	if mode == source.ParseFull {
		for _, e := range rawErrors {
			srcErr, err := sourceError(ctx, fset, pkg, e)
			if err != nil {
				log.Error(ctx, "unable to compute error positions", err, telemetry.Package.Of(pkg.ID()))
				continue
			}
			pkg.errors = append(pkg.errors, srcErr)
		}
	}
	return pkg, nil
}

// An importFunc is an implementation of the single-method
// types.Importer interface based on a function value.
type importerFunc func(path string) (*types.Package, error)

func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
<|endoftext|>


<commit_before>package oidc

/*

func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients: []schema.OpenIDConnectClientConfiguration{
			{
				ID:          "myclient",
				Description: "myclient desc",
				Policy:      "one_factor",
				Scopes:      []string{"openid", "profile"},
				Secret:      "mysecret",
			},
			{
				ID:          "myotherclient",
				Description: "myclient desc",
				Policy:      "two_factor",
				Scopes:      []string{"openid", "profile"},
				Secret:      "mysecret",
			},
		},
	})

	policyOne := s.GetClientPolicy("myclient")
	assert.Equal(t, authorization.OneFactor, policyOne)

	policyTwo := s.GetClientPolicy("myotherclient")
	assert.Equal(t, authorization.TwoFactor, policyTwo)

	policyInvalid := s.GetClientPolicy("invalidclient")
	assert.Equal(t, authorization.TwoFactor, policyInvalid)
}

func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients: []schema.OpenIDConnectClientConfiguration{
			{
				ID:          "myclient",
				Description: "myclient desc",
				Policy:      "one_factor",
				Scopes:      []string{"openid", "profile"},
				Secret:      "mysecret",
			},
		},
	})

	client, err := s.GetClient(context.Background(), "myinvalidclient")
	assert.EqualError(t, err, "not_found")
	assert.Nil(t, client)

	client, err = s.GetClient(context.Background(), "myclient")
	require.NoError(t, err)
	require.NotNil(t, client)
	assert.Equal(t, "myclient", client.GetID())
}

func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
	c1 := schema.OpenIDConnectClientConfiguration{
		ID:          "myclient",
		Description: "myclient desc",
		Policy:      "one_factor",
		Scopes:      []string{"openid", "profile"},
		Secret:      "mysecret",
	}

	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients:          []schema.OpenIDConnectClientConfiguration{c1},
	})

	client, err := s.GetFullClient(c1.ID)
	require.NoError(t, err)
	require.NotNil(t, client)
	assert.Equal(t, client.ID, c1.ID)
	assert.Equal(t, client.Description, c1.Description)
	assert.Equal(t, client.Scopes, c1.Scopes)
	assert.Equal(t, client.GrantTypes, c1.GrantTypes)
	assert.Equal(t, client.ResponseTypes, c1.ResponseTypes)
	assert.Equal(t, client.RedirectURIs, c1.RedirectURIs)
	assert.Equal(t, client.Policy, authorization.OneFactor)
	assert.Equal(t, client.Secret, []byte(c1.Secret))
}

func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
	c1 := schema.OpenIDConnectClientConfiguration{
		ID:          "myclient",
		Description: "myclient desc",
		Policy:      "one_factor",
		Scopes:      []string{"openid", "profile"},
		Secret:      "mysecret",
	}

	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients:          []schema.OpenIDConnectClientConfiguration{c1},
	})

	client, err := s.GetFullClient("another-client")
	assert.Nil(t, client)
	assert.EqualError(t, err, "not_found")
}

func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients: []schema.OpenIDConnectClientConfiguration{
			{
				ID:          "myclient",
				Description: "myclient desc",
				Policy:      "one_factor",
				Scopes:      []string{"openid", "profile"},
				Secret:      "mysecret",
			},
		},
	})

	validClient := s.IsValidClientID("myclient")
	invalidClient := s.IsValidClientID("myinvalidclient")

	assert.True(t, validClient)
	assert.False(t, invalidClient)
}.
*/
<commit_msg>test(oidc): fix disabled tests (#3173)<commit_after>package oidc

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/authelia/authelia/v4/internal/authorization"
	"github.com/authelia/authelia/v4/internal/configuration/schema"
)

func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients: []schema.OpenIDConnectClientConfiguration{
			{
				ID:          "myclient",
				Description: "myclient desc",
				Policy:      "one_factor",
				Scopes:      []string{"openid", "profile"},
				Secret:      "mysecret",
			},
			{
				ID:          "myotherclient",
				Description: "myclient desc",
				Policy:      "two_factor",
				Scopes:      []string{"openid", "profile"},
				Secret:      "mysecret",
			},
		},
	}, nil)

	policyOne := s.GetClientPolicy("myclient")
	assert.Equal(t, authorization.OneFactor, policyOne)

	policyTwo := s.GetClientPolicy("myotherclient")
	assert.Equal(t, authorization.TwoFactor, policyTwo)

	policyInvalid := s.GetClientPolicy("invalidclient")
	assert.Equal(t, authorization.TwoFactor, policyInvalid)
}

func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients: []schema.OpenIDConnectClientConfiguration{
			{
				ID:          "myclient",
				Description: "myclient desc",
				Policy:      "one_factor",
				Scopes:      []string{"openid", "profile"},
				Secret:      "mysecret",
			},
		},
	}, nil)

	client, err := s.GetClient(context.Background(), "myinvalidclient")
	assert.EqualError(t, err, "not_found")
	assert.Nil(t, client)

	client, err = s.GetClient(context.Background(), "myclient")
	require.NoError(t, err)
	require.NotNil(t, client)
	assert.Equal(t, "myclient", client.GetID())
}

func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
	c1 := schema.OpenIDConnectClientConfiguration{
		ID:          "myclient",
		Description: "myclient desc",
		Policy:      "one_factor",
		Scopes:      []string{"openid", "profile"},
		Secret:      "mysecret",
	}

	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients:          []schema.OpenIDConnectClientConfiguration{c1},
	}, nil)

	client, err := s.GetFullClient(c1.ID)
	require.NoError(t, err)
	require.NotNil(t, client)
	assert.Equal(t, client.ID, c1.ID)
	assert.Equal(t, client.Description, c1.Description)
	assert.Equal(t, client.Scopes, c1.Scopes)
	assert.Equal(t, client.GrantTypes, c1.GrantTypes)
	assert.Equal(t, client.ResponseTypes, c1.ResponseTypes)
	assert.Equal(t, client.RedirectURIs, c1.RedirectURIs)
	assert.Equal(t, client.Policy, authorization.OneFactor)
	assert.Equal(t, client.Secret, []byte(c1.Secret))
}

func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
	c1 := schema.OpenIDConnectClientConfiguration{
		ID:          "myclient",
		Description: "myclient desc",
		Policy:      "one_factor",
		Scopes:      []string{"openid", "profile"},
		Secret:      "mysecret",
	}

	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients:          []schema.OpenIDConnectClientConfiguration{c1},
	}, nil)

	client, err := s.GetFullClient("another-client")
	assert.Nil(t, client)
	assert.EqualError(t, err, "not_found")
}

func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
	s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
		IssuerPrivateKey: exampleIssuerPrivateKey,
		Clients: []schema.OpenIDConnectClientConfiguration{
			{
				ID:          "myclient",
				Description: "myclient desc",
				Policy:      "one_factor",
				Scopes:      []string{"openid", "profile"},
				Secret:      "mysecret",
			},
		},
	}, nil)

	validClient := s.IsValidClientID("myclient")
	invalidClient := s.IsValidClientID("myinvalidclient")

	assert.True(t, validClient)
	assert.False(t, invalidClient)
}
<|endoftext|>


<commit_before>package registry

import (
	"fmt"

	packerSvc "github.com/hashicorp/hcp-sdk-go/clients/cloud-packer-service/preview/2021-04-30/client/packer_service"
	organizationSvc "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/preview/2019-12-10/client/organization_service"
	projectSvc "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/preview/2019-12-10/client/project_service"
	rmmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/preview/2019-12-10/models"
	"github.com/hashicorp/hcp-sdk-go/httpclient"
	"github.com/hashicorp/packer/internal/registry/env"
)

// Client is an HCP client capable of making requests on behalf of a service principal
type Client struct {
	Packer       packerSvc.ClientService
	Organization organizationSvc.ClientService
	Project      projectSvc.ClientService

	// OrganizationID  is the organization unique identifier on HCP.
	OrganizationID string

	// ProjectID  is the project unique identifier on HCP.
	ProjectID string
}

// NewClient returns an authenticated client to a HCP Packer Registry.
// Client authentication requires the following environment variables be set HCP_CLIENT_ID and HCP_CLIENT_SECRET.
// Upon error a HCPClientError will be returned.
func NewClient() (*Client, error) {
	if !env.HasHCPCredentials() {
		return nil, &ClientError{
			StatusCode: InvalidClientConfig,
			Err:        fmt.Errorf("the client authentication requires both %s and %s environment variables to be set", env.HCPClientID, env.HCPClientSecret),
		}
	}

	cl, err := httpclient.New(httpclient.Config{})
	if err != nil {
		return nil, &ClientError{
			StatusCode: InvalidClientConfig,
			Err:        err,
		}
	}

	client := &Client{
		Packer:       packerSvc.New(cl, nil),
		Organization: organizationSvc.New(cl, nil),
		Project:      projectSvc.New(cl, nil),
	}

	if err := client.loadOrganizationID(); err != nil {
		return nil, &ClientError{
			StatusCode: InvalidClientConfig,
			Err:        err,
		}
	}
	if err := client.loadProjectID(); err != nil {
		return nil, &ClientError{
			StatusCode: InvalidClientConfig,
			Err:        err,
		}
	}

	return client, nil
}

func (c *Client) loadOrganizationID() error {
	// Get the organization ID.
	listOrgParams := organizationSvc.NewOrganizationServiceListParams()
	listOrgResp, err := c.Organization.OrganizationServiceList(listOrgParams, nil)
	if err != nil {
		return fmt.Errorf("unable to fetch organization list: %v", err)
	}
	orgLen := len(listOrgResp.Payload.Organizations)
	if orgLen != 1 {
		return fmt.Errorf("unexpected number of organizations: expected 1, actual: %v", orgLen)
	}
	c.OrganizationID = listOrgResp.Payload.Organizations[0].ID
	return nil
}

func (c *Client) loadProjectID() error {
	// Get the project using the organization ID.
	listProjParams := projectSvc.NewProjectServiceListParams()
	listProjParams.ScopeID = &c.OrganizationID
	scopeType := string(rmmodels.HashicorpCloudResourcemanagerResourceIDResourceTypeORGANIZATION)
	listProjParams.ScopeType = &scopeType
	listProjResp, err := c.Project.ProjectServiceList(listProjParams, nil)
	if err != nil {
		return fmt.Errorf("unable to fetch project id: %v", err)
	}
	if len(listProjResp.Payload.Projects) > 1 {
		return fmt.Errorf("this version of Packer does not support multiple projects")
	}
	c.ProjectID = listProjResp.Payload.Projects[0].ID
	return nil
}
<commit_msg>add packer user agent to HCP client (#11455)<commit_after>package registry

import (
	"fmt"

	packerSvc "github.com/hashicorp/hcp-sdk-go/clients/cloud-packer-service/preview/2021-04-30/client/packer_service"
	organizationSvc "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/preview/2019-12-10/client/organization_service"
	projectSvc "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/preview/2019-12-10/client/project_service"
	rmmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/preview/2019-12-10/models"
	"github.com/hashicorp/hcp-sdk-go/httpclient"
	"github.com/hashicorp/packer/internal/registry/env"
	"github.com/hashicorp/packer/version"
)

// Client is an HCP client capable of making requests on behalf of a service principal
type Client struct {
	Packer       packerSvc.ClientService
	Organization organizationSvc.ClientService
	Project      projectSvc.ClientService

	// OrganizationID  is the organization unique identifier on HCP.
	OrganizationID string

	// ProjectID  is the project unique identifier on HCP.
	ProjectID string
}

// NewClient returns an authenticated client to a HCP Packer Registry.
// Client authentication requires the following environment variables be set HCP_CLIENT_ID and HCP_CLIENT_SECRET.
// Upon error a HCPClientError will be returned.
func NewClient() (*Client, error) {
	if !env.HasHCPCredentials() {
		return nil, &ClientError{
			StatusCode: InvalidClientConfig,
			Err:        fmt.Errorf("the client authentication requires both %s and %s environment variables to be set", env.HCPClientID, env.HCPClientSecret),
		}
	}

	cl, err := httpclient.New(httpclient.Config{
		SourceChannel: fmt.Sprintf("packer/%s", version.PackerVersion.FormattedVersion()),
	})
	if err != nil {
		return nil, &ClientError{
			StatusCode: InvalidClientConfig,
			Err:        err,
		}
	}

	client := &Client{
		Packer:       packerSvc.New(cl, nil),
		Organization: organizationSvc.New(cl, nil),
		Project:      projectSvc.New(cl, nil),
	}

	if err := client.loadOrganizationID(); err != nil {
		return nil, &ClientError{
			StatusCode: InvalidClientConfig,
			Err:        err,
		}
	}
	if err := client.loadProjectID(); err != nil {
		return nil, &ClientError{
			StatusCode: InvalidClientConfig,
			Err:        err,
		}
	}

	return client, nil
}

func (c *Client) loadOrganizationID() error {
	// Get the organization ID.
	listOrgParams := organizationSvc.NewOrganizationServiceListParams()
	listOrgResp, err := c.Organization.OrganizationServiceList(listOrgParams, nil)
	if err != nil {
		return fmt.Errorf("unable to fetch organization list: %v", err)
	}
	orgLen := len(listOrgResp.Payload.Organizations)
	if orgLen != 1 {
		return fmt.Errorf("unexpected number of organizations: expected 1, actual: %v", orgLen)
	}
	c.OrganizationID = listOrgResp.Payload.Organizations[0].ID
	return nil
}

func (c *Client) loadProjectID() error {
	// Get the project using the organization ID.
	listProjParams := projectSvc.NewProjectServiceListParams()
	listProjParams.ScopeID = &c.OrganizationID
	scopeType := string(rmmodels.HashicorpCloudResourcemanagerResourceIDResourceTypeORGANIZATION)
	listProjParams.ScopeType = &scopeType
	listProjResp, err := c.Project.ProjectServiceList(listProjParams, nil)
	if err != nil {
		return fmt.Errorf("unable to fetch project id: %v", err)
	}
	if len(listProjResp.Payload.Projects) > 1 {
		return fmt.Errorf("this version of Packer does not support multiple projects")
	}
	c.ProjectID = listProjResp.Payload.Projects[0].ID
	return nil
}
<|endoftext|>


<commit_before>// Package storage hold and abstraction of the filesystem

package storage

import (
	"io"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
)

// Storage is an abstraction of the filesystem
type Storage interface {
	fs.FS
	// WriteFile writes data to the named file, creating it if necessary. If the file does not exist, WriteFile creates it with permissions perm (before umask); otherwise WriteFile truncates it before writing, without changing permissions.
	WriteFile(name string, data []byte, perm fs.FileMode) error
	// Mkdir creates a new directory with the specified name and permission bits (before umask). If there is an error, it will be of type *PathError.
	Mkdir(name string, perm fs.FileMode) error
	// RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error). If there is an error, it will be of type *PathError.
	RemoveAll(name string) error
	// Create creates or truncates the named file. If the file already exists, it is truncated. If the file does not exist, it is created with mode 0666 (before umask). If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR. If there is an error, it will be of type *PathError.
	Create(name string) (File, error)
}

type File interface {
	fs.File
	io.Writer
}

// ReadFile returns the content of name in the filesystem
func ReadFile(fs Storage, name string) ([]byte, error) {
	f, err := fs.Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ioutil.ReadAll(f)
}
func MkdirAll(fs Storage, dir string, perm fs.FileMode) error {
	list := make([]string, 0)
	for dir := filepath.Dir(dir); dir != string(filepath.Separator) && dir != "."; dir = filepath.Dir(dir) {
		list = append(list, dir)
	}
	for i := len(list); i > 0; i-- {
		err := fs.Mkdir(list[i-1], perm)
		if err != nil && !os.IsExist(err) {
			return err
		}
	}
	return nil

}
<commit_msg>fix(MkdirAll): the stop condition should work on windows<commit_after>// Package storage hold and abstraction of the filesystem

package storage

import (
	"io"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
)

// Storage is an abstraction of the filesystem
type Storage interface {
	fs.FS
	// WriteFile writes data to the named file, creating it if necessary. If the file does not exist, WriteFile creates it with permissions perm (before umask); otherwise WriteFile truncates it before writing, without changing permissions.
	WriteFile(name string, data []byte, perm fs.FileMode) error
	// Mkdir creates a new directory with the specified name and permission bits (before umask). If there is an error, it will be of type *PathError.
	Mkdir(name string, perm fs.FileMode) error
	// RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error). If there is an error, it will be of type *PathError.
	RemoveAll(name string) error
	// Create creates or truncates the named file. If the file already exists, it is truncated. If the file does not exist, it is created with mode 0666 (before umask). If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR. If there is an error, it will be of type *PathError.
	Create(name string) (File, error)
}

type File interface {
	fs.File
	io.Writer
}

// ReadFile returns the content of name in the filesystem
func ReadFile(fs Storage, name string) ([]byte, error) {
	f, err := fs.Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ioutil.ReadAll(f)
}
func MkdirAll(fs Storage, dir string, perm fs.FileMode) error {
	list := make([]string, 0)
	stop := ""
	for dir := filepath.Dir(dir); dir != stop; dir = filepath.Dir(dir) {
		list = append(list, dir)
		stop = dir
	}
	for i := len(list); i > 0; i-- {
		err := fs.Mkdir(list[i-1], perm)
		if err != nil && !os.IsExist(err) {
			return err
		}
	}
	return nil

}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2015-2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

// Package version provides a single location to house the version information
// for dcrd and other utilities provided in the same repository.
package version

import (
	"bytes"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

const (
	// semanticAlphabet defines the allowed characters for the pre-release and
	// build metadata portions of a semantic version string.
	semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."
)

// semverRE is a regular expression used to parse a semantic version string into
// its constituent parts.
var semverRE = regexp.MustCompile(`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)` +
	`(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*` +
	`[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`)

// These variables define the application version and follow the semantic
// versioning 2.0.0 spec (https://semver.org/).
var (
	// Note for maintainers:
	//
	// The expected process for setting the version in releases is as follows:
	// - Create a release branch of the form 'release-vMAJOR.MINOR'
	// - Modify the Version variable below on that branch to:
	//   - Remove the pre-release portion
	//   - Set the build metadata to 'release.local'
	// - Update the Version variable below on the master branch to the next
	//   expected version while retaining a pre-release of 'pre'
	//
	// These steps ensure that building from source produces versions that are
	// distinct from reproducible builds that override the Version via linker
	// flags.

	// Version is the application version per the semantic versioning 2.0.0 spec
	// (https://semver.org/).
	//
	// It is defined as a variable so it can be overridden during the build
	// process with:
	// '-ldflags "-X github.com/decred/dcrd/internal/version.Version=fullsemver"'
	// if needed.
	//
	// It MUST be a full semantic version per the semantic versioning spec or
	// the package will panic at runtime.  Of particular note is the pre-release
	// and build metadata portions MUST only contain characters from
	// semanticAlphabet.
	Version = "1.7.0-pre"

	// NOTE: The following values are set via init by parsing the above Version
	// string.

	// These fields are the individual semantic version components that define
	// the application version.
	Major         uint32
	Minor         uint32
	Patch         uint32
	PreRelease    string
	BuildMetadata string
)

// parseUint32 converts the passed string to an unsigned integer or returns an
// error if it is invalid.
func parseUint32(s string, fieldName string) (uint32, error) {
	val, err := strconv.ParseUint(s, 10, 32)
	if err != nil {
		return 0, fmt.Errorf("malformed semver %s: %w", fieldName, err)
	}
	return uint32(val), err
}

// checkSemString returns an error if the passed string contains characters that
// are not in the provided alphabet.
func checkSemString(s, alphabet, fieldName string) error {
	for _, r := range s {
		if !strings.ContainsRune(alphabet, r) {
			return fmt.Errorf("malformed semver %s: %q invalid", fieldName, r)
		}
	}
	return nil
}

// parseSemVer parses various semver components from the provided string.
func parseSemVer(s string) (uint32, uint32, uint32, string, string, error) {
	// Parse the various semver component from the version string via a regular
	// expression.
	m := semverRE.FindStringSubmatch(s)
	if m == nil {
		err := fmt.Errorf("malformed version string %q: does not conform to "+
			"semver specification", s)
		return 0, 0, 0, "", "", err
	}

	major, err := parseUint32(m[1], "major")
	if err != nil {
		return 0, 0, 0, "", "", err
	}

	minor, err := parseUint32(m[2], "minor")
	if err != nil {
		return 0, 0, 0, "", "", err
	}

	patch, err := parseUint32(m[3], "patch")
	if err != nil {
		return 0, 0, 0, "", "", err
	}

	preRel := m[4]
	err = checkSemString(preRel, semanticAlphabet, "pre-release")
	if err != nil {
		return 0, 0, 0, s, s, err
	}

	build := m[5]
	err = checkSemString(build, semanticAlphabet, "buildmetadata")
	if err != nil {
		return 0, 0, 0, s, s, err
	}

	return major, minor, patch, preRel, build, nil
}

func init() {
	var err error
	Major, Minor, Patch, PreRelease, BuildMetadata, err = parseSemVer(Version)
	if err != nil {
		panic(err)
	}
	if BuildMetadata == "" {
		BuildMetadata = vcsCommitID()
		if BuildMetadata != "" {
			Version = fmt.Sprintf("%d.%d.%d", Major, Minor, Patch)
			if PreRelease != "" {
				Version += "-" + PreRelease
			}
			Version += "+" + BuildMetadata
		}
	}
}

// String returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (https://semver.org/).
func String() string {
	return Version
}

// NormalizeString returns the passed string stripped of all characters which
// are not valid according to the semantic versioning guidelines for pre-release
// and build metadata strings.  In particular they MUST only contain characters
// in semanticAlphabet.
func NormalizeString(str string) string {
	var result bytes.Buffer
	for _, r := range str {
		if strings.ContainsRune(semanticAlphabet, r) {
			result.WriteRune(r)
		}
	}
	return result.String()
}
<commit_msg>release: Bump for 1.8 release cycle.<commit_after>// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2015-2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

// Package version provides a single location to house the version information
// for dcrd and other utilities provided in the same repository.
package version

import (
	"bytes"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

const (
	// semanticAlphabet defines the allowed characters for the pre-release and
	// build metadata portions of a semantic version string.
	semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."
)

// semverRE is a regular expression used to parse a semantic version string into
// its constituent parts.
var semverRE = regexp.MustCompile(`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)` +
	`(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*` +
	`[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`)

// These variables define the application version and follow the semantic
// versioning 2.0.0 spec (https://semver.org/).
var (
	// Note for maintainers:
	//
	// The expected process for setting the version in releases is as follows:
	// - Create a release branch of the form 'release-vMAJOR.MINOR'
	// - Modify the Version variable below on that branch to:
	//   - Remove the pre-release portion
	//   - Set the build metadata to 'release.local'
	// - Update the Version variable below on the master branch to the next
	//   expected version while retaining a pre-release of 'pre'
	//
	// These steps ensure that building from source produces versions that are
	// distinct from reproducible builds that override the Version via linker
	// flags.

	// Version is the application version per the semantic versioning 2.0.0 spec
	// (https://semver.org/).
	//
	// It is defined as a variable so it can be overridden during the build
	// process with:
	// '-ldflags "-X github.com/decred/dcrd/internal/version.Version=fullsemver"'
	// if needed.
	//
	// It MUST be a full semantic version per the semantic versioning spec or
	// the package will panic at runtime.  Of particular note is the pre-release
	// and build metadata portions MUST only contain characters from
	// semanticAlphabet.
	Version = "1.8.0-pre"

	// NOTE: The following values are set via init by parsing the above Version
	// string.

	// These fields are the individual semantic version components that define
	// the application version.
	Major         uint32
	Minor         uint32
	Patch         uint32
	PreRelease    string
	BuildMetadata string
)

// parseUint32 converts the passed string to an unsigned integer or returns an
// error if it is invalid.
func parseUint32(s string, fieldName string) (uint32, error) {
	val, err := strconv.ParseUint(s, 10, 32)
	if err != nil {
		return 0, fmt.Errorf("malformed semver %s: %w", fieldName, err)
	}
	return uint32(val), err
}

// checkSemString returns an error if the passed string contains characters that
// are not in the provided alphabet.
func checkSemString(s, alphabet, fieldName string) error {
	for _, r := range s {
		if !strings.ContainsRune(alphabet, r) {
			return fmt.Errorf("malformed semver %s: %q invalid", fieldName, r)
		}
	}
	return nil
}

// parseSemVer parses various semver components from the provided string.
func parseSemVer(s string) (uint32, uint32, uint32, string, string, error) {
	// Parse the various semver component from the version string via a regular
	// expression.
	m := semverRE.FindStringSubmatch(s)
	if m == nil {
		err := fmt.Errorf("malformed version string %q: does not conform to "+
			"semver specification", s)
		return 0, 0, 0, "", "", err
	}

	major, err := parseUint32(m[1], "major")
	if err != nil {
		return 0, 0, 0, "", "", err
	}

	minor, err := parseUint32(m[2], "minor")
	if err != nil {
		return 0, 0, 0, "", "", err
	}

	patch, err := parseUint32(m[3], "patch")
	if err != nil {
		return 0, 0, 0, "", "", err
	}

	preRel := m[4]
	err = checkSemString(preRel, semanticAlphabet, "pre-release")
	if err != nil {
		return 0, 0, 0, s, s, err
	}

	build := m[5]
	err = checkSemString(build, semanticAlphabet, "buildmetadata")
	if err != nil {
		return 0, 0, 0, s, s, err
	}

	return major, minor, patch, preRel, build, nil
}

func init() {
	var err error
	Major, Minor, Patch, PreRelease, BuildMetadata, err = parseSemVer(Version)
	if err != nil {
		panic(err)
	}
	if BuildMetadata == "" {
		BuildMetadata = vcsCommitID()
		if BuildMetadata != "" {
			Version = fmt.Sprintf("%d.%d.%d", Major, Minor, Patch)
			if PreRelease != "" {
				Version += "-" + PreRelease
			}
			Version += "+" + BuildMetadata
		}
	}
}

// String returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (https://semver.org/).
func String() string {
	return Version
}

// NormalizeString returns the passed string stripped of all characters which
// are not valid according to the semantic versioning guidelines for pre-release
// and build metadata strings.  In particular they MUST only contain characters
// in semanticAlphabet.
func NormalizeString(str string) string {
	var result bytes.Buffer
	for _, r := range str {
		if strings.ContainsRune(semanticAlphabet, r) {
			result.WriteRune(r)
		}
	}
	return result.String()
}
<|endoftext|>


<commit_before>package of10

import (
	"net"

	. "github.com/oshothebig/goflow/openflow"
)

type ActionType uint16

const (
	OFPAT_OUTPUT ActionType = iota
	OFPAT_SET_VLAN_VID
	OFPAT_SET_VLAN_PCP
	OFPAT_SET_STRIP_VLAN
	OFPAT_SET_DL_SRC
	OFPAT_SET_DL_DST
	OFPAT_SET_NW_SRC
	OFPAT_SET_NW_DST
	OFPAT_SET_NW_TOS
	OFPAT_SET_TP_SRC
	OFPAT_SET_TP_DST
	OFPAT_ENQUEUE
	OFPAT_VENDOR ActionType = 0xffff
)

var ActionTypes = struct {
	Output        ActionType
	SetVlanId     ActionType
	SetVlanPcp    ActionType
	StripVlan     ActionType
	SetEtherSrc   ActionType
	SetEtherDst   ActionType
	SetIpSrc      ActionType
	SetIpDst      ActionType
	SetIpTos      ActionType
	SetNetworkSrc ActionType
	SetNetworkDst ActionType
	Enqueue       ActionType
	Vendor        ActionType
}{
	OFPAT_OUTPUT,
	OFPAT_SET_VLAN_VID,
	OFPAT_SET_VLAN_PCP,
	OFPAT_SET_STRIP_VLAN,
	OFPAT_SET_DL_SRC,
	OFPAT_SET_DL_DST,
	OFPAT_SET_NW_SRC,
	OFPAT_SET_NW_DST,
	OFPAT_SET_NW_TOS,
	OFPAT_SET_TP_SRC,
	OFPAT_SET_TP_DST,
	OFPAT_ENQUEUE,
	OFPAT_VENDOR,
}

type Action interface {
	Packetizable
	GetType() ActionType
}

type ActionHeader struct {
	Type   ActionType
	Length uint16
}

func (header *ActionHeader) GetType() ActionType {
	return header.Type
}

type SendOutPort struct {
	ActionHeader
	Port      PortNumber
	MaxLength uint16
}

type Enqueue struct {
	ActionHeader
	Port    PortNumber
	pad     [6]uint8
	QueueId uint32
}

type SetVlanVid struct {
	ActionHeader
	VlanId VlanId
	pad    [2]uint32
}

type SetVlanPcp struct {
	ActionHeader
	VlanPcp VlanPriority
	pad     [3]uint8
}

type SetEtherAddress struct {
	ActionHeader
	EtherAddress net.HardwareAddr
	pad          [6]uint8
}

type SetIpAddress struct {
	ActionHeader
	IpAddress net.IP
}

type SetIpTos struct {
	ActionHeader
	IpTos Dscp
	pad   [3]uint8
}

type SetTransportPort struct {
	ActionHeader
	TransportPort NetworkPort
	pad           [2]uint8
}

type VendorHeader struct {
	ActionHeader
	Vendor VendorId
}

type VendorId uint32
<commit_msg>Shorten field names to remove redundancy<commit_after>package of10

import (
	"net"

	. "github.com/oshothebig/goflow/openflow"
)

type ActionType uint16

const (
	OFPAT_OUTPUT ActionType = iota
	OFPAT_SET_VLAN_VID
	OFPAT_SET_VLAN_PCP
	OFPAT_SET_STRIP_VLAN
	OFPAT_SET_DL_SRC
	OFPAT_SET_DL_DST
	OFPAT_SET_NW_SRC
	OFPAT_SET_NW_DST
	OFPAT_SET_NW_TOS
	OFPAT_SET_TP_SRC
	OFPAT_SET_TP_DST
	OFPAT_ENQUEUE
	OFPAT_VENDOR ActionType = 0xffff
)

var ActionTypes = struct {
	Output        ActionType
	SetVlanId     ActionType
	SetVlanPcp    ActionType
	StripVlan     ActionType
	SetEtherSrc   ActionType
	SetEtherDst   ActionType
	SetIpSrc      ActionType
	SetIpDst      ActionType
	SetIpTos      ActionType
	SetNetworkSrc ActionType
	SetNetworkDst ActionType
	Enqueue       ActionType
	Vendor        ActionType
}{
	OFPAT_OUTPUT,
	OFPAT_SET_VLAN_VID,
	OFPAT_SET_VLAN_PCP,
	OFPAT_SET_STRIP_VLAN,
	OFPAT_SET_DL_SRC,
	OFPAT_SET_DL_DST,
	OFPAT_SET_NW_SRC,
	OFPAT_SET_NW_DST,
	OFPAT_SET_NW_TOS,
	OFPAT_SET_TP_SRC,
	OFPAT_SET_TP_DST,
	OFPAT_ENQUEUE,
	OFPAT_VENDOR,
}

type Action interface {
	Packetizable
	GetType() ActionType
}

type ActionHeader struct {
	Type   ActionType
	Length uint16
}

func (header *ActionHeader) GetType() ActionType {
	return header.Type
}

type SendOutPort struct {
	ActionHeader
	Port      PortNumber
	MaxLength uint16
}

type Enqueue struct {
	ActionHeader
	Port    PortNumber
	pad     [6]uint8
	QueueId uint32
}

type SetVlanVid struct {
	ActionHeader
	Id  VlanId
	pad [2]uint32
}

type SetVlanPcp struct {
	ActionHeader
	Priority VlanPriority
	pad      [3]uint8
}

type SetEtherAddress struct {
	ActionHeader
	Address net.HardwareAddr
	pad     [6]uint8
}

type SetIpAddress struct {
	ActionHeader
	Address net.IP
}

type SetIpTos struct {
	ActionHeader
	Tos Dscp
	pad [3]uint8
}

type SetTransportPort struct {
	ActionHeader
	Port NetworkPort
	pad  [2]uint8
}

type VendorHeader struct {
	ActionHeader
	Vendor VendorId
}

type VendorId uint32
<|endoftext|>


<commit_before>package chalk

const (
	bgBlack attribute = iota + 40
	bgRed
	bgGreen
	bgYellow
	bgBlue
	bgMagenta
	bgCyan
	bgWhite
)

// BlackBackground reports Formatter with Black as initial format
func BlackBackground() Formatter {
	return Formatter{bgBlack}
}

// AsBlackBackground reports Black string based on provided content
func AsBlackBackground(a ...interface{}) string {
	return BlackBackground().Sprint(a...)
}

// BlackBackground reports Formatter with Black as additional format
func (f Formatter) BlackBackground() Formatter {
	return append(f, bgBlack)
}

// RedBackground reports Formatter with Red as initial format
func RedBackground() Formatter {
	return Formatter{bgRed}
}

// AsRedBackground reports Red string based on provided content
func AsRedBackground(a ...interface{}) string {
	return Red().Sprint(a...)
}

// RedBackground reports Formatter with Red as additional format
func (f Formatter) RedBackground() Formatter {
	return append(f, bgRed)
}

// GreenBackground reports Formatter with Green as initial format
func GreenBackground() Formatter {
	return Formatter{bgGreen}
}

// AsGreenBackground reports Green string based on provided content
func AsGreenBackground(a ...interface{}) string {
	return Green().Sprint(a...)
}

// GreenBackground reports Formatter with Green as additional format
func (f Formatter) GreenBackground() Formatter {
	return append(f, bgGreen)
}

// YellowBackground reports Formatter with Yellow as initial format
func YellowBackground() Formatter {
	return Formatter{bgYellow}
}

// AsYellowBackground reports Yellow string based on provided content
func AsYellowBackground(a ...interface{}) string {
	return Yellow().Sprint(a...)
}

// YellowBackground reports Formatter with Yellow as additional format
func (f Formatter) YellowBackground() Formatter {
	return append(f, bgYellow)
}

// BlueBackground reports Formatter with Blue as initial format
func BlueBackground() Formatter {
	return Formatter{bgBlue}
}

// AsBlueBackground reports Blue string based on provided content
func AsBlueBackground(a ...interface{}) string {
	return Blue().Sprint(a...)
}

// BlueBackground reports Formatter with Blue as additional format
func (f Formatter) BlueBackground() Formatter {
	return append(f, bgBlue)
}

// MagentaBackground reports Formatter with Magenta as initial format
func MagentaBackground() Formatter {
	return Formatter{bgMagenta}
}

// AsMagentaBackground reports Magenta string based on provided content
func AsMagentaBackground(a ...interface{}) string {
	return Magenta().Sprint(a...)
}

// MagentaBackground reports Formatter with Magenta as additional format
func (f Formatter) MagentaBackground() Formatter {
	return append(f, bgMagenta)
}

// CyanBackground reports Formatter with Cyan as initial format
func CyanBackground() Formatter {
	return Formatter{bgCyan}
}

// AsCyanBackground reports Cyan string based on provided content
func AsCyanBackground(a ...interface{}) string {
	return Cyan().Sprint(a...)
}

// CyanBackground reports Formatter with Cyan as additional format
func (f Formatter) CyanBackground() Formatter {
	return append(f, bgCyan)
}

// WhiteBackground reports Formatter with White as initial format
func WhiteBackground() Formatter {
	return Formatter{bgWhite}
}

// AsWhiteBackground reports White string based on provided content
func AsWhiteBackground(a ...interface{}) string {
	return White().Sprint(a...)
}

// WhiteBackground reports Formatter with White as additional format
func (f Formatter) WhiteBackground() Formatter {
	return append(f, bgWhite)
}
<commit_msg>++missing comment<commit_after>package chalk

// Background text colors
const (
	bgBlack attribute = iota + 40
	bgRed
	bgGreen
	bgYellow
	bgBlue
	bgMagenta
	bgCyan
	bgWhite
)

// BlackBackground reports Formatter with Black as initial format
func BlackBackground() Formatter {
	return Formatter{bgBlack}
}

// AsBlackBackground reports Black string based on provided content
func AsBlackBackground(a ...interface{}) string {
	return BlackBackground().Sprint(a...)
}

// BlackBackground reports Formatter with Black as additional format
func (f Formatter) BlackBackground() Formatter {
	return append(f, bgBlack)
}

// RedBackground reports Formatter with Red as initial format
func RedBackground() Formatter {
	return Formatter{bgRed}
}

// AsRedBackground reports Red string based on provided content
func AsRedBackground(a ...interface{}) string {
	return Red().Sprint(a...)
}

// RedBackground reports Formatter with Red as additional format
func (f Formatter) RedBackground() Formatter {
	return append(f, bgRed)
}

// GreenBackground reports Formatter with Green as initial format
func GreenBackground() Formatter {
	return Formatter{bgGreen}
}

// AsGreenBackground reports Green string based on provided content
func AsGreenBackground(a ...interface{}) string {
	return Green().Sprint(a...)
}

// GreenBackground reports Formatter with Green as additional format
func (f Formatter) GreenBackground() Formatter {
	return append(f, bgGreen)
}

// YellowBackground reports Formatter with Yellow as initial format
func YellowBackground() Formatter {
	return Formatter{bgYellow}
}

// AsYellowBackground reports Yellow string based on provided content
func AsYellowBackground(a ...interface{}) string {
	return Yellow().Sprint(a...)
}

// YellowBackground reports Formatter with Yellow as additional format
func (f Formatter) YellowBackground() Formatter {
	return append(f, bgYellow)
}

// BlueBackground reports Formatter with Blue as initial format
func BlueBackground() Formatter {
	return Formatter{bgBlue}
}

// AsBlueBackground reports Blue string based on provided content
func AsBlueBackground(a ...interface{}) string {
	return Blue().Sprint(a...)
}

// BlueBackground reports Formatter with Blue as additional format
func (f Formatter) BlueBackground() Formatter {
	return append(f, bgBlue)
}

// MagentaBackground reports Formatter with Magenta as initial format
func MagentaBackground() Formatter {
	return Formatter{bgMagenta}
}

// AsMagentaBackground reports Magenta string based on provided content
func AsMagentaBackground(a ...interface{}) string {
	return Magenta().Sprint(a...)
}

// MagentaBackground reports Formatter with Magenta as additional format
func (f Formatter) MagentaBackground() Formatter {
	return append(f, bgMagenta)
}

// CyanBackground reports Formatter with Cyan as initial format
func CyanBackground() Formatter {
	return Formatter{bgCyan}
}

// AsCyanBackground reports Cyan string based on provided content
func AsCyanBackground(a ...interface{}) string {
	return Cyan().Sprint(a...)
}

// CyanBackground reports Formatter with Cyan as additional format
func (f Formatter) CyanBackground() Formatter {
	return append(f, bgCyan)
}

// WhiteBackground reports Formatter with White as initial format
func WhiteBackground() Formatter {
	return Formatter{bgWhite}
}

// AsWhiteBackground reports White string based on provided content
func AsWhiteBackground(a ...interface{}) string {
	return White().Sprint(a...)
}

// WhiteBackground reports Formatter with White as additional format
func (f Formatter) WhiteBackground() Formatter {
	return append(f, bgWhite)
}
<|endoftext|>


<commit_before>// 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 spinner is a simple package to add a spinner / progress indicator to any terminal application.
package spinner

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/fatih/color"
)

// errInvalidColor is returned when attempting to set an invalid color
var errInvalidColor = errors.New("invalid color")

// validColors holds an array of the only colors allowed
var validColors = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white"}

// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
	for _, i := range validColors {
		if c == i {
			return true
		}
	}
	return false
}

// Spinner struct to hold the provided options
type Spinner struct {
	Delay      time.Duration                 // Delay is the speed of the indicator
	chars      []string                      // chars holds the chosen character set
	Prefix     string                        // Prefix is the text preppended to the indicator
	Suffix     string                        // Suffix is the text appended to the indicator
	FinalMSG   string                        // string displayed after Stop() is called
	lastOutput string                        // last character(set) written
	color      func(a ...interface{}) string // default color is white
	active     bool                          // active holds the state of the spinner
	lock       *sync.RWMutex                 // Lock useed for
	Writer     io.Writer                     // to make testing better, exported so users have access
	stopChan   chan struct{}                 // stopChan is a channel used to stop the indicator
}

// New provides a pointer to an instance of Spinner with the supplied options
func New(cs []string, d time.Duration) *Spinner {
	return &Spinner{
		Delay:    d,
		chars:    cs,
		color:    color.New(color.FgWhite).SprintFunc(),
		active:   false,
		lock:     &sync.RWMutex{},
		Writer:   color.Output,
		stopChan: make(chan struct{}, 1),
	}
}

// Start will start the indicator
func (s *Spinner) Start() {
	if s.active {
		return
	}
	s.active = true

	go func() {
		for {
			for i := 0; i < len(s.chars); i++ {
				select {
				case <-s.stopChan:
					return
				default:
					fmt.Fprint(s.Writer, fmt.Sprintf("%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix))
					out := fmt.Sprintf("%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
					s.lastOutput = out
					s.lock.RLock()
					time.Sleep(s.Delay)
					s.lock.RUnlock()
					s.erase(out)
				}
			}
		}
	}()
}

// Stop stops the indicator
func (s *Spinner) Stop() {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.active {
		s.stopChan <- struct{}{}
		s.active = false
		if s.FinalMSG != "" {
			fmt.Fprintf(s.Writer, s.FinalMSG)
		}
	}
}

// Restart will stop and start the indicator
func (s *Spinner) Restart() {
	s.Stop()
	s.Start()
}

// Reverse will reverse the order of the slice assigned to the indicator
func (s *Spinner) Reverse() {
	s.lock.Lock()
	defer s.lock.Unlock()
	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
	}
}

// Color will set the struct field for the given color to be used
func (s *Spinner) Color(c string) error {
	if validColor(c) {
		switch c {
		case "red":
			s.color = color.New(color.FgRed).SprintFunc()
			s.Restart()
		case "yellow":
			s.color = color.New(color.FgYellow).SprintFunc()
			s.Restart()
		case "green":
			s.color = color.New(color.FgGreen).SprintFunc()
			s.Restart()
		case "magenta":
			s.color = color.New(color.FgMagenta).SprintFunc()
			s.Restart()
		case "blue":
			s.color = color.New(color.FgBlue).SprintFunc()
			s.Restart()
		case "cyan":
			s.color = color.New(color.FgCyan).SprintFunc()
			s.Restart()
		case "white":
			s.color = color.New(color.FgWhite).SprintFunc()
			s.Restart()
		default:
			return errInvalidColor
		}
	}
	return nil
}

// UpdateSpeed will set the indicator delay to the given value
func (s *Spinner) UpdateSpeed(d time.Duration) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.Delay = d
}

// UpdateCharSet will change the current character set to the given one
func (s *Spinner) UpdateCharSet(cs []string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.chars = cs
}

// erase deletes written characters
func (s *Spinner) erase(a string) {
	n := utf8.RuneCountInString(a)
	s.lock.RLock()
	defer s.lock.RUnlock()
	for i := 0; i < n; i++ {
		fmt.Fprintf(s.Writer, "\b")
	}
}

// GenerateNumberSequence will generate a slice of integers at the
// provided length and convert them each to a string
func GenerateNumberSequence(length int) []string {
	var numSeq []string
	for i := 0; i < length; i++ {
		numSeq = append(numSeq, strconv.Itoa(i))
	}
	return numSeq
}
<commit_msg>small change in struct field ordering<commit_after>// 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 spinner is a simple package to add a spinner / progress indicator to any terminal application.
package spinner

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/fatih/color"
)

// errInvalidColor is returned when attempting to set an invalid color
var errInvalidColor = errors.New("invalid color")

// validColors holds an array of the only colors allowed
var validColors = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white"}

// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
	for _, i := range validColors {
		if c == i {
			return true
		}
	}
	return false
}

// Spinner struct to hold the provided options
type Spinner struct {
	Delay      time.Duration                 // Delay is the speed of the indicator
	chars      []string                      // chars holds the chosen character set
	Prefix     string                        // Prefix is the text preppended to the indicator
	Suffix     string                        // Suffix is the text appended to the indicator
	FinalMSG   string                        // string displayed after Stop() is called
	lastOutput string                        // last character(set) written
	color      func(a ...interface{}) string // default color is white
	lock       *sync.RWMutex                 // Lock useed for
	Writer     io.Writer                     // to make testing better, exported so users have access
	active     bool                          // active holds the state of the spinner
	stopChan   chan struct{}                 // stopChan is a channel used to stop the indicator
}

// New provides a pointer to an instance of Spinner with the supplied options
func New(cs []string, d time.Duration) *Spinner {
	return &Spinner{
		Delay:    d,
		chars:    cs,
		color:    color.New(color.FgWhite).SprintFunc(),
		lock:     &sync.RWMutex{},
		Writer:   color.Output,
		active:   false,
		stopChan: make(chan struct{}, 1),
	}
}

// Start will start the indicator
func (s *Spinner) Start() {
	if s.active {
		return
	}
	s.active = true

	go func() {
		for {
			for i := 0; i < len(s.chars); i++ {
				select {
				case <-s.stopChan:
					return
				default:
					fmt.Fprint(s.Writer, fmt.Sprintf("%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix))
					out := fmt.Sprintf("%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
					s.lastOutput = out
					s.lock.RLock()
					time.Sleep(s.Delay)
					s.lock.RUnlock()
					s.erase(out)
				}
			}
		}
	}()
}

// Stop stops the indicator
func (s *Spinner) Stop() {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.active {
		s.stopChan <- struct{}{}
		s.active = false
		if s.FinalMSG != "" {
			fmt.Fprintf(s.Writer, s.FinalMSG)
		}
	}
}

// Restart will stop and start the indicator
func (s *Spinner) Restart() {
	s.Stop()
	s.Start()
}

// Reverse will reverse the order of the slice assigned to the indicator
func (s *Spinner) Reverse() {
	s.lock.Lock()
	defer s.lock.Unlock()
	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
	}
}

// Color will set the struct field for the given color to be used
func (s *Spinner) Color(c string) error {
	if validColor(c) {
		switch c {
		case "red":
			s.color = color.New(color.FgRed).SprintFunc()
			s.Restart()
		case "yellow":
			s.color = color.New(color.FgYellow).SprintFunc()
			s.Restart()
		case "green":
			s.color = color.New(color.FgGreen).SprintFunc()
			s.Restart()
		case "magenta":
			s.color = color.New(color.FgMagenta).SprintFunc()
			s.Restart()
		case "blue":
			s.color = color.New(color.FgBlue).SprintFunc()
			s.Restart()
		case "cyan":
			s.color = color.New(color.FgCyan).SprintFunc()
			s.Restart()
		case "white":
			s.color = color.New(color.FgWhite).SprintFunc()
			s.Restart()
		default:
			return errInvalidColor
		}
	}
	return nil
}

// UpdateSpeed will set the indicator delay to the given value
func (s *Spinner) UpdateSpeed(d time.Duration) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.Delay = d
}

// UpdateCharSet will change the current character set to the given one
func (s *Spinner) UpdateCharSet(cs []string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.chars = cs
}

// erase deletes written characters
func (s *Spinner) erase(a string) {
	n := utf8.RuneCountInString(a)
	s.lock.RLock()
	defer s.lock.RUnlock()
	for i := 0; i < n; i++ {
		fmt.Fprintf(s.Writer, "\b")
	}
}

// GenerateNumberSequence will generate a slice of integers at the
// provided length and convert them each to a string
func GenerateNumberSequence(length int) []string {
	var numSeq []string
	for i := 0; i < length; i++ {
		numSeq = append(numSeq, strconv.Itoa(i))
	}
	return numSeq
}
<|endoftext|>


<commit_before>package gnuplot

import (
	"fmt"
	"io/ioutil"
	"os"
	// "os/exec"
	"strconv"
	"strings"
)

// Plotter
type Plotter struct {
	configures map[string]string
}

func NewPlotter() *Plotter {
	plotter := new(Plotter)
	plotter.configures = map[string]string{}
	return plotter
}

func (p *Plotter) Configure(key, val string) {
	p.configures[key] = val
}

func (p *Plotter) GetC(key string) string {
	return p.configures[key]
}

const DefaultFunction2dSplitNum int = 1000

type Function2d struct {
	plotter  Plotter
	splitNum int
	f        func(float64) float64
}

func NewFunction2d() *Function2d {
	fun := new(Function2d)
	fun.splitNum = DefaultFunction2dSplitNum
	fun.plotter.configures = map[string]string{
		"_xMin": "-10.0",
		"_xMax": "10.0"}
	return fun
}

func (fun *Function2d) Configure(key, val string) {
	fun.plotter.Configure(key, val)
}

func (fun *Function2d) Configures(m map[string]string) {
	for key, val := range m {
		fun.plotter.Configure(key, val)
	}
}

func (fun *Function2d) UpdatePlotter(plotter *Plotter) {
	for key, val := range plotter.configures {
		fun.plotter.configures[key] = val
	}
}

func (fun *Function2d) GetData() [][2]float64 { // TODO: テスト書く
	xMin, _ := strconv.ParseFloat(fun.plotter.configures["_xMin"], 32)
	xMax, _ := strconv.ParseFloat(fun.plotter.configures["_xMax"], 32)
	var sep = float64(xMax-xMin) / float64(fun.splitNum-1)

	var a [][2]float64
	for j := 0; j < fun.splitNum; j++ {
		t := xMin + float64(j)*sep
		y := fun.f(t)
		a = append(a, [2]float64{t, y})
	}
	return a
}

func (fun *Function2d) getGnuData() string {
	var s string
	for _, xs := range fun.GetData() {
		s += fmt.Sprintf("%f %f\n", xs[0], xs[1])
	}
	return s
}

func (fun *Function2d) SetF(_f func(float64) float64) {
	fun.f = _f
}

func (fun Function2d) gnuplot(filename string) string {
	var s = fmt.Sprintf("\"%v\"", filename)
	for key, val := range fun.plotter.configures {
		if !strings.HasPrefix(key, "_") {
			s += fmt.Sprintf(" %v %v", key, val)
		}
	}
	return s
}

func (fun *Function2d) writeIntoGnufile(f os.File) {
	f.WriteString(fun.getGnuData())
}

const DefaultCurve2dSplitNum int = 100

type Curve2d struct {
	plotter  Plotter
	splitNum int
	c        func(float64) [2]float64
}

func NewCurve2d() *Curve2d {
	c := new(Curve2d)
	c.splitNum = DefaultCurve2dSplitNum
	c.plotter.configures = map[string]string{
		"_tMin": "-10.0",
		"_tMax": "10.0"}
	return c
}

func (c *Curve2d) Configure(key, val string) {
	c.plotter.Configure(key, val)
}

func (c *Curve2d) Configures(m map[string]string) {
	for key, val := range m {
		c.plotter.Configure(key, val)
	}
}

func (c *Curve2d) UpdatePlotter(plotter *Plotter) {
	for key, val := range plotter.configures {
		c.plotter.Configure(key, val)
	}
}

func (c *Curve2d) GetData() [][2]float64 { // TODO: test
	tMin, _ := strconv.ParseFloat(c.plotter.configures["_tMin"], 32)
	tMax, _ := strconv.ParseFloat(c.plotter.configures["_tMax"], 32)
	var sep = float64(tMax-tMin) / float64(c.splitNum-1)

	var a [][2]float64
	for j := 0; j < c.splitNum; j++ {
		var t float64 = tMin + float64(j)*sep
		cs := c.c(tMin + t*float64(j))
		a = append(a, [2]float64{cs[0], cs[1]})
	}
	return a
}

func (c *Curve2d) getGnuData() string {
	var s string
	for _, xs := range c.GetData() {
		s += fmt.Sprintf("%f %f\n", xs[0], xs[1])
	}
	return s
}

func (c *Curve2d) SetC(_c func(float64) [2]float64) {
	c.c = _c
}

func (c Curve2d) gnuplot(fileName string) string {
	var s = fmt.Sprintf("\"%v\" ", fileName)
	for key, val := range c.plotter.configures {
		if !strings.HasPrefix(key, "_") {
			s += fmt.Sprintf(" %v %v", key, val)
		}
	}
	return s
}

// Graph
type Graph2d struct {
	plotter   Plotter
	functions []Function2d
	curves    []Curve2d
}

func NewGraph2d() *Graph2d {
	g := new(Graph2d)
	g.plotter.configures = map[string]string{}
	return g
}

func (g *Graph2d) Configure(key, val string) {
	g.plotter.Configure(key, val)
}

func (g *Graph2d) Configures(m map[string]string) {
	for key, val := range m {
		g.plotter.Configure(key, val)
	}
}

func (g *Graph2d) AppendFunc(f Function2d) {
	g.functions = append(g.functions, f)
}

func (g *Graph2d) AppendCurve(c Curve2d) {
	g.curves = append(g.curves, c)
}

func (g Graph2d) writeIntoFile(data string, f *os.File) {
	f.WriteString(data)
}

func (g *Graph2d) UpdatePlotter(plotter *Plotter) {
	for key, val := range plotter.configures {
		g.plotter.Configure(key, val)
	}
}

func (g Graph2d) gnuplot(funcFilenames []string, curveFilenames []string) string {
	var s string

	for key, val := range g.plotter.configures {
		if !strings.HasPrefix(key, "_") {
			if val == "true" {
				s += fmt.Sprintf("set %v;\n", key)
			} else if val == "false" {
				s += fmt.Sprintf("set no%v;\n", key)
			} else {
				s += fmt.Sprintf("set %v %v;\n", key, val)
			}
		}
	}

	s += "plot "
	for j, _ := range g.functions {
		s += g.functions[j].gnuplot(funcFilenames[j]) + ", "
	}
	for j, _ := range g.curves {
		s += g.curves[j].gnuplot(curveFilenames[j])
		if j != len(g.curves)-1 {
			s += ", "
		}
	}
	s += ";\n"
	s += "pause -1;\n"
	return s
}

func (g *Graph2d) Run() {
	tmpDir := os.TempDir() + "/gnuplot.go/"
	// TODO: tmpDirがなければ作る
	// execFilename := tmpDir + "exec.gnu"
	execFilename := "exec.gnu"

	// それぞれのfunctionのdataをtempファイルに書き込む
	// また, それらのファイルの名前を func_filenames []string に格納する
	var funcFilenames []string
	for _, fun := range g.functions {
		file, err := ioutil.TempFile(tmpDir, "")
		defer func() {
			file.Close()
		}()
		if err != nil {
			panic(fmt.Sprintf("%v", err))
		} else {
			g.writeIntoFile(fun.getGnuData(), file)
			funcFilenames = append(funcFilenames, file.Name())
		}
	}

	// それぞれのcurveのdataをtempファイルに書き込む
	// また, それらのファイルの名前を curve_filenames []stringに格納する
	var curveFilenames []string
	for _, c := range g.curves {
		file, _ := ioutil.TempFile(tmpDir, "")
		defer func() {
			file.Close()
		}()
		g.writeIntoFile(c.getGnuData(), file)
		curveFilenames = append(curveFilenames, file.Name())
	}

	// 実行するgnuplotの実行ファイルをtempファイルに書き込む
	os.Remove(execFilename)
	execFile, _ := os.OpenFile(execFilename, os.O_CREATE|os.O_WRONLY, 0666)
	defer func() {
		execFile.Close()
	}()
	execFile.WriteString(g.gnuplot(funcFilenames, curveFilenames))
}
<commit_msg>basic of configure<commit_after>package gnuplot

import (
	"fmt"
	"io/ioutil"
	"os"
	// "os/exec"
	"strconv"
	"strings"
)

// Plotter
type Plotter struct {
	configures map[string]string
}

func NewPlotter() *Plotter {
	plotter := new(Plotter)
	plotter.configures = map[string]string{}
	return plotter
}

func (p *Plotter) Configure(key, val string) {
	p.configures[key] = val
}

func (p *Plotter) GetC(key string) string {
	return p.configures[key]
}

// Configure
type Configure struct {
	key               string
	val               string
	requiredCondition func(val string) bool
}

func NewConfigure(key, defaultVal string, requiredCondition func(val string) bool) *Configure {
	conf := new(Configure)
	conf.key = key
	conf.val = defaultVal
	conf.requiredCondition = requiredCondition
	return conf
}

var WITH_CONF = NewConfigure("with", "line", func(val string) bool {
	return val == "line" || val == "dots"
})

func (conf *Configure) SetVal(val string) {
	if conf.requiredCondition(val) {
		conf.val = val
	} else {
		panic(fmt.Sprintf("%v is illegal value of %v.", val, conf.key))
	}
}

// Function2d
const DefaultFunction2dSplitNum int = 1000

type Function2d struct {
	plotter  Plotter
	splitNum int
	f        func(float64) float64
}

func NewFunction2d() *Function2d {
	fun := new(Function2d)
	fun.splitNum = DefaultFunction2dSplitNum
	fun.plotter.configures = map[string]string{
		"_xMin": "-10.0",
		"_xMax": "10.0"}
	return fun
}

func (fun *Function2d) Configure(key, val string) {
	fun.plotter.Configure(key, val)
}

func (fun *Function2d) Configures(m map[string]string) {
	for key, val := range m {
		fun.plotter.Configure(key, val)
	}
}

func (fun *Function2d) UpdatePlotter(plotter *Plotter) {
	for key, val := range plotter.configures {
		fun.plotter.configures[key] = val
	}
}

func (fun *Function2d) GetData() [][2]float64 { // TODO: テスト書く
	xMin, _ := strconv.ParseFloat(fun.plotter.configures["_xMin"], 32)
	xMax, _ := strconv.ParseFloat(fun.plotter.configures["_xMax"], 32)
	var sep = float64(xMax-xMin) / float64(fun.splitNum-1)

	var a [][2]float64
	for j := 0; j < fun.splitNum; j++ {
		t := xMin + float64(j)*sep
		y := fun.f(t)
		a = append(a, [2]float64{t, y})
	}
	return a
}

func (fun *Function2d) getGnuData() string {
	var s string
	for _, xs := range fun.GetData() {
		s += fmt.Sprintf("%f %f\n", xs[0], xs[1])
	}
	return s
}

func (fun *Function2d) SetF(_f func(float64) float64) {
	fun.f = _f
}

func (fun Function2d) gnuplot(filename string) string {
	var s = fmt.Sprintf("\"%v\"", filename)
	for key, val := range fun.plotter.configures {
		if !strings.HasPrefix(key, "_") {
			s += fmt.Sprintf(" %v %v", key, val)
		}
	}
	return s
}

func (fun *Function2d) writeIntoGnufile(f os.File) {
	f.WriteString(fun.getGnuData())
}

const DefaultCurve2dSplitNum int = 100

type Curve2d struct {
	plotter  Plotter
	splitNum int
	c        func(float64) [2]float64
}

func NewCurve2d() *Curve2d {
	c := new(Curve2d)
	c.splitNum = DefaultCurve2dSplitNum
	c.plotter.configures = map[string]string{
		"_tMin": "-10.0",
		"_tMax": "10.0"}
	return c
}

func (c *Curve2d) Configure(key, val string) {
	c.plotter.Configure(key, val)
}

func (c *Curve2d) Configures(m map[string]string) {
	for key, val := range m {
		c.plotter.Configure(key, val)
	}
}

func (c *Curve2d) UpdatePlotter(plotter *Plotter) {
	for key, val := range plotter.configures {
		c.plotter.Configure(key, val)
	}
}

func (c *Curve2d) GetData() [][2]float64 { // TODO: test
	tMin, _ := strconv.ParseFloat(c.plotter.configures["_tMin"], 32)
	tMax, _ := strconv.ParseFloat(c.plotter.configures["_tMax"], 32)
	var sep = float64(tMax-tMin) / float64(c.splitNum-1)

	var a [][2]float64
	for j := 0; j < c.splitNum; j++ {
		var t float64 = tMin + float64(j)*sep
		cs := c.c(tMin + t*float64(j))
		a = append(a, [2]float64{cs[0], cs[1]})
	}
	return a
}

func (c *Curve2d) getGnuData() string {
	var s string
	for _, xs := range c.GetData() {
		s += fmt.Sprintf("%f %f\n", xs[0], xs[1])
	}
	return s
}

func (c *Curve2d) SetC(_c func(float64) [2]float64) {
	c.c = _c
}

func (c Curve2d) gnuplot(fileName string) string {
	var s = fmt.Sprintf("\"%v\" ", fileName)
	for key, val := range c.plotter.configures {
		if !strings.HasPrefix(key, "_") {
			s += fmt.Sprintf(" %v %v", key, val)
		}
	}
	return s
}

// Graph
type Graph2d struct {
	plotter   Plotter
	functions []Function2d
	curves    []Curve2d
}

func NewGraph2d() *Graph2d {
	g := new(Graph2d)
	g.plotter.configures = map[string]string{}
	return g
}

func (g *Graph2d) Configure(key, val string) {
	g.plotter.Configure(key, val)
}

func (g *Graph2d) Configures(m map[string]string) {
	for key, val := range m {
		g.plotter.Configure(key, val)
	}
}

func (g *Graph2d) AppendFunc(f Function2d) {
	g.functions = append(g.functions, f)
}

func (g *Graph2d) AppendCurve(c Curve2d) {
	g.curves = append(g.curves, c)
}

func (g Graph2d) writeIntoFile(data string, f *os.File) {
	f.WriteString(data)
}

func (g *Graph2d) UpdatePlotter(plotter *Plotter) {
	for key, val := range plotter.configures {
		g.plotter.Configure(key, val)
	}
}

func (g Graph2d) gnuplot(funcFilenames []string, curveFilenames []string) string {
	var s string

	for key, val := range g.plotter.configures {
		if !strings.HasPrefix(key, "_") {
			if val == "true" {
				s += fmt.Sprintf("set %v;\n", key)
			} else if val == "false" {
				s += fmt.Sprintf("set no%v;\n", key)
			} else {
				s += fmt.Sprintf("set %v %v;\n", key, val)
			}
		}
	}

	s += "plot "
	for j, _ := range g.functions {
		s += g.functions[j].gnuplot(funcFilenames[j]) + ", "
	}
	for j, _ := range g.curves {
		s += g.curves[j].gnuplot(curveFilenames[j])
		if j != len(g.curves)-1 {
			s += ", "
		}
	}
	s += ";\n"
	s += "pause -1;\n"
	return s
}

func (g *Graph2d) Run() {
	tmpDir := os.TempDir() + "/gnuplot.go/"
	// TODO: tmpDirがなければ作る
	// execFilename := tmpDir + "exec.gnu"
	execFilename := "exec.gnu"

	// それぞれのfunctionのdataをtempファイルに書き込む
	// また, それらのファイルの名前を func_filenames []string に格納する
	var funcFilenames []string
	for _, fun := range g.functions {
		file, err := ioutil.TempFile(tmpDir, "")
		defer func() {
			file.Close()
		}()
		if err != nil {
			panic(fmt.Sprintf("%v", err))
		} else {
			g.writeIntoFile(fun.getGnuData(), file)
			funcFilenames = append(funcFilenames, file.Name())
		}
	}

	// それぞれのcurveのdataをtempファイルに書き込む
	// また, それらのファイルの名前を curve_filenames []stringに格納する
	var curveFilenames []string
	for _, c := range g.curves {
		file, _ := ioutil.TempFile(tmpDir, "")
		defer func() {
			file.Close()
		}()
		g.writeIntoFile(c.getGnuData(), file)
		curveFilenames = append(curveFilenames, file.Name())
	}

	// 実行するgnuplotの実行ファイルをtempファイルに書き込む
	os.Remove(execFilename)
	execFile, _ := os.OpenFile(execFilename, os.O_CREATE|os.O_WRONLY, 0666)
	defer func() {
		execFile.Close()
	}()
	execFile.WriteString(g.gnuplot(funcFilenames, curveFilenames))
}
<|endoftext|>


<commit_before>package upstream

import (
	"fmt"
	"github.com/ngmoco/falcore"
	"net"
	"net/http"
	"time"
	"io"
	"bytes"
)

type passThruReadCloser struct {
	io.Reader
	io.Closer
}

type Upstream struct {
	// The upstream host to connect to
	Host string
	// The port on the upstream host
	Port int
	// Default 60 seconds
	Timeout time.Duration
	// Will ignore https on the incoming request and always upstream http
	ForceHttp bool
	// Ping URL Path-only for checking upness
	PingPath string

	transport *http.Transport
	host      string
	tcpaddr   *net.TCPAddr
	tcpconn   *net.TCPConn
}

func NewUpstream(host string, port int, forceHttp bool) *Upstream {
	u := new(Upstream)
	u.Host = host
	u.Port = port
	u.ForceHttp = forceHttp
	ips, err := net.LookupIP(host)
	var ip net.IP = nil
	for i := range ips {
		ip = ips[i].To4()
		if ip != nil {
			break
		}
	}
	if err == nil && ip != nil {
		u.tcpaddr = new(net.TCPAddr)
		u.tcpaddr.Port = port
		u.tcpaddr.IP = ip
	} else {
		falcore.Warn("Can't get IP addr for %v: %v", host, err)
	}
	u.Timeout = 60e9
	u.host = fmt.Sprintf("%v:%v", u.Host, u.Port)

	u.transport = new(http.Transport)

	u.transport.Dial = func(n, addr string) (c net.Conn, err error) {
		falcore.Fine("Dialing connection to %v", u.tcpaddr)
		var ctcp *net.TCPConn
		ctcp, err = net.DialTCP("tcp4", nil, u.tcpaddr)
		if ctcp != nil {
			u.tcpconn = ctcp
			u.tcpconn.SetDeadline(time.Now().Add(u.Timeout))
		}
		if err != nil {
			falcore.Error("Dial Failed: %v", err)
		}
		return ctcp, err
	}
	u.transport.MaxIdleConnsPerHost = 15
	return u
}

// Alter the number of connections to multiplex with
func (u *Upstream) SetPoolSize(size int) {
	u.transport.MaxIdleConnsPerHost = size
}

func (u *Upstream) FilterRequest(request *falcore.Request) (res *http.Response) {
	var err error
	req := request.HttpRequest

	// Force the upstream to use http
	if u.ForceHttp || req.URL.Scheme == "" {
		req.URL.Scheme = "http"
		req.URL.Host = req.Host
	}
	before := time.Now()
	req.Header.Set("Connection", "Keep-Alive")
	if u.tcpconn != nil {
		u.tcpconn.SetDeadline(time.Now().Add(u.Timeout))
	}
	var upstrRes *http.Response
	upstrRes, err = u.transport.RoundTrip(req)
	diff := falcore.TimeDiff(before, time.Now())
	if err == nil {
		// Copy response over to new record.  Remove connection noise.  Add some sanity.
		res = falcore.SimpleResponse(req, upstrRes.StatusCode, nil, "")
		if upstrRes.ContentLength > 0 && upstrRes.Body != nil {
			res.Body = upstrRes.Body
		} else if upstrRes.ContentLength == 0 && upstrRes.Body != nil {
			// Any bytes?
			var testBuf [1]byte
			n, _ := io.ReadFull(upstrRes.Body, testBuf[:])
			if n == 1 {
				// Yes there are.  Chunked it is.
				res.TransferEncoding = []string{"chunked"}
				rc := &passThruReadCloser{
					io.MultiReader(bytes.NewBuffer(testBuf[:]), upstrRes.Body),
					upstrRes.Body,
				}

				res.Body = rc
			}
		} else if upstrRes.Body != nil {
			res.Body = upstrRes.Body
			res.TransferEncoding = []string{"chunked"}
		}
		// Copy over headers with a few exceptions
		res.Header = make(http.Header)
		for hn, hv := range upstrRes.Header {
			switch hn {
			case "Content-Length":
			case "Connection":
			case "Transfer-Encoding":
			default:
				res.Header[hn] = hv
			}
		}
	} else {
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			falcore.Error("%s Upstream Timeout error: %v", request.ID, err)
			res = falcore.SimpleResponse(req, 504, nil, "Gateway Timeout\n")
			request.CurrentStage.Status = 2 // Fail
		} else {
			falcore.Error("%s Upstream error: %v", request.ID, err)
			res = falcore.SimpleResponse(req, 502, nil, "Bad Gateway\n")
			request.CurrentStage.Status = 2 // Fail
		}
	}
	falcore.Debug("%s [%s] [%s] %s s=%d Time=%.4f", request.ID, req.Method, u.host, req.URL, res.StatusCode, diff)
	return
}

func (u *Upstream) ping() (up bool, ok bool) {
	if u.PingPath != "" {
		// the url must be syntactically valid for this to work but the host will be ignored because we
		// are overriding the connection always
		request, err := http.NewRequest("GET", "http://localhost"+u.PingPath, nil)
		request.Header.Set("Connection", "Keep-Alive") // not sure if this should be here for a ping
		if err != nil {
			falcore.Error("Bad Ping request: %v", err)
			return false, true
		}
		if u.tcpconn != nil {
			u.tcpconn.SetDeadline(time.Now().Add(u.Timeout))
		}
		res, err := u.transport.RoundTrip(request)

		if err != nil {
			falcore.Error("Failed Ping to %v:%v: %v", u.Host, u.Port, err)
			return false, true
		} else {
			res.Body.Close()
		}
		if res.StatusCode == 200 {
			return true, true
		}
		falcore.Error("Failed Ping to %v:%v: %v", u.Host, u.Port, res.Status)
		// bad status
		return false, true
	}
	return false, false
}
<commit_msg>leaving ContentLength at 0 breaks other things<commit_after>package upstream

import (
	"fmt"
	"github.com/ngmoco/falcore"
	"net"
	"net/http"
	"time"
	"io"
	"bytes"
)

type passThruReadCloser struct {
	io.Reader
	io.Closer
}

type Upstream struct {
	// The upstream host to connect to
	Host string
	// The port on the upstream host
	Port int
	// Default 60 seconds
	Timeout time.Duration
	// Will ignore https on the incoming request and always upstream http
	ForceHttp bool
	// Ping URL Path-only for checking upness
	PingPath string

	transport *http.Transport
	host      string
	tcpaddr   *net.TCPAddr
	tcpconn   *net.TCPConn
}

func NewUpstream(host string, port int, forceHttp bool) *Upstream {
	u := new(Upstream)
	u.Host = host
	u.Port = port
	u.ForceHttp = forceHttp
	ips, err := net.LookupIP(host)
	var ip net.IP = nil
	for i := range ips {
		ip = ips[i].To4()
		if ip != nil {
			break
		}
	}
	if err == nil && ip != nil {
		u.tcpaddr = new(net.TCPAddr)
		u.tcpaddr.Port = port
		u.tcpaddr.IP = ip
	} else {
		falcore.Warn("Can't get IP addr for %v: %v", host, err)
	}
	u.Timeout = 60e9
	u.host = fmt.Sprintf("%v:%v", u.Host, u.Port)

	u.transport = new(http.Transport)

	u.transport.Dial = func(n, addr string) (c net.Conn, err error) {
		falcore.Fine("Dialing connection to %v", u.tcpaddr)
		var ctcp *net.TCPConn
		ctcp, err = net.DialTCP("tcp4", nil, u.tcpaddr)
		if ctcp != nil {
			u.tcpconn = ctcp
			u.tcpconn.SetDeadline(time.Now().Add(u.Timeout))
		}
		if err != nil {
			falcore.Error("Dial Failed: %v", err)
		}
		return ctcp, err
	}
	u.transport.MaxIdleConnsPerHost = 15
	return u
}

// Alter the number of connections to multiplex with
func (u *Upstream) SetPoolSize(size int) {
	u.transport.MaxIdleConnsPerHost = size
}

func (u *Upstream) FilterRequest(request *falcore.Request) (res *http.Response) {
	var err error
	req := request.HttpRequest

	// Force the upstream to use http
	if u.ForceHttp || req.URL.Scheme == "" {
		req.URL.Scheme = "http"
		req.URL.Host = req.Host
	}
	before := time.Now()
	req.Header.Set("Connection", "Keep-Alive")
	if u.tcpconn != nil {
		u.tcpconn.SetDeadline(time.Now().Add(u.Timeout))
	}
	var upstrRes *http.Response
	upstrRes, err = u.transport.RoundTrip(req)
	diff := falcore.TimeDiff(before, time.Now())
	if err == nil {
		// Copy response over to new record.  Remove connection noise.  Add some sanity.
		res = falcore.SimpleResponse(req, upstrRes.StatusCode, nil, "")
		if upstrRes.ContentLength > 0 && upstrRes.Body != nil {
			res.Body = upstrRes.Body
		} else if upstrRes.ContentLength == 0 && upstrRes.Body != nil {
			// Any bytes?
			var testBuf [1]byte
			n, _ := io.ReadFull(upstrRes.Body, testBuf[:])
			if n == 1 {
				// Yes there are.  Chunked it is.
				res.TransferEncoding = []string{"chunked"}
				res.ContentLength = -1
				rc := &passThruReadCloser{
					io.MultiReader(bytes.NewBuffer(testBuf[:]), upstrRes.Body),
					upstrRes.Body,
				}

				res.Body = rc
			}
		} else if upstrRes.Body != nil {
			res.Body = upstrRes.Body
			res.ContentLength = -1
			res.TransferEncoding = []string{"chunked"}
		}
		// Copy over headers with a few exceptions
		res.Header = make(http.Header)
		for hn, hv := range upstrRes.Header {
			switch hn {
			case "Content-Length":
			case "Connection":
			case "Transfer-Encoding":
			default:
				res.Header[hn] = hv
			}
		}
	} else {
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			falcore.Error("%s Upstream Timeout error: %v", request.ID, err)
			res = falcore.SimpleResponse(req, 504, nil, "Gateway Timeout\n")
			request.CurrentStage.Status = 2 // Fail
		} else {
			falcore.Error("%s Upstream error: %v", request.ID, err)
			res = falcore.SimpleResponse(req, 502, nil, "Bad Gateway\n")
			request.CurrentStage.Status = 2 // Fail
		}
	}
	falcore.Debug("%s [%s] [%s] %s s=%d Time=%.4f", request.ID, req.Method, u.host, req.URL, res.StatusCode, diff)
	return
}

func (u *Upstream) ping() (up bool, ok bool) {
	if u.PingPath != "" {
		// the url must be syntactically valid for this to work but the host will be ignored because we
		// are overriding the connection always
		request, err := http.NewRequest("GET", "http://localhost"+u.PingPath, nil)
		request.Header.Set("Connection", "Keep-Alive") // not sure if this should be here for a ping
		if err != nil {
			falcore.Error("Bad Ping request: %v", err)
			return false, true
		}
		if u.tcpconn != nil {
			u.tcpconn.SetDeadline(time.Now().Add(u.Timeout))
		}
		res, err := u.transport.RoundTrip(request)

		if err != nil {
			falcore.Error("Failed Ping to %v:%v: %v", u.Host, u.Port, err)
			return false, true
		} else {
			res.Body.Close()
		}
		if res.StatusCode == 200 {
			return true, true
		}
		falcore.Error("Failed Ping to %v:%v: %v", u.Host, u.Port, res.Status)
		// bad status
		return false, true
	}
	return false, false
}
<|endoftext|>