<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package poll

import (
	"fmt"
	"strings"
	"time"

	"github.com/ava-labs/avalanchego/utils/linkedhashmap"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/utils/metric"
)

type PollHolder interface {
	GetPoll() Poll
	StartTime() time.Time
}

type poll struct {
	Poll
	start time.Time
}

func (p poll) GetPoll() Poll {
	return p
}

func (p poll) StartTime() time.Time {
	return p.start
}

type set struct {
	log      logging.Logger
	numPolls prometheus.Gauge
	durPolls prometheus.Histogram
	factory  Factory
	// maps requestID -> poll
	polls linkedhashmap.LinkedHashmap
}

// NewSet returns a new empty set of polls
func NewSet(
	factory Factory,
	log logging.Logger,
	namespace string,
	registerer prometheus.Registerer,
) Set {
	numPolls := prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: namespace,
		Name:      "polls",
		Help:      "Number of pending network polls",
	})
	if err := registerer.Register(numPolls); err != nil {
		log.Error("failed to register polls statistics due to %s", err)
	}

	durPolls := prometheus.NewHistogram(prometheus.HistogramOpts{
		Namespace: namespace,
		Name:      "poll_duration",
		Help:      "Length of time the poll existed in milliseconds",
		Buckets:   metric.MillisecondsBuckets,
	})
	if err := registerer.Register(durPolls); err != nil {
		log.Error("failed to register poll_duration statistics due to %s", err)
	}

	return &set{
		log:      log,
		numPolls: numPolls,
		durPolls: durPolls,
		factory:  factory,
		polls:    linkedhashmap.New(),
	}
}

// Add to the current set of polls
// Returns true if the poll was registered correctly and the network sample
//         should be made.
func (s *set) Add(requestID uint32, vdrs ids.ShortBag) bool {
	if _, exists := s.polls.Get(requestID); exists {
		s.log.Debug("dropping poll due to duplicated requestID: %d", requestID)
		return false
	}

	s.log.Verbo("creating poll with requestID %d and validators %s",
		requestID,
		&vdrs)

	s.polls.Put(requestID, poll{
		Poll:  s.factory.New(vdrs), // create the new poll
		start: time.Now(),
	})
	s.numPolls.Inc() // increase the metrics
	return true
}

// Vote registers the connections response to a query for [id]. If there was no
// query, or the response has already be registered, nothing is performed.
func (s *set) Vote(requestID uint32, vdr ids.ShortID, vote ids.ID) ([]ids.Bag, bool) {
	pollHolderIntf, exists := s.polls.Get(requestID)
	if !exists {
		s.log.Verbo("dropping vote from %s to an unknown poll with requestID: %d",
			vdr,
			requestID)
		return []ids.Bag{}, false
	}

	pollHolder := pollHolderIntf.(PollHolder)
	poll := pollHolder.GetPoll()

	s.log.Verbo("processing vote from %s in the poll with requestID: %d with the vote %s",
		vdr,
		requestID,
		vote)

	poll.Vote(vdr, vote)
	if !poll.Finished() {
		return []ids.Bag{}, false
	}

	s.log.Verbo("poll with requestID %d finished as %s", requestID, poll)

	s.polls.Delete(requestID) // remove the poll from the current set
	s.durPolls.Observe(float64(time.Since(pollHolder.StartTime()).Milliseconds()))
	s.numPolls.Dec() // decrease the metrics

	result := poll.Result()
	return []ids.Bag{result}, true
}

// Drop registers the connections response to a query for [id]. If there was no
// query, or the response has already be registered, nothing is performed.
func (s *set) Drop(requestID uint32, vdr ids.ShortID) ([]ids.Bag, bool) {
	pollHolderIntf, exists := s.polls.Get(requestID)
	if !exists {
		s.log.Verbo("dropping vote from %s to an unknown poll with requestID: %d",
			vdr,
			requestID)
		return []ids.Bag{}, false
	}

	s.log.Verbo("processing dropped vote from %s in the poll with requestID: %d",
		vdr,
		requestID)

	pollHolder := pollHolderIntf.(PollHolder)
	poll := pollHolder.GetPoll()

	poll.Drop(vdr)
	if !poll.Finished() {
		return []ids.Bag{}, false
	}

	s.log.Verbo("poll with requestID %d finished as %s", requestID, poll)

	s.polls.Delete(requestID) // remove the poll from the current set
	s.durPolls.Observe(float64(time.Since(pollHolder.StartTime()).Milliseconds()))
	s.numPolls.Dec() // decrease the metrics
	return []ids.Bag{poll.Result()}, true
}

// Len returns the number of outstanding polls
func (s *set) Len() int { return s.polls.Len() }

func (s *set) String() string {
	sb := strings.Builder{}
	sb.WriteString(fmt.Sprintf("current polls: (Size = %d)", s.polls.Len()))
	iter := s.polls.NewIterator()
	for iter.Next() {
		requestID := iter.Key()
		poll := iter.Value().(Poll)
		sb.WriteString(fmt.Sprintf("\n    %d: %s", requestID, poll.PrefixedString("    ")))
	}
	return sb.String()
}
<commit_msg>unexport<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package poll

import (
	"fmt"
	"strings"
	"time"

	"github.com/ava-labs/avalanchego/utils/linkedhashmap"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/utils/metric"
)

type pollHolder interface {
	GetPoll() Poll
	StartTime() time.Time
}

type poll struct {
	Poll
	start time.Time
}

func (p poll) GetPoll() Poll {
	return p
}

func (p poll) StartTime() time.Time {
	return p.start
}

type set struct {
	log      logging.Logger
	numPolls prometheus.Gauge
	durPolls prometheus.Histogram
	factory  Factory
	// maps requestID -> poll
	polls linkedhashmap.LinkedHashmap
}

// NewSet returns a new empty set of polls
func NewSet(
	factory Factory,
	log logging.Logger,
	namespace string,
	registerer prometheus.Registerer,
) Set {
	numPolls := prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: namespace,
		Name:      "polls",
		Help:      "Number of pending network polls",
	})
	if err := registerer.Register(numPolls); err != nil {
		log.Error("failed to register polls statistics due to %s", err)
	}

	durPolls := prometheus.NewHistogram(prometheus.HistogramOpts{
		Namespace: namespace,
		Name:      "poll_duration",
		Help:      "Length of time the poll existed in milliseconds",
		Buckets:   metric.MillisecondsBuckets,
	})
	if err := registerer.Register(durPolls); err != nil {
		log.Error("failed to register poll_duration statistics due to %s", err)
	}

	return &set{
		log:      log,
		numPolls: numPolls,
		durPolls: durPolls,
		factory:  factory,
		polls:    linkedhashmap.New(),
	}
}

// Add to the current set of polls
// Returns true if the poll was registered correctly and the network sample
//         should be made.
func (s *set) Add(requestID uint32, vdrs ids.ShortBag) bool {
	if _, exists := s.polls.Get(requestID); exists {
		s.log.Debug("dropping poll due to duplicated requestID: %d", requestID)
		return false
	}

	s.log.Verbo("creating poll with requestID %d and validators %s",
		requestID,
		&vdrs)

	s.polls.Put(requestID, poll{
		Poll:  s.factory.New(vdrs), // create the new poll
		start: time.Now(),
	})
	s.numPolls.Inc() // increase the metrics
	return true
}

// Vote registers the connections response to a query for [id]. If there was no
// query, or the response has already be registered, nothing is performed.
func (s *set) Vote(requestID uint32, vdr ids.ShortID, vote ids.ID) ([]ids.Bag, bool) {
	pollHolderIntf, exists := s.polls.Get(requestID)
	if !exists {
		s.log.Verbo("dropping vote from %s to an unknown poll with requestID: %d",
			vdr,
			requestID)
		return []ids.Bag{}, false
	}

	pollHolder := pollHolderIntf.(pollHolder)
	poll := pollHolder.GetPoll()

	s.log.Verbo("processing vote from %s in the poll with requestID: %d with the vote %s",
		vdr,
		requestID,
		vote)

	poll.Vote(vdr, vote)
	if !poll.Finished() {
		return []ids.Bag{}, false
	}

	s.log.Verbo("poll with requestID %d finished as %s", requestID, poll)

	s.polls.Delete(requestID) // remove the poll from the current set
	s.durPolls.Observe(float64(time.Since(pollHolder.StartTime()).Milliseconds()))
	s.numPolls.Dec() // decrease the metrics

	result := poll.Result()
	return []ids.Bag{result}, true
}

// Drop registers the connections response to a query for [id]. If there was no
// query, or the response has already be registered, nothing is performed.
func (s *set) Drop(requestID uint32, vdr ids.ShortID) ([]ids.Bag, bool) {
	pollHolderIntf, exists := s.polls.Get(requestID)
	if !exists {
		s.log.Verbo("dropping vote from %s to an unknown poll with requestID: %d",
			vdr,
			requestID)
		return []ids.Bag{}, false
	}

	s.log.Verbo("processing dropped vote from %s in the poll with requestID: %d",
		vdr,
		requestID)

	pollHolder := pollHolderIntf.(pollHolder)
	poll := pollHolder.GetPoll()

	poll.Drop(vdr)
	if !poll.Finished() {
		return []ids.Bag{}, false
	}

	s.log.Verbo("poll with requestID %d finished as %s", requestID, poll)

	s.polls.Delete(requestID) // remove the poll from the current set
	s.durPolls.Observe(float64(time.Since(pollHolder.StartTime()).Milliseconds()))
	s.numPolls.Dec() // decrease the metrics
	return []ids.Bag{poll.Result()}, true
}

// Len returns the number of outstanding polls
func (s *set) Len() int { return s.polls.Len() }

func (s *set) String() string {
	sb := strings.Builder{}
	sb.WriteString(fmt.Sprintf("current polls: (Size = %d)", s.polls.Len()))
	iter := s.polls.NewIterator()
	for iter.Next() {
		requestID := iter.Key()
		poll := iter.Value().(Poll)
		sb.WriteString(fmt.Sprintf("\n    %d: %s", requestID, poll.PrefixedString("    ")))
	}
	return sb.String()
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"time"

	"github.com/flynn/go-tuf/Godeps/_workspace/src/github.com/boltdb/bolt"
)

func MemoryLocalStore() LocalStore {
	return make(memoryLocalStore)
}

type memoryLocalStore map[string]json.RawMessage

func (m memoryLocalStore) GetMeta() (map[string]json.RawMessage, error) {
	return m, nil
}

func (m memoryLocalStore) SetMeta(name string, meta json.RawMessage) error {
	m[name] = meta
	return nil
}

const dbBucket = "tuf-client"

func FileLocalStore(path string) (LocalStore, error) {
	db, err := bolt.Open(path, 0600, &bolt.Options{Timeout: time.Second})
	if err != nil {
		return nil, err
	}
	if err := db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(dbBucket))
		return err
	}); err != nil {
		return nil, err
	}
	return &fileLocalStore{db: db}, nil
}

type fileLocalStore struct {
	db *bolt.DB
}

func (f *fileLocalStore) GetMeta() (map[string]json.RawMessage, error) {
	meta := make(map[string]json.RawMessage)
	if err := f.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(dbBucket))
		b.ForEach(func(k, v []byte) error {
			meta[string(k)] = v
			return nil
		})
		return nil
	}); err != nil {
		return nil, err
	}
	return meta, nil
}

func (f *fileLocalStore) SetMeta(name string, meta json.RawMessage) error {
	return f.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(dbBucket))
		return b.Put([]byte(name), meta)
	})
}
<commit_msg>Ensure we copy boltdb values<commit_after>package client

