<commit_before>package coretest

import (
	"io"
	"os"
	"path"
	"strings"
	"syscall"
	"testing"
	"time"
)

const (
	fleetctlBinPath = "/usr/bin/fleetctl"
	tryTimes = 5
	tryInterval = 500 * time.Millisecond
	serviceData = `[Unit]
Description=Hello World
[Service]
ExecStart=/bin/bash -c "while true; do echo \"Hello, world\"; sleep 1; done"
`
)

// TestFleetctlListMachines tests that 'fleetctl list-machines' works
// and print itself out at least.
func TestFleetctlListMachines(t *testing.T) {
	stdout, stderr, err := Run(fleetctlBinPath, "list-machines", "--no-legend")
	if err != nil {
		t.Fatalf("fleetctl list-machines failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	stdout = strings.TrimSpace(stdout)
	if len(strings.Split(stdout, "\n")) == 0 {
		t.Fatalf("Failed listing out at least one machine\nstdout: %s", stdout)
	}
}

func checkServiceState(name string, t *testing.T) (exist bool, active bool) {
	stdout, stderr, err := Run(fleetctlBinPath, "list-units", "--no-legend")
	if err != nil {
		t.Fatalf("fleetctl list-units failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	lines := strings.Split(stdout, "\n")
	for _, line := range lines {
		if !strings.Contains(line, name) {
			continue
		}
		exist = true
		if strings.Contains(line, "running") {
			active = true
			return
		}
		return
	}
	return
}

// TestFleetctlRunService tests that fleetctl could start, stop and destroy
// unit file.
func TestFleetctlRunService(t *testing.T) {
	serviceName := "hello.service"

	serviceFile, err := os.Create(path.Join(os.TempDir(), serviceName))
	if err != nil {
		t.Fatalf("Failed creating %v: %v", serviceName, err)
	}
	defer syscall.Unlink(serviceFile.Name())

	if _, err := io.WriteString(serviceFile, serviceData); err != nil {
		t.Fatalf("Failed writing %v: %v", serviceFile.Name(), err)
	}

	stdout, stderr, err := Run(fleetctlBinPath, "start", serviceFile.Name())
	if err != nil {
		t.Fatalf("fleetctl start failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceActive := func() bool {
		exist, active := checkServiceState(serviceName, t)
		return exist && active
	}
	if !Retry(checkServiceActive, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is active", serviceName)
	}

	stdout, stderr, err = Run(fleetctlBinPath, "stop", serviceName)
	if err != nil {
		t.Fatalf("fleetctl stop failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceInactive := func() bool {
		exist, active := checkServiceState(serviceName, t)
		return exist && !active
	}
	if !Retry(checkServiceInactive, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is inactive", serviceName)
	}

	stdout, stderr, err = Run(fleetctlBinPath, "destroy", serviceName)
	if err != nil {
		t.Fatalf("fleetctl destroy failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceNonexist := func() bool {
		exist, _ := checkServiceState(serviceName, t)
		return !exist
	}
	if !Retry(checkServiceNonexist, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is nonexist", serviceName)
	}
}
<commit_msg>fix(fleet): Call unload rather than stop<commit_after>package coretest

import (
	"io"
	"os"
	"path"
	"strings"
	"syscall"
	"testing"
	"time"
)

const (
	fleetctlBinPath = "/usr/bin/fleetctl"
	tryTimes = 5
	tryInterval = 500 * time.Millisecond
	serviceData = `[Unit]
Description=Hello World
[Service]
ExecStart=/bin/bash -c "while true; do echo \"Hello, world\"; sleep 1; done"
`
)

// TestFleetctlListMachines tests that 'fleetctl list-machines' works
// and print itself out at least.
func TestFleetctlListMachines(t *testing.T) {
	stdout, stderr, err := Run(fleetctlBinPath, "list-machines", "--no-legend")
	if err != nil {
		t.Fatalf("fleetctl list-machines failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	stdout = strings.TrimSpace(stdout)
	if len(strings.Split(stdout, "\n")) == 0 {
		t.Fatalf("Failed listing out at least one machine\nstdout: %s", stdout)
	}
}

func checkServiceState(name string, t *testing.T) (exist bool, active bool) {
	stdout, stderr, err := Run(fleetctlBinPath, "list-units", "--no-legend")
	if err != nil {
		t.Fatalf("fleetctl list-units failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	lines := strings.Split(stdout, "\n")
	for _, line := range lines {
		if !strings.Contains(line, name) {
			continue
		}
		exist = true
		if strings.Contains(line, "running") {
			active = true
			return
		}
		return
	}
	return
}

// TestFleetctlRunService tests that fleetctl could start, unload and destroy
// unit file.
func TestFleetctlRunService(t *testing.T) {
	serviceName := "hello.service"

	serviceFile, err := os.Create(path.Join(os.TempDir(), serviceName))
	if err != nil {
		t.Fatalf("Failed creating %v: %v", serviceName, err)
	}
	defer syscall.Unlink(serviceFile.Name())

	if _, err := io.WriteString(serviceFile, serviceData); err != nil {
		t.Fatalf("Failed writing %v: %v", serviceFile.Name(), err)
	}

	stdout, stderr, err := Run(fleetctlBinPath, "start", serviceFile.Name())
	if err != nil {
		t.Fatalf("fleetctl start failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceActive := func() bool {
		exist, active := checkServiceState(serviceName, t)
		return exist && active
	}
	if !Retry(checkServiceActive, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is active", serviceName)
	}

	stdout, stderr, err = Run(fleetctlBinPath, "unload", serviceName)
	if err != nil {
		t.Fatalf("fleetctl unload failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceInactive := func() bool {
		exist, active := checkServiceState(serviceName, t)
		return exist && !active
	}
	if !Retry(checkServiceInactive, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is inactive", serviceName)
	}

	stdout, stderr, err = Run(fleetctlBinPath, "destroy", serviceName)
	if err != nil {
		t.Fatalf("fleetctl destroy failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceNonexist := func() bool {
		exist, _ := checkServiceState(serviceName, t)
		return !exist
	}
	if !Retry(checkServiceNonexist, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is nonexist", serviceName)
	}
}
<|endoftext|>


<commit_before>/*
Collection is a container class for organizing raw line coordinate data.

Collection containers are hierarchical.  A Collection contains one or more Axis
objects. And each Axis contains one or more Line objects. So, if you only need
a single "Collection", a default is provided using:

    collection.Default()

Since collections exist to help with plotting line data, there is one
convenience function for plotting. To generate an image representing the
collection data, use:

		collection.Plot()
*/
package collection

import (
	"encoding/hex"
	"image/color"
	"io"
	"math"
	"sync"
	"time"

	// third party
	"github.com/stephen-soltesz/go/plotter"
)

var defaultCollection *Collection
var lock *sync.RWMutex

func init() {
	lock = &sync.RWMutex{}
}

// A Collection is the root container.
// Axes is a map, so that Axis objects can be referred to by name, see GetAxis().
type Collection struct {
	Title   string
	Axes    map[string]*Axis
	Usetime bool
}

// A Collection contains one or more Axis objects.
type Axis struct {
	Name       string
	XLabel     string
	YLabel     string
	Lines      map[string]*Line
	Ylimit     bool
	Ymin, Ymax float64
	Uselog     bool
}

// An Axis contains one or more Line objects.
// The X and Y arrays contain coordinates of each point on the Line.
type Line struct {
	Name  string
	Style plotter.Style
	X     []float64
	Y     []float64

	// internal book keeping
	xyrange
}

type xyrange struct {
	// internal book keeping
	max_x float64
	min_x float64
	max_y float64
	min_y float64
}

func initXYrange() xyrange {
	return xyrange{math.SmallestNonzeroFloat64, math.MaxFloat64,
		math.SmallestNonzeroFloat64, math.MaxFloat64}
}

func (xy *xyrange) updateXYrange(x, y float64) {
	xy.min_x = math.Min(x, xy.min_x)
	xy.max_x = math.Max(x, xy.max_x)

	xy.min_y = math.Min(y, xy.min_y)
	xy.max_y = math.Max(y, xy.max_y)
}

func New() *Collection {
	c := &Collection{}
	c.Axes = make(map[string]*Axis)
	return c
}

func Default() *Collection {
	if defaultCollection == nil {
		defaultCollection = New()
	}
	return defaultCollection
}

// Returns the Axis associated with given name. If no Axis exists, a new Axis
// is created and associated with given name.
func (c *Collection) GetAxis(name string) *Axis {
	// TODO: make thread safe
	if axis, ok := c.Axes[name]; !ok {
		return c.AddAxis(name, "", "")
	} else {
		return axis
	}
}

func (c *Collection) AddAxis(name, xlabel, ylabel string) *Axis {
	lock.Lock()
	defer lock.Unlock()

	axis := &Axis{name, xlabel, ylabel, make(map[string]*Line), false, 0, 0, false}
	c.Axes[name] = axis
	return axis
}

func (ax *Axis) GetLine(name string) *Line {
	// TODO: make thread safe
	if line, ok := ax.Lines[name]; !ok {
		return ax.AddLine(name)
	} else {
		return line
	}
}

func (ax *Axis) AddLine(name string) *Line {
	lock.Lock()
	defer lock.Unlock()

	line := &Line{}
	line.Name = name
	line.Style = plotter.NextStyle()
	line.xyrange = initXYrange()
	ax.Lines[name] = line
	return line
}

func (line *Line) SetColor(hexColor string) {
	if len(hexColor) > 0 && hexColor[0] == '#' {
		hexColor = hexColor[1:]
	}
	if len(hexColor) == 6 {
		rgb, err := hex.DecodeString(hexColor)
		if err != nil {
			return
		}
		r, g, b := rgb[0], rgb[1], rgb[2]
		line.Style.SymbolColor = color.RGBA{r, g, b, 255}
		line.Style.LineColor = color.RGBA{r, g, b, 255}
		line.Style.FillColor = color.RGBA{r, g, b, 255}
	}
}

// Append adds a new x,y coordinate to the end of the Line.
func (line *Line) Append(x, y float64) {
	lock.Lock()
	defer lock.Unlock()

	line.X = append(line.X, x)
	line.Y = append(line.Y, y)
	line.xyrange.updateXYrange(x, y)
}

// Count returns the length of Line X and Y coordinates.
func (line *Line) Count() (int, int) {
	lock.RLock()
	defer lock.RUnlock()

	return len(line.X), len(line.Y)
}

func (line *Line) RangeX() (float64, float64) {
	lock.RLock()
	defer lock.RUnlock()

	min := line.xyrange.min_x - 0.1
	max := line.xyrange.max_x + 0.1
	return min, max
}

func (line *Line) RangeY() (float64, float64) {
	lock.RLock()
	defer lock.RUnlock()

	min := line.xyrange.min_y - 0.1
	max := line.xyrange.max_y + 0.1
	return min, max
}

// MaxX returns the greatest X coordinate for all Lines in this Axis.
func (ax *Axis) MaxX() float64 {
	lock.RLock()
	defer lock.RUnlock()

	xmax := 0.0
	if len(ax.Lines) == 0 {
		return xmax
	}
	for _, line := range ax.Lines {
		xlen, _ := line.Count()
		if xlen == 0 {
			continue
		}
		_, lmax := line.RangeX()
		xmax = math.Max(lmax, xmax)
	}
	return xmax
}

func (c *Collection) Plot(writer io.Writer, width, height int, offset float64, samples float64) error {
	lock.RLock()
	defer lock.RUnlock()

	fig := plotter.NewFigure(c.Usetime)
	for _, ax := range c.Axes {
		xmax := 0.0
		if c.Usetime {
			// continuously update plot with most current time.
			// TODO: make this an option.
			xmax = float64(time.Now().Unix()) - offset
		} else {
			xmax = ax.MaxX() - offset
		}
		chart := fig.AddChart(c.Title, ax.XLabel, ax.YLabel, xmax-samples, xmax)
		chart.YRange.Log = ax.Uselog
		if ax.Ylimit {
			chart.YRange.Fixed(ax.Ymin, ax.Ymax, 0)
			chart.YRange.Init()
		}
		for _, line := range ax.Lines {
			if xc, _ := line.Count(); xc > 0 {
				chart.AddData(line.Name, line.X, line.Y, line.Style)
			}
		}
	}

	return fig.RenderSVG(writer, width, height)
}
<commit_msg>use generic render method.<commit_after>/*
Collection is a container class for organizing raw line coordinate data.

Collection containers are hierarchical.  A Collection contains one or more Axis
objects. And each Axis contains one or more Line objects. So, if you only need
a single "Collection", a default is provided using:

    collection.Default()

Since collections exist to help with plotting line data, there is one
convenience function for plotting. To generate an image representing the
collection data, use:

		collection.Plot()
*/
package collection

import (
	"encoding/hex"
	"image/color"
	"io"
	"math"
	"sync"
	"time"

	// third party
	"github.com/stephen-soltesz/go/plotter"
)

var defaultCollection *Collection
var lock *sync.RWMutex

func init() {
	lock = &sync.RWMutex{}
}

// A Collection is the root container.
// Axes is a map, so that Axis objects can be referred to by name, see GetAxis().
type Collection struct {
	Title   string
	Axes    map[string]*Axis
	Usetime bool
}

// A Collection contains one or more Axis objects.
type Axis struct {
	Name       string
	XLabel     string
	YLabel     string
	Lines      map[string]*Line
	Ylimit     bool
	Ymin, Ymax float64
	Uselog     bool
}

// An Axis contains one or more Line objects.
// The X and Y arrays contain coordinates of each point on the Line.
type Line struct {
	Name  string
	Style plotter.Style
	X     []float64
	Y     []float64

	// internal book keeping
	xyrange
}

type xyrange struct {
	// internal book keeping
	max_x float64
	min_x float64
	max_y float64
	min_y float64
}

func initXYrange() xyrange {
	return xyrange{math.SmallestNonzeroFloat64, math.MaxFloat64,
		math.SmallestNonzeroFloat64, math.MaxFloat64}
}

func (xy *xyrange) updateXYrange(x, y float64) {
	xy.min_x = math.Min(x, xy.min_x)
	xy.max_x = math.Max(x, xy.max_x)

	xy.min_y = math.Min(y, xy.min_y)
	xy.max_y = math.Max(y, xy.max_y)
}

func New() *Collection {
	c := &Collection{}
	c.Axes = make(map[string]*Axis)
	return c
}

func Default() *Collection {
	if defaultCollection == nil {
		defaultCollection = New()
	}
	return defaultCollection
}

// Returns the Axis associated with given name. If no Axis exists, a new Axis
// is created and associated with given name.
func (c *Collection) GetAxis(name string) *Axis {
	// TODO: make thread safe
	if axis, ok := c.Axes[name]; !ok {
		return c.AddAxis(name, "", "")
	} else {
		return axis
	}
}

func (c *Collection) AddAxis(name, xlabel, ylabel string) *Axis {
	lock.Lock()
	defer lock.Unlock()

	axis := &Axis{name, xlabel, ylabel, make(map[string]*Line), false, 0, 0, false}
	c.Axes[name] = axis
	return axis
}

func (ax *Axis) GetLine(name string) *Line {
	// TODO: make thread safe
	if line, ok := ax.Lines[name]; !ok {
		return ax.AddLine(name)
	} else {
		return line
	}
}

func (ax *Axis) AddLine(name string) *Line {
	lock.Lock()
	defer lock.Unlock()

	line := &Line{}
	line.Name = name
	line.Style = plotter.NextStyle()
	line.xyrange = initXYrange()
	ax.Lines[name] = line
	return line
}

func (line *Line) SetColor(hexColor string) {
	if len(hexColor) > 0 && hexColor[0] == '#' {
		hexColor = hexColor[1:]
	}
	if len(hexColor) == 6 {
		rgb, err := hex.DecodeString(hexColor)
		if err != nil {
			return
		}
		r, g, b := rgb[0], rgb[1], rgb[2]
		line.Style.SymbolColor = color.RGBA{r, g, b, 255}
		line.Style.LineColor = color.RGBA{r, g, b, 255}
		line.Style.FillColor = color.RGBA{r, g, b, 255}
	}
}

// Append adds a new x,y coordinate to the end of the Line.
func (line *Line) Append(x, y float64) {
	lock.Lock()
	defer lock.Unlock()

	line.X = append(line.X, x)
	line.Y = append(line.Y, y)
	line.xyrange.updateXYrange(x, y)
}

// Count returns the length of Line X and Y coordinates.
func (line *Line) Count() (int, int) {
	lock.RLock()
	defer lock.RUnlock()

	return len(line.X), len(line.Y)
}

func (line *Line) RangeX() (float64, float64) {
	lock.RLock()
	defer lock.RUnlock()

	min := line.xyrange.min_x - 0.1
	max := line.xyrange.max_x + 0.1
	return min, max
}

func (line *Line) RangeY() (float64, float64) {
	lock.RLock()
	defer lock.RUnlock()

	min := line.xyrange.min_y - 0.1
	max := line.xyrange.max_y + 0.1
	return min, max
}

// MaxX returns the greatest X coordinate for all Lines in this Axis.
func (ax *Axis) MaxX() float64 {
	lock.RLock()
	defer lock.RUnlock()

	xmax := 0.0
	if len(ax.Lines) == 0 {
		return xmax
	}
	for _, line := range ax.Lines {
		xlen, _ := line.Count()
		if xlen == 0 {
			continue
		}
		_, lmax := line.RangeX()
		xmax = math.Max(lmax, xmax)
	}
	return xmax
}

func (c *Collection) Plot(writer io.Writer, width, height int, offset float64, samples float64) error {
	lock.RLock()
	defer lock.RUnlock()

	fig := plotter.NewFigure(c.Usetime)
	for _, ax := range c.Axes {
		xmax := 0.0
		if c.Usetime {
			// continuously update plot with most current time.
			// TODO: make this an option.
			xmax = float64(time.Now().Unix()) - offset
		} else {
			xmax = ax.MaxX() - offset
		}
		chart := fig.AddChart(c.Title, ax.XLabel, ax.YLabel, xmax-samples, xmax)
		chart.YRange.Log = ax.Uselog
		if ax.Ylimit {
			chart.YRange.Fixed(ax.Ymin, ax.Ymax, 0)
			chart.YRange.Init()
		}
		for _, line := range ax.Lines {
			if xc, _ := line.Count(); xc > 0 {
				chart.AddData(line.Name, line.X, line.Y, line.Style)
			}
		}
	}

	return fig.Render(writer, width, height)
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

var (
	logsCmd = &cobra.Command{
		Use:   "logs",
		Short: "View error logs",
		Run:   logsCommand,
	}

	logsLastCmd = &cobra.Command{
		Use:   "last",
		Short: "View latest error log",
		Run:   logsLastCommand,
	}

	logsShowCmd = &cobra.Command{
		Use:   "show",
		Short: "View a single error log",
		Run:   logsShowCommand,
	}

	logsClearCmd = &cobra.Command{
		Use:   "clear",
		Short: "Clear all logs",
		Run:   logsClearCommand,
	}

	logsBoomtownCmd = &cobra.Command{
		Use:   "boomtown",
		Short: "Trigger a sample error",
		Run:   logsBoomtownCommand,
	}
)

func logsCommand(cmd *cobra.Command, args []string) {
	for _, path := range sortedLogs() {
		Print(path)
	}
}

func logsLastCommand(cmd *cobra.Command, args []string) {
	logs := sortedLogs()
	if len(logs) < 1 {
		Print("No logs to show")
		return
	}

	logsShowCommand(cmd, logs[len(logs)-1:])
}

func logsShowCommand(cmd *cobra.Command, args []string) {
	if len(args) == 0 {
		Print("Supply a log name.")
		return
	}

	name := args[0]
	by, err := ioutil.ReadFile(filepath.Join(lfs.LocalLogDir, name))
	if err != nil {
		Exit("Error reading log: %s", name)
	}

	Debug("Reading log: %s", name)
	os.Stdout.Write(by)
}

func logsClearCommand(cmd *cobra.Command, args []string) {
	err := os.RemoveAll(lfs.LocalLogDir)
	if err != nil {
		Panic(err, "Error clearing %s", lfs.LocalLogDir)
	}

	Print("Cleared %s", lfs.LocalLogDir)
}

func logsBoomtownCommand(cmd *cobra.Command, args []string) {
	Debug("Debug message")
	err := lfs.Errorf(errors.New("Inner error message!"), "Error!")
	Panic(err, "Welcome to Boomtown")
	Debug("Never seen")
}

func sortedLogs() []string {
	fileinfos, err := ioutil.ReadDir(lfs.LocalLogDir)
	if err != nil {
		return []string{}
	}

	names := make([]string, len(fileinfos))
	for index, info := range fileinfos {
		names[index] = info.Name()
	}

	return names
}

func init() {
	logsCmd.AddCommand(logsLastCmd, logsShowCmd, logsClearCmd, logsBoomtownCmd)
	RootCmd.AddCommand(logsCmd)
}
<commit_msg>Logs commands should skip directories<commit_after>package commands

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

var (
	logsCmd = &cobra.Command{
		Use:   "logs",
		Short: "View error logs",
		Run:   logsCommand,
	}

	logsLastCmd = &cobra.Command{
		Use:   "last",
		Short: "View latest error log",
		Run:   logsLastCommand,
	}

	logsShowCmd = &cobra.Command{
		Use:   "show",
		Short: "View a single error log",
		Run:   logsShowCommand,
	}

	logsClearCmd = &cobra.Command{
		Use:   "clear",
		Short: "Clear all logs",
		Run:   logsClearCommand,
	}

	logsBoomtownCmd = &cobra.Command{
		Use:   "boomtown",
		Short: "Trigger a sample error",
		Run:   logsBoomtownCommand,
	}
)

func logsCommand(cmd *cobra.Command, args []string) {
	for _, path := range sortedLogs() {
		Print(path)
	}
}

func logsLastCommand(cmd *cobra.Command, args []string) {
	logs := sortedLogs()
	if len(logs) < 1 {
		Print("No logs to show")
		return
	}

	logsShowCommand(cmd, logs[len(logs)-1:])
}

func logsShowCommand(cmd *cobra.Command, args []string) {
	if len(args) == 0 {
		Print("Supply a log name.")
		return
	}

	name := args[0]
	by, err := ioutil.ReadFile(filepath.Join(lfs.LocalLogDir, name))
	if err != nil {
		Exit("Error reading log: %s", name)
	}

	Debug("Reading log: %s", name)
	os.Stdout.Write(by)
}

func logsClearCommand(cmd *cobra.Command, args []string) {
	err := os.RemoveAll(lfs.LocalLogDir)
	if err != nil {
		Panic(err, "Error clearing %s", lfs.LocalLogDir)
	}

	Print("Cleared %s", lfs.LocalLogDir)
}

func logsBoomtownCommand(cmd *cobra.Command, args []string) {
	Debug("Debug message")
	err := lfs.Errorf(errors.New("Inner error message!"), "Error!")
	Panic(err, "Welcome to Boomtown")
	Debug("Never seen")
}

func sortedLogs() []string {
	fileinfos, err := ioutil.ReadDir(lfs.LocalLogDir)
	if err != nil {
		return []string{}
	}

	names := make([]string, 0, len(fileinfos))
	for index, info := range fileinfos {
		if info.IsDir() {
			continue
		}
		names[index] = info.Name()
	}

	return names
}

func init() {
	logsCmd.AddCommand(logsLastCmd, logsShowCmd, logsClearCmd, logsBoomtownCmd)
	RootCmd.AddCommand(logsCmd)
}
<|endoftext|>


<commit_before>package gitmedia

import (
	".."
	"../client"
)

type SyncCommand struct {
	*Command
}

func (c *SyncCommand) Setup() {
}

func (c *SyncCommand) Run() {
	err := gitmedia.UploadQueue().Walk(func(id string, sha []byte) error {
		path := gitmedia.LocalMediaPath(string(sha))
		return gitmediaclient.Send(path)
	})

	if err != nil {
		gitmedia.Panic(err, "error uploading file")
	}
}

func init() {
	registerCommand("sync", func(c *Command) RunnableCommand {
		return &SyncCommand{Command: c}
	})
}
<commit_msg>"git media sync" now walks to the upload queue<commit_after>package gitmedia

import (
	".."
	"../client"
)

type SyncCommand struct {
	*Command
}

func (c *SyncCommand) Setup() {
}

func (c *SyncCommand) Run() {
	q := gitmedia.UploadQueue()
	q.Walk(func(id string, body []byte) error {
		sha := string(body)
		path := gitmedia.LocalMediaPath(sha)
		err := gitmediaclient.Send(path)
		if err != nil {
			gitmedia.Panic(err, "error uploading file %s", sha)
		}

		if err := q.Del(id); err != nil {
			gitmedia.Panic(err, "error removing %s from queue", sha)
		}
	})
}

func init() {
	registerCommand("sync", func(c *Command) RunnableCommand {
		return &SyncCommand{Command: c}
	})
}
<|endoftext|>


<commit_before>package main

import (
	. "github.com/bytbox/go-mail"
)

type Threaded struct {
	Message
	Parent   *Threaded
	Children []*Threaded

	parId    []string
}

func (tm *Threaded) addChild(c *Threaded) {
	tm.Children = append(tm.Children, c)
}

func Thread(msgs []Message) []*Threaded {
	tmap := map[string]*Threaded{}
	for _, msg := range msgs {
		mid := msg.MessageId
		tm := &Threaded{msg, nil, nil, nil}
		// we just use the first parent
		if len(msg.References) > 0 {
			tm.parId = msg.References
		}
		tmap[mid] = tm
	}

	for _, tm := range tmap {
		if tm.parId != nil {
			// The parent should be set using the last id we have
			// registered.
			for _, id := range tm.parId {
				p, ok := tmap[id]
				if ok {
					tm.Parent = p
				}
			}
			if tm.Parent != nil {
				tm.Parent.addChild(tm)
			}
		}
	}

	threaded := []*Threaded{}
	for _, tm := range tmap {
		if tm.Parent == nil {
			threaded = append(threaded, tm)
		}
	}
	return threaded
}
<commit_msg>Sorting (by message)<commit_after>package main

import (
	"sort"
	"time"

	. "github.com/bytbox/go-mail"
)

type Threaded struct {
	Message
	Parent   *Threaded
	Children []*Threaded

	parId    []string
}

func (tm *Threaded) addChild(c *Threaded) {
	tm.Children = append(tm.Children, c)
}

func (tm *Threaded) modified() time.Time {
	return tm.Date
}

type Sortable []*Threaded

func (s Sortable) Len() int {
	return len(s)
}

func (s Sortable) Less(i, j int) bool {
	return s[i].Date.Before(s[j].Date)
}

func (s Sortable) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func Thread(msgs []Message) []*Threaded {
	tmap := map[string]*Threaded{}
	for _, msg := range msgs {
		mid := msg.MessageId
		tm := &Threaded{msg, nil, nil, nil}
		// we just use the first parent
		if len(msg.References) > 0 {
			tm.parId = msg.References
		}
		tmap[mid] = tm
	}

	for _, tm := range tmap {
		if tm.parId != nil {
			// The parent should be set using the last id we have
			// registered.
			for _, id := range tm.parId {
				p, ok := tmap[id]
				if ok {
					tm.Parent = p
				}
			}
			if tm.Parent != nil {
				tm.Parent.addChild(tm)
			}
		}
	}

	threaded := []*Threaded{}
	for _, tm := range tmap {
		if tm.Parent == nil {
			threaded = append(threaded, tm)
		}
	}

	sort.Sort(Sortable(threaded))
	return threaded
}
<|endoftext|>


<commit_before>package sensu

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"strings"

	"github.com/upfluence/sensu-client-go/Godeps/_workspace/src/github.com/upfluence/sensu-go/sensu/check"
	"github.com/upfluence/sensu-client-go/Godeps/_workspace/src/github.com/upfluence/sensu-go/sensu/client"
)

const defaultRabbitMQURI string = "amqp://guest:guest@localhost:5672/%2f"

type configFlagSet struct {
	configFile string
	verbose    bool
}

type Config struct {
	flagSet *configFlagSet
	config  *configPayload
}

type configPayload struct {
	Client      *client.Client `json:"client,omitempty"`
	Checks      []*check.Check `json:"checks,omitempty"`
	RabbitMQURI *string        `json:"rabbitmq_uri,omitempty"`
}

func fetchEnv(envs ...string) string {
	for _, env := range envs {
		if v := os.Getenv(env); v != "" {
			return v
		}
	}

	return ""
}

// split is a wrapper for strings.Split, which returns an empty array
// for empty string inputs instead of an array containing an empty string
func split(str string, token string) []string {
	if len(str) == 0 {
		return []string{}
	}

	return strings.Split(str, token)
}

func NewConfigFromFlagSet(flagset *configFlagSet) (*Config, error) {
	var cfg = Config{flagset, &configPayload{}}

	if flagset != nil && flagset.configFile != "" {
		buf, err := ioutil.ReadFile(flagset.configFile)

		if err != nil {
			return nil, err
		}

		if err := json.Unmarshal(buf, &cfg.config); err != nil {
			return nil, err
		}
	}

	return &cfg, nil
}

func (c *Config) RabbitMQURI() string {
	if cfg := c.config; cfg != nil && cfg.RabbitMQURI != nil {
		return *cfg.RabbitMQURI
	} else if uri := fetchEnv("RABBITMQ_URI", "RABBITMQ_URL"); uri != "" {
		return uri
	}

	return defaultRabbitMQURI
}

func (c *Config) Client() *client.Client {
	if cfg := c.config; cfg != nil && cfg.Client != nil {
		return cfg.Client
	}

	return &client.Client{
		Name:          os.Getenv("SENSU_CLIENT_NAME"),
		Address:       fetchEnv("SENSU_CLIENT_ADDRESS", "SENSU_ADDRESS"),
		Subscriptions: split(os.Getenv("SENSU_CLIENT_SUBSCRIPTIONS"), ","),
	}
}

func (c *Config) Checks() []*check.Check {
	if cfg := c.config; cfg != nil {
		return cfg.Checks
	}

	return []*check.Check{}
}
<commit_msg>add workaround/option to select Sensu config file from within tool<commit_after>package sensu

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"strings"

	"github.com/upfluence/sensu-client-go/Godeps/_workspace/src/github.com/upfluence/sensu-go/sensu/check"
	"github.com/upfluence/sensu-client-go/Godeps/_workspace/src/github.com/upfluence/sensu-go/sensu/client"
)

const defaultRabbitMQURI string = "amqp://guest:guest@localhost:5672/%2f"

type configFlagSet struct {
	configFile string
	verbose    bool
}

type Config struct {
	flagSet *configFlagSet
	config  *configPayload
}

type configPayload struct {
	Client      *client.Client `json:"client,omitempty"`
	Checks      []*check.Check `json:"checks,omitempty"`
	RabbitMQURI *string        `json:"rabbitmq_uri,omitempty"`
}

func fetchEnv(envs ...string) string {
	for _, env := range envs {
		if v := os.Getenv(env); v != "" {
			return v
		}
	}

	return ""
}

// split is a wrapper for strings.Split, which returns an empty array
// for empty string inputs instead of an array containing an empty string
func split(str string, token string) []string {
	if len(str) == 0 {
		return []string{}
	}

	return strings.Split(str, token)
}

func NewConfigFromFile(flagset *configFlagSet, configFile string) (*Config, error) {
	var cfg = Config{flagset, &configPayload{}}
	buf, err := ioutil.ReadFile(configFile)

	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(buf, &cfg.config); err != nil {
		return nil, err
	}
	return &cfg, nil
}

func NewConfigFromFlagSet(flagset *configFlagSet) (*Config, error) {
	var cfg = Config{flagset, &configPayload{}}

	if flagset != nil && flagset.configFile != "" {
		buf, err := ioutil.ReadFile(flagset.configFile)

		if err != nil {
			return nil, err
		}

		if err := json.Unmarshal(buf, &cfg.config); err != nil {
			return nil, err
		}
		return &cfg, nil
	}
	return nil, errors.New("No config file is set")

}

func (c *Config) RabbitMQURI() string {
	if cfg := c.config; cfg != nil && cfg.RabbitMQURI != nil {
		return *cfg.RabbitMQURI
	} else if uri := fetchEnv("RABBITMQ_URI", "RABBITMQ_URL"); uri != "" {
		return uri
	}

	return defaultRabbitMQURI
}

func (c *Config) Client() *client.Client {
	if cfg := c.config; cfg != nil && cfg.Client != nil {
		return cfg.Client
	}

	return &client.Client{
		Name:          os.Getenv("SENSU_CLIENT_NAME"),
		Address:       fetchEnv("SENSU_CLIENT_ADDRESS", "SENSU_ADDRESS"),
		Subscriptions: split(os.Getenv("SENSU_CLIENT_SUBSCRIPTIONS"), ","),
	}
}

func (c *Config) Checks() []*check.Check {
	if cfg := c.config; cfg != nil {
		return cfg.Checks
	}

	return []*check.Check{}
}
<|endoftext|>


<commit_before>package sdl

/*
#include "sdl_wrapper.h"

#if !(SDL_VERSION_ATLEAST(2,0,9))

#if defined(WARN_OUTDATED)
#pragma message("SDL_LinuxSetThreadPriority is not supported before SDL 2.0.9")
#endif

static int SDL_LinuxSetThreadPriority(Sint64 threadID, int priority)
{
	return -1;
}

#endif

#if !(SDL_VERSION_ATLEAST(2,0,18))

#if defined(WARN_OUTDATED)
#pragma message("SDL_LinuxSetThreadPriorityAndPolicy is not supported before SDL 2.0.18")
#endif

static int SDL_LinuxSetThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy)
{
	return -1;
}

#endif
*/
import "C"

// LinuxSetThreadPriority sets the UNIX nice value for a thread.
//
// This uses setpriority() if possible, and RealtimeKit if available.
//
// (https://wiki.libsdl.org/SDL_LinuxSetThreadPriority)
func LinuxSetThreadPriority(threadID int64, priority int) (err error) {
	_threadID := C.Sint64(threadID)
	_priority := C.int(priority)
	return errorFromInt(int(C.SDL_LinuxSetThreadPriority(_threadID, _priority)))
}

// LinuxSetThreadPriority sets the priority (not nice level) and scheduling policy for a thread.
//
// This uses setpriority() if possible, and RealtimeKit if available.
//
// (https://wiki.libsdl.org/SDL_LinuxSetThreadPriorityAndPolicy)
func LinuxSetThreadPriorityAndPolicy(threadID int64, sdlPriority, schedPolicy int) (err error) {
	_threadID := C.Sint64(threadID)
	_sdlPriority := C.int(sdlPriority)
	_schedPolicy := C.int(schedPolicy)
	return errorFromInt(int(C.SDL_LinuxSetThreadPriorityAndPolicy(_threadID, _sdlPriority, _schedPolicy)))
}
<commit_msg>Prevent system_linux.go from compiling for android (#512)<commit_after>// +build !android

package sdl

/*
#include "sdl_wrapper.h"

#if !(SDL_VERSION_ATLEAST(2,0,9))

#if defined(WARN_OUTDATED)
#pragma message("SDL_LinuxSetThreadPriority is not supported before SDL 2.0.9")
#endif

static int SDL_LinuxSetThreadPriority(Sint64 threadID, int priority)
{
	return -1;
}

#endif

#if !(SDL_VERSION_ATLEAST(2,0,18))

#if defined(WARN_OUTDATED)
#pragma message("SDL_LinuxSetThreadPriorityAndPolicy is not supported before SDL 2.0.18")
#endif

static int SDL_LinuxSetThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy)
{
	return -1;
}

#endif
*/
import "C"

// LinuxSetThreadPriority sets the UNIX nice value for a thread.
//
// This uses setpriority() if possible, and RealtimeKit if available.
//
// (https://wiki.libsdl.org/SDL_LinuxSetThreadPriority)
func LinuxSetThreadPriority(threadID int64, priority int) (err error) {
	_threadID := C.Sint64(threadID)
	_priority := C.int(priority)
	return errorFromInt(int(C.SDL_LinuxSetThreadPriority(_threadID, _priority)))
}

// LinuxSetThreadPriority sets the priority (not nice level) and scheduling policy for a thread.
//
// This uses setpriority() if possible, and RealtimeKit if available.
//
// (https://wiki.libsdl.org/SDL_LinuxSetThreadPriorityAndPolicy)
func LinuxSetThreadPriorityAndPolicy(threadID int64, sdlPriority, schedPolicy int) (err error) {
	_threadID := C.Sint64(threadID)
	_sdlPriority := C.int(sdlPriority)
	_schedPolicy := C.int(schedPolicy)
	return errorFromInt(int(C.SDL_LinuxSetThreadPriorityAndPolicy(_threadID, _sdlPriority, _schedPolicy)))
}
<|endoftext|>


<commit_before>// +build !windows,cgo

package serial

// #include <termios.h>
// #include <unistd.h>
import "C"

// TODO: Maybe change to using syscall package + ioctl instead of cgo

import (
	"errors"
	"fmt"
	"io"
	"os"
	"syscall"
	//"unsafe"
)

func openPort(name string, baud int) (rwc io.ReadWriteCloser, err error) {
	f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return
	}

	fd := C.int(f.Fd())
	if C.isatty(fd) != 1 {
		f.Close()
		return nil, errors.New("File is not a tty")
	}

	var st C.struct_termios
	_, err = C.tcgetattr(fd, &st)
	if err != nil {
		f.Close()
		return nil, err
	}
	var speed C.speed_t
	switch baud {
	case 115200:
		speed = C.B115200
	case 57600:
		speed = C.B57600
	case 38400:
		speed = C.B38400
	case 19200:
		speed = C.B19200
	case 9600:
		speed = C.B9600
	case 4800:
		speed = C.B4800
	case 2400:
		speed = C.B2400
	default:
		f.Close()
		return nil, fmt.Errorf("Unknown baud rate %v", baud)
	}

	_, err = C.cfsetispeed(&st, speed)
	if err != nil {
		f.Close()
		return nil, err
	}
	_, err = C.cfsetospeed(&st, speed)
	if err != nil {
		f.Close()
		return nil, err
	}

	// Select local mode
	st.c_cflag |= (C.CLOCAL | C.CREAD)

	// Select raw mode
	st.c_lflag &= ^C.tcflag_t(C.ICANON | C.ECHO | C.ECHOE | C.ISIG)
	st.c_oflag &= ^C.tcflag_t(C.OPOST)

	_, err = C.tcsetattr(fd, C.TCSANOW, &st)
	if err != nil {
		f.Close()
		return nil, err
	}

	//fmt.Println("Tweaking", name)
	r1, _, e := syscall.Syscall(syscall.SYS_FCNTL,
		uintptr(f.Fd()),
		uintptr(syscall.F_SETFL),
		uintptr(0))
	if e != 0 || r1 != 0 {
		s := fmt.Sprint("Clearing NONBLOCK syscall error:", e, r1)
		f.Close()
		return nil, errors.New(s)
	}

	/*
				r1, _, e = syscall.Syscall(syscall.SYS_IOCTL,
			                uintptr(f.Fd()),
			                uintptr(0x80045402), // IOSSIOSPEED
			                uintptr(unsafe.Pointer(&baud)));
			        if e != 0 || r1 != 0 {
			                s := fmt.Sprint("Baudrate syscall error:", e, r1)
					f.Close()
		                        return nil, os.NewError(s)
				}
	*/

	return f, nil
}
<commit_msg>Clear more serial bits like XON<commit_after>// +build !windows,cgo

package serial

// #include <termios.h>
// #include <unistd.h>
import "C"

// TODO: Maybe change to using syscall package + ioctl instead of cgo

import (
	"errors"
	"fmt"
	"io"
	"os"
	"syscall"
	//"unsafe"
)

func openPort(name string, baud int) (rwc io.ReadWriteCloser, err error) {
	f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return
	}

	fd := C.int(f.Fd())
	if C.isatty(fd) != 1 {
		f.Close()
		return nil, errors.New("File is not a tty")
	}

	var st C.struct_termios
	_, err = C.tcgetattr(fd, &st)
	if err != nil {
		f.Close()
		return nil, err
	}
	var speed C.speed_t
	switch baud {
	case 115200:
		speed = C.B115200
	case 57600:
		speed = C.B57600
	case 38400:
		speed = C.B38400
	case 19200:
		speed = C.B19200
	case 9600:
		speed = C.B9600
	case 4800:
		speed = C.B4800
	case 2400:
		speed = C.B2400
	default:
		f.Close()
		return nil, fmt.Errorf("Unknown baud rate %v", baud)
	}

	_, err = C.cfsetispeed(&st, speed)
	if err != nil {
		f.Close()
		return nil, err
	}
	_, err = C.cfsetospeed(&st, speed)
	if err != nil {
		f.Close()
		return nil, err
	}

	// Turn off break interrupts, CR->NL, Parity checks, strip, and IXON
	st.c_iflag &= ^C.tcflag_t(C.BRKINT | C.ICRNL | C.INPCK | C.ISTRIP | C.IXOFF | C.IXON | C.PARMRK)

	// Select local mode, turn off parity, set to 8 bits
	st.c_cflag &= ^C.tcflag_t(C.CSIZE | C.PARENB)
	st.c_cflag |= (C.CLOCAL | C.CREAD | C.CS8)

	// Select raw mode
	st.c_lflag &= ^C.tcflag_t(C.ICANON | C.ECHO | C.ECHOE | C.ISIG)
	st.c_oflag &= ^C.tcflag_t(C.OPOST)

	_, err = C.tcsetattr(fd, C.TCSANOW, &st)
	if err != nil {
		f.Close()
		return nil, err
	}

	//fmt.Println("Tweaking", name)
	r1, _, e := syscall.Syscall(syscall.SYS_FCNTL,
		uintptr(f.Fd()),
		uintptr(syscall.F_SETFL),
		uintptr(0))
	if e != 0 || r1 != 0 {
		s := fmt.Sprint("Clearing NONBLOCK syscall error:", e, r1)
		f.Close()
		return nil, errors.New(s)
	}

	/*
				r1, _, e = syscall.Syscall(syscall.SYS_IOCTL,
			                uintptr(f.Fd()),
			                uintptr(0x80045402), // IOSSIOSPEED
			                uintptr(unsafe.Pointer(&baud)));
			        if e != 0 || r1 != 0 {
			                s := fmt.Sprint("Baudrate syscall error:", e, r1)
					f.Close()
		                        return nil, os.NewError(s)
				}
	*/

	return f, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gambol99/go-oidc/jose"
	"github.com/gambol99/go-oidc/oidc"
	"go.uber.org/zap"
)

// proxyMiddleware is responsible for handles reverse proxy request to the upstream endpoint
func (r *oauthProxy) proxyMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		next.ServeHTTP(w, req)

		// step: retrieve the request scope
		scope := req.Context().Value(contextScopeName)
		if scope != nil {
			sc := scope.(*RequestScope)
			if sc.AccessDenied {
				return
			}
		}

		if isUpgradedConnection(req) {
			r.log.Debug("upgrading the connnection", zap.String("client_ip", req.RemoteAddr))
			if err := tryUpdateConnection(req, w, r.endpoint); err != nil {
				r.log.Error("failed to upgrade connection", zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			return
		}

		// add any custom headers to the request
		for k, v := range r.config.Headers {
			req.Header.Set(k, v)
		}

		// By default goproxy only provides a forwarding proxy, thus all requests have to be absolute
		// and we must update the host headers
		req.URL.Host = r.endpoint.Host
		req.URL.Scheme = r.endpoint.Scheme
		req.Host = r.endpoint.Host

		req.Header.Add("X-Forwarded-For", realIP(req))
		req.Header.Set("X-Forwarded-Host", req.URL.Host)
		req.Header.Set("X-Forwarded-Proto", req.Header.Get("X-Forwarded-Proto"))

		r.upstream.ServeHTTP(w, req)
	})
}

// forwardProxyHandler is responsible for signing outbound requests
func (r *oauthProxy) forwardProxyHandler() func(*http.Request, *http.Response) {
	client, err := r.client.OAuthClient()
	if err != nil {
		r.log.Fatal("failed to create oauth client", zap.Error(err))
	}
	// the loop state
	var state struct {
		// the access token
		token jose.JWT
		// the refresh token if any
		refresh string
		// the identity of the user
		identity *oidc.Identity
		// the expiry time of the access token
		expiration time.Time
		// whether we need to login
		login bool
		// whether we should wait for expiration
		wait bool
	}
	state.login = true

	// create a routine to refresh the access tokens or login on expiration
	go func() {
		for {
			state.wait = false

			// step: do we have a access token
			if state.login {
				r.log.Info("requesting access token for user",
					zap.String("username", r.config.ForwardingUsername))

				// step: login into the service
				resp, err := client.UserCredsToken(r.config.ForwardingUsername, r.config.ForwardingPassword)
				if err != nil {
					r.log.Error("failed to login to authentication service", zap.Error(err))
					// step: back-off and reschedule
					<-time.After(time.Duration(5) * time.Second)
					continue
				}

				// step: parse the token
				token, identity, err := parseToken(resp.AccessToken)
				if err != nil {
					r.log.Error("failed to parse the access token", zap.Error(err))
					// step: we should probably hope and reschedule here
					<-time.After(time.Duration(5) * time.Second)
					continue
				}

				// step: update the loop state
				state.token = token
				state.identity = identity
				state.expiration = identity.ExpiresAt
				state.wait = true
				state.login = false
				state.refresh = resp.RefreshToken

				r.log.Info("successfully retrieved access token for subject",
					zap.String("subject", state.identity.ID),
					zap.String("email", state.identity.Email),
					zap.String("expires", state.expiration.Format(time.RFC3339)))

			} else {
				r.log.Info("access token is about to expiry",
					zap.String("subject", state.identity.ID),
					zap.String("email", state.identity.Email))

				// step: if we a have a refresh token, we need to login again
				if state.refresh != "" {
					r.log.Info("attempting to refresh the access token",
						zap.String("subject", state.identity.ID),
						zap.String("email", state.identity.Email),
						zap.String("expires", state.expiration.Format(time.RFC3339)))

					// step: attempt to refresh the access
					token, expiration, err := getRefreshedToken(r.client, state.refresh)
					if err != nil {
						state.login = true
						switch err {
						case ErrRefreshTokenExpired:
							r.log.Warn("the refresh token has expired, need to login again",
								zap.String("subject", state.identity.ID),
								zap.String("email", state.identity.Email))
						default:
							r.log.Error("failed to refresh the access token", zap.Error(err))
						}
						continue
					}

					// step: update the state
					state.token = token
					state.expiration = expiration
					state.wait = true
					state.login = false

					// step: add some debugging
					r.log.Info("successfully refreshed the access token",
						zap.String("subject", state.identity.ID),
						zap.String("email", state.identity.Email),
						zap.String("expires", state.expiration.Format(time.RFC3339)))

				} else {
					r.log.Info("session does not support refresh token, acquiring new token",
						zap.String("subject", state.identity.ID),
						zap.String("email", state.identity.Email))

					// we don't have a refresh token, we must perform a login again
					state.wait = false
					state.login = true
				}
			}

			// wait for an expiration to come close
			if state.wait {
				// set the expiration of the access token within a random 85% of actual expiration
				duration := getWithin(state.expiration, 0.85)
				r.log.Info("waiting for expiration of access token",
					zap.String("token_expiration", state.expiration.Format(time.RFC3339)),
					zap.String("renewel_duration", duration.String()))

				<-time.After(duration)
			}
		}
	}()

	return func(req *http.Request, resp *http.Response) {
		hostname := req.Host
		req.URL.Host = hostname
		// is the host being signed?
		if len(r.config.ForwardingDomains) == 0 || containsSubString(hostname, r.config.ForwardingDomains) {
			req.Header.Set("X-Forwarded-Agent", prog)
			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", state.token.Encode()))
		}
	}
}
<commit_msg>Allow setting upstream Host header (#290)<commit_after>/*
Copyright 2015 All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gambol99/go-oidc/jose"
	"github.com/gambol99/go-oidc/oidc"
	"go.uber.org/zap"
)

// proxyMiddleware is responsible for handles reverse proxy request to the upstream endpoint
func (r *oauthProxy) proxyMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		next.ServeHTTP(w, req)

		// step: retrieve the request scope
		scope := req.Context().Value(contextScopeName)
		if scope != nil {
			sc := scope.(*RequestScope)
			if sc.AccessDenied {
				return
			}
		}

		if isUpgradedConnection(req) {
			r.log.Debug("upgrading the connnection", zap.String("client_ip", req.RemoteAddr))
			if err := tryUpdateConnection(req, w, r.endpoint); err != nil {
				r.log.Error("failed to upgrade connection", zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			return
		}

		// add any custom headers to the request
		for k, v := range r.config.Headers {
			req.Header.Set(k, v)
		}

		// By default goproxy only provides a forwarding proxy, thus all requests have to be absolute
		// and we must update the host headers
		req.URL.Host = r.endpoint.Host
		req.URL.Scheme = r.endpoint.Scheme
		if v := req.Header.Get("Host"); v != "" {
			req.Host = v
			req.Header.Del("Host")
		} else {
			req.Host = r.endpoint.Host
		}

		req.Header.Add("X-Forwarded-For", realIP(req))
		req.Header.Set("X-Forwarded-Host", req.URL.Host)
		req.Header.Set("X-Forwarded-Proto", req.Header.Get("X-Forwarded-Proto"))

		r.upstream.ServeHTTP(w, req)
	})
}

// forwardProxyHandler is responsible for signing outbound requests
func (r *oauthProxy) forwardProxyHandler() func(*http.Request, *http.Response) {
	client, err := r.client.OAuthClient()
	if err != nil {
		r.log.Fatal("failed to create oauth client", zap.Error(err))
	}
	// the loop state
	var state struct {
		// the access token
		token jose.JWT
		// the refresh token if any
		refresh string
		// the identity of the user
		identity *oidc.Identity
		// the expiry time of the access token
		expiration time.Time
		// whether we need to login
		login bool
		// whether we should wait for expiration
		wait bool
	}
	state.login = true

	// create a routine to refresh the access tokens or login on expiration
	go func() {
		for {
			state.wait = false

			// step: do we have a access token
			if state.login {
				r.log.Info("requesting access token for user",
					zap.String("username", r.config.ForwardingUsername))

				// step: login into the service
				resp, err := client.UserCredsToken(r.config.ForwardingUsername, r.config.ForwardingPassword)
				if err != nil {
					r.log.Error("failed to login to authentication service", zap.Error(err))
					// step: back-off and reschedule
					<-time.After(time.Duration(5) * time.Second)
					continue
				}

				// step: parse the token
				token, identity, err := parseToken(resp.AccessToken)
				if err != nil {
					r.log.Error("failed to parse the access token", zap.Error(err))
					// step: we should probably hope and reschedule here
					<-time.After(time.Duration(5) * time.Second)
					continue
				}

				// step: update the loop state
				state.token = token
				state.identity = identity
				state.expiration = identity.ExpiresAt
				state.wait = true
				state.login = false
				state.refresh = resp.RefreshToken

				r.log.Info("successfully retrieved access token for subject",
					zap.String("subject", state.identity.ID),
					zap.String("email", state.identity.Email),
					zap.String("expires", state.expiration.Format(time.RFC3339)))

			} else {
				r.log.Info("access token is about to expiry",
					zap.String("subject", state.identity.ID),
					zap.String("email", state.identity.Email))

				// step: if we a have a refresh token, we need to login again
				if state.refresh != "" {
					r.log.Info("attempting to refresh the access token",
						zap.String("subject", state.identity.ID),
						zap.String("email", state.identity.Email),
						zap.String("expires", state.expiration.Format(time.RFC3339)))

					// step: attempt to refresh the access
					token, expiration, err := getRefreshedToken(r.client, state.refresh)
					if err != nil {
						state.login = true
						switch err {
						case ErrRefreshTokenExpired:
							r.log.Warn("the refresh token has expired, need to login again",
								zap.String("subject", state.identity.ID),
								zap.String("email", state.identity.Email))
						default:
							r.log.Error("failed to refresh the access token", zap.Error(err))
						}
						continue
					}

					// step: update the state
					state.token = token
					state.expiration = expiration
					state.wait = true
					state.login = false

					// step: add some debugging
					r.log.Info("successfully refreshed the access token",
						zap.String("subject", state.identity.ID),
						zap.String("email", state.identity.Email),
						zap.String("expires", state.expiration.Format(time.RFC3339)))

				} else {
					r.log.Info("session does not support refresh token, acquiring new token",
						zap.String("subject", state.identity.ID),
						zap.String("email", state.identity.Email))

					// we don't have a refresh token, we must perform a login again
					state.wait = false
					state.login = true
				}
			}

			// wait for an expiration to come close
			if state.wait {
				// set the expiration of the access token within a random 85% of actual expiration
				duration := getWithin(state.expiration, 0.85)
				r.log.Info("waiting for expiration of access token",
					zap.String("token_expiration", state.expiration.Format(time.RFC3339)),
					zap.String("renewel_duration", duration.String()))

				<-time.After(duration)
			}
		}
	}()

	return func(req *http.Request, resp *http.Response) {
		hostname := req.Host
		req.URL.Host = hostname
		// is the host being signed?
		if len(r.config.ForwardingDomains) == 0 || containsSubString(hostname, r.config.ForwardingDomains) {
			req.Header.Set("X-Forwarded-Agent", prog)
			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", state.token.Encode()))
		}
	}
}
<|endoftext|>


<commit_before>package foundation

import (
	"errors"
	"os"

	"github.com/metacoin/flojson"
)

func RPCCall(command string, args ...interface{}) (interface{}, error) {
	// create a new command based on the command string given
	// handle errors, and return results to main function
	id := 1
	var cmd flojson.Cmd
	var err error
	var ret interface{}

	switch command {

	case "getblock":
		cmd, err = flojson.NewGetBlockCmd(id, args[0].(string))
	case "getblockhash":
		cmd, err = flojson.NewGetBlockHashCmd(id, args[0].(int))
	case "getblockcount":
		cmd, err = flojson.NewGetBlockCountCmd(id)
	case "getrawtransaction":
		cmd, err = flojson.NewGetRawTransactionCmd(id, args[0].(string))
	case "decoderawtransaction":
		cmd, err = flojson.NewDecodeRawTransactionCmd(id, args[0].(string))
	case "getnewaddress":
		cmd, err = flojson.NewGetNewAddressCmd(id)
	case "getmininginfo":
		cmd, err = flojson.NewGetMiningInfoCmd(id)
	case "getconnectioncount":
		cmd, err = flojson.NewGetConnectionCountCmd(id)
	case "getnetworkhashps":
		cmd, err = flojson.NewGetNetworkHashPSCmd(id)
	case "settxfee":
		cmd, err = flojson.NewSetTxFeeCmd(id, args[0].(int64))
	case "sendtoaddress":
		cmd, err = flojson.NewSendToAddressCmd(id, args[0].(string), args[1].(int64), args[2], args[3], args[4])
	case "walletpassphrase":
		cmd, err = flojson.NewWalletPassphraseCmd(id, args[0].(string), args[1].(int))
	case "signmessage":
		cmd, err = flojson.NewSignMessageCmd(id, args[0].(string), args[1].(string))
	case "verifymessage":
		cmd, err = flojson.NewVerifyMessageCmd(id, args[0].(string), args[1].(string), args[2].(string))
	case "validateaddress":
		cmd, err = flojson.NewValidateAddressCmd(id, args[0].(string))
	case "listtransactions":
		cmd, err = flojson.NewListTransactionsCmd(id)
	case "getbalance":
		cmd, err = flojson.NewGetBalanceCmd(id)
	default:
		err = errors.New("command " + command + " not found")
	}

	if err != nil {
		return ret, err
	}

	if cmd != nil {
		reply, err := SendCommand(cmd)
		if err != nil {
			return ret, err
		} else {
			return reply, nil
		}
	}
	return ret, errors.New("flojson replied with empty NewCmd struct")
}

func SendCommand(cmd flojson.Cmd) (interface{}, error) {

	var ret interface{}
	ftoken := os.Getenv("F_TOKEN")
	if ftoken == "" {
		return ret, errors.New("F_TOKEN environment variable not set")
	}

	fuser := os.Getenv("F_USER")
	if ftoken == "" {
		return ret, errors.New("F_USER environment variable not set")
	}

	furi := os.Getenv("F_URI")
	if ftoken == "" {
		return ret, errors.New("F_URI environment variable not set")
	}

	// send command to RPC, get a response in reply
	reply, err := flojson.RpcSend(fuser, ftoken, furi, cmd)
	if err != nil {
		return ret, err
	}

	if reply.Result != nil {
		return reply.Result, nil
	} else {
		// if for some reason the result is nil, but err isn't nil, return a generic warning message
		return ret, errors.New("didn't get a response from the RPC - check auth and permission settings")
	}
}
<commit_msg>fixed a bug with F_URI env variable<commit_after>package foundation

import (
	"errors"
	"os"

	"github.com/metacoin/flojson"
)

func RPCCall(command string, args ...interface{}) (interface{}, error) {
	// create a new command based on the command string given
	// handle errors, and return results to main function
	id := 1
	var cmd flojson.Cmd
	var err error
	var ret interface{}

	switch command {

	case "getblock":
		cmd, err = flojson.NewGetBlockCmd(id, args[0].(string))
	case "getblockhash":
		cmd, err = flojson.NewGetBlockHashCmd(id, args[0].(int))
	case "getblockcount":
		cmd, err = flojson.NewGetBlockCountCmd(id)
	case "getrawtransaction":
		cmd, err = flojson.NewGetRawTransactionCmd(id, args[0].(string))
	case "decoderawtransaction":
		cmd, err = flojson.NewDecodeRawTransactionCmd(id, args[0].(string))
	case "getnewaddress":
		cmd, err = flojson.NewGetNewAddressCmd(id)
	case "getmininginfo":
		cmd, err = flojson.NewGetMiningInfoCmd(id)
	case "getconnectioncount":
		cmd, err = flojson.NewGetConnectionCountCmd(id)
	case "getnetworkhashps":
		cmd, err = flojson.NewGetNetworkHashPSCmd(id)
	case "settxfee":
		cmd, err = flojson.NewSetTxFeeCmd(id, args[0].(int64))
	case "sendtoaddress":
		cmd, err = flojson.NewSendToAddressCmd(id, args[0].(string), args[1].(int64), args[2], args[3], args[4])
	case "walletpassphrase":
		cmd, err = flojson.NewWalletPassphraseCmd(id, args[0].(string), args[1].(int))
	case "signmessage":
		cmd, err = flojson.NewSignMessageCmd(id, args[0].(string), args[1].(string))
	case "verifymessage":
		cmd, err = flojson.NewVerifyMessageCmd(id, args[0].(string), args[1].(string), args[2].(string))
	case "validateaddress":
		cmd, err = flojson.NewValidateAddressCmd(id, args[0].(string))
	case "listtransactions":
		cmd, err = flojson.NewListTransactionsCmd(id)
	case "getbalance":
		cmd, err = flojson.NewGetBalanceCmd(id)
	default:
		err = errors.New("command " + command + " not found")
	}

	if err != nil {
		return ret, err
	}

	if cmd != nil {
		reply, err := SendCommand(cmd)
		if err != nil {
			return ret, err
		} else {
			return reply, nil
		}
	}
	return ret, errors.New("flojson replied with empty NewCmd struct")
}

func SendCommand(cmd flojson.Cmd) (interface{}, error) {

	var ret interface{}
	ftoken := os.Getenv("F_TOKEN")
	if ftoken == "" {
		return ret, errors.New("F_TOKEN environment variable not set")
	}

	fuser := os.Getenv("F_USER")
	if ftoken == "" {
		return ret, errors.New("F_USER environment variable not set")
	}

	furi := os.Getenv("F_URI")
	if furi == "" || len(furi) < 1 {
		//return ret, errors.New("F_URI environment variable not set")
		furi = "127.0.0.1:18322"
	}

	// send command to RPC, get a response in reply
	reply, err := flojson.RpcSend(fuser, ftoken, furi, cmd)
	if err != nil {
		return ret, err
	}

	if reply.Result != nil {
		return reply.Result, nil
	} else {
		// if for some reason the result is nil, but err isn't nil, return a generic warning message
		return ret, errors.New("didn't get a response from the RPC - check auth and permission settings")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
)

const videoPath string = "/home/kestein/Videos"
const referer string = "Referer"
const pageStart string = `

`
const content string = `
			{{block "links" .}}
				{{range .}}

{{.Filename}}

				{{end}}
			{{end}}
`
const pageEnd string = `

`

type videoLine struct {
	Action, Url, Filename string
}

type videoLineList []videoLine

func (a videoLineList) Len() int           { return len(a) }
func (a videoLineList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a videoLineList) Less(i, j int) bool { return a[i].Url < a[j].Url }

// TODO: Sanitize URLs so only subdirectories of videoPath can be queried
func list(w http.ResponseWriter, req *http.Request) {
	localURL := req.RequestURI[6:] // Take out the '/list'
	// ls the directory
	endDir, err := url.QueryUnescape(localURL)
	if err != nil {
		log.Fatal("Unable to parse video path %s", err)
	}
	dir, err := os.Open(fmt.Sprintf("%s/%s", videoPath, endDir))
	if err != nil {
		log.Fatal("Unable to open video path %s", err)
	}
	files, err := dir.Readdir(0)
	if err != nil {
		log.Fatal("Unable to read dir %s", err)
	}
	// Write the HTML. Sort out by directories and files
	subDirs := []videoLine{}
	orphanFiles := []videoLine{}
	for i := 0; i < len(files); i++ {
		// For printing
		fileName := files[i].Name()
		// For RESTing
		vidURL := ""
		if len(req.URL.String()) > 0 {
			vidURL = fmt.Sprintf("%s/%s", req.URL.String(), url.QueryEscape(fileName))
		} else {
			vidURL = fmt.Sprintf("%s", url.QueryEscape(fileName))
		}
		if files[i].IsDir() {
			url := videoLine{"list", vidURL, fileName}
			subDirs = append(subDirs, url)
		} else {
			url := videoLine{"play", vidURL, fileName}
			orphanFiles = append(orphanFiles, url)
		}
	}
	// Write HTML to the reponse
	sort.Sort(videoLineList(subDirs))
	// Make the '..' directory at the top
	prev := strings.Split(req.URL.String(), "/")
	back := ""
	if len(prev) > 1 {
		back = strings.Join(prev[:len(prev)-1], "/")
	}
	previousLink := []videoLine{videoLine{"list", back, ".."}}
	subDirs = append(previousLink, subDirs...)
	sort.Sort(videoLineList(orphanFiles))
	// Write the HTML
	io.WriteString(w, pageStart)
	t := template.Must(template.New("content").Parse(content))
	err = t.Execute(w, subDirs)
	if err != nil {
		panic(err)
	}
	err = t.Execute(w, orphanFiles)
	if err != nil {
		panic(err)
	}
	io.WriteString(w, pageEnd)
}

func play(w http.ResponseWriter, req *http.Request) {
	// Take everything after the '/' and play it in VLC
	/*rawVideo := string(req.URL.Path[1:])
	video, err := url.QueryUnescape(rawVideo)
	if err != nil {
		fmt.Println("WRONG")
	}
	cartoon := exec.Command("vlc", "-f", fmt.Sprintf("%s/%s", videoPath, video))
	err = cartoon.Start()
	if err != nil {
		log.Fatal("WRONG")
	}*/
	fmt.Println("asdf")
}

func main() {
	port := flag.String("p", "8100", "port to serve on")
	directory := flag.String("d", ".", "the directory of static file to host")
	flag.Parse()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "list", 301)
	})
	http.Handle("/list/", http.StripPrefix("/list/", http.HandlerFunc(list)))
	http.Handle("/play/", http.StripPrefix("/play/", http.HandlerFunc(play)))
	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
	log.Fatal(http.ListenAndServe("localhost:"+*port, nil))
}
<commit_msg>Minimal video working<commit_after>package main

import (
	"flag"
	"fmt"
	vlc "github.com/jteeuwen/go-vlc"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
)

const videoPath string = "/home/kestein/Videos"
const referer string = "Referer"
const pageStart string = `

`
const content string = `
			{{block "links" .}}
				{{range .}}

{{.Filename}}

				{{end}}
			{{end}}
`
const pageEnd string = `

`

type videoLine struct {
	Action, Url, Filename string
}

type cartoon struct {
	inst   *vlc.Instance
	player *vlc.Player
}

type videoLineList []videoLine

func (a videoLineList) Len() int           { return len(a) }
func (a videoLineList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a videoLineList) Less(i, j int) bool { return a[i].Url < a[j].Url }

// TODO: Sanitize URLs so only subdirectories of videoPath can be queried
func list(w http.ResponseWriter, req *http.Request) {
	localURL := req.RequestURI[6:] // Take out the '/list'
	// ls the directory
	endDir, err := url.QueryUnescape(localURL)
	if err != nil {
		log.Fatal("Unable to parse video path %s", err)
	}
	dir, err := os.Open(fmt.Sprintf("%s/%s", videoPath, endDir))
	if err != nil {
		log.Fatal("Unable to open video path %s", err)
	}
	files, err := dir.Readdir(0)
	if err != nil {
		log.Fatal("Unable to read dir %s", err)
	}
	// Write the HTML. Sort out by directories and files
	subDirs := []videoLine{}
	orphanFiles := []videoLine{}
	for i := 0; i < len(files); i++ {
		// For printing
		fileName := files[i].Name()
		// For RESTing
		vidURL := ""
		if len(req.URL.String()) > 0 {
			vidURL = fmt.Sprintf("%s/%s", req.URL.String(), url.QueryEscape(fileName))
		} else {
			vidURL = fmt.Sprintf("%s", url.QueryEscape(fileName))
		}
		if files[i].IsDir() {
			url := videoLine{"list", vidURL, fileName}
			subDirs = append(subDirs, url)
		} else {
			url := videoLine{"play", vidURL, fileName}
			orphanFiles = append(orphanFiles, url)
		}
	}
	// Write HTML to the reponse
	sort.Sort(videoLineList(subDirs))
	// Make the '..' directory at the top
	prev := strings.Split(req.URL.String(), "/")
	back := ""
	if len(prev) > 1 {
		back = strings.Join(prev[:len(prev)-1], "/")
	}
	previousLink := []videoLine{videoLine{"list", back, ".."}}
	subDirs = append(previousLink, subDirs...)
	sort.Sort(videoLineList(orphanFiles))
	// Write the HTML
	io.WriteString(w, pageStart)
	t := template.Must(template.New("content").Parse(content))
	err = t.Execute(w, subDirs)
	if err != nil {
		panic(err)
	}
	err = t.Execute(w, orphanFiles)
	if err != nil {
		panic(err)
	}
	io.WriteString(w, pageEnd)
}

func play(state *cartoon, w http.ResponseWriter, req *http.Request) {
	var media *vlc.Media
	// var evt *vlc.EventManager
	var err error

	// Obtain the name of the video to view
	rawVideo := string(req.URL.Path[0:])
	video, err := url.QueryUnescape(rawVideo)
	if err != nil {
		fmt.Println("WRONG")
	}
	// Make VLC instance
	if state.inst, err = vlc.New([]string{}); err != nil {
		fmt.Println(err)
	}
	// Open the video file
	if media, err = state.inst.OpenMediaFile(fmt.Sprintf("%s/%s", videoPath, video)); err != nil {
		fmt.Println(err)
	}
	// Create the media player
	if state.player, err = media.NewPlayer(); err != nil {
		fmt.Println(err)
	}
	// Do not need media anymore since player now owns it
	media.Release()
	media = nil

	// Make the page to control the video
	// Websockets?
	state.player.Play()
}

func closePlayer(state *cartoon) {
	state.player.Release()
	state.inst.Release()
	state.player = nil
	state.inst = nil
}

func main() {
	port := flag.String("p", "8100", "port to serve on")
	directory := flag.String("d", ".", "the directory of static file to host")
	flag.Parse()

	state := cartoon{inst: nil, player: nil}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "list", 301)
	})
	http.Handle("/list/", http.StripPrefix("/list/", http.HandlerFunc(list)))
	http.Handle("/play/", http.StripPrefix("/play/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			play(&state, w, req)
		})))
	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
	log.Fatal(http.ListenAndServe("localhost:"+*port, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/rs/cors"
)

func main() {
	store := make(map[uint64][]byte)
	var n uint64
	var lck sync.Mutex
	http.Handle("/add", cors.New(cors.Options{
		AllowedMethods:   []string{"PUT"},
		AllowCredentials: true,
	}).Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPut {
			http.Error(w, "Not a put", http.StatusBadRequest)
			log.Printf("Incorrect method %s\n", r.Method)
			return
		}
		buf := bytes.NewBuffer(nil)
		gz := gzip.NewWriter(buf)
		_, err := io.Copy(gz, r.Body)
		if err != nil {
			return
		}
		err = gz.Flush()
		if err != nil {
			return
		}
		lck.Lock()
		defer lck.Unlock()
		store[n] = buf.Bytes()
		go func() {
			time.Sleep(time.Minute)
			lck.Lock()
			defer lck.Unlock()
			delete(store, n)
		}()
		w.Write([]byte(fmt.Sprint(n)))
		n++
	})))
	http.Handle("/get", cors.New(cors.Options{
		AllowedMethods:   []string{"GET"},
		AllowCredentials: true,
	}).Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "Form parse error", http.StatusBadRequest)
			return
		}
		ids := r.FormValue("id")
		if ids == "" {
			http.Error(w, "No id in request", http.StatusBadRequest)
			return
		}
		id, err := strconv.ParseUint(ids, 10, 64)
		if err != nil {
			http.Error(w, "Id is not an int", http.StatusBadRequest)
			return
		}
		dat := func() []byte {
			lck.Lock()
			defer lck.Unlock()
			return store[id]
		}()
		if dat == nil {
			http.Error(w, "Data gone", http.StatusGone)
			return
		}
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			buf := bytes.NewBuffer(dat)
			gzr, err := gzip.NewReader(buf)
			if err != nil {
				return
			}
			io.Copy(w, gzr)
		} else {
			io.Copy(w, bytes.NewBuffer(dat))
		}
	})))
	http.ListenAndServe(":80", nil)
}
<commit_msg>fix s60 storage compression<commit_after>package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/rs/cors"
)

