<commit_before>package main

// secretshare server - mediate access to Amazon S3 by secretshare client
// Copyright (C) 2016  Alexander Wauck
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"

	"github.com/codegangsta/cli"
	"github.com/waucka/secretshare/commonlib"
)

var (
	ErrIDGen   = errors.New("Failed to generate random ID!")
	ErrIDShort = errors.New("Not enough random bytes for ID!  This should never happen!")
	ErrPreSign = errors.New("Failed to generate pre-signed upload URL!")

	Version           = 2 //deploy.sh:VERSION
	DefaultConfigPath = "/etc/secretshare-server.json"
)

type serverConfig struct {
	ListenAddr string `json:"addr"`
	ListenPort int    `json:"port"`
	SecretKey  string `json:"secret_key"`
}

func generateId() (string, error) {
	idbin := make([]byte, 32)
	num_id_bytes, err := rand.Read(idbin)
	if err != nil {
		return "", ErrIDGen
	}
	if num_id_bytes < 32 {
		return "", ErrIDShort
	}
	return hex.EncodeToString(idbin), nil
}

func generateSignedURL(svc *s3.S3, id, prefix string, ttl time.Duration) (string, http.Header, error) {
	s3key := prefix + id

	putObjectInput := &s3.PutObjectInput{
		Bucket:      &commonlib.Bucket,
		Key:         &s3key,
		Expires:     aws.Time(time.Now().Add(ttl)),
		ACL:         aws.String("public-read"),
		ContentType: aws.String("application/octet-stream"),
	}
	req, _ := svc.PutObjectRequest(putObjectInput)
	return req.PresignRequest(time.Minute * 5)
}

func main() {
	app := cli.NewApp()
	app.Name = "secretshare-server"
	app.Usage = "Securely share secrets"
	app.Version = fmt.Sprintf("%d", Version)
	app.Action = runServer
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config",
			Value: "/etc/secretshare-server.json",
			Usage: "Server configuration file",
		},
	}
	app.Run(os.Args)
}

func runServer(c *cli.Context) {
	sess := session.New(&aws.Config{
		Region:      aws.String("us-west-1"),
		Credentials: credentials.NewSharedCredentials("", "default"),
	})
	svc := s3.New(sess)

	var config serverConfig
	{
		configPath := c.String("config")
		if len(configPath) == 0 {
			configPath = DefaultConfigPath
		}
		configFile, err := os.Open(configPath)
		if err != nil {
			log.Fatalf(`Failed to open config file "%s"`, configPath)
		}
		configData, err := ioutil.ReadAll(configFile)
		if err != nil {
			log.Fatalf(`Failed to read config file "%s"`, configPath)
		}
		err = json.Unmarshal(configData, &config)
		if err != nil {
			log.Fatalf(`Config file "%s" is not valid JSON`, configPath)
		}

		if len(config.ListenAddr) == 0 {
			config.ListenAddr = "0.0.0.0"
		}
	}

	r := gin.Default()
	r.GET("/version", func(c *gin.Context) {
		c.JSON(http.StatusOK, &commonlib.ServerVersionResponse{
			ServerVersion: Version,
			APIVersion:    commonlib.APIVersion,
		})
	})
	r.POST("/upload", func(c *gin.Context) {
		var requestData commonlib.UploadRequest
		ttl := time.Minute * 60 * 4
		err := c.BindJSON(&requestData)
		if err != nil {
			c.JSON(http.StatusBadRequest, &commonlib.ErrorResponse{
				Message: err.Error(),
			})
			log.Print(err.Error())
			return
		}
		if requestData.SecretKey != config.SecretKey {
			c.JSON(http.StatusUnauthorized, &commonlib.ErrorResponse{
				Message: "Incorrect secret key",
			})
			log.Print("401: client provided incorrect secret key")
			return
		}
		if requestData.TTL > 0 {
			ttl = time.Minute * time.Duration(requestData.TTL)
		}

		id, err := generateId()
		if err != nil {
			c.JSON(http.StatusInternalServerError, &commonlib.ErrorResponse{
				Message: err.Error(),
			})
			log.Print(err.Error())
			return
		}

		putURL, headers, err := generateSignedURL(svc, id, "", ttl)
		if err != nil {
			c.JSON(http.StatusInternalServerError, &commonlib.ErrorResponse{
				Message: err.Error(),
			})
			log.Print(err.Error())
			return
		}

		metaPutURL, metaHeaders, err := generateSignedURL(svc, id, "meta/", ttl)
		if err != nil {
			c.JSON(http.StatusInternalServerError, &commonlib.ErrorResponse{
				Message: err.Error(),
			})
			log.Print(err.Error())
			return
		}

		c.JSON(http.StatusOK, &commonlib.UploadResponse{
			Id:          id,
			PutURL:      putURL,
			MetaPutURL:  metaPutURL,
			Headers:     headers,
			MetaHeaders: metaHeaders,
		})
	})

	log.Printf("Listening on %s:%d\n", config.ListenAddr, config.ListenPort)
	r.Run(fmt.Sprintf("%s:%d", config.ListenAddr, config.ListenPort))
}
<commit_msg>De-hardcoded bucket region<commit_after>package main

// secretshare server - mediate access to Amazon S3 by secretshare client
// Copyright (C) 2016  Alexander Wauck
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"

	"github.com/codegangsta/cli"
	"github.com/waucka/secretshare/commonlib"
)

var (
	ErrIDGen   = errors.New("Failed to generate random ID!")
	ErrIDShort = errors.New("Not enough random bytes for ID!  This should never happen!")
	ErrPreSign = errors.New("Failed to generate pre-signed upload URL!")

	Version           = 2 //deploy.sh:VERSION
	DefaultConfigPath = "/etc/secretshare-server.json"
)

type serverConfig struct {
	ListenAddr string `json:"addr"`
	ListenPort int    `json:"port"`
	SecretKey  string `json:"secret_key"`
}

func generateId() (string, error) {
	idbin := make([]byte, 32)
	num_id_bytes, err := rand.Read(idbin)
	if err != nil {
		return "", ErrIDGen
	}
	if num_id_bytes < 32 {
		return "", ErrIDShort
	}
	return hex.EncodeToString(idbin), nil
}

func generateSignedURL(svc *s3.S3, id, prefix string, ttl time.Duration) (string, http.Header, error) {
	s3key := prefix + id

	putObjectInput := &s3.PutObjectInput{
		Bucket:      &commonlib.Bucket,
		Key:         &s3key,
		Expires:     aws.Time(time.Now().Add(ttl)),
		ACL:         aws.String("public-read"),
		ContentType: aws.String("application/octet-stream"),
	}
	req, _ := svc.PutObjectRequest(putObjectInput)
	return req.PresignRequest(time.Minute * 5)
}

func main() {
	app := cli.NewApp()
	app.Name = "secretshare-server"
	app.Usage = "Securely share secrets"
	app.Version = fmt.Sprintf("%d", Version)
	app.Action = runServer
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config",
			Value: "/etc/secretshare-server.json",
			Usage: "Server configuration file",
		},
	}
	app.Run(os.Args)
}

func runServer(c *cli.Context) {
	sess := session.New(&aws.Config{
		Region:      aws.String(commonlib.BucketRegion),
		Credentials: credentials.NewSharedCredentials("", "default"),
	})
	svc := s3.New(sess)

	var config serverConfig
	{
		configPath := c.String("config")
		if len(configPath) == 0 {
			configPath = DefaultConfigPath
		}
		configFile, err := os.Open(configPath)
		if err != nil {
			log.Fatalf(`Failed to open config file "%s"`, configPath)
		}
		configData, err := ioutil.ReadAll(configFile)
		if err != nil {
			log.Fatalf(`Failed to read config file "%s"`, configPath)
		}
		err = json.Unmarshal(configData, &config)
		if err != nil {
			log.Fatalf(`Config file "%s" is not valid JSON`, configPath)
		}

		if len(config.ListenAddr) == 0 {
			config.ListenAddr = "0.0.0.0"
		}
	}

	r := gin.Default()
	r.GET("/version", func(c *gin.Context) {
		c.JSON(http.StatusOK, &commonlib.ServerVersionResponse{
			ServerVersion: Version,
			APIVersion:    commonlib.APIVersion,
		})
	})
	r.POST("/upload", func(c *gin.Context) {
		var requestData commonlib.UploadRequest
		ttl := time.Minute * 60 * 4
		err := c.BindJSON(&requestData)
		if err != nil {
			c.JSON(http.StatusBadRequest, &commonlib.ErrorResponse{
				Message: err.Error(),
			})
			log.Print(err.Error())
			return
		}
		if requestData.SecretKey != config.SecretKey {
			c.JSON(http.StatusUnauthorized, &commonlib.ErrorResponse{
				Message: "Incorrect secret key",
			})
			log.Print("401: client provided incorrect secret key")
			return
		}
		if requestData.TTL > 0 {
			ttl = time.Minute * time.Duration(requestData.TTL)
		}

		id, err := generateId()
		if err != nil {
			c.JSON(http.StatusInternalServerError, &commonlib.ErrorResponse{
				Message: err.Error(),
			})
			log.Print(err.Error())
			return
		}

		putURL, headers, err := generateSignedURL(svc, id, "", ttl)
		if err != nil {
			c.JSON(http.StatusInternalServerError, &commonlib.ErrorResponse{
				Message: err.Error(),
			})
			log.Print(err.Error())
			return
		}

		metaPutURL, metaHeaders, err := generateSignedURL(svc, id, "meta/", ttl)
		if err != nil {
			c.JSON(http.StatusInternalServerError, &commonlib.ErrorResponse{
				Message: err.Error(),
			})
			log.Print(err.Error())
			return
		}

		c.JSON(http.StatusOK, &commonlib.UploadResponse{
			Id:          id,
			PutURL:      putURL,
			MetaPutURL:  metaPutURL,
			Headers:     headers,
			MetaHeaders: metaHeaders,
		})
	})

	log.Printf("Listening on %s:%d\n", config.ListenAddr, config.ListenPort)
	r.Run(fmt.Sprintf("%s:%d", config.ListenAddr, config.ListenPort))
}
<|endoftext|>


<commit_before>package main

import(
	"fmt"
	"math/rand"
	"time"
	//"io"
	"net/http"
	//"reflect"
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"

	//"golang.org/x/net/websocket"
)

type Card struct{
	name string
	rank uint16
	description string
}

type Deck struct{
	cards Stack
}

type Hand struct{
	cards[] Card
}

type Msg struct{
	Name string `json:"name"`
	Type string `json:"type"` // "chat", "turn", "connect", "disconnect"
	Data string `json:"data"`
}

type Room struct{
	players []Player
	I chan Msg
}

type Player struct{
	name string
	score uint8
	O chan string
}

type Server struct{
	rooms []Room
	players []Player
}

func (s *Server) handle(c *gin.Context) {
	s.wshandler(c.Writer, c.Request)
}

func (s *Server) findSuitableRoom() Room{
	fmt.Println("finding suitable room")
	return s.rooms[0] // dunno for now
}

func (s *Server) wshandler(w http.ResponseWriter, r *http.Request) {
	// so at this point we can have some fun, yeah

	// maybe take in a message at this point?
	// grab the room and grab the user id?
	// first message would be like:
	// {channel: channel, nick: myNick}
	room := s.findSuitableRoom()

	// if you have to create a room, make sure to give it an I:
	// I := make(chan string) // unbuffered channels

	player := Player{}
	s.players = append(s.players, player)

	conn, err := wsupgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println("Failed to set websocket upgrade: %+v", err)
		return
	}

	//make new channels
	O := make(chan string) // more of a basket than a tube
	player.O = O

	// assign O to player
	// assign I to room

	go func() {
		// loop over channel’s contents
		// quit only if the channel closes
		for s := range O {
			conn.WriteMessage(websocket.TextMessage, []byte(s))
		}
	}()

	go func(){
		for {
			_, msg, err := conn.ReadMessage()
			if err != nil {
				return
			}

			res := Msg{}
			json.Unmarshal(msg, &res)

			room.I <- res
		}
	}()

	// keep note: there's a limit to how many players can join a room
	// make([]Room, 4) when creating the room?
	// and make them shut when the players start playing?

	room.players = append(room.players, player)
}

// Loop does the game loop
// slap this in a goroutine?
func (r Room) Loop(){
	currentplayer := 0
	for m := range r.I{//m is a Msg
		switch m.Type{
			case "turn":
				if m.Name == r.players[currentplayer].name{
					fmt.Println("uhhhh")
				}
			case "chat":
				//chat stuf
			case "connect":
				// do we need to do this?
				// idek, its just an example
				// i intend to reuse this code...

			case "disconnect":
				//probably need to do this right?
		}
		currentplayer = (currentplayer+1)%len(r.players)
	}
}

func main() {
	var d = []Card{}
	d = append(d, Card{name:"Princess", rank: 8, description:"princess"})
	d = append(d, Card{name:"Countess", rank: 7, description:"countess"})
	d = append(d, Card{name:"King", rank: 6, description:"king"})
	d = append(d, Card{name:"Prince", rank: 5, description:"prince"})
	d = append(d, Card{name:"Prince", rank: 5, description:"prince"})
	d = append(d, Card{name:"Handmaid", rank: 4, description:"priest"})
	d = append(d, Card{name:"Handmaid", rank: 4, description:"priest"})
	d = append(d, Card{name:"Baron", rank: 3, description:"baron"})
	d = append(d, Card{name:"Baron", rank: 3, description:"baron"})
	d = append(d, Card{name:"Priest", rank: 2, description:"priest"})
	d = append(d, Card{name:"Priest", rank: 2, description:"priest"})
	d = append(d, Card{name:"Guard", rank: 1, description:"guard"})
	d = append(d, Card{name:"Guard", rank: 1, description:"guard"})
	d = append(d, Card{name:"Guard", rank: 1, description:"guard"})
	d = append(d, Card{name:"Guard", rank: 1, description:"guard"})
	d = append(d, Card{name:"Guard", rank: 1, description:"guard"})

	rand.Seed(time.Now().UTC().UnixNano())

	for i := range d {
		j := rand.Intn(i + 1)
		d[i], d[j] = d[j], d[i]
	}

	var myhand = Hand{}

	deck := Deck{}

	for i := 0; i < len(d); i++{
		deck.cards.Push(d[i])
	}

	fmt.Println(deck.cards.Peek())
	fmt.Println(myhand)
	var t = deck.cards.Pop().(Card)
	myhand.cards = append(myhand.cards, t)
	fmt.Println(deck.cards.Peek())
	fmt.Println(myhand)

	server := Server{}

	r := gin.Default()
	r.LoadHTMLFiles("index.html")

	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "index.html", nil)
	})

	r.GET("/ws", func(c *gin.Context) {
		server.wshandler(c.Writer, c.Request)
	})

	r.Run("localhost:8080")

}

var wsupgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}
<commit_msg>Update main.go<commit_after>package main

import(
	"fmt"
	"math/rand"
	"time"
	//"io"
	"net/http"
	//"reflect"
	"encoding/json"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	//"golang.org/x/net/websocket"
)

// Card is a struct that holds the information of a card type in the game
type Card struct{
	name string
	rank uint16
	description string
}

// Deck is a stack of card (with a defined order).
type Deck struct{
	cards Stack
}

// Hand is all the cards a player currently holds.
type Hand struct{
	cards[] Card
}

// Mgs is the message format passed between client and server.
type Msg struct{
	Name string `json:"name, omitempty"`
	Type string `json:"type, omitempty"` // "chat", "turn", "connect", "disconnect"
	Data string `json:"data, omitempty"`
}

// Room is a manager for a game of loveletters.
type Room struct{
	players []Player
	I chan Msg
}

// Player is a person that plays a game of loveletters.
type Player struct{
	name string
	score int
	O chan string
}

// Server holds all the information about the loveletter server.
type Server struct{
	rooms []Room
	players []Player
}

func (s *Server) handle(c *gin.Context) {
	s.wshandler(c.Writer, c.Request)
}

func (s *Server) findSuitableRoom() Room{
	fmt.Println("finding suitable room")
	return s.rooms[0] // dunno for now
}

func (s *Server) wshandler(w http.ResponseWriter, r *http.Request) {
	// so at this point we can have some fun, yeah

	// maybe take in a message at this point?
	// grab the room and grab the user id?
	// first message would be like:
	// {channel: channel, nick: myNick}
	room := s.findSuitableRoom()

	// if you have to create a room, make sure to give it an I:
	// I := make(chan string) // unbuffered channels

	player := Player{}
	s.players = append(s.players, player)

	conn, err := wsupgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println("Failed to set websocket upgrade: %+v", err)
		return
	}

	//make new channels
	O := make(chan string) // more of a basket than a tube
	player.O = O

	// assign O to player
	// assign I to room

	go func() {
		// loop over channel’s contents
		// quit only if the channel closes
		for s := range O {
			conn.WriteMessage(websocket.TextMessage, []byte(s))
		}
	}()

	go func(){
		for {
			_, msg, err := conn.ReadMessage()
			if err != nil {
				return
			}

			res := Msg{}
			json.Unmarshal(msg, &res)

			room.I <- res
		}
	}()

	// keep note: there's a limit to how many players can join a room
	// make([]Room, 4) when creating the room?
	// and make them shut when the players start playing?

	room.players = append(room.players, player)
}

// Loop does the game loop
// slap this in a goroutine?
func (r Room) Loop(){
	currentplayer := 0
	for m := range r.I{//m is a Msg
		switch m.Type{
			case "turn":
				if m.Name == r.players[currentplayer].name{
					fmt.Println("uhhhh")
				}
			case "chat":
				//chat stuf
			case "connect":
				// do we need to do this?
				// idek, its just an example
				// i intend to reuse this code...

			case "disconnect":
				//probably need to do this right?
		}
		currentplayer = (currentplayer+1)%len(r.players)
	}
}

func main() {
	d: = []Card{
		{name:"Princess", rank: 8, description:"princess"},
		{name:"Countess", rank: 7, description:"countess"},
		{name:"King", rank: 6, description:"king"},
		{name:"Prince", rank: 5, description:"prince"},
		{name:"Prince", rank: 5, description:"prince"},
		{name:"Handmaid", rank: 4, description:"priest"},
		{name:"Handmaid", rank: 4, description:"priest"},
		{name:"Baron", rank: 3, description:"baron"},
		{name:"Baron", rank: 3, description:"baron"},
		{name:"Priest", rank: 2, description:"priest"},
		{name:"Priest", rank: 2, description:"priest"},
		{name:"Guard", rank: 1, description:"guard"},
		{name:"Guard", rank: 1, description:"guard"},
		{name:"Guard", rank: 1, description:"guard"},
		{name:"Guard", rank: 1, description:"guard"},
		{name:"Guard", rank: 1, description:"guard"},
	}

	rand.Seed(time.Now().UTC().UnixNano())

	for i := range d {
		j := rand.Intn(i + 1)
		d[i], d[j] = d[j], d[i]
	}

	myhand := Hand{}

	deck := Deck{}

	for i := 0; i < len(d); i++{
		deck.cards.Push(d[i])
	}

	fmt.Println(deck.cards.Peek())
	fmt.Println(myhand)
	t := deck.cards.Pop().(Card)
	myhand.cards = append(myhand.cards, t)
	fmt.Println(deck.cards.Peek())
	fmt.Println(myhand)

	server := Server{}

	r := gin.Default()
	r.LoadHTMLFiles("index.html")

	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "index.html", nil)
	})

	r.GET("/ws", func(c *gin.Context) {
		server.wshandler(c.Writer, c.Request)
	})

	r.Run("localhost:8080")
}

var wsupgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}
<|endoftext|>


<commit_before>// Package server maps a connection to each player(who is online) so we have a comunication chanel.
package server

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"path"
	"runtime/debug"
	"sync"

	"golang.org/x/net/websocket"

	"warcluster/config"
	"warcluster/leaderboard"
	"warcluster/server/response"
)

type Server struct {
	http.Server
	listener  net.Listener
	isRunning bool
}

var (
	cfg         config.Config
	clients     *ClientPool
	leaderBoard *leaderboard.Leaderboard
	listener    net.Listener
	once        sync.Once
	empty       = struct{}{}
)

// Exports to given loaded config file into server.cfg
func ExportConfig(loadedCfg config.Config) {
	cfg = loadedCfg
}

// Create new server and setup its routes.
func NewServer(host string, port uint16) *Server {
	s := new(Server)
	s.Addr = fmt.Sprintf("%v:%v", host, port)
	s.setupRoutes()
	return s
}

// Sets http routes if this haven't been done before.
func (s *Server) setupRoutes() {
	once.Do(func() {
		http.HandleFunc("/console", consoleHandler)
		http.HandleFunc("/leaderboard/players/", leaderboardPlayersHandler)
		http.HandleFunc("/leaderboard/races/", leaderboardRacesHandler)
		http.HandleFunc("/leaderboard/races/info/", leaderboardRacesInfoHandler)
		http.HandleFunc("/search/", searchHandler)
		http.Handle("/universe", websocket.Handler(Handle))
	})
}

// Create an empty connections pool and start listening.
func (s *Server) Start() error {
	clients = NewClientPool(13)

	log.Print(fmt.Sprintf("Server is running at http://%s/", s.Addr))
	log.Print("Quit the server with Ctrl-C.")

	if err := s.ListenAndServe(); err != nil {
		log.Println(err)
		return err
	}

	return s.Stop()
}

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.o
// It's re-defined here in order to have the listener which allows us
// to stop listening and clean up before exit.
func (s *Server) ListenAndServe() error {
	var err error

	s.listener, err = net.Listen("tcp", s.Addr)
	if err != nil {
		return err
	}

	s.isRunning = true
	return s.Serve(s.listener)
}

// Stops the server.
func (s *Server) Stop() error {
	log.Println("Server is shutting down...")
	s.isRunning = false
	s.listener.Close()
	log.Println("Server has stopped.")
	return nil
}

// Returns the HTML page needed to display the debug page (server "chat" window).
func consoleHandler(response http.ResponseWriter, request *http.Request) {
	if !cfg.Server.Console {
		http.NotFound(response, request)
		return
	}

	response.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
	response.Header().Add("Pragma", "no-cache")                                   // HTTP 1.0.
	response.Header().Add("Expires", "0")                                         // Proxies
	http.ServeFile(response, request, path.Join(getStaticDir(), "index.html"))
}

// On the first received message from each connection the server will call the handler.
// Add new session to the session pool, call the login func to validate the connection and
// if the connection is valid enters "while true" state and uses ParseRequest to parse the requests.
func Handle(ws *websocket.Conn) {
	var request Request
	defer func() {
		if panicked := recover(); panicked != nil {
			log.Println(fmt.Sprintf("%s\n\nStacktrace:\n\n%s", panicked, debug.Stack()))
			return
		}
	}()
	defer ws.Close()

	client, logResponse, err := login(ws)
	if err != nil {
		log.Print("Error in server.main.handler.login:", err.Error())
		websocket.JSON.Send(ws, &logResponse)
		return
	}
	clients.Add(client)
	defer clients.Remove(client)

	clients.Send(client.Player, logResponse)

	client.Player.UpdateSpyReports()
	for {
		err := websocket.JSON.Receive(client.Conn, &request)
		if err != nil {
			clients.Send(client.Player, response.NewError(err.Error()))
			continue
		}
		request.Client = client
		action, err := ParseRequest(&request)
		if err != nil {
			log.Println("Error in server.main.handler.ParseRequest:", err.Error())
			clients.Send(client.Player, response.NewError(err.Error()))
			continue
		}

		if err := action(&request); err != nil {
			log.Println(err)
			continue
		}
	}
}

// getStaticDir return an absolute path to the static files
func getStaticDir() string {
	return path.Join(config.ConfigDir, "../static")
}
<commit_msg>Kill the connection if receiving from ws fails<commit_after>// Package server maps a connection to each player(who is online) so we have a comunication chanel.
package server

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"path"
	"runtime/debug"
	"sync"

	"golang.org/x/net/websocket"

	"warcluster/config"
	"warcluster/leaderboard"
	"warcluster/server/response"
)

type Server struct {
	http.Server
	listener  net.Listener
	isRunning bool
}

var (
	cfg         config.Config
	clients     *ClientPool
	leaderBoard *leaderboard.Leaderboard
	listener    net.Listener
	once        sync.Once
	empty       = struct{}{}
)

// Exports to given loaded config file into server.cfg
func ExportConfig(loadedCfg config.Config) {
	cfg = loadedCfg
}

// Create new server and setup its routes.
func NewServer(host string, port uint16) *Server {
	s := new(Server)
	s.Addr = fmt.Sprintf("%v:%v", host, port)
	s.setupRoutes()
	return s
}

// Sets http routes if this haven't been done before.
func (s *Server) setupRoutes() {
	once.Do(func() {
		http.HandleFunc("/console", consoleHandler)
		http.HandleFunc("/leaderboard/players/", leaderboardPlayersHandler)
		http.HandleFunc("/leaderboard/races/", leaderboardRacesHandler)
		http.HandleFunc("/leaderboard/races/info/", leaderboardRacesInfoHandler)
		http.HandleFunc("/search/", searchHandler)
		http.Handle("/universe", websocket.Handler(Handle))
	})
}

// Create an empty connections pool and start listening.
func (s *Server) Start() error {
	clients = NewClientPool(13)

	log.Print(fmt.Sprintf("Server is running at http://%s/", s.Addr))
	log.Print("Quit the server with Ctrl-C.")

	if err := s.ListenAndServe(); err != nil {
		log.Println(err)
		return err
	}

	return s.Stop()
}

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.o
// It's re-defined here in order to have the listener which allows us
// to stop listening and clean up before exit.
func (s *Server) ListenAndServe() error {
	var err error

	s.listener, err = net.Listen("tcp", s.Addr)
	if err != nil {
		return err
	}

	s.isRunning = true
	return s.Serve(s.listener)
}

// Stops the server.
func (s *Server) Stop() error {
	log.Println("Server is shutting down...")
	s.isRunning = false
	s.listener.Close()
	log.Println("Server has stopped.")
	return nil
}

// Returns the HTML page needed to display the debug page (server "chat" window).
func consoleHandler(response http.ResponseWriter, request *http.Request) {
	if !cfg.Server.Console {
		http.NotFound(response, request)
		return
	}

	response.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
	response.Header().Add("Pragma", "no-cache")                                   // HTTP 1.0.
	response.Header().Add("Expires", "0")                                         // Proxies
	http.ServeFile(response, request, path.Join(getStaticDir(), "index.html"))
}

// On the first received message from each connection the server will call the handler.
// Add new session to the session pool, call the login func to validate the connection and
// if the connection is valid enters "while true" state and uses ParseRequest to parse the requests.
func Handle(ws *websocket.Conn) {
	var request Request
	defer func() {
		if panicked := recover(); panicked != nil {
			log.Println(fmt.Sprintf("%s\n\nStacktrace:\n\n%s", panicked, debug.Stack()))
			return
		}
	}()
	defer ws.Close()

	client, logResponse, err := login(ws)
	if err != nil {
		log.Print("Error in server.main.handler.login:", err.Error())
		websocket.JSON.Send(ws, &logResponse)
		return
	}
	clients.Add(client)
	defer clients.Remove(client)

	clients.Send(client.Player, logResponse)

	client.Player.UpdateSpyReports()
	for {
		err := websocket.JSON.Receive(client.Conn, &request)
		if err != nil {
			log.Println("Error in server.main.Handler.Receive:", err.Error())
			return
		}
		request.Client = client
		action, err := ParseRequest(&request)
		if err != nil {
			log.Println("Error in server.main.Handler.ParseRequest:", err.Error())
			clients.Send(client.Player, response.NewError(err.Error()))
			continue
		}

		if err := action(&request); err != nil {
			log.Println("Error in server.main.Handler.action:", err.Error())
			clients.Send(client.Player, response.NewError(err.Error()))
			continue
		}
	}
}

// getStaticDir return an absolute path to the static files
func getStaticDir() string {
	return path.Join(config.ConfigDir, "../static")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io"
	"log"
	"net/http"

	"golang.org/x/net/websocket"
)

var debug = flag.Bool("d", false, "Enable debug mode.")

var rooms map[string]*room

type room struct {
	users []*websocket.Conn
}

func echo(ws *websocket.Conn) {
	defer ws.Close()
	if ws.Config().Origin.Host != ws.Request().Host {
		return
	}
	var m string
	var r *room

	id := ws.Request().FormValue("id")
	if id == "" {
		m = "Error: No id supplied"
		if err := websocket.Message.Send(ws, m); err != nil {
			log.Printf("%v", err)
		}
		return
	}

	if _, ok := rooms[id]; !ok {
		rooms[id] = new(room)
	}
	r = rooms[id]
	r.users = append(r.users, ws)
	i := len(r.users) - 1

	for {
		err := websocket.Message.Receive(ws, &m)
		if err == io.EOF { //User Disconnected
			r.users = append(r.users[:i], r.users[i+1:]...)
			if len(r.users) == 0 {
				delete(rooms, id)
			}
			break
		} else if err != nil {
			log.Printf("%v", err)
		}
		for _, u := range r.users {
			if err := websocket.Message.Send(u, m); err != nil {
				log.Printf("%v", err)
			}
		}
	}
}

func main() {
	flag.Parse()
	var root http.Dir
	if *debug {
		root = http.Dir("./")
	} else {
		root = http.Dir("dist")
	}

	rooms = make(map[string]*room)

	http.Handle("/", http.FileServer(root))
	http.Handle("/ws", websocket.Handler(echo))
	http.ListenAndServe(":8080", nil)
	log.Println("Server running on http://localhost:8080")
}
<commit_msg>Remove -d flag<commit_after>package main

import (
	"io"
	"log"
	"net/http"

	"golang.org/x/net/websocket"
)

var rooms map[string]*room

type room struct {
	users []*websocket.Conn
}

func echo(ws *websocket.Conn) {
	defer ws.Close()
	if ws.Config().Origin.Host != ws.Request().Host {
		return
	}
	var m string
	var r *room

	id := ws.Request().FormValue("id")
	if id == "" {
		m = "Error: No id supplied"
		if err := websocket.Message.Send(ws, m); err != nil {
			log.Printf("%v", err)
		}
		return
	}

	if _, ok := rooms[id]; !ok {
		rooms[id] = new(room)
	}
	r = rooms[id]
	r.users = append(r.users, ws)
	i := len(r.users) - 1

	for {
		err := websocket.Message.Receive(ws, &m)
		if err == io.EOF { //User Disconnected
			r.users = append(r.users[:i], r.users[i+1:]...)
			if len(r.users) == 0 {
				delete(rooms, id)
			}
			break
		} else if err != nil {
			log.Printf("%v", err)
		}
		for _, u := range r.users {
			if err := websocket.Message.Send(u, m); err != nil {
				log.Printf("%v", err)
			}
		}
	}
}

func main() {
	rooms = make(map[string]*room)

	http.Handle("/", http.FileServer(http.Dir("")))
	http.Handle("/ws", websocket.Handler(echo))
	http.ListenAndServe(":8080", nil)
	log.Println("Server running on http://localhost:8080")
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"fmt"
	"github.com/albertyw/devops-reactions-index/tumblr"
	"io/ioutil"
	"net/http"
	"os"
)

const dataURLPath = "/data.json"

var templateDir = os.Getenv("SERVER_TEMPLATES")
var indexPath = fmt.Sprintf("%s/index.htm", templateDir)
var uRLFilePaths = map[string]func() (string, error){
	"/":         readFile(indexPath),
	dataURLPath: dataURLHandler,
}
var posts []tumblr.Post

func readFile(p string) func() (string, error) {
	path := p
	return func() (string, error) {
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return "", err
		}
		html := string(data)
		return html, nil
	}
}

func fakeHandler() (string, error) {
	return "", errors.New("Called Fake Handler")
}

func getURLHandler(urlPath string) (func() (string, error), error) {
	handler, exists := uRLFilePaths[urlPath]
	if !exists {
		return fakeHandler, errors.New("")
	}
	return handler, nil
}

func dataURLHandler() (string, error) {
	return tumblr.PostsToJSON(posts), nil
}

func handler(w http.ResponseWriter, r *http.Request) {
	// Execute the template per HTTP request
	urlPath := r.URL.Path
	handler, err := getURLHandler(urlPath)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
	}
	data, err := handler()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	fmt.Fprintf(w, data)
}

// Run starts up the HTTP server
func Run(p []tumblr.Post) {
	posts = p
	address := ":" + os.Getenv("PORT")
	fmt.Println("server listening on", address)
	http.HandleFunc("/", handler)
	http.ListenAndServe(address, nil)
}
<commit_msg>Fix bug with writing server responses multiple times<commit_after>package server

import (
	"errors"
	"fmt"
	"github.com/albertyw/devops-reactions-index/tumblr"
	"io/ioutil"
	"net/http"
	"os"
)

const dataURLPath = "/data.json"

var templateDir = os.Getenv("SERVER_TEMPLATES")
var indexPath = fmt.Sprintf("%s/index.htm", templateDir)
var uRLFilePaths = map[string]func() (string, error){
	"/":         readFile(indexPath),
	dataURLPath: dataURLHandler,
}
var posts []tumblr.Post

func readFile(p string) func() (string, error) {
	path := p
	return func() (string, error) {
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return "", err
		}
		html := string(data)
		return html, nil
	}
}

func fakeHandler() (string, error) {
	return "", errors.New("Called Fake Handler")
}

func getURLHandler(urlPath string) (func() (string, error), error) {
	handler, exists := uRLFilePaths[urlPath]
	if !exists {
		return fakeHandler, errors.New("")
	}
	return handler, nil
}

func dataURLHandler() (string, error) {
	return tumblr.PostsToJSON(posts), nil
}

func handler(w http.ResponseWriter, r *http.Request) {
	// Execute the template per HTTP request
	urlPath := r.URL.Path
	handler, err := getURLHandler(urlPath)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	data, err := handler()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, data)
}

// Run starts up the HTTP server
func Run(p []tumblr.Post) {
	posts = p
	address := ":" + os.Getenv("PORT")
	fmt.Println("server listening on", address)
	http.HandleFunc("/", handler)
	http.ListenAndServe(address, nil)
}
<|endoftext|>


<commit_before>// Copyright © 2016-2019 Wei Shen <shenwei356@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

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

	"github.com/spf13/cobra"
)

// VERSION of seqkit
const VERSION = "2.0.0"

// versionCmd represents the version command
var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "print version information and check for update",
	Long: `print version information and check for update

`,
	Run: func(cmd *cobra.Command, args []string) {
		app := "seqkit"
		fmt.Printf("%s v%s\n", app, VERSION)

		if !getFlagBool(cmd, "check-update") {
			return
		}

		fmt.Println("\nChecking new version...")

		resp, err := http.Get(fmt.Sprintf("https://github.com/shenwei356/%s/releases/latest", app))
		if err != nil {
			checkError(fmt.Errorf("Network error"))
		}
		items := strings.Split(resp.Request.URL.String(), "/")
		version := ""
		if items[len(items)-1] == "" {
			version = items[len(items)-2]
		} else {
			version = items[len(items)-1]
		}
		if version == "v"+VERSION {
			fmt.Printf("You are using the latest version of %s\n", app)
		} else {
			fmt.Printf("New version available: %s %s at %s\n", app, version, resp.Request.URL.String())
		}
	},
}

func init() {
	RootCmd.AddCommand(versionCmd)

	versionCmd.Flags().BoolP("check-update", "u", false, `check update`)
}
<commit_msg>change version<commit_after>// Copyright © 2016-2019 Wei Shen <shenwei356@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

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

	"github.com/spf13/cobra"
)

// VERSION of seqkit
const VERSION = "2.1.0"

// versionCmd represents the version command
var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "print version information and check for update",
	Long: `print version information and check for update

`,
	Run: func(cmd *cobra.Command, args []string) {
		app := "seqkit"
		fmt.Printf("%s v%s\n", app, VERSION)

		if !getFlagBool(cmd, "check-update") {
			return
		}

		fmt.Println("\nChecking new version...")

		resp, err := http.Get(fmt.Sprintf("https://github.com/shenwei356/%s/releases/latest", app))
		if err != nil {
			checkError(fmt.Errorf("Network error"))
		}
		items := strings.Split(resp.Request.URL.String(), "/")
		version := ""
		if items[len(items)-1] == "" {
			version = items[len(items)-2]
		} else {
			version = items[len(items)-1]
		}
		if version == "v"+VERSION {
			fmt.Printf("You are using the latest version of %s\n", app)
		} else {
			fmt.Printf("New version available: %s %s at %s\n", app, version, resp.Request.URL.String())
		}
	},
}

func init() {
	RootCmd.AddCommand(versionCmd)

	versionCmd.Flags().BoolP("check-update", "u", false, `check update`)
}
<|endoftext|>


<commit_before>package serf

import (
	"io/ioutil"
	"log"
	"os"
	"reflect"
	"testing"
	"time"
)

func TestSnapshoter(t *testing.T) {
	td, err := ioutil.TempDir("", "serf")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.RemoveAll(td)

	clock := new(LamportClock)
	outCh := make(chan Event, 64)
	stopCh := make(chan struct{})
	logger := log.New(os.Stderr, "", log.LstdFlags)
	inCh, snap, err := NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, outCh, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Write some user events
	ue := UserEvent{
		LTime: 42,
		Name:  "bar",
	}
	inCh <- ue

	// Write some member events
	clock.Witness(100)
	meJoin := MemberEvent{
		Type: EventMemberJoin,
		Members: []Member{
			Member{
				Name: "foo",
				Addr: []byte{127, 0, 0, 1},
				Port: 5000,
			},
		},
	}
	meFail := MemberEvent{
		Type: EventMemberFailed,
		Members: []Member{
			Member{
				Name: "foo",
				Addr: []byte{127, 0, 0, 1},
				Port: 5000,
			},
		},
	}
	inCh <- meJoin
	inCh <- meFail
	inCh <- meJoin

	// Check these get passed through
	select {
	case e := <-outCh:
		if !reflect.DeepEqual(e, ue) {
			t.Fatalf("expected user event: %#v", e)
		}
	case <-time.After(50 * time.Millisecond):
		t.Fatalf("timeout")
	}

	select {
	case e := <-outCh:
		if !reflect.DeepEqual(e, meJoin) {
			t.Fatalf("expected member event: %#v", e)
		}
	case <-time.After(50 * time.Millisecond):
		t.Fatalf("timeout")
	}

	select {
	case e := <-outCh:
		if !reflect.DeepEqual(e, meFail) {
			t.Fatalf("expected member event: %#v", e)
		}
	case <-time.After(50 * time.Millisecond):
		t.Fatalf("timeout")
	}

	select {
	case e := <-outCh:
		if !reflect.DeepEqual(e, meJoin) {
			t.Fatalf("expected member event: %#v", e)
		}
	case <-time.After(50 * time.Millisecond):
		t.Fatalf("timeout")
	}

	// Close the snapshoter
	close(stopCh)
	snap.Wait()

	// Open the snapshoter
	stopCh = make(chan struct{})
	_, snap, err = NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, outCh, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Check the values
	if snap.LastClock() != 100 {
		t.Fatalf("bad clock %d", snap.LastClock())
	}
	if snap.LastEventClock() != 42 {
		t.Fatalf("bad clock %d", snap.LastEventClock())
	}

	prev := snap.AliveNodes()
	if len(prev) != 1 {
		t.Fatalf("expected alive: %#v", prev)
	}
	if prev[0].Name != "foo" {
		t.Fatalf("bad name: %#v", prev[0])
	}
	if prev[0].Addr != "127.0.0.1:5000" {
		t.Fatalf("bad addr: %#v", prev[0])
	}
}

func TestSnapshoter_forceCompact(t *testing.T) {
	td, err := ioutil.TempDir("", "serf")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.RemoveAll(td)

	clock := new(LamportClock)
	stopCh := make(chan struct{})
	logger := log.New(os.Stderr, "", log.LstdFlags)

	// Create a very low limit
	inCh, snap, err := NewSnapshotter(td+"snap", 1024,
		logger, clock, nil, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Write lots of user events
	for i := 0; i < 1024; i++ {
		ue := UserEvent{
			LTime: LamportTime(i),
		}
		inCh <- ue
	}
	time.Sleep(200 * time.Millisecond)

	// Close the snapshoter
	close(stopCh)
	snap.Wait()

	// Open the snapshoter
	stopCh = make(chan struct{})
	_, snap, err = NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, nil, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Check the values
	if snap.LastEventClock() != 1023 {
		t.Fatalf("bad clock %d", snap.LastEventClock())
	}

	close(stopCh)
	snap.Wait()
}

func TestSnapshoter_leave(t *testing.T) {
	td, err := ioutil.TempDir("", "serf")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.RemoveAll(td)

	clock := new(LamportClock)
	stopCh := make(chan struct{})
	logger := log.New(os.Stderr, "", log.LstdFlags)
	inCh, snap, err := NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, nil, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Write a user event
	ue := UserEvent{
		LTime: 42,
		Name:  "bar",
	}
	inCh <- ue

	// Write some member events
	clock.Witness(100)
	meJoin := MemberEvent{
		Type: EventMemberJoin,
		Members: []Member{
			Member{
				Name: "foo",
				Addr: []byte{127, 0, 0, 1},
				Port: 5000,
			},
		},
	}
	inCh <- meJoin

	// Leave the cluster!
	snap.Leave()

	// Close the snapshoter
	close(stopCh)
	snap.Wait()

	// Open the snapshoter
	stopCh = make(chan struct{})
	_, snap, err = NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, nil, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Check the values
	if snap.LastClock() != 0 {
		t.Fatalf("bad clock %d", snap.LastClock())
	}
	if snap.LastEventClock() != 0 {
		t.Fatalf("bad clock %d", snap.LastEventClock())
	}

	prev := snap.AliveNodes()
	if len(prev) != 0 {
		t.Fatalf("expected none alive: %#v", prev)
	}
}
<commit_msg>Even more timing allowance for travis<commit_after>package serf

import (
	"io/ioutil"
	"log"
	"os"
	"reflect"
	"testing"
	"time"
)

func TestSnapshoter(t *testing.T) {
	td, err := ioutil.TempDir("", "serf")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.RemoveAll(td)

	clock := new(LamportClock)
	outCh := make(chan Event, 64)
	stopCh := make(chan struct{})
	logger := log.New(os.Stderr, "", log.LstdFlags)
	inCh, snap, err := NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, outCh, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Write some user events
	ue := UserEvent{
		LTime: 42,
		Name:  "bar",
	}
	inCh <- ue

	// Write some member events
	clock.Witness(100)
	meJoin := MemberEvent{
		Type: EventMemberJoin,
		Members: []Member{
			Member{
				Name: "foo",
				Addr: []byte{127, 0, 0, 1},
				Port: 5000,
			},
		},
	}
	meFail := MemberEvent{
		Type: EventMemberFailed,
		Members: []Member{
			Member{
				Name: "foo",
				Addr: []byte{127, 0, 0, 1},
				Port: 5000,
			},
		},
	}
	inCh <- meJoin
	inCh <- meFail
	inCh <- meJoin

	// Check these get passed through
	select {
	case e := <-outCh:
		if !reflect.DeepEqual(e, ue) {
			t.Fatalf("expected user event: %#v", e)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("timeout")
	}

	select {
	case e := <-outCh:
		if !reflect.DeepEqual(e, meJoin) {
			t.Fatalf("expected member event: %#v", e)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("timeout")
	}

	select {
	case e := <-outCh:
		if !reflect.DeepEqual(e, meFail) {
			t.Fatalf("expected member event: %#v", e)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("timeout")
	}

	select {
	case e := <-outCh:
		if !reflect.DeepEqual(e, meJoin) {
			t.Fatalf("expected member event: %#v", e)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("timeout")
	}

	// Close the snapshoter
	close(stopCh)
	snap.Wait()

	// Open the snapshoter
	stopCh = make(chan struct{})
	_, snap, err = NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, outCh, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Check the values
	if snap.LastClock() != 100 {
		t.Fatalf("bad clock %d", snap.LastClock())
	}
	if snap.LastEventClock() != 42 {
		t.Fatalf("bad clock %d", snap.LastEventClock())
	}

	prev := snap.AliveNodes()
	if len(prev) != 1 {
		t.Fatalf("expected alive: %#v", prev)
	}
	if prev[0].Name != "foo" {
		t.Fatalf("bad name: %#v", prev[0])
	}
	if prev[0].Addr != "127.0.0.1:5000" {
		t.Fatalf("bad addr: %#v", prev[0])
	}
}

func TestSnapshoter_forceCompact(t *testing.T) {
	td, err := ioutil.TempDir("", "serf")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.RemoveAll(td)

	clock := new(LamportClock)
	stopCh := make(chan struct{})
	logger := log.New(os.Stderr, "", log.LstdFlags)

	// Create a very low limit
	inCh, snap, err := NewSnapshotter(td+"snap", 1024,
		logger, clock, nil, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Write lots of user events
	for i := 0; i < 1024; i++ {
		ue := UserEvent{
			LTime: LamportTime(i),
		}
		inCh <- ue
	}

	// Wait for drain
	for len(inCh) > 0 {
		time.Sleep(20 * time.Millisecond)
	}

	// Close the snapshoter
	close(stopCh)
	snap.Wait()

	// Open the snapshoter
	stopCh = make(chan struct{})
	_, snap, err = NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, nil, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Check the values
	if snap.LastEventClock() != 1023 {
		t.Fatalf("bad clock %d", snap.LastEventClock())
	}

	close(stopCh)
	snap.Wait()
}

func TestSnapshoter_leave(t *testing.T) {
	td, err := ioutil.TempDir("", "serf")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.RemoveAll(td)

	clock := new(LamportClock)
	stopCh := make(chan struct{})
	logger := log.New(os.Stderr, "", log.LstdFlags)
	inCh, snap, err := NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, nil, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Write a user event
	ue := UserEvent{
		LTime: 42,
		Name:  "bar",
	}
	inCh <- ue

	// Write some member events
	clock.Witness(100)
	meJoin := MemberEvent{
		Type: EventMemberJoin,
		Members: []Member{
			Member{
				Name: "foo",
				Addr: []byte{127, 0, 0, 1},
				Port: 5000,
			},
		},
	}
	inCh <- meJoin

	// Leave the cluster!
	snap.Leave()

	// Close the snapshoter
	close(stopCh)
	snap.Wait()

	// Open the snapshoter
	stopCh = make(chan struct{})
	_, snap, err = NewSnapshotter(td+"snap", snapshotSizeLimit,
		logger, clock, nil, stopCh)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Check the values
	if snap.LastClock() != 0 {
		t.Fatalf("bad clock %d", snap.LastClock())
	}
	if snap.LastEventClock() != 0 {
		t.Fatalf("bad clock %d", snap.LastEventClock())
	}

	prev := snap.AliveNodes()
	if len(prev) != 0 {
		t.Fatalf("expected none alive: %#v", prev)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"strconv"

	"github.com/coreos/go-oidc/jose"
	"github.com/coreos/go-oidc/oidc"
	"github.com/julienschmidt/httprouter"

	"github.com/coreos/dex/client"
	"github.com/coreos/dex/pkg/log"
	schema "github.com/coreos/dex/schema/workerschema"
	"github.com/coreos/dex/user"
	"github.com/coreos/dex/user/api"
	"github.com/coreos/dex/user/manager"
)

const (
	defaultMaxResults = 20
)

var (
	UsersSubTree                  = "/users"
	UsersListEndpoint             = addBasePath(UsersSubTree)
	UsersCreateEndpoint           = addBasePath(UsersSubTree)
	UsersGetEndpoint              = addBasePath(UsersSubTree + "/:id")
	UsersDisableEndpoint          = addBasePath(UsersSubTree + "/:id/disable")
	UsersResendInvitationEndpoint = addBasePath(UsersSubTree + "/:id/resend-invitation")
	AccountSubTree                = "/account"
	AccountListRefreshTokens      = addBasePath(AccountSubTree + "/:userid/refresh")
	AccountRevokeRefreshToken     = addBasePath(AccountSubTree + "/:userid/refresh/:clientid")
)

type UserMgmtServer struct {
	api         *api.UsersAPI
	jwtvFactory JWTVerifierFactory
	um          *manager.UserManager
	cir         client.ClientIdentityRepo
}

func NewUserMgmtServer(userMgmtAPI *api.UsersAPI, jwtvFactory JWTVerifierFactory, um *manager.UserManager, cir client.ClientIdentityRepo) *UserMgmtServer {
	return &UserMgmtServer{
		api:         userMgmtAPI,
		jwtvFactory: jwtvFactory,
		um:          um,
		cir:         cir,
	}
}

func (s *UserMgmtServer) HTTPHandler() http.Handler {
	r := httprouter.New()
	r.RedirectTrailingSlash = false
	r.RedirectFixedPath = false

	r.GET(UsersListEndpoint, s.authAdminUser(s.listUsers))
	r.POST(UsersCreateEndpoint, s.authAdminUser(s.createUser))
	r.POST(UsersDisableEndpoint, s.authAdminUser(s.disableUser))
	r.GET(UsersGetEndpoint, s.authAdminUser(s.getUser))
	r.POST(UsersResendInvitationEndpoint, s.authAdminUser(s.resendInvitationEmail))

	r.GET(AccountListRefreshTokens, s.authAccount(s.listClientsWithRefreshTokens))
	r.DELETE(AccountRevokeRefreshToken, s.authAccount(s.revokeRefreshTokensForClient))
	return r
}

func (s *UserMgmtServer) authAdminUser(handle authedHandle) httprouter.Handle {
	return s.authAPIHandle(handle, true)
}

func (s *UserMgmtServer) authAccount(handle authedHandle) httprouter.Handle {
	return s.authAPIHandle(handle, false)
}

// authedHandle is an HTTP handle which requires requests to be authenticated as an admin user.
type authedHandle func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds)

// authAPIHandle is a middleware function with authenticates an HTTP request before passing
// it along to the authedHandle.
//
// The authorization checks for an ID token bearer token in the request header, requiring the
// audience (aud claim) be a client ID of an admin client.
//
// If requiresAdmin is true, the subject identifier (sub claim) of the ID token provided must be
// that of an admin user.
func (s *UserMgmtServer) authAPIHandle(handle authedHandle, requiresAdmin bool) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		creds, err := s.getCreds(r)
		if err != nil {
			s.writeError(w, err)
			return
		}
		if creds.User.Disabled || (requiresAdmin && !creds.User.Admin) {
			s.writeError(w, api.ErrorUnauthorized)
			return
		}
		handle(w, r, ps, creds)
	}
}

func (s *UserMgmtServer) listUsers(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	nextPageToken := r.URL.Query().Get("nextPageToken")

	maxResults, err := intFromQuery(r.URL.Query(), "maxResults", defaultMaxResults)
	if err != nil {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "maxResults must be an integer"))
		return
	}

	users, nextPageToken, err := s.api.ListUsers(creds, maxResults, nextPageToken)
	if err != nil {
		s.writeError(w, err)
		return
	}

	usersResponse := schema.UsersResponse{
		Users:         users,
		NextPageToken: nextPageToken,
	}
	writeResponseWithBody(w, http.StatusOK, usersResponse)
}

func (s *UserMgmtServer) getUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	id := ps.ByName("id")
	if id == "" {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "id is required"))
		return
	}

	usr, err := s.api.GetUser(creds, id)
	if err != nil {
		s.writeError(w, err)
		return
	}
	userResponse := schema.UserResponse{
		User: &usr,
	}
	writeResponseWithBody(w, http.StatusOK, userResponse)
}

func (s *UserMgmtServer) createUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	createReq := schema.UserCreateRequest{}
	if err := json.NewDecoder(r.Body).Decode(&createReq); err != nil {
		writeInvalidRequest(w, "cannot parse JSON body")
		return
	}

	redirURL, err := url.Parse(createReq.RedirectURL)
	if err != nil {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "redirectURL must be a valid URL"))
		return
	}

	createdResponse, err := s.api.CreateUser(creds, *createReq.User, *redirURL)
	if err != nil {
		s.writeError(w, err)
		return
	}

	writeResponseWithBody(w, http.StatusOK, createdResponse)
}

func (s *UserMgmtServer) disableUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	id := ps.ByName("id")
	if id == "" {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "id is required"))
		return
	}

	disableReq := schema.UserDisableRequest{}
	if err := json.NewDecoder(r.Body).Decode(&disableReq); err != nil {
		writeInvalidRequest(w, "cannot parse JSON body")
	}

	resp, err := s.api.DisableUser(creds, id, disableReq.Disable)
	if err != nil {
		s.writeError(w, err)
		return
	}

	writeResponseWithBody(w, http.StatusOK, resp)
}

func (s *UserMgmtServer) resendInvitationEmail(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	id := ps.ByName("id")
	if id == "" {
		writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "id is required"))
		return
	}
	resendEmailInvitationReq := schema.ResendEmailInvitationRequest{}
	if err := json.NewDecoder(r.Body).Decode(&resendEmailInvitationReq); err != nil {
		writeInvalidRequest(w, "cannot parse JSON body")
		return
	}

	redirURL, err := url.Parse(resendEmailInvitationReq.RedirectURL)
	if err != nil {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "redirectURL must be a valid URL"))
		return
	}

	resendEmailInvitationResponse, err := s.api.ResendEmailInvitation(creds, id, *redirURL)
	if err != nil {
		s.writeError(w, err)
		return
	}

	writeResponseWithBody(w, http.StatusOK, resendEmailInvitationResponse)
}

func (s *UserMgmtServer) listClientsWithRefreshTokens(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	clients, err := s.api.ListClientsWithRefreshTokens(creds, ps.ByName("userid"))
	if err != nil {
		s.writeError(w, err)
		return
	}
	writeResponseWithBody(w, http.StatusOK, schema.RefreshClientList{Clients: clients})
}

func (s *UserMgmtServer) revokeRefreshTokensForClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	if err := s.api.RevokeRefreshTokensForClient(creds, ps.ByName("userid"), ps.ByName("clientid")); err != nil {
		s.writeError(w, err)
		return
	}
	w.WriteHeader(http.StatusOK) // NOTE (ericchiang): http.StatusNoContent or return an empty JSON object?
}

func (s *UserMgmtServer) writeError(w http.ResponseWriter, err error) {
	log.Errorf("Error calling user management API: %v: ", err)
	if apiErr, ok := err.(api.Error); ok {
		writeAPIError(w, apiErr.Code, newAPIError(apiErr.Type, apiErr.Desc))
		return
	}

	writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, err.Error()))
}

func (s *UserMgmtServer) getCreds(r *http.Request) (api.Creds, error) {
	token, err := oidc.ExtractBearerToken(r)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	jwt, err := jose.ParseJWT(token)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	claims, err := jwt.Claims()
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	clientID, ok, err := claims.StringClaim("aud")
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if !ok || clientID == "" {
		return api.Creds{}, errors.New("no aud(client ID) claim")
	}

	verifier := s.jwtvFactory(clientID)
	if err := verifier.Verify(jwt); err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	sub, ok, err := claims.StringClaim("sub")
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if !ok || sub == "" {
		return api.Creds{}, api.ErrorUnauthorized
	}

	usr, err := s.um.Get(sub)
	if err != nil {
		if err == user.ErrorNotFound {
			return api.Creds{}, api.ErrorUnauthorized
		}
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}

	isAdmin, err := s.cir.IsDexAdmin(clientID)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if !isAdmin {
		return api.Creds{}, api.ErrorForbidden
	}

	return api.Creds{
		ClientID: clientID,
		User:     usr,
	}, nil
}

func intFromQuery(ps url.Values, name string, defaultVal int) (int, error) {
	s := ps.Get(name)
	if s == "" {
		return defaultVal, nil
	}
	return strconv.Atoi(s)
}
<commit_msg>bugfix: make getCreds work for non-admins (#396)<commit_after>package server

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"strconv"

	"github.com/coreos/go-oidc/jose"
	"github.com/coreos/go-oidc/oidc"
	"github.com/julienschmidt/httprouter"

	"github.com/coreos/dex/client"
	"github.com/coreos/dex/pkg/log"
	schema "github.com/coreos/dex/schema/workerschema"
	"github.com/coreos/dex/user"
	"github.com/coreos/dex/user/api"
	"github.com/coreos/dex/user/manager"
)

const (
	defaultMaxResults = 20
)

var (
	UsersSubTree                  = "/users"
	UsersListEndpoint             = addBasePath(UsersSubTree)
	UsersCreateEndpoint           = addBasePath(UsersSubTree)
	UsersGetEndpoint              = addBasePath(UsersSubTree + "/:id")
	UsersDisableEndpoint          = addBasePath(UsersSubTree + "/:id/disable")
	UsersResendInvitationEndpoint = addBasePath(UsersSubTree + "/:id/resend-invitation")
	AccountSubTree                = "/account"
	AccountListRefreshTokens      = addBasePath(AccountSubTree + "/:userid/refresh")
	AccountRevokeRefreshToken     = addBasePath(AccountSubTree + "/:userid/refresh/:clientid")
)

type UserMgmtServer struct {
	api         *api.UsersAPI
	jwtvFactory JWTVerifierFactory
	um          *manager.UserManager
	cir         client.ClientIdentityRepo
}

func NewUserMgmtServer(userMgmtAPI *api.UsersAPI, jwtvFactory JWTVerifierFactory, um *manager.UserManager, cir client.ClientIdentityRepo) *UserMgmtServer {
	return &UserMgmtServer{
		api:         userMgmtAPI,
		jwtvFactory: jwtvFactory,
		um:          um,
		cir:         cir,
	}
}

func (s *UserMgmtServer) HTTPHandler() http.Handler {
	r := httprouter.New()
	r.RedirectTrailingSlash = false
	r.RedirectFixedPath = false

	r.GET(UsersListEndpoint, s.authAdminUser(s.listUsers))
	r.POST(UsersCreateEndpoint, s.authAdminUser(s.createUser))
	r.POST(UsersDisableEndpoint, s.authAdminUser(s.disableUser))
	r.GET(UsersGetEndpoint, s.authAdminUser(s.getUser))
	r.POST(UsersResendInvitationEndpoint, s.authAdminUser(s.resendInvitationEmail))

	r.GET(AccountListRefreshTokens, s.authAccount(s.listClientsWithRefreshTokens))
	r.DELETE(AccountRevokeRefreshToken, s.authAccount(s.revokeRefreshTokensForClient))
	return r
}

func (s *UserMgmtServer) authAdminUser(handle authedHandle) httprouter.Handle {
	return s.authAPIHandle(handle, true)
}

func (s *UserMgmtServer) authAccount(handle authedHandle) httprouter.Handle {
	return s.authAPIHandle(handle, false)
}

// authedHandle is an HTTP handle which requires requests to be authenticated as an admin user.
type authedHandle func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds)

// authAPIHandle is a middleware function with authenticates an HTTP request before passing
// it along to the authedHandle.
//
// The authorization checks for an ID token bearer token in the request header, requiring the
// audience (aud claim) be a client ID of an admin client.
//
// If requiresAdmin is true, the subject identifier (sub claim) of the ID token provided must be
// that of an admin user.
func (s *UserMgmtServer) authAPIHandle(handle authedHandle, requiresAdmin bool) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		creds, err := s.getCreds(r, requiresAdmin)
		if err != nil {
			s.writeError(w, err)
			return
		}
		if creds.User.Disabled || (requiresAdmin && !creds.User.Admin) {
			s.writeError(w, api.ErrorUnauthorized)
			return
		}
		handle(w, r, ps, creds)
	}
}

func (s *UserMgmtServer) listUsers(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	nextPageToken := r.URL.Query().Get("nextPageToken")

	maxResults, err := intFromQuery(r.URL.Query(), "maxResults", defaultMaxResults)
	if err != nil {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "maxResults must be an integer"))
		return
	}

	users, nextPageToken, err := s.api.ListUsers(creds, maxResults, nextPageToken)
	if err != nil {
		s.writeError(w, err)
		return
	}

	usersResponse := schema.UsersResponse{
		Users:         users,
		NextPageToken: nextPageToken,
	}
	writeResponseWithBody(w, http.StatusOK, usersResponse)
}

func (s *UserMgmtServer) getUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	id := ps.ByName("id")
	if id == "" {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "id is required"))
		return
	}

	usr, err := s.api.GetUser(creds, id)
	if err != nil {
		s.writeError(w, err)
		return
	}
	userResponse := schema.UserResponse{
		User: &usr,
	}
	writeResponseWithBody(w, http.StatusOK, userResponse)
}

func (s *UserMgmtServer) createUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	createReq := schema.UserCreateRequest{}
	if err := json.NewDecoder(r.Body).Decode(&createReq); err != nil {
		writeInvalidRequest(w, "cannot parse JSON body")
		return
	}

	redirURL, err := url.Parse(createReq.RedirectURL)
	if err != nil {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "redirectURL must be a valid URL"))
		return
	}

	createdResponse, err := s.api.CreateUser(creds, *createReq.User, *redirURL)
	if err != nil {
		s.writeError(w, err)
		return
	}

	writeResponseWithBody(w, http.StatusOK, createdResponse)
}

func (s *UserMgmtServer) disableUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	id := ps.ByName("id")
	if id == "" {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "id is required"))
		return
	}

	disableReq := schema.UserDisableRequest{}
	if err := json.NewDecoder(r.Body).Decode(&disableReq); err != nil {
		writeInvalidRequest(w, "cannot parse JSON body")
	}

	resp, err := s.api.DisableUser(creds, id, disableReq.Disable)
	if err != nil {
		s.writeError(w, err)
		return
	}

	writeResponseWithBody(w, http.StatusOK, resp)
}

func (s *UserMgmtServer) resendInvitationEmail(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	id := ps.ByName("id")
	if id == "" {
		writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "id is required"))
		return
	}
	resendEmailInvitationReq := schema.ResendEmailInvitationRequest{}
	if err := json.NewDecoder(r.Body).Decode(&resendEmailInvitationReq); err != nil {
		writeInvalidRequest(w, "cannot parse JSON body")
		return
	}

	redirURL, err := url.Parse(resendEmailInvitationReq.RedirectURL)
	if err != nil {
		writeAPIError(w, http.StatusBadRequest,
			newAPIError(errorInvalidRequest, "redirectURL must be a valid URL"))
		return
	}

	resendEmailInvitationResponse, err := s.api.ResendEmailInvitation(creds, id, *redirURL)
	if err != nil {
		s.writeError(w, err)
		return
	}

	writeResponseWithBody(w, http.StatusOK, resendEmailInvitationResponse)
}

func (s *UserMgmtServer) listClientsWithRefreshTokens(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	clients, err := s.api.ListClientsWithRefreshTokens(creds, ps.ByName("userid"))
	if err != nil {
		s.writeError(w, err)
		return
	}
	writeResponseWithBody(w, http.StatusOK, schema.RefreshClientList{Clients: clients})
}

func (s *UserMgmtServer) revokeRefreshTokensForClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
	if err := s.api.RevokeRefreshTokensForClient(creds, ps.ByName("userid"), ps.ByName("clientid")); err != nil {
		s.writeError(w, err)
		return
	}
	w.WriteHeader(http.StatusOK) // NOTE (ericchiang): http.StatusNoContent or return an empty JSON object?
}

func (s *UserMgmtServer) writeError(w http.ResponseWriter, err error) {
	log.Errorf("Error calling user management API: %v: ", err)
	if apiErr, ok := err.(api.Error); ok {
		writeAPIError(w, apiErr.Code, newAPIError(apiErr.Type, apiErr.Desc))
		return
	}

	writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, err.Error()))
}

func (s *UserMgmtServer) getCreds(r *http.Request, requiresAdmin bool) (api.Creds, error) {
	token, err := oidc.ExtractBearerToken(r)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	jwt, err := jose.ParseJWT(token)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	claims, err := jwt.Claims()
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	clientID, ok, err := claims.StringClaim("aud")
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if !ok || clientID == "" {
		return api.Creds{}, errors.New("no aud(client ID) claim")
	}

	verifier := s.jwtvFactory(clientID)
	if err := verifier.Verify(jwt); err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	sub, ok, err := claims.StringClaim("sub")
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if !ok || sub == "" {
		return api.Creds{}, api.ErrorUnauthorized
	}

	usr, err := s.um.Get(sub)
	if err != nil {
		if err == user.ErrorNotFound {
			return api.Creds{}, api.ErrorUnauthorized
		}
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}

	isAdmin, err := s.cir.IsDexAdmin(clientID)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if requiresAdmin && !isAdmin {
		return api.Creds{}, api.ErrorForbidden
	}

	return api.Creds{
		ClientID: clientID,
		User:     usr,
	}, nil
}

func intFromQuery(ps url.Values, name string, defaultVal int) (int, error) {
	s := ps.Get(name)
	if s == "" {
		return defaultVal, nil
	}
	return strconv.Atoi(s)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/gorilla/websocket"
	lige "github.com/lucibus/lige/output"

	. "github.com/smartystreets/goconvey/convey"
)

func shouldSend(conn *websocket.Conn, message []byte) {
	err := conn.WriteMessage(websocket.TextMessage, message)
	So(err, ShouldBeNil)
}

func shouldGet(conn *websocket.Conn, message []byte) {
	messageType, p, err := conn.ReadMessage()
	So(err, ShouldBeNil)
	So(messageType, ShouldEqual, websocket.TextMessage)
	So(p, ShouldResemble, message)
}

type testOutputDevice struct {
	output lige.State
}

func (od *testOutputDevice) Set(s lige.State) error {
	od.output = s
	return nil
}

func TestServer(t *testing.T) {
	Convey("when i start the server", t, func() {
		port := 9001
		od := testOutputDevice{}
		cancelF, err := CreateServer(port, &od)
		So(err, ShouldBeNil)
		url := fmt.Sprintf("ws://localhost:%v/", port)
		d := websocket.Dialer{}
		Convey("and connect", func() {
			conn, _, err := d.Dial(url, http.Header{})
			So(err, ShouldBeNil)
			Convey("it should return a default state", func() {
				shouldGet(conn, []byte(`{}`))

				Convey("it should take an updated state", func() {
					newState := []byte(`
	                    {
	                        "live": {
	                            "level": 1,
	                            "systems": [{
	                                "type": "filter",
	                                "level": 1,
	                                "specifiers": {
	                                    "address": 1
	                                }
	                            }]
	                        }
	                    }
					`)
					shouldSend(conn, newState)

					Convey("and it should output the new state", func() {
						time.Sleep(time.Second / 2)
						So(od.output, ShouldResemble, lige.State{1: 255})
					})

					Convey("and reconnect", func() {
						conn.Close()
						conn, _, err = d.Dial(url, http.Header{})
						So(err, ShouldBeNil)

						Convey("it should return the same state", func() {
							shouldGet(conn, newState)
						})
					})
					Convey("and connect with another client", func() {
						conn2, _, err := d.Dial(url, http.Header{})
						So(err, ShouldBeNil)
						Convey("it should initally recieve the state set by the first", func() {
							shouldGet(conn2, newState)
							Convey("and then when the first changes the state, it should get it", func() {
								secondNewState := []byte(`{"test2": "hi"}`)
								shouldSend(conn, secondNewState)
								shouldGet(conn2, secondNewState)
								Convey("and vice versa", func() {
									thirdNewState := []byte(`{"test3": "hi"}`)
									shouldSend(conn2, thirdNewState)
									shouldGet(conn, thirdNewState)
								})

							})
							conn.WriteMessage(websocket.TextMessage, newState)
						})
						Reset(func() {
							So(conn2.Close(), ShouldBeNil)
						})
					})
				})
			})

			Convey("output should be blank", func() {
				So(od.output, ShouldResemble, lige.State{})
			})
			Reset(func() {
				So(conn.Close(), ShouldBeNil)
			})

		})

		Reset(func() {
			cancelF()
			Println("killing server")

			time.Sleep(time.Millisecond)
		})

	})

}
<commit_msg>add benchmark<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"testing"
	"time"

	"github.com/gorilla/websocket"
	lige "github.com/lucibus/lige/output"

	. "github.com/smartystreets/goconvey/convey"
)

var stateOneDimmer = []byte(`
	{
		"live": {
			"level": 1,
			"systems": [{
				"type": "filter",
				"level": 1,
				"specifiers": {
					"address": 1
				}
			}]
		}
	}
`)

var stateNoDimmers = []byte(`{}`)

func shouldSend(conn *websocket.Conn, message []byte) {
	err := conn.WriteMessage(websocket.TextMessage, message)
	So(err, ShouldBeNil)
}

func shouldGet(conn *websocket.Conn, message []byte) {
	messageType, p, err := conn.ReadMessage()
	So(err, ShouldBeNil)
	So(messageType, ShouldEqual, websocket.TextMessage)
	So(p, ShouldResemble, message)
}

type testOutputDevice struct {
	output lige.State
}

func (od *testOutputDevice) Set(s lige.State) error {
	od.output = s
	return nil
}

func TestServer(t *testing.T) {
	Convey("when i start the server", t, func() {
		port := 9001
		od := testOutputDevice{}
		cancelF, err := CreateServer(port, &od)
		So(err, ShouldBeNil)
		url := fmt.Sprintf("ws://localhost:%v/", port)
		d := websocket.Dialer{}
		Convey("and connect", func() {
			conn, _, err := d.Dial(url, http.Header{})
			So(err, ShouldBeNil)
			Convey("it should return a default state", func() {
				shouldGet(conn, stateNoDimmers)

				Convey("it should take an updated state", func() {
					newState := stateOneDimmer
					shouldSend(conn, newState)

					Convey("and it should output the new state", func() {
						time.Sleep(time.Second / 2)
						So(od.output, ShouldResemble, lige.State{1: 255})
					})

					Convey("and reconnect", func() {
						conn.Close()
						conn, _, err = d.Dial(url, http.Header{})
						So(err, ShouldBeNil)

						Convey("it should return the same state", func() {
							shouldGet(conn, newState)
						})
					})
					Convey("and connect with another client", func() {
						conn2, _, err := d.Dial(url, http.Header{})
						So(err, ShouldBeNil)
						Convey("it should initally recieve the state set by the first", func() {
							shouldGet(conn2, newState)
							Convey("and then when the first changes the state, it should get it", func() {
								secondNewState := []byte(`{"test2": "hi"}`)
								shouldSend(conn, secondNewState)
								shouldGet(conn2, secondNewState)
								Convey("and vice versa", func() {
									thirdNewState := []byte(`{"test3": "hi"}`)
									shouldSend(conn2, thirdNewState)
									shouldGet(conn, thirdNewState)
								})

							})
							conn.WriteMessage(websocket.TextMessage, newState)
						})
						Reset(func() {
							So(conn2.Close(), ShouldBeNil)
						})
					})
				})
			})

			Convey("output should be blank", func() {
				So(od.output, ShouldResemble, lige.State{})
			})
			Reset(func() {
				So(conn.Close(), ShouldBeNil)
			})

		})

		Reset(func() {
			cancelF()
			time.Sleep(time.Millisecond)
		})

	})

}

func BenchmarkServer(b *testing.B) {
	port := 9001
	od := testOutputDevice{}
	cancelF, err := CreateServer(port, &od)
	if err != nil {
		log.Panicln("error in create server", err)
	}
	url := fmt.Sprintf("ws://localhost:%v/", port)
	d := websocket.Dialer{}
	conn, _, err := d.Dial(url, http.Header{})
	if err != nil {
		log.Panicln("error in dial", err)
	}
	conn.ReadMessage()

	toggleSendOneDimmer := true
	var message []byte
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		prevLength := len(od.output)
		if toggleSendOneDimmer {
			message = stateOneDimmer
		} else {
			message = stateNoDimmers
		}
		toggleSendOneDimmer = !toggleSendOneDimmer
		b.StartTimer()
		conn.WriteMessage(websocket.TextMessage, message)
		for {
			time.Sleep(time.Nanosecond)
			if len(od.output) != prevLength {
				break
			}
		}
	}
	b.StopTimer()
	conn.Close()
	cancelF()
	time.Sleep(time.Nanosecond)
}
<|endoftext|>


<commit_before>package radius_test

import (
	"context"
	"net"
	"testing"
	"time"

	"layeh.com/radius"
	. "layeh.com/radius/rfc2865"
)

func Test_PacketServer_basic(t *testing.T) {
	addr, err := net.ResolveUDPAddr("udp", "localhost:0")
	if err != nil {
		t.Fatal(err)
	}
	pc, err := net.ListenUDP("udp", addr)
	if err != nil {
		t.Fatal(err)
	}

	secret := []byte("123456790")

	server := radius.PacketServer{
		SecretSource: radius.StaticSecretSource(secret),
		Handler: radius.HandlerFunc(func(w radius.ResponseWriter, r *radius.Request) {
			username := UserName_GetString(r.Packet)
			if username == "tim" {
				w.Write(r.Response(radius.CodeAccessAccept))
			} else {
				w.Write(r.Response(radius.CodeAccessReject))
			}
		}),
	}

	go func() {
		defer server.Shutdown(context.Background())

		packet := radius.New(radius.CodeAccessRequest, secret)
		UserName_SetString(packet, "tim")
		client := radius.Client{
			Retry: time.Millisecond * 50,
		}
		response, err := client.Exchange(context.Background(), packet, pc.LocalAddr().String())
		if err != nil {
			t.Fatal(err)
		}
		if response.Code != radius.CodeAccessAccept {
			t.Fatalf("expected CodeAccessAccept, got %s\n", response.Code)
		}
	}()

	if err := server.Serve(pc); err != nil {
		t.Fatal(err)
	}
}
<commit_msg>fix calling t.Fatal from wrong go routine<commit_after>package radius_test

import (
	"context"
	"fmt"
	"net"
	"testing"
	"time"

	"layeh.com/radius"
	. "layeh.com/radius/rfc2865"
)

func TestPacketServer_basic(t *testing.T) {
	addr, err := net.ResolveUDPAddr("udp", "localhost:0")
	if err != nil {
		t.Fatal(err)
	}
	pc, err := net.ListenUDP("udp", addr)
	if err != nil {
		t.Fatal(err)
	}

	secret := []byte("123456790")

	server := radius.PacketServer{
		SecretSource: radius.StaticSecretSource(secret),
		Handler: radius.HandlerFunc(func(w radius.ResponseWriter, r *radius.Request) {
			username := UserName_GetString(r.Packet)
			if username == "tim" {
				w.Write(r.Response(radius.CodeAccessAccept))
			} else {
				w.Write(r.Response(radius.CodeAccessReject))
			}
		}),
	}

	var clientErr error
	go func() {
		defer server.Shutdown(context.Background())

		packet := radius.New(radius.CodeAccessRequest, secret)
		UserName_SetString(packet, "tim")
		client := radius.Client{
			Retry: time.Millisecond * 50,
		}
		response, err := client.Exchange(context.Background(), packet, pc.LocalAddr().String())
		if err != nil {
			clientErr = err
			return
		}
		if response.Code != radius.CodeAccessAccept {
			clientErr = fmt.Errorf("expected CodeAccessAccept, got %s\n", response.Code)
		}
	}()

	if err := server.Serve(pc); err != nil {
		t.Fatal(err)
	}

	server.Shutdown(context.Background())

	if clientErr != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/fhs/gompd/mpd"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
	"time"
)

func TestLibrary(t *testing.T) {
	m := new(MockMusic)
	api := apiHandler{m}
	ts := httptest.NewServer(http.HandlerFunc(api.library))
	defer ts.Close()
	t.Run("no parameter", func(t *testing.T) {
		m.LibraryRet1 = []mpd.Attrs{mpd.Attrs{"foo": "bar"}}
		m.LibraryRet2 = time.Unix(0, 0)
		res, err := http.Get(ts.URL)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		st := struct {
			Data   []mpd.Attrs `json:"data"`
			Errors error       `json:"errors"`
		}{[]mpd.Attrs{}, nil}
		json.Unmarshal(body, &st)
		if !reflect.DeepEqual(m.LibraryRet1, st.Data) {
			t.Errorf("unexpected body: %s", body)
		}
		if st.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
	t.Run("If-Modified-Since", func(t *testing.T) {
		m.LibraryRet1 = []mpd.Attrs{mpd.Attrs{"foo": "bar"}}
		m.LibraryRet2 = time.Unix(60, 0)
		req, _ := http.NewRequest("GET", ts.URL, nil)
		req.Header.Set("If-Modified-Since", m.LibraryRet2.Format(http.TimeFormat))
		client := new(http.Client)
		res, err := client.Do(req)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 304 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})
}
func TestPlaylist(t *testing.T) {
	m := new(MockMusic)
	api := apiHandler{m}
	ts := httptest.NewServer(http.HandlerFunc(api.playlist))
	defer ts.Close()
	t.Run("no parameter", func(t *testing.T) {
		m.PlaylistRet1 = []mpd.Attrs{mpd.Attrs{"foo": "bar"}}
		m.PlaylistRet2 = time.Unix(0, 0)
		res, err := http.Get(ts.URL)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		st := struct {
			Data   []mpd.Attrs `json:"data"`
			Errors error       `json:"errors"`
		}{[]mpd.Attrs{}, nil}
		json.Unmarshal(body, &st)
		if !reflect.DeepEqual(m.PlaylistRet1, st.Data) {
			t.Errorf("unexpected body: %s", body)
		}
		if st.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
	t.Run("If-Modified-Since", func(t *testing.T) {
		m.PlaylistRet1 = []mpd.Attrs{mpd.Attrs{"foo": "bar"}}
		m.PlaylistRet2 = time.Unix(60, 0)
		req, _ := http.NewRequest("GET", ts.URL, nil)
		req.Header.Set("If-Modified-Since", m.PlaylistRet2.Format(http.TimeFormat))
		client := new(http.Client)
		res, err := client.Do(req)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 304 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})
}
func TestCurrent(t *testing.T) {
	m := new(MockMusic)
	api := apiHandler{m}
	ts := httptest.NewServer(http.HandlerFunc(api.current))
	defer ts.Close()
	t.Run("no parameter", func(t *testing.T) {
		m.CurrentRet1 = mpd.Attrs{"foo": "bar"}
		m.CurrentRet2 = time.Unix(0, 0)
		res, err := http.Get(ts.URL)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		st := struct {
			Data   mpd.Attrs `json:"data"`
			Errors error     `json:"errors"`
		}{mpd.Attrs{}, nil}
		json.Unmarshal(body, &st)
		if !reflect.DeepEqual(m.CurrentRet1, st.Data) {
			t.Errorf("unexpected body: %s", body)
		}
		if st.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
	t.Run("If-Modified-Since", func(t *testing.T) {
		m.CurrentRet1 = mpd.Attrs{"foo": "bar"}
		m.CurrentRet2 = time.Unix(60, 0)
		req, _ := http.NewRequest("GET", ts.URL, nil)
		req.Header.Set("If-Modified-Since", m.CurrentRet2.Format(http.TimeFormat))
		client := new(http.Client)
		res, err := client.Do(req)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 304 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})
}
func TestControl(t *testing.T) {
	m := new(MockMusic)
	api := apiHandler{m}
	ts := httptest.NewServer(http.HandlerFunc(api.control))
	defer ts.Close()
	t.Run("no parameter", func(t *testing.T) {
		s := convStatus(mpd.Attrs{}, mpd.Attrs{})
		s.LastModified = 0
		m.StatusRet1 = s
		m.StatusRet2 = time.Unix(0, 0)
		res, err := http.Get(ts.URL)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		st := struct {
			Data   PlayerStatus `json:"data"`
			Errors error        `json:"errors"`
		}{PlayerStatus{}, nil}
		json.Unmarshal(body, &st)
		if !reflect.DeepEqual(s, st.Data) {
			t.Errorf("unexpected body: %s", body)
		}
		if st.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
	t.Run("If-Modified-Since", func(t *testing.T) {
		s := convStatus(mpd.Attrs{}, mpd.Attrs{})
		s.LastModified = 60
		m.StatusRet1 = s
		m.StatusRet2 = time.Unix(60, 0)
		req, _ := http.NewRequest("GET", ts.URL, nil)
		req.Header.Set("If-Modified-Since", m.StatusRet2.Format(http.TimeFormat))
		client := new(http.Client)
		res, err := client.Do(req)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 304 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})
	t.Run("action=play", func(t *testing.T) {
		res, err := http.Get(ts.URL + "?action=play")
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})

	t.Run("action=pause", func(t *testing.T) {
		res, err := http.Get(ts.URL + "?action=pause")
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})

	t.Run("action=next", func(t *testing.T) {
		res, err := http.Get(ts.URL + "?action=next")
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})

	t.Run("action=prev", func(t *testing.T) {
		res, err := http.Get(ts.URL + "?action=prev")
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})

}

type MockMusic struct {
	PlayErr         error
	PauseErr        error
	NextErr         error
	PrevErr         error
	PlaylistRet1    []mpd.Attrs
	PlaylistRet2    time.Time
	LibraryRet1     []mpd.Attrs
	LibraryRet2     time.Time
	CurrentRet1     mpd.Attrs
	CurrentRet2     time.Time
	CommentsRet1    mpd.Attrs
	CommentsRet2    time.Time
	StatusRet1      PlayerStatus
	StatusRet2      time.Time
	SortPlaylistErr error
}

func (p *MockMusic) Play() error {
	return p.PlayErr
}

func (p *MockMusic) Pause() error {
	return p.PauseErr
}
func (p *MockMusic) Next() error {
	return p.NextErr
}
func (p *MockMusic) Prev() error {
	return p.PrevErr
}
func (p *MockMusic) Comments() (mpd.Attrs, time.Time) {
	return p.CommentsRet1, p.CommentsRet2
}
func (p *MockMusic) Current() (mpd.Attrs, time.Time) {
	return p.CurrentRet1, p.CurrentRet2
}
func (p *MockMusic) Library() ([]mpd.Attrs, time.Time) {
	return p.LibraryRet1, p.LibraryRet2
}
func (p *MockMusic) Playlist() ([]mpd.Attrs, time.Time) {
	return p.PlaylistRet1, p.PlaylistRet2
}
func (p *MockMusic) Status() (PlayerStatus, time.Time) {
	return p.StatusRet1, p.StatusRet2
}
func (p *MockMusic) SortPlaylist([]string, string) error {
	return p.SortPlaylistErr
}
<commit_msg>add playlist sort test<commit_after>package main

import (
	"encoding/json"
	"github.com/fhs/gompd/mpd"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestLibrary(t *testing.T) {
	m := new(MockMusic)
	api := apiHandler{m}
	ts := httptest.NewServer(http.HandlerFunc(api.library))
	defer ts.Close()
	t.Run("no parameter", func(t *testing.T) {
		m.LibraryRet1 = []mpd.Attrs{mpd.Attrs{"foo": "bar"}}
		m.LibraryRet2 = time.Unix(0, 0)
		res, err := http.Get(ts.URL)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		st := struct {
			Data   []mpd.Attrs `json:"data"`
			Errors error       `json:"errors"`
		}{[]mpd.Attrs{}, nil}
		json.Unmarshal(body, &st)
		if !reflect.DeepEqual(m.LibraryRet1, st.Data) {
			t.Errorf("unexpected body: %s", body)
		}
		if st.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
	t.Run("If-Modified-Since", func(t *testing.T) {
		m.LibraryRet1 = []mpd.Attrs{mpd.Attrs{"foo": "bar"}}
		m.LibraryRet2 = time.Unix(60, 0)
		req, _ := http.NewRequest("GET", ts.URL, nil)
		req.Header.Set("If-Modified-Since", m.LibraryRet2.Format(http.TimeFormat))
		client := new(http.Client)
		res, err := client.Do(req)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 304 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})
}
func TestPlaylist(t *testing.T) {
	m := new(MockMusic)
	api := apiHandler{m}
	ts := httptest.NewServer(http.HandlerFunc(api.playlist))
	defer ts.Close()
	t.Run("no parameter", func(t *testing.T) {
		m.PlaylistRet1 = []mpd.Attrs{mpd.Attrs{"foo": "bar"}}
		m.PlaylistRet2 = time.Unix(0, 0)
		res, err := http.Get(ts.URL)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		st := struct {
			Data   []mpd.Attrs `json:"data"`
			Errors error       `json:"errors"`
		}{[]mpd.Attrs{}, nil}
		json.Unmarshal(body, &st)
		if !reflect.DeepEqual(m.PlaylistRet1, st.Data) {
			t.Errorf("unexpected body: %s", body)
		}
		if st.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
	t.Run("If-Modified-Since", func(t *testing.T) {
		m.PlaylistRet1 = []mpd.Attrs{mpd.Attrs{"foo": "bar"}}
		m.PlaylistRet2 = time.Unix(60, 0)
		req, _ := http.NewRequest("GET", ts.URL, nil)
		req.Header.Set("If-Modified-Since", m.PlaylistRet2.Format(http.TimeFormat))
		client := new(http.Client)
		res, err := client.Do(req)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 304 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})
	t.Run("sort", func(t *testing.T) {
		m.SortPlaylistErr = nil
		j := strings.NewReader(
			"{\"action\": \"sort\", \"keys\": [\"file\"], \"uri\": \"path\"}",
		)
		res, err := http.Post(ts.URL, "application/json", j)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}

		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		b := struct {
			Errors error `json:"errors"`
		}{nil}
		json.Unmarshal(body, &b)
		if b.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
}
func TestCurrent(t *testing.T) {
	m := new(MockMusic)
	api := apiHandler{m}
	ts := httptest.NewServer(http.HandlerFunc(api.current))
	defer ts.Close()
	t.Run("no parameter", func(t *testing.T) {
		m.CurrentRet1 = mpd.Attrs{"foo": "bar"}
		m.CurrentRet2 = time.Unix(0, 0)
		res, err := http.Get(ts.URL)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		st := struct {
			Data   mpd.Attrs `json:"data"`
			Errors error     `json:"errors"`
		}{mpd.Attrs{}, nil}
		json.Unmarshal(body, &st)
		if !reflect.DeepEqual(m.CurrentRet1, st.Data) {
			t.Errorf("unexpected body: %s", body)
		}
		if st.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
	t.Run("If-Modified-Since", func(t *testing.T) {
		m.CurrentRet1 = mpd.Attrs{"foo": "bar"}
		m.CurrentRet2 = time.Unix(60, 0)
		req, _ := http.NewRequest("GET", ts.URL, nil)
		req.Header.Set("If-Modified-Since", m.CurrentRet2.Format(http.TimeFormat))
		client := new(http.Client)
		res, err := client.Do(req)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 304 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})
}
func TestControl(t *testing.T) {
	m := new(MockMusic)
	api := apiHandler{m}
	ts := httptest.NewServer(http.HandlerFunc(api.control))
	defer ts.Close()
	t.Run("no parameter", func(t *testing.T) {
		s := convStatus(mpd.Attrs{}, mpd.Attrs{})
		s.LastModified = 0
		m.StatusRet1 = s
		m.StatusRet2 = time.Unix(0, 0)
		res, err := http.Get(ts.URL)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
		defer res.Body.Close()
		body, _ := ioutil.ReadAll(res.Body)
		st := struct {
			Data   PlayerStatus `json:"data"`
			Errors error        `json:"errors"`
		}{PlayerStatus{}, nil}
		json.Unmarshal(body, &st)
		if !reflect.DeepEqual(s, st.Data) {
			t.Errorf("unexpected body: %s", body)
		}
		if st.Errors != nil {
			t.Errorf("unexpected body: %s", body)
		}
	})
	t.Run("If-Modified-Since", func(t *testing.T) {
		s := convStatus(mpd.Attrs{}, mpd.Attrs{})
		s.LastModified = 60
		m.StatusRet1 = s
		m.StatusRet2 = time.Unix(60, 0)
		req, _ := http.NewRequest("GET", ts.URL, nil)
		req.Header.Set("If-Modified-Since", m.StatusRet2.Format(http.TimeFormat))
		client := new(http.Client)
		res, err := client.Do(req)
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 304 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})
	t.Run("action=play", func(t *testing.T) {
		res, err := http.Get(ts.URL + "?action=play")
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})

	t.Run("action=pause", func(t *testing.T) {
		res, err := http.Get(ts.URL + "?action=pause")
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})

	t.Run("action=next", func(t *testing.T) {
		res, err := http.Get(ts.URL + "?action=next")
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})

	t.Run("action=prev", func(t *testing.T) {
		res, err := http.Get(ts.URL + "?action=prev")
		if err != nil {
			t.Errorf("unexpected error %s", err.Error())
		}
		if res.StatusCode != 200 {
			t.Errorf("unexpected status %d", res.StatusCode)
		}
	})

}

type MockMusic struct {
	PlayErr          error
	PauseErr         error
	NextErr          error
	PrevErr          error
	PlaylistRet1     []mpd.Attrs
	PlaylistRet2     time.Time
	LibraryRet1      []mpd.Attrs
	LibraryRet2      time.Time
	CurrentRet1      mpd.Attrs
	CurrentRet2      time.Time
	CommentsRet1     mpd.Attrs
	CommentsRet2     time.Time
	StatusRet1       PlayerStatus
	StatusRet2       time.Time
	SortPlaylistArg1 []string
	SortPlaylistArg2 string
	SortPlaylistErr  error
}

func (p *MockMusic) Play() error {
	return p.PlayErr
}

func (p *MockMusic) Pause() error {
	return p.PauseErr
}
func (p *MockMusic) Next() error {
	return p.NextErr
}
func (p *MockMusic) Prev() error {
	return p.PrevErr
}
func (p *MockMusic) Comments() (mpd.Attrs, time.Time) {
	return p.CommentsRet1, p.CommentsRet2
}
func (p *MockMusic) Current() (mpd.Attrs, time.Time) {
	return p.CurrentRet1, p.CurrentRet2
}
func (p *MockMusic) Library() ([]mpd.Attrs, time.Time) {
	return p.LibraryRet1, p.LibraryRet2
}
func (p *MockMusic) Playlist() ([]mpd.Attrs, time.Time) {
	return p.PlaylistRet1, p.PlaylistRet2
}
func (p *MockMusic) Status() (PlayerStatus, time.Time) {
	return p.StatusRet1, p.StatusRet2
}
func (p *MockMusic) SortPlaylist(s []string, u string) error {
	p.SortPlaylistArg1 = s
	p.SortPlaylistArg2 = u
	return p.SortPlaylistErr
}
<|endoftext|>