<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"time"

	"github.com/BurntSushi/toml"
	xj "github.com/basgys/goxml2json"
	"github.com/gorilla/websocket"
)

//VERSION of the program
var version = "undefined-autogenerated"

//gobal variable to store all websocket connections for the update
var cons = make(map[*websocket.Conn]bool)

//mutex to lock the NMAP JSON information
var lock = sync.RWMutex{}

var nmapJSON *bytes.Buffer

//just for development
type msg struct {
	Command string
	Num     int
}

//Config data struct to read the config file
type Config struct {
	NMAPRange    string
	NMAPPorts    string //comma separated
	HTTPPort     int
	ScanInterval int //seconds
}

func echo(conn *websocket.Conn) {
	for {
		m := msg{}
		err := conn.ReadJSON(&m)
		if err != nil {
			log.Println("Error reading json.", err)
			break
		}
		lock.RLock()
		if nmapJSON != nil {
			err := conn.WriteMessage(1, nmapJSON.Bytes())
			if err != nil {
				log.Println(err)
				break
			}
		}
		lock.RUnlock()
	}
	delete(cons, conn)
	conn.Close()
}

//ReadConfig reads the config file
func readConfig(configfile string) Config {
	var config Config
	if _, err := toml.DecodeFile(configfile, &config); err != nil {
		log.Fatal(err)
	}
	return config
}

func callNMAP(conf Config) {
	log.Println("Starting nmap caller")
	var counter = 1
	var scanResultsFileName = "scan.xml"

	for {
		cmd := exec.Command("nmap", "-p", conf.NMAPPorts, "-oX", scanResultsFileName, conf.NMAPRange)
		log.Println("Init NMAP scan no:", counter)
		var out bytes.Buffer
		cmd.Stdout = &out
		err := cmd.Run()
		if err != nil {
			log.Println(err)
		}
		log.Println("Scan no.", counter, "complete")
		counter = counter + 1

		//read the xml file, convert to json and send via the websockets
		xmlfile, err := os.Open(scanResultsFileName)
		if err != nil {
			log.Println(err)
		}
		lock.Lock()
		nmapJSON, err = xj.Convert(xmlfile)
		lock.Unlock()
		if err != nil {
			log.Println(err)
		}
		log.Println("Writing to websockets")
		for c := range cons {
			c.WriteMessage(1, nmapJSON.Bytes())
		}
		<-time.After(time.Duration(conf.ScanInterval) * time.Second)
	}
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Ws request from:", r.Header.Get("Origin"))
	// log.Println("accepted from: http://" + r.Host)
	// if r.Header.Get("Origin") != "http://"+r.Host {
	// 	http.Error(w, "Origin not allowed", 403)
	// 	log.Println("Origin not allowed")
	// 	return
	// }
	conn, err := websocket.Upgrade(w, r, w.Header(), 0, 0)
	if err != nil {
		http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
		log.Println("Upgrade error")
	}

	//add the connection to the pool for broadcast
	cons[conn] = true
	go echo(conn)
}

func main() {
	log.Println("Starting lan-monitor-server ver: " + version)

	//process the config
	//1st the config file is read and set parameters applied
	//2nd the command line parameters are interpreted,
	//if they are set they will overrule the config file
	//3rd if none of the above is applied the program reverts to the hardcoded defaults

	//defaults
	var config Config
	defaultConfigFileLocation := "/etc/lan-monitor.conf"
	config.HTTPPort = 8080
	config.NMAPRange = "192.168.0.1/24"
	config.NMAPPorts = "22,80"
	config.ScanInterval = 120 //seconds

	displayVersion := flag.Bool("version", false, "Prints the version number")
	cmdlineHTTPPort := flag.Int("port", config.HTTPPort, "HTTP port for the webserver")
	cmdlineNMAPScanRange := flag.String("range", config.NMAPRange, "The range NMAP should scan e.g. 192.168.1.1/24 it has to be nmap compatible")
	cmdlineScanInterval := flag.Int("scan-rate", config.ScanInterval, "The interval of the scans in seconds")
	configFileLocation := flag.String("config-file", defaultConfigFileLocation, "Location of the config file")
	cmdlinePorts := flag.String("scan-ports", config.NMAPPorts, "The ports that will be scanned")
	flag.Parse()

	//try to read the configfile
	_, err := os.Stat(*configFileLocation)
	if err == nil {
		config = readConfig(*configFileLocation)
	} else {
		log.Println("Config file is missing - looked at:", *configFileLocation)
		log.Println("Reverting to commandline/defaults")
	}

	//if no range is defined in the config file
	if config.NMAPRange == "" {
		config.NMAPRange = *cmdlineNMAPScanRange
	}

	//if no port is defined in the config file
	if config.HTTPPort == 0 {
		config.HTTPPort = *cmdlineHTTPPort
	}

	//if no scan interval is defined in the config file
	if config.ScanInterval == 0 {
		config.ScanInterval = *cmdlineScanInterval
	}

	//if no ports to be scanned are defined
	if config.NMAPPorts == "" {
		config.NMAPPorts = *cmdlinePorts
	}

	log.Println("Config - range:", config.NMAPRange, "webserver port:", config.HTTPPort, "interval:", config.ScanInterval, "sec", "scan-ports", config.NMAPPorts)

	if *displayVersion == true {
		fmt.Println("Version: " + version)
		return
	}

	//changing working dir
	log.Println("Changing working dir to: ")
	err = os.Chdir("../www")
	if err != nil {
		log.Fatalln("Unable to switch working dir")
	}

	workingDir, _ := os.Getwd()
	log.Println("Dir:" + workingDir)

	//init the scanning routine
	go callNMAP(config)

	//starting and configuring the webserver
	fs := http.FileServer(http.Dir(workingDir))
	http.HandleFunc("/ws", wsHandler)
	http.Handle("/", fs)
	listenAddress := ":" + strconv.Itoa(config.HTTPPort)
	log.Fatal(http.ListenAndServe(listenAddress, nil))
}
<commit_msg>no swop of working dir<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"time"

	"github.com/BurntSushi/toml"
	xj "github.com/basgys/goxml2json"
	"github.com/gorilla/websocket"
)

//VERSION of the program
var version = "undefined-autogenerated"

//gobal variable to store all websocket connections for the update
var cons = make(map[*websocket.Conn]bool)

//mutex to lock the NMAP JSON information
var lock = sync.RWMutex{}

var nmapJSON *bytes.Buffer

//just for development
type msg struct {
	Command string
	Num     int
}

//Config data struct to read the config file
type Config struct {
	NMAPRange    string
	NMAPPorts    string //comma separated
	HTTPPort     int
	ScanInterval int //seconds
}

func echo(conn *websocket.Conn) {
	for {
		m := msg{}
		err := conn.ReadJSON(&m)
		if err != nil {
			log.Println("Error reading json.", err)
			break
		}
		lock.RLock()
		if nmapJSON != nil {
			err := conn.WriteMessage(1, nmapJSON.Bytes())
			if err != nil {
				log.Println(err)
				break
			}
		}
		lock.RUnlock()
	}
	delete(cons, conn)
	conn.Close()
}

//ReadConfig reads the config file
func readConfig(configfile string) Config {
	var config Config
	if _, err := toml.DecodeFile(configfile, &config); err != nil {
		log.Fatal(err)
	}
	return config
}

func callNMAP(conf Config) {
	log.Println("Starting nmap caller")
	var counter = 1
	var scanResultsFileName = "scan.xml"

	for {
		cmd := exec.Command("nmap", "-p", conf.NMAPPorts, "-oX", scanResultsFileName, conf.NMAPRange)
		log.Println("Init NMAP scan no:", counter)
		var out bytes.Buffer
		cmd.Stdout = &out
		err := cmd.Run()
		if err != nil {
			log.Println(err)
		}
		log.Println("Scan no.", counter, "complete")
		counter = counter + 1

		//read the xml file, convert to json and send via the websockets
		xmlfile, err := os.Open(scanResultsFileName)
		if err != nil {
			log.Println(err)
		}
		lock.Lock()
		nmapJSON, err = xj.Convert(xmlfile)
		lock.Unlock()
		if err != nil {
			log.Println(err)
		}
		log.Println("Writing to websockets")
		for c := range cons {
			c.WriteMessage(1, nmapJSON.Bytes())
		}
		<-time.After(time.Duration(conf.ScanInterval) * time.Second)
	}
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Ws request from:", r.Header.Get("Origin"))
	// log.Println("accepted from: http://" + r.Host)
	// if r.Header.Get("Origin") != "http://"+r.Host {
	// 	http.Error(w, "Origin not allowed", 403)
	// 	log.Println("Origin not allowed")
	// 	return
	// }
	conn, err := websocket.Upgrade(w, r, w.Header(), 0, 0)
	if err != nil {
		http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
		log.Println("Upgrade error")
	}

	//add the connection to the pool for broadcast
	cons[conn] = true
	go echo(conn)
}

func main() {
	log.Println("Starting lan-monitor-server ver: " + version)

	//process the config
	//1st the config file is read and set parameters applied
	//2nd the command line parameters are interpreted,
	//if they are set they will overrule the config file
	//3rd if none of the above is applied the program reverts to the hardcoded defaults

	//defaults
	var config Config
	defaultConfigFileLocation := "/etc/lan-monitor.conf"
	config.HTTPPort = 8080
	config.NMAPRange = "192.168.0.1/24"
	config.NMAPPorts = "22,80"
	config.ScanInterval = 120 //seconds

	displayVersion := flag.Bool("version", false, "Prints the version number")
	cmdlineHTTPPort := flag.Int("port", config.HTTPPort, "HTTP port for the webserver")
	cmdlineNMAPScanRange := flag.String("range", config.NMAPRange, "The range NMAP should scan e.g. 192.168.1.1/24 it has to be nmap compatible")
	cmdlineScanInterval := flag.Int("scan-rate", config.ScanInterval, "The interval of the scans in seconds")
	configFileLocation := flag.String("config-file", defaultConfigFileLocation, "Location of the config file")
	cmdlinePorts := flag.String("scan-ports", config.NMAPPorts, "The ports that will be scanned")
	flag.Parse()

	//try to read the configfile
	_, err := os.Stat(*configFileLocation)
	if err == nil {
		config = readConfig(*configFileLocation)
	} else {
		log.Println("Config file is missing - looked at:", *configFileLocation)
		log.Println("Reverting to commandline/defaults")
	}

	//if no range is defined in the config file
	if config.NMAPRange == "" {
		config.NMAPRange = *cmdlineNMAPScanRange
	}

	//if no port is defined in the config file
	if config.HTTPPort == 0 {
		config.HTTPPort = *cmdlineHTTPPort
	}

	//if no scan interval is defined in the config file
	if config.ScanInterval == 0 {
		config.ScanInterval = *cmdlineScanInterval
	}

	//if no ports to be scanned are defined
	if config.NMAPPorts == "" {
		config.NMAPPorts = *cmdlinePorts
	}

	log.Println("Config - range:", config.NMAPRange, "webserver port:", config.HTTPPort, "interval:", config.ScanInterval, "sec", "scan-ports", config.NMAPPorts)

	if *displayVersion == true {
		fmt.Println("Version: " + version)
		return
	}

	workingDir, _ := os.Getwd()
	log.Println("Dir:" + workingDir)

	//init the scanning routine
	go callNMAP(config)

	//starting and configuring the webserver
	fs := http.FileServer(http.Dir(workingDir))
	http.HandleFunc("/ws", wsHandler)
	http.Handle("/", fs)
	listenAddress := ":" + strconv.Itoa(config.HTTPPort)
	log.Fatal(http.ListenAndServe(listenAddress, nil))
}
<|endoftext|>


<commit_before>package nomadexec

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"reflect"
	"regexp"
	"testing"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/e2e/e2eutil"
	"github.com/hashicorp/nomad/e2e/framework"
	"github.com/hashicorp/nomad/helper/uuid"
	dtestutils "github.com/hashicorp/nomad/plugins/drivers/testutils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type NomadExecE2ETest struct {
	framework.TC

	name        string
	jobFilePath string

	jobID string
	alloc api.Allocation
}

func init() {
	framework.AddSuites(&framework.TestSuite{
		Component:   "Nomad exec",
		CanRunLocal: true,
		Cases: []framework.TestCase{
			newNomadExecE2eTest("docker", "./nomadexec/testdata/docker.nomad"),
		},
	})
}

