<commit_before>// Copyright 2016-2019 Granitic. All rights reserved.
// Use of this source code is governed by an Apache 2.0 license that can be found in the LICENSE file at the root of this project.

/*
Package httpserver provides the HTTPServer facility which defines a configurable HTTP server for processing web-service requests.

The HTTPServer facility provides a server that will listen for HTTP web-service requests and map them to the web-service
endpoints defined by your application. A full description of how to configure this facility can be found at https://granitic.io/ref/http-server

This package defines two main types HTTPServer and AccessLogWriter. HTTPServer is a layer over Go's built-in http.Server adding runtime control (suspension, resumption)
and mapping of requests to instances of ws.Handler. AccessLogWriter supports Apache/Tomcat style access log formatting and writing.

Most applications will only need to enable this facility (probably changing the listen Port) and define mappings between incoming paths and application logic in their
component definition files. See handler.WsHandler for more details.

*/
package httpserver

import (
	"context"
	"errors"
	"fmt"
	"github.com/graniticio/granitic/v2/httpendpoint"
	"github.com/graniticio/granitic/v2/instrument"
	"github.com/graniticio/granitic/v2/ioc"
	"github.com/graniticio/granitic/v2/logging"
	"github.com/graniticio/granitic/v2/ws"
	"net"
	"net/http"
	"regexp"
	"sync/atomic"
	"time"
)

type registeredProvider struct {
	Provider httpendpoint.Provider
	Pattern  *regexp.Regexp
}

// HTTPServer is the server that accepts incoming HTTP requests and maps them to handlers to process them.
type HTTPServer struct {
	registeredProvidersByMethod map[string][]*registeredProvider
	unregisteredProviders       map[string]httpendpoint.Provider
	componentContainer          *ioc.ComponentContainer

	// Logger used by Granitic framework components. Automatically injected.
	FrameworkLogger logging.Logger

	// A component able to write an access log. Automatically added by this facility's builder, is access log support is enabled.
	AccessLogWriter *AccessLogWriter

	// Whether or not access logging should be enabled.
	AccessLogging bool

	// Whether or not instances of httpendpoint.Provider found in the IoC container should be automatically
	// registered with this server
	AutoFindHandlers bool

	// The TCP port on which the HTTP server should listen for requests.
	Port int

	// The IP/hostname this server should listen on, follows standard Go net package syntax. Empty string means listen on all.
	Address string

	// A component able to write valid HTTP responses in the event a user request results in an abnormal result
	// (not found, server too busy, panic in application logic). If you use the JSONWs or XMLWs facility, this is automatically injected.
	AbnormalStatusWriter ws.AbnormalStatusWriter

	// The name of a component in the IoC container that should be used as an AbnormalStatusWriter if one is not being auto-injected.
	AbnormalStatusWriterName string

	// The number of HTTP requests currently being handled by the server.
	ActiveRequests int64

	// Allow request instrumentation to begin BEFORE too-busy/suspended checks. Allows instrumentation of requests that would be trivially
	// rejected, but potentially increases risk of denial-of-service if instrumentation setup causes load or consumes memory.
	AllowEarlyInstrumentation bool

	// Prevents this server from finding and using RequestInstrumentationManager components
	DisableInstrumentationAutoWire bool

	// A component able to instrument a web service request in some way
	InstrumentationManager instrument.RequestInstrumentationManager

	// How many concurrent requests the server should allow before returning 'too busy' responses to subsequent requests.
	MaxConcurrent int64

	// The HTTP status code returned with 'too busy responses'. Normally 503
	TooBusyStatus int

	// A component able to examine an incoming request and determine which version of functionality is being requested.
	VersionExtractor httpendpoint.RequestedVersionExtractor

	// A component able to use data in an HTTP request's headers to populate a context
	IDContextBuilder IdentifiedRequestContextBuilder

	state  ioc.ComponentState
	server *http.Server
}

// Container allows Granitic to inject a reference to the IOC container
func (h *HTTPServer) Container(container *ioc.ComponentContainer) {
	h.componentContainer = container
}

func (h *HTTPServer) registerProvider(endPointProvider httpendpoint.Provider) {

	for _, method := range endPointProvider.SupportedHTTPMethods() {
		var compiledRegex *regexp.Regexp
		var err error

		pattern := endPointProvider.RegexPattern()

		if compiledRegex, err = regexp.Compile(pattern); err != nil {
			h.FrameworkLogger.LogErrorf("Unable to compile regular expression from pattern %s: %s", pattern, err.Error())
		}

		h.FrameworkLogger.LogTracef("Registering %s %s", pattern, method)

		rp := registeredProvider{endPointProvider, compiledRegex}

		providersForMethod := h.registeredProvidersByMethod[method]

		if providersForMethod == nil {
			providersForMethod = make([]*registeredProvider, 1)
			providersForMethod[0] = &rp
			h.registeredProvidersByMethod[method] = providersForMethod
		} else {
			h.registeredProvidersByMethod[method] = append(providersForMethod, &rp)
		}
	}

}

// StartComponent Finds and registers any available components that implement httpendpoint.Provider (normally instances of
// handler.WsHandler) unless auto finding of handlers is disabled. The server does not actually start listening for
// requests until the IoC container calls AllowAccess.
func (h *HTTPServer) StartComponent() error {

	if h.state != ioc.StoppedState {
		return nil
	}

	h.state = ioc.StartingState
	h.registeredProvidersByMethod = make(map[string][]*registeredProvider)

	if h.AutoFindHandlers {
		for _, component := range h.componentContainer.AllComponents() {

			name := component.Name

			if provider, found := component.Instance.(httpendpoint.Provider); found && provider.AutoWireable() {
				h.FrameworkLogger.LogDebugf("Found Provider %s", name)
				h.registerProvider(provider)
			}
		}
	} else if h.unregisteredProviders != nil {

		for _, provider := range h.unregisteredProviders {

			h.registerProvider(provider)

		}

	} else {
		return errors.New("auto finding of handlers is disabled, but handlers have not been set manually")
	}

	if h.AbnormalStatusWriter == nil {

		return errors.New("no AbnormalStatusWriter set - make sure you have enabled a web services facility")
	}

	if h.InstrumentationManager == nil {
		//No RequestInstrumentationManager component injected, use a 'noop' implementation
		h.FrameworkLogger.LogDebugf("No RequestInstrumentationManager set. Using noop implementation")
		h.InstrumentationManager = new(noopRequestInstrumentationManager)
	}

	h.state = ioc.AwaitingAccessState

	return nil
}

// Suspend causes all subsequent new HTTP requests to receive a 'too busy' response until Resume is called.
func (h *HTTPServer) Suspend() error {

	if h.state != ioc.RunningState {
		return nil
	}

	h.state = ioc.SuspendedState

	return nil
}

// Resume allows subsequent requests to be processed normally (reverses the effect of calling Suspend).
func (h *HTTPServer) Resume() error {

	if h.state != ioc.SuspendedState {
		return nil
	}

	h.state = ioc.RunningState

	return nil
}

// AllowAccess starts the server listening on the configured address and port. Returns an error if the port is already in use.
func (h *HTTPServer) AllowAccess() error {

	if h.state != ioc.AwaitingAccessState {
		return nil
	}

	sm := http.NewServeMux()
	sm.Handle("/", http.HandlerFunc(h.handleAll))

	sv := new(http.Server)
	sv.Handler = sm

	listenAddress := fmt.Sprintf("%s:%d", h.Address, h.Port)

	//Check if the address is already in use
	if ln, err := net.Listen("tcp", listenAddress); err == nil {
		ln.Close()
	} else {
		return err
	}

	sv.Addr = listenAddress

	go sv.ListenAndServe()

	h.server = sv

	h.FrameworkLogger.LogInfof("Listening on %d", h.Port)

	h.state = ioc.RunningState

	return nil
}

// SetProvidersManually manually injects a set of httpendpoint.HTTPEndpointProviders when auto finding is disabled.
func (h *HTTPServer) SetProvidersManually(p map[string]httpendpoint.Provider) {
	h.unregisteredProviders = p
}

func (h *HTTPServer) writeAbnormal(ctx context.Context, status int, wrw *httpendpoint.HTTPResponseWriter, err ...error) {

	if len(err) > 0 {
		h.FrameworkLogger.LogErrorf(err[0].Error())
	}

	state := ws.NewAbnormalState(status, wrw)
	if err := h.AbnormalStatusWriter.WriteAbnormalStatus(ctx, state); err != nil {
		h.FrameworkLogger.LogErrorfCtx(ctx, err.Error())
	}

}

func (h *HTTPServer) handleAll(res http.ResponseWriter, req *http.Request) {

	var instrumentor instrument.Instrumentor
	var endInstrumentation func()
	ctx, cancelFunc := context.WithCancel(req.Context())
	defer cancelFunc()

	if h.AllowEarlyInstrumentation {
		ctx, instrumentor, endInstrumentation = h.InstrumentationManager.Begin(ctx, res, req)
		defer endInstrumentation()
	}

	wrw := httpendpoint.NewHTTPResponseWriter(res)

	if h.state != ioc.RunningState {
		// The HTTP server is suspended - reject the request
		h.writeAbnormal(ctx, h.TooBusyStatus, wrw)
		return
	}

	rCount := atomic.AddInt64(&h.ActiveRequests, 1)
	defer atomic.AddInt64(&h.ActiveRequests, -1)

	if h.MaxConcurrent > 0 && rCount > h.MaxConcurrent {
		// Too many requests already being processed
		h.writeAbnormal(ctx, h.TooBusyStatus, wrw)
		return
	}

	if instrumentor == nil {
		ctx, instrumentor, endInstrumentation = h.InstrumentationManager.Begin(ctx, res, req)
		defer endInstrumentation()
	}

	var requestID string
	received := time.Now()

	if h.IDContextBuilder != nil {

		if idCtx, err := h.IDContextBuilder.WithIdentity(ctx, req); err == nil {
			ctx = idCtx.(context.Context)
			requestID = h.IDContextBuilder.ID(idCtx)

			ctx = ws.StoreRequestIDFunction(ctx, h.IDContextBuilder.ID)

			instrumentor.Amend(instrument.RequestID, requestID)

			if h.FrameworkLogger.IsLevelEnabled(logging.Trace) {
				h.FrameworkLogger.LogTracef("Request ID: %s\n", requestID)
			}

		} else {

			//Something went wrong trying to use HTTP data to identify a context - treat as a bad request (400)
			h.writeAbnormal(ctx, http.StatusBadRequest, wrw, err)

			return

		}
	}

	matched := false

	providersByMethod := h.registeredProvidersByMethod[req.Method]

	path := req.URL.Path

	h.FrameworkLogger.LogTracef("Finding provider to handle %s %s from %d providers", path, req.Method, len(providersByMethod))

	for _, handlerPattern := range providersByMethod {

		pattern := handlerPattern.Pattern

		h.FrameworkLogger.LogTracef("Testing %s", pattern.String())

		if pattern.MatchString(path) && h.versionMatch(instrumentor, req, handlerPattern.Provider) {
			h.FrameworkLogger.LogTracef("Matches %s", pattern.String())
			matched = true
			ctx = handlerPattern.Provider.ServeHTTP(ctx, wrw, req)
		}
	}

	if !matched {
		state := ws.NewAbnormalState(http.StatusNotFound, wrw)

		if err := h.AbnormalStatusWriter.WriteAbnormalStatus(ctx, state); err != nil {
			h.FrameworkLogger.LogErrorfCtx(ctx, err.Error())
		}
	}

	if h.AccessLogging {
		finished := time.Now()
		h.AccessLogWriter.LogRequest(ctx, req, wrw, &received, &finished)
	}

}

func (h *HTTPServer) versionMatch(ri instrument.Instrumentor, r *http.Request, p httpendpoint.Provider) bool {

	if h.VersionExtractor == nil || !p.VersionAware() {
		return true
	}

	version := h.VersionExtractor.Extract(r)

	ri.Amend(instrument.RequestVersion, version)

	return p.SupportsVersion(version)

}

// PrepareToStop sets state to Stopping. Any subsequent requests will receive a 'too busy response'
func (h *HTTPServer) PrepareToStop() {
	h.state = ioc.StoppingState

	if h.server != nil {
		h.server.Shutdown(context.Background())
	}

}

// ReadyToStop returns false is the server is currently handling any requests.
func (h *HTTPServer) ReadyToStop() (bool, error) {
	a := h.ActiveRequests
	ready := a <= 0

	if ready {
		return true, nil
	}

	return false, fmt.Errorf("HTTP server listening on %d is still serving %d request(s)", h.Port, a)

}

