<commit_before>package static

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"html/template"
	"io"
	"path"
	"strings"
)

type Static struct {
	// The directory where templates can be found.
	SourceDir string
	// The directory where files will be written when building.
	BuildDir string
	// The number of files that will be built concurrently, default 50.
	BuildConcurrency int
	// The port served from when in serve mode, default 4567.
	ServerPort int

	// Functions available to templates.
	TemplateFuncs template.FuncMap

	*static
}

type static struct {
	// Templates cached.
	templates map[string]*template.Template
	// Pages registered.
	pages map[string]*Page
}

// Create a new Static with defaults.
func New() Static {
	return Static{
		SourceDir:        "source",
		BuildDir:         "build",
		BuildConcurrency: 50,
		ServerPort:       4567,
		TemplateFuncs: template.FuncMap{
			"UnsafeHTML": unsafeHTML,
			"ToLower":    strings.ToLower,
			"ToUpper":    strings.ToUpper,
		},
		static: &static{
			templates: make(map[string]*template.Template),
			pages:     make(map[string]*Page),
		},
	}
}

// Register a page with a relative path and function to call when the page is served or built.
func (s Static) Page(path string, pageFunc PageFunc) {
	s.pages[path] = &Page{
		Path: path,
		Func: pageFunc,
	}
}

var errNotFound = errors.New("No handler for path")

func (s Static) handleRequest(w io.Writer, path string, ignoreCache bool) error {
	page := s.getPageForPath(path)
	if page == nil {
		return errNotFound
	}
	data, tmpls, tmpl := page.Func(path)
	err := s.writeResponse(w, data, tmpls, tmpl, ignoreCache)
	return err
}

func (s Static) getPageForPath(path string) *Page {
	return s.pages[path]
}

func (s Static) expandTemplatePaths(tmpl []string) []string {
	expandedTmpl := make([]string, len(tmpl))
	for i, t := range tmpl {
		expandedTmpl[i] = s.templatePath(t)
	}
	return expandedTmpl
}

func getTemplateCacheHash(tmpl []string) string {
	h := sha1.New()
	for _, t := range tmpl {
		io.WriteString(h, t)
	}
	return fmt.Sprintf("% x", h.Sum(nil))
}

func (s Static) getTemplates(tmpl []string, ignoreCache bool) (*template.Template, error) {
	var err error
	tmpl = s.expandTemplatePaths(tmpl)
	h := getTemplateCacheHash(tmpl)
	if s.templates[h] == nil || ignoreCache {
		s.templates[h], err = template.New("all").Funcs(s.TemplateFuncs).ParseFiles(tmpl...)
	}
	return s.templates[h], err
}

func (s Static) writeResponse(w io.Writer, data interface{}, tmpls []string, tmpl string, ignoreCache bool) error {
	templates, err := s.getTemplates(tmpls, ignoreCache)
	if err != nil {
		return err
	}
	return templates.ExecuteTemplate(w, tmpl, data)
}

func (s Static) templatePath(filename string) string {
	return path.Join(s.SourceDir, filename)
}

func unsafeHTML(s string) template.HTML {
	return template.HTML(s)
}
<commit_msg>Reorganizing functions in static.go<commit_after>package static

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"html/template"
	"io"
	"path"
	"strings"
)

type Static struct {
	// The directory where templates can be found.
	SourceDir string
	// The directory where files will be written when building.
	BuildDir string
	// The number of files that will be built concurrently, default 50.
	BuildConcurrency int
	// The port served from when in serve mode, default 4567.
	ServerPort int

	// Functions available to templates.
	TemplateFuncs template.FuncMap

	*static
}

type static struct {
	// Templates cached.
	templates map[string]*template.Template
	// Pages registered.
	pages map[string]*Page
}

// Create a new Static with defaults.
func New() Static {
	return Static{
		SourceDir:        "source",
		BuildDir:         "build",
		BuildConcurrency: 50,
		ServerPort:       4567,
		TemplateFuncs: template.FuncMap{
			"UnsafeHTML": unsafeHTML,
			"ToLower":    strings.ToLower,
			"ToUpper":    strings.ToUpper,
		},
		static: &static{
			templates: make(map[string]*template.Template),
			pages:     make(map[string]*Page),
		},
	}
}

// Register a page with a relative path and function to call when the page is served or built.
func (s Static) Page(path string, pageFunc PageFunc) {
	s.pages[path] = &Page{
		Path: path,
		Func: pageFunc,
	}
}

var errNotFound = errors.New("No handler for path")

func (s Static) handleRequest(w io.Writer, path string, ignoreCache bool) error {
	page := s.getPageForPath(path)
	if page == nil {
		return errNotFound
	}
	data, tmpls, tmpl := page.Func(path)
	err := s.writeResponse(w, data, tmpls, tmpl, ignoreCache)
	return err
}

func (s Static) writeResponse(w io.Writer, data interface{}, tmpls []string, tmpl string, ignoreCache bool) error {
	templates, err := s.getTemplates(tmpls, ignoreCache)
	if err != nil {
		return err
	}
	return templates.ExecuteTemplate(w, tmpl, data)
}

func (s Static) getPageForPath(path string) *Page {
	return s.pages[path]
}

func (s Static) expandTemplatePaths(tmpl []string) []string {
	expandedTmpl := make([]string, len(tmpl))
	for i, t := range tmpl {
		expandedTmpl[i] = s.templatePath(t)
	}
	return expandedTmpl
}

func getTemplateCacheHash(tmpl []string) string {
	h := sha1.New()
	for _, t := range tmpl {
		io.WriteString(h, t)
	}
	return fmt.Sprintf("% x", h.Sum(nil))
}

func (s Static) getTemplates(tmpl []string, ignoreCache bool) (*template.Template, error) {
	var err error
	tmpl = s.expandTemplatePaths(tmpl)
	h := getTemplateCacheHash(tmpl)
	if s.templates[h] == nil || ignoreCache {
		s.templates[h], err = template.New("all").Funcs(s.TemplateFuncs).ParseFiles(tmpl...)
	}
	return s.templates[h], err
}

func (s Static) templatePath(filename string) string {
	return path.Join(s.SourceDir, filename)
}

func unsafeHTML(s string) template.HTML {
	return template.HTML(s)
}
<|endoftext|>


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

package steven

import (
	"fmt"
	"math"
	"runtime"
	"time"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol/mojang"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/resource"
	"github.com/thinkofdeath/steven/ui"
)

var (
	profile       mojang.Profile
	server        string
	loadChan      = make(chan struct{})
	currentScreen screen
	connected     bool

	stevenBuildVersion string = "dev"
)

func stevenVersion() string {
	return fmt.Sprintf("%s-%s", resource.ResourcesVersion, stevenBuildVersion)
}

type screen interface {
	init()
	tick(delta float64)
	hover(x, y float64, w, h int)
	click(down bool, x, y float64, w, h int)
	remove()
}

func setScreen(s screen) {
	if currentScreen != nil {
		currentScreen.remove()
	}
	currentScreen = s
	if s != nil {
		Client.scene.Hide()
		Client.hotbarScene.Hide()
		lockMouse = false
		window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
		for i := range Client.KeyState {
			Client.KeyState[i] = false
		}
		s.init()
	} else {
		Client.scene.Show()
		Client.hotbarScene.Show()
	}
}

func Main(username, uuid, accessToken, s string) {
	profile = mojang.Profile{
		Username:    username,
		ID:          uuid,
		AccessToken: accessToken,
	}
	server = s

	for _, pck := range Config.Game.ResourcePacks {
		resource.LoadZip(pck)
	}
	loadBiomes()

	if profile.IsComplete() && server != "" {
		// Start connecting whilst starting the renderer
		connect()
	}

	setUIScale()

	startWindow()
}

func connect() {
	initClient()
	connected = true
	disconnectReason.Value = nil
	Client.network.Connect(profile, server)
	server = ""
}

func setUIScale() {
	switch Config.Game.UIScale {
	case uiAuto:
		ui.DrawMode = ui.Scaled
		ui.Scale = 1.0
	case uiSmall:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 0.4
	case uiMedium:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 0.6
	case uiLarge:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 1.0
	}
	ui.ForceDraw()
}

func start() {
	render.LoadTextures()
	initBlocks()
	if Client == nil {
		initClient()
		fakeGen()
		if !profile.IsComplete() {
			setScreen(newLoginScreen())
		} else {
			setScreen(newServerList())
		}
	}
	render.FOV = Config.Render.FOV
	render.Start()
}

func rotate(x, y float64) {
	Client.Yaw -= x
	Client.Pitch -= y
	if Client.Pitch < (math.Pi/2)+0.01 {
		Client.Pitch = (math.Pi / 2) + 0.01
	}
	if Client.Pitch > (math.Pi/2)*3-0.01 {
		Client.Pitch = (math.Pi/2)*3 - 0.01
	}
}

var maxBuilders = runtime.NumCPU() * 2

var (
	ready            bool
	freeBuilders     = maxBuilders
	completeBuilders = make(chan buildPos, maxBuilders)
	syncChan         = make(chan func(), 200)
	ticker           = time.NewTicker(time.Second / 20)
	lastFrame        = time.Now()
)

func handleErrors() {
handle:
	for {
		select {
		case err := <-Client.network.Error():
			if !connected {
				continue
			}
			connected = false

			Client.network.Close()
			fmt.Printf("Disconnected: %s\n", err)
			// Reset the ready state to stop packets from being
			// sent.
			ready = false
			if err != errManualDisconnect && disconnectReason.Value == nil {
				txt := &chat.TextComponent{Text: err.Error()}
				txt.Color = chat.Red
				disconnectReason.Value = txt
			}

			if Client.entity != nil && Client.entityAdded {
				Client.entityAdded = false
				Client.entities.container.RemoveEntity(Client.entity)
			}

			setScreen(newServerList())
		default:
			break handle
		}
	}
}

func draw() {
	now := time.Now()
	diff := now.Sub(lastFrame)
	lastFrame = now
	delta := float64(diff.Nanoseconds()) / (float64(time.Second) / 60)
handle:
	for {
		select {
		case packet := <-Client.network.Read():
			defaultHandler.Handle(packet)
		case pos := <-completeBuilders:
			freeBuilders++
			if c := chunkMap[chunkPosition{pos.X, pos.Z}]; c != nil {
				if s := c.Sections[pos.Y]; s != nil {
					s.building = false
				}
			}
		case f := <-syncChan:
			f()
		default:
			break handle
		}
	}
	handleErrors()

	width, height := window.GetFramebufferSize()

	if currentScreen != nil {
		currentScreen.tick(delta)
	}

	if ready && Client != nil {
		Client.renderTick(delta)
		select {
		case <-ticker.C:
			tick()
		default:
		}
	} else {
		render.Camera.Yaw += 0.005 * delta
		if render.Camera.Yaw > math.Pi*2 {
			render.Camera.Yaw = 0
		}
	}
	ui.Draw(width, height, delta)

	render.Draw(width, height, delta)
	chunks := sortedChunks()

	// Search for 'dirty' chunk sections and start building
	// them if we have any builders free. To prevent race conditions
	// two flags are used, dirty and building, to allow a second
	// build to be requested whilst the chunk is still building
	// without either losing the change or having two builds
	// for the same section going on at once (where the second
	// could finish quicker causing the old version to be
	// displayed.
dirtyClean:
	for _, c := range chunks {
		for _, s := range c.Sections {
			if s == nil {
				continue
			}
			if freeBuilders <= 0 {
				break dirtyClean
			}
			if s.dirty && !s.building {
				freeBuilders--
				s.dirty = false
				s.building = true
				s.build(completeBuilders)
			}
		}
	}
}

// tick is called 20 times a second (bar any preformance issues).
// Minecraft is built around this fact so we have to follow it
// as well.
func tick() {
	Client.tick()
}
<commit_msg>steven: remove the 'quick connect' short-cut<commit_after>// Copyright 2015 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package steven

import (
	"fmt"
	"math"
	"runtime"
	"time"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol/mojang"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/resource"
	"github.com/thinkofdeath/steven/ui"
)

var (
	profile       mojang.Profile
	server        string
	loadChan      = make(chan struct{})
	currentScreen screen
	connected     bool

	stevenBuildVersion string = "dev"
)

func stevenVersion() string {
	return fmt.Sprintf("%s-%s", resource.ResourcesVersion, stevenBuildVersion)
}

type screen interface {
	init()
	tick(delta float64)
	hover(x, y float64, w, h int)
	click(down bool, x, y float64, w, h int)
	remove()
}