func newNomadExecE2eTest(name, jobFilePath string) *NomadExecE2ETest {
	return &NomadExecE2ETest{
		name:        name,
		jobFilePath: jobFilePath,
	}
}

func (tc *NomadExecE2ETest) Name() string {
	return fmt.Sprintf("%v (%v)", tc.TC.Name(), tc.name)
}

func (tc *NomadExecE2ETest) BeforeAll(f *framework.F) {
	// Ensure cluster has leader before running tests
	e2eutil.WaitForLeader(f.T(), tc.Nomad())
	e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1)

	// register a job for execing into
	tc.jobID = "nomad-exec" + uuid.Generate()[:8]
	allocs := e2eutil.RegisterAndWaitForAllocs(f.T(), tc.Nomad(), tc.jobFilePath, tc.jobID, "")
	f.Len(allocs, 1)

	e2eutil.WaitForAllocRunning(f.T(), tc.Nomad(), allocs[0].ID)

	tc.alloc = api.Allocation{
		ID:        allocs[0].ID,
		Namespace: allocs[0].Namespace,
		NodeID:    allocs[0].NodeID,
	}
}

func (tc *NomadExecE2ETest) TestExecBasicResponses(f *framework.F) {
	for _, c := range dtestutils.ExecTaskStreamingBasicCases {
		f.T().Run(c.Name, func(t *testing.T) {

			stdin := newTestStdin(c.Tty, c.Stdin)
			var stdout, stderr bytes.Buffer

			resizeCh := make(chan api.TerminalSize)
			go func() {
				resizeCh <- api.TerminalSize{Height: 100, Width: 100}
			}()

			ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second)
			defer cancelFn()

			exitCode, err := tc.Nomad().Allocations().Exec(ctx,
				&tc.alloc, "task", c.Tty,
				[]string{"/bin/sh", "-c", c.Command},
				stdin, &stdout, &stderr,
				resizeCh, nil)

			require.NoError(t, err)

			assert.Equal(t, c.ExitCode, exitCode)

			switch s := c.Stdout.(type) {
			case string:
				require.Equal(t, s, stdout.String())
			case *regexp.Regexp:
				require.Regexp(t, s, stdout.String())
			case nil:
				require.Empty(t, stdout.String())
			default:
				require.Fail(t, "unexpected stdout type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s))
			}

			switch s := c.Stderr.(type) {
			case string:
				require.Equal(t, s, stderr.String())
			case *regexp.Regexp:
				require.Regexp(t, s, stderr.String())
			case nil:
				require.Empty(t, stderr.String())
			default:
				require.Fail(t, "unexpected stderr type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s))
			}
		})
	}
}

func (tc *NomadExecE2ETest) AfterAll(f *framework.F) {
	jobs := tc.Nomad().Jobs()
	if tc.jobID != "" {
		jobs.Deregister(tc.jobID, true, nil)
	}
	tc.Nomad().System().GarbageCollect()
}

func newTestStdin(tty bool, d string) io.Reader {
	pr, pw := io.Pipe()
	go func() {
		pw.Write([]byte(d))

		// when testing TTY, leave connection open for the entire duration of command
		// closing stdin may cause TTY session prematurely before command completes
		if !tty {
			pw.Close()
		}

	}()

	return pr
}
<commit_msg>e2e: special case "Unexpected EOF" errors<commit_after>package nomadexec

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"reflect"
	"regexp"
	"strings"
	"testing"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/e2e/e2eutil"
	"github.com/hashicorp/nomad/e2e/framework"
	"github.com/hashicorp/nomad/helper/uuid"
	dtestutils "github.com/hashicorp/nomad/plugins/drivers/testutils"
	"github.com/stretchr/testify/assert"
)

type NomadExecE2ETest struct {
	framework.TC

	name        string
	jobFilePath string

	jobID string
	alloc api.Allocation
}

func init() {
	framework.AddSuites(&framework.TestSuite{
		Component:   "Nomad exec",
		CanRunLocal: true,
		Cases: []framework.TestCase{
			newNomadExecE2eTest("docker", "./nomadexec/testdata/docker.nomad"),
		},
	})
}

func newNomadExecE2eTest(name, jobFilePath string) *NomadExecE2ETest {
	return &NomadExecE2ETest{
		name:        name,
		jobFilePath: jobFilePath,
	}
}

func (tc *NomadExecE2ETest) Name() string {
	return fmt.Sprintf("%v (%v)", tc.TC.Name(), tc.name)
}

func (tc *NomadExecE2ETest) BeforeAll(f *framework.F) {
	// Ensure cluster has leader before running tests
	e2eutil.WaitForLeader(f.T(), tc.Nomad())
	e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1)

	// register a job for execing into
	tc.jobID = "nomad-exec" + uuid.Generate()[:8]
	allocs := e2eutil.RegisterAndWaitForAllocs(f.T(), tc.Nomad(), tc.jobFilePath, tc.jobID, "")
	f.Len(allocs, 1)

	e2eutil.WaitForAllocRunning(f.T(), tc.Nomad(), allocs[0].ID)

	tc.alloc = api.Allocation{
		ID:        allocs[0].ID,
		Namespace: allocs[0].Namespace,
		NodeID:    allocs[0].NodeID,
	}
}

