<commit_before>package fake_bbs

import (
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/runtime-schema/bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
)

type FakeExecutorBBS struct {
	callsToConverge int

	desiredTaskChan     chan models.Task
	desiredTaskStopChan chan bool
	desiredTaskErrChan  chan error

	desiredLrpChan chan models.TransitionalLongRunningProcess

	maintainConvergeInterval      time.Duration
	maintainConvergeExecutorID    string
	maintainConvergeStatusChannel <-chan bool
	maintainConvergeStopChannel   chan<- chan bool
	maintainConvergeLockError     error

	MaintainExecutorPresenceInputs struct {
		HeartbeatInterval chan time.Duration
		ExecutorID        chan string
	}
	MaintainExecutorPresenceOutputs struct {
		Presence *FakePresence
		Error    error
	}

	claimedTasks []models.Task
	claimTaskErr error

	startedTasks []models.Task
	startTaskErr error

	completedTasks           []models.Task
	completeTaskErr          error
	convergeTimeToClaimTasks time.Duration

	startedLrps []models.TransitionalLongRunningProcess
	startLrpErr error

	sync.RWMutex
}

func NewFakeExecutorBBS() *FakeExecutorBBS {
	fakeBBS := &FakeExecutorBBS{}
	fakeBBS.MaintainExecutorPresenceInputs.HeartbeatInterval = make(chan time.Duration, 1)
	fakeBBS.MaintainExecutorPresenceInputs.ExecutorID = make(chan string, 1)
	fakeBBS.desiredTaskChan = make(chan models.Task, 1)
	fakeBBS.desiredTaskStopChan = make(chan bool)
	fakeBBS.desiredTaskErrChan = make(chan error)
	fakeBBS.desiredLrpChan = make(chan models.TransitionalLongRunningProcess, 1)
	return fakeBBS
}

func (fakeBBS *FakeExecutorBBS) MaintainExecutorPresence(heartbeatInterval time.Duration, executorID string) (bbs.Presence, <-chan bool, error) {
	fakeBBS.MaintainExecutorPresenceInputs.HeartbeatInterval <- heartbeatInterval
	fakeBBS.MaintainExecutorPresenceInputs.ExecutorID <- executorID

	presence := fakeBBS.MaintainExecutorPresenceOutputs.Presence

	if presence == nil {
		presence = &FakePresence{
			MaintainStatus: true,
		}
	}

	status, _ := presence.Maintain(heartbeatInterval)

	return presence, status, fakeBBS.MaintainExecutorPresenceOutputs.Error
}

func (fakeBBS *FakeExecutorBBS) GetMaintainExecutorPresenceHeartbeatInterval() time.Duration {
	return <-fakeBBS.MaintainExecutorPresenceInputs.HeartbeatInterval
}

func (fakeBBS *FakeExecutorBBS) GetMaintainExecutorPresenceId() string {
	return <-fakeBBS.MaintainExecutorPresenceInputs.ExecutorID
}

func (fakeBBS *FakeExecutorBBS) WatchForDesiredTask() (<-chan models.Task, chan<- bool, <-chan error) {
	return fakeBBS.desiredTaskChan, fakeBBS.desiredTaskStopChan, fakeBBS.desiredTaskErrChan
}

func (fakeBBS *FakeExecutorBBS) WatchForDesiredTransitionalLongRunningProcess() (<-chan models.TransitionalLongRunningProcess, chan<- bool, <-chan error) {
	return fakeBBS.desiredLrpChan, nil, nil
}

func (fakeBBS *FakeExecutorBBS) EmitDesiredTask(task models.Task) {
	fakeBBS.desiredTaskChan <- task
}

func (fakeBBS *FakeExecutorBBS) ClaimTask(task models.Task, executorID string) (models.Task, error) {
	task.ExecutorID = executorID

	fakeBBS.RLock()
	err := fakeBBS.claimTaskErr
	fakeBBS.RUnlock()

	if err != nil {
		return task, err
	}

	fakeBBS.Lock()
	fakeBBS.claimedTasks = append(fakeBBS.claimedTasks, task)
	fakeBBS.Unlock()

	return task, nil
}

func (fakeBBS *FakeExecutorBBS) ClaimedTasks() []models.Task {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	claimed := make([]models.Task, len(fakeBBS.claimedTasks))
	copy(claimed, fakeBBS.claimedTasks)

	return claimed
}

func (fakeBBS *FakeExecutorBBS) SetClaimTaskErr(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.claimTaskErr = err
}

func (fakeBBS *FakeExecutorBBS) StartTask(task models.Task, containerHandle string) (models.Task, error) {
	fakeBBS.RLock()
	err := fakeBBS.startTaskErr
	fakeBBS.RUnlock()

	if err != nil {
		return task, err
	}

	task.ContainerHandle = containerHandle

	fakeBBS.Lock()
	fakeBBS.startedTasks = append(fakeBBS.startedTasks, task)
	fakeBBS.Unlock()

	return task, nil
}

func (fakeBBS *FakeExecutorBBS) StartTransitionalLongRunningProcess(lrp models.TransitionalLongRunningProcess) error {
	fakeBBS.RLock()
	err := fakeBBS.startLrpErr
	fakeBBS.RUnlock()

	if err != nil {
		return err
	}

	fakeBBS.Lock()
	fakeBBS.startedLrps = append(fakeBBS.startedLrps, lrp)
	fakeBBS.Unlock()

	return nil
}

func (fakeBBS *FakeExecutorBBS) StartedTasks() []models.Task {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	started := make([]models.Task, len(fakeBBS.startedTasks))
	copy(started, fakeBBS.startedTasks)

	return started
}

func (fakeBBS *FakeExecutorBBS) SetStartTaskErr(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.startTaskErr = err
}

func (fakeBBS *FakeExecutorBBS) CompleteTask(task models.Task, failed bool, failureReason string, result string) (models.Task, error) {
	fakeBBS.RLock()
	err := fakeBBS.completeTaskErr
	fakeBBS.RUnlock()

	if err != nil {
		return task, err
	}

	task.Failed = failed
	task.FailureReason = failureReason
	task.Result = result

	fakeBBS.Lock()
	fakeBBS.completedTasks = append(fakeBBS.completedTasks, task)
	fakeBBS.Unlock()

	return task, nil
}

func (fakeBBS *FakeExecutorBBS) CompletedTasks() []models.Task {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	completed := make([]models.Task, len(fakeBBS.completedTasks))
	copy(completed, fakeBBS.completedTasks)

	return completed
}

func (fakeBBS *FakeExecutorBBS) SetCompleteTaskErr(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.completeTaskErr = err
}

func (fakeBBS *FakeExecutorBBS) ConvergeTask(timeToClaim time.Duration) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.convergeTimeToClaimTasks = timeToClaim
	fakeBBS.callsToConverge++
}

func (fakeBBS *FakeExecutorBBS) MaintainConvergeLock(interval time.Duration, executorID string) (<-chan bool, chan<- chan bool, error) {
	status := make(chan bool)
	stop := make(chan chan bool)

	fakeBBS.RLock()
	err := fakeBBS.maintainConvergeLockError
	fakeBBS.RUnlock()

	if err != nil {
		return nil, nil, err
	}

	fakeBBS.Lock()
	fakeBBS.maintainConvergeInterval = interval
	fakeBBS.maintainConvergeExecutorID = executorID
	fakeBBS.maintainConvergeStatusChannel = status
	fakeBBS.maintainConvergeStopChannel = stop
	fakeBBS.Unlock()

	ticker := time.NewTicker(interval)
	go func() {
		status <- true
		for {
			select {
			case <-ticker.C:
				status <- true
			case release := <-stop:
				ticker.Stop()
				close(status)
				if release != nil {
					close(release)
				}

				return
			}
		}
	}()

	return status, stop, nil
}

func (fakeBBS *FakeExecutorBBS) SetMaintainConvergeLockError(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.maintainConvergeLockError = err
}

func (fakeBBS *FakeExecutorBBS) Stop() {
	fakeBBS.RLock()
	presence := fakeBBS.MaintainExecutorPresenceOutputs.Presence
	stopConverge := fakeBBS.maintainConvergeStopChannel
	fakeBBS.RUnlock()

	if presence != nil {
		presence.Remove()
	}

	if stopConverge != nil {
		stopConverge <- nil
	}
}

func (fakeBBS *FakeExecutorBBS) ConvergeTimeToClaimTasks() time.Duration {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	return fakeBBS.convergeTimeToClaimTasks
}

func (fakeBBS *FakeExecutorBBS) CallsToConverge() int {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	return fakeBBS.callsToConverge
}
<commit_msg>Add lrp-related methods to fake executor bbs<commit_after>package fake_bbs

import (
	"sync"
	"time"

	"github.com/cloudfoundry-incubator/runtime-schema/bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
)

type FakeExecutorBBS struct {
	callsToConverge int

	desiredTaskChan     chan models.Task
	desiredTaskStopChan chan bool
	desiredTaskErrChan  chan error

	desiredLrpChan chan models.TransitionalLongRunningProcess

	maintainConvergeInterval      time.Duration
	maintainConvergeExecutorID    string
	maintainConvergeStatusChannel <-chan bool
	maintainConvergeStopChannel   chan<- chan bool
	maintainConvergeLockError     error

	MaintainExecutorPresenceInputs struct {
		HeartbeatInterval chan time.Duration
		ExecutorID        chan string
	}
	MaintainExecutorPresenceOutputs struct {
		Presence *FakePresence
		Error    error
	}

	claimedTasks []models.Task
	claimTaskErr error

	startedTasks []models.Task
	startTaskErr error

	completedTasks           []models.Task
	completeTaskErr          error
	convergeTimeToClaimTasks time.Duration

	startedLrps []models.TransitionalLongRunningProcess
	startLrpErr error

	sync.RWMutex
}

func NewFakeExecutorBBS() *FakeExecutorBBS {
	fakeBBS := &FakeExecutorBBS{}
	fakeBBS.MaintainExecutorPresenceInputs.HeartbeatInterval = make(chan time.Duration, 1)
	fakeBBS.MaintainExecutorPresenceInputs.ExecutorID = make(chan string, 1)
	fakeBBS.desiredTaskChan = make(chan models.Task, 1)
	fakeBBS.desiredTaskStopChan = make(chan bool)
	fakeBBS.desiredTaskErrChan = make(chan error)
	fakeBBS.desiredLrpChan = make(chan models.TransitionalLongRunningProcess, 1)
	return fakeBBS
}

func (fakeBBS *FakeExecutorBBS) MaintainExecutorPresence(heartbeatInterval time.Duration, executorID string) (bbs.Presence, <-chan bool, error) {
	fakeBBS.MaintainExecutorPresenceInputs.HeartbeatInterval <- heartbeatInterval
	fakeBBS.MaintainExecutorPresenceInputs.ExecutorID <- executorID

	presence := fakeBBS.MaintainExecutorPresenceOutputs.Presence

	if presence == nil {
		presence = &FakePresence{
			MaintainStatus: true,
		}
	}

	status, _ := presence.Maintain(heartbeatInterval)

	return presence, status, fakeBBS.MaintainExecutorPresenceOutputs.Error
}

func (fakeBBS *FakeExecutorBBS) GetMaintainExecutorPresenceHeartbeatInterval() time.Duration {
	return <-fakeBBS.MaintainExecutorPresenceInputs.HeartbeatInterval
}

func (fakeBBS *FakeExecutorBBS) GetMaintainExecutorPresenceId() string {
	return <-fakeBBS.MaintainExecutorPresenceInputs.ExecutorID
}

func (fakeBBS *FakeExecutorBBS) WatchForDesiredTask() (<-chan models.Task, chan<- bool, <-chan error) {
	return fakeBBS.desiredTaskChan, fakeBBS.desiredTaskStopChan, fakeBBS.desiredTaskErrChan
}

func (fakeBBS *FakeExecutorBBS) WatchForDesiredTransitionalLongRunningProcess() (<-chan models.TransitionalLongRunningProcess, chan<- bool, <-chan error) {
	return fakeBBS.desiredLrpChan, nil, nil
}

func (fakeBBS *FakeExecutorBBS) EmitDesiredTask(task models.Task) {
	fakeBBS.desiredTaskChan <- task
}

func (fakeBBS *FakeExecutorBBS) EmitDesiredLrp(lrp models.TransitionalLongRunningProcess) {
	fakeBBS.desiredLrpChan <- lrp
}

func (fakeBBS *FakeExecutorBBS) ClaimTask(task models.Task, executorID string) (models.Task, error) {
	task.ExecutorID = executorID

	fakeBBS.RLock()
	err := fakeBBS.claimTaskErr
	fakeBBS.RUnlock()

	if err != nil {
		return task, err
	}

	fakeBBS.Lock()
	fakeBBS.claimedTasks = append(fakeBBS.claimedTasks, task)
	fakeBBS.Unlock()

	return task, nil
}

func (fakeBBS *FakeExecutorBBS) ClaimedTasks() []models.Task {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	claimed := make([]models.Task, len(fakeBBS.claimedTasks))
	copy(claimed, fakeBBS.claimedTasks)

	return claimed
}

func (fakeBBS *FakeExecutorBBS) SetClaimTaskErr(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.claimTaskErr = err
}

