<commit_before>package gol

import (
	"github.com/ianremmler/gochipmunk/chipmunk"
	"github.com/ianremmler/gordian"

	"fmt"
	"math"
	"math/rand"
	"sync"
	"time"
)

const (
	simTime       = time.Second / 60
	updateTime    = time.Second / 24
	pauseTime     = time.Second
	headStartTime = time.Second
	maxScore      = 10
	fieldWidth    = 1000
	fieldHeight   = 500
	edgeSize      = 16
	goalSize      = 200
	playerRadius  = 10
	playerMass    = 1
	ballRadius    = 10
	ballMass      = 0.1
)

const (
	normLayer = 1 << iota
	goalLayer
)

type player struct {
	id          gordian.ClientId
	team        int
	body        chipmunk.Body
	shape       chipmunk.Shape
	cursorBody  chipmunk.Body
	cursorJoint chipmunk.Constraint
}

type ball struct {
	body  chipmunk.Body
	shape chipmunk.Shape
}

type Player struct {
	Pos  chipmunk.Vect
	Team int
}

type Ball struct {
	Pos chipmunk.Vect
}

type configMsg struct {
	Id           string
	FieldWidth   float64
	FieldHeight  float64
	GoalSize     float64
	PlayerRadius float64
	BallRadius   float64
}

type stateMsg struct {
	Players map[string]Player
	Ball    Ball
	Score   []int
}

type Gol struct {
	players     map[gordian.ClientId]*player
	ball        ball
	score       []int
	pauseTicks  []int
	simTimer    <-chan time.Time
	updateTimer <-chan time.Time
	curId       int
	space       *chipmunk.Space
	mu          sync.Mutex
	*gordian.Gordian
}

func NewGol() *Gol {
	g := &Gol{
		players:     map[gordian.ClientId]*player{},
		score:       []int{0, 0},
		pauseTicks:  []int{0, 0},
		simTimer:    time.Tick(simTime),
		updateTimer: time.Tick(updateTime),
		Gordian:     gordian.New(24),
	}
	g.setup()
	return g
}

func (g *Gol) setup() {
	g.space = chipmunk.SpaceNew()
	g.space.SetDamping(0.1)
	hfw, hfh, hgs := 0.5*fieldWidth, 0.5*fieldHeight, 0.5*goalSize
	sidePts := []chipmunk.Vect{{-hfw, hgs}, {-hfw, hfh}, {hfw, hfh}, {hfw, hgs}}
	numSideSegs := len(sidePts) - 1
	for i := 0; i < 2; i++ {
		sign := 2*float64(i) - 1
		for j := 0; j < numSideSegs; j++ {
			p0, p1 := sidePts[j], sidePts[j+1]
			p0.Y *= sign
			p1.Y *= sign
			fieldSeg := chipmunk.SegmentShapeNew(g.space.StaticBody(), p0, p1, 0.5*edgeSize)
			fieldSeg.SetLayers(normLayer)
			fieldSeg.SetElasticity(1.0)
			fieldSeg.SetFriction(1.0)
			g.space.AddShape(fieldSeg)
		}
		p0, p1 := chipmunk.Vect{sign * hfw, -hgs}, chipmunk.Vect{sign * hfw, hgs}
		goal := chipmunk.SegmentShapeNew(g.space.StaticBody(), p0, p1, 0.5*edgeSize)
		goal.SetLayers(goalLayer)
		goal.SetElasticity(1.0)
		goal.SetFriction(1.0)
		g.space.AddShape(goal)
	}
	moment := chipmunk.MomentForCircle(ballMass, 0, ballRadius, chipmunk.Origin())
	g.ball.body = chipmunk.BodyNew(ballMass, moment)
	g.space.AddBody(g.ball.body)
	g.ball.shape = chipmunk.CircleShapeNew(g.ball.body, ballRadius, chipmunk.Origin())
	g.ball.shape.SetLayers(normLayer)
	g.ball.shape.SetElasticity(0.9)
	g.ball.shape.SetFriction(0.1)
	g.space.AddShape(g.ball.shape)
	g.space.SetUserData(g)
}

func (g *Gol) Run() {
	go g.run()
	go g.sim()
	g.Gordian.Run()
}

func (g *Gol) run() {
	for {
		select {
		case client := <-g.Control:
			g.clientCtrl(client)
		case msg := <-g.InBox:
			g.handleMessage(&msg)
		case <-g.updateTimer:
			g.update()
		}
	}
}

func (g *Gol) sim() {
	for {
		<-g.simTimer

		g.mu.Lock()

		g.space.Step(float64(simTime) / float64(time.Second))

		// enable control if pause is ending
		for _, player := range g.players {
			if g.pauseTicks[player.team] == 1 {
				g.space.AddConstraint(player.cursorJoint)
			}
		}
		// update pause countdown
		for i := range g.pauseTicks {
			if g.pauseTicks[i] > 0 {
				g.pauseTicks[i]--
			}
		}
		// check for goals
		ballX := g.ball.body.Position().X
		if math.Abs(ballX) > fieldWidth/2 { // GOL!
			team := 0
			if ballX < 0 {
				team = 1
			}
			g.score[team]++
			if g.score[0] >= maxScore || g.score[1] >= maxScore {
				g.score[0], g.score[1] = 0, 0
			}
			g.kickoff(team)
		}

		g.mu.Unlock()
	}
}

func (g *Gol) kickoff(team int) {
	otherTeam := 1 - team

	g.ball.body.SetPosition(chipmunk.Vect{})
	g.ball.body.SetVelocity(chipmunk.Vect{})
	for _, player := range g.players {
		player.body.SetPosition(playerPos(player.team))
		player.body.SetVelocity(chipmunk.Vect{})
		if g.pauseTicks[player.team] == 0 {
			// disable control for a bit
			g.space.RemoveConstraint(player.cursorJoint)
		}
	}
	// give the team that was scored on a little head start for "kickoff"
	g.pauseTicks[team] = int((pauseTime + headStartTime) / simTime)
	g.pauseTicks[otherTeam] = int(pauseTime / simTime)
}

func (g *Gol) clientCtrl(client *gordian.Client) {
	switch client.Ctrl {
	case gordian.Connect:
		g.connect(client)
	case gordian.Close:
		g.close(client)
	}
}

func (g *Gol) nextTeam() int {
	t0Size := 0
	for _, player := range g.players {
		if player.team == 0 {
			t0Size++
		}
	}
	diff := len(g.players) - 2*t0Size
	switch {
	case diff > 0:
		return 0
	case diff < 0:
		return 1
	default:
		return rand.Int() % 2
	}
}

func (g *Gol) connect(client *gordian.Client) {
	g.curId++

	client.Id = g.curId
	client.Ctrl = gordian.Register
	g.Control <- client
	client = <-g.Control
	if client.Ctrl != gordian.Establish {
		return
	}

	g.mu.Lock()

	player := &player{id: client.Id}
	moment := chipmunk.MomentForCircle(playerMass, 0, playerRadius, chipmunk.Origin())
	player.body = chipmunk.BodyNew(playerMass, moment)
	player.body.SetUserData(client.Id)
	g.space.AddBody(player.body)
	player.shape = chipmunk.CircleShapeNew(player.body, playerRadius, chipmunk.Origin())
	player.shape.SetLayers(normLayer | goalLayer)
	player.shape.SetElasticity(0.9)
	player.shape.SetFriction(0.1)
	g.space.AddShape(player.shape)

	player.cursorBody = chipmunk.BodyNew(math.Inf(0), math.Inf(0))
	player.cursorJoint = chipmunk.PivotJointNew2(player.cursorBody, player.body,
		chipmunk.Vect{}, chipmunk.Vect{})
	player.cursorJoint.SetMaxForce(1000.0)
	g.space.AddConstraint(player.cursorJoint)
	player.team = g.nextTeam()
	player.body.SetPosition(playerPos(player.team))

	g.players[player.id] = player

	g.mu.Unlock()

	data := configMsg{
		FieldWidth:   fieldWidth,
		FieldHeight:  fieldHeight,
		GoalSize:     goalSize,
		PlayerRadius: playerRadius,
		BallRadius:   ballRadius,
		Id:           fmt.Sprintf("%d", player.id),
	}
	msg := gordian.Message{
		To:   player.id,
		Type: "config",
		Data: data,
	}
	g.OutBox <- msg
}

func (g *Gol) close(client *gordian.Client) {
	g.mu.Lock()
	defer g.mu.Unlock()

	player, ok := g.players[client.Id]
	if !ok {
		return
	}
	g.space.RemoveConstraint(player.cursorJoint)
	player.cursorJoint.Free()
	g.space.RemoveBody(player.body)
	g.space.RemoveShape(player.shape)
	player.body.Free()
	player.shape.Free()
	player.cursorBody.Free()
	delete(g.players, client.Id)
}

func (g *Gol) handleMessage(msg *gordian.Message) {
	g.mu.Lock()
	defer g.mu.Unlock()

	id := msg.From
	player, ok := g.players[id]
	if !ok {
		return
	}
	switch msg.Type {
	case "player":
		state := &Player{}
		err := msg.Unmarshal(state)
		if err != nil {
			return
		}
		player.cursorBody.SetPosition(state.Pos)
	}
}

func (g *Gol) update() {
	g.mu.Lock()

	state := stateMsg{
		Players: map[string]Player{},
		Ball:    Ball{g.ball.body.Position()},
		Score:   g.score,
	}
	for i, player := range g.players {
		state.Players[fmt.Sprintf("%d", i)] = Player{
			Pos:  player.body.Position(),
			Team: player.team,
		}
	}

	g.mu.Unlock()

	msg := gordian.Message{
		Type: "state",
		Data: state,
	}
	for id := range g.players {
		msg.To = id
		g.OutBox <- msg
	}
}

func playerPos(team int) chipmunk.Vect {
	fw, fh := 0.5*fieldWidth - playerRadius, 0.5*fieldHeight - playerRadius
	pos := chipmunk.Vect{rand.Float64() * fw, rand.Float64()*(2*fh) - fh}
	if team == 0 {
		pos.X *= -1
	}
	gs := 0.25 * fieldHeight + playerRadius
	len := pos.Length()
	if len < gs {
		pos = pos.Div(len).Mul(gs)
	}
	return pos
}
<commit_msg>Updated chipmunk import location.<commit_after>package gol

import (
	"github.com/ianremmler/chipmunk"
	"github.com/ianremmler/gordian"

	"fmt"
	"math"
	"math/rand"
	"sync"
	"time"
)

const (
	simTime       = time.Second / 60
	updateTime    = time.Second / 24
	pauseTime     = time.Second
	headStartTime = time.Second
	maxScore      = 10
	fieldWidth    = 1000
	fieldHeight   = 500
	edgeSize      = 16
	goalSize      = 200
	playerRadius  = 10
	playerMass    = 1
	ballRadius    = 10
	ballMass      = 0.1
)

const (
	normLayer = 1 << iota
	goalLayer
)

type player struct {
	id          gordian.ClientId
	team        int
	body        chipmunk.Body
	shape       chipmunk.Shape
	cursorBody  chipmunk.Body
	cursorJoint chipmunk.Constraint
}

type ball struct {
	body  chipmunk.Body
	shape chipmunk.Shape
}

type Player struct {
	Pos  chipmunk.Vect
	Team int
}

type Ball struct {
	Pos chipmunk.Vect
}

type configMsg struct {
	Id           string
	FieldWidth   float64
	FieldHeight  float64
	GoalSize     float64
	PlayerRadius float64
	BallRadius   float64
}

type stateMsg struct {
	Players map[string]Player
	Ball    Ball
	Score   []int
}

