<commit_before>package routing

import (
	"fmt"
	"sync"

	"github.com/go-errors/errors"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/htlcswitch"
	"github.com/lightningnetwork/lnd/lntypes"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/lightningnetwork/lnd/routing/route"
)

type mockPaymentAttemptDispatcher struct {
	onPayment func(firstHop lnwire.ShortChannelID) ([32]byte, error)
	results   map[uint64]*htlcswitch.PaymentResult

	sync.Mutex
}

var _ PaymentAttemptDispatcher = (*mockPaymentAttemptDispatcher)(nil)

func (m *mockPaymentAttemptDispatcher) SendHTLC(firstHop lnwire.ShortChannelID,
	pid uint64,
	_ *lnwire.UpdateAddHTLC) error {

	if m.onPayment == nil {
		return nil
	}

	var result *htlcswitch.PaymentResult
	preimage, err := m.onPayment(firstHop)
	if err != nil {
		rtErr, ok := err.(htlcswitch.ClearTextError)
		if !ok {
			return err
		}
		result = &htlcswitch.PaymentResult{
			Error: rtErr,
		}
	} else {
		result = &htlcswitch.PaymentResult{Preimage: preimage}
	}

	m.Lock()
	if m.results == nil {
		m.results = make(map[uint64]*htlcswitch.PaymentResult)
	}

	m.results[pid] = result
	m.Unlock()

	return nil
}

func (m *mockPaymentAttemptDispatcher) GetPaymentResult(paymentID uint64,
	_ lntypes.Hash, _ htlcswitch.ErrorDecrypter) (
	<-chan *htlcswitch.PaymentResult, error) {

	c := make(chan *htlcswitch.PaymentResult, 1)

	m.Lock()
	res, ok := m.results[paymentID]
	m.Unlock()

	if !ok {
		return nil, htlcswitch.ErrPaymentIDNotFound
	}
	c <- res

	return c, nil

}
func (m *mockPaymentAttemptDispatcher) CleanStore(map[uint64]struct{}) error {
	return nil
}

func (m *mockPaymentAttemptDispatcher) setPaymentResult(
	f func(firstHop lnwire.ShortChannelID) ([32]byte, error)) {

	m.onPayment = f
}

type mockPaymentSessionSource struct {
	routes []*route.Route
}

var _ PaymentSessionSource = (*mockPaymentSessionSource)(nil)

func (m *mockPaymentSessionSource) NewPaymentSession(
	_ *LightningPayment) (PaymentSession, error) {

	return &mockPaymentSession{m.routes}, nil
}

func (m *mockPaymentSessionSource) NewPaymentSessionForRoute(
	preBuiltRoute *route.Route) PaymentSession {
	return nil
}

func (m *mockPaymentSessionSource) NewPaymentSessionEmpty() PaymentSession {
	return &mockPaymentSession{}
}

type mockMissionControl struct {
	MissionControl
}

var _ MissionController = (*mockMissionControl)(nil)

func (m *mockMissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
	failureSourceIdx *int, failure lnwire.FailureMessage) (
	*channeldb.FailureReason, error) {

	// Report a permanent failure if this is an error caused
	// by incorrect details.
	if failure.Code() == lnwire.CodeIncorrectOrUnknownPaymentDetails {
		reason := channeldb.FailureReasonPaymentDetails
		return &reason, nil
	}

	return nil, nil
}

func (m *mockMissionControl) ReportPaymentSuccess(paymentID uint64,
	rt *route.Route) error {

	return nil
}

func (m *mockMissionControl) GetProbability(fromNode, toNode route.Vertex,
	amt lnwire.MilliSatoshi) float64 {

	return 0
}

type mockPaymentSession struct {
	routes []*route.Route
}

var _ PaymentSession = (*mockPaymentSession)(nil)

func (m *mockPaymentSession) RequestRoute(_, _ lnwire.MilliSatoshi,
	_, height uint32) (*route.Route, error) {

	if len(m.routes) == 0 {
		return nil, errNoPathFound
	}

	r := m.routes[0]
	m.routes = m.routes[1:]

	return r, nil
}

type mockPayer struct {
	sendResult       chan error
	paymentResultErr chan error
	paymentResult    chan *htlcswitch.PaymentResult
	quit             chan struct{}
}

var _ PaymentAttemptDispatcher = (*mockPayer)(nil)

func (m *mockPayer) SendHTLC(_ lnwire.ShortChannelID,
	paymentID uint64,
	_ *lnwire.UpdateAddHTLC) error {

	select {
	case res := <-m.sendResult:
		return res
	case <-m.quit:
		return fmt.Errorf("test quitting")
	}

}

func (m *mockPayer) GetPaymentResult(paymentID uint64, _ lntypes.Hash,
	_ htlcswitch.ErrorDecrypter) (<-chan *htlcswitch.PaymentResult, error) {

	select {
	case res := <-m.paymentResult:
		resChan := make(chan *htlcswitch.PaymentResult, 1)
		resChan <- res
		return resChan, nil
	case err := <-m.paymentResultErr:
		return nil, err
	case <-m.quit:
		return nil, fmt.Errorf("test quitting")
	}
}

func (m *mockPayer) CleanStore(pids map[uint64]struct{}) error {
	return nil
}

type initArgs struct {
	c *channeldb.PaymentCreationInfo
}

type registerAttemptArgs struct {
	a *channeldb.HTLCAttemptInfo
}

type settleAttemptArgs struct {
	preimg lntypes.Preimage
}

type failAttemptArgs struct {
	reason *channeldb.HTLCFailInfo
}

type failPaymentArgs struct {
	reason channeldb.FailureReason
}

type testPayment struct {
	info     channeldb.PaymentCreationInfo
	attempts []channeldb.HTLCAttempt
}

type mockControlTower struct {
	payments   map[lntypes.Hash]*testPayment
	successful map[lntypes.Hash]struct{}
	failed     map[lntypes.Hash]channeldb.FailureReason

	init            chan initArgs
	registerAttempt chan registerAttemptArgs
	settleAttempt   chan settleAttemptArgs
	failAttempt     chan failAttemptArgs
	failPayment     chan failPaymentArgs
	fetchInFlight   chan struct{}

	sync.Mutex
}

var _ ControlTower = (*mockControlTower)(nil)

func makeMockControlTower() *mockControlTower {
	return &mockControlTower{
		payments:   make(map[lntypes.Hash]*testPayment),
		successful: make(map[lntypes.Hash]struct{}),
		failed:     make(map[lntypes.Hash]channeldb.FailureReason),
	}
}

func (m *mockControlTower) InitPayment(phash lntypes.Hash,
	c *channeldb.PaymentCreationInfo) error {

	m.Lock()
	defer m.Unlock()

	if m.init != nil {
		m.init <- initArgs{c}
	}

	// Don't allow re-init a successful payment.
	if _, ok := m.successful[phash]; ok {
		return channeldb.ErrAlreadyPaid
	}

	_, failed := m.failed[phash]
	_, ok := m.payments[phash]

	// If the payment is known, only allow re-init if failed.
	if ok && !failed {
		return channeldb.ErrPaymentInFlight
	}

	delete(m.failed, phash)
	m.payments[phash] = &testPayment{
		info: *c,
	}

	return nil
}

func (m *mockControlTower) RegisterAttempt(phash lntypes.Hash,
	a *channeldb.HTLCAttemptInfo) error {

	m.Lock()
	defer m.Unlock()

	if m.registerAttempt != nil {
		m.registerAttempt <- registerAttemptArgs{a}
	}

	// Cannot register attempts for successful or failed payments.
	if _, ok := m.successful[phash]; ok {
		return channeldb.ErrPaymentAlreadySucceeded
	}

	if _, ok := m.failed[phash]; ok {
		return channeldb.ErrPaymentAlreadyFailed
	}

	p, ok := m.payments[phash]
	if !ok {
		return channeldb.ErrPaymentNotInitiated
	}

	p.attempts = append(p.attempts, channeldb.HTLCAttempt{
		HTLCAttemptInfo: *a,
	})
	m.payments[phash] = p

	return nil
}

func (m *mockControlTower) SettleAttempt(phash lntypes.Hash,
	pid uint64, settleInfo *channeldb.HTLCSettleInfo) (
	*channeldb.HTLCAttempt, error) {

	m.Lock()
	defer m.Unlock()

	if m.settleAttempt != nil {
		m.settleAttempt <- settleAttemptArgs{settleInfo.Preimage}
	}

	// Only allow setting attempts if the payment is known.
	p, ok := m.payments[phash]
	if !ok {
		return nil, channeldb.ErrPaymentNotInitiated
	}

	// Find the attempt with this pid, and set the settle info.
	for i, a := range p.attempts {
		if a.AttemptID != pid {
			continue
		}

		if a.Settle != nil {
			return nil, channeldb.ErrAttemptAlreadySettled
		}
		if a.Failure != nil {
			return nil, channeldb.ErrAttemptAlreadyFailed
		}

		p.attempts[i].Settle = settleInfo

		// Mark the payment successful on first settled attempt.
		m.successful[phash] = struct{}{}
		return &channeldb.HTLCAttempt{
			Settle: settleInfo,
		}, nil
	}

	return nil, fmt.Errorf("pid not found")
}

func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64,
	failInfo *channeldb.HTLCFailInfo) (*channeldb.HTLCAttempt, error) {

	m.Lock()
	defer m.Unlock()

	if m.failAttempt != nil {
		m.failAttempt <- failAttemptArgs{failInfo}
	}

	// Only allow failing attempts if the payment is known.
	p, ok := m.payments[phash]
	if !ok {
		return nil, channeldb.ErrPaymentNotInitiated
	}

	// Find the attempt with this pid, and set the failure info.
	for i, a := range p.attempts {
		if a.AttemptID != pid {
			continue
		}

		if a.Settle != nil {
			return nil, channeldb.ErrAttemptAlreadySettled
		}
		if a.Failure != nil {
			return nil, channeldb.ErrAttemptAlreadyFailed
		}

		p.attempts[i].Failure = failInfo
		return &channeldb.HTLCAttempt{
			Failure: failInfo,
		}, nil
	}

	return nil, fmt.Errorf("pid not found")
}

func (m *mockControlTower) Fail(phash lntypes.Hash,
	reason channeldb.FailureReason) error {

	m.Lock()
	defer m.Unlock()

	if m.failPayment != nil {
		m.failPayment <- failPaymentArgs{reason}
	}

	// Payment must be known.
	if _, ok := m.payments[phash]; !ok {
		return channeldb.ErrPaymentNotInitiated
	}

	m.failed[phash] = reason

	return nil
}

func (m *mockControlTower) FetchPayment(phash lntypes.Hash) (
	*channeldb.MPPayment, error) {

	m.Lock()
	defer m.Unlock()

	p, ok := m.payments[phash]
	if !ok {
		return nil, channeldb.ErrPaymentNotInitiated
	}

	mp := &channeldb.MPPayment{
		Info: &p.info,
	}

	reason, ok := m.failed[phash]
	if ok {
		mp.FailureReason = &reason
	}

	// Return a copy of the current attempts.
	mp.HTLCs = append(mp.HTLCs, p.attempts...)

	return mp, nil
}

func (m *mockControlTower) FetchInFlightPayments() (
	[]*channeldb.InFlightPayment, error) {

	m.Lock()
	defer m.Unlock()

	if m.fetchInFlight != nil {
		m.fetchInFlight <- struct{}{}
	}

	// In flight are all payments not successful or failed.
	var fl []*channeldb.InFlightPayment
	for hash, p := range m.payments {
		if _, ok := m.successful[hash]; ok {
			continue
		}
		if _, ok := m.failed[hash]; ok {
			continue
		}

		ifl := channeldb.InFlightPayment{
			Info: &p.info,
		}

		fl = append(fl, &ifl)
	}

	return fl, nil
}

func (m *mockControlTower) SubscribePayment(paymentHash lntypes.Hash) (
	*ControlTowerSubscriber, error) {

	return nil, errors.New("not implemented")
}
<commit_msg>routing/tests: move lock acquisition after state driving channels<commit_after>package routing