func setScreen(s screen) {
	if currentScreen != nil {
		currentScreen.remove()
	}
	currentScreen = s
	if s != nil {
		Client.scene.Hide()
		Client.hotbarScene.Hide()
		lockMouse = false
		window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
		for i := range Client.KeyState {
			Client.KeyState[i] = false
		}
		s.init()
	} else {
		Client.scene.Show()
		Client.hotbarScene.Show()
	}
}

func Main(username, uuid, accessToken, s string) {
	profile = mojang.Profile{
		Username:    username,
		ID:          uuid,
		AccessToken: accessToken,
	}
	server = s

	for _, pck := range Config.Game.ResourcePacks {
		resource.LoadZip(pck)
	}
	loadBiomes()

	setUIScale()

	startWindow()
}

func connect() {
	initClient()
	connected = true
	disconnectReason.Value = nil
	Client.network.Connect(profile, server)
	server = ""
}

func setUIScale() {
	switch Config.Game.UIScale {
	case uiAuto:
		ui.DrawMode = ui.Scaled
		ui.Scale = 1.0
	case uiSmall:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 0.4
	case uiMedium:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 0.6
	case uiLarge:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 1.0
	}
	ui.ForceDraw()
}

func start() {
	render.LoadTextures()
	initBlocks()

	if profile.IsComplete() && server != "" {
		connect()
	} else {
		initClient()
		fakeGen()
		if !profile.IsComplete() {
			setScreen(newLoginScreen())
		} else {
			setScreen(newServerList())
		}
	}
	render.FOV = Config.Render.FOV
	render.Start()
}

func rotate(x, y float64) {
	Client.Yaw -= x
	Client.Pitch -= y
	if Client.Pitch < (math.Pi/2)+0.01 {
		Client.Pitch = (math.Pi / 2) + 0.01
	}
	if Client.Pitch > (math.Pi/2)*3-0.01 {
		Client.Pitch = (math.Pi/2)*3 - 0.01
	}
}

var maxBuilders = runtime.NumCPU() * 2

var (
	ready            bool
	freeBuilders     = maxBuilders
	completeBuilders = make(chan buildPos, maxBuilders)
	syncChan         = make(chan func(), 200)
	ticker           = time.NewTicker(time.Second / 20)
	lastFrame        = time.Now()
)

func handleErrors() {
handle:
	for {
		select {
		case err := <-Client.network.Error():
			if !connected {
				continue
			}
			connected = false

			Client.network.Close()
			fmt.Printf("Disconnected: %s\n", err)
			// Reset the ready state to stop packets from being
			// sent.
			ready = false
			if err != errManualDisconnect && disconnectReason.Value == nil {
				txt := &chat.TextComponent{Text: err.Error()}
				txt.Color = chat.Red
				disconnectReason.Value = txt
			}

			if Client.entity != nil && Client.entityAdded {
				Client.entityAdded = false
				Client.entities.container.RemoveEntity(Client.entity)
			}

			setScreen(newServerList())
		default:
			break handle
		}
	}
}

func draw() {
	now := time.Now()
	diff := now.Sub(lastFrame)
	lastFrame = now
	delta := float64(diff.Nanoseconds()) / (float64(time.Second) / 60)
handle:
	for {
		select {
		case packet := <-Client.network.Read():
			defaultHandler.Handle(packet)
		case pos := <-completeBuilders:
			freeBuilders++
			if c := chunkMap[chunkPosition{pos.X, pos.Z}]; c != nil {
				if s := c.Sections[pos.Y]; s != nil {
					s.building = false
				}
			}
		case f := <-syncChan:
			f()
		default:
			break handle
		}
	}
	handleErrors()

	width, height := window.GetFramebufferSize()

	if currentScreen != nil {
		currentScreen.tick(delta)
	}

	if ready && Client != nil {
		Client.renderTick(delta)
		select {
		case <-ticker.C:
			tick()
		default:
		}
	} else {
		render.Camera.Yaw += 0.005 * delta
		if render.Camera.Yaw > math.Pi*2 {
			render.Camera.Yaw = 0
		}
	}
	ui.Draw(width, height, delta)

	render.Draw(width, height, delta)
	chunks := sortedChunks()

	// Search for 'dirty' chunk sections and start building
	// them if we have any builders free. To prevent race conditions
	// two flags are used, dirty and building, to allow a second
	// build to be requested whilst the chunk is still building
	// without either losing the change or having two builds
	// for the same section going on at once (where the second
	// could finish quicker causing the old version to be
	// displayed.
dirtyClean:
	for _, c := range chunks {
		for _, s := range c.Sections {
			if s == nil {
				continue
			}
			if freeBuilders <= 0 {
				break dirtyClean
			}
			if s.dirty && !s.building {
				freeBuilders--
				s.dirty = false
				s.building = true
				s.build(completeBuilders)
			}
		}
	}
}

// tick is called 20 times a second (bar any preformance issues).
// Minecraft is built around this fact so we have to follow it
// as well.
func tick() {
	Client.tick()
}
<|endoftext|>


<commit_before>package sock

import (
	"net"
	"testing"
)

func TestJoin(t *testing.T) {
	ifname := "eth2"

	mcastSock, err1 := MulticastListener(2000, "lo")
	if err1 != nil {
		t.Errorf("Unable to create multicast listener socket: %v", err1)
		return
	}

	group := net.IPv4(224, 0, 0, 9)

	if err := Join(mcastSock, group, ifname); err != nil {
		t.Errorf("Unable to join multicast group: %v", err)
		Close(mcastSock)
		return
	}

	data := []byte{0}
	dst := net.UDPAddr{IP: net.IPv4(1, 0, 0, 1), Port: 3000}
	n, err2 := mcastSock.U.WriteToUDP(data, &dst)
	if err2 != nil {
		t.Errorf("Error sending to %v,%d: %v", dst.IP, dst.Port, err2)
		Close(mcastSock)
		return
	}

	if n != len(data) {
		t.Errorf("Partial send: %d of %d", n, len(data))
		Close(mcastSock)
		return
	}

	ifi, err3 := net.InterfaceByName(ifname)
	if err3 != nil {
		t.Errorf("Unable to get interface: %v", err3)
		Close(mcastSock)
		return
	}

	if err := Leave(mcastSock, group, ifi); err != nil {
		t.Errorf("Unable to leave multicast group: %v", err)
		Close(mcastSock)
		return
	}

	Close(mcastSock)

}
<commit_msg>Fix interface for multicast join test.<commit_after>package sock

import (
	"net"
	"testing"
)

func TestJoin(t *testing.T) {
	ifname := "lo"

	mcastSock, err1 := MulticastListener(2000, ifname)
	if err1 != nil {
		t.Errorf("Unable to create multicast listener socket: %v", err1)
		return
	}

	group := net.IPv4(224, 0, 0, 9)

	if err := Join(mcastSock, group, ifname); err != nil {
		t.Errorf("Unable to join multicast group: %v", err)
		Close(mcastSock)
		return
	}

	data := []byte{0}
	dst := net.UDPAddr{IP: net.IPv4(1, 0, 0, 1), Port: 3000}
	n, err2 := mcastSock.U.WriteToUDP(data, &dst)
	if err2 != nil {
		t.Errorf("Error sending to %v,%d: %v", dst.IP, dst.Port, err2)
		Close(mcastSock)
		return
	}

	if n != len(data) {
		t.Errorf("Partial send: %d of %d", n, len(data))
		Close(mcastSock)
		return
	}

	ifi, err3 := net.InterfaceByName(ifname)
	if err3 != nil {
		t.Errorf("Unable to get interface: %v", err3)
		Close(mcastSock)
		return
	}

	if err := Leave(mcastSock, group, ifi); err != nil {
		t.Errorf("Unable to leave multicast group: %v", err)
		Close(mcastSock)
		return
	}

	Close(mcastSock)

}
<|endoftext|>


<commit_before>package socketApi

import (
	"encoding/json"
	"github.com/CapillarySoftware/gostat/repo"
	"github.com/CapillarySoftware/gostat/stat"
	log "github.com/cihub/seelog"
	"github.com/googollee/go-socket.io"
	"net/http"
	"time"
)

type rawStat struct {
	// UNIX EPOCH Timestamp specifies the moment in time the statistic is applicable to
	Ts int64 `json:"ts"`

	// Value is the numeric representation of the statistic
	Value float64 `json:"value"`
}

func SocketApiServer() {
	server, err := socketio.NewServer(nil)
	if err != nil {
		log.Error(err)
	}

	server.On("connection", func(so socketio.Socket) {
		log.Debug("on connection")
		so.Join("chat")
		so.On("rawStats", func(msg string) {

			log.Debug("got chat message: ", msg)
			so.Emit("chat message", "reply: "+msg)

			const longForm = "2006-01-02 15:04:05-0700"
			startDate, _ := time.Parse(longForm, "2014-09-30 20:50:18-0600")
			endDate, _ := time.Parse(longForm, "2014-09-30 21:50:15-0600")
			rawStats, _ := repo.GetRawStats("stat8", startDate, endDate)

			so.Emit("rawStats", toJson(rawStats))
		})
		so.On("disconnection", func() {
			log.Debug("on disconnect")
		})
	})
	server.On("error", func(so socketio.Socket, err error) {
		log.Error("SocketApiServer: ", err)
	})

	http.Handle("/socket.io/", server)
	http.Handle("/", http.FileServer(http.Dir("./asset")))
	log.Debug("socket.io API serving at localhost:5000...")
	log.Error(http.ListenAndServe(":5000", nil))
}

func runRawLogQuery(req string) {

}

func toJson(stats []stat.Stat) string {
	converted := make([]rawStat, 0)

	for _, stat := range stats {
		c := rawStat{Ts: stat.Timestamp.Unix(), Value: stat.Value}
		converted = append(converted, c)
	}

	convertedJson, _ := json.Marshal(converted)

	return string(convertedJson)
}
<commit_msg>chat -> rawStats<commit_after>package socketApi

import (
	"encoding/json"
	"github.com/CapillarySoftware/gostat/repo"
	"github.com/CapillarySoftware/gostat/stat"
	log "github.com/cihub/seelog"
	"github.com/googollee/go-socket.io"
	"net/http"
	"time"
)

type rawStat struct {
	// UNIX EPOCH Timestamp specifies the moment in time the statistic is applicable to
	Ts int64 `json:"ts"`

	// Value is the numeric representation of the statistic
	Value float64 `json:"value"`
}

type rawStatsRequest struct {
	Name      string  `json:"name"`
	StartDate float64 `json:"startDate"`
	EndDate   float64 `json:"endDate"`
}

func SocketApiServer() {
	server, err := socketio.NewServer(nil)
	if err != nil {
		log.Error(err)
	}

	server.On("connection", func(so socketio.Socket) {
		log.Debug("on connection")
		so.Join("chat")
		so.On("rawStats", func(msg string) {

			log.Debug("got chat message: ", msg)
			so.Emit("rawStats", "reply: "+msg)

			const longForm = "2006-01-02 15:04:05-0700"
			startDate, _ := time.Parse(longForm, "2014-09-30 20:50:18-0600")
			endDate, _ := time.Parse(longForm, "2014-09-30 21:50:15-0600")
			rawStats, _ := repo.GetRawStats("stat8", startDate, endDate)

			so.Emit("rawStats", toJson(rawStats))
		})
		so.On("disconnection", func() {
			log.Debug("on disconnect")
		})
	})
	server.On("error", func(so socketio.Socket, err error) {
		log.Error("SocketApiServer: ", err)
	})

	http.Handle("/socket.io/", server)
	http.Handle("/", http.FileServer(http.Dir("./asset")))
	log.Debug("socket.io API serving at localhost:5000...")
	log.Error(http.ListenAndServe(":5000", nil))
}

func runRawLogQuery(req string) {

}

func toJson(stats []stat.Stat) string {
	converted := make([]rawStat, 0)

	for _, stat := range stats {
		c := rawStat{Ts: stat.Timestamp.Unix(), Value: stat.Value}
		converted = append(converted, c)
	}

	convertedJson, _ := json.Marshal(converted)

	return string(convertedJson)
}
<|endoftext|>


<commit_before>package hdf5

// #cgo LDFLAGS: -lhdf5 -lhdf5_hl
// #cgo darwin CFLAGS: -I/opt/local/include
// #cgo darwin LDFLAGS: -L/opt/local/lib
// #include "hdf5.h"
import "C"
<commit_msg>Update library and include paths for homebrew on darwin.<commit_after>package hdf5

// #cgo LDFLAGS: -lhdf5 -lhdf5_hl
// #cgo darwin CFLAGS: -I/usr/local/include
// #cgo darwin LDFLAGS: -L/usr/local/lib
// #include "hdf5.h"
import "C"
<|endoftext|>