// Stop sets state to Stopped. Any subsequent requests will receive a 'too busy response'. Note that the HTTP
// server is still listening on its configured port and address.
func (h *HTTPServer) Stop() error {

	h.state = ioc.StoppedState

	if h.server != nil {
		h.server.Close()
	}

	return nil
}
<commit_msg>Removed redundant AbnormalStatusWriterName field<commit_after>// Copyright 2016-2019 Granitic. All rights reserved.
// Use of this source code is governed by an Apache 2.0 license that can be found in the LICENSE file at the root of this project.

/*
Package httpserver provides the HTTPServer facility which defines a configurable HTTP server for processing web-service requests.

The HTTPServer facility provides a server that will listen for HTTP web-service requests and map them to the web-service
endpoints defined by your application. A full description of how to configure this facility can be found at https://granitic.io/ref/http-server

This package defines two main types HTTPServer and AccessLogWriter. HTTPServer is a layer over Go's built-in http.Server adding runtime control (suspension, resumption)
and mapping of requests to instances of ws.Handler. AccessLogWriter supports Apache/Tomcat style access log formatting and writing.

Most applications will only need to enable this facility (probably changing the listen Port) and define mappings between incoming paths and application logic in their
component definition files. See handler.WsHandler for more details.

*/
package httpserver

import (
	"context"
	"errors"
	"fmt"
	"github.com/graniticio/granitic/v2/httpendpoint"
	"github.com/graniticio/granitic/v2/instrument"
	"github.com/graniticio/granitic/v2/ioc"
	"github.com/graniticio/granitic/v2/logging"
	"github.com/graniticio/granitic/v2/ws"
	"net"
	"net/http"
	"regexp"
	"sync/atomic"
	"time"
)

type registeredProvider struct {
	Provider httpendpoint.Provider
	Pattern  *regexp.Regexp
}

// HTTPServer is the server that accepts incoming HTTP requests and maps them to handlers to process them.
type HTTPServer struct {
	registeredProvidersByMethod map[string][]*registeredProvider
	unregisteredProviders       map[string]httpendpoint.Provider
	componentContainer          *ioc.ComponentContainer

	// Logger used by Granitic framework components. Automatically injected.
	FrameworkLogger logging.Logger

	// A component able to write an access log. Automatically added by this facility's builder, is access log support is enabled.
	AccessLogWriter *AccessLogWriter

	// Whether or not access logging should be enabled.
	AccessLogging bool

	// Whether or not instances of httpendpoint.Provider found in the IoC container should be automatically
	// registered with this server
	AutoFindHandlers bool

	// The TCP port on which the HTTP server should listen for requests.
	Port int

	// The IP/hostname this server should listen on, follows standard Go net package syntax. Empty string means listen on all.
	Address string

	// A component able to write valid HTTP responses in the event a user request results in an abnormal result
	// (not found, server too busy, panic in application logic). If you use the JSONWs or XMLWs facility, this is automatically injected.
	AbnormalStatusWriter ws.AbnormalStatusWriter

	// The number of HTTP requests currently being handled by the server.
	ActiveRequests int64

	// Allow request instrumentation to begin BEFORE too-busy/suspended checks. Allows instrumentation of requests that would be trivially
	// rejected, but potentially increases risk of denial-of-service if instrumentation setup causes load or consumes memory.
	AllowEarlyInstrumentation bool

	// Prevents this server from finding and using RequestInstrumentationManager components
	DisableInstrumentationAutoWire bool

	// A component able to instrument a web service request in some way
	InstrumentationManager instrument.RequestInstrumentationManager

	// How many concurrent requests the server should allow before returning 'too busy' responses to subsequent requests.
	MaxConcurrent int64

	// The HTTP status code returned with 'too busy responses'. Normally 503
	TooBusyStatus int

	// A component able to examine an incoming request and determine which version of functionality is being requested.
	VersionExtractor httpendpoint.RequestedVersionExtractor

	// A component able to use data in an HTTP request's headers to populate a context
	IDContextBuilder IdentifiedRequestContextBuilder

	state  ioc.ComponentState
	server *http.Server
}

// Container allows Granitic to inject a reference to the IOC container
func (h *HTTPServer) Container(container *ioc.ComponentContainer) {
	h.componentContainer = container
}

func (h *HTTPServer) registerProvider(endPointProvider httpendpoint.Provider) {

	for _, method := range endPointProvider.SupportedHTTPMethods() {
		var compiledRegex *regexp.Regexp
		var err error

		pattern := endPointProvider.RegexPattern()

		if compiledRegex, err = regexp.Compile(pattern); err != nil {
			h.FrameworkLogger.LogErrorf("Unable to compile regular expression from pattern %s: %s", pattern, err.Error())
		}

		h.FrameworkLogger.LogTracef("Registering %s %s", pattern, method)

		rp := registeredProvider{endPointProvider, compiledRegex}

		providersForMethod := h.registeredProvidersByMethod[method]

		if providersForMethod == nil {
			providersForMethod = make([]*registeredProvider, 1)
			providersForMethod[0] = &rp
			h.registeredProvidersByMethod[method] = providersForMethod
		} else {
			h.registeredProvidersByMethod[method] = append(providersForMethod, &rp)
		}
	}

}

// StartComponent Finds and registers any available components that implement httpendpoint.Provider (normally instances of
// handler.WsHandler) unless auto finding of handlers is disabled. The server does not actually start listening for
// requests until the IoC container calls AllowAccess.
func (h *HTTPServer) StartComponent() error {

	if h.state != ioc.StoppedState {
		return nil
	}

	h.state = ioc.StartingState
	h.registeredProvidersByMethod = make(map[string][]*registeredProvider)

	if h.AutoFindHandlers {
		for _, component := range h.componentContainer.AllComponents() {

			name := component.Name

			if provider, found := component.Instance.(httpendpoint.Provider); found && provider.AutoWireable() {
				h.FrameworkLogger.LogDebugf("Found Provider %s", name)
				h.registerProvider(provider)
			}
		}
	} else if h.unregisteredProviders != nil {

		for _, provider := range h.unregisteredProviders {

			h.registerProvider(provider)

		}

	} else {
		return errors.New("auto finding of handlers is disabled, but handlers have not been set manually")
	}

	if h.AbnormalStatusWriter == nil {

		return errors.New("no AbnormalStatusWriter set - make sure you have enabled a web services facility")
	}

	if h.InstrumentationManager == nil {
		//No RequestInstrumentationManager component injected, use a 'noop' implementation
		h.FrameworkLogger.LogDebugf("No RequestInstrumentationManager set. Using noop implementation")
		h.InstrumentationManager = new(noopRequestInstrumentationManager)
	}

	h.state = ioc.AwaitingAccessState

	return nil
}

// Suspend causes all subsequent new HTTP requests to receive a 'too busy' response until Resume is called.
func (h *HTTPServer) Suspend() error {

	if h.state != ioc.RunningState {
		return nil
	}

	h.state = ioc.SuspendedState

	return nil
}

// Resume allows subsequent requests to be processed normally (reverses the effect of calling Suspend).
func (h *HTTPServer) Resume() error {

	if h.state != ioc.SuspendedState {
		return nil
	}

	h.state = ioc.RunningState

	return nil
}

// AllowAccess starts the server listening on the configured address and port. Returns an error if the port is already in use.
func (h *HTTPServer) AllowAccess() error {

	if h.state != ioc.AwaitingAccessState {
		return nil
	}

	sm := http.NewServeMux()
	sm.Handle("/", http.HandlerFunc(h.handleAll))

	sv := new(http.Server)
	sv.Handler = sm

	listenAddress := fmt.Sprintf("%s:%d", h.Address, h.Port)

	//Check if the address is already in use
	if ln, err := net.Listen("tcp", listenAddress); err == nil {
		ln.Close()
	} else {
		return err
	}

	sv.Addr = listenAddress

	go sv.ListenAndServe()

	h.server = sv

	h.FrameworkLogger.LogInfof("Listening on %d", h.Port)

	h.state = ioc.RunningState

	return nil
}

// SetProvidersManually manually injects a set of httpendpoint.HTTPEndpointProviders when auto finding is disabled.
func (h *HTTPServer) SetProvidersManually(p map[string]httpendpoint.Provider) {
	h.unregisteredProviders = p
}

func (h *HTTPServer) writeAbnormal(ctx context.Context, status int, wrw *httpendpoint.HTTPResponseWriter, err ...error) {

	if len(err) > 0 {
		h.FrameworkLogger.LogErrorf(err[0].Error())
	}

	state := ws.NewAbnormalState(status, wrw)
	if err := h.AbnormalStatusWriter.WriteAbnormalStatus(ctx, state); err != nil {
		h.FrameworkLogger.LogErrorfCtx(ctx, err.Error())
	}

}

func (h *HTTPServer) handleAll(res http.ResponseWriter, req *http.Request) {

	var instrumentor instrument.Instrumentor
	var endInstrumentation func()
	ctx, cancelFunc := context.WithCancel(req.Context())
	defer cancelFunc()

	if h.AllowEarlyInstrumentation {
		ctx, instrumentor, endInstrumentation = h.InstrumentationManager.Begin(ctx, res, req)
		defer endInstrumentation()
	}

	wrw := httpendpoint.NewHTTPResponseWriter(res)

	if h.state != ioc.RunningState {
		// The HTTP server is suspended - reject the request
		h.writeAbnormal(ctx, h.TooBusyStatus, wrw)
		return
	}

	rCount := atomic.AddInt64(&h.ActiveRequests, 1)
	defer atomic.AddInt64(&h.ActiveRequests, -1)

	if h.MaxConcurrent > 0 && rCount > h.MaxConcurrent {
		// Too many requests already being processed
		h.writeAbnormal(ctx, h.TooBusyStatus, wrw)
		return
	}

	if instrumentor == nil {
		ctx, instrumentor, endInstrumentation = h.InstrumentationManager.Begin(ctx, res, req)
		defer endInstrumentation()
	}

	var requestID string
	received := time.Now()

	if h.IDContextBuilder != nil {

		if idCtx, err := h.IDContextBuilder.WithIdentity(ctx, req); err == nil {
			ctx = idCtx.(context.Context)
			requestID = h.IDContextBuilder.ID(idCtx)

			ctx = ws.StoreRequestIDFunction(ctx, h.IDContextBuilder.ID)

			instrumentor.Amend(instrument.RequestID, requestID)

			if h.FrameworkLogger.IsLevelEnabled(logging.Trace) {
				h.FrameworkLogger.LogTracef("Request ID: %s\n", requestID)
			}

		} else {

			//Something went wrong trying to use HTTP data to identify a context - treat as a bad request (400)
			h.writeAbnormal(ctx, http.StatusBadRequest, wrw, err)

			return

		}
	}

	matched := false

	providersByMethod := h.registeredProvidersByMethod[req.Method]

	path := req.URL.Path

	h.FrameworkLogger.LogTracef("Finding provider to handle %s %s from %d providers", path, req.Method, len(providersByMethod))

	for _, handlerPattern := range providersByMethod {

		pattern := handlerPattern.Pattern

		h.FrameworkLogger.LogTracef("Testing %s", pattern.String())

		if pattern.MatchString(path) && h.versionMatch(instrumentor, req, handlerPattern.Provider) {
			h.FrameworkLogger.LogTracef("Matches %s", pattern.String())
			matched = true
			ctx = handlerPattern.Provider.ServeHTTP(ctx, wrw, req)
		}
	}

	if !matched {
		state := ws.NewAbnormalState(http.StatusNotFound, wrw)

		if err := h.AbnormalStatusWriter.WriteAbnormalStatus(ctx, state); err != nil {
			h.FrameworkLogger.LogErrorfCtx(ctx, err.Error())
		}
	}

	if h.AccessLogging {
		finished := time.Now()
		h.AccessLogWriter.LogRequest(ctx, req, wrw, &received, &finished)
	}

}

func (h *HTTPServer) versionMatch(ri instrument.Instrumentor, r *http.Request, p httpendpoint.Provider) bool {

	if h.VersionExtractor == nil || !p.VersionAware() {
		return true
	}

	version := h.VersionExtractor.Extract(r)

	ri.Amend(instrument.RequestVersion, version)

	return p.SupportsVersion(version)

}

// PrepareToStop sets state to Stopping. Any subsequent requests will receive a 'too busy response'
func (h *HTTPServer) PrepareToStop() {
	h.state = ioc.StoppingState

	if h.server != nil {
		h.server.Shutdown(context.Background())
	}

}

// ReadyToStop returns false is the server is currently handling any requests.
func (h *HTTPServer) ReadyToStop() (bool, error) {
	a := h.ActiveRequests
	ready := a <= 0

	if ready {
		return true, nil
	}

	return false, fmt.Errorf("HTTP server listening on %d is still serving %d request(s)", h.Port, a)

}