import (
	"encoding/json"
	"time"

	"github.com/flynn/go-tuf/Godeps/_workspace/src/github.com/boltdb/bolt"
)

func MemoryLocalStore() LocalStore {
	return make(memoryLocalStore)
}

type memoryLocalStore map[string]json.RawMessage

func (m memoryLocalStore) GetMeta() (map[string]json.RawMessage, error) {
	return m, nil
}

func (m memoryLocalStore) SetMeta(name string, meta json.RawMessage) error {
	m[name] = meta
	return nil
}

const dbBucket = "tuf-client"

func FileLocalStore(path string) (LocalStore, error) {
	db, err := bolt.Open(path, 0600, &bolt.Options{Timeout: time.Second})
	if err != nil {
		return nil, err
	}
	if err := db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(dbBucket))
		return err
	}); err != nil {
		return nil, err
	}
	return &fileLocalStore{db: db}, nil
}

type fileLocalStore struct {
	db *bolt.DB
}

func (f *fileLocalStore) GetMeta() (map[string]json.RawMessage, error) {
	meta := make(map[string]json.RawMessage)
	if err := f.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(dbBucket))
		b.ForEach(func(k, v []byte) error {
			vcopy := make([]byte, len(v))
			copy(vcopy, v)
			meta[string(k)] = vcopy
			return nil
		})
		return nil
	}); err != nil {
		return nil, err
	}
	return meta, nil
}

func (f *fileLocalStore) SetMeta(name string, meta json.RawMessage) error {
	return f.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(dbBucket))
		return b.Put([]byte(name), meta)
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package revel

import (
	"io"
	"net/http"
	"strconv"
	"strings"

	"github.com/klauspost/compress/gzip"
	"github.com/klauspost/compress/zlib"
)

var compressionTypes = [...]string{
	"gzip",
	"deflate",
}

var compressableMimes = [...]string{
	"text/plain",
	"text/html",
	"text/xml",
	"text/css",
	"application/json",
	"application/xml",
	"application/xhtml+xml",
	"application/rss+xml",
	"application/javascript",
	"application/x-javascript",
}

// Local log instance for this class
var compressLog = RevelLog.New("section", "compress")

// WriteFlusher interface for compress writer
type WriteFlusher interface {
	io.Writer
	io.Closer
	Flush() error
}

type CompressResponseWriter struct {
	Header             *BufferedServerHeader
	ControllerResponse *Response
	OriginalWriter     io.Writer
	compressWriter     WriteFlusher
	compressionType    string
	headersWritten     bool
	closeNotify        chan bool
	parentNotify       <-chan bool
	closed             bool
}

// CompressFilter does compression of response body in gzip/deflate if
// `results.compressed=true` in the app.conf
func CompressFilter(c *Controller, fc []Filter) {
	if c.Response.Out.internalHeader.Server != nil && Config.BoolDefault("results.compressed", false) {
		if c.Response.Status != http.StatusNoContent && c.Response.Status != http.StatusNotModified {
			if found, compressType, compressWriter := detectCompressionType(c.Request, c.Response); found {
				writer := CompressResponseWriter{
					ControllerResponse: c.Response,
					OriginalWriter:     c.Response.GetWriter(),
					compressWriter:     compressWriter,
					compressionType:    compressType,
					headersWritten:     false,
					closeNotify:        make(chan bool, 1),
					closed:             false,
				}
				// Swap out the header with our own
				writer.Header = NewBufferedServerHeader(c.Response.Out.internalHeader.Server)
				c.Response.Out.internalHeader.Server = writer.Header
				if w, ok := c.Response.GetWriter().(http.CloseNotifier); ok {
					writer.parentNotify = w.CloseNotify()
				}
				c.Response.SetWriter(&writer)
			}
		} else {
			compressLog.Debug("CompressFilter: Compression disabled for response ", "status", c.Response.Status)
		}
	}
	fc[0](c, fc[1:])
}

func (c CompressResponseWriter) CloseNotify() <-chan bool {
	if c.parentNotify != nil {
		return c.parentNotify
	}
	return c.closeNotify
}

func (c *CompressResponseWriter) cancel() {
	c.closed = true
}
func (c *CompressResponseWriter) prepareHeaders() {
	if c.compressionType != "" {
		responseMime := ""
		if t := c.Header.Get("Content-Type"); len(t) > 0 {
			responseMime = t[0]
		}
		responseMime = strings.TrimSpace(strings.SplitN(responseMime, ";", 2)[0])
		shouldEncode := false

		if len(c.Header.Get("Content-Encoding")) == 0 {
			for _, compressableMime := range compressableMimes {
				if responseMime == compressableMime {
					shouldEncode = true
					c.Header.Set("Content-Encoding", c.compressionType)
					c.Header.Del("Content-Length")
					break
				}
			}
		}

		if !shouldEncode {
			c.compressWriter = nil
			c.compressionType = ""
		}
	}
	c.Header.Release()
}

func (c *CompressResponseWriter) WriteHeader(status int) {
	if c.closed {
		return
	}
	c.headersWritten = true
	c.prepareHeaders()
	c.Header.SetStatus(status)
}

func (c *CompressResponseWriter) Close() error {
	if c.closed {
		return nil
	}
	if !c.headersWritten {
		c.prepareHeaders()
	}
	if c.compressionType != "" {
		c.Header.Del("Content-Length")
		if err := c.compressWriter.Close(); err != nil {
			// TODO When writing directly to stream, an error will be generated
			compressLog.Error("Close: Error closing compress writer", "type", c.compressionType, "error", err)
		}

	}
	// Non-blocking write to the closenotifier, if we for some reason should
	// get called multiple times
	select {
	case c.closeNotify <- true:
	default:
	}
	c.closed = true
	return nil
}

func (c *CompressResponseWriter) Write(b []byte) (int, error) {
	if c.closed {
		return 0, io.ErrClosedPipe
	}
	// Abort if parent has been closed
	if c.parentNotify != nil {
		select {
		case <-c.parentNotify:
			return 0, io.ErrClosedPipe
		default:
		}
	}
	// Abort if we ourselves have been closed
	if c.closed {
		return 0, io.ErrClosedPipe
	}

	if !c.headersWritten {
		c.prepareHeaders()
		c.headersWritten = true
	}
	if c.compressionType != "" {
		return c.compressWriter.Write(b)
	}
	return c.OriginalWriter.Write(b)
}

// DetectCompressionType method detects the compression type
// from header "Accept-Encoding"
func detectCompressionType(req *Request, resp *Response) (found bool, compressionType string, compressionKind WriteFlusher) {
	if Config.BoolDefault("results.compressed", false) {
		acceptedEncodings := strings.Split(req.GetHttpHeader("Accept-Encoding"), ",")

		largestQ := 0.0
		chosenEncoding := len(compressionTypes)

		// I have fixed one edge case for issue #914
		// But it's better to cover all possible edge cases or
		// Adapt to https://github.com/golang/gddo/blob/master/httputil/header/header.go#L172
		for _, encoding := range acceptedEncodings {
			encoding = strings.TrimSpace(encoding)
			encodingParts := strings.SplitN(encoding, ";", 2)

			// If we are the format "gzip;q=0.8"
			if len(encodingParts) > 1 {
				q := strings.TrimSpace(encodingParts[1])
				if len(q) == 0 || !strings.HasPrefix(q, "q=") {
					continue
				}

				// Strip off the q=
				num, err := strconv.ParseFloat(q[2:], 32)
				if err != nil {
					continue
				}

				if num >= largestQ && num > 0 {
					if encodingParts[0] == "*" {
						chosenEncoding = 0
						largestQ = num
						continue
					}
					for i, encoding := range compressionTypes {
						if encoding == encodingParts[0] {
							if i < chosenEncoding {
								largestQ = num
								chosenEncoding = i
							}
							break
						}
					}
				}
			} else {
				// If we can accept anything, chose our preferred method.
				if encodingParts[0] == "*" {
					chosenEncoding = 0
					largestQ = 1
					break
				}
				// This is for just plain "gzip"
				for i, encoding := range compressionTypes {
					if encoding == encodingParts[0] {
						if i < chosenEncoding {
							largestQ = 1.0
							chosenEncoding = i
						}
						break
					}
				}
			}
		}

		if largestQ == 0 {
			return
		}

		compressionType = compressionTypes[chosenEncoding]

		switch compressionType {
		case "gzip":
			compressionKind = gzip.NewWriter(resp.GetWriter())
			found = true
		case "deflate":
			compressionKind = zlib.NewWriter(resp.GetWriter())
			found = true
		}
	}
	return
}

// BufferedServerHeader will not send content out until the Released is called, from that point on it will act normally
// It implements all the ServerHeader
type BufferedServerHeader struct {
	cookieList []string
	headerMap  map[string][]string
	status     int
	released   bool
	original   ServerHeader
}

func NewBufferedServerHeader(o ServerHeader) *BufferedServerHeader {
	return &BufferedServerHeader{original: o, headerMap: map[string][]string{}}
}
func (bsh *BufferedServerHeader) SetCookie(cookie string) {
	if bsh.released {
		bsh.original.SetCookie(cookie)
	} else {
		bsh.cookieList = append(bsh.cookieList, cookie)
	}
}
func (bsh *BufferedServerHeader) GetCookie(key string) (ServerCookie, error) {
	return bsh.original.GetCookie(key)
}
func (bsh *BufferedServerHeader) Set(key string, value string) {
	if bsh.released {
		bsh.original.Set(key, value)
	} else {
		bsh.headerMap[key] = []string{value}
	}
}
func (bsh *BufferedServerHeader) Add(key string, value string) {
	if bsh.released {
		bsh.original.Set(key, value)
	} else {
		old := []string{}
		if v, found := bsh.headerMap[key]; found {
			old = v
		}
		bsh.headerMap[key] = append(old, value)
	}

}
func (bsh *BufferedServerHeader) Del(key string) {
	if bsh.released {
		bsh.original.Del(key)
	} else {
		delete(bsh.headerMap, key)
	}

}
func (bsh *BufferedServerHeader) Get(key string) (value []string) {
	if bsh.released {
		value = bsh.original.Get(key)
	} else {
		if v, found := bsh.headerMap[key]; found && len(v) > 0 {
			value = v
		} else {
			value = bsh.original.Get(key)
		}
	}
	return
}
func (bsh *BufferedServerHeader) SetStatus(statusCode int) {
	if bsh.released {
		bsh.original.SetStatus(statusCode)
	} else {
		bsh.status = statusCode
	}
}
func (bsh *BufferedServerHeader) Release() {
	bsh.released = true
	for k, v := range bsh.headerMap {
		for _, r := range v {
			bsh.original.Set(k, r)
		}
	}
	for _, c := range bsh.cookieList {
		bsh.original.SetCookie(c)
	}
	if bsh.status>0 {
		bsh.original.SetStatus(bsh.status)
	}
}
<commit_msg>Compression libraries changed <commit_after>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package revel

import (
	"io"
	"net/http"
	"strconv"
	"strings"
	"compress/gzip"
	"compress/zlib"
)

var compressionTypes = [...]string{
	"gzip",
	"deflate",
}

var compressableMimes = [...]string{
	"text/plain",
	"text/html",
	"text/xml",
	"text/css",
	"application/json",
	"application/xml",
	"application/xhtml+xml",
	"application/rss+xml",
	"application/javascript",
	"application/x-javascript",
}