import (
	"fmt"
	"sync"

	"github.com/go-errors/errors"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/htlcswitch"
	"github.com/lightningnetwork/lnd/lntypes"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/lightningnetwork/lnd/routing/route"
)

type mockPaymentAttemptDispatcher struct {
	onPayment func(firstHop lnwire.ShortChannelID) ([32]byte, error)
	results   map[uint64]*htlcswitch.PaymentResult

	sync.Mutex
}

var _ PaymentAttemptDispatcher = (*mockPaymentAttemptDispatcher)(nil)

func (m *mockPaymentAttemptDispatcher) SendHTLC(firstHop lnwire.ShortChannelID,
	pid uint64,
	_ *lnwire.UpdateAddHTLC) error {

	if m.onPayment == nil {
		return nil
	}

	var result *htlcswitch.PaymentResult
	preimage, err := m.onPayment(firstHop)
	if err != nil {
		rtErr, ok := err.(htlcswitch.ClearTextError)
		if !ok {
			return err
		}
		result = &htlcswitch.PaymentResult{
			Error: rtErr,
		}
	} else {
		result = &htlcswitch.PaymentResult{Preimage: preimage}
	}

	m.Lock()
	if m.results == nil {
		m.results = make(map[uint64]*htlcswitch.PaymentResult)
	}

	m.results[pid] = result
	m.Unlock()

	return nil
}

func (m *mockPaymentAttemptDispatcher) GetPaymentResult(paymentID uint64,
	_ lntypes.Hash, _ htlcswitch.ErrorDecrypter) (
	<-chan *htlcswitch.PaymentResult, error) {

	c := make(chan *htlcswitch.PaymentResult, 1)

	m.Lock()
	res, ok := m.results[paymentID]
	m.Unlock()

	if !ok {
		return nil, htlcswitch.ErrPaymentIDNotFound
	}
	c <- res

	return c, nil

}
func (m *mockPaymentAttemptDispatcher) CleanStore(map[uint64]struct{}) error {
	return nil
}

func (m *mockPaymentAttemptDispatcher) setPaymentResult(
	f func(firstHop lnwire.ShortChannelID) ([32]byte, error)) {

	m.onPayment = f
}

type mockPaymentSessionSource struct {
	routes []*route.Route
}

var _ PaymentSessionSource = (*mockPaymentSessionSource)(nil)

func (m *mockPaymentSessionSource) NewPaymentSession(
	_ *LightningPayment) (PaymentSession, error) {

	return &mockPaymentSession{m.routes}, nil
}

func (m *mockPaymentSessionSource) NewPaymentSessionForRoute(
	preBuiltRoute *route.Route) PaymentSession {
	return nil
}

func (m *mockPaymentSessionSource) NewPaymentSessionEmpty() PaymentSession {
	return &mockPaymentSession{}
}

type mockMissionControl struct {
	MissionControl
}

var _ MissionController = (*mockMissionControl)(nil)

func (m *mockMissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
	failureSourceIdx *int, failure lnwire.FailureMessage) (
	*channeldb.FailureReason, error) {

	// Report a permanent failure if this is an error caused
	// by incorrect details.
	if failure.Code() == lnwire.CodeIncorrectOrUnknownPaymentDetails {
		reason := channeldb.FailureReasonPaymentDetails
		return &reason, nil
	}

	return nil, nil
}

func (m *mockMissionControl) ReportPaymentSuccess(paymentID uint64,
	rt *route.Route) error {

	return nil
}

func (m *mockMissionControl) GetProbability(fromNode, toNode route.Vertex,
	amt lnwire.MilliSatoshi) float64 {

	return 0
}

type mockPaymentSession struct {
	routes []*route.Route
}

var _ PaymentSession = (*mockPaymentSession)(nil)

func (m *mockPaymentSession) RequestRoute(_, _ lnwire.MilliSatoshi,
	_, height uint32) (*route.Route, error) {

	if len(m.routes) == 0 {
		return nil, errNoPathFound
	}

	r := m.routes[0]
	m.routes = m.routes[1:]

	return r, nil
}

type mockPayer struct {
	sendResult       chan error
	paymentResultErr chan error
	paymentResult    chan *htlcswitch.PaymentResult
	quit             chan struct{}
}

var _ PaymentAttemptDispatcher = (*mockPayer)(nil)

func (m *mockPayer) SendHTLC(_ lnwire.ShortChannelID,
	paymentID uint64,
	_ *lnwire.UpdateAddHTLC) error {

	select {
	case res := <-m.sendResult:
		return res
	case <-m.quit:
		return fmt.Errorf("test quitting")
	}

}

func (m *mockPayer) GetPaymentResult(paymentID uint64, _ lntypes.Hash,
	_ htlcswitch.ErrorDecrypter) (<-chan *htlcswitch.PaymentResult, error) {

	select {
	case res := <-m.paymentResult:
		resChan := make(chan *htlcswitch.PaymentResult, 1)
		resChan <- res
		return resChan, nil
	case err := <-m.paymentResultErr:
		return nil, err
	case <-m.quit:
		return nil, fmt.Errorf("test quitting")
	}
}

func (m *mockPayer) CleanStore(pids map[uint64]struct{}) error {
	return nil
}

type initArgs struct {
	c *channeldb.PaymentCreationInfo
}

type registerAttemptArgs struct {
	a *channeldb.HTLCAttemptInfo
}

type settleAttemptArgs struct {
	preimg lntypes.Preimage
}

type failAttemptArgs struct {
	reason *channeldb.HTLCFailInfo
}

type failPaymentArgs struct {
	reason channeldb.FailureReason
}

type testPayment struct {
	info     channeldb.PaymentCreationInfo
	attempts []channeldb.HTLCAttempt
}

type mockControlTower struct {
	payments   map[lntypes.Hash]*testPayment
	successful map[lntypes.Hash]struct{}
	failed     map[lntypes.Hash]channeldb.FailureReason

	init            chan initArgs
	registerAttempt chan registerAttemptArgs
	settleAttempt   chan settleAttemptArgs
	failAttempt     chan failAttemptArgs
	failPayment     chan failPaymentArgs
	fetchInFlight   chan struct{}

	sync.Mutex
}

var _ ControlTower = (*mockControlTower)(nil)

func makeMockControlTower() *mockControlTower {
	return &mockControlTower{
		payments:   make(map[lntypes.Hash]*testPayment),
		successful: make(map[lntypes.Hash]struct{}),
		failed:     make(map[lntypes.Hash]channeldb.FailureReason),
	}
}

func (m *mockControlTower) InitPayment(phash lntypes.Hash,
	c *channeldb.PaymentCreationInfo) error {

	if m.init != nil {
		m.init <- initArgs{c}
	}

	m.Lock()
	defer m.Unlock()

	// Don't allow re-init a successful payment.
	if _, ok := m.successful[phash]; ok {
		return channeldb.ErrAlreadyPaid
	}

	_, failed := m.failed[phash]
	_, ok := m.payments[phash]

	// If the payment is known, only allow re-init if failed.
	if ok && !failed {
		return channeldb.ErrPaymentInFlight
	}

	delete(m.failed, phash)
	m.payments[phash] = &testPayment{
		info: *c,
	}

	return nil
}

func (m *mockControlTower) RegisterAttempt(phash lntypes.Hash,
	a *channeldb.HTLCAttemptInfo) error {

	if m.registerAttempt != nil {
		m.registerAttempt <- registerAttemptArgs{a}
	}

	m.Lock()
	defer m.Unlock()

	// Cannot register attempts for successful or failed payments.
	if _, ok := m.successful[phash]; ok {
		return channeldb.ErrPaymentAlreadySucceeded
	}

	if _, ok := m.failed[phash]; ok {
		return channeldb.ErrPaymentAlreadyFailed
	}

	p, ok := m.payments[phash]
	if !ok {
		return channeldb.ErrPaymentNotInitiated
	}

	p.attempts = append(p.attempts, channeldb.HTLCAttempt{
		HTLCAttemptInfo: *a,
	})
	m.payments[phash] = p

	return nil
}

func (m *mockControlTower) SettleAttempt(phash lntypes.Hash,
	pid uint64, settleInfo *channeldb.HTLCSettleInfo) (
	*channeldb.HTLCAttempt, error) {

	if m.settleAttempt != nil {
		m.settleAttempt <- settleAttemptArgs{settleInfo.Preimage}
	}

	m.Lock()
	defer m.Unlock()

	// Only allow setting attempts if the payment is known.
	p, ok := m.payments[phash]
	if !ok {
		return nil, channeldb.ErrPaymentNotInitiated
	}

	// Find the attempt with this pid, and set the settle info.
	for i, a := range p.attempts {
		if a.AttemptID != pid {
			continue
		}

		if a.Settle != nil {
			return nil, channeldb.ErrAttemptAlreadySettled
		}
		if a.Failure != nil {
			return nil, channeldb.ErrAttemptAlreadyFailed
		}

		p.attempts[i].Settle = settleInfo

		// Mark the payment successful on first settled attempt.
		m.successful[phash] = struct{}{}
		return &channeldb.HTLCAttempt{
			Settle: settleInfo,
		}, nil
	}

	return nil, fmt.Errorf("pid not found")
}

func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64,
	failInfo *channeldb.HTLCFailInfo) (*channeldb.HTLCAttempt, error) {

	if m.failAttempt != nil {
		m.failAttempt <- failAttemptArgs{failInfo}
	}

	m.Lock()
	defer m.Unlock()

	// Only allow failing attempts if the payment is known.
	p, ok := m.payments[phash]
	if !ok {
		return nil, channeldb.ErrPaymentNotInitiated
	}

	// Find the attempt with this pid, and set the failure info.
	for i, a := range p.attempts {
		if a.AttemptID != pid {
			continue
		}

		if a.Settle != nil {
			return nil, channeldb.ErrAttemptAlreadySettled
		}
		if a.Failure != nil {
			return nil, channeldb.ErrAttemptAlreadyFailed
		}

		p.attempts[i].Failure = failInfo
		return &channeldb.HTLCAttempt{
			Failure: failInfo,
		}, nil
	}

	return nil, fmt.Errorf("pid not found")
}

func (m *mockControlTower) Fail(phash lntypes.Hash,
	reason channeldb.FailureReason) error {

	m.Lock()
	defer m.Unlock()

	if m.failPayment != nil {
		m.failPayment <- failPaymentArgs{reason}
	}

	// Payment must be known.
	if _, ok := m.payments[phash]; !ok {
		return channeldb.ErrPaymentNotInitiated
	}

	m.failed[phash] = reason

	return nil
}

func (m *mockControlTower) FetchPayment(phash lntypes.Hash) (
	*channeldb.MPPayment, error) {

	m.Lock()
	defer m.Unlock()

	p, ok := m.payments[phash]
	if !ok {
		return nil, channeldb.ErrPaymentNotInitiated
	}

	mp := &channeldb.MPPayment{
		Info: &p.info,
	}

	reason, ok := m.failed[phash]
	if ok {
		mp.FailureReason = &reason
	}

	// Return a copy of the current attempts.
	mp.HTLCs = append(mp.HTLCs, p.attempts...)

	return mp, nil
}

func (m *mockControlTower) FetchInFlightPayments() (
	[]*channeldb.InFlightPayment, error) {

	if m.fetchInFlight != nil {
		m.fetchInFlight <- struct{}{}
	}

	m.Lock()
	defer m.Unlock()

	// In flight are all payments not successful or failed.
	var fl []*channeldb.InFlightPayment
	for hash, p := range m.payments {
		if _, ok := m.successful[hash]; ok {
			continue
		}
		if _, ok := m.failed[hash]; ok {
			continue
		}

		ifl := channeldb.InFlightPayment{
			Info: &p.info,
		}

		fl = append(fl, &ifl)
	}

	return fl, nil
}

func (m *mockControlTower) SubscribePayment(paymentHash lntypes.Hash) (
	*ControlTowerSubscriber, error) {

	return nil, errors.New("not implemented")
}
<|endoftext|>


<commit_before>// SILVER - Service Wrapper
//
// Copyright (c) 2016 PaperCut Software http://www.papercut.com/
// Use of this source code is governed by an MIT or GPL Version 2 license.
// See the project's LICENSE file for more information.
//