// Stop sets state to Stopped. Any subsequent requests will receive a 'too busy response'. Note that the HTTP
// server is still listening on its configured port and address.
func (h *HTTPServer) Stop() error {

	h.state = ioc.StoppedState

	if h.server != nil {
		h.server.Close()
	}

	return nil
}
<|endoftext|>


<commit_before>package jpush

import (
	"encoding/base64"
	"errors"
	"strings"

	. "github.com/eynstudio/gobreak"
	"github.com/eynstudio/gobreak/net/http"
)

const (
	JPUSH_VALIDATE = "https://api.jpush.cn/v3/push/validate"
	JPUSH_URL      = "https://api.jpush.cn/v3/push"
	BASE64         = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
)

var (
	appKey    string
	appSecret string
	auth      string
)

func Init(key, secret string) {
	appKey = key
	appSecret = secret
	auth = "Basic " + base64.NewEncoding(BASE64).EncodeToString([]byte(key+":"+secret))
}

func Send(push *JPush) (err error)     { return send(JPUSH_URL, push) }
func SendTest(push *JPush) (err error) { return send(JPUSH_VALIDATE, push) }

func send(url string, push *JPush) (err error) {
	data, err := http.PostJsonWiHeader(url, push, M{"Authorization": auth})
	if err != nil {
		return err
	}
	str := string(data)
	if strings.Contains(str, "msg_id") {
		return nil
	}
	return errors.New(str)
}

type JPush struct {
	Platform     interface{} `json:"platform"`
	Audience     interface{} `json:"audience"`
	Notification interface{} `json:"notification,omitempty"`
	Message      interface{} `json:"message,omitempty"`
	Options      *Option     `json:"options,omitempty"`
}

func NewJPush() *JPush           { return &JPush{} }
func (p *JPush) Send() error     { return Send(p) }
func (p *JPush) SendTest() error { return SendTest(p) }

func (p *JPush) SetPlatform(all, android, ios, winphone bool) *JPush {
	if all {
		p.Platform = "all"
		p.Options = &Option{}
		p.Options.ApnsProduction = false
		return p
	}

	lst := make([]string, 0)
	if android {
		lst = append(lst, "android")
	}
	if ios {
		lst = append(lst, "ios")
		p.Options = &Option{}
		p.Options.ApnsProduction = false
	}
	if winphone {
		lst = append(lst, "winphone")
	}
	p.Platform = lst
	return p
}

func (p *JPush) SetNotification(alert string, android *AndroidNotice, ios *IOSNotice, win *WinPhoneNotice) *JPush {
	p.Notification = &Notice{Alert: alert, Android: android, IOS: ios, WINPhone: win}
	return p
}

func (p *JPush) SetMessage(content, title, content_type string, extras M) *JPush {
	p.Message = &Message{Content: content, Title: title, ContentType: content_type, Extras: extras}
	return p
}

func (p *JPush) SetAudience(all bool, tag, tag_and, alias, id []string) *JPush {
	if all {
		p.Audience = "all"
		return p
	}

	m := make(map[string][]string, 0)
	set := func(k string, v []string) {
		if v != nil {
			m[k] = v
		}
	}
	set("tag", tag)
	set("tag_and", tag_and)
	set("alias", alias)
	set("registration_id", id)
	p.Audience = m
	return p
}
<commit_msg>update<commit_after>package jpush

import (
	"encoding/base64"
	"errors"
	"strings"

	. "github.com/eynstudio/gobreak"
	"github.com/eynstudio/gobreak/net/http"
)

const (
	JPUSH_VALIDATE = "https://api.jpush.cn/v3/push/validate"
	JPUSH_URL      = "https://api.jpush.cn/v3/push"
	BASE64         = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
)

var (
	appKey    string
	appSecret string
	auth      string
)

func Init(key, secret string) {
	appKey = key
	appSecret = secret
	auth = "Basic " + base64.NewEncoding(BASE64).EncodeToString([]byte(key+":"+secret))
}

func Send(push *JPush) (err error)     { return send(JPUSH_URL, push) }
func SendTest(push *JPush) (err error) { return send(JPUSH_VALIDATE, push) }

func send(url string, push *JPush) (err error) {
	h := http.PostJsonWiHeader(url, push, M{"Authorization": auth})
	str := h.GetStr()
	if h.IsErr() {
		return h.Err
	}
	if strings.Contains(str, "msg_id") {
		return nil
	}
	return errors.New(str)
}

type JPush struct {
	Platform     interface{} `json:"platform"`
	Audience     interface{} `json:"audience"`
	Notification interface{} `json:"notification,omitempty"`
	Message      interface{} `json:"message,omitempty"`
	Options      *Option     `json:"options,omitempty"`
}

func NewJPush() *JPush           { return &JPush{} }
func (p *JPush) Send() error     { return Send(p) }
func (p *JPush) SendTest() error { return SendTest(p) }

func (p *JPush) setApnsProduction(v bool) *JPush {
	if p.Options == nil {
		p.Options = &Option{}
	}
	p.Options.ApnsProduction = v
	return p
}

func (p *JPush) SetPlatformAll() *JPush {
	p.Platform = "all"
	return p.setApnsProduction(false)
}

func (p *JPush) SetPlatform(android, ios, winphone bool) *JPush {
	lst := make([]string, 0)
	if android {
		lst = append(lst, "android")
	}
	if ios {
		lst = append(lst, "ios")
		p.setApnsProduction(false)
	}
	if winphone {
		lst = append(lst, "winphone")
	}
	p.Platform = lst
	return p
}

func (p *JPush) SetNotification(alert string, android *AndroidNotice, ios *IOSNotice, win *WinPhoneNotice) *JPush {
	p.Notification = &Notice{Alert: alert, Android: android, IOS: ios, WINPhone: win}
	return p
}

func (p *JPush) SetMessage(content, title, content_type string, extras M) *JPush {
	p.Message = &Message{Content: content, Title: title, ContentType: content_type, Extras: extras}
	return p
}

func (p *JPush) SetAudienceAll() *JPush {
	p.Audience = "all"
	return p
}
func (p *JPush) SetAudience(tag, tag_and, alias, id []string) *JPush {
	m := make(map[string][]string, 0)
	set := func(k string, v []string) {
		if v != nil {
			m[k] = v
		}
	}
	set("tag", tag)
	set("tag_and", tag_and)
	set("alias", alias)
	set("registration_id", id)
	p.Audience = m
	return p
}
<|endoftext|>


<commit_before>package forest

import "testing"

func TestPost(t *testing.T) {
	r := tsAPI.POST(t, Path("/echo").Body("data").Header("ECHO", "ping"))
	ExpectString(t, r, func(m string) {
		if m != "data" {
			t.Errorf("expected data but got %v", m)
		}
	})
	ExpectHeader(t, r, "ECHO", "ping")
}
func TestDelete(t *testing.T) {
	r := tsAPI.DELETE(t, Path("/"))
	ExpectStatus(t, r, 204)
}

func TestPut(t *testing.T) {
	r := tsAPI.PUT(t, Path("/"))
	ExpectStatus(t, r, 204)
}

func TestPatch(t *testing.T) {
	r := tsAPI.PATCH(t, Path("/"))
	ExpectStatus(t, r, 204)
}

func TestPut404(t *testing.T) {
	r := tsAPI.PUT(t, Path("/{code}", 404))
	ExpectStatus(t, r, 404)
}

func TestPut404UsingDo(t *testing.T) {
	r, err := tsAPI.Do("PUT", Path("/{code}", 404))
	if err != nil {
		t.Fail()
	}
	ExpectStatus(t, r, 404)
}

func TestDoWithInvalidUrl(t *testing.T) {
	_, err := tsAPI.Do("HEAD", Path(":"))
	if err == nil {
		t.Fail()
	}
}
<commit_msg>add BasicAuth test<commit_after>package forest

import (
	"net/http"
	"testing"
)

func TestPost(t *testing.T) {
	r := tsAPI.POST(t, Path("/echo").Body("data").Header("ECHO", "ping"))
	ExpectString(t, r, func(m string) {
		if m != "data" {
			t.Errorf("expected data but got %v", m)
		}
	})
	ExpectHeader(t, r, "ECHO", "ping")
}
func TestDelete(t *testing.T) {
	r := tsAPI.DELETE(t, Path("/"))
	ExpectStatus(t, r, 204)
}

func TestPut(t *testing.T) {
	r := tsAPI.PUT(t, Path("/"))
	ExpectStatus(t, r, 204)
}

func TestPatch(t *testing.T) {
	r := tsAPI.PATCH(t, Path("/"))
	ExpectStatus(t, r, 204)
}

func TestPut404(t *testing.T) {
	r := tsAPI.PUT(t, Path("/{code}", 404))
	ExpectStatus(t, r, 404)
}

func TestPut404UsingDo(t *testing.T) {
	r, err := tsAPI.Do("PUT", Path("/{code}", 404))
	if err != nil {
		t.Fail()
	}
	ExpectStatus(t, r, 404)
}

func TestDoWithInvalidUrl(t *testing.T) {
	_, err := tsAPI.Do("HEAD", Path(":"))
	if err == nil {
		t.Fail()
	}
}

func TestBasicAuth(t *testing.T) {
	cfg := NewConfig("/").BasicAuth("a", "b")
	req, _ := http.NewRequest("GET", "/", nil)
	setBasicAuth(cfg, req)
	if len(req.Header.Get("Authorization")) == 0 {
		t.Errorf("expected Authorization header")
	}
}
<|endoftext|>


<commit_before><commit_msg>Ensure initd runs in the pivoted root file system.<commit_after><|endoftext|>


<commit_before>package db

import (
	"gopkg.in/mgo.v2"
)

type DBConnection struct {
	session *mgo.Session
}

func NewConnection(host string) (conn *DBConnection) {
	session, err := mgo.Dial(host)
	if err != nil {
		panic(err)
	}
	session.SetMode(mgo.Monotonic, true)
	conn = &DBConnection{session}

	return conn
}

func (conn *DBConnection) Use(dbName, tableName string) (db *mgo.Collection) {
	return conn.session.DB(dbName).C(tableName)
}

func (conn *DBConnection) Close() {
	conn.session.Close()
	return
}
<commit_msg>refactor db connection<commit_after>package db

import (
	"gopkg.in/mgo.v2"
)

type DBConnection struct {
	session *mgo.Session
}

func NewConnection(host string) (conn *DBConnection) {
	session, err := mgo.Dial(host)
	if err != nil {
		panic(err)
	}
	session.SetMode(mgo.Monotonic, true)
	conn = &DBConnection{session}

	return conn
}

func (conn *DBConnection) Use(dbName, tableName string) (collection *mgo.Collection) {
	return conn.session.DB(dbName).C(tableName)
}