<commit_before>package termite

import (
	"fmt"
	"log"
	"net"
	"os"
	"rpc"
	"sync"
	"strings"
	"runtime"
	"time"
)

var _ = log.Println

type WorkReply struct {
	Exit   *os.Waitmsg
	Files  []AttrResponse
	Stderr string
	Stdout string
}

type WorkRequest struct {
	// Id of connection streaming stdin.
	StdinId      string
	Debug        bool
	WritableRoot string
	Binary       string
	Argv         []string
	Env          []string
	Dir          string
}

func (me *WorkRequest) Summary() string {
	return fmt.Sprintf("stdin %s cmd %s", me.StdinId, me.Argv)
}

type WorkerDaemon struct {
	secret         []byte
	ChrootBinary   string
	httpStatusPort int

	contentCache   *ContentCache
	contentServer  *ContentServer
	maxJobCount    int
	pending        *PendingConnections
	cacheDir       string
	tmpDir         string
	mirrorMapMutex sync.Mutex
	mirrorMap      map[string]*Mirror
}

func (me *WorkerDaemon) getMirror(rpcConn, revConn net.Conn, reserveCount int) (*Mirror, os.Error) {
	if reserveCount <= 0 {
		return nil, os.NewError("must ask positive jobcount")
	}
	me.mirrorMapMutex.Lock()
	defer me.mirrorMapMutex.Unlock()
	used := 0
	for _, v := range me.mirrorMap {
		used += v.maxJobCount
	}

	remaining := me.maxJobCount - used
	if remaining <= 0 {
		return nil, os.NewError("no processes available")
	}
	if remaining < reserveCount {
		reserveCount = remaining
	}

	mirror := NewMirror(me, rpcConn, revConn)
	mirror.maxJobCount = reserveCount
	key := fmt.Sprintf("%v", rpcConn.RemoteAddr())
	me.mirrorMap[key] = mirror
	mirror.key = key
	return mirror, nil
}

func NewWorkerDaemon(secret []byte, tmpDir string, cacheDir string, jobs int) *WorkerDaemon {
	cache := NewContentCache(cacheDir)
	w := &WorkerDaemon{
		secret:        secret,
		contentCache:  cache,
		mirrorMap:     make(map[string]*Mirror),
		contentServer: &ContentServer{Cache: cache},
		pending:       NewPendingConnections(),
		maxJobCount:   jobs,
		tmpDir:        tmpDir,
	}
	return w
}

const _REPORT_DELAY = 60.0

func (me *WorkerDaemon) PeriodicReport(coordinator string, port int) {
	if coordinator == "" {
		log.Println("No coordinator - not doing period reports.")
		return
	}
	me.report(coordinator, port)
	for {
		c := time.After(_REPORT_DELAY * 1e9)
		<-c
		me.report(coordinator, port)
	}
}

func (me *WorkerDaemon) report(coordinator string, port int) {
	client, err := rpc.DialHTTP("tcp", coordinator)
	if err != nil {
		log.Println("dialing coordinator:", err)
		return
	}

	hostname, err := os.Hostname()
	if err != nil {
		log.Println("hostname", err)
		return
	}

	cname, err := net.LookupCNAME(hostname)
	if err != nil {
		log.Println("cname", err)
		return
	}
	cname = strings.TrimRight(cname, ".")
	req := Registration{
		Address: fmt.Sprintf("%v:%d", cname, port),
		Name:    fmt.Sprintf("%s:%d", hostname, port),
		Version: Version(),
	}
	if me.httpStatusPort != 0 {
		req.HttpStatusAddress = fmt.Sprintf("%v:%d", cname, me.httpStatusPort)
	}

	rep := 0
	err = client.Call("Coordinator.Register", &req, &rep)
	if err != nil {
		log.Println("coordinator rpc error:", err)
	}
}

// TODO - should expose under ContentServer name?
func (me *WorkerDaemon) FileContent(req *ContentRequest, rep *ContentResponse) os.Error {
	return me.contentServer.FileContent(req, rep)
}

func trim(s string) string {
	l := 1024
	if len(s) < l {
		l = len(s)
	}
	return s[:l]
}

type CreateMirrorRequest struct {
	RpcId        string
	RevRpcId     string
	WritableRoot string
	// Max number of processes to reserve.
	MaxJobCount int
}

type CreateMirrorResponse struct {
	MaxJobCount int
}

func (me *WorkerDaemon) CreateMirror(req *CreateMirrorRequest, rep *CreateMirrorResponse) os.Error {
	log.Println("CreateMirror")
	rpcConn := me.pending.WaitConnection(req.RpcId)
	revConn := me.pending.WaitConnection(req.RevRpcId)
	mirror, err := me.getMirror(rpcConn, revConn, req.MaxJobCount)
	if err != nil {
		return err
	}
	mirror.writableRoot = req.WritableRoot

	rep.MaxJobCount = mirror.maxJobCount
	return nil
}
func (me *WorkerDaemon) DropMirror(mirror *Mirror) {
	me.mirrorMapMutex.Lock()
	defer me.mirrorMapMutex.Unlock()

	log.Println("dropping mirror", mirror.key)
	me.mirrorMap[mirror.key] = nil, false

	runtime.GC()
}

func (me *WorkerDaemon) RunWorkerServer(port int, coordinator string) {
	out := make(chan net.Conn)
	go SetupServer(port, me.secret, out)
	go me.PeriodicReport(coordinator, port)

	for {
		conn := <-out
		log.Println("Authenticated connection from", conn.RemoteAddr())
		if !me.pending.Accept(conn) {
			rpcServer := rpc.NewServer()
			rpcServer.Register(me)
			go rpcServer.ServeConn(conn)
		}
	}
}

type StatusRequest struct {

}

type StatusReply struct {
	Processes int
}

func (me *WorkerDaemon) Status(req *StatusRequest, rep *StatusReply) os.Error {
	me.mirrorMapMutex.Lock()
	defer me.mirrorMapMutex.Unlock()
	for _, mirror := range me.mirrorMap {
		mirror.Status(req, rep)
	}

	// Always return nil, so we know any errors are due to connection problems.
	return nil
}
<commit_msg>Close rpc/reverse connections if createMirror failed.<commit_after>package termite

import (
	"fmt"
	"log"
	"net"
	"os"
	"rpc"
	"sync"
	"strings"
	"runtime"
	"time"
)

var _ = log.Println

type WorkReply struct {
	Exit   *os.Waitmsg
	Files  []AttrResponse
	Stderr string
	Stdout string
}

type WorkRequest struct {
	// Id of connection streaming stdin.
	StdinId      string
	Debug        bool
	WritableRoot string
	Binary       string
	Argv         []string
	Env          []string
	Dir          string
}

func (me *WorkRequest) Summary() string {
	return fmt.Sprintf("stdin %s cmd %s", me.StdinId, me.Argv)
}

type WorkerDaemon struct {
	secret         []byte
	ChrootBinary   string
	httpStatusPort int

	contentCache   *ContentCache
	contentServer  *ContentServer
	maxJobCount    int
	pending        *PendingConnections
	cacheDir       string
	tmpDir         string
	mirrorMapMutex sync.Mutex
	mirrorMap      map[string]*Mirror
}

func (me *WorkerDaemon) getMirror(rpcConn, revConn net.Conn, reserveCount int) (*Mirror, os.Error) {
	if reserveCount <= 0 {
		return nil, os.NewError("must ask positive jobcount")
	}
	me.mirrorMapMutex.Lock()
	defer me.mirrorMapMutex.Unlock()
	used := 0
	for _, v := range me.mirrorMap {
		used += v.maxJobCount
	}

	remaining := me.maxJobCount - used
	if remaining <= 0 {
		return nil, os.NewError("no processes available")
	}
	if remaining < reserveCount {
		reserveCount = remaining
	}

	mirror := NewMirror(me, rpcConn, revConn)
	mirror.maxJobCount = reserveCount
	key := fmt.Sprintf("%v", rpcConn.RemoteAddr())
	me.mirrorMap[key] = mirror
	mirror.key = key
	return mirror, nil
}

func NewWorkerDaemon(secret []byte, tmpDir string, cacheDir string, jobs int) *WorkerDaemon {
	cache := NewContentCache(cacheDir)
	w := &WorkerDaemon{
		secret:        secret,
		contentCache:  cache,
		mirrorMap:     make(map[string]*Mirror),
		contentServer: &ContentServer{Cache: cache},
		pending:       NewPendingConnections(),
		maxJobCount:   jobs,
		tmpDir:        tmpDir,
	}
	return w
}

const _REPORT_DELAY = 60.0

func (me *WorkerDaemon) PeriodicReport(coordinator string, port int) {
	if coordinator == "" {
		log.Println("No coordinator - not doing period reports.")
		return
	}
	me.report(coordinator, port)
	for {
		c := time.After(_REPORT_DELAY * 1e9)
		<-c
		me.report(coordinator, port)
	}
}

func (me *WorkerDaemon) report(coordinator string, port int) {
	client, err := rpc.DialHTTP("tcp", coordinator)
	if err != nil {
		log.Println("dialing coordinator:", err)
		return
	}

	hostname, err := os.Hostname()
	if err != nil {
		log.Println("hostname", err)
		return
	}

	cname, err := net.LookupCNAME(hostname)
	if err != nil {
		log.Println("cname", err)
		return
	}
	cname = strings.TrimRight(cname, ".")
	req := Registration{
		Address: fmt.Sprintf("%v:%d", cname, port),
		Name:    fmt.Sprintf("%s:%d", hostname, port),
		Version: Version(),
	}
	if me.httpStatusPort != 0 {
		req.HttpStatusAddress = fmt.Sprintf("%v:%d", cname, me.httpStatusPort)
	}

	rep := 0
	err = client.Call("Coordinator.Register", &req, &rep)
	if err != nil {
		log.Println("coordinator rpc error:", err)
	}
}

// TODO - should expose under ContentServer name?
func (me *WorkerDaemon) FileContent(req *ContentRequest, rep *ContentResponse) os.Error {
	return me.contentServer.FileContent(req, rep)
}

func trim(s string) string {
	l := 1024
	if len(s) < l {
		l = len(s)
	}
	return s[:l]
}

type CreateMirrorRequest struct {
	RpcId        string
	RevRpcId     string
	WritableRoot string
	// Max number of processes to reserve.
	MaxJobCount int
}

type CreateMirrorResponse struct {
	MaxJobCount int
}

func (me *WorkerDaemon) CreateMirror(req *CreateMirrorRequest, rep *CreateMirrorResponse) os.Error {
	log.Println("CreateMirror")
	rpcConn := me.pending.WaitConnection(req.RpcId)
	revConn := me.pending.WaitConnection(req.RevRpcId)
	mirror, err := me.getMirror(rpcConn, revConn, req.MaxJobCount)
	if err != nil {
		rpcConn.Close()
		revConn.Close()
		return err
	}
	mirror.writableRoot = req.WritableRoot

	rep.MaxJobCount = mirror.maxJobCount
	return nil
}
func (me *WorkerDaemon) DropMirror(mirror *Mirror) {
	me.mirrorMapMutex.Lock()
	defer me.mirrorMapMutex.Unlock()

	log.Println("dropping mirror", mirror.key)
	me.mirrorMap[mirror.key] = nil, false

	runtime.GC()
}

func (me *WorkerDaemon) RunWorkerServer(port int, coordinator string) {
	out := make(chan net.Conn)
	go SetupServer(port, me.secret, out)
	go me.PeriodicReport(coordinator, port)

	for {
		conn := <-out
		log.Println("Authenticated connection from", conn.RemoteAddr())
		if !me.pending.Accept(conn) {
			rpcServer := rpc.NewServer()
			rpcServer.Register(me)
			go rpcServer.ServeConn(conn)
		}
	}
}

type StatusRequest struct {

}

type StatusReply struct {
	Processes int
}

func (me *WorkerDaemon) Status(req *StatusRequest, rep *StatusReply) os.Error {
	me.mirrorMapMutex.Lock()
	defer me.mirrorMapMutex.Unlock()
	for _, mirror := range me.mirrorMap {
		mirror.Status(req, rep)
	}

	// Always return nil, so we know any errors are due to connection problems.
	return nil
}
<|endoftext|>


<commit_before>// +build !windows

package sockets

import (
	"net"
	"os"
	"syscall"
)

