<commit_before>package main

import (
	"bufio"
	"crypto/rand"
	"crypto/subtle"
	"encoding/binary"
	"io"
	"log"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/crypto/ed25519"
)

// ClientConnection - A client connection
type ClientConnection struct {
	conf          Conf
	conn          net.Conn
	reader        *bufio.Reader
	writer        *bufio.Writer
	clientVersion byte
}

// StoredContent - Paste buffer
type StoredContent struct {
	sync.RWMutex

	encryptSkID         []byte
	ts                  []byte
	signature           []byte
	ciphertextWithNonce []byte
}

var storedContent StoredContent
var trustedClients TrustedClients
var clientsCount = uint64(0)

func (cnx *ClientConnection) getOperation(h1 []byte, isMove bool) {
	conf, reader, writer := cnx.conf, cnx.reader, cnx.writer
	rbuf := make([]byte, 32)
	if _, err := io.ReadFull(reader, rbuf); err != nil {
		log.Print(err)
		return
	}
	h2 := rbuf
	opcode := byte('G')
	if isMove {
		opcode = byte('M')
	}
	wh2 := auth2get(conf, cnx.clientVersion, h1, opcode)
	if subtle.ConstantTimeCompare(wh2, h2) != 1 {
		return
	}

	var encryptSkID, ts, signature, ciphertextWithNonce []byte
	if isMove {
		storedContent.Lock()
		encryptSkID, ts, signature, ciphertextWithNonce =
			storedContent.encryptSkID, storedContent.ts,
			storedContent.signature, storedContent.ciphertextWithNonce
		storedContent.encryptSkID, storedContent.ts, storedContent.signature,
			storedContent.ciphertextWithNonce = nil, nil, nil, nil
		storedContent.Unlock()
	} else {
		storedContent.RLock()
		encryptSkID, ts, signature, ciphertextWithNonce =
			storedContent.encryptSkID, storedContent.ts, storedContent.signature,
			storedContent.ciphertextWithNonce
		storedContent.RUnlock()
	}

	cnx.conn.SetDeadline(time.Now().Add(conf.DataTimeout))
	h3 := auth3get(conf, cnx.clientVersion, h2, encryptSkID, ts, signature)
	writer.Write(h3)
	ciphertextWithNonceLen := uint64(len(ciphertextWithNonce))
	binary.Write(writer, binary.LittleEndian, ciphertextWithNonceLen)
	writer.Write(encryptSkID)
	if cnx.clientVersion >= 5 {
		writer.Write(ts)
	}
	writer.Write(signature)
	writer.Write(ciphertextWithNonce)
	if err := writer.Flush(); err != nil {
		log.Print(err)
		return
	}
}

func (cnx *ClientConnection) storeOperation(h1 []byte) {
	conf, reader, writer := cnx.conf, cnx.reader, cnx.writer
	var rbuf []byte
	if cnx.clientVersion < 5 {
		rbuf = make([]byte, 112)
	} else {
		rbuf = make([]byte, 120)
	}
	if _, err := io.ReadFull(reader, rbuf); err != nil {
		log.Print(err)
		return
	}
	h2 := rbuf[0:32]
	ciphertextWithNonceLen := binary.LittleEndian.Uint64(rbuf[32:40])
	if conf.MaxLen > 0 && ciphertextWithNonceLen > conf.MaxLen {
		log.Printf("%v bytes requested to be stored, but limit set to %v bytes (%v Mb)\n",
			ciphertextWithNonceLen, conf.MaxLen, conf.MaxLen/(1024*1024))
		return
	}
	encryptSkID := rbuf[40:48]
	var ts, signature []byte
	if cnx.clientVersion < 5 {
		ts = make([]byte, 8)
		binary.LittleEndian.PutUint64(ts, uint64(time.Now().Unix()))
		signature = rbuf[48:112]
	} else {
		ts = rbuf[48:56]
		signature = rbuf[56:120]
	}
	opcode := byte('S')

	wh2 := auth2store(conf, cnx.clientVersion, h1, opcode, encryptSkID, ts, signature)
	if subtle.ConstantTimeCompare(wh2, h2) != 1 {
		return
	}
	ciphertextWithNonce := make([]byte, ciphertextWithNonceLen)

	cnx.conn.SetDeadline(time.Now().Add(conf.DataTimeout))
	if _, err := io.ReadFull(reader, ciphertextWithNonce); err != nil {
		log.Print(err)
		return
	}
	if ed25519.Verify(conf.SignPk, ciphertextWithNonce, signature) != true {
		return
	}
	h3 := auth3store(conf, cnx.clientVersion, h2)

	storedContent.Lock()
	storedContent.encryptSkID = encryptSkID
	storedContent.ts = ts
	storedContent.signature = signature
	storedContent.ciphertextWithNonce = ciphertextWithNonce
	storedContent.Unlock()

	writer.Write(h3)
	if err := writer.Flush(); err != nil {
		log.Print(err)
		return
	}
}

func handleClientConnection(conf Conf, conn net.Conn) {
	defer conn.Close()
	reader, writer := bufio.NewReader(conn), bufio.NewWriter(conn)
	cnx := ClientConnection{
		conf:   conf,
		conn:   conn,
		reader: reader,
		writer: writer,
	}
	rbuf := make([]byte, 65)
	if _, err := io.ReadFull(reader, rbuf); err != nil {
		log.Print(err)
		return
	}
	cnx.clientVersion = rbuf[0]
	if cnx.clientVersion < 4 || cnx.clientVersion > 5 {
		log.Print("Unsupported client version - Please run the same version on the server and on the client")
		return
	}
	r := rbuf[1:33]
	h0 := rbuf[33:65]
	wh0 := auth0(conf, cnx.clientVersion, r)
	if subtle.ConstantTimeCompare(wh0, h0) != 1 {
		return
	}
	r2 := make([]byte, 32)
	if _, err := rand.Read(r); err != nil {
		log.Fatal(err)
	}
	h1 := auth1(conf, cnx.clientVersion, h0, r2)
	writer.Write([]byte{cnx.clientVersion})
	writer.Write(r2)
	writer.Write(h1)
	if err := writer.Flush(); err != nil {
		log.Print(err)
		return
	}
	remoteIP := cnx.conn.RemoteAddr().(*net.TCPAddr).IP
	addToTrustedIPs(conf, remoteIP)
	opcode, err := reader.ReadByte()
	if err != nil {
		return
	}
	switch opcode {
	case byte('G'):
		cnx.getOperation(h1, false)
	case byte('M'):
		cnx.getOperation(h1, true)
	case byte('S'):
		cnx.storeOperation(h1)
	}
}

type TrustedClients struct {
	sync.RWMutex

	ips []net.IP
}

func addToTrustedIPs(conf Conf, ip net.IP) {
	trustedClients.Lock()
	if uint64(len(trustedClients.ips)) >= conf.TrustedIPCount {
		trustedClients.ips = append(trustedClients.ips[1:], ip)
	} else {
		trustedClients.ips = append(trustedClients.ips, ip)
	}
	trustedClients.Unlock()
}

func isIPTrusted(conf Conf, ip net.IP) bool {
	trustedClients.RLock()
	defer trustedClients.RUnlock()
	if len(trustedClients.ips) == 0 {
		return true
	}
	for _, foundIP := range trustedClients.ips {
		if foundIP.Equal(ip) {
			return true
		}
	}
	return false
}

func acceptClient(conf Conf, conn net.Conn) {
	conn.SetDeadline(time.Now().Add(conf.Timeout))
	remoteIP := conn.RemoteAddr().(*net.TCPAddr).IP
	for {
		count := atomic.LoadUint64(&clientsCount)
		if count >= conf.MaxClients-conf.TrustedIPCount && isIPTrusted(conf, remoteIP) == false {
			conn.Close()
			return
		}
		if count >= conf.MaxClients {
			conn.Close()
			return
		} else if atomic.CompareAndSwapUint64(&clientsCount, count, count+1) {
			break
		}
	}
	handleClientConnection(conf, conn)
	atomic.AddUint64(&clientsCount, ^uint64(0))
}

// RunServer - run a server
func RunServer(conf Conf) {
	listen, err := net.Listen("tcp", conf.Listen)
	if err != nil {
		log.Fatal(err)
	}
	defer listen.Close()
	for {
		conn, err := listen.Accept()
		if err != nil {
			log.Fatal(err)
		}
		go acceptClient(conf, conn)
	}
}
<commit_msg>use workers instead of go connection. add a simple signal handler.<commit_after>package main

import (
	"bufio"
	"crypto/rand"
	"crypto/subtle"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"sync/atomic"
	"syscall"
	"time"

	"golang.org/x/crypto/ed25519"
)

// ClientConnection - A client connection
type ClientConnection struct {
	conf          Conf
	conn          net.Conn
	reader        *bufio.Reader
	writer        *bufio.Writer
	clientVersion byte
}

// StoredContent - Paste buffer
type StoredContent struct {
	sync.RWMutex

	encryptSkID         []byte
	ts                  []byte
	signature           []byte
	ciphertextWithNonce []byte
}

var storedContent StoredContent
var trustedClients TrustedClients
var clientsCount = uint64(0)

func (cnx *ClientConnection) getOperation(h1 []byte, isMove bool) {
	conf, reader, writer := cnx.conf, cnx.reader, cnx.writer
	rbuf := make([]byte, 32)
	if _, err := io.ReadFull(reader, rbuf); err != nil {
		log.Print(err)
		return
	}
	h2 := rbuf
	opcode := byte('G')
	if isMove {
		opcode = byte('M')
	}
	wh2 := auth2get(conf, cnx.clientVersion, h1, opcode)
	if subtle.ConstantTimeCompare(wh2, h2) != 1 {
		return
	}

	var encryptSkID, ts, signature, ciphertextWithNonce []byte
	if isMove {
		storedContent.Lock()
		encryptSkID, ts, signature, ciphertextWithNonce =
			storedContent.encryptSkID, storedContent.ts,
			storedContent.signature, storedContent.ciphertextWithNonce
		storedContent.encryptSkID, storedContent.ts, storedContent.signature,
			storedContent.ciphertextWithNonce = nil, nil, nil, nil
		storedContent.Unlock()
	} else {
		storedContent.RLock()
		encryptSkID, ts, signature, ciphertextWithNonce =
			storedContent.encryptSkID, storedContent.ts, storedContent.signature,
			storedContent.ciphertextWithNonce
		storedContent.RUnlock()
	}

	cnx.conn.SetDeadline(time.Now().Add(conf.DataTimeout))
	h3 := auth3get(conf, cnx.clientVersion, h2, encryptSkID, ts, signature)
	writer.Write(h3)
	ciphertextWithNonceLen := uint64(len(ciphertextWithNonce))
	binary.Write(writer, binary.LittleEndian, ciphertextWithNonceLen)
	writer.Write(encryptSkID)
	if cnx.clientVersion >= 5 {
		writer.Write(ts)
	}
	writer.Write(signature)
	writer.Write(ciphertextWithNonce)
	if err := writer.Flush(); err != nil {
		log.Print(err)
		return
	}
}

func (cnx *ClientConnection) storeOperation(h1 []byte) {
	conf, reader, writer := cnx.conf, cnx.reader, cnx.writer
	var rbuf []byte
	if cnx.clientVersion < 5 {
		rbuf = make([]byte, 112)
	} else {
		rbuf = make([]byte, 120)
	}
	if _, err := io.ReadFull(reader, rbuf); err != nil {
		log.Print(err)
		return
	}
	h2 := rbuf[0:32]
	ciphertextWithNonceLen := binary.LittleEndian.Uint64(rbuf[32:40])
	if conf.MaxLen > 0 && ciphertextWithNonceLen > conf.MaxLen {
		log.Printf("%v bytes requested to be stored, but limit set to %v bytes (%v Mb)\n",
			ciphertextWithNonceLen, conf.MaxLen, conf.MaxLen/(1024*1024))
		return
	}
	encryptSkID := rbuf[40:48]
	var ts, signature []byte
	if cnx.clientVersion < 5 {
		ts = make([]byte, 8)
		binary.LittleEndian.PutUint64(ts, uint64(time.Now().Unix()))
		signature = rbuf[48:112]
	} else {
		ts = rbuf[48:56]
		signature = rbuf[56:120]
	}
	opcode := byte('S')

	wh2 := auth2store(conf, cnx.clientVersion, h1, opcode, encryptSkID, ts, signature)
	if subtle.ConstantTimeCompare(wh2, h2) != 1 {
		return
	}
	ciphertextWithNonce := make([]byte, ciphertextWithNonceLen)

	cnx.conn.SetDeadline(time.Now().Add(conf.DataTimeout))
	if _, err := io.ReadFull(reader, ciphertextWithNonce); err != nil {
		log.Print(err)
		return
	}
	if ed25519.Verify(conf.SignPk, ciphertextWithNonce, signature) != true {
		return
	}
	h3 := auth3store(conf, cnx.clientVersion, h2)

	storedContent.Lock()
	storedContent.encryptSkID = encryptSkID
	storedContent.ts = ts
	storedContent.signature = signature
	storedContent.ciphertextWithNonce = ciphertextWithNonce
	storedContent.Unlock()

	writer.Write(h3)
	if err := writer.Flush(); err != nil {
		log.Print(err)
		return
	}
}