func (conn *DBConnection) Close() {
	conn.session.Close()
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 Jan Christian Grünhage
//
// 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 gomatrixserverlib

import (
	"errors"
	"math"
)

// Filter is used by clients to specify how the server should filter responses to e.g. sync requests
// Specified by: https://matrix.org/docs/spec/client_server/r0.5.0.html#filtering
type Filter struct {
	EventFields []string    `json:"event_fields,omitempty"`
	EventFormat string      `json:"event_format,omitempty"`
	Presence    EventFilter `json:"presence,omitempty"`
	AccountData EventFilter `json:"account_data,omitempty"`
	Room        RoomFilter  `json:"room,omitempty"`
}

// EventFilter is used to define filtering rules for events
type EventFilter struct {
	Limit      int      `json:"limit,omitempty"`
	NotSenders []string `json:"not_senders,omitempty"`
	NotTypes   []string `json:"not_types,omitempty"`
	Senders    []string `json:"senders,omitempty"`
	Types      []string `json:"types,omitempty"`
}

// RoomFilter is used to define filtering rules for room-related events
type RoomFilter struct {
	NotRooms     []string        `json:"not_rooms,omitempty"`
	Rooms        []string        `json:"rooms,omitempty"`
	Ephemeral    RoomEventFilter `json:"ephemeral,omitempty"`
	IncludeLeave bool            `json:"include_leave,omitempty"`
	State        StateFilter     `json:"state,omitempty"`
	Timeline     RoomEventFilter `json:"timeline,omitempty"`
	AccountData  RoomEventFilter `json:"account_data,omitempty"`
}

// StateFilter is used to define filtering rules for state events
type StateFilter struct {
	Limit                   int      `json:"limit,omitempty"`
	NotSenders              []string `json:"not_senders,omitempty"`
	NotTypes                []string `json:"not_types,omitempty"`
	Senders                 []string `json:"senders,omitempty"`
	Types                   []string `json:"types,omitempty"`
	LazyLoadMembers         bool     `json:"lazy_load_members,omitempty"`
	IncludeRedundantMembers bool     `json:"include_redundant_members,omitempty"`
	NotRooms                []string `json:"not_rooms,omitempty"`
	Rooms                   []string `json:"rooms,omitempty"`
	ContainsURL             *bool    `json:"contains_url,omitempty"`
}

// RoomEventFilter is used to define filtering rules for events in rooms
type RoomEventFilter struct {
	Limit                   int      `json:"limit,omitempty"`
	NotSenders              []string `json:"not_senders,omitempty"`
	NotTypes                []string `json:"not_types,omitempty"`
	Senders                 []string `json:"senders,omitempty"`
	Types                   []string `json:"types,omitempty"`
	LazyLoadMembers         bool     `json:"lazy_load_members,omitempty"`
	IncludeRedundantMembers bool     `json:"include_redundant_members,omitempty"`
	NotRooms                []string `json:"not_rooms,omitempty"`
	Rooms                   []string `json:"rooms,omitempty"`
	ContainsURL             *bool    `json:"contains_url,omitempty"`
}

// Validate checks if the filter contains valid property values
func (filter *Filter) Validate() error {
	if filter.EventFormat != "" && filter.EventFormat != "client" && filter.EventFormat != "federation" {
		return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
	}
	return nil
}

// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in
// the request
func DefaultFilter() Filter {
	return Filter{
		AccountData: DefaultEventFilter(),
		EventFields: nil,
		EventFormat: "client",
		Presence:    DefaultEventFilter(),
		Room: RoomFilter{
			AccountData:  DefaultRoomEventFilter(),
			Ephemeral:    DefaultRoomEventFilter(),
			IncludeLeave: false,
			NotRooms:     nil,
			Rooms:        nil,
			State:        DefaultStateFilter(),
			Timeline:     DefaultRoomEventFilter(),
		},
	}
}

// DefaultEventFilter returns the default event filter used by the Matrix server if no filter is
// provided in the request
func DefaultEventFilter() EventFilter {
	return EventFilter{
		Limit:      20,
		NotSenders: nil,
		NotTypes:   nil,
		Senders:    nil,
		Types:      nil,
	}
}

// DefaultStateFilter returns the default state event filter used by the Matrix server if no filter
// is provided in the request
func DefaultStateFilter() StateFilter {
	return StateFilter{
		Limit:                   math.MaxUint32,
		NotSenders:              nil,
		NotTypes:                nil,
		Senders:                 nil,
		Types:                   nil,
		LazyLoadMembers:         false,
		IncludeRedundantMembers: false,
		NotRooms:                nil,
		Rooms:                   nil,
		ContainsURL:             nil,
	}
}

// DefaultRoomEventFilter returns the default room event filter used by the Matrix server if no
// filter is provided in the request
func DefaultRoomEventFilter() RoomEventFilter {
	return RoomEventFilter{
		Limit:       20,
		NotSenders:  nil,
		NotTypes:    nil,
		Senders:     nil,
		Types:       nil,
		NotRooms:    nil,
		Rooms:       nil,
		ContainsURL: nil,
	}
}
<commit_msg>Use MaxInt32 instead of MaxUint32 (#188)<commit_after>// Copyright 2017 Jan Christian Grünhage
//
// 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 gomatrixserverlib

import (
	"errors"
	"math"
)

// Filter is used by clients to specify how the server should filter responses to e.g. sync requests
// Specified by: https://matrix.org/docs/spec/client_server/r0.5.0.html#filtering
type Filter struct {
	EventFields []string    `json:"event_fields,omitempty"`
	EventFormat string      `json:"event_format,omitempty"`
	Presence    EventFilter `json:"presence,omitempty"`
	AccountData EventFilter `json:"account_data,omitempty"`
	Room        RoomFilter  `json:"room,omitempty"`
}

// EventFilter is used to define filtering rules for events
type EventFilter struct {
	Limit      int      `json:"limit,omitempty"`
	NotSenders []string `json:"not_senders,omitempty"`
	NotTypes   []string `json:"not_types,omitempty"`
	Senders    []string `json:"senders,omitempty"`
	Types      []string `json:"types,omitempty"`
}

// RoomFilter is used to define filtering rules for room-related events
type RoomFilter struct {
	NotRooms     []string        `json:"not_rooms,omitempty"`
	Rooms        []string        `json:"rooms,omitempty"`
	Ephemeral    RoomEventFilter `json:"ephemeral,omitempty"`
	IncludeLeave bool            `json:"include_leave,omitempty"`
	State        StateFilter     `json:"state,omitempty"`
	Timeline     RoomEventFilter `json:"timeline,omitempty"`
	AccountData  RoomEventFilter `json:"account_data,omitempty"`
}

// StateFilter is used to define filtering rules for state events
type StateFilter struct {
	Limit                   int      `json:"limit,omitempty"`
	NotSenders              []string `json:"not_senders,omitempty"`
	NotTypes                []string `json:"not_types,omitempty"`
	Senders                 []string `json:"senders,omitempty"`
	Types                   []string `json:"types,omitempty"`
	LazyLoadMembers         bool     `json:"lazy_load_members,omitempty"`
	IncludeRedundantMembers bool     `json:"include_redundant_members,omitempty"`
	NotRooms                []string `json:"not_rooms,omitempty"`
	Rooms                   []string `json:"rooms,omitempty"`
	ContainsURL             *bool    `json:"contains_url,omitempty"`
}

// RoomEventFilter is used to define filtering rules for events in rooms
type RoomEventFilter struct {
	Limit                   int      `json:"limit,omitempty"`
	NotSenders              []string `json:"not_senders,omitempty"`
	NotTypes                []string `json:"not_types,omitempty"`
	Senders                 []string `json:"senders,omitempty"`
	Types                   []string `json:"types,omitempty"`
	LazyLoadMembers         bool     `json:"lazy_load_members,omitempty"`
	IncludeRedundantMembers bool     `json:"include_redundant_members,omitempty"`
	NotRooms                []string `json:"not_rooms,omitempty"`
	Rooms                   []string `json:"rooms,omitempty"`
	ContainsURL             *bool    `json:"contains_url,omitempty"`
}

// Validate checks if the filter contains valid property values
func (filter *Filter) Validate() error {
	if filter.EventFormat != "" && filter.EventFormat != "client" && filter.EventFormat != "federation" {
		return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
	}
	return nil
}

// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in
// the request
func DefaultFilter() Filter {
	return Filter{
		AccountData: DefaultEventFilter(),
		EventFields: nil,
		EventFormat: "client",
		Presence:    DefaultEventFilter(),
		Room: RoomFilter{
			AccountData:  DefaultRoomEventFilter(),
			Ephemeral:    DefaultRoomEventFilter(),
			IncludeLeave: false,
			NotRooms:     nil,
			Rooms:        nil,
			State:        DefaultStateFilter(),
			Timeline:     DefaultRoomEventFilter(),
		},
	}
}

// DefaultEventFilter returns the default event filter used by the Matrix server if no filter is
// provided in the request
func DefaultEventFilter() EventFilter {
	return EventFilter{
		Limit:      20,
		NotSenders: nil,
		NotTypes:   nil,
		Senders:    nil,
		Types:      nil,
	}
}

// DefaultStateFilter returns the default state event filter used by the Matrix server if no filter
// is provided in the request
func DefaultStateFilter() StateFilter {
	return StateFilter{
		Limit:                   math.MaxInt32,
		NotSenders:              nil,
		NotTypes:                nil,
		Senders:                 nil,
		Types:                   nil,
		LazyLoadMembers:         false,
		IncludeRedundantMembers: false,
		NotRooms:                nil,
		Rooms:                   nil,
		ContainsURL:             nil,
	}
}

// DefaultRoomEventFilter returns the default room event filter used by the Matrix server if no
// filter is provided in the request
func DefaultRoomEventFilter() RoomEventFilter {
	return RoomEventFilter{
		Limit:       20,
		NotSenders:  nil,
		NotTypes:    nil,
		Senders:     nil,
		Types:       nil,
		NotRooms:    nil,
		Rooms:       nil,
		ContainsURL: nil,
	}
}
<|endoftext|>


<commit_before>package main

import (
	logging "github.com/ckeyer/go-log"
)

//
func main() {
	log := logging.GetDefaultLogger("ckeyer test")

	log.Debug("heelosdf")
	log.Error("heleo")
	log.Info("helelo")
	log.Warning("hhhhh")
}
<commit_msg>modify for test<commit_after>package main

import (
	logging "github.com/ckeyer/go-log"
)

//
func main2() {
	log := logging.GetDefaultLogger("ckeyer test")

	log.Debug("heelosdf")
	log.Error("heleo")
	log.Info("helelo")
	log.Warning("hhhhh")
}
<|endoftext|>


<commit_before>package generic

import (
	mach "github.com/jeffjen/machine/lib/machine"

	"github.com/codegangsta/cli"

	"fmt"
	"net"
	"os"
)

func NewCommand() cli.Command {
	return cli.Command{
		Name:  "generic",
		Usage: "Setup Machine to use Docker Engine",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "user", EnvVar: "MACHINE_USER", Usage: "Run command as user"},
			cli.StringFlag{Name: "cert", EnvVar: "MACHINE_CERT_FILE", Usage: "Private key to use in Authentication"},
		},
		Subcommands: []cli.Command{
			newCreateCommand(),
		},
	}
}

func newCreateCommand() cli.Command {
	return cli.Command{
		Name:  "create",
		Usage: "Install Docker Engine on target",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "host", Usage: "Host to install Docker Engine"},
			cli.StringSliceFlag{Name: "altname", Usage: "Alternative name for Host"},
			cli.StringFlag{Name: "name", Usage: "Name to identify Docker Host"},
		},
		Action: func(c *cli.Context) error {
			var (
				org, certpath, _ = mach.ParseCertArgs(c)

				user     = c.GlobalString("user")
				cert     = c.GlobalString("cert")
				hostname = c.String("host")
				altnames = c.StringSlice("altname")

				name    = c.String("name")
				addr, _ = net.ResolveTCPAddr("tcp", hostname+":2376")

				instList = make(mach.RegisteredInstances)

				inst = mach.NewDockerHost(org, certpath, user, cert)
			)

			if name == "" {
				fmt.Fprintln(os.Stderr, "Required argument `name` missing")
				os.Exit(1)
			}

			// Load from Instance Roster to register and defer write back
			defer instList.Load().Dump()

			if err := inst.InstallDockerEngine(hostname); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if err := inst.InstallDockerEngineCertificate(hostname, altnames...); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			instList[name] = &mach.Instance{
				Name:       name,
				Driver:     "generic",
				DockerHost: addr,
				State:      "running",
			}

			return nil
		},
	}
}
<commit_msg>NIT: generic command requires machine auth info<commit_after>package generic

import (
	mach "github.com/jeffjen/machine/lib/machine"

	"github.com/codegangsta/cli"

	"fmt"
	"net"
	"os"
)

func NewCommand() cli.Command {
	return cli.Command{
		Name:  "generic",
		Usage: "Setup Machine to use Docker Engine",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "user", EnvVar: "MACHINE_USER", Usage: "Run command as user"},
			cli.StringFlag{Name: "cert", EnvVar: "MACHINE_CERT_FILE", Usage: "Private key to use in Authentication"},
		},
		Subcommands: []cli.Command{
			newCreateCommand(),
		},
	}
}

func newCreateCommand() cli.Command {
	return cli.Command{
		Name:  "create",
		Usage: "Install Docker Engine on target",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "host", Usage: "Host to install Docker Engine"},
			cli.StringSliceFlag{Name: "altname", Usage: "Alternative name for Host"},
			cli.StringFlag{Name: "name", Usage: "Name to identify Docker Host"},
		},
		Action: func(c *cli.Context) error {
			var (
				org, certpath, _ = mach.ParseCertArgs(c)

				user     = c.GlobalString("user")
				cert     = c.GlobalString("cert")
				hostname = c.String("host")
				altnames = c.StringSlice("altname")

				name    = c.String("name")
				addr, _ = net.ResolveTCPAddr("tcp", hostname+":2376")

				instList = make(mach.RegisteredInstances)

				inst = mach.NewDockerHost(org, certpath, user, cert)
			)

			if name == "" {
				fmt.Fprintln(os.Stderr, "Required argument `name` missing")
				os.Exit(1)
			}

			if user == "" || cert == "" {
				fmt.Fprintln(os.Stderr, "Missing required remote auth info")
				os.Exit(1)
			}

			// Load from Instance Roster to register and defer write back
			defer instList.Load().Dump()

			if err := inst.InstallDockerEngine(hostname); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			if err := inst.InstallDockerEngineCertificate(hostname, altnames...); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			instList[name] = &mach.Instance{
				Name:       name,
				Driver:     "generic",
				DockerHost: addr,
				State:      "running",
			}

			return nil
		},
	}
}
<|endoftext|>