// NewUnixSocket creates a unix socket with the specified path and group.
func NewUnixSocket(path string, gid int) (net.Listener, error) {
	if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	mask := syscall.Umask(0777)
	defer syscall.Umask(mask)

	l, err := net.Listen("unix", path)
	if err != nil {
		return nil, err
	}
	if err := os.Chown(path, 0, gid); err != nil {
		return nil, err
	}
	if err := os.Chmod(path, 0660); err != nil {
		l.Close()
		return nil, err
	}
	return l, nil
}
<commit_msg>sockets: close net.Listener after failed Chown<commit_after>// +build !windows

package sockets

import (
	"net"
	"os"
	"syscall"
)

// NewUnixSocket creates a unix socket with the specified path and group.
func NewUnixSocket(path string, gid int) (net.Listener, error) {
	if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	mask := syscall.Umask(0777)
	defer syscall.Umask(mask)

	l, err := net.Listen("unix", path)
	if err != nil {
		return nil, err
	}
	if err := os.Chown(path, 0, gid); err != nil {
		l.Close()
		return nil, err
	}
	if err := os.Chmod(path, 0660); err != nil {
		l.Close()
		return nil, err
	}
	return l, nil
}
<|endoftext|>


<commit_before>package gokeepasslib

import (
	"reflect"
	"testing"
)

func TestNewEntry(t *testing.T) {
	cases := []struct {
		title         string
		options       []EntryOption
		expectedEntry Entry
	}{
		{
			title:         "without options",
			expectedEntry: Entry{},
		},
		{
			title: "with multiple options",
			options: []EntryOption{
				WithEntryFormattedTime(false),
				func(e *Entry) {
					e.Tags = "Test Tags"
				},
			},
			expectedEntry: Entry{
				Tags: "Test Tags",
			},
		},
	}

	for _, c := range cases {
		t.Run(c.title, func(t *testing.T) {
			entry := NewEntry(c.options...)

			// times and uuids are generated at random.
			// testing them with a clear comparison does not work
			c.expectedEntry.Times = entry.Times
			c.expectedEntry.UUID = entry.UUID

			if !reflect.DeepEqual(entry, c.expectedEntry) {
				t.Errorf(
					"Did not receive expected Entry %+v, received %+v",
					c.expectedEntry,
					entry,
				)
			}

		})
	}
}
<commit_msg>Add entry setKdbxFormatVersion test<commit_after>package gokeepasslib

import (
	"reflect"
	"testing"
)

func TestNewEntry(t *testing.T) {
	cases := []struct {
		title         string
		options       []EntryOption
		expectedEntry Entry
	}{
		{
			title:         "without options",
			expectedEntry: Entry{},
		},
		{
			title: "with multiple options",
			options: []EntryOption{
				WithEntryFormattedTime(false),
				func(e *Entry) {
					e.Tags = "Test Tags"
				},
			},
			expectedEntry: Entry{
				Tags: "Test Tags",
			},
		},
	}

	for _, c := range cases {
		t.Run(c.title, func(t *testing.T) {
			entry := NewEntry(c.options...)

			// times and uuids are generated at random.
			// testing them with a clear comparison does not work
			c.expectedEntry.Times = entry.Times
			c.expectedEntry.UUID = entry.UUID

			if !reflect.DeepEqual(entry, c.expectedEntry) {
				t.Errorf(
					"Did not receive expected Entry %+v, received %+v",
					c.expectedEntry,
					entry,
				)
			}

		})
	}
}

func TestEntrySetKdbxFormatVersion(t *testing.T) {
	cases := []struct {
		title                  string
		formattedInitValue     bool
		version                formatVersion
		expectedFormattedValue bool
	}{
		{
			title:                  "initialized as v3, changed to v4",
			formattedInitValue:     true,
			version:                4,
			expectedFormattedValue: false,
		},
		{
			title:                  "initialized as v4, changed to v3",
			formattedInitValue:     false,
			version:                3,
			expectedFormattedValue: true,
		},
		{
			title:                  "initialized as v3, not changed",
			formattedInitValue:     true,
			version:                3,
			expectedFormattedValue: true,
		},
		{
			title:                  "initialized as v4, not changed",
			formattedInitValue:     false,
			version:                4,
			expectedFormattedValue: false,
		},
	}

	for _, c := range cases {
		t.Run(c.title, func(t *testing.T) {
			entry := NewEntry(
				WithEntryFormattedTime(c.formattedInitValue),
			)

			history := History{
				Entries: []Entry{
					NewEntry(
						WithEntryFormattedTime(c.formattedInitValue),
					),
				},
			}

			entry.Histories = append(entry.Histories, history)

			(&entry).setKdbxFormatVersion(c.version)

			// Takes a single time value as an example, as TimeData is independently tested.
			if entry.Times.CreationTime.Formatted != c.expectedFormattedValue {

				t.Errorf("Failed to set root CreationTime formatted value accordingly")
			}

			if entry.Histories[0].Entries[0].Times.CreationTime.Formatted != c.expectedFormattedValue {

				t.Errorf("Failed to set history CreationTime formatted value accordingly")
			}
		})
	}

}
<|endoftext|>


<commit_before>// Package stripe provides the binding for Stripe REST APIs.
package stripe

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const (
	apiURL     = "https://api.stripe.com/v1"
	uploadsURL = "https://uploads.stripe.com/v1"
)

// apiversion is the currently supported API version
const apiversion = "2015-04-07"

// clientversion is the binding version
const clientversion = "6.3.0"

// defaultHTTPTimeout is the default timeout on the http.Client used by the library.
// This is chosen to be consistent with the other Stripe language libraries and
// to coordinate with other timeouts configured in the Stripe infrastructure.
const defaultHTTPTimeout = 80 * time.Second

// Totalbackends is the total number of Stripe API endpoints supported by the binding.
const TotalBackends = 2

// Backend is an interface for making calls against a Stripe service.
// This interface exists to enable mocking for during testing if needed.
type Backend interface {
	Call(method, path, key string, body *url.Values, params *Params, v interface{}) error
	CallMultipart(method, path, key, boundary string, body io.Reader, params *Params, v interface{}) error
}

// BackendConfiguration is the internal implementation for making HTTP calls to Stripe.
type BackendConfiguration struct {
	Type       SupportedBackend
	URL        string
	HTTPClient *http.Client
}

// SupportedBackend is an enumeration of supported Stripe endpoints.
// Currently supported values are "api" and "uploads".
type SupportedBackend string

const (
	APIBackend     SupportedBackend = "api"
	UploadsBackend SupportedBackend = "uploads"
)

// Backends are the currently supported endpoints.
type Backends struct {
	API, Uploads Backend
}

// Key is the Stripe API key used globally in the binding.
var Key string

// LogLevel is the logging level for this library.
// 0: no logging
// 1: errors only
// 2: errors + informational (default)
// 3: errors + informational + debug
var LogLevel = 2

var httpClient = &http.Client{Timeout: defaultHTTPTimeout}
var backends Backends

// SetHTTPClient overrides the default HTTP client.
// This is useful if you're running in a Google AppEngine environment
// where the http.DefaultClient is not available.
func SetHTTPClient(client *http.Client) {
	httpClient = client
}

// GetBackend returns the currently used backend in the binding.
func GetBackend(backend SupportedBackend) Backend {
	var ret Backend
	switch backend {
	case APIBackend:
		if backends.API == nil {
			backends.API = BackendConfiguration{backend, apiURL, httpClient}
		}

		ret = backends.API
	case UploadsBackend:
		if backends.Uploads == nil {
			backends.Uploads = BackendConfiguration{backend, uploadsURL, httpClient}
		}
		ret = backends.Uploads
	}

	return ret
}

// SetBackend sets the backend used in the binding.
func SetBackend(backend SupportedBackend, b Backend) {
	switch backend {
	case APIBackend:
		backends.API = b
	case UploadsBackend:
		backends.Uploads = b
	}
}

// Call is the Backend.Call implementation for invoking Stripe APIs.
func (s BackendConfiguration) Call(method, path, key string, form *url.Values, params *Params, v interface{}) error {
	var body io.Reader
	if form != nil && len(*form) > 0 {
		data := form.Encode()
		if strings.ToUpper(method) == "GET" {
			path += "?" + data
		} else {
			body = bytes.NewBufferString(data)
		}
	}

	req, err := s.NewRequest(method, path, key, "application/x-www-form-urlencoded", body, params)
	if err != nil {
		return err
	}

	if err := s.Do(req, v); err != nil {
		return err
	}

	return nil
}

// CallMultipart is the Backend.CallMultipart implementation for invoking Stripe APIs.
func (s BackendConfiguration) CallMultipart(method, path, key, boundary string, body io.Reader, params *Params, v interface{}) error {
	contentType := "multipart/form-data; boundary=" + boundary

	req, err := s.NewRequest(method, path, key, contentType, body, params)
	if err != nil {
		return err
	}

	if err := s.Do(req, v); err != nil {
		return err
	}

	return nil
}

// NewRequest is used by Call to generate an http.Request. It handles encoding
// parameters and attaching the appropriate headers.
func (s *BackendConfiguration) NewRequest(method, path, key, contentType string, body io.Reader, params *Params) (*http.Request, error) {
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}

	path = s.URL + path

	req, err := http.NewRequest(method, path, body)
	if err != nil {
		if LogLevel > 0 {
			log.Printf("Cannot create Stripe request: %v\n", err)
		}
		return nil, err
	}

	req.SetBasicAuth(key, "")

	if params != nil {
		if idempotency := strings.TrimSpace(params.IdempotencyKey); idempotency != "" {
			if len(idempotency) > 255 {
				return nil, errors.New("Cannot use an IdempotencyKey longer than 255 characters long.")
			}

			req.Header.Add("Idempotency-Key", idempotency)
		}

		if account := strings.TrimSpace(params.Account); account != "" {
			req.Header.Add("Stripe-Account", account)
		}
	}

	req.Header.Add("Stripe-Version", apiversion)
	req.Header.Add("User-Agent", "Stripe/v1 GoBindings/"+clientversion)
	req.Header.Add("Content-Type", contentType)

	return req, nil
}

// Do is used by Call to execute an API request and parse the response. It uses
// the backend's HTTP client to execute the request and unmarshals the response
// into v. It also handles unmarshaling errors returned by the API.
func (s *BackendConfiguration) Do(req *http.Request, v interface{}) error {
	if LogLevel > 1 {
		log.Printf("Requesting %v %v%v\n", req.Method, req.URL.Host, req.URL.Path)
	}

	start := time.Now()

	res, err := s.HTTPClient.Do(req)

	if LogLevel > 2 {
		log.Printf("Completed in %v\n", time.Since(start))
	}

	if err != nil {
		if LogLevel > 0 {
			log.Printf("Request to Stripe failed: %v\n", err)
		}
		return err
	}
	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		if LogLevel > 0 {
			log.Printf("Cannot parse Stripe response: %v\n", err)
		}
		return err
	}

	if res.StatusCode >= 400 {
		// for some odd reason, the Erro structure doesn't unmarshal
		// initially I thought it was because it's a struct inside of a struct
		// but even after trying that, it still didn't work
		// so unmarshalling to a map for now and parsing the results manually
		// but should investigate later
		var errMap map[string]interface{}
		json.Unmarshal(resBody, &errMap)

		if e, found := errMap["error"]; !found {
			err := errors.New(string(resBody))
			if LogLevel > 0 {
				log.Printf("Unparsable error returned from Stripe: %v\n", err)
			}
			return err
		} else {
			root := e.(map[string]interface{})
			err := &Error{
				Type:           ErrorType(root["type"].(string)),
				Msg:            root["message"].(string),
				HTTPStatusCode: res.StatusCode,
			}

			if code, found := root["code"]; found {
				err.Code = ErrorCode(code.(string))
			}

			if param, found := root["param"]; found {
				err.Param = param.(string)
			}

			if LogLevel > 0 {
				log.Printf("Error encountered from Stripe: %v\n", err)
			}
			return err
		}
	}

	if LogLevel > 2 {
		log.Printf("Stripe Response: %q\n", resBody)
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}
<commit_msg>Exposed logging object<commit_after>// Package stripe provides the binding for Stripe REST APIs.
package stripe

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

const (
	apiURL     = "https://api.stripe.com/v1"
	uploadsURL = "https://uploads.stripe.com/v1"
)

// apiversion is the currently supported API version
const apiversion = "2015-04-07"

// clientversion is the binding version
const clientversion = "6.3.0"

// defaultHTTPTimeout is the default timeout on the http.Client used by the library.
// This is chosen to be consistent with the other Stripe language libraries and
// to coordinate with other timeouts configured in the Stripe infrastructure.
const defaultHTTPTimeout = 80 * time.Second

// Totalbackends is the total number of Stripe API endpoints supported by the binding.
const TotalBackends = 2