func handleClientConnection(conf Conf, conn net.Conn) {
	defer conn.Close()
	reader, writer := bufio.NewReader(conn), bufio.NewWriter(conn)
	cnx := ClientConnection{
		conf:   conf,
		conn:   conn,
		reader: reader,
		writer: writer,
	}
	rbuf := make([]byte, 65)
	if _, err := io.ReadFull(reader, rbuf); err != nil {
		log.Print(err)
		return
	}
	cnx.clientVersion = rbuf[0]
	if cnx.clientVersion < 4 || cnx.clientVersion > 5 {
		log.Print("Unsupported client version - Please run the same version on the server and on the client")
		return
	}
	r := rbuf[1:33]
	h0 := rbuf[33:65]
	wh0 := auth0(conf, cnx.clientVersion, r)
	if subtle.ConstantTimeCompare(wh0, h0) != 1 {
		return
	}
	r2 := make([]byte, 32)
	if _, err := rand.Read(r); err != nil {
		log.Fatal(err)
	}
	h1 := auth1(conf, cnx.clientVersion, h0, r2)
	writer.Write([]byte{cnx.clientVersion})
	writer.Write(r2)
	writer.Write(h1)
	if err := writer.Flush(); err != nil {
		log.Print(err)
		return
	}
	remoteIP := cnx.conn.RemoteAddr().(*net.TCPAddr).IP
	addToTrustedIPs(conf, remoteIP)
	opcode, err := reader.ReadByte()
	if err != nil {
		return
	}
	switch opcode {
	case byte('G'):
		cnx.getOperation(h1, false)
	case byte('M'):
		cnx.getOperation(h1, true)
	case byte('S'):
		cnx.storeOperation(h1)
	}
}

type TrustedClients struct {
	sync.RWMutex

	ips []net.IP
}

func addToTrustedIPs(conf Conf, ip net.IP) {
	trustedClients.Lock()
	if uint64(len(trustedClients.ips)) >= conf.TrustedIPCount {
		trustedClients.ips = append(trustedClients.ips[1:], ip)
	} else {
		trustedClients.ips = append(trustedClients.ips, ip)
	}
	trustedClients.Unlock()
}

func isIPTrusted(conf Conf, ip net.IP) bool {
	trustedClients.RLock()
	defer trustedClients.RUnlock()
	if len(trustedClients.ips) == 0 {
		return true
	}
	for _, foundIP := range trustedClients.ips {
		if foundIP.Equal(ip) {
			return true
		}
	}
	return false
}

func acceptClient(conf Conf, conn net.Conn) {
	conn.SetDeadline(time.Now().Add(conf.Timeout))
	remoteIP := conn.RemoteAddr().(*net.TCPAddr).IP
	for {
		count := atomic.LoadUint64(&clientsCount)
		if count >= conf.MaxClients-conf.TrustedIPCount && isIPTrusted(conf, remoteIP) == false {
			conn.Close()
			return
		}
		if count >= conf.MaxClients {
			conn.Close()
			return
		} else if atomic.CompareAndSwapUint64(&clientsCount, count, count+1) {
			break
		}
	}
	handleClientConnection(conf, conn)
	atomic.AddUint64(&clientsCount, ^uint64(0))
}

// RunServer - run a server
func RunServer(conf Conf) {
	go handleSignals()

	listen, err := net.Listen("tcp", conf.Listen)
	if err != nil {
		log.Fatal(err)
	}
	defer listen.Close()

	// start 2 workers per cpu
	conns := make(chan net.Conn, runtime.NumCPU() * 4)
	defer close(conns)
	for i := 0; i <= runtime.NumCPU() * 2; i++ {
		go func() {
			for conn := range conns {
				acceptClient(conf, conn)
			}
		}()
	}

	for {
		conn, err := listen.Accept()
		if err != nil {
			log.Fatal(err)
		}
		conns <-conn
	}

}

func handleSignals() {
        signals := make(chan os.Signal, 1)
        signal.Notify(signals, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGUSR1)

	for {
		select {
		case signal, ok := <-signals:
			if !ok { break }

			switch(signal) {
			case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM:
				os.Exit(2)
			case syscall.SIGUSR1:
				if len(storedContent.ciphertextWithNonce) > 0 {
					fmt.Printf("%v: some data is stored\n", os.Args[0])
				} else {
					fmt.Printf("%v: no data stored yet\n", os.Args[0])
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/byuoitav/av-api/controllers"
	"github.com/byuoitav/av-api/packages/hateoas"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/fasthttp"
)

func main() {
	err := hateoas.Load("https://raw.githubusercontent.com/byuoitav/av-api/master/swagger.yaml")
	if err != nil {
		fmt.Printf("Could not locate swagger.yaml file. Error: %s", err.Error())
		panic(err)
	}

	port := ":8000"
	e := echo.New()

	// Echo doesn't like doing things "magically" which means it won't auto-redirect endpoints without a trailing slash to one with a trailing slash (and vice versa) which is why endpoints are duplicated

	// GET requests
	e.Get("/", controllers.Root)

	e.Get("/health", controllers.Health)
	e.Get("/health/", controllers.Health)

	e.Get("/rooms", controllers.GetAllRooms)
	e.Get("/rooms/", controllers.GetAllRooms)
	e.Get("/rooms/:room", controllers.GetRoomByName)
	e.Get("/rooms/:room/", controllers.GetRoomByName)

	e.Get("/buildings", controllers.GetAllBuildings)
	e.Get("/buildings/", controllers.GetAllBuildings)
	e.Get("/buildings/:building", controllers.GetBuildingByName)
	e.Get("/buildings/:building/", controllers.GetBuildingByName)
	e.Get("/buildings/:building/rooms", controllers.GetRoomsByBuilding)
	e.Get("/buildings/:building/rooms/", controllers.GetRoomsByBuilding)
	e.Get("/buildings/:building/rooms/:room", controllers.GetRoomByNameAndBuilding)
	e.Get("/buildings/:building/rooms/:room/", controllers.GetRoomByNameAndBuilding)
	e.Get("/buildings/:building/rooms/:room/signals", controllers.UnimplementedResponse)
	e.Get("/buildings/:building/rooms/:room/signals/", controllers.UnimplementedResponse)
	e.Get("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)
	e.Get("/buildings/:building/rooms/:room/signals/:signal/", controllers.UnimplementedResponse)

	// POST requests
	e.Post("/rooms", controllers.UnimplementedResponse)
	e.Post("/rooms/", controllers.UnimplementedResponse)
	e.Post("/buildings", controllers.UnimplementedResponse)
	e.Post("/buildings/", controllers.UnimplementedResponse)
	e.Post("/buildings/:building/rooms/:room/signals", controllers.UnimplementedResponse)
	e.Post("/buildings/:building/rooms/:room/signals/", controllers.UnimplementedResponse)

	// PUT requests
	e.Put("/rooms/:room", controllers.UnimplementedResponse)
	e.Put("/rooms/:room/", controllers.UnimplementedResponse)
	e.Put("/buildings/:building", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room/", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room/signals/:signal/", controllers.UnimplementedResponse)

	// DELETE requests
	e.Delete("/rooms/:room", controllers.UnimplementedResponse)
	e.Delete("/rooms/:room/", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room/", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room/signals/:signal/", controllers.UnimplementedResponse)

	fmt.Printf("AV API is listening on %s\n", port)
	e.Run(fasthttp.New(port))
}
<commit_msg>Fixing merge conflicts<commit_after>package main

import (
	"fmt"

	"github.com/byuoitav/av-api/controllers"
	"github.com/byuoitav/av-api/packages/hateoas"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/fasthttp"
)

func main() {
	err := hateoas.Load("https://raw.githubusercontent.com/byuoitav/av-api/master/swagger.yaml")
	if err != nil {
		fmt.Printf("Could not locate swagger.yaml file. Error: %s", err.Error())
		panic(err)
	}

	port := ":8000"
	e := echo.New()

	// Echo doesn't like doing things "magically" which means it won't auto-redirect endpoints without a trailing slash to one with a trailing slash (and vice versa) which is why endpoints are duplicated

	// GET requests
	e.Get("/", controllers.Root)

	e.Get("/health", controllers.Health)
	e.Get("/health/", controllers.Health)

	e.Get("/rooms", controllers.GetAllRooms)
	e.Get("/rooms/", controllers.GetAllRooms)
	e.Get("/rooms/:room", controllers.GetRoomByName)
	e.Get("/rooms/:room/", controllers.GetRoomByName)

	e.Get("/buildings", controllers.GetAllBuildings)
	e.Get("/buildings/", controllers.GetAllBuildings)
<<<<<<< HEAD
	e.Get("/buildings/:building", controllers.GetBuildingByName)
	e.Get("/buildings/:building/", controllers.GetBuildingByName)
=======
	e.Get("/buildings/:building", controllers.GetRoomsByBuilding)
	e.Get("/buildings/:building/", controllers.GetRoomsByBuilding)
>>>>>>> f8ae4b1442df9786b8fe19dce6e2c62d8946070a
	e.Get("/buildings/:building/rooms", controllers.GetRoomsByBuilding)
	e.Get("/buildings/:building/rooms/", controllers.GetRoomsByBuilding)
	e.Get("/buildings/:building/rooms/:room", controllers.GetRoomByNameAndBuilding)
	e.Get("/buildings/:building/rooms/:room/", controllers.GetRoomByNameAndBuilding)
	e.Get("/buildings/:building/rooms/:room/signals", controllers.UnimplementedResponse)
	e.Get("/buildings/:building/rooms/:room/signals/", controllers.UnimplementedResponse)
	e.Get("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)
	e.Get("/buildings/:building/rooms/:room/signals/:signal/", controllers.UnimplementedResponse)

	// POST requests
	e.Post("/rooms", controllers.UnimplementedResponse)
	e.Post("/rooms/", controllers.UnimplementedResponse)
	e.Post("/buildings", controllers.UnimplementedResponse)
	e.Post("/buildings/", controllers.UnimplementedResponse)
	e.Post("/buildings/:building/rooms/:room/signals", controllers.UnimplementedResponse)
	e.Post("/buildings/:building/rooms/:room/signals/", controllers.UnimplementedResponse)

	// PUT requests
	e.Put("/rooms/:room", controllers.UnimplementedResponse)
	e.Put("/rooms/:room/", controllers.UnimplementedResponse)
	e.Put("/buildings/:building", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room/", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room/signals/:signal/", controllers.UnimplementedResponse)

	// DELETE requests
	e.Delete("/rooms/:room", controllers.UnimplementedResponse)
	e.Delete("/rooms/:room/", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room/", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room/signals/:signal/", controllers.UnimplementedResponse)

	fmt.Printf("AV API is listening on %s\n", port)
	e.Run(fasthttp.New(port))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
)

const re = `(?i)uploads/(?P<env>\w+)/(?P<username>\w+)?/?picture/attachment/(?P<id>\d+)/(?P<name>\w+)$`
const watermarkPathFmt = "%s/uploads/%s/photographer_info/picture/%d/%s"
const picturePathFmt = "%s/uploads/%s/picture/attachment/%d/%s"

var pathMatcher *regexp.Regexp
var imagizerHost *url.URL

type imagizerHandler struct {
	config *Config
	db     *DB
}

func init() {
	pathMatcher = regexp.MustCompile(re)
}

// Start initializes and then starts the HTTP server
func Start(c *Config) {
	db, err := NewDB(c)
	handleErr(err)

	imagizerHost, err = url.Parse(c.ImagizerHost)
	handleErr(err)

	handler := imagizerHandler{
		config: c,
		db:     db,
	}

	s := &http.Server{
		Addr:    c.BindAddr(),
		Handler: handler,
	}

	log.Printf("Listening on %s", s.Addr)
	err = s.ListenAndServe()
	handleErr(err)
}

func (h imagizerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if !pathMatcher.MatchString(req.URL.Path) {
		log.Printf("Malformed path %s", req.URL.Path)
		http.NotFound(w, req)
		return
	}

	parts := extractPathPartsToMap(req.URL.Path)

	version, ok := h.config.versionsByName[parts["name"]]
	if !ok {
		log.Printf("Version not found with name %s", parts["name"])
		http.NotFound(w, req)
		return
	}

	pictureID, err := strconv.Atoi(parts["id"])
	handleErr(err)

	pic := h.db.loadPicture(pictureID)
	if pic.eventID == 0 {
		log.Printf("Picture not found for ID %d", pictureID)
		http.NotFound(w, req)
		return
	}

	log.Printf("Attempting proxy for %v", version)
	proxy := h.imagizerURL(version, parts)
	resp, err := http.Get(proxy.String())
	defer closeQuietly(resp.Body)
	handleErr(err)

	_, err = io.Copy(w, resp.Body)
	handleErr(err)
}

func (h imagizerHandler) imagizerURL(version map[string]interface{}, parts map[string]string) url.URL {
	vals := url.Values{}
	pictureID, err := strconv.Atoi(parts["id"])
	handleErr(err)

	for key, val := range version {
		if key == "watermark" && val == true && h.isPhotographerImage(pictureID) {
			vals.Add("mark", h.getWatermarkURL(pictureID, parts["env"]))
			vals.Add("mark_scale", "15")
			vals.Add("mark_pos", "bottom,right")
			vals.Add("mark_offset", "3")
			vals.Add("mark_alpha", "70")
			continue
		}

		if key == "function_name" || key == "name" || key == "only_shrink_larger" {
			continue
		}

		switch val := val.(type) {
		default:
			log.Fatalf("Unexpected type %T for %v", val, val)
		case string:
			vals.Add(key, val)
		case int:
			vals.Add(key, strconv.Itoa(val))
		case float64:
			vals.Add(key, strconv.Itoa(int(val)))
		case bool:
			vals.Add(key, fmt.Sprintf("%t", val))
		}
	}

	retURL := url.URL{}
	retURL.Scheme = imagizerHost.Scheme
	retURL.Host = imagizerHost.Host
	retURL.Path = h.pathForImage(parts)
	retURL.RawQuery = vals.Encode()

	log.Printf("Proxying to %v", retURL)

	return retURL
}

func (h imagizerHandler) isPhotographerImage(id int) bool {
	pic := h.db.loadPicture(id)
	ev := h.db.loadEvent(pic.eventID)

	return pic.userID == ev.ownerID
}

func (h imagizerHandler) getWatermarkURL(id int, env string) string {
	pic := h.db.loadPicture(id)
	pi := h.db.loadPhotographerInfo(pic.userID)

	if pi.picture.Valid {
		return fmt.Sprintf(watermarkPathFmt, h.config.CDNHost, env, id, pi.picture)
	}

	return "https://snapshots.com/images/icon.png"
}

func (h imagizerHandler) pathForImage(parts map[string]string) string {
	pictureID, err := strconv.Atoi(parts["id"])
	handleErr(err)

	pic := h.db.loadPicture(pictureID)
	env, _ := parts["env"]

	var envAndUsername string

	if env == "development" {
		envAndUsername = fmt.Sprintf("%s/%s", env, parts["username"])
	} else {
		envAndUsername = env
	}

	return fmt.Sprintf(picturePathFmt,
		BucketNames[env], envAndUsername, pictureID, pic.attachment)
}

func extractPathPartsToMap(path string) map[string]string {
	names := pathMatcher.SubexpNames()[1:]
	matches := pathMatcher.FindStringSubmatch(path)

	md := map[string]string{}

	for i, s := range matches[1:] {
		md[names[i]] = s
	}

	return md
}
<commit_msg>Quiet the log a bit<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
)

const re = `(?i)uploads/(?P<env>\w+)/(?P<username>\w+)?/?picture/attachment/(?P<id>\d+)/(?P<name>\w+)$`
const watermarkPathFmt = "%s/uploads/%s/photographer_info/picture/%d/%s"
const picturePathFmt = "%s/uploads/%s/picture/attachment/%d/%s"

var pathMatcher *regexp.Regexp
var imagizerHost *url.URL

type imagizerHandler struct {
	config *Config
	db     *DB
}

func init() {
	pathMatcher = regexp.MustCompile(re)
}

// Start initializes and then starts the HTTP server
func Start(c *Config) {
	db, err := NewDB(c)
	handleErr(err)

	imagizerHost, err = url.Parse(c.ImagizerHost)
	handleErr(err)

	handler := imagizerHandler{
		config: c,
		db:     db,
	}

	s := &http.Server{
		Addr:    c.BindAddr(),
		Handler: handler,
	}

	log.Printf("Listening on %s", s.Addr)
	err = s.ListenAndServe()
	handleErr(err)
}

func (h imagizerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if !pathMatcher.MatchString(req.URL.Path) {
		log.Printf("Malformed path %s", req.URL.Path)
		http.NotFound(w, req)
		return
	}

	parts := extractPathPartsToMap(req.URL.Path)

	version, ok := h.config.versionsByName[parts["name"]]
	if !ok {
		log.Printf("Version not found with name %s", parts["name"])
		http.NotFound(w, req)
		return
	}

	pictureID, err := strconv.Atoi(parts["id"])
	handleErr(err)

	pic := h.db.loadPicture(pictureID)
	if pic.eventID == 0 {
		log.Printf("Picture not found for ID %d", pictureID)
		http.NotFound(w, req)
		return
	}

	proxy := h.imagizerURL(version, parts)
	resp, err := http.Get(proxy.String())
	defer closeQuietly(resp.Body)
	handleErr(err)

	_, err = io.Copy(w, resp.Body)
	handleErr(err)
}

func (h imagizerHandler) imagizerURL(version map[string]interface{}, parts map[string]string) url.URL {
	vals := url.Values{}
	pictureID, err := strconv.Atoi(parts["id"])
	handleErr(err)

	for key, val := range version {
		if key == "watermark" && val == true && h.isPhotographerImage(pictureID) {
			vals.Add("mark", h.getWatermarkURL(pictureID, parts["env"]))
			vals.Add("mark_scale", "15")
			vals.Add("mark_pos", "bottom,right")
			vals.Add("mark_offset", "3")
			vals.Add("mark_alpha", "70")
			continue
		}

		if key == "function_name" || key == "name" || key == "only_shrink_larger" {
			continue
		}

		switch val := val.(type) {
		default:
			log.Fatalf("Unexpected type %T for %v", val, val)
		case string:
			vals.Add(key, val)
		case int:
			vals.Add(key, strconv.Itoa(val))
		case float64:
			vals.Add(key, strconv.Itoa(int(val)))
		case bool:
			vals.Add(key, fmt.Sprintf("%t", val))
		}
	}

	retURL := url.URL{}
	retURL.Scheme = imagizerHost.Scheme
	retURL.Host = imagizerHost.Host
	retURL.Path = h.pathForImage(parts)
	retURL.RawQuery = vals.Encode()

	return retURL
}

func (h imagizerHandler) isPhotographerImage(id int) bool {
	pic := h.db.loadPicture(id)
	ev := h.db.loadEvent(pic.eventID)

	return pic.userID == ev.ownerID
}

func (h imagizerHandler) getWatermarkURL(id int, env string) string {
	pic := h.db.loadPicture(id)
	pi := h.db.loadPhotographerInfo(pic.userID)

	if pi.picture.Valid {
		return fmt.Sprintf(watermarkPathFmt, h.config.CDNHost, env, id, pi.picture)
	}

	return "https://snapshots.com/images/icon.png"
}

func (h imagizerHandler) pathForImage(parts map[string]string) string {
	pictureID, err := strconv.Atoi(parts["id"])
	handleErr(err)

	pic := h.db.loadPicture(pictureID)
	env, _ := parts["env"]

	var envAndUsername string

	if env == "development" {
		envAndUsername = fmt.Sprintf("%s/%s", env, parts["username"])
	} else {
		envAndUsername = env
	}

	return fmt.Sprintf(picturePathFmt,
		BucketNames[env], envAndUsername, pictureID, pic.attachment)
}

func extractPathPartsToMap(path string) map[string]string {
	names := pathMatcher.SubexpNames()[1:]
	matches := pathMatcher.FindStringSubmatch(path)

	md := map[string]string{}

	for i, s := range matches[1:] {
		md[names[i]] = s
	}

	return md
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"

	"github.com/byuoitav/authmiddleware"
	"github.com/byuoitav/av-api/base"
	"github.com/byuoitav/av-api/handlers"
	"github.com/byuoitav/av-api/health"
	avapi "github.com/byuoitav/av-api/init"
	"github.com/byuoitav/device-monitoring-microservice/microservicestatus"
	"github.com/byuoitav/event-router-microservice/eventinfrastructure"
	"github.com/byuoitav/hateoas"
	jh "github.com/jessemillar/health"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func main() {
	base.Pub = eventinfrastructure.NewPublisher("7001")

	var req eventinfrastructure.ConnectionRequest
	req.PublisherAddr = "localhost:7001"
	go eventinfrastructure.SendConnectionRequest("http://localhost:6999/subscribe", req, true)

	err := avapi.CheckRoomInitialization()
	if err != nil {
		base.PublishError("Fail to run init script. Terminating. ERROR:"+err.Error(), eventinfrastructure.INTERNAL)

		log.Fatalf("Could not initialize room. Error: %v\n", err.Error())
	}

	port := ":8000"
	router := echo.New()
	router.Pre(middleware.RemoveTrailingSlash())
	router.Use(middleware.CORS())

	// Use the `secure` routing group to require authentication
	secure := router.Group("", echo.WrapMiddleware(authmiddleware.Authenticate))

	// GET requests
	router.GET("/", echo.WrapHandler(http.HandlerFunc(hateoas.RootResponse)))

	router.GET("/health", echo.WrapHandler(http.HandlerFunc(jh.Check)))
	router.GET("/microserivcestatus", GetStatus)
	secure.GET("/status", health.Status)

	// PUT requests
	secure.PUT("/buildings/:building/rooms/:room", handlers.SetRoomState)

	// room status
	secure.GET("/buildings/:building/rooms/:room", handlers.GetRoomStatus)
	secure.GET("/buildings/:building/rooms/:room/configuration", handlers.GetRoomByNameAndBuilding)

	server := http.Server{
		Addr:           port,
		MaxHeaderBytes: 1024 * 10,
	}

	go health.StartupCheckAndReport()

	router.StartServer(&server)
}

func GetStatus(context echo.Context) error {
	var s microservicestatus.Status
	var err error

	s.Version, err = microservicestatus.GetVersion("version.txt")
	if err != nil {
		return context.JSON(http.StatusOK, "Failed to open version.txt")
	}

	s.Status = microservicestatus.StatusOK
	s.StatusInfo = ""

	return context.JSON(http.StatusOK, s)
}
<commit_msg>change status endpoint<commit_after>package main

import (
	"log"
	"net/http"

	"github.com/byuoitav/authmiddleware"
	"github.com/byuoitav/av-api/base"
	"github.com/byuoitav/av-api/handlers"
	"github.com/byuoitav/av-api/health"
	avapi "github.com/byuoitav/av-api/init"
	"github.com/byuoitav/device-monitoring-microservice/microservicestatus"
	"github.com/byuoitav/event-router-microservice/eventinfrastructure"
	"github.com/byuoitav/hateoas"
	jh "github.com/jessemillar/health"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func main() {
	base.Pub = eventinfrastructure.NewPublisher("7001")

	var req eventinfrastructure.ConnectionRequest
	req.PublisherAddr = "localhost:7001"
	go eventinfrastructure.SendConnectionRequest("http://localhost:6999/subscribe", req, true)

	err := avapi.CheckRoomInitialization()
	if err != nil {
		base.PublishError("Fail to run init script. Terminating. ERROR:"+err.Error(), eventinfrastructure.INTERNAL)

		log.Fatalf("Could not initialize room. Error: %v\n", err.Error())
	}

	port := ":8000"
	router := echo.New()
	router.Pre(middleware.RemoveTrailingSlash())
	router.Use(middleware.CORS())

	// Use the `secure` routing group to require authentication
	secure := router.Group("", echo.WrapMiddleware(authmiddleware.Authenticate))

	// GET requests
	router.GET("/", echo.WrapHandler(http.HandlerFunc(hateoas.RootResponse)))

	router.GET("/health", echo.WrapHandler(http.HandlerFunc(jh.Check)))
	router.GET("/mstatus", GetStatus)
	secure.GET("/status", health.Status)

	// PUT requests
	secure.PUT("/buildings/:building/rooms/:room", handlers.SetRoomState)

	// room status
	secure.GET("/buildings/:building/rooms/:room", handlers.GetRoomStatus)
	secure.GET("/buildings/:building/rooms/:room/configuration", handlers.GetRoomByNameAndBuilding)

	server := http.Server{
		Addr:           port,
		MaxHeaderBytes: 1024 * 10,
	}

	go health.StartupCheckAndReport()

	router.StartServer(&server)
}

func GetStatus(context echo.Context) error {
	var s microservicestatus.Status
	var err error

	s.Version, err = microservicestatus.GetVersion("version.txt")
	if err != nil {
		return context.JSON(http.StatusOK, "Failed to open version.txt")
	}

	s.Status = microservicestatus.StatusOK
	s.StatusInfo = ""

	return context.JSON(http.StatusOK, s)
}
<|endoftext|>


<commit_before>package rest

import (
	"log"
	"fmt"
	"http"
	"strings"
)

var resources = make(map[string]interface{})

// Generic resource handler
func resourceHandler(c http.ResponseWriter, req *http.Request) {
	// Parse request URI to resource URI and (potential) ID
	var resourceEnd = strings.Index(req.URL.Path[1:], "/") + 1
	var resourceName string
	if resourceEnd == -1 {
		resourceName = req.URL.Path[1:]
	} else {
		resourceName = req.URL.Path[1:resourceEnd]
	}
	var id = req.URL.Path[resourceEnd+1:]

	resource, ok := resources[resourceName]
	if !ok {
		fmt.Fprintf(c, "resource %s not found\n", resourceName)
	}

	var hasAccess bool = false
	if accesschecker, ok := resource.(accessChecker); ok {
		hasAccess, _ = accesschecker.HasAccess(req)
	} else {
		log.Println("Resource ", resourceName, " has no accessChecker. Giving access …")
		// no checker for resource, so always give access
		hasAccess = true
	}
	if hasAccess {
		if len(id) == 0 {
			switch req.Method {
			case "GET":
				// Index
				if resIndex, ok := resource.(indexer); ok {
					resIndex.Index(c)
				} else {
					NotImplemented(c)
				}
			case "POST":
				// Create
				if resCreate, ok := resource.(creater); ok {
					resCreate.Create(c, req)
				} else {
					NotImplemented(c)
				}
			case "DELETE":
				// Delete
				if resDelete, ok := resource.(deleter); ok {
					resDelete.Delete(c, id)
				} else {
					NotImplemented(c)
				}
			case "OPTIONS":
				// automatic options listing
				if resOptions, ok := resource.(optioner); ok {
					resOptions.Options(c, id)
				} else {
					NotImplemented(c)
				}
			default:
				NotImplemented(c)
			}
		} else { // ID was passed
			switch req.Method {
			case "GET":
				// Find
				if resFind, ok := resource.(finder); ok {
					resFind.Find(c, id)
				} else {
					NotImplemented(c)
				}
			case "PUT":
				// Update
				if resUpdate, ok := resource.(updater); ok {
					resUpdate.Update(c, id, req)
				} else {
					NotImplemented(c)
				}
			case "DELETE":
				// Delete
				if resDelete, ok := resource.(deleter); ok {
					resDelete.Delete(c, id)
				} else {
					NotImplemented(c)
				}
			case "OPTIONS":
				// automatic options
				if resOptions, ok := resource.(optioner); ok {
					resOptions.Options(c, id)
				} else {
					NotImplemented(c)
				}
			default:
				NotImplemented(c)
			}
		}
	}
}

// Add a resource route to http
func Resource(name string, res interface{}) {
	resources[name] = res
	http.Handle("/"+name+"/", http.HandlerFunc(resourceHandler))
}

// Emits a 404 Not Found
func NotFound(c http.ResponseWriter) {
	http.Error(c, "404 Not Found", http.StatusNotFound)
}

// Emits a 501 Not Implemented
func NotImplemented(c http.ResponseWriter) {
	http.Error(c, "501 Not Implemented", http.StatusNotImplemented)
}

// Emits a 201 Created with the URI for the new location
func Created(c http.ResponseWriter, location string) {
	c.Header().Set("Location", location)
	http.Error(c, "201 Created", http.StatusCreated)
}

// Emits a 200 OK with a location. Used when after a PUT
func Updated(c http.ResponseWriter, location string) {
	c.Header().Set("Location", location)
	http.Error(c, "200 OK", http.StatusOK)
}

// Emits a bad request with the specified instructions
func BadRequest(c http.ResponseWriter, instructions string) {
	c.WriteHeader(http.StatusBadRequest)
	c.Write([]byte(instructions))
}

// Emits a 204 No Content
func NoContent(c http.ResponseWriter) {
	http.Error(c, "204 No Content", http.StatusNoContent)
}
<commit_msg>fixes issue #8 and #7 Each HTTP-method should be implementable, not matter if resource or resource with ID. The special case is GET, either Index or Find -> use appropriate interface.<commit_after>package rest

import (
	"log"
	"fmt"
	"http"
	"strings"
)

var resources = make(map[string]interface{})

// Generic resource handler
func resourceHandler(c http.ResponseWriter, req *http.Request) {
	// Parse request URI to resource URI and (potential) ID
	var resourceEnd = strings.Index(req.URL.Path[1:], "/") + 1
	var resourceName string
	if resourceEnd == -1 {
		resourceName = req.URL.Path[1:]
	} else {
		resourceName = req.URL.Path[1:resourceEnd]
	}
	var id = req.URL.Path[resourceEnd+1:]

	resource, ok := resources[resourceName]
	if !ok {
		fmt.Fprintf(c, "resource %s not found\n", resourceName)
	}

	var hasAccess bool = false
	if accesschecker, ok := resource.(accessChecker); ok {
		hasAccess, _ = accesschecker.HasAccess(req)
	} else {
		log.Println("Resource ", resourceName, " has no accessChecker. Giving access …")
		// no checker for resource, so always give access
		hasAccess = true
	}
	if hasAccess {
		switch req.Method {
			case "GET":
				if len(id) == 0 {
					// no ID -> Index
					if resIndex, ok := resource.(indexer); ok {
						resIndex.Index(c)
					} else {
						NotImplemented(c)
					}
				} else {
					// Find by ID
					if resFind, ok := resource.(finder); ok {
						resFind.Find(c, id)
					} else {
						NotImplemented(c)
					}
				}
			case "POST":
				// Create
				if resCreate, ok := resource.(creater); ok {
					resCreate.Create(c, req)
				} else {
					NotImplemented(c)
				}
			case "PUT":
				// Update
				if resUpdate, ok := resource.(updater); ok {
					resUpdate.Update(c, id, req)
				} else {
					NotImplemented(c)
				}
			case "DELETE":
				// Delete
				if resDelete, ok := resource.(deleter); ok {
					resDelete.Delete(c, id)
				} else {
					NotImplemented(c)
				}
			case "OPTIONS":
				// List usable HTTP methods
				if resOptions, ok := resource.(optioner); ok {
					resOptions.Options(c, id)
				} else {
					NotImplemented(c)
				}
			default:
				NotImplemented(c)
		}
	}
}

// Add a resource route to http
func Resource(name string, res interface{}) {
	resources[name] = res
	http.Handle("/"+name+"/", http.HandlerFunc(resourceHandler))
}

// Emits a 404 Not Found
func NotFound(c http.ResponseWriter) {
	http.Error(c, "404 Not Found", http.StatusNotFound)
}

// Emits a 501 Not Implemented
func NotImplemented(c http.ResponseWriter) {
	http.Error(c, "501 Not Implemented", http.StatusNotImplemented)
}

// Emits a 201 Created with the URI for the new location
func Created(c http.ResponseWriter, location string) {
	c.Header().Set("Location", location)
	http.Error(c, "201 Created", http.StatusCreated)
}

// Emits a 200 OK with a location. Used when after a PUT
func Updated(c http.ResponseWriter, location string) {
	c.Header().Set("Location", location)
	http.Error(c, "200 OK", http.StatusOK)
}

// Emits a bad request with the specified instructions
func BadRequest(c http.ResponseWriter, instructions string) {
	c.WriteHeader(http.StatusBadRequest)
	c.Write([]byte(instructions))
}

// Emits a 204 No Content
func NoContent(c http.ResponseWriter) {
	http.Error(c, "204 No Content", http.StatusNoContent)
}
<|endoftext|>


<commit_before>package main

import (
	//"context"
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/hashicorp/logutils"
	"github.com/zmb3/spotify"
	//"golang.org/x/oauth2/clientcredentials"
	"github.com/skratchdot/open-golang/open"
	"log"
	"net/http"
	"os"
)

// redirectURI is the OAuth redirect URI for the application.
// You must register an application at Spotify's developer portal
// and enter this value.
const redirectURI = "http://localhost:5007/callback"

var (
	config Config
	rule = Rule{ Explicit: true }
	client *spotify.Client
	track *spotify.FullTrack
	roomName string
	auth  = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserReadCurrentlyPlaying, spotify.ScopeUserReadPlaybackState, spotify.ScopeUserModifyPlaybackState)
	ch    = make(chan *spotify.Client)
	state = "abc123"
)

func GetFilter(w http.ResponseWriter, req *http.Request) {
	json.NewEncoder(w).Encode(rule)
}

func ToggleFilter(w http.ResponseWriter, req *http.Request) {
	rule.Explicit = !rule.Explicit
	json.NewEncoder(w).Encode(rule)
}

func completeAuth(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
		log.Fatal(err)
	}
	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
		log.Fatalf("State mismatch: %s != %s\n", st, state)
	}
	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprintf(w, "Monitoring authorization completed! You can close this window now.")
	ch <- &client
}

func main() {
	logFilter := &logutils.LevelFilter{
		Levels: []logutils.LogLevel{"DEBUG", "INFO", "WARN", "ERROR"},
		MinLevel: logutils.LogLevel("INFO"),
		Writer: os.Stderr,
	}
	log.SetOutput(logFilter)

	conf, err := os.Open("config.json")
	if err != nil {
		log.Print("[DEBUG] No config file specified, ignoring.")
	} else {
		decoder := json.NewDecoder(conf)
		err = decoder.Decode(&config)
		if err != nil {
			log.Fatalf("Config file 'config.json could not be read, %v", err)
		}
		if config.LogLevel != "" {
			logFilter.SetMinLevel(config.LogLevel)
		}
	}

	router := mux.NewRouter()

	router.HandleFunc("/callback", completeAuth).Methods("GET")
	router.HandleFunc("/filter", GetFilter).Methods("GET")
	router.HandleFunc("/filter", ToggleFilter).Methods("PUT")

	sonos := router.PathPrefix("/sonos").Subrouter()
	sonos.HandleFunc("/updates", HandleUpdate).Methods("POST")

	go func() {
		if config.ClientId != "" && config.ClientSecret != "" {
			auth.SetAuthInfo(config.ClientId, config.ClientSecret)
		}
		url := auth.AuthURL(state)
		err := open.Run(url)
		if err != nil {
			log.Fatalf("Could not open %s: %v", url, err)
		}

		// wait for auth to complete
		client = <-ch

		// use the client to make calls that require authorization
		user, err := client.CurrentUser()
		if err != nil {
			log.Fatal(err)
		}
		log.Println("[DEBUG] You are logged in as:", user.ID)
		go Monitor()
	}()

	log.Fatal(http.ListenAndServe(":5007", router))
}
<commit_msg>defer Close of config file<commit_after>package main

import (
	//"context"
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/hashicorp/logutils"
	"github.com/zmb3/spotify"
	//"golang.org/x/oauth2/clientcredentials"
	"github.com/skratchdot/open-golang/open"
	"log"
	"net/http"
	"os"
)

// redirectURI is the OAuth redirect URI for the application.
// You must register an application at Spotify's developer portal
// and enter this value.
const redirectURI = "http://localhost:5007/callback"

var (
	config Config
	rule = Rule{ Explicit: true }
	client *spotify.Client
	track *spotify.FullTrack
	roomName string
	auth  = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserReadCurrentlyPlaying, spotify.ScopeUserReadPlaybackState, spotify.ScopeUserModifyPlaybackState)
	ch    = make(chan *spotify.Client)
	state = "abc123"
)

func GetFilter(w http.ResponseWriter, req *http.Request) {
	json.NewEncoder(w).Encode(rule)
}

func ToggleFilter(w http.ResponseWriter, req *http.Request) {
	rule.Explicit = !rule.Explicit
	json.NewEncoder(w).Encode(rule)
}

func completeAuth(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
		log.Fatal(err)
	}
	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
		log.Fatalf("State mismatch: %s != %s\n", st, state)
	}
	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprintf(w, "Monitoring authorization completed! You can close this window now.")
	ch <- &client
}

func main() {
	logFilter := &logutils.LevelFilter{
		Levels: []logutils.LogLevel{"DEBUG", "INFO", "WARN", "ERROR"},
		MinLevel: logutils.LogLevel("INFO"),
		Writer: os.Stderr,
	}
	log.SetOutput(logFilter)

	conf, err := os.Open("config.json")
	if err != nil {
		log.Print("[DEBUG] No config file specified, ignoring.")
	} else {
		defer os.Close(conf)

		decoder := json.NewDecoder(conf)
		err = decoder.Decode(&config)
		if err != nil {
			log.Fatalf("Config file 'config.json could not be read, %v", err)
		}
		if config.LogLevel != "" {
			logFilter.SetMinLevel(config.LogLevel)
		}
	}

	router := mux.NewRouter()

	router.HandleFunc("/callback", completeAuth).Methods("GET")
	router.HandleFunc("/filter", GetFilter).Methods("GET")
	router.HandleFunc("/filter", ToggleFilter).Methods("PUT")

	sonos := router.PathPrefix("/sonos").Subrouter()
	sonos.HandleFunc("/updates", HandleUpdate).Methods("POST")

	go func() {
		if config.ClientId != "" && config.ClientSecret != "" {
			auth.SetAuthInfo(config.ClientId, config.ClientSecret)
		}
		url := auth.AuthURL(state)
		err := open.Run(url)
		if err != nil {
			log.Fatalf("Could not open %s: %v", url, err)
		}

		// wait for auth to complete
		client = <-ch

		// use the client to make calls that require authorization
		user, err := client.CurrentUser()
		if err != nil {
			log.Fatal(err)
		}
		log.Println("[DEBUG] You are logged in as:", user.ID)
		go Monitor()
	}()

	log.Fatal(http.ListenAndServe(":5007", router))
}
<|endoftext|>


<commit_before>// Package stateful defines a nested stateful lexer.
//
// This lexer is based heavily on the approach used by Chroma (and Pygments).
//
// The lexer is a state machine defined by a map of rules keyed by state. Each rule
// is a named regex and optional operation to apply when the rule matches.
//
// As a convenience, any Rule starting with a lowercase letter will be elided from output.
//
// Lexing starts in the "Root" group. Each rule is matched in order, with the first
// successful match producing a lexeme. If the matching rule has an associated Action
// it will be executed. The name of each non-root rule is prefixed with the name
// of its group to yield the token identifier used during matching.
//
// A state change can be introduced with the Action `Push(state)`. `Pop()` will
// return to the previous state.
//
// To reuse rules from another state, use `Include(state)`.
//
// As a special case, regexes containing backrefs in the form \N (where N is a digit)
// will match the corresponding capture group from the immediate parent group. This
// can be used to parse, among other things, heredocs.
//
// See the example and tests in this package for details.
package stateful

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"regexp"
	"sort"
	"strconv"
	"unicode"
	"unicode/utf8"

	"github.com/alecthomas/participle"
	"github.com/alecthomas/participle/lexer"
)

var (
	eolBytes       = []byte("\n")
	backrefReplace = regexp.MustCompile(`\\(\d)`)
)

// A Rule matching input and possibly changing state.
type Rule struct {
	Name    string
	Pattern string
	Action  Action
}

// Rules grouped by name.
type Rules map[string][]Rule

// compiledRule is a Rule with its pattern compiled.
type compiledRule struct {
	Rule
	ignore bool
	RE     *regexp.Regexp
}

// compiledRules grouped by name.
type compiledRules map[string][]compiledRule

// A Action is applied when a rule matches.
type Action interface {
	// Actions are responsible for validating the match. ie. if they consumed any input.
	applyAction(lexer *Lexer, groups []string) error
}

// RulesAction is an optional interface that Actions can implement.
//
// It is applied during rule construction to mutate the rule map.
type RulesAction interface {
	applyRules(state string, rule int, rules compiledRules) error
}

// ActionFunc is a function that is also a Action.
type ActionFunc func(*Lexer, []string) error

func (m ActionFunc) applyAction(lexer *Lexer, groups []string) error { return m(lexer, groups) } // nolint: golint

// Pop to the previous state.
func Pop() Action {
	return ActionFunc(func(lexer *Lexer, groups []string) error {
		if groups[0] == "" {
			return errors.New("did not consume any input")
		}
		lexer.stack = lexer.stack[:len(lexer.stack)-1]
		return nil
	})
}

// PopIfEmpty pops to the parent state if the rule did not match.
func PopIfEmpty() Action {
	return ActionFunc(func(lexer *Lexer, groups []string) error {
		if groups[0] == "" {
			lexer.stack = lexer.stack[:len(lexer.stack)-1]
		}
		return nil
	})
}

// Push to the given state.
//
// The target state will then be the set of rules used for matching
// until another Push or Pop is encountered.
func Push(state string) Action {
	return ActionFunc(func(lexer *Lexer, groups []string) error {
		if groups[0] == "" {
			return errors.New("did not consume any input")
		}
		lexer.stack = append(lexer.stack, lexerState{name: state, groups: groups})
		return nil
	})
}

type include struct{ state string }

func (i include) applyAction(lexer *Lexer, groups []string) error { panic("should not be called") }

func (i include) applyRules(state string, rule int, rules compiledRules) error {
	includedRules, ok := rules[i.state]
	if !ok {
		return fmt.Errorf("invalid include state %q", i.state)
	}
	clone := make([]compiledRule, len(includedRules))
	copy(clone, includedRules)
	rules[state] = append(rules[state][:rule], append(clone, rules[state][rule+1:]...)...)
	return nil
}

// Include rules from another state in this one.
func Include(state string) Rule {
	return Rule{Action: include{state}}
}

// Definition is the lexer.Definition.
type Definition struct {
	rules   compiledRules
	symbols map[string]rune
}

// New constructs a new stateful lexer from rules.
func New(rules Rules) (*Definition, error) {
	compiled := compiledRules{}
	for key, set := range rules {
		for i, rule := range set {
			pattern := "^(?:" + rule.Pattern + ")"
			var (
				re  *regexp.Regexp
				err error
			)
			if backrefReplace.FindString(rule.Pattern) == "" {
				re, err = regexp.Compile(pattern)
				if err != nil {
					return nil, fmt.Errorf("%s.%d: %s", key, i, err)
				}
			}
			compiled[key] = append(compiled[key], compiledRule{
				Rule:   rule,
				ignore: len(rule.Name) > 0 && unicode.IsLower(rune(rule.Name[0])),
				RE:     re,
			})
		}
	}
restart:
	for state, rules := range compiled {
		for i, rule := range rules {
			if action, ok := rule.Action.(RulesAction); ok {
				if err := action.applyRules(state, i, compiled); err != nil {
					return nil, fmt.Errorf("%s.%d: %s", state, i, err)
				}
				goto restart
			}
		}
	}
	keys := make([]string, 0, len(compiled))
	for key := range compiled {
		keys = append(keys, key)
	}
	symbols := map[string]rune{
		"EOF": lexer.EOF,
	}
	sort.Strings(keys)
	duplicates := map[string]compiledRule{}
	rn := lexer.EOF - 1
	for _, key := range keys {
		for i, rule := range compiled[key] {
			if dup, ok := duplicates[rule.Name]; ok && rule.Pattern != dup.Pattern {
				panic(fmt.Sprintf("duplicate key %q with different patterns %q != %q", rule.Name, rule.Pattern, dup.Pattern))
			}
			duplicates[rule.Name] = rule
			compiled[key][i] = rule
			symbols[rule.Name] = rn
			rn--
		}
	}
	return &Definition{rules: compiled, symbols: symbols}, nil
}

func (d *Definition) Lex(r io.Reader) (lexer.Lexer, error) { // nolint: golint
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	return &Lexer{
		def:   d,
		data:  data,
		stack: []lexerState{{name: "Root"}},
		pos: lexer.Position{
			Filename: lexer.NameOfReader(r),
			Line:     1,
			Column:   1,
		},
	}, nil
}

func (d *Definition) Symbols() map[string]rune { // nolint: golint
	return d.symbols
}

type lexerState struct {
	name   string
	groups []string
}

// Lexer implementation.
type Lexer struct {
	stack []lexerState
	def   *Definition
	data  []byte
	pos   lexer.Position
}

func (l *Lexer) Next() (lexer.Token, error) { // nolint: golint
	parent := l.stack[len(l.stack)-1]
	rules := l.def.rules[parent.name]
	for len(l.data) > 0 {
		var (
			rule  *compiledRule
			match []int
		)
		for _, candidate := range rules {
			re, err := l.getPattern(candidate)
			if err != nil {
				return lexer.Token{}, participle.Wrapf(l.pos, err, "rule %q", candidate.Name)
			}
			match = re.FindSubmatchIndex(l.data)
			if match != nil {
				rule = &candidate // nolint: scopelint
				break
			}
		}
		if match == nil || rule == nil {
			return lexer.Token{}, participle.Errorf(l.pos, "no match")
		}

		if rule.Action != nil {
			groups := make([]string, 0, len(match)/2)
			for i := 0; i < len(match); i += 2 {
				groups = append(groups, string(l.data[match[i]:match[i+1]]))
			}
			if err := rule.Action.applyAction(l, groups); err != nil {
				return lexer.Token{}, participle.Errorf(l.pos, "rule %q: %s", rule.Name, err)
			}
		} else if match[0] == match[1] {
			return lexer.Token{}, participle.Errorf(l.pos, "rule %q did not match any input", rule.Name)
		}

		span := l.data[match[0]:match[1]]
		l.data = l.data[match[1]:]
		// l.groups = groups

		// Update position.
		pos := l.pos
		l.pos.Offset += match[1]
		lines := bytes.Count(span, eolBytes)
		l.pos.Line += lines
		// Update column.
		if lines == 0 {
			l.pos.Column += utf8.RuneCount(span)
		} else {
			l.pos.Column = utf8.RuneCount(span[bytes.LastIndex(span, eolBytes):])
		}
		if rule.ignore {
			parent = l.stack[len(l.stack)-1]
			rules = l.def.rules[parent.name]
			continue
		}
		return lexer.Token{
			Type:  l.def.symbols[rule.Name],
			Value: string(span),
			Pos:   pos,
		}, nil
	}
	return lexer.EOFToken(l.pos), nil
}

func (l *Lexer) getPattern(candidate compiledRule) (*regexp.Regexp, error) {
	if candidate.RE != nil {
		return candidate.RE, nil
	}

	// We don't have a compiled RE. This means there are back-references
	// that need to be substituted first.
	// TODO: Cache?
	parent := l.stack[len(l.stack)-1]
	var (
		re  *regexp.Regexp
		err error
	)
	pattern := backrefReplace.ReplaceAllStringFunc(candidate.Pattern, func(s string) string {
		n, nerr := strconv.ParseInt(s[1:], 10, 64)
		if nerr != nil {
			err = nerr
			return s
		}
		if len(parent.groups) == 0 || int(n) >= len(parent.groups) {
			err = fmt.Errorf("invalid group %d from parent with %d groups", n, len(parent.groups))
			return s
		}
		return regexp.QuoteMeta(parent.groups[n])
	})
	if err == nil {
		re, err = regexp.Compile("^(?:" + pattern + ")")
	}
	if err != nil {
		return nil, fmt.Errorf("invalid backref expansion: %q: %s", pattern, err)
	}
	return re, nil
}
<commit_msg>Fix documentation.<commit_after>// Package stateful defines a nested stateful lexer.
//
// This lexer is based heavily on the approach used by Chroma (and Pygments).
//
// The lexer is a state machine defined by a map of rules keyed by state. Each rule
// is a named regex and optional operation to apply when the rule matches.
//
// As a convenience, any Rule starting with a lowercase letter will be elided from output.
//
// Lexing starts in the "Root" group. Each rule is matched in order, with the first
// successful match producing a lexeme. If the matching rule has an associated Action
// it will be executed. The name of each non-root rule is prefixed with the name
// of its group to yield the token identifier used during matching.
//
// A state change can be introduced with the Action `Push(state)`. `Pop()` will
// return to the previous state.
//
// To reuse rules from another state, use `Include(state)`.
//
// As a special case, regexes containing backrefs in the form \N (where N is a digit)
// will match the corresponding capture group from the immediate parent group. This
// can be used to parse, among other things, heredocs.
//
// See the example and tests in this package for details.
package stateful

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"regexp"
	"sort"
	"strconv"
	"unicode"
	"unicode/utf8"

	"github.com/alecthomas/participle"
	"github.com/alecthomas/participle/lexer"
)

var (
	eolBytes       = []byte("\n")
	backrefReplace = regexp.MustCompile(`\\(\d)`)
)

// A Rule matching input and possibly changing state.
type Rule struct {
	Name    string
	Pattern string
	Action  Action
}

// Rules grouped by name.
type Rules map[string][]Rule

// compiledRule is a Rule with its pattern compiled.
type compiledRule struct {
	Rule
	ignore bool
	RE     *regexp.Regexp
}

// compiledRules grouped by name.
type compiledRules map[string][]compiledRule

// A Action is applied when a rule matches.
type Action interface {
	// Actions are responsible for validating the match. ie. if they consumed any input.
	applyAction(lexer *Lexer, groups []string) error
}

// RulesAction is an optional interface that Actions can implement.
//
// It is applied during rule construction to mutate the rule map.
type RulesAction interface {
	applyRules(state string, rule int, rules compiledRules) error
}

// ActionFunc is a function that is also a Action.
type ActionFunc func(*Lexer, []string) error

func (m ActionFunc) applyAction(lexer *Lexer, groups []string) error { return m(lexer, groups) } // nolint: golint

// Pop to the previous state.
func Pop() Action {
	return ActionFunc(func(lexer *Lexer, groups []string) error {
		if groups[0] == "" {
			return errors.New("did not consume any input")
		}
		lexer.stack = lexer.stack[:len(lexer.stack)-1]
		return nil
	})
}

// PopIfEmpty pops to the parent state if the rule matches an empty string.
func PopIfEmpty() Action {
	return ActionFunc(func(lexer *Lexer, groups []string) error {
		if groups[0] == "" {
			lexer.stack = lexer.stack[:len(lexer.stack)-1]
		}
		return nil
	})
}

// Push to the given state.
//
// The target state will then be the set of rules used for matching
// until another Push or Pop is encountered.
func Push(state string) Action {
	return ActionFunc(func(lexer *Lexer, groups []string) error {
		if groups[0] == "" {
			return errors.New("did not consume any input")
		}
		lexer.stack = append(lexer.stack, lexerState{name: state, groups: groups})
		return nil
	})
}

type include struct{ state string }

func (i include) applyAction(lexer *Lexer, groups []string) error { panic("should not be called") }

func (i include) applyRules(state string, rule int, rules compiledRules) error {
	includedRules, ok := rules[i.state]
	if !ok {
		return fmt.Errorf("invalid include state %q", i.state)
	}
	clone := make([]compiledRule, len(includedRules))
	copy(clone, includedRules)
	rules[state] = append(rules[state][:rule], append(clone, rules[state][rule+1:]...)...)
	return nil
}

// Include rules from another state in this one.
func Include(state string) Rule {
	return Rule{Action: include{state}}
}

// Definition is the lexer.Definition.
type Definition struct {
	rules   compiledRules
	symbols map[string]rune
}

// New constructs a new stateful lexer from rules.
func New(rules Rules) (*Definition, error) {
	compiled := compiledRules{}
	for key, set := range rules {
		for i, rule := range set {
			pattern := "^(?:" + rule.Pattern + ")"
			var (
				re  *regexp.Regexp
				err error
			)
			if backrefReplace.FindString(rule.Pattern) == "" {
				re, err = regexp.Compile(pattern)
				if err != nil {
					return nil, fmt.Errorf("%s.%d: %s", key, i, err)
				}
			}
			compiled[key] = append(compiled[key], compiledRule{
				Rule:   rule,
				ignore: len(rule.Name) > 0 && unicode.IsLower(rune(rule.Name[0])),
				RE:     re,
			})
		}
	}
restart:
	for state, rules := range compiled {
		for i, rule := range rules {
			if action, ok := rule.Action.(RulesAction); ok {
				if err := action.applyRules(state, i, compiled); err != nil {
					return nil, fmt.Errorf("%s.%d: %s", state, i, err)
				}
				goto restart
			}
		}
	}
	keys := make([]string, 0, len(compiled))
	for key := range compiled {
		keys = append(keys, key)
	}
	symbols := map[string]rune{
		"EOF": lexer.EOF,
	}
	sort.Strings(keys)
	duplicates := map[string]compiledRule{}
	rn := lexer.EOF - 1
	for _, key := range keys {
		for i, rule := range compiled[key] {
			if dup, ok := duplicates[rule.Name]; ok && rule.Pattern != dup.Pattern {
				panic(fmt.Sprintf("duplicate key %q with different patterns %q != %q", rule.Name, rule.Pattern, dup.Pattern))
			}
			duplicates[rule.Name] = rule
			compiled[key][i] = rule
			symbols[rule.Name] = rn
			rn--
		}
	}
	return &Definition{rules: compiled, symbols: symbols}, nil
}

func (d *Definition) Lex(r io.Reader) (lexer.Lexer, error) { // nolint: golint
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	return &Lexer{
		def:   d,
		data:  data,
		stack: []lexerState{{name: "Root"}},
		pos: lexer.Position{
			Filename: lexer.NameOfReader(r),
			Line:     1,
			Column:   1,
		},
	}, nil
}

func (d *Definition) Symbols() map[string]rune { // nolint: golint
	return d.symbols
}

type lexerState struct {
	name   string
	groups []string
}

// Lexer implementation.
type Lexer struct {
	stack []lexerState
	def   *Definition
	data  []byte
	pos   lexer.Position
}

func (l *Lexer) Next() (lexer.Token, error) { // nolint: golint
	parent := l.stack[len(l.stack)-1]
	rules := l.def.rules[parent.name]
	for len(l.data) > 0 {
		var (
			rule  *compiledRule
			match []int
		)
		for _, candidate := range rules {
			re, err := l.getPattern(candidate)
			if err != nil {
				return lexer.Token{}, participle.Wrapf(l.pos, err, "rule %q", candidate.Name)
			}
			match = re.FindSubmatchIndex(l.data)
			if match != nil {
				rule = &candidate // nolint: scopelint
				break
			}
		}
		if match == nil || rule == nil {
			return lexer.Token{}, participle.Errorf(l.pos, "no match")
		}

		if rule.Action != nil {
			groups := make([]string, 0, len(match)/2)
			for i := 0; i < len(match); i += 2 {
				groups = append(groups, string(l.data[match[i]:match[i+1]]))
			}
			if err := rule.Action.applyAction(l, groups); err != nil {
				return lexer.Token{}, participle.Errorf(l.pos, "rule %q: %s", rule.Name, err)
			}
		} else if match[0] == match[1] {
			return lexer.Token{}, participle.Errorf(l.pos, "rule %q did not match any input", rule.Name)
		}

		span := l.data[match[0]:match[1]]
		l.data = l.data[match[1]:]
		// l.groups = groups

		// Update position.
		pos := l.pos
		l.pos.Offset += match[1]
		lines := bytes.Count(span, eolBytes)
		l.pos.Line += lines
		// Update column.
		if lines == 0 {
			l.pos.Column += utf8.RuneCount(span)
		} else {
			l.pos.Column = utf8.RuneCount(span[bytes.LastIndex(span, eolBytes):])
		}
		if rule.ignore {
			parent = l.stack[len(l.stack)-1]
			rules = l.def.rules[parent.name]
			continue
		}
		return lexer.Token{
			Type:  l.def.symbols[rule.Name],
			Value: string(span),
			Pos:   pos,
		}, nil
	}
	return lexer.EOFToken(l.pos), nil
}

func (l *Lexer) getPattern(candidate compiledRule) (*regexp.Regexp, error) {
	if candidate.RE != nil {
		return candidate.RE, nil
	}

	// We don't have a compiled RE. This means there are back-references
	// that need to be substituted first.
	// TODO: Cache?
	parent := l.stack[len(l.stack)-1]
	var (
		re  *regexp.Regexp
		err error
	)
	pattern := backrefReplace.ReplaceAllStringFunc(candidate.Pattern, func(s string) string {
		n, nerr := strconv.ParseInt(s[1:], 10, 64)
		if nerr != nil {
			err = nerr
			return s
		}
		if len(parent.groups) == 0 || int(n) >= len(parent.groups) {
			err = fmt.Errorf("invalid group %d from parent with %d groups", n, len(parent.groups))
			return s
		}
		return regexp.QuoteMeta(parent.groups[n])
	})
	if err == nil {
		re, err = regexp.Compile("^(?:" + pattern + ")")
	}
	if err != nil {
		return nil, fmt.Errorf("invalid backref expansion: %q: %s", pattern, err)
	}
	return re, nil
}
<|endoftext|>


<commit_before>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRulesFunc func() map[string]func(string) error)
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(volType VolumeType, volName string) bool

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	Mount() (bool, error)
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)

	// Volumes.
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler func(path string) error, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	DeleteVolume(volType VolumeType, volName string, op *operations.Operation) error
	RenameVolume(volType VolumeType, volName string, newName string, op *operations.Operation) error
	UpdateVolume(vol Volume, changedConfig map[string]string) error

	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
	// already mounted.
	MountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(volType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(VolumeType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(volType VolumeType, volName string, newSnapshotName string, op *operations.Operation) error
	DeleteVolumeSnapshot(volType VolumeType, volName string, snapshotName string, op *operations.Operation) error
	RenameVolumeSnapshot(volType VolumeType, volName string, snapshotName string, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(volType VolumeType, volName string, op *operations.Operation) ([]string, error)

	// Migration.
	MigrationTypes(contentType ContentType) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, op *operations.Operation) error
}
<commit_msg>lxd/storage/drivers/interface: Adds RestoreVolume<commit_after>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRulesFunc func() map[string]func(string) error)
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(volType VolumeType, volName string) bool

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	Mount() (bool, error)
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)

	// Volumes.
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler func(path string) error, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	DeleteVolume(volType VolumeType, volName string, op *operations.Operation) error
	RenameVolume(volType VolumeType, volName string, newName string, op *operations.Operation) error
	UpdateVolume(vol Volume, changedConfig map[string]string) error

	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
	// already mounted.
	MountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(volType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(VolumeType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(volType VolumeType, volName string, newSnapshotName string, op *operations.Operation) error
	DeleteVolumeSnapshot(volType VolumeType, volName string, snapshotName string, op *operations.Operation) error
	RenameVolumeSnapshot(volType VolumeType, volName string, snapshotName string, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(volType VolumeType, volName string, op *operations.Operation) ([]string, error)
	RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error

	// Migration.
	MigrationTypes(contentType ContentType) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, op *operations.Operation) error
}
<|endoftext|>


<commit_before>// Package bla provides ...
package bla

import (
	"flag"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
	"text/template"
)

const Version = "0.1 alpha"

func New() {

	flag.Parse()
	LoadConfig(*configPath)

	server := &Server{
		Docs:        make(map[string]*Doc, 0),
		sortedDocs:  make([]*Doc, 0),
		DocLock:     &sync.RWMutex{},
		Tags:        make(map[string][]*Doc, 0),
		TagLock:     &sync.RWMutex{},
		StaticFiles: make([]string, 0),
		Version:     Version,
		StopWatch:   make(chan bool)}

	server.Reset()
	if err := server.LoadTempalte(); err != nil {
		log.Fatal(err)
	}
	server.LoadStaticFiles()
	server.LoadAllDocs()
	if err := server.SaveAllDocs(); err != nil {
		log.Fatal(err)
	}
	server.MakeHome()
	log.Print(Cfg)
	server.Watch()
	server.ConfigWatch()
	http.HandleFunc(path.Join(Cfg.BasePath, "/.login"), Login)
	http.Handle("/", http.FileServer(http.Dir(Cfg.PublicPath)))
	http.ListenAndServe(Cfg.Addr, nil)
}

func Login(w http.ResponseWriter, r *http.Request) {
	log.Print(r.BasicAuth())
}

func (s *Server) Reset() {

	if err := os.RemoveAll(Cfg.PublicPath); err != nil {
		log.Print(err)
	}
	os.MkdirAll(Cfg.PublicPath, 0755)

	if Cfg.Favicon != "" {
		fav, _ := filepath.Abs(Cfg.Favicon)
		dest, _ := filepath.Abs(filepath.Join(Cfg.PublicPath, "favicon.ico"))

		if err := os.Symlink(fav, dest); err != nil {
			log.Print(err)
		} else {
			log.Print("Rebuild link:", fav, " -> ", dest)
		}
	}

	uploads, _ := filepath.Abs(Cfg.UploadPath)
	dest, _ := filepath.Abs(filepath.Join(Cfg.PublicPath, Cfg.BasePath, "uploads"))

	os.MkdirAll(filepath.Join(Cfg.PublicPath, Cfg.BasePath), 0755)

	if err := os.Symlink(uploads, dest); err != nil {
		log.Print(err)
	} else {
		log.Print("Rebuild link:", uploads, " -> ", dest)
	}
}

func (s *Server) LoadStaticFiles() {

	log.Print("Rebuild from: ", Cfg.TemplatePath)
	orig, err := filepath.Abs(filepath.Join(Cfg.TemplatePath, "asset"))
	dest, err := filepath.Abs(filepath.Join(Cfg.PublicPath, "asset"))
	if err != nil {
		log.Print(err)
	}

	log.Print("Rebuild link:", orig, " -> ", dest)
	if err := os.Symlink(orig, dest); err != nil {
		log.Print(err)
	}
	log.Print("Loading Asset files")
	filepath.Walk(orig,
		func(path string, info os.FileInfo, e error) (err error) {

			if info == nil || !info.Mode().IsRegular() {
				return
			}
			if !(filepath.Ext(path) == ".css" || filepath.Ext(path) == ".js") {
				return
			}
			log.Print("|- ", path)
			s.StaticFiles = append(s.StaticFiles, path)
			return
		})
}

func (s *Server) LoadTempalte() (err error) {

	var funcMap = template.FuncMap{
		"base": path.Base,
		"ext":  filepath.Ext,
	}
	root := filepath.Join(Cfg.TemplatePath, "root.tmpl")
	parsed := func(fname string) (t *template.Template) {
		t, err = template.New("").Funcs(funcMap).ParseFiles(root, filepath.Join(Cfg.TemplatePath, fname))
		if err != nil {
			log.Fatal(err)
		}
		return t
	}

	s.template.home = parsed("home.tmpl")
	s.template.doc = parsed("doc.tmpl")
	s.template.index = parsed("index.tmpl")
	return
}

type Server struct {
	Docs        map[string]*Doc
	sortedDocs  []*Doc
	Tags        map[string][]*Doc
	StaticFiles []string
	template    struct{ home, doc, index *template.Template }
	Version     string
	StopWatch   chan bool
	DocLock     *sync.RWMutex
	TagLock     *sync.RWMutex
}
<commit_msg>add http auth<commit_after>// Package bla provides ...
package bla

import (
	"flag"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
	"text/template"
)

const Version = "0.1 alpha"

func New() {

	flag.Parse()
	LoadConfig(*configPath)

	server := &Server{
		Docs:        make(map[string]*Doc, 0),
		sortedDocs:  make([]*Doc, 0),
		DocLock:     &sync.RWMutex{},
		Tags:        make(map[string][]*Doc, 0),
		TagLock:     &sync.RWMutex{},
		StaticFiles: make([]string, 0),
		Version:     Version,
		StopWatch:   make(chan bool)}

	server.Reset()
	if err := server.LoadTempalte(); err != nil {
		log.Fatal(err)
	}
	server.LoadStaticFiles()
	server.LoadAllDocs()
	if err := server.SaveAllDocs(); err != nil {
		log.Fatal(err)
	}
	server.MakeHome()
	log.Print(Cfg)
	server.Watch()
	server.ConfigWatch()
	http.HandleFunc(path.Join(Cfg.BasePath, "/.login"), Login)
	http.Handle("/", http.FileServer(http.Dir(Cfg.PublicPath)))
	http.ListenAndServe(Cfg.Addr, nil)
}

func Login(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		log.Print(r.BasicAuth())
	} else {
		w.Header().Add("WWW-Authenticate", `Basic realm=""`)
		w.WriteHeader(401)
	}
}

func (s *Server) Reset() {

	if err := os.RemoveAll(Cfg.PublicPath); err != nil {
		log.Print(err)
	}
	os.MkdirAll(Cfg.PublicPath, 0755)

	if Cfg.Favicon != "" {
		fav, _ := filepath.Abs(Cfg.Favicon)
		dest, _ := filepath.Abs(filepath.Join(Cfg.PublicPath, "favicon.ico"))

		if err := os.Symlink(fav, dest); err != nil {
			log.Print(err)
		} else {
			log.Print("Rebuild link:", fav, " -> ", dest)
		}
	}

	uploads, _ := filepath.Abs(Cfg.UploadPath)
	dest, _ := filepath.Abs(filepath.Join(Cfg.PublicPath, Cfg.BasePath, "uploads"))

	os.MkdirAll(filepath.Join(Cfg.PublicPath, Cfg.BasePath), 0755)

	if err := os.Symlink(uploads, dest); err != nil {
		log.Print(err)
	} else {
		log.Print("Rebuild link:", uploads, " -> ", dest)
	}
}

func (s *Server) LoadStaticFiles() {

	log.Print("Rebuild from: ", Cfg.TemplatePath)
	orig, err := filepath.Abs(filepath.Join(Cfg.TemplatePath, "asset"))
	dest, err := filepath.Abs(filepath.Join(Cfg.PublicPath, "asset"))
	if err != nil {
		log.Print(err)
	}

	log.Print("Rebuild link:", orig, " -> ", dest)
	if err := os.Symlink(orig, dest); err != nil {
		log.Print(err)
	}
	log.Print("Loading Asset files")
	filepath.Walk(orig,
		func(path string, info os.FileInfo, e error) (err error) {

			if info == nil || !info.Mode().IsRegular() {
				return
			}
			if !(filepath.Ext(path) == ".css" || filepath.Ext(path) == ".js") {
				return
			}
			log.Print("|- ", path)
			s.StaticFiles = append(s.StaticFiles, path)
			return
		})
}

func (s *Server) LoadTempalte() (err error) {

	var funcMap = template.FuncMap{
		"base": path.Base,
		"ext":  filepath.Ext,
	}
	root := filepath.Join(Cfg.TemplatePath, "root.tmpl")
	parsed := func(fname string) (t *template.Template) {
		t, err = template.New("").Funcs(funcMap).ParseFiles(root, filepath.Join(Cfg.TemplatePath, fname))
		if err != nil {
			log.Fatal(err)
		}
		return t
	}

	s.template.home = parsed("home.tmpl")
	s.template.doc = parsed("doc.tmpl")
	s.template.index = parsed("index.tmpl")
	return
}

type Server struct {
	Docs        map[string]*Doc
	sortedDocs  []*Doc
	Tags        map[string][]*Doc
	StaticFiles []string
	template    struct{ home, doc, index *template.Template }
	Version     string
	StopWatch   chan bool
	DocLock     *sync.RWMutex
	TagLock     *sync.RWMutex
}
<|endoftext|>


<commit_before>// Goals:
// - can be used in the same manner with http.Server
//   - Make API as much compatible as possible
//   - Make the zero value useful
// - inherit as much battle tested code from net/http
// - much flexiblity with built-in components
//   - ConnHandler
//     - Raw
//     - KeepAliveHandler
//     - PipelineHandler
//   - ConnTracker
//     - MapConnTracker(same with net/http
//     - WGConnTracker(fast, less memory than Map*
//   - NewConn (BufferedConn, StatsConn, DebugConn
//     - can be layered
//   - Logger
//   - Retry
//   - Statistics (# of connections, in bytes, out bytes
//   - Limiter (Connection, IP
// - Get GC pressure as little as possible with sync.Pool
// - Zero 3rd party depentencies
// TODO:
// - tls
// - multiple listeners
package gtcp

import (
	"context"
	"errors"
	"net"
	"runtime"
	"sync"
	"time"
)

type (
	Server struct {
		Addr string

		ConnHandler ConnHandler

		// Configurable components
		NewConn     NewConn
		ConnTracker ConnTracker
		Logger      Logger
		Retry       Retry
		Limiters    []Limiter
		Statistics  Statistics

		listener net.Listener

		mu       sync.Mutex
		doneChan chan struct{}
	}
)

var (
	ErrServerClosed = errors.New("gtcp: Server closed")
	ErrAbortHandler = errors.New("gtcp: abort Handler")
)

func (s *Server) getDoneChan() <-chan struct{} {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.getDoneChanLocked()
}

func (s *Server) getDoneChanLocked() chan struct{} {
	if s.doneChan == nil {
		s.doneChan = make(chan struct{})
	}
	return s.doneChan
}

func (s *Server) closeDoneChanLocked() {
	ch := s.getDoneChanLocked()
	select {
	case <-ch:
		// Already closed. Don't close again.
	default:
		// Safe to close here. We're the only closer, guarded
		// by s.mu.
		close(ch)
	}
}

func (s *Server) Close() (err error) {
	s.mu.Lock()
	s.closeDoneChanLocked()
	s.mu.Unlock()
	err = s.listener.Close()
	s.ConnTracker.Close()
	return
}

func (s *Server) Shutdown(ctx context.Context) (err error) {
	s.mu.Lock()
	s.closeDoneChanLocked()
	s.mu.Unlock()
	err = s.listener.Close()
	s.ConnTracker.Shutdown(ctx)
	return
}

func (s *Server) ListenerAddr() net.Addr {
	if s.listener != nil {
		return s.listener.Addr()
	}
	return nil
}

func (s *Server) ListenAndServe() error {
	addr := s.Addr
	if addr == "" {
		addr = ":1979"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return s.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

func ListenAndServe(addr string, handler ConnHandler) error {
	server := &Server{Addr: addr, ConnHandler: handler}
	return server.ListenAndServe()
}

func (s *Server) Serve(l net.Listener) error {
	var retry uint64
	defer l.Close()

	if s.ConnHandler == nil {
		panic("gtcp: nil handler")
	}

	// set reasonable default to each component
	if s.ConnTracker == nil {
		s.ConnTracker = NewMapConnTracker()
	}
	if s.Logger == nil {
		s.Logger = DefaultLogger
	}
	if s.Retry == nil {
		s.Retry = DefaultRetry
	}
	if s.Statistics == nil {
		s.Statistics = &TrafficStatistics{}
	}

	s.listener = l

	// context per listener
	// notify goroutines executing ConnHandler that your listner got closed
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	for {
		rw, e := l.Accept()
		if e != nil {
			select {
			case <-s.getDoneChan():
				return ErrServerClosed
			default:
			}
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				delay := s.Retry.Backoff(retry)
				s.Logger.Errorf("gtcp: Accept error: %v; retrying in %v", e, delay)
				time.Sleep(delay)
				retry += 1
				continue
			}
			return e
		}
		retry = 0
		var conn Conn = NewBaseConn(rw)
		if s.NewConn != nil {
			conn = s.NewConn(conn)
		}
		s.serve(ctx, conn)
	}
}

func (s *Server) serve(ctx context.Context, conn Conn) {
	for _, limiter := range s.Limiters {
		if limiter.OnConnected(conn) == false {
			s.Logger.Errorf("gtcp: connection refused %v: by limiter %+v", conn.RemoteAddr(), limiter)
			conn.Close()
			return
		}
	}
	s.ConnTracker.AddConn(conn)
	go func() {
		defer func() {
			for _, limiter := range s.Limiters {
				limiter.OnClosed(conn)
			}
			s.ConnTracker.DelConn(conn)
			s.Statistics.AddConnStats(conn)
			if err := recover(); err != nil && err != ErrAbortHandler {
				const size = 64 << 10
				buf := make([]byte, size)
				buf = buf[:runtime.Stack(buf, false)]
				s.Logger.Errorf("gtcp: panic serving %v: %v\n%s", conn.RemoteAddr(), err, buf)
			}
			conn.Close()
		}()
		// context per connection
		ctx, cancel := context.WithCancel(ctx)
		conn.SetCancelFunc(cancel)
		defer cancel()
		s.ConnHandler(ctx, conn)
	}()
}
<commit_msg>Add comments for server.go<commit_after>package gtcp

import (
	"context"
	"errors"
	"net"
	"runtime"
	"sync"
	"time"
)

type (
	// Server defines parameters for running a gtcp server.
	// The zero value for Server is a valid configuration.
	Server struct {
		// Addr to listen on, ":1979" if empty.
		Addr string

		// ConnHandler handles a tcp connection accepted.
		// It can be used not only directly setting ConnHandler but
		// - Server.SetKeepAliveHandler
		// - Server.SetPipelineHandler
		// Panic occurred if empty.
		ConnHandler ConnHandler

		// Configurable components

		// NewConn that applied to each tcp connection accepted by listener.
		// It can be
		// - gtcp.NewBufferedConn
		// - gtcp.NewStatsConn
		// - gtcp.NewDebugConn
		// also can be layered like the below.
		//		func(conn Conn) Conn {
		//			return gtcp.NewBufferedConn(gtcp.NewStatsConn(conn))
		//		}
		// None NewConn is applied if empty.
		NewConn NewConn

		// ConnTracker that handles active connections.
		// It can be
		// - gtcp.MapConnTracker
		// - gtcp.WGConnTracker
		// gtcp.MapConnTracker is set if empty.
		ConnTracker ConnTracker

		// Logger that logs if an error occurred in gtcp.
		// It can be
		// - gtcp.BuiltinLogger
		// gtcp.DefaultLogger is set if empty.
		Logger Logger

		// Retry that handles the retry interval when Accept on listner failed.
		// It can be
		// - gtcp.ExponentialRetry
		// gtcp.DefaultRetry is set if empty.(it behaves in the same manner with net/http/Server.
		Retry Retry

		// Limiters that limits connections.
		// It can be
		// - gtcp.MaxConnLimiter
		// also multiple limiters can be set.
		// None Limiter is set if empty.
		Limiters []Limiter

		// Statistics that measures some statistics.
		// It can be
		// - gtcp.TrafficStatistics
		// gtcp.TrafficStatistics is set if empty.
		Statistics Statistics

		listener net.Listener

		mu       sync.Mutex
		doneChan chan struct{}
	}
)

var (
	// ErrServerClosed returned when listener got closed through Close/Shutdown.
	ErrServerClosed = errors.New("gtcp: Server closed")
	// ErrAbortHandler is a sentinel panic value to abort a handler.
	// panicking with ErrAbortHandler also suppresses logging of a stack
	// trace to the server's error log.
	ErrAbortHandler = errors.New("gtcp: abort Handler")
)

func (s *Server) getDoneChan() <-chan struct{} {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.getDoneChanLocked()
}

func (s *Server) getDoneChanLocked() chan struct{} {
	if s.doneChan == nil {
		s.doneChan = make(chan struct{})
	}
	return s.doneChan
}

func (s *Server) closeDoneChanLocked() {
	ch := s.getDoneChanLocked()
	select {
	case <-ch:
		// Already closed. Don't close again.
	default:
		// Safe to close here. We're the only closer, guarded
		// by s.mu.
		close(ch)
	}
}

// Close immediately closes the listner and any
// connections tracked by ConnTracker.
// Close returns any error returned from closing the listner.
func (s *Server) Close() (err error) {
	s.mu.Lock()
	s.closeDoneChanLocked()
	s.mu.Unlock()
	err = s.listener.Close()
	s.ConnTracker.Close()
	return
}

// Shutdown gracefully shuts down the server without interrupting any
// active connections.
// If the provided context expires before the shutdown is complete,
// then the context's error is returned.
func (s *Server) Shutdown(ctx context.Context) (err error) {
	s.mu.Lock()
	s.closeDoneChanLocked()
	s.mu.Unlock()
	err = s.listener.Close()
	s.ConnTracker.Shutdown(ctx)
	return
}

// ListenerAddr returns the listner.Addr() or nil if listner is empty.
func (s *Server) ListenerAddr() net.Addr {
	if s.listener != nil {
		return s.listener.Addr()
	}
	return nil
}

// ListenAndServe listens on the TCP network address Addr and then
// calls Serve to handle requests on incoming connections.
// If Addr is blank, ":1979" is used.
// ListenAndServe always returns a non-nil error.
func (s *Server) ListenAndServe() error {
	addr := s.Addr
	if addr == "" {
		addr = ":1979"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return s.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

// ListenAndServe listens on the TCP network address addr and then
// calls Serve to handle requests on incoming connections.
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler ConnHandler) error {
	server := &Server{Addr: addr, ConnHandler: handler}
	return server.ListenAndServe()
}

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each.
// The service goroutines call ConnHandler to reply to them.
func (s *Server) Serve(l net.Listener) error {
	var retry uint64
	defer l.Close()

	if s.ConnHandler == nil {
		panic("gtcp: nil handler")
	}

	// set reasonable default to each component
	if s.ConnTracker == nil {
		s.ConnTracker = NewMapConnTracker()
	}
	if s.Logger == nil {
		s.Logger = DefaultLogger
	}
	if s.Retry == nil {
		s.Retry = DefaultRetry
	}
	if s.Statistics == nil {
		s.Statistics = &TrafficStatistics{}
	}

	s.listener = l

	// context per listener
	// notify goroutines executing ConnHandler that your listner got closed
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	for {
		rw, e := l.Accept()
		if e != nil {
			select {
			case <-s.getDoneChan():
				return ErrServerClosed
			default:
			}
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				delay := s.Retry.Backoff(retry)
				s.Logger.Errorf("gtcp: Accept error: %v; retrying in %v", e, delay)
				time.Sleep(delay)
				retry += 1
				continue
			}
			return e
		}
		retry = 0
		var conn Conn = NewBaseConn(rw)
		if s.NewConn != nil {
			conn = s.NewConn(conn)
		}
		s.serve(ctx, conn)
	}
}

func (s *Server) serve(ctx context.Context, conn Conn) {
	for _, limiter := range s.Limiters {
		if limiter.OnConnected(conn) == false {
			s.Logger.Errorf("gtcp: connection refused %v: by limiter %+v", conn.RemoteAddr(), limiter)
			conn.Close()
			return
		}
	}
	s.ConnTracker.AddConn(conn)
	go func() {
		defer func() {
			for _, limiter := range s.Limiters {
				limiter.OnClosed(conn)
			}
			s.ConnTracker.DelConn(conn)
			s.Statistics.AddConnStats(conn)
			if err := recover(); err != nil && err != ErrAbortHandler {
				const size = 64 << 10
				buf := make([]byte, size)
				buf = buf[:runtime.Stack(buf, false)]
				s.Logger.Errorf("gtcp: panic serving %v: %v\n%s", conn.RemoteAddr(), err, buf)
			}
			conn.Close()
		}()
		// context per connection
		ctx, cancel := context.WithCancel(ctx)
		conn.SetCancelFunc(cancel)
		defer cancel()
		s.ConnHandler(ctx, conn)
	}()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"

	"github.com/hoisie/web"
	"github.com/nfleet/via/geo"
	"github.com/nfleet/via/geotypes"
	"github.com/nfleet/via/postgeodb"

	// register this postgres driver with the SQL module
	_ "github.com/lib/pq"
)

type (
	Server struct {
		Geo              *geo.Geo
		AllowedCountries map[string]bool
		Port             int
	}
)

const (
	expiry int = 3600
)

var (
	Debug    bool
	Parallel bool
)

func Splash(ctx *web.Context) {
	ctx.ContentType("image/jpeg")
	http.ServeFile(ctx, ctx.Request, "./splash.jpg")
}

func parse_flags() {
	flag.BoolVar(&Debug, "debug", false, "toggle debugging on/off")
	flag.BoolVar(&Parallel, "par", false, "turn on parallel execution")
	flag.Parse()
}

func test_connection() {
	// redis
	fmt.Printf("loading services")

}

func Options(ctx *web.Context, route string) string {
	ctx.SetHeader("Access-Control-Allow-Origin", "*", false)
	ctx.SetHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, If-None-Match", false)
	ctx.SetHeader("Access-control-Allow-Methods", "GET, PUT, POST, DELETE", false)
	ctx.ContentType("application/json")
	return "{}"
}

func main() {
	parse_flags()

	var config geotypes.Config
	var configFile string
	var geoDB *postgeodb.GeoPostgresDB

	args := flag.Args()
	if len(args) < 1 {
		configFile = "production.json"
	} else {
		configFile = args[0]
	}

	log.Print("loading config from " + configFile + "... ")
	config, err := geo.LoadConfig(configFile)
	if err != nil {
		log.Printf("failed: %s\n", configFile, err.Error())
		return
	}

	log.Print("establishing database connection... ")
	geoDB, err = postgeodb.NewGeoPostgresDB(config)
	ifrr != nil {
		log.Println("error: " + err.Error())
		return
	}

	// Handle SIGINT and SIGKILL
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	go func() {
		for sig := range c {
			log.Printf("received %v, closing database connections and exiting...", sig)
			geoDB.DB.Close()
			os.Exit(1)
		}
	}()

	procs := runtime.NumCPU()
	runtime.GOMAXPROCS(procs)

	log.Printf("starting server, running on %d cores...", procs)

	geo := geo.NewGeo(Debug, geoDB, expiry, dataDir)
	server := Server{Geo: geo, Port: config.Port, AllowedCountries: config.AllowedCountries}

	// Basic
	web.Get("/", Splash)
	web.Get("/status", server.GetServerStatus)

	// Dmatrix
	web.Get("/dm/(.*)/result", server.GetMatrixResult)
	web.Get("/dm/(.*)$", server.GetMatrix)
	web.Post("/dm/", server.PostMatrix)
	web.Get("/spp/(.*)/result", server.GetMatrixResult)
	web.Get("/spp/(.*)$", server.GetMatrix)
	web.Post("/spp/", server.PostMatrix)

	// Path
	web.Post("/paths", server.PostCoordinatePaths)
	web.Post("/cpaths", server.PostCoordinatePaths)

	// Address/Coordinate
	web.Post("/resolve", server.PostResolve)

	web.Match("OPTIONS", "/(.*)", Options)

	web.Run(fmt.Sprintf("127.0.0.1:%d", config.Port))
}
<commit_msg>Fix typo wtf.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"

	"github.com/hoisie/web"
	"github.com/nfleet/via/geo"
	"github.com/nfleet/via/geotypes"
	"github.com/nfleet/via/postgeodb"

	// register this postgres driver with the SQL module
	_ "github.com/lib/pq"
)

type (
	Server struct {
		Geo              *geo.Geo
		AllowedCountries map[string]bool
		Port             int
	}
)

const (
	expiry int = 3600
)

var (
	Debug    bool
	Parallel bool
)

func Splash(ctx *web.Context) {
	ctx.ContentType("image/jpeg")
	http.ServeFile(ctx, ctx.Request, "./splash.jpg")
}

func parse_flags() {
	flag.BoolVar(&Debug, "debug", false, "toggle debugging on/off")
	flag.BoolVar(&Parallel, "par", false, "turn on parallel execution")
	flag.Parse()
}

func test_connection() {
	// redis
	fmt.Printf("loading services")

}

func Options(ctx *web.Context, route string) string {
	ctx.SetHeader("Access-Control-Allow-Origin", "*", false)
	ctx.SetHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, If-None-Match", false)
	ctx.SetHeader("Access-control-Allow-Methods", "GET, PUT, POST, DELETE", false)
	ctx.ContentType("application/json")
	return "{}"
}

func main() {
	parse_flags()

	var config geotypes.Config
	var configFile string
	var geoDB *postgeodb.GeoPostgresDB

	args := flag.Args()
	if len(args) < 1 {
		configFile = "production.json"
	} else {
		configFile = args[0]
	}

	log.Print("loading config from " + configFile + "... ")
	config, err := geo.LoadConfig(configFile)
	if err != nil {
		log.Printf("failed: %s\n", configFile, err.Error())
		return
	}

	log.Print("establishing database connection... ")
	geoDB, err = postgeodb.NewGeoPostgresDB(config)
	if err != nil {
		log.Println("error: " + err.Error())
		return
	}

	// Handle SIGINT and SIGKILL
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	go func() {
		for sig := range c {
			log.Printf("received %v, closing database connections and exiting...", sig)
			geoDB.DB.Close()
			os.Exit(1)
		}
	}()

	procs := runtime.NumCPU()
	runtime.GOMAXPROCS(procs)

	log.Printf("starting server, running on %d cores...", procs)

	geo := geo.NewGeo(Debug, geoDB, expiry, dataDir)
	server := Server{Geo: geo, Port: config.Port, AllowedCountries: config.AllowedCountries}

	// Basic
	web.Get("/", Splash)
	web.Get("/status", server.GetServerStatus)

	// Dmatrix
	web.Get("/dm/(.*)/result", server.GetMatrixResult)
	web.Get("/dm/(.*)$", server.GetMatrix)
	web.Post("/dm/", server.PostMatrix)
	web.Get("/spp/(.*)/result", server.GetMatrixResult)
	web.Get("/spp/(.*)$", server.GetMatrix)
	web.Post("/spp/", server.PostMatrix)

	// Path
	web.Post("/paths", server.PostCoordinatePaths)
	web.Post("/cpaths", server.PostCoordinatePaths)

	// Address/Coordinate
	web.Post("/resolve", server.PostResolve)

	web.Match("OPTIONS", "/(.*)", Options)

	web.Run(fmt.Sprintf("127.0.0.1:%d", config.Port))
}
<|endoftext|>