<commit_before>package diff

type Delta int

const (
	// Element is present in both Left and Right collections.
	// Index uses the Left collection.
	Both Delta = iota
	// Element is present only in the Left collection.
	// Index uses the Left collection.
	Left
	// Element is present only in the Right collection.
	// Index uses the Right collection.
	Right
)

func (delta Delta) String() string {
	switch delta {
	case Both:
		return "Both"
	case Left:
		return "Left"
	case Right:
		return "Right"
	}
	return "unknown"
}

// One Diff record per element.
// If Delta is Left or Both, Index is for the left collection.
// If Delta is Right, Index is for the right collection.
type Diff struct {
	Delta Delta
	Index int
}
<commit_msg>Add minor doc<commit_after>package diff

// Describe in which collection the element occurs; Left, Right or Both.
type Delta int

const (
	// Element is present in both Left and Right collections.
	// Index uses the Left collection.
	Both Delta = iota
	// Element is present only in the Left collection.
	// Index uses the Left collection.
	Left
	// Element is present only in the Right collection.
	// Index uses the Right collection.
	Right
)

func (delta Delta) String() string {
	switch delta {
	case Both:
		return "Both"
	case Left:
		return "Left"
	case Right:
		return "Right"
	}
	return "unknown"
}

// One Diff record per element.
// If Delta is Left or Both, Index is for the left collection.
// If Delta is Right, Index is for the right collection.
type Diff struct {
	Delta Delta
	Index int
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/git-lfs/git-lfs/config"
)

var (
	BuildOS      = flag.String("os", "", "OS to target: darwin,freebsd,linux,windows")
	BuildArch    = flag.String("arch", "", "Arch to target: 386,amd64")
	BuildAll     = flag.Bool("all", false, "Builds all architectures")
	BuildDwarf   = flag.Bool("dwarf", false, "Includes DWARF tables in build artifacts")
	BuildLdFlags = flag.String("ldflags", "", "-ldflags to pass to the compiler")
	BuildGcFlags = flag.String("gcflags", "", "-gcflags to pass to the compiler")
	ShowHelp     = flag.Bool("help", false, "Shows help")
	matrixKeys   = map[string]string{
		"darwin":  "Mac",
		"freebsd": "FreeBSD",
		"linux":   "Linux",
		"windows": "Windows",
		"amd64":   "AMD64",
	}
	LdFlags []string
)

func mainBuild() {
	if *ShowHelp {
		fmt.Println("usage: script/bootstrap [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	fmt.Printf("Using %s\n", runtime.Version())

	genOut, err := exec.Command("go", "generate", "./commands").CombinedOutput()
	if err != nil {
		fmt.Fprintf(os.Stderr, "go generate failed:\n%v", string(genOut))
		os.Exit(1)
	}
	cmd, _ := exec.Command("git", "rev-parse", "--short", "HEAD").Output()

	if len(cmd) > 0 {
		LdFlags = append(LdFlags, "-X", strings.TrimSpace(
			"github.com/git-lfs/git-lfs/config.GitCommit="+string(cmd),
		))
	}
	if !*BuildDwarf {
		LdFlags = append(LdFlags, "-s", "-w")
	}

	buildMatrix := make(map[string]Release)
	errored := false

	var platforms, arches []string
	if len(*BuildOS) > 0 {
		platforms = strings.Split(*BuildOS, ",")
	}
	if len(*BuildArch) > 0 {
		arches = strings.Split(*BuildArch, ",")
	}
	if *BuildAll {
		platforms = []string{"linux", "darwin", "freebsd", "windows"}
		arches = []string{"amd64", "386"}
	}

	if len(platforms) < 1 || len(arches) < 1 {
		if err := build("", "", buildMatrix); err != nil {
			log.Fatalln(err)
		}
		return // skip build matrix stuff
	}

	for _, buildos := range platforms {
		for _, buildarch := range arches {
			err := build(strings.TrimSpace(buildos), strings.TrimSpace(buildarch), buildMatrix)
			if err != nil {
				errored = true
			}
		}
	}

	if errored {
		os.Exit(1)
	}

	by, err := json.Marshal(buildMatrix)
	if err != nil {
		log.Fatalln("Error encoding build matrix to json:", err)
	}

	file, err := os.Create("bin/releases/build_matrix.json")
	if err != nil {
		log.Fatalln("Error creating build_matrix.json:", err)
	}

	written, err := file.Write(by)
	file.Close()

	if err != nil {
		log.Fatalln("Error writing build_matrix.json", err)
	}

	if jsonSize := len(by); written != jsonSize {
		log.Fatalf("Expected to write %d bytes, actually wrote %d.\n", jsonSize, written)
	}
}

func build(buildos, buildarch string, buildMatrix map[string]Release) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-lfs-" + config.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	if err := buildCommand(dir, buildos, buildarch); err != nil {
		return err
	}

	if addenv {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}

		err = setupInstaller(buildos, buildarch, dir, buildMatrix)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}
	}

	return nil
}

func buildCommand(dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0

	bin := filepath.Join(dir, "git-lfs")

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	args := make([]string, 1, 6)
	args[0] = "build"
	if len(*BuildLdFlags) > 0 {
		args = append(args, "-ldflags", *BuildLdFlags)
	} else if len(LdFlags) > 0 {
		args = append(args, "-ldflags", strings.Join(LdFlags, " "))
	}

	if len(*BuildGcFlags) > 0 {
		args = append(args, "-gcflags", *BuildGcFlags)
	}

	args = append(args, "-o", bin, ".")

	cmd := exec.Command("go", args...)
	if addenv {
		cmd.Env = buildGoEnv(buildos, buildarch)
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func buildGoEnv(buildos, buildarch string) []string {
	env := make([]string, 6, 9)
	env[0] = "GOOS=" + buildos
	env[1] = "GOARCH=" + buildarch
	env[2] = "GOPATH=" + os.Getenv("GOPATH")
	env[3] = "GOROOT=" + os.Getenv("GOROOT")
	env[4] = "PATH=" + os.Getenv("PATH")
	env[5] = "GO15VENDOREXPERIMENT=" + os.Getenv("GO15VENDOREXPERIMENT")
	for _, key := range []string{"TMP", "TEMP", "TEMPDIR"} {
		v := os.Getenv(key)
		if len(v) == 0 {
			continue
		}
		env = append(env, key+"="+v)
	}
	return env
}

func setupInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	textfiles := []string{
		"README.md", "CHANGELOG.md",
	}

	if buildos == "windows" {
		return winInstaller(textfiles, buildos, buildarch, dir, buildMatrix)
	} else {
		return unixInstaller(textfiles, buildos, buildarch, dir, buildMatrix)
	}
}

func unixInstaller(textfiles []string, buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	for _, filename := range textfiles {
		cmd := exec.Command("cp", filename, filepath.Join(dir, filename))
		if err := logAndRun(cmd); err != nil {
			return err
		}
	}

	fullInstallPath := filepath.Join(dir, "install.sh")
	cmd := exec.Command("cp", "script/install.sh.example", fullInstallPath)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	if err := os.Chmod(fullInstallPath, 0755); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"
	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	if err := logAndRun(cmd); err != nil {
		return nil
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)
	return nil
}

func winInstaller(textfiles []string, buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	for _, filename := range textfiles {
		by, err := ioutil.ReadFile(filename)
		if err != nil {
			return err
		}

		winEndings := strings.Replace(string(by), "\n", "\r\n", -1)
		err = ioutil.WriteFile(filepath.Join(dir, filename), []byte(winEndings), 0644)
		if err != nil {
			return err
		}
	}

	installerPath := filepath.Dir(filepath.Dir(dir))
	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd := exec.Command("zip", args...)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)
	return nil
}

func addToMatrix(buildMatrix map[string]Release, buildos, buildarch, name string) {
	buildMatrix[fmt.Sprintf("%s-%s", buildos, buildarch)] = Release{
		Label:    releaseLabel(buildos, buildarch),
		Filename: name,
		SHA256:   hashRelease(name),
	}
}

func hashRelease(name string) string {
	full := filepath.Join("bin/releases", name)
	file, err := os.Open(full)
	if err != nil {
		fmt.Printf("unable to open release %q: %+v\n", full, err)
		os.Exit(1)
	}

	defer file.Close()

	h := sha256.New()
	if _, err = io.Copy(h, file); err != nil {
		fmt.Printf("error reading release %q: %+v\n", full, err)
		os.Exit(1)
	}

	return hex.EncodeToString(h.Sum(nil))
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-lfs-%s-%s-%s", os, arch, config.Version)
}

func releaseLabel(buildos, buildarch string) string {
	return fmt.Sprintf("%s %s", key(buildos), key(buildarch))
}

func key(k string) string {
	if s, ok := matrixKeys[k]; ok {
		return s
	}
	return k
}
<commit_msg>bootstrap: set exe extension if built on windows<commit_after>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/git-lfs/git-lfs/config"
)

var (
	BuildOS      = flag.String("os", "", "OS to target: darwin,freebsd,linux,windows")
	BuildArch    = flag.String("arch", "", "Arch to target: 386,amd64")
	BuildAll     = flag.Bool("all", false, "Builds all architectures")
	BuildDwarf   = flag.Bool("dwarf", false, "Includes DWARF tables in build artifacts")
	BuildLdFlags = flag.String("ldflags", "", "-ldflags to pass to the compiler")
	BuildGcFlags = flag.String("gcflags", "", "-gcflags to pass to the compiler")
	ShowHelp     = flag.Bool("help", false, "Shows help")
	matrixKeys   = map[string]string{
		"darwin":  "Mac",
		"freebsd": "FreeBSD",
		"linux":   "Linux",
		"windows": "Windows",
		"amd64":   "AMD64",
	}
	LdFlags []string
)

func mainBuild() {
	if *ShowHelp {
		fmt.Println("usage: script/bootstrap [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	fmt.Printf("Using %s\n", runtime.Version())

	genOut, err := exec.Command("go", "generate", "./commands").CombinedOutput()
	if err != nil {
		fmt.Fprintf(os.Stderr, "go generate failed:\n%v", string(genOut))
		os.Exit(1)
	}
	cmd, _ := exec.Command("git", "rev-parse", "--short", "HEAD").Output()

	if len(cmd) > 0 {
		LdFlags = append(LdFlags, "-X", strings.TrimSpace(
			"github.com/git-lfs/git-lfs/config.GitCommit="+string(cmd),
		))
	}
	if !*BuildDwarf {
		LdFlags = append(LdFlags, "-s", "-w")
	}

	buildMatrix := make(map[string]Release)
	errored := false

	var platforms, arches []string
	if len(*BuildOS) > 0 {
		platforms = strings.Split(*BuildOS, ",")
	}
	if len(*BuildArch) > 0 {
		arches = strings.Split(*BuildArch, ",")
	}
	if *BuildAll {
		platforms = []string{"linux", "darwin", "freebsd", "windows"}
		arches = []string{"amd64", "386"}
	}

	if len(platforms) < 1 || len(arches) < 1 {
		if err := build("", "", buildMatrix); err != nil {
			log.Fatalln(err)
		}
		return // skip build matrix stuff
	}

	for _, buildos := range platforms {
		for _, buildarch := range arches {
			err := build(strings.TrimSpace(buildos), strings.TrimSpace(buildarch), buildMatrix)
			if err != nil {
				errored = true
			}
		}
	}

	if errored {
		os.Exit(1)
	}

	by, err := json.Marshal(buildMatrix)
	if err != nil {
		log.Fatalln("Error encoding build matrix to json:", err)
	}

	file, err := os.Create("bin/releases/build_matrix.json")
	if err != nil {
		log.Fatalln("Error creating build_matrix.json:", err)
	}

	written, err := file.Write(by)
	file.Close()

	if err != nil {
		log.Fatalln("Error writing build_matrix.json", err)
	}

	if jsonSize := len(by); written != jsonSize {
		log.Fatalf("Expected to write %d bytes, actually wrote %d.\n", jsonSize, written)
	}
}

func build(buildos, buildarch string, buildMatrix map[string]Release) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-lfs-" + config.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	if err := buildCommand(dir, buildos, buildarch); err != nil {
		return err
	}

	if addenv {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}

		err = setupInstaller(buildos, buildarch, dir, buildMatrix)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}
	}

	return nil
}