// Backend is an interface for making calls against a Stripe service.
// This interface exists to enable mocking for during testing if needed.
type Backend interface {
	Call(method, path, key string, body *url.Values, params *Params, v interface{}) error
	CallMultipart(method, path, key, boundary string, body io.Reader, params *Params, v interface{}) error
}

// BackendConfiguration is the internal implementation for making HTTP calls to Stripe.
type BackendConfiguration struct {
	Type       SupportedBackend
	URL        string
	HTTPClient *http.Client
}

// SupportedBackend is an enumeration of supported Stripe endpoints.
// Currently supported values are "api" and "uploads".
type SupportedBackend string

const (
	APIBackend     SupportedBackend = "api"
	UploadsBackend SupportedBackend = "uploads"
)

// Backends are the currently supported endpoints.
type Backends struct {
	API, Uploads Backend
}

// Key is the Stripe API key used globally in the binding.
var Key string

// LogLevel is the logging level for this library.
// 0: no logging
// 1: errors only
// 2: errors + informational (default)
// 3: errors + informational + debug
var LogLevel = 2

// Logger controls how stripe performs logging at a package level. It is useful
// to customise if you need it prefixed for your application to meet other
// requirements
var Logger *log.Logger

func init() {
	// setup the logger
	Logger = log.New(os.Stderr, "", log.LstdFlags)
}

var httpClient = &http.Client{Timeout: defaultHTTPTimeout}
var backends Backends

// SetHTTPClient overrides the default HTTP client.
// This is useful if you're running in a Google AppEngine environment
// where the http.DefaultClient is not available.
func SetHTTPClient(client *http.Client) {
	httpClient = client
}

// GetBackend returns the currently used backend in the binding.
func GetBackend(backend SupportedBackend) Backend {
	var ret Backend
	switch backend {
	case APIBackend:
		if backends.API == nil {
			backends.API = BackendConfiguration{backend, apiURL, httpClient}
		}

		ret = backends.API
	case UploadsBackend:
		if backends.Uploads == nil {
			backends.Uploads = BackendConfiguration{backend, uploadsURL, httpClient}
		}
		ret = backends.Uploads
	}

	return ret
}

// SetBackend sets the backend used in the binding.
func SetBackend(backend SupportedBackend, b Backend) {
	switch backend {
	case APIBackend:
		backends.API = b
	case UploadsBackend:
		backends.Uploads = b
	}
}

// Call is the Backend.Call implementation for invoking Stripe APIs.
func (s BackendConfiguration) Call(method, path, key string, form *url.Values, params *Params, v interface{}) error {
	var body io.Reader
	if form != nil && len(*form) > 0 {
		data := form.Encode()
		if strings.ToUpper(method) == "GET" {
			path += "?" + data
		} else {
			body = bytes.NewBufferString(data)
		}
	}

	req, err := s.NewRequest(method, path, key, "application/x-www-form-urlencoded", body, params)
	if err != nil {
		return err
	}

	if err := s.Do(req, v); err != nil {
		return err
	}

	return nil
}

// CallMultipart is the Backend.CallMultipart implementation for invoking Stripe APIs.
func (s BackendConfiguration) CallMultipart(method, path, key, boundary string, body io.Reader, params *Params, v interface{}) error {
	contentType := "multipart/form-data; boundary=" + boundary

	req, err := s.NewRequest(method, path, key, contentType, body, params)
	if err != nil {
		return err
	}

	if err := s.Do(req, v); err != nil {
		return err
	}

	return nil
}

// NewRequest is used by Call to generate an http.Request. It handles encoding
// parameters and attaching the appropriate headers.
func (s *BackendConfiguration) NewRequest(method, path, key, contentType string, body io.Reader, params *Params) (*http.Request, error) {
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}

	path = s.URL + path

	req, err := http.NewRequest(method, path, body)
	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Cannot create Stripe request: %v\n", err)
		}
		return nil, err
	}

	req.SetBasicAuth(key, "")

	if params != nil {
		if idempotency := strings.TrimSpace(params.IdempotencyKey); idempotency != "" {
			if len(idempotency) > 255 {
				return nil, errors.New("Cannot use an IdempotencyKey longer than 255 characters long.")
			}

			req.Header.Add("Idempotency-Key", idempotency)
		}

		if account := strings.TrimSpace(params.Account); account != "" {
			req.Header.Add("Stripe-Account", account)
		}
	}

	req.Header.Add("Stripe-Version", apiversion)
	req.Header.Add("User-Agent", "Stripe/v1 GoBindings/"+clientversion)
	req.Header.Add("Content-Type", contentType)

	return req, nil
}

// Do is used by Call to execute an API request and parse the response. It uses
// the backend's HTTP client to execute the request and unmarshals the response
// into v. It also handles unmarshaling errors returned by the API.
func (s *BackendConfiguration) Do(req *http.Request, v interface{}) error {
	if LogLevel > 1 {
		Logger.Printf("Requesting %v %v%v\n", req.Method, req.URL.Host, req.URL.Path)
	}

	start := time.Now()

	res, err := s.HTTPClient.Do(req)

	if LogLevel > 2 {
		Logger.Printf("Completed in %v\n", time.Since(start))
	}

	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Request to Stripe failed: %v\n", err)
		}
		return err
	}
	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Cannot parse Stripe response: %v\n", err)
		}
		return err
	}

	if res.StatusCode >= 400 {
		// for some odd reason, the Erro structure doesn't unmarshal
		// initially I thought it was because it's a struct inside of a struct
		// but even after trying that, it still didn't work
		// so unmarshalling to a map for now and parsing the results manually
		// but should investigate later
		var errMap map[string]interface{}
		json.Unmarshal(resBody, &errMap)

		if e, found := errMap["error"]; !found {
			err := errors.New(string(resBody))
			if LogLevel > 0 {
				Logger.Printf("Unparsable error returned from Stripe: %v\n", err)
			}
			return err
		} else {
			root := e.(map[string]interface{})
			err := &Error{
				Type:           ErrorType(root["type"].(string)),
				Msg:            root["message"].(string),
				HTTPStatusCode: res.StatusCode,
			}

			if code, found := root["code"]; found {
				err.Code = ErrorCode(code.(string))
			}

			if param, found := root["param"]; found {
				err.Param = param.(string)
			}

			if LogLevel > 0 {
				Logger.Printf("Error encountered from Stripe: %v\n", err)
			}
			return err
		}
	}

	if LogLevel > 2 {
		Logger.Printf("Stripe Response: %q\n", resBody)
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}
<|endoftext|>


<commit_before>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-08-22
//  Update on 2014-08-22
//  Email  slowfei#foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"bytes"
)

/**
 *  file info contain parser
 */
type DocFile struct {
	FileCont *bytes.Buffer //
	Parser   DocParser     //
}

/**
 *  output `content.json`
 */
type ContentJson struct {
	HtmlTitle string // html document title
	DocTitle  string // html top show title
	MenuTitle string // html left menu title
}

/**
 *  package info
 */
type PackageInfo struct {
	Name string // package name plain text
	Desc string // description plain text
}

/**
 *  document struct info
 */
type Document struct {
	SortTag int    // sort tag
	Title   string // title plain text
	Content string // markdown text or plain text
}

/**
 *  preview struct info
 */
type Preview struct {
	SortTag  int       // sort tag
	ShowText string    // show plain text
	Anchor   string    // preferably unique, with the func link
	DescText string    // markdown brief description or implement objects, can empty.
	Children []Preview //
}

/**
 *  body code block struct
 */
type CodeBlock struct {
	SortTag    int    // sort tag
	Title      string // function name or custom title
	Desc       string // description markdown text or plain text
	Code       string // show code text
	SourceLink string // source code link text
	Anchor     string // function anchor text. with the Preview link
}
<commit_msg>修改或增加结构FileBuf、CodeFile、CodeFiles<commit_after>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-08-22
//  Update on 2014-08-22
//  Email  slowfei#foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"container/list"
	"os"
	// "regexp"
)

/**
 *	file content buffer
 */
type FileBuf struct {
	path     string
	fileInfo os.FileInfo
	buf      []byte
}

func NewFileBuf(fileContent []byte) *FileBuf {
	buf := new(FileBuf)
	buf.buf = fileContent
	return buf
}

/**
 *	get file path
 *
 *	@return
 */
func (f *FileBuf) Path() string {
	return f.path
}

/**
 *	get file info
 *
 *	@return
 */
func (f *FileBuf) FileInfo() os.FileInfo {
	return f.fileInfo
}

/**
 *  source code file
 */
type CodeFile struct {
	parser      DocParser //
	FileCont    *FileBuf  //
	PrivateDoc  bool      // if private document not output
	PrivateCode bool      // if private source code not output
}

/**
 *	source code file array
 */
type CodeFiles struct {
	files *list.List
}

/**
 *	new CodeFiles
 */
func NewCodeFiles() *CodeFiles {
	cf := new(CodeFiles)
	cf.files = list.New()
	return cf
}

/**
 *	add file
 *
 *	@param file
 */
func (c *CodeFiles) addFile(file CodeFile) {
	if nil == c.files {
		c.files = list.New()
	}
	c.files.PushBack(file)
}

/**
 *	each CodeFile
 *
 *	@param `f` func return true continue
 */
func (c *CodeFiles) Each(f func(file CodeFile) bool) {
	if nil == f {
		return
	}
	for e := c.files.Front(); e != nil; e = e.Next() {
		if !f(e.Value.(CodeFile)) {
			break
		}
	}
}

/**
 *  output `content.json`
 */
type ContentJson struct {
	HtmlTitle string // html document title
	DocTitle  string // html top show title
	MenuTitle string // html left menu title
}

/**
 *  package info
 */
type PackageInfo struct {
	Name string // package name plain text
	Desc string // description plain text
}

/**
 *  document struct info
 */
type Document struct {
	SortTag int    // sort tag
	Title   string // title plain text
	Content string // markdown text or plain text
}

/**
 *  preview struct info
 */
type Preview struct {
	SortTag  int       // sort tag
	ShowText string    // show plain text
	Anchor   string    // preferably unique, with the func link
	DescText string    // markdown brief description or implement objects, can empty.
	Children []Preview //
}

/**
 *  body code block struct
 */
type CodeBlock struct {
	SortTag    int    // sort tag
	Title      string // function name or custom title
	Desc       string // description markdown text or plain text
	Code       string // show code text
	SourceLink string // source code link text
	Anchor     string // function anchor text. with the Preview link
}
<|endoftext|>


<commit_before>package knx

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

	"github.com/vapourismo/knx-go/knx/cemi"
	"github.com/vapourismo/knx-go/knx/proto"
)

// ClientConfig allows you to configure the client's behavior.
type ClientConfig struct {
	// ResendInterval is how long to wait for a response, until the request is resend. A interval
	// <= 0 can't be used. The default value will be used instead.
	ResendInterval time.Duration

	// HeartbeatDelay specifies the time which has to elapse without any incoming communication,
	// until a heartbeat is triggered. A delay <= 0 will result in the use of a default value.
	HeartbeatDelay time.Duration

	// ResponseTimeout specifies how long to wait for a response. A timeout <= 0 will not be
	// accepted. Instead, the default value will be used.
	ResponseTimeout time.Duration
}

// Default configuration elements
var (
	defaultResendInterval  = 500 * time.Millisecond
	defaultHeartbeatDelay  = 10 * time.Second
	defaultResponseTimeout = 10 * time.Second

	DefaultClientConfig = ClientConfig{
		defaultResendInterval,
		defaultHeartbeatDelay,
		defaultResponseTimeout,
	}
)

// checkClientConfig makes sure that the configuration is actually usable.
func checkClientConfig(config ClientConfig) ClientConfig {
	if config.ResendInterval <= 0 {
		config.ResendInterval = defaultResendInterval
	}

	if config.HeartbeatDelay <= 0 {
		config.HeartbeatDelay = defaultHeartbeatDelay
	}

	if config.ResponseTimeout <= 0 {
		config.ResponseTimeout = defaultResponseTimeout
	}

	return config
}

// tunnelConn is a handle for a tunnel connection.
type tunnelConn struct {
	sock    Socket
	config  ClientConfig
	channel uint8
}