// Local log instance for this class
var compressLog = RevelLog.New("section", "compress")

// WriteFlusher interface for compress writer
type WriteFlusher interface {
	io.Writer
	io.Closer
	Flush() error
}

type CompressResponseWriter struct {
	Header             *BufferedServerHeader
	ControllerResponse *Response
	OriginalWriter     io.Writer
	compressWriter     WriteFlusher
	compressionType    string
	headersWritten     bool
	closeNotify        chan bool
	parentNotify       <-chan bool
	closed             bool
}

// CompressFilter does compression of response body in gzip/deflate if
// `results.compressed=true` in the app.conf
func CompressFilter(c *Controller, fc []Filter) {
	if c.Response.Out.internalHeader.Server != nil && Config.BoolDefault("results.compressed", false) {
		if c.Response.Status != http.StatusNoContent && c.Response.Status != http.StatusNotModified {
			if found, compressType, compressWriter := detectCompressionType(c.Request, c.Response); found {
				writer := CompressResponseWriter{
					ControllerResponse: c.Response,
					OriginalWriter:     c.Response.GetWriter(),
					compressWriter:     compressWriter,
					compressionType:    compressType,
					headersWritten:     false,
					closeNotify:        make(chan bool, 1),
					closed:             false,
				}
				// Swap out the header with our own
				writer.Header = NewBufferedServerHeader(c.Response.Out.internalHeader.Server)
				c.Response.Out.internalHeader.Server = writer.Header
				if w, ok := c.Response.GetWriter().(http.CloseNotifier); ok {
					writer.parentNotify = w.CloseNotify()
				}
				c.Response.SetWriter(&writer)
			}
		} else {
			compressLog.Debug("CompressFilter: Compression disabled for response ", "status", c.Response.Status)
		}
	}
	fc[0](c, fc[1:])
}

func (c CompressResponseWriter) CloseNotify() <-chan bool {
	if c.parentNotify != nil {
		return c.parentNotify
	}
	return c.closeNotify
}

func (c *CompressResponseWriter) cancel() {
	c.closed = true
}
func (c *CompressResponseWriter) prepareHeaders() {
	if c.compressionType != "" {
		responseMime := ""
		if t := c.Header.Get("Content-Type"); len(t) > 0 {
			responseMime = t[0]
		}
		responseMime = strings.TrimSpace(strings.SplitN(responseMime, ";", 2)[0])
		shouldEncode := false

		if len(c.Header.Get("Content-Encoding")) == 0 {
			for _, compressableMime := range compressableMimes {
				if responseMime == compressableMime {
					shouldEncode = true
					c.Header.Set("Content-Encoding", c.compressionType)
					c.Header.Del("Content-Length")
					break
				}
			}
		}

		if !shouldEncode {
			c.compressWriter = nil
			c.compressionType = ""
		}
	}
	c.Header.Release()
}

func (c *CompressResponseWriter) WriteHeader(status int) {
	if c.closed {
		return
	}
	c.headersWritten = true
	c.prepareHeaders()
	c.Header.SetStatus(status)
}

func (c *CompressResponseWriter) Close() error {
	if c.closed {
		return nil
	}
	if !c.headersWritten {
		c.prepareHeaders()
	}
	if c.compressionType != "" {
		c.Header.Del("Content-Length")
		if err := c.compressWriter.Close(); err != nil {
			// TODO When writing directly to stream, an error will be generated
			compressLog.Error("Close: Error closing compress writer", "type", c.compressionType, "error", err)
		}

	}
	// Non-blocking write to the closenotifier, if we for some reason should
	// get called multiple times
	select {
	case c.closeNotify <- true:
	default:
	}
	c.closed = true
	return nil
}

func (c *CompressResponseWriter) Write(b []byte) (int, error) {
	if c.closed {
		return 0, io.ErrClosedPipe
	}
	// Abort if parent has been closed
	if c.parentNotify != nil {
		select {
		case <-c.parentNotify:
			return 0, io.ErrClosedPipe
		default:
		}
	}
	// Abort if we ourselves have been closed
	if c.closed {
		return 0, io.ErrClosedPipe
	}

	if !c.headersWritten {
		c.prepareHeaders()
		c.headersWritten = true
	}
	if c.compressionType != "" {
		return c.compressWriter.Write(b)
	}
	return c.OriginalWriter.Write(b)
}

// DetectCompressionType method detects the compression type
// from header "Accept-Encoding"
func detectCompressionType(req *Request, resp *Response) (found bool, compressionType string, compressionKind WriteFlusher) {
	if Config.BoolDefault("results.compressed", false) {
		acceptedEncodings := strings.Split(req.GetHttpHeader("Accept-Encoding"), ",")

		largestQ := 0.0
		chosenEncoding := len(compressionTypes)

		// I have fixed one edge case for issue #914
		// But it's better to cover all possible edge cases or
		// Adapt to https://github.com/golang/gddo/blob/master/httputil/header/header.go#L172
		for _, encoding := range acceptedEncodings {
			encoding = strings.TrimSpace(encoding)
			encodingParts := strings.SplitN(encoding, ";", 2)

			// If we are the format "gzip;q=0.8"
			if len(encodingParts) > 1 {
				q := strings.TrimSpace(encodingParts[1])
				if len(q) == 0 || !strings.HasPrefix(q, "q=") {
					continue
				}

				// Strip off the q=
				num, err := strconv.ParseFloat(q[2:], 32)
				if err != nil {
					continue
				}

				if num >= largestQ && num > 0 {
					if encodingParts[0] == "*" {
						chosenEncoding = 0
						largestQ = num
						continue
					}
					for i, encoding := range compressionTypes {
						if encoding == encodingParts[0] {
							if i < chosenEncoding {
								largestQ = num
								chosenEncoding = i
							}
							break
						}
					}
				}
			} else {
				// If we can accept anything, chose our preferred method.
				if encodingParts[0] == "*" {
					chosenEncoding = 0
					largestQ = 1
					break
				}
				// This is for just plain "gzip"
				for i, encoding := range compressionTypes {
					if encoding == encodingParts[0] {
						if i < chosenEncoding {
							largestQ = 1.0
							chosenEncoding = i
						}
						break
					}
				}
			}
		}

		if largestQ == 0 {
			return
		}

		compressionType = compressionTypes[chosenEncoding]

		switch compressionType {
		case "gzip":
			compressionKind = gzip.NewWriter(resp.GetWriter())
			found = true
		case "deflate":
			compressionKind = zlib.NewWriter(resp.GetWriter())
			found = true
		}
	}
	return
}

// BufferedServerHeader will not send content out until the Released is called, from that point on it will act normally
// It implements all the ServerHeader
type BufferedServerHeader struct {
	cookieList []string
	headerMap  map[string][]string
	status     int
	released   bool
	original   ServerHeader
}

func NewBufferedServerHeader(o ServerHeader) *BufferedServerHeader {
	return &BufferedServerHeader{original: o, headerMap: map[string][]string{}}
}
func (bsh *BufferedServerHeader) SetCookie(cookie string) {
	if bsh.released {
		bsh.original.SetCookie(cookie)
	} else {
		bsh.cookieList = append(bsh.cookieList, cookie)
	}
}
func (bsh *BufferedServerHeader) GetCookie(key string) (ServerCookie, error) {
	return bsh.original.GetCookie(key)
}
func (bsh *BufferedServerHeader) Set(key string, value string) {
	if bsh.released {
		bsh.original.Set(key, value)
	} else {
		bsh.headerMap[key] = []string{value}
	}
}
func (bsh *BufferedServerHeader) Add(key string, value string) {
	if bsh.released {
		bsh.original.Set(key, value)
	} else {
		old := []string{}
		if v, found := bsh.headerMap[key]; found {
			old = v
		}
		bsh.headerMap[key] = append(old, value)
	}

}
func (bsh *BufferedServerHeader) Del(key string) {
	if bsh.released {
		bsh.original.Del(key)
	} else {
		delete(bsh.headerMap, key)
	}

}
func (bsh *BufferedServerHeader) Get(key string) (value []string) {
	if bsh.released {
		value = bsh.original.Get(key)
	} else {
		if v, found := bsh.headerMap[key]; found && len(v) > 0 {
			value = v
		} else {
			value = bsh.original.Get(key)
		}
	}
	return
}
func (bsh *BufferedServerHeader) SetStatus(statusCode int) {
	if bsh.released {
		bsh.original.SetStatus(statusCode)
	} else {
		bsh.status = statusCode
	}
}
func (bsh *BufferedServerHeader) Release() {
	bsh.released = true
	for k, v := range bsh.headerMap {
		for _, r := range v {
			bsh.original.Set(k, r)
		}
	}
	for _, c := range bsh.cookieList {
		bsh.original.SetCookie(c)
	}
	if bsh.status>0 {
		bsh.original.SetStatus(bsh.status)
	}
}
<|endoftext|>


<commit_before>package supervisor

import (
	"fmt"
	"sync"
	"testing"
	"time"

	"golang.org/x/net/context"
)

func ExampleSupervisor() {
	var supervisor Supervisor

	svc := Simpleservice(1)
	supervisor.Add(&svc)

	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	supervisor.Serve(ctx)
}

func TestString(t *testing.T) {
	t.Parallel()

	const expected = "test"
	var supervisor Supervisor
	supervisor.Name = expected

	if got := fmt.Sprintf("%s", &supervisor); got != expected {
		t.Errorf("error getting supervisor name: %s", got)
	}
}

func TestCascaded(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := waitservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := waitservice{id: 2}
	supervisor.Add(&svc2)

	var childSupervisor Supervisor
	svc3 := waitservice{id: 3}
	childSupervisor.Add(&svc3)
	svc4 := waitservice{id: 4}
	childSupervisor.Add(&svc4)

	supervisor.Add(&childSupervisor)

	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	supervisor.Serve(ctx)

	if count := getServiceCount(&supervisor); count != 3 {
		t.Errorf("unexpected service count: %v", count)
	}

	switch {
	case svc1.count != 1, svc2.count != 1, svc3.count != 1, svc4.count != 1:
		t.Errorf("services should have been executed only once. %d %d %d %d",
			svc1.count, svc2.count, svc3.count, svc4.count)
	}
}

func TestPanic(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Backoff: 500 * time.Millisecond,
		Log: func(msg string) {
			t.Log("supervisor log (panic):", msg)
		},
	}
	svc1 := panicservice(1)
	supervisor.Add(&svc1)

	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	supervisor.Serve(ctx)

	// should arrive here with no panic
	if svc1 == 1 {
		t.Error("the failed service should have been started at least once.")
	}
}

func TestFailing(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Backoff: 1 * time.Second,
		Log: func(msg string) {
			t.Log("supervisor log (failing):", msg)
		},
	}

	svc1 := failingservice(1)
	supervisor.Add(&svc1)

	ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
	supervisor.Serve(ctx)

	// should arrive here with no panic
	if svc1 == 1 {
		t.Error("the failed service should have been started at least once.")
	}
}

func TestAddServiceAfterServe(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	<-supervisor.startedServices
	svc2 := Simpleservice(2)
	supervisor.Add(&svc2)
	<-supervisor.startedServices

	cancel()
	<-ctx.Done()
	wg.Wait()

	if count := getServiceCount(&supervisor); count != 2 {
		t.Errorf("unexpected service count: %v", count)
	}
}