func buildCommand(dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0

	bin := filepath.Join(dir, "git-lfs")

	cmdOS := runtime.GOOS
	if len(buildos) > 0 {
		cmdOS = buildos
	}
	if cmdOS == "windows" {
		bin = bin + ".exe"
	}

	args := make([]string, 1, 6)
	args[0] = "build"
	if len(*BuildLdFlags) > 0 {
		args = append(args, "-ldflags", *BuildLdFlags)
	} else if len(LdFlags) > 0 {
		args = append(args, "-ldflags", strings.Join(LdFlags, " "))
	}

	if len(*BuildGcFlags) > 0 {
		args = append(args, "-gcflags", *BuildGcFlags)
	}

	args = append(args, "-o", bin, ".")

	cmd := exec.Command("go", args...)
	if addenv {
		cmd.Env = buildGoEnv(buildos, buildarch)
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func buildGoEnv(buildos, buildarch string) []string {
	env := make([]string, 6, 9)
	env[0] = "GOOS=" + buildos
	env[1] = "GOARCH=" + buildarch
	env[2] = "GOPATH=" + os.Getenv("GOPATH")
	env[3] = "GOROOT=" + os.Getenv("GOROOT")
	env[4] = "PATH=" + os.Getenv("PATH")
	env[5] = "GO15VENDOREXPERIMENT=" + os.Getenv("GO15VENDOREXPERIMENT")
	for _, key := range []string{"TMP", "TEMP", "TEMPDIR"} {
		v := os.Getenv(key)
		if len(v) == 0 {
			continue
		}
		env = append(env, key+"="+v)
	}
	return env
}

func setupInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	textfiles := []string{
		"README.md", "CHANGELOG.md",
	}

	if buildos == "windows" {
		return winInstaller(textfiles, buildos, buildarch, dir, buildMatrix)
	} else {
		return unixInstaller(textfiles, buildos, buildarch, dir, buildMatrix)
	}
}

func unixInstaller(textfiles []string, buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	for _, filename := range textfiles {
		cmd := exec.Command("cp", filename, filepath.Join(dir, filename))
		if err := logAndRun(cmd); err != nil {
			return err
		}
	}

	fullInstallPath := filepath.Join(dir, "install.sh")
	cmd := exec.Command("cp", "script/install.sh.example", fullInstallPath)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	if err := os.Chmod(fullInstallPath, 0755); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"
	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	if err := logAndRun(cmd); err != nil {
		return nil
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)
	return nil
}

func winInstaller(textfiles []string, buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	for _, filename := range textfiles {
		by, err := ioutil.ReadFile(filename)
		if err != nil {
			return err
		}

		winEndings := strings.Replace(string(by), "\n", "\r\n", -1)
		err = ioutil.WriteFile(filepath.Join(dir, filename), []byte(winEndings), 0644)
		if err != nil {
			return err
		}
	}

	installerPath := filepath.Dir(filepath.Dir(dir))
	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd := exec.Command("zip", args...)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)
	return nil
}

func addToMatrix(buildMatrix map[string]Release, buildos, buildarch, name string) {
	buildMatrix[fmt.Sprintf("%s-%s", buildos, buildarch)] = Release{
		Label:    releaseLabel(buildos, buildarch),
		Filename: name,
		SHA256:   hashRelease(name),
	}
}

func hashRelease(name string) string {
	full := filepath.Join("bin/releases", name)
	file, err := os.Open(full)
	if err != nil {
		fmt.Printf("unable to open release %q: %+v\n", full, err)
		os.Exit(1)
	}

	defer file.Close()

	h := sha256.New()
	if _, err = io.Copy(h, file); err != nil {
		fmt.Printf("error reading release %q: %+v\n", full, err)
		os.Exit(1)
	}

	return hex.EncodeToString(h.Sum(nil))
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-lfs-%s-%s-%s", os, arch, config.Version)
}

func releaseLabel(buildos, buildarch string) string {
	return fmt.Sprintf("%s %s", key(buildos), key(buildarch))
}

func key(k string) string {
	if s, ok := matrixKeys[k]; ok {
		return s
	}
	return k
}
<|endoftext|>


<commit_before>package trees

import (
	"errors"
	"sync"
	"time"

	"github.com/joushou/qp"
)

// ErrTerminatedRead indicates that a read was terminated because the file was
// closed.
var ErrTerminatedRead = errors.New("read terminated")

// BroadcastHandle implements the R/W access to the broadcasting mechanism of
// BroadcastFile.
type BroadcastHandle struct {
	sync.RWMutex
	f *BroadcastFile

	Readable bool
	Writable bool

	queue     [][]byte
	queueCond *sync.Cond

	curbuf     []byte
	curbufLock sync.RWMutex

	unwanted bool
}

// Seek is a noop on a BroadcastHandle.
func (h *BroadcastHandle) Seek(int64, int) (int64, error) {
	return 0, nil
}

// Read reads the current message, or if the end is reached, retrieves a new
// message. If no new messages are available, Read blocks to wait for one. It
// is woken up, returning ErrTerminatedRead if Close is called on the handle.
func (h *BroadcastHandle) Read(p []byte) (int, error) {
	h.RLock()
	if !h.Readable || h.f == nil {
		return 0, errors.New("file not open for reading")
	}
	h.RUnlock()

	h.curbufLock.Lock()
	defer h.curbufLock.Unlock()

	if h.curbuf == nil {
		// If we don't have a buffer, wait for one
		var err error
		h.curbuf, err = h.fetch()
		if err != nil {
			return 0, err
		}
	} else if len(h.curbuf) == 0 {
		// If our buffer is empty, clear it - next read will fetch us a new one.
		h.curbuf = nil
		return 0, nil
	}

	m := len(h.curbuf)
	if len(p) < m {
		m = len(p)
	}

	copy(p, h.curbuf[:m])
	h.curbuf = h.curbuf[m:]

	return m, nil
}

// Write adds a message to the BroadcastFile.
func (h *BroadcastHandle) Write(p []byte) (int, error) {
	h.RLock()
	defer h.RUnlock()
	if !h.Writable || h.f == nil {
		return 0, errors.New("file not open for writing")
	}
	h.f.Push(p)
	return len(p), nil
}

// fetch waits for a new message.
func (h *BroadcastHandle) fetch() ([]byte, error) {
	// We always read from the channel to avoid complex locking schemes.
	h.queueCond.L.Lock()
	defer h.queueCond.L.Unlock()

	for len(h.queue) == 0 && h.unwanted == false {
		h.queueCond.Wait()
	}

	if h.unwanted {
		return nil, ErrTerminatedRead
	}

	b := h.queue[0]
	h.queue = h.queue[1:]
	return b, nil
}

// push pushes a message to the local queue, waking up fetch as necessary.
func (h *BroadcastHandle) push(b []byte) {
	h.queueCond.L.Lock()
	defer h.queueCond.L.Unlock()
	h.queue = append(h.queue, b)
	h.queueCond.Signal()
}

// Close closes the handle, waking up any blocked reads.
func (h *BroadcastHandle) Close() error {
	h.Lock()
	defer h.Unlock()
	h.queue = nil
	h.unwanted = true
	h.queueCond.Broadcast()

	if h.Readable && h.f != nil {
		// Only readable files are registered
		h.f.deregister(h)
	}
	h.f = nil
	return nil
}

// BroadcastFile provides broadcast functionality. Writing to the file allows
// everyone who had the file open for reading during the write to read and
// receive the message.
type BroadcastFile struct {
	sync.RWMutex
	files []*BroadcastHandle

	*SyntheticFile
}

// Open returns a new BroadcastHandle. If it is readable, it is also
// registered on the list of open files to receive broadcast messages. Do note
// that BroadcastFile keeps a reference to all listening handles. A handle can
// therefore only be garbage collected if Close has been called on it.
func (f *BroadcastFile) Open(user string, mode qp.OpenMode) (ReadWriteSeekCloser, error) {
	if !f.CanOpen(user, mode) {
		return nil, errors.New("access denied")
	}

	f.Lock()
	defer f.Unlock()
	f.Atime = time.Now()

	readable := mode&3 == qp.OREAD || mode&3 == qp.OEXEC || mode&3 == qp.ORDWR
	writable := mode&3 == qp.OWRITE || mode&3 == qp.ORDWR

	x := &BroadcastHandle{
		f:         f,
		queueCond: sync.NewCond(&sync.Mutex{}),
		Readable:  readable,
		Writable:  writable,
	}

	if readable {
		// We only register readable files for the queue
		f.files = append(f.files, x)
	}

	return x, nil
}

// Push pushes a new broadcast message for all current listeners.
func (f *BroadcastFile) Push(b []byte) error {
	f.Lock()
	f.Version++
	f.Unlock()
	f.RLock()
	defer f.RUnlock()
	for _, h := range f.files {
		h.push(b)
	}
	return nil
}

// deregister removes a handle from the listener list.
func (f *BroadcastFile) deregister(h *BroadcastHandle) {
	f.Lock()
	defer f.Unlock()
	for i := range f.files {
		if f.files[i] == h {
			f.files = append(f.files[:i], f.files[i+1:]...)
			return
		}
	}
}

// NewBroadcastFile returns a new BroadcastFile.
func NewBroadcastFile(name string, permissions qp.FileMode, user, group string) *BroadcastFile {
	return &BroadcastFile{
		SyntheticFile: NewSyntheticFile(name, permissions, user, group),
	}
}
<commit_msg>Fix a lock issue in broadcastfile<commit_after>package trees

import (
	"errors"
	"sync"
	"time"

	"github.com/joushou/qp"
)

// ErrTerminatedRead indicates that a read was terminated because the file was
// closed.
var ErrTerminatedRead = errors.New("read terminated")

// BroadcastHandle implements the R/W access to the broadcasting mechanism of
// BroadcastFile.
type BroadcastHandle struct {
	sync.RWMutex
	f *BroadcastFile

	Readable bool
	Writable bool

	queue     [][]byte
	queueCond *sync.Cond

	curbuf     []byte
	curbufLock sync.RWMutex

	unwanted bool
}

// Seek is a noop on a BroadcastHandle.
func (h *BroadcastHandle) Seek(int64, int) (int64, error) {
	return 0, nil
}

// Read reads the current message, or if the end is reached, retrieves a new
// message. If no new messages are available, Read blocks to wait for one. It
// is woken up, returning ErrTerminatedRead if Close is called on the handle.
func (h *BroadcastHandle) Read(p []byte) (int, error) {
	h.RLock()
	if !h.Readable || h.f == nil {
		h.RUnlock()
		return 0, errors.New("file not open for reading")
	}
	h.RUnlock()

	h.curbufLock.Lock()
	defer h.curbufLock.Unlock()

	if h.curbuf == nil {
		// If we don't have a buffer, wait for one
		var err error
		h.curbuf, err = h.fetch()
		if err != nil {
			return 0, err
		}
	} else if len(h.curbuf) == 0 {
		// If our buffer is empty, clear it - next read will fetch us a new one.
		h.curbuf = nil
		return 0, nil
	}

	m := len(h.curbuf)
	if len(p) < m {
		m = len(p)
	}

	copy(p, h.curbuf[:m])
	h.curbuf = h.curbuf[m:]

	return m, nil
}

// Write adds a message to the BroadcastFile.
func (h *BroadcastHandle) Write(p []byte) (int, error) {
	h.RLock()
	defer h.RUnlock()
	if !h.Writable || h.f == nil {
		return 0, errors.New("file not open for writing")
	}
	h.f.Push(p)
	return len(p), nil
}

// fetch waits for a new message.
func (h *BroadcastHandle) fetch() ([]byte, error) {
	// We always read from the channel to avoid complex locking schemes.
	h.queueCond.L.Lock()
	defer h.queueCond.L.Unlock()

	for len(h.queue) == 0 && h.unwanted == false {
		h.queueCond.Wait()
	}

	if h.unwanted {
		return nil, ErrTerminatedRead
	}

	b := h.queue[0]
	h.queue = h.queue[1:]
	return b, nil
}

// push pushes a message to the local queue, waking up fetch as necessary.
func (h *BroadcastHandle) push(b []byte) {
	h.queueCond.L.Lock()
	defer h.queueCond.L.Unlock()
	h.queue = append(h.queue, b)
	h.queueCond.Signal()
}

// Close closes the handle, waking up any blocked reads.
func (h *BroadcastHandle) Close() error {
	h.Lock()
	defer h.Unlock()
	h.queue = nil
	h.unwanted = true
	h.queueCond.Broadcast()

	if h.Readable && h.f != nil {
		// Only readable files are registered
		h.f.deregister(h)
	}
	h.f = nil
	return nil
}

// BroadcastFile provides broadcast functionality. Writing to the file allows
// everyone who had the file open for reading during the write to read and
// receive the message.
type BroadcastFile struct {
	sync.RWMutex
	files []*BroadcastHandle

	*SyntheticFile
}