// newTunnelConn repeatedly sends a connection request through the socket until the provided context gets
// canceled, or a response is received. A response that renders the gateway as busy will not stop
// newTunnelConn.
func newTunnelConn(
	ctx context.Context,
	sock Socket,
	config ClientConfig,
) (*tunnelConn, error) {
	req := &proto.ConnReq{}

	// Send the initial request.
	err := sock.Send(req)
	if err != nil {
		return nil, err
	}

	// Create a resend timer.
	ticker := time.NewTicker(config.ResendInterval)
	defer ticker.Stop()

	// Cycle until a request gets a response.
	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return nil, ctx.Err()

		// Resend timer triggered.
		case <-ticker.C:
			err := sock.Send(req)
			if err != nil {
				return nil, err
			}

		// A message has been received or the channel has been closed.
		case msg, open := <-sock.Inbound():
			if !open {
				return nil, errors.New("Inbound channel has been closed")
			}

			// We're only interested in connection responses.
			if res, ok := msg.(*proto.ConnRes); ok {
				switch res.Status {
				// Conection has been established.
				case proto.ConnResOk:
					return &tunnelConn{sock, config, res.Channel}, nil

				// The gateway is busy, but we don't stop yet.
				case proto.ConnResBusy:
					continue

				// Connection request has been denied.
				default:
					return nil, res.Status
				}
			}
		}
	}
}

// requestState periodically sends a connection state request to the gateway until it has
// received a response or the context is done.
func (conn *tunnelConn) requestState(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
) (proto.ConnState, error) {
	req := &proto.ConnStateReq{Channel: conn.channel, Status: 0, Control: proto.HostInfo{}}

	// Send first connection state request
	err := conn.sock.Send(req)
	if err != nil {
		return 0, err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return 0, ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return 0, err
			}

		// Received a connection state response.
		case res, open := <-heartbeat:
			if !open {
				return 0, errors.New("Heartbeat channel is closed")
			}

			return res, nil
		}
	}
}

//
func (conn *tunnelConn) requestTunnel(
	ctx context.Context,
	seqNumber uint8,
	data cemi.CEMI,
	ack <-chan *proto.TunnelRes,
) error {
	req := &proto.TunnelReq{
		Channel:   conn.channel,
		SeqNumber: seqNumber,
		Payload:   data,
	}

	// Send initial request.
	err := conn.sock.Send(req)
	if err != nil {
		return err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return err
			}

		// Received a tunnel response.
		case res, open := <-ack:
			if !open {
				return errors.New("Ack channel is closed")
			}

			// Ignore mismatching sequence numbers.
			if res.SeqNumber != seqNumber {
				continue
			}

			// Check if the response confirms the tunnel request.
			if res.Status == 0 {
				return nil
			}

			return fmt.Errorf("Tunnel request has been rejected with status %#x", res.Status)
		}
	}
}

// performHeartbeat uses requestState to determine if the gateway is still alive.
func (conn *tunnelConn) performHeartbeat(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
	timeout chan<- struct{},
) {
	// Setup a child context which will time out with the given heartbeat timeout.
	childCtx, cancel := context.WithTimeout(ctx, conn.config.ResponseTimeout)
	defer cancel()

	// Request the connction state.
	state, err := conn.requestState(childCtx, heartbeat)
	if err != nil || state != proto.ConnStateNormal {
		if err != nil {
			log(conn, "conn", "Error while requesting connection state: %v", err)
		} else {
			log(conn, "conn", "Bad connection state: %v", state)
		}

		// Write to timeout as an indication that the heartbeat has failed.
		select {
		case <-ctx.Done():
		case timeout <- struct{}{}:
		}
	}
}

// handleDisconnectRequest validates the request.
func (conn *tunnelConn) handleDisconnectRequest(
	ctx context.Context,
	req *proto.DiscReq,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect request")
	}

	// We don't need to check if this errors or not. It doesn't matter.
	conn.sock.Send(&proto.DiscRes{Channel: req.Channel, Status: 0})

	return nil
}

// handleDisconnectResponse validates the response.
func (conn *tunnelConn) handleDisconnectResponse(
	ctx context.Context,
	res *proto.DiscRes,
) error {
	// Validate the response channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect response")
	}

	return nil
}

// handleTunnelRequest validates the request, pushes the data to the client and acknowledges the
// request for the gateway.
func (conn *tunnelConn) handleTunnelRequest(
	ctx context.Context,
	req *proto.TunnelReq,
	seqNumber *uint8,
	inbound chan<- *cemi.CEMI,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in tunnel request")
	}

	// Is the sequence number what we expected?
	if req.SeqNumber == *seqNumber {
		*seqNumber++

		// Send tunnel data to the client.
		go func() {
			select {
			case <-ctx.Done():
			case inbound <- &req.Payload:
			}
		}()
	}

	// Send the acknowledgement.
	return conn.sock.Send(&proto.TunnelRes{
		Channel:   conn.channel,
		SeqNumber: req.SeqNumber,
		Status:    0,
	})
}

// handleTunnelResponse validates the response and relays it to a sender that is awaiting an
// acknowledgement.
func (conn *tunnelConn) handleTunnelResponse(
	ctx context.Context,
	res *proto.TunnelRes,
	ack chan<- *proto.TunnelRes,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send to client.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case ack <- res:
		}
	}()

	return nil
}

// handleConnectionStateResponse validates the response and sends it to the heartbeat routine, if
// there is a waiting one.
func (conn *tunnelConn) handleConnectionStateResponse(
	ctx context.Context,
	res *proto.ConnStateRes,
	heartbeat chan<- proto.ConnState,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send connection state to the heartbeat goroutine.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case heartbeat <- res.Status:
		}
	}()

	return nil
}

// serveInbound processes incoming packets.
func (conn *tunnelConn) serveInbound(
	ctx context.Context,
	inbound chan<- *cemi.CEMI,
	ack chan<- *proto.TunnelRes,
) error {
	defer close(ack)
	defer close(inbound)

	heartbeat := make(chan proto.ConnState)
	timeout := make(chan struct{})

	var seqNumber uint8

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Heartbeat worker signals a result.
		case <-timeout:
			return errors.New("Heartbeat did not succeed")

		// There were no incoming packets for some time.
		case <-time.After(conn.config.HeartbeatDelay):
			go conn.performHeartbeat(ctx, heartbeat, timeout)

		// A message has been received or the channel is closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel is closed")
			}

			// Determine what to do with the message.
			switch msg := msg.(type) {
			case *proto.DiscReq:
				err := conn.handleDisconnectRequest(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect request %v: %v", msg, err)

			case *proto.DiscRes:
				err := conn.handleDisconnectResponse(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect response %v: %v", msg, err)

			case *proto.TunnelReq:
				err := conn.handleTunnelRequest(ctx, msg, &seqNumber, inbound)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel request %v: %v", msg, err)
				}

			case *proto.TunnelRes:
				err := conn.handleTunnelResponse(ctx, msg, ack)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel response %v: %v", msg, err)
				}

			case *proto.ConnStateRes:
				err := conn.handleConnectionStateResponse(ctx, msg, heartbeat)
				if err != nil {
					log(conn, "conn",
						"Error while handling connection state response: %v", err)
				}
			}
		}
	}
}

// Client represents the client endpoint in a connection with a gateway.
type Client struct {
	ctx    context.Context
	cancel context.CancelFunc

	conn *tunnelConn

	mu        sync.Mutex
	seqNumber uint8
	ack       chan *proto.TunnelRes

	inbound chan *cemi.CEMI
}

// Connect establishes a connection with a gateway. You can pass a zero initialized ClientConfig;
// the function will take care of filling in the default values.
func Connect(gatewayAddr string, config ClientConfig) (*Client, error) {
	// Create socket which will be used for communication.
	sock, err := NewClientSocket(gatewayAddr)
	if err != nil {
		return nil, err
	}

	config = checkClientConfig(config)

	// Prepare a context, so that the connection request cannot run forever.
	connectCtx, cancelConnect := context.WithTimeout(context.Background(), config.ResponseTimeout)
	defer cancelConnect()

	// Connect to the gateway.
	conn, err := newTunnelConn(connectCtx, sock, config)
	if err != nil {
		return nil, err
	}

	// Prepare a context for the inbound server.
	ctx, cancel := context.WithCancel(context.Background())

	return &Client{
		ctx,
		cancel,
		conn,
		sync.Mutex{},
		0,
		make(chan *proto.TunnelRes),
		make(chan *cemi.CEMI),
	}, nil
}

// Serve starts the internal connection server, which is needed to process incoming packets.
func (client *Client) Serve() error {
	return client.conn.serveInbound(client.ctx, client.inbound, client.ack)
}

// Close will terminate the connection.
func (client *Client) Close() {
	client.cancel()
}

// Inbound retrieves the channel which transmits incoming data.
func (client *Client) Inbound() <-chan *cemi.CEMI {
	return client.inbound
}

// Send relays a tunnel request to the gateway with the given contents.
func (client *Client) Send(data cemi.CEMI) error {
	// Establish a lock so that nobody else can modify the sequence number.
	client.mu.Lock()
	defer client.mu.Unlock()

	// Prepare a context, so that we won't wait forever for a tunnel response.
	ctx, cancel := context.WithTimeout(client.ctx, client.conn.config.ResponseTimeout)
	defer cancel()

	// Send the tunnel reqest.
	err := client.conn.requestTunnel(ctx, client.seqNumber, data, client.ack)
	if err != nil {
		return err
	}

	// We are able to increase the sequence number of success.
	client.seqNumber++

	return nil
}
<commit_msg>Document tunnelConn requestTunnel<commit_after>package knx

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

	"github.com/vapourismo/knx-go/knx/cemi"
	"github.com/vapourismo/knx-go/knx/proto"
)

// ClientConfig allows you to configure the client's behavior.
type ClientConfig struct {
	// ResendInterval is how long to wait for a response, until the request is resend. A interval
	// <= 0 can't be used. The default value will be used instead.
	ResendInterval time.Duration

	// HeartbeatDelay specifies the time which has to elapse without any incoming communication,
	// until a heartbeat is triggered. A delay <= 0 will result in the use of a default value.
	HeartbeatDelay time.Duration

	// ResponseTimeout specifies how long to wait for a response. A timeout <= 0 will not be
	// accepted. Instead, the default value will be used.
	ResponseTimeout time.Duration
}

// Default configuration elements
var (
	defaultResendInterval  = 500 * time.Millisecond
	defaultHeartbeatDelay  = 10 * time.Second
	defaultResponseTimeout = 10 * time.Second

	DefaultClientConfig = ClientConfig{
		defaultResendInterval,
		defaultHeartbeatDelay,
		defaultResponseTimeout,
	}
)

// checkClientConfig makes sure that the configuration is actually usable.
func checkClientConfig(config ClientConfig) ClientConfig {
	if config.ResendInterval <= 0 {
		config.ResendInterval = defaultResendInterval
	}

	if config.HeartbeatDelay <= 0 {
		config.HeartbeatDelay = defaultHeartbeatDelay
	}

	if config.ResponseTimeout <= 0 {
		config.ResponseTimeout = defaultResponseTimeout
	}

	return config
}

// tunnelConn is a handle for a tunnel connection.
type tunnelConn struct {
	sock    Socket
	config  ClientConfig
	channel uint8
}

// newTunnelConn repeatedly sends a connection request through the socket until the provided context gets
// canceled, or a response is received. A response that renders the gateway as busy will not stop
// newTunnelConn.
func newTunnelConn(
	ctx context.Context,
	sock Socket,
	config ClientConfig,
) (*tunnelConn, error) {
	req := &proto.ConnReq{}

	// Send the initial request.
	err := sock.Send(req)
	if err != nil {
		return nil, err
	}

	// Create a resend timer.
	ticker := time.NewTicker(config.ResendInterval)
	defer ticker.Stop()

	// Cycle until a request gets a response.
	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return nil, ctx.Err()

		// Resend timer triggered.
		case <-ticker.C:
			err := sock.Send(req)
			if err != nil {
				return nil, err
			}

		// A message has been received or the channel has been closed.
		case msg, open := <-sock.Inbound():
			if !open {
				return nil, errors.New("Inbound channel has been closed")
			}

			// We're only interested in connection responses.
			if res, ok := msg.(*proto.ConnRes); ok {
				switch res.Status {
				// Conection has been established.
				case proto.ConnResOk:
					return &tunnelConn{sock, config, res.Channel}, nil

				// The gateway is busy, but we don't stop yet.
				case proto.ConnResBusy:
					continue

				// Connection request has been denied.
				default:
					return nil, res.Status
				}
			}
		}
	}
}

// requestState periodically sends a connection state request to the gateway until it has
// received a response or the context is done.
func (conn *tunnelConn) requestState(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
) (proto.ConnState, error) {
	req := &proto.ConnStateReq{Channel: conn.channel, Status: 0, Control: proto.HostInfo{}}

	// Send first connection state request
	err := conn.sock.Send(req)
	if err != nil {
		return 0, err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return 0, ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return 0, err
			}

		// Received a connection state response.
		case res, open := <-heartbeat:
			if !open {
				return 0, errors.New("Heartbeat channel is closed")
			}

			return res, nil
		}
	}
}