func TestRemoveServiceAfterServe(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)
	svc2 := Simpleservice(2)
	supervisor.Add(&svc2)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	lbefore := getServiceCount(&supervisor)
	supervisor.Remove("unknown service")
	lafter := getServiceCount(&supervisor)

	if lbefore != lafter {
		t.Error("the removal of an unknown service shouldn't happen")
	}

	<-supervisor.startedServices
	supervisor.Remove(svc1.String())

	lremoved := getServiceCount(&supervisor)
	if lbefore != lremoved {
		t.Error("the removal of a service should have affected the supervisor:", lbefore, lremoved)
	}

	cancel()
	<-ctx.Done()
	wg.Wait()
}

func TestServices(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)
	svc2 := Simpleservice(2)
	supervisor.Add(&svc2)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()

	<-supervisor.startedServices
	svcs := supervisor.Services()
	for _, svcname := range []string{svc1.String(), svc2.String()} {
		if _, ok := svcs[svcname]; !ok {
			t.Errorf("expected service not found: %s", svcname)
		}
	}

	cancel()
	<-ctx.Done()
	wg.Done()
}

func TestManualCancelation(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Log: func(msg string) {
			t.Log("supervisor log (restartable):", msg)
		},
	}

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)
	svc2 := restartableservice{2, make(chan struct{})}
	supervisor.Add(&svc2)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()

	<-supervisor.startedServices
	<-svc2.restarted

	// Testing restart
	svcs := supervisor.Cancelations()
	svcancel := svcs[svc2.String()]
	svcancel()
	<-svc2.restarted

	cancel()
	<-ctx.Done()
	wg.Done()

	// should arrive here with no panic
}

func TestServiceList(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()

	<-supervisor.startedServices

	svcs := supervisor.Services()
	if svc, ok := svcs[svc1.String()]; !ok || &svc1 != svc.(*Simpleservice) {
		t.Errorf("could not find service when listing them. %s missing", svc1.String())
	}

	cancel()
	<-ctx.Done()
	wg.Done()
}

func TestDoubleStart(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	var svc1 waitservice
	supervisor.Add(&svc1)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)

	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			c := context.WithValue(ctx, "supervisor", i)
			supervisor.Serve(c)

			svc1.mu.Lock()
			count := svc1.count
			supervisors := svc1.supervisors
			if count > 1 {
				t.Error("wait service should have been started once:", count, "supervisor IDs:", supervisors)
			}
			svc1.mu.Unlock()

			wg.Done()
		}(i)
	}
	<-supervisor.startedServices

	cancel()
	<-ctx.Done()
	wg.Wait()
}

func TestRestart(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	var svc1 waitservice
	supervisor.Add(&svc1)

	for i := 1; i <= 2; i++ {
		var wg sync.WaitGroup
		wg.Add(1)

		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		go func(i int) {
			supervisor.Serve(ctx)
			svc1.mu.Lock()
			if svc1.count != i {
				t.Errorf("wait service should have been started %d. got: %d", i, svc1.count)
			}
			svc1.mu.Unlock()
			wg.Done()
		}(i)
		<-supervisor.startedServices

		cancel()
		<-ctx.Done()
		wg.Wait()
	}
}

func TestFailingRestarts(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	var svc1 defectiveservice
	supervisor.Add(&svc1)

	for i := 0; i < 2; i++ {
		var wg sync.WaitGroup
		wg.Add(1)

		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		go func() {
			supervisor.Serve(ctx)
			wg.Done()
		}()
		<-supervisor.startedServices

		cancel()
		<-ctx.Done()
		wg.Wait()
	}

	// should arrive here with no panic
}

type defectiveservice struct{}

func (s *defectiveservice) Serve(ctx context.Context) {
	<-ctx.Done()
}

func (s *defectiveservice) String() string {
	return fmt.Sprintf("defective service")
}

type failingservice int

func (s *failingservice) Serve(ctx context.Context) {
	select {
	case <-ctx.Done():
		return
	default:
		time.Sleep(100 * time.Millisecond)
		*s++
		return
	}
}

func (s *failingservice) String() string {
	return fmt.Sprintf("failing service %v", *s)
}

type panicservice int

func (s *panicservice) Serve(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(100 * time.Millisecond)
			*s++
			panic("forcing panic")
		}
	}
}

func (s *panicservice) String() string {
	return fmt.Sprintf("panic service %v", *s)
}

type restartableservice struct {
	id        int
	restarted chan struct{}
}

func (s *restartableservice) Serve(ctx context.Context) {
	var i int
	for {
		i++
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(500 * time.Millisecond)
			select {
			case s.restarted <- struct{}{}:
			default:
			}
		}
	}
}

func (s *restartableservice) String() string {
	return fmt.Sprintf("restartable service %v", *s)
}

type Simpleservice int

func (s *Simpleservice) String() string {
	return fmt.Sprintf("simple service %d", int(*s))
}

func (s *Simpleservice) Serve(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(500 * time.Millisecond)
		}
	}
}

type waitservice struct {
	id          int
	mu          sync.Mutex
	count       int
	supervisors []int
}

func (s *waitservice) Serve(ctx context.Context) {
	s.mu.Lock()
	s.count++
	id := ctx.Value("supervisor")
	if id != nil {
		s.supervisors = append(s.supervisors, id.(int))
	}
	s.mu.Unlock()
	<-ctx.Done()
}

func (s *waitservice) String() string {
	return fmt.Sprintf("wait service %v", s.id)
}

func getServiceCount(s *Supervisor) int {
	s.servicesMu.Lock()
	l := len(s.services)
	s.servicesMu.Unlock()
	return l
}
<commit_msg>add test for supervisor tree with failing services<commit_after>package supervisor

import (
	"fmt"
	"sync"
	"testing"
	"time"

	"golang.org/x/net/context"
)

func ExampleSupervisor() {
	var supervisor Supervisor

	svc := Simpleservice(1)
	supervisor.Add(&svc)

	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	supervisor.Serve(ctx)
}

func TestString(t *testing.T) {
	t.Parallel()

	const expected = "test"
	var supervisor Supervisor
	supervisor.Name = expected

	if got := fmt.Sprintf("%s", &supervisor); got != expected {
		t.Errorf("error getting supervisor name: %s", got)
	}
}

func TestCascaded(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := waitservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := waitservice{id: 2}
	supervisor.Add(&svc2)

	var childSupervisor Supervisor
	svc3 := waitservice{id: 3}
	childSupervisor.Add(&svc3)
	svc4 := waitservice{id: 4}
	childSupervisor.Add(&svc4)

	supervisor.Add(&childSupervisor)

	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	supervisor.Serve(ctx)

	if count := getServiceCount(&supervisor); count != 3 {
		t.Errorf("unexpected service count: %v", count)
	}

	switch {
	case svc1.count != 1, svc2.count != 1, svc3.count != 1, svc4.count != 1:
		t.Errorf("services should have been executed only once. %d %d %d %d",
			svc1.count, svc2.count, svc3.count, svc4.count)
	}
}

func TestCascadedWithProblems(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Backoff: 1 * time.Second,
		Log: func(msg string) {
			t.Log("supervisor log (cascaded with problems):", msg)
		},
	}
	svc1 := waitservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := panicservice{id: 2}
	supervisor.Add(&svc2)

	childSupervisor := Supervisor{
		Backoff: 1 * time.Second,
		Log: func(msg string) {
			t.Log("supervisor log (cascaded with problems - child):", msg)
		},
	}
	svc3 := waitservice{id: 3}
	childSupervisor.Add(&svc3)
	svc4 := failingservice{id: 4}
	childSupervisor.Add(&svc4)

	supervisor.Add(&childSupervisor)

	ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
	supervisor.Serve(ctx)

	if count := getServiceCount(&supervisor); count != 3 {
		t.Errorf("unexpected service count: %v", count)
	}

	switch {
	case svc1.count != 1, svc3.count != 1:
		t.Errorf("services should have been executed only once. %d %d %d %d",
			svc1.count, svc2.count, svc3.count, svc4.count)
	case svc2.count <= 1, svc4.count <= 1:
		t.Errorf("services should have been executed at least once. %d %d %d %d",
			svc1.count, svc2.count, svc3.count, svc4.count)
	}
}

func TestPanic(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Backoff: 500 * time.Millisecond,
		Log: func(msg string) {
			t.Log("supervisor log (panic):", msg)
		},
	}
	svc1 := panicservice{id: 1}
	supervisor.Add(&svc1)

	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	supervisor.Serve(ctx)

	// should arrive here with no panic
	if svc1.count == 1 {
		t.Error("the failed service should have been started at least once.")
	}
}

func TestFailing(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Backoff: 1 * time.Second,
		Log: func(msg string) {
			t.Log("supervisor log (failing):", msg)
		},
	}

	svc1 := failingservice{id: 1}
	supervisor.Add(&svc1)

	ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
	supervisor.Serve(ctx)

	// should arrive here with no panic
	if svc1.count == 1 {
		t.Error("the failed service should have been started at least once.")
	}
}

func TestAddServiceAfterServe(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	<-supervisor.startedServices
	svc2 := Simpleservice(2)
	supervisor.Add(&svc2)
	<-supervisor.startedServices

	cancel()
	<-ctx.Done()
	wg.Wait()

	if count := getServiceCount(&supervisor); count != 2 {
		t.Errorf("unexpected service count: %v", count)
	}
}

func TestRemoveServiceAfterServe(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)
	svc2 := Simpleservice(2)
	supervisor.Add(&svc2)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	lbefore := getServiceCount(&supervisor)
	supervisor.Remove("unknown service")
	lafter := getServiceCount(&supervisor)

	if lbefore != lafter {
		t.Error("the removal of an unknown service shouldn't happen")
	}

	<-supervisor.startedServices
	supervisor.Remove(svc1.String())

	lremoved := getServiceCount(&supervisor)
	if lbefore != lremoved {
		t.Error("the removal of a service should have affected the supervisor:", lbefore, lremoved)
	}

	cancel()
	<-ctx.Done()
	wg.Wait()
}

func TestServices(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)
	svc2 := Simpleservice(2)
	supervisor.Add(&svc2)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()

	<-supervisor.startedServices
	svcs := supervisor.Services()
	for _, svcname := range []string{svc1.String(), svc2.String()} {
		if _, ok := svcs[svcname]; !ok {
			t.Errorf("expected service not found: %s", svcname)
		}
	}

	cancel()
	<-ctx.Done()
	wg.Done()
}

func TestManualCancelation(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Log: func(msg string) {
			t.Log("supervisor log (restartable):", msg)
		},
	}

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)
	svc2 := restartableservice{2, make(chan struct{})}
	supervisor.Add(&svc2)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()

	<-supervisor.startedServices
	<-svc2.restarted

	// Testing restart
	svcs := supervisor.Cancelations()
	svcancel := svcs[svc2.String()]
	svcancel()
	<-svc2.restarted

	cancel()
	<-ctx.Done()
	wg.Done()

	// should arrive here with no panic
}

func TestServiceList(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	svc1 := Simpleservice(1)
	supervisor.Add(&svc1)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()

	<-supervisor.startedServices

	svcs := supervisor.Services()
	if svc, ok := svcs[svc1.String()]; !ok || &svc1 != svc.(*Simpleservice) {
		t.Errorf("could not find service when listing them. %s missing", svc1.String())
	}

	cancel()
	<-ctx.Done()
	wg.Done()
}