type Gol struct {
	players     map[gordian.ClientId]*player
	ball        ball
	score       []int
	pauseTicks  []int
	simTimer    <-chan time.Time
	updateTimer <-chan time.Time
	curId       int
	space       *chipmunk.Space
	mu          sync.Mutex
	*gordian.Gordian
}

func NewGol() *Gol {
	g := &Gol{
		players:     map[gordian.ClientId]*player{},
		score:       []int{0, 0},
		pauseTicks:  []int{0, 0},
		simTimer:    time.Tick(simTime),
		updateTimer: time.Tick(updateTime),
		Gordian:     gordian.New(24),
	}
	g.setup()
	return g
}

func (g *Gol) setup() {
	g.space = chipmunk.SpaceNew()
	g.space.SetDamping(0.1)
	hfw, hfh, hgs := 0.5*fieldWidth, 0.5*fieldHeight, 0.5*goalSize
	sidePts := []chipmunk.Vect{{-hfw, hgs}, {-hfw, hfh}, {hfw, hfh}, {hfw, hgs}}
	numSideSegs := len(sidePts) - 1
	for i := 0; i < 2; i++ {
		sign := 2*float64(i) - 1
		for j := 0; j < numSideSegs; j++ {
			p0, p1 := sidePts[j], sidePts[j+1]
			p0.Y *= sign
			p1.Y *= sign
			fieldSeg := chipmunk.SegmentShapeNew(g.space.StaticBody(), p0, p1, 0.5*edgeSize)
			fieldSeg.SetLayers(normLayer)
			fieldSeg.SetElasticity(1.0)
			fieldSeg.SetFriction(1.0)
			g.space.AddShape(fieldSeg)
		}
		p0, p1 := chipmunk.Vect{sign * hfw, -hgs}, chipmunk.Vect{sign * hfw, hgs}
		goal := chipmunk.SegmentShapeNew(g.space.StaticBody(), p0, p1, 0.5*edgeSize)
		goal.SetLayers(goalLayer)
		goal.SetElasticity(1.0)
		goal.SetFriction(1.0)
		g.space.AddShape(goal)
	}
	moment := chipmunk.MomentForCircle(ballMass, 0, ballRadius, chipmunk.Origin())
	g.ball.body = chipmunk.BodyNew(ballMass, moment)
	g.space.AddBody(g.ball.body)
	g.ball.shape = chipmunk.CircleShapeNew(g.ball.body, ballRadius, chipmunk.Origin())
	g.ball.shape.SetLayers(normLayer)
	g.ball.shape.SetElasticity(0.9)
	g.ball.shape.SetFriction(0.1)
	g.space.AddShape(g.ball.shape)
	g.space.SetUserData(g)
}

func (g *Gol) Run() {
	go g.run()
	go g.sim()
	g.Gordian.Run()
}

func (g *Gol) run() {
	for {
		select {
		case client := <-g.Control:
			g.clientCtrl(client)
		case msg := <-g.InBox:
			g.handleMessage(&msg)
		case <-g.updateTimer:
			g.update()
		}
	}
}

func (g *Gol) sim() {
	for {
		<-g.simTimer

		g.mu.Lock()

		g.space.Step(float64(simTime) / float64(time.Second))

		// enable control if pause is ending
		for _, player := range g.players {
			if g.pauseTicks[player.team] == 1 {
				g.space.AddConstraint(player.cursorJoint)
			}
		}
		// update pause countdown
		for i := range g.pauseTicks {
			if g.pauseTicks[i] > 0 {
				g.pauseTicks[i]--
			}
		}
		// check for goals
		ballX := g.ball.body.Position().X
		if math.Abs(ballX) > fieldWidth/2 { // GOL!
			team := 0
			if ballX < 0 {
				team = 1
			}
			g.score[team]++
			if g.score[0] >= maxScore || g.score[1] >= maxScore {
				g.score[0], g.score[1] = 0, 0
			}
			g.kickoff(team)
		}

		g.mu.Unlock()
	}
}

func (g *Gol) kickoff(team int) {
	otherTeam := 1 - team

	g.ball.body.SetPosition(chipmunk.Vect{})
	g.ball.body.SetVelocity(chipmunk.Vect{})
	for _, player := range g.players {
		player.body.SetPosition(playerPos(player.team))
		player.body.SetVelocity(chipmunk.Vect{})
		if g.pauseTicks[player.team] == 0 {
			// disable control for a bit
			g.space.RemoveConstraint(player.cursorJoint)
		}
	}
	// give the team that was scored on a little head start for "kickoff"
	g.pauseTicks[team] = int((pauseTime + headStartTime) / simTime)
	g.pauseTicks[otherTeam] = int(pauseTime / simTime)
}

func (g *Gol) clientCtrl(client *gordian.Client) {
	switch client.Ctrl {
	case gordian.Connect:
		g.connect(client)
	case gordian.Close:
		g.close(client)
	}
}

func (g *Gol) nextTeam() int {
	t0Size := 0
	for _, player := range g.players {
		if player.team == 0 {
			t0Size++
		}
	}
	diff := len(g.players) - 2*t0Size
	switch {
	case diff > 0:
		return 0
	case diff < 0:
		return 1
	default:
		return rand.Int() % 2
	}
}

func (g *Gol) connect(client *gordian.Client) {
	g.curId++

	client.Id = g.curId
	client.Ctrl = gordian.Register
	g.Control <- client
	client = <-g.Control
	if client.Ctrl != gordian.Establish {
		return
	}

	g.mu.Lock()

	player := &player{id: client.Id}
	moment := chipmunk.MomentForCircle(playerMass, 0, playerRadius, chipmunk.Origin())
	player.body = chipmunk.BodyNew(playerMass, moment)
	player.body.SetUserData(client.Id)
	g.space.AddBody(player.body)
	player.shape = chipmunk.CircleShapeNew(player.body, playerRadius, chipmunk.Origin())
	player.shape.SetLayers(normLayer | goalLayer)
	player.shape.SetElasticity(0.9)
	player.shape.SetFriction(0.1)
	g.space.AddShape(player.shape)

	player.cursorBody = chipmunk.BodyNew(math.Inf(0), math.Inf(0))
	player.cursorJoint = chipmunk.PivotJointNew2(player.cursorBody, player.body,
		chipmunk.Vect{}, chipmunk.Vect{})
	player.cursorJoint.SetMaxForce(1000.0)
	g.space.AddConstraint(player.cursorJoint)
	player.team = g.nextTeam()
	player.body.SetPosition(playerPos(player.team))

	g.players[player.id] = player

	g.mu.Unlock()

	data := configMsg{
		FieldWidth:   fieldWidth,
		FieldHeight:  fieldHeight,
		GoalSize:     goalSize,
		PlayerRadius: playerRadius,
		BallRadius:   ballRadius,
		Id:           fmt.Sprintf("%d", player.id),
	}
	msg := gordian.Message{
		To:   player.id,
		Type: "config",
		Data: data,
	}
	g.OutBox <- msg
}

func (g *Gol) close(client *gordian.Client) {
	g.mu.Lock()
	defer g.mu.Unlock()

	player, ok := g.players[client.Id]
	if !ok {
		return
	}
	g.space.RemoveConstraint(player.cursorJoint)
	player.cursorJoint.Free()
	g.space.RemoveBody(player.body)
	g.space.RemoveShape(player.shape)
	player.body.Free()
	player.shape.Free()
	player.cursorBody.Free()
	delete(g.players, client.Id)
}

func (g *Gol) handleMessage(msg *gordian.Message) {
	g.mu.Lock()
	defer g.mu.Unlock()

	id := msg.From
	player, ok := g.players[id]
	if !ok {
		return
	}
	switch msg.Type {
	case "player":
		state := &Player{}
		err := msg.Unmarshal(state)
		if err != nil {
			return
		}
		player.cursorBody.SetPosition(state.Pos)
	}
}

func (g *Gol) update() {
	g.mu.Lock()

	state := stateMsg{
		Players: map[string]Player{},
		Ball:    Ball{g.ball.body.Position()},
		Score:   g.score,
	}
	for i, player := range g.players {
		state.Players[fmt.Sprintf("%d", i)] = Player{
			Pos:  player.body.Position(),
			Team: player.team,
		}
	}

	g.mu.Unlock()

	msg := gordian.Message{
		Type: "state",
		Data: state,
	}
	for id := range g.players {
		msg.To = id
		g.OutBox <- msg
	}
}

func playerPos(team int) chipmunk.Vect {
	fw, fh := 0.5*fieldWidth - playerRadius, 0.5*fieldHeight - playerRadius
	pos := chipmunk.Vect{rand.Float64() * fw, rand.Float64()*(2*fh) - fh}
	if team == 0 {
		pos.X *= -1
	}
	gs := 0.25 * fieldHeight + playerRadius
	len := pos.Length()
	if len < gs {
		pos = pos.Div(len).Mul(gs)
	}
	return pos
}
<|endoftext|>


<commit_before>package gop

import (
    "github.com/gorilla/mux"
    "github.com/gorilla/context"
    "github.com/gorilla/schema"
    "github.com/jbert/timber"

    "fmt"
    "net"
    "time"
    "runtime"
    "net/http"
    "strings"
    "log"
	"syscall"
)

// Stuff we include in both App and Req, for convenience
type common struct {
    timber.Logger
    Cfg             *Config
    Stats           StatsdClient
    Decoder         *schema.Decoder
}

// Represents a managed gop app. Returned by init.
// Embeds logging, provides .Cfg for configuration access.
type App struct {
    common

    AppName         string
    ProjectName     string
    GorillaRouter   *mux.Router
    listener        net.Listener
    wantReq         chan *wantReq
    doneReq         chan *Req
    getReqs         chan chan *Req
    startTime       time.Time
    currentReqs     int
    totalReqs       int
    doingGraceful   bool
}

// Used for RPC to get a new request
type wantReq struct {
    r       *http.Request
    reply   chan *Req
}

// Per request struct. has convenience references to functionality
// in the app singleton. Passed into the request handler.
type Req struct {
    common

    id              int
    startTime       time.Time
    app             *App
    r               *http.Request
    RealRemoteIP    string
    IsHTTPS         bool
}

// The function signature your http handlers need.
type HandlerFunc func(g *Req, w http.ResponseWriter, r *http.Request)

// Set up the application. Reads config. Panic if runtime environment is deficient.
func Init(projectName, appName string) *App {
    app := &App{
        common: common{
            Decoder:        schema.NewDecoder(),
        },
        AppName:        appName,
        ProjectName:    projectName,
        GorillaRouter:  mux.NewRouter(),
        wantReq:        make(chan *wantReq),
        doneReq:        make(chan *Req),
        getReqs:        make(chan chan *Req),
        startTime:      time.Now(),
    }

    app.loadAppConfigFile()

    app.setUserAndGroup()

    app.initLogging()

    maxProcs, _ := app.Cfg.GetInt("gop", "maxprocs", 4 * runtime.NumCPU())
    app.Debug("Setting maxprocs to %d\n", maxProcs)
    runtime.GOMAXPROCS(maxProcs)

    app.initStatsd()

    return app
}

// Clean shutdown
func (a *App) Finish() {
    // Start a log flush
    timber.Close()
}

func (a *App) setUserAndGroup() {
    // We do not have logging set up yet. We just panic() on error.

    desiredUserName, _ := a.Cfg.Get("gop", "user", "")
    // Usernames it's ok to run as, in order of preference
    possibleUserNames := []string{desiredUserName, a.AppName, a.ProjectName}

    doneIt := false
    for _, desiredUserName := range possibleUserNames {
        if runAsUserName(desiredUserName) {
            doneIt = true
            break
        }
    }

    if !doneIt {
        panic(fmt.Sprintf("Can't run as any of these users: %v, please set config and/or create user", possibleUserNames))
    }

// Can't log at this stage :-/
//    a.Info("Running as user %s (%d)", desiredUserName, desiredUser.Uid)
}

