<commit_before>package upload_step_test

import (
	"errors"
	"io/ioutil"
	"os/user"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/cloudfoundry-incubator/executor/compressor/fake_compressor"
	"github.com/cloudfoundry-incubator/executor/log_streamer/fake_log_streamer"
	"github.com/cloudfoundry-incubator/executor/uploader/fake_uploader"
	"github.com/vito/gordon/fake_gordon"

	"github.com/cloudfoundry-incubator/executor/sequence"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	steno "github.com/cloudfoundry/gosteno"

	. "github.com/cloudfoundry-incubator/executor/steps/upload_step"
)

var _ = Describe("UploadStep", func() {
	var step sequence.Step
	var result chan error

	var uploadAction models.UploadAction
	var uploader *fake_uploader.FakeUploader
	var tempDir string
	var wardenClient *fake_gordon.FakeGordon
	var logger *steno.Logger
	var compressor *fake_compressor.FakeCompressor
	var streamer *fake_log_streamer.FakeLogStreamer

	BeforeEach(func() {
		var err error

		result = make(chan error)

		uploadAction = models.UploadAction{
			Name:     "Mr. Jones",
			To:       "http://mr_jones",
			From:     "/Antarctica",
			Compress: false,
		}

		uploader = &fake_uploader.FakeUploader{}

		tempDir, err = ioutil.TempDir("", "upload-step-tmpdir")
		Ω(err).ShouldNot(HaveOccurred())

		wardenClient = fake_gordon.New()

		logger = steno.NewLogger("test-logger")
		compressor = &fake_compressor.FakeCompressor{}
		streamer = fake_log_streamer.New()
	})

	var stepErr error
	JustBeforeEach(func() {
		step = New(
			"some-container-handle",
			uploadAction,
			uploader,
			compressor,
			tempDir,
			wardenClient,
			streamer,
			logger,
		)

		stepErr = step.Perform()
	})

	Describe("Perform", func() {
		Context("when successful", func() {
			It("does not return an error", func() {
				Ω(stepErr).ShouldNot(HaveOccurred())
			})

			It("uploads the file to the given URL", func() {
				Ω(uploader.UploadUrls).ShouldNot(BeEmpty())
				Ω(uploader.UploadUrls[0].Host).To(ContainSubstring("mr_jones"))
				Ω(uploader.UploadedFileLocations).ShouldNot(BeEmpty())
				Ω(uploader.UploadedFileLocations[0]).To(ContainSubstring(tempDir))
			})

			It("copies the file out of the container", func() {
				currentUser, err := user.Current()
				Ω(err).ShouldNot(HaveOccurred())

				Ω(wardenClient.ThingsCopiedOut()).ShouldNot(BeEmpty())

				copiedFile := wardenClient.ThingsCopiedOut()[0]
				Ω(copiedFile.Handle).Should(Equal("some-container-handle"))
				Ω(copiedFile.Src).To(Equal("/Antarctica"))
				Ω(copiedFile.Owner).To(Equal(currentUser.Username))
			})

			It("loggregates an upload message", func() {
				Ω(streamer.StreamedStdout).Should(ContainSubstring("Uploading Mr. Jones"))
			})
		})

		Context("when there is an error copying the file out", func() {
			disaster := errors.New("no room in the copy inn")

			BeforeEach(func() {
				wardenClient.SetCopyOutErr(disaster)
			})

			It("returns the error", func() {
				Ω(stepErr).Should(Equal(disaster))
			})
		})

		Context("when there is an error uploading", func() {
			BeforeEach(func() {
				uploader.AlwaysFail() //and bring shame and dishonor to your house
			})

			It("fails", func() {
				Ω(stepErr).Should(HaveOccurred())
			})
		})

		Context("when compress is set to true", func() {
			BeforeEach(func() {
				uploadAction = models.UploadAction{
					To:       "http://mr_jones",
					From:     "/Antarctica",
					Compress: true,
				}
			})

			It("compresses the src to a tgz file", func() {
				err := step.Perform()
				Ω(err).ShouldNot(HaveOccurred())

				Ω(compressor.Src).Should(ContainSubstring(tempDir))
				Ω(compressor.Dest).Should(ContainSubstring(tempDir))
			})

			Context("and compressing fails", func() {
				disaster := errors.New("oh no!")

				BeforeEach(func() {
					compressor.CompressError = disaster
				})

				It("returns the error", func() {
					err := step.Perform()
					Ω(err).Should(Equal(disaster))
				})
			})
		})
	})
})
<commit_msg>moved tests around<commit_after>package upload_step_test

import (
	"errors"
	"io/ioutil"
	"os/user"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/cloudfoundry-incubator/executor/compressor/fake_compressor"
	"github.com/cloudfoundry-incubator/executor/log_streamer/fake_log_streamer"
	"github.com/cloudfoundry-incubator/executor/uploader/fake_uploader"
	"github.com/vito/gordon/fake_gordon"

	"github.com/cloudfoundry-incubator/executor/sequence"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	steno "github.com/cloudfoundry/gosteno"

	. "github.com/cloudfoundry-incubator/executor/steps/upload_step"
)