func main() {
	store := make(map[uint64][]byte)
	var n uint64
	var lck sync.Mutex
	http.Handle("/add", cors.New(cors.Options{
		AllowedMethods:   []string{"PUT"},
		AllowCredentials: true,
	}).Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPut {
			http.Error(w, "Not a put", http.StatusBadRequest)
			log.Printf("Incorrect method %s\n", r.Method)
			return
		}
		buf := bytes.NewBuffer(nil)
		gz := gzip.NewWriter(buf)
		_, err := io.Copy(gz, r.Body)
		if err != nil {
			return
		}
		err = gz.Close()
		if err != nil {
			return
		}
		lck.Lock()
		defer lck.Unlock()
		store[n] = buf.Bytes()
		go func() {
			time.Sleep(time.Minute)
			lck.Lock()
			defer lck.Unlock()
			delete(store, n)
		}()
		w.Write([]byte(fmt.Sprint(n)))
		n++
	})))
	http.Handle("/get", cors.New(cors.Options{
		AllowedMethods:   []string{"GET"},
		AllowCredentials: true,
	}).Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "Form parse error", http.StatusBadRequest)
			return
		}
		ids := r.FormValue("id")
		if ids == "" {
			http.Error(w, "No id in request", http.StatusBadRequest)
			return
		}
		id, err := strconv.ParseUint(ids, 10, 64)
		if err != nil {
			http.Error(w, "Id is not an int", http.StatusBadRequest)
			return
		}
		dat := func() []byte {
			lck.Lock()
			defer lck.Unlock()
			return store[id]
		}()
		if dat == nil {
			http.Error(w, "Data gone", http.StatusGone)
			return
		}
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			buf := bytes.NewBuffer(dat)
			gzr, err := gzip.NewReader(buf)
			if err != nil {
				return
			}
			io.Copy(w, gzr)
		} else {
			io.Copy(w, bytes.NewBuffer(dat))
		}
	})))
	http.ListenAndServe(":80", nil)
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"github.com/Comcast/webpa-common/concurrent"
	"github.com/Comcast/webpa-common/health"
	"github.com/Comcast/webpa-common/logging"
	"github.com/Comcast/webpa-common/xhttp"
	"github.com/Comcast/webpa-common/xmetrics"
	"github.com/go-kit/kit/log"
	"github.com/justinas/alice"
	stdprometheus "github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
	DefaultBuild = "development"
)