func (a *App) setProcessGroupForNelly() {
	// Nelly knows our pid and will check that there is always at
	// least one process in the process group with the same id as our pid
	mypid := syscall.Getpid()
	err := syscall.Setpgid(mypid, mypid)
	if err != nil {
        panic(fmt.Sprintf("Failed to setprgp]: %s\n", mypid, mypid, err.Error()))
	}
}

// Hands out 'request' objects
func (a *App) requestMaker() {
    nextReqId := 0
    openReqs := make(map[int] *Req)
    for {
        select {
            case wantReq := <- a.wantReq: {
                realRemoteIP := wantReq.r.RemoteAddr
                isHTTPS := false
                useXF, _:= a.Cfg.GetBool("gop", "use_xf_headers", false)
                if useXF {
                    xff := wantReq.r.Header.Get("X-Forwarded-For")
                    if xff != "" {
                        ips := strings.Split(xff, ",")
                        for i, ip := range ips {
                            ips[i] = strings.TrimSpace(ip)
                        }
                        // The only trustworthy component is the *last* (and only if we
                        // are behind nginx or other proxy which is stripping x-f-f from incoming requests)
                        realRemoteIP = ips[len(ips)-1]
                    }
                    xfp := wantReq.r.Header.Get("X-Forwarded-Proto")
                    isHTTPS = strings.ToLower(xfp) == "https"
                }
                req := Req{
                    common: common{
                        Logger:     a.Logger,
                        Cfg:        a.Cfg,
                        Stats:      a.Stats,
                        Decoder:    a.Decoder,
                    },

                    id:         nextReqId,
                    app:        a,
                    startTime:  time.Now(),
                    r:          wantReq.r,
                    RealRemoteIP: realRemoteIP,
                    IsHTTPS:    isHTTPS,
                }
                openReqs[req.id] = &req
                nextReqId++
                a.Stats.Inc("http_reqs", 1)
                a.Stats.Inc("current_http_reqs", 1)
                a.totalReqs++
                a.currentReqs++
                wantReq.reply <- &req
            }
            case doneReq := <- a.doneReq: {
                _, found := openReqs[doneReq.id]
                if !found {
                    a.Error("BUG! Unknown request id [%d] being retired")
                } else {
                    a.Stats.Dec("current_http_reqs", 1)
                    doneReq.finished()
                    a.currentReqs--
                    delete (openReqs, doneReq.id)
                }
            }
            case reply := <- a.getReqs: {
                go func() {
                    for _, req := range openReqs {
                        reply <- req
                    }
                    close(reply)
                }()
            }
        }
    }
}

// Ask requestMaker for a request
func (a *App) getReq(r *http.Request) *Req {
    reply := make(chan *Req)
    a.wantReq <- &wantReq{r: r, reply: reply}
    return <- reply
}

func (g *Req) finished() {
    context.Clear(g.r)      // Cleanup  gorilla stash

    reqDuration := time.Since(g.startTime)
    slowReqSecs, _ := g.Cfg.GetFloat32("gop", "slow_req_secs", 10)
    if reqDuration.Seconds() > float64(slowReqSecs) {
        g.Error("Slow request [%s] took %s", g.r.URL, reqDuration)
    } else {
        g.Info("Request took %s", reqDuration)
    }

    // Tidy up request finalistion (requestMaker, req.finish() method, app.requestFinished())
    restartReqs, _ := g.Cfg.GetInt("gop", "max_requests", 0)
    if restartReqs > 0 && g.app.totalReqs > restartReqs {
        g.Error("Graceful restart after max_requests: %d", restartReqs)
        g.app.StartGracefulRestart("Max requests reached")
    }

    gcEveryReqs, _ := g.Cfg.GetInt("gop", "gc_requests", 0)
    if gcEveryReqs > 0 && g.app.totalReqs % gcEveryReqs == 0 {
        g.Info("Forcing GC after %d reqs", g.app.totalReqs)
        runtime.GC()
    }
}

func (a *App) initLogging() {

    logPattern, _ := a.Cfg.Get("gop", "log_pattern", "[%D %T] [%L] %S %M")

    // If set, hack all logging to stdout for dev
    forceStdout, _:= a.Cfg.GetBool("gop", "stdout_only_logging", false)
    configLogger := timber.ConfigLogger{
        LogWriter:  new(timber.ConsoleWriter),
        Level:      timber.INFO,
        Formatter:  timber.NewPatFormatter(logPattern),
    }

    if !forceStdout {
        defaultLogDir, _ := a.Cfg.Get("gop", "log_dir", "/var/log")
        defaultLogFname := defaultLogDir + "/" + a.ProjectName + "/" + a.AppName + ".log"
        logFname, _ := a.Cfg.Get("gop", "log_file", defaultLogFname)

        newWriter, err := timber.NewFileWriter(logFname)
        if err != nil {
            panic(fmt.Sprintf("Can't open log file: %s", err))
        }
        configLogger.LogWriter = newWriter
    }

    logLevelStr, _ := a.Cfg.Get("gop", "log_level", "INFO")
    logLevelStr = strings.ToUpper(logLevelStr)
    for logLevel, levelStr := range timber.LongLevelStrings {
        if logLevelStr == levelStr {
            configLogger.Level = timber.Level(logLevel)
            break
        }
    }

    // *Don't* create a NewTImber here. Logs are only flushed on Close() and if we
    // have more than one timber, it's easy to only Close() one of them...
    l := timber.Global
    l.AddLogger(configLogger)
    a.Logger = l

    // Set up the default go logger to go here too, so 3rd party
    // module logging plays nicely
    log.SetFlags(0)
    log.SetOutput(l)
}

func (a *App) watchdog() {
    repeat, _ := a.Cfg.GetInt("gop", "watchdog_secs", 300)
    ticker := time.Tick(time.Second * time.Duration(repeat))

    for {
        sysMemBytesLimit, _ := a.Cfg.GetInt64("gop", "sysmem_bytes_limit", 0)
        allocMemBytesLimit, _ := a.Cfg.GetInt64("gop", "allocmem_bytes_limit", 0)
        numFDsLimit, _ := a.Cfg.GetInt64("gop", "numfds_limit", 0)
        numGorosLimit, _ := a.Cfg.GetInt64("gop", "numgoros_limit", 0)

        sysMemBytes, allocMemBytes := getMemInfo()
        numFDs, err := fdsInUse()
        numGoros := int64(runtime.NumGoroutine())
        if err != nil {
            a.Error("Failed to get number of fds in use: %s", err.Error())
            // Continue without
        }

        a.Info("TICK: sys=%d,alloc=%d,fds=%d,current_req=%d,total_req=%d,goros=%d",
            sysMemBytes,
            allocMemBytes,
            numFDs,
            a.currentReqs,
            a.totalReqs,
            numGoros)
        a.Stats.Gauge("mem.sys", sysMemBytes)
        a.Stats.Gauge("mem.alloc", allocMemBytes)
        a.Stats.Gauge("numfds", numFDs)
        a.Stats.Gauge("numgoro", numGoros)

        if sysMemBytesLimit > 0 && sysMemBytes >= sysMemBytesLimit {
            a.Error("SYS MEM LIMIT REACHED [%d >= %d] - starting graceful restart", sysMemBytes, sysMemBytesLimit)
            a.StartGracefulRestart("Sys Memory limit reached")
        }
        if allocMemBytesLimit > 0 && allocMemBytes >= allocMemBytesLimit {
            a.Error("ALLOC MEM LIMIT REACHED [%d >= %d] - starting graceful restart", allocMemBytes, allocMemBytesLimit)
            a.StartGracefulRestart("Alloc Memory limit reached")
        }
        if numFDsLimit > 0 && numFDs >= numFDsLimit {
            a.Error("NUM FDS LIMIT REACHED [%d >= %d] - starting graceful restart", numFDs, numFDsLimit)
            a.StartGracefulRestart("Number of fds limit reached")
        }
        if numGorosLimit > 0 && numGoros >= numGorosLimit {
            a.Error("NUM GOROS LIMIT REACHED [%d >= %d] - starting graceful restart", numGoros, numGorosLimit)
            a.StartGracefulRestart("Number of goros limit reached")
        }

        restartAfterSecs, _ := a.Cfg.GetFloat32("gop", "restart_after_secs", 0)
        appRunTime := time.Since(a.startTime).Seconds()
        if restartAfterSecs > 0 && appRunTime > float64(restartAfterSecs) {
            a.Error("TIME LIMIT REACHED [%f >= %f] - starting graceful restart", appRunTime, restartAfterSecs)
            a.StartGracefulRestart("Run time limit reached")
        }
        <- ticker
    }
}

type responseWriter struct {
    http.ResponseWriter
    app *App
    code int
    notedCode bool
}

// Satisfy the interface
func (w *responseWriter) Header() http.Header {
    return w.ResponseWriter.Header()
}

func (w *responseWriter) Write(buf []byte) (int, error) {
    w.noteCode()
    return w.ResponseWriter.Write(buf)
}