var _ = Describe("UploadStep", func() {
	var step sequence.Step
	var result chan error

	var uploadAction models.UploadAction
	var uploader *fake_uploader.FakeUploader
	var tempDir string
	var wardenClient *fake_gordon.FakeGordon
	var logger *steno.Logger
	var compressor *fake_compressor.FakeCompressor
	var streamer *fake_log_streamer.FakeLogStreamer

	BeforeEach(func() {
		var err error

		result = make(chan error)

		uploadAction = models.UploadAction{
			Name:     "Mr. Jones",
			To:       "http://mr_jones",
			From:     "/Antarctica",
			Compress: false,
		}

		uploader = &fake_uploader.FakeUploader{}

		tempDir, err = ioutil.TempDir("", "upload-step-tmpdir")
		Ω(err).ShouldNot(HaveOccurred())

		wardenClient = fake_gordon.New()

		logger = steno.NewLogger("test-logger")
		compressor = &fake_compressor.FakeCompressor{}
		streamer = fake_log_streamer.New()
	})

	var stepErr error
	JustBeforeEach(func() {
		step = New(
			"some-container-handle",
			uploadAction,
			uploader,
			compressor,
			tempDir,
			wardenClient,
			streamer,
			logger,
		)

		stepErr = step.Perform()
	})

	Describe("Perform", func() {
		Context("when successful", func() {
			It("does not return an error", func() {
				Ω(stepErr).ShouldNot(HaveOccurred())
			})

			It("uploads the file to the given URL", func() {
				Ω(uploader.UploadUrls).ShouldNot(BeEmpty())
				Ω(uploader.UploadUrls[0].Host).To(ContainSubstring("mr_jones"))
			})

			It("uploads the correct file location", func() {
				Ω(uploader.UploadedFileLocations).ShouldNot(BeEmpty())
				Ω(uploader.UploadedFileLocations[0]).To(ContainSubstring(tempDir))
			})

			It("copies the file out of the container", func() {
				currentUser, err := user.Current()
				Ω(err).ShouldNot(HaveOccurred())

				Ω(wardenClient.ThingsCopiedOut()).ShouldNot(BeEmpty())

				copiedFile := wardenClient.ThingsCopiedOut()[0]
				Ω(copiedFile.Handle).Should(Equal("some-container-handle"))
				Ω(copiedFile.Src).To(Equal("/Antarctica"))
				Ω(copiedFile.Owner).To(Equal(currentUser.Username))
			})

			It("loggregates an upload message", func() {
				Ω(streamer.StreamedStdout).Should(ContainSubstring("Uploading Mr. Jones"))
			})
		})

		Context("when there is an error copying the file out", func() {
			disaster := errors.New("no room in the copy inn")

			BeforeEach(func() {
				wardenClient.SetCopyOutErr(disaster)
			})

			It("returns the error", func() {
				Ω(stepErr).Should(Equal(disaster))
			})
		})

		Context("when there is an error uploading", func() {
			BeforeEach(func() {
				uploader.AlwaysFail() //and bring shame and dishonor to your house
			})

			It("fails", func() {
				Ω(stepErr).Should(HaveOccurred())
			})
		})

		Context("when compress is set to true", func() {
			BeforeEach(func() {
				uploadAction = models.UploadAction{
					To:       "http://mr_jones",
					From:     "/Antarctica",
					Compress: true,
				}
			})

			It("compresses the src to a tgz file", func() {
				err := step.Perform()
				Ω(err).ShouldNot(HaveOccurred())

				Ω(compressor.Src).Should(ContainSubstring(tempDir))
				Ω(compressor.Dest).Should(ContainSubstring(tempDir))
			})

			Context("and compressing fails", func() {
				disaster := errors.New("oh no!")

				BeforeEach(func() {
					compressor.CompressError = disaster
				})

				It("returns the error", func() {
					err := step.Perform()
					Ω(err).Should(Equal(disaster))
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package querier

import (
	"context"
	"sync"
	"time"

	"github.com/go-kit/log/level"
	"github.com/pkg/errors"

	"github.com/grafana/loki/pkg/iter"
	loghttp "github.com/grafana/loki/pkg/loghttp/legacy"
	"github.com/grafana/loki/pkg/logproto"
	util_log "github.com/grafana/loki/pkg/util/log"
)

const (
	// keep checking connections with ingesters in duration
	checkConnectionsWithIngestersPeriod = time.Second * 5

	// the size of the channel buffer used to send tailing streams
	// back to the requesting client
	maxBufferedTailResponses = 10

	// the maximum number of entries to return in a TailResponse
	maxEntriesPerTailResponse = 100

	// the maximum number of dropped entries to keep in memory that will be sent along
	// with the next successfully pushed response. Once the dropped entries memory buffer
	// exceed this value, we start skipping dropped entries too.
	maxDroppedEntriesPerTailResponse = 1000
)

// Tailer manages complete lifecycle of a tail request
type Tailer struct {
	// openStreamIterator is for streams already open
	openStreamIterator iter.HeapIterator
	streamMtx          sync.Mutex // for synchronizing access to openStreamIterator

	currEntry  logproto.Entry
	currLabels string

	tailDisconnectedIngesters func([]string) (map[string]logproto.Querier_TailClient, error)

	querierTailClients    map[string]logproto.Querier_TailClient // addr -> grpc clients for tailing logs from ingesters
	querierTailClientsMtx sync.RWMutex

	stopped         bool
	delayFor        time.Duration
	responseChan    chan *loghttp.TailResponse
	closeErrChan    chan error
	tailMaxDuration time.Duration

	// if we are not seeing any response from ingester,
	// how long do we want to wait by going into sleep
	waitEntryThrottle time.Duration
}

func (t *Tailer) readTailClients() {
	t.querierTailClientsMtx.RLock()
	defer t.querierTailClientsMtx.RUnlock()

	for addr, querierTailClient := range t.querierTailClients {
		go t.readTailClient(addr, querierTailClient)
	}
}

// keeps sending oldest entry to responseChan. If channel is blocked drop the entry
// When channel is unblocked, send details of dropped entries with current entry
func (t *Tailer) loop() {
	checkConnectionTicker := time.NewTicker(checkConnectionsWithIngestersPeriod)
	defer checkConnectionTicker.Stop()

	tailMaxDurationTicker := time.NewTicker(t.tailMaxDuration)
	defer tailMaxDurationTicker.Stop()

	droppedEntries := make([]loghttp.DroppedEntry, 0)

	for !t.stopped {
		select {
		case <-checkConnectionTicker.C:
			// Try to reconnect dropped ingesters and connect to new ingesters
			if err := t.checkIngesterConnections(); err != nil {
				level.Error(util_log.Logger).Log("msg", "Error reconnecting to disconnected ingesters", "err", err)
			}
		case <-tailMaxDurationTicker.C:
			if err := t.close(); err != nil {
				level.Error(util_log.Logger).Log("msg", "Error closing Tailer", "err", err)
			}
			t.closeErrChan <- errors.New("reached tail max duration limit")
			return
		default:
		}

		// Read as much entries as we can (up to the max allowed) and populate the
		// tail response we'll send over the response channel
		tailResponse := new(loghttp.TailResponse)
		entriesCount := 0

		for ; entriesCount < maxEntriesPerTailResponse && t.next(); entriesCount++ {
			// If the response channel channel is blocked, we drop the current entry directly
			// to save the effort
			if t.isResponseChanBlocked() {
				droppedEntries = dropEntry(droppedEntries, t.currEntry.Timestamp, t.currLabels)
				continue
			}

			tailResponse.Streams = append(tailResponse.Streams, logproto.Stream{
				Labels:  t.currLabels,
				Entries: []logproto.Entry{t.currEntry},
			})
		}

		// If all consumed entries have been dropped because the response channel is blocked
		// we should reiterate on the loop
		if len(tailResponse.Streams) == 0 && entriesCount > 0 {
			continue
		}

		// If no entry has been consumed we should ensure it's not caused by all ingesters
		// connections dropped and then throttle for a while
		if len(tailResponse.Streams) == 0 {
			t.querierTailClientsMtx.RLock()
			numClients := len(t.querierTailClients)
			t.querierTailClientsMtx.RUnlock()

			if numClients == 0 {
				// All the connections to ingesters are dropped, try reconnecting or return error
				if err := t.checkIngesterConnections(); err != nil {
					level.Error(util_log.Logger).Log("msg", "Error reconnecting to ingesters", "err", err)
				} else {
					continue
				}
				if err := t.close(); err != nil {
					level.Error(util_log.Logger).Log("msg", "Error closing Tailer", "err", err)
				}
				t.closeErrChan <- errors.New("all ingesters closed the connection")
				return
			}

			time.Sleep(t.waitEntryThrottle)
			continue
		}

		// Send the tail response through the response channel without blocking.
		// Drop the entry if the response channel buffer is full.
		if len(droppedEntries) > 0 {
			tailResponse.DroppedEntries = droppedEntries
		}

		select {
		case t.responseChan <- tailResponse:
			if len(droppedEntries) > 0 {
				droppedEntries = make([]loghttp.DroppedEntry, 0)
			}
		default:
			droppedEntries = dropEntries(droppedEntries, tailResponse.Streams)
		}
	}
}

// Checks whether we are connected to all the ingesters to tail the logs.
// Helps in connecting to disconnected ingesters or connecting to new ingesters
func (t *Tailer) checkIngesterConnections() error {
	t.querierTailClientsMtx.Lock()
	defer t.querierTailClientsMtx.Unlock()

	connectedIngestersAddr := make([]string, 0, len(t.querierTailClients))
	for addr := range t.querierTailClients {
		connectedIngestersAddr = append(connectedIngestersAddr, addr)
	}

	newConnections, err := t.tailDisconnectedIngesters(connectedIngestersAddr)
	if err != nil {
		return err
	}

	if len(newConnections) != 0 {
		for addr, tailClient := range newConnections {
			t.querierTailClients[addr] = tailClient
			go t.readTailClient(addr, tailClient)
		}
	}
	return nil
}

// removes disconnected tail client from map
func (t *Tailer) dropTailClient(addr string) {
	t.querierTailClientsMtx.Lock()
	defer t.querierTailClientsMtx.Unlock()

	delete(t.querierTailClients, addr)
}

// keeps reading streams from grpc connection with ingesters
func (t *Tailer) readTailClient(addr string, querierTailClient logproto.Querier_TailClient) {
	var resp *logproto.TailResponse
	var err error
	defer t.dropTailClient(addr)

	logger := util_log.WithContext(querierTailClient.Context(), util_log.Logger)
	for {
		if t.stopped {
			if err := querierTailClient.CloseSend(); err != nil {
				level.Error(logger).Log("msg", "Error closing grpc tail client", "err", err)
			}
			break
		}
		resp, err = querierTailClient.Recv()
		if err != nil {
			// We don't want to log error when its due to stopping the tail request
			if !t.stopped {
				level.Error(logger).Log("msg", "Error receiving response from grpc tail client", "err", err)
			}
			break
		}
		t.pushTailResponseFromIngester(resp)
	}
}

// pushes new streams from ingesters synchronously
func (t *Tailer) pushTailResponseFromIngester(resp *logproto.TailResponse) {
	t.streamMtx.Lock()
	defer t.streamMtx.Unlock()

	t.openStreamIterator.Push(iter.NewStreamIterator(*resp.Stream))
}

// finds oldest entry by peeking at open stream iterator.
// Response from ingester is pushed to open stream for further processing
func (t *Tailer) next() bool {
	t.streamMtx.Lock()
	defer t.streamMtx.Unlock()

	if t.openStreamIterator.Len() == 0 || !time.Now().After(t.openStreamIterator.Peek().Add(t.delayFor)) || !t.openStreamIterator.Next() {
		return false
	}

	t.currEntry = t.openStreamIterator.Entry()
	t.currLabels = t.openStreamIterator.Labels()
	return true
}

func (t *Tailer) close() error {
	t.streamMtx.Lock()
	defer t.streamMtx.Unlock()

	t.stopped = true
	return t.openStreamIterator.Close()
}

func (t *Tailer) isResponseChanBlocked() bool {
	// Thread-safety: len() and cap() on a channel are thread-safe. The cap() doesn't
	// change over the time, while len() does.
	return len(t.responseChan) == cap(t.responseChan)
}

func (t *Tailer) getResponseChan() <-chan *loghttp.TailResponse {
	return t.responseChan
}

func (t *Tailer) getCloseErrorChan() <-chan error {
	return t.closeErrChan
}

func newTailer(
	delayFor time.Duration,
	querierTailClients map[string]logproto.Querier_TailClient,
	historicEntries iter.EntryIterator,
	tailDisconnectedIngesters func([]string) (map[string]logproto.Querier_TailClient, error),
	tailMaxDuration time.Duration,
	waitEntryThrottle time.Duration,
) *Tailer {
	t := Tailer{
		openStreamIterator:        iter.NewMergeEntryIterator(context.Background(), []iter.EntryIterator{historicEntries}, logproto.FORWARD),
		querierTailClients:        querierTailClients,
		delayFor:                  delayFor,
		responseChan:              make(chan *loghttp.TailResponse, maxBufferedTailResponses),
		closeErrChan:              make(chan error),
		tailDisconnectedIngesters: tailDisconnectedIngesters,
		tailMaxDuration:           tailMaxDuration,
		waitEntryThrottle:         waitEntryThrottle,
	}

	t.readTailClients()
	go t.loop()
	return &t
}

func dropEntry(droppedEntries []loghttp.DroppedEntry, timestamp time.Time, labels string) []loghttp.DroppedEntry {
	if len(droppedEntries) >= maxDroppedEntriesPerTailResponse {
		return droppedEntries
	}

	return append(droppedEntries, loghttp.DroppedEntry{Timestamp: timestamp, Labels: labels})
}

func dropEntries(droppedEntries []loghttp.DroppedEntry, streams []logproto.Stream) []loghttp.DroppedEntry {
	for _, stream := range streams {
		for _, entry := range stream.Entries {
			droppedEntries = dropEntry(droppedEntries, entry.Timestamp, entry.Line)
		}
	}

	return droppedEntries
}
<commit_msg>Add more context to tailer-> ingester connect error. (#5394)<commit_after>package querier

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/go-kit/log/level"
	"github.com/pkg/errors"

	"github.com/grafana/loki/pkg/iter"
	loghttp "github.com/grafana/loki/pkg/loghttp/legacy"
	"github.com/grafana/loki/pkg/logproto"
	util_log "github.com/grafana/loki/pkg/util/log"
)

const (
	// keep checking connections with ingesters in duration
	checkConnectionsWithIngestersPeriod = time.Second * 5

	// the size of the channel buffer used to send tailing streams
	// back to the requesting client
	maxBufferedTailResponses = 10

	// the maximum number of entries to return in a TailResponse
	maxEntriesPerTailResponse = 100

	// the maximum number of dropped entries to keep in memory that will be sent along
	// with the next successfully pushed response. Once the dropped entries memory buffer
	// exceed this value, we start skipping dropped entries too.
	maxDroppedEntriesPerTailResponse = 1000
)

// Tailer manages complete lifecycle of a tail request
type Tailer struct {
	// openStreamIterator is for streams already open
	openStreamIterator iter.HeapIterator
	streamMtx          sync.Mutex // for synchronizing access to openStreamIterator

	currEntry  logproto.Entry
	currLabels string

	tailDisconnectedIngesters func([]string) (map[string]logproto.Querier_TailClient, error)

	querierTailClients    map[string]logproto.Querier_TailClient // addr -> grpc clients for tailing logs from ingesters
	querierTailClientsMtx sync.RWMutex

	stopped         bool
	delayFor        time.Duration
	responseChan    chan *loghttp.TailResponse
	closeErrChan    chan error
	tailMaxDuration time.Duration

	// if we are not seeing any response from ingester,
	// how long do we want to wait by going into sleep
	waitEntryThrottle time.Duration
}

func (t *Tailer) readTailClients() {
	t.querierTailClientsMtx.RLock()
	defer t.querierTailClientsMtx.RUnlock()

	for addr, querierTailClient := range t.querierTailClients {
		go t.readTailClient(addr, querierTailClient)
	}
}

// keeps sending oldest entry to responseChan. If channel is blocked drop the entry
// When channel is unblocked, send details of dropped entries with current entry
func (t *Tailer) loop() {
	checkConnectionTicker := time.NewTicker(checkConnectionsWithIngestersPeriod)
	defer checkConnectionTicker.Stop()

	tailMaxDurationTicker := time.NewTicker(t.tailMaxDuration)
	defer tailMaxDurationTicker.Stop()

	droppedEntries := make([]loghttp.DroppedEntry, 0)

	for !t.stopped {
		select {
		case <-checkConnectionTicker.C:
			// Try to reconnect dropped ingesters and connect to new ingesters
			if err := t.checkIngesterConnections(); err != nil {
				level.Error(util_log.Logger).Log("msg", "Error reconnecting to disconnected ingesters", "err", err)
			}
		case <-tailMaxDurationTicker.C:
			if err := t.close(); err != nil {
				level.Error(util_log.Logger).Log("msg", "Error closing Tailer", "err", err)
			}
			t.closeErrChan <- errors.New("reached tail max duration limit")
			return
		default:
		}

		// Read as much entries as we can (up to the max allowed) and populate the
		// tail response we'll send over the response channel
		tailResponse := new(loghttp.TailResponse)
		entriesCount := 0

		for ; entriesCount < maxEntriesPerTailResponse && t.next(); entriesCount++ {
			// If the response channel channel is blocked, we drop the current entry directly
			// to save the effort
			if t.isResponseChanBlocked() {
				droppedEntries = dropEntry(droppedEntries, t.currEntry.Timestamp, t.currLabels)
				continue
			}

			tailResponse.Streams = append(tailResponse.Streams, logproto.Stream{
				Labels:  t.currLabels,
				Entries: []logproto.Entry{t.currEntry},
			})
		}

		// If all consumed entries have been dropped because the response channel is blocked
		// we should reiterate on the loop
		if len(tailResponse.Streams) == 0 && entriesCount > 0 {
			continue
		}

		// If no entry has been consumed we should ensure it's not caused by all ingesters
		// connections dropped and then throttle for a while
		if len(tailResponse.Streams) == 0 {
			t.querierTailClientsMtx.RLock()
			numClients := len(t.querierTailClients)
			t.querierTailClientsMtx.RUnlock()

			if numClients == 0 {
				// All the connections to ingesters are dropped, try reconnecting or return error
				if err := t.checkIngesterConnections(); err != nil {
					level.Error(util_log.Logger).Log("msg", "Error reconnecting to ingesters", "err", err)
				} else {
					continue
				}
				if err := t.close(); err != nil {
					level.Error(util_log.Logger).Log("msg", "Error closing Tailer", "err", err)
				}
				t.closeErrChan <- errors.New("all ingesters closed the connection")
				return
			}

			time.Sleep(t.waitEntryThrottle)
			continue
		}

		// Send the tail response through the response channel without blocking.
		// Drop the entry if the response channel buffer is full.
		if len(droppedEntries) > 0 {
			tailResponse.DroppedEntries = droppedEntries
		}

		select {
		case t.responseChan <- tailResponse:
			if len(droppedEntries) > 0 {
				droppedEntries = make([]loghttp.DroppedEntry, 0)
			}
		default:
			droppedEntries = dropEntries(droppedEntries, tailResponse.Streams)
		}
	}
}

// Checks whether we are connected to all the ingesters to tail the logs.
// Helps in connecting to disconnected ingesters or connecting to new ingesters
func (t *Tailer) checkIngesterConnections() error {
	t.querierTailClientsMtx.Lock()
	defer t.querierTailClientsMtx.Unlock()

	connectedIngestersAddr := make([]string, 0, len(t.querierTailClients))
	for addr := range t.querierTailClients {
		connectedIngestersAddr = append(connectedIngestersAddr, addr)
	}

	newConnections, err := t.tailDisconnectedIngesters(connectedIngestersAddr)
	if err != nil {
		return fmt.Errorf("failed to connect with one or more ingester(s) during tailing: %w", err)
	}

	if len(newConnections) != 0 {
		for addr, tailClient := range newConnections {
			t.querierTailClients[addr] = tailClient
			go t.readTailClient(addr, tailClient)
		}
	}
	return nil
}

// removes disconnected tail client from map
func (t *Tailer) dropTailClient(addr string) {
	t.querierTailClientsMtx.Lock()
	defer t.querierTailClientsMtx.Unlock()

	delete(t.querierTailClients, addr)
}

// keeps reading streams from grpc connection with ingesters
func (t *Tailer) readTailClient(addr string, querierTailClient logproto.Querier_TailClient) {
	var resp *logproto.TailResponse
	var err error
	defer t.dropTailClient(addr)

	logger := util_log.WithContext(querierTailClient.Context(), util_log.Logger)
	for {
		if t.stopped {
			if err := querierTailClient.CloseSend(); err != nil {
				level.Error(logger).Log("msg", "Error closing grpc tail client", "err", err)
			}
			break
		}
		resp, err = querierTailClient.Recv()
		if err != nil {
			// We don't want to log error when its due to stopping the tail request
			if !t.stopped {
				level.Error(logger).Log("msg", "Error receiving response from grpc tail client", "err", err)
			}
			break
		}
		t.pushTailResponseFromIngester(resp)
	}
}

// pushes new streams from ingesters synchronously
func (t *Tailer) pushTailResponseFromIngester(resp *logproto.TailResponse) {
	t.streamMtx.Lock()
	defer t.streamMtx.Unlock()

	t.openStreamIterator.Push(iter.NewStreamIterator(*resp.Stream))
}

// finds oldest entry by peeking at open stream iterator.
// Response from ingester is pushed to open stream for further processing
func (t *Tailer) next() bool {
	t.streamMtx.Lock()
	defer t.streamMtx.Unlock()

	if t.openStreamIterator.Len() == 0 || !time.Now().After(t.openStreamIterator.Peek().Add(t.delayFor)) || !t.openStreamIterator.Next() {
		return false
	}

	t.currEntry = t.openStreamIterator.Entry()
	t.currLabels = t.openStreamIterator.Labels()
	return true
}

func (t *Tailer) close() error {
	t.streamMtx.Lock()
	defer t.streamMtx.Unlock()

	t.stopped = true
	return t.openStreamIterator.Close()
}

func (t *Tailer) isResponseChanBlocked() bool {
	// Thread-safety: len() and cap() on a channel are thread-safe. The cap() doesn't
	// change over the time, while len() does.
	return len(t.responseChan) == cap(t.responseChan)
}

func (t *Tailer) getResponseChan() <-chan *loghttp.TailResponse {
	return t.responseChan
}

func (t *Tailer) getCloseErrorChan() <-chan error {
	return t.closeErrChan
}

func newTailer(
	delayFor time.Duration,
	querierTailClients map[string]logproto.Querier_TailClient,
	historicEntries iter.EntryIterator,
	tailDisconnectedIngesters func([]string) (map[string]logproto.Querier_TailClient, error),
	tailMaxDuration time.Duration,
	waitEntryThrottle time.Duration,
) *Tailer {
	t := Tailer{
		openStreamIterator:        iter.NewMergeEntryIterator(context.Background(), []iter.EntryIterator{historicEntries}, logproto.FORWARD),
		querierTailClients:        querierTailClients,
		delayFor:                  delayFor,
		responseChan:              make(chan *loghttp.TailResponse, maxBufferedTailResponses),
		closeErrChan:              make(chan error),
		tailDisconnectedIngesters: tailDisconnectedIngesters,
		tailMaxDuration:           tailMaxDuration,
		waitEntryThrottle:         waitEntryThrottle,
	}

	t.readTailClients()
	go t.loop()
	return &t
}

func dropEntry(droppedEntries []loghttp.DroppedEntry, timestamp time.Time, labels string) []loghttp.DroppedEntry {
	if len(droppedEntries) >= maxDroppedEntriesPerTailResponse {
		return droppedEntries
	}

	return append(droppedEntries, loghttp.DroppedEntry{Timestamp: timestamp, Labels: labels})
}

func dropEntries(droppedEntries []loghttp.DroppedEntry, streams []logproto.Stream) []loghttp.DroppedEntry {
	for _, stream := range streams {
		for _, entry := range stream.Entries {
			droppedEntries = dropEntry(droppedEntries, entry.Timestamp, entry.Line)
		}
	}

	return droppedEntries
}
<|endoftext|>


<commit_before>package slinga

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
)

import (
	"k8s.io/kubernetes/pkg/api"
	k8slabels "k8s.io/kubernetes/pkg/labels"
)

// ProcessIstioIngress processes global rules and applies Istio routing rules for ingresses
func (usage *ServiceUsageState) ProcessIstioIngress(noop bool) {
	if len(usage.getResolvedUsage().ComponentProcessingOrder) == 0 {
		return
	}

	fmt.Println("[Routes]")

	progress := NewProgress()
	progressBar := AddProgressBar(progress, len(usage.getResolvedUsage().ComponentProcessingOrder))

	desiredBlockedServices := make([]string, 0)

	// Process in the right order
	for _, key := range usage.getResolvedUsage().ComponentProcessingOrder {
		services, err := processComponent(key, usage)
		if err != nil {
			debug.WithFields(log.Fields{
				"key":   key,
				"error": err,
			}).Fatal("Unable to process Istio Ingress for component")
		}
		desiredBlockedServices = append(desiredBlockedServices, services...)
		progressBar.Incr()
	}

	progress.Stop()

	fmt.Println("Desired blocked services:", desiredBlockedServices)

	// todo(slukjanov): add actual istio route rules creation/deletion here
}

func processComponent(key string, usage *ServiceUsageState) ([]string, error) {
	serviceName, _, _, componentName := ParseServiceUsageKey(key)
	component := usage.Policy.Services[serviceName].getComponentsMap()[componentName]

	labels := usage.ResolvedUsage.ComponentInstanceMap[key].CalculatedLabels

	// todo(slukjanov): temp hack - expecting that cluster is always passed through the label "cluster"
	var cluster *Cluster
	if clusterLabel, ok := labels.Labels["cluster"]; ok {
		if cluster, ok = usage.Policy.Clusters[clusterLabel]; !ok {
			debug.WithFields(log.Fields{
				"component": key,
				"labels":    labels.Labels,
			}).Fatal("Can't find cluster for component (based on label 'cluster')")
		}
	}

	// get all users who're using service
	userIds := usage.ResolvedUsage.ComponentInstanceMap[key].UserIds
	users := make([]*User, 0)
	for _, userID := range userIds {
		// todo check if user doesn't exists
		users = append(users, usage.users.Users[userID])
	}

	if !usage.Policy.Rules.allowsIngressAccess(labels, users, cluster) && component != nil && component.Code != nil {
		codeExecutor, err := component.Code.GetCodeExecutor(key, component.Code.Metadata, usage.getResolvedUsage().ComponentInstanceMap[key].CalculatedCodeParams, usage.Policy.Clusters)
		if err != nil {
			return nil, err
		}

		if helmCodeExecutor, ok := codeExecutor.(HelmCodeExecutor); ok {
			services, err := helmCodeExecutor.httpServices()
			if err != nil {
				return nil, err
			}

			return services, nil
		}
	}

	return nil, nil
}

// httpServices returns list of services for the current chart
func (exec HelmCodeExecutor) httpServices() ([]string, error) {
	_, clientset, err := exec.newKubeClient()
	if err != nil {
		return nil, err
	}

	coreClient := clientset.Core()

	releaseName := releaseName(exec.Key)
	chartName := exec.Metadata["chartName"]

	selector := k8slabels.Set{"release": releaseName, "chart": chartName}.AsSelector()
	options := api.ListOptions{LabelSelector: selector}

	// Check all corresponding services
	services, err := coreClient.Services(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}

	// Check all corresponding Istio ingresses
	ingresses, err := clientset.Extensions().Ingresses(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}

	if len(ingresses.Items) > 0 {
		result := make([]string, 0)
		for _, service := range services.Items {
			result = append(result, service.Name)
		}

		return result, nil
	}

	return nil, nil
}
<commit_msg>Next steps for istio ingress block rules support<commit_after>package slinga

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
)

import (
	"k8s.io/kubernetes/pkg/api"
	k8slabels "k8s.io/kubernetes/pkg/labels"
)

// ProcessIstioIngress processes global rules and applies Istio routing rules for ingresses
func (usage *ServiceUsageState) ProcessIstioIngress(noop bool) {
	if len(usage.getResolvedUsage().ComponentProcessingOrder) == 0 || noop {
		return
	}

	fmt.Println("[Routes]")

	progress := NewProgress()
	progressBar := AddProgressBar(progress, len(usage.getResolvedUsage().ComponentProcessingOrder))

	desiredBlockedServices := make([]string, 0)

	// Process in the right order
	for _, key := range usage.getResolvedUsage().ComponentProcessingOrder {
		services, err := processComponent(key, usage)
		if err != nil {
			debug.WithFields(log.Fields{
				"key":   key,
				"error": err,
			}).Fatal("Unable to process Istio Ingress for component")
		}
		desiredBlockedServices = append(desiredBlockedServices, services...)
		progressBar.Incr()
	}

	progress.Stop()

	fmt.Println("Desired blocked services:", desiredBlockedServices)

	// todo(slukjanov): add actual istio route rules creation/deletion here
}

func processComponent(key string, usage *ServiceUsageState) ([]string, error) {
	serviceName, _, _, componentName := ParseServiceUsageKey(key)
	component := usage.Policy.Services[serviceName].getComponentsMap()[componentName]

	labels := usage.ResolvedUsage.ComponentInstanceMap[key].CalculatedLabels

	// todo(slukjanov): temp hack - expecting that cluster is always passed through the label "cluster"
	var cluster *Cluster
	if clusterLabel, ok := labels.Labels["cluster"]; ok {
		if cluster, ok = usage.Policy.Clusters[clusterLabel]; !ok {
			debug.WithFields(log.Fields{
				"component": key,
				"labels":    labels.Labels,
			}).Fatal("Can't find cluster for component (based on label 'cluster')")
		}
	}

	// get all users who're using service
	userIds := usage.ResolvedUsage.ComponentInstanceMap[key].UserIds
	users := make([]*User, 0)
	for _, userID := range userIds {
		// todo check if user doesn't exists
		users = append(users, usage.users.Users[userID])
	}

	if !usage.Policy.Rules.allowsIngressAccess(labels, users, cluster) && component != nil && component.Code != nil {
		codeExecutor, err := component.Code.GetCodeExecutor(key, component.Code.Metadata, usage.getResolvedUsage().ComponentInstanceMap[key].CalculatedCodeParams, usage.Policy.Clusters)
		if err != nil {
			return nil, err
		}

		if helmCodeExecutor, ok := codeExecutor.(HelmCodeExecutor); ok {
			services, err := helmCodeExecutor.httpServices()
			if err != nil {
				return nil, err
			}

			for _, service := range services {
				content := "type: route-rule\n"
				content += "name: block-" + service + "\n"
				content += "spec:\n"
				content += "  destination: " + service + "." + cluster.Metadata.Namespace + ".svc.cluster.local\n"
				content += "  httpReqTimeout:\n"
				content += "    simpleTimeout:\n"
				content += "      timeout: 1ms\n"

				ruleFile := writeTempFile("istio-rule", content)
				fmt.Println("Istio rule file:", ruleFile.Name())
				//TODO: slukjanov: defer os.Remove(tmpFile.Name())

				content = "set -ex\n"
				content += "kubectl config use-context " + cluster.Name + "\n"
				// todo(slukjanov): find istio pilot service automatically
				content += "istioctl --configAPIService istio-prod-production-istio-istio-pilot:8081 --namespace " + cluster.Metadata.Namespace + " "
				content += "create -f " + ruleFile.Name() + "\n"

				cmdFile := writeTempFile("istioctl", content)
				fmt.Println("Istio cmd file:", cmdFile.Name())
			}

			return services, nil
		}
	}

	return nil, nil
}

// httpServices returns list of services for the current chart
func (exec HelmCodeExecutor) httpServices() ([]string, error) {
	_, clientset, err := exec.newKubeClient()
	if err != nil {
		return nil, err
	}

	coreClient := clientset.Core()

	releaseName := releaseName(exec.Key)
	chartName := exec.Metadata["chartName"]

	selector := k8slabels.Set{"release": releaseName, "chart": chartName}.AsSelector()
	options := api.ListOptions{LabelSelector: selector}

	// Check all corresponding services
	services, err := coreClient.Services(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}

	// Check all corresponding Istio ingresses
	ingresses, err := clientset.Extensions().Ingresses(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}

	if len(ingresses.Items) > 0 {
		result := make([]string, 0)
		for _, service := range services.Items {
			result = append(result, service.Name)
		}

		return result, nil
	}

	return nil, nil
}
<|endoftext|>


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

package runtime_test

import (
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	. "runtime"
	"strconv"
	"strings"
	"testing"
)

var errf error

func errfn() error {
	return errf
}

func errfn1() error {
	return io.EOF
}

func BenchmarkIfaceCmp100(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for j := 0; j < 100; j++ {
			if errfn() == io.EOF {
				b.Fatal("bad comparison")
			}
		}
	}
}

func BenchmarkIfaceCmpNil100(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for j := 0; j < 100; j++ {
			if errfn1() == nil {
				b.Fatal("bad comparison")
			}
		}
	}
}

func BenchmarkDefer(b *testing.B) {
	for i := 0; i < b.N; i++ {
		defer1()
	}
}

func defer1() {
	defer func(x, y, z int) {
		if recover() != nil || x != 1 || y != 2 || z != 3 {
			panic("bad recover")
		}
	}(1, 2, 3)
	return
}

func BenchmarkDefer10(b *testing.B) {
	for i := 0; i < b.N/10; i++ {
		defer2()
	}
}

func defer2() {
	for i := 0; i < 10; i++ {
		defer func(x, y, z int) {
			if recover() != nil || x != 1 || y != 2 || z != 3 {
				panic("bad recover")
			}
		}(1, 2, 3)
	}
}

func BenchmarkDeferMany(b *testing.B) {
	for i := 0; i < b.N; i++ {
		defer func(x, y, z int) {
			if recover() != nil || x != 1 || y != 2 || z != 3 {
				panic("bad recover")
			}
		}(1, 2, 3)
	}
}

// The profiling signal handler needs to know whether it is executing runtime.gogo.
// The constant RuntimeGogoBytes in arch_*.h gives the size of the function;
// we don't have a way to obtain it from the linker (perhaps someday).
// Test that the constant matches the size determined by 'go tool nm -S'.
// The value reported will include the padding between runtime.gogo and the
// next function in memory. That's fine.
func TestRuntimeGogoBytes(t *testing.T) {
	dir, err := ioutil.TempDir("", "go-build")
	if err != nil {
		t.Fatalf("failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(dir)

	out, err := exec.Command("go", "build", "-o", dir+"/hello", "../../../test/helloworld.go").CombinedOutput()
	if err != nil {
		t.Fatalf("building hello world: %v\n%s", err, out)
	}

	out, err = exec.Command("go", "tool", "nm", "-S", dir+"/hello").CombinedOutput()
	if err != nil {
		t.Fatalf("go tool nm: %v\n%s", err, out)
	}

	for _, line := range strings.Split(string(out), "\n") {
		f := strings.Fields(line)
		if len(f) == 4 && f[3] == "runtime.gogo" {
			size, _ := strconv.Atoi(f[1])
			if GogoBytes() != int32(size) {
				t.Fatalf("RuntimeGogoBytes = %d, should be %d", GogoBytes(), size)
			}
			return
		}
	}

	t.Fatalf("go tool nm did not report size for runtime.gogo")
}
<commit_msg>runtime: fix test<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime_test

import (
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	. "runtime"
	"strconv"
	"strings"
	"testing"
)

var errf error

func errfn() error {
	return errf
}

func errfn1() error {
	return io.EOF
}

func BenchmarkIfaceCmp100(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for j := 0; j < 100; j++ {
			if errfn() == io.EOF {
				b.Fatal("bad comparison")
			}
		}
	}
}

func BenchmarkIfaceCmpNil100(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for j := 0; j < 100; j++ {
			if errfn1() == nil {
				b.Fatal("bad comparison")
			}
		}
	}
}

func BenchmarkDefer(b *testing.B) {
	for i := 0; i < b.N; i++ {
		defer1()
	}
}

func defer1() {
	defer func(x, y, z int) {
		if recover() != nil || x != 1 || y != 2 || z != 3 {
			panic("bad recover")
		}
	}(1, 2, 3)
	return
}

func BenchmarkDefer10(b *testing.B) {
	for i := 0; i < b.N/10; i++ {
		defer2()
	}
}

func defer2() {
	for i := 0; i < 10; i++ {
		defer func(x, y, z int) {
			if recover() != nil || x != 1 || y != 2 || z != 3 {
				panic("bad recover")
			}
		}(1, 2, 3)
	}
}

func BenchmarkDeferMany(b *testing.B) {
	for i := 0; i < b.N; i++ {
		defer func(x, y, z int) {
			if recover() != nil || x != 1 || y != 2 || z != 3 {
				panic("bad recover")
			}
		}(1, 2, 3)
	}
}

// The profiling signal handler needs to know whether it is executing runtime.gogo.
// The constant RuntimeGogoBytes in arch_*.h gives the size of the function;
// we don't have a way to obtain it from the linker (perhaps someday).
// Test that the constant matches the size determined by 'go tool nm -S'.
// The value reported will include the padding between runtime.gogo and the
// next function in memory. That's fine.
func TestRuntimeGogoBytes(t *testing.T) {
	dir, err := ioutil.TempDir("", "go-build")
	if err != nil {
		t.Fatalf("failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(dir)

	out, err := exec.Command("go", "build", "-o", dir+"/hello", "../../../test/helloworld.go").CombinedOutput()
	if err != nil {
		t.Fatalf("building hello world: %v\n%s", err, out)
	}

	out, err = exec.Command("go", "tool", "nm", "-size", dir+"/hello").CombinedOutput()
	if err != nil {
		t.Fatalf("go tool nm: %v\n%s", err, out)
	}

	for _, line := range strings.Split(string(out), "\n") {
		f := strings.Fields(line)
		if len(f) == 4 && f[3] == "runtime.gogo" {
			size, _ := strconv.Atoi(f[1])
			if GogoBytes() != int32(size) {
				t.Fatalf("RuntimeGogoBytes = %d, should be %d", GogoBytes(), size)
			}
			return
		}
	}

	t.Fatalf("go tool nm did not report size for runtime.gogo")
}
<|endoftext|>


<commit_before>// Boringstreamer looks for mp3 files and broadcasts via http.
// $ boringstreamer -addr 4444 -max 42 /
// recursively looks for mp3 files starting from / and broadcasts on port 4444 for at most 42 concurrent streamer clients.
// Browse to listen (e.g. http://localhost:4444/)
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/tcolgate/mp3"
)

var (
	addr           = flag.String("addr", ":4444", "listen on address (format: :port or host:port)")
	maxConnections = flag.Int("max", 42, "set maximum number of streaming connections")
	recursively    = flag.Bool("r", true, "recursively look for music starting from path")
	verbose        = flag.Bool("v", false, "display verbose messages")
)

// like /dev/null
type nullWriter struct {
}

func (nw nullWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

type streamFrame []byte

// client's event
type broadcastResult struct {
	qid int
	err error
}

// After a start() mux broadcasts audio stream to it's listener clients.
// Clients subscribe() and unsubscribe by writing to result chanel.
type mux struct {
	sync.Mutex

	clients map[int]chan streamFrame // set of listener clients to be notified
	result  chan broadcastResult     // clients share broadcast success-failure here

	nextFile   chan string      // next file to be broadcast
	nextStream chan io.Reader   // next (ID3 stripped) raw audio stream
	nextFrame  chan streamFrame // next audio frame
}

// subscribe(ch) adds ch to the set of channels to be received on by the clients when a new audio frame is available.
// Returns uniq client id (qid) for ch and a broadcast result channel for the client.
// Returns -1, nil if too many clients are already listening.
// clients: qid, br := m.subscribe(ch)
func (m *mux) subscribe(ch chan streamFrame) (int, chan broadcastResult) {
	m.Lock()
	defer m.Unlock()
	// search for available qid
	qid := 0
	_, ok := m.clients[qid]
	for ; ok; _, ok = m.clients[qid] {
		if qid >= *maxConnections-1 {
			return -1, nil
		}
		qid++
	}
	m.clients[qid] = ch
	if *verbose {
		log.Printf("New connection (qid: %v), streaming to %v connections.", qid, len(m.clients))
	}

	return qid, m.result
}

// stripID3Header(r) reads file from r, strips id3v2 headers and returns the rest
// id3v2 tag details: id3.org
func stripID3Header(r io.Reader) io.Reader {
	buf, err := ioutil.ReadAll(r)
	if err != nil {
		log.Printf("Error: skipping file, stripID3Header(), err=%v", err)
		return bytes.NewReader(make([]byte, 0))
	}

	// TODO(fgergo) add ID3 v1 detection
	if string(buf[:3]) != "ID3" {
		return bytes.NewReader(buf) // no ID3 header
	}

	// The ID3v2 tag size is encoded in four bytes
	// where msb (bit 7) is set to zero in every byte,
	// ie. tag size is at most 2^28 (4*8-4=28).
	id3size := int32(buf[6])<<21 | int32(buf[7])<<14 | int32(buf[8])<<7 | int32(buf[9])
	id3size += 10 // calculated tag size is excluding the header => +10

	return bytes.NewReader(buf[id3size:])
}

// genFileList() periodically checks for files available from root and
// sends filenames down chan queue.
func genFileList(root string, queue chan string) {
	rand.Seed(time.Now().Unix()) // minimal randomness

	rescan := make(chan chan string)
	go func() {
		for {
			files := <-rescan
			filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
				if err != nil {
					return err
				}
				if !info.Mode().IsRegular() {
					return nil
				}
				ok := strings.HasSuffix(strings.ToLower(info.Name()), ".mp3") // probably file is mp3
				if !info.IsDir() && !ok {
					return nil
				}
				files <- path // found file

				return nil
			})
			close(files)
			time.Sleep(1 * time.Second) // poll at least with 1Hz
		}
	}()

	// buffer and shuffle
	go func() {
		for {
			files := make(chan string)
			rescan <- files

			shuffled := make([]string, 0) // randomized set of files

			for f := range files {
				select {
				case queue <- f: // start playing as soon as possible
					if *verbose {
						fmt.Printf("Next: %v\n", f)
					}
					continue
				default:
					// shuffle files for random playback
					// (random permutation)
					if len(shuffled) == 0 {
						shuffled = append(shuffled, f)
					} else {
						i := rand.Intn(len(shuffled))
						shuffled = append(shuffled, shuffled[i])
						shuffled[i] = f
					}
				}
			}

			// queue shuffled files
			for _, f := range shuffled {
				queue <- f
				if *verbose {
					fmt.Printf("Next: %v\n", f)
				}
			}
		}
	}()
}

// start() initializes a multiplexer for raw audio streams
// e.g: m := new(mux).start(path)
func (m *mux) start(path string) *mux {
	m.result = make(chan broadcastResult)
	m.clients = make(map[int]chan streamFrame)

	m.nextFile = make(chan string)
	m.nextStream = make(chan io.Reader)
	m.nextFrame = make(chan streamFrame)

	// generate randomized list of files available from path
	genFileList(path, m.nextFile)

	// read file, strip ID3 header
	go func() {
		for {
			filename := <-m.nextFile
			f, err := os.Open(filename)
			if err != nil {
				log.Printf("Skipped \"%v\", err=%v", filename, err)
				continue
			}
			m.nextStream <- stripID3Header(f)
			if *verbose {
				fmt.Printf("Now playing: %v\n", filename)
			}
		}
	}()

	// decode stream to frames
	go func() {
		nullwriter := new(nullWriter)
		var cumwait time.Duration
		for {
			streamReader := <-m.nextStream
			d := mp3.NewDecoder(streamReader)
			var f mp3.Frame
			//			sent := 0 // TODO(fgergo) remove later
			//			lastSent := time.Now().UTC()
			for {
				t0 := time.Now()
				tmp := log.Prefix()
				if !*verbose {
					log.SetOutput(nullwriter) // hack to silence mp3 debug/log output
				} else {
					log.SetPrefix("info: mp3 decode msg: ")
				}
				err := d.Decode(&f)
				log.SetPrefix(tmp)
				if !*verbose {
					log.SetOutput(os.Stderr)
				}
				if err == io.EOF {
					break
				}
				if err != nil {
					if *verbose {
						log.Printf("Skipping frame, d.Decode() err=%v", err)
					}
					continue
				}
				buf, err := ioutil.ReadAll(f.Reader())
				if err != nil {
					if *verbose {
						log.Printf("Skipping frame, ioutil.ReadAll() err=%v", err)
					}
					continue
				}
				m.nextFrame <- buf

				/*
					sent += len(buf)
					if sent >= 1*1024*1024 {
						now := time.Now().UTC()
						dur := now.Sub(lastSent)
						kBps := int64(sent)*1e9/1024/dur.Nanoseconds()
						if *verbose {
							log.Printf("Info: sent %#v bytes in the last %v (%vkB/sec)", sent, dur, int(kBps))
						}
						lastSent = now
						sent = 0
					}
				*/
				towait := f.Duration() - time.Now().Sub(t0)
				cumwait += towait	// towait can be negative -> cumwait
				if cumwait > 4*time.Second {
					time.Sleep(cumwait)
					cumwait = 0
				}
			}
		}
	}()

	// broadcast frame to clients
	go func() {
		for {
			f := <-m.nextFrame
			// notify clients of new audio frame or let them quit
			for _, ch := range m.clients {
				ch <- f
				br := <-m.result // handle quitting clients
				if br.err != nil {
					m.Lock()
					close(m.clients[br.qid])
					delete(m.clients, br.qid)
					m.Unlock()
					if *verbose {
						log.Printf("Connection exited, qid: %v, error %v. Now streaming to %v connections.", br.qid, br.err, len(m.clients))
					}
				}
			}
		}
	}()

	return m
}

type streamHandler struct {
	stream *mux
}

// chrome and firefox play mp3 audio stream directly
func (sh streamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	now := time.Now().UTC()
	frames := make(chan streamFrame)
	qid, br := sh.stream.subscribe(frames)
	if qid < 0 {
		log.Printf("Error: new connection request denied, already serving %v connections. See -h for details.", *maxConnections)
		w.WriteHeader(http.StatusTooManyRequests)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Header().Set("Date", now.Format(http.TimeFormat))
	w.Header().Set("Connection", "Keep-Alive")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Content-Type", "audio/mpeg")
	w.Header().Set("Server", "BoringStreamer/4.0")

	// browsers need ID3 tag to identify frames as media to be played
	// minimal id3 header to designate mp3 stream
	b := []byte{0x49, 0x44, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	_, err := io.Copy(w, bytes.NewReader(b))
	if err == nil {
		// broadcast mp3 stream to w
		broadcastTimeout := 4 * time.Second // timeout for slow clients
		result := make(chan error)
		for {
			buf := <-frames
			go func(r chan error, b []byte) {
				_, err = io.Copy(w, bytes.NewReader(b))
				if err == nil {
					w.(http.Flusher).Flush()
				}
				r <- err
			}(result, buf)
			select {
			case err = <-result:
				if err != nil {
					break
				}
				br <- broadcastResult{qid, nil} // frame streamed, no error, send ack
			case <-time.After(broadcastTimeout): // it's an error if io.Copy() is not finished within broadcastTimeout, ServeHTTP should exit
				err = errors.New(fmt.Sprintf("timeout: %v", broadcastTimeout))
			}
			if err != nil {
				break
			}
		}
	}
	br <- broadcastResult{qid, err} // error, send nack
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [flags] [path]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Browse to listen (e.g. http://localhost:4444/)\n\nflags:\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	if len(flag.Args()) > 1 {
		flag.Usage()
		os.Exit(1)
	}

	path := ""
	switch len(flag.Args()) {
	case 0:
		path = "."
		if *verbose {
			fmt.Printf("Using path %#v, see -h for details.\n", path)
		}
	case 1:
		path = flag.Args()[0]
	}

	if *verbose {
		fmt.Printf("Looking for files available from \"%v\" ...\n", path)
	}

	// check if path is available
	matches, err := filepath.Glob(path)
	if err != nil || len(matches) != 1 {
		fmt.Fprintf(os.Stderr, "Error: \"%v\" unavailable.\n", path)
		os.Exit(1)
	}

	// initialize and start mp3 streamer
	http.Handle("/", streamHandler{new(mux).start(path)})
	if *verbose {
		fmt.Printf("Waiting for connections on %v\n", *addr)
	}

	err = http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>removed sent bytes calculation, not needed.<commit_after>// Boringstreamer looks for mp3 files and broadcasts via http.
//
// $ boringstreamer -addr 4444 -max 42 /
//
// recursively looks for mp3 files starting from / and broadcasts on port 4444 for at most 42 concurrent streamer clients.
//
// Browse to listen (e.g. http://localhost:4444/)
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/tcolgate/mp3"
)

var (
	addr           = flag.String("addr", ":4444", "listen on address (format: :port or host:port)")
	maxConnections = flag.Int("max", 42, "set maximum number of streaming connections")
	recursively    = flag.Bool("r", true, "recursively look for music starting from path")
	verbose        = flag.Bool("v", false, "display verbose messages")
)

// like /dev/null
type nullWriter struct {
}

func (nw nullWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

type streamFrame []byte

// client's event
type broadcastResult struct {
	qid int
	err error
}

// After a start() mux broadcasts audio stream to it's listener clients.
// Clients subscribe() and unsubscribe by writing to result chanel.
type mux struct {
	sync.Mutex

	clients map[int]chan streamFrame // set of listener clients to be notified
	result  chan broadcastResult     // clients share broadcast success-failure here

	nextFile   chan string      // next file to be broadcast
	nextStream chan io.Reader   // next (ID3 stripped) raw audio stream
	nextFrame  chan streamFrame // next audio frame
}

// subscribe(ch) adds ch to the set of channels to be received on by the clients when a new audio frame is available.
// Returns uniq client id (qid) for ch and a broadcast result channel for the client.
// Returns -1, nil if too many clients are already listening.
// clients: qid, br := m.subscribe(ch)
func (m *mux) subscribe(ch chan streamFrame) (int, chan broadcastResult) {
	m.Lock()
	defer m.Unlock()
	// search for available qid
	qid := 0
	_, ok := m.clients[qid]
	for ; ok; _, ok = m.clients[qid] {
		if qid >= *maxConnections-1 {
			return -1, nil
		}
		qid++
	}
	m.clients[qid] = ch
	if *verbose {
		log.Printf("New connection (qid: %v), streaming to %v connections.", qid, len(m.clients))
	}

	return qid, m.result
}

// stripID3Header(r) reads file from r, strips id3v2 headers and returns the rest
// id3v2 tag details: id3.org
func stripID3Header(r io.Reader) io.Reader {
	buf, err := ioutil.ReadAll(r)
	if err != nil {
		log.Printf("Error: skipping file, stripID3Header(), err=%v", err)
		return bytes.NewReader(make([]byte, 0))
	}

	// TODO(fgergo) add ID3 v1 detection
	if string(buf[:3]) != "ID3" {
		return bytes.NewReader(buf) // no ID3 header
	}

	// The ID3v2 tag size is encoded in four bytes
	// where msb (bit 7) is set to zero in every byte,
	// ie. tag size is at most 2^28 (4*8-4=28).
	id3size := int32(buf[6])<<21 | int32(buf[7])<<14 | int32(buf[8])<<7 | int32(buf[9])
	id3size += 10 // calculated tag size is excluding the header => +10

	return bytes.NewReader(buf[id3size:])
}

// genFileList() periodically checks for files available from root and
// sends filenames down chan queue.
func genFileList(root string, queue chan string) {
	rand.Seed(time.Now().Unix()) // minimal randomness

	rescan := make(chan chan string)
	go func() {
		for {
			files := <-rescan
			filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
				if err != nil {
					return err
				}
				if !info.Mode().IsRegular() {
					return nil
				}
				ok := strings.HasSuffix(strings.ToLower(info.Name()), ".mp3") // probably file is mp3
				if !info.IsDir() && !ok {
					return nil
				}
				files <- path // found file

				return nil
			})
			close(files)
			time.Sleep(1 * time.Second) // poll at least with 1Hz
		}
	}()

	// buffer and shuffle
	go func() {
		for {
			files := make(chan string)
			rescan <- files

			shuffled := make([]string, 0) // randomized set of files

			for f := range files {
				select {
				case queue <- f: // start playing as soon as possible
					if *verbose {
						fmt.Printf("Next: %v\n", f)
					}
					continue
				default:
					// shuffle files for random playback
					// (random permutation)
					if len(shuffled) == 0 {
						shuffled = append(shuffled, f)
					} else {
						i := rand.Intn(len(shuffled))
						shuffled = append(shuffled, shuffled[i])
						shuffled[i] = f
					}
				}
			}

			// queue shuffled files
			for _, f := range shuffled {
				queue <- f
				if *verbose {
					fmt.Printf("Next: %v\n", f)
				}
			}
		}
	}()
}

// start() initializes a multiplexer for raw audio streams
// e.g: m := new(mux).start(path)
func (m *mux) start(path string) *mux {
	m.result = make(chan broadcastResult)
	m.clients = make(map[int]chan streamFrame)

	m.nextFile = make(chan string)
	m.nextStream = make(chan io.Reader)
	m.nextFrame = make(chan streamFrame)

	// generate randomized list of files available from path
	genFileList(path, m.nextFile)

	// read file, strip ID3 header
	go func() {
		for {
			filename := <-m.nextFile
			f, err := os.Open(filename)
			if err != nil {
				log.Printf("Skipped \"%v\", err=%v", filename, err)
				continue
			}
			m.nextStream <- stripID3Header(f)
			if *verbose {
				fmt.Printf("Now playing: %v\n", filename)
			}
		}
	}()

	// decode stream to frames
	go func() {
		nullwriter := new(nullWriter)
		var cumwait time.Duration
		for {
			streamReader := <-m.nextStream
			d := mp3.NewDecoder(streamReader)
			var f mp3.Frame
			for {
				t0 := time.Now()
				tmp := log.Prefix()
				if !*verbose {
					log.SetOutput(nullwriter) // hack to silence mp3 debug/log output
				} else {
					log.SetPrefix("info: mp3 decode msg: ")
				}
				err := d.Decode(&f)
				log.SetPrefix(tmp)
				if !*verbose {
					log.SetOutput(os.Stderr)
				}
				if err == io.EOF {
					break
				}
				if err != nil {
					if *verbose {
						log.Printf("Skipping frame, d.Decode() err=%v", err)
					}
					continue
				}
				buf, err := ioutil.ReadAll(f.Reader())
				if err != nil {
					if *verbose {
						log.Printf("Skipping frame, ioutil.ReadAll() err=%v", err)
					}
					continue
				}
				m.nextFrame <- buf

				towait := f.Duration() - time.Now().Sub(t0)
				cumwait += towait // towait can be negative -> cumwait
				if cumwait > 4*time.Second {
					time.Sleep(cumwait)
					cumwait = 0
				}
			}
		}
	}()

	// broadcast frame to clients
	go func() {
		for {
			f := <-m.nextFrame
			// notify clients of new audio frame or let them quit
			for _, ch := range m.clients {
				ch <- f
				br := <-m.result // handle quitting clients
				if br.err != nil {
					m.Lock()
					close(m.clients[br.qid])
					delete(m.clients, br.qid)
					m.Unlock()
					if *verbose {
						log.Printf("Connection exited, qid: %v, error %v. Now streaming to %v connections.", br.qid, br.err, len(m.clients))
					}
				}
			}
		}
	}()

	return m
}

type streamHandler struct {
	stream *mux
}

// chrome and firefox play mp3 audio stream directly
func (sh streamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	now := time.Now().UTC()
	frames := make(chan streamFrame)
	qid, br := sh.stream.subscribe(frames)
	if qid < 0 {
		log.Printf("Error: new connection request denied, already serving %v connections. See -h for details.", *maxConnections)
		w.WriteHeader(http.StatusTooManyRequests)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Header().Set("Date", now.Format(http.TimeFormat))
	w.Header().Set("Connection", "Keep-Alive")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Content-Type", "audio/mpeg")
	w.Header().Set("Server", "BoringStreamer/4.0")

	// browsers need ID3 tag to identify frames as media to be played
	// minimal id3 header to designate mp3 stream
	b := []byte{0x49, 0x44, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	_, err := io.Copy(w, bytes.NewReader(b))
	if err == nil {
		// broadcast mp3 stream to w
		broadcastTimeout := 4 * time.Second // timeout for slow clients
		result := make(chan error)
		for {
			buf := <-frames
			go func(r chan error, b []byte) {
				_, err = io.Copy(w, bytes.NewReader(b))
				if err == nil {
					w.(http.Flusher).Flush()
				}
				r <- err
			}(result, buf)
			select {
			case err = <-result:
				if err != nil {
					break
				}
				br <- broadcastResult{qid, nil} // frame streamed, no error, send ack
			case <-time.After(broadcastTimeout): // it's an error if io.Copy() is not finished within broadcastTimeout, ServeHTTP should exit
				err = errors.New(fmt.Sprintf("timeout: %v", broadcastTimeout))
			}
			if err != nil {
				break
			}
		}
	}
	br <- broadcastResult{qid, err} // error, send nack
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [flags] [path]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Browse to listen (e.g. http://localhost:4444/)\n\nflags:\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	if len(flag.Args()) > 1 {
		flag.Usage()
		os.Exit(1)
	}

	path := "."
	switch len(flag.Args()) {
	case 0:
		if *verbose {
			fmt.Printf("Using path %#v, see -h for details.\n", path)
		}
	case 1:
		path = flag.Args()[0]
	}

	if *verbose {
		fmt.Printf("Looking for files available from \"%v\" ...\n", path)
	}

	// check if path is available
	matches, err := filepath.Glob(path)
	if err != nil || len(matches) != 1 {
		fmt.Fprintf(os.Stderr, "Error: \"%v\" unavailable.\n", path)
		os.Exit(1)
	}

	// initialize and start mp3 streamer
	http.Handle("/", streamHandler{new(mux).start(path)})
	if *verbose {
		fmt.Printf("Waiting for connections on %v\n", *addr)
	}

	err = http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


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

package strings_test

import (
	"fmt"
	"strings"
)

func ExampleFields() {
	fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
	// Output: Fields are: ["foo" "bar" "baz"]
}

func ExampleContains() {
	fmt.Println(strings.Contains("seafood", "foo"))
	fmt.Println(strings.Contains("seafood", "bar"))
	fmt.Println(strings.Contains("seafood", ""))
	fmt.Println(strings.Contains("", ""))
	// Output:
	// true
	// false
	// true
	// true
}

func ExampleContainsAny() {
	fmt.Println(strings.ContainsAny("team", "i"))
	fmt.Println(strings.ContainsAny("failure", "u & i"))
	fmt.Println(strings.ContainsAny("foo", ""))
	fmt.Println(strings.ContainsAny("", ""))
	// Output:
	// false
	// true
	// false
	// false
}

func ExampleCount() {
	fmt.Println(strings.Count("cheese", "e"))
	fmt.Println(strings.Count("five", "")) // before & after each rune

	// Output:
	// 3
	// 5
}

func ExampleEqualFold() {
	fmt.Println(strings.EqualFold("Go", "go"))
	// Output: true
}

func ExampleIndex() {
	fmt.Println(strings.Index("chicken", "ken"))
	fmt.Println(strings.Index("chicken", "dmr"))
	// Output:
	// 4
	// -1
}

func ExampleRune() {
	fmt.Println(strings.IndexRune("chicken", 'k'))
	fmt.Println(strings.IndexRune("chicken", 'd'))
	// Output:
	// 4
	// -1
}

func ExampleLastIndex() {
	fmt.Println(strings.Index("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "rodent"))
	// Output:
	// 0
	// 3
	// -1
}

func ExampleJoin() {
	s := []string{"foo", "bar", "baz"}
	fmt.Println(strings.Join(s, ", "))
	// Output: foo, bar, baz
}

func ExampleRepeat() {
	fmt.Println("ba" + strings.Repeat("na", 2))
	// Output: banana
}

func ExampleReplace() {
	fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
	fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
	// Output:
	// oinky oinky oink
	// moo moo moo
}

func ExampleSplit() {
	fmt.Printf("%q\n", strings.Split("a,b,c", ","))
	fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
	fmt.Printf("%q\n", strings.Split(" xyz ", ""))
	fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
	// Output:
	// ["a" "b" "c"]
	// ["" "man " "plan " "canal panama"]
	// [" " "x" "y" "z" " "]
	// [""]
}

func ExampleSplitN() {
	fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2))
	z := strings.SplitN("a,b,c", ",", 0)
	fmt.Printf("%q (nil = %v)\n", z, z == nil)
	// Output:
	// ["a" "b,c"]
	// [] (nil = true)
}

func ExampleSplitAfter() {
	fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ","))
	// Output: ["a," "b," "c"]
}

func ExampleSplitAfterN() {
	fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
	// Output: ["a," "b,c"]
}

func ExampleTitle() {
	fmt.Println(strings.Title("her royal highness"))
	// Output: Her Royal Highness
}

func ExampleToTitle() {
	fmt.Println(strings.ToTitle("loud noises"))
	fmt.Println(strings.ToTitle("хлеб"))
	// Output:
	// LOUD NOISES
	// ХЛЕБ
}

func ExampleTrim() {
	fmt.Printf("[%q]", strings.Trim(" !!! Achtung !!! ", "! "))
	// Output: ["Achtung"]
}

func ExampleMap() {
	rot13 := func(r rune) rune {
		switch {
		case r >= 'A' && r <= 'Z':
			return 'A' + (r-'A'+13)%26
		case r >= 'a' && r <= 'z':
			return 'a' + (r-'a'+13)%26
		}
		return r
	}
	fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..."))
	// Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
}

func ExampleTrimSpace() {
	fmt.Println(strings.TrimSpace(" \t\n a lone gopher \n\t\r\n"))
	// Output: a lone gopher
}

func ExampleNewReplacer() {
	r := strings.NewReplacer("<", "<", ">", ">")
	fmt.Println(r.Replace("This is HTML!"))
	// Output: This is <b>HTML</b>!
}

func ExampleToUpper() {
	fmt.Println(strings.ToUpper("Gopher"))
	// Output: GOPHER
}

func ExampleToLower() {
	fmt.Println(strings.ToLower("Gopher"))
	// Output: gopher
}
<commit_msg>strings: Rename example to match function name.<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strings_test

import (
	"fmt"
	"strings"
)

func ExampleFields() {
	fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
	// Output: Fields are: ["foo" "bar" "baz"]
}

func ExampleContains() {
	fmt.Println(strings.Contains("seafood", "foo"))
	fmt.Println(strings.Contains("seafood", "bar"))
	fmt.Println(strings.Contains("seafood", ""))
	fmt.Println(strings.Contains("", ""))
	// Output:
	// true
	// false
	// true
	// true
}

func ExampleContainsAny() {
	fmt.Println(strings.ContainsAny("team", "i"))
	fmt.Println(strings.ContainsAny("failure", "u & i"))
	fmt.Println(strings.ContainsAny("foo", ""))
	fmt.Println(strings.ContainsAny("", ""))
	// Output:
	// false
	// true
	// false
	// false
}

func ExampleCount() {
	fmt.Println(strings.Count("cheese", "e"))
	fmt.Println(strings.Count("five", "")) // before & after each rune

	// Output:
	// 3
	// 5
}

func ExampleEqualFold() {
	fmt.Println(strings.EqualFold("Go", "go"))
	// Output: true
}

func ExampleIndex() {
	fmt.Println(strings.Index("chicken", "ken"))
	fmt.Println(strings.Index("chicken", "dmr"))
	// Output:
	// 4
	// -1
}

func ExampleIndexRune() {
	fmt.Println(strings.IndexRune("chicken", 'k'))
	fmt.Println(strings.IndexRune("chicken", 'd'))
	// Output:
	// 4
	// -1
}

func ExampleLastIndex() {
	fmt.Println(strings.Index("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "rodent"))
	// Output:
	// 0
	// 3
	// -1
}

func ExampleJoin() {
	s := []string{"foo", "bar", "baz"}
	fmt.Println(strings.Join(s, ", "))
	// Output: foo, bar, baz
}

func ExampleRepeat() {
	fmt.Println("ba" + strings.Repeat("na", 2))
	// Output: banana
}

func ExampleReplace() {
	fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
	fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
	// Output:
	// oinky oinky oink
	// moo moo moo
}

func ExampleSplit() {
	fmt.Printf("%q\n", strings.Split("a,b,c", ","))
	fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
	fmt.Printf("%q\n", strings.Split(" xyz ", ""))
	fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
	// Output:
	// ["a" "b" "c"]
	// ["" "man " "plan " "canal panama"]
	// [" " "x" "y" "z" " "]
	// [""]
}

func ExampleSplitN() {
	fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2))
	z := strings.SplitN("a,b,c", ",", 0)
	fmt.Printf("%q (nil = %v)\n", z, z == nil)
	// Output:
	// ["a" "b,c"]
	// [] (nil = true)
}

func ExampleSplitAfter() {
	fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ","))
	// Output: ["a," "b," "c"]
}

func ExampleSplitAfterN() {
	fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
	// Output: ["a," "b,c"]
}

func ExampleTitle() {
	fmt.Println(strings.Title("her royal highness"))
	// Output: Her Royal Highness
}

func ExampleToTitle() {
	fmt.Println(strings.ToTitle("loud noises"))
	fmt.Println(strings.ToTitle("хлеб"))
	// Output:
	// LOUD NOISES
	// ХЛЕБ
}

func ExampleTrim() {
	fmt.Printf("[%q]", strings.Trim(" !!! Achtung !!! ", "! "))
	// Output: ["Achtung"]
}

func ExampleMap() {
	rot13 := func(r rune) rune {
		switch {
		case r >= 'A' && r <= 'Z':
			return 'A' + (r-'A'+13)%26
		case r >= 'a' && r <= 'z':
			return 'a' + (r-'a'+13)%26
		}
		return r
	}
	fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..."))
	// Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
}

func ExampleTrimSpace() {
	fmt.Println(strings.TrimSpace(" \t\n a lone gopher \n\t\r\n"))
	// Output: a lone gopher
}

func ExampleNewReplacer() {
	r := strings.NewReplacer("<", "<", ">", ">")
	fmt.Println(r.Replace("This is HTML!"))
	// Output: This is <b>HTML</b>!
}

func ExampleToUpper() {
	fmt.Println(strings.ToUpper("Gopher"))
	// Output: GOPHER
}

func ExampleToLower() {
	fmt.Println(strings.ToLower("Gopher"))
	// Output: gopher
}
<|endoftext|>


<commit_before>package store

import (
	"fmt"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/adamdecaf/cert-manage/pkg/file"
)

var (
	chromeBinaryPaths = []string{
		// TODO(adam): Support other OS's (and probably Chromium)
		`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,
	}
)

// From: https://www.chromium.org/Home/chromium-security/root-ca-policy
func ChromeStore() Store {
	switch runtime.GOOS {
	case "darwin", "windows":
		// we need to wrap the platform store and override GetInfo() for
		// chrome's name/version
		return chromeStore{
			Platform(),
		}
	case "linux":
		return chromeLinux()
	}
	return emptyStore{}
}

type chromeStore struct {
	Store
}

func (s chromeStore) GetInfo() *Info {
	return &Info{
		Name:    "Chrome",
		Version: chromeVersion(),
	}
}

func chromeVersion() string {
	for i := range chromeBinaryPaths {
		path := chromeBinaryPaths[i]
		if file.Exists(path) {
			// returns "Google Chrome 63.0.3239.132"
			out, err := exec.Command(path, "--version").CombinedOutput()
			if err == nil && len(out) > 0 {
				return strings.Replace(string(out), "Google Chrome", "", -1)
			}
		}
	}
	return ""
}

func chromeCertdbLocations() []cert8db {
	uhome := file.HomeDir()
	if uhome == "" {
		if debug {
			fmt.Println("store/chrome: unable to find user's home dir")
		}
		return nil
	}

	return []cert8db{
		cert8db(filepath.Join(uhome, ".pki/nssdb")),
	}
}

func chromeLinux() Store {
	suggestions := chromeCertdbLocations()
	found := locateCert8db(suggestions)
	return NssStore("chrome", chromeVersion(), suggestions, found)
}
<commit_msg>store/chrome: trim version string<commit_after>package store

import (
	"fmt"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/adamdecaf/cert-manage/pkg/file"
)

var (
	chromeBinaryPaths = []string{
		// TODO(adam): Support other OS's (and probably Chromium)
		`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,
	}
)

// From: https://www.chromium.org/Home/chromium-security/root-ca-policy
func ChromeStore() Store {
	switch runtime.GOOS {
	case "darwin", "windows":
		// we need to wrap the platform store and override GetInfo() for
		// chrome's name/version
		return chromeStore{
			Platform(),
		}
	case "linux":
		return chromeLinux()
	}
	return emptyStore{}
}

type chromeStore struct {
	Store
}

func (s chromeStore) GetInfo() *Info {
	return &Info{
		Name:    "Chrome",
		Version: chromeVersion(),
	}
}

func chromeVersion() string {
	for i := range chromeBinaryPaths {
		path := chromeBinaryPaths[i]
		if file.Exists(path) {
			// returns "Google Chrome 63.0.3239.132"
			out, err := exec.Command(path, "--version").CombinedOutput()
			if err == nil && len(out) > 0 {
				r := strings.NewReplacer("Google Chrome", "")
				return strings.TrimSpace(r.Replace(string(out)))
			}
		}
	}
	return ""
}

func chromeCertdbLocations() []cert8db {
	uhome := file.HomeDir()
	if uhome == "" {
		if debug {
			fmt.Println("store/chrome: unable to find user's home dir")
		}
		return nil
	}

	return []cert8db{
		cert8db(filepath.Join(uhome, ".pki/nssdb")),
	}
}

func chromeLinux() Store {
	suggestions := chromeCertdbLocations()
	found := locateCert8db(suggestions)
	return NssStore("chrome", chromeVersion(), suggestions, found)
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// Native Client system calls.

package syscall

const OS = "nacl"

// Auto-generated

//sys	Chmod(path string, mode int) (errno int)
//sys	Clock() (clock int)
//sys	Close(fd int) (errno int)
//sys	Exit(code int)
//sys	Fstat(fd int, stat *Stat_t) (errno int)
//sys	Getdents(fd int, buf []byte) (n int, errno int)
//sys	Getpid() (pid int)
//sys	Gettimeofday(tv *Timeval) (errno int)
//sys	Open(path string, mode int, perm int) (fd int, errno int)
//sys	Read(fd int, p []byte) (n int, errno int)
//sys	read(fd int, buf *byte, nbuf int) (n int, errno int)
//sys	Stat(path string, stat *Stat_t) (errno int)
//sys	Write(fd int, p []byte) (n int, errno int)

//sys	MultimediaInit(subsys int) (errno int)
//sys	MultimediaShutdown() (errno int)

//sys	CondCreate() (cv int, errno int)
//sys	CondWait(cv int, mutex int) (errno int)
//sys	CondSignal(cv int) (errno int)
//sys	CondBroadcast(cv int) (errno int)
//sys	CondTimedWaitAbs(cv int, mutex int, abstime *Timespec) (errno int)
//sys	MutexCreate() (mutex int, errno int)
//sys	MutexLock(mutex int) (errno int)
//sys	MutexUnlock(mutex int) (errno int)
//sys	MutexTryLock(mutex int) (errno int) = SYS_MUTEX_TRYLOCK
//sys	SemCreate() (sema int, errno int)
//sys	SemWait(sema int) (errno int)
//sys	SemPost(sema int) (errno int)
//sys	VideoInit(dx int, dy int) (errno int)
//sys	VideoUpdate(data *uint32) (errno int)
//sys	VideoPollEvent(ev *byte) (errno int)
//sys	VideoShutdown() (errno int)
//sys	AudioInit(fmt int, nreq int, data *int) (errno int)
//sys	AudioShutdown() (errno int)
//sys	AudioStream(data *uint16, size *uintptr) (errno int)

// Hand-written

func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) {
	// Offset passed to system call is 32 bits.  Failure of vision by NaCl.
	if int64(int32(offset)) != offset {
		return 0, ERANGE
	}
	o, _, e := Syscall(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(whence))
	return int64(o), int(e)
}

// Sleep by waiting on a condition variable that will never be signaled.
// TODO(rsc): Replace when NaCl adds a proper sleep system call.
var tcv, tmu int

func init() {
	tmu, _ = MutexCreate()
	tcv, _ = CondCreate()
}

func Sleep(ns int64) (errno int) {
	ts := NsecToTimespec(ns)
	var tv Timeval
	if errno = Gettimeofday(&tv); errno != 0 {
		return
	}
	ts.Sec += tv.Sec
	ts.Nsec += tv.Usec * 1000
	switch {
	case ts.Nsec >= 1e9:
		ts.Nsec -= 1e9
		ts.Sec++
	case ts.Nsec <= -1e9:
		ts.Nsec += 1e9
		ts.Sec--
	}
	if errno = MutexLock(tmu); errno != 0 {
		return
	}
	errno = CondTimedWaitAbs(tcv, tmu, &ts)
	if e := MutexUnlock(tmu); e != 0 && errno == 0 {
		errno = e
	}
	return
}

// Implemented in NaCl but not here; maybe later:
//	SYS_IOCTL
//	SYS_IMC_*
//	SYS_MMAP ???
//	SYS_SRPC_*
//	SYS_SYSCONF

// Implemented in NaCl but not here; used by runtime instead:
//	SYS_SYSBRK
//	SYS_MMAP
//	SYS_MUNMAP
//	SYS_THREAD_*
//	SYS_TLS_*
//	SYS_SCHED_YIELD

// #define'd in NaCl but not picked up by mkerrors_nacl.sh.

const EWOULDBLOCK = EAGAIN

// Not implemented in NaCl but needed to compile other packages.

const (
	SIGTRAP = 5
)

func Pipe(p []int) (errno int) { return ENACL }

func fcntl(fd, cmd, arg int) (val int, errno int) {
	return 0, ENACL
}

func Pread(fd int, p []byte, offset int64) (n int, errno int) {
	return 0, ENACL
}

func Pwrite(fd int, p []byte, offset int64) (n int, errno int) {
	return 0, ENACL
}

func Mkdir(path string, mode int) (errno int) { return ENACL }

func Lstat(path string, stat *Stat_t) (errno int) {
	return Stat(path, stat)
}

func Chdir(path string) (errno int) { return ENACL }

func Fchdir(fd int) (errno int) { return ENACL }

func Unlink(path string) (errno int) { return ENACL }

func Rmdir(path string) (errno int) { return ENACL }

func Link(oldpath, newpath string) (errno int) {
	return ENACL
}

func Symlink(path, link string) (errno int) { return ENACL }

func Readlink(path string, buf []byte) (n int, errno int) {
	return 0, ENACL
}

func Rename(oldpath, newpath string) (errno int) {
	return ENACL
}

func Fchmod(fd int, mode int) (errno int) { return ENACL }

func Chown(path string, uid int, gid int) (errno int) {
	return ENACL
}

func Lchown(path string, uid int, gid int) (errno int) {
	return ENACL
}

func Fchown(fd int, uid int, gid int) (errno int) {
	return ENACL
}

func Utimes(path string, tv []Timeval) (errno int) {
	return ENACL
}

func Futimes(fd int, tv []Timeval) (errno int) {
	return ENACL
}

func Truncate(name string, size int64) (errno int) {
	return ENACL
}

func Ftruncate(fd int, length int64) (errno int) {
	return ENACL
}

// NaCL doesn't actually implement Getwd, but it also
// don't implement Chdir, so the fallback algorithm
// fails worse than calling Getwd does.

const ImplementsGetwd = true

func Getwd() (wd string, errno int) { return "", ENACL }

func Getuid() (uid int) { return -1 }

func Geteuid() (euid int) { return -1 }

func Getgid() (gid int) { return -1 }

func Getegid() (egid int) { return -1 }

func Getppid() (ppid int) { return -1 }

func Getgroups() (gids []int, errno int) { return nil, ENACL }

type Sockaddr interface {
	sockaddr()
}

type SockaddrInet4 struct {
	Port int
	Addr [4]byte
}

func (*SockaddrInet4) sockaddr() {}

type SockaddrInet6 struct {
	Port int
	Addr [16]byte
}

func (*SockaddrInet6) sockaddr() {}

type SockaddrUnix struct {
	Name string
}

func (*SockaddrUnix) sockaddr() {}

const (
	AF_INET = 1 + iota
	AF_INET6
	AF_UNIX
	IPPROTO_TCP
	SOCK_DGRAM
	SOCK_STREAM
	SOL_SOCKET
	SOMAXCONN
	SO_DONTROUTE
	SO_KEEPALIVE
	SO_LINGER
	SO_RCVBUF
	SO_REUSEADDR
	SO_SNDBUF
	TCP_NODELAY
	WNOHANG
	WSTOPPED
	PTRACE_TRACEME
	SO_BROADCAST = 0
	SHUT_RDWR    = 0
)

func Accept(fd int) (nfd int, sa Sockaddr, errno int) {
	return 0, nil, ENACL
}

func Getsockname(fd int) (sa Sockaddr, errno int) {
	return nil, ENACL
}

func Getpeername(fd int) (sa Sockaddr, errno int) {
	return nil, ENACL
}

func Bind(fd int, sa Sockaddr) (errno int) { return ENACL }

func Connect(fd int, sa Sockaddr) (errno int) { return ENACL }

func Socket(domain, typ, proto int) (fd, errno int) {
	return 0, ENACL
}

func SetsockoptInt(fd, level, opt int, value int) (errno int) {
	return ENACL
}

func Shutdown(fd, how int) (errno int) { return ENACL }

func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, errno int) {
	return 0, nil, ENACL
}

func Sendto(fd int, p []byte, flags int, to Sockaddr) (errno int) {
	return ENACL
}

func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) {
	return ENACL
}

type Linger struct {
	Onoff  int32
	Linger int32
}

func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) {
	return ENACL
}

func Listen(s int, n int) (errno int) { return ENACL }

type Rusage struct {
	Utime    Timeval
	Stime    Timeval
	Maxrss   int32
	Ixrss    int32
	Idrss    int32
	Isrss    int32
	Minflt   int32
	Majflt   int32
	Nswap    int32
	Inblock  int32
	Oublock  int32
	Msgsnd   int32
	Msgrcv   int32
	Nsignals int32
	Nvcsw    int32
	Nivcsw   int32
}

func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) {
	return 0, ENACL
}

type WaitStatus uint32

func (WaitStatus) Exited() bool { return false }

func (WaitStatus) ExitStatus() int { return -1 }

func (WaitStatus) Signal() int { return -1 }

func (WaitStatus) CoreDump() bool { return false }

func (WaitStatus) Stopped() bool { return false }

func (WaitStatus) Continued() bool { return false }

func (WaitStatus) StopSignal() int { return -1 }

func (WaitStatus) Signaled() bool { return false }

func (WaitStatus) TrapCause() int { return -1 }
<commit_msg>fix build - nacl stubs<commit_after>// Copyright 2009 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.

// Native Client system calls.

package syscall

const OS = "nacl"

// Auto-generated

//sys	Chmod(path string, mode int) (errno int)
//sys	Clock() (clock int)
//sys	Close(fd int) (errno int)
//sys	Exit(code int)
//sys	Fstat(fd int, stat *Stat_t) (errno int)
//sys	Getdents(fd int, buf []byte) (n int, errno int)
//sys	Getpid() (pid int)
//sys	Gettimeofday(tv *Timeval) (errno int)
//sys	Open(path string, mode int, perm int) (fd int, errno int)
//sys	Read(fd int, p []byte) (n int, errno int)
//sys	read(fd int, buf *byte, nbuf int) (n int, errno int)
//sys	Stat(path string, stat *Stat_t) (errno int)
//sys	Write(fd int, p []byte) (n int, errno int)

//sys	MultimediaInit(subsys int) (errno int)
//sys	MultimediaShutdown() (errno int)

//sys	CondCreate() (cv int, errno int)
//sys	CondWait(cv int, mutex int) (errno int)
//sys	CondSignal(cv int) (errno int)
//sys	CondBroadcast(cv int) (errno int)
//sys	CondTimedWaitAbs(cv int, mutex int, abstime *Timespec) (errno int)
//sys	MutexCreate() (mutex int, errno int)
//sys	MutexLock(mutex int) (errno int)
//sys	MutexUnlock(mutex int) (errno int)
//sys	MutexTryLock(mutex int) (errno int) = SYS_MUTEX_TRYLOCK
//sys	SemCreate() (sema int, errno int)
//sys	SemWait(sema int) (errno int)
//sys	SemPost(sema int) (errno int)
//sys	VideoInit(dx int, dy int) (errno int)
//sys	VideoUpdate(data *uint32) (errno int)
//sys	VideoPollEvent(ev *byte) (errno int)
//sys	VideoShutdown() (errno int)
//sys	AudioInit(fmt int, nreq int, data *int) (errno int)
//sys	AudioShutdown() (errno int)
//sys	AudioStream(data *uint16, size *uintptr) (errno int)

// Hand-written

func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) {
	// Offset passed to system call is 32 bits.  Failure of vision by NaCl.
	if int64(int32(offset)) != offset {
		return 0, ERANGE
	}
	o, _, e := Syscall(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(whence))
	return int64(o), int(e)
}

// Sleep by waiting on a condition variable that will never be signaled.
// TODO(rsc): Replace when NaCl adds a proper sleep system call.
var tcv, tmu int

func init() {
	tmu, _ = MutexCreate()
	tcv, _ = CondCreate()
}

func Sleep(ns int64) (errno int) {
	ts := NsecToTimespec(ns)
	var tv Timeval
	if errno = Gettimeofday(&tv); errno != 0 {
		return
	}
	ts.Sec += tv.Sec
	ts.Nsec += tv.Usec * 1000
	switch {
	case ts.Nsec >= 1e9:
		ts.Nsec -= 1e9
		ts.Sec++
	case ts.Nsec <= -1e9:
		ts.Nsec += 1e9
		ts.Sec--
	}
	if errno = MutexLock(tmu); errno != 0 {
		return
	}
	errno = CondTimedWaitAbs(tcv, tmu, &ts)
	if e := MutexUnlock(tmu); e != 0 && errno == 0 {
		errno = e
	}
	return
}

// Implemented in NaCl but not here; maybe later:
//	SYS_IOCTL
//	SYS_IMC_*
//	SYS_MMAP ???
//	SYS_SRPC_*
//	SYS_SYSCONF

// Implemented in NaCl but not here; used by runtime instead:
//	SYS_SYSBRK
//	SYS_MMAP
//	SYS_MUNMAP
//	SYS_THREAD_*
//	SYS_TLS_*
//	SYS_SCHED_YIELD

// #define'd in NaCl but not picked up by mkerrors_nacl.sh.

const EWOULDBLOCK = EAGAIN

// Not implemented in NaCl but needed to compile other packages.

const (
	SIGTRAP = 5
)

func Pipe(p []int) (errno int) { return ENACL }

func fcntl(fd, cmd, arg int) (val int, errno int) {
	return 0, ENACL
}

func Pread(fd int, p []byte, offset int64) (n int, errno int) {
	return 0, ENACL
}

func Pwrite(fd int, p []byte, offset int64) (n int, errno int) {
	return 0, ENACL
}

func Mkdir(path string, mode int) (errno int) { return ENACL }

func Lstat(path string, stat *Stat_t) (errno int) {
	return Stat(path, stat)
}

func Chdir(path string) (errno int) { return ENACL }

func Fchdir(fd int) (errno int) { return ENACL }

func Unlink(path string) (errno int) { return ENACL }

func Rmdir(path string) (errno int) { return ENACL }

func Link(oldpath, newpath string) (errno int) {
	return ENACL
}

func Symlink(path, link string) (errno int) { return ENACL }

func Readlink(path string, buf []byte) (n int, errno int) {
	return 0, ENACL
}

func Rename(oldpath, newpath string) (errno int) {
	return ENACL
}

func Fchmod(fd int, mode int) (errno int) { return ENACL }

func Chown(path string, uid int, gid int) (errno int) {
	return ENACL
}

func Lchown(path string, uid int, gid int) (errno int) {
	return ENACL
}

func Fchown(fd int, uid int, gid int) (errno int) {
	return ENACL
}

func Utimes(path string, tv []Timeval) (errno int) {
	return ENACL
}

func Futimes(fd int, tv []Timeval) (errno int) {
	return ENACL
}

func Truncate(name string, size int64) (errno int) {
	return ENACL
}

func Ftruncate(fd int, length int64) (errno int) {
	return ENACL
}

// NaCL doesn't actually implement Getwd, but it also
// don't implement Chdir, so the fallback algorithm
// fails worse than calling Getwd does.

const ImplementsGetwd = true

func Getwd() (wd string, errno int) { return "", ENACL }

func Getuid() (uid int) { return -1 }

func Geteuid() (euid int) { return -1 }

func Getgid() (gid int) { return -1 }

func Getegid() (egid int) { return -1 }

func Getppid() (ppid int) { return -1 }

func Getgroups() (gids []int, errno int) { return nil, ENACL }

type Sockaddr interface {
	sockaddr()
}

type SockaddrInet4 struct {
	Port int
	Addr [4]byte
}

func (*SockaddrInet4) sockaddr() {}

type SockaddrInet6 struct {
	Port int
	Addr [16]byte
}

func (*SockaddrInet6) sockaddr() {}

type SockaddrUnix struct {
	Name string
}

func (*SockaddrUnix) sockaddr() {}

const (
	AF_INET = 1 + iota
	AF_INET6
	AF_UNIX
	IPPROTO_TCP
	SOCK_DGRAM
	SOCK_STREAM
	SOCK_RAW
	SOL_SOCKET
	SOMAXCONN
	SO_DONTROUTE
	SO_KEEPALIVE
	SO_LINGER
	SO_RCVBUF
	SO_REUSEADDR
	SO_SNDBUF
	TCP_NODELAY
	WNOHANG
	WSTOPPED
	PTRACE_TRACEME
	SO_BROADCAST = 0
	SHUT_RDWR    = 0
)

func Accept(fd int) (nfd int, sa Sockaddr, errno int) {
	return 0, nil, ENACL
}

func Getsockname(fd int) (sa Sockaddr, errno int) {
	return nil, ENACL
}

func Getpeername(fd int) (sa Sockaddr, errno int) {
	return nil, ENACL
}

func Bind(fd int, sa Sockaddr) (errno int) { return ENACL }

func BindToDevice(fd int, device string) (errno int) { return ENACL }

func Connect(fd int, sa Sockaddr) (errno int) { return ENACL }

func Socket(domain, typ, proto int) (fd, errno int) {
	return 0, ENACL
}

func SetsockoptInt(fd, level, opt int, value int) (errno int) {
	return ENACL
}

func Shutdown(fd, how int) (errno int) { return ENACL }

func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, errno int) {
	return 0, nil, ENACL
}