var (
	// ErrorNoPrimaryAddress is the error returned when no primary address is specified in a WebPA instance
	ErrorNoPrimaryAddress = errors.New("No primary address configured")
)

// executor is an internal type used to start an HTTP server.  *http.Server implements
// this interface.  It can be mocked for testing.
type executor interface {
	ListenAndServe() error
	ListenAndServeTLS(certificateFile, keyFile string) error
}

// Secure exposes the optional certificate information to be used when starting an HTTP server.
type Secure interface {
	// Certificate returns the certificate information associated with this Secure instance.
	// BOTH the returned file paths must be non-empty if a TLS server is desired.
	Certificate() (certificateFile, keyFile string)
}

// ListenAndServe invokes the appropriate server method based on the secure information.
// If Secure.Certificate() returns both a certificateFile and a keyFile, e.ListenAndServeTLS()
// is called to start the server.  Otherwise, e.ListenAndServe() is used.
func ListenAndServe(logger log.Logger, s Secure, e executor) {
	certificateFile, keyFile := s.Certificate()
	if len(certificateFile) > 0 && len(keyFile) > 0 {

		go func() {
			logging.Error(logger).Log(
				logging.ErrorKey(), e.ListenAndServeTLS(certificateFile, keyFile),
			)
		}()
	} else {
		go func() {
			logging.Error(logger).Log(
				logging.ErrorKey(), e.ListenAndServe(),
			)
		}()
	}
}