func TestDoubleStart(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	var svc1 waitservice
	supervisor.Add(&svc1)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)

	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			c := context.WithValue(ctx, "supervisor", i)
			supervisor.Serve(c)

			svc1.mu.Lock()
			count := svc1.count
			supervisors := svc1.supervisors
			if count > 1 {
				t.Error("wait service should have been started once:", count, "supervisor IDs:", supervisors)
			}
			svc1.mu.Unlock()

			wg.Done()
		}(i)
	}
	<-supervisor.startedServices

	cancel()
	<-ctx.Done()
	wg.Wait()
}

func TestRestart(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	var svc1 waitservice
	supervisor.Add(&svc1)

	for i := 1; i <= 2; i++ {
		var wg sync.WaitGroup
		wg.Add(1)

		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		go func(i int) {
			supervisor.Serve(ctx)
			svc1.mu.Lock()
			if svc1.count != i {
				t.Errorf("wait service should have been started %d. got: %d", i, svc1.count)
			}
			svc1.mu.Unlock()
			wg.Done()
		}(i)
		<-supervisor.startedServices

		cancel()
		<-ctx.Done()
		wg.Wait()
	}
}

func TestFailingRestarts(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor

	var svc1 defectiveservice
	supervisor.Add(&svc1)

	for i := 0; i < 2; i++ {
		var wg sync.WaitGroup
		wg.Add(1)

		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		go func() {
			supervisor.Serve(ctx)
			wg.Done()
		}()
		<-supervisor.startedServices

		cancel()
		<-ctx.Done()
		wg.Wait()
	}

	// should arrive here with no panic
}

type defectiveservice struct{}

func (s *defectiveservice) Serve(ctx context.Context) {
	<-ctx.Done()
}

func (s *defectiveservice) String() string {
	return fmt.Sprintf("defective service")
}

type failingservice struct {
	id, count int
}

func (s *failingservice) Serve(ctx context.Context) {
	select {
	case <-ctx.Done():
		return
	default:
		time.Sleep(100 * time.Millisecond)
		s.count++
		return
	}
}

func (s *failingservice) String() string {
	return fmt.Sprintf("failing service %v", s.id)
}

type panicservice struct {
	id, count int
}

func (s *panicservice) Serve(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(100 * time.Millisecond)
			s.count++
			panic("forcing panic")
		}
	}
}

func (s *panicservice) String() string {
	return fmt.Sprintf("panic service %v", s.id)
}

type restartableservice struct {
	id        int
	restarted chan struct{}
}

func (s *restartableservice) Serve(ctx context.Context) {
	var i int
	for {
		i++
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(500 * time.Millisecond)
			select {
			case s.restarted <- struct{}{}:
			default:
			}
		}
	}
}

func (s *restartableservice) String() string {
	return fmt.Sprintf("restartable service %v", *s)
}

type Simpleservice int

func (s *Simpleservice) String() string {
	return fmt.Sprintf("simple service %d", int(*s))
}

func (s *Simpleservice) Serve(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(500 * time.Millisecond)
		}
	}
}

type waitservice struct {
	id          int
	mu          sync.Mutex
	count       int
	supervisors []int
}

func (s *waitservice) Serve(ctx context.Context) {
	s.mu.Lock()
	s.count++
	id := ctx.Value("supervisor")
	if id != nil {
		s.supervisors = append(s.supervisors, id.(int))
	}
	s.mu.Unlock()
	<-ctx.Done()
}

func (s *waitservice) String() string {
	return fmt.Sprintf("wait service %v", s.id)
}

func getServiceCount(s *Supervisor) int {
	s.servicesMu.Lock()
	l := len(s.services)
	s.servicesMu.Unlock()
	return l
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package test

import (
	"context"
	"reflect"
	"testing"

	driver "github.com/arangodb/go-driver"
)

// createDocument creates a document in the given collection, failing the test on error.
func createDocument(ctx context.Context, col driver.Collection, document interface{}, t *testing.T) driver.DocumentMeta {
	meta, err := col.CreateDocument(ctx, document)
	if err != nil {
		t.Fatalf("Failed to create document: %s", describe(err))
	}
	return meta
}

// TestCreateDocument creates a document and then checks that it exists.
func TestCreateDocument(t *testing.T) {
	c := createClientFromEnv(t, true)
	db := ensureDatabase(nil, c, "document_test", nil, t)
	col := ensureCollection(nil, db, "document_test", nil, t)
	doc := UserDoc{
		"Jan",
		40,
	}
	meta, err := col.CreateDocument(nil, doc)
	if err != nil {
		t.Fatalf("Failed to create new document: %s", describe(err))
	}
	// Document must exists now
	var readDoc UserDoc
	if _, err := col.ReadDocument(nil, meta.Key, &readDoc); err != nil {
		t.Fatalf("Failed to read document '%s': %s", meta.Key, describe(err))
	}
	if !reflect.DeepEqual(doc, readDoc) {
		t.Errorf("Got wrong document. Expected %+v, got %+v", doc, readDoc)
	}
}

// TestCreateDocumentWithKey creates a document with given key and then checks that it exists.
func TestCreateDocumentWithKey(t *testing.T) {
	c := createClientFromEnv(t, true)
	db := ensureDatabase(nil, c, "document_test", nil, t)
	col := ensureCollection(nil, db, "document_withKey_test", nil, t)
	doc := UserDocWithKey{
		"jan",
		"Jan",
		40,
	}
	meta, err := col.CreateDocument(nil, doc)
	if err != nil {
		t.Fatalf("Failed to create new document: %s", describe(err))
	}
	// Key must be given key
	if meta.Key != doc.Key {
		t.Errorf("Expected key to be '%s', got '%s'", doc.Key, meta.Key)
	}
	// Document must exists now
	var readDoc UserDocWithKey
	if _, err := col.ReadDocument(nil, meta.Key, &readDoc); err != nil {
		t.Fatalf("Failed to read document '%s': %s", meta.Key, describe(err))
	}
	if !reflect.DeepEqual(doc, readDoc) {
		t.Errorf("Got wrong document. Expected %+v, got %+v", doc, readDoc)
	}
}

// TestCreateDocumentReturnNew creates a document and checks the document returned in in ReturnNew.
func TestCreateDocumentReturnNew(t *testing.T) {
	ctx := context.Background()
	c := createClientFromEnv(t, true)
	db := ensureDatabase(ctx, c, "document_test", nil, t)
	col := ensureCollection(ctx, db, "document_test", nil, t)
	doc := UserDoc{
		"JanNew",
		1,
	}
	var newDoc UserDoc
	meta, err := col.CreateDocument(driver.WithReturnNew(ctx, &newDoc), doc)
	if err != nil {
		t.Fatalf("Failed to create new document: %s", describe(err))
	}
	// NewDoc must equal doc
	if !reflect.DeepEqual(doc, newDoc) {
		t.Errorf("Got wrong ReturnNew document. Expected %+v, got %+v", doc, newDoc)
	}
	// Document must exists now
	var readDoc UserDoc
	if _, err := col.ReadDocument(ctx, meta.Key, &readDoc); err != nil {
		t.Fatalf("Failed to read document '%s': %s", meta.Key, describe(err))
	}
	if !reflect.DeepEqual(doc, readDoc) {
		t.Errorf("Got wrong document. Expected %+v, got %+v", doc, readDoc)
	}
}

// TestCreateDocumentSilent creates a document with WithSilent.
func TestCreateDocumentSilent(t *testing.T) {
	ctx := context.Background()
	c := createClientFromEnv(t, true)
	db := ensureDatabase(ctx, c, "document_test", nil, t)
	col := ensureCollection(ctx, db, "document_test", nil, t)
	doc := UserDoc{
		"Sjjjj",
		1,
	}
	if meta, err := col.CreateDocument(driver.WithSilent(ctx), doc); err != nil {
		t.Fatalf("Failed to create new document: %s", describe(err))
	} else if meta.Key != "" {
		t.Errorf("Expected empty meta, got %v", meta)
	}
}

// TestCreateDocumentNil creates a document with a nil document.
func TestCreateDocumentNil(t *testing.T) {
	c := createClientFromEnv(t, true)
	db := ensureDatabase(nil, c, "document_test", nil, t)
	col := ensureCollection(nil, db, "document_test", nil, t)
	if _, err := col.CreateDocument(nil, nil); !driver.IsInvalidArgument(err) {
		t.Fatalf("Expected InvalidArgumentError, got %s", describe(err))
	}
}
<commit_msg>Added duplicate document (key) test with ConflictError check<commit_after>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package test

import (
	"context"
	"reflect"
	"testing"

	driver "github.com/arangodb/go-driver"
)

// createDocument creates a document in the given collection, failing the test on error.
func createDocument(ctx context.Context, col driver.Collection, document interface{}, t *testing.T) driver.DocumentMeta {
	meta, err := col.CreateDocument(ctx, document)
	if err != nil {
		t.Fatalf("Failed to create document: %s", describe(err))
	}
	return meta
}

// TestCreateDocument creates a document and then checks that it exists.
func TestCreateDocument(t *testing.T) {
	c := createClientFromEnv(t, true)
	db := ensureDatabase(nil, c, "document_test", nil, t)
	col := ensureCollection(nil, db, "document_test", nil, t)
	doc := UserDoc{
		"Jan",
		40,
	}
	meta, err := col.CreateDocument(nil, doc)
	if err != nil {
		t.Fatalf("Failed to create new document: %s", describe(err))
	}
	// Document must exists now
	var readDoc UserDoc
	if _, err := col.ReadDocument(nil, meta.Key, &readDoc); err != nil {
		t.Fatalf("Failed to read document '%s': %s", meta.Key, describe(err))
	}
	if !reflect.DeepEqual(doc, readDoc) {
		t.Errorf("Got wrong document. Expected %+v, got %+v", doc, readDoc)
	}
}

// TestCreateDocumentWithKey creates a document with given key and then checks that it exists.
func TestCreateDocumentWithKey(t *testing.T) {
	c := createClientFromEnv(t, true)
	db := ensureDatabase(nil, c, "document_test", nil, t)
	col := ensureCollection(nil, db, "document_withKey_test", nil, t)
	doc := UserDocWithKey{
		"jan",
		"Jan",
		40,
	}
	meta, err := col.CreateDocument(nil, doc)
	if err != nil {
		t.Fatalf("Failed to create new document: %s", describe(err))
	}
	// Key must be given key
	if meta.Key != doc.Key {
		t.Errorf("Expected key to be '%s', got '%s'", doc.Key, meta.Key)
	}
	// Document must exists now
	var readDoc UserDocWithKey
	if _, err := col.ReadDocument(nil, meta.Key, &readDoc); err != nil {
		t.Fatalf("Failed to read document '%s': %s", meta.Key, describe(err))
	}
	if !reflect.DeepEqual(doc, readDoc) {
		t.Errorf("Got wrong document. Expected %+v, got %+v", doc, readDoc)
	}

	// Retry creating the document with same key. This must fail.
	if _, err := col.CreateDocument(nil, doc); !driver.IsConflict(err) {
		t.Fatalf("Expected ConflictError, got %s", describe(err))
	}
}

// TestCreateDocumentReturnNew creates a document and checks the document returned in in ReturnNew.
func TestCreateDocumentReturnNew(t *testing.T) {
	ctx := context.Background()
	c := createClientFromEnv(t, true)
	db := ensureDatabase(ctx, c, "document_test", nil, t)
	col := ensureCollection(ctx, db, "document_test", nil, t)
	doc := UserDoc{
		"JanNew",
		1,
	}
	var newDoc UserDoc
	meta, err := col.CreateDocument(driver.WithReturnNew(ctx, &newDoc), doc)
	if err != nil {
		t.Fatalf("Failed to create new document: %s", describe(err))
	}
	// NewDoc must equal doc
	if !reflect.DeepEqual(doc, newDoc) {
		t.Errorf("Got wrong ReturnNew document. Expected %+v, got %+v", doc, newDoc)
	}
	// Document must exists now
	var readDoc UserDoc
	if _, err := col.ReadDocument(ctx, meta.Key, &readDoc); err != nil {
		t.Fatalf("Failed to read document '%s': %s", meta.Key, describe(err))
	}
	if !reflect.DeepEqual(doc, readDoc) {
		t.Errorf("Got wrong document. Expected %+v, got %+v", doc, readDoc)
	}
}

// TestCreateDocumentSilent creates a document with WithSilent.
func TestCreateDocumentSilent(t *testing.T) {
	ctx := context.Background()
	c := createClientFromEnv(t, true)
	db := ensureDatabase(ctx, c, "document_test", nil, t)
	col := ensureCollection(ctx, db, "document_test", nil, t)
	doc := UserDoc{
		"Sjjjj",
		1,
	}
	if meta, err := col.CreateDocument(driver.WithSilent(ctx), doc); err != nil {
		t.Fatalf("Failed to create new document: %s", describe(err))
	} else if meta.Key != "" {
		t.Errorf("Expected empty meta, got %v", meta)
	}
}

// TestCreateDocumentNil creates a document with a nil document.
func TestCreateDocumentNil(t *testing.T) {
	c := createClientFromEnv(t, true)
	db := ensureDatabase(nil, c, "document_test", nil, t)
	col := ensureCollection(nil, db, "document_test", nil, t)
	if _, err := col.CreateDocument(nil, nil); !driver.IsInvalidArgument(err) {
		t.Fatalf("Expected InvalidArgumentError, got %s", describe(err))
	}
}
<|endoftext|>


<commit_before>// errorcheck

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

// Issue 11371 (cmd/compile: meaningless error message "truncated to
// integer")

package issue11371

const a int = 1.1        // ERROR "constant 1.1 truncated to integer|floating-point constant truncated to integer"
const b int = 1e20       // ERROR "overflows int|integer constant overflow"
const c int = 1 + 1e-100 // ERROR "constant truncated to integer"
const d int = 1 - 1e-100 // ERROR "constant truncated to integer"
const e int = 1.00000001 // ERROR "constant truncated to integer"
const f int = 0.00000001 // ERROR "constant 1e-08 truncated to integer|floating-point constant truncated to integer"
<commit_msg>test: adjust issue11371 to fit in required precision<commit_after>// errorcheck

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

// Issue 11371 (cmd/compile: meaningless error message "truncated to
// integer")

package issue11371

const a int = 1.1        // ERROR "constant 1.1 truncated to integer|floating-point constant truncated to integer"
const b int = 1e20       // ERROR "overflows int|integer constant overflow"
const c int = 1 + 1e-70  // ERROR "constant truncated to integer"
const d int = 1 - 1e-70  // ERROR "constant truncated to integer"
const e int = 1.00000001 // ERROR "constant truncated to integer"
const f int = 0.00000001 // ERROR "constant 1e-08 truncated to integer|floating-point constant truncated to integer"
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Command custommetric creates a custom metric and writes TimeSeries value
// to it. It writes a GAUGE measurement, which is a measure of value at a
// specific point in time. This means the startTime and endTime of the interval
// are the same. To make it easier to see the output, a random value is written.
// When reading the TimeSeries back, a window of the last 5 minutes is used.
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"os"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"

	"google.golang.org/api/monitoring/v3"
)