// requestTunnel sends a tunnel request to the gateway and waits for an appropriate acknowledgement.
func (conn *tunnelConn) requestTunnel(
	ctx context.Context,
	seqNumber uint8,
	data cemi.CEMI,
	ack <-chan *proto.TunnelRes,
) error {
	req := &proto.TunnelReq{
		Channel:   conn.channel,
		SeqNumber: seqNumber,
		Payload:   data,
	}

	// Send initial request.
	err := conn.sock.Send(req)
	if err != nil {
		return err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return err
			}

		// Received a tunnel response.
		case res, open := <-ack:
			if !open {
				return errors.New("Ack channel is closed")
			}

			// Ignore mismatching sequence numbers.
			if res.SeqNumber != seqNumber {
				continue
			}

			// Check if the response confirms the tunnel request.
			if res.Status == 0 {
				return nil
			}

			return fmt.Errorf("Tunnel request has been rejected with status %#x", res.Status)
		}
	}
}

// performHeartbeat uses requestState to determine if the gateway is still alive.
func (conn *tunnelConn) performHeartbeat(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
	timeout chan<- struct{},
) {
	// Setup a child context which will time out with the given heartbeat timeout.
	childCtx, cancel := context.WithTimeout(ctx, conn.config.ResponseTimeout)
	defer cancel()

	// Request the connction state.
	state, err := conn.requestState(childCtx, heartbeat)
	if err != nil || state != proto.ConnStateNormal {
		if err != nil {
			log(conn, "conn", "Error while requesting connection state: %v", err)
		} else {
			log(conn, "conn", "Bad connection state: %v", state)
		}

		// Write to timeout as an indication that the heartbeat has failed.
		select {
		case <-ctx.Done():
		case timeout <- struct{}{}:
		}
	}
}

// handleDisconnectRequest validates the request.
func (conn *tunnelConn) handleDisconnectRequest(
	ctx context.Context,
	req *proto.DiscReq,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect request")
	}

	// We don't need to check if this errors or not. It doesn't matter.
	conn.sock.Send(&proto.DiscRes{Channel: req.Channel, Status: 0})

	return nil
}

// handleDisconnectResponse validates the response.
func (conn *tunnelConn) handleDisconnectResponse(
	ctx context.Context,
	res *proto.DiscRes,
) error {
	// Validate the response channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect response")
	}

	return nil
}

// handleTunnelRequest validates the request, pushes the data to the client and acknowledges the
// request for the gateway.
func (conn *tunnelConn) handleTunnelRequest(
	ctx context.Context,
	req *proto.TunnelReq,
	seqNumber *uint8,
	inbound chan<- *cemi.CEMI,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in tunnel request")
	}

	// Is the sequence number what we expected?
	if req.SeqNumber == *seqNumber {
		*seqNumber++

		// Send tunnel data to the client.
		go func() {
			select {
			case <-ctx.Done():
			case inbound <- &req.Payload:
			}
		}()
	}

	// Send the acknowledgement.
	return conn.sock.Send(&proto.TunnelRes{
		Channel:   conn.channel,
		SeqNumber: req.SeqNumber,
		Status:    0,
	})
}

// handleTunnelResponse validates the response and relays it to a sender that is awaiting an
// acknowledgement.
func (conn *tunnelConn) handleTunnelResponse(
	ctx context.Context,
	res *proto.TunnelRes,
	ack chan<- *proto.TunnelRes,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send to client.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case ack <- res:
		}
	}()

	return nil
}

// handleConnectionStateResponse validates the response and sends it to the heartbeat routine, if
// there is a waiting one.
func (conn *tunnelConn) handleConnectionStateResponse(
	ctx context.Context,
	res *proto.ConnStateRes,
	heartbeat chan<- proto.ConnState,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send connection state to the heartbeat goroutine.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case heartbeat <- res.Status:
		}
	}()

	return nil
}

// serveInbound processes incoming packets.
func (conn *tunnelConn) serveInbound(
	ctx context.Context,
	inbound chan<- *cemi.CEMI,
	ack chan<- *proto.TunnelRes,
) error {
	defer close(ack)
	defer close(inbound)

	heartbeat := make(chan proto.ConnState)
	timeout := make(chan struct{})

	var seqNumber uint8

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Heartbeat worker signals a result.
		case <-timeout:
			return errors.New("Heartbeat did not succeed")

		// There were no incoming packets for some time.
		case <-time.After(conn.config.HeartbeatDelay):
			go conn.performHeartbeat(ctx, heartbeat, timeout)

		// A message has been received or the channel is closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel is closed")
			}

			// Determine what to do with the message.
			switch msg := msg.(type) {
			case *proto.DiscReq:
				err := conn.handleDisconnectRequest(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect request %v: %v", msg, err)

			case *proto.DiscRes:
				err := conn.handleDisconnectResponse(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect response %v: %v", msg, err)

			case *proto.TunnelReq:
				err := conn.handleTunnelRequest(ctx, msg, &seqNumber, inbound)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel request %v: %v", msg, err)
				}

			case *proto.TunnelRes:
				err := conn.handleTunnelResponse(ctx, msg, ack)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel response %v: %v", msg, err)
				}

			case *proto.ConnStateRes:
				err := conn.handleConnectionStateResponse(ctx, msg, heartbeat)
				if err != nil {
					log(conn, "conn",
						"Error while handling connection state response: %v", err)
				}
			}
		}
	}
}

// Client represents the client endpoint in a connection with a gateway.
type Client struct {
	ctx    context.Context
	cancel context.CancelFunc

	conn *tunnelConn

	mu        sync.Mutex
	seqNumber uint8
	ack       chan *proto.TunnelRes

	inbound chan *cemi.CEMI
}

// Connect establishes a connection with a gateway. You can pass a zero initialized ClientConfig;
// the function will take care of filling in the default values.
func Connect(gatewayAddr string, config ClientConfig) (*Client, error) {
	// Create socket which will be used for communication.
	sock, err := NewClientSocket(gatewayAddr)
	if err != nil {
		return nil, err
	}

	config = checkClientConfig(config)

	// Prepare a context, so that the connection request cannot run forever.
	connectCtx, cancelConnect := context.WithTimeout(context.Background(), config.ResponseTimeout)
	defer cancelConnect()

	// Connect to the gateway.
	conn, err := newTunnelConn(connectCtx, sock, config)
	if err != nil {
		return nil, err
	}

	// Prepare a context for the inbound server.
	ctx, cancel := context.WithCancel(context.Background())

	return &Client{
		ctx,
		cancel,
		conn,
		sync.Mutex{},
		0,
		make(chan *proto.TunnelRes),
		make(chan *cemi.CEMI),
	}, nil
}

// Serve starts the internal connection server, which is needed to process incoming packets.
func (client *Client) Serve() error {
	return client.conn.serveInbound(client.ctx, client.inbound, client.ack)
}

// Close will terminate the connection.
func (client *Client) Close() {
	client.cancel()
}

// Inbound retrieves the channel which transmits incoming data.
func (client *Client) Inbound() <-chan *cemi.CEMI {
	return client.inbound
}

// Send relays a tunnel request to the gateway with the given contents.
func (client *Client) Send(data cemi.CEMI) error {
	// Establish a lock so that nobody else can modify the sequence number.
	client.mu.Lock()
	defer client.mu.Unlock()

	// Prepare a context, so that we won't wait forever for a tunnel response.
	ctx, cancel := context.WithTimeout(client.ctx, client.conn.config.ResponseTimeout)
	defer cancel()

	// Send the tunnel reqest.
	err := client.conn.requestTunnel(ctx, client.seqNumber, data, client.ack)
	if err != nil {
		return err
	}

	// We are able to increase the sequence number of success.
	client.seqNumber++

	return nil
}
<|endoftext|>


<commit_before>// 4 january 2016
package main

// TODO - ideally the keywords would be separate from the topic list, but I'm not sure how — or even if — devhelp maps them back to a topic in the contents

import (
	"os"
	"encoding/xml"
	"path/filepath"
	"net/url"
	"strings"
	"errors"

	"github.com/andlabs/ohv/internal/ui"
)

type devhelpSub struct {
	Name	string			`xml:"name,attr"`
	Link		string			`xml:"link,attr"`
	Subs		[]*devhelpSub		`xml:"sub"`
}

type devhelpKeyword struct {
	Name	string		`xml:"name,attr"`
	Link		string		`xml:"link,attr"`
}

type devhelpBook struct {
	Title			string			`xml:"title,attr"`
	Link			string			`xml:"link,attr"`
	Chapters		[]*devhelpSub		`xml:"chapters>sub"`
	Functions		[]*devhelpKeyword	`xml:"functions>keyword"`
}

type Devhelp struct {
	name	string
	dir		string
	root		Topic
}

func (d *Devhelp) Name() string {
	return d.name
}

func (d *Devhelp) Books() []Topic {
	return []Topic{d.root}
}

func (d *Devhelp) Orphans() []Topic {
	return nil
}

func (d *Devhelp) Lookup(url *url.URL) Topic {
	// TODO
	println(url.String())
	return nil
}

func OpenDevhelp(dir string) (*Devhelp, error) {
	d := new(Devhelp)
	d.dir = dir

	files, err := filepath.Glob(filepath.Join(d.dir, "*.devhelp2"))
	if err != nil {
		return nil, err
	}
	if len(files) != 1 {
		return nil, errors.New("zero or multiple possible devhelp files in " + d.dir)
	}

	file, err := os.Open(files[0])
	if err != nil {
		return nil, err
	}
	defer file.Close()

	dd := new(devhelpBook)
	decoder := xml.NewDecoder(file)
	err = decoder.Decode(dd)
	if err != nil {
		return nil, err
	}

	d.root = newDevhelpTopic(dd.Title, dd.Link, dd.Chapters, d.dir, nil, d)
	return d, nil
}

type DevhelpTopic struct {
	name	string
	dir		string
	path		string
	anchor	string
	parent	Topic
	children	[]Topic
	source	HelpSource
}

func newDevhelpTopic(name string, link string, children []*devhelpSub, dir string, parent Topic, source HelpSource) *DevhelpTopic {
	d := new(DevhelpTopic)
	d.name = name
	// devhelp splits filename from anchor with strchr(): https://git.gnome.org/browse/devhelp/tree/src/dh-assistant-view.c?id=b65a383b5f46908ac16ae4f55c7dcce26c891713#n228
	// TODO does the code REQUIRE an anchor? some s don't have them
	parts := strings.SplitN(link, "#", 2)
	d.path = parts[0]
	if len(parts) == 2 {
		d.anchor = parts[1]
	}
	d.dir = dir
	d.parent = parent
	d.source = source
	for _, child := range children {
		ct := newDevhelpTopic(child.Name, child.Link, child.Subs, d.dir, d, d.source)
		d.children = append(d.children, ct)
	}
	return d
}

func (d *DevhelpTopic) Prepare() (*Prepared, error) {
	return &Prepared{
		Path:			filepath.Join(d.dir, d.path),
		Anchor:		d.anchor,
	}, nil
}

func (d *DevhelpTopic) Name() string {
	return d.name
}

func (d *DevhelpTopic) Parent() Topic {
	return d.parent
}

func (d *DevhelpTopic) Children() []Topic {
	return d.children
}

func (d *DevhelpTopic) Source() HelpSource {
	return d.source
}

func (d *DevhelpTopic) Less(t Topic) bool {
	panic("unreachable; DevhelpTopics are already sorted")
}

func (d *DevhelpTopic) TreeNodeText() string {
	return d.Name()
}

func (d *DevhelpTopic) TreeNodeParent() ui.TreeNode {
	return d.Parent()
}

func (d *DevhelpTopic) TreeNodeChildren() []ui.TreeNode {
	if len(d.children) == 0 {
		return nil
	}
	c := make([]ui.TreeNode, len(d.children))
	for i, n := range d.children {
		c[i] = n
	}
	return c
}
<commit_msg>More TODOs.<commit_after>// 4 january 2016
package main

// TODO add keywords to the TOC
// TODO - ideally the keywords would be separate from the topic list, but I'm not sure how — or even if — devhelp maps them back to a topic in the contents

import (
	"os"
	"encoding/xml"
	"path/filepath"
	"net/url"
	"strings"
	"errors"

	"github.com/andlabs/ohv/internal/ui"
)