package procmngt_test

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/papercutsoftware/silver/lib/procmngt"
)

func Test_CustomLoggerForStdout(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)
	output := &bytes.Buffer{}
	execConf := procmngt.ExecConfig{
		Path:   testExe,
		Args:   []string{"go test"},
		Stdout: output,
	}
	executable := procmngt.NewExecutable(execConf)

	// Act
	exitCode, err := executable.Execute(nil)

	// Assert
	if err != nil {
		t.Fatalf("The command should be executed successfully")
	}
	if exitCode != 0 {
		t.Fatalf("The command should exit with 0")
	}
	if !strings.Contains(output.String(), "go test") {
		t.Fatalf("The command did not use the custom logger for standard out")
	}
}

func Test_CustomLoggerForStderr(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)
	output := &bytes.Buffer{}
	execConf := procmngt.ExecConfig{
		Path:   testExe,
		Args:   []string{"ERROR:go test"},
		Stderr: output,
	}
	executable := procmngt.NewExecutable(execConf)

	// Act
	exitCode, err := executable.Execute(nil)

	// Assert
	if err != nil {
		t.Fatalf("The command should be executed successfully")
	}
	if exitCode != 1 {
		t.Fatalf("The command should exit with 1")
	}
	if !strings.Contains(output.String(), "ERROR:go test") {
		t.Fatalf("The command did not use the custom logger for standard error")
	}
}

func Test_FixedDelayedStartupCommand(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)
	delayed := 1 * time.Second
	execConf := procmngt.ExecConfig{
		Path:         testExe,
		StartupDelay: delayed,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()

	// Act
	executable.Execute(nil)

	// Assert
	elapsed := time.Since(start)
	if elapsed < delayed {
		t.Fatalf("Startup delayed is not enforced")
	}
}

func Test_FixedDelayedStoppedByTerminateBeforeStart(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)
	delayed := 2 * time.Second
	execConf := procmngt.ExecConfig{
		Path:         testExe,
		StartupDelay: delayed,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()

	// Act
	stopped := 1 * time.Second
	stop := make(chan struct{})
	go func() {
		defer close(stop)
		time.Sleep(stopped)
	}()

	actualExitCode, err := executable.Execute(stop)
	const expectExitCode = 255
	if actualExitCode != expectExitCode {
		t.Fatalf("Expected exitCode: %v but got :%v", expectExitCode, actualExitCode)
	}
	if err == nil {
		t.Fatalf("Expected error but got nothing")
	}

	// Assert
	threshold := 500 * time.Millisecond
	elapsed := time.Since(start)
	if elapsed > stopped+threshold {
		t.Fatalf("terminate channel is not enforced")
	}
}

func Test_TimedOutCommand(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorldForever(t)
	defer os.RemoveAll(tmpDir)
	timeout := 1 * time.Second
	execConf := procmngt.ExecConfig{
		Path:        testExe,
		ExecTimeout: timeout,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()

	// Act
	executable.Execute(nil)

	// Assert
	elapsed := time.Since(start)
	if elapsed < timeout {
		t.Fatalf("The command exit before the timeout")
	}
	threshold := 500 * time.Millisecond
	if elapsed > timeout+threshold {
		t.Fatalf("Timeout is not enforced")
	}
}

func Test_StartupDelayedAndTimedOutCommand(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorldForever(t)
	defer os.RemoveAll(tmpDir)
	delayed := 1 * time.Second
	timeout := 1 * time.Second
	execConf := procmngt.ExecConfig{
		Path:         testExe,
		ExecTimeout:  timeout,
		StartupDelay: delayed,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()

	// Act
	executable.Execute(nil)

	// Assert
	elapsed := time.Since(start)
	threshold := 500 * time.Millisecond // the command should take less than threshold to run
	if elapsed >= timeout+delayed+threshold {
		t.Fatalf("The command took longer than expected")
	}
}

func Test_GracefulShutDownCommand(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorldForever(t)
	defer os.RemoveAll(tmpDir)
	execConf := procmngt.ExecConfig{
		Path:             testExe,
		GracefulShutDown: 1 * time.Second,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()
	terminate := make(chan struct{})
	go func() {
		select {
		case <-time.After(2 * time.Second):
			close(terminate)
		}
	}()

	// Act
	executable.Execute(terminate)

	// Assert
	elapsed := time.Since(start)
	threshold := 500 * time.Millisecond // the command should take less than threshold to run
	if elapsed > 3*time.Second+threshold {
		t.Fatalf("The command was not shut down")
	}
}

func Test_CommandWithCustomEnv(t *testing.T) {
	//Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)

	expect := ""
	output := &bytes.Buffer{}
	execConf := procmngt.ExecConfig{
		Path:   testExe,
		Stdout: output,
		Env:    []string{"ENV_TEST_VAR=" + expect},
	}
	executable := procmngt.NewExecutable(execConf)

	//Act
	executable.Execute(nil)

	//Assert
	if !strings.Contains(output.String(), expect) {
		t.Fatalf("Expected to contain '%s' but got '%s'", expect, output.String())
	}
}

func makeHelloWorld(t *testing.T) (tmpDir, testExe string) {
	_, thisFile, _, _ := runtime.Caller(0)
	helloWorldGo := path.Dir(thisFile) + "/testexes/helloworld.go"
	return makeTestExe(t, helloWorldGo)
}

func makeHelloWorldForever(t *testing.T) (tmpDir, testExe string) {
	_, thisFile, _, _ := runtime.Caller(0)
	helloWorldGo := path.Dir(thisFile) + "/testexes/helloforever.go"
	return makeTestExe(t, helloWorldGo)
}

func makeTestExe(t *testing.T, testSrc string) (tmpDir, testExe string) {
	tmpDir, err := ioutil.TempDir("", "TestProcmgmt")
	if err != nil {
		t.Fatal("TempDir failed: ", err)
	}

	// compile it
	exe := filepath.Join(tmpDir, path.Base(testSrc)) + ".exe"
	o, err := exec.Command("go", "build", "-o", exe, testSrc).CombinedOutput()
	if err != nil {
		t.Fatalf("Failed to compile: %v\n%v", err, string(o))
	}
	return tmpDir, exe
}
<commit_msg>Fix build error in a test case<commit_after>// SILVER - Service Wrapper
//
// Copyright (c) 2016 PaperCut Software http://www.papercut.com/
// Use of this source code is governed by an MIT or GPL Version 2 license.
// See the project's LICENSE file for more information.
//

package procmngt_test

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/papercutsoftware/silver/lib/procmngt"
)

func Test_CustomLoggerForStdout(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)
	output := &bytes.Buffer{}
	execConf := procmngt.ExecConfig{
		Path:   testExe,
		Args:   []string{"go test"},
		Stdout: output,
	}
	executable := procmngt.NewExecutable(execConf)

	// Act
	exitCode, err := executable.Execute(nil)

	// Assert
	if err != nil {
		t.Fatalf("The command should be executed successfully")
	}
	if exitCode != 0 {
		t.Fatalf("The command should exit with 0")
	}
	if !strings.Contains(output.String(), "go test") {
		t.Fatalf("The command did not use the custom logger for standard out")
	}
}

func Test_CustomLoggerForStderr(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)
	output := &bytes.Buffer{}
	execConf := procmngt.ExecConfig{
		Path:   testExe,
		Args:   []string{"ERROR:go test"},
		Stderr: output,
	}
	executable := procmngt.NewExecutable(execConf)

	// Act
	exitCode, err := executable.Execute(nil)

	// Assert
	if err != nil {
		t.Fatalf("The command should be executed successfully")
	}
	if exitCode != 1 {
		t.Fatalf("The command should exit with 1")
	}
	if !strings.Contains(output.String(), "ERROR:go test") {
		t.Fatalf("The command did not use the custom logger for standard error")
	}
}

func Test_FixedDelayedStartupCommand(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)
	delayed := 1 * time.Second
	execConf := procmngt.ExecConfig{
		Path:         testExe,
		StartupDelay: delayed,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()

	// Act
	executable.Execute(nil)

	// Assert
	elapsed := time.Since(start)
	if elapsed < delayed {
		t.Fatalf("Startup delayed is not enforced")
	}
}

func Test_FixedDelayedStoppedByTerminateBeforeStart(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)
	delayed := 2 * time.Second
	execConf := procmngt.ExecConfig{
		Path:         testExe,
		StartupDelay: delayed,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()

	// Act
	stopped := 1 * time.Second
	stop := make(chan struct{})
	go func() {
		defer close(stop)
		time.Sleep(stopped)
	}()

	actualExitCode, err := executable.Execute(stop)
	const expectExitCode = 255
	if actualExitCode != expectExitCode {
		t.Fatalf("Expected exitCode: %v but got :%v", expectExitCode, actualExitCode)
	}
	if err != nil {
		t.Fatalf("Expected no error but got: %v", err)
	}

	// Assert
	threshold := 500 * time.Millisecond
	elapsed := time.Since(start)
	if elapsed > stopped+threshold {
		t.Fatalf("terminate channel is not enforced")
	}
}

func Test_TimedOutCommand(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorldForever(t)
	defer os.RemoveAll(tmpDir)
	timeout := 1 * time.Second
	execConf := procmngt.ExecConfig{
		Path:        testExe,
		ExecTimeout: timeout,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()

	// Act
	executable.Execute(nil)

	// Assert
	elapsed := time.Since(start)
	if elapsed < timeout {
		t.Fatalf("The command exit before the timeout")
	}
	threshold := 500 * time.Millisecond
	if elapsed > timeout+threshold {
		t.Fatalf("Timeout is not enforced")
	}
}

func Test_StartupDelayedAndTimedOutCommand(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorldForever(t)
	defer os.RemoveAll(tmpDir)
	delayed := 1 * time.Second
	timeout := 1 * time.Second
	execConf := procmngt.ExecConfig{
		Path:         testExe,
		ExecTimeout:  timeout,
		StartupDelay: delayed,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()

	// Act
	executable.Execute(nil)

	// Assert
	elapsed := time.Since(start)
	threshold := 500 * time.Millisecond // the command should take less than threshold to run
	if elapsed >= timeout+delayed+threshold {
		t.Fatalf("The command took longer than expected")
	}
}

func Test_GracefulShutDownCommand(t *testing.T) {
	// Arrange
	tmpDir, testExe := makeHelloWorldForever(t)
	defer os.RemoveAll(tmpDir)
	execConf := procmngt.ExecConfig{
		Path:             testExe,
		GracefulShutDown: 1 * time.Second,
	}
	executable := procmngt.NewExecutable(execConf)
	start := time.Now()
	terminate := make(chan struct{})
	go func() {
		select {
		case <-time.After(2 * time.Second):
			close(terminate)
		}
	}()

	// Act
	executable.Execute(terminate)

	// Assert
	elapsed := time.Since(start)
	threshold := 500 * time.Millisecond // the command should take less than threshold to run
	if elapsed > 3*time.Second+threshold {
		t.Fatalf("The command was not shut down")
	}
}

func Test_CommandWithCustomEnv(t *testing.T) {
	//Arrange
	tmpDir, testExe := makeHelloWorld(t)
	defer os.RemoveAll(tmpDir)

	expect := ""
	output := &bytes.Buffer{}
	execConf := procmngt.ExecConfig{
		Path:   testExe,
		Stdout: output,
		Env:    []string{"ENV_TEST_VAR=" + expect},
	}
	executable := procmngt.NewExecutable(execConf)

	//Act
	executable.Execute(nil)

	//Assert
	if !strings.Contains(output.String(), expect) {
		t.Fatalf("Expected to contain '%s' but got '%s'", expect, output.String())
	}
}

func makeHelloWorld(t *testing.T) (tmpDir, testExe string) {
	_, thisFile, _, _ := runtime.Caller(0)
	helloWorldGo := path.Dir(thisFile) + "/testexes/helloworld.go"
	return makeTestExe(t, helloWorldGo)
}

func makeHelloWorldForever(t *testing.T) (tmpDir, testExe string) {
	_, thisFile, _, _ := runtime.Caller(0)
	helloWorldGo := path.Dir(thisFile) + "/testexes/helloforever.go"
	return makeTestExe(t, helloWorldGo)
}

func makeTestExe(t *testing.T, testSrc string) (tmpDir, testExe string) {
	tmpDir, err := ioutil.TempDir("", "TestProcmgmt")
	if err != nil {
		t.Fatal("TempDir failed: ", err)
	}

	// compile it
	exe := filepath.Join(tmpDir, path.Base(testSrc)) + ".exe"
	o, err := exec.Command("go", "build", "-o", exe, testSrc).CombinedOutput()
	if err != nil {
		t.Fatalf("Failed to compile: %v\n%v", err, string(o))
	}
	return tmpDir, exe
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	log "github.com/gonet2/libs/nsq-logger"
	"io"
	"sync"
	"time"
)

import (
	"client_handler"
	"misc/packet"
	. "proto"
	"registry"
	. "types"
)

const (
	_port = ":51000"
)

const (
	SERVICE      = "[GAME]"
	RECV_TIMEOUT = 5 * time.Second
)

var (
	ERROR_INCORRECT_FRAME_TYPE = errors.New("incorrect frame type")
	ERROR_SERVICE_NOT_BIND     = errors.New("service not bind")
	ERROR_USER_NOT_REGISTERED  = errors.New("user not registered")
)

type server struct {
	sync.Mutex
}

func (s *server) latch(f func()) {
	s.Lock()
	defer s.Unlock()
	f()
}

// stream receiver
func (s *server) recv(stream GameService_StreamServer) chan *Game_Frame {
	ch := make(chan *Game_Frame, 1)
	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF { // client closed
				close(ch)
				return
			}

			if err != nil {
				log.Critical(err)
				close(ch)
				return
			}
			select {
			case ch <- in:
			case <-time.After(RECV_TIMEOUT):
				log.Warning("recv deliver timeout")
				close(ch)
				return
			}
		}
	}()
	return ch
}