func (fakeBBS *FakeExecutorBBS) StartTask(task models.Task, containerHandle string) (models.Task, error) {
	fakeBBS.RLock()
	err := fakeBBS.startTaskErr
	fakeBBS.RUnlock()

	if err != nil {
		return task, err
	}

	task.ContainerHandle = containerHandle

	fakeBBS.Lock()
	fakeBBS.startedTasks = append(fakeBBS.startedTasks, task)
	fakeBBS.Unlock()

	return task, nil
}

func (fakeBBS *FakeExecutorBBS) StartTransitionalLongRunningProcess(lrp models.TransitionalLongRunningProcess) error {
	fakeBBS.RLock()
	err := fakeBBS.startLrpErr
	fakeBBS.RUnlock()

	if err != nil {
		return err
	}

	fakeBBS.Lock()
	fakeBBS.startedLrps = append(fakeBBS.startedLrps, lrp)
	fakeBBS.Unlock()

	return nil
}

func (fakeBBS *FakeExecutorBBS) StartedTasks() []models.Task {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	started := make([]models.Task, len(fakeBBS.startedTasks))
	copy(started, fakeBBS.startedTasks)

	return started
}

func (fakeBBS *FakeExecutorBBS) StartedLongRunningProcesses() []models.TransitionalLongRunningProcess {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	started := make([]models.TransitionalLongRunningProcess, len(fakeBBS.startedLrps))
	copy(started, fakeBBS.startedLrps)

	return started
}

func (fakeBBS *FakeExecutorBBS) SetStartTaskErr(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.startTaskErr = err
}

func (fakeBBS *FakeExecutorBBS) SetStartLrpErr(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.startLrpErr = err
}

func (fakeBBS *FakeExecutorBBS) CompleteTask(task models.Task, failed bool, failureReason string, result string) (models.Task, error) {
	fakeBBS.RLock()
	err := fakeBBS.completeTaskErr
	fakeBBS.RUnlock()

	if err != nil {
		return task, err
	}

	task.Failed = failed
	task.FailureReason = failureReason
	task.Result = result

	fakeBBS.Lock()
	fakeBBS.completedTasks = append(fakeBBS.completedTasks, task)
	fakeBBS.Unlock()

	return task, nil
}

func (fakeBBS *FakeExecutorBBS) CompletedTasks() []models.Task {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	completed := make([]models.Task, len(fakeBBS.completedTasks))
	copy(completed, fakeBBS.completedTasks)

	return completed
}

func (fakeBBS *FakeExecutorBBS) SetCompleteTaskErr(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.completeTaskErr = err
}

func (fakeBBS *FakeExecutorBBS) ConvergeTask(timeToClaim time.Duration) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.convergeTimeToClaimTasks = timeToClaim
	fakeBBS.callsToConverge++
}

func (fakeBBS *FakeExecutorBBS) MaintainConvergeLock(interval time.Duration, executorID string) (<-chan bool, chan<- chan bool, error) {
	status := make(chan bool)
	stop := make(chan chan bool)

	fakeBBS.RLock()
	err := fakeBBS.maintainConvergeLockError
	fakeBBS.RUnlock()

	if err != nil {
		return nil, nil, err
	}

	fakeBBS.Lock()
	fakeBBS.maintainConvergeInterval = interval
	fakeBBS.maintainConvergeExecutorID = executorID
	fakeBBS.maintainConvergeStatusChannel = status
	fakeBBS.maintainConvergeStopChannel = stop
	fakeBBS.Unlock()

	ticker := time.NewTicker(interval)
	go func() {
		status <- true
		for {
			select {
			case <-ticker.C:
				status <- true
			case release := <-stop:
				ticker.Stop()
				close(status)
				if release != nil {
					close(release)
				}

				return
			}
		}
	}()

	return status, stop, nil
}

func (fakeBBS *FakeExecutorBBS) SetMaintainConvergeLockError(err error) {
	fakeBBS.Lock()
	defer fakeBBS.Unlock()

	fakeBBS.maintainConvergeLockError = err
}

func (fakeBBS *FakeExecutorBBS) Stop() {
	fakeBBS.RLock()
	presence := fakeBBS.MaintainExecutorPresenceOutputs.Presence
	stopConverge := fakeBBS.maintainConvergeStopChannel
	fakeBBS.RUnlock()

	if presence != nil {
		presence.Remove()
	}

	if stopConverge != nil {
		stopConverge <- nil
	}
}

func (fakeBBS *FakeExecutorBBS) ConvergeTimeToClaimTasks() time.Duration {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	return fakeBBS.convergeTimeToClaimTasks
}

func (fakeBBS *FakeExecutorBBS) CallsToConverge() int {
	fakeBBS.RLock()
	defer fakeBBS.RUnlock()

	return fakeBBS.callsToConverge
}
<|endoftext|>


<commit_before>// $G $D/$F.go && $L $F.$A && ./$A.out

// Copyright 2011 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.

//line foo/bar.y:4
package main
//line foo/bar.y:60
func main() {
//line foo/bar.y:297
	f, l := 0, 0
//line yacctab:1
	f, l = 1, 1
//line yaccpar:1
	f, l = 2, 1
//line foo/bar.y:82
	f, l = 3, 82
//line foo/bar.y:90
	f, l = 3, 90
//line foo/bar.y:92
	f, l = 3, 92
//line foo/bar.y:100
	f, l = 3, 100
//line foo/bar.y:104
	l = 104
//line foo/bar.y:112
	l = 112
//line foo/bar.y:117
	l = 117
//line foo/bar.y:121
	l = 121
//line foo/bar.y:125
	l = 125
//line foo/bar.y:133
	l = 133
//line foo/bar.y:146
	l = 146
//line foo/bar.y:148
//line foo/bar.y:153
//line foo/bar.y:155
	l = 155
//line foo/bar.y:160

//line foo/bar.y:164
//line foo/bar.y:173

//line foo/bar.y:178
//line foo/bar.y:180
//line foo/bar.y:185
//line foo/bar.y:195
//line foo/bar.y:197
//line foo/bar.y:202
//line foo/bar.y:204
//line foo/bar.y:208
//line foo/bar.y:211
//line foo/bar.y:213
//line foo/bar.y:215
//line foo/bar.y:217
//line foo/bar.y:221
//line foo/bar.y:229
//line foo/bar.y:236
//line foo/bar.y:238
//line foo/bar.y:240
//line foo/bar.y:244
//line foo/bar.y:249
//line foo/bar.y:253
//line foo/bar.y:257
//line foo/bar.y:262
//line foo/bar.y:267
//line foo/bar.y:272
	if l == f {
//line foo/bar.y:277
	panic("aie!")
//line foo/bar.y:281
	}
//line foo/bar.y:285
	return
//line foo/bar.y:288
//line foo/bar.y:290
}
//line foo/bar.y:293
//line foo/bar.y:295
<commit_msg>test: clear execute bit from source file<commit_after>// $G $D/$F.go && $L $F.$A && ./$A.out

// Copyright 2011 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.

//line foo/bar.y:4
package main
//line foo/bar.y:60
func main() {
//line foo/bar.y:297
	f, l := 0, 0
//line yacctab:1
	f, l = 1, 1
//line yaccpar:1
	f, l = 2, 1
//line foo/bar.y:82
	f, l = 3, 82
//line foo/bar.y:90
	f, l = 3, 90
//line foo/bar.y:92
	f, l = 3, 92
//line foo/bar.y:100
	f, l = 3, 100
//line foo/bar.y:104
	l = 104
//line foo/bar.y:112
	l = 112
//line foo/bar.y:117
	l = 117
//line foo/bar.y:121
	l = 121
//line foo/bar.y:125
	l = 125
//line foo/bar.y:133
	l = 133
//line foo/bar.y:146
	l = 146
//line foo/bar.y:148
//line foo/bar.y:153
//line foo/bar.y:155
	l = 155
//line foo/bar.y:160

//line foo/bar.y:164
//line foo/bar.y:173

//line foo/bar.y:178
//line foo/bar.y:180
//line foo/bar.y:185
//line foo/bar.y:195
//line foo/bar.y:197
//line foo/bar.y:202
//line foo/bar.y:204
//line foo/bar.y:208
//line foo/bar.y:211
//line foo/bar.y:213
//line foo/bar.y:215
//line foo/bar.y:217
//line foo/bar.y:221
//line foo/bar.y:229
//line foo/bar.y:236
//line foo/bar.y:238
//line foo/bar.y:240
//line foo/bar.y:244
//line foo/bar.y:249
//line foo/bar.y:253
//line foo/bar.y:257
//line foo/bar.y:262
//line foo/bar.y:267
//line foo/bar.y:272
	if l == f {
//line foo/bar.y:277
	panic("aie!")
//line foo/bar.y:281
	}
//line foo/bar.y:285
	return
//line foo/bar.y:288
//line foo/bar.y:290
}
//line foo/bar.y:293
//line foo/bar.y:295
<|endoftext|>


<commit_before>package middleware

import (
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/newrelic/go-agent"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/controller"
	"github.com/oinume/lekcije/server/controller/flash_message"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/rs/cors"
	"github.com/uber-go/zap"
)

var _ = fmt.Print

const maxDBConnections = 5

func PanicHandler(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				fmt.Printf("r = %+v\n", r)
				var err error
				switch errorType := r.(type) {
				case string:
					err = fmt.Errorf(errorType)
				case error:
					err = errorType
				default:
					err = fmt.Errorf("Unknown error type: %v", errorType)
				}
				fmt.Printf("err = %v\n", err)
				controller.InternalServerError(w, errors.InternalWrapf(err, "panic ocurred"))
				return
			}
		}()
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func AccessLogger(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		writerProxy := controller.WrapWriter(w)
		h.ServeHTTP(writerProxy, r)
		func() {
			end := time.Now()
			status := writerProxy.Status()
			if status == 0 {
				status = http.StatusOK
			}

			// 180.76.15.26 - - [31/Jul/2016:13:18:07 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
			logger.Access.Info(
				"",
				zap.String("date", start.Format(time.RFC3339)),
				zap.String("method", r.Method),
				zap.String("url", r.URL.String()),
				zap.Int("status", status),
				zap.Int("bytes", writerProxy.BytesWritten()),
				zap.String("remoteAddr", controller.GetRemoteAddress(r)),
				zap.String("userAgent", r.Header.Get("User-Agent")),
				zap.String("referer", r.Referer()),
				zap.Duration("elapsed", end.Sub(start)/time.Millisecond),
			)
		}()
	}
	return http.HandlerFunc(fn)
}

func NewRelic(h http.Handler) http.Handler {
	key := os.Getenv("NEW_RELIC_LICENSE_KEY")
	if key == "" {
		return h
	}

	c := newrelic.NewConfig("lekcije", key)
	app, err := newrelic.NewApplication(c)
	if err != nil {
		logger.App.Error("Failed to newrelic.NewApplication()", zap.Error(err))
		return h
	}
	fn := func(w http.ResponseWriter, r *http.Request) {
		tx := app.StartTransaction(r.URL.Path, w, r)
		defer tx.End()
		h.ServeHTTP(tx, r)
	}
	return http.HandlerFunc(fn)
}