type devhelpSub struct {
	Name	string			`xml:"name,attr"`
	Link		string			`xml:"link,attr"`
	Subs		[]*devhelpSub		`xml:"sub"`
}

type devhelpKeyword struct {
	Name	string		`xml:"name,attr"`
	Link		string		`xml:"link,attr"`
}

type devhelpBook struct {
	Title			string			`xml:"title,attr"`
	Link			string			`xml:"link,attr"`
	Chapters		[]*devhelpSub		`xml:"chapters>sub"`
	Functions		[]*devhelpKeyword	`xml:"functions>keyword"`
}

type Devhelp struct {
	name	string
	dir		string
	root		Topic
}

func (d *Devhelp) Name() string {
	return d.name
}

func (d *Devhelp) Books() []Topic {
	return []Topic{d.root}
}

func (d *Devhelp) Orphans() []Topic {
	return nil
}

func (d *Devhelp) Lookup(url *url.URL) Topic {
	// TODO
	println(url.String())
	return nil
}

func OpenDevhelp(dir string) (*Devhelp, error) {
	d := new(Devhelp)
	d.dir = dir

	files, err := filepath.Glob(filepath.Join(d.dir, "*.devhelp2"))
	if err != nil {
		return nil, err
	}
	if len(files) != 1 {
		return nil, errors.New("zero or multiple possible devhelp files in " + d.dir)
	}

	file, err := os.Open(files[0])
	if err != nil {
		return nil, err
	}
	defer file.Close()

	dd := new(devhelpBook)
	decoder := xml.NewDecoder(file)
	err = decoder.Decode(dd)
	if err != nil {
		return nil, err
	}

	d.root = newDevhelpTopic(dd.Title, dd.Link, dd.Chapters, d.dir, nil, d)
	return d, nil
}

type DevhelpTopic struct {
	name	string
	dir		string
	path		string
	anchor	string
	parent	Topic
	children	[]Topic
	source	HelpSource
}

func newDevhelpTopic(name string, link string, children []*devhelpSub, dir string, parent Topic, source HelpSource) *DevhelpTopic {
	d := new(DevhelpTopic)
	d.name = name
	// devhelp splits filename from anchor with strchr(): https://git.gnome.org/browse/devhelp/tree/src/dh-assistant-view.c?id=b65a383b5f46908ac16ae4f55c7dcce26c891713#n228
	// TODO does the code REQUIRE an anchor? some s don't have them
	parts := strings.SplitN(link, "#", 2)
	d.path = parts[0]
	if len(parts) == 2 {
		d.anchor = parts[1]
	}
	d.dir = dir
	d.parent = parent
	d.source = source
	for _, child := range children {
		ct := newDevhelpTopic(child.Name, child.Link, child.Subs, d.dir, d, d.source)
		d.children = append(d.children, ct)
	}
	return d
}

func (d *DevhelpTopic) Prepare() (*Prepared, error) {
	return &Prepared{
		Path:			filepath.Join(d.dir, d.path),
		Anchor:		d.anchor,
	}, nil
}

func (d *DevhelpTopic) Name() string {
	return d.name
}

func (d *DevhelpTopic) Parent() Topic {
	return d.parent
}

func (d *DevhelpTopic) Children() []Topic {
	return d.children
}

func (d *DevhelpTopic) Source() HelpSource {
	return d.source
}

func (d *DevhelpTopic) Less(t Topic) bool {
	panic("unreachable; DevhelpTopics are already sorted")
}

func (d *DevhelpTopic) TreeNodeText() string {
	return d.Name()
}

func (d *DevhelpTopic) TreeNodeParent() ui.TreeNode {
	return d.Parent()
}

func (d *DevhelpTopic) TreeNodeChildren() []ui.TreeNode {
	if len(d.children) == 0 {
		return nil
	}
	c := make([]ui.TreeNode, len(d.children))
	for i, n := range d.children {
		c[i] = n
	}
	return c
}
<|endoftext|>


<commit_before>package junos

import (
	"encoding/xml"
	"fmt"
	"regexp"
)

// Devices contains a list of managed devices.
type Devices struct {
	XMLName xml.Name `xml:"devices"`
	Devices []Device `xml:"device"`
}

// A Device contains information about each individual device.
type Device struct {
	ID        int    `xml:"key,attr"`
	Family    string `xml:"deviceFamily"`
	Version   string `xml:"OSVersion"`
	Platform  string `xml:"platform"`
	Serial    string `xml:"serialNumber"`
	IPAddress string `xml:"ipAddr"`
	Name      string `xml:"name"`
}

// addDeviceIPXML is the XML we send (POST) for adding a device by IP address.
var addDeviceIPXML = `
<discover-devices>
    <ipAddressDiscoveryTarget>
        <ipAddress>%s</ipAddress>
    </ipAddressDiscoveryTarget>
    <sshCredential>
        <userName>%s</userName>
        <password>%s</password>
    </sshCredential>
    <manageDiscoveredSystemsFlag>true</manageDiscoveredSystemsFlag>
    <usePing>true</usePing>
</discover-devices>
`

// addDeviceHostXML is the XML we send (POST) for adding a device by hostname.
var addDeviceHostXML = `
<discover-devices>
    <hostNameDiscoveryTarget>
        <hostName>%s</hostName>
    </hostNameDiscoveryTarget>
    <sshCredential>
        <userName>%s</userName>
        <password>%s</password>
    </sshCredential>
    <manageDiscoveredSystemsFlag>true</manageDiscoveredSystemsFlag>
    <usePing>true</usePing>
</discover-devices>
`

// getDeviceID returns the ID of a managed device.
func (s *JunosSpace) getDeviceID(device interface{}) (int, error) {
	var err error
	var deviceID int
	ipRegex := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
	devices, err := s.Devices()
	if err != nil {
		return 0, err
	}

	switch device.(type) {
	case int:
		deviceID = device.(int)
	case string:
		if ipRegex.MatchString(device.(string)) {
			for _, d := range devices.Devices {
				if d.IPAddress == device.(string) {
					deviceID = d.ID
				}
			}
		}
		for _, d := range devices.Devices {
			if d.Name == device.(string) {
				deviceID = d.ID
			}
		}
	}

	return deviceID, nil
}

// AddDevice adds a new managed device to Junos Space, and returns the Job ID.
func (s *JunosSpace) AddDevice(host, user, password string) (int, error) {
	var job jobID
	var addDevice string
	ipRegex := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)

	if ipRegex.MatchString(host) {
		addDevice = addDeviceIPXML
	}

	addDevice = addDeviceHostXML

	req := &APIRequest{
		Method:      "post",
		URL:         "/api/space/device-management/discover-devices",
		Body:        fmt.Sprintf(addDevice, host, user, password),
		ContentType: contentDiscoverDevices,
	}
	data, err := s.APICall(req)
	if err != nil {
		return 0, err
	}

	err = xml.Unmarshal(data, &job)
	if err != nil {
		return 0, err
	}

	return job.ID, nil
}

// Devices queries the Junos Space server and returns all of the information
// about each device that is managed by Space.
func (s *JunosSpace) Devices() (*Devices, error) {
	var devices Devices
	req := &APIRequest{
		Method: "get",
		URL:    "/api/space/device-management/devices",
	}
	data, err := s.APICall(req)
	if err != nil {
		return nil, err
	}

	err = xml.Unmarshal(data, &devices)
	if err != nil {
		return nil, err
	}

	return &devices, nil
}

// RemoveDevice removes a device from Junos Space. You can specify the device ID, name
// or IP address.
func (s *JunosSpace) RemoveDevice(device interface{}) error {
	var err error
	deviceID, err := s.getDeviceID(device)
	if err != nil {
		return err
	}

	if deviceID != 0 {
		req := &APIRequest{
			Method: "delete",
			URL:    fmt.Sprintf("/api/space/device-management/devices/%d", deviceID),
		}
		_, err = s.APICall(req)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Added Resync() function to resynchronize a device with Junos Space.<commit_after>package junos

import (
	"encoding/xml"
	"fmt"
	"regexp"
)

// Devices contains a list of managed devices.
type Devices struct {
	XMLName xml.Name `xml:"devices"`
	Devices []Device `xml:"device"`
}

// A Device contains information about each individual device.
type Device struct {
	ID        int    `xml:"key,attr"`
	Family    string `xml:"deviceFamily"`
	Version   string `xml:"OSVersion"`
	Platform  string `xml:"platform"`
	Serial    string `xml:"serialNumber"`
	IPAddress string `xml:"ipAddr"`
	Name      string `xml:"name"`
}

// addDeviceIPXML is the XML we send (POST) for adding a device by IP address.
var addDeviceIPXML = `
<discover-devices>
    <ipAddressDiscoveryTarget>
        <ipAddress>%s</ipAddress>
    </ipAddressDiscoveryTarget>
    <sshCredential>
        <userName>%s</userName>
        <password>%s</password>
    </sshCredential>
    <manageDiscoveredSystemsFlag>true</manageDiscoveredSystemsFlag>
    <usePing>true</usePing>
</discover-devices>
`

// addDeviceHostXML is the XML we send (POST) for adding a device by hostname.
var addDeviceHostXML = `
<discover-devices>
    <hostNameDiscoveryTarget>
        <hostName>%s</hostName>
    </hostNameDiscoveryTarget>
    <sshCredential>
        <userName>%s</userName>
        <password>%s</password>
    </sshCredential>
    <manageDiscoveredSystemsFlag>true</manageDiscoveredSystemsFlag>
    <usePing>true</usePing>
</discover-devices>
`

// getDeviceID returns the ID of a managed device.
func (s *JunosSpace) getDeviceID(device interface{}) (int, error) {
	var err error
	var deviceID int
	ipRegex := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
	devices, err := s.Devices()
	if err != nil {
		return 0, err
	}

	switch device.(type) {
	case int:
		deviceID = device.(int)
	case string:
		if ipRegex.MatchString(device.(string)) {
			for _, d := range devices.Devices {
				if d.IPAddress == device.(string) {
					deviceID = d.ID
				}
			}
		}
		for _, d := range devices.Devices {
			if d.Name == device.(string) {
				deviceID = d.ID
			}
		}
	}

	return deviceID, nil
}

// AddDevice adds a new managed device to Junos Space, and returns the Job ID.
func (s *JunosSpace) AddDevice(host, user, password string) (int, error) {
	var job jobID
	var addDevice string
	ipRegex := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)

	if ipRegex.MatchString(host) {
		addDevice = addDeviceIPXML
	}

	addDevice = addDeviceHostXML

	req := &APIRequest{
		Method:      "post",
		URL:         "/api/space/device-management/discover-devices",
		Body:        fmt.Sprintf(addDevice, host, user, password),
		ContentType: contentDiscoverDevices,
	}
	data, err := s.APICall(req)
	if err != nil {
		return 0, err
	}

	err = xml.Unmarshal(data, &job)
	if err != nil {
		return 0, err
	}

	return job.ID, nil
}

// Devices queries the Junos Space server and returns all of the information
// about each device that is managed by Space.
func (s *JunosSpace) Devices() (*Devices, error) {
	var devices Devices
	req := &APIRequest{
		Method: "get",
		URL:    "/api/space/device-management/devices",
	}
	data, err := s.APICall(req)
	if err != nil {
		return nil, err
	}

	err = xml.Unmarshal(data, &devices)
	if err != nil {
		return nil, err
	}

	return &devices, nil
}

// RemoveDevice removes a device from Junos Space. You can specify the device ID, name
// or IP address.
func (s *JunosSpace) RemoveDevice(device interface{}) error {
	var err error
	deviceID, err := s.getDeviceID(device)
	if err != nil {
		return err
	}

	if deviceID != 0 {
		req := &APIRequest{
			Method: "delete",
			URL:    fmt.Sprintf("/api/space/device-management/devices/%d", deviceID),
		}
		_, err = s.APICall(req)
		if err != nil {
			return err
		}
	}

	return nil
}

// Resync synchronizes the device with Junos Space. Good to use if you make a lot of
// changes outside of Junos Space such as adding interfaces, zones, etc.
func (s *JunosSpace) Resync(device interface{}) (int, error) {
	var job jobID
	deviceID, err := s.getDeviceID(device)
	if err != nil {
		return 0, err
	}

	req := &APIRequest{
		Method:      "post",
		URL:         fmt.Sprintf("/api/space/device-management/devices/%d/exec-resync", deviceID),
		ContentType: contentResync,
	}
	data, err := s.APICall(req)
	if err != nil {
		return 0, err
	}

	err = xml.Unmarshal(data, &job)
	if err != nil {
		return 0, err
	}

	return job.ID, nil
}
<|endoftext|>