func (tc *NomadExecE2ETest) TestExecBasicResponses(f *framework.F) {
	for _, c := range dtestutils.ExecTaskStreamingBasicCases {
		f.T().Run(c.Name, func(t *testing.T) {

			stdin := newTestStdin(c.Tty, c.Stdin)
			var stdout, stderr bytes.Buffer

			resizeCh := make(chan api.TerminalSize)
			go func() {
				resizeCh <- api.TerminalSize{Height: 100, Width: 100}
			}()

			ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second)
			defer cancelFn()

			exitCode, err := tc.Nomad().Allocations().Exec(ctx,
				&tc.alloc, "task", c.Tty,
				[]string{"/bin/sh", "-c", c.Command},
				stdin, &stdout, &stderr,
				resizeCh, nil)

			// TODO: Occasionally, we get "Unexpected EOF" error, but with the correct output.
			// investigate why
			if err != nil && strings.Contains(err.Error(), io.ErrUnexpectedEOF.Error()) {
				f.T().Logf("got unexpected EOF error, ignoring: %v", err)
			} else {
				assert.NoError(t, err)
			}

			assert.Equal(t, c.ExitCode, exitCode)

			switch s := c.Stdout.(type) {
			case string:
				assert.Equal(t, s, stdout.String())
			case *regexp.Regexp:
				assert.Regexp(t, s, stdout.String())
			case nil:
				assert.Empty(t, stdout.String())
			default:
				assert.Fail(t, "unexpected stdout type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s))
			}

			switch s := c.Stderr.(type) {
			case string:
				assert.Equal(t, s, stderr.String())
			case *regexp.Regexp:
				assert.Regexp(t, s, stderr.String())
			case nil:
				assert.Empty(t, stderr.String())
			default:
				assert.Fail(t, "unexpected stderr type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s))
			}
		})
	}
}

func (tc *NomadExecE2ETest) AfterAll(f *framework.F) {
	jobs := tc.Nomad().Jobs()
	if tc.jobID != "" {
		jobs.Deregister(tc.jobID, true, nil)
	}
	tc.Nomad().System().GarbageCollect()
}

func newTestStdin(tty bool, d string) io.Reader {
	pr, pw := io.Pipe()
	go func() {
		pw.Write([]byte(d))

		// when testing TTY, leave connection open for the entire duration of command
		// closing stdin may cause TTY session prematurely before command completes
		if !tty {
			pw.Close()
		}

	}()

	return pr
}
<|endoftext|>


<commit_before>package main

import (
  "log"
  "github.com/gorilla/mux"
  "net/http"
  "net/http/httputil"
  "net/url"
  "github.com/fhs/gompd/mpd"
  "github.com/ascherkus/go-id3/src/id3"
  "os"
  "fmt"
  "encoding/json"
  "strconv"
  "io"
)

// TODO: consider if global is really the best idea, or if we should
//       make some classes, or something...
var mpd_conn *mpd.Client

func main() {
  // setup our global MPD connection
  mpd_conn = mpdConnect("localhost:6600")
  defer mpd_conn.Close()

  if mpd_conn == nil {
    log.Fatal("MPD Connection is nil!")
  }

  // create a new mux router for our server.
  r := mux.NewRouter()

  // requests to `/stream` are proxied to the MPD httpd.
  r.HandleFunc("/stream",
    httputil.NewSingleHostReverseProxy(
      &url.URL{
        Scheme:"http",
        Host: "localhost:8000",
        Path: "/",
      }).ServeHTTP)

  r.HandleFunc("/songs", listSongs)
  r.HandleFunc("/current", getCurrentSong)
  r.HandleFunc("/upcoming", getUpcomingSongs)
  r.HandleFunc("/add", addSong)

  // This MUST go last! It takes precidence over any after it, meaning
  // the server will try to serve a file, which most likely doesn't exist,
  // and will 404.
  //
  // serve up the frontend files.
  r.PathPrefix("/").Handler(http.FileServer(http.Dir("../frontend/turbo_wookie/web")))

  // sit, waiting, like a hunter, spying on its prey.
  log.Println("Starting server on port 9000")
  http.ListenAndServe(":9000", r)
}

/********************
  Handler Functions
 ********************/

// return all songs known to MPD to the client.
func listSongs(w http.ResponseWriter, r *http.Request) {
  // get all files from MPD
  mpdfiles, err := mpd_conn.GetFiles()
  if err != nil {
    MPDReconnect()

    mpdfiles, err = mpd_conn.GetFiles()

    if err != nil {
      error(w, "Couldn't get a list of files...", err)
      return
    }
  }

  // create a slice of id3.File s
  //files := make([]*id3.File, 0)
  files := make([]*TBFile, 0)

  for _, song := range mpdfiles {
    // grab the file on the filesystem
    file, err := os.Open("mpd/music/" + song)
    if err != nil {
      error(w, "Couldn't open file: " + song, err)
      return
    }

    // add the current file to our slice
    id3_file := id3Read(file, song)
    files = append(files, id3_file)
  }

  // send the json to the client.
  fmt.Fprintf(w, jsoniffy(files))
}

// Return a JSON representation of the currently playing song.
func getCurrentSong(w http.ResponseWriter, r *http.Request) {
  currentSong, err := mpd_conn.CurrentSong()
  if err != nil {
    MPDReconnect()

    currentSong, err = mpd_conn.CurrentSong()

    if err != nil {
      error(w, "Couldn't get current song info for upcoming list", err)
      return
    }
  }

  fmt.Fprintf(w, jsoniffy(currentSong))
}

func getUpcomingSongs(w http.ResponseWriter, r *http.Request) {
  currentSong, err := mpd_conn.CurrentSong()
  if err != nil {
    MPDReconnect()

    currentSong, err = mpd_conn.CurrentSong()

    if err != nil {
      error(w, "Couldn't get current song info for upcoming list", err)
      return
    }
  }

  pos, err := strconv.Atoi(currentSong["Pos"])
  if err != nil {
    error(w, "Couldn't turn current song's position to int", err)
    return
  }

  playlist, err := mpd_conn.PlaylistInfo(-1, -1)
  if err != nil {
    MPDReconnect()
    playlist, err = mpd_conn.PlaylistInfo(-1, -1)

    if err != nil {
      error(w, "Couldn't get the current playlist", err)
      return
    }
  }

  upcoming := playlist[pos + 1:]

  fmt.Fprintf(w, jsoniffy(upcoming))
}

func addSong(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()
  log.Println(r.Form)

  song, ok := r.Form["song"]
  if !ok {
    jsonError(w, "No song given", nil)
    return
  }

  err := mpd_conn.Add(song[0])
  log.Println(err)
  ue := "unexpected response: "

  if err.Error()[:len(ue)] != ue {
    jsonError(w, "Unknown song", err)
    return
  } else if err != nil {
    MPDReconnect()
    err = mpd_conn.Add(song[0])

    if err != nil {
      jsonError(w, "Couldn't add song to playlist.", err)
      return
    }
  }

}

/*******************
  Helper Functions
 *******************/

// Connect to MPD's control channel, and set the global mpd_conn to it.
func mpdConnect(url string) *mpd.Client {
  conn, err := mpd.Dial("tcp", url)

  // if we can't connect to MPD everything's fucked, nothing's going to work
  // kill all humans, and die, respectfully, after explaining what the issue
  // is.
  if err != nil {
    log.Println("\n\nServer quiting because it can't connect to MPD");
    log.Println(err)

    return nil
  }

  return conn
}

// helper struct; used to hold some ID3 info, plus an MPD file path.
type TBFile struct {id3.File; FilePath string;}

// helper method, returns a pointer to one of our helper structs (see above).
func id3Read(reader io.Reader, filePath string) *TBFile {
  id3File := id3.Read(reader)

  file := new(TBFile)
  file.Header = id3File.Header
  file.Name = id3File.Name
  file.Artist = id3File.Artist
  file.Album = id3File.Album
  file.Year = id3File.Year
  file.FilePath = filePath

  return file
}

func MPDReconnect() {
  mpd_conn.Close()
  mpd_conn = mpdConnect("localhost:6600")
}

// turn anything into JSON.
func jsoniffy(v interface {}) string {
  obj, err := json.MarshalIndent(v, "", "  ")
  if err != nil {
    log.Print("Couldn't turn something into JSON: ", v)
    log.Fatal(err)
  }

  return string(obj)
}

func error(w http.ResponseWriter, message string, err interface{Error() string;}) {
  log.Println("An error occured; telling the client.")
  log.Println("Message:", message)
  log.Println("Error:", err, "\n\n")

  fmt.Fprintf(w, message + "\n")
}

func jsonError(w http.ResponseWriter, message string, err interface{Error() string;}) {
  m := make(map[string]string)
  m["error"] = message
  error(w, jsoniffy(m), err)
}

<commit_msg>Songs can be added now.<commit_after>package main

import (
  "log"
  "github.com/gorilla/mux"
  "net/http"
  "net/http/httputil"
  "net/url"
  "github.com/fhs/gompd/mpd"
  "github.com/ascherkus/go-id3/src/id3"
  "os"
  "fmt"
  "encoding/json"
  "strconv"
  "io"
)

// TODO: consider if global is really the best idea, or if we should
//       make some classes, or something...
var mpd_conn *mpd.Client

func main() {
  // setup our global MPD connection
  mpd_conn = mpdConnect("localhost:6600")
  defer mpd_conn.Close()

  if mpd_conn == nil {
    log.Fatal("MPD Connection is nil!")
  }

  // create a new mux router for our server.
  r := mux.NewRouter()

  // requests to `/stream` are proxied to the MPD httpd.
  r.HandleFunc("/stream",
    httputil.NewSingleHostReverseProxy(
      &url.URL{
        Scheme:"http",
        Host: "localhost:8000",
        Path: "/",
      }).ServeHTTP)

  r.HandleFunc("/songs", listSongs)
  r.HandleFunc("/current", getCurrentSong)
  r.HandleFunc("/upcoming", getUpcomingSongs)
  r.HandleFunc("/add", addSong)

  // This MUST go last! It takes precidence over any after it, meaning
  // the server will try to serve a file, which most likely doesn't exist,
  // and will 404.
  //
  // serve up the frontend files.
  r.PathPrefix("/").Handler(http.FileServer(http.Dir("../frontend/turbo_wookie/web")))

  // sit, waiting, like a hunter, spying on its prey.
  log.Println("Starting server on port 9000")
  http.ListenAndServe(":9000", r)
}

/********************
  Handler Functions
 ********************/

// return all songs known to MPD to the client.
func listSongs(w http.ResponseWriter, r *http.Request) {
  // get all files from MPD
  mpdfiles, err := mpd_conn.GetFiles()
  if err != nil {
    MPDReconnect()

    mpdfiles, err = mpd_conn.GetFiles()

    if err != nil {
      error(w, "Couldn't get a list of files...", err)
      return
    }
  }

  // create a slice of id3.File s
  //files := make([]*id3.File, 0)
  files := make([]*TBFile, 0)

  for _, song := range mpdfiles {
    // grab the file on the filesystem
    file, err := os.Open("mpd/music/" + song)
    if err != nil {
      error(w, "Couldn't open file: " + song, err)
      return
    }

    // add the current file to our slice
    id3_file := id3Read(file, song)
    files = append(files, id3_file)
  }

  // send the json to the client.
  fmt.Fprintf(w, jsoniffy(files))
}

// Return a JSON representation of the currently playing song.
func getCurrentSong(w http.ResponseWriter, r *http.Request) {
  currentSong, err := mpd_conn.CurrentSong()
  if err != nil {
    MPDReconnect()

    currentSong, err = mpd_conn.CurrentSong()

    if err != nil {
      error(w, "Couldn't get current song info for upcoming list", err)
      return
    }
  }

  fmt.Fprintf(w, jsoniffy(currentSong))
}

func getUpcomingSongs(w http.ResponseWriter, r *http.Request) {
  currentSong, err := mpd_conn.CurrentSong()
  if err != nil {
    MPDReconnect()

    currentSong, err = mpd_conn.CurrentSong()

    if err != nil {
      error(w, "Couldn't get current song info for upcoming list", err)
      return
    }
  }

  pos, err := strconv.Atoi(currentSong["Pos"])
  if err != nil {
    error(w, "Couldn't turn current song's position to int", err)
    return
  }

  playlist, err := mpd_conn.PlaylistInfo(-1, -1)
  if err != nil {
    MPDReconnect()
    playlist, err = mpd_conn.PlaylistInfo(-1, -1)

    if err != nil {
      error(w, "Couldn't get the current playlist", err)
      return
    }
  }

  upcoming := playlist[pos + 1:]

  fmt.Fprintf(w, jsoniffy(upcoming))
}

func addSong(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()
  song, ok := r.Form["song"]
  if !ok {
    jsonError(w, "No song given", nil)
    return
  }

  err := mpd_conn.Add(song[0])
  ue := "unexpected response: "

  if err != nil {
    if err.Error()[:len(ue)] == ue {
      log.Println("add UE song error:", err)
      jsonError(w, "Unknown song", err)
      return
    }

    log.Println("add song error:", err)
    MPDReconnect()
    err = mpd_conn.Add(song[0])

    if err != nil {
      jsonError(w, "Couldn't add song to playlist.", err)
      return
    }
  }

  m := make(map[string]string)
  m["note"] = "Added song: " + song[0]
  fmt.Fprintf(w, jsoniffy(m))
}

/*******************
  Helper Functions
 *******************/

// Connect to MPD's control channel, and set the global mpd_conn to it.
func mpdConnect(url string) *mpd.Client {
  conn, err := mpd.Dial("tcp", url)

  // if we can't connect to MPD everything's fucked, nothing's going to work
  // kill all humans, and die, respectfully, after explaining what the issue
  // is.
  if err != nil {
    log.Println("\n\nServer quiting because it can't connect to MPD");
    log.Println(err)

    return nil
  }

  return conn
}

// helper struct; used to hold some ID3 info, plus an MPD file path.
type TBFile struct {id3.File; FilePath string;}

// helper method, returns a pointer to one of our helper structs (see above).
func id3Read(reader io.Reader, filePath string) *TBFile {
  id3File := id3.Read(reader)

  file := new(TBFile)
  file.Header = id3File.Header
  file.Name = id3File.Name
  file.Artist = id3File.Artist
  file.Album = id3File.Album
  file.Year = id3File.Year
  file.FilePath = filePath

  return file
}

func MPDReconnect() {
  mpd_conn.Close()
  mpd_conn = mpdConnect("localhost:6600")
}

// turn anything into JSON.
func jsoniffy(v interface {}) string {
  obj, err := json.MarshalIndent(v, "", "  ")
  if err != nil {
    log.Print("Couldn't turn something into JSON: ", v)
    log.Fatal(err)
  }

  return string(obj)
}

func error(w http.ResponseWriter, message string, err interface{Error() string;}) {
  log.Println("An error occured; telling the client.")
  log.Println("Message:", message)
  log.Println("Error:", err, "\n\n")

  fmt.Fprintf(w, message + "\n")
}

func jsonError(w http.ResponseWriter, message string, err interface{Error() string;}) {
  m := make(map[string]string)
  m["error"] = message
  error(w, jsoniffy(m), err)
}

<|endoftext|>


<commit_before>package dnsimple

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
)

var (
	// mux is the HTTP request multiplexer used with the test server.
	mux *http.ServeMux

	// client is the DNSimple client being tested.
	client *Client

	// server is a test HTTP server used to provide mock API responses.
	server *httptest.Server
)

// This method of testing http client APIs is borrowed from
// Will Norris's work in go-github @ https://github.com/google/go-github
func setup() {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	client = NewClient("mytoken", "me@example.com")
	client.BaseURL = server.URL + "/"
}

func teardown() {
	server.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if want != r.Method {
		t.Errorf("Request method = %v, want %v", r.Method, want)
	}
}

func testRequestJSON(t *testing.T, r *http.Request, values map[string]interface{}) {
	var dat map[string]interface{}

	body, _ := ioutil.ReadAll(r.Body)

	if err := json.Unmarshal(body, &dat); err != nil {
		t.Errorf("Could not decode json body: %v", err)
	}

	if !reflect.DeepEqual(values, dat) {
		t.Errorf("Request parameters = %v, want %v", dat, values)
	}
}

type values map[string]string

func testFormValues(t *testing.T, r *http.Request, values values) {
	want := url.Values{}
	for k, v := range values {
		want.Add(k, v)
	}

	r.ParseForm()
	if !reflect.DeepEqual(want, r.Form) {
		t.Errorf("Request parameters = %v, want %v", r.Form, want)
	}
}

func testString(t *testing.T, test, value, want string) {
	if value != want {
		t.Errorf("%s returned %+v, want %+v", test, value, want)
	}
}

func TestNewClient(t *testing.T) {
	c := NewClient("mytoken", "me@example.com")

	if c.BaseURL != baseURL {
		t.Errorf("NewClient BaseURL = %v, want %v", c.BaseURL, baseURL)
	}
}

func TestNewAuthenticatedClient(t *testing.T) {
	c := NewAuthenticatedClient(NewApiTokenCredentials("me@example.com", "mytoken"))

	if c.BaseURL != baseURL {
		t.Errorf("NewClient BaseURL = %v, want %v", c.BaseURL, baseURL)
	}
}

func TestNewRequest(t *testing.T) {
	c := NewClient("mytoken", "me@example.com")
	c.BaseURL = "https://go.example.com/"

	inURL, outURL := "foo", "https://go.example.com/v1/foo"
	req, _ := c.NewRequest("GET", inURL, nil)

	// test that relative URL was expanded with the proper BaseURL
	if req.URL.String() != outURL {
		t.Errorf("makeRequest(%v) URL = %v, want %v", inURL, req.URL, outURL)
	}

	// test that default user-agent is attached to the request
	userAgent := req.Header.Get("User-Agent")
	if c.UserAgent != userAgent {
		t.Errorf("makeRequest() User-Agent = %v, want %v", userAgent, c.UserAgent)
	}
}
<commit_msg>Test error handling when MarshalJSON() returns an error.<commit_after>package dnsimple

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
)

var (
	// mux is the HTTP request multiplexer used with the test server.
	mux *http.ServeMux

	// client is the DNSimple client being tested.
	client *Client

	// server is a test HTTP server used to provide mock API responses.
	server *httptest.Server
)

// This method of testing http client APIs is borrowed from
// Will Norris's work in go-github @ https://github.com/google/go-github
func setup() {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	client = NewClient("mytoken", "me@example.com")
	client.BaseURL = server.URL + "/"
}

func teardown() {
	server.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if want != r.Method {
		t.Errorf("Request method = %v, want %v", r.Method, want)
	}
}

func testRequestJSON(t *testing.T, r *http.Request, values map[string]interface{}) {
	var dat map[string]interface{}

	body, _ := ioutil.ReadAll(r.Body)

	if err := json.Unmarshal(body, &dat); err != nil {
		t.Errorf("Could not decode json body: %v", err)
	}

	if !reflect.DeepEqual(values, dat) {
		t.Errorf("Request parameters = %v, want %v", dat, values)
	}
}

type values map[string]string

func testFormValues(t *testing.T, r *http.Request, values values) {
	want := url.Values{}
	for k, v := range values {
		want.Add(k, v)
	}

	r.ParseForm()
	if !reflect.DeepEqual(want, r.Form) {
		t.Errorf("Request parameters = %v, want %v", r.Form, want)
	}
}