const metricType = "custom.googleapis.com/custom_measurement"

func projectResource(projectID string) string {
	return "projects/" + projectID
}

// createCustomMetric creates a custom metric specified by the metric type.
func createCustomMetric(s *monitoring.Service, projectID, metricType string) error {
	ld := monitoring.LabelDescriptor{Key: "environment", ValueType: "STRING", Description: "An arbitrary measurement"}
	md := monitoring.MetricDescriptor{
		Type:        metricType,
		Labels:      []*monitoring.LabelDescriptor{&ld},
		MetricKind:  "GAUGE",
		ValueType:   "INT64",
		Unit:        "items",
		Description: "An arbitrary measurement",
		DisplayName: "Custom Metric",
	}
	resp, err := s.Projects.MetricDescriptors.Create(projectResource(projectID), &md).Do()
	if err != nil {
		return fmt.Errorf("Could not create custom metric: %v", err)
	}

	log.Printf("createCustomMetric: %s\n", formatResource(resp))
	return nil
}

// getCustomMetric reads the custom metric created.
func getCustomMetric(s *monitoring.Service, projectID, metricType string) (*monitoring.ListMetricDescriptorsResponse, error) {
	resp, err := s.Projects.MetricDescriptors.List(projectResource(projectID)).
		Filter(fmt.Sprintf("metric.type=\"%s\"", metricType)).Do()
	if err != nil {
		return nil, fmt.Errorf("Could not get custom metric: %v", err)
	}

	log.Printf("getCustomMetric: %s\n", formatResource(resp))
	return resp, nil
}

// writeTimeSeriesValue writes a value for the custom metric created
func writeTimeSeriesValue(s *monitoring.Service, projectID, metricType string) error {
	now := time.Now().UTC().Format(time.RFC3339Nano)
	timeseries := monitoring.TimeSeries{
		Metric: &monitoring.Metric{
			Type: metricType,
			Labels: map[string]string{
				"environment": "STAGING",
			},
		},
		Resource: &monitoring.MonitoredResource{
			Labels: map[string]string{
				"instance_id": "test-instance",
				"zone":        "us-central1-f",
			},
			Type: "gce_instance",
		},
		MetricKind: "GAUGE",
		ValueType:  "INT64",
		Points: []*monitoring.Point{
			{
				Interval: &monitoring.TimeInterval{
					StartTime: now,
					EndTime:   now,
				},
				Value: &monitoring.TypedValue{
					Int64Value: rand.Int63n(10),
				},
			},
		},
	}

	createTimeseriesRequest := monitoring.CreateTimeSeriesRequest{
		TimeSeries: []*monitoring.TimeSeries{&timeseries},
	}

	log.Printf("writeTimeseriesRequest: %s\n", formatResource(createTimeseriesRequest))
	_, err := s.Projects.TimeSeries.Create(projectResource(projectID), &createTimeseriesRequest).Do()
	if err != nil {
		return fmt.Errorf("Could not write time series value, %v ", err)
	}
	return nil
}

// readTimeSeriesValue reads the TimeSeries for the value specified by metric type in a time window from the last 5 minutes.
func readTimeSeriesValue(s *monitoring.Service, projectID, metricType string) error {
	startTime := time.Now().UTC().Add(time.Minute * -5)
	endTime := time.Now().UTC()
	resp, err := s.Projects.TimeSeries.List(projectResource(projectID)).
		Filter(fmt.Sprintf("metric.type=\"%s\"", metricType)).
		IntervalStartTime(startTime.Format(time.RFC3339Nano)).
		IntervalEndTime(endTime.Format(time.RFC3339Nano)).
		Do()
	if err != nil {
		return fmt.Errorf("Could not read time series value, %v ", err)
	}
	log.Printf("readTimeseriesValue: %s\n", formatResource(resp))
	return nil
}

func createService(ctx context.Context) (*monitoring.Service, error) {
	hc, err := google.DefaultClient(ctx, monitoring.MonitoringScope)
	if err != nil {
		return nil, err
	}
	s, err := monitoring.New(hc)
	if err != nil {
		return nil, err
	}
	return s, nil
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	if len(os.Args) < 2 {
		fmt.Println("Usage: custommetric <project_id>")
		return
	}

	ctx := context.Background()
	s, err := createService(ctx)
	if err != nil {
		log.Fatal(err)
	}

	projectID := os.Args[1]

	// Create the metric.
	if err := createCustomMetric(s, projectID, metricType); err != nil {
		log.Fatal(err)
	}

	// Wait until the new metric can be read back.
	for {
		resp, err := getCustomMetric(s, projectID, metricType)
		if err != nil {
			log.Fatal(err)
		}
		if len(resp.MetricDescriptors) != 0 {
			break
		}
		time.Sleep(2 * time.Second)
	}

	// Write a TimeSeries value for that metric
	if err := writeTimeSeriesValue(s, projectID, metricType); err != nil {
		log.Fatal(err)
	}

	time.Sleep(2 * time.Second)

	// Read the TimeSeries for the last 5 minutes for that metric.
	if err := readTimeSeriesValue(s, projectID, metricType); err != nil {
		log.Fatal(err)
	}
}

// formatResource marshals a response objects as JSON.
func formatResource(resource interface{}) []byte {
	b, err := json.MarshalIndent(resource, "", "    ")
	if err != nil {
		panic(err)
	}
	return b
}
<commit_msg>monitoring: use pointer for typed value<commit_after>// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Command custommetric creates a custom metric and writes TimeSeries value
// to it. It writes a GAUGE measurement, which is a measure of value at a
// specific point in time. This means the startTime and endTime of the interval
// are the same. To make it easier to see the output, a random value is written.
// When reading the TimeSeries back, a window of the last 5 minutes is used.
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"os"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"

	"google.golang.org/api/monitoring/v3"
)

const metricType = "custom.googleapis.com/custom_measurement"

func projectResource(projectID string) string {
	return "projects/" + projectID
}

// createCustomMetric creates a custom metric specified by the metric type.
func createCustomMetric(s *monitoring.Service, projectID, metricType string) error {
	ld := monitoring.LabelDescriptor{Key: "environment", ValueType: "STRING", Description: "An arbitrary measurement"}
	md := monitoring.MetricDescriptor{
		Type:        metricType,
		Labels:      []*monitoring.LabelDescriptor{&ld},
		MetricKind:  "GAUGE",
		ValueType:   "INT64",
		Unit:        "items",
		Description: "An arbitrary measurement",
		DisplayName: "Custom Metric",
	}
	resp, err := s.Projects.MetricDescriptors.Create(projectResource(projectID), &md).Do()
	if err != nil {
		return fmt.Errorf("Could not create custom metric: %v", err)
	}

	log.Printf("createCustomMetric: %s\n", formatResource(resp))
	return nil
}

// getCustomMetric reads the custom metric created.
func getCustomMetric(s *monitoring.Service, projectID, metricType string) (*monitoring.ListMetricDescriptorsResponse, error) {
	resp, err := s.Projects.MetricDescriptors.List(projectResource(projectID)).
		Filter(fmt.Sprintf("metric.type=\"%s\"", metricType)).Do()
	if err != nil {
		return nil, fmt.Errorf("Could not get custom metric: %v", err)
	}

	log.Printf("getCustomMetric: %s\n", formatResource(resp))
	return resp, nil
}

// writeTimeSeriesValue writes a value for the custom metric created
func writeTimeSeriesValue(s *monitoring.Service, projectID, metricType string) error {
	now := time.Now().UTC().Format(time.RFC3339Nano)
	randVal := rand.Int63n(10)
	timeseries := monitoring.TimeSeries{
		Metric: &monitoring.Metric{
			Type: metricType,
			Labels: map[string]string{
				"environment": "STAGING",
			},
		},
		Resource: &monitoring.MonitoredResource{
			Labels: map[string]string{
				"instance_id": "test-instance",
				"zone":        "us-central1-f",
			},
			Type: "gce_instance",
		},
		MetricKind: "GAUGE",
		ValueType:  "INT64",
		Points: []*monitoring.Point{
			{
				Interval: &monitoring.TimeInterval{
					StartTime: now,
					EndTime:   now,
				},
				Value: &monitoring.TypedValue{
					Int64Value: &randVal,
				},
			},
		},
	}

	createTimeseriesRequest := monitoring.CreateTimeSeriesRequest{
		TimeSeries: []*monitoring.TimeSeries{&timeseries},
	}

	log.Printf("writeTimeseriesRequest: %s\n", formatResource(createTimeseriesRequest))
	_, err := s.Projects.TimeSeries.Create(projectResource(projectID), &createTimeseriesRequest).Do()
	if err != nil {
		return fmt.Errorf("Could not write time series value, %v ", err)
	}
	return nil
}