// Basic describes a simple HTTP server.  Typically, this struct has its values
// injected via Viper.  See the New function in this package.
type Basic struct {
	Name               string
	Address            string
	CertificateFile    string
	KeyFile            string
	ClientCACertFile   string
	LogConnectionState bool
}

func (b *Basic) Certificate() (certificateFile, keyFile string) {
	return b.CertificateFile, b.KeyFile
}

// New creates an http.Server using this instance's configuration.  The given logger is required,
// but the handler may be nil.  If the handler is nil, http.DefaultServeMux is used, which matches
// the behavior of http.Server.
//
// This method returns nil if the configured address is empty, effectively disabling
// this server from startup.
func (b *Basic) New(logger log.Logger, handler http.Handler) *http.Server {
	if len(b.Address) == 0 {
		return nil
	}

	// Adding MTLS support using client CA cert pool
	var tlsConfig *tls.Config
	certificateFile, keyFile := b.Certificate()
	// Only when HTTPS i.e. cert & key present, check for client CA and set TLS config for MTLS
	if len(certificateFile) > 0 && len(keyFile) > 0 && len(b.ClientCACertFile) > 0 {

		caCert, err := ioutil.ReadFile(b.ClientCACertFile)
		if err != nil {
			logging.Error(logger).Log(logging.MessageKey(), "Error in reading ClientCACertFile ",
				logging.ErrorKey(), err)
		} else {
			caCertPool := x509.NewCertPool()
			caCertPool.AppendCertsFromPEM(caCert)
			tlsConfig = &tls.Config{
				ClientCAs:  caCertPool,
				ClientAuth: tls.RequireAndVerifyClientCert,
			}
			tlsConfig.BuildNameToCertificate()
		}
	}

	server := &http.Server{
		Addr:      b.Address,
		Handler:   handler,
		ErrorLog:  NewErrorLog(b.Name, logger),
		TLSConfig: tlsConfig,
	}

	if b.LogConnectionState {
		server.ConnState = NewConnectionStateLogger(b.Name, logger)
	}

	return server
}