func (w *responseWriter) CloseNotify() <-chan bool {
    return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

func (w *responseWriter) WriteHeader(code int) {
    w.code = code
    w.noteCode()
    w.ResponseWriter.WriteHeader(code)
}

func (w *responseWriter) noteCode() {
    if !w.notedCode {
        statsKey := fmt.Sprintf("http_status.%d", w.code)
        w.app.Stats.Inc(statsKey, 1)
        w.notedCode = true
    }
}

func (a *App) WrapHandler(h HandlerFunc) http.HandlerFunc {
    // Wrap the handler, so we can do before/after logic
    f := func(w http.ResponseWriter, r *http.Request) {
        gopRequest := a.getReq(r)
        defer func() {
            a.doneReq <- gopRequest
        }()
        gopWriter := responseWriter{code: 200, app: a, ResponseWriter: w}

        err := r.ParseForm()
        if err != nil {
            http.Error(&gopWriter, "Failed to parse form: " + err.Error(), http.StatusInternalServerError)
            return
        }

        // Pass in the gop, for logging, cfg etc
        h(gopRequest, &gopWriter, r)
    }

    return http.HandlerFunc(f)
}

// Register an http handler managed by gop.
// We use Gorilla muxxer, since it is back-compatible and nice to use :-)
func (a *App) HandleFunc(u string, h HandlerFunc) {
    gopHandler := a.WrapHandler(h)

    a.GorillaRouter.HandleFunc(u, gopHandler)
}

func (a *App) Run() {
	a.setProcessGroupForNelly()

    a.goAgainSetup()

    a.registerGopHandlers()

    go a.watchdog()

    go a.requestMaker()

    listenAddr, _ := a.Cfg.Get("gop", "listen_addr", ":http")
    listenNet, _ := a.Cfg.Get("gop", "listen_net", "tcp")
    a.goAgainListenAndServe(listenNet, listenAddr)
}

func (a *App) Serve(l net.Listener) {
    http.Serve(l, a.GorillaRouter)
}

func getMemInfo() (int64, int64) {
    var memStats runtime.MemStats
    runtime.ReadMemStats(&memStats)
    // There's lots of stuff in here.
    return int64(memStats.Sys), int64(memStats.Alloc)
}
<commit_msg>remove setuid code from gop - not reliable on threaded procs on linux<commit_after>package gop

import (
    "github.com/gorilla/mux"
    "github.com/gorilla/context"
    "github.com/gorilla/schema"
    "github.com/jbert/timber"

    "fmt"
    "net"
    "time"
    "runtime"
    "net/http"
    "strings"
    "log"
	"syscall"
)

// Stuff we include in both App and Req, for convenience
type common struct {
    timber.Logger
    Cfg             *Config
    Stats           StatsdClient
    Decoder         *schema.Decoder
}

// Represents a managed gop app. Returned by init.
// Embeds logging, provides .Cfg for configuration access.
type App struct {
    common

    AppName         string
    ProjectName     string
    GorillaRouter   *mux.Router
    listener        net.Listener
    wantReq         chan *wantReq
    doneReq         chan *Req
    getReqs         chan chan *Req
    startTime       time.Time
    currentReqs     int
    totalReqs       int
    doingGraceful   bool
}

// Used for RPC to get a new request
type wantReq struct {
    r       *http.Request
    reply   chan *Req
}

// Per request struct. has convenience references to functionality
// in the app singleton. Passed into the request handler.
type Req struct {
    common

    id              int
    startTime       time.Time
    app             *App
    r               *http.Request
    RealRemoteIP    string
    IsHTTPS         bool
}

// The function signature your http handlers need.
type HandlerFunc func(g *Req, w http.ResponseWriter, r *http.Request)

// Set up the application. Reads config. Panic if runtime environment is deficient.
func Init(projectName, appName string) *App {
    app := &App{
        common: common{
            Decoder:        schema.NewDecoder(),
        },
        AppName:        appName,
        ProjectName:    projectName,
        GorillaRouter:  mux.NewRouter(),
        wantReq:        make(chan *wantReq),
        doneReq:        make(chan *Req),
        getReqs:        make(chan chan *Req),
        startTime:      time.Now(),
    }

    app.loadAppConfigFile()

    // Linux setuid() doesn't work with threaded procs :-O
    // and the go runtime threads before we can get going.
    //
    // http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/linux-thread-problems.html
//    app.setUserAndGroup()

    app.initLogging()

    maxProcs, _ := app.Cfg.GetInt("gop", "maxprocs", 4 * runtime.NumCPU())
    app.Debug("Setting maxprocs to %d\n", maxProcs)
    runtime.GOMAXPROCS(maxProcs)

    app.initStatsd()

    return app
}

// Clean shutdown
func (a *App) Finish() {
    // Start a log flush
    timber.Close()
}

//
// Commented out - not reliable - see comment at call site
// func (a *App) setUserAndGroup() {
//     // We do not have logging set up yet. We just panic() on error.
//
//     desiredUserName, _ := a.Cfg.Get("gop", "user", "")
//     // Usernames it's ok to run as, in order of preference
//     possibleUserNames := []string{desiredUserName, a.AppName, a.ProjectName}
//
//     doneIt := false
//     for _, desiredUserName := range possibleUserNames {
//         if runAsUserName(desiredUserName) {
//             doneIt = true
//             break
//         }
//     }
//
//     if !doneIt {
//         panic(fmt.Sprintf("Can't run as any of these users: %v, please set config and/or create user", possibleUserNames))
//     }
//
// // Can't log at this stage :-/
// //    a.Info("Running as user %s (%d)", desiredUserName, desiredUser.Uid)
// }

func (a *App) setProcessGroupForNelly() {
	// Nelly knows our pid and will check that there is always at
	// least one process in the process group with the same id as our pid
	mypid := syscall.Getpid()
	err := syscall.Setpgid(mypid, mypid)
	if err != nil {
        panic(fmt.Sprintf("Failed to setprgp]: %s\n", mypid, mypid, err.Error()))
	}
}

// Hands out 'request' objects
func (a *App) requestMaker() {
    nextReqId := 0
    openReqs := make(map[int] *Req)
    for {
        select {
            case wantReq := <- a.wantReq: {
                realRemoteIP := wantReq.r.RemoteAddr
                isHTTPS := false
                useXF, _:= a.Cfg.GetBool("gop", "use_xf_headers", false)
                if useXF {
                    xff := wantReq.r.Header.Get("X-Forwarded-For")
                    if xff != "" {
                        ips := strings.Split(xff, ",")
                        for i, ip := range ips {
                            ips[i] = strings.TrimSpace(ip)
                        }
                        // The only trustworthy component is the *last* (and only if we
                        // are behind nginx or other proxy which is stripping x-f-f from incoming requests)
                        realRemoteIP = ips[len(ips)-1]
                    }
                    xfp := wantReq.r.Header.Get("X-Forwarded-Proto")
                    isHTTPS = strings.ToLower(xfp) == "https"
                }
                req := Req{
                    common: common{
                        Logger:     a.Logger,
                        Cfg:        a.Cfg,
                        Stats:      a.Stats,
                        Decoder:    a.Decoder,
                    },

                    id:         nextReqId,
                    app:        a,
                    startTime:  time.Now(),
                    r:          wantReq.r,
                    RealRemoteIP: realRemoteIP,
                    IsHTTPS:    isHTTPS,
                }
                openReqs[req.id] = &req
                nextReqId++
                a.Stats.Inc("http_reqs", 1)
                a.Stats.Inc("current_http_reqs", 1)
                a.totalReqs++
                a.currentReqs++
                wantReq.reply <- &req
            }
            case doneReq := <- a.doneReq: {
                _, found := openReqs[doneReq.id]
                if !found {
                    a.Error("BUG! Unknown request id [%d] being retired")
                } else {
                    a.Stats.Dec("current_http_reqs", 1)
                    doneReq.finished()
                    a.currentReqs--
                    delete (openReqs, doneReq.id)
                }
            }
            case reply := <- a.getReqs: {
                go func() {
                    for _, req := range openReqs {
                        reply <- req
                    }
                    close(reply)
                }()
            }
        }
    }
}

// Ask requestMaker for a request
func (a *App) getReq(r *http.Request) *Req {
    reply := make(chan *Req)
    a.wantReq <- &wantReq{r: r, reply: reply}
    return <- reply
}

func (g *Req) finished() {
    context.Clear(g.r)      // Cleanup  gorilla stash

    reqDuration := time.Since(g.startTime)
    slowReqSecs, _ := g.Cfg.GetFloat32("gop", "slow_req_secs", 10)
    if reqDuration.Seconds() > float64(slowReqSecs) {
        g.Error("Slow request [%s] took %s", g.r.URL, reqDuration)
    } else {
        g.Info("Request took %s", reqDuration)
    }

    // Tidy up request finalistion (requestMaker, req.finish() method, app.requestFinished())
    restartReqs, _ := g.Cfg.GetInt("gop", "max_requests", 0)
    if restartReqs > 0 && g.app.totalReqs > restartReqs {
        g.Error("Graceful restart after max_requests: %d", restartReqs)
        g.app.StartGracefulRestart("Max requests reached")
    }

    gcEveryReqs, _ := g.Cfg.GetInt("gop", "gc_requests", 0)
    if gcEveryReqs > 0 && g.app.totalReqs % gcEveryReqs == 0 {
        g.Info("Forcing GC after %d reqs", g.app.totalReqs)
        runtime.GC()
    }
}

func (a *App) initLogging() {

    logPattern, _ := a.Cfg.Get("gop", "log_pattern", "[%D %T] [%L] %S %M")

    // If set, hack all logging to stdout for dev
    forceStdout, _:= a.Cfg.GetBool("gop", "stdout_only_logging", false)
    configLogger := timber.ConfigLogger{
        LogWriter:  new(timber.ConsoleWriter),
        Level:      timber.INFO,
        Formatter:  timber.NewPatFormatter(logPattern),
    }

    if !forceStdout {
        defaultLogDir, _ := a.Cfg.Get("gop", "log_dir", "/var/log")
        defaultLogFname := defaultLogDir + "/" + a.ProjectName + "/" + a.AppName + ".log"
        logFname, _ := a.Cfg.Get("gop", "log_file", defaultLogFname)

        newWriter, err := timber.NewFileWriter(logFname)
        if err != nil {
            panic(fmt.Sprintf("Can't open log file: %s", err))
        }
        configLogger.LogWriter = newWriter
    }

    logLevelStr, _ := a.Cfg.Get("gop", "log_level", "INFO")
    logLevelStr = strings.ToUpper(logLevelStr)
    for logLevel, levelStr := range timber.LongLevelStrings {
        if logLevelStr == levelStr {
            configLogger.Level = timber.Level(logLevel)
            break
        }
    }

    // *Don't* create a NewTImber here. Logs are only flushed on Close() and if we
    // have more than one timber, it's easy to only Close() one of them...
    l := timber.Global
    l.AddLogger(configLogger)
    a.Logger = l

    // Set up the default go logger to go here too, so 3rd party
    // module logging plays nicely
    log.SetFlags(0)
    log.SetOutput(l)
}

func (a *App) watchdog() {
    repeat, _ := a.Cfg.GetInt("gop", "watchdog_secs", 300)
    ticker := time.Tick(time.Second * time.Duration(repeat))

    for {
        sysMemBytesLimit, _ := a.Cfg.GetInt64("gop", "sysmem_bytes_limit", 0)
        allocMemBytesLimit, _ := a.Cfg.GetInt64("gop", "allocmem_bytes_limit", 0)
        numFDsLimit, _ := a.Cfg.GetInt64("gop", "numfds_limit", 0)
        numGorosLimit, _ := a.Cfg.GetInt64("gop", "numgoros_limit", 0)

        sysMemBytes, allocMemBytes := getMemInfo()
        numFDs, err := fdsInUse()
        numGoros := int64(runtime.NumGoroutine())
        if err != nil {
            a.Error("Failed to get number of fds in use: %s", err.Error())
            // Continue without
        }

        a.Info("TICK: sys=%d,alloc=%d,fds=%d,current_req=%d,total_req=%d,goros=%d",
            sysMemBytes,
            allocMemBytes,
            numFDs,
            a.currentReqs,
            a.totalReqs,
            numGoros)
        a.Stats.Gauge("mem.sys", sysMemBytes)
        a.Stats.Gauge("mem.alloc", allocMemBytes)
        a.Stats.Gauge("numfds", numFDs)
        a.Stats.Gauge("numgoro", numGoros)

        if sysMemBytesLimit > 0 && sysMemBytes >= sysMemBytesLimit {
            a.Error("SYS MEM LIMIT REACHED [%d >= %d] - starting graceful restart", sysMemBytes, sysMemBytesLimit)
            a.StartGracefulRestart("Sys Memory limit reached")
        }
        if allocMemBytesLimit > 0 && allocMemBytes >= allocMemBytesLimit {
            a.Error("ALLOC MEM LIMIT REACHED [%d >= %d] - starting graceful restart", allocMemBytes, allocMemBytesLimit)
            a.StartGracefulRestart("Alloc Memory limit reached")
        }
        if numFDsLimit > 0 && numFDs >= numFDsLimit {
            a.Error("NUM FDS LIMIT REACHED [%d >= %d] - starting graceful restart", numFDs, numFDsLimit)
            a.StartGracefulRestart("Number of fds limit reached")
        }
        if numGorosLimit > 0 && numGoros >= numGorosLimit {
            a.Error("NUM GOROS LIMIT REACHED [%d >= %d] - starting graceful restart", numGoros, numGorosLimit)
            a.StartGracefulRestart("Number of goros limit reached")
        }

        restartAfterSecs, _ := a.Cfg.GetFloat32("gop", "restart_after_secs", 0)
        appRunTime := time.Since(a.startTime).Seconds()
        if restartAfterSecs > 0 && appRunTime > float64(restartAfterSecs) {
            a.Error("TIME LIMIT REACHED [%f >= %f] - starting graceful restart", appRunTime, restartAfterSecs)
            a.StartGracefulRestart("Run time limit reached")
        }
        <- ticker
    }
}

type responseWriter struct {
    http.ResponseWriter
    app *App
    code int
    notedCode bool
}

// Satisfy the interface
func (w *responseWriter) Header() http.Header {
    return w.ResponseWriter.Header()
}

func (w *responseWriter) Write(buf []byte) (int, error) {
    w.noteCode()
    return w.ResponseWriter.Write(buf)
}

func (w *responseWriter) CloseNotify() <-chan bool {
    return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

func (w *responseWriter) WriteHeader(code int) {
    w.code = code
    w.noteCode()
    w.ResponseWriter.WriteHeader(code)
}

func (w *responseWriter) noteCode() {
    if !w.notedCode {
        statsKey := fmt.Sprintf("http_status.%d", w.code)
        w.app.Stats.Inc(statsKey, 1)
        w.notedCode = true
    }
}

func (a *App) WrapHandler(h HandlerFunc) http.HandlerFunc {
    // Wrap the handler, so we can do before/after logic
    f := func(w http.ResponseWriter, r *http.Request) {
        gopRequest := a.getReq(r)
        defer func() {
            a.doneReq <- gopRequest
        }()
        gopWriter := responseWriter{code: 200, app: a, ResponseWriter: w}

        err := r.ParseForm()
        if err != nil {
            http.Error(&gopWriter, "Failed to parse form: " + err.Error(), http.StatusInternalServerError)
            return
        }

        // Pass in the gop, for logging, cfg etc
        h(gopRequest, &gopWriter, r)
    }

    return http.HandlerFunc(f)
}

// Register an http handler managed by gop.
// We use Gorilla muxxer, since it is back-compatible and nice to use :-)
func (a *App) HandleFunc(u string, h HandlerFunc) {
    gopHandler := a.WrapHandler(h)

    a.GorillaRouter.HandleFunc(u, gopHandler)
}

func (a *App) Run() {
	a.setProcessGroupForNelly()

    a.goAgainSetup()

    a.registerGopHandlers()

    go a.watchdog()

    go a.requestMaker()

    listenAddr, _ := a.Cfg.Get("gop", "listen_addr", ":http")
    listenNet, _ := a.Cfg.Get("gop", "listen_net", "tcp")
    a.goAgainListenAndServe(listenNet, listenAddr)
}

func (a *App) Serve(l net.Listener) {
    http.Serve(l, a.GorillaRouter)
}

func getMemInfo() (int64, int64) {
    var memStats runtime.MemStats
    runtime.ReadMemStats(&memStats)
    // There's lots of stuff in here.
    return int64(memStats.Sys), int64(memStats.Alloc)
}
<|endoftext|>


<commit_before>package consensus

import (
	"crypto/rand"
	"path/filepath"
	"testing"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/gateway"
	"github.com/NebulousLabs/Sia/modules/miner"
	"github.com/NebulousLabs/Sia/modules/transactionpool"
	"github.com/NebulousLabs/Sia/modules/wallet"
	"github.com/NebulousLabs/Sia/types"
)

// A consensusSetTester is the helper object for consensus set testing,
// including helper modules and methods for controlling synchronization between
// the tester and the modules.
type consensusSetTester struct {
	gateway   modules.Gateway
	miner     modules.TestMiner
	tpool     modules.TransactionPool
	wallet    modules.Wallet
	walletKey crypto.TwofishKey

	cs *ConsensusSet

	persistDir string
}

// randAddress returns a random address that is not spendable.
func randAddress() types.UnlockHash {
	var uh types.UnlockHash
	_, err := rand.Read(uh[:])
	if err != nil {
		panic(err)
	}
	return uh
}

// addSiafunds makes a transaction that moves some testing genesis siafunds
// into the wallet.
func (cst *consensusSetTester) addSiafunds() {
	// Get an address to receive the siafunds.
	uc, err := cst.wallet.NextAddress()
	if err != nil {
		panic(err)
	}

	// Create the transaction that sends the anyone-can-spend siafund output to
	// the wallet address (output only available during testing).
	txn := types.Transaction{
		SiafundInputs: []types.SiafundInput{{
			ParentID:         cst.cs.blockRoot.Block.Transactions[0].SiafundOutputID(2),
			UnlockConditions: types.UnlockConditions{},
		}},
		SiafundOutputs: []types.SiafundOutput{{
			Value:      types.NewCurrency64(1e3),
			UnlockHash: uc.UnlockHash(),
		}},
	}

	// Mine the transaction into the blockchain.
	err = cst.tpool.AcceptTransactionSet([]types.Transaction{txn})
	if err != nil {
		panic(err)
	}
	_, err = cst.miner.AddBlock()
	if err != nil {
		panic(err)
	}

	// Check that the siafunds made it to the wallet.
	_, siafundBalance, _ := cst.wallet.ConfirmedBalance()
	if siafundBalance.Cmp(types.NewCurrency64(1e3)) != 0 {
		panic("wallet does not have the siafunds")
	}
}

// mineCoins mines blocks until there are siacoins in the wallet.
func (cst *consensusSetTester) mineSiacoins() {
	for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
		b, _ := cst.miner.FindBlock()
		err := cst.cs.AcceptBlock(b)
		if err != nil {
			panic(err)
		}
	}
}

// blankConsensusSetTester creates a consensusSetTester that has only the
// genesis block.
func blankConsensusSetTester(name string) (*consensusSetTester, error) {
	testdir := build.TempDir(modules.ConsensusDir, name)

	// Create modules.
	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
	if err != nil {
		return nil, err
	}
	cs, err := New(g, filepath.Join(testdir, modules.ConsensusDir))
	if err != nil {
		return nil, err
	}
	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.ConsensusDir))
	if err != nil {
		return nil, err
	}
	w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
	if err != nil {
		return nil, err
	}
	key, err := crypto.GenerateTwofishKey()
	if err != nil {
		return nil, err
	}
	_, err = w.Encrypt(key)
	if err != nil {
		return nil, err
	}
	err = w.Unlock(key)
	if err != nil {
		return nil, err
	}
	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
	if err != nil {
		return nil, err
	}

	// Assemble all objects into a consensusSetTester.
	cst := &consensusSetTester{
		gateway:   g,
		miner:     m,
		tpool:     tp,
		wallet:    w,
		walletKey: key,

		cs: cs,

		persistDir: testdir,
	}
	return cst, nil
}