// readTimeSeriesValue reads the TimeSeries for the value specified by metric type in a time window from the last 5 minutes.
func readTimeSeriesValue(s *monitoring.Service, projectID, metricType string) error {
	startTime := time.Now().UTC().Add(time.Minute * -5)
	endTime := time.Now().UTC()
	resp, err := s.Projects.TimeSeries.List(projectResource(projectID)).
		Filter(fmt.Sprintf("metric.type=\"%s\"", metricType)).
		IntervalStartTime(startTime.Format(time.RFC3339Nano)).
		IntervalEndTime(endTime.Format(time.RFC3339Nano)).
		Do()
	if err != nil {
		return fmt.Errorf("Could not read time series value, %v ", err)
	}
	log.Printf("readTimeseriesValue: %s\n", formatResource(resp))
	return nil
}

func createService(ctx context.Context) (*monitoring.Service, error) {
	hc, err := google.DefaultClient(ctx, monitoring.MonitoringScope)
	if err != nil {
		return nil, err
	}
	s, err := monitoring.New(hc)
	if err != nil {
		return nil, err
	}
	return s, nil
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	if len(os.Args) < 2 {
		fmt.Println("Usage: custommetric <project_id>")
		return
	}

	ctx := context.Background()
	s, err := createService(ctx)
	if err != nil {
		log.Fatal(err)
	}

	projectID := os.Args[1]

	// Create the metric.
	if err := createCustomMetric(s, projectID, metricType); err != nil {
		log.Fatal(err)
	}

	// Wait until the new metric can be read back.
	for {
		resp, err := getCustomMetric(s, projectID, metricType)
		if err != nil {
			log.Fatal(err)
		}
		if len(resp.MetricDescriptors) != 0 {
			break
		}
		time.Sleep(2 * time.Second)
	}

	// Write a TimeSeries value for that metric
	if err := writeTimeSeriesValue(s, projectID, metricType); err != nil {
		log.Fatal(err)
	}

	time.Sleep(2 * time.Second)

	// Read the TimeSeries for the last 5 minutes for that metric.
	if err := readTimeSeriesValue(s, projectID, metricType); err != nil {
		log.Fatal(err)
	}
}

// formatResource marshals a response objects as JSON.
func formatResource(resource interface{}) []byte {
	b, err := json.MarshalIndent(resource, "", "    ")
	if err != nil {
		panic(err)
	}
	return b
}
<|endoftext|>


<commit_before>package fetcher

import (
	"context"
	"errors"
	"io"
	"sync"

	"github.com/taskcluster/taskcluster-worker/runtime/atomics"
)

// ErrStreamReset returned from io.Reader when the fetch process is reset
var ErrStreamReset = errors.New("stream was reset by fetcher")

// A StreamHandler is a function that handles a stream, the stream maybe a
// aborted in which case io.Reader will return ErrStreamReset and the Context
// will be canceled.
type StreamHandler func(context.Context, io.Reader) error

// FetchAsStream gets a reference as a stream.
//
// Notice that target StreamHandler may be invoked multiple times, if the
// connection breaks while fetching it might be retried. In which case the
// Context passed to the target StreamHandler will be canceled.
func FetchAsStream(context Context, reference Reference, target StreamHandler) error {
	s := &streamReseter{handler: target}
	s.Reset() // initialize

	// fetch and close after fetching
	ferr := reference.Fetch(context, s) // Notice that Fetch() may invoke s.Reset()
	cerr := s.CloseWithError(ferr)

	if ferr != nil {
		return ferr
	}
	return cerr
}

type streamReseter struct {
	m       sync.Mutex
	handler StreamHandler
	reader  *io.PipeReader
	writer  *io.PipeWriter
	ctx     context.Context
	cancel  func()
	done    atomics.Once // covers err
	err     error
}

func (s *streamReseter) Write(p []byte) (n int, err error) {
	s.m.Lock()
	defer s.m.Unlock()

	return s.writer.Write(p)
}

func (s *streamReseter) Reset() error {
	s.m.Lock()
	defer s.m.Unlock()

	// Discard current state if any
	if s.cancel != nil {
		s.cancel()
		s.writer.CloseWithError(ErrStreamReset)
	}

	ctx, cancel := context.WithCancel(context.Background())
	reader, writer := io.Pipe()
	s.reader = reader
	s.writer = writer
	s.ctx = ctx
	s.cancel = cancel

	go func() {
		defer cancel()

		err := s.handler(ctx, reader)
		reader.CloseWithError(err)

		s.m.Lock()
		defer s.m.Unlock()
		if ctx.Err() == nil {
			s.err = err
			s.done.Do(nil)
		}
	}()

	return nil
}

func (s *streamReseter) CloseWithError(err error) error {
	s.m.Lock()
	defer s.m.Unlock()

	// Ensure that the writer pipe is closed
	s.writer.CloseWithError(err) // ignore error, we don't care if it's closed twice

	// Wait for the most recent handler call to be finished
	s.m.Unlock()
	s.done.Wait()
	s.m.Lock()

	return s.err
}
<commit_msg>Reduced concurrency in FetchAsStream<commit_after>package fetcher

import (
	"context"
	"errors"
	"io"
	"sync"
)

// ErrStreamReset returned from io.Reader when the fetch process is reset
var ErrStreamReset = errors.New("stream was reset by fetcher")

// A StreamHandler is a function that handles a stream, the stream maybe a
// aborted in which case io.Reader will return ErrStreamReset and the Context
// will be canceled.
type StreamHandler func(context.Context, io.Reader) error

// FetchAsStream gets a reference as a stream.
//
// Notice that target StreamHandler may be invoked multiple times, if the
// connection breaks while fetching it might be retried. In which case the
// Context passed to the target StreamHandler will be canceled.
func FetchAsStream(context Context, reference Reference, target StreamHandler) error {
	s := &streamReseter{handler: target}
	s.Reset() // initialize

	// fetch and close after fetching
	ferr := reference.Fetch(context, s) // Notice that Fetch() may invoke s.Reset()
	cerr := s.CloseWithError(ferr)

	if ferr != nil {
		return ferr
	}
	return cerr
}

type streamReseter struct {
	m       sync.Mutex
	handler StreamHandler
	reader  *io.PipeReader
	writer  *io.PipeWriter
	ctx     context.Context
	cancel  func()
	wg      sync.WaitGroup
	err     error
}

func (s *streamReseter) Write(p []byte) (n int, err error) {
	s.m.Lock()
	defer s.m.Unlock()

	return s.writer.Write(p)
}

func (s *streamReseter) Reset() error {
	s.m.Lock()
	defer s.m.Unlock()

	// Discard current state if any
	if s.cancel != nil {
		s.cancel()
		s.writer.CloseWithError(ErrStreamReset)
	}

	// Wait for handler call to be done, so we don't call it concurrently
	// Even if calling concurrently wouldn't be a problem here, doing it is a
	// tiny optimization that only affects cases where we a broken connection
	// and Fetch() needs to reset the download process. And doing it would make
	// a lot harder to write a property StreamHandler as it would likely need to
	// deal with this concurrency.
	s.wg.Wait()

	ctx, cancel := context.WithCancel(context.Background())
	reader, writer := io.Pipe()
	s.reader = reader
	s.writer = writer
	s.ctx = ctx
	s.cancel = cancel
	s.err = nil

	s.wg.Add(1)
	go func() {
		defer s.wg.Done()
		defer cancel()

		err := s.handler(ctx, reader)
		reader.CloseWithError(err)
		if ctx.Err() == nil {
			s.err = err
		}
	}()

	return nil
}

func (s *streamReseter) CloseWithError(err error) error {
	s.m.Lock()
	defer s.m.Unlock()

	// Ensure that the writer pipe is closed
	s.writer.CloseWithError(err) // ignore error, we don't care if it's closed twice

	// Wait for the handler call to be finished
	s.wg.Wait()

	return s.err
}
<|endoftext|>


<commit_before>package rafted

import (
    hsm "github.com/hhkbp2/go-hsm"
    ev "github.com/hhkbp2/rafted/event"
    logging "github.com/hhkbp2/rafted/logging"
    "time"
)

type LocalState struct {
    *LogStateHead
}

func NewLocalState(super hsm.State, logger logging.Logger) *LocalState {
    object := &LocalState{
        LogStateHead: NewLogStateHead(super, logger),
    }
    super.AddChild(object)
    return object
}

func (*LocalState) ID() string {
    return StateLocalID
}

func (self *LocalState) Entry(sm hsm.HSM, event hsm.Event) (state hsm.State) {
    self.Debug("STATE: %s, -> Entry", self.ID())
    // ignore events
    return nil
}

func (self *LocalState) Init(sm hsm.HSM, event hsm.Event) (state hsm.State) {
    self.Debug("STATE: %s, -> Init", self.ID())
    sm.QInit(StateFollowerID)
    return nil
}

func (self *LocalState) Exit(sm hsm.HSM, event hsm.Event) (state hsm.State) {
    self.Debug("STATE: %s, -> Exit", self.ID())
    // ignore events
    return nil
}

func (self *LocalState) Handle(sm hsm.HSM, event hsm.Event) (state hsm.State) {
    self.Debug("STATE: %s, -> Handle event: %s", self.ID(),
        ev.EventTypeString(event))
    switch event.Type() {
    case ev.EventPersistError:
        sm.QTranOnEvent(StatePeerID, event)
        return nil
    case ev.EventTerm:
        sm.QTran(hsm.TerminalStateID)
    }
    return self.Super()
}

type NeedPeersState struct {
    *LogStateHead
}

func NewNeedPeersState(super hsm.State, logger logging.Logger) *NeedPeersState {
    object := &NeedPeersState{
        LogStateHead: NewLogStateHead(super, logger),
    }
    super.AddChild(object)
    return object
}

func (*NeedPeersState) ID() string {
    return StateNeedPeersID
}

func (self *NeedPeersState) Entry(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Entry", self.ID())
    localHSM, ok := sm.(*LocalHSM)
    hsm.AssertTrue(ok)
    // coordinate peer
    memberChangeStatus := localHSM.GetMemberChangeStatus()
    switch memberChangeStatus {
    case NewConfigSeen:
        // TODO add impl
        committedIndex, err := localHSM.Log().CommittedIndex()
        if err != nil {
            // TODO error handling
        }
        metas, err := localHSM.ConfigManager().ListAfter(committedIndex)
        if err != nil {
            // TODO error handling
        }
        if len(metas) != 2 {
            // TODO error handling
        }
        conf := metas[0].Conf
        localHSM.PeerManager().AddPeers(localHSM.GetLocalAddr(), conf)
    case NotInMemeberChange:
        fallthrough
    case OldNewConfigSeen:
        fallthrough
    case OldNewConfigCommitted:
        fallthrough
    default:
        conf, err := localHSM.ConfigManager().RNth(0)
        if err != nil {
            // TODO error handling
        }
        localHSM.PeerManager().AddPeers(localHSM.GetLocalAddr(), conf)
    }
    localHSM.PeerManager().Broadcast(ev.NewPeerActivateEvent())
    return nil
}