// stream server
func (s *server) Stream(stream GameService_StreamServer) error {
	var sess Session
	ch_agent := s.recv(stream)
	ch_ipc := make(chan *Game_Frame, 1)

	// >> main message loop <<
	for {
		select {
		case frame, ok := <-ch_agent: // frames from agent
			if !ok { // EOF
				return nil
			}
			switch frame.Type {
			case Game_Message:
				// validation
				if sess.Flag&SESS_REGISTERED == 0 {
					log.Critical("user not registered")
					return ERROR_USER_NOT_REGISTERED
				}

				// locate handler by proto number
				reader := packet.Reader(frame.Message)
				c, err := reader.ReadS16()
				if err != nil {
					log.Critical(err)
					return err
				}
				handle := client_handler.Handlers[c]
				if handle == nil {
					log.Criticalf("service not bind: %v", c)
					return ERROR_SERVICE_NOT_BIND

				}

				// serialized processing, no future locks needed.
				// multiple agents can connect simutaneously to games
				var ret []byte
				wrap := func() { ret = handle(&sess, reader) }
				s.latch(wrap)

				// construct frame & return message from logic
				if ret != nil {
					if err := stream.Send(&Game_Frame{Type: Game_Message, Message: ret}); err != nil {
						log.Critical(err)
						return err
					}
				}

				// session control by logic
				if sess.Flag&SESS_KICKED_OUT != 0 { // logic kick out
					if err := stream.Send(&Game_Frame{Type: Game_Kick}); err != nil {
						log.Critical(err)
						return err
					}
					return nil
				}
			case Game_Register:
				// TODO: create session
				sess.Flag |= SESS_REGISTERED
				registry.Register(sess.UserId, ch_ipc)
				log.Trace("user registered")
			case Game_Unregister:
				// TODO: destroy session & return
				registry.Unregister(sess.UserId)
				log.Trace("user unregistered")
				return nil
			case Game_Ping:
				if err := stream.Send(&Game_Frame{Type: Game_Ping, Message: frame.Message}); err != nil {
					log.Critical(err)
					return err
				}
				log.Trace("pinged")
			default:
				log.Errorf("incorrect frame type: %v", frame.Type)
				return ERROR_INCORRECT_FRAME_TYPE
			}
		case frame := <-ch_ipc: // forward async messages from interprocess(goroutines) communication
			if err := stream.Send(frame); err != nil {
				log.Critical(err)
				return err
			}
		}
	}
}
<commit_msg>update<commit_after>package main

import (
	"errors"
	log "github.com/gonet2/libs/nsq-logger"
	"io"
	"sync"
	"time"
)

import (
	"client_handler"
	"misc/packet"
	. "proto"
	"registry"
	. "types"
)

const (
	_port = ":51000"
)

const (
	SERVICE      = "[GAME]"
	RECV_TIMEOUT = 5 * time.Second
)

var (
	ERROR_INCORRECT_FRAME_TYPE = errors.New("incorrect frame type")
	ERROR_SERVICE_NOT_BIND     = errors.New("service not bind")
	ERROR_USER_NOT_REGISTERED  = errors.New("user not registered")
)

type server struct {
	sync.Mutex
}

func (s *server) latch(f func()) {
	s.Lock()
	defer s.Unlock()
	f()
}

// stream receiver
func (s *server) recv(stream GameService_StreamServer) chan *Game_Frame {
	ch := make(chan *Game_Frame, 1)
	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF { // client closed
				close(ch)
				return
			}

			if err != nil {
				log.Critical(err)
				close(ch)
				return
			}
			select {
			case ch <- in:
			case <-time.After(RECV_TIMEOUT):
				log.Warning("recv deliver timeout")
				close(ch)
				return
			}
		}
	}()
	return ch
}

// stream server
func (s *server) Stream(stream GameService_StreamServer) error {
	var sess Session
	ch_agent := s.recv(stream)
	ch_ipc := make(chan *Game_Frame, 1)

	defer func() {
		if sess.Flag&SESS_REGISTERED != 0 {
			// TODO: destroy session & return
			sess.Flag |= SESS_REGISTERED
			registry.Unregister(sess.UserId)
		}
		log.Trace("stream end")
	}()

	// >> main message loop <<
	for {
		select {
		case frame, ok := <-ch_agent: // frames from agent
			if !ok { // EOF
				return nil
			}
			switch frame.Type {
			case Game_Message:
				// validation
				if sess.Flag&SESS_REGISTERED == 0 {
					log.Critical("user not registered")
					return ERROR_USER_NOT_REGISTERED
				}

				// locate handler by proto number
				reader := packet.Reader(frame.Message)
				c, err := reader.ReadS16()
				if err != nil {
					log.Critical(err)
					return err
				}
				handle := client_handler.Handlers[c]
				if handle == nil {
					log.Criticalf("service not bind: %v", c)
					return ERROR_SERVICE_NOT_BIND

				}

				// serialized processing, no future locks needed.
				// multiple agents can connect simutaneously to games
				var ret []byte
				wrap := func() { ret = handle(&sess, reader) }
				s.latch(wrap)

				// construct frame & return message from logic
				if ret != nil {
					if err := stream.Send(&Game_Frame{Type: Game_Message, Message: ret}); err != nil {
						log.Critical(err)
						return err
					}
				}

				// session control by logic
				if sess.Flag&SESS_KICKED_OUT != 0 { // logic kick out
					if err := stream.Send(&Game_Frame{Type: Game_Kick}); err != nil {
						log.Critical(err)
						return err
					}
					return nil
				}
			case Game_Register:
				// TODO: create session
				if sess.Flag&SESS_REGISTERED == 0 {
					sess.Flag |= SESS_REGISTERED
					registry.Register(sess.UserId, ch_ipc)
					log.Trace("user registered")
				} else {
					log.Critical("user already registered")
				}
			case Game_Unregister:
				if sess.Flag&SESS_REGISTERED != 0 {
					// TODO: destroy session & return
					sess.Flag &^= SESS_REGISTERED
					registry.Unregister(sess.UserId)
					log.Trace("user unregistered")
				} else {
					log.Critical("user not registered")
				}
				return nil
			case Game_Ping:
				if err := stream.Send(&Game_Frame{Type: Game_Ping, Message: frame.Message}); err != nil {
					log.Critical(err)
					return err
				}
				log.Trace("pinged")
			default:
				log.Errorf("incorrect frame type: %v", frame.Type)
				return ERROR_INCORRECT_FRAME_TYPE
			}
		case frame := <-ch_ipc: // forward async messages from interprocess(goroutines) communication
			if err := stream.Send(frame); err != nil {
				log.Critical(err)
				return err
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io"
	"sync"
	"time"

	log "github.com/gonet2/libs/nsq-logger"
)

import (
	"client_handler"
	"misc/packet"
	. "proto"
	"registry"
	. "types"
)

const (
	_port = ":51000"
)

const (
	SERVICE      = "[GAME]"
	RECV_TIMEOUT = 5 * time.Second
)

var (
	ERROR_INCORRECT_FRAME_TYPE = errors.New("incorrect frame type")
	ERROR_SERVICE_NOT_BIND     = errors.New("service not bind")
	ERROR_USER_NOT_REGISTERED  = errors.New("user not registered")
)

type server struct {
	sync.Mutex
}

func (s *server) latch(f func()) {
	s.Lock()
	defer s.Unlock()
	f()
}

// stream receiver
func (s *server) recv(stream GameService_StreamServer) chan *Game_Frame {
	ch := make(chan *Game_Frame, 1)
	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF { // client closed
				close(ch)
				return
			}

			if err != nil {
				log.Critical(err)
				close(ch)
				return
			}
			select {
			case ch <- in:
			case <-time.After(RECV_TIMEOUT):
				log.Warning("recv deliver timeout")
				close(ch)
				return
			}
		}
	}()
	return ch
}

// stream server
func (s *server) Stream(stream GameService_StreamServer) error {
	var sess Session
	ch_agent := s.recv(stream)
	ch_ipc := make(chan *Game_Frame, 1)

	defer func() {
		if sess.Flag&SESS_REGISTERED != 0 {
			// TODO: destroy session & return
			sess.Flag &^= SESS_REGISTERED
			registry.Unregister(sess.UserId)
		}
		log.Trace("stream end:", sess.UserId)
	}()

	// >> main message loop <<
	for {
		select {
		case frame, ok := <-ch_agent: // frames from agent
			if !ok { // EOF
				return nil
			}
			switch frame.Type {
			case Game_Message:
				// validation
				if sess.Flag&SESS_REGISTERED == 0 {
					log.Critical("user not registered")
					return ERROR_USER_NOT_REGISTERED
				}

				// locate handler by proto number
				reader := packet.Reader(frame.Message)
				c, err := reader.ReadS16()
				if err != nil {
					log.Critical(err)
					return err
				}
				handle := client_handler.Handlers[c]
				if handle == nil {
					log.Criticalf("service not bind: %v", c)
					return ERROR_SERVICE_NOT_BIND

				}

				// serialized processing, no future locks needed.
				// multiple agents can connect simutaneously to games
				var ret []byte
				wrap := func() { ret = handle(&sess, reader) }
				s.latch(wrap)

				// construct frame & return message from logic
				if ret != nil {
					if err := stream.Send(&Game_Frame{Type: Game_Message, Message: ret}); err != nil {
						log.Critical(err)
						return err
					}
				}

				// session control by logic
				if sess.Flag&SESS_KICKED_OUT != 0 { // logic kick out
					if err := stream.Send(&Game_Frame{Type: Game_Kick}); err != nil {
						log.Critical(err)
						return err
					}
					return nil
				}
			case Game_Register:
				// TODO: create session
				if sess.Flag&SESS_REGISTERED == 0 {
					sess.Flag |= SESS_REGISTERED
					sess.UserId = frame.UserId
					registry.Register(frame.UserId, ch_ipc)
					log.Trace("user registered")
				} else {
					log.Critical("user already registered")
				}
			case Game_Unregister:
				if sess.Flag&SESS_REGISTERED != 0 {
					// TODO: destroy session & return
					sess.Flag &^= SESS_REGISTERED
					registry.Unregister(sess.UserId)
					log.Trace("user unregistered")
				} else {
					log.Critical("user not registered")
				}
				return nil
			case Game_Ping:
				if err := stream.Send(&Game_Frame{Type: Game_Ping, Message: frame.Message}); err != nil {
					log.Critical(err)
					return err
				}
				log.Trace("pinged")
			default:
				log.Errorf("incorrect frame type: %v", frame.Type)
				return ERROR_INCORRECT_FRAME_TYPE
			}
		case frame := <-ch_ipc: // forward async messages from interprocess(goroutines) communication
			if err := stream.Send(frame); err != nil {
				log.Critical(err)
				return err
			}
		}
	}
}
<commit_msg>update<commit_after>package main

import (
	"errors"
	"io"
	"sync"
	"time"

	log "github.com/gonet2/libs/nsq-logger"
)

import (
	"client_handler"
	"misc/packet"
	. "proto"
	"registry"
	. "types"
)

const (
	_port = ":51000"
)

const (
	SERVICE      = "[GAME]"
	RECV_TIMEOUT = 5 * time.Second
)

var (
	ERROR_INCORRECT_FRAME_TYPE = errors.New("incorrect frame type")
	ERROR_SERVICE_NOT_BIND     = errors.New("service not bind")
	ERROR_USER_NOT_REGISTERED  = errors.New("user not registered")
)

type server struct {
	sync.Mutex
}

func (s *server) latch(f func()) {
	s.Lock()
	defer s.Unlock()
	f()
}

// stream receiver
func (s *server) recv(stream GameService_StreamServer) chan *Game_Frame {
	ch := make(chan *Game_Frame, 1)
	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF { // client closed
				close(ch)
				return
			}

			if err != nil {
				log.Critical(err)
				close(ch)
				return
			}
			select {
			case ch <- in:
			case <-time.After(RECV_TIMEOUT):
				log.Warning("recv deliver timeout")
				close(ch)
				return
			}
		}
	}()
	return ch
}