// createConsensusSetTester creates a consensusSetTester that's ready for use,
// including siacoins and siafunds available in the wallet.
func createConsensusSetTester(name string) (*consensusSetTester, error) {
	cst, err := blankConsensusSetTester(name)
	if err != nil {
		return nil, err
	}
	cst.addSiafunds()
	cst.mineSiacoins()
	return cst, nil
}

// Close safely closes the consensus set tester. Because there's not a good way
// to errcheck when deferring a close, a panic is called in the event of an
// error.
func (cst *consensusSetTester) Close() error {
	err := cst.cs.Close()
	if err != nil {
		panic(err)
	}
	err = cst.gateway.Close()
	if err != nil {
		panic(err)
	}
	return nil
}

// TestNilInputs tries to create new consensus set modules using nil inputs.
func TestNilInputs(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	t.Parallel()
	testdir := build.TempDir(modules.ConsensusDir, "TestNilInputs")
	_, err := New(nil, testdir)
	if err != errNilGateway {
		t.Fatal(err)
	}
}

// TestClosing tries to close a consenuss set.
func TestDatabaseClosing(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	t.Parallel()
	testdir := build.TempDir(modules.ConsensusDir, "TestClosing")

	// Create the gateway.
	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
	if err != nil {
		t.Fatal(err)
	}
	cs, err := New(g, testdir)
	if err != nil {
		t.Fatal(err)
	}
	err = cs.Close()
	if err != nil {
		t.Error(err)
	}
}
<commit_msg>close miner in consensusSetTester.Close<commit_after>package consensus

import (
	"crypto/rand"
	"path/filepath"
	"testing"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/gateway"
	"github.com/NebulousLabs/Sia/modules/miner"
	"github.com/NebulousLabs/Sia/modules/transactionpool"
	"github.com/NebulousLabs/Sia/modules/wallet"
	"github.com/NebulousLabs/Sia/types"
)

// A consensusSetTester is the helper object for consensus set testing,
// including helper modules and methods for controlling synchronization between
// the tester and the modules.
type consensusSetTester struct {
	gateway   modules.Gateway
	miner     modules.TestMiner
	tpool     modules.TransactionPool
	wallet    modules.Wallet
	walletKey crypto.TwofishKey

	cs *ConsensusSet

	persistDir string
}

// randAddress returns a random address that is not spendable.
func randAddress() types.UnlockHash {
	var uh types.UnlockHash
	_, err := rand.Read(uh[:])
	if err != nil {
		panic(err)
	}
	return uh
}

// addSiafunds makes a transaction that moves some testing genesis siafunds
// into the wallet.
func (cst *consensusSetTester) addSiafunds() {
	// Get an address to receive the siafunds.
	uc, err := cst.wallet.NextAddress()
	if err != nil {
		panic(err)
	}

	// Create the transaction that sends the anyone-can-spend siafund output to
	// the wallet address (output only available during testing).
	txn := types.Transaction{
		SiafundInputs: []types.SiafundInput{{
			ParentID:         cst.cs.blockRoot.Block.Transactions[0].SiafundOutputID(2),
			UnlockConditions: types.UnlockConditions{},
		}},
		SiafundOutputs: []types.SiafundOutput{{
			Value:      types.NewCurrency64(1e3),
			UnlockHash: uc.UnlockHash(),
		}},
	}

	// Mine the transaction into the blockchain.
	err = cst.tpool.AcceptTransactionSet([]types.Transaction{txn})
	if err != nil {
		panic(err)
	}
	_, err = cst.miner.AddBlock()
	if err != nil {
		panic(err)
	}

	// Check that the siafunds made it to the wallet.
	_, siafundBalance, _ := cst.wallet.ConfirmedBalance()
	if siafundBalance.Cmp(types.NewCurrency64(1e3)) != 0 {
		panic("wallet does not have the siafunds")
	}
}

// mineCoins mines blocks until there are siacoins in the wallet.
func (cst *consensusSetTester) mineSiacoins() {
	for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
		b, _ := cst.miner.FindBlock()
		err := cst.cs.AcceptBlock(b)
		if err != nil {
			panic(err)
		}
	}
}

// blankConsensusSetTester creates a consensusSetTester that has only the
// genesis block.
func blankConsensusSetTester(name string) (*consensusSetTester, error) {
	testdir := build.TempDir(modules.ConsensusDir, name)

	// Create modules.
	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
	if err != nil {
		return nil, err
	}
	cs, err := New(g, filepath.Join(testdir, modules.ConsensusDir))
	if err != nil {
		return nil, err
	}
	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.ConsensusDir))
	if err != nil {
		return nil, err
	}
	w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
	if err != nil {
		return nil, err
	}
	key, err := crypto.GenerateTwofishKey()
	if err != nil {
		return nil, err
	}
	_, err = w.Encrypt(key)
	if err != nil {
		return nil, err
	}
	err = w.Unlock(key)
	if err != nil {
		return nil, err
	}
	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
	if err != nil {
		return nil, err
	}

	// Assemble all objects into a consensusSetTester.
	cst := &consensusSetTester{
		gateway:   g,
		miner:     m,
		tpool:     tp,
		wallet:    w,
		walletKey: key,

		cs: cs,

		persistDir: testdir,
	}
	return cst, nil
}

// createConsensusSetTester creates a consensusSetTester that's ready for use,
// including siacoins and siafunds available in the wallet.
func createConsensusSetTester(name string) (*consensusSetTester, error) {
	cst, err := blankConsensusSetTester(name)
	if err != nil {
		return nil, err
	}
	cst.addSiafunds()
	cst.mineSiacoins()
	return cst, nil
}