func testString(t *testing.T, test, value, want string) {
	if value != want {
		t.Errorf("%s returned %+v, want %+v", test, value, want)
	}
}

func TestNewClient(t *testing.T) {
	c := NewClient("mytoken", "me@example.com")

	if c.BaseURL != baseURL {
		t.Errorf("NewClient BaseURL = %v, want %v", c.BaseURL, baseURL)
	}
}

func TestNewAuthenticatedClient(t *testing.T) {
	c := NewAuthenticatedClient(NewApiTokenCredentials("me@example.com", "mytoken"))

	if c.BaseURL != baseURL {
		t.Errorf("NewClient BaseURL = %v, want %v", c.BaseURL, baseURL)
	}
}

func TestNewRequest(t *testing.T) {
	c := NewClient("mytoken", "me@example.com")
	c.BaseURL = "https://go.example.com/"

	inURL, outURL := "foo", "https://go.example.com/v1/foo"
	req, _ := c.NewRequest("GET", inURL, nil)

	// test that relative URL was expanded with the proper BaseURL
	if req.URL.String() != outURL {
		t.Errorf("makeRequest(%v) URL = %v, want %v", inURL, req.URL, outURL)
	}

	// test that default user-agent is attached to the request
	userAgent := req.Header.Get("User-Agent")
	if c.UserAgent != userAgent {
		t.Errorf("makeRequest() User-Agent = %v, want %v", userAgent, c.UserAgent)
	}
}

type badObject struct {
}

func (o *badObject) MarshalJSON() ([]byte, error) {
	return nil, errors.New("Bad object is bad")
}