func Sendto(fd int, p []byte, flags int, to Sockaddr) (errno int) {
	return ENACL
}

func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) {
	return ENACL
}

type Linger struct {
	Onoff  int32
	Linger int32
}

func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) {
	return ENACL
}

func Listen(s int, n int) (errno int) { return ENACL }

type Rusage struct {
	Utime    Timeval
	Stime    Timeval
	Maxrss   int32
	Ixrss    int32
	Idrss    int32
	Isrss    int32
	Minflt   int32
	Majflt   int32
	Nswap    int32
	Inblock  int32
	Oublock  int32
	Msgsnd   int32
	Msgrcv   int32
	Nsignals int32
	Nvcsw    int32
	Nivcsw   int32
}

func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) {
	return 0, ENACL
}

type WaitStatus uint32

func (WaitStatus) Exited() bool { return false }

func (WaitStatus) ExitStatus() int { return -1 }

func (WaitStatus) Signal() int { return -1 }

func (WaitStatus) CoreDump() bool { return false }

func (WaitStatus) Stopped() bool { return false }

func (WaitStatus) Continued() bool { return false }

func (WaitStatus) StopSignal() int { return -1 }

func (WaitStatus) Signaled() bool { return false }

func (WaitStatus) TrapCause() int { return -1 }
<|endoftext|>