// Open returns a new BroadcastHandle. If it is readable, it is also
// registered on the list of open files to receive broadcast messages. Do note
// that BroadcastFile keeps a reference to all listening handles. A handle can
// therefore only be garbage collected if Close has been called on it.
func (f *BroadcastFile) Open(user string, mode qp.OpenMode) (ReadWriteSeekCloser, error) {
	if !f.CanOpen(user, mode) {
		return nil, errors.New("access denied")
	}

	f.Lock()
	defer f.Unlock()
	f.Atime = time.Now()

	readable := mode&3 == qp.OREAD || mode&3 == qp.OEXEC || mode&3 == qp.ORDWR
	writable := mode&3 == qp.OWRITE || mode&3 == qp.ORDWR

	x := &BroadcastHandle{
		f:         f,
		queueCond: sync.NewCond(&sync.Mutex{}),
		Readable:  readable,
		Writable:  writable,
	}

	if readable {
		// We only register readable files for the queue
		f.files = append(f.files, x)
	}

	return x, nil
}

// Push pushes a new broadcast message for all current listeners.
func (f *BroadcastFile) Push(b []byte) error {
	f.Lock()
	f.Version++
	f.Unlock()
	f.RLock()
	defer f.RUnlock()
	for _, h := range f.files {
		h.push(b)
	}
	return nil
}

// deregister removes a handle from the listener list.
func (f *BroadcastFile) deregister(h *BroadcastHandle) {
	f.Lock()
	defer f.Unlock()
	for i := range f.files {
		if f.files[i] == h {
			f.files = append(f.files[:i], f.files[i+1:]...)
			return
		}
	}
}