func TestNewRequestWithBody(t *testing.T) {
	c := NewClient("mytoken", "me@example.com")
	c.BaseURL = "https://go.example.com/"

	inURL, _ := "foo", "https://go.example.com/v1/foo"
	badObject := badObject{}
	_, err := c.NewRequest("GET", inURL, &badObject)

	if err == nil {
		t.Errorf("NewRequest with body expected error with blank string")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"reflect"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/h2so5/utp"
)

func main() {
	var l = flag.Int("c", 10485760, "Payload length (bytes)")
	var h = flag.Bool("h", false, "Human readable")
	flag.Parse()

	payload := make([]byte, *l)
	for i := range payload {
		payload[i] = byte(rand.Int())
	}

	if *h {
		fmt.Printf("Payload: %s\n", humanize.IBytes(uint64(len(payload))))
	} else {
		fmt.Printf("Payload: %d\n", len(payload))
	}

	c2s := c2s(payload)
	n, p := humanize.ComputeSI(c2s)
	if *h {
		fmt.Printf("C2S: %f%sbps\n", n, p)
	} else {
		fmt.Printf("C2S: %f\n", c2s)
	}

	s2c := s2c(payload)
	n, p = humanize.ComputeSI(s2c)
	if *h {
		fmt.Printf("S2C: %f%sbps\n", n, p)
	} else {
		fmt.Printf("S2C: %f\n", c2s)
	}

	avg := (c2s + s2c) / 2.0
	n, p = humanize.ComputeSI(avg)

	if *h {
		fmt.Printf("AVG: %f%sbps\n", n, p)
	} else {
		fmt.Printf("AVG: %f\n", avg)
	}
}

func c2s(payload []byte) float64 {
	ln, err := utp.Listen("utp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}

	raddr, err := utp.ResolveUTPAddr("utp", ln.Addr().String())
	if err != nil {
		log.Fatal(err)
	}

	c, err := utp.DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	if err != nil {
		log.Fatal(err)
	}

	s, err := ln.Accept()
	if err != nil {
		log.Fatal(err)
	}
	defer s.Close()
	ln.Close()

	rch := make(chan []byte)

	go func() {
		defer c.Close()
		c.Write(payload[:])
	}()

	go func() {
		b, _ := ioutil.ReadAll(s)
		rch <- b
	}()

	start := time.Now()
	r := <-rch
	bps := float64(len(payload)*8) / (float64(time.Now().Sub(start)) / float64(time.Second))

	if !reflect.DeepEqual(payload, r) {
		log.Fatal("Broken payload")
	}

	return bps
}

func s2c(payload []byte) float64 {
	ln, err := utp.Listen("utp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}

	raddr, err := utp.ResolveUTPAddr("utp", ln.Addr().String())
	if err != nil {
		log.Fatal(err)
	}

	c, err := utp.DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	if err != nil {
		log.Fatal(err)
	}

	s, err := ln.Accept()
	if err != nil {
		log.Fatal(err)
	}
	defer s.Close()
	ln.Close()

	rch := make(chan []byte)

	go func() {
		defer s.Close()
		s.Write(payload[:])
	}()

	go func() {
		b, _ := ioutil.ReadAll(c)
		rch <- b
	}()

	start := time.Now()
	r := <-rch
	bps := float64(len(payload)*8) / (float64(time.Now().Sub(start)) / float64(time.Second))

	if !reflect.DeepEqual(payload, r) {
		log.Fatal("Broken payload")
	}

	return bps
}
<commit_msg>Fix benchmark typo<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"reflect"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/h2so5/utp"
)

func main() {
	var l = flag.Int("c", 10485760, "Payload length (bytes)")
	var h = flag.Bool("h", false, "Human readable")
	flag.Parse()

	payload := make([]byte, *l)
	for i := range payload {
		payload[i] = byte(rand.Int())
	}

	if *h {
		fmt.Printf("Payload: %s\n", humanize.IBytes(uint64(len(payload))))
	} else {
		fmt.Printf("Payload: %d\n", len(payload))
	}

	c2s := c2s(payload)
	n, p := humanize.ComputeSI(c2s)
	if *h {
		fmt.Printf("C2S: %f%sbps\n", n, p)
	} else {
		fmt.Printf("C2S: %f\n", c2s)
	}

	s2c := s2c(payload)
	n, p = humanize.ComputeSI(s2c)
	if *h {
		fmt.Printf("S2C: %f%sbps\n", n, p)
	} else {
		fmt.Printf("S2C: %f\n", s2c)
	}

	avg := (c2s + s2c) / 2.0
	n, p = humanize.ComputeSI(avg)

	if *h {
		fmt.Printf("AVG: %f%sbps\n", n, p)
	} else {
		fmt.Printf("AVG: %f\n", avg)
	}
}

func c2s(payload []byte) float64 {
	ln, err := utp.Listen("utp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}

	raddr, err := utp.ResolveUTPAddr("utp", ln.Addr().String())
	if err != nil {
		log.Fatal(err)
	}

	c, err := utp.DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	if err != nil {
		log.Fatal(err)
	}

	s, err := ln.Accept()
	if err != nil {
		log.Fatal(err)
	}
	defer s.Close()
	ln.Close()

	rch := make(chan []byte)

	go func() {
		defer c.Close()
		c.Write(payload[:])
	}()

	go func() {
		b, _ := ioutil.ReadAll(s)
		rch <- b
	}()

	start := time.Now()
	r := <-rch
	bps := float64(len(payload)*8) / (float64(time.Now().Sub(start)) / float64(time.Second))

	if !reflect.DeepEqual(payload, r) {
		log.Fatal("Broken payload")
	}

	return bps
}

func s2c(payload []byte) float64 {
	ln, err := utp.Listen("utp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}

	raddr, err := utp.ResolveUTPAddr("utp", ln.Addr().String())
	if err != nil {
		log.Fatal(err)
	}

	c, err := utp.DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	if err != nil {
		log.Fatal(err)
	}

	s, err := ln.Accept()
	if err != nil {
		log.Fatal(err)
	}
	defer s.Close()
	ln.Close()

	rch := make(chan []byte)

	go func() {
		defer s.Close()
		s.Write(payload[:])
	}()

	go func() {
		b, _ := ioutil.ReadAll(c)
		rch <- b
	}()

	start := time.Now()
	r := <-rch
	bps := float64(len(payload)*8) / (float64(time.Now().Sub(start)) / float64(time.Second))

	if !reflect.DeepEqual(payload, r) {
		log.Fatal("Broken payload")
	}

	return bps
}
<|endoftext|>


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

// Represents bencode data structure using native Go types: booleans, floats,
// strings, slices, and maps.

package bencode

import (
	"io"
)

// Decode a bencode stream

// Decode parses the stream r and returns the
// generic bencode object representation.  The object representation is a tree
// of Go data types.  The data return value may be one of string,
// int64, uint64, []interface{} or map[string]interface{}.  The slice and map
// elements may in turn contain any of the types listed above and so on.
//
// If Decode encounters a syntax error, it returns with err set to an
// instance of ParseError.  See ParseError documentation for details.
func Decode(r io.Reader) (data interface{}, err error) {
	jb := newDecoder(nil, nil)
	err = Parse(r, jb)
	if err == nil {
		data = jb.Copy()
	}
	return
}

type decoder struct {
	// A value being constructed.
	value interface{}
	// Container entity to flush into.  Can be either []interface{} or
	// map[string]interface{}.
	container interface{}
	// The index into the container interface.  Either int or string.
	index interface{}
}

func newDecoder(container interface{}, key interface{}) *decoder {
	return &decoder{container: container, index: key}
}

func (j *decoder) Int64(i int64) { j.value = int64(i) }

func (j *decoder) Uint64(i uint64) { j.value = uint64(i) }

func (j *decoder) Float64(f float64) { j.value = float64(f) }

func (j *decoder) String(s string) { j.value = s }

func (j *decoder) Bool(b bool) { j.value = b }

func (j *decoder) Null() { j.value = nil }

func (j *decoder) Array() { j.value = make([]interface{}, 0, 8) }

func (j *decoder) Map() { j.value = make(map[string]interface{}) }

func (j *decoder) Elem(i int) Builder {
	v, ok := j.value.([]interface{})
	if !ok {
		v = make([]interface{}, 0, 8)
		j.value = v
	}
	lens := len(v)
	if cap(v) <= lens {
		news := make([]interface{}, 0, lens*2)
		copy(news, j.value.([]interface{}))
		v = news
	}
	v = v[0 : lens+1]
	j.value = v
	return newDecoder(v, i)
}

func (j *decoder) Key(s string) Builder {
	m, ok := j.value.(map[string]interface{})
	if !ok {
		m = make(map[string]interface{})
		j.value = m
	}
	return newDecoder(m, s)
}

func (j *decoder) Flush() {
	switch c := j.container.(type) {
	case []interface{}:
		index := j.index.(int)
		c[index] = j.Copy()
	case map[string]interface{}:
		index := j.index.(string)
		c[index] = j.Copy()
	}
}

// Get the value built by this builder.
func (j *decoder) Copy() interface{} {
	return j.value
}
<commit_msg>There is some kind of off-by-one bug here and we end up with nils in the slice.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Represents bencode data structure using native Go types: booleans, floats,
// strings, slices, and maps.

package bencode

import (
	"io"
)

// Decode a bencode stream

// Decode parses the stream r and returns the
// generic bencode object representation.  The object representation is a tree
// of Go data types.  The data return value may be one of string,
// int64, uint64, []interface{} or map[string]interface{}.  The slice and map
// elements may in turn contain any of the types listed above and so on.
//
// If Decode encounters a syntax error, it returns with err set to an
// instance of ParseError.  See ParseError documentation for details.
func Decode(r io.Reader) (data interface{}, err error) {
	jb := newDecoder(nil, nil)
	err = Parse(r, jb)
	if err == nil {
		data = jb.Copy()
	}
	return
}

type decoder struct {
	// A value being constructed.
	value interface{}
	// Container entity to flush into.  Can be either []interface{} or
	// map[string]interface{}.
	container interface{}
	// The index into the container interface.  Either int or string.
	index interface{}
}

func newDecoder(container interface{}, key interface{}) *decoder {
	return &decoder{container: container, index: key}
}

func (j *decoder) Int64(i int64) { j.value = int64(i) }

func (j *decoder) Uint64(i uint64) { j.value = uint64(i) }

func (j *decoder) Float64(f float64) { j.value = float64(f) }

func (j *decoder) String(s string) { j.value = s }

func (j *decoder) Bool(b bool) { j.value = b }

func (j *decoder) Null() { j.value = nil }

func (j *decoder) Array() { j.value = make([]interface{}, 0, 8) }

func (j *decoder) Map() { j.value = make(map[string]interface{}) }

func (j *decoder) Elem(i int) Builder {
	v, ok := j.value.([]interface{})
	if !ok {
		v = make([]interface{}, 0, 8)
		j.value = v
	}
/* XXX There is a bug in here somewhere, but append() works fine.
	lens := len(v)
	if cap(v) <= lens {
		news := make([]interface{}, 0, lens*2)
		copy(news, j.value.([]interface{}))
		v = news
	}
	v = v[0 : lens+1]
*/
	v = append(v, nil)
	j.value = v
	return newDecoder(v, i)
}

func (j *decoder) Key(s string) Builder {
	m, ok := j.value.(map[string]interface{})
	if !ok {
		m = make(map[string]interface{})
		j.value = m
	}
	return newDecoder(m, s)
}

func (j *decoder) Flush() {
	switch c := j.container.(type) {
	case []interface{}:
		index := j.index.(int)
		c[index] = j.Copy()
	case map[string]interface{}:
		index := j.index.(string)
		c[index] = j.Copy()
	}
}

// Get the value built by this builder.
func (j *decoder) Copy() interface{} {
	return j.value
}
<|endoftext|>


<commit_before>package bencode

import (
	"bufio"
	"reflect"
	"runtime"
	"sort"
	"strconv"
	"sync"
)

func is_empty_value(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

type encoder struct {
	*bufio.Writer
	scratch [64]byte
}

func (e *encoder) encode(v interface{}) (err error) {
	if v == nil {
		return
	}
	defer func() {
		if e := recover(); e != nil {
			if _, ok := e.(runtime.Error); ok {
				panic(e)
			}
			var ok bool
			err, ok = e.(error)
			if !ok {
				panic(e)
			}
		}
	}()
	e.reflect_value(reflect.ValueOf(v))
	return e.Flush()
}

type string_values []reflect.Value

func (sv string_values) Len() int           { return len(sv) }
func (sv string_values) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
func (sv string_values) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
func (sv string_values) get(i int) string   { return sv[i].String() }

func (e *encoder) write(s []byte) {
	_, err := e.Write(s)
	if err != nil {
		panic(err)
	}
}

func (e *encoder) write_string(s string) {
	_, err := e.WriteString(s)
	if err != nil {
		panic(err)
	}
}

func (e *encoder) reflect_string(s string) {
	b := strconv.AppendInt(e.scratch[:0], int64(len(s)), 10)
	e.write(b)
	e.write_string(":")
	e.write_string(s)
}

func (e *encoder) reflect_byte_slice(s []byte) {
	b := strconv.AppendInt(e.scratch[:0], int64(len(s)), 10)
	e.write(b)
	e.write_string(":")
	e.write(s)
}

// returns true if the value implements Marshaler interface and marshaling was
// done successfully
func (e *encoder) reflect_marshaler(v reflect.Value) bool {
	m, ok := v.Interface().(Marshaler)
	if !ok {
		// T doesn't work, try *T
		if v.Kind() != reflect.Ptr && v.CanAddr() {
			m, ok = v.Addr().Interface().(Marshaler)
			if ok {
				v = v.Addr()
			}
		}
	}
	if ok && (v.Kind() != reflect.Ptr || !v.IsNil()) {
		data, err := m.MarshalBencode()
		if err != nil {
			panic(&MarshalerError{v.Type(), err})
		}
		e.write(data)
		return true
	}

	return false
}

func (e *encoder) reflect_value(v reflect.Value) {

	if e.reflect_marshaler(v) {
		return
	}

	switch v.Kind() {
	case reflect.Bool:
		if v.Bool() {
			e.write_string("i1e")
		} else {
			e.write_string("i0e")
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
		e.write_string("i")
		e.write(b)
		e.write_string("e")
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
		e.write_string("i")
		e.write(b)
		e.write_string("e")
	case reflect.String:
		e.reflect_string(v.String())
	case reflect.Struct:
		e.write_string("d")
		for _, ef := range encode_fields(v.Type()) {
			field_value := v.Field(ef.i)
			if ef.omit_empty && is_empty_value(field_value) {
				continue
			}
			e.reflect_string(ef.tag)
			e.reflect_value(field_value)
		}
		e.write_string("e")
	case reflect.Map:
		if v.Type().Key().Kind() != reflect.String {
			panic(&MarshalTypeError{v.Type()})
		}
		if v.IsNil() {
			e.write_string("de")
			break
		}
		e.write_string("d")
		sv := string_values(v.MapKeys())
		sort.Sort(sv)
		for _, key := range sv {
			e.reflect_string(key.String())
			e.reflect_value(v.MapIndex(key))
		}
		e.write_string("e")
	case reflect.Slice:
		if v.IsNil() {
			e.write_string("le")
			break
		}
		if v.Type().Elem().Kind() == reflect.Uint8 {
			s := v.Bytes()
			e.reflect_byte_slice(s)
			break
		}
		fallthrough
	case reflect.Array:
		e.write_string("l")
		for i, n := 0, v.Len(); i < n; i++ {
			e.reflect_value(v.Index(i))
		}
		e.write_string("e")
	case reflect.Interface, reflect.Ptr:
		if v.IsNil() {
			v = reflect.Zero(v.Type().Elem())
		} else {
			v = v.Elem()
		}
		e.reflect_value(v)
	default:
		panic(&MarshalTypeError{v.Type()})
	}
}

type encode_field struct {
	i          int
	tag        string
	omit_empty bool
}

type encode_fields_sort_type []encode_field

func (ef encode_fields_sort_type) Len() int           { return len(ef) }
func (ef encode_fields_sort_type) Swap(i, j int)      { ef[i], ef[j] = ef[j], ef[i] }
func (ef encode_fields_sort_type) Less(i, j int) bool { return ef[i].tag < ef[j].tag }

var (
	type_cache_lock     sync.RWMutex
	encode_fields_cache = make(map[reflect.Type][]encode_field)
)

func encode_fields(t reflect.Type) []encode_field {
	type_cache_lock.RLock()
	fs, ok := encode_fields_cache[t]
	type_cache_lock.RUnlock()
	if ok {
		return fs
	}

	type_cache_lock.Lock()
	defer type_cache_lock.Unlock()
	fs, ok = encode_fields_cache[t]
	if ok {
		return fs
	}

	for i, n := 0, t.NumField(); i < n; i++ {
		f := t.Field(i)
		if f.PkgPath != "" {
			continue
		}
		if f.Anonymous {
			continue
		}
		var ef encode_field
		ef.i = i
		ef.tag = f.Name

		tv := f.Tag.Get("bencode")
		if tv != "" {
			if tv == "-" {
				continue
			}
			name, opts := parse_tag(tv)
			if name != "" {
				ef.tag = name
			}
			ef.omit_empty = opts.contains("omitempty")
		}
		fs = append(fs, ef)
	}
	fss := encode_fields_sort_type(fs)
	sort.Sort(fss)
	encode_fields_cache[t] = fs
	return fs
}
<commit_msg>bencode: Can't get zero type of a nil interface<commit_after>package bencode

import (
	"bufio"
	"reflect"
	"runtime"
	"sort"
	"strconv"
	"sync"
)

func is_empty_value(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

type encoder struct {
	*bufio.Writer
	scratch [64]byte
}

func (e *encoder) encode(v interface{}) (err error) {
	if v == nil {
		return
	}
	defer func() {
		if e := recover(); e != nil {
			if _, ok := e.(runtime.Error); ok {
				panic(e)
			}
			var ok bool
			err, ok = e.(error)
			if !ok {
				panic(e)
			}
		}
	}()
	e.reflect_value(reflect.ValueOf(v))
	return e.Flush()
}

type string_values []reflect.Value

func (sv string_values) Len() int           { return len(sv) }
func (sv string_values) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
func (sv string_values) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
func (sv string_values) get(i int) string   { return sv[i].String() }

func (e *encoder) write(s []byte) {
	_, err := e.Write(s)
	if err != nil {
		panic(err)
	}
}

func (e *encoder) write_string(s string) {
	_, err := e.WriteString(s)
	if err != nil {
		panic(err)
	}
}

func (e *encoder) reflect_string(s string) {
	b := strconv.AppendInt(e.scratch[:0], int64(len(s)), 10)
	e.write(b)
	e.write_string(":")
	e.write_string(s)
}

func (e *encoder) reflect_byte_slice(s []byte) {
	b := strconv.AppendInt(e.scratch[:0], int64(len(s)), 10)
	e.write(b)
	e.write_string(":")
	e.write(s)
}

// returns true if the value implements Marshaler interface and marshaling was
// done successfully
func (e *encoder) reflect_marshaler(v reflect.Value) bool {
	m, ok := v.Interface().(Marshaler)
	if !ok {
		// T doesn't work, try *T
		if v.Kind() != reflect.Ptr && v.CanAddr() {
			m, ok = v.Addr().Interface().(Marshaler)
			if ok {
				v = v.Addr()
			}
		}
	}
	if ok && (v.Kind() != reflect.Ptr || !v.IsNil()) {
		data, err := m.MarshalBencode()
		if err != nil {
			panic(&MarshalerError{v.Type(), err})
		}
		e.write(data)
		return true
	}

	return false
}

func (e *encoder) reflect_value(v reflect.Value) {

	if e.reflect_marshaler(v) {
		return
	}

	switch v.Kind() {
	case reflect.Bool:
		if v.Bool() {
			e.write_string("i1e")
		} else {
			e.write_string("i0e")
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
		e.write_string("i")
		e.write(b)
		e.write_string("e")
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
		e.write_string("i")
		e.write(b)
		e.write_string("e")
	case reflect.String:
		e.reflect_string(v.String())
	case reflect.Struct:
		e.write_string("d")
		for _, ef := range encode_fields(v.Type()) {
			field_value := v.Field(ef.i)
			if ef.omit_empty && is_empty_value(field_value) {
				continue
			}
			e.reflect_string(ef.tag)
			e.reflect_value(field_value)
		}
		e.write_string("e")
	case reflect.Map:
		if v.Type().Key().Kind() != reflect.String {
			panic(&MarshalTypeError{v.Type()})
		}
		if v.IsNil() {
			e.write_string("de")
			break
		}
		e.write_string("d")
		sv := string_values(v.MapKeys())
		sort.Sort(sv)
		for _, key := range sv {
			e.reflect_string(key.String())
			e.reflect_value(v.MapIndex(key))
		}
		e.write_string("e")
	case reflect.Slice:
		if v.IsNil() {
			e.write_string("le")
			break
		}
		if v.Type().Elem().Kind() == reflect.Uint8 {
			s := v.Bytes()
			e.reflect_byte_slice(s)
			break
		}
		fallthrough
	case reflect.Array:
		e.write_string("l")
		for i, n := 0, v.Len(); i < n; i++ {
			e.reflect_value(v.Index(i))
		}
		e.write_string("e")
	case reflect.Interface:
		e.reflect_value(v.Elem())
	case reflect.Ptr:
		if v.IsNil() {
			v = reflect.Zero(v.Type().Elem())
		} else {
			v = v.Elem()
		}
		e.reflect_value(v)
	default:
		panic(&MarshalTypeError{v.Type()})
	}
}

type encode_field struct {
	i          int
	tag        string
	omit_empty bool
}

type encode_fields_sort_type []encode_field

func (ef encode_fields_sort_type) Len() int           { return len(ef) }
func (ef encode_fields_sort_type) Swap(i, j int)      { ef[i], ef[j] = ef[j], ef[i] }
func (ef encode_fields_sort_type) Less(i, j int) bool { return ef[i].tag < ef[j].tag }

var (
	type_cache_lock     sync.RWMutex
	encode_fields_cache = make(map[reflect.Type][]encode_field)
)

func encode_fields(t reflect.Type) []encode_field {
	type_cache_lock.RLock()
	fs, ok := encode_fields_cache[t]
	type_cache_lock.RUnlock()
	if ok {
		return fs
	}

	type_cache_lock.Lock()
	defer type_cache_lock.Unlock()
	fs, ok = encode_fields_cache[t]
	if ok {
		return fs
	}

	for i, n := 0, t.NumField(); i < n; i++ {
		f := t.Field(i)
		if f.PkgPath != "" {
			continue
		}
		if f.Anonymous {
			continue
		}
		var ef encode_field
		ef.i = i
		ef.tag = f.Name

		tv := f.Tag.Get("bencode")
		if tv != "" {
			if tv == "-" {
				continue
			}
			name, opts := parse_tag(tv)
			if name != "" {
				ef.tag = name
			}
			ef.omit_empty = opts.contains("omitempty")
		}
		fs = append(fs, ef)
	}
	fss := encode_fields_sort_type(fs)
	sort.Sort(fss)
	encode_fields_cache[t] = fs
	return fs
}
<|endoftext|>


<commit_before>package booking

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/marcusolsson/goddd/cargo"
	"github.com/marcusolsson/goddd/location"
	"github.com/marcusolsson/goddd/routing"
	"github.com/marcusolsson/goddd/voyage"
)

var errMissingParameters = errors.New("missing parameters")

func encodeResponse(w http.ResponseWriter, resp interface{}) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	return json.NewEncoder(w).Encode(resp)
}

func decodeBookCargoRequest(r *http.Request) (interface{}, error) {
	var body struct {
		Origin          string `json:"origin"`
		Destination     string `json:"destination"`
		ArrivalDeadline int64  `json:"arrival_deadline"`
	}

	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		return nil, err
	}
	defer r.Body.Close()

	if body.Origin == "" || body.Destination == "" || body.ArrivalDeadline == 0 {
		return nil, errMissingParameters
	}

	return bookCargoRequest{
		Origin:          location.UNLocode(body.Origin),
		Destination:     location.UNLocode(body.Destination),
		ArrivalDeadline: time.Unix(body.ArrivalDeadline/1000, 0),
	}, nil
}

func decodeRequestRoutesRequest(r *http.Request) (interface{}, error) {
	id, ok := mux.Vars(r)["id"]
	if !ok {
		return nil, errMissingParameters
	}
	return requestRoutesRequest{ID: cargo.TrackingID(id)}, nil
}

func decodeAssignToRouteRequest(r *http.Request) (interface{}, error) {
	id, ok := mux.Vars(r)["id"]
	if !ok {
		return nil, errMissingParameters
	}

	var route routing.Route
	if err := json.NewDecoder(r.Body).Decode(&route); err != nil {
		return nil, err
	}
	defer r.Body.Close()

	var legs []cargo.Leg
	for _, l := range route.Legs {
		legs = append(legs, cargo.Leg{
			VoyageNumber:   voyage.Number(l.VoyageNumber),
			LoadLocation:   location.UNLocode(l.From),
			UnloadLocation: location.UNLocode(l.To),
			LoadTime:       l.LoadTime,
			UnloadTime:     l.UnloadTime,
		})
	}

	return assignToRouteRequest{
		ID:        cargo.TrackingID(id),
		Itinerary: cargo.Itinerary{Legs: legs},
	}, nil
}

func decodeChangeDestinationRequest(r *http.Request) (interface{}, error) {
	id, ok := mux.Vars(r)["id"]
	if !ok {
		return nil, errMissingParameters
	}

	var body struct {
		Destination string `json:"destination"`
	}

	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		return nil, err
	}
	defer r.Body.Close()

	if body.Destination == "" {
		return nil, errMissingParameters
	}

	return changeDestinationRequest{
		ID:          cargo.TrackingID(id),
		Destination: location.UNLocode(body.Destination),
	}, nil
}

func decodeListCargosRequest(r *http.Request) (interface{}, error) {
	return listCargosRequest{}, nil
}

func decodeListLocationsRequest(r *http.Request) (interface{}, error) {
	return listLocationsRequest{}, nil
}

func decodeFetchRoutesResponse(resp *http.Response) (interface{}, error) {
	var response fetchRoutesResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return response, nil
}

func encodeFetchRoutesRequest(r *http.Request, request interface{}) error {
	req := request.(fetchRoutesRequest)

	vals := r.URL.Query()
	vals.Add("from", req.From)
	vals.Add("to", req.To)
	r.URL.RawQuery = vals.Encode()

	return nil
}
<commit_msg>Only allow UTC time in book cargo request<commit_after>package booking

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/marcusolsson/goddd/cargo"
	"github.com/marcusolsson/goddd/location"
	"github.com/marcusolsson/goddd/routing"
	"github.com/marcusolsson/goddd/voyage"
)

var errMissingParameters = errors.New("missing parameters")

func encodeResponse(w http.ResponseWriter, resp interface{}) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	return json.NewEncoder(w).Encode(resp)
}

func decodeBookCargoRequest(r *http.Request) (interface{}, error) {
	var body struct {
		Origin          string    `json:"origin"`
		Destination     string    `json:"destination"`
		ArrivalDeadline time.Time `json:"arrival_deadline"`
	}

	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		return nil, err
	}
	defer r.Body.Close()

	if body.Origin == "" || body.Destination == "" || body.ArrivalDeadline.IsZero() {
		return nil, errMissingParameters
	}

	return bookCargoRequest{
		Origin:          location.UNLocode(body.Origin),
		Destination:     location.UNLocode(body.Destination),
		ArrivalDeadline: body.ArrivalDeadline,
	}, nil
}

func decodeRequestRoutesRequest(r *http.Request) (interface{}, error) {
	id, ok := mux.Vars(r)["id"]
	if !ok {
		return nil, errMissingParameters
	}
	return requestRoutesRequest{ID: cargo.TrackingID(id)}, nil
}

func decodeAssignToRouteRequest(r *http.Request) (interface{}, error) {
	id, ok := mux.Vars(r)["id"]
	if !ok {
		return nil, errMissingParameters
	}

	var route routing.Route
	if err := json.NewDecoder(r.Body).Decode(&route); err != nil {
		return nil, err
	}
	defer r.Body.Close()

	var legs []cargo.Leg
	for _, l := range route.Legs {
		legs = append(legs, cargo.Leg{
			VoyageNumber:   voyage.Number(l.VoyageNumber),
			LoadLocation:   location.UNLocode(l.From),
			UnloadLocation: location.UNLocode(l.To),
			LoadTime:       l.LoadTime,
			UnloadTime:     l.UnloadTime,
		})
	}

	return assignToRouteRequest{
		ID:        cargo.TrackingID(id),
		Itinerary: cargo.Itinerary{Legs: legs},
	}, nil
}

func decodeChangeDestinationRequest(r *http.Request) (interface{}, error) {
	id, ok := mux.Vars(r)["id"]
	if !ok {
		return nil, errMissingParameters
	}

	var body struct {
		Destination string `json:"destination"`
	}

	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		return nil, err
	}
	defer r.Body.Close()

	if body.Destination == "" {
		return nil, errMissingParameters
	}

	return changeDestinationRequest{
		ID:          cargo.TrackingID(id),
		Destination: location.UNLocode(body.Destination),
	}, nil
}