// Metric is the configurable factory for a metrics server.
type Metric struct {
	Name               string
	Address            string
	CertificateFile    string
	KeyFile            string
	LogConnectionState bool
	HandlerOptions     promhttp.HandlerOpts
	MetricsOptions     xmetrics.Options
}

func (m *Metric) Certificate() (certificateFile, keyFile string) {
	return m.CertificateFile, m.KeyFile
}

func (m *Metric) NewRegistry(modules ...xmetrics.Module) (xmetrics.Registry, error) {
	// always append the builtin server metrics, which can be overridden in configuration
	modules = append(modules, Metrics)
	return xmetrics.NewRegistry(&m.MetricsOptions, modules...)
}

func (m *Metric) New(logger log.Logger, chain alice.Chain, gatherer stdprometheus.Gatherer) *http.Server {
	if len(m.Address) == 0 {
		return nil
	}

	var (
		mux     = http.NewServeMux()
		handler = chain.Then(promhttp.HandlerFor(gatherer, m.HandlerOptions))
	)

	mux.Handle("/metrics", handler)
	server := &http.Server{
		Addr:     m.Address,
		Handler:  mux,
		ErrorLog: NewErrorLog(m.Name, logger),
	}

	if m.LogConnectionState {
		server.ConnState = NewConnectionStateLogger(m.Name, logger)
	}

	return server
}