// NewBroadcastFile returns a new BroadcastFile.
func NewBroadcastFile(name string, permissions qp.FileMode, user, group string) *BroadcastFile {
	return &BroadcastFile{
		SyntheticFile: NewSyntheticFile(name, permissions, user, group),
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 csp

import (
	"context"
	"errors"
	"os"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/safehttptest"
)

type dummyReader struct{}

func (dummyReader) Read(b []byte) (int, error) {
	for i := range b {
		b[i] = 41
	}
	return len(b), nil
}

func TestMain(m *testing.M) {
	randReader = dummyReader{}
	os.Exit(m.Run())
}

func TestSerialize(t *testing.T) {
	tests := []struct {
		name       string
		policy     Policy
		wantString string
	}{
		{
			name:       "StrictCSP",
			policy:     StrictCSPBuilder{}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret'; base-uri 'none'",
		},
		{
			name:       "StrictCSP with strict-dynamic",
			policy:     StrictCSPBuilder{StrictDynamic: true}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret' 'strict-dynamic'; base-uri 'none'",
		},
		{
			name:       "StrictCSP with unsafe-eval",
			policy:     StrictCSPBuilder{UnsafeEval: true}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret' 'unsafe-eval'; base-uri 'none'",
		},
		{
			name:       "StrictCSP with set base-uri",
			policy:     StrictCSPBuilder{BaseURI: "https://example.com"}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret'; base-uri https://example.com",
		},
		{
			name:       "StrictCSP with report-uri",
			policy:     StrictCSPBuilder{ReportURI: "https://example.com/collector"}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret'; base-uri 'none'; report-uri https://example.com/collector",
		},
		{
			name:       "FramingCSP",
			policy:     FramingPolicyBuilder{}.Build(),
			wantString: "frame-ancestors 'self'",
		},
		{
			name:       "FramingCSP with report-uri",
			policy:     FramingPolicyBuilder{ReportURI: "httsp://example.com/collector"}.Build(),
			wantString: "frame-ancestors 'self'; report-uri httsp://example.com/collector",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := tt.policy.serialize("super-secret")

			if s != tt.wantString {
				t.Errorf("tt.policy.serialize() got: %q want: %q", s, tt.wantString)
			}
		})
	}
}

func TestBefore(t *testing.T) {
	tests := []struct {
		name                  string
		interceptor           Interceptor
		wantEnforcementPolicy []string
		wantReportOnlyPolicy  []string
		wantNonce             string
	}{
		{
			name:        "No policies",
			interceptor: Interceptor{},
			wantNonce:   "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
		{
			name:        "Default policies",
			interceptor: Default(""),
			wantEnforcementPolicy: []string{
				"object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-KSkpKSkpKSkpKSkpKSkpKSkpKSk='; base-uri 'none'",
				"frame-ancestors 'self'",
			},
			wantNonce: "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
		{
			name:        "Default policies with reporting URI",
			interceptor: Default("https://example.com/collector"),
			wantEnforcementPolicy: []string{
				"object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-KSkpKSkpKSkpKSkpKSkpKSkpKSk='; base-uri 'none'; report-uri https://example.com/collector",
				"frame-ancestors 'self'; report-uri https://example.com/collector",
			},
			wantNonce: "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
		{
			name:        "StrictCSP reportonly",
			interceptor: NewInterceptor(StrictCSPBuilder{ReportOnly: true, ReportURI: "https://example.com/collector"}.Build()),
			wantReportOnlyPolicy: []string{
				"object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-KSkpKSkpKSkpKSkpKSkpKSkpKSk='; base-uri 'none'; report-uri https://example.com/collector",
			},
			wantNonce: "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
		{
			name:                 "FramingCSP reportonly",
			interceptor:          NewInterceptor(FramingPolicyBuilder{ReportOnly: true, ReportURI: "https://example.com/collector"}.Build()),
			wantReportOnlyPolicy: []string{"frame-ancestors 'self'; report-uri https://example.com/collector"},
			wantNonce:            "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rr := safehttptest.NewResponseRecorder()
			req := safehttptest.NewRequest(safehttp.MethodGet, "/", nil)

			tt.interceptor.Before(rr.ResponseWriter, req)

			h := rr.Header()
			if diff := cmp.Diff(tt.wantEnforcementPolicy, h.Values("Content-Security-Policy"), cmpopts.EquateEmpty()); diff != "" {
				t.Errorf("h.Values(\"Content-Security-Policy\") mismatch (-want +got):\n%s", diff)
			}

			if diff := cmp.Diff(tt.wantReportOnlyPolicy, h.Values("Content-Security-Policy-Report-Only"), cmpopts.EquateEmpty()); diff != "" {
				t.Errorf("h.Values(\"Content-Security-Policy-Report-Only\") mismatch (-want +got):\n%s", diff)
			}

			v := req.Context().Value(ctxKey{})
			if v == nil {
				t.Fatalf("req.Context().Value(ctxKey{}) got: nil want: %q", tt.wantNonce)
			}
			if got := v.(string); got != tt.wantNonce {
				t.Errorf("v.(string) got: %q want: %q", got, tt.wantNonce)
			}
		})
	}
}

func TestAlreadyClaimed(t *testing.T) {
	headers := []string{
		"Content-Security-Policy",
		"Content-Security-Policy-Report-Only",
	}

	for _, h := range headers {
		t.Run(h, func(t *testing.T) {
			rr := safehttptest.NewResponseRecorder()
			if _, err := rr.ResponseWriter.Header().Claim(h); err != nil {
				t.Fatalf("rr.ResponseWriter.Header().Claim(h) got err: %v want: nil", err)
			}
			req := safehttptest.NewRequest(safehttp.MethodGet, "/", nil)

			it := Interceptor{}
			it.Before(rr.ResponseWriter, req)

			if got, want := rr.Status(), int(safehttp.StatusInternalServerError); got != want {
				t.Errorf("rr.Status() got: %v want: %v", got, want)
			}

			wantHeaders := map[string][]string{
				"Content-Type":           {"text/plain; charset=utf-8"},
				"X-Content-Type-Options": {"nosniff"},
			}
			if diff := cmp.Diff(wantHeaders, map[string][]string(rr.Header())); diff != "" {
				t.Errorf("rr.Header() mismatch (-want +got):\n%s", diff)
			}

			if got, want := rr.Body(), "Internal Server Error\n"; got != want {
				t.Errorf("rr.Body() got: %q want: %q", got, want)
			}
		})
	}
}

type errorReader struct{}

func (errorReader) Read(b []byte) (int, error) {
	return 0, errors.New("bad")
}

func TestPanicWhileGeneratingNonce(t *testing.T) {
	randReader = errorReader{}
	defer func() {
		if r := recover(); r == nil {
			t.Error("generateNonce() expected panic")
		}
	}()
	generateNonce()
}

func TestNonce(t *testing.T) {
	ctx := context.WithValue(context.Background(), ctxKey{}, "nonce")
	if got, want := Nonce(ctx), "nonce"; got != want {
		t.Errorf("Nonce(ctx) got: %v want: %v", got, want)
	}
}

func TestNonceEmptyContext(t *testing.T) {
	ctx := context.Background()
	if got, want := Nonce(ctx), ""; got != want {
		t.Errorf("Nonce(ctx) got: %v want: %v", got, want)
	}
}
<commit_msg>Renamed TestNonce, wantEnforcementPolicy and subtest name.<commit_after>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 csp

import (
	"context"
	"errors"
	"os"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/safehttptest"
)

type dummyReader struct{}

func (dummyReader) Read(b []byte) (int, error) {
	for i := range b {
		b[i] = 41
	}
	return len(b), nil
}

func TestMain(m *testing.M) {
	randReader = dummyReader{}
	os.Exit(m.Run())
}

func TestSerialize(t *testing.T) {
	tests := []struct {
		name       string
		policy     Policy
		wantString string
	}{
		{
			name:       "StrictCSP",
			policy:     StrictCSPBuilder{}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret'; base-uri 'none'",
		},
		{
			name:       "StrictCSP with strict-dynamic",
			policy:     StrictCSPBuilder{StrictDynamic: true}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret' 'strict-dynamic'; base-uri 'none'",
		},
		{
			name:       "StrictCSP with unsafe-eval",
			policy:     StrictCSPBuilder{UnsafeEval: true}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret' 'unsafe-eval'; base-uri 'none'",
		},
		{
			name:       "StrictCSP with set base-uri",
			policy:     StrictCSPBuilder{BaseURI: "https://example.com"}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret'; base-uri https://example.com",
		},
		{
			name:       "StrictCSP with report-uri",
			policy:     StrictCSPBuilder{ReportURI: "https://example.com/collector"}.Build(),
			wantString: "object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-super-secret'; base-uri 'none'; report-uri https://example.com/collector",
		},
		{
			name:       "FramingCSP",
			policy:     FramingPolicyBuilder{}.Build(),
			wantString: "frame-ancestors 'self'",
		},
		{
			name:       "FramingCSP with report-uri",
			policy:     FramingPolicyBuilder{ReportURI: "httsp://example.com/collector"}.Build(),
			wantString: "frame-ancestors 'self'; report-uri httsp://example.com/collector",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := tt.policy.serialize("super-secret")

			if s != tt.wantString {
				t.Errorf("tt.policy.serialize() got: %q want: %q", s, tt.wantString)
			}
		})
	}
}

func TestBefore(t *testing.T) {
	tests := []struct {
		name                 string
		interceptor          Interceptor
		wantEnforcPolicy     []string
		wantReportOnlyPolicy []string
		wantNonce            string
	}{
		{
			name:        "No policies",
			interceptor: Interceptor{},
			wantNonce:   "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
		{
			name:        "Default policies",
			interceptor: Default(""),
			wantEnforcPolicy: []string{
				"object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-KSkpKSkpKSkpKSkpKSkpKSkpKSk='; base-uri 'none'",
				"frame-ancestors 'self'",
			},
			wantNonce: "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
		{
			name:        "Default policies with reporting URI",
			interceptor: Default("https://example.com/collector"),
			wantEnforcPolicy: []string{
				"object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-KSkpKSkpKSkpKSkpKSkpKSkpKSk='; base-uri 'none'; report-uri https://example.com/collector",
				"frame-ancestors 'self'; report-uri https://example.com/collector",
			},
			wantNonce: "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
		{
			name:        "StrictCSP Report Only",
			interceptor: NewInterceptor(StrictCSPBuilder{ReportOnly: true, ReportURI: "https://example.com/collector"}.Build()),
			wantReportOnlyPolicy: []string{
				"object-src 'none'; script-src 'unsafe-inline' https: http: 'nonce-KSkpKSkpKSkpKSkpKSkpKSkpKSk='; base-uri 'none'; report-uri https://example.com/collector",
			},
			wantNonce: "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
		{
			name:                 "FramingCSP Report Only",
			interceptor:          NewInterceptor(FramingPolicyBuilder{ReportOnly: true, ReportURI: "https://example.com/collector"}.Build()),
			wantReportOnlyPolicy: []string{"frame-ancestors 'self'; report-uri https://example.com/collector"},
			wantNonce:            "KSkpKSkpKSkpKSkpKSkpKSkpKSk=",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rr := safehttptest.NewResponseRecorder()
			req := safehttptest.NewRequest(safehttp.MethodGet, "/", nil)

			tt.interceptor.Before(rr.ResponseWriter, req)

			h := rr.Header()
			if diff := cmp.Diff(tt.wantEnforcPolicy, h.Values("Content-Security-Policy"), cmpopts.EquateEmpty()); diff != "" {
				t.Errorf("h.Values(\"Content-Security-Policy\") mismatch (-want +got):\n%s", diff)
			}

			if diff := cmp.Diff(tt.wantReportOnlyPolicy, h.Values("Content-Security-Policy-Report-Only"), cmpopts.EquateEmpty()); diff != "" {
				t.Errorf("h.Values(\"Content-Security-Policy-Report-Only\") mismatch (-want +got):\n%s", diff)
			}

			v := req.Context().Value(ctxKey{})
			if v == nil {
				t.Fatalf("req.Context().Value(ctxKey{}) got: nil want: %q", tt.wantNonce)
			}
			if got := v.(string); got != tt.wantNonce {
				t.Errorf("v.(string) got: %q want: %q", got, tt.wantNonce)
			}
		})
	}
}

func TestAlreadyClaimed(t *testing.T) {
	headers := []string{
		"Content-Security-Policy",
		"Content-Security-Policy-Report-Only",
	}

	for _, h := range headers {
		t.Run(h, func(t *testing.T) {
			rr := safehttptest.NewResponseRecorder()
			if _, err := rr.ResponseWriter.Header().Claim(h); err != nil {
				t.Fatalf("rr.ResponseWriter.Header().Claim(h) got err: %v want: nil", err)
			}
			req := safehttptest.NewRequest(safehttp.MethodGet, "/", nil)

			it := Interceptor{}
			it.Before(rr.ResponseWriter, req)

			if got, want := rr.Status(), int(safehttp.StatusInternalServerError); got != want {
				t.Errorf("rr.Status() got: %v want: %v", got, want)
			}

			wantHeaders := map[string][]string{
				"Content-Type":           {"text/plain; charset=utf-8"},
				"X-Content-Type-Options": {"nosniff"},
			}
			if diff := cmp.Diff(wantHeaders, map[string][]string(rr.Header())); diff != "" {
				t.Errorf("rr.Header() mismatch (-want +got):\n%s", diff)
			}

			if got, want := rr.Body(), "Internal Server Error\n"; got != want {
				t.Errorf("rr.Body() got: %q want: %q", got, want)
			}
		})
	}
}

type errorReader struct{}

func (errorReader) Read(b []byte) (int, error) {
	return 0, errors.New("bad")
}

func TestPanicWhileGeneratingNonce(t *testing.T) {
	randReader = errorReader{}
	defer func() {
		if r := recover(); r == nil {
			t.Error("generateNonce() expected panic")
		}
	}()
	generateNonce()
}

func TestValidNonce(t *testing.T) {
	ctx := context.WithValue(context.Background(), ctxKey{}, "nonce")
	if got, want := Nonce(ctx), "nonce"; got != want {
		t.Errorf("Nonce(ctx) got: %v want: %v", got, want)
	}
}

func TestNonceEmptyContext(t *testing.T) {
	ctx := context.Background()
	if got, want := Nonce(ctx), ""; got != want {
		t.Errorf("Nonce(ctx) got: %v want: %v", got, want)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"sync"

	"github.com/rs/zerolog/log"
)

// BitbucketEventWorker processes events pushed to channels
type BitbucketEventWorker interface {
	ListenToEventChannels()
	Stop()
	CreateJobForBitbucketPush(BitbucketRepositoryPushEvent)
}

type bitbucketEventWorkerImpl struct {
	WaitGroup   *sync.WaitGroup
	QuitChannel chan bool
}

func newBitbucketEventWorker(waitGroup *sync.WaitGroup) BitbucketEventWorker {
	return &bitbucketEventWorkerImpl{
		WaitGroup:   waitGroup,
		QuitChannel: make(chan bool)}
}

func (w *bitbucketEventWorkerImpl) ListenToEventChannels() {
	go func() {
		// handle github events via channels
		log.Debug().Msg("Listening to Bitbucket events channels...")
		for {
			select {
			case pushEvent := <-bitbucketPushEvents:
				go func() {
					w.WaitGroup.Add(1)
					w.CreateJobForBitbucketPush(pushEvent)
					w.WaitGroup.Done()
				}()
			case <-w.QuitChannel:
				log.Debug().Msg("Stopping Bitbucket event worker...")
				return
			}
		}
	}()
}

func (w *bitbucketEventWorkerImpl) Stop() {
	go func() {
		w.QuitChannel <- true
	}()
}

func (w *bitbucketEventWorkerImpl) CreateJobForBitbucketPush(pushEvent BitbucketRepositoryPushEvent) {

	// check to see that it's a cloneable event
	if len(pushEvent.Push.Changes) == 0 || pushEvent.Push.Changes[0].New == nil || pushEvent.Push.Changes[0].New.Type != "branch" || len(pushEvent.Push.Changes[0].New.Target.Hash) == 0 {
		return
	}

	// create bitbucket api client
	bbClient := newBitbucketAPIClient(*bitbucketAPIKey, *bitbucketAppOAuthKey, *bitbucketAppOAuthSecret)

	// get access token
	accessToken, err := bbClient.GetAccessToken()

	// get manifest file
	manifest, err := bbClient.GetEstafetteManifest(accessToken, pushEvent)
	if err != nil {
		log.Error().Err(err).
			Msg("Retrieving Estafettte manifest failed")
		return
	}

	if manifest == "" {
		log.Info().Interface("pushEvent", pushEvent).Msgf("No Estaffette manifest for repo %v and revision %v, not creating a job", pushEvent.Repository.FullName, pushEvent.Push.Changes[0].New.Target.Hash)
		return
	}

	// get authenticated url for the repository
	authenticatedRepositoryURL, err := bbClient.GetAuthenticatedRepositoryURL(accessToken, pushEvent.Repository.Links.HTML.Href)
	if err != nil {
		log.Error().Err(err).
			Msg("Retrieving authenticated repository failed")
		return
	}

	// create ci builder client
	ciBuilderClient, err := newCiBuilderClient()
	if err != nil {
		log.Error().Err(err).Msg("Initializing ci builder client failed")
		return
	}

	// define ci builder params
	ciBuilderParams := CiBuilderParams{
		RepoFullName:         pushEvent.Repository.FullName,
		RepoURL:              authenticatedRepositoryURL,
		RepoBranch:           pushEvent.Push.Changes[0].New.Name,
		RepoRevision:         pushEvent.Push.Changes[0].New.Target.Hash,
		EnvironmentVariables: map[string]string{"ESTAFETTE_BITBUCKET_API_TOKEN": accessToken.AccessToken},
	}

	// create ci builder job
	_, err = ciBuilderClient.CreateCiBuilderJob(ciBuilderParams)
	if err != nil {
		log.Error().Err(err).
			Str("fullname", ciBuilderParams.RepoFullName).
			Str("url", ciBuilderParams.RepoURL).
			Str("branch", ciBuilderParams.RepoBranch).
			Str("revision", ciBuilderParams.RepoRevision).
			Msgf("Creating estafette-ci-builder job for Bitbucket repository %v revision %v failed", ciBuilderParams.RepoFullName, ciBuilderParams.RepoRevision)

		return
	}

	log.Info().
		Str("fullname", ciBuilderParams.RepoFullName).
		Str("url", ciBuilderParams.RepoURL).
		Str("branch", ciBuilderParams.RepoBranch).
		Str("revision", ciBuilderParams.RepoRevision).
		Msgf("Created estafette-ci-builder job for Bitbucket repository %v revision %v", ciBuilderParams.RepoFullName, ciBuilderParams.RepoRevision)
}
<commit_msg>debug log when manifest exists<commit_after>package main

import (
	"sync"

	"github.com/rs/zerolog/log"
)

// BitbucketEventWorker processes events pushed to channels
type BitbucketEventWorker interface {
	ListenToEventChannels()
	Stop()
	CreateJobForBitbucketPush(BitbucketRepositoryPushEvent)
}

type bitbucketEventWorkerImpl struct {
	WaitGroup   *sync.WaitGroup
	QuitChannel chan bool
}

func newBitbucketEventWorker(waitGroup *sync.WaitGroup) BitbucketEventWorker {
	return &bitbucketEventWorkerImpl{
		WaitGroup:   waitGroup,
		QuitChannel: make(chan bool)}
}

func (w *bitbucketEventWorkerImpl) ListenToEventChannels() {
	go func() {
		// handle github events via channels
		log.Debug().Msg("Listening to Bitbucket events channels...")
		for {
			select {
			case pushEvent := <-bitbucketPushEvents:
				go func() {
					w.WaitGroup.Add(1)
					w.CreateJobForBitbucketPush(pushEvent)
					w.WaitGroup.Done()
				}()
			case <-w.QuitChannel:
				log.Debug().Msg("Stopping Bitbucket event worker...")
				return
			}
		}
	}()
}

func (w *bitbucketEventWorkerImpl) Stop() {
	go func() {
		w.QuitChannel <- true
	}()
}

func (w *bitbucketEventWorkerImpl) CreateJobForBitbucketPush(pushEvent BitbucketRepositoryPushEvent) {

	// check to see that it's a cloneable event
	if len(pushEvent.Push.Changes) == 0 || pushEvent.Push.Changes[0].New == nil || pushEvent.Push.Changes[0].New.Type != "branch" || len(pushEvent.Push.Changes[0].New.Target.Hash) == 0 {
		return
	}

	// create bitbucket api client
	bbClient := newBitbucketAPIClient(*bitbucketAPIKey, *bitbucketAppOAuthKey, *bitbucketAppOAuthSecret)

	// get access token
	accessToken, err := bbClient.GetAccessToken()

	// get manifest file
	manifest, err := bbClient.GetEstafetteManifest(accessToken, pushEvent)
	if err != nil {
		log.Error().Err(err).
			Msg("Retrieving Estafettte manifest failed")
		return
	}

	if manifest == "" {
		log.Info().Interface("pushEvent", pushEvent).Msgf("No Estaffette manifest for repo %v and revision %v, not creating a job", pushEvent.Repository.FullName, pushEvent.Push.Changes[0].New.Target.Hash)
		return
	}
	log.Debug().Interface("pushEvent", pushEvent).Str("manifest", manifest).Msgf(" Estaffette manifest for repo %v and revision %v exists creating a builder job...", pushEvent.Repository.FullName, pushEvent.Push.Changes[0].New.Target.Hash)

	// get authenticated url for the repository
	authenticatedRepositoryURL, err := bbClient.GetAuthenticatedRepositoryURL(accessToken, pushEvent.Repository.Links.HTML.Href)
	if err != nil {
		log.Error().Err(err).
			Msg("Retrieving authenticated repository failed")
		return
	}

	// create ci builder client
	ciBuilderClient, err := newCiBuilderClient()
	if err != nil {
		log.Error().Err(err).Msg("Initializing ci builder client failed")
		return
	}

	// define ci builder params
	ciBuilderParams := CiBuilderParams{
		RepoFullName:         pushEvent.Repository.FullName,
		RepoURL:              authenticatedRepositoryURL,
		RepoBranch:           pushEvent.Push.Changes[0].New.Name,
		RepoRevision:         pushEvent.Push.Changes[0].New.Target.Hash,
		EnvironmentVariables: map[string]string{"ESTAFETTE_BITBUCKET_API_TOKEN": accessToken.AccessToken},
	}

	// create ci builder job
	_, err = ciBuilderClient.CreateCiBuilderJob(ciBuilderParams)
	if err != nil {
		log.Error().Err(err).
			Str("fullname", ciBuilderParams.RepoFullName).
			Str("url", ciBuilderParams.RepoURL).
			Str("branch", ciBuilderParams.RepoBranch).
			Str("revision", ciBuilderParams.RepoRevision).
			Msgf("Creating estafette-ci-builder job for Bitbucket repository %v revision %v failed", ciBuilderParams.RepoFullName, ciBuilderParams.RepoRevision)

		return
	}

	log.Info().
		Str("fullname", ciBuilderParams.RepoFullName).
		Str("url", ciBuilderParams.RepoURL).
		Str("branch", ciBuilderParams.RepoBranch).
		Str("revision", ciBuilderParams.RepoRevision).
		Msgf("Created estafette-ci-builder job for Bitbucket repository %v revision %v", ciBuilderParams.RepoFullName, ciBuilderParams.RepoRevision)
}
<|endoftext|>