func decodeListCargosRequest(r *http.Request) (interface{}, error) {
	return listCargosRequest{}, nil
}

func decodeListLocationsRequest(r *http.Request) (interface{}, error) {
	return listLocationsRequest{}, nil
}

func decodeFetchRoutesResponse(resp *http.Response) (interface{}, error) {
	var response fetchRoutesResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return response, nil
}

func encodeFetchRoutesRequest(r *http.Request, request interface{}) error {
	req := request.(fetchRoutesRequest)

	vals := r.URL.Query()
	vals.Add("from", req.From)
	vals.Add("to", req.To)
	r.URL.RawQuery = vals.Encode()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/badgerodon/proxy"
	"github.com/go-contrib/uuid"
	"github.com/moraes/config"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func DisableAppInProxy(cfg *config.Config, name string) error {
	pcfg, err := proxy.GetConfig("/opt/proxy/config.json")
	if err != nil {
		return fmt.Errorf("error reading proxy config: %v", err)
	}
	host, err := cfg.String(name + ".host")
	if err != nil {
		return fmt.Errorf("error reading host: %v", err)
	}
	delete(pcfg.Routes, host)
	err = pcfg.Save("/opt/proxy/config.json")
	if err != nil {
		return fmt.Errorf("error saving config: %v", err)
	}
	return nil
}
func EnableAppInProxy(cfg *config.Config, name string) error {
	pcfg, err := proxy.GetConfig("/opt/proxy/config.json")
	if err != nil {
		return fmt.Errorf("error reading proxy config: %v", err)
	}
	host, err := cfg.String(name + ".host")
	if err != nil {
		return fmt.Errorf("error reading host: %v", err)
	}
	port, err := cfg.Int(name + ".port")
	if err != nil {
		return fmt.Errorf("error reading port: %v", err)
	}
	pcfg.Routes[host] = proxy.Entry{
		Endpoints: []string{fmt.Sprint("127.0.0.1:", port)},
	}
	err = pcfg.Save("/opt/proxy/config.json")
	if err != nil {
		return fmt.Errorf("error saving config: %v", err)
	}
	return nil
}

func PreReceive(dir, oldrev, newrev, ref string) error {
	// We only care about master
	if ref != "refs/heads/master" {
		return nil
	}

	temp := filepath.Join(os.TempDir(), uuid.NewV4().String())
	err := os.Mkdir(temp, 0777)
	if err != nil {
		return fmt.Errorf("- failed to create directory: %v", err)
	}
	defer os.RemoveAll(temp)

	// Export to temp
	log.Println("exporting", dir, newrev, "to", temp)
	os.Chdir(dir)
	bs, err := exec.Command("bash", "-c", "git archive --format=tar "+newrev+" | tar -C "+temp+" -x ").CombinedOutput()
	if err != nil {
		return fmt.Errorf("- failed to export: %s", bs)
	}
	log.Println("-", string(bs))

	// Get config
	log.Println("reading config")
	os.Chdir(temp)
	cfg, err := config.ParseJsonFile("config.json")
	if err != nil {
		return fmt.Errorf("- failed to read config: %v", err)
	}
	apps, err := cfg.Map("")
	if err != nil {
		return fmt.Errorf("- failed to read applications: %v", err)
	}

	// Build
	for k, v := range apps {
		app, _ := cfg.Get(k)

		//folder, err := app.String("folder")
		//if err != nil {
		//	return fmt.Errorf("- expected folder in: %v, %v", v, err)
		//}
		//typ, err := app.String("type")
		//if err != nil {
		//	return fmt.Errorf("- expected type in: %v, %v", v, err)
		//}
		build, err := app.String("build")
		if err != nil {
			return fmt.Errorf("- expected build in: %v, %v", v, err)
		}

		switch build {
		case "go":
			err = BuildGo(temp, k, app)
		default:
			err = fmt.Errorf("unknown build type %v", build)
		}

		if err != nil {
			return fmt.Errorf("error building %v: %v", k, err)
		}
	}

	// Clean
	log.Println("cleaning")
	filepath.Walk(temp, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			if strings.HasPrefix(filepath.Base(path), ".") || filepath.Base(path) == "Godeps" {
				os.RemoveAll(path)
				return filepath.SkipDir
			}
		} else {
			if strings.HasSuffix(path, ".go") {
				os.Remove(path)
			}
		}

		return nil
	})

	for k, _ := range apps {
		// Sync to endpoints
		log.Println("syncing", k)
		os.Mkdir("/opt/"+k, 0777)
		os.Mkdir("/opt/"+k+"/staging", 0777)
		bs, err := exec.Command("rsync",
			"--recursive",
			"--links",
			"--perms",
			"--times",
			"--devices",
			"--specials",
			"--hard-links",
			"--acls",
			"--delete",
			"--xattrs",
			"--numeric-ids",
			temp+"/",              // from
			"/opt/"+k+"/staging/", // to
		).CombinedOutput()
		if err != nil {
			return fmt.Errorf("error syncing folder: %s", bs)
		}

		// Disable app in load balancer
		err = DisableAppInProxy(cfg, k)
		if err != nil {
			return fmt.Errorf("error disabling app in proxy: %v", err)
		}

		// Stop the app
		log.Println("stopping")
		exec.Command("/etc/init.d/"+k, "stop").Run()

		// Swap to new version
		log.Println("swapping")
		os.Mkdir("/opt/"+k+"/current", 0777)
		bs, err = exec.Command("rsync",
			"--recursive",
			"--links",
			"--perms",
			"--times",
			"--devices",
			"--specials",
			"--hard-links",
			"--acls",
			"--delete",
			"--xattrs",
			"--numeric-ids",
			"/opt/"+k+"/staging/", // from
			"/opt/"+k+"/current/", // to
		).CombinedOutput()
		if err != nil {
			return fmt.Errorf("error syncing folder: %s", bs)
		}
		// Start the app
		log.Println("starting")
		exec.Command("/etc/init.d/"+k, "start").Run()

		// Enable app in load balancer
		err = EnableAppInProxy(cfg, k)
		if err != nil {
			return fmt.Errorf("error enabling app in proxy: %v", err)
		}
	}

	return fmt.Errorf("Not Implemented")
}
<commit_msg>latest<commit_after>package main