// Health represents a configurable factory for a Health server.  As with the Basic type,
// if the Address is not specified, health is considered to be disabled.
//
// Due to a limitation of Viper, this struct does not use an embedded Basic
// instance.  Rather, it duplicates the fields so that Viper can inject them.
type Health struct {
	Name               string
	Address            string
	CertificateFile    string
	KeyFile            string
	LogConnectionState bool
	LogInterval        time.Duration
	Options            []string
}

func (h *Health) Certificate() (certificateFile, keyFile string) {
	return h.CertificateFile, h.KeyFile
}

// NewHealth creates a Health instance from this instance's configuration.  If the Address
// field is not supplied, this method returns nil.
func (h *Health) NewHealth(logger log.Logger, options ...health.Option) *health.Health {
	if len(h.Address) == 0 {
		return nil
	}

	for _, value := range h.Options {
		options = append(options, health.Stat(value))
	}

	return health.New(
		h.LogInterval,
		logger,
		options...,
	)
}

// New creates an HTTP server instance for serving health statistics.  If the health parameter
// is nil, then h.NewHealth is used to create a Health instance.  Otherwise, the health parameter
// is returned as is.
//
// If the Address option is not supplied, the health module is considered to be disabled.  In that
// case, this method simply returns the health parameter as the monitor and a nil server instance.
func (h *Health) New(logger log.Logger, chain alice.Chain, health *health.Health) (*health.Health, *http.Server) {
	if len(h.Address) == 0 {
		// health is disabled
		return nil, nil
	}

	if health == nil {
		if health = h.NewHealth(logger); health == nil {
			// should never hit this case, since NewHealth performs the same
			// Address field check as this method.  but, just to be safe ...
			return nil, nil
		}
	}

	mux := http.NewServeMux()
	mux.Handle("/health", chain.Then(health))

	server := &http.Server{
		Addr:     h.Address,
		Handler:  mux,
		ErrorLog: NewErrorLog(h.Name, logger),
	}

	if h.LogConnectionState {
		server.ConnState = NewConnectionStateLogger(h.Name, logger)
	}

	return health, server
}