// Close safely closes the consensus set tester. Because there's not a good way
// to errcheck when deferring a close, a panic is called in the event of an
// error.
func (cst *consensusSetTester) Close() error {
	errs := []error{
		cst.cs.Close(),
		cst.gateway.Close(),
		cst.miner.Close(),
	}
	if err := build.JoinErrors(errs, "; "); err != nil {
		panic(err)
	}
	return nil
}

// TestNilInputs tries to create new consensus set modules using nil inputs.
func TestNilInputs(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	t.Parallel()
	testdir := build.TempDir(modules.ConsensusDir, "TestNilInputs")
	_, err := New(nil, testdir)
	if err != errNilGateway {
		t.Fatal(err)
	}
}

// TestClosing tries to close a consenuss set.
func TestDatabaseClosing(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	t.Parallel()
	testdir := build.TempDir(modules.ConsensusDir, "TestClosing")

	// Create the gateway.
	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
	if err != nil {
		t.Fatal(err)
	}
	cs, err := New(g, testdir)
	if err != nil {
		t.Fatal(err)
	}
	err = cs.Close()
	if err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package bittrex

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

// IsAPIAlive returns true if the bittrex api is reachable with the current network connection.
func IsAPIAlive() error {
	var pingResponse pingResponse
	timestamp := time.Now().UTC().Unix()
	URL := fmt.Sprintf("https://socket.bittrex.com/signalr/ping?_=%d", timestamp)
	response, err := http.Get(URL)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return fmt.Errorf("Status Code: %d", response.StatusCode)
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, &pingResponse)
	if err != nil {
		return err
	}
	if pingResponse.Response == "pong" {
		return nil
	}
	return errors.New("API is not live")
}

// GetServerAPIVersion returns the version which is currently running on the server.
func GetServerAPIVersion() (string, error) {
	var versionResponse versionResponse
	timestamp := time.Now().UTC().Unix()
	URL := fmt.Sprintf("https://bittrex.com/Content/version.txt?_=%d", timestamp)
	response, err := http.Get(URL)
	if err != nil {
		return "", err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return "", fmt.Errorf("Status Code: %d", response.StatusCode)
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", err
	}
	body = bytes.TrimPrefix(body, []byte("\xef\xbb\xbf"))

	err = json.Unmarshal(body, &versionResponse)
	if err != nil {
		return "", err
	}

	return versionResponse.Version.String(), nil
}

// GetBTCPrice returns the current BTC Price.
func GetBTCPrice() (*BTCPrice, error) {
	result, err := publicCall("currencies", "GetBTCPrice", nil, nil)
	if err != nil {
		return nil, err
	}

	var btcPrice BTCPrice

	err = json.Unmarshal(*result, &btcPrice)
	if err != nil {
		return nil, err
	}
	return &btcPrice, nil
}

// GetLatestTick returns the latest tick of the
// specified market's candlestick chart,
// following the specified tick interval.
func GetLatestTick(marketName, tickInterval string) (*CandleStick, error) {
	ticks, err := tickFunc(marketName, tickInterval, "GetLatestTick")
	if err != nil {
		return nil, err
	}
	return &ticks[0], nil
}

// GetTicks returns the ticks of the
// specified market's candlestick chart,
// following the specified tick interval.
func GetTicks(marketName, tickInterval string) (CandleSticks, error) {
	return tickFunc(marketName, tickInterval, "GetTicks")
}

// GetMarketSummaries gets the summary of all markets.
func GetMarketSummaries() (MarketSummaries, error) {
	now := time.Now().Unix()
	GetParameters := publicParams{
		Timestamp: &now,
	}
	result, err := publicCall("markets", "GetMarketSummaries", &GetParameters, nil)
	if err != nil {
		return nil, err
	}
	var resp marketSummariesResult
	err = json.Unmarshal(*result, &resp)
	if err != nil {
		return nil, err
	}
	ret := make(MarketSummaries, len(resp))
	for i, respItem := range resp {
		ret[i] = respItem.Summary
	}
	return ret, nil
}

// GetMarketSummary gets the summary of a single market.
func GetMarketSummary(marketName string) (*MarketSummary, error) {
	now := time.Now().Unix()
	GetParameters := publicParams{
		MarketName: &marketName,
		Timestamp:  &now,
	}
	result, err := publicCall("market", "GetMarketSummary", &GetParameters, nil)
	if err != nil {
		return nil, err
	}

	var ret MarketSummary
	err = json.Unmarshal(*result, &ret)
	if err != nil {
		return nil, err
	}
	return &ret, nil
}

// tickFunc is a common pattern for GetTicks and GetLatestTick functions.
func tickFunc(marketName, tickInterval, tickFeature string) (CandleSticks, error) {
	now := time.Now().Unix()
	GetParameters := publicParams{
		MarketName:   &marketName,
		TickInterval: &tickInterval,
		Timestamp:    &now,
	}
	result, err := publicCall("market", "GetLatestTick", &GetParameters, nil)
	if err != nil {
		return nil, err
	}
	var ret CandleSticks
	err = json.Unmarshal(*result, &ret)
	if err != nil {
		return nil, err
	}
	return ret, nil
}

// GetOrderBook gets the current order book, made up by currently open orders.
func GetOrderBook(marketName string) (OrderBook, error) {
	return nil, nil
}

func GetMarkets() (Markets, error) {
	now := time.Now().Unix()
	GetParameters := publicParams{
		Timestamp: &now,
	}
	result, err := publicCall("markets", "GetMarkets", &GetParameters, nil)
	if err != nil {
		return nil, err
	}
	var resp Markets
	err = json.Unmarshal(*result, &resp)
	if err != nil {
		return nil, err
	}

	return resp, nil
}
<commit_msg>adding docs to GetMarkets func<commit_after>package bittrex

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

// IsAPIAlive returns true if the bittrex api is reachable with the current network connection.
func IsAPIAlive() error {
	var pingResponse pingResponse
	timestamp := time.Now().UTC().Unix()
	URL := fmt.Sprintf("https://socket.bittrex.com/signalr/ping?_=%d", timestamp)
	response, err := http.Get(URL)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return fmt.Errorf("Status Code: %d", response.StatusCode)
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, &pingResponse)
	if err != nil {
		return err
	}
	if pingResponse.Response == "pong" {
		return nil
	}
	return errors.New("API is not live")
}

// GetServerAPIVersion returns the version which is currently running on the server.
func GetServerAPIVersion() (string, error) {
	var versionResponse versionResponse
	timestamp := time.Now().UTC().Unix()
	URL := fmt.Sprintf("https://bittrex.com/Content/version.txt?_=%d", timestamp)
	response, err := http.Get(URL)
	if err != nil {
		return "", err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return "", fmt.Errorf("Status Code: %d", response.StatusCode)
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", err
	}
	body = bytes.TrimPrefix(body, []byte("\xef\xbb\xbf"))

	err = json.Unmarshal(body, &versionResponse)
	if err != nil {
		return "", err
	}

	return versionResponse.Version.String(), nil
}

// GetBTCPrice returns the current BTC Price.
func GetBTCPrice() (*BTCPrice, error) {
	result, err := publicCall("currencies", "GetBTCPrice", nil, nil)
	if err != nil {
		return nil, err
	}

	var btcPrice BTCPrice

	err = json.Unmarshal(*result, &btcPrice)
	if err != nil {
		return nil, err
	}
	return &btcPrice, nil
}

// GetLatestTick returns the latest tick of the
// specified market's candlestick chart,
// following the specified tick interval.
func GetLatestTick(marketName, tickInterval string) (*CandleStick, error) {
	ticks, err := tickFunc(marketName, tickInterval, "GetLatestTick")
	if err != nil {
		return nil, err
	}
	return &ticks[0], nil
}

// GetTicks returns the ticks of the
// specified market's candlestick chart,
// following the specified tick interval.
func GetTicks(marketName, tickInterval string) (CandleSticks, error) {
	return tickFunc(marketName, tickInterval, "GetTicks")
}

// GetMarketSummaries gets the summary of all markets.
func GetMarketSummaries() (MarketSummaries, error) {
	now := time.Now().Unix()
	GetParameters := publicParams{
		Timestamp: &now,
	}
	result, err := publicCall("markets", "GetMarketSummaries", &GetParameters, nil)
	if err != nil {
		return nil, err
	}
	var resp marketSummariesResult
	err = json.Unmarshal(*result, &resp)
	if err != nil {
		return nil, err
	}
	ret := make(MarketSummaries, len(resp))
	for i, respItem := range resp {
		ret[i] = respItem.Summary
	}
	return ret, nil
}

// GetMarketSummary gets the summary of a single market.
func GetMarketSummary(marketName string) (*MarketSummary, error) {
	now := time.Now().Unix()
	GetParameters := publicParams{
		MarketName: &marketName,
		Timestamp:  &now,
	}
	result, err := publicCall("market", "GetMarketSummary", &GetParameters, nil)
	if err != nil {
		return nil, err
	}

	var ret MarketSummary
	err = json.Unmarshal(*result, &ret)
	if err != nil {
		return nil, err
	}
	return &ret, nil
}

// tickFunc is a common pattern for GetTicks and GetLatestTick functions.
func tickFunc(marketName, tickInterval, tickFeature string) (CandleSticks, error) {
	now := time.Now().Unix()
	GetParameters := publicParams{
		MarketName:   &marketName,
		TickInterval: &tickInterval,
		Timestamp:    &now,
	}
	result, err := publicCall("market", "GetLatestTick", &GetParameters, nil)
	if err != nil {
		return nil, err
	}
	var ret CandleSticks
	err = json.Unmarshal(*result, &ret)
	if err != nil {
		return nil, err
	}
	return ret, nil
}

// GetOrderBook gets the current order book, made up by currently open orders.
func GetOrderBook(marketName string) (OrderBook, error) {
	return nil, nil
}

//GetMarkets gets all markets data.
func GetMarkets() (Markets, error) {
	now := time.Now().Unix()
	GetParameters := publicParams{
		Timestamp: &now,
	}
	result, err := publicCall("markets", "GetMarkets", &GetParameters, nil)
	if err != nil {
		return nil, err
	}
	var resp Markets
	err = json.Unmarshal(*result, &resp)
	if err != nil {
		return nil, err
	}

	return resp, nil
}
<|endoftext|>


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

// Parse URLs (actually URIs, but that seems overly pedantic).
// RFC 2396

package http

import (
	"os";
	"strconv";
	"strings";
)

// URLError reports an error and the operation and URL that caused it.
type URLError struct {
	Op string;
	URL string;
	Error os.Error;
}

func (e *URLError) String() string {
	return e.Op + " " + e.URL + ": " + e.Error.String();
}

func ishex(c byte) bool {
	switch {
	case '0' <= c && c <= '9':
		return true;
	case 'a' <= c && c <= 'f':
		return true;
	case 'A' <= c && c <= 'F':
		return true;
	}
	return false
}

func unhex(c byte) byte {
	switch {
	case '0' <= c && c <= '9':
		return c - '0';
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10;
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10;
	}
	return 0
}

type URLEscapeError string
func (e URLEscapeError) String() string {
	return "invalid URL escape " + strconv.Quote(string(e));
}

// Return true if the specified character should be escaped when appearing in a
// URL string.
//
// TODO: for now, this is a hack; it only flags a few common characters that have
// special meaning in URLs.  That will get the job done in the common cases.
func shouldEscape(c byte) bool {
	switch c {
	case ' ', '?', '&', '=', '#', '+', '%':
		return true;
	}
	return false;
}