import (
	"fmt"
	"github.com/badgerodon/proxy"
	"github.com/go-contrib/uuid"
	"github.com/moraes/config"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func DisableAppInProxy(cfg *config.Config) error {
	pcfg, err := proxy.GetConfig("/opt/proxy/config.json")
	if err != nil {
		return fmt.Errorf("error reading proxy config: %v", err)
	}
	host, err := cfg.String("host")
	if err != nil {
		return fmt.Errorf("error reading host: %v", err)
	}
	delete(pcfg.Routes, host)
	err = pcfg.Save("/opt/proxy/config.json")
	if err != nil {
		return fmt.Errorf("error saving config: %v", err)
	}
	return nil
}
func EnableAppInProxy(cfg *config.Config) error {
	pcfg, err := proxy.GetConfig("/opt/proxy/config.json")
	if err != nil {
		return fmt.Errorf("error reading proxy config: %v", err)
	}
	host, err := cfg.String("host")
	if err != nil {
		return fmt.Errorf("error reading host: %v", err)
	}
	port, err := cfg.Int("port")
	if err != nil {
		return fmt.Errorf("error reading port: %v", err)
	}
	pcfg.Routes[host] = proxy.Entry{
		Endpoints: []string{fmt.Sprint("127.0.0.1:", port)},
	}
	err = pcfg.Save("/opt/proxy/config.json")
	if err != nil {
		return fmt.Errorf("error saving config: %v", err)
	}
	return nil
}

func PreReceive(dir, oldrev, newrev, ref string) error {
	// We only care about master
	if ref != "refs/heads/master" {
		return nil
	}

	temp := filepath.Join(os.TempDir(), uuid.NewV4().String())
	err := os.Mkdir(temp, 0777)
	if err != nil {
		return fmt.Errorf("- failed to create directory: %v", err)
	}
	defer os.RemoveAll(temp)

	// Export to temp
	log.Println("exporting", dir, newrev, "to", temp)
	os.Chdir(dir)
	bs, err := exec.Command("bash", "-c", "git archive --format=tar "+newrev+" | tar -C "+temp+" -x ").CombinedOutput()
	if err != nil {
		return fmt.Errorf("- failed to export: %s", bs)
	}
	log.Println("-", string(bs))

	// Get config
	log.Println("reading config")
	os.Chdir(temp)
	cfg, err := config.ParseJsonFile("config.json")
	if err != nil {
		return fmt.Errorf("- failed to read config: %v", err)
	}
	apps, err := cfg.Map("")
	if err != nil {
		return fmt.Errorf("- failed to read applications: %v", err)
	}

	// Build
	for k, v := range apps {
		app, _ := cfg.Get(k)

		//folder, err := app.String("folder")
		//if err != nil {
		//	return fmt.Errorf("- expected folder in: %v, %v", v, err)
		//}
		//typ, err := app.String("type")
		//if err != nil {
		//	return fmt.Errorf("- expected type in: %v, %v", v, err)
		//}
		build, err := app.String("build")
		if err != nil {
			return fmt.Errorf("- expected build in: %v, %v", v, err)
		}

		switch build {
		case "go":
			err = BuildGo(temp, k, app)
		default:
			err = fmt.Errorf("unknown build type %v", build)
		}

		if err != nil {
			return fmt.Errorf("error building %v: %v", k, err)
		}
	}

	// Clean
	log.Println("cleaning")
	filepath.Walk(temp, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			if strings.HasPrefix(filepath.Base(path), ".") || filepath.Base(path) == "Godeps" {
				os.RemoveAll(path)
				return filepath.SkipDir
			}
		} else {
			if strings.HasSuffix(path, ".go") {
				os.Remove(path)
			}
		}

		return nil
	})

	for k, _ := range apps {
		app, _ := cfg.Get(k)
		typ, _ := app.String("type")

		// Sync to endpoints
		log.Println("syncing", k)
		os.Mkdir("/opt/"+k, 0777)
		os.Mkdir("/opt/"+k+"/staging", 0777)
		bs, err := exec.Command("rsync",
			"--recursive",
			"--links",
			"--perms",
			"--times",
			"--devices",
			"--specials",
			"--hard-links",
			"--acls",
			"--delete",
			"--xattrs",
			"--numeric-ids",
			temp+"/",              // from
			"/opt/"+k+"/staging/", // to
		).CombinedOutput()
		if err != nil {
			return fmt.Errorf("error syncing folder: %s", bs)
		}

		// Disable app in load balancer
		if typ == "web" {
			err = DisableAppInProxy(app)
			if err != nil {
				return fmt.Errorf("error disabling app in proxy: %v", err)
			}
		}

		// Stop the app
		log.Println("stopping")
		exec.Command("/etc/init.d/"+k, "stop").Run()

		// Swap to new version
		log.Println("swapping")
		os.Mkdir("/opt/"+k+"/current", 0777)
		bs, err = exec.Command("rsync",
			"--recursive",
			"--links",
			"--perms",
			"--times",
			"--devices",
			"--specials",
			"--hard-links",
			"--acls",
			"--delete",
			"--xattrs",
			"--numeric-ids",
			"/opt/"+k+"/staging/", // from
			"/opt/"+k+"/current/", // to
		).CombinedOutput()
		if err != nil {
			return fmt.Errorf("error syncing folder: %s", bs)
		}
		// Start the app
		log.Println("starting")
		exec.Command("/etc/init.d/"+k, "start").Run()

		// Enable app in load balancer
		if typ == "web" {
			err = EnableAppInProxy(app)
			if err != nil {
				return fmt.Errorf("error enabling app in proxy: %v", err)
			}
		}
	}

	return fmt.Errorf("Not Implemented")
}
<|endoftext|>


<commit_before>package broker

import (
	"fmt"
	"sync"

	"github.com/apoydence/talaria/logging"
	"github.com/apoydence/talaria/messages"
	"github.com/gogo/protobuf/proto"
	"github.com/gorilla/websocket"
)

type Connection struct {
	log       logging.Logger
	conn      *websocket.Conn
	messageId uint64
	writeCh   chan clientMsgInfo
	errCh     chan error

	lock      sync.Mutex
	clientMap map[uint64]chan<- *messages.Server
}

type clientMsgInfo struct {
	msg    *messages.Client
	respCh chan<- *messages.Server
}

func NewConnection(URL string) (*Connection, error) {
	log := logging.Log("Connection")
	conn, _, err := websocket.DefaultDialer.Dial(URL, nil)
	if err != nil {
		return nil, err
	}

	c := &Connection{
		log:       log,
		conn:      conn,
		errCh:     make(chan error, 100),
		writeCh:   make(chan clientMsgInfo, 100),
		clientMap: make(map[uint64]chan<- *messages.Server),
	}

	go c.readCore()
	go c.writeCore()

	return c, nil
}

func (c *Connection) FetchFile(fileId uint64, name string) *FetchFileError {
	respCh := c.writeFetchFile(c.nextMsgId(), fileId, name)
	serverMsg := <-respCh

	if serverMsg.GetMessageType() == messages.Server_Error {
		return NewFetchFileError(serverMsg.Error.GetMessage(), "")
	}

	if serverMsg.GetMessageType() != messages.Server_FileLocation {
		return NewFetchFileError(fmt.Sprintf("Unexpected MessageType: %v", serverMsg.GetMessageType()), "")
	}

	if !serverMsg.FileLocation.GetLocal() {
		return NewFetchFileError("Redirect", serverMsg.FileLocation.GetUri())
	}

	return nil
}

func (c *Connection) WriteToFile(fileId uint64, data []byte) (int64, error) {
	respCh := c.writeWriteToFile(c.nextMsgId(), fileId, data)
	serverMsg := <-respCh

	if serverMsg.GetMessageType() == messages.Server_Error {
		return 0, fmt.Errorf(serverMsg.Error.GetMessage())
	}

	if serverMsg.GetMessageType() != messages.Server_FileOffset {
		return 0, fmt.Errorf("Unexpected MessageType: %v", serverMsg.GetMessageType())
	}

	return serverMsg.FileOffset.GetOffset(), nil
}

func (c *Connection) ReadFromFile(fileId uint64) ([]byte, error) {
	respCh := c.writeReadFromFile(c.nextMsgId(), fileId)
	serverMsg := <-respCh

	if serverMsg.GetMessageType() == messages.Server_Error {
		return nil, fmt.Errorf(serverMsg.Error.GetMessage())
	}

	if serverMsg.GetMessageType() != messages.Server_ReadData {
		return nil, fmt.Errorf("Unexpected MessageType: %v", serverMsg.GetMessageType())
	}

	return serverMsg.ReadData.GetData(), nil
}

func (c *Connection) Errored() error {
	return nil
}

func (c *Connection) Close() {
	c.conn.Close()
	close(c.writeCh)
}

func (c *Connection) nextMsgId() uint64 {
	c.messageId++
	return c.messageId
}

func (c *Connection) writeError(err error) {
	select {
	case c.errCh <- err:
	default:
		c.log.Panic("Errors are not being read fast enough", err)
	}
}

func (c *Connection) readMessage() (*messages.Server, error) {
	_, data, err := c.conn.ReadMessage()
	if err != nil {
		return nil, err
	}

	server := &messages.Server{}
	err = server.Unmarshal(data)
	if err != nil {
		return nil, err
	}

	return server, nil
}

func (c *Connection) writeCore() {
	for msg := range c.writeCh {
		c.lock.Lock()
		c.clientMap[msg.msg.GetMessageId()] = msg.respCh
		c.lock.Unlock()

		c.writeMessage(msg.msg)
	}
}

func (c *Connection) readCore() {
	for {
		msg, err := c.readMessage()
		if err != nil {
			c.writeError(err)
			return
		}

		c.lock.Lock()
		respCh, ok := c.clientMap[msg.GetMessageId()]
		if !ok {
			c.log.Error("Reading client message", fmt.Errorf("Unexpected message ID: %d", msg.GetMessageId()))
			c.lock.Unlock()
			continue
		}
		delete(c.clientMap, msg.GetMessageId())
		c.lock.Unlock()

		respCh <- msg
	}
}

func (c *Connection) writeMessage(msg *messages.Client) {
	data, err := msg.Marshal()
	if err != nil {
		c.log.Panic("Unable to marshal message", err)
	}

	if err = c.conn.WriteMessage(websocket.BinaryMessage, data); err != nil {
		c.writeError(err)
		return
	}
}

func (c *Connection) writeFetchFile(msgId, fileId uint64, name string) <-chan *messages.Server {
	messageType := messages.Client_FetchFile
	msg := &messages.Client{
		MessageType: messageType.Enum(),
		MessageId:   proto.Uint64(msgId),
		FetchFile: &messages.FetchFile{
			Name:   proto.String(name),
			FileId: proto.Uint64(fileId),
		},
	}

	respCh := make(chan *messages.Server, 1)
	c.writeCh <- clientMsgInfo{
		respCh: respCh,
		msg:    msg,
	}

	return respCh
}

func (c *Connection) writeWriteToFile(msgId, fileId uint64, data []byte) <-chan *messages.Server {
	messageType := messages.Client_WriteToFile
	msg := &messages.Client{
		MessageType: messageType.Enum(),
		MessageId:   proto.Uint64(msgId),
		WriteToFile: &messages.WriteToFile{
			FileId: proto.Uint64(fileId),
			Data:   data,
		},
	}

	respCh := make(chan *messages.Server, 1)
	c.writeCh <- clientMsgInfo{
		respCh: respCh,
		msg:    msg,
	}

	return respCh
}

func (c *Connection) writeReadFromFile(msgId, fileId uint64) <-chan *messages.Server {
	messageType := messages.Client_ReadFromFile
	msg := &messages.Client{
		MessageType: messageType.Enum(),
		MessageId:   proto.Uint64(msgId),
		ReadFromFile: &messages.ReadFromFile{
			FileId: proto.Uint64(fileId),
		},
	}

	respCh := make(chan *messages.Server, 1)
	c.writeCh <- clientMsgInfo{
		respCh: respCh,
		msg:    msg,
	}

	return respCh
}
<commit_msg>Fixes race condition<commit_after>package broker