func SetDBAndRedisToContext(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		if config.IsLocalEnv() {
			fmt.Printf("%s %s\n", r.Method, r.RequestURI)
		}

		db, c, err := model.OpenDBAndSetToContext(
			ctx, os.Getenv("DB_URL"), maxDBConnections, !config.IsProductionEnv(),
		)
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer db.Close()

		redisClient, c, err := model.OpenRedisAndSetToContext(c, os.Getenv("REDIS_URL"))
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer redisClient.Close()

		_, c = flash_message.NewStoreRedisAndSetToContext(c, redisClient)

		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetLoggedInUserToContext(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.APITokenCookieName)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}

		user, c, err := model.FindLoggedInUserAndSetToContext(cookie.Value, ctx)
		if err != nil {
			fmt.Printf("loggedInUser = %+v\n", user)
			h.ServeHTTP(w, r)
			return
		}
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetTrackingID(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		cookie, err := r.Cookie(controller.TrackingIDCookieName)
		var trackingID string
		if err == nil {
			trackingID = cookie.Value
		} else {
			trackingID = uuid.New().String()
			c := &http.Cookie{
				Name:     controller.TrackingIDCookieName,
				Value:    trackingID,
				Path:     "/",
				Domain:   strings.Replace(r.Host, "www.", "", 1),
				Expires:  time.Now().UTC().Add(time.Hour * 24 * 365 * 2),
				HttpOnly: true,
			}
			http.SetCookie(w, c)
		}
		c := model.SetTrackingIDToContext(r.Context(), trackingID)
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func LoginRequiredFilter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if !strings.HasPrefix(r.RequestURI, "/me") {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.APITokenCookieName)
		if err != nil {
			logger.App.Debug("Not logged in")
			http.Redirect(w, r, config.WebURL(), http.StatusFound)
			return
		}

		user, c, err := model.FindLoggedInUserAndSetToContext(cookie.Value, ctx)
		if err != nil {
			switch err.(type) {
			case *errors.NotFound:
				logger.App.Debug("not logged in")
				http.Redirect(w, r, config.WebURL(), http.StatusFound)
				return
			default:
				controller.InternalServerError(w, err)
				return
			}
		}
		logger.App.Debug("Logged in user", zap.Object("user", user))
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func CORS(h http.Handler) http.Handler {
	origins := []string{}
	if strings.HasPrefix(config.StaticURL(), "http") {
		origins = append(origins, strings.TrimRight(config.StaticURL(), "/static"))
	}
	c := cors.New(cors.Options{
		AllowedOrigins: origins,
		//Debug:          true,
	})
	fn := func(w http.ResponseWriter, r *http.Request) {
		c.HandlerFunc(w, r)
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func Redirecter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if r.Host == "lekcije.herokuapp.com" {
			http.Redirect(w, r, config.WebURL()+r.RequestURI, http.StatusMovedPermanently)
			return
		}
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<commit_msg>Set cookie trackingId<commit_after>package middleware

import (
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/newrelic/go-agent"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/controller"
	"github.com/oinume/lekcije/server/controller/flash_message"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/rs/cors"
	"github.com/uber-go/zap"
)

var _ = fmt.Print

const maxDBConnections = 5

func PanicHandler(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				fmt.Printf("r = %+v\n", r)
				var err error
				switch errorType := r.(type) {
				case string:
					err = fmt.Errorf(errorType)
				case error:
					err = errorType
				default:
					err = fmt.Errorf("Unknown error type: %v", errorType)
				}
				fmt.Printf("err = %v\n", err)
				controller.InternalServerError(w, errors.InternalWrapf(err, "panic ocurred"))
				return
			}
		}()
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func AccessLogger(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		writerProxy := controller.WrapWriter(w)
		h.ServeHTTP(writerProxy, r)
		func() {
			end := time.Now()
			status := writerProxy.Status()
			if status == 0 {
				status = http.StatusOK
			}

			// 180.76.15.26 - - [31/Jul/2016:13:18:07 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
			logger.Access.Info(
				"",
				zap.String("date", start.Format(time.RFC3339)),
				zap.String("method", r.Method),
				zap.String("url", r.URL.String()),
				zap.Int("status", status),
				zap.Int("bytes", writerProxy.BytesWritten()),
				zap.String("remoteAddr", controller.GetRemoteAddress(r)),
				zap.String("userAgent", r.Header.Get("User-Agent")),
				zap.String("referer", r.Referer()),
				zap.Duration("elapsed", end.Sub(start)/time.Millisecond),
			)
		}()
	}
	return http.HandlerFunc(fn)
}

func NewRelic(h http.Handler) http.Handler {
	key := os.Getenv("NEW_RELIC_LICENSE_KEY")
	if key == "" {
		return h
	}

	c := newrelic.NewConfig("lekcije", key)
	app, err := newrelic.NewApplication(c)
	if err != nil {
		logger.App.Error("Failed to newrelic.NewApplication()", zap.Error(err))
		return h
	}
	fn := func(w http.ResponseWriter, r *http.Request) {
		tx := app.StartTransaction(r.URL.Path, w, r)
		defer tx.End()
		h.ServeHTTP(tx, r)
	}
	return http.HandlerFunc(fn)
}

func SetDBAndRedisToContext(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		if config.IsLocalEnv() {
			fmt.Printf("%s %s\n", r.Method, r.RequestURI)
		}

		db, c, err := model.OpenDBAndSetToContext(
			ctx, os.Getenv("DB_URL"), maxDBConnections, !config.IsProductionEnv(),
		)
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer db.Close()

		redisClient, c, err := model.OpenRedisAndSetToContext(c, os.Getenv("REDIS_URL"))
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer redisClient.Close()

		_, c = flash_message.NewStoreRedisAndSetToContext(c, redisClient)

		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetLoggedInUserToContext(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.APITokenCookieName)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}

		user, c, err := model.FindLoggedInUserAndSetToContext(cookie.Value, ctx)
		if err != nil {
			fmt.Printf("loggedInUser = %+v\n", user)
			h.ServeHTTP(w, r)
			return
		}
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetTrackingID(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		cookie, err := r.Cookie(controller.TrackingIDCookieName)
		var trackingID string
		if err == nil {
			trackingID = cookie.Value
		} else {
			trackingID = uuid.New().String()
			domain := strings.Replace(r.Host, "www.", "", 1)
			domain = strings.Replace(domain, ":4000", "", 1) // TODO: local only
			c := &http.Cookie{
				Name:     controller.TrackingIDCookieName,
				Value:    trackingID,
				Path:     "/",
				Domain:   domain,
				Expires:  time.Now().UTC().Add(time.Hour * 24 * 365 * 2),
				HttpOnly: true,
			}
			http.SetCookie(w, c)
		}
		c := model.SetTrackingIDToContext(r.Context(), trackingID)
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func LoginRequiredFilter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if !strings.HasPrefix(r.RequestURI, "/me") {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.APITokenCookieName)
		if err != nil {
			logger.App.Debug("Not logged in")
			http.Redirect(w, r, config.WebURL(), http.StatusFound)
			return
		}

		user, c, err := model.FindLoggedInUserAndSetToContext(cookie.Value, ctx)
		if err != nil {
			switch err.(type) {
			case *errors.NotFound:
				logger.App.Debug("not logged in")
				http.Redirect(w, r, config.WebURL(), http.StatusFound)
				return
			default:
				controller.InternalServerError(w, err)
				return
			}
		}
		logger.App.Debug("Logged in user", zap.Object("user", user))
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func CORS(h http.Handler) http.Handler {
	origins := []string{}
	if strings.HasPrefix(config.StaticURL(), "http") {
		origins = append(origins, strings.TrimRight(config.StaticURL(), "/static"))
	}
	c := cors.New(cors.Options{
		AllowedOrigins: origins,
		//Debug:          true,
	})
	fn := func(w http.ResponseWriter, r *http.Request) {
		c.HandlerFunc(w, r)
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func Redirecter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if r.Host == "lekcije.herokuapp.com" {
			http.Redirect(w, r, config.WebURL()+r.RequestURI, http.StatusMovedPermanently)
			return
		}
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<|endoftext|>


<commit_before>//usr/bin/env go run $0; exit $?
/* content/src/replace_txt_chunks.go
 * ============================================================================
 * Usage:
 *   $ curl -O http://rgolubtsov.github.io/srcs/replace_txt_chunks.go && \
       chmod 700                                replace_txt_chunks.go && \
                                              ./replace_txt_chunks.go; echo $?
 * ============================================================================
 * This is a demo script. It has to be run as a Go script using the Go tool's
 * runtime facility. Tested and known to run exactly the same way
 * on modern versions of OpenBSD/amd64, Ubuntu Server LTS x86-64, Arch Linux /
 * Arch Linux 32 operating systems.
 *
 * Create a function that will take a String value as the first parameter,
 * a Number value as the second parameter, and a String value as the third one.
 * The first parameter should be a sentence or a set of sentences,
 * the second parameter should be a positional number of a letter in each word
 * in the given sentence that should be replaced by the third parameter.
 * That function should return the updated sentence.
 * ============================================================================
 * Copyright (C) 2018 Radislav (Radicchio) Golubtsov
 *
 * (See the LICENSE file at the top of the source tree.)
 */

package main

import (
    "fmt"
    "strings"
)

// Helper constants.
const (
    EMPTY_STRING string =     ""
    SPACE        string =    " "
    COMMA        string =    ","
    POINT        string =    "."
    NEW_LINE     string =   "\n"
    DBG_PREF     string = "==> "
)

// The replace function.
func replace(text string, pos int, subst string) string {
    text_        := EMPTY_STRING
    text__       := EMPTY_STRING
    text_ary     := strings.Split(text, SPACE)
    text_ary_len := len(text_ary)

    for i := 0; i < text_ary_len; i++ {
        text_ary_i_len := len(text_ary[i])

//      fmt.Printf(DBG_PREF + text_ary[i] + NEW_LINE)

        if ((pos - 1 < text_ary_i_len)       &&
            (text_ary[i][pos - 1:] != COMMA) &&
            (text_ary[i][pos - 1:] != POINT)) {

            text__ = strings.Replace(text_ary[i],
                                     text_ary[i][pos - 1:], subst, -1) +
                                     text_ary[i][pos    :]

//          fmt.Printf(DBG_PREF + text__      + NEW_LINE)

            text_ += text__
        } else {
//          fmt.Printf(DBG_PREF + text_ary[i] + NEW_LINE)

            text_ += text_ary[i]
        }

        text_ += SPACE
    }

    return text_
}

// The script entry point.
func main() {
    text  := EMPTY_STRING                                              +
"A guard sequence is either a single guard or a series of guards, "    +
"separated by semicolons (';'). The guard sequence 'G1; G2; ...; Gn' " +
"is true if at least one of the guards 'G1', 'G2', ..., 'Gn' "         +
"evaluates to 'true'."                                                 +
             EMPTY_STRING

    pos   := 3 // <== Can be set to either from 1 to infinity.
//  subst := "callable entity"
//  subst := "AAA"
//  subst := "+-="
    subst := "|"

    fmt.Printf(DBG_PREF + "%d"  + NEW_LINE,  pos     )
    fmt.Printf(DBG_PREF + subst + NEW_LINE + NEW_LINE)

    text_ := replace(text, pos, subst)

    fmt.Printf(NEW_LINE + DBG_PREF + text  + NEW_LINE)
    fmt.Printf(           DBG_PREF + text_ + NEW_LINE)
}

// vim:set nu et ts=4 sw=4:
<commit_msg>Make inputs immutable.<commit_after>//usr/bin/env go run $0; exit $?
/* content/src/replace_txt_chunks.go
 * ============================================================================
 * Usage:
 *   $ curl -O http://rgolubtsov.github.io/srcs/replace_txt_chunks.go && \
       chmod 700                                replace_txt_chunks.go && \
                                              ./replace_txt_chunks.go; echo $?
 * ============================================================================
 * This is a demo script. It has to be run as a Go script using the Go tool's
 * runtime facility. Tested and known to run exactly the same way
 * on modern versions of OpenBSD/amd64, Ubuntu Server LTS x86-64, Arch Linux /
 * Arch Linux 32 operating systems.
 *
 * Create a function that will take a String value as the first parameter,
 * a Number value as the second parameter, and a String value as the third one.
 * The first parameter should be a sentence or a set of sentences,
 * the second parameter should be a positional number of a letter in each word
 * in the given sentence that should be replaced by the third parameter.
 * That function should return the updated sentence.
 * ============================================================================
 * Copyright (C) 2018 Radislav (Radicchio) Golubtsov
 *
 * (See the LICENSE file at the top of the source tree.)
 */

package main

import (
    "fmt"
    "strings"
)

// Helper constants.
const (
    EMPTY_STRING string =     ""
    SPACE        string =    " "
    COMMA        string =    ","
    POINT        string =    "."
    NEW_LINE     string =   "\n"
    DBG_PREF     string = "==> "
)

// The replace function.
func replace(text string, pos int, subst string) string {
    text_        := EMPTY_STRING
    text__       := EMPTY_STRING
    text_ary     := strings.Split(text, SPACE)
    text_ary_len := len(text_ary)

    for i := 0; i < text_ary_len; i++ {
        text_ary_i_len := len(text_ary[i])

//      fmt.Printf(DBG_PREF + text_ary[i] + NEW_LINE)

        if ((pos - 1 < text_ary_i_len)       &&
            (text_ary[i][pos - 1:] != COMMA) &&
            (text_ary[i][pos - 1:] != POINT)) {

            text__ = strings.Replace(text_ary[i],
                                     text_ary[i][pos - 1:], subst, -1) +
                                     text_ary[i][pos    :]

//          fmt.Printf(DBG_PREF + text__      + NEW_LINE)

            text_ += text__
        } else {
//          fmt.Printf(DBG_PREF + text_ary[i] + NEW_LINE)

            text_ += text_ary[i]
        }

        text_ += SPACE
    }

    return text_
}

// The script entry point.
func main() {
    text  := EMPTY_STRING                                              +
"A guard sequence is either a single guard or a series of guards, "    +
"separated by semicolons (';'). The guard sequence 'G1; G2; ...; Gn' " +
"is true if at least one of the guards 'G1', 'G2', ..., 'Gn' "         +
"evaluates to 'true'."                                                 +
             EMPTY_STRING

    const pos int = 3 // <== Can be set to either from 1 to infinity.
//  subst := "callable entity"
//  subst := "AAA"
//  subst := "+-="
    subst := "|"

    fmt.Printf(DBG_PREF + "%d"  + NEW_LINE,  pos     )
    fmt.Printf(DBG_PREF + subst + NEW_LINE + NEW_LINE)

    text_ := replace(text, pos, subst)

    fmt.Printf(NEW_LINE + DBG_PREF + text  + NEW_LINE)
    fmt.Printf(           DBG_PREF + text_ + NEW_LINE)
}

// vim:set nu et ts=4 sw=4:
<|endoftext|>


<commit_before>package proxy

import (
	"fmt"
	"github.com/trendrr/goshire-shards/shards"
	"github.com/trendrr/goshire/cheshire"
	"github.com/trendrr/goshire/client"
	"log"
	"net"
)

// What the proxy needs to do:
// Listen on ports
// Shard on partition key
// forward request to proper server.
// Allow for overriding of processing for specific routes
// Manage the router table

type Server struct {
	//the bootstrap
	Bootstrap *cheshire.Bootstrap
	services  map[string]*Service
	Config    *cheshire.ServerConfig
}

func NewServerFile(configPath string) *Server {
	conf := cheshire.NewServerConfigFile(configPath)
	return NewServer(conf)
}

func NewServer(config *cheshire.ServerConfig) *Server {
	//create the config
	s := &Server{
		Bootstrap: cheshire.NewBootstrap(config),
		services:  make(map[string]*Service),
		Config:    config,
	}
	return s
}

// Sets the router tables via these seed urls
func (this *Server) Seeds(urls ...string) {
	for _, url := range urls {
		c := client.NewHttp(url)
		rt, err := shards.RequestRouterTable(c)
		if err == nil {
			this.RegisterService(rt)
		}
	}
}

// Adding a new service.  Note this is NOT threadsafe, should be done during
// initialization
func (this *Server) RegisterService(rt *shards.RouterTable) error {
	if _, ok := this.services[rt.Service]; ok {
		return fmt.Errorf("Service already registered %s", rt.Service)
	}

	service, err := NewService(rt)
	if err != nil {
		return err
	}
	this.services[rt.Service] = service
	return nil
}

// REturns the router table for the specified service
// If this proxy has only one service registered then
// it will use that one
func (this *Server) Service(service string) (*Service, error) {
	rt, ok := this.services[service]
	if ok {
		return rt, nil
	}
	if len(service) == 0 && len(this.services) == 1 {
		for _, v := range this.services {
			return v, nil
		}
	}
	return nil, fmt.Errorf("No service with name: %s found", service)
}

//Start listening
func (this *Server) Start() {

	this.Bootstrap.InitProcs()
	//TODO: any other bootstrapping we need?

	log.Println("********** Starting Cheshire Shard Proxy **************")
	// //now start listening.
	// if this.Conf.Exists("http.port") {
	//     port, ok := this.Conf.GetInt("http.port")
	//     if !ok {
	//         log.Println("ERROR: Couldn't start http listener ", port)
	//     } else {
	//         go HttpListen(port, this.Conf)
	//     }
	// }

	if this.Config.Exists("json.port") {
		port, ok := this.Config.GetInt("json.port")
		if !ok {
			log.Println("ERROR: Couldn't start binary listener")
		} else {
			go protocollisten(cheshire.JSON, port, this)
		}
	}

	if this.Config.Exists("bin.port") {
		port, ok := this.Config.GetInt("bin.port")
		if !ok {
			log.Println("ERROR: Couldn't start binary listener")
		} else {
			go binarylisten(port, this)
		}
	}

	//this just makes the current thread sleep.  kinda stupid currently.
	//but we should update to get messages from the listeners, like when a listener quites
	channel := make(chan string)
	val := <-channel
	log.Println(val)
}

func binarylisten(port int, server *Server) {
	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	defer ln.Close()
	if err != nil {
		// handle error
		log.Println(err)
		return
	}
	proxy := &BinProxy{

	}

	log.Printf("Binary Proxy Listener on port: %d", port)
	for {
		conn, err := ln.Accept()

		log.Printf("ACCEPT! %s", conn)
		if err != nil {
			log.Print(err)
			// handle error
			continue
		}

		//TODO: handle hello, and associate to correct service.
		go proxy.StartProxy(conn, server)
	}
}

func protocollisten(protocol cheshire.Protocol, port int, server *Server) error {
	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	defer ln.Close()
	if err != nil {
		// handle error
		log.Println(err)
		return err
	}
	log.Printf("%s Listener on port: %d", protocol.Type(), port)
	for {
		conn, err := ln.Accept()

		log.Printf("ACCEPT! %s", conn)
		if err != nil {
			log.Print(err)
			// handle error
			continue
		}
		//TODO: handle hello, and associate to correct service.
		go HandleShardConns(conn, protocol, server)
	}
	return nil
}
<commit_msg>exit if no service found<commit_after>package proxy

import (
	"fmt"
	"github.com/trendrr/goshire-shards/shards"
	"github.com/trendrr/goshire/cheshire"
	"github.com/trendrr/goshire/client"
	"log"
	"net"
)

// What the proxy needs to do:
// Listen on ports
// Shard on partition key
// forward request to proper server.
// Allow for overriding of processing for specific routes
// Manage the router table

type Server struct {
	//the bootstrap
	Bootstrap *cheshire.Bootstrap
	services  map[string]*Service
	Config    *cheshire.ServerConfig
}

func NewServerFile(configPath string) *Server {
	conf := cheshire.NewServerConfigFile(configPath)
	return NewServer(conf)
}

func NewServer(config *cheshire.ServerConfig) *Server {
	//create the config
	s := &Server{
		Bootstrap: cheshire.NewBootstrap(config),
		services:  make(map[string]*Service),
		Config:    config,
	}
	return s
}

// Sets the router tables via these seed urls
func (this *Server) Seeds(urls ...string) {
	for _, url := range urls {
		c := client.NewHttp(url)
		rt, err := shards.RequestRouterTable(c)
		if err == nil {
			this.RegisterService(rt)
		}
	}
}

// Adding a new service.  Note this is NOT threadsafe, should be done during
// initialization
func (this *Server) RegisterService(rt *shards.RouterTable) error {
	if _, ok := this.services[rt.Service]; ok {
		return fmt.Errorf("Service already registered %s", rt.Service)
	}

	service, err := NewService(rt)
	if err != nil {
		return err
	}
	this.services[rt.Service] = service
	return nil
}

// REturns the router table for the specified service
// If this proxy has only one service registered then
// it will use that one
func (this *Server) Service(service string) (*Service, error) {
	rt, ok := this.services[service]
	if ok {
		return rt, nil
	}
	if len(service) == 0 && len(this.services) == 1 {
		for _, v := range this.services {
			return v, nil
		}
	}
	return nil, fmt.Errorf("No service with name: %s found", service)
}

//Start listening
func (this *Server) Start() {

	this.Bootstrap.InitProcs()
	//TODO: any other bootstrapping we need?

	log.Println("********** Starting Cheshire Shard Proxy **************")
	// //now start listening.
	// if this.Conf.Exists("http.port") {
	//     port, ok := this.Conf.GetInt("http.port")
	//     if !ok {
	//         log.Println("ERROR: Couldn't start http listener ", port)
	//     } else {
	//         go HttpListen(port, this.Conf)
	//     }
	// }

	if len(this.services) == 0 {
		log.Fatal("NO Services available to proxy.  Exiting..")
	}

	if this.Config.Exists("json.port") {
		port, ok := this.Config.GetInt("json.port")
		if !ok {
			log.Println("ERROR: Couldn't start binary listener")
		} else {
			go protocollisten(cheshire.JSON, port, this)
		}
	}

	if this.Config.Exists("bin.port") {
		port, ok := this.Config.GetInt("bin.port")
		if !ok {
			log.Println("ERROR: Couldn't start binary listener")
		} else {
			go binarylisten(port, this)
		}
	}

	//this just makes the current thread sleep.  kinda stupid currently.
	//but we should update to get messages from the listeners, like when a listener quites
	channel := make(chan string)
	val := <-channel
	log.Println(val)
}

func binarylisten(port int, server *Server) {
	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	defer ln.Close()
	if err != nil {
		// handle error
		log.Println(err)
		return
	}
	proxy := &BinProxy{

	}

	log.Printf("Binary Proxy Listener on port: %d", port)
	for {
		conn, err := ln.Accept()

		log.Printf("ACCEPT! %s", conn)
		if err != nil {
			log.Print(err)
			// handle error
			continue
		}

		//TODO: handle hello, and associate to correct service.
		go proxy.StartProxy(conn, server)
	}
}

func protocollisten(protocol cheshire.Protocol, port int, server *Server) error {
	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	defer ln.Close()
	if err != nil {
		// handle error
		log.Println(err)
		return err
	}
	log.Printf("%s Listener on port: %d", protocol.Type(), port)
	for {
		conn, err := ln.Accept()

		log.Printf("ACCEPT! %s", conn)
		if err != nil {
			log.Print(err)
			// handle error
			continue
		}
		//TODO: handle hello, and associate to correct service.
		go HandleShardConns(conn, protocol, server)
	}
	return nil
}
<|endoftext|>


<commit_before>package proxy

import (
	"io"
	"net"
	"net/http"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/juju/errors"
	"github.com/ngaut/arena"
	stats "github.com/ngaut/gostats"
	log "github.com/ngaut/logging"
	"github.com/ngaut/tokenlimiter"
	"github.com/wandoulabs/cm/config"
	. "github.com/wandoulabs/cm/mysql"
	"github.com/wandoulabs/cm/vt/tabletserver"
)

var (
	baseConnId uint32 = 10000
)

type Server struct {
	configFile        string
	cfg               *config.Config
	addr              string
	user              string
	password          string
	listener          net.Listener
	nodes             map[string]*Node
	schemas           map[string]*Schema
	autoSchamas       map[string]*tabletserver.SchemaInfo
	rwlock            *sync.RWMutex
	taskQ             chan *execTask
	concurrentLimiter *tokenlimiter.TokenLimiter

	counter *stats.Counters

	clients  map[int64]*Conn
	clientId int64
}

type IServer interface {
	GetSchema(string) *Schema
	GetRowCacheSchema(string) (*tabletserver.SchemaInfo, bool)
	CfgGetPwd() string
	GetToken() *tokenlimiter.Token
	ReleaseToken(token *tokenlimiter.Token)
	GetRWlock() *sync.RWMutex
	GetNode(name string) *Node
	GetNodeNames() []string
	AsynExec(task *execTask)
	IncCounter(key string)
	DecCounter(key string)
}

func (s *Server) IncCounter(key string) {
	s.counter.Add(key, 1)
}

func (s *Server) DecCounter(key string) {
	s.counter.Add(key, -1)
}

func (s *Server) GetToken() *tokenlimiter.Token {
	return s.concurrentLimiter.Get()
}

func (s *Server) ReleaseToken(token *tokenlimiter.Token) {
	s.concurrentLimiter.Put(token)
}

func (s *Server) GetNode(name string) *Node {
	return s.nodes[name]
}

func (s *Server) GetRowCacheSchema(db string) (*tabletserver.SchemaInfo, bool) {
	si, ok := s.autoSchamas[db]
	return si, ok
}

func (s *Server) GetNodeNames() []string {
	ret := make([]string, 0, len(s.nodes))
	for name, _ := range s.nodes {
		ret = append(ret, name)
	}

	return ret
}

func (s *Server) parseNodes() error {
	cfg := s.cfg
	s.nodes = make(map[string]*Node, len(cfg.Nodes))

	for _, v := range cfg.Nodes {
		if _, ok := s.nodes[v.Name]; ok {
			return errors.Errorf("duplicate node [%s].", v.Name)
		}

		n, err := s.parseNode(v)
		if err != nil {
			return errors.Trace(err)
		}

		s.nodes[v.Name] = n
	}

	return nil
}

func (s *Server) parseNode(cfg config.NodeConfig) (*Node, error) {
	n := &Node{
		server: s,
		cfg:    cfg,
	}
	if len(cfg.Master) == 0 {
		return nil, errors.Errorf("must setting master MySQL node.")
	}

	var err error
	if n.master, err = n.openDB(cfg.Master); err != nil {
		return nil, errors.Trace(err)
	}

	return n, nil
}

func (s *Server) newConn(co net.Conn) *Conn {
	c := &Conn{
		c:            co,
		pkg:          NewPacketIO(co),
		server:       s,
		connectionId: atomic.AddUint32(&baseConnId, 1),
		status:       SERVER_STATUS_AUTOCOMMIT,
		collation:    DEFAULT_COLLATION_ID,
		charset:      DEFAULT_CHARSET,
		alloc:        arena.NewArenaAllocator(8 * 1024),
		txConns:      make(map[string]*SqlConn),
	}
	c.salt, _ = RandomBuf(20)

	return c
}

func (s *Server) GetRWlock() *sync.RWMutex {
	return s.rwlock
}

func (s *Server) AsynExec(task *execTask) {
	s.taskQ <- task
}

func (s *Server) CfgGetPwd() string {
	return s.cfg.Password
}

func (s *Server) loadSchemaInfo() error {
	if err := s.parseNodes(); err != nil {
		return errors.Trace(err)
	}

	if err := s.parseSchemas(); err != nil {
		return errors.Trace(err)
	}

	for _, v := range s.cfg.Schemas {
		rc := v.RulesConifg
		var overrides []tabletserver.SchemaOverride
		for _, sc := range rc.ShardRule {
			or := tabletserver.SchemaOverride{Name: sc.Table}
			pks := strings.Split(sc.Key, ",")
			for _, pk := range pks {
				or.PKColumns = append(or.PKColumns, strings.TrimSpace(pk))
			}
			or.Cache = &tabletserver.OverrideCacheDesc{Type: sc.RowCacheType, Prefix: or.Name, Table: or.Name}
			overrides = append(overrides, or)
		}

		//fix hard code node
		s.autoSchamas[v.DB] = tabletserver.NewSchemaInfo(s.cfg.RowCacheConf, s.cfg.Nodes[0].Master, s.cfg.User, s.cfg.Password, v.DB, overrides)
	}

	return nil
}

func makeServer(configFile string) *Server {
	cfg, err := config.ParseConfigFile(configFile)
	if err != nil {
		log.Error(err.Error())
		return nil
	}

	log.Warningf("%#v", cfg)

	s := &Server{
		configFile:        configFile,
		cfg:               cfg,
		addr:              cfg.Addr,
		user:              cfg.User,
		password:          cfg.Password,
		autoSchamas:       make(map[string]*tabletserver.SchemaInfo),
		taskQ:             make(chan *execTask, 100),
		concurrentLimiter: tokenlimiter.NewTokenLimiter(100),
		counter:           stats.NewCounters("stats"),
		rwlock:            &sync.RWMutex{},
		clients:           make(map[int64]*Conn),
	}

	f := func(wg *sync.WaitGroup, rs []interface{}, i int, co *SqlConn, sql string, args []interface{}) {
		r, err := co.Execute(sql, args...)
		if err != nil {
			rs[i] = err
		} else {
			rs[i] = r
		}

		wg.Done()
	}

	for i := 0; i < 100; i++ {
		go func() {
			for task := range s.taskQ {
				f(task.wg, task.rs, task.idx, task.co, task.sql, task.args)
			}
		}()
	}

	return s
}

func NewServer(configFile string) (*Server, error) {
	s := makeServer(configFile)
	s.loadSchemaInfo()

	netProto := "tcp"
	if strings.Contains(netProto, "/") {
		netProto = "unix"
	}

	var err error
	s.listener, err = net.Listen(netProto, s.addr)
	if err != nil {
		return nil, errors.Trace(err)
	}

	log.Infof("Server run MySql Protocol Listen(%s) at [%s]", netProto, s.addr)
	return s, nil
}

func (s *Server) cleanup() {
	for _, si := range s.autoSchamas {
		si.Close()
	}
}

func (s *Server) resetSchemaInfo() error {
	for _, c := range s.clients {
		if len(c.txConns) > 0 {
			return errors.Errorf("transaction exist")
		}
	}

	s.cleanup()
	s.autoSchamas = make(map[string]*tabletserver.SchemaInfo)
	for _, n := range s.nodes {
		n.Close()
	}

	s.nodes = nil
	s.schemas = nil

	cfg, err := config.ParseConfigFile(s.configFile)
	if err != nil {
		log.Fatal(err.Error())
	}

	log.Warningf("%#v", cfg)

	s.cfg = cfg
	s.loadSchemaInfo()
	return nil
}

func (s *Server) HandleReload(w http.ResponseWriter, req *http.Request) {
	log.Warning("reload config")
	s.rwlock.Lock()
	defer s.rwlock.Unlock()

	s.resetSchemaInfo()

	io.WriteString(w, "ok")
}

func (s *Server) Run() error {
	for {
		conn, err := s.listener.Accept()
		if err != nil {
			log.Errorf("accept error %s", err.Error())
			return err
		}

		go s.onConn(conn)
	}

	return nil
}

func (s *Server) Close() {
	s.rwlock.Lock()
	defer s.rwlock.Unlock()

	if s.listener != nil {
		s.listener.Close()
		s.listener = nil
	}

	s.cleanup()
}

func (s *Server) getClientId() int64 {
	return atomic.AddInt64(&s.clientId, 1)
}

func (s *Server) onConn(c net.Conn) {
	conn := s.newConn(c)
	if err := conn.Handshake(); err != nil {
		log.Errorf("handshake error %s", err.Error())
		c.Close()
		return
	}

	const key = "connections"

	s.IncCounter(key)
	defer s.DecCounter(key)

	s.rwlock.Lock()
	s.clients[s.getClientId()] = conn
	s.rwlock.Unlock()

	conn.Run()
}
<commit_msg>add more log<commit_after>package proxy

import (
	"io"
	"net"
	"net/http"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/juju/errors"
	"github.com/ngaut/arena"
	stats "github.com/ngaut/gostats"
	log "github.com/ngaut/logging"
	"github.com/ngaut/tokenlimiter"
	"github.com/wandoulabs/cm/config"
	. "github.com/wandoulabs/cm/mysql"
	"github.com/wandoulabs/cm/vt/tabletserver"
)

var (
	baseConnId uint32 = 10000
)

type Server struct {
	configFile        string
	cfg               *config.Config
	addr              string
	user              string
	password          string
	listener          net.Listener
	nodes             map[string]*Node
	schemas           map[string]*Schema
	autoSchamas       map[string]*tabletserver.SchemaInfo
	rwlock            *sync.RWMutex
	taskQ             chan *execTask
	concurrentLimiter *tokenlimiter.TokenLimiter

	counter *stats.Counters

	clients  map[int64]*Conn
	clientId int64
}

type IServer interface {
	GetSchema(string) *Schema
	GetRowCacheSchema(string) (*tabletserver.SchemaInfo, bool)
	CfgGetPwd() string
	GetToken() *tokenlimiter.Token
	ReleaseToken(token *tokenlimiter.Token)
	GetRWlock() *sync.RWMutex
	GetNode(name string) *Node
	GetNodeNames() []string
	AsynExec(task *execTask)
	IncCounter(key string)
	DecCounter(key string)
}

func (s *Server) IncCounter(key string) {
	s.counter.Add(key, 1)
}

func (s *Server) DecCounter(key string) {
	s.counter.Add(key, -1)
}

func (s *Server) GetToken() *tokenlimiter.Token {
	return s.concurrentLimiter.Get()
}

func (s *Server) ReleaseToken(token *tokenlimiter.Token) {
	s.concurrentLimiter.Put(token)
}

func (s *Server) GetNode(name string) *Node {
	return s.nodes[name]
}

func (s *Server) GetRowCacheSchema(db string) (*tabletserver.SchemaInfo, bool) {
	si, ok := s.autoSchamas[db]
	return si, ok
}

func (s *Server) GetNodeNames() []string {
	ret := make([]string, 0, len(s.nodes))
	for name, _ := range s.nodes {
		ret = append(ret, name)
	}

	return ret
}

func (s *Server) parseNodes() error {
	cfg := s.cfg
	s.nodes = make(map[string]*Node, len(cfg.Nodes))

	for _, v := range cfg.Nodes {
		if _, ok := s.nodes[v.Name]; ok {
			return errors.Errorf("duplicate node [%s].", v.Name)
		}

		n, err := s.parseNode(v)
		if err != nil {
			return errors.Trace(err)
		}

		s.nodes[v.Name] = n
	}

	return nil
}

func (s *Server) parseNode(cfg config.NodeConfig) (*Node, error) {
	n := &Node{
		server: s,
		cfg:    cfg,
	}
	if len(cfg.Master) == 0 {
		return nil, errors.Errorf("must setting master MySQL node.")
	}

	var err error
	if n.master, err = n.openDB(cfg.Master); err != nil {
		return nil, errors.Trace(err)
	}

	return n, nil
}

func (s *Server) newConn(co net.Conn) *Conn {
	c := &Conn{
		c:            co,
		pkg:          NewPacketIO(co),
		server:       s,
		connectionId: atomic.AddUint32(&baseConnId, 1),
		status:       SERVER_STATUS_AUTOCOMMIT,
		collation:    DEFAULT_COLLATION_ID,
		charset:      DEFAULT_CHARSET,
		alloc:        arena.NewArenaAllocator(8 * 1024),
		txConns:      make(map[string]*SqlConn),
	}
	c.salt, _ = RandomBuf(20)

	return c
}

func (s *Server) GetRWlock() *sync.RWMutex {
	return s.rwlock
}

func (s *Server) AsynExec(task *execTask) {
	s.taskQ <- task
}

func (s *Server) CfgGetPwd() string {
	return s.cfg.Password
}

func (s *Server) loadSchemaInfo() error {
	if err := s.parseNodes(); err != nil {
		return errors.Trace(err)
	}

	if err := s.parseSchemas(); err != nil {
		return errors.Trace(err)
	}

	for _, v := range s.cfg.Schemas {
		rc := v.RulesConifg
		var overrides []tabletserver.SchemaOverride
		for _, sc := range rc.ShardRule {
			or := tabletserver.SchemaOverride{Name: sc.Table}
			pks := strings.Split(sc.Key, ",")
			for _, pk := range pks {
				or.PKColumns = append(or.PKColumns, strings.TrimSpace(pk))
			}
			or.Cache = &tabletserver.OverrideCacheDesc{Type: sc.RowCacheType, Prefix: or.Name, Table: or.Name}
			overrides = append(overrides, or)
		}

		//fix hard code node
		s.autoSchamas[v.DB] = tabletserver.NewSchemaInfo(s.cfg.RowCacheConf, s.cfg.Nodes[0].Master, s.cfg.User, s.cfg.Password, v.DB, overrides)
	}

	return nil
}

func makeServer(configFile string) *Server {
	cfg, err := config.ParseConfigFile(configFile)
	if err != nil {
		log.Error(err.Error())
		return nil
	}

	log.Warningf("%#v", cfg)

	s := &Server{
		configFile:        configFile,
		cfg:               cfg,
		addr:              cfg.Addr,
		user:              cfg.User,
		password:          cfg.Password,
		autoSchamas:       make(map[string]*tabletserver.SchemaInfo),
		taskQ:             make(chan *execTask, 100),
		concurrentLimiter: tokenlimiter.NewTokenLimiter(100),
		counter:           stats.NewCounters("stats"),
		rwlock:            &sync.RWMutex{},
		clients:           make(map[int64]*Conn),
	}

	f := func(wg *sync.WaitGroup, rs []interface{}, i int, co *SqlConn, sql string, args []interface{}) {
		r, err := co.Execute(sql, args...)
		if err != nil {
			log.Warning(err)
			rs[i] = err
		} else {
			rs[i] = r
		}

		wg.Done()
	}

	for i := 0; i < 100; i++ {
		go func() {
			for task := range s.taskQ {
				f(task.wg, task.rs, task.idx, task.co, task.sql, task.args)
			}
		}()
	}

	return s
}

func NewServer(configFile string) (*Server, error) {
	s := makeServer(configFile)
	s.loadSchemaInfo()

	netProto := "tcp"
	if strings.Contains(netProto, "/") {
		netProto = "unix"
	}

	var err error
	s.listener, err = net.Listen(netProto, s.addr)
	if err != nil {
		return nil, errors.Trace(err)
	}

	log.Infof("Server run MySql Protocol Listen(%s) at [%s]", netProto, s.addr)
	return s, nil
}

func (s *Server) cleanup() {
	for _, si := range s.autoSchamas {
		si.Close()
	}
}

func (s *Server) resetSchemaInfo() error {
	for _, c := range s.clients {
		if len(c.txConns) > 0 {
			return errors.Errorf("transaction exist")
		}
	}

	s.cleanup()
	s.autoSchamas = make(map[string]*tabletserver.SchemaInfo)
	for _, n := range s.nodes {
		n.Close()
	}

	s.nodes = nil
	s.schemas = nil

	cfg, err := config.ParseConfigFile(s.configFile)
	if err != nil {
		log.Fatal(err.Error())
	}

	log.Warningf("%#v", cfg)

	s.cfg = cfg
	s.loadSchemaInfo()
	return nil
}

func (s *Server) HandleReload(w http.ResponseWriter, req *http.Request) {
	log.Warning("reload config")
	s.rwlock.Lock()
	defer s.rwlock.Unlock()

	s.resetSchemaInfo()

	io.WriteString(w, "ok")
}

func (s *Server) Run() error {
	for {
		conn, err := s.listener.Accept()
		if err != nil {
			log.Errorf("accept error %s", err.Error())
			return err
		}

		go s.onConn(conn)
	}

	return nil
}

func (s *Server) Close() {
	s.rwlock.Lock()
	defer s.rwlock.Unlock()

	if s.listener != nil {
		s.listener.Close()
		s.listener = nil
	}

	s.cleanup()
}

func (s *Server) getClientId() int64 {
	return atomic.AddInt64(&s.clientId, 1)
}

func (s *Server) onConn(c net.Conn) {
	conn := s.newConn(c)
	if err := conn.Handshake(); err != nil {
		log.Errorf("handshake error %s", err.Error())
		c.Close()
		return
	}

	const key = "connections"

	s.IncCounter(key)
	defer s.DecCounter(key)

	s.rwlock.Lock()
	s.clients[s.getClientId()] = conn
	s.rwlock.Unlock()

	conn.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"log"
	"os"
)

type ConfFile struct {
	MaxAge  int `json:"max_age"`
	Verbose int `json:"verbose"`
	Http    struct {
		Address string `json:"address"`
		Port    int    `json:"port"`
	}
	Https struct {
		Address string `json:"address"`
		Port    int    `json:"port"`
	}
}

const HTTPMaxAge = 31536000 // RFC 2616, Section 14.21
const TCPMaxPort = 65535    // RFC 793, Sction 3.1, 16-bit port size

var Config ConfFile

func ConfValidate() {
	if Config.MaxAge < 0 || Config.MaxAge > HTTPMaxAge {
		log.Fatalf("MaxAge must be in range 0..%d", HTTPMaxAge)
	}

	if Config.Verbose < 0 {
		log.Fatal("Verbose must be 0 or larger")
	}

	if Config.Http.Port < 0 || Config.Http.Port > TCPMaxPort {
		log.Fatalf("HTTP port must be in range 0..%d", TCPMaxPort)
	}

	if Config.Https.Port < 0 || Config.Https.Port > TCPMaxPort {
		log.Fatalf("HTTPS port must be in range 0..%d", TCPMaxPort)
	}

	if Config.Verbose < 2 {
		return
	}

	log.Println("-- conf start ------------")
	log.Printf("MaxAge  = %d\n", Config.MaxAge)
	log.Printf("Verbose = %d\n", Config.Verbose)
	log.Printf("Http  -> { Address = %s, Port = %d }\n",
		Config.Http.Address, Config.Http.Port)
	log.Printf("Https -> { Address = %s, Port = %d }\n",
		Config.Https.Address, Config.Https.Port)
	log.Println("-- conf end --------------")
}

func ConfInit() {
	cf := flag.String("c", "", "JSON config file")
	ma := flag.Int("m", HTTPMaxAge, "content cache age in secs")
	ha := flag.String("a", "", "http address (default '' = all)")
	hp := flag.Int("p", 80, "http port")
	sa := flag.String("A", "", "https address (default '' = all)")
	sp := flag.Int("P", 443, "https port")
	vl := flag.Int("v", 0, "verbose level")
	flag.Parse()

	Config.MaxAge = *ma
	Config.Verbose = *vl
	Config.Http.Address = *ha
	Config.Http.Port = *hp
	Config.Https.Address = *sa
	Config.Https.Port = *sp
	defer ConfValidate()

	if *cf == "" {
		return
	}

	jf, err := os.Open(*cf)
	if err != nil {
		log.Println("unable to open conf file " + *cf)
		log.Fatal(err)
	}
	defer jf.Close()

	jsonParser := json.NewDecoder(jf)
	err = jsonParser.Decode(&Config)
	if err != nil {
		log.Println("error parsing JSON conf file " + *cf)
		log.Fatal(err)
	}
}
<commit_msg>conf: ensure json formatting is applied to http/https structs<commit_after>package main

import (
	"encoding/json"
	"flag"
	"log"
	"os"
)

type ConfFile struct {
	MaxAge  int `json:"max_age"`
	Verbose int `json:"verbose"`
	Http    struct {
		Address string `json:"address"`
		Port    int    `json:"port"`
	} `json:"http"`
	Https struct {
		Address string `json:"address"`
		Port    int    `json:"port"`
	} `json:"https"`
}

const HTTPMaxAge = 31536000 // RFC 2616, Section 14.21
const TCPMaxPort = 65535    // RFC 793, Sction 3.1, 16-bit port size

var Config ConfFile

func ConfValidate() {
	if Config.MaxAge < 0 || Config.MaxAge > HTTPMaxAge {
		log.Fatalf("MaxAge must be in range 0..%d", HTTPMaxAge)
	}

	if Config.Verbose < 0 {
		log.Fatal("Verbose must be 0 or larger")
	}

	if Config.Http.Port < 0 || Config.Http.Port > TCPMaxPort {
		log.Fatalf("HTTP port must be in range 0..%d", TCPMaxPort)
	}

	if Config.Https.Port < 0 || Config.Https.Port > TCPMaxPort {
		log.Fatalf("HTTPS port must be in range 0..%d", TCPMaxPort)
	}

	if Config.Verbose < 2 {
		return
	}

	log.Println("-- conf start ------------")
	log.Printf("MaxAge  = %d\n", Config.MaxAge)
	log.Printf("Verbose = %d\n", Config.Verbose)
	log.Printf("Http  -> { Address = %s, Port = %d }\n",
		Config.Http.Address, Config.Http.Port)
	log.Printf("Https -> { Address = %s, Port = %d }\n",
		Config.Https.Address, Config.Https.Port)
	log.Println("-- conf end --------------")
}

func ConfInit() {
	cf := flag.String("c", "", "JSON config file")
	ma := flag.Int("m", HTTPMaxAge, "content cache age in secs")
	ha := flag.String("a", "", "http address (default '' = all)")
	hp := flag.Int("p", 80, "http port")
	sa := flag.String("A", "", "https address (default '' = all)")
	sp := flag.Int("P", 443, "https port")
	vl := flag.Int("v", 0, "verbose level")
	flag.Parse()

	Config.MaxAge = *ma
	Config.Verbose = *vl
	Config.Http.Address = *ha
	Config.Http.Port = *hp
	Config.Https.Address = *sa
	Config.Https.Port = *sp
	defer ConfValidate()

	if *cf == "" {
		return
	}

	jf, err := os.Open(*cf)
	if err != nil {
		log.Println("unable to open conf file " + *cf)
		log.Fatal(err)
	}
	defer jf.Close()

	jsonParser := json.NewDecoder(jf)
	err = jsonParser.Decode(&Config)
	if err != nil {
		log.Println("error parsing JSON conf file " + *cf)
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// This file is part of *kellner*
//
// Copyright (C) 2015, Travelping GmbH <copyright@travelping.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"archive/tar"
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"net/textproto"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/blakesmith/ar"
)

type ipkArchive struct {
	Name     string
	Control  string // content of 'control' file
	Header   map[string]string
	FileInfo os.FileInfo
	Md5      string
	Sha1     string
}

// ControlToHeader parses 'control' and stores the result in ipkg.Header
func (ipk *ipkArchive) ControlToHeader(control string) error {
	reader := bufio.NewReader(strings.NewReader(control))
	protoReader := textproto.NewReader(reader)
	for {
		line, err := protoReader.ReadContinuedLine()
		if err == io.EOF {
			break
		}
		i := strings.IndexByte(line, ':')
		if i == -1 {
			return fmt.Errorf("invalid package-field %q", line)
		}

		ipk.Header[line[:i]] = strings.TrimSpace(line[i+1:])
	}
	return nil
}

func (ipk *ipkArchive) EnhanceHeader() {
	ipk.Header["Size"] = strconv.FormatInt(ipk.FileInfo.Size(), 10)
	if ipk.Md5 != "" {
		ipk.Header["MD5Sum"] = ipk.Md5
	}
	if ipk.Sha1 != "" {
		ipk.Header["SHA1"] = ipk.Sha1
	}
}

func (ipk *ipkArchive) DirEntry() dirEntry {

	descr := ipk.Header["Description"]
	if len(descr) > 64 {
		descr = descr[:64] + "..."
	}

	return dirEntry{
		Name:     ipk.Name,
		ModTime:  ipk.FileInfo.ModTime(),
		Size:     ipk.FileInfo.Size(),
		Descr:    descr,
		RawDescr: ipk.Header["Description"],
	}
}

// HeaderTo writes the package-header to 'to'.
// according to https://www.debian.org/doc/debian-policy/ch-controlfields.html
// the order of the fields does not matter
// according to https://wiki.debian.org/RepositoryFormat#A.22Packages.22_Indices
// 'Packages' should be the first field.
func (ipk *ipkArchive) HeaderTo(w io.Writer) {

	p, ok := ipk.Header["Package"]
	if ok {
		fmt.Fprintf(w, "Package: %s\n", p)
	}

	for key := range ipk.Header {
		if key != "Package" {
			fmt.Fprintf(w, "%s: %s\n", key, ipk.Header[key])
		}
	}
}

// ControlAndChecksumTo writes the 'control' file,
// the file name, the size and the checksums to the
// writer 'w'
func (ipk *ipkArchive) ControlAndChecksumTo(w io.Writer) {
	io.WriteString(w, ipk.Control)
	fmt.Fprintf(w, "Filename: %s\n", ipk.Name)
	fmt.Fprintf(w, "Size: %d\n", ipk.FileInfo.Size())
	if ipk.Md5 != "" {
		fmt.Fprintf(w, "MD5Sum: %s\n", ipk.Md5)
	}
	if ipk.Sha1 != "" {
		fmt.Fprintf(w, "SHA1: %s\n", ipk.Sha1)
	}
}

type ipkArchiveChan chan *ipkArchive

// extract 'control' file from 'reader'. the contents of a 'control' file
// is a set of key-value pairs as described in
// https://www.debian.org/doc/debian-policy/ch-controlfields.html
func extractControlFromIpk(reader io.Reader) (string, error) {

	var (
		arReader  *ar.Reader
		tarReader *tar.Reader
		gzReader  *gzip.Reader
	)

	arReader = ar.NewReader(reader)
	for {
		header, err := arReader.Next()
		if err != nil && err != io.EOF {
			return "", fmt.Errorf("extracting contents: %v", err)
		} else if header == nil {
			break
		}

		// NOTE: strangeley the name of the files end with a "/" ... content error?
		if header.Name == "control.tar.gz/" || header.Name == "control.tar.gz" {
			gzReader, err = gzip.NewReader(arReader)
			if err != nil {
				return "", fmt.Errorf("analyzing control.tar.gz: %v", err)
			}
			break
		}
	}

	if gzReader == nil {
		return "", fmt.Errorf("missing control.tar.gz entry")
	}
	defer gzReader.Close()

	buffer := bytes.NewBuffer(nil)
	tarReader = tar.NewReader(gzReader)
	for {
		header, err := tarReader.Next()
		if err != nil && err != io.EOF {
			return "", fmt.Errorf("extracting control.tar.gz: %v", err)
		} else if header == nil {
			break
		}
		if header.Name != "./control" {
			continue
		}

		io.Copy(buffer, tarReader)
		break
	}

	if buffer.Len() == 0 {
		return "", fmt.Errorf("missing or empty 'control' file inside 'control.tar.gz'")
	}
	return buffer.String(), nil
}

func newIpkFromFile(fullName string, doMD5, doSHA1 bool) (*ipkArchive, error) {

	var (
		file     *os.File
		writer   = make([]io.Writer, 0, 3)
		err      error
		md5er    hash.Hash
		sha1er   hash.Hash
	)

	file, err = os.Open(fullName)
	if err != nil {
		return nil, fmt.Errorf("openening %q: %v", fullName, err)
	}
	defer file.Close()

	writer = append(writer, ioutil.Discard)
	if doMD5 {
		md5er = md5.New()
		writer = append(writer, md5er)
	}
	if doSHA1 {
		sha1er = sha1.New()
		writer = append(writer, sha1er)
	}

	tee := io.TeeReader(file, io.MultiWriter(writer...))

	control, err := extractControlFromIpk(tee)
	if err != nil {
		return nil, fmt.Errorf("extract pkg-info from %q: %v", fullName, err)
	}

	archive := &ipkArchive{Name: path.Base(fullName), Control: control, Header: make(map[string]string)}

	if err := archive.ControlToHeader(control); err != nil {
		return nil, fmt.Errorf("header parse error in %q: %v", fullName, err)
	}

	// consume the rest of the file to calculate md5/sha1
	io.Copy(ioutil.Discard, tee)
	file.Close() // close to free handles, 'collector' might block freeing otherwise

	archive.FileInfo, _ = os.Lstat(fullName)
	if md5er != nil {
		archive.Md5 = hex.EncodeToString(md5er.Sum(nil))
	}
	if sha1er != nil {
		archive.Sha1 = hex.EncodeToString(sha1er.Sum(nil))
	}

	return archive, nil
}

func newIpkFromCache(name, cachepath string, doMD5, doSHA1 bool) (*ipkArchive, error) {

	var (
		ctrlName = genCachedControlName(name, cachepath)
		control  []byte
		err      error
	)

	if control, err = ioutil.ReadFile(ctrlName); err != nil {
		return nil, fmt.Errorf("reading cache %q: %v\n", ctrlName, err)
	}

	var archive = &ipkArchive{Name: name,
		Control: string(control),
		Header:  make(map[string]string),
	}
	archive.FileInfo, _ = os.Stat(ctrlName)
	archive.ControlToHeader(archive.Control)

	return archive, nil
}
<commit_msg>add ScanLocation to ipkArchive<commit_after>// This file is part of *kellner*
//
// Copyright (C) 2015, Travelping GmbH <copyright@travelping.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"archive/tar"
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"net/textproto"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/blakesmith/ar"
)

type ipkArchive struct {
	Name         string
	Control      string // content of 'control' file
	Header       map[string]string
	FileInfo     os.FileInfo
	Md5          string
	Sha1         string
	ScanLocation string // location where the ipk was found
}

// ControlToHeader parses 'control' and stores the result in ipkg.Header
func (ipk *ipkArchive) ControlToHeader(control string) error {
	reader := bufio.NewReader(strings.NewReader(control))
	protoReader := textproto.NewReader(reader)
	for {
		line, err := protoReader.ReadContinuedLine()
		if err == io.EOF {
			break
		}
		i := strings.IndexByte(line, ':')
		if i == -1 {
			return fmt.Errorf("invalid package-field %q", line)
		}

		ipk.Header[line[:i]] = strings.TrimSpace(line[i+1:])
	}
	return nil
}

func (ipk *ipkArchive) EnhanceHeader() {
	ipk.Header["Size"] = strconv.FormatInt(ipk.FileInfo.Size(), 10)
	if ipk.Md5 != "" {
		ipk.Header["MD5Sum"] = ipk.Md5
	}
	if ipk.Sha1 != "" {
		ipk.Header["SHA1"] = ipk.Sha1
	}
}

func (ipk *ipkArchive) DirEntry() dirEntry {

	descr := ipk.Header["Description"]
	if len(descr) > 64 {
		descr = descr[:64] + "..."
	}

	return dirEntry{
		Name:     ipk.Name,
		ModTime:  ipk.FileInfo.ModTime(),
		Size:     ipk.FileInfo.Size(),
		Descr:    descr,
		RawDescr: ipk.Header["Description"],
	}
}

// HeaderTo writes the package-header to 'to'.
// according to https://www.debian.org/doc/debian-policy/ch-controlfields.html
// the order of the fields does not matter
// according to https://wiki.debian.org/RepositoryFormat#A.22Packages.22_Indices
// 'Packages' should be the first field.
func (ipk *ipkArchive) HeaderTo(w io.Writer) {

	p, ok := ipk.Header["Package"]
	if ok {
		fmt.Fprintf(w, "Package: %s\n", p)
	}

	for key := range ipk.Header {
		if key != "Package" {
			fmt.Fprintf(w, "%s: %s\n", key, ipk.Header[key])
		}
	}
}

// ControlAndChecksumTo writes the 'control' file,
// the file name, the size and the checksums to the
// writer 'w'
func (ipk *ipkArchive) ControlAndChecksumTo(w io.Writer) {
	io.WriteString(w, ipk.Control)
	fmt.Fprintf(w, "Filename: %s\n", ipk.Name)
	fmt.Fprintf(w, "Size: %d\n", ipk.FileInfo.Size())
	if ipk.Md5 != "" {
		fmt.Fprintf(w, "MD5Sum: %s\n", ipk.Md5)
	}
	if ipk.Sha1 != "" {
		fmt.Fprintf(w, "SHA1: %s\n", ipk.Sha1)
	}
}

type ipkArchiveChan chan *ipkArchive

// extract 'control' file from 'reader'. the contents of a 'control' file
// is a set of key-value pairs as described in
// https://www.debian.org/doc/debian-policy/ch-controlfields.html
func extractControlFromIpk(reader io.Reader) (string, error) {

	var (
		arReader  *ar.Reader
		tarReader *tar.Reader
		gzReader  *gzip.Reader
	)

	arReader = ar.NewReader(reader)
	for {
		header, err := arReader.Next()
		if err != nil && err != io.EOF {
			return "", fmt.Errorf("extracting contents: %v", err)
		} else if header == nil {
			break
		}

		// NOTE: strangeley the name of the files end with a "/" ... content error?
		if header.Name == "control.tar.gz/" || header.Name == "control.tar.gz" {
			gzReader, err = gzip.NewReader(arReader)
			if err != nil {
				return "", fmt.Errorf("analyzing control.tar.gz: %v", err)
			}
			break
		}
	}

	if gzReader == nil {
		return "", fmt.Errorf("missing control.tar.gz entry")
	}
	defer gzReader.Close()

	buffer := bytes.NewBuffer(nil)
	tarReader = tar.NewReader(gzReader)
	for {
		header, err := tarReader.Next()
		if err != nil && err != io.EOF {
			return "", fmt.Errorf("extracting control.tar.gz: %v", err)
		} else if header == nil {
			break
		}
		if header.Name != "./control" {
			continue
		}

		io.Copy(buffer, tarReader)
		break
	}

	if buffer.Len() == 0 {
		return "", fmt.Errorf("missing or empty 'control' file inside 'control.tar.gz'")
	}
	return buffer.String(), nil
}

func newIpkFromFile(fullName string, doMD5, doSHA1 bool) (*ipkArchive, error) {

	var (
		file   *os.File
		writer = make([]io.Writer, 0, 3)
		err    error
		md5er  hash.Hash
		sha1er hash.Hash
	)

	file, err = os.Open(fullName)
	if err != nil {
		return nil, fmt.Errorf("openening %q: %v", fullName, err)
	}
	defer file.Close()

	writer = append(writer, ioutil.Discard)
	if doMD5 {
		md5er = md5.New()
		writer = append(writer, md5er)
	}
	if doSHA1 {
		sha1er = sha1.New()
		writer = append(writer, sha1er)
	}

	tee := io.TeeReader(file, io.MultiWriter(writer...))

	control, err := extractControlFromIpk(tee)
	if err != nil {
		return nil, fmt.Errorf("extract pkg-info from %q: %v", fullName, err)
	}

	archive := &ipkArchive{
		Name:         path.Base(fullName),
		Control:      control,
		Header:       make(map[string]string),
		ScanLocation: fullName}

	if err := archive.ControlToHeader(control); err != nil {
		return nil, fmt.Errorf("header parse error in %q: %v", fullName, err)
	}

	// consume the rest of the file to calculate md5/sha1
	io.Copy(ioutil.Discard, tee)
	file.Close() // close to free handles, 'collector' might block freeing otherwise

	archive.FileInfo, _ = os.Lstat(fullName)
	if md5er != nil {
		archive.Md5 = hex.EncodeToString(md5er.Sum(nil))
	}
	if sha1er != nil {
		archive.Sha1 = hex.EncodeToString(sha1er.Sum(nil))
	}

	return archive, nil
}

func newIpkFromCache(name, cachepath string, doMD5, doSHA1 bool) (*ipkArchive, error) {

	var (
		ctrlName = genCachedControlName(name, cachepath)
		control  []byte
		err      error
	)

	if control, err = ioutil.ReadFile(ctrlName); err != nil {
		return nil, fmt.Errorf("reading cache %q: %v\n", ctrlName, err)
	}

	var archive = &ipkArchive{Name: name,
		Control: string(control),
		Header:  make(map[string]string),
	}
	archive.FileInfo, _ = os.Stat(ctrlName)
	archive.ControlToHeader(archive.Control)

	return archive, nil
}
<|endoftext|>


<commit_before>// run

// Copyright 2015 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.

// darwin/386 seems to mangle the PC and SP before
// it manages to invoke the signal handler, so this test fails there.
// +build !darwin !386
//
// openbsd/386 and plan9/386 don't work, not sure why.
// +build !openbsd !386
// +build !plan9 !386
//
// windows doesn't work, because Windows exception handling
// delivers signals based on the current PC, and that current PC
// doesn't go into the Go runtime.
// +build !windows
//
// arm64 gets "illegal instruction" (why is the data executable?)
// and is unable to do the traceback correctly (why?).
// +build !arm64

package main

import (
	"runtime"
	"runtime/debug"
	"unsafe"
)

func main() {
	debug.SetPanicOnFault(true)
	defer func() {
		if err := recover(); err == nil {
			panic("not panicking")
		}
		pc, _, _, _ := runtime.Caller(10)
		f := runtime.FuncForPC(pc)
		if f == nil || f.Name() != "main.f" {
			if f == nil {
				println("no func for ", unsafe.Pointer(pc))
			} else {
				println("found func:", f.Name())
			}
			panic("cannot find main.f on stack")
		}
	}()
	f(20)
}

func f(n int) {
	if n > 0 {
		f(n - 1)
	}
	var f struct {
		x uintptr
	}
	f.x = uintptr(unsafe.Pointer(&f))
	fn := *(*func())(unsafe.Pointer(&f))
	fn()
}
<commit_msg>test: don't run fixedbugs/issue11656.go on netbsd/386<commit_after>// run

// Copyright 2015 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.

// darwin/386 seems to mangle the PC and SP before
// it manages to invoke the signal handler, so this test fails there.
// +build !darwin !386
//
// openbsd/386, netbsd/386, and plan9/386 don't work, not sure why.
// +build !openbsd !386
// +build !netbsd !386
// +build !plan9 !386
//
// windows doesn't work, because Windows exception handling
// delivers signals based on the current PC, and that current PC
// doesn't go into the Go runtime.
// +build !windows
//
// arm64 gets "illegal instruction" (why is the data executable?)
// and is unable to do the traceback correctly (why?).
// +build !arm64

package main

import (
	"runtime"
	"runtime/debug"
	"unsafe"
)

func main() {
	debug.SetPanicOnFault(true)
	defer func() {
		if err := recover(); err == nil {
			panic("not panicking")
		}
		pc, _, _, _ := runtime.Caller(10)
		f := runtime.FuncForPC(pc)
		if f == nil || f.Name() != "main.f" {
			if f == nil {
				println("no func for ", unsafe.Pointer(pc))
			} else {
				println("found func:", f.Name())
			}
			panic("cannot find main.f on stack")
		}
	}()
	f(20)
}

func f(n int) {
	if n > 0 {
		f(n - 1)
	}
	var f struct {
		x uintptr
	}
	f.x = uintptr(unsafe.Pointer(&f))
	fn := *(*func())(unsafe.Pointer(&f))
	fn()
}
<|endoftext|>


<commit_before>// Package chrome provides Chrome-specific options for WebDriver.
package chrome

import (
	"bufio"
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/base64"
	"encoding/binary"
	"io"
	"os"

	"github.com/tebeka/selenium/internal/zip"
)

// DeprecatedCapabilitiesKey and CapabilitiesKey are the key in the top-level
// Capabilities map under which ChromeDriver expects the Chrome-specific
// options to be set.
const DeprecatedCapabilitiesKey = "chromeOptions"
const CapabilitiesKey = "goog:chromeOptions"

// Capabilities defines the Chrome-specific desired capabilities when using
// ChromeDriver. An instance of this struct can be stored in the Capabilities
// map with a key of CapabilitiesKey ("goog:chromeOptions").  See
// https://sites.google.com/a/chromium.org/chromedriver/capabilities
type Capabilities struct {
	// Path is the file path to the Chrome binary to use.
	Path string `json:"binary,omitempty"`
	// Args are the command-line arguments to pass to the Chrome binary, in
	// addition to the ChromeDriver-supplied ones.
	Args []string `json:"args,omitempty"`
	// ExcludeSwitches are the command line flags that should be removed from
	// the ChromeDriver-supplied default flags. The strings included here should
	// not include a preceding '--'.
	ExcludeSwitches []string `json:"excludeSwitches,omitempty"`
	// Extensions are the list of extentions to install at startup. The
	// elements of this list should be the base-64, padded contents of a Chrome
	// extension file (.crx). Use the AddExtension method to add a local file.
	Extensions []string `json:"extensions,omitempty"`
	// LocalState are key/value pairs that are applied to the Local State file
	// in the user data folder.
	LocalState map[string]interface{} `json:"localState,omitempty"`
	// Prefs are the key/value pairs that are applied to the preferences of the
	// user profile in use.
	Prefs map[string]interface{} `json:"prefs,omitempty"`
	// Detatch, if true, will cause the browser to not be killed when
	// ChromeDriver quits if the session was not terminated.
	Detach *bool `json:"detach,omitempty"`
	// DebuggerAddr is the TCP/IP address of a Chrome debugger server to connect
	// to.
	DebuggerAddr string `json:"debuggerAddress,omitempty"`
	// MinidumpPath specifies the directory in which to store Chrome minidumps.
	// (This is only available on Linux).
	MinidumpPath string `json:"minidumpPath,omitempty"`
	// MobileEmulation provides options for mobile emulation.
	MobileEmulation *MobileEmulation `json:"mobileEmulation,omitempty"`
	// PerfLoggingPrefs specifies options for performance logging.
	PerfLoggingPrefs *PerfLoggingPreferences `json:"perfLoggingPrefs,omitempty"`
	// WindowTypes is a list of window types that will appear in the list of
	// window handles. For access to <webview> elements, include "webview" in
	// this list.
	WindowTypes []string `json:"windowTypes,omitempty"`
	// Android Chrome WebDriver path "com.android.chrome"
	AndroidPackage string `json:"androidPackage,omitempty"`
	// Must be true; not useful to consume.
	W3C bool `json:"w3c"`
}

// TODO(minusnine): https://bugs.chromium.org/p/chromedriver/issues/detail?id=1625
// mentions "experimental options". Implement that.

// MobileEmulation provides options for mobile emulation. Only
// DeviceName or both of DeviceMetrics and UserAgent may be set at once.
type MobileEmulation struct {
	// DeviceName is the name of the device to emulate, e.g. "Google Nexus 5".
	// It should not be set if DeviceMetrics and UserAgent are set.
	DeviceName string `json:"deviceName,omitempty"`
	// DeviceMetrics provides specifications of an device to emulate. It should
	// not be set if DeviceName is set.
	DeviceMetrics *DeviceMetrics `json:"deviceMetrics,omitempty"`
	// UserAgent specifies the user agent string to send to the remote web
	// server.
	UserAgent string `json:"userAgent,omitempty"`
}

// DeviceMetrics specifies device attributes for emulation.
type DeviceMetrics struct {
	// Width is the width of the screen.
	Width uint `json:"width"`
	// Height is the height of the screen.
	Height uint `json:"height"`
	// PixelRatio is the pixel ratio of the screen.
	PixelRatio float64 `json:"pixelRatio"`
	// Touch indicates whether to emulate touch events. The default is true, if
	// unset.
	Touch *bool `json:"touch,omitempty"`
}

// PerfLoggingPreferences specifies configuration options for performance
// logging.
type PerfLoggingPreferences struct {
	// EnableNetwork specifies whether of not to collect events from the Network
	// domain. The default is true.
	EnableNetwork *bool `json:"enableNetwork,omitempty"`
	// EnablePage specifies whether or not to collect events from the Page
	// domain. The default is true.
	EnablePage *bool `json:"enablePage,omitempty"`
	// EnableTimeline specifies whether or not to collect events from the
	// Timeline domain. When tracing is enabled, Timeline domain is implicitly
	// disabled, unless enableTimeline is explicitly set to true.
	EnableTimeline *bool `json:"enableTimeline,omitempty"`
	// TracingCategories is a comma-separated string of Chrome tracing categories
	// for which trace events should be collected. An unspecified or empty string
	// disables tracing.
	TracingCategories string `json:"tracingCategories,omitempty"`
	// BufferUsageReportingIntervalMillis is the requested number of milliseconds
	// between DevTools trace buffer usage events. For example, if 1000, then
	// once per second, DevTools will report how full the trace buffer is. If a
	// report indicates the buffer usage is 100%, a warning will be issued.
	BufferUsageReportingIntervalMillis uint `json:"bufferUsageReportingInterval,omitempty"`
}

// AddExtension adds an extension for the browser to load at startup. The path
// parameter should be a path to an extension file (which typically has a
// `.crx` file extension. Note that the contents of the file will be loaded
// into memory, as required by the protocol.
func (c *Capabilities) AddExtension(path string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()
	return c.addExtension(f)
}

// addExtension reads a Chrome extension's data from r, base64-encodes it, and
// attaches it to the Capabilities instance.
func (c *Capabilities) addExtension(r io.Reader) error {
	var buf bytes.Buffer
	encoder := base64.NewEncoder(base64.StdEncoding, &buf)
	if _, err := io.Copy(encoder, bufio.NewReader(r)); err != nil {
		return err
	}
	encoder.Close()
	c.Extensions = append(c.Extensions, buf.String())
	return nil
}

// AddUnpackedExtension creates a packaged Chrome extension with the files
// below the provided directory path and causes the browser to load that
// extension at startup.
func (c *Capabilities) AddUnpackedExtension(basePath string) error {
	buf, _, err := NewExtension(basePath)
	if err != nil {
		return err
	}
	return c.addExtension(bytes.NewBuffer(buf))
}

// NewExtension creates the payload of a Chrome extension file which is signed
// using the returned private key.
func NewExtension(basePath string) ([]byte, *rsa.PrivateKey, error) {
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}
	data, err := NewExtensionWithKey(basePath, key)
	if err != nil {
		return nil, nil, err
	}
	return data, key, nil
}

// NewExtensionWithKey creates the payload of a Chrome extension file which is
// signed by the provided private key.
func NewExtensionWithKey(basePath string, key *rsa.PrivateKey) ([]byte, error) {
	zip, err := zip.New(basePath)
	if err != nil {
		return nil, err
	}

	h := sha1.New()
	if _, err := io.Copy(h, bytes.NewReader(zip.Bytes())); err != nil {
		return nil, err
	}
	hashed := h.Sum(nil)

	signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA1, hashed[:])
	if err != nil {
		return nil, err
	}

	pubKey, err := x509.MarshalPKIXPublicKey(key.Public())
	if err != nil {
		return nil, err
	}

	// This format is documented at https://developer.chrome.com/extensions/crx .
	buf := new(bytes.Buffer)
	if _, err := buf.Write([]byte("Cr24")); err != nil { // Magic number.
		return nil, err
	}

	// Version.
	if err := binary.Write(buf, binary.LittleEndian, uint32(2)); err != nil {
		return nil, err
	}

	// Public key length.
	if err := binary.Write(buf, binary.LittleEndian, uint32(len(pubKey))); err != nil {
		return nil, err
	}
	// Signature length.
	if err := binary.Write(buf, binary.LittleEndian, uint32(len(signature))); err != nil {
		return nil, err
	}

	// Public key payload.
	if err := binary.Write(buf, binary.LittleEndian, pubKey); err != nil {
		return nil, err
	}

	// Signature payload.
	if err := binary.Write(buf, binary.LittleEndian, signature); err != nil {
		return nil, err
	}

	// Zipped extension directory payload.
	if err := binary.Write(buf, binary.LittleEndian, zip.Bytes()); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}
<commit_msg>fixed json tag for tracing categories in chrome performance logging preferences<commit_after>// Package chrome provides Chrome-specific options for WebDriver.
package chrome

import (
	"bufio"
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/base64"
	"encoding/binary"
	"io"
	"os"

	"github.com/tebeka/selenium/internal/zip"
)

// DeprecatedCapabilitiesKey and CapabilitiesKey are the key in the top-level
// Capabilities map under which ChromeDriver expects the Chrome-specific
// options to be set.
const DeprecatedCapabilitiesKey = "chromeOptions"
const CapabilitiesKey = "goog:chromeOptions"

// Capabilities defines the Chrome-specific desired capabilities when using
// ChromeDriver. An instance of this struct can be stored in the Capabilities
// map with a key of CapabilitiesKey ("goog:chromeOptions").  See
// https://sites.google.com/a/chromium.org/chromedriver/capabilities
type Capabilities struct {
	// Path is the file path to the Chrome binary to use.
	Path string `json:"binary,omitempty"`
	// Args are the command-line arguments to pass to the Chrome binary, in
	// addition to the ChromeDriver-supplied ones.
	Args []string `json:"args,omitempty"`
	// ExcludeSwitches are the command line flags that should be removed from
	// the ChromeDriver-supplied default flags. The strings included here should
	// not include a preceding '--'.
	ExcludeSwitches []string `json:"excludeSwitches,omitempty"`
	// Extensions are the list of extentions to install at startup. The
	// elements of this list should be the base-64, padded contents of a Chrome
	// extension file (.crx). Use the AddExtension method to add a local file.
	Extensions []string `json:"extensions,omitempty"`
	// LocalState are key/value pairs that are applied to the Local State file
	// in the user data folder.
	LocalState map[string]interface{} `json:"localState,omitempty"`
	// Prefs are the key/value pairs that are applied to the preferences of the
	// user profile in use.
	Prefs map[string]interface{} `json:"prefs,omitempty"`
	// Detatch, if true, will cause the browser to not be killed when
	// ChromeDriver quits if the session was not terminated.
	Detach *bool `json:"detach,omitempty"`
	// DebuggerAddr is the TCP/IP address of a Chrome debugger server to connect
	// to.
	DebuggerAddr string `json:"debuggerAddress,omitempty"`
	// MinidumpPath specifies the directory in which to store Chrome minidumps.
	// (This is only available on Linux).
	MinidumpPath string `json:"minidumpPath,omitempty"`
	// MobileEmulation provides options for mobile emulation.
	MobileEmulation *MobileEmulation `json:"mobileEmulation,omitempty"`
	// PerfLoggingPrefs specifies options for performance logging.
	PerfLoggingPrefs *PerfLoggingPreferences `json:"perfLoggingPrefs,omitempty"`
	// WindowTypes is a list of window types that will appear in the list of
	// window handles. For access to <webview> elements, include "webview" in
	// this list.
	WindowTypes []string `json:"windowTypes,omitempty"`
	// Android Chrome WebDriver path "com.android.chrome"
	AndroidPackage string `json:"androidPackage,omitempty"`
	// Must be true; not useful to consume.
	W3C bool `json:"w3c"`
}

// TODO(minusnine): https://bugs.chromium.org/p/chromedriver/issues/detail?id=1625
// mentions "experimental options". Implement that.

// MobileEmulation provides options for mobile emulation. Only
// DeviceName or both of DeviceMetrics and UserAgent may be set at once.
type MobileEmulation struct {
	// DeviceName is the name of the device to emulate, e.g. "Google Nexus 5".
	// It should not be set if DeviceMetrics and UserAgent are set.
	DeviceName string `json:"deviceName,omitempty"`
	// DeviceMetrics provides specifications of an device to emulate. It should
	// not be set if DeviceName is set.
	DeviceMetrics *DeviceMetrics `json:"deviceMetrics,omitempty"`
	// UserAgent specifies the user agent string to send to the remote web
	// server.
	UserAgent string `json:"userAgent,omitempty"`
}

// DeviceMetrics specifies device attributes for emulation.
type DeviceMetrics struct {
	// Width is the width of the screen.
	Width uint `json:"width"`
	// Height is the height of the screen.
	Height uint `json:"height"`
	// PixelRatio is the pixel ratio of the screen.
	PixelRatio float64 `json:"pixelRatio"`
	// Touch indicates whether to emulate touch events. The default is true, if
	// unset.
	Touch *bool `json:"touch,omitempty"`
}

// PerfLoggingPreferences specifies configuration options for performance
// logging.
type PerfLoggingPreferences struct {
	// EnableNetwork specifies whether of not to collect events from the Network
	// domain. The default is true.
	EnableNetwork *bool `json:"enableNetwork,omitempty"`
	// EnablePage specifies whether or not to collect events from the Page
	// domain. The default is true.
	EnablePage *bool `json:"enablePage,omitempty"`
	// EnableTimeline specifies whether or not to collect events from the
	// Timeline domain. When tracing is enabled, Timeline domain is implicitly
	// disabled, unless enableTimeline is explicitly set to true.
	EnableTimeline *bool `json:"enableTimeline,omitempty"`
	// TraceCategories is a comma-separated string of Chrome tracing categories
	// for which trace events should be collected. An unspecified or empty string
	// disables tracing.
	TraceCategories string `json:"traceCategories,omitempty"`
	// BufferUsageReportingIntervalMillis is the requested number of milliseconds
	// between DevTools trace buffer usage events. For example, if 1000, then
	// once per second, DevTools will report how full the trace buffer is. If a
	// report indicates the buffer usage is 100%, a warning will be issued.
	BufferUsageReportingIntervalMillis uint `json:"bufferUsageReportingInterval,omitempty"`
}

// AddExtension adds an extension for the browser to load at startup. The path
// parameter should be a path to an extension file (which typically has a
// `.crx` file extension. Note that the contents of the file will be loaded
// into memory, as required by the protocol.
func (c *Capabilities) AddExtension(path string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()
	return c.addExtension(f)
}

// addExtension reads a Chrome extension's data from r, base64-encodes it, and
// attaches it to the Capabilities instance.
func (c *Capabilities) addExtension(r io.Reader) error {
	var buf bytes.Buffer
	encoder := base64.NewEncoder(base64.StdEncoding, &buf)
	if _, err := io.Copy(encoder, bufio.NewReader(r)); err != nil {
		return err
	}
	encoder.Close()
	c.Extensions = append(c.Extensions, buf.String())
	return nil
}

// AddUnpackedExtension creates a packaged Chrome extension with the files
// below the provided directory path and causes the browser to load that
// extension at startup.
func (c *Capabilities) AddUnpackedExtension(basePath string) error {
	buf, _, err := NewExtension(basePath)
	if err != nil {
		return err
	}
	return c.addExtension(bytes.NewBuffer(buf))
}

// NewExtension creates the payload of a Chrome extension file which is signed
// using the returned private key.
func NewExtension(basePath string) ([]byte, *rsa.PrivateKey, error) {
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}
	data, err := NewExtensionWithKey(basePath, key)
	if err != nil {
		return nil, nil, err
	}
	return data, key, nil
}

// NewExtensionWithKey creates the payload of a Chrome extension file which is
// signed by the provided private key.
func NewExtensionWithKey(basePath string, key *rsa.PrivateKey) ([]byte, error) {
	zip, err := zip.New(basePath)
	if err != nil {
		return nil, err
	}

	h := sha1.New()
	if _, err := io.Copy(h, bytes.NewReader(zip.Bytes())); err != nil {
		return nil, err
	}
	hashed := h.Sum(nil)

	signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA1, hashed[:])
	if err != nil {
		return nil, err
	}

	pubKey, err := x509.MarshalPKIXPublicKey(key.Public())
	if err != nil {
		return nil, err
	}

	// This format is documented at https://developer.chrome.com/extensions/crx .
	buf := new(bytes.Buffer)
	if _, err := buf.Write([]byte("Cr24")); err != nil { // Magic number.
		return nil, err
	}

	// Version.
	if err := binary.Write(buf, binary.LittleEndian, uint32(2)); err != nil {
		return nil, err
	}

	// Public key length.
	if err := binary.Write(buf, binary.LittleEndian, uint32(len(pubKey))); err != nil {
		return nil, err
	}
	// Signature length.
	if err := binary.Write(buf, binary.LittleEndian, uint32(len(signature))); err != nil {
		return nil, err
	}

	// Public key payload.
	if err := binary.Write(buf, binary.LittleEndian, pubKey); err != nil {
		return nil, err
	}

	// Signature payload.
	if err := binary.Write(buf, binary.LittleEndian, signature); err != nil {
		return nil, err
	}

	// Zipped extension directory payload.
	if err := binary.Write(buf, binary.LittleEndian, zip.Bytes()); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}
<|endoftext|>