// WebPA represents a server component within the WebPA cluster.  It is used for both
// primary servers (e.g. petasos) and supporting, embedded servers such as pprof.
type WebPA struct {
	// ApplicationName is the short identifier for the enclosing application, e.g. "talaria".
	// This value is defaulted to what's passed in via Initialize, but can be changed via injection.
	ApplicationName string

	// Primary is the main server for this application, e.g. petasos.
	Primary Basic

	// Alternate is an alternate server which serves the primary application logic.
	// Used to have the same API served on more than one port and possibly more than
	// one protocol, e.g. HTTP and HTTPS.
	Alternate Basic

	// Health describes the health server for this application.  Note that if the Address
	// is empty, no health server is started.
	Health Health

	// Pprof describes the pprof server for this application.  Note that if the Address
	// is empty, no pprof server is started.
	Pprof Basic

	// Metric describes the metrics provider server for this application
	Metric Metric

	// Build is the build string for the current codebase
	Build string

	// Log is the logging configuration for this application.
	Log *logging.Options
}

// build returns the injected build string if available, DefaultBuild otherwise
func (w *WebPA) build() string {
	if w != nil && len(w.Build) > 0 {
		return w.Build
	}

	return DefaultBuild
}

// Prepare gets a WebPA server ready for execution.  This method does not return errors, but the returned
// Runnable may return an error.  The supplied logger will usually come from the New function, but the
// WebPA.Log object can be used to create a different logger if desired.
//
// The caller may pass an arbitrary Health instance.  If this parameter is nil, this method will attempt to
// create one using Health.NewHealth.  In either case, if Health.Address is not supplied, no health server
// will be instantiated.
//
// The caller may also pass a gatherer type. If it is not provided, the default provided by prometheus is used.
//
// The supplied http.Handler is used for the primary server.  If the alternate server has an address,
// it will also be used for that server.  The health server uses an internally create handler, while pprof and metrics
// servers use http.DefaultServeMux.  The health Monitor created from configuration is returned so that other
// infrastructure can make use of it.
func (w *WebPA) Prepare(logger log.Logger, health *health.Health, registry xmetrics.Registry, primaryHandler http.Handler) (health.Monitor, concurrent.Runnable) {
	// allow the health instance to be non-nil, in which case it will be used in favor of
	// the WebPA-configured instance.
	var (
		staticHeaders = xhttp.StaticHeaders(http.Header{
			fmt.Sprintf("X-%s-Build", w.ApplicationName):      {w.build()},
			fmt.Sprintf("X-%s-Start-Time", w.ApplicationName): {time.Now().UTC().Format(time.RFC822)},
		})

		healthHandler, healthServer = w.Health.New(logger, alice.New(staticHeaders), health)
		infoLog                     = logging.Info(logger)
	)

	return healthHandler, concurrent.RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error {
		if healthHandler != nil && healthServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Health.Name, "address", w.Health.Address)
			ListenAndServe(logger, &w.Health, healthServer)
			healthHandler.Run(waitGroup, shutdown)
		}

		if pprofServer := w.Pprof.New(logger, nil); pprofServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Pprof.Name, "address", w.Pprof.Address)
			ListenAndServe(logger, &w.Pprof, pprofServer)
		}

		primaryHandler = staticHeaders(w.decorateWithBasicMetrics(registry, primaryHandler))
		if primaryServer := w.Primary.New(logger, primaryHandler); primaryServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Primary.Name, "address", w.Primary.Address)
			ListenAndServe(logger, &w.Primary, primaryServer)
		} else {
			return ErrorNoPrimaryAddress
		}

		if alternateServer := w.Alternate.New(logger, primaryHandler); alternateServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Alternate.Name, "address", w.Alternate.Address)
			ListenAndServe(logger, &w.Alternate, alternateServer)
		}

		if metricsServer := w.Metric.New(logger, alice.New(staticHeaders), registry); metricsServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Metric.Name, "address", w.Metric.Address)
			ListenAndServe(logger, &w.Metric, metricsServer)
		}

		return nil
	})
}

//decorateWithBasicMetrics wraps a WebPA server handler with basic instrumentation metrics
func (w *WebPA) decorateWithBasicMetrics(p xmetrics.PrometheusProvider, next http.Handler) http.Handler {
	var (
		requestCounterVec    = p.NewCounterVec("api_requests_total")
		inFlightGauge        = p.NewGaugeVec("in_flight_requests").WithLabelValues()
		requestDurationVec   = p.NewHistogramVec("request_duration_seconds")
		requestSizeVec       = p.NewHistogramVec("request_size_bytes")
		responseSizeVec      = p.NewHistogramVec("response_size_bytes")
		timeToWriteHeaderVec = p.NewHistogramVec("time_writing_header_seconds")
	)

	//todo: Example documentation does something interesting with /pull vs. /push endpoints
	//https://godoc.org/github.com/prometheus/client_golang/prometheus/promhttp#InstrumentHandlerDuration
	//for now, let's keep it simple so /metrics only

	return promhttp.InstrumentHandlerInFlight(inFlightGauge,
		promhttp.InstrumentHandlerCounter(requestCounterVec,
			promhttp.InstrumentHandlerDuration(requestDurationVec,
				promhttp.InstrumentHandlerResponseSize(responseSizeVec,
					promhttp.InstrumentHandlerRequestSize(requestSizeVec,
						promhttp.InstrumentHandlerTimeToWriteHeader(timeToWriteHeaderVec, next))),
			),
		),
	)
}
<commit_msg>Support FQDN server headers<commit_after>package server

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"github.com/Comcast/webpa-common/concurrent"
	"github.com/Comcast/webpa-common/health"
	"github.com/Comcast/webpa-common/logging"
	"github.com/Comcast/webpa-common/xhttp"
	"github.com/Comcast/webpa-common/xmetrics"
	"github.com/go-kit/kit/log"
	"github.com/justinas/alice"
	stdprometheus "github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
	DefaultBuild = "development"
	DefaultFqdn  = "localhost"
)

var (
	// ErrorNoPrimaryAddress is the error returned when no primary address is specified in a WebPA instance
	ErrorNoPrimaryAddress = errors.New("No primary address configured")
)

// executor is an internal type used to start an HTTP server.  *http.Server implements
// this interface.  It can be mocked for testing.
type executor interface {
	ListenAndServe() error
	ListenAndServeTLS(certificateFile, keyFile string) error
}

// Secure exposes the optional certificate information to be used when starting an HTTP server.
type Secure interface {
	// Certificate returns the certificate information associated with this Secure instance.
	// BOTH the returned file paths must be non-empty if a TLS server is desired.
	Certificate() (certificateFile, keyFile string)
}

// ListenAndServe invokes the appropriate server method based on the secure information.
// If Secure.Certificate() returns both a certificateFile and a keyFile, e.ListenAndServeTLS()
// is called to start the server.  Otherwise, e.ListenAndServe() is used.
func ListenAndServe(logger log.Logger, s Secure, e executor) {
	certificateFile, keyFile := s.Certificate()
	if len(certificateFile) > 0 && len(keyFile) > 0 {

		go func() {
			logging.Error(logger).Log(
				logging.ErrorKey(), e.ListenAndServeTLS(certificateFile, keyFile),
			)
		}()
	} else {
		go func() {
			logging.Error(logger).Log(
				logging.ErrorKey(), e.ListenAndServe(),
			)
		}()
	}
}

// Basic describes a simple HTTP server.  Typically, this struct has its values
// injected via Viper.  See the New function in this package.
type Basic struct {
	Name               string
	Address            string
	CertificateFile    string
	KeyFile            string
	ClientCACertFile   string
	LogConnectionState bool
}

func (b *Basic) Certificate() (certificateFile, keyFile string) {
	return b.CertificateFile, b.KeyFile
}

// New creates an http.Server using this instance's configuration.  The given logger is required,
// but the handler may be nil.  If the handler is nil, http.DefaultServeMux is used, which matches
// the behavior of http.Server.
//
// This method returns nil if the configured address is empty, effectively disabling
// this server from startup.
func (b *Basic) New(logger log.Logger, handler http.Handler) *http.Server {
	if len(b.Address) == 0 {
		return nil
	}

	// Adding MTLS support using client CA cert pool
	var tlsConfig *tls.Config
	certificateFile, keyFile := b.Certificate()
	// Only when HTTPS i.e. cert & key present, check for client CA and set TLS config for MTLS
	if len(certificateFile) > 0 && len(keyFile) > 0 && len(b.ClientCACertFile) > 0 {

		caCert, err := ioutil.ReadFile(b.ClientCACertFile)
		if err != nil {
			logging.Error(logger).Log(logging.MessageKey(), "Error in reading ClientCACertFile ",
				logging.ErrorKey(), err)
		} else {
			caCertPool := x509.NewCertPool()
			caCertPool.AppendCertsFromPEM(caCert)
			tlsConfig = &tls.Config{
				ClientCAs:  caCertPool,
				ClientAuth: tls.RequireAndVerifyClientCert,
			}
			tlsConfig.BuildNameToCertificate()
		}
	}

	server := &http.Server{
		Addr:      b.Address,
		Handler:   handler,
		ErrorLog:  NewErrorLog(b.Name, logger),
		TLSConfig: tlsConfig,
	}

	if b.LogConnectionState {
		server.ConnState = NewConnectionStateLogger(b.Name, logger)
	}

	return server
}

// Metric is the configurable factory for a metrics server.
type Metric struct {
	Name               string
	Address            string
	CertificateFile    string
	KeyFile            string
	LogConnectionState bool
	HandlerOptions     promhttp.HandlerOpts
	MetricsOptions     xmetrics.Options
}

func (m *Metric) Certificate() (certificateFile, keyFile string) {
	return m.CertificateFile, m.KeyFile
}

func (m *Metric) NewRegistry(modules ...xmetrics.Module) (xmetrics.Registry, error) {
	// always append the builtin server metrics, which can be overridden in configuration
	modules = append(modules, Metrics)
	return xmetrics.NewRegistry(&m.MetricsOptions, modules...)
}

func (m *Metric) New(logger log.Logger, chain alice.Chain, gatherer stdprometheus.Gatherer) *http.Server {
	if len(m.Address) == 0 {
		return nil
	}

	var (
		mux     = http.NewServeMux()
		handler = chain.Then(promhttp.HandlerFor(gatherer, m.HandlerOptions))
	)

	mux.Handle("/metrics", handler)
	server := &http.Server{
		Addr:     m.Address,
		Handler:  mux,
		ErrorLog: NewErrorLog(m.Name, logger),
	}

	if m.LogConnectionState {
		server.ConnState = NewConnectionStateLogger(m.Name, logger)
	}

	return server
}

// Health represents a configurable factory for a Health server.  As with the Basic type,
// if the Address is not specified, health is considered to be disabled.
//
// Due to a limitation of Viper, this struct does not use an embedded Basic
// instance.  Rather, it duplicates the fields so that Viper can inject them.
type Health struct {
	Name               string
	Address            string
	CertificateFile    string
	KeyFile            string
	LogConnectionState bool
	LogInterval        time.Duration
	Options            []string
}

func (h *Health) Certificate() (certificateFile, keyFile string) {
	return h.CertificateFile, h.KeyFile
}

// NewHealth creates a Health instance from this instance's configuration.  If the Address
// field is not supplied, this method returns nil.
func (h *Health) NewHealth(logger log.Logger, options ...health.Option) *health.Health {
	if len(h.Address) == 0 {
		return nil
	}

	for _, value := range h.Options {
		options = append(options, health.Stat(value))
	}

	return health.New(
		h.LogInterval,
		logger,
		options...,
	)
}

// New creates an HTTP server instance for serving health statistics.  If the health parameter
// is nil, then h.NewHealth is used to create a Health instance.  Otherwise, the health parameter
// is returned as is.
//
// If the Address option is not supplied, the health module is considered to be disabled.  In that
// case, this method simply returns the health parameter as the monitor and a nil server instance.
func (h *Health) New(logger log.Logger, chain alice.Chain, health *health.Health) (*health.Health, *http.Server) {
	if len(h.Address) == 0 {
		// health is disabled
		return nil, nil
	}

	if health == nil {
		if health = h.NewHealth(logger); health == nil {
			// should never hit this case, since NewHealth performs the same
			// Address field check as this method.  but, just to be safe ...
			return nil, nil
		}
	}

	mux := http.NewServeMux()
	mux.Handle("/health", chain.Then(health))

	server := &http.Server{
		Addr:     h.Address,
		Handler:  mux,
		ErrorLog: NewErrorLog(h.Name, logger),
	}

	if h.LogConnectionState {
		server.ConnState = NewConnectionStateLogger(h.Name, logger)
	}

	return health, server
}

// WebPA represents a server component within the WebPA cluster.  It is used for both
// primary servers (e.g. petasos) and supporting, embedded servers such as pprof.
type WebPA struct {
	// ApplicationName is the short identifier for the enclosing application, e.g. "talaria".
	// This value is defaulted to what's passed in via Initialize, but can be changed via injection.
	ApplicationName string

	// Primary is the main server for this application, e.g. petasos.
	Primary Basic

	// Alternate is an alternate server which serves the primary application logic.
	// Used to have the same API served on more than one port and possibly more than
	// one protocol, e.g. HTTP and HTTPS.
	Alternate Basic

	// Health describes the health server for this application.  Note that if the Address
	// is empty, no health server is started.
	Health Health

	// Pprof describes the pprof server for this application.  Note that if the Address
	// is empty, no pprof server is started.
	Pprof Basic

	// Metric describes the metrics provider server for this application
	Metric Metric

	// Build is the build string for the current codebase
	Build string

	// Fqdn is the fully-qualified domain name of this server, typically injected as a fact
	Fqdn string

	// Log is the logging configuration for this application.
	Log *logging.Options
}

// build returns the injected build string if available, DefaultBuild otherwise
func (w *WebPA) build() string {
	if w != nil && len(w.Build) > 0 {
		return w.Build
	}

	return DefaultBuild
}

// fqdn returns the injected fully-qualified domain name if available, DefaultFqdn otherwise
func (w *WebPA) fqdn() string {
	if w != nil && len(w.Fqdn) > 0 {
		return w.Fqdn
	}

	return DefaultFqdn
}

// Prepare gets a WebPA server ready for execution.  This method does not return errors, but the returned
// Runnable may return an error.  The supplied logger will usually come from the New function, but the
// WebPA.Log object can be used to create a different logger if desired.
//
// The caller may pass an arbitrary Health instance.  If this parameter is nil, this method will attempt to
// create one using Health.NewHealth.  In either case, if Health.Address is not supplied, no health server
// will be instantiated.
//
// The caller may also pass a gatherer type. If it is not provided, the default provided by prometheus is used.
//
// The supplied http.Handler is used for the primary server.  If the alternate server has an address,
// it will also be used for that server.  The health server uses an internally create handler, while pprof and metrics
// servers use http.DefaultServeMux.  The health Monitor created from configuration is returned so that other
// infrastructure can make use of it.
func (w *WebPA) Prepare(logger log.Logger, health *health.Health, registry xmetrics.Registry, primaryHandler http.Handler) (health.Monitor, concurrent.Runnable) {
	// allow the health instance to be non-nil, in which case it will be used in favor of
	// the WebPA-configured instance.
	var (
		staticHeaders = xhttp.StaticHeaders(http.Header{
			fmt.Sprintf("X-%s-Build", w.ApplicationName):      {w.build()},
			fmt.Sprintf("X-%s-Server", w.ApplicationName):     {w.fqdn()},
			fmt.Sprintf("X-%s-Start-Time", w.ApplicationName): {time.Now().UTC().Format(time.RFC822)},
		})

		healthHandler, healthServer = w.Health.New(logger, alice.New(staticHeaders), health)
		infoLog                     = logging.Info(logger)
	)

	return healthHandler, concurrent.RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error {
		if healthHandler != nil && healthServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Health.Name, "address", w.Health.Address)
			ListenAndServe(logger, &w.Health, healthServer)
			healthHandler.Run(waitGroup, shutdown)
		}

		if pprofServer := w.Pprof.New(logger, nil); pprofServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Pprof.Name, "address", w.Pprof.Address)
			ListenAndServe(logger, &w.Pprof, pprofServer)
		}

		primaryHandler = staticHeaders(w.decorateWithBasicMetrics(registry, primaryHandler))
		if primaryServer := w.Primary.New(logger, primaryHandler); primaryServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Primary.Name, "address", w.Primary.Address)
			ListenAndServe(logger, &w.Primary, primaryServer)
		} else {
			return ErrorNoPrimaryAddress
		}

		if alternateServer := w.Alternate.New(logger, primaryHandler); alternateServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Alternate.Name, "address", w.Alternate.Address)
			ListenAndServe(logger, &w.Alternate, alternateServer)
		}

		if metricsServer := w.Metric.New(logger, alice.New(staticHeaders), registry); metricsServer != nil {
			infoLog.Log(logging.MessageKey(), "starting server", "name", w.Metric.Name, "address", w.Metric.Address)
			ListenAndServe(logger, &w.Metric, metricsServer)
		}

		return nil
	})
}

//decorateWithBasicMetrics wraps a WebPA server handler with basic instrumentation metrics
func (w *WebPA) decorateWithBasicMetrics(p xmetrics.PrometheusProvider, next http.Handler) http.Handler {
	var (
		requestCounterVec    = p.NewCounterVec("api_requests_total")
		inFlightGauge        = p.NewGaugeVec("in_flight_requests").WithLabelValues()
		requestDurationVec   = p.NewHistogramVec("request_duration_seconds")
		requestSizeVec       = p.NewHistogramVec("request_size_bytes")
		responseSizeVec      = p.NewHistogramVec("response_size_bytes")
		timeToWriteHeaderVec = p.NewHistogramVec("time_writing_header_seconds")
	)

	//todo: Example documentation does something interesting with /pull vs. /push endpoints
	//https://godoc.org/github.com/prometheus/client_golang/prometheus/promhttp#InstrumentHandlerDuration
	//for now, let's keep it simple so /metrics only

	return promhttp.InstrumentHandlerInFlight(inFlightGauge,
		promhttp.InstrumentHandlerCounter(requestCounterVec,
			promhttp.InstrumentHandlerDuration(requestDurationVec,
				promhttp.InstrumentHandlerResponseSize(responseSizeVec,
					promhttp.InstrumentHandlerRequestSize(requestSizeVec,
						promhttp.InstrumentHandlerTimeToWriteHeader(timeToWriteHeaderVec, next))),
			),
		),
	)
}
<|endoftext|>