<commit_before>/*
 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 * 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.
 *
 * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @license 	Apache-2.0
 */

package oauth2

// Introspection contains an access token's session data as specified by IETF RFC 7662, see:
// https://tools.ietf.org/html/rfc7662
// swagger:model oAuth2TokenIntrospection
type Introspection struct {
	// Active is a boolean indicator of whether or not the presented token
	// is currently active.  The specifics of a token's "active" state
	// will vary depending on the implementation of the authorization
	// server and the information it keeps about its tokens, but a "true"
	// value return for the "active" property will generally indicate
	// that a given token has been issued by this authorization server,
	// has not been revoked by the resource owner, and is within its
	// given time window of validity (e.g., after its issuance time and
	// before its expiration time).
	//
	// required: true
	Active bool `json:"active"`

	// Scope is a JSON string containing a space-separated list of
	// scopes associated with this token.
	Scope string `json:"scope,omitempty"`

	// ID is aclient identifier for the OAuth 2.0 client that
	// requested this token.
	ClientID string `json:"client_id,omitempty"`

	// Subject of the token, as defined in JWT [RFC7519].
	// Usually a machine-readable identifier of the resource owner who
	// authorized this token.
	Subject string `json:"sub,omitempty"`

	// ObfuscatedSubject is set when the subject identifier algorithm was set to "pairwise" during authorization.
	// It is the `sub` value of the ID Token that was issued.
	ObfuscatedSubject string `json:"obfuscated_subject,omitempty"`

	// Expires at is an integer timestamp, measured in the number of seconds
	// since January 1 1970 UTC, indicating when this token will expire.
	ExpiresAt int64 `json:"exp,omitempty"`

	// Issued at is an integer timestamp, measured in the number of seconds
	// since January 1 1970 UTC, indicating when this token was
	// originally issued.
	IssuedAt int64 `json:"iat,omitempty"`

	// NotBefore is an integer timestamp, measured in the number of seconds
	// since January 1 1970 UTC, indicating when this token is not to be
	// used before.
	NotBefore int64 `json:"nbf,omitempty"`

	// Username is a human-readable identifier for the resource owner who
	// authorized this token.
	Username string `json:"username,omitempty"`

	// Audience contains a list of the token's intended audiences.
	Audience []string `json:"aud,omitempty"`

	// IssuerURL is a string representing the issuer of this token
	Issuer string `json:"iss,omitempty"`

	// TokenType is the introspected token's type, for example `access_token` or `refresh_token`.
	TokenType string `json:"token_type,omitempty"`

	// Extra is arbitrary data set by the session.
	Extra map[string]interface{} `json:"ext,omitempty"`
}
<commit_msg>refactor: rename `token_type` to `token_use` in introspection<commit_after>/*
 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 * 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.
 *
 * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @license 	Apache-2.0
 */