// stream server
func (s *server) Stream(stream GameService_StreamServer) error {
	var sess Session
	ch_agent := s.recv(stream)
	ch_ipc := make(chan *Game_Frame, 1)

	defer func() {
		if sess.Flag&SESS_REGISTERED != 0 {
			// TODO: destroy session & return
			sess.Flag &^= SESS_REGISTERED
			registry.Unregister(sess.UserId)
		}
		log.Trace("stream end:", sess.UserId)
	}()

	// >> main message loop <<
	for {
		select {
		case frame, ok := <-ch_agent: // frames from agent
			if !ok { // EOF
				return nil
			}
			switch frame.Type {
			case Game_Message:
				// validation
				if sess.Flag&SESS_REGISTERED == 0 {
					log.Critical("user not registered")
					return ERROR_USER_NOT_REGISTERED
				}

				// locate handler by proto number
				reader := packet.Reader(frame.Message)
				c, err := reader.ReadS16()
				if err != nil {
					log.Critical(err)
					return err
				}
				handle := client_handler.Handlers[c]
				if handle == nil {
					log.Criticalf("service not bind: %v", c)
					return ERROR_SERVICE_NOT_BIND

				}

				// serialized processing, no future locks needed.
				// multiple agents can connect simutaneously to games
				var ret []byte
				wrap := func() { ret = handle(&sess, reader) }
				s.latch(wrap)

				// construct frame & return message from logic
				if ret != nil {
					if err := stream.Send(&Game_Frame{Type: Game_Message, Message: ret}); err != nil {
						log.Critical(err)
						return err
					}
				}

				// session control by logic
				if sess.Flag&SESS_KICKED_OUT != 0 { // logic kick out
					if err := stream.Send(&Game_Frame{Type: Game_Kick}); err != nil {
						log.Critical(err)
						return err
					}
					return nil
				}
			case Game_Register:
				if sess.Flag&SESS_REGISTERED == 0 {
					// TODO: create session
					sess.Flag |= SESS_REGISTERED
					sess.UserId = frame.UserId
					registry.Register(frame.UserId, ch_ipc)
					log.Trace("user registered")
				} else {
					log.Critical("user already registered")
				}
			case Game_Unregister:
				if sess.Flag&SESS_REGISTERED != 0 {
					// TODO: destroy session & return
					sess.Flag &^= SESS_REGISTERED
					registry.Unregister(sess.UserId)
					log.Trace("user unregistered")
				} else {
					log.Critical("user not registered")
				}
			case Game_Ping:
				if err := stream.Send(&Game_Frame{Type: Game_Ping, Message: frame.Message}); err != nil {
					log.Critical(err)
					return err
				}
				log.Trace("pinged")
			default:
				log.Errorf("incorrect frame type: %v", frame.Type)
				return ERROR_INCORRECT_FRAME_TYPE
			}
		case frame := <-ch_ipc: // forward async messages from interprocess(goroutines) communication
			if err := stream.Send(frame); err != nil {
				log.Critical(err)
				return err
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2020 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

package local

import (
	"context"

	"github.com/gravitational/teleport/api/types"
	"github.com/gravitational/teleport/lib/backend"
	"github.com/gravitational/teleport/lib/services"

	"github.com/gravitational/trace"
	"github.com/sirupsen/logrus"
)

// GetAppSession gets an application web session.
func (s *IdentityService) GetAppSession(ctx context.Context, req types.GetAppSessionRequest) (types.WebSession, error) {
	if err := req.Check(); err != nil {
		return nil, trace.Wrap(err)
	}

	item, err := s.Get(ctx, backend.Key(appsPrefix, sessionsPrefix, req.SessionID))
	if err != nil {
		return nil, trace.Wrap(err)
	}
	session, err := services.UnmarshalWebSession(item.Value)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return session, nil
}

// GetAppSessions gets all application web sessions.
func (s *IdentityService) GetAppSessions(ctx context.Context) ([]types.WebSession, error) {
	startKey := backend.Key(appsPrefix, sessionsPrefix)
	result, err := s.GetRange(ctx, startKey, backend.RangeEnd(startKey), backend.NoLimit)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	out := make([]types.WebSession, len(result.Items))
	for i, item := range result.Items {
		session, err := services.UnmarshalWebSession(item.Value)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		out[i] = session
	}
	return out, nil
}

// UpsertAppSession creates an application web session.
func (s *IdentityService) UpsertAppSession(ctx context.Context, session types.WebSession) error {
	value, err := services.MarshalWebSession(session)
	if err != nil {
		return trace.Wrap(err)
	}
	item := backend.Item{
		Key:     backend.Key(appsPrefix, sessionsPrefix, session.GetName()),
		Value:   value,
		Expires: session.GetExpiryTime(),
	}

	if _, err = s.Put(ctx, item); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// DeleteAppSession removes an application web session.
func (s *IdentityService) DeleteAppSession(ctx context.Context, req types.DeleteAppSessionRequest) error {
	if err := s.Delete(ctx, backend.Key(appsPrefix, sessionsPrefix, req.SessionID)); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// DeleteAllAppSessions removes all application web sessions.
func (s *IdentityService) DeleteAllAppSessions(ctx context.Context) error {
	startKey := backend.Key(appsPrefix, sessionsPrefix)
	if err := s.DeleteRange(ctx, startKey, backend.RangeEnd(startKey)); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// WebSessions returns the web sessions manager.
func (s *IdentityService) WebSessions() types.WebSessionInterface {
	return &webSessions{backend: s.Backend, log: s.log}
}

// Get returns the web session state described with req.
func (r *webSessions) Get(ctx context.Context, req types.GetWebSessionRequest) (types.WebSession, error) {
	if err := req.Check(); err != nil {
		return nil, trace.Wrap(err)
	}
	item, err := r.backend.Get(ctx, webSessionKey(req.SessionID))
	if err != nil {
		return nil, trace.Wrap(err)
	}
	session, err := services.UnmarshalWebSession(item.Value)
	if err != nil && !trace.IsNotFound(err) {
		return nil, trace.Wrap(err)
	}
	if session != nil {
		return session, nil
	}
	// DELETE IN 7.x:
	// Return web sessions from a legacy path under /web/users/<user>/sessions/<id>
	return getLegacyWebSession(ctx, r.backend, req.User, req.SessionID)
}

// List gets all regular web sessions.
func (r *webSessions) List(ctx context.Context) (out []types.WebSession, err error) {
	key := backend.Key(webPrefix, sessionsPrefix)
	result, err := r.backend.GetRange(ctx, key, backend.RangeEnd(key), backend.NoLimit)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	for _, item := range result.Items {
		session, err := services.UnmarshalWebSession(item.Value)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		out = append(out, session)
	}
	// DELETE IN 7.x:
	// Return web sessions from a legacy path under /web/users/<user>/sessions/<id>
	legacySessions, err := r.listLegacySessions(ctx)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return append(out, legacySessions...), nil
}

// Upsert updates the existing or inserts a new web session.
func (r *webSessions) Upsert(ctx context.Context, session types.WebSession) error {
	value, err := services.MarshalWebSession(session)
	if err != nil {
		return trace.Wrap(err)
	}
	sessionMetadata := session.GetMetadata()
	item := backend.Item{
		Key:     webSessionKey(session.GetName()),
		Value:   value,
		Expires: backend.EarliestExpiry(session.GetBearerTokenExpiryTime(), sessionMetadata.Expiry()),
	}
	_, err = r.backend.Put(ctx, item)
	if err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// Delete deletes the web session specified with req from the storage.
func (r *webSessions) Delete(ctx context.Context, req types.DeleteWebSessionRequest) error {
	if err := req.Check(); err != nil {
		return trace.Wrap(err)
	}
	return trace.Wrap(r.backend.Delete(ctx, webSessionKey(req.SessionID)))
}

// DeleteAll removes all regular web sessions.
func (r *webSessions) DeleteAll(ctx context.Context) error {
	startKey := backend.Key(webPrefix, sessionsPrefix)
	return trace.Wrap(r.backend.DeleteRange(ctx, startKey, backend.RangeEnd(startKey)))
}

// DELETE IN 7.x.
// listLegacySessions lists web sessions under a legacy path /web/users/<user>/sessions/<id>
func (r *webSessions) listLegacySessions(ctx context.Context) ([]types.WebSession, error) {
	startKey := backend.Key(webPrefix, usersPrefix)
	result, err := r.backend.GetRange(ctx, startKey, backend.RangeEnd(startKey), backend.NoLimit)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	out := make([]types.WebSession, 0, len(result.Items))
	for _, item := range result.Items {
		suffix, _, err := baseTwoKeys(item.Key)
		if err != nil && trace.IsNotFound(err) {
			return nil, trace.Wrap(err)
		}
		if suffix != sessionsPrefix {
			continue
		}
		session, err := services.UnmarshalWebSession(item.Value)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		out = append(out, session)
	}
	return out, nil
}

type webSessions struct {
	backend backend.Backend
	log     logrus.FieldLogger
}

// WebTokens returns the web token manager.
func (s *IdentityService) WebTokens() types.WebTokenInterface {
	return &webTokens{backend: s.Backend, log: s.log}
}

// Get returns the web token described with req.
func (r *webTokens) Get(ctx context.Context, req types.GetWebTokenRequest) (types.WebToken, error) {
	if err := req.Check(); err != nil {
		return nil, trace.Wrap(err)
	}
	item, err := r.backend.Get(ctx, webTokenKey(req.Token))
	if err != nil {
		return nil, trace.Wrap(err)
	}
	token, err := services.UnmarshalWebToken(item.Value)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return token, nil
}

// List gets all web tokens.
func (r *webTokens) List(ctx context.Context) (out []types.WebToken, err error) {
	key := backend.Key(webPrefix, tokensPrefix)
	result, err := r.backend.GetRange(ctx, key, backend.RangeEnd(key), backend.NoLimit)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	for _, item := range result.Items {
		token, err := services.UnmarshalWebToken(item.Value)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		out = append(out, token)
	}
	return out, nil
}

// Upsert updates the existing or inserts a new web token.
func (r *webTokens) Upsert(ctx context.Context, token types.WebToken) error {
	bytes, err := services.MarshalWebToken(token, services.WithVersion(types.V3))
	if err != nil {
		return trace.Wrap(err)
	}
	metadata := token.GetMetadata()
	item := backend.Item{
		Key:     webTokenKey(token.GetToken()),
		Value:   bytes,
		Expires: metadata.Expiry(),
	}
	_, err = r.backend.Put(ctx, item)
	if err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// Delete deletes the web token specified with req from the storage.
func (r *webTokens) Delete(ctx context.Context, req types.DeleteWebTokenRequest) error {
	if err := req.Check(); err != nil {
		return trace.Wrap(err)
	}
	return trace.Wrap(r.backend.Delete(ctx, webTokenKey(req.Token)))
}

// DeleteAll removes all web tokens.
func (r *webTokens) DeleteAll(ctx context.Context) error {
	startKey := backend.Key(webPrefix, tokensPrefix)
	if err := r.backend.DeleteRange(ctx, startKey, backend.RangeEnd(startKey)); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

type webTokens struct {
	backend backend.Backend
	log     logrus.FieldLogger
}

// DELETE in 7.x.
// getLegacySession returns the web session for the specified user/sessionID
// under a legacy path /web/users/<user>/sessions/<id>
func getLegacyWebSession(ctx context.Context, backend backend.Backend, user, sessionID string) (types.WebSession, error) {
	item, err := backend.Get(ctx, legacyWebSessionKey(user, sessionID))
	if err != nil {
		return nil, trace.Wrap(err)
	}
	session, err := services.UnmarshalWebSession(item.Value)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	// this is for backwards compatibility to ensure we
	// always have these values
	session.SetUser(user)
	session.SetName(sessionID)
	return session, nil
}

func webSessionKey(sessionID string) (key []byte) {
	return backend.Key(webPrefix, sessionsPrefix, sessionID)
}

func webTokenKey(token string) (key []byte) {
	return backend.Key(webPrefix, tokensPrefix, token)
}

func legacyWebSessionKey(user, sessionID string) (key []byte) {
	return backend.Key(webPrefix, usersPrefix, user, sessionsPrefix, sessionID)
}
<commit_msg>Remove legacy session backend key (#11123)<commit_after>/*
Copyright 2020 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

package local

import (
	"context"

	"github.com/gravitational/teleport/api/types"
	"github.com/gravitational/teleport/lib/backend"
	"github.com/gravitational/teleport/lib/services"

	"github.com/gravitational/trace"
	"github.com/sirupsen/logrus"
)

// GetAppSession gets an application web session.
func (s *IdentityService) GetAppSession(ctx context.Context, req types.GetAppSessionRequest) (types.WebSession, error) {
	if err := req.Check(); err != nil {
		return nil, trace.Wrap(err)
	}

	item, err := s.Get(ctx, backend.Key(appsPrefix, sessionsPrefix, req.SessionID))
	if err != nil {
		return nil, trace.Wrap(err)
	}
	session, err := services.UnmarshalWebSession(item.Value)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return session, nil
}

// GetAppSessions gets all application web sessions.
func (s *IdentityService) GetAppSessions(ctx context.Context) ([]types.WebSession, error) {
	startKey := backend.Key(appsPrefix, sessionsPrefix)
	result, err := s.GetRange(ctx, startKey, backend.RangeEnd(startKey), backend.NoLimit)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	out := make([]types.WebSession, len(result.Items))
	for i, item := range result.Items {
		session, err := services.UnmarshalWebSession(item.Value)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		out[i] = session
	}
	return out, nil
}

// UpsertAppSession creates an application web session.
func (s *IdentityService) UpsertAppSession(ctx context.Context, session types.WebSession) error {
	value, err := services.MarshalWebSession(session)
	if err != nil {
		return trace.Wrap(err)
	}
	item := backend.Item{
		Key:     backend.Key(appsPrefix, sessionsPrefix, session.GetName()),
		Value:   value,
		Expires: session.GetExpiryTime(),
	}

	if _, err = s.Put(ctx, item); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// DeleteAppSession removes an application web session.
func (s *IdentityService) DeleteAppSession(ctx context.Context, req types.DeleteAppSessionRequest) error {
	if err := s.Delete(ctx, backend.Key(appsPrefix, sessionsPrefix, req.SessionID)); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// DeleteAllAppSessions removes all application web sessions.
func (s *IdentityService) DeleteAllAppSessions(ctx context.Context) error {
	startKey := backend.Key(appsPrefix, sessionsPrefix)
	if err := s.DeleteRange(ctx, startKey, backend.RangeEnd(startKey)); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// WebSessions returns the web sessions manager.
func (s *IdentityService) WebSessions() types.WebSessionInterface {
	return &webSessions{backend: s.Backend, log: s.log}
}

// Get returns the web session state described with req.
func (r *webSessions) Get(ctx context.Context, req types.GetWebSessionRequest) (types.WebSession, error) {
	if err := req.Check(); err != nil {
		return nil, trace.Wrap(err)
	}
	item, err := r.backend.Get(ctx, webSessionKey(req.SessionID))
	if err != nil {
		return nil, trace.Wrap(err)
	}
	session, err := services.UnmarshalWebSession(item.Value)
	if err != nil && !trace.IsNotFound(err) {
		return nil, trace.Wrap(err)
	}

	return session, trace.Wrap(err)
}

// List gets all regular web sessions.
func (r *webSessions) List(ctx context.Context) (out []types.WebSession, err error) {
	key := backend.Key(webPrefix, sessionsPrefix)
	result, err := r.backend.GetRange(ctx, key, backend.RangeEnd(key), backend.NoLimit)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	for _, item := range result.Items {
		session, err := services.UnmarshalWebSession(item.Value)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		out = append(out, session)
	}
	// DELETE IN 7.x:
	// Return web sessions from a legacy path under /web/users/<user>/sessions/<id>
	legacySessions, err := r.listLegacySessions(ctx)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return append(out, legacySessions...), nil
}

// Upsert updates the existing or inserts a new web session.
func (r *webSessions) Upsert(ctx context.Context, session types.WebSession) error {
	value, err := services.MarshalWebSession(session)
	if err != nil {
		return trace.Wrap(err)
	}
	sessionMetadata := session.GetMetadata()
	item := backend.Item{
		Key:     webSessionKey(session.GetName()),
		Value:   value,
		Expires: backend.EarliestExpiry(session.GetBearerTokenExpiryTime(), sessionMetadata.Expiry()),
	}
	_, err = r.backend.Put(ctx, item)
	if err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// Delete deletes the web session specified with req from the storage.
func (r *webSessions) Delete(ctx context.Context, req types.DeleteWebSessionRequest) error {
	if err := req.Check(); err != nil {
		return trace.Wrap(err)
	}
	return trace.Wrap(r.backend.Delete(ctx, webSessionKey(req.SessionID)))
}

// DeleteAll removes all regular web sessions.
func (r *webSessions) DeleteAll(ctx context.Context) error {
	startKey := backend.Key(webPrefix, sessionsPrefix)
	return trace.Wrap(r.backend.DeleteRange(ctx, startKey, backend.RangeEnd(startKey)))
}

// DELETE IN 7.x.
// listLegacySessions lists web sessions under a legacy path /web/users/<user>/sessions/<id>
func (r *webSessions) listLegacySessions(ctx context.Context) ([]types.WebSession, error) {
	startKey := backend.Key(webPrefix, usersPrefix)
	result, err := r.backend.GetRange(ctx, startKey, backend.RangeEnd(startKey), backend.NoLimit)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	out := make([]types.WebSession, 0, len(result.Items))
	for _, item := range result.Items {
		suffix, _, err := baseTwoKeys(item.Key)
		if err != nil && trace.IsNotFound(err) {
			return nil, trace.Wrap(err)
		}
		if suffix != sessionsPrefix {
			continue
		}
		session, err := services.UnmarshalWebSession(item.Value)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		out = append(out, session)
	}
	return out, nil
}

type webSessions struct {
	backend backend.Backend
	log     logrus.FieldLogger
}

// WebTokens returns the web token manager.
func (s *IdentityService) WebTokens() types.WebTokenInterface {
	return &webTokens{backend: s.Backend, log: s.log}
}

// Get returns the web token described with req.
func (r *webTokens) Get(ctx context.Context, req types.GetWebTokenRequest) (types.WebToken, error) {
	if err := req.Check(); err != nil {
		return nil, trace.Wrap(err)
	}
	item, err := r.backend.Get(ctx, webTokenKey(req.Token))
	if err != nil {
		return nil, trace.Wrap(err)
	}
	token, err := services.UnmarshalWebToken(item.Value)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return token, nil
}

// List gets all web tokens.
func (r *webTokens) List(ctx context.Context) (out []types.WebToken, err error) {
	key := backend.Key(webPrefix, tokensPrefix)
	result, err := r.backend.GetRange(ctx, key, backend.RangeEnd(key), backend.NoLimit)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	for _, item := range result.Items {
		token, err := services.UnmarshalWebToken(item.Value)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		out = append(out, token)
	}
	return out, nil
}

// Upsert updates the existing or inserts a new web token.
func (r *webTokens) Upsert(ctx context.Context, token types.WebToken) error {
	bytes, err := services.MarshalWebToken(token, services.WithVersion(types.V3))
	if err != nil {
		return trace.Wrap(err)
	}
	metadata := token.GetMetadata()
	item := backend.Item{
		Key:     webTokenKey(token.GetToken()),
		Value:   bytes,
		Expires: metadata.Expiry(),
	}
	_, err = r.backend.Put(ctx, item)
	if err != nil {
		return trace.Wrap(err)
	}
	return nil
}

// Delete deletes the web token specified with req from the storage.
func (r *webTokens) Delete(ctx context.Context, req types.DeleteWebTokenRequest) error {
	if err := req.Check(); err != nil {
		return trace.Wrap(err)
	}
	return trace.Wrap(r.backend.Delete(ctx, webTokenKey(req.Token)))
}

// DeleteAll removes all web tokens.
func (r *webTokens) DeleteAll(ctx context.Context) error {
	startKey := backend.Key(webPrefix, tokensPrefix)
	if err := r.backend.DeleteRange(ctx, startKey, backend.RangeEnd(startKey)); err != nil {
		return trace.Wrap(err)
	}
	return nil
}

type webTokens struct {
	backend backend.Backend
	log     logrus.FieldLogger
}

func webSessionKey(sessionID string) (key []byte) {
	return backend.Key(webPrefix, sessionsPrefix, sessionID)
}

func webTokenKey(token string) (key []byte) {
	return backend.Key(webPrefix, tokensPrefix, token)
}
<|endoftext|>


<commit_before>package horo_test

import (
	"net/http"

	"github.com/k2wanko/horo"
	"github.com/k2wanko/horo/middleware"
	"golang.org/x/net/context"
)

func Example_basic() {
	h := horo.New()
	h.Use(middleware.Logger())
	h.GET("/", func(c context.Context) error {
		return horo.Text(c, 200, "Hello, World")
	})
	h.ListenAndServe(":8080")
}

func Example_appengine() {
	// Write in func init()
	h := horo.New()
	h.GET("/", func(c context.Context) error {
		return horo.Text(c, 200, "Hello, World")
	})
	http.Handle("/", h)
}
<commit_msg>Fix examples_test<commit_after>package horo_test

import (
	"net/http"

	"github.com/k2wanko/horo"
	"github.com/k2wanko/horo-middleware"
	"golang.org/x/net/context"
)

func Example_basic() {
	h := horo.New()
	h.Use(middleware.Logger())
	h.GET("/", func(c context.Context) error {
		return horo.Text(c, 200, "Hello, World")
	})
	h.ListenAndServe(":8080")
}

func Example_appengine() {
	// Write in func init()
	h := horo.New()
	h.GET("/", func(c context.Context) error {
		return horo.Text(c, 200, "Hello, World")
	})
	http.Handle("/", h)
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package googlecloudgotesting

import (
	"context"
	"fmt"

	"cloud.google.com/go/storage"
	"github.com/googleapis/google-cloud-go-testing/storage/stiface"
)

type RecordingClient struct {
	stiface.Client
	bucketCalls int
}

func (rc *RecordingClient) Bucket(name string) stiface.BucketHandle {
	rc.bucketCalls++
	return rc.Client.Bucket(name)
}

// We do not need to implement methods that we don't want to record - by default
// the embedded type will be used.

func Example_recordBuckets() {
	// This example demonstrates building a simple mock that the number of
	// Bucket calls before calling the real client and returning its output.

	ctx := context.Background()
	c, err := storage.NewClient(ctx)
	if err != nil {
		// TODO: Handle error.
	}
	client := stiface.AdaptClient(c)
	recordingClient := RecordingClient{client, 0}

	recordingClient.Bucket("my-bucket-1")
	recordingClient.Bucket("my-bucket-2")
	recordingClient.Bucket("my-bucket-3")

	fmt.Println(recordingClient.bucketCalls)
	// Output: 3
}
<commit_msg>small docs fix (#19)<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package googlecloudgotesting

import (
	"context"
	"fmt"

	"cloud.google.com/go/storage"
	"github.com/googleapis/google-cloud-go-testing/storage/stiface"
)

type RecordingClient struct {
	stiface.Client
	bucketCalls int
}

func (rc *RecordingClient) Bucket(name string) stiface.BucketHandle {
	rc.bucketCalls++
	return rc.Client.Bucket(name)
}

// We do not need to implement methods that we don't want to record - by default
// the embedded type will be used.

func Example_recordBuckets() {
	// This example demonstrates building a simple mock that counts the number
	// of Bucket calls before calling the real client and returning its output.

	ctx := context.Background()
	c, err := storage.NewClient(ctx)
	if err != nil {
		// TODO: Handle error.
	}
	client := stiface.AdaptClient(c)
	recordingClient := RecordingClient{client, 0}

	recordingClient.Bucket("my-bucket-1")
	recordingClient.Bucket("my-bucket-2")
	recordingClient.Bucket("my-bucket-3")

	fmt.Println(recordingClient.bucketCalls)
	// Output: 3
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

package examples_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/yaml"
	"github.com/golang/glog"
)

func validateObject(obj runtime.Object) (errors []error) {
	ctx := api.NewDefaultContext()
	switch t := obj.(type) {
	case *api.ReplicationController:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		errors = validation.ValidateReplicationController(t)
	case *api.ReplicationControllerList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.Service:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidateService(t)
	case *api.ServiceList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.Pod:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidatePod(t)
	case *api.PodList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.PersistentVolume:
		errors = validation.ValidatePersistentVolume(t)
	case *api.PersistentVolumeClaim:
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidatePersistentVolumeClaim(t)
	default:
		return []error{fmt.Errorf("no validation defined for %#v", obj)}
	}
	return errors
}

func walkJSONFiles(inDir string, fn func(name, path string, data []byte)) error {
	return filepath.Walk(inDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() && path != inDir {
			return filepath.SkipDir
		}

		file := filepath.Base(path)
		if ext := filepath.Ext(file); ext == ".json" || ext == ".yaml" {
			glog.Infof("Testing %s", path)
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}
			name := strings.TrimSuffix(file, ext)

			if ext == ".yaml" {
				out, err := yaml.ToJSON(data)
				if err != nil {
					return err
				}
				data = out
			}

			fn(name, path, data)
		}
		return nil
	})
}

func TestExampleObjectSchemas(t *testing.T) {
	cases := map[string]map[string]runtime.Object{
		"../docs/getting-started-guides": {
			"pod": &api.Pod{},
		},
		"../cmd/integration": {
			"v1beta1-controller": &api.ReplicationController{},
			"v1beta3-controller": &api.ReplicationController{},
		},
		"../examples/guestbook": {
			"frontend-controller":     &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"frontend-service":        &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/guestbook/v1beta3": {
			"frontend-controller":    &api.ReplicationController{},
			"redis-slave-controller": &api.ReplicationController{},
			"redis-master":           &api.ReplicationController{},
			"frontend-service":       &api.Service{},
			"redis-master-service":   &api.Service{},
			"redis-slave-service":    &api.Service{},
		},
		"../examples/guestbook-go": {
			"guestbook-controller":    &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"guestbook-service":       &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/guestbook-go/v1beta3": {
			"guestbook-controller":    &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"guestbook-service":       &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/walkthrough": {
			"pod1": &api.Pod{},
			"pod2": &api.Pod{},
			"pod-with-http-healthcheck": &api.Pod{},
			"service":                   &api.Service{},
			"replication-controller":    &api.ReplicationController{},
		},
		"../examples/update-demo/v1beta1": {
			"kitten-rc":   &api.ReplicationController{},
			"nautilus-rc": &api.ReplicationController{},
		},
		"../examples/update-demo/v1beta3": {
			"kitten-rc":   &api.ReplicationController{},
			"nautilus-rc": &api.ReplicationController{},
		},
		"../examples/persistent-volumes/volumes": {
			"local-01": &api.PersistentVolume{},
			"local-02": &api.PersistentVolume{},
			"gce":      &api.PersistentVolume{},
		},
		"../examples/persistent-volumes/claims": {
			"claim-01": &api.PersistentVolumeClaim{},
			"claim-02": &api.PersistentVolumeClaim{},
			"claim-03": &api.PersistentVolumeClaim{},
		},
		"../examples/iscsi/v1beta1": {
			"iscsi": &api.Pod{},
		},
		"../examples/iscsi/v1beta3": {
			"iscsi": &api.Pod{},
		},
		"../examples/glusterfs/v1beta3": {
			"glusterfs": &api.Pod{},
		},
	}

	for path, expected := range cases {
		tested := 0
		err := walkJSONFiles(path, func(name, path string, data []byte) {
			expectedType, found := expected[name]
			if !found {
				t.Errorf("%s: %s does not have a test case defined", path, name)
				return
			}
			tested += 1
			if err := latest.Codec.DecodeInto(data, expectedType); err != nil {
				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data))
				return
			}
			if errors := validateObject(expectedType); len(errors) > 0 {
				t.Errorf("%s did not validate correctly: %v", path, errors)
			}
		})
		if err != nil {
			t.Errorf("Expected no error, Got %v", err)
		}
		if tested != len(expected) {
			t.Errorf("Expected %d examples, Got %d", len(expected), tested)
		}
	}
}

// This regex is tricky, but it works.  For future me, here is the decode:
//
// Flags: (?ms) = multiline match, allow . to match \n
// 1) Look for a line that starts with ``` (a markdown code block)
// 2) (?: ... ) = non-capturing group
// 3) (P<name>) = capture group as "name"
// 4) Look for #1 followed by either:
// 4a)    "yaml" followed by any word-characters followed by a newline (e.g. ```yamlfoo\n)
// 4b)    "any word-characters followed by a newline (e.g. ```json\n)
// 5) Look for either:
// 5a)    #4a followed by one or more characters (non-greedy)
// 5b)    #4b followed by { followed by one or more characters (non-greedy) followed by }
// 6) Look for #5 followed by a newline followed by ``` (end of the code block)
//
// This could probably be simplified, but is already too delicate.  Before any
// real changes, we should have a testscase that just tests this regex.
var sampleRegexp = regexp.MustCompile("(?ms)^```(?:(?P<type>yaml)\\w*\\n(?P<content>.+?)|\\w*\\n(?P<content>\\{.+?\\}))\\n^```")
var subsetRegexp = regexp.MustCompile("(?ms)\\.{3}")

func TestReadme(t *testing.T) {
	paths := []string{
		"../README.md",
		"../examples/walkthrough/README.md",
		"../examples/iscsi/README.md",
	}

	for _, path := range paths {
		data, err := ioutil.ReadFile(path)
		if err != nil {
			t.Errorf("Unable to read file %s: %v", path, err)
			continue
		}

		matches := sampleRegexp.FindAllStringSubmatch(string(data), -1)
		if matches == nil {
			continue
		}
		for _, match := range matches {
			var content, subtype string
			for i, name := range sampleRegexp.SubexpNames() {
				if name == "type" {
					subtype = match[i]
				}
				if name == "content" && match[i] != "" {
					content = match[i]
				}
			}
			if subtype == "yaml" && subsetRegexp.FindString(content) != "" {
				t.Logf("skipping (%s): \n%s", subtype, content)
				continue
			}

			//t.Logf("testing (%s): \n%s", subtype, content)
			expectedType := &api.Pod{}
			json, err := yaml.ToJSON([]byte(content))
			if err != nil {
				t.Errorf("%s could not be converted to JSON: %v\n%s", path, err, string(content))
			}
			if err := latest.Codec.DecodeInto(json, expectedType); err != nil {
				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(content))
				continue
			}
			if errors := validateObject(expectedType); len(errors) > 0 {
				t.Errorf("%s did not validate correctly: %v", path, errors)
			}
			_, err = latest.Codec.Encode(expectedType)
			if err != nil {
				t.Errorf("Could not encode object: %v", err)
				continue
			}
		}
	}
}
<commit_msg>v1beta1/3 version of update-demo removed<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

package examples_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/yaml"
	"github.com/golang/glog"
)

func validateObject(obj runtime.Object) (errors []error) {
	ctx := api.NewDefaultContext()
	switch t := obj.(type) {
	case *api.ReplicationController:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		errors = validation.ValidateReplicationController(t)
	case *api.ReplicationControllerList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.Service:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidateService(t)
	case *api.ServiceList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.Pod:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidatePod(t)
	case *api.PodList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.PersistentVolume:
		errors = validation.ValidatePersistentVolume(t)
	case *api.PersistentVolumeClaim:
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidatePersistentVolumeClaim(t)
	default:
		return []error{fmt.Errorf("no validation defined for %#v", obj)}
	}
	return errors
}

func walkJSONFiles(inDir string, fn func(name, path string, data []byte)) error {
	return filepath.Walk(inDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() && path != inDir {
			return filepath.SkipDir
		}

		file := filepath.Base(path)
		if ext := filepath.Ext(file); ext == ".json" || ext == ".yaml" {
			glog.Infof("Testing %s", path)
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}
			name := strings.TrimSuffix(file, ext)

			if ext == ".yaml" {
				out, err := yaml.ToJSON(data)
				if err != nil {
					return err
				}
				data = out
			}

			fn(name, path, data)
		}
		return nil
	})
}

func TestExampleObjectSchemas(t *testing.T) {
	cases := map[string]map[string]runtime.Object{
		"../docs/getting-started-guides": {
			"pod": &api.Pod{},
		},
		"../cmd/integration": {
			"v1beta1-controller": &api.ReplicationController{},
			"v1beta3-controller": &api.ReplicationController{},
		},
		"../examples/guestbook": {
			"frontend-controller":     &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"frontend-service":        &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/guestbook/v1beta3": {
			"frontend-controller":    &api.ReplicationController{},
			"redis-slave-controller": &api.ReplicationController{},
			"redis-master":           &api.ReplicationController{},
			"frontend-service":       &api.Service{},
			"redis-master-service":   &api.Service{},
			"redis-slave-service":    &api.Service{},
		},
		"../examples/guestbook-go": {
			"guestbook-controller":    &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"guestbook-service":       &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/guestbook-go/v1beta3": {
			"guestbook-controller":    &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"guestbook-service":       &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/walkthrough": {
			"pod1": &api.Pod{},
			"pod2": &api.Pod{},
			"pod-with-http-healthcheck": &api.Pod{},
			"service":                   &api.Service{},
			"replication-controller":    &api.ReplicationController{},
		},
		"../examples/update-demo": {
			"kitten-rc":   &api.ReplicationController{},
			"nautilus-rc": &api.ReplicationController{},
		},
		"../examples/persistent-volumes/volumes": {
			"local-01": &api.PersistentVolume{},
			"local-02": &api.PersistentVolume{},
			"gce":      &api.PersistentVolume{},
		},
		"../examples/persistent-volumes/claims": {
			"claim-01": &api.PersistentVolumeClaim{},
			"claim-02": &api.PersistentVolumeClaim{},
			"claim-03": &api.PersistentVolumeClaim{},
		},
		"../examples/iscsi/v1beta1": {
			"iscsi": &api.Pod{},
		},
		"../examples/iscsi/v1beta3": {
			"iscsi": &api.Pod{},
		},
		"../examples/glusterfs/v1beta3": {
			"glusterfs": &api.Pod{},
		},
	}

	for path, expected := range cases {
		tested := 0
		err := walkJSONFiles(path, func(name, path string, data []byte) {
			expectedType, found := expected[name]
			if !found {
				t.Errorf("%s: %s does not have a test case defined", path, name)
				return
			}
			tested += 1
			if err := latest.Codec.DecodeInto(data, expectedType); err != nil {
				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data))
				return
			}
			if errors := validateObject(expectedType); len(errors) > 0 {
				t.Errorf("%s did not validate correctly: %v", path, errors)
			}
		})
		if err != nil {
			t.Errorf("Expected no error, Got %v", err)
		}
		if tested != len(expected) {
			t.Errorf("Expected %d examples, Got %d", len(expected), tested)
		}
	}
}

// This regex is tricky, but it works.  For future me, here is the decode:
//
// Flags: (?ms) = multiline match, allow . to match \n
// 1) Look for a line that starts with ``` (a markdown code block)
// 2) (?: ... ) = non-capturing group
// 3) (P<name>) = capture group as "name"
// 4) Look for #1 followed by either:
// 4a)    "yaml" followed by any word-characters followed by a newline (e.g. ```yamlfoo\n)
// 4b)    "any word-characters followed by a newline (e.g. ```json\n)
// 5) Look for either:
// 5a)    #4a followed by one or more characters (non-greedy)
// 5b)    #4b followed by { followed by one or more characters (non-greedy) followed by }
// 6) Look for #5 followed by a newline followed by ``` (end of the code block)
//
// This could probably be simplified, but is already too delicate.  Before any
// real changes, we should have a testscase that just tests this regex.
var sampleRegexp = regexp.MustCompile("(?ms)^```(?:(?P<type>yaml)\\w*\\n(?P<content>.+?)|\\w*\\n(?P<content>\\{.+?\\}))\\n^```")
var subsetRegexp = regexp.MustCompile("(?ms)\\.{3}")

func TestReadme(t *testing.T) {
	paths := []string{
		"../README.md",
		"../examples/walkthrough/README.md",
		"../examples/iscsi/README.md",
	}

	for _, path := range paths {
		data, err := ioutil.ReadFile(path)
		if err != nil {
			t.Errorf("Unable to read file %s: %v", path, err)
			continue
		}

		matches := sampleRegexp.FindAllStringSubmatch(string(data), -1)
		if matches == nil {
			continue
		}
		for _, match := range matches {
			var content, subtype string
			for i, name := range sampleRegexp.SubexpNames() {
				if name == "type" {
					subtype = match[i]
				}
				if name == "content" && match[i] != "" {
					content = match[i]
				}
			}
			if subtype == "yaml" && subsetRegexp.FindString(content) != "" {
				t.Logf("skipping (%s): \n%s", subtype, content)
				continue
			}

			//t.Logf("testing (%s): \n%s", subtype, content)
			expectedType := &api.Pod{}
			json, err := yaml.ToJSON([]byte(content))
			if err != nil {
				t.Errorf("%s could not be converted to JSON: %v\n%s", path, err, string(content))
			}
			if err := latest.Codec.DecodeInto(json, expectedType); err != nil {
				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(content))
				continue
			}
			if errors := validateObject(expectedType); len(errors) > 0 {
				t.Errorf("%s did not validate correctly: %v", path, errors)
			}
			_, err = latest.Codec.Encode(expectedType)
			if err != nil {
				t.Errorf("Could not encode object: %v", err)
				continue
			}
		}
	}
}
<|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.

// HTTP file system request handler

package http

import (
	"fmt";
	"io";
	"os";
	"path";
	"strings";
	"utf8";
)

// TODO this should be in a mime package somewhere
var contentByExt = map[string]string{
	".css": "text/css",
	".gif": "image/gif",
	".html": "text/html; charset=utf-8",
	".jpg": "image/jpeg",
	".js": "application/x-javascript",
	".pdf": "application/pdf",
	".png": "image/png",
}

// Heuristic: b is text if it is valid UTF-8 and doesn't
// contain any unprintable ASCII or Unicode characters.
func isText(b []byte) bool {
	for len(b) > 0 && utf8.FullRune(b) {
		rune, size := utf8.DecodeRune(b);
		if size == 1 && rune == utf8.RuneError {
			// decoding error
			return false
		}
		if 0x80 <= rune && rune <= 0x9F {
			return false
		}
		if rune < ' ' {
			switch rune {
			case '\n', '\r', '\t':
				// okay
			default:
				// binary garbage
				return false
			}
		}
		b = b[size:];
	}
	return true;
}

func dirList(c *Conn, f *os.File) {
	fmt.Fprintf(c, "
\n");
	for {
		dirs, err := f.Readdir(100);
		if err != nil || len(dirs) == 0 {
			break
		}
		for _, d := range dirs {
			name := d.Name;
			if d.IsDirectory() {
				name += "/"
			}
			// TODO htmlescape
			fmt.Fprintf(c, "%s\n", name, name);
		}
	}
	fmt.Fprintf(c, "
\n");
}

func serveFileInternal(c *Conn, r *Request, name string, redirect bool) {
	const indexPage = "/index.html";

	// redirect to strip off any index.html
	n := len(name) - len(indexPage);
	if n >= 0 && name[n:] == indexPage {
		Redirect(c, name[0:n+1], StatusMovedPermanently);
		return;
	}

	f, err := os.Open(name, os.O_RDONLY, 0);
	if err != nil {
		// TODO expose actual error?
		NotFound(c, r);
		return;
	}
	defer f.Close();

	d, err1 := f.Stat();
	if err1 != nil {
		// TODO expose actual error?
		NotFound(c, r);
		return;
	}

	if redirect {
		// redirect to canonical path: / at end of directory url
		// r.URL.Path always begins with /
		url := r.URL.Path;
		if d.IsDirectory() {
			if url[len(url)-1] != '/' {
				Redirect(c, url+"/", StatusMovedPermanently);
				return;
			}
		} else {
			if url[len(url)-1] == '/' {
				Redirect(c, url[0:len(url)-1], StatusMovedPermanently);
				return;
			}
		}
	}

	// use contents of index.html for directory, if present
	if d.IsDirectory() {
		index := name + indexPage;
		ff, err := os.Open(index, os.O_RDONLY, 0);
		if err == nil {
			defer ff.Close();
			dd, err := ff.Stat();
			if err == nil {
				name = index;
				d = dd;
				f = ff;
			}
		}
	}

	if d.IsDirectory() {
		dirList(c, f);
		return;
	}

	// serve file
	// use extension to find content type.
	ext := path.Ext(name);
	if ctype, ok := contentByExt[ext]; ok {
		c.SetHeader("Content-Type", ctype)
	} else {
		// read first chunk to decide between utf-8 text and binary
		var buf [1024]byte;
		n, _ := io.ReadFull(f, &buf);
		b := buf[0:n];
		if isText(b) {
			c.SetHeader("Content-Type", "text-plain; charset=utf-8")
		} else {
			c.SetHeader("Content-Type", "application/octet-stream")	// generic binary
		}
		c.Write(b);
	}
	io.Copy(c, f);
}

// ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(c *Conn, r *Request, name string) {
	serveFileInternal(c, r, name, false)
}

type fileHandler struct {
	root	string;
	prefix	string;
}

// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
// It strips prefix from the incoming requests before
// looking up the file name in the file system.
func FileServer(root, prefix string) Handler	{ return &fileHandler{root, prefix} }

func (f *fileHandler) ServeHTTP(c *Conn, r *Request) {
	path := r.URL.Path;
	if !strings.HasPrefix(path, f.prefix) {
		NotFound(c, r);
		return;
	}
	path = path[len(f.prefix):];
	serveFileInternal(c, r, f.root+"/"+path, true);
}
<commit_msg>http: redirect to correct URL<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.

// HTTP file system request handler

package http

import (
	"fmt";
	"io";
	"os";
	"path";
	"strings";
	"utf8";
)

// TODO this should be in a mime package somewhere
var contentByExt = map[string]string{
	".css": "text/css",
	".gif": "image/gif",
	".html": "text/html; charset=utf-8",
	".jpg": "image/jpeg",
	".js": "application/x-javascript",
	".pdf": "application/pdf",
	".png": "image/png",
}

// Heuristic: b is text if it is valid UTF-8 and doesn't
// contain any unprintable ASCII or Unicode characters.
func isText(b []byte) bool {
	for len(b) > 0 && utf8.FullRune(b) {
		rune, size := utf8.DecodeRune(b);
		if size == 1 && rune == utf8.RuneError {
			// decoding error
			return false
		}
		if 0x80 <= rune && rune <= 0x9F {
			return false
		}
		if rune < ' ' {
			switch rune {
			case '\n', '\r', '\t':
				// okay
			default:
				// binary garbage
				return false
			}
		}
		b = b[size:];
	}
	return true;
}

func dirList(c *Conn, f *os.File) {
	fmt.Fprintf(c, "
\n");
	for {
		dirs, err := f.Readdir(100);
		if err != nil || len(dirs) == 0 {
			break
		}
		for _, d := range dirs {
			name := d.Name;
			if d.IsDirectory() {
				name += "/"
			}
			// TODO htmlescape
			fmt.Fprintf(c, "%s\n", name, name);
		}
	}
	fmt.Fprintf(c, "
\n");
}

func serveFileInternal(c *Conn, r *Request, name string, redirect bool) {
	const indexPage = "/index.html";

	// redirect .../index.html to .../
	if strings.HasSuffix(r.URL.Path, indexPage) {
		Redirect(c, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], StatusMovedPermanently);
		return;
	}

	f, err := os.Open(name, os.O_RDONLY, 0);
	if err != nil {
		// TODO expose actual error?
		NotFound(c, r);
		return;
	}
	defer f.Close();

	d, err1 := f.Stat();
	if err1 != nil {
		// TODO expose actual error?
		NotFound(c, r);
		return;
	}

	if redirect {
		// redirect to canonical path: / at end of directory url
		// r.URL.Path always begins with /
		url := r.URL.Path;
		if d.IsDirectory() {
			if url[len(url)-1] != '/' {
				Redirect(c, url+"/", StatusMovedPermanently);
				return;
			}
		} else {
			if url[len(url)-1] == '/' {
				Redirect(c, url[0:len(url)-1], StatusMovedPermanently);
				return;
			}
		}
	}

	// use contents of index.html for directory, if present
	if d.IsDirectory() {
		index := name + indexPage;
		ff, err := os.Open(index, os.O_RDONLY, 0);
		if err == nil {
			defer ff.Close();
			dd, err := ff.Stat();
			if err == nil {
				name = index;
				d = dd;
				f = ff;
			}
		}
	}

	if d.IsDirectory() {
		dirList(c, f);
		return;
	}

	// serve file
	// use extension to find content type.
	ext := path.Ext(name);
	if ctype, ok := contentByExt[ext]; ok {
		c.SetHeader("Content-Type", ctype)
	} else {
		// read first chunk to decide between utf-8 text and binary
		var buf [1024]byte;
		n, _ := io.ReadFull(f, &buf);
		b := buf[0:n];
		if isText(b) {
			c.SetHeader("Content-Type", "text-plain; charset=utf-8")
		} else {
			c.SetHeader("Content-Type", "application/octet-stream")	// generic binary
		}
		c.Write(b);
	}
	io.Copy(c, f);
}

// ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(c *Conn, r *Request, name string) {
	serveFileInternal(c, r, name, false)
}

type fileHandler struct {
	root	string;
	prefix	string;
}

// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
// It strips prefix from the incoming requests before
// looking up the file name in the file system.
func FileServer(root, prefix string) Handler	{ return &fileHandler{root, prefix} }

func (f *fileHandler) ServeHTTP(c *Conn, r *Request) {
	path := r.URL.Path;
	if !strings.HasPrefix(path, f.prefix) {
		NotFound(c, r);
		return;
	}
	path = path[len(f.prefix):];
	serveFileInternal(c, r, f.root+"/"+path, true);
}
<|endoftext|>