// URLUnescape unescapes a URL-encoded string,
// converting %AB into the byte 0xAB and '+' into ' ' (space).
// It returns an error if any % is not followed
// by two hexadecimal digits.
func URLUnescape(s string) (string, os.Error) {
	// Count %, check that they're well-formed.
	n := 0;
	anyPlusses := false;
	for i := 0; i < len(s); {
		switch s[i] {
		case '%':
			n++;
			if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
				s = s[i:len(s)];
				if len(s) > 3 {
					s = s[0:3];
				}
				return "", URLEscapeError(s);
			}
			i += 3;
		case '+':
			anyPlusses = true;
			i++;
		default:
			i++
		}
	}

	if n == 0 && !anyPlusses {
		return s, nil
	}

	t := make([]byte, len(s)-2*n);
	j := 0;
	for i := 0; i < len(s); {
		switch s[i] {
		case '%':
			t[j] = unhex(s[i+1]) << 4 | unhex(s[i+2]);
			j++;
			i += 3;
		case '+':
			t[j] = ' ';
			j++;
			i++;
		default:
			t[j] = s[i];
			j++;
			i++;
		}
	}
	return string(t), nil;
}

// URLEscape converts a string into URL-encoded form.
func URLEscape(s string) string {
	spaceCount, hexCount := 0, 0;
	for i := 0; i < len(s); i++ {
		c := s[i];
		if shouldEscape(c) {
			if c == ' ' {
				spaceCount++;
			} else {
				hexCount++;
			}
		}
	}

	if spaceCount == 0 && hexCount == 0 {
		return s;
	}

	t := make([]byte, len(s)+2*hexCount);
	j := 0;
	for i := 0; i < len(s); i++ {
		switch c := s[i]; {
		case c == ' ':
			t[j] = '+';
			j++;
		case shouldEscape(c):
			t[j] = '%';
			t[j+1] = "0123456789abcdef"[c>>4];
			t[j+2] = "0123456789abcdef"[c&15];
			j += 3;
		default:
			t[j] = s[i];
			j++;
		}
	}
	return string(t);
}

// A URL represents a parsed URL (technically, a URI reference).
// The general form represented is:
//	scheme://[userinfo@]host/path[?query][#fragment]
// The Raw, RawPath, and RawQuery fields are in "wire format" (special
// characters must be hex-escaped if not meant to have special meaning).
// All other fields are logical values; '+' or '%' represent themselves.
//
// Note, the reason for using wire format for the query is that it needs
// to be split into key/value pairs before decoding.
type URL struct {
	Raw string;		// the original string
	Scheme string;		// scheme
	RawPath string;		// //[userinfo@]host/path[?query][#fragment]
	Authority string;	// [userinfo@]host
	Userinfo string;	// userinfo
	Host string;		// host
	Path string;		// /path
	RawQuery string;	// query
	Fragment string;	// fragment
}

// Maybe rawurl is of the form scheme:path.
// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*)
// If so, return scheme, path; else return "", rawurl.
func getscheme(rawurl string) (scheme, path string, err os.Error) {
	for i := 0; i < len(rawurl); i++ {
		c := rawurl[i];
		switch {
		case 'a' <= c && c <= 'z' ||'A' <= c && c <= 'Z':
			// do nothing
		case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
			if i == 0 {
				return "", rawurl, nil
			}
		case c == ':':
			if i == 0 {
				return "", "", os.ErrorString("missing protocol scheme")
			}
			return rawurl[0:i], rawurl[i+1:len(rawurl)], nil
		default:
			// we have encountered an invalid character,
			// so there is no valid scheme
			return "", rawurl, nil
		}
	}
	return "", rawurl, nil
}

// Maybe s is of the form t c u.
// If so, return t, c u (or t, u if cutc == true).
// If not, return s, "".
func split(s string, c byte, cutc bool) (string, string) {
	for i := 0; i < len(s); i++ {
		if s[i] == c {
			if cutc {
				return s[0:i], s[i+1:len(s)]
			}
			return s[0:i], s[i:len(s)]
		}
	}
	return s, ""
}

// TODO(rsc): The BUG comment is supposed to appear in the godoc output
// in a BUGS section, but that got lost in the transition to godoc.

// BUG(rsc): ParseURL should canonicalize the path,
// removing unnecessary . and .. elements.

// ParseURL parses rawurl into a URL structure.
// The string rawurl is assumed not to have a #fragment suffix.
// (Web browsers strip #fragment before sending the URL to a web server.)
func ParseURL(rawurl string) (url *URL, err os.Error) {
	if rawurl == "" {
		err = os.ErrorString("empty url");
		goto Error;
	}
	url = new(URL);
	url.Raw = rawurl;

	// split off possible leading "http:", "mailto:", etc.
	var path string;
	if url.Scheme, path, err = getscheme(rawurl); err != nil {
		goto Error;
	}
	url.RawPath = path;

	// RFC 2396: a relative URI (no scheme) has a ?query,
	// but absolute URIs only have query if path begins with /
	if url.Scheme == "" || len(path) > 0 && path[0] == '/' {
		path, url.RawQuery = split(path, '?', true);
	}

	// Maybe path is //authority/path
	if len(path) > 2 && path[0:2] == "//" {
		url.Authority, path = split(path[2:len(path)], '/', false);
	}

	// If there's no @, split's default is wrong.  Check explicitly.
	if strings.Index(url.Authority, "@") < 0 {
		url.Host = url.Authority;
	} else {
		url.Userinfo, url.Host = split(url.Authority, '@', true);
	}

	// What's left is the path.
	// TODO: Canonicalize (remove . and ..)?
	if url.Path, err = URLUnescape(path); err != nil {
		goto Error;
	}

	// Remove escapes from the Authority and Userinfo fields, and verify
	// that Scheme and Host contain no escapes (that would be illegal).
	if url.Authority, err = URLUnescape(url.Authority); err != nil {
		goto Error;
	}
	if url.Userinfo, err = URLUnescape(url.Userinfo); err != nil {
		goto Error;
	}
	if strings.Index(url.Scheme, "%") >= 0 {
		err = os.ErrorString("hexadecimal escape in scheme");
		goto Error;
	}
	if strings.Index(url.Host, "%") >= 0 {
		err = os.ErrorString("hexadecimal escape in host");
		goto Error;
	}

	return url, nil;

Error:
	return nil, &URLError{"parse", rawurl, err}

}

// ParseURLReference is like ParseURL but allows a trailing #fragment.
func ParseURLReference(rawurlref string) (url *URL, err os.Error) {
	// Cut off #frag.
	rawurl, frag := split(rawurlref, '#', true);
	if url, err = ParseURL(rawurl); err != nil {
		return nil, err
	}
	if url.Fragment, err = URLUnescape(frag); err != nil {
		return nil, &URLError{"parse", rawurl, err}
	}
	return url, nil
}

// String reassembles url into a valid URL string.
//
// There are redundant fields stored in the URL structure:
// the String method consults Scheme, Path, Host, Userinfo,
// RawQuery, and Fragment, but not Raw, RawPath or Authority.
func (url *URL) String() string {
	result := "";
	if url.Scheme != "" {
		result += url.Scheme + ":";
	}
	if url.Host != "" || url.Userinfo != "" {
		result += "//";
		if url.Userinfo != "" {
			result += URLEscape(url.Userinfo) + "@";
		}
		result += url.Host;
	}
	result += URLEscape(url.Path);
	if url.RawQuery != "" {
		result += "?" + url.RawQuery;
	}
	if url.Fragment != "" {
		result += "#" + URLEscape(url.Fragment);
	}
	return result;
}
<commit_msg>fix spelling that is correct but bothers me anyway<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Parse URLs (actually URIs, but that seems overly pedantic).
// RFC 2396

package http

import (
	"os";
	"strconv";
	"strings";
)

// URLError reports an error and the operation and URL that caused it.
type URLError struct {
	Op string;
	URL string;
	Error os.Error;
}

func (e *URLError) String() string {
	return e.Op + " " + e.URL + ": " + e.Error.String();
}

func ishex(c byte) bool {
	switch {
	case '0' <= c && c <= '9':
		return true;
	case 'a' <= c && c <= 'f':
		return true;
	case 'A' <= c && c <= 'F':
		return true;
	}
	return false
}

func unhex(c byte) byte {
	switch {
	case '0' <= c && c <= '9':
		return c - '0';
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10;
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10;
	}
	return 0
}

type URLEscapeError string
func (e URLEscapeError) String() string {
	return "invalid URL escape " + strconv.Quote(string(e));
}

// Return true if the specified character should be escaped when appearing in a
// URL string.
//
// TODO: for now, this is a hack; it only flags a few common characters that have
// special meaning in URLs.  That will get the job done in the common cases.
func shouldEscape(c byte) bool {
	switch c {
	case ' ', '?', '&', '=', '#', '+', '%':
		return true;
	}
	return false;
}

// URLUnescape unescapes a URL-encoded string,
// converting %AB into the byte 0xAB and '+' into ' ' (space).
// It returns an error if any % is not followed
// by two hexadecimal digits.
func URLUnescape(s string) (string, os.Error) {
	// Count %, check that they're well-formed.
	n := 0;
	hasPlus := false;
	for i := 0; i < len(s); {
		switch s[i] {
		case '%':
			n++;
			if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
				s = s[i:len(s)];
				if len(s) > 3 {
					s = s[0:3];
				}
				return "", URLEscapeError(s);
			}
			i += 3;
		case '+':
			hasPlus = true;
			i++;
		default:
			i++
		}
	}

	if n == 0 && !hasPlus {
		return s, nil
	}

	t := make([]byte, len(s)-2*n);
	j := 0;
	for i := 0; i < len(s); {
		switch s[i] {
		case '%':
			t[j] = unhex(s[i+1]) << 4 | unhex(s[i+2]);
			j++;
			i += 3;
		case '+':
			t[j] = ' ';
			j++;
			i++;
		default:
			t[j] = s[i];
			j++;
			i++;
		}
	}
	return string(t), nil;
}

// URLEscape converts a string into URL-encoded form.
func URLEscape(s string) string {
	spaceCount, hexCount := 0, 0;
	for i := 0; i < len(s); i++ {
		c := s[i];
		if shouldEscape(c) {
			if c == ' ' {
				spaceCount++;
			} else {
				hexCount++;
			}
		}
	}

	if spaceCount == 0 && hexCount == 0 {
		return s;
	}

	t := make([]byte, len(s)+2*hexCount);
	j := 0;
	for i := 0; i < len(s); i++ {
		switch c := s[i]; {
		case c == ' ':
			t[j] = '+';
			j++;
		case shouldEscape(c):
			t[j] = '%';
			t[j+1] = "0123456789abcdef"[c>>4];
			t[j+2] = "0123456789abcdef"[c&15];
			j += 3;
		default:
			t[j] = s[i];
			j++;
		}
	}
	return string(t);
}

// A URL represents a parsed URL (technically, a URI reference).
// The general form represented is:
//	scheme://[userinfo@]host/path[?query][#fragment]
// The Raw, RawPath, and RawQuery fields are in "wire format" (special
// characters must be hex-escaped if not meant to have special meaning).
// All other fields are logical values; '+' or '%' represent themselves.
//
// Note, the reason for using wire format for the query is that it needs
// to be split into key/value pairs before decoding.
type URL struct {
	Raw string;		// the original string
	Scheme string;		// scheme
	RawPath string;		// //[userinfo@]host/path[?query][#fragment]
	Authority string;	// [userinfo@]host
	Userinfo string;	// userinfo
	Host string;		// host
	Path string;		// /path
	RawQuery string;	// query
	Fragment string;	// fragment
}