package oauth2

// Introspection contains an access token's session data as specified by IETF RFC 7662, see:
// https://tools.ietf.org/html/rfc7662
// swagger:model oAuth2TokenIntrospection
type Introspection struct {
	// Active is a boolean indicator of whether or not the presented token
	// is currently active.  The specifics of a token's "active" state
	// will vary depending on the implementation of the authorization
	// server and the information it keeps about its tokens, but a "true"
	// value return for the "active" property will generally indicate
	// that a given token has been issued by this authorization server,
	// has not been revoked by the resource owner, and is within its
	// given time window of validity (e.g., after its issuance time and
	// before its expiration time).
	//
	// required: true
	Active bool `json:"active"`

	// Scope is a JSON string containing a space-separated list of
	// scopes associated with this token.
	Scope string `json:"scope,omitempty"`

	// ID is aclient identifier for the OAuth 2.0 client that
	// requested this token.
	ClientID string `json:"client_id,omitempty"`

	// Subject of the token, as defined in JWT [RFC7519].
	// Usually a machine-readable identifier of the resource owner who
	// authorized this token.
	Subject string `json:"sub,omitempty"`

	// ObfuscatedSubject is set when the subject identifier algorithm was set to "pairwise" during authorization.
	// It is the `sub` value of the ID Token that was issued.
	ObfuscatedSubject string `json:"obfuscated_subject,omitempty"`

	// Expires at is an integer timestamp, measured in the number of seconds
	// since January 1 1970 UTC, indicating when this token will expire.
	ExpiresAt int64 `json:"exp,omitempty"`

	// Issued at is an integer timestamp, measured in the number of seconds
	// since January 1 1970 UTC, indicating when this token was
	// originally issued.
	IssuedAt int64 `json:"iat,omitempty"`

	// NotBefore is an integer timestamp, measured in the number of seconds
	// since January 1 1970 UTC, indicating when this token is not to be
	// used before.
	NotBefore int64 `json:"nbf,omitempty"`

	// Username is a human-readable identifier for the resource owner who
	// authorized this token.
	Username string `json:"username,omitempty"`

	// Audience contains a list of the token's intended audiences.
	Audience []string `json:"aud,omitempty"`

	// IssuerURL is a string representing the issuer of this token
	Issuer string `json:"iss,omitempty"`

	// TokenType is the introspected token's type, typically `Bearer`.
	TokenType string `json:"token_type,omitempty"`

	// TokenUse is the introspected token's use, for example `access_token` or `refresh_token`.
	TokenUse string `json:"token_use,omitempty"`

	// Extra is arbitrary data set by the session.
	Extra map[string]interface{} `json:"ext,omitempty"`
}
<|endoftext|>