func (self *NeedPeersState) Exit(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Exit", self.ID())
    localHSM, ok := sm.(*LocalHSM)
    hsm.AssertTrue(ok)
    // coordinate peer into DeactivatePeerState
    localHSM.PeerManager().Broadcast(ev.NewPeerDeactivateEvent())
    return nil
}

func (self *NeedPeersState) Handle(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Handle event: %s", self.ID(),
        ev.EventTypeString(event))
    // TODO add log
    return self.Super()
}

type PersistErrorState struct {
    *LogStateHead

    err           error
    notifyTimeout time.Duration
    ticker        Ticker
}

func NewPersistErrorState(
    super hsm.State,
    persistErrorNotifyTimeout time.Duration,
    logger logging.Logger) *PersistErrorState {

    object := &PersistErrorState{
        LogStateHead:  NewLogStateHead(super, logger),
        notifyTimeout: persistErrorNotifyTimeout,
        ticker:        NewSimpleTicker(persistErrorNotifyTimeout),
    }
    super.AddChild(object)
    return object
}

func (*PersistErrorState) ID() string {
    return StatePersistErrorID
}

func (self *PersistErrorState) Entry(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Entry", self.ID())
    localHSM, ok := sm.(*LocalHSM)
    hsm.AssertTrue(ok)
    e, ok := event.(*ev.PersistErrorEvent)
    hsm.AssertTrue(ok)
    self.err = e.Error
    self.Error("%#v", e.Error)
    localHSM.SelfDispatch(ev.NewNotifyPersistErrorEvent(self.err))
    dispatchTimeout := func() {
        localHSM.SelfDispatch(ev.NewNotifyPersistErrorEvent(self.err))
    }
    self.ticker.Start(dispatchTimeout)
    return nil
}

func (self *PersistErrorState) Exit(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Exit", self.ID())
    self.ticker.Stop()
    return nil
}

func (self *PersistErrorState) Handle(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Handle event: %s", self.ID(),
        ev.EventTypeString(event))
    switch {
    case event.Type() == ev.EventPersistError:
        self.Error("already in state: %s, ignore event: %s", self.ID(),
            ev.EventTypeString(event))
        return nil
    case ev.IsClientEvent(event.Type()):
        e, ok := event.(ev.ClientRequestEvent)
        hsm.AssertTrue(ok)
        e.SendResponse(ev.NewPersistErrorResponseEvent(self.err))
        return nil
    }
    return self.Super()
}
<commit_msg>add log for states<commit_after>package rafted

import (
    "errors"
    hsm "github.com/hhkbp2/go-hsm"
    ev "github.com/hhkbp2/rafted/event"
    logging "github.com/hhkbp2/rafted/logging"
    "time"
)

type LocalState struct {
    *LogStateHead
}

func NewLocalState(super hsm.State, logger logging.Logger) *LocalState {
    object := &LocalState{
        LogStateHead: NewLogStateHead(super, logger),
    }
    super.AddChild(object)
    return object
}

func (*LocalState) ID() string {
    return StateLocalID
}

func (self *LocalState) Entry(sm hsm.HSM, event hsm.Event) (state hsm.State) {
    self.Debug("STATE: %s, -> Entry", self.ID())
    return nil
}

func (self *LocalState) Init(sm hsm.HSM, event hsm.Event) (state hsm.State) {
    self.Debug("STATE: %s, -> Init", self.ID())
    sm.QInit(StateFollowerID)
    return nil
}

func (self *LocalState) Exit(sm hsm.HSM, event hsm.Event) (state hsm.State) {
    self.Debug("STATE: %s, -> Exit", self.ID())
    return nil
}

func (self *LocalState) Handle(sm hsm.HSM, event hsm.Event) (state hsm.State) {
    self.Debug("STATE: %s, -> Handle event: %s", self.ID(),
        ev.EventTypeString(event))
    switch event.Type() {
    case ev.EventPersistError:
        sm.QTranOnEvent(StatePeerID, event)
        return nil
    case ev.EventTerm:
        sm.QTran(hsm.TerminalStateID)
    }
    return self.Super()
}

type NeedPeersState struct {
    *LogStateHead
}

func NewNeedPeersState(super hsm.State, logger logging.Logger) *NeedPeersState {
    object := &NeedPeersState{
        LogStateHead: NewLogStateHead(super, logger),
    }
    super.AddChild(object)
    return object
}

func (*NeedPeersState) ID() string {
    return StateNeedPeersID
}

func (self *NeedPeersState) Entry(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Entry", self.ID())
    localHSM, ok := sm.(*LocalHSM)
    hsm.AssertTrue(ok)
    // coordinate peer
    memberChangeStatus := localHSM.GetMemberChangeStatus()
    switch memberChangeStatus {
    case NewConfigSeen:
        conf, err := localHSM.ConfigManager().RNth(0)
        if err != nil {
            localHSM.SelfDispatch(ev.NewPersistErrorEvent(errors.New(
                "fail to read last config")))
            return nil
        }
        localHSM.PeerManager().AddPeers(localHSM.GetLocalAddr(), conf)
    case NotInMemeberChange:
        fallthrough
    case OldNewConfigSeen:
        fallthrough
    case OldNewConfigCommitted:
        fallthrough
    default:
        conf, err := localHSM.ConfigManager().RNth(0)
        if err != nil {
            localHSM.SelfDispatch(ev.NewPersistErrorEvent(errors.New(
                "fail to read last config")))
        }
        localHSM.PeerManager().AddPeers(localHSM.GetLocalAddr(), conf)
    }
    localHSM.PeerManager().Broadcast(ev.NewPeerActivateEvent())
    return nil
}

func (self *NeedPeersState) Exit(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Exit", self.ID())
    localHSM, ok := sm.(*LocalHSM)
    hsm.AssertTrue(ok)
    // coordinate peer into DeactivatePeerState
    localHSM.PeerManager().Broadcast(ev.NewPeerDeactivateEvent())
    return nil
}

func (self *NeedPeersState) Handle(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Handle event: %s", self.ID(),
        ev.EventTypeString(event))
    return self.Super()
}

type PersistErrorState struct {
    *LogStateHead

    err           error
    notifyTimeout time.Duration
    ticker        Ticker
}

func NewPersistErrorState(
    super hsm.State,
    persistErrorNotifyTimeout time.Duration,
    logger logging.Logger) *PersistErrorState {

    object := &PersistErrorState{
        LogStateHead:  NewLogStateHead(super, logger),
        notifyTimeout: persistErrorNotifyTimeout,
        ticker:        NewSimpleTicker(persistErrorNotifyTimeout),
    }
    super.AddChild(object)
    return object
}

func (*PersistErrorState) ID() string {
    return StatePersistErrorID
}

func (self *PersistErrorState) Entry(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Entry", self.ID())
    localHSM, ok := sm.(*LocalHSM)
    hsm.AssertTrue(ok)
    e, ok := event.(*ev.PersistErrorEvent)
    hsm.AssertTrue(ok)
    self.err = e.Error
    self.Error("%#v", e.Error)
    localHSM.SelfDispatch(ev.NewNotifyPersistErrorEvent(self.err))
    dispatchTimeout := func() {
        localHSM.SelfDispatch(ev.NewNotifyPersistErrorEvent(self.err))
    }
    self.ticker.Start(dispatchTimeout)
    return nil
}

func (self *PersistErrorState) Exit(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Exit", self.ID())
    self.ticker.Stop()
    return nil
}

func (self *PersistErrorState) Handle(
    sm hsm.HSM, event hsm.Event) (state hsm.State) {

    self.Debug("STATE: %s, -> Handle event: %s", self.ID(),
        ev.EventTypeString(event))
    switch {
    case event.Type() == ev.EventPersistError:
        self.Error("already in state: %s, ignore event: %s", self.ID(),
            ev.EventTypeString(event))
        return nil
    case ev.IsClientEvent(event.Type()):
        e, ok := event.(ev.ClientRequestEvent)
        hsm.AssertTrue(ok)
        e.SendResponse(ev.NewPersistErrorResponseEvent(self.err))
        return nil
    }
    return self.Super()
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"github.com/HouzuoGuo/laitos/feature"
	"github.com/HouzuoGuo/laitos/frontend/common"
	"github.com/HouzuoGuo/laitos/global"
	"html"
	"net/http"
)

const HandleCommandFormPage = `<!doctype html>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Command Form</title>

    <form action="#" method="post">

<input type="password" name="cmd" /><input type="submit" value="Exec"/>

        <textarea rows="12", cols="80">%s</textarea>
    </form>

` // Run Command page content

const CommandFormTimeoutSec = 110 // Form commands may enjoy a less constrained timeout

type HandleCommandForm struct {
}

func (_ *HandleCommandForm) MakeHandler(logger global.Logger, cmdProc *common.CommandProcessor) (http.HandlerFunc, error) {
	fun := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Header().Set("Cache-Control", "must-revalidate")
		if r.Method == http.MethodGet {
			w.Write([]byte(fmt.Sprintf(HandleCommandFormPage, "")))
		} else if r.Method == http.MethodPost {
			if cmd := r.FormValue("cmd"); cmd == "" {
				w.Write([]byte(fmt.Sprintf(HandleCommandFormPage, "")))
			} else {
				result := cmdProc.Process(feature.Command{
					Content:    cmd,
					TimeoutSec: CommandFormTimeoutSec,
				})
				w.Write([]byte(fmt.Sprintf(HandleCommandFormPage, html.EscapeString(result.CombinedOutput))))
			}
		}
	}
	return fun, nil
}

func (_ *HandleCommandForm) GetRateLimitFactor() int {
	return 1
}
<commit_msg>remove typo comma from command form<commit_after>package api

import (
	"fmt"
	"github.com/HouzuoGuo/laitos/feature"
	"github.com/HouzuoGuo/laitos/frontend/common"
	"github.com/HouzuoGuo/laitos/global"
	"html"
	"net/http"
)

const HandleCommandFormPage = `<!doctype html>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Command Form</title>

    <form action="#" method="post">

<input type="password" name="cmd" /><input type="submit" value="Exec"/>

        <textarea rows="12" cols="80">%s</textarea>
    </form>

` // Run Command page content

const CommandFormTimeoutSec = 110 // Form commands may enjoy a less constrained timeout

type HandleCommandForm struct {
}

func (_ *HandleCommandForm) MakeHandler(logger global.Logger, cmdProc *common.CommandProcessor) (http.HandlerFunc, error) {
	fun := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Header().Set("Cache-Control", "must-revalidate")
		if r.Method == http.MethodGet {
			w.Write([]byte(fmt.Sprintf(HandleCommandFormPage, "")))
		} else if r.Method == http.MethodPost {
			if cmd := r.FormValue("cmd"); cmd == "" {
				w.Write([]byte(fmt.Sprintf(HandleCommandFormPage, "")))
			} else {
				result := cmdProc.Process(feature.Command{
					Content:    cmd,
					TimeoutSec: CommandFormTimeoutSec,
				})
				w.Write([]byte(fmt.Sprintf(HandleCommandFormPage, html.EscapeString(result.CombinedOutput))))
			}
		}
	}
	return fun, nil
}

func (_ *HandleCommandForm) GetRateLimitFactor() int {
	return 1
}
<|endoftext|>