// Maybe rawurl is of the form scheme:path.
// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*)
// If so, return scheme, path; else return "", rawurl.
func getscheme(rawurl string) (scheme, path string, err os.Error) {
	for i := 0; i < len(rawurl); i++ {
		c := rawurl[i];
		switch {
		case 'a' <= c && c <= 'z' ||'A' <= c && c <= 'Z':
			// do nothing
		case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
			if i == 0 {
				return "", rawurl, nil
			}
		case c == ':':
			if i == 0 {
				return "", "", os.ErrorString("missing protocol scheme")
			}
			return rawurl[0:i], rawurl[i+1:len(rawurl)], nil
		default:
			// we have encountered an invalid character,
			// so there is no valid scheme
			return "", rawurl, nil
		}
	}
	return "", rawurl, nil
}

// Maybe s is of the form t c u.
// If so, return t, c u (or t, u if cutc == true).
// If not, return s, "".
func split(s string, c byte, cutc bool) (string, string) {
	for i := 0; i < len(s); i++ {
		if s[i] == c {
			if cutc {
				return s[0:i], s[i+1:len(s)]
			}
			return s[0:i], s[i:len(s)]
		}
	}
	return s, ""
}

// TODO(rsc): The BUG comment is supposed to appear in the godoc output
// in a BUGS section, but that got lost in the transition to godoc.

// BUG(rsc): ParseURL should canonicalize the path,
// removing unnecessary . and .. elements.

// ParseURL parses rawurl into a URL structure.
// The string rawurl is assumed not to have a #fragment suffix.
// (Web browsers strip #fragment before sending the URL to a web server.)
func ParseURL(rawurl string) (url *URL, err os.Error) {
	if rawurl == "" {
		err = os.ErrorString("empty url");
		goto Error;
	}
	url = new(URL);
	url.Raw = rawurl;

	// split off possible leading "http:", "mailto:", etc.
	var path string;
	if url.Scheme, path, err = getscheme(rawurl); err != nil {
		goto Error;
	}
	url.RawPath = path;

	// RFC 2396: a relative URI (no scheme) has a ?query,
	// but absolute URIs only have query if path begins with /
	if url.Scheme == "" || len(path) > 0 && path[0] == '/' {
		path, url.RawQuery = split(path, '?', true);
	}

	// Maybe path is //authority/path
	if len(path) > 2 && path[0:2] == "//" {
		url.Authority, path = split(path[2:len(path)], '/', false);
	}

	// If there's no @, split's default is wrong.  Check explicitly.
	if strings.Index(url.Authority, "@") < 0 {
		url.Host = url.Authority;
	} else {
		url.Userinfo, url.Host = split(url.Authority, '@', true);
	}

	// What's left is the path.
	// TODO: Canonicalize (remove . and ..)?
	if url.Path, err = URLUnescape(path); err != nil {
		goto Error;
	}

	// Remove escapes from the Authority and Userinfo fields, and verify
	// that Scheme and Host contain no escapes (that would be illegal).
	if url.Authority, err = URLUnescape(url.Authority); err != nil {
		goto Error;
	}
	if url.Userinfo, err = URLUnescape(url.Userinfo); err != nil {
		goto Error;
	}
	if strings.Index(url.Scheme, "%") >= 0 {
		err = os.ErrorString("hexadecimal escape in scheme");
		goto Error;
	}
	if strings.Index(url.Host, "%") >= 0 {
		err = os.ErrorString("hexadecimal escape in host");
		goto Error;
	}

	return url, nil;

Error:
	return nil, &URLError{"parse", rawurl, err}

}

// ParseURLReference is like ParseURL but allows a trailing #fragment.
func ParseURLReference(rawurlref string) (url *URL, err os.Error) {
	// Cut off #frag.
	rawurl, frag := split(rawurlref, '#', true);
	if url, err = ParseURL(rawurl); err != nil {
		return nil, err
	}
	if url.Fragment, err = URLUnescape(frag); err != nil {
		return nil, &URLError{"parse", rawurl, err}
	}
	return url, nil
}

// String reassembles url into a valid URL string.
//
// There are redundant fields stored in the URL structure:
// the String method consults Scheme, Path, Host, Userinfo,
// RawQuery, and Fragment, but not Raw, RawPath or Authority.
func (url *URL) String() string {
	result := "";
	if url.Scheme != "" {
		result += url.Scheme + ":";
	}
	if url.Host != "" || url.Userinfo != "" {
		result += "//";
		if url.Userinfo != "" {
			result += URLEscape(url.Userinfo) + "@";
		}
		result += url.Host;
	}
	result += URLEscape(url.Path);
	if url.RawQuery != "" {
		result += "?" + url.RawQuery;
	}
	if url.Fragment != "" {
		result += "#" + URLEscape(url.Fragment);
	}
	return result;
}
<|endoftext|>


<commit_before>package softlayer

import (
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/softlayer/softlayer-go/datatypes"
	"github.com/softlayer/softlayer-go/services"
	"github.com/softlayer/softlayer-go/session"
	"github.com/softlayer/softlayer-go/sl"
)

func resourceSoftLayerSSHKey() *schema.Resource {
	return &schema.Resource{
		Create:   resourceSoftLayerSSHKeyCreate,
		Read:     resourceSoftLayerSSHKeyRead,
		Update:   resourceSoftLayerSSHKeyUpdate,
		Delete:   resourceSoftLayerSSHKeyDelete,
		Exists:   resourceSoftLayerSSHKeyExists,
		Importer: &schema.ResourceImporter{},

		Schema: map[string]*schema.Schema{
			"id": &schema.Schema{
				Type:     schema.TypeInt,
				Computed: true,
			},

			"label": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"public_key": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"fingerprint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"notes": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  nil,
			},
		},
	}
}

func resourceSoftLayerSSHKeyCreate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	// Build up our creation options
	opts := datatypes.Security_Ssh_Key{
		Label: sl.String(d.Get("label").(string)),
		Key:   sl.String(d.Get("public_key").(string)),
	}

	if notes, ok := d.GetOk("notes"); ok {
		opts.Notes = sl.String(notes.(string))
	}

	res, err := service.CreateObject(&opts)
	if err != nil {
		return fmt.Errorf("Error creating SSH Key: %s", err)
	}

	d.SetId(strconv.Itoa(*res.Id))
	log.Printf("[INFO] SSH Key: %d", *res.Id)

	return resourceSoftLayerSSHKeyRead(d, meta)
}

func resourceSoftLayerSSHKeyRead(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, _ := strconv.Atoi(d.Id())

	key, err := service.Id(keyId).GetObject()
	if err != nil {
		// If the key is somehow already destroyed, mark as
		// succesfully gone
		if strings.Contains(err.Error(), "404 Not Found") {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	d.Set("id", *key.Id)
	d.Set("label", *key.Label)
	d.Set("public_key", strings.TrimSpace(*key.Key))
	d.Set("fingerprint", *key.Fingerprint)

	if key.Notes != nil {
		d.Set("notes", *key.Notes)
	}

	return nil
}

func resourceSoftLayerSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, _ := strconv.Atoi(d.Id())

	key, err := service.Id(keyId).GetObject()
	if err != nil {
		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	if d.HasChange("label") {
		key.Label = sl.String(d.Get("label").(string))
	}

	if d.HasChange("notes") {
		key.Notes = sl.String(d.Get("notes").(string))
	}

	_, err = service.Id(keyId).EditObject(&key)
	if err != nil {
		return fmt.Errorf("Error editing SSH key: %s", err)
	}
	return nil
}

func resourceSoftLayerSSHKeyDelete(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	id, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Error deleting SSH Key: %s", err)
	}

	log.Printf("[INFO] Deleting SSH key: %d", id)
	_, err = service.Id(id).DeleteObject()
	if err != nil {
		return fmt.Errorf("Error deleting SSH key: %s", err)
	}

	d.SetId("")
	return nil
}

func resourceSoftLayerSSHKeyExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, err := strconv.Atoi(d.Id())
	if err != nil {
		return false, fmt.Errorf("Not a valid ID, must be an integer: %s", err)
	}

	result, err := service.Id(keyId).GetObject()
	return result.Id != nil && err == nil && *result.Id == keyId, nil
}
<commit_msg>Ignore whitespace when computing diffs for ssh public key (#25)<commit_after>package softlayer

import (
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/softlayer/softlayer-go/datatypes"
	"github.com/softlayer/softlayer-go/services"
	"github.com/softlayer/softlayer-go/session"
	"github.com/softlayer/softlayer-go/sl"
)

func resourceSoftLayerSSHKey() *schema.Resource {
	return &schema.Resource{
		Create:   resourceSoftLayerSSHKeyCreate,
		Read:     resourceSoftLayerSSHKeyRead,
		Update:   resourceSoftLayerSSHKeyUpdate,
		Delete:   resourceSoftLayerSSHKeyDelete,
		Exists:   resourceSoftLayerSSHKeyExists,
		Importer: &schema.ResourceImporter{},

		Schema: map[string]*schema.Schema{
			"id": {
				Type:     schema.TypeInt,
				Computed: true,
			},

			"label": {
				Type:     schema.TypeString,
				Required: true,
			},

			"public_key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
					return strings.TrimSpace(old) == strings.TrimSpace(new)
				},
			},

			"fingerprint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"notes": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  nil,
			},
		},
	}
}

func resourceSoftLayerSSHKeyCreate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	// Build up our creation options
	opts := datatypes.Security_Ssh_Key{
		Label: sl.String(d.Get("label").(string)),
		Key:   sl.String(d.Get("public_key").(string)),
	}

	if notes, ok := d.GetOk("notes"); ok {
		opts.Notes = sl.String(notes.(string))
	}

	res, err := service.CreateObject(&opts)
	if err != nil {
		return fmt.Errorf("Error creating SSH Key: %s", err)
	}

	d.SetId(strconv.Itoa(*res.Id))
	log.Printf("[INFO] SSH Key: %d", *res.Id)

	return resourceSoftLayerSSHKeyRead(d, meta)
}

func resourceSoftLayerSSHKeyRead(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, _ := strconv.Atoi(d.Id())

	key, err := service.Id(keyId).GetObject()
	if err != nil {
		// If the key is somehow already destroyed, mark as
		// succesfully gone
		if strings.Contains(err.Error(), "404 Not Found") {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	d.Set("id", *key.Id)
	d.Set("label", *key.Label)
	d.Set("public_key", *key.Key)
	d.Set("fingerprint", *key.Fingerprint)

	if key.Notes != nil {
		d.Set("notes", *key.Notes)
	}

	return nil
}

func resourceSoftLayerSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, _ := strconv.Atoi(d.Id())

	key, err := service.Id(keyId).GetObject()
	if err != nil {
		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	if d.HasChange("label") {
		key.Label = sl.String(d.Get("label").(string))
	}

	if d.HasChange("notes") {
		key.Notes = sl.String(d.Get("notes").(string))
	}

	_, err = service.Id(keyId).EditObject(&key)
	if err != nil {
		return fmt.Errorf("Error editing SSH key: %s", err)
	}
	return nil
}

func resourceSoftLayerSSHKeyDelete(d *schema.ResourceData, meta interface{}) error {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	id, err := strconv.Atoi(d.Id())
	if err != nil {
		return fmt.Errorf("Error deleting SSH Key: %s", err)
	}

	log.Printf("[INFO] Deleting SSH key: %d", id)
	_, err = service.Id(id).DeleteObject()
	if err != nil {
		return fmt.Errorf("Error deleting SSH key: %s", err)
	}

	d.SetId("")
	return nil
}

func resourceSoftLayerSSHKeyExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	sess := meta.(*session.Session)
	service := services.GetSecuritySshKeyService(sess)

	keyId, err := strconv.Atoi(d.Id())
	if err != nil {
		return false, fmt.Errorf("Not a valid ID, must be an integer: %s", err)
	}

	result, err := service.Id(keyId).GetObject()
	return result.Id != nil && err == nil && *result.Id == keyId, nil
}
<|endoftext|>