import (
	"fmt"
	"sync"
	"sync/atomic"

	"github.com/apoydence/talaria/logging"
	"github.com/apoydence/talaria/messages"
	"github.com/gogo/protobuf/proto"
	"github.com/gorilla/websocket"
)

type Connection struct {
	log       logging.Logger
	conn      *websocket.Conn
	messageId uint64
	writeCh   chan clientMsgInfo
	errCh     chan error

	lock      sync.Mutex
	clientMap map[uint64]chan<- *messages.Server
}

type clientMsgInfo struct {
	msg    *messages.Client
	respCh chan<- *messages.Server
}

func NewConnection(URL string) (*Connection, error) {
	log := logging.Log("Connection")
	conn, _, err := websocket.DefaultDialer.Dial(URL, nil)
	if err != nil {
		return nil, err
	}

	c := &Connection{
		log:       log,
		conn:      conn,
		errCh:     make(chan error, 100),
		writeCh:   make(chan clientMsgInfo, 100),
		clientMap: make(map[uint64]chan<- *messages.Server),
	}

	go c.readCore()
	go c.writeCore()

	return c, nil
}

func (c *Connection) FetchFile(fileId uint64, name string) *FetchFileError {
	respCh := c.writeFetchFile(c.nextMsgId(), fileId, name)
	serverMsg := <-respCh

	if serverMsg.GetMessageType() == messages.Server_Error {
		return NewFetchFileError(serverMsg.Error.GetMessage(), "")
	}

	if serverMsg.GetMessageType() != messages.Server_FileLocation {
		return NewFetchFileError(fmt.Sprintf("Unexpected MessageType: %v", serverMsg.GetMessageType()), "")
	}

	if !serverMsg.FileLocation.GetLocal() {
		return NewFetchFileError("Redirect", serverMsg.FileLocation.GetUri())
	}

	return nil
}

func (c *Connection) WriteToFile(fileId uint64, data []byte) (int64, error) {
	respCh := c.writeWriteToFile(c.nextMsgId(), fileId, data)
	serverMsg := <-respCh

	if serverMsg.GetMessageType() == messages.Server_Error {
		return 0, fmt.Errorf(serverMsg.Error.GetMessage())
	}

	if serverMsg.GetMessageType() != messages.Server_FileOffset {
		return 0, fmt.Errorf("Unexpected MessageType: %v", serverMsg.GetMessageType())
	}

	return serverMsg.FileOffset.GetOffset(), nil
}

func (c *Connection) ReadFromFile(fileId uint64) ([]byte, error) {
	respCh := c.writeReadFromFile(c.nextMsgId(), fileId)
	serverMsg := <-respCh

	if serverMsg.GetMessageType() == messages.Server_Error {
		return nil, fmt.Errorf(serverMsg.Error.GetMessage())
	}

	if serverMsg.GetMessageType() != messages.Server_ReadData {
		return nil, fmt.Errorf("Unexpected MessageType: %v", serverMsg.GetMessageType())
	}

	return serverMsg.ReadData.GetData(), nil
}

func (c *Connection) Errored() error {
	return nil
}

func (c *Connection) Close() {
	c.conn.Close()
	close(c.writeCh)
}

func (c *Connection) nextMsgId() uint64 {
	return atomic.AddUint64(&c.messageId, 1)
}

func (c *Connection) writeError(err error) {
	select {
	case c.errCh <- err:
	default:
		c.log.Panic("Errors are not being read fast enough", err)
	}
}

func (c *Connection) readMessage() (*messages.Server, error) {
	_, data, err := c.conn.ReadMessage()
	if err != nil {
		return nil, err
	}

	server := &messages.Server{}
	err = server.Unmarshal(data)
	if err != nil {
		return nil, err
	}

	return server, nil
}

func (c *Connection) writeCore() {
	for msg := range c.writeCh {
		c.lock.Lock()
		c.clientMap[msg.msg.GetMessageId()] = msg.respCh
		c.lock.Unlock()

		c.writeMessage(msg.msg)
	}
}

func (c *Connection) readCore() {
	for {
		msg, err := c.readMessage()
		if err != nil {
			c.writeError(err)
			return
		}

		c.lock.Lock()
		respCh, ok := c.clientMap[msg.GetMessageId()]
		if !ok {
			c.log.Error("Reading client message", fmt.Errorf("Unexpected message ID: %d", msg.GetMessageId()))
			c.lock.Unlock()
			continue
		}
		delete(c.clientMap, msg.GetMessageId())
		c.lock.Unlock()

		respCh <- msg
	}
}

func (c *Connection) writeMessage(msg *messages.Client) {
	data, err := msg.Marshal()
	if err != nil {
		c.log.Panic("Unable to marshal message", err)
	}

	if err = c.conn.WriteMessage(websocket.BinaryMessage, data); err != nil {
		c.writeError(err)
		return
	}
}

func (c *Connection) writeFetchFile(msgId, fileId uint64, name string) <-chan *messages.Server {
	messageType := messages.Client_FetchFile
	msg := &messages.Client{
		MessageType: messageType.Enum(),
		MessageId:   proto.Uint64(msgId),
		FetchFile: &messages.FetchFile{
			Name:   proto.String(name),
			FileId: proto.Uint64(fileId),
		},
	}

	respCh := make(chan *messages.Server, 1)
	c.writeCh <- clientMsgInfo{
		respCh: respCh,
		msg:    msg,
	}

	return respCh
}

func (c *Connection) writeWriteToFile(msgId, fileId uint64, data []byte) <-chan *messages.Server {
	messageType := messages.Client_WriteToFile
	msg := &messages.Client{
		MessageType: messageType.Enum(),
		MessageId:   proto.Uint64(msgId),
		WriteToFile: &messages.WriteToFile{
			FileId: proto.Uint64(fileId),
			Data:   data,
		},
	}

	respCh := make(chan *messages.Server, 1)
	c.writeCh <- clientMsgInfo{
		respCh: respCh,
		msg:    msg,
	}

	return respCh
}

func (c *Connection) writeReadFromFile(msgId, fileId uint64) <-chan *messages.Server {
	messageType := messages.Client_ReadFromFile
	msg := &messages.Client{
		MessageType: messageType.Enum(),
		MessageId:   proto.Uint64(msgId),
		ReadFromFile: &messages.ReadFromFile{
			FileId: proto.Uint64(fileId),
		},
	}

	respCh := make(chan *messages.Server, 1)
	c.writeCh <- clientMsgInfo{
		respCh: respCh,
		msg:    msg,
	}

	return respCh
}
<|endoftext|>


<commit_before>package brontide

import (
	"io"
	"net"

	"github.com/roasbeef/btcd/btcec"
)

// Listener is an implementation of a net.Conn which executes an authenticated
// key exchange and message encryption protocol dubeed "Machine" after
// initial connection acceptance. See the Machine struct for additional
// details w.r.t the handshake and encryption scheme used within the
// connection.
type Listener struct {
	localStatic *btcec.PrivateKey

	tcp *net.TCPListener
}

// A compile-time assertion to ensure that Conn meets the net.Listener interface.
var _ net.Listener = (*Listener)(nil)

// NewListener returns a new net.Listener which enforces the Brontide scheme
// during both initial connection establishment and data transfer.
func NewListener(localStatic *btcec.PrivateKey, listenAddr string) (*Listener,
	error) {
	addr, err := net.ResolveTCPAddr("tcp", listenAddr)
	if err != nil {
		return nil, err
	}

	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		return nil, err
	}

	return &Listener{
		localStatic: localStatic,
		tcp:         l,
	}, nil
}

// Accept waits for and returns the next connection to the listener. All
// incoming connections are authenticated via the three act Brontide
// key-exchange scheme. This funciton will fail with a non-nil error in the
// case that either the handhska breaks down, or the remote peer doesn't know
// our static public key.
//
// Part of the net.Listener interface.
func (l *Listener) Accept() (net.Conn, error) {
	conn, err := l.tcp.Accept()
	if err != nil {
		return nil, err
	}

	brontideConn := &Conn{
		conn:  conn,
		noise: NewBrontideMachine(false, l.localStatic, nil),
	}

	// Attempt to carry out the first act of the handshake protocol. If the
	// connecting node doesn't know our long-term static public key, then
	// this portion will fail with a non-nil error.
	var actOne [ActOneSize]byte
	if _, err := io.ReadFull(conn, actOne[:]); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}
	if err := brontideConn.noise.RecvActOne(actOne); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}

	// Next, progress the handshake processes by sending over our ephemeral
	// key for the session along with an authenticating tag.
	actTwo, err := brontideConn.noise.GenActTwo()
	if err != nil {
		brontideConn.conn.Close()
		return nil, err
	}
	if _, err := conn.Write(actTwo[:]); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}

	// Finally, finish the handhskae processes by reading and decrypting
	// the conneciton peer's static public key. If this succeeeds then both
	// sides have mutually authenticated each other.
	var actThree [ActThreeSize]byte
	if _, err := io.ReadFull(conn, actThree[:]); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}
	if err := brontideConn.noise.RecvActThree(actThree); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}

	return brontideConn, nil
}

// Close closes the listener.  Any blocked Accept operations will be unblocked
// and return errors.
//
// Part of the net.Listener interface.
func (l *Listener) Close() error {
	return l.tcp.Close()
}

// Addr returns the listener's network address.
//
// Part of the net.Listener interface.
func (l *Listener) Addr() net.Addr {
	return l.tcp.Addr()
}
<commit_msg>brontide: fix minor typo<commit_after>package brontide

import (
	"io"
	"net"

	"github.com/roasbeef/btcd/btcec"
)

// Listener is an implementation of a net.Conn which executes an authenticated
// key exchange and message encryption protocol dubeed "Machine" after
// initial connection acceptance. See the Machine struct for additional
// details w.r.t the handshake and encryption scheme used within the
// connection.
type Listener struct {
	localStatic *btcec.PrivateKey

	tcp *net.TCPListener
}

// A compile-time assertion to ensure that Conn meets the net.Listener interface.
var _ net.Listener = (*Listener)(nil)

// NewListener returns a new net.Listener which enforces the Brontide scheme
// during both initial connection establishment and data transfer.
func NewListener(localStatic *btcec.PrivateKey, listenAddr string) (*Listener,
	error) {
	addr, err := net.ResolveTCPAddr("tcp", listenAddr)
	if err != nil {
		return nil, err
	}

	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		return nil, err
	}

	return &Listener{
		localStatic: localStatic,
		tcp:         l,
	}, nil
}

// Accept waits for and returns the next connection to the listener. All
// incoming connections are authenticated via the three act Brontide
// key-exchange scheme. This funciton will fail with a non-nil error in the
// case that either the handshake breaks down, or the remote peer doesn't know
// our static public key.
//
// Part of the net.Listener interface.
func (l *Listener) Accept() (net.Conn, error) {
	conn, err := l.tcp.Accept()
	if err != nil {
		return nil, err
	}

	brontideConn := &Conn{
		conn:  conn,
		noise: NewBrontideMachine(false, l.localStatic, nil),
	}

	// Attempt to carry out the first act of the handshake protocol. If the
	// connecting node doesn't know our long-term static public key, then
	// this portion will fail with a non-nil error.
	var actOne [ActOneSize]byte
	if _, err := io.ReadFull(conn, actOne[:]); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}
	if err := brontideConn.noise.RecvActOne(actOne); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}

	// Next, progress the handshake processes by sending over our ephemeral
	// key for the session along with an authenticating tag.
	actTwo, err := brontideConn.noise.GenActTwo()
	if err != nil {
		brontideConn.conn.Close()
		return nil, err
	}
	if _, err := conn.Write(actTwo[:]); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}

	// Finally, finish the handshake processes by reading and decrypting
	// the connection peer's static public key. If this succeeds then both
	// sides have mutually authenticated each other.
	var actThree [ActThreeSize]byte
	if _, err := io.ReadFull(conn, actThree[:]); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}
	if err := brontideConn.noise.RecvActThree(actThree); err != nil {
		brontideConn.conn.Close()
		return nil, err
	}

	return brontideConn, nil
}

// Close closes the listener.  Any blocked Accept operations will be unblocked
// and return errors.
//
// Part of the net.Listener interface.
func (l *Listener) Close() error {
	return l.tcp.Close()
}

// Addr returns the listener's network address.
//
// Part of the net.Listener interface.
func (l *Listener) Addr() net.Addr {
	return l.tcp.Addr()
}
<|endoftext|>