<commit_before>// Copyright 2015-2017 trivago GmbH
//
// 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 producer

import (
	"fmt"
	"github.com/trivago/gollum/core"
	"github.com/trivago/gollum/core/components"
	"github.com/trivago/gollum/producer/file"
	"github.com/trivago/tgo/tmath"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// File producer plugin
//
// The file producer writes messages to a file. This producer also allows log
// rotation and compression of the rotated logs. Folders in the file path will
// be created if necessary.
//
// Configuration example
//
//  myProducer:
//    Type: producer.File
//    File: "/var/log/gollum.log"
//    FileOverwrite: false
//    Permissions: "0664"
//    FolderPermissions: "0755"
//    Batch:
// 		MaxCount: 8192
//    	FlushCount: 4096
//    	TimeoutSec: 5
//      FlushTimeoutSec: 5
//    Rotation:
//		Enable: false
// 		Timestamp: 2006-01-02_15
//    	TimeoutMin: 1440
//    	SizeMB: 1024
// 		Compress: false
// 		ZeroPadding: 0
// 		At: 13:05
// 	  Prune:
//    	Count: 0
//    	AfterHours: 0
//    	TotalSizeMB: 0
//
// File contains the path to the log file to write. The wildcard character "*"
// can be used as a placeholder for the stream name.
// By default this is set to /var/log/gollum.log.
//
// FileOverwrite enables files to be overwritten instead of appending new data
// to it. This is set to false by default.
//
// Permissions accepts an octal number string that contains the unix file
// permissions used when creating a file. By default this is set to "0664".
//
// FolderPermissions accepts an octal number string that contains the unix file
// permissions used when creating a folder. By default this is set to "0755".
//
// Batch/MaxCount defines the maximum number of messages that can be buffered
// before a flush is mandatory. If the buffer is full and a flush is still
// underway or cannot be triggered out of other reasons, the producer will
// block. By default this is set to 8192.
//
// Batch/FlushCount defines the number of messages to be buffered before they are
// written to disk. This setting is clamped to BatchMaxCount.
// By default this is set to BatchMaxCount / 2.
//
// Batch/TimeoutSec defines the maximum number of seconds to wait after the last
// message arrived before a batch is flushed automatically. By default this is
// set to 5.
//
// Batch/FlushTimeoutSec sets the maximum number of seconds to wait before a flush is
// aborted during shutdown. By default this is set to 0, which does not abort
// the flushing procedure.
//
type File struct {
	core.DirectProducer `gollumdoc:"embed_type"`

	// Rotate is public to make Pruner.Configure() callable (bug in treflect package)
	// Prune is public to make FileRotateConfig.Configure() callable (bug in treflect package)
	Rotate components.RotateConfig `gollumdoc:"embed_type"`
	Pruner file.Pruner             `gollumdoc:"embed_type"`

	// configuration
	batchTimeout      time.Duration `config:"Batch/TimeoutSec" default:"5" metric:"sec"`
	batchMaxCount     int           `config:"Batch/MaxCount" default:"8192"`
	batchFlushCount   int           `config:"Batch/FlushCount" default:"4096"`
	batchFlushTimeout time.Duration `config:"Batch/FlushTimeoutSec" default:"0" metric:"sec"`
	overwriteFile     bool          `config:"FileOverwrite"`
	filePermissions   os.FileMode   `config:"Permissions" default:"0644"`
	folderPermissions os.FileMode   `config:"FolderPermissions" default:"0755"`

	// properties
	filesByStream    map[core.MessageStreamID]*components.BatchedWriterAssembly
	files            map[string]*components.BatchedWriterAssembly
	fileDir          string
	fileName         string
	fileExt          string
	wildcardPath     bool
	batchedFileGuard *sync.RWMutex
}

func init() {
	core.TypeRegistry.Register(File{})
}

// Configure initializes this producer with values from a plugin config.
func (prod *File) Configure(conf core.PluginConfigReader) {
	prod.Pruner.Logger = prod.Logger

	prod.SetRollCallback(prod.rotateLog)
	prod.SetStopCallback(prod.close)

	prod.filesByStream = make(map[core.MessageStreamID]*components.BatchedWriterAssembly)
	prod.files = make(map[string]*components.BatchedWriterAssembly)
	prod.batchFlushCount = tmath.MinI(prod.batchFlushCount, prod.batchMaxCount)

	logFile := conf.GetString("File", "/var/log/gollum.log")
	prod.wildcardPath = strings.IndexByte(logFile, '*') != -1

	prod.fileDir = filepath.Dir(logFile)
	prod.fileExt = filepath.Ext(logFile)
	prod.fileName = filepath.Base(logFile)
	prod.fileName = prod.fileName[:len(prod.fileName)-len(prod.fileExt)]

	prod.batchedFileGuard = new(sync.RWMutex)
}

// Produce writes to a buffer that is dumped to a file.
func (prod *File) Produce(workers *sync.WaitGroup) {
	prod.AddMainWorker(workers)
	prod.TickerMessageControlLoop(prod.writeMessage, prod.batchTimeout, prod.writeBatchOnTimeOut)
}

func (prod *File) getBatchedFile(streamID core.MessageStreamID, forceRotate bool) (*components.BatchedWriterAssembly, error) {
	var err error

	// get batchedFile from filesByStream[streamID] map
	prod.batchedFileGuard.RLock()
	batchedFile, fileExists := prod.filesByStream[streamID]
	prod.batchedFileGuard.RUnlock()
	if fileExists {
		if rotate, err := batchedFile.NeedsRotate(prod.Rotate, forceRotate); !rotate {
			return batchedFile, err // ### return, already open or error ###
		}
	}

	prod.batchedFileGuard.Lock()
	defer prod.batchedFileGuard.Unlock()

	// check again to avoid race conditions
	if batchedFile, fileExists := prod.filesByStream[streamID]; fileExists {
		if rotate, err := batchedFile.NeedsRotate(prod.Rotate, forceRotate); !rotate {
			return batchedFile, err // ### return, already open or error ###
		}
	}

	streamTargetFile := prod.newStreamTargetFile(streamID)

	// get batchedFile from files[path] and assure the file is correctly mapped
	batchedFile, fileExists = prod.files[streamTargetFile.GetOriginalPath()]
	if !fileExists {
		// batchedFile does not yet exist: create and map it
		batchedFile = components.NewBatchedWriterAssembly(
			prod.batchMaxCount,
			prod.batchTimeout,
			prod.batchFlushCount,
			prod,
			prod.TryFallback,
			prod.batchFlushTimeout,
			prod.Logger,
		)

		prod.files[streamTargetFile.GetOriginalPath()] = batchedFile
		prod.filesByStream[streamID] = batchedFile
	} else if _, mappingExists := prod.filesByStream[streamID]; !mappingExists {
		// batchedFile exists but is not mapped: map it and see if we need to Rotate
		prod.filesByStream[streamID] = batchedFile
		if rotate, err := batchedFile.NeedsRotate(prod.Rotate, forceRotate); !rotate {
			return batchedFile, err // ### return, already open or error ###
		}
	}

	// Assure directory is existing
	if _, err = streamTargetFile.GetDir(); err != nil {
		return nil, err // ### return, missing directory ###
	}

	finalPath := streamTargetFile.GetFinalPath(prod.Rotate)

	// Close existing batchedFile.writer
	if batchedFile.HasWriter() {
		currentLog := batchedFile.GetWriterAndUnset()

		prod.Logger.Info("Rotated ", currentLog.Name(), " -> ", finalPath)
		go currentLog.Close() // close in subroutine for eventually compression in the background
	}

	// Update BatchedWriterAssembly writer and creation time
	fileWriter, err := prod.newFileStateWriterDisk(finalPath)
	if err != nil {
		return batchedFile, err // ### return error ###
	}

	batchedFile.SetWriter(fileWriter)

	// Create "current" symlink
	if prod.Rotate.Enabled {
		prod.createCurrentSymlink(finalPath, streamTargetFile.GetSymlinkPath())
	}

	// Prune old logs if requested
	go prod.Pruner.Prune(streamTargetFile.GetOriginalPath())

	return batchedFile, err
}

func (prod *File) createCurrentSymlink(source, target string) {
	symLinkNameTemporary := fmt.Sprintf("%s.tmp", target)

	os.Symlink(source, symLinkNameTemporary)
	os.Rename(symLinkNameTemporary, target)
}

func (prod *File) newFileStateWriterDisk(path string) (*file.BatchedFileWriter, error) {
	openFlags := os.O_RDWR | os.O_CREATE | os.O_APPEND
	if prod.overwriteFile {
		openFlags |= os.O_TRUNC
	} else {
		openFlags |= os.O_APPEND
	}

	fileHandler, err := os.OpenFile(path, openFlags, prod.filePermissions)
	if err != nil {
		return nil, err // ### return error ###
	}

	batchedFileWriter := file.NewBatchedFileWriter(fileHandler, prod.Rotate.Compress, prod.Logger)
	return &batchedFileWriter, nil
}

func (prod *File) newStreamTargetFile(streamID core.MessageStreamID) file.TargetFile {
	var fileDir, fileName, fileExt string

	if prod.wildcardPath {
		// Get state from filename (without timestamp, etc.)
		var streamName string
		switch streamID {
		case core.WildcardStreamID:
			streamName = "ALL"
		default:
			streamName = core.StreamRegistry.GetStreamName(streamID)
		}

		fileDir = strings.Replace(prod.fileDir, "*", streamName, -1)
		fileName = strings.Replace(prod.fileName, "*", streamName, -1)
		fileExt = strings.Replace(prod.fileExt, "*", streamName, -1)
	} else {
		// Simple case: only one file used
		fileDir = prod.fileDir
		fileName = prod.fileName
		fileExt = prod.fileExt
	}

	return file.NewTargetFile(fileDir, fileName, fileExt, prod.folderPermissions)
}

func (prod *File) rotateLog() {
	for streamID := range prod.filesByStream {
		if _, err := prod.getBatchedFile(streamID, true); err != nil {
			prod.Logger.Error("Rotate error: ", err)
		}
	}
}

func (prod *File) writeBatchOnTimeOut() {
	for _, batchedFile := range prod.files {
		batchedFile.FlushOnTimeOut()
	}
}

func (prod *File) writeMessage(msg *core.Message) {
	batchedFile, err := prod.getBatchedFile(msg.GetStreamID(), false)
	if err != nil {
		prod.Logger.Error("Write error: ", err)
		prod.TryFallback(msg)
		return // ### return, fallback ###
	}

	batchedFile.Batch.AppendOrFlush(msg, batchedFile.Flush, prod.IsActiveOrStopping, prod.TryFallback)
}

func (prod *File) close() {
	defer prod.WorkerDone()

	for _, batchedFile := range prod.files {
		batchedFile.Close()
	}
}
<commit_msg>removed unnecessary code<commit_after>// Copyright 2015-2017 trivago GmbH
//
// 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 producer

import (
	"fmt"
	"github.com/trivago/gollum/core"
	"github.com/trivago/gollum/core/components"
	"github.com/trivago/gollum/producer/file"
	"github.com/trivago/tgo/tmath"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// File producer plugin
//
// The file producer writes messages to a file. This producer also allows log
// rotation and compression of the rotated logs. Folders in the file path will
// be created if necessary.
//
// Configuration example
//
//  myProducer:
//    Type: producer.File
//    File: "/var/log/gollum.log"
//    FileOverwrite: false
//    Permissions: "0664"
//    FolderPermissions: "0755"
//    Batch:
// 		MaxCount: 8192
//    	FlushCount: 4096
//    	TimeoutSec: 5
//      FlushTimeoutSec: 5
//    Rotation:
//		Enable: false
// 		Timestamp: 2006-01-02_15
//    	TimeoutMin: 1440
//    	SizeMB: 1024
// 		Compress: false
// 		ZeroPadding: 0
// 		At: 13:05
// 	  Prune:
//    	Count: 0
//    	AfterHours: 0
//    	TotalSizeMB: 0
//
// File contains the path to the log file to write. The wildcard character "*"
// can be used as a placeholder for the stream name.
// By default this is set to /var/log/gollum.log.
//
// FileOverwrite enables files to be overwritten instead of appending new data
// to it. This is set to false by default.
//
// Permissions accepts an octal number string that contains the unix file
// permissions used when creating a file. By default this is set to "0664".
//
// FolderPermissions accepts an octal number string that contains the unix file
// permissions used when creating a folder. By default this is set to "0755".
//
// Batch/MaxCount defines the maximum number of messages that can be buffered
// before a flush is mandatory. If the buffer is full and a flush is still
// underway or cannot be triggered out of other reasons, the producer will
// block. By default this is set to 8192.
//
// Batch/FlushCount defines the number of messages to be buffered before they are
// written to disk. This setting is clamped to BatchMaxCount.
// By default this is set to BatchMaxCount / 2.
//
// Batch/TimeoutSec defines the maximum number of seconds to wait after the last
// message arrived before a batch is flushed automatically. By default this is
// set to 5.
//
// Batch/FlushTimeoutSec sets the maximum number of seconds to wait before a flush is
// aborted during shutdown. By default this is set to 0, which does not abort
// the flushing procedure.
//
type File struct {
	core.DirectProducer `gollumdoc:"embed_type"`

	// Rotate is public to make Pruner.Configure() callable (bug in treflect package)
	// Prune is public to make FileRotateConfig.Configure() callable (bug in treflect package)
	Rotate components.RotateConfig `gollumdoc:"embed_type"`
	Pruner file.Pruner             `gollumdoc:"embed_type"`

	// configuration
	batchTimeout      time.Duration `config:"Batch/TimeoutSec" default:"5" metric:"sec"`
	batchMaxCount     int           `config:"Batch/MaxCount" default:"8192"`
	batchFlushCount   int           `config:"Batch/FlushCount" default:"4096"`
	batchFlushTimeout time.Duration `config:"Batch/FlushTimeoutSec" default:"0" metric:"sec"`
	overwriteFile     bool          `config:"FileOverwrite"`
	filePermissions   os.FileMode   `config:"Permissions" default:"0644"`
	folderPermissions os.FileMode   `config:"FolderPermissions" default:"0755"`

	// properties
	filesByStream    map[core.MessageStreamID]*components.BatchedWriterAssembly
	files            map[string]*components.BatchedWriterAssembly
	fileDir          string
	fileName         string
	fileExt          string
	wildcardPath     bool
	batchedFileGuard *sync.RWMutex
}

func init() {
	core.TypeRegistry.Register(File{})
}

// Configure initializes this producer with values from a plugin config.
func (prod *File) Configure(conf core.PluginConfigReader) {
	prod.Pruner.Logger = prod.Logger

	prod.SetRollCallback(prod.rotateLog)
	prod.SetStopCallback(prod.close)

	prod.filesByStream = make(map[core.MessageStreamID]*components.BatchedWriterAssembly)
	prod.files = make(map[string]*components.BatchedWriterAssembly)
	prod.batchFlushCount = tmath.MinI(prod.batchFlushCount, prod.batchMaxCount)

	logFile := conf.GetString("File", "/var/log/gollum.log")
	prod.wildcardPath = strings.IndexByte(logFile, '*') != -1

	prod.fileDir = filepath.Dir(logFile)
	prod.fileExt = filepath.Ext(logFile)
	prod.fileName = filepath.Base(logFile)
	prod.fileName = prod.fileName[:len(prod.fileName)-len(prod.fileExt)]

	prod.batchedFileGuard = new(sync.RWMutex)
}

// Produce writes to a buffer that is dumped to a file.
func (prod *File) Produce(workers *sync.WaitGroup) {
	prod.AddMainWorker(workers)
	prod.TickerMessageControlLoop(prod.writeMessage, prod.batchTimeout, prod.writeBatchOnTimeOut)
}

func (prod *File) getBatchedFile(streamID core.MessageStreamID, forceRotate bool) (*components.BatchedWriterAssembly, error) {
	var err error

	// get batchedFile from filesByStream[streamID] map
	prod.batchedFileGuard.RLock()
	batchedFile, fileExists := prod.filesByStream[streamID]
	prod.batchedFileGuard.RUnlock()
	if fileExists {
		if rotate, err := batchedFile.NeedsRotate(prod.Rotate, forceRotate); !rotate {
			return batchedFile, err // ### return, already open or error ###
		}
	}

	prod.batchedFileGuard.Lock()
	defer prod.batchedFileGuard.Unlock()

	// check again to avoid race conditions
	if batchedFile, fileExists := prod.filesByStream[streamID]; fileExists {
		if rotate, err := batchedFile.NeedsRotate(prod.Rotate, forceRotate); !rotate {
			return batchedFile, err // ### return, already open or error ###
		}
	}

	streamTargetFile := prod.newStreamTargetFile(streamID)

	// get batchedFile from files[path] and assure the file is correctly mapped
	batchedFile, fileExists = prod.files[streamTargetFile.GetOriginalPath()]
	if !fileExists {
		// batchedFile does not yet exist: create and map it
		batchedFile = components.NewBatchedWriterAssembly(
			prod.batchMaxCount,
			prod.batchTimeout,
			prod.batchFlushCount,
			prod,
			prod.TryFallback,
			prod.batchFlushTimeout,
			prod.Logger,
		)

		prod.files[streamTargetFile.GetOriginalPath()] = batchedFile
		prod.filesByStream[streamID] = batchedFile
	}

	// Assure directory is existing
	if _, err = streamTargetFile.GetDir(); err != nil {
		return nil, err // ### return, missing directory ###
	}

	finalPath := streamTargetFile.GetFinalPath(prod.Rotate)

	// Close existing batchedFile.writer
	if batchedFile.HasWriter() {
		currentLog := batchedFile.GetWriterAndUnset()

		prod.Logger.Info("Rotated ", currentLog.Name(), " -> ", finalPath)
		go currentLog.Close() // close in subroutine for eventually compression in the background
	}

	// Update BatchedWriterAssembly writer and creation time
	fileWriter, err := prod.newFileStateWriterDisk(finalPath)
	if err != nil {
		return batchedFile, err // ### return error ###
	}

	batchedFile.SetWriter(fileWriter)

	// Create "current" symlink
	if prod.Rotate.Enabled {
		prod.createCurrentSymlink(finalPath, streamTargetFile.GetSymlinkPath())
	}

	// Prune old logs if requested
	go prod.Pruner.Prune(streamTargetFile.GetOriginalPath())

	return batchedFile, err
}

func (prod *File) createCurrentSymlink(source, target string) {
	symLinkNameTemporary := fmt.Sprintf("%s.tmp", target)

	os.Symlink(source, symLinkNameTemporary)
	os.Rename(symLinkNameTemporary, target)
}

func (prod *File) newFileStateWriterDisk(path string) (*file.BatchedFileWriter, error) {
	openFlags := os.O_RDWR | os.O_CREATE | os.O_APPEND
	if prod.overwriteFile {
		openFlags |= os.O_TRUNC
	} else {
		openFlags |= os.O_APPEND
	}

	fileHandler, err := os.OpenFile(path, openFlags, prod.filePermissions)
	if err != nil {
		return nil, err // ### return error ###
	}

	batchedFileWriter := file.NewBatchedFileWriter(fileHandler, prod.Rotate.Compress, prod.Logger)
	return &batchedFileWriter, nil
}

func (prod *File) newStreamTargetFile(streamID core.MessageStreamID) file.TargetFile {
	var fileDir, fileName, fileExt string

	if prod.wildcardPath {
		// Get state from filename (without timestamp, etc.)
		var streamName string
		switch streamID {
		case core.WildcardStreamID:
			streamName = "ALL"
		default:
			streamName = core.StreamRegistry.GetStreamName(streamID)
		}

		fileDir = strings.Replace(prod.fileDir, "*", streamName, -1)
		fileName = strings.Replace(prod.fileName, "*", streamName, -1)
		fileExt = strings.Replace(prod.fileExt, "*", streamName, -1)
	} else {
		// Simple case: only one file used
		fileDir = prod.fileDir
		fileName = prod.fileName
		fileExt = prod.fileExt
	}

	return file.NewTargetFile(fileDir, fileName, fileExt, prod.folderPermissions)
}

func (prod *File) rotateLog() {
	for streamID := range prod.filesByStream {
		if _, err := prod.getBatchedFile(streamID, true); err != nil {
			prod.Logger.Error("Rotate error: ", err)
		}
	}
}

func (prod *File) writeBatchOnTimeOut() {
	for _, batchedFile := range prod.files {
		batchedFile.FlushOnTimeOut()
	}
}

func (prod *File) writeMessage(msg *core.Message) {
	batchedFile, err := prod.getBatchedFile(msg.GetStreamID(), false)
	if err != nil {
		prod.Logger.Error("Write error: ", err)
		prod.TryFallback(msg)
		return // ### return, fallback ###
	}

	batchedFile.Batch.AppendOrFlush(msg, batchedFile.Flush, prod.IsActiveOrStopping, prod.TryFallback)
}

func (prod *File) close() {
	defer prod.WorkerDone()

	for _, batchedFile := range prod.files {
		batchedFile.Close()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"log"
	"net/rpc/jsonrpc"
	"strings"

	"github.com/natefinch/pie"
)

type LuaPlugin struct{}

const namespace = "Lua"

// --- Plugin functionality ---

func add3(a, b int) int {
	return a + b + 3
}

// --- Lua wrapper code ($0 is replaced with the plugin path) ---

const luacode = `
function add3(a, b)
  return CallPlugin("$0", "Add3", a, b)
end
`

// --- Lua help text (will be syntax highlighted) ---

const luahelp = `
add3(number, number) -> number // Adds two numbers and then the number 3
`

// --- Plugin wrapper functions ---

func (LuaPlugin) Add3(jsonargs []byte, response *[]byte) (err error) {
	var args []int
	err = json.Unmarshal(jsonargs, &args)
	if err != nil || len(args) < 2 {
		// Could not unmarshal the given arguments, or too few arguments
		return errors.New("add3 requires two integer arguments")
	}
	result := add3(args[0], args[1])
	*response, err = json.Marshal(result)
	return
}

// --- Plugin functions that must be present ---

// Called once when the Plugin function is used in Algernon
func (LuaPlugin) LuaCode(pluginPath string, response *string) error {
	*response = strings.Replace(luacode, "$0", pluginPath, -1)
	return nil
}

// Called once when the Plugin function is used in Algernon
func (LuaPlugin) LuaHelp(_ string, response *string) error {
	*response = luahelp
	return nil
}

// Called once when the Plugin or CallPlugin function is used in Algernon
func main() {
	log.SetPrefix("[plugin log] ")
	p := pie.NewProvider()
	if err := p.RegisterName(namespace, LuaPlugin{}); err != nil {
		log.Fatalf("Failed to register plugin: %s", err)
	}
	p.ServeCodec(jsonrpc.NewServerCodec)
}
<commit_msg>Minor changes to the add3 sample plugin<commit_after>package main

import (
	"encoding/json"
	"errors"
	"log"
	"net/rpc/jsonrpc"
	"strings"

	"github.com/natefinch/pie"
)

type LuaPlugin struct{}

const namespace = "Lua"

// --- Plugin functionality ---

func add3(a, b int) int {
	// Functionality not otherwise available in Lua goes here
	return a + b + 3
}

// --- Lua wrapper code ($0 is replaced with the plugin path) ---

const luacode = `
function add3(a, b)
  return CallPlugin("$0", "Add3", a, b)
end
`

// --- Lua help text (will be syntax highlighted) ---

const luahelp = `
add3(number, number) -> number // Adds two numbers and then the number 3
`

// --- Plugin wrapper functions ---

func (LuaPlugin) Add3(jsonargs []byte, response *[]byte) (err error) {
	var args []int
	err = json.Unmarshal(jsonargs, &args)
	if err != nil || len(args) < 2 {
		// Could not unmarshal the given arguments, or too few arguments
		return errors.New("add3 requires two integer arguments")
	}
	result := add3(args[0], args[1])
	*response, err = json.Marshal(result)
	return
}

// --- Plugin functions that must be present ---

// Called once when the Plugin function is used in Algernon
func (LuaPlugin) LuaCode(pluginPath string, response *string) error {
	*response = strings.Replace(luacode, "$0", pluginPath, -1)
	return nil
}

// Called once when the Plugin function is used in Algernon
func (LuaPlugin) LuaHelp(_ string, response *string) error {
	*response = luahelp
	return nil
}

// Called once when the Plugin or CallPlugin function is used in Algernon
func main() {
	log.SetPrefix("[plugin log] ")
	p := pie.NewProvider()
	if err := p.RegisterName(namespace, LuaPlugin{}); err != nil {
		log.Fatalf("Failed to register plugin: %s", err)
	}
	p.ServeCodec(jsonrpc.NewServerCodec)
}
<|endoftext|>


<commit_before>// Package importer defines the Importer, which loads, parses and
// type-checks packages of Go code plus their transitive closure, and
// retains both the ASTs and the derived facts.
//
// TODO(adonovan): document and test this package better, with examples.
// Currently it's covered by the ssa/ tests.
//
package importer

import (
	"fmt"
	"go/ast"
	"go/token"
	"os"

	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
)

// An Importer's methods are not thread-safe.
type Importer struct {
	config   *Config                 // the client configuration
	Fset     *token.FileSet          // position info for all files seen
	Packages map[string]*PackageInfo // keys are import paths
	errors   map[string]error        // cache of errors by import path
}

// Config specifies the configuration for the importer.
//
type Config struct {
	// TypeChecker contains options relating to the type checker.
	// The Importer will override any user-supplied values for its
	// Error and Import fields; other fields will be passed
	// through to the type checker.
	TypeChecker types.Config

	// If Loader is non-nil, it is used to satisfy imports.
	//
	// If it is nil, binary object files produced by the gc
	// compiler will be loaded instead of source code for all
	// imported packages.  Such files supply only the types of
	// package-level declarations and values of constants, but no
	// code, so this mode will not yield a whole program.  It is
	// intended for analyses that perform intraprocedural analysis
	// of a single package.
	Loader SourceLoader
}

// SourceLoader is the signature of a function that locates, reads and
// parses a set of source files given an import path.
//
// fset is the fileset to which the ASTs should be added.
// path is the imported path, e.g. "sync/atomic".
//
// On success, the function returns files, the set of ASTs produced,
// or the first error encountered.
//
// The MakeGoBuildLoader utility can be used to construct a
// SourceLoader based on go/build.
//
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)

// New returns a new, empty Importer using configuration options
// specified by config.
//
func New(config *Config) *Importer {
	imp := &Importer{
		config:   config,
		Fset:     token.NewFileSet(),
		Packages: make(map[string]*PackageInfo),
		errors:   make(map[string]error),
	}
	imp.config.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
	imp.config.TypeChecker.Import = imp.doImport
	return imp
}

// doImport loads the typechecker package identified by path
// Implements the types.Importer prototype.
//
func (imp *Importer) doImport(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
	// Package unsafe is handled specially, and has no PackageInfo.
	if path == "unsafe" {
		return types.Unsafe, nil
	}

	// Load the source/binary for 'path', type-check it, construct
	// a PackageInfo and update our map (imp.Packages) and the
	// type-checker's map (imports).
	var info *PackageInfo
	if imp.config.Loader != nil {
		info, err = imp.LoadPackage(path)
	} else {
		if info, ok := imp.Packages[path]; ok {
			imports[path] = info.Pkg
			pkg = info.Pkg
			return // positive cache hit
		}

		if err = imp.errors[path]; err != nil {
			return // negative cache hit
		}

		if pkg, err = types.GcImport(imports, path); err == nil {
			info = &PackageInfo{Pkg: pkg}
			imp.Packages[path] = info
		}
	}

	if err == nil {
		// Cache success.
		pkg = info.Pkg
		imports[path] = pkg
		return pkg, nil
	}

	// Cache failure
	imp.errors[path] = err
	return nil, err
}

// LoadPackage loads the package of the specified import-path,
// performs type-checking, and returns the corresponding
// PackageInfo.
//
// Not idempotent!
// Precondition: Importer.config.Loader != nil.
// TODO(adonovan): fix: violated in call from CreatePackageFromArgs!
// Not thread-safe!
// TODO(adonovan): rethink this API.
//
func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
	if info, ok := imp.Packages[importPath]; ok {
		return info, nil // positive cache hit
	}

	if err := imp.errors[importPath]; err != nil {
		return nil, err // negative cache hit
	}

	if imp.config.Loader == nil {
		panic("Importer.LoadPackage without a SourceLoader")
	}
	files, err := imp.config.Loader(imp.Fset, importPath)
	if err != nil {
		return nil, err
	}
	info := imp.CreateSourcePackage(importPath, files)
	if info.Err != nil {
		return nil, info.Err
	}
	return info, nil
}

// CreateSourcePackage invokes the type-checker on files and returns a
// PackageInfo containing the resulting type-checker package, the
// ASTs, and other type information.
//
// The order of files determines the package initialization order.
//
// importPath is the full name under which this package is known, such
// as appears in an import declaration. e.g. "sync/atomic".
//
// The ParseFiles utility may be helpful for parsing a set of Go
// source files.
//
// The result is always non-nil; the presence of errors is indicated
// by the PackageInfo.Err field.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) *PackageInfo {
	pkgInfo := &PackageInfo{
		Files: files,
		Info: types.Info{
			Types:      make(map[ast.Expr]types.Type),
			Values:     make(map[ast.Expr]exact.Value),
			Objects:    make(map[*ast.Ident]types.Object),
			Implicits:  make(map[ast.Node]types.Object),
			Selections: make(map[*ast.SelectorExpr]*types.Selection),
		},
	}
	pkgInfo.Pkg, pkgInfo.Err = imp.config.TypeChecker.Check(importPath, imp.Fset, files, &pkgInfo.Info)
	imp.Packages[importPath] = pkgInfo
	return pkgInfo
}
<commit_msg>go.tools/importer: retain scope information.<commit_after>// Package importer defines the Importer, which loads, parses and
// type-checks packages of Go code plus their transitive closure, and
// retains both the ASTs and the derived facts.
//
// TODO(adonovan): document and test this package better, with examples.
// Currently it's covered by the ssa/ tests.
//
package importer

import (
	"fmt"
	"go/ast"
	"go/token"
	"os"

	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
)

// An Importer's methods are not thread-safe.
type Importer struct {
	config   *Config                 // the client configuration
	Fset     *token.FileSet          // position info for all files seen
	Packages map[string]*PackageInfo // keys are import paths
	errors   map[string]error        // cache of errors by import path
}

// Config specifies the configuration for the importer.
//
type Config struct {
	// TypeChecker contains options relating to the type checker.
	// The Importer will override any user-supplied values for its
	// Error and Import fields; other fields will be passed
	// through to the type checker.
	TypeChecker types.Config

	// If Loader is non-nil, it is used to satisfy imports.
	//
	// If it is nil, binary object files produced by the gc
	// compiler will be loaded instead of source code for all
	// imported packages.  Such files supply only the types of
	// package-level declarations and values of constants, but no
	// code, so this mode will not yield a whole program.  It is
	// intended for analyses that perform intraprocedural analysis
	// of a single package.
	Loader SourceLoader
}

// SourceLoader is the signature of a function that locates, reads and
// parses a set of source files given an import path.
//
// fset is the fileset to which the ASTs should be added.
// path is the imported path, e.g. "sync/atomic".
//
// On success, the function returns files, the set of ASTs produced,
// or the first error encountered.
//
// The MakeGoBuildLoader utility can be used to construct a
// SourceLoader based on go/build.
//
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)

// New returns a new, empty Importer using configuration options
// specified by config.
//
func New(config *Config) *Importer {
	imp := &Importer{
		config:   config,
		Fset:     token.NewFileSet(),
		Packages: make(map[string]*PackageInfo),
		errors:   make(map[string]error),
	}
	imp.config.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
	imp.config.TypeChecker.Import = imp.doImport
	return imp
}

// doImport loads the typechecker package identified by path
// Implements the types.Importer prototype.
//
func (imp *Importer) doImport(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
	// Package unsafe is handled specially, and has no PackageInfo.
	if path == "unsafe" {
		return types.Unsafe, nil
	}

	// Load the source/binary for 'path', type-check it, construct
	// a PackageInfo and update our map (imp.Packages) and the
	// type-checker's map (imports).
	var info *PackageInfo
	if imp.config.Loader != nil {
		info, err = imp.LoadPackage(path)
	} else {
		if info, ok := imp.Packages[path]; ok {
			imports[path] = info.Pkg
			pkg = info.Pkg
			return // positive cache hit
		}

		if err = imp.errors[path]; err != nil {
			return // negative cache hit
		}

		if pkg, err = types.GcImport(imports, path); err == nil {
			info = &PackageInfo{Pkg: pkg}
			imp.Packages[path] = info
		}
	}

	if err == nil {
		// Cache success.
		pkg = info.Pkg
		imports[path] = pkg
		return pkg, nil
	}

	// Cache failure
	imp.errors[path] = err
	return nil, err
}

// LoadPackage loads the package of the specified import-path,
// performs type-checking, and returns the corresponding
// PackageInfo.
//
// Not idempotent!
// Precondition: Importer.config.Loader != nil.
// TODO(adonovan): fix: violated in call from CreatePackageFromArgs!
// Not thread-safe!
// TODO(adonovan): rethink this API.
//
func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
	if info, ok := imp.Packages[importPath]; ok {
		return info, nil // positive cache hit
	}

	if err := imp.errors[importPath]; err != nil {
		return nil, err // negative cache hit
	}

	if imp.config.Loader == nil {
		panic("Importer.LoadPackage without a SourceLoader")
	}
	files, err := imp.config.Loader(imp.Fset, importPath)
	if err != nil {
		return nil, err
	}
	info := imp.CreateSourcePackage(importPath, files)
	if info.Err != nil {
		return nil, info.Err
	}
	return info, nil
}

// CreateSourcePackage invokes the type-checker on files and returns a
// PackageInfo containing the resulting type-checker package, the
// ASTs, and other type information.
//
// The order of files determines the package initialization order.
//
// importPath is the full name under which this package is known, such
// as appears in an import declaration. e.g. "sync/atomic".
//
// The ParseFiles utility may be helpful for parsing a set of Go
// source files.
//
// The result is always non-nil; the presence of errors is indicated
// by the PackageInfo.Err field.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) *PackageInfo {
	pkgInfo := &PackageInfo{
		Files: files,
		Info: types.Info{
			Types:      make(map[ast.Expr]types.Type),
			Values:     make(map[ast.Expr]exact.Value),
			Objects:    make(map[*ast.Ident]types.Object),
			Implicits:  make(map[ast.Node]types.Object),
			Scopes:     make(map[ast.Node]*types.Scope),
			Selections: make(map[*ast.SelectorExpr]*types.Selection),
		},
	}
	pkgInfo.Pkg, pkgInfo.Err = imp.config.TypeChecker.Check(importPath, imp.Fset, files, &pkgInfo.Info)
	imp.Packages[importPath] = pkgInfo
	return pkgInfo
}
<|endoftext|>


<commit_before>package hello

import (
    "fmt"
    "net/http"
)

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello, world!")
}
<commit_msg>Users API<commit_after>package hello

import (
    "fmt"
    "net/http"

    "appengine"
    "appengine/user"
)

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    u := user.Current(c)
    if u == nil {
        url, err := user.LoginURL(c, r.URL.String())
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        w.Header().Set("Location", url)
        w.WriteHeader(http.StatusFound)
        return
    }
    fmt.Fprintf(w, "Hello, %v!", u)
}
<|endoftext|>


<commit_before>package info

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"regexp"
	"time"

	"github.com/koding/klient/info/publicip"
)

// ProviderChecker funcs check the local machine to assert whether or
// not the current VM is is of that specific Provider.
type ProviderChecker func(whois string) (isProvider bool, err error)

type ProviderName int

const (
	// UnknownProvider is the zero value of the ProviderName type.
	UnknownProvider ProviderName = iota

	AWS
	Azure
	DigitalOcean
	GoogleCloud
	HPCloud
	Joylent
	Rackspace
	SoftLayer
)

func (pn ProviderName) String() string {
	switch pn {
	case AWS:
		return "AWS"
	case Azure:
		return "Azure"
	case DigitalOcean:
		return "DigitalOcean"
	case GoogleCloud:
		return "GoogleCloud"
	case HPCloud:
		return "HPCloud"
	case Joylent:
		return "Joylent"
	case Rackspace:
		return "Rackspace"
	case SoftLayer:
		return "SoftLayer"
	default:
		return "UnknownProvider"
	}
}

const (
	// The whois server WhoisQuery uses by default.
	whoisServer string = "whois.arin.net"

	// Default timeout for the whoisQuery
	whoisTimeout time.Duration = 5 * time.Second
)

// DefaultProviderCheckers is a map of each ProviderName and the
// corresponding checker.
var DefaultProviderCheckers = map[ProviderName]ProviderChecker{
	AWS:          CheckAWS,
	Azure:        CheckAzure,
	DigitalOcean: CheckDigitalOcean,
	GoogleCloud:  CheckGoogleCloud,
	Joylent:      CheckJoylent,
	Rackspace:    CheckRackspace,
	SoftLayer:    CheckSoftLayer,
}

// CheckProvider uses the current machine's IP and runs a whois on it,
// then feeds the whois to all DefaultProviderCheckers.
func CheckProvider() (ProviderName, error) {
	// Get the IP of this machine, to whois against
	ip, err := publicip.PublicIP()
	if err != nil {
		return UnknownProvider, err
	}

	// Get the whois of the current vm's IP
	whois, err := WhoisQuery(ip.String(), whoisServer, whoisTimeout)
	if err != nil {
		return UnknownProvider, err
	}

	return checkProvider(DefaultProviderCheckers, whois)
}

// checkProvider implements the testable functionality of CheckProvider.
// Ie, a pure func, aside from any impurities passed in via checkers.
func checkProvider(checkers map[ProviderName]ProviderChecker, whois string) (
	ProviderName, error) {

	for providerName, checker := range checkers {
		isProvider, err := checker(whois)
		if err != nil {
			return UnknownProvider, err
		}

		if isProvider == true {
			return providerName, nil
		}
	}

	return UnknownProvider, nil
}

// generateChecker returns a ProviderChecker matching one or more whois
// regexp objects against the typical ProviderChecker whois.
func generateChecker(res ...*regexp.Regexp) ProviderChecker {
	return func(whois string) (bool, error) {
		if whois == "" {
			return false, errors.New("generateChecker: Whois is required")
		}

		for _, re := range res {
			if !re.MatchString(whois) {
				return false, nil
			}
		}

		return true, nil
	}
}

// CheckDigitalOcean is a ProviderChecker for DigitalOcean
func CheckDigitalOcean(_ string) (bool, error) {
	return checkDigitalOcean("http://169.254.169.254/metadata/v1/hostname")
}

// checkDigitalOcean implements the testable functionality of
// CheckDigitalOcean by quering the given DigitalOcean API address
// and if it returns 404, the check fails.
func checkDigitalOcean(metadataApi string) (bool, error) {
	res, err := http.Get(metadataApi)
	if err != nil {
		return false, err
	}

	return res.StatusCode == http.StatusOK, nil
}

// CheckAWS is a generic whois checker for Amazon
var CheckAWS ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)amazon`))

var CheckAzure ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)azure`))

var CheckGoogleCloud ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)google\s*cloud`))

var CheckHPCloud ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)hp\s*cloud`))

var CheckJoylent ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)joylent`))

var CheckRackspace ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)rackspace`))

var CheckSoftLayer ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)softlayer`))

// WhoisQuery is a simple func to query a whois service with the (limited)
// whois protocol.
//
// It's worth noting that because the whois protocol is so basic, the
// response can be formatted in any way. Because of this, WhoisQuery has to
// simply return the entire response to the caller - and is unable to
// marshall/etc the response in any meaningful format.
func WhoisQuery(query, server string, timeout time.Duration) (string, error) {
	host := net.JoinHostPort(server, "43")
	conn, err := net.DialTimeout("tcp", host, timeout)
	if err != nil {
		return "", err
	}
	defer conn.Close()

	// Query the whois server with the ip or domain given to this func,
	// as per Whois spec.
	_, err = conn.Write([]byte(fmt.Sprintf("%s\r\n", query)))
	if err != nil {
		return "", err
	}

	// After the query, the server will respond with the unformatted data.
	// Read it all and return it.
	b, err := ioutil.ReadAll(conn)
	if err != nil {
		return "", err
	}

	return string(b), nil
}
<commit_msg>info: Moved whois const block to top<commit_after>package info

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"regexp"
	"time"

	"github.com/koding/klient/info/publicip"
)

const (
	// The whois server WhoisQuery uses by default.
	whoisServer string = "whois.arin.net"

	// Default timeout for the whoisQuery
	whoisTimeout time.Duration = 5 * time.Second
)

// ProviderChecker funcs check the local machine to assert whether or
// not the current VM is is of that specific Provider.
type ProviderChecker func(whois string) (isProvider bool, err error)

type ProviderName int

const (
	// UnknownProvider is the zero value of the ProviderName type.
	UnknownProvider ProviderName = iota

	AWS
	Azure
	DigitalOcean
	GoogleCloud
	HPCloud
	Joylent
	Rackspace
	SoftLayer
)

func (pn ProviderName) String() string {
	switch pn {
	case AWS:
		return "AWS"
	case Azure:
		return "Azure"
	case DigitalOcean:
		return "DigitalOcean"
	case GoogleCloud:
		return "GoogleCloud"
	case HPCloud:
		return "HPCloud"
	case Joylent:
		return "Joylent"
	case Rackspace:
		return "Rackspace"
	case SoftLayer:
		return "SoftLayer"
	default:
		return "UnknownProvider"
	}
}

// DefaultProviderCheckers is a map of each ProviderName and the
// corresponding checker.
var DefaultProviderCheckers = map[ProviderName]ProviderChecker{
	AWS:          CheckAWS,
	Azure:        CheckAzure,
	DigitalOcean: CheckDigitalOcean,
	GoogleCloud:  CheckGoogleCloud,
	Joylent:      CheckJoylent,
	Rackspace:    CheckRackspace,
	SoftLayer:    CheckSoftLayer,
}

// CheckProvider uses the current machine's IP and runs a whois on it,
// then feeds the whois to all DefaultProviderCheckers.
func CheckProvider() (ProviderName, error) {
	// Get the IP of this machine, to whois against
	ip, err := publicip.PublicIP()
	if err != nil {
		return UnknownProvider, err
	}

	// Get the whois of the current vm's IP
	whois, err := WhoisQuery(ip.String(), whoisServer, whoisTimeout)
	if err != nil {
		return UnknownProvider, err
	}

	return checkProvider(DefaultProviderCheckers, whois)
}

// checkProvider implements the testable functionality of CheckProvider.
// Ie, a pure func, aside from any impurities passed in via checkers.
func checkProvider(checkers map[ProviderName]ProviderChecker, whois string) (
	ProviderName, error) {

	for providerName, checker := range checkers {
		isProvider, err := checker(whois)
		if err != nil {
			return UnknownProvider, err
		}

		if isProvider == true {
			return providerName, nil
		}
	}

	return UnknownProvider, nil
}

// generateChecker returns a ProviderChecker matching one or more whois
// regexp objects against the typical ProviderChecker whois.
func generateChecker(res ...*regexp.Regexp) ProviderChecker {
	return func(whois string) (bool, error) {
		if whois == "" {
			return false, errors.New("generateChecker: Whois is required")
		}

		for _, re := range res {
			if !re.MatchString(whois) {
				return false, nil
			}
		}

		return true, nil
	}
}

// CheckDigitalOcean is a ProviderChecker for DigitalOcean
func CheckDigitalOcean(_ string) (bool, error) {
	return checkDigitalOcean("http://169.254.169.254/metadata/v1/hostname")
}

// checkDigitalOcean implements the testable functionality of
// CheckDigitalOcean by quering the given DigitalOcean API address
// and if it returns 404, the check fails.
func checkDigitalOcean(metadataApi string) (bool, error) {
	res, err := http.Get(metadataApi)
	if err != nil {
		return false, err
	}

	return res.StatusCode == http.StatusOK, nil
}

// CheckAWS is a generic whois checker for Amazon
var CheckAWS ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)amazon`))

var CheckAzure ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)azure`))

var CheckGoogleCloud ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)google\s*cloud`))

var CheckHPCloud ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)hp\s*cloud`))

var CheckJoylent ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)joylent`))

var CheckRackspace ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)rackspace`))

var CheckSoftLayer ProviderChecker = generateChecker(
	regexp.MustCompile(`(?i)softlayer`))

// WhoisQuery is a simple func to query a whois service with the (limited)
// whois protocol.
//
// It's worth noting that because the whois protocol is so basic, the
// response can be formatted in any way. Because of this, WhoisQuery has to
// simply return the entire response to the caller - and is unable to
// marshall/etc the response in any meaningful format.
func WhoisQuery(query, server string, timeout time.Duration) (string, error) {
	host := net.JoinHostPort(server, "43")
	conn, err := net.DialTimeout("tcp", host, timeout)
	if err != nil {
		return "", err
	}
	defer conn.Close()

	// Query the whois server with the ip or domain given to this func,
	// as per Whois spec.
	_, err = conn.Write([]byte(fmt.Sprintf("%s\r\n", query)))
	if err != nil {
		return "", err
	}

	// After the query, the server will respond with the unformatted data.
	// Read it all and return it.
	b, err := ioutil.ReadAll(conn)
	if err != nil {
		return "", err
	}

	return string(b), nil
}
<|endoftext|>


<commit_before>package web

import (
	"regexp"
	"strings"

	"github.com/labstack/echo"
	emw "github.com/labstack/echo/middleware"
	"github.com/spf13/viper"
)

type (
	AuthedContextLookup interface {
		Lookup(echo.Context) (echo.Context, error)
	}
	// AuthedConfig config for Authed middleware.
	AuthedConfig struct {
		// Skipper defines a function to skip middleware.
		Skipper emw.Skipper
	}
)

var (
	// DefaultAuthedConfig default Authed middleware config.
	DefaultAuthedConfig = AuthedConfig{
		Skipper: AuthedSkipper(),
	}
)

type authSkipperConfig map[string]*regexp.Regexp

func AuthedSkipper() func(echo.Context) bool {
	config := viper.GetStringMapString("skipjwt")

	if config == nil || len(config) == 0 {
		return emw.DefaultSkipper
	}

	skipper := authSkipperConfig{}
	for method, exp := range config {
		skipper[strings.ToUpper(method)] = regexp.MustCompile(exp)
	}

	return func(c echo.Context) bool {
		if c.Request().Method == echo.OPTIONS {
			return true
		}
		re, ok := skipper[c.Request().Method]
		if !ok {
			return false
		}

		return re.MatchString(c.Request().URL.Path)
	}
}

// AuthedWithConfig ...
func AuthedWithConfig(config AuthedConfig, cl AuthedContextLookup) echo.MiddlewareFunc {
	if config.Skipper == nil {
		config.Skipper = DefaultAuthedConfig.Skipper
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			if config.Skipper(c) {
				return next(c)
			}
			ac, err := cl.Lookup(c)
			if err != nil {
				return err
			}

			return next(ac)
		}
	}
}
<commit_msg>Allow for routes to be auth and anon<commit_after>package web

import (
	"regexp"
	"strings"

	"github.com/labstack/echo"
	emw "github.com/labstack/echo/middleware"
	"github.com/spf13/viper"
)

type (
	AuthedContextLookup interface {
		Lookup(echo.Context) (echo.Context, error)
	}
	// AuthedConfig config for Authed middleware.
	AuthedConfig struct {
		// Skipper defines a function to skip middleware.
		Skipper emw.Skipper
	}
)

var (
	// DefaultAuthedConfig default Authed middleware config.
	DefaultAuthedConfig = AuthedConfig{
		Skipper: AuthedSkipper(),
	}
)

type authSkipperConfig map[string]*regexp.Regexp

func AuthedSkipper() func(echo.Context) bool {
	config := viper.GetStringMapString("skipjwt")

	if config == nil || len(config) == 0 {
		return emw.DefaultSkipper
	}

	skipper := authSkipperConfig{}
	for method, exp := range config {
		skipper[strings.ToUpper(method)] = regexp.MustCompile(exp)
	}

	return func(c echo.Context) bool {
		if c.Request().Method == echo.OPTIONS {
			return true
		}
		re, ok := skipper[c.Request().Method]
		if !ok {
			return false
		}

		if hasAuthHeader(c) {
			return false
		}

		return re.MatchString(c.Request().URL.Path)
	}
}

// AuthedWithConfig ...
func AuthedWithConfig(config AuthedConfig, cl AuthedContextLookup) echo.MiddlewareFunc {
	if config.Skipper == nil {
		config.Skipper = DefaultAuthedConfig.Skipper
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			if config.Skipper(c) {
				return next(c)
			}
			ac, err := cl.Lookup(c)
			if err != nil {
				return err
			}

			return next(ac)
		}
	}
}

func hasAuthHeader(c echo.Context) bool {
	return c.Request().Header.Get("authorization") != ""
}<|endoftext|>


<commit_before>package bitrise

import (
	"errors"
	"fmt"
	"runtime"

	"github.com/bitrise-io/bitrise/configs"
	"github.com/bitrise-io/bitrise/plugins"
	"github.com/bitrise-io/bitrise/toolkits"
	"github.com/bitrise-io/bitrise/version"
	"github.com/bitrise-io/go-utils/colorstring"
	"github.com/bitrise-io/go-utils/log"
)

const (
	minEnvmanVersion  = "1.1.8"
	minStepmanVersion = "0.9.35"
)

// PluginDependency ..
type PluginDependency struct {
	Source     string
	MinVersion string
}

// PluginDependencyMap ...
var PluginDependencyMap = map[string]PluginDependency{
	"init": PluginDependency{
		Source:     "https://github.com/bitrise-core/bitrise-plugins-init.git",
		MinVersion: "0.9.11",
	},
	"step": PluginDependency{
		Source:     "https://github.com/bitrise-core/bitrise-plugins-step.git",
		MinVersion: "0.9.5",
	},
	"workflow-editor": PluginDependency{
		Source:     "https://github.com/bitrise-io/bitrise-workflow-editor.git",
		MinVersion: "1.0.19",
	},
	"analytics": PluginDependency{
		Source:     "https://github.com/bitrise-core/bitrise-plugins-analytics.git",
		MinVersion: "0.9.10",
	},
}

// RunSetupIfNeeded ...
func RunSetupIfNeeded(appVersion string, isFullSetupMode bool) error {
	if !configs.CheckIsSetupWasDoneForVersion(version.VERSION) {
		log.Warnf(colorstring.Yellow("Setup was not performed for this version of bitrise, doing it now..."))
		return RunSetup(version.VERSION, false, false)
	}
	return nil
}

// RunSetup ...
func RunSetup(appVersion string, isFullSetupMode bool, isCleanSetupMode bool) error {
	log.Infof("Setup")
	log.Printf("Full setup: %v", isFullSetupMode)
	log.Printf("Clean setup: %v", isCleanSetupMode)
	log.Printf("Detected OS: %s", runtime.GOOS)

	if isCleanSetupMode {
		if err := configs.DeleteBitriseConfigDir(); err != nil {
			return err
		}

		if err := configs.InitPaths(); err != nil {
			return err
		}

		if err := plugins.InitPaths(); err != nil {
			return err
		}
	}

	if err := doSetupBitriseCoreTools(); err != nil {
		return fmt.Errorf("Failed to do common/platform independent setup, error: %s", err)
	}

	switch runtime.GOOS {
	case "darwin":
		if err := doSetupOnOSX(isFullSetupMode); err != nil {
			return fmt.Errorf("Failed to do MacOS specific setup, error: %s", err)
		}
	case "linux":
	default:
		return errors.New("unsupported platform :(")
	}

	if err := doSetupPlugins(); err != nil {
		return fmt.Errorf("Failed to do Plugins setup, error: %s", err)
	}

	if err := doSetupToolkits(); err != nil {
		return fmt.Errorf("Failed to do Toolkits setup, error: %s", err)
	}

	fmt.Println()
	log.Donef("All the required tools are installed! We're ready to rock!!")

	if err := configs.SaveSetupSuccessForVersion(appVersion); err != nil {
		return fmt.Errorf("failed to save setup-success into config file, error: %s", err)
	}

	return nil
}

func doSetupToolkits() error {
	fmt.Println()
	log.Infof("Checking Bitrise Toolkits...")

	coreToolkits := toolkits.AllSupportedToolkits()

	for _, aCoreTK := range coreToolkits {
		toolkitName := aCoreTK.ToolkitName()
		isInstallRequired, checkResult, err := aCoreTK.Check()
		if err != nil {
			return fmt.Errorf("Failed to perform toolkit check (%s), error: %s", toolkitName, err)
		}

		if isInstallRequired {
			log.Warnf("No installed/suitable %s found, installing toolkit ...", toolkitName)
			if err := aCoreTK.Install(); err != nil {
				return fmt.Errorf("Failed to install toolkit (%s), error: %s", toolkitName, err)
			}

			isInstallRequired, checkResult, err = aCoreTK.Check()
			if err != nil {
				return fmt.Errorf("Failed to perform toolkit check (%s), error: %s", toolkitName, err)
			}
		}
		if isInstallRequired {
			return fmt.Errorf("Toolkit (%s) still reports that it isn't (properly) installed", toolkitName)
		}

		log.Printf("%s %s (%s): %s", colorstring.Green("[OK]"), toolkitName, checkResult.Version, checkResult.Path)
	}

	return nil
}

func doSetupPlugins() error {
	fmt.Println()
	log.Infof("Checking Bitrise Plugins...")

	for pluginName, pluginDependency := range PluginDependencyMap {
		if err := CheckIsPluginInstalled(pluginName, pluginDependency); err != nil {
			return fmt.Errorf("Plugin (%s) failed to install: %s", pluginName, err)
		}
	}

	return nil
}

func doSetupBitriseCoreTools() error {
	fmt.Println()
	log.Infof("Checking Bitrise Core tools...")

	if err := CheckIsEnvmanInstalled(minEnvmanVersion); err != nil {
		return fmt.Errorf("Envman failed to install: %s", err)
	}

	if err := CheckIsStepmanInstalled(minStepmanVersion); err != nil {
		return fmt.Errorf("Stepman failed to install: %s", err)
	}

	return nil
}

func doSetupOnOSX(isMinimalSetupMode bool) error {
	fmt.Println()
	log.Infof("Doing OS X specific setup")
	log.Printf("Checking required tools...")

	if err := CheckIsHomebrewInstalled(isMinimalSetupMode); err != nil {
		return errors.New(fmt.Sprint("Homebrew not installed or has some issues. Please fix these before calling setup again. Err:", err))
	}

	if err := PrintInstalledXcodeInfos(); err != nil {
		return errors.New(fmt.Sprint("Failed to detect installed Xcode and Xcode Command Line Tools infos. Err:", err))
	}
	return nil
}
<commit_msg>update bitrise tools versions (#562)<commit_after>package bitrise

import (
	"errors"
	"fmt"
	"runtime"

	"github.com/bitrise-io/bitrise/configs"
	"github.com/bitrise-io/bitrise/plugins"
	"github.com/bitrise-io/bitrise/toolkits"
	"github.com/bitrise-io/bitrise/version"
	"github.com/bitrise-io/go-utils/colorstring"
	"github.com/bitrise-io/go-utils/log"
)

const (
	minEnvmanVersion  = "1.1.8"
	minStepmanVersion = "0.9.36"
)

// PluginDependency ..
type PluginDependency struct {
	Source     string
	MinVersion string
}

// PluginDependencyMap ...
var PluginDependencyMap = map[string]PluginDependency{
	"init": PluginDependency{
		Source:     "https://github.com/bitrise-core/bitrise-plugins-init.git",
		MinVersion: "1.0.0",
	},
	"step": PluginDependency{
		Source:     "https://github.com/bitrise-core/bitrise-plugins-step.git",
		MinVersion: "0.9.5",
	},
	"workflow-editor": PluginDependency{
		Source:     "https://github.com/bitrise-io/bitrise-workflow-editor.git",
		MinVersion: "1.1.0",
	},
	"analytics": PluginDependency{
		Source:     "https://github.com/bitrise-core/bitrise-plugins-analytics.git",
		MinVersion: "0.9.10",
	},
}

// RunSetupIfNeeded ...
func RunSetupIfNeeded(appVersion string, isFullSetupMode bool) error {
	if !configs.CheckIsSetupWasDoneForVersion(version.VERSION) {
		log.Warnf(colorstring.Yellow("Setup was not performed for this version of bitrise, doing it now..."))
		return RunSetup(version.VERSION, false, false)
	}
	return nil
}

// RunSetup ...
func RunSetup(appVersion string, isFullSetupMode bool, isCleanSetupMode bool) error {
	log.Infof("Setup")
	log.Printf("Full setup: %v", isFullSetupMode)
	log.Printf("Clean setup: %v", isCleanSetupMode)
	log.Printf("Detected OS: %s", runtime.GOOS)

	if isCleanSetupMode {
		if err := configs.DeleteBitriseConfigDir(); err != nil {
			return err
		}

		if err := configs.InitPaths(); err != nil {
			return err
		}

		if err := plugins.InitPaths(); err != nil {
			return err
		}
	}

	if err := doSetupBitriseCoreTools(); err != nil {
		return fmt.Errorf("Failed to do common/platform independent setup, error: %s", err)
	}

	switch runtime.GOOS {
	case "darwin":
		if err := doSetupOnOSX(isFullSetupMode); err != nil {
			return fmt.Errorf("Failed to do MacOS specific setup, error: %s", err)
		}
	case "linux":
	default:
		return errors.New("unsupported platform :(")
	}

	if err := doSetupPlugins(); err != nil {
		return fmt.Errorf("Failed to do Plugins setup, error: %s", err)
	}

	if err := doSetupToolkits(); err != nil {
		return fmt.Errorf("Failed to do Toolkits setup, error: %s", err)
	}

	fmt.Println()
	log.Donef("All the required tools are installed! We're ready to rock!!")

	if err := configs.SaveSetupSuccessForVersion(appVersion); err != nil {
		return fmt.Errorf("failed to save setup-success into config file, error: %s", err)
	}

	return nil
}

func doSetupToolkits() error {
	fmt.Println()
	log.Infof("Checking Bitrise Toolkits...")

	coreToolkits := toolkits.AllSupportedToolkits()

	for _, aCoreTK := range coreToolkits {
		toolkitName := aCoreTK.ToolkitName()
		isInstallRequired, checkResult, err := aCoreTK.Check()
		if err != nil {
			return fmt.Errorf("Failed to perform toolkit check (%s), error: %s", toolkitName, err)
		}

		if isInstallRequired {
			log.Warnf("No installed/suitable %s found, installing toolkit ...", toolkitName)
			if err := aCoreTK.Install(); err != nil {
				return fmt.Errorf("Failed to install toolkit (%s), error: %s", toolkitName, err)
			}

			isInstallRequired, checkResult, err = aCoreTK.Check()
			if err != nil {
				return fmt.Errorf("Failed to perform toolkit check (%s), error: %s", toolkitName, err)
			}
		}
		if isInstallRequired {
			return fmt.Errorf("Toolkit (%s) still reports that it isn't (properly) installed", toolkitName)
		}

		log.Printf("%s %s (%s): %s", colorstring.Green("[OK]"), toolkitName, checkResult.Version, checkResult.Path)
	}

	return nil
}

func doSetupPlugins() error {
	fmt.Println()
	log.Infof("Checking Bitrise Plugins...")

	for pluginName, pluginDependency := range PluginDependencyMap {
		if err := CheckIsPluginInstalled(pluginName, pluginDependency); err != nil {
			return fmt.Errorf("Plugin (%s) failed to install: %s", pluginName, err)
		}
	}

	return nil
}

func doSetupBitriseCoreTools() error {
	fmt.Println()
	log.Infof("Checking Bitrise Core tools...")

	if err := CheckIsEnvmanInstalled(minEnvmanVersion); err != nil {
		return fmt.Errorf("Envman failed to install: %s", err)
	}

	if err := CheckIsStepmanInstalled(minStepmanVersion); err != nil {
		return fmt.Errorf("Stepman failed to install: %s", err)
	}

	return nil
}

func doSetupOnOSX(isMinimalSetupMode bool) error {
	fmt.Println()
	log.Infof("Doing OS X specific setup")
	log.Printf("Checking required tools...")

	if err := CheckIsHomebrewInstalled(isMinimalSetupMode); err != nil {
		return errors.New(fmt.Sprint("Homebrew not installed or has some issues. Please fix these before calling setup again. Err:", err))
	}

	if err := PrintInstalledXcodeInfos(); err != nil {
		return errors.New(fmt.Sprint("Failed to detect installed Xcode and Xcode Command Line Tools infos. Err:", err))
	}
	return nil
}
<|endoftext|>


<commit_before>package aoe

import (
	"net"
	"syscall"
	"time"

	"github.com/coreos/torus/block"

	"github.com/coreos/pkg/capnslog"
	"github.com/mdlayher/aoe"
	"github.com/mdlayher/raw"
)

var (
	clog          = capnslog.NewPackageLogger("github.com/coreos/torus", "aoe")
	broadcastAddr = net.HardwareAddr([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
)

type Server struct {
	dfs *block.BlockVolume

	dev Device

	major uint16
	minor uint8
}

// ServerOptions specifies options for a Server.
type ServerOptions struct {
	// Major and Minor specify the major and minor address of an AoE server.
	// Typically, all AoE devices on a single server will share the same
	// major address, but have different minor addresses.
	//
	// It is important to note that all AoE servers on the same layer 2
	// network must have different major and minor addresses.
	Major uint16
	Minor uint8
}

// DefaultServerOptions is the default ServerOptions configuration used
// by NewServer when none is specified.
var DefaultServerOptions = &ServerOptions{
	Major: 1,
	Minor: 1,
}

// NewServer creates a new Server which utilizes the specified block volume.
// If options is nil, DefaultServerOptions will be used.
func NewServer(b *block.BlockVolume, options *ServerOptions) (*Server, error) {
	if options == nil {
		options = DefaultServerOptions
	}

	f, err := b.OpenBlockFile()
	if err != nil {
		return nil, err
	}

	f.Sync()

	fd := &FileDevice{f}

	as := &Server{
		dfs:   b,
		dev:   fd,
		major: options.Major,
		minor: options.Minor,
	}

	return as, nil
}

func (s *Server) advertise(iface *Interface) error {
	// little hack to trigger a broadcast
	from := &raw.Addr{
		HardwareAddr: broadcastAddr,
	}

	fr := &Frame{
		Header: aoe.Header{
			Command: aoe.CommandQueryConfigInformation,
			Arg: &aoe.ConfigArg{
				Command: aoe.ConfigCommandRead,
			},
		},
	}

	_, err := s.handleFrame(from, iface, fr)
	return err
}

func (s *Server) Serve(iface *Interface) error {
	clog.Tracef("beginning server loop on %+v", iface)

	// cheap sync proc, should stop when server is shut off
	go func() {
		for {
			if err := s.dev.Sync(); err != nil {
				clog.Warningf("failed to sync %s: %v", s.dev, err)
			}

			time.Sleep(5 * time.Second)
		}
	}()

	// broadcast ourselves
	if err := s.advertise(iface); err != nil {
		clog.Errorf("advertisement failed: %v", err)
		return err
	}

	for {
		payload := make([]byte, iface.MTU)
		n, addr, err := iface.ReadFrom(payload)
		if err != nil {
			clog.Errorf("ReadFrom failed: %v", err)
			// will be syscall.EBADF if the conn from raw closed
			if err == syscall.EBADF {
				break
			}
			return err
		}

		// resize payload
		payload = payload[:n]

		var f Frame
		if err := f.UnmarshalBinary(payload); err != nil {
			clog.Errorf("Failed to unmarshal frame: %v", err)
			continue
		}

		clog.Debugf("recv %d %s %+v", n, addr, f.Header)
		//clog.Debugf("recv arg %+v", f.Header.Arg)

		s.handleFrame(addr, iface, &f)
	}

	return nil
}

func (s *Server) handleFrame(from net.Addr, iface *Interface, f *Frame) (int, error) {
	hdr := &f.Header

	// Ignore client requests that are not being broadcast or sent to
	// our major/minor address combination.
	if hdr.Major != aoe.BroadcastMajor && hdr.Major != s.major {
		clog.Debugf("ignoring header with major address %d", hdr.Major)
		return 0, nil
	}
	if hdr.Minor != aoe.BroadcastMinor && hdr.Minor != s.minor {
		clog.Debugf("ignoring header with minor address %d", hdr.Minor)
		return 0, nil
	}

	sender := &FrameSender{
		orig:  f,
		dst:   from.(*raw.Addr).HardwareAddr,
		src:   iface.HardwareAddr,
		conn:  iface.PacketConn,
		major: s.major,
		minor: s.minor,
	}

	switch hdr.Command {
	case aoe.CommandIssueATACommand:
		n, err := aoe.ServeATA(sender, hdr, s.dev)
		if err != nil {
			clog.Errorf("ServeATA failed: %v", err)
			switch err {
			case aoe.ErrInvalidATARequest:
				return sender.SendError(aoe.ErrorBadArgumentParameter)
			default:
				return sender.SendError(aoe.ErrorDeviceUnavailable)
			}
		}

		return n, nil
	case aoe.CommandQueryConfigInformation:
		cfgarg := f.Header.Arg.(*aoe.ConfigArg)
		clog.Debugf("cfgarg: %+v", cfgarg)

		switch cfgarg.Command {
		case aoe.ConfigCommandRead:
			hdr.Arg = &aoe.ConfigArg{
				// if < 2, linux aoe handles it poorly
				BufferCount:     2,
				FirmwareVersion: 0,
				// naive, but works.
				SectorCount:  uint8(iface.MTU / 512),
				Version:      1,
				Command:      aoe.ConfigCommandRead,
				StringLength: 0,
				String:       []byte{},
			}

			return sender.Send(hdr)
		}

		return sender.SendError(aoe.ErrorUnrecognizedCommandCode)
	case aoe.CommandMACMaskList:
		fallthrough
	case aoe.CommandReserveRelease:
		fallthrough
	default:
		return sender.SendError(aoe.ErrorUnrecognizedCommandCode)
	}
}

func (s *Server) Close() error {
	return s.dev.Close()
}
<commit_msg>block/aoe: use BPF to filter requests for other servers<commit_after>package aoe

import (
	"fmt"
	"net"
	"syscall"
	"time"

	"github.com/coreos/torus/block"

	"github.com/coreos/pkg/capnslog"
	"github.com/mdlayher/aoe"
	"github.com/mdlayher/raw"
	"golang.org/x/net/bpf"
)

var (
	clog          = capnslog.NewPackageLogger("github.com/coreos/torus", "aoe")
	broadcastAddr = net.HardwareAddr([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
)

type Server struct {
	dfs *block.BlockVolume

	dev Device

	major uint16
	minor uint8
}

// ServerOptions specifies options for a Server.
type ServerOptions struct {
	// Major and Minor specify the major and minor address of an AoE server.
	// Typically, all AoE devices on a single server will share the same
	// major address, but have different minor addresses.
	//
	// It is important to note that all AoE servers on the same layer 2
	// network must have different major and minor addresses.
	Major uint16
	Minor uint8
}

// DefaultServerOptions is the default ServerOptions configuration used
// by NewServer when none is specified.
var DefaultServerOptions = &ServerOptions{
	Major: 1,
	Minor: 1,
}

// NewServer creates a new Server which utilizes the specified block volume.
// If options is nil, DefaultServerOptions will be used.
func NewServer(b *block.BlockVolume, options *ServerOptions) (*Server, error) {
	if options == nil {
		options = DefaultServerOptions
	}

	f, err := b.OpenBlockFile()
	if err != nil {
		return nil, err
	}

	f.Sync()

	fd := &FileDevice{f}

	as := &Server{
		dfs:   b,
		dev:   fd,
		major: options.Major,
		minor: options.Minor,
	}

	return as, nil
}

func (s *Server) advertise(iface *Interface) error {
	// little hack to trigger a broadcast
	from := &raw.Addr{
		HardwareAddr: broadcastAddr,
	}

	fr := &Frame{
		Header: aoe.Header{
			Command: aoe.CommandQueryConfigInformation,
			Arg: &aoe.ConfigArg{
				Command: aoe.ConfigCommandRead,
			},
		},
	}

	_, err := s.handleFrame(from, iface, fr)
	return err
}

func (s *Server) Serve(iface *Interface) error {
	err := raw.AttachBPF(
		iface.PacketConn,
		s.mustAssembleBPF(iface.MTU),
	)
	if err != nil {
		return err
	}

	clog.Tracef("beginning server loop on %+v", iface)

	// cheap sync proc, should stop when server is shut off
	go func() {
		for {
			if err := s.dev.Sync(); err != nil {
				clog.Warningf("failed to sync %s: %v", s.dev, err)
			}

			time.Sleep(5 * time.Second)
		}
	}()

	// broadcast ourselves
	if err := s.advertise(iface); err != nil {
		clog.Errorf("advertisement failed: %v", err)
		return err
	}

	for {
		payload := make([]byte, iface.MTU)
		n, addr, err := iface.ReadFrom(payload)
		if err != nil {
			clog.Errorf("ReadFrom failed: %v", err)
			// will be syscall.EBADF if the conn from raw closed
			if err == syscall.EBADF {
				break
			}
			return err
		}

		// resize payload
		payload = payload[:n]

		var f Frame
		if err := f.UnmarshalBinary(payload); err != nil {
			clog.Errorf("Failed to unmarshal frame: %v", err)
			continue
		}

		clog.Debugf("recv %d %s %+v", n, addr, f.Header)
		//clog.Debugf("recv arg %+v", f.Header.Arg)

		s.handleFrame(addr, iface, &f)
	}

	return nil
}

func (s *Server) handleFrame(from net.Addr, iface *Interface, f *Frame) (int, error) {
	hdr := &f.Header

	sender := &FrameSender{
		orig:  f,
		dst:   from.(*raw.Addr).HardwareAddr,
		src:   iface.HardwareAddr,
		conn:  iface.PacketConn,
		major: s.major,
		minor: s.minor,
	}

	switch hdr.Command {
	case aoe.CommandIssueATACommand:
		n, err := aoe.ServeATA(sender, hdr, s.dev)
		if err != nil {
			clog.Errorf("ServeATA failed: %v", err)
			switch err {
			case aoe.ErrInvalidATARequest:
				return sender.SendError(aoe.ErrorBadArgumentParameter)
			default:
				return sender.SendError(aoe.ErrorDeviceUnavailable)
			}
		}

		return n, nil
	case aoe.CommandQueryConfigInformation:
		cfgarg := f.Header.Arg.(*aoe.ConfigArg)
		clog.Debugf("cfgarg: %+v", cfgarg)

		switch cfgarg.Command {
		case aoe.ConfigCommandRead:
			hdr.Arg = &aoe.ConfigArg{
				// if < 2, linux aoe handles it poorly
				BufferCount:     2,
				FirmwareVersion: 0,
				// naive, but works.
				SectorCount:  uint8(iface.MTU / 512),
				Version:      1,
				Command:      aoe.ConfigCommandRead,
				StringLength: 0,
				String:       []byte{},
			}

			return sender.Send(hdr)
		}

		return sender.SendError(aoe.ErrorUnrecognizedCommandCode)
	case aoe.CommandMACMaskList:
		fallthrough
	case aoe.CommandReserveRelease:
		fallthrough
	default:
		return sender.SendError(aoe.ErrorUnrecognizedCommandCode)
	}
}

func (s *Server) Close() error {
	return s.dev.Close()
}

// mustAssembleBPF assembles a BPF program to filter out packets not bound
// for this server.
func (s *Server) mustAssembleBPF(mtu int) []bpf.RawInstruction {
	// This BPF program filters out packets that are not bound for this server
	// by checking against both the AoE broadcast addresses and this server's
	// major/minor address combination.  The structure of the incoming ethernet
	// frame and AoE header is as follows:
	//
	// Offset | Length | Comment
	// -------------------------
	//   00   |   06   | Ethernet destination MAC address
	//   06   |   06   | Ethernet source MAC address
	//   12   |   02   | Ethernet EtherType
	// -------------------------
	//   14   |   01   | AoE version + flags
	//   15   |   01   | AoE error
	//   16   |   02   | AoE major address
	//   18   |   01   | AoE minor address
	//
	// Thus, our BPF program needs to check for:
	//  - major address: offset 16, length 2
	//  - minor address: offset 18, length 1
	const (
		majorOffset = 16
		majorLen    = 2
		minorOffset = 18
		minorLen    = 1
	)

	// TODO(mdlayher): this routine likely belongs in package AoE, once the server
	// component is more complete.

	prog, err := bpf.Assemble([]bpf.Instruction{
		// Load major address value from AoE header
		bpf.LoadAbsolute{
			Off:  majorOffset,
			Size: majorLen,
		},
		// If major address is equal to broadcast address, jump to minor address
		// filtering
		bpf.JumpIf{
			Cond:     bpf.JumpEqual,
			Val:      uint32(aoe.BroadcastMajor),
			SkipTrue: 2,
		},
		// If major address is equal to our server's, jump to minor address
		// filtering
		bpf.JumpIf{
			Cond:     bpf.JumpEqual,
			Val:      uint32(s.major),
			SkipTrue: 1,
		},
		// Major address is not our server's or broadcast address
		bpf.RetConstant{
			Val: 0,
		},
		// Load minor address value from AoE header
		bpf.LoadAbsolute{
			Off:  minorOffset,
			Size: minorLen,
		},
		// If minor address is equal to broadcast address, jump to accept packet
		bpf.JumpIf{
			Cond:     bpf.JumpEqual,
			Val:      uint32(aoe.BroadcastMinor),
			SkipTrue: 2,
		},
		// If minor address is equal to our server's, jump to accept packet
		bpf.JumpIf{
			Cond:     bpf.JumpEqual,
			Val:      uint32(s.minor),
			SkipTrue: 1,
		},
		// Minor address is not our server's or broadcast address
		bpf.RetConstant{
			Val: 0,
		},
		// Accept the packet bytes up to the interface's MTU
		bpf.RetConstant{
			Val: uint32(mtu),
		},
	})
	if err != nil {
		panic(fmt.Sprintf("failed to assemble BPF program: %v", err))
	}

	return prog
}
<|endoftext|>


<commit_before>package blockset

import (
	"bytes"
	"encoding/binary"
	"sync/atomic"

	"github.com/barakmich/agro"
)

type basicBlockset struct {
	ids    int64
	blocks []agro.BlockID
	store  agro.BlockStore
}

var _ blockset = &basicBlockset{}

func newBasicBlockset(store agro.BlockStore) *basicBlockset {
	b := &basicBlockset{
		blocks: make([]agro.BlockID, 0),
		store:  store,
	}
	return b
}

func (b *basicBlockset) Length() int {
	return len(b.blocks)
}

func (b *basicBlockset) GetBlock(i int) ([]byte, error) {
	if i >= len(b.blocks) {
		return nil, agro.ErrBlockNotExist
	}
	return b.store.GetBlock(b.blocks[i])
}

func (b *basicBlockset) PutBlock(inode agro.INodeRef, i int, data []byte) error {
	if i > len(b.blocks) {
		return agro.ErrBlockNotExist
	}
	newBlockID := b.makeID(inode)
	err := b.store.WriteBlock(newBlockID, data)
	if err != nil {
		return err
	}
	if i == len(b.blocks) {
		b.blocks = append(b.blocks, newBlockID)
	} else {
		b.blocks[i] = newBlockID
	}
	return nil
}

func (b *basicBlockset) makeID(i agro.INodeRef) agro.BlockID {
	id := atomic.AddInt64(&b.ids, 2)
	return agro.BlockID{
		INodeRef: i,
		Index:    id,
	}
}

func (b *basicBlockset) Marshal() ([]byte, error) {
	buf := new(bytes.Buffer)
	for _, x := range b.blocks {
		err := binary.Write(buf, binary.LittleEndian, x)
		if err != nil {
			return nil, err
		}
	}
	return buf.Bytes(), nil
}

func (b *basicBlockset) setStore(s agro.BlockStore) {
	b.store = s
}

func (b *basicBlockset) Unmarshal(data []byte) error {
	r := bytes.NewReader(data)
	var out []agro.BlockID
	err := binary.Read(r, binary.LittleEndian, &out)
	if err != nil {
		return err
	}
	b.blocks = out
	return nil
}
<commit_msg>add tests<commit_after>package blockset

import (
	"bytes"
	"encoding/binary"
	"sync/atomic"

	"github.com/barakmich/agro"
)

type basicBlockset struct {
	ids    int64
	blocks []agro.BlockID
	store  agro.BlockStore
}

var _ blockset = &basicBlockset{}

func newBasicBlockset(store agro.BlockStore) *basicBlockset {
	b := &basicBlockset{
		blocks: make([]agro.BlockID, 0),
		store:  store,
	}
	return b
}

func (b *basicBlockset) Length() int {
	return len(b.blocks)
}

func (b *basicBlockset) GetBlock(i int) ([]byte, error) {
	if i >= len(b.blocks) {
		return nil, agro.ErrBlockNotExist
	}
	return b.store.GetBlock(b.blocks[i])
}

func (b *basicBlockset) PutBlock(inode agro.INodeRef, i int, data []byte) error {
	if i > len(b.blocks) {
		return agro.ErrBlockNotExist
	}
	newBlockID := b.makeID(inode)
	err := b.store.WriteBlock(newBlockID, data)
	if err != nil {
		return err
	}
	if i == len(b.blocks) {
		b.blocks = append(b.blocks, newBlockID)
	} else {
		b.blocks[i] = newBlockID
	}
	return nil
}

func (b *basicBlockset) makeID(i agro.INodeRef) agro.BlockID {
	id := atomic.AddInt64(&b.ids, 2)
	return agro.BlockID{
		INodeRef: i,
		Index:    id,
	}
}

func (b *basicBlockset) Marshal() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.LittleEndian, int32(len(b.blocks)))
	if err != nil {
		return nil, err
	}
	for _, x := range b.blocks {
		err := binary.Write(buf, binary.LittleEndian, x)
		if err != nil {
			return nil, err
		}
	}
	return buf.Bytes(), nil
}

func (b *basicBlockset) setStore(s agro.BlockStore) {
	b.store = s
}

func (b *basicBlockset) Unmarshal(data []byte) error {
	r := bytes.NewReader(data)
	var l int32
	err := binary.Read(r, binary.LittleEndian, &l)
	if err != nil {
		return err
	}
	out := make([]agro.BlockID, l)
	err = binary.Read(r, binary.LittleEndian, &out)
	if err != nil {
		return err
	}
	b.blocks = out
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"
)

type LibratoMetric struct {
	Name   string      `json:"name"`
	Value  interface{} `json:"value"`
	When   int64       `json:"measure_time"`
	Source string      `json:"source,omitempty"`
}

type PostBody struct {
	Gauges   []LibratoMetric `json:"gauges,omitempty"`
	Counters []LibratoMetric `json:"counters,omitempty"`
}

const (
	LibratoBacklog               = 8 // No more than N pending batches in-flight
	LibratoMaxAttempts           = 4 // Max attempts before dropping batch
	LibratoStartingBackoffMillis = 200 * time.Millisecond
)

type Librato struct {
	Timeout      time.Duration
	BatchSize    int
	User         string
	Token        string
	Url          string
	measurements <-chan Measurement
	batches      chan []Measurement
	prefix       string
	source       string
	client       *http.Client
}

func NewLibratoOutputter(measurements <-chan Measurement, config Config) *Librato {
	return &Librato{
		measurements: measurements,
		source:       config.Source,
		batches:      make(chan []Measurement, LibratoBacklog),
		Timeout:      config.LibratoBatchTimeout,
		BatchSize:    config.LibratoBatchSize,
		User:         config.LibratoUser,
		Token:        config.LibratoToken,
		Url:          config.LibratoUrl,
		client: &http.Client{
			Transport: &http.Transport{
				ResponseHeaderTimeout: config.LibratoNetworkTimeout,
				Dial: func(network, address string) (net.Conn, error) {
					return net.DialTimeout(network, address, config.LibratoNetworkTimeout)
				},
			},
		},
	}
}

func (out *Librato) Start() {
	go out.deliver()
	go out.batch()
}

func (out *Librato) makeBatch() []Measurement {
	return make([]Measurement, 0, out.BatchSize)
}

func (out *Librato) batch() {
	var ready bool
	ctx := Slog{"fn": "batch", "outputter": "librato"}
	ticker := time.Tick(out.Timeout)
	batch := out.makeBatch()
	for {
		select {
		case measurement := <-out.measurements:
			batch = append(batch, measurement)
			if len(batch) == cap(batch) {
				ready = true
			}
		case <-ticker:
			if len(batch) > 0 {
				ready = true
			}
		}

		if ready {
			select {
			case out.batches <- batch:
			default:
				ctx.Error(nil, "Batches backlogged, dropping")
			}
			batch = out.makeBatch()
			ready = false
		}
	}
}

func (out *Librato) deliver() {
	ctx := Slog{"fn": "prepare", "outputter": "librato"}
	for batch := range out.batches {
		gauges := make([]LibratoMetric, 0)
		counters := make([]LibratoMetric, 0)
		for _, mm := range batch {
			libratoMetric := LibratoMetric{mm.Name(out.prefix), mm.Value(), mm.Time().Unix(), out.source}
			switch mm.Type() {
			case CounterType:
				counters = append(counters, libratoMetric)
			case GaugeType, FloatGaugeType:
				gauges = append(gauges, libratoMetric)
			}
		}

		payload := PostBody{gauges, counters}
		j, err := json.Marshal(payload)
		if err != nil {
			ctx.FatalError(err, "marshaling json")
		}

		out.sendWithBackoff(j)
	}
}

func (out *Librato) sendWithBackoff(payload []byte) bool {
	ctx := Slog{"fn": "retry", "outputter": "librato"}
	attempts := 0
	bo := 0 * time.Millisecond

	for attempts < LibratoMaxAttempts {
		if retry, err := out.send(ctx, payload); retry {
			ctx["backoff"] = bo
			ctx["message"] = err
			fmt.Println(ctx)
			bo = backoff(bo)
		} else if err != nil {
			ctx["error"] = err
			fmt.Println(ctx)
			return false
		} else {
			return true
		}

		resetCtx(ctx)
		attempts++
	}
	return false
}

func (out *Librato) send(ctx Slog, payload []byte) (retry bool, e error) {
	body := bytes.NewBuffer(payload)
	req, err := http.NewRequest("POST", out.Url, body)
	if err != nil {
		ctx.FatalError(err, "creating new request")
	}

	req.Header.Add("Content-Type", "application/json")
	req.SetBasicAuth(out.User, out.Token)

	resp, err := out.client.Do(req)
	if err != nil {
		if nerr, ok := err.(net.Error); ok && (nerr.Temporary() || nerr.Timeout()) {
			retry = true
			e = fmt.Errorf("Backing off due to transport error")
		} else if strings.Contains(err.Error(), "timeout awaiting response") {
			retry = false
			e = nil
		} else {
			ctx.FatalError(err, "doing request")
		}
	} else {
		defer resp.Body.Close()

		if resp.StatusCode >= 300 {
			b, _ := ioutil.ReadAll(resp.Body)
			ctx["body"] = string(b)
			ctx["code"] = resp.StatusCode

			if resp.StatusCode >= 500 {
				retry = true
				e = fmt.Errorf("Backing off due to server error")
			} else {
				e = fmt.Errorf("Client error")
			}
		}
	}

	return
}

// Sleeps `bo` and then returns double, unless 0, in which case
// returns the initial starting sleep time.
func backoff(bo time.Duration) time.Duration {
	if bo > 0 {
		time.Sleep(bo)
		return bo * 2
	} else {
		return LibratoStartingBackoffMillis
	}
}

func resetCtx(ctx Slog) {
	delete(ctx, "backoff")
	delete(ctx, "message")
	delete(ctx, "body")
	delete(ctx, "code")
}
<commit_msg>Bug: we were never setting prefix in librato outputter<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"
)

type LibratoMetric struct {
	Name   string      `json:"name"`
	Value  interface{} `json:"value"`
	When   int64       `json:"measure_time"`
	Source string      `json:"source,omitempty"`
}

type PostBody struct {
	Gauges   []LibratoMetric `json:"gauges,omitempty"`
	Counters []LibratoMetric `json:"counters,omitempty"`
}

const (
	LibratoBacklog               = 8 // No more than N pending batches in-flight
	LibratoMaxAttempts           = 4 // Max attempts before dropping batch
	LibratoStartingBackoffMillis = 200 * time.Millisecond
)

type Librato struct {
	Timeout      time.Duration
	BatchSize    int
	User         string
	Token        string
	Url          string
	measurements <-chan Measurement
	batches      chan []Measurement
	prefix       string
	source       string
	client       *http.Client
}

func NewLibratoOutputter(measurements <-chan Measurement, config Config) *Librato {
	return &Librato{
		measurements: measurements,
		prefix:       config.Prefix,
		source:       config.Source,
		batches:      make(chan []Measurement, LibratoBacklog),
		Timeout:      config.LibratoBatchTimeout,
		BatchSize:    config.LibratoBatchSize,
		User:         config.LibratoUser,
		Token:        config.LibratoToken,
		Url:          config.LibratoUrl,
		client: &http.Client{
			Transport: &http.Transport{
				ResponseHeaderTimeout: config.LibratoNetworkTimeout,
				Dial: func(network, address string) (net.Conn, error) {
					return net.DialTimeout(network, address, config.LibratoNetworkTimeout)
				},
			},
		},
	}
}

func (out *Librato) Start() {
	go out.deliver()
	go out.batch()
}

func (out *Librato) makeBatch() []Measurement {
	return make([]Measurement, 0, out.BatchSize)
}

func (out *Librato) batch() {
	var ready bool
	ctx := Slog{"fn": "batch", "outputter": "librato"}
	ticker := time.Tick(out.Timeout)
	batch := out.makeBatch()
	for {
		select {
		case measurement := <-out.measurements:
			batch = append(batch, measurement)
			if len(batch) == cap(batch) {
				ready = true
			}
		case <-ticker:
			if len(batch) > 0 {
				ready = true
			}
		}

		if ready {
			select {
			case out.batches <- batch:
			default:
				ctx.Error(nil, "Batches backlogged, dropping")
			}
			batch = out.makeBatch()
			ready = false
		}
	}
}

func (out *Librato) deliver() {
	ctx := Slog{"fn": "prepare", "outputter": "librato"}
	for batch := range out.batches {
		gauges := make([]LibratoMetric, 0)
		counters := make([]LibratoMetric, 0)
		for _, mm := range batch {
			libratoMetric := LibratoMetric{mm.Name(out.prefix), mm.Value(), mm.Time().Unix(), out.source}
			switch mm.Type() {
			case CounterType:
				counters = append(counters, libratoMetric)
			case GaugeType, FloatGaugeType:
				gauges = append(gauges, libratoMetric)
			}
		}

		payload := PostBody{gauges, counters}
		j, err := json.Marshal(payload)
		if err != nil {
			ctx.FatalError(err, "marshaling json")
		}

		out.sendWithBackoff(j)
	}
}

func (out *Librato) sendWithBackoff(payload []byte) bool {
	ctx := Slog{"fn": "retry", "outputter": "librato"}
	attempts := 0
	bo := 0 * time.Millisecond

	for attempts < LibratoMaxAttempts {
		if retry, err := out.send(ctx, payload); retry {
			ctx["backoff"] = bo
			ctx["message"] = err
			fmt.Println(ctx)
			bo = backoff(bo)
		} else if err != nil {
			ctx["error"] = err
			fmt.Println(ctx)
			return false
		} else {
			return true
		}

		resetCtx(ctx)
		attempts++
	}
	return false
}

func (out *Librato) send(ctx Slog, payload []byte) (retry bool, e error) {
	body := bytes.NewBuffer(payload)
	req, err := http.NewRequest("POST", out.Url, body)
	if err != nil {
		ctx.FatalError(err, "creating new request")
	}

	req.Header.Add("Content-Type", "application/json")
	req.SetBasicAuth(out.User, out.Token)

	resp, err := out.client.Do(req)
	if err != nil {
		if nerr, ok := err.(net.Error); ok && (nerr.Temporary() || nerr.Timeout()) {
			retry = true
			e = fmt.Errorf("Backing off due to transport error")
		} else if strings.Contains(err.Error(), "timeout awaiting response") {
			retry = false
			e = nil
		} else {
			ctx.FatalError(err, "doing request")
		}
	} else {
		defer resp.Body.Close()

		if resp.StatusCode >= 300 {
			b, _ := ioutil.ReadAll(resp.Body)
			ctx["body"] = string(b)
			ctx["code"] = resp.StatusCode

			if resp.StatusCode >= 500 {
				retry = true
				e = fmt.Errorf("Backing off due to server error")
			} else {
				e = fmt.Errorf("Client error")
			}
		}
	}

	return
}

// Sleeps `bo` and then returns double, unless 0, in which case
// returns the initial starting sleep time.
func backoff(bo time.Duration) time.Duration {
	if bo > 0 {
		time.Sleep(bo)
		return bo * 2
	} else {
		return LibratoStartingBackoffMillis
	}
}

func resetCtx(ctx Slog) {
	delete(ctx, "backoff")
	delete(ctx, "message")
	delete(ctx, "body")
	delete(ctx, "code")
}
<|endoftext|>


<commit_before>package protocol

import (
	"bytes"
	"errors"
	"io"
	"sync"
)

type chanBuf struct {
	sync.Mutex
	bytes.Buffer
	ready   chan<- *chanBuf
	closed  bool
	pending bool
	p       []byte
	// TODO: limit
}

var errBufClosed = errors.New("buffer closed")

func (cb *chanBuf) Reset() {
	cb.pending = false
	cb.Buffer.Reset()
}

func (cb *chanBuf) Write(p []byte) (int, error) {
	var send bool
	cb.Lock()

	if cb.closed {
		cb.Unlock()
		return 0, errBufClosed
	}

	n, err := cb.Buffer.Write(p)
	if n > 0 && !cb.pending {
		cb.pending = true
		send = true
	}
	cb.Unlock()

	if send {
		cb.ready <- cb
	}
	return n, err
}

func (cb *chanBuf) Close() error {
	if !cb.closed {
		cb.Lock()
		cb.closed = true
		cb.Unlock()
	}
	return nil
}

func (cb *chanBuf) writeTo(w io.Writer) (int, error) {
	return w.Write(cb.drain())
}

func (cb *chanBuf) drain() []byte {
	cb.Lock()
	if cap(cb.p) < cb.Len() {
		cb.p = make([]byte, cb.Len())
	}

	n := copy(cb.p[:cap(cb.p)], cb.Bytes())
	cb.p = cb.p[:n]
	cb.Reset()
	cb.Unlock()
	return cb.p
}
<commit_msg>chan_buf: trivial cleanup<commit_after>package protocol

import (
	"bytes"
	"errors"
	"io"
	"sync"
)

var errBufClosed = errors.New("buffer closed")

type chanBuf struct {
	sync.Mutex
	bytes.Buffer
	ready   chan<- *chanBuf
	closed  bool
	pending bool
	p       []byte
	// TODO: limit
}

func (cb *chanBuf) Reset() {
	cb.pending = false
	cb.Buffer.Reset()
}

func (cb *chanBuf) Write(p []byte) (int, error) {
	var send bool
	cb.Lock()

	if cb.closed {
		cb.Unlock()
		return 0, errBufClosed
	}

	n, err := cb.Buffer.Write(p)
	if n > 0 && !cb.pending {
		cb.pending = true
		send = true
	}
	cb.Unlock()

	if send {
		cb.ready <- cb
	}
	return n, err
}

func (cb *chanBuf) Close() error {
	if !cb.closed {
		cb.Lock()
		cb.closed = true
		cb.Unlock()
	}
	return nil
}

func (cb *chanBuf) writeTo(w io.Writer) (int, error) {
	return w.Write(cb.drain())
}

func (cb *chanBuf) drain() []byte {
	cb.Lock()
	if cap(cb.p) < cb.Len() {
		cb.p = make([]byte, cb.Len())
	}

	n := copy(cb.p[:cap(cb.p)], cb.Bytes())
	cb.p = cb.p[:n]
	cb.Reset()
	cb.Unlock()
	return cb.p
}
<|endoftext|>


<commit_before>// Copyright 2015 go-swagger maintainers
//
// 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 middleware

import (
	"mime"
	"net/http"

	"github.com/go-swagger/go-swagger/errors"
	"github.com/go-swagger/go-swagger/httpkit"
	"github.com/go-swagger/go-swagger/swag"
)

// NewValidation starts a new validation middleware
func newValidation(ctx *Context, next http.Handler) http.Handler {

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		matched, _ := ctx.RouteInfo(r)
		_, result := ctx.BindAndValidate(r, matched)

		if result != nil {
			ctx.Respond(rw, r, matched.Produces, matched, result)
			return
		}

		next.ServeHTTP(rw, r)
	})
}

type validation struct {
	context *Context
	result  []error
	request *http.Request
	route   *MatchedRoute
	bound   map[string]interface{}
}

type untypedBinder map[string]interface{}

func (ub untypedBinder) BindRequest(r *http.Request, route *MatchedRoute, consumer httpkit.Consumer) error {
	if err := route.Binder.Bind(r, route.Params, consumer, ub); err != nil {
		return err
	}
	return nil
}

// ContentType validates the content type of a request
func validateContentType(allowed []string, actual string) *errors.Validation {
	mt, _, err := mime.ParseMediaType(actual)
	if err != nil {
		return errors.InvalidContentType(actual, allowed)
	}
	if swag.ContainsStringsCI(allowed, mt) {
		return nil
	}
	return errors.InvalidContentType(actual, allowed)
}

func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *validation {
	validate := &validation{
		context: ctx,
		request: request,
		route:   route,
		bound:   make(map[string]interface{}),
	}

	validate.contentType()
	validate.responseFormat()
	if len(validate.result) == 0 {
		validate.parameters()
	}

	return validate
}

func (v *validation) parameters() {
	if result := v.route.Binder.Bind(v.request, v.route.Params, v.route.Consumer, v.bound); result != nil {
		if result.Error() == "validation failure list" {
			for _, e := range result.(*errors.Validation).Value.([]interface{}) {
				v.result = append(v.result, e.(error))
			}
			return
		}
		v.result = append(v.result, result)
	}
}

func (v *validation) contentType() {
	if httpkit.CanHaveBody(v.request.Method) {
		ct, _, err := v.context.ContentType(v.request)
		if err != nil {
			v.result = append(v.result, err)
		} else if httpkit.NeedsContentType(v.request.Method) {
			if err := validateContentType(v.route.Consumes, ct); err != nil {
				v.result = append(v.result, err)
			}
			v.route.Consumer = v.route.Consumers[ct]
		}
	}
}

func (v *validation) responseFormat() {
	if str := v.context.ResponseFormat(v.request, v.route.Produces); str == "" && httpkit.NeedsContentType(v.request.Method) {
		v.result = append(v.result, errors.InvalidResponseFormat(v.request.Header.Get(httpkit.HeaderAccept), v.route.Produces))
	}
}
<commit_msg>Allow empty content-type when allowed list is empty<commit_after>// Copyright 2015 go-swagger maintainers
//
// 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 middleware

import (
	"mime"
	"net/http"

	"github.com/go-swagger/go-swagger/errors"
	"github.com/go-swagger/go-swagger/httpkit"
	"github.com/go-swagger/go-swagger/swag"
)

// NewValidation starts a new validation middleware
func newValidation(ctx *Context, next http.Handler) http.Handler {

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		matched, _ := ctx.RouteInfo(r)
		_, result := ctx.BindAndValidate(r, matched)

		if result != nil {
			ctx.Respond(rw, r, matched.Produces, matched, result)
			return
		}

		next.ServeHTTP(rw, r)
	})
}

type validation struct {
	context *Context
	result  []error
	request *http.Request
	route   *MatchedRoute
	bound   map[string]interface{}
}

type untypedBinder map[string]interface{}

func (ub untypedBinder) BindRequest(r *http.Request, route *MatchedRoute, consumer httpkit.Consumer) error {
	if err := route.Binder.Bind(r, route.Params, consumer, ub); err != nil {
		return err
	}
	return nil
}

// ContentType validates the content type of a request
func validateContentType(allowed []string, actual string) *errors.Validation {
	if len(allowed) == 0 {
		return nil
	}
	mt, _, err := mime.ParseMediaType(actual)
	if err != nil {
		return errors.InvalidContentType(actual, allowed)
	}
	if swag.ContainsStringsCI(allowed, mt) {
		return nil
	}
	return errors.InvalidContentType(actual, allowed)
}

func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *validation {
	validate := &validation{
		context: ctx,
		request: request,
		route:   route,
		bound:   make(map[string]interface{}),
	}

	validate.contentType()
	validate.responseFormat()
	if len(validate.result) == 0 {
		validate.parameters()
	}

	return validate
}

func (v *validation) parameters() {
	if result := v.route.Binder.Bind(v.request, v.route.Params, v.route.Consumer, v.bound); result != nil {
		if result.Error() == "validation failure list" {
			for _, e := range result.(*errors.Validation).Value.([]interface{}) {
				v.result = append(v.result, e.(error))
			}
			return
		}
		v.result = append(v.result, result)
	}
}

func (v *validation) contentType() {
	if httpkit.CanHaveBody(v.request.Method) {
		ct, _, err := v.context.ContentType(v.request)
		if err != nil {
			v.result = append(v.result, err)
		} else if httpkit.NeedsContentType(v.request.Method) {
			if err := validateContentType(v.route.Consumes, ct); err != nil {
				v.result = append(v.result, err)
			}
			v.route.Consumer = v.route.Consumers[ct]
		}
	}
}

func (v *validation) responseFormat() {
	if str := v.context.ResponseFormat(v.request, v.route.Produces); str == "" && httpkit.NeedsContentType(v.request.Method) {
		v.result = append(v.result, errors.InvalidResponseFormat(v.request.Header.Get(httpkit.HeaderAccept), v.route.Produces))
	}
}
<|endoftext|>