<commit_before>package falcore

import (
	"bufio"
	"crypto/rand"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"reflect"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"
)

type Server struct {
	Addr             string
	Pipeline         *Pipeline
	listener         net.Listener
	listenerFile     *os.File
	stopAccepting    chan int
	handlerWaitGroup *sync.WaitGroup
	logPrefix        string
	AcceptReady      chan int
	sendfile         bool
	sockOpt          int
	bufferPool       *bufferPool
}

func NewServer(port int, pipeline *Pipeline) *Server {
	s := new(Server)
	s.Addr = fmt.Sprintf(":%v", port)
	s.Pipeline = pipeline
	s.stopAccepting = make(chan int)
	s.AcceptReady = make(chan int, 1)
	s.handlerWaitGroup = new(sync.WaitGroup)
	s.logPrefix = fmt.Sprintf("%d", syscall.Getpid())

	// openbsd/netbsd don't have TCP_NOPUSH so it's likely sendfile will be slower
	// without these socket options, just enable for linux, mac and freebsd.
	// TODO (Graham) windows has TransmitFile zero-copy mechanism, try to use it
	switch runtime.GOOS {
	case "linux":
		s.sendfile = true
		s.sockOpt = 0x3 // syscall.TCP_CORK
	case "freebsd", "darwin":
		s.sendfile = true
		s.sockOpt = 0x4 // syscall.TCP_NOPUSH
	default:
		s.sendfile = false
	}

	// buffer pool for reusing connection bufio.Readers
	s.bufferPool = newBufferPool(100, 8192)

	return s
}

func (srv *Server) FdListen(fd int) error {
	var err error
	srv.listenerFile = os.NewFile(uintptr(fd), "")
	if srv.listener, err = net.FileListener(srv.listenerFile); err != nil {
		return err
	}
	if _, ok := srv.listener.(*net.TCPListener); !ok {
		return errors.New("Broken listener isn't TCP")
	}
	return nil
}

func (srv *Server) socketListen() error {
	var la *net.TCPAddr
	var err error
	if la, err = net.ResolveTCPAddr("tcp", srv.Addr); err != nil {
		return err
	}

	var l *net.TCPListener
	if l, err = net.ListenTCP("tcp", la); err != nil {
		return err
	}
	srv.listener = l
	// setup listener to be non-blocking if we're not on windows.
	// this is required for hot restart to work.
	if runtime.GOOS != "windows" {
		if srv.listenerFile, err = l.File(); err != nil {
			return err
		}
		fd := int(srv.listenerFile.Fd())
		if e := setupFDNonblock(fd); e != nil {
			return e
		}

		if srv.sendfile {
			if e := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, srv.sockOpt, 1); e != nil {
				return e
			}
		}
	}
	return nil
}

// Calling syscall.SetNonblock using reflection to avoid compile errors
// on windows.  This call is not used on windows as hot restart is not supported.
func setupFDNonblock(fd int) error {
	// if function exists
	if fun := reflect.ValueOf(syscall.SetNonblock); fun.Kind() == reflect.Func {
		// if first argument is an int
		if fun.Type().In(0).Kind() == reflect.Int {
			args := []reflect.Value{reflect.ValueOf(fd), reflect.ValueOf(true)}
			if res := fun.Call(args); len(res) == 1 && !res[0].IsNil() {
				err := res[0].Interface().(error)
				return err
			}
		}
	}
	return nil
}

func (srv *Server) ListenAndServe() error {
	if srv.Addr == "" {
		srv.Addr = ":http"
	}
	if srv.listener == nil {
		if err := srv.socketListen(); err != nil {
			return err
		}
	}
	return srv.serve()
}

func (srv *Server) SocketFd() int {
	return int(srv.listenerFile.Fd())
}

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	if srv.Addr == "" {
		srv.Addr = ":https"
	}
	config := &tls.Config{
		Rand:       rand.Reader,
		Time:       time.Now,
		NextProtos: []string{"http/1.1"},
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	if srv.listener == nil {
		if err := srv.socketListen(); err != nil {
			return err
		}
	}

	srv.listener = tls.NewListener(srv.listener, config)

	return srv.serve()
}

func (srv *Server) StopAccepting() {
	srv.stopAccepting <- 1
}

func (srv *Server) Port() int {
	if l := srv.listener; l != nil {
		a := l.Addr()
		if _, p, e := net.SplitHostPort(a.String()); e == nil && p != "" {
			server_port, _ := strconv.Atoi(p)
			return server_port
		}
	}
	return 0
}

func (srv *Server) serve() (e error) {
	var accept = true
	srv.AcceptReady <- 1
	for accept {
		var c net.Conn
		if l, ok := srv.listener.(*net.TCPListener); ok {
			l.SetDeadline(time.Now().Add(3e9))
		}
		c, e = srv.listener.Accept()
		if e != nil {
			if ope, ok := e.(*net.OpError); ok {
				if !(ope.Timeout() && ope.Temporary()) {
					Error("%s SERVER Accept Error: %v", srv.serverLogPrefix(), ope)
				}
			} else {
				Error("%s SERVER Accept Error: %v", srv.serverLogPrefix(), e)
			}
		} else {
			//Trace("Handling!")
			srv.handlerWaitGroup.Add(1)
			go srv.handler(c)
		}
		select {
		case <-srv.stopAccepting:
			accept = false
		default:
		}
	}
	Trace("Stopped accepting, waiting for handlers")
	// wait for handlers
	srv.handlerWaitGroup.Wait()
	return nil
}

func (srv *Server) handler(c net.Conn) {
	startTime := time.Now()
	defer srv.connectionFinished(c)
	bpe := srv.bufferPool.take(c)
	defer srv.bufferPool.give(bpe)
	var err error
	var req *http.Request
	// no keepalive (for now)
	reqCount := 0
	keepAlive := true
	for err == nil && keepAlive {
		if req, err = http.ReadRequest(bpe.br); err == nil {
			if req.Header.Get("Connection") != "Keep-Alive" {
				keepAlive = false
			}
			request := newRequest(req, c, startTime)
			reqCount++
			var res *http.Response

			pssInit := new(PipelineStageStat)
			pssInit.Name = "server.Init"
			pssInit.StartTime = startTime
			pssInit.EndTime = time.Now()
			request.appendPipelineStage(pssInit)
			// execute the pipeline
			if res = srv.Pipeline.execute(request); res == nil {
				res = SimpleResponse(req, 404, nil, "Not Found")
			}
			// cleanup
			request.startPipelineStage("server.ResponseWrite")
			req.Body.Close()

			if srv.sendfile {
				res.Write(c)
			} else {
				wbuf := bufio.NewWriter(c)
				res.Write(wbuf)
				wbuf.Flush()
			}

			if res.Body != nil {
				res.Body.Close()
			}
			request.finishPipelineStage()
			request.finishRequest()
			srv.requestFinished(request)
		} else {
			// EOF is socket closed
			if err != io.ErrUnexpectedEOF {
				Error("%s %v ERROR reading request: %v", srv.serverLogPrefix(), c.RemoteAddr(), err)
			}
		}
	}
	//Debug("%s Processed %v requests on connection %v", srv.serverLogPrefix(), reqCount, c.RemoteAddr())
}

func (srv *Server) serverLogPrefix() string {
	return srv.logPrefix
}

func (srv *Server) requestFinished(request *Request) {
	if srv.Pipeline.RequestDoneCallback != nil {
		// Don't block the connecion for this
		go srv.Pipeline.RequestDoneCallback.FilterRequest(request)
	}
}

func (srv *Server) connectionFinished(c net.Conn) {
	c.Close()
	srv.handlerWaitGroup.Done()
}
<commit_msg>fixing a bug with calculation of the init stage on keepalive requests<commit_after>package falcore

import (
	"bufio"
	"crypto/rand"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"reflect"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"
)

type Server struct {
	Addr             string
	Pipeline         *Pipeline
	listener         net.Listener
	listenerFile     *os.File
	stopAccepting    chan int
	handlerWaitGroup *sync.WaitGroup
	logPrefix        string
	AcceptReady      chan int
	sendfile         bool
	sockOpt          int
	bufferPool       *bufferPool
}

func NewServer(port int, pipeline *Pipeline) *Server {
	s := new(Server)
	s.Addr = fmt.Sprintf(":%v", port)
	s.Pipeline = pipeline
	s.stopAccepting = make(chan int)
	s.AcceptReady = make(chan int, 1)
	s.handlerWaitGroup = new(sync.WaitGroup)
	s.logPrefix = fmt.Sprintf("%d", syscall.Getpid())

	// openbsd/netbsd don't have TCP_NOPUSH so it's likely sendfile will be slower
	// without these socket options, just enable for linux, mac and freebsd.
	// TODO (Graham) windows has TransmitFile zero-copy mechanism, try to use it
	switch runtime.GOOS {
	case "linux":
		s.sendfile = true
		s.sockOpt = 0x3 // syscall.TCP_CORK
	case "freebsd", "darwin":
		s.sendfile = true
		s.sockOpt = 0x4 // syscall.TCP_NOPUSH
	default:
		s.sendfile = false
	}

	// buffer pool for reusing connection bufio.Readers
	s.bufferPool = newBufferPool(100, 8192)

	return s
}

func (srv *Server) FdListen(fd int) error {
	var err error
	srv.listenerFile = os.NewFile(uintptr(fd), "")
	if srv.listener, err = net.FileListener(srv.listenerFile); err != nil {
		return err
	}
	if _, ok := srv.listener.(*net.TCPListener); !ok {
		return errors.New("Broken listener isn't TCP")
	}
	return nil
}

func (srv *Server) socketListen() error {
	var la *net.TCPAddr
	var err error
	if la, err = net.ResolveTCPAddr("tcp", srv.Addr); err != nil {
		return err
	}

	var l *net.TCPListener
	if l, err = net.ListenTCP("tcp", la); err != nil {
		return err
	}
	srv.listener = l
	// setup listener to be non-blocking if we're not on windows.
	// this is required for hot restart to work.
	if runtime.GOOS != "windows" {
		if srv.listenerFile, err = l.File(); err != nil {
			return err
		}
		fd := int(srv.listenerFile.Fd())
		if e := setupFDNonblock(fd); e != nil {
			return e
		}

		if srv.sendfile {
			if e := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, srv.sockOpt, 1); e != nil {
				return e
			}
		}
	}
	return nil
}

// Calling syscall.SetNonblock using reflection to avoid compile errors
// on windows.  This call is not used on windows as hot restart is not supported.
func setupFDNonblock(fd int) error {
	// if function exists
	if fun := reflect.ValueOf(syscall.SetNonblock); fun.Kind() == reflect.Func {
		// if first argument is an int
		if fun.Type().In(0).Kind() == reflect.Int {
			args := []reflect.Value{reflect.ValueOf(fd), reflect.ValueOf(true)}
			if res := fun.Call(args); len(res) == 1 && !res[0].IsNil() {
				err := res[0].Interface().(error)
				return err
			}
		}
	}
	return nil
}

func (srv *Server) ListenAndServe() error {
	if srv.Addr == "" {
		srv.Addr = ":http"
	}
	if srv.listener == nil {
		if err := srv.socketListen(); err != nil {
			return err
		}
	}
	return srv.serve()
}

func (srv *Server) SocketFd() int {
	return int(srv.listenerFile.Fd())
}

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	if srv.Addr == "" {
		srv.Addr = ":https"
	}
	config := &tls.Config{
		Rand:       rand.Reader,
		Time:       time.Now,
		NextProtos: []string{"http/1.1"},
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	if srv.listener == nil {
		if err := srv.socketListen(); err != nil {
			return err
		}
	}

	srv.listener = tls.NewListener(srv.listener, config)

	return srv.serve()
}

func (srv *Server) StopAccepting() {
	srv.stopAccepting <- 1
}

func (srv *Server) Port() int {
	if l := srv.listener; l != nil {
		a := l.Addr()
		if _, p, e := net.SplitHostPort(a.String()); e == nil && p != "" {
			server_port, _ := strconv.Atoi(p)
			return server_port
		}
	}
	return 0
}

func (srv *Server) serve() (e error) {
	var accept = true
	srv.AcceptReady <- 1
	for accept {
		var c net.Conn
		if l, ok := srv.listener.(*net.TCPListener); ok {
			l.SetDeadline(time.Now().Add(3e9))
		}
		c, e = srv.listener.Accept()
		if e != nil {
			if ope, ok := e.(*net.OpError); ok {
				if !(ope.Timeout() && ope.Temporary()) {
					Error("%s SERVER Accept Error: %v", srv.serverLogPrefix(), ope)
				}
			} else {
				Error("%s SERVER Accept Error: %v", srv.serverLogPrefix(), e)
			}
		} else {
			//Trace("Handling!")
			srv.handlerWaitGroup.Add(1)
			go srv.handler(c)
		}
		select {
		case <-srv.stopAccepting:
			accept = false
		default:
		}
	}
	Trace("Stopped accepting, waiting for handlers")
	// wait for handlers
	srv.handlerWaitGroup.Wait()
	return nil
}

func (srv *Server) handler(c net.Conn) {
	startTime := time.Now()
	defer srv.connectionFinished(c)
	bpe := srv.bufferPool.take(c)
	defer srv.bufferPool.give(bpe)
	var err error
	var req *http.Request
	// no keepalive (for now)
	reqCount := 0
	keepAlive := true
	for err == nil && keepAlive {
		if req, err = http.ReadRequest(bpe.br); err == nil {
			if req.Header.Get("Connection") != "Keep-Alive" {
				keepAlive = false
			}
			request := newRequest(req, c, startTime)
			reqCount++
			var res *http.Response

			pssInit := new(PipelineStageStat)
			pssInit.Name = "server.Init"
			pssInit.StartTime = startTime
			pssInit.EndTime = time.Now()
			request.appendPipelineStage(pssInit)
			// execute the pipeline
			if res = srv.Pipeline.execute(request); res == nil {
				res = SimpleResponse(req, 404, nil, "Not Found")
			}
			// cleanup
			request.startPipelineStage("server.ResponseWrite")
			req.Body.Close()

			if srv.sendfile {
				res.Write(c)
			} else {
				wbuf := bufio.NewWriter(c)
				res.Write(wbuf)
				wbuf.Flush()
			}

			if res.Body != nil {
				res.Body.Close()
			}
			request.finishPipelineStage()
			request.finishRequest()
			srv.requestFinished(request)
			// Reset the startTime
			// this isn't great since there may be lag between requests; but it's the best we've got
			startTime = time.Now()
		} else {
			// EOF is socket closed
			if err != io.ErrUnexpectedEOF {
				Error("%s %v ERROR reading request: %v", srv.serverLogPrefix(), c.RemoteAddr(), err)
			}
		}
	}
	//Debug("%s Processed %v requests on connection %v", srv.serverLogPrefix(), reqCount, c.RemoteAddr())
}

func (srv *Server) serverLogPrefix() string {
	return srv.logPrefix
}

func (srv *Server) requestFinished(request *Request) {
	if srv.Pipeline.RequestDoneCallback != nil {
		// Don't block the connecion for this
		go srv.Pipeline.RequestDoneCallback.FilterRequest(request)
	}
}

func (srv *Server) connectionFinished(c net.Conn) {
	c.Close()
	srv.handlerWaitGroup.Done()
}
<|endoftext|>


<commit_before>package main

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

type Cafeteria struct {
	CoverName   string `json:"cover_name"`
	Endpoint    string `json:"endpoint"`
	IsClosed    bool   `json:"is_closed"`
	Restaurants []struct {
		Breakfast []string `json:"breakfast"`
		Dinner    []string `json:"dinner"`
		Endpoint  string   `json:"endpoint"`
		LateNight []string `json:"late_night"`
		Lunch     []string `json:"lunch"`
		Name      string   `json:"name"`
	} `json:"restaurants"`
	UniqueName string `json:"unique_name"`
}

func loadMenuJson() (map[string]Cafeteria, []Cafeteria) {
	b, err := ioutil.ReadFile("menus.json")
	if err != nil {
		panic(err)
	}

	var data []Cafeteria
	if err = json.Unmarshal(b, &data); err != nil {
		panic(err)
	}

	// Build the map of caf name to caf object
	cafeteriaMap := make(map[string]Cafeteria)
	for i := range data {
		rest := data[i]
		//fmt.Println(rest.CoverName)
		cafeteriaMap[rest.UniqueName] = rest
	}

	/*
		Sample marshalling.

			testRest := data[4]
			b, err = json.Marshal(testRest)
			if err = ioutil.WriteFile("test.json", b, 0644); err != nil {
				panic(err)
			}
	*/

	return cafeteriaMap, data
}

type MenuHeaders struct {
	CafeteriaName string `json:"cafeteria_name"`
}

func serveCafeteriaJson(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	if r.Method == "POST" {
		// check if name was in the body and respond with 402 if not
		if r.Body == nil {
			//http.error(w, "empty request body.", 402)
			return
		}

		// extract the cafeteria name from the request
		var requestHeaders MenuHeaders
		if err := json.NewDecoder(r.Body).Decode(&requestHeaders); err != nil {
			fmt.Println(err)
			return
		}

		// TODO: Handle invalid cafeteria name
		cafeteria := currentCafeMap[requestHeaders.CafeteriaName]
		json.NewEncoder(w).Encode(cafeteria)
	}
}

func serveAllCafeteriaJson(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	if r.Method == "GET" {
		json.NewEncoder(w).Encode(currentCafeArray)
	}
}

var currentCafeMap map[string]Cafeteria
var currentCafeArray []Cafeteria

func main() {
	// this will be used to serve up the data
	currentCafeMap, currentCafeArray = loadMenuJson()

	// setup http server and serve the cafes
	http.HandleFunc("/menu/cafeteria", serveCafeteriaJson)
	http.HandleFunc("/menu/all_cafeterias", serveAllCafeteriaJson)
	http.ListenAndServe(":8000", nil)
}
<commit_msg>Add support for is_closed for restaurant class in the server.<commit_after>package main

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

type Cafeteria struct {
	CoverName   string `json:"cover_name"`
	Endpoint    string `json:"endpoint"`
	IsClosed    bool   `json:"is_closed"`
	Restaurants []struct {
		Breakfast []string `json:"breakfast"`
		Dinner    []string `json:"dinner"`
		Endpoint  string   `json:"endpoint"`
		LateNight []string `json:"late_night"`
		Lunch     []string `json:"lunch"`
		Name      string   `json:"name"`
        IsClosed    bool   `json:"is_closed"`
	} `json:"restaurants"`
	UniqueName string `json:"unique_name"`
}

func loadMenuJson() (map[string]Cafeteria, []Cafeteria) {
	b, err := ioutil.ReadFile("menus.json")
	if err != nil {
		panic(err)
	}

	var data []Cafeteria
	if err = json.Unmarshal(b, &data); err != nil {
		panic(err)
	}

	// Build the map of caf name to caf object
	cafeteriaMap := make(map[string]Cafeteria)
	for i := range data {
		rest := data[i]
		//fmt.Println(rest.CoverName)
		cafeteriaMap[rest.UniqueName] = rest
	}

	/*
		Sample marshalling.

			testRest := data[4]
			b, err = json.Marshal(testRest)
			if err = ioutil.WriteFile("test.json", b, 0644); err != nil {
				panic(err)
			}
	*/

	return cafeteriaMap, data
}

type MenuHeaders struct {
	CafeteriaName string `json:"cafeteria_name"`
}

func serveCafeteriaJson(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	if r.Method == "POST" {
		// check if name was in the body and respond with 402 if not
		if r.Body == nil {
			//http.error(w, "empty request body.", 402)
			return
		}

		// extract the cafeteria name from the request
		var requestHeaders MenuHeaders
		if err := json.NewDecoder(r.Body).Decode(&requestHeaders); err != nil {
			fmt.Println(err)
			return
		}

		// TODO: Handle invalid cafeteria name
		cafeteria := currentCafeMap[requestHeaders.CafeteriaName]
		json.NewEncoder(w).Encode(cafeteria)
	}
}

func serveAllCafeteriaJson(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	if r.Method == "GET" {
		json.NewEncoder(w).Encode(currentCafeArray)
	}
}

var currentCafeMap map[string]Cafeteria
var currentCafeArray []Cafeteria

func main() {
	// this will be used to serve up the data
	currentCafeMap, currentCafeArray = loadMenuJson()

	// setup http server and serve the cafes
	http.HandleFunc("/menu/cafeteria", serveCafeteriaJson)
	http.HandleFunc("/menu/all_cafeterias", serveAllCafeteriaJson)
	http.ListenAndServe(":8000", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"
)
import (
	"github.com/satori/go.uuid"
	"github.com/speps/go-hashids"
)

var rooms = make(map[int]*Room)
var roomsMU sync.Mutex
var counter int

type card struct {
	Name string `json:"name"`
	Path string `json:"path"`
}

var cards []card
var hashID *hashids.HashID
var colors = []string{"Magenta", "LightSlateGray", "PaleVioletRed", "Peru", "RebeccaPurple", "LightSeaGreen", "Tomato", "SeaGreen", "Maroon", "GoldenRod", "DarkSlateBlue"}

func main() {
	var (
		httpAddr = flag.String("http", ":3000", "HTTP service address")
		salt     = flag.String("salt", "super secret salt", "Salt is the secret used to make the generated id harder to guess")
	)
	flag.Parse()

	cardsJSON, _ := ioutil.ReadFile("static/cards/cards.json")
	json.Unmarshal([]byte(cardsJSON), &cards)

	hd := hashids.NewData()
	hd.Salt = *salt
	hd.MinLength = 5
	hd.Alphabet = "abcdefghijklmnopqrstuvwxyz"
	hashID = hashids.NewWithData(hd)

	http.HandleFunc("/room/new", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.NotFound(w, r)
			return
		}
		roomsMU.Lock()
		defer roomsMU.Unlock()

		c, _ := hashID.Encode([]int{counter})

		uuidS := uuid.NewV4().String()
		admin := player{Name: colors[0], Color: colors[0], Admin: true, UUID: uuidS, Spy: false}
		players := []player{admin}
		rooms[counter] = &Room{ID: counter, Players: players, Started: false}

		expiration := time.Now().Add(365 * 24 * time.Hour)
		cookie := http.Cookie{Name: "spyfall", Value: uuidS, Path: "/", Expires: expiration}
		http.SetCookie(w, &cookie)
		fmt.Println("created new room", c, counter)

		counter++
		http.Redirect(w, r, "/room?code="+c, 303)
	})

	http.HandleFunc("/room", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.NotFound(w, r)
			return
		}
		roomsMU.Lock()
		defer roomsMU.Unlock()

		c := r.URL.Query().Get("code")

		d := hashID.Decode(c)
		roomID := d[0]
		if room, ok := rooms[roomID]; ok {
			if room.Started {
				http.Redirect(w, r, "/", 303) // TODO: show error message that room already started
				return
			}
			var newPlayer player

			if p, ok := room.playerForCookies(r.Cookies()); ok {
				newPlayer = p
			} else {
				uuidS := uuid.NewV4().String()
				color := colors[len(room.Players)]
				newPlayer = Player{Name: color, Color: color, Admin: false, UUID: uuidS, Spy: false}

				expiration := time.Now().Add(365 * 24 * time.Hour)
				cookie := http.Cookie{Name: "spyfall", Value: uuidS, Path: "/", Expires: expiration}
				http.SetCookie(w, &cookie)

				room.Players = append(room.Players, newPlayer)
			}

			t, _ := template.ParseFiles("static/room.html")
			data := struct {
				ID     int
				Code   string
				Player player
			}{
				ID: roomID, Code: c, Player: newPlayer,
			}
			t.Execute(w, data)
		} else {
			http.Redirect(w, r, "/", 303)
			return
		}
	})

	http.HandleFunc("/game", func(w http.ResponseWriter, r *http.Request) {
		roomIDStr := r.URL.Query().Get("id")
		roomID, err := strconv.Atoi(roomIDStr)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		roomsMU.Lock()
		defer roomsMU.Unlock()
		if room, ok := rooms[roomID]; ok {
			if room.Started == false {
				room.setup()
				fmt.Println("Started room", roomID)
			}
			t, _ := template.ParseFiles("static/game.html")
			cookie := r.Cookies()[0]
			uuid := cookie.Value
			player, ok := room.playerForUUID(uuid)
			if !ok {
				http.NotFound(w, r)
				return
			}

			data := struct {
				Spy       bool
				Location  int
				Cards     []card
				Countdown int
			}{
				Spy: player.Spy, Location: room.Location, Cards: cards, Countdown: room.Countdown,
			}

			t.Execute(w, data)
		} else {
			http.NotFound(w, r)
			return
		}
	})

	http.HandleFunc("/players.json", func(w http.ResponseWriter, r *http.Request) {
		roomIDStr := r.URL.Query().Get("id")
		roomID, err := strconv.Atoi(roomIDStr)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		roomsMU.Lock()
		defer roomsMU.Unlock()
		if room, ok := rooms[roomID]; ok {
			js, err := json.Marshal(room)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			w.Write(js)
		} else {
			http.NotFound(w, r)
			return
		}
	})

	http.Handle("/", http.FileServer(http.Dir("./static")))
	fmt.Println("listen on", *httpAddr)
	log.Fatal(http.ListenAndServe(*httpAddr, nil))
}
<commit_msg>fixe type<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"
)
import (
	"github.com/satori/go.uuid"
	"github.com/speps/go-hashids"
)

var rooms = make(map[int]*Room)
var roomsMU sync.Mutex
var counter int

type card struct {
	Name string `json:"name"`
	Path string `json:"path"`
}

var cards []card
var hashID *hashids.HashID
var colors = []string{"Magenta", "LightSlateGray", "PaleVioletRed", "Peru", "RebeccaPurple", "LightSeaGreen", "Tomato", "SeaGreen", "Maroon", "GoldenRod", "DarkSlateBlue"}

func main() {
	var (
		httpAddr = flag.String("http", ":3000", "HTTP service address")
		salt     = flag.String("salt", "super secret salt", "Salt is the secret used to make the generated id harder to guess")
	)
	flag.Parse()

	cardsJSON, _ := ioutil.ReadFile("static/cards/cards.json")
	json.Unmarshal([]byte(cardsJSON), &cards)

	hd := hashids.NewData()
	hd.Salt = *salt
	hd.MinLength = 5
	hd.Alphabet = "abcdefghijklmnopqrstuvwxyz"
	hashID = hashids.NewWithData(hd)

	http.HandleFunc("/room/new", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.NotFound(w, r)
			return
		}
		roomsMU.Lock()
		defer roomsMU.Unlock()

		c, _ := hashID.Encode([]int{counter})

		uuidS := uuid.NewV4().String()
		admin := player{Name: colors[0], Color: colors[0], Admin: true, UUID: uuidS, Spy: false}
		players := []player{admin}
		rooms[counter] = &Room{ID: counter, Players: players, Started: false}

		expiration := time.Now().Add(365 * 24 * time.Hour)
		cookie := http.Cookie{Name: "spyfall", Value: uuidS, Path: "/", Expires: expiration}
		http.SetCookie(w, &cookie)
		fmt.Println("created new room", c, counter)

		counter++
		http.Redirect(w, r, "/room?code="+c, 303)
	})

	http.HandleFunc("/room", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.NotFound(w, r)
			return
		}
		roomsMU.Lock()
		defer roomsMU.Unlock()

		c := r.URL.Query().Get("code")

		d := hashID.Decode(c)
		roomID := d[0]
		if room, ok := rooms[roomID]; ok {
			if room.Started {
				http.Redirect(w, r, "/", 303) // TODO: show error message that room already started
				return
			}
			var newPlayer player

			if p, ok := room.playerForCookies(r.Cookies()); ok {
				newPlayer = p
			} else {
				uuidS := uuid.NewV4().String()
				color := colors[len(room.Players)]
				newPlayer = player{Name: color, Color: color, Admin: false, UUID: uuidS, Spy: false}

				expiration := time.Now().Add(365 * 24 * time.Hour)
				cookie := http.Cookie{Name: "spyfall", Value: uuidS, Path: "/", Expires: expiration}
				http.SetCookie(w, &cookie)

				room.Players = append(room.Players, newPlayer)
			}

			t, _ := template.ParseFiles("static/room.html")
			data := struct {
				ID     int
				Code   string
				Player player
			}{
				ID: roomID, Code: c, Player: newPlayer,
			}
			t.Execute(w, data)
		} else {
			http.Redirect(w, r, "/", 303)
			return
		}
	})

	http.HandleFunc("/game", func(w http.ResponseWriter, r *http.Request) {
		roomIDStr := r.URL.Query().Get("id")
		roomID, err := strconv.Atoi(roomIDStr)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		roomsMU.Lock()
		defer roomsMU.Unlock()
		if room, ok := rooms[roomID]; ok {
			if room.Started == false {
				room.setup()
				fmt.Println("Started room", roomID)
			}
			t, _ := template.ParseFiles("static/game.html")
			cookie := r.Cookies()[0]
			uuid := cookie.Value
			player, ok := room.playerForUUID(uuid)
			if !ok {
				http.NotFound(w, r)
				return
			}

			data := struct {
				Spy       bool
				Location  int
				Cards     []card
				Countdown int
			}{
				Spy: player.Spy, Location: room.Location, Cards: cards, Countdown: room.Countdown,
			}

			t.Execute(w, data)
		} else {
			http.NotFound(w, r)
			return
		}
	})

	http.HandleFunc("/players.json", func(w http.ResponseWriter, r *http.Request) {
		roomIDStr := r.URL.Query().Get("id")
		roomID, err := strconv.Atoi(roomIDStr)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		roomsMU.Lock()
		defer roomsMU.Unlock()
		if room, ok := rooms[roomID]; ok {
			js, err := json.Marshal(room)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			w.Write(js)
		} else {
			http.NotFound(w, r)
			return
		}
	})

	http.Handle("/", http.FileServer(http.Dir("./static")))
	fmt.Println("listen on", *httpAddr)
	log.Fatal(http.ListenAndServe(*httpAddr, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)
import (
	"github.com/satori/go.uuid"
	"github.com/speps/go-hashids"
)

var rooms = make(map[int]*Room)
var counter int = 0
var cards []Card
var HashID *hashids.HashID
var colors = []string{"Magenta", "LightSlateGray", "PaleVioletRed", "Peru", "RebeccaPurple", "LightSeaGreen", "Tomato", "SeaGreen", "Maroon", "GoldenRod", "DarkSlateBlue"}

type Card struct {
	Name string `json:"name"`
	Path string `json:"path"`
}

type PageLobby struct {
	ID     int
	Code   string
	Player Player
}

type PageGame struct {
	Spy       bool
	Location  int
	Cards     []Card
	Countdown int
}

type Room struct {
	ID        int
	Players   []Player `json:"players"`
	Started   bool     `json:"started"`
	Location  int      `json:"-"`
	Countdown int      `json:"-"`
}

type Player struct {
	Name  string `json:"name"`
	Color string `json:"color"`
	Admin bool   `json:"-"`
	UUID  string `json:"-"`
	Spy   bool   `json:"-"`
}

func (r Room) playerForUUID(uuid string) Player {
	for _, value := range r.Players {
		if value.UUID == uuid {
			return value
		}
	}
	return Player{"Grey", "grey", false, "", false} // TODO: return error
}

func (r *Room) setup() {
	// select location
	r.Location = rand.Intn(len(cards))
	// assign a spy
	spy := rand.Intn(len(r.Players))
	r.Players[spy].Spy = true

	// set the countdown
	t := int(time.Now().Unix())
	t = t + (60 * 10)
	r.Countdown = t
	r.Started = true

}

func main() {
	cardsJSON, _ := ioutil.ReadFile("static/cards/cards.json")
	json.Unmarshal([]byte(cardsJSON), &cards)

	hd := hashids.NewData()
	hd.Salt = "super secret salt"
	hd.MinLength = 5
	hd.Alphabet = "abcdefghijklmnopqrstuvwxyz"
	HashID = hashids.NewWithData(hd)

	http.HandleFunc("/room/new", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.NotFound(w, r)
			return
		}
		c, _ := HashID.Encode([]int{counter})

		// create new room
		uuidS := uuid.NewV4().String()
		admin := Player{Name: colors[0], Color: colors[0], Admin: true, UUID: uuidS, Spy: false}
		players := []Player{admin}
		rooms[counter] = &Room{ID: counter, Players: players, Started: false}

		// give admin a cookie with UUID
		expiration := time.Now().Add(365 * 24 * time.Hour)
		cookie := http.Cookie{Name: "spyfall", Value: uuidS, Path: "/", Expires: expiration}
		http.SetCookie(w, &cookie)

		t, _ := template.ParseFiles("static/room.html")
		p := &PageLobby{Code: c, ID: counter, Player: admin}
		t.Execute(w, p)
		fmt.Println("created new room", c, counter)

		counter++
	})

	http.HandleFunc("/room/join", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.NotFound(w, r)
			return
		}
		r.ParseForm()
		c := r.Form["Code"][0]
		d := HashID.Decode(c)
		roomID := d[0]
		if room, ok := rooms[roomID]; ok {
			if room.Started {
				http.Redirect(w, r, "/", 303) // TODO: show message that room started
				return
			}

			// create player, with unused color
			uuidS := uuid.NewV4().String()
			color := colors[len(room.Players)]
			player := Player{Name: color, Color: color, Admin: false, UUID: uuidS, Spy: false}

			// set cookie with uuid
			expiration := time.Now().Add(365 * 24 * time.Hour)
			cookie := http.Cookie{Name: "spyfall", Value: uuidS, Path: "/", Expires: expiration}
			http.SetCookie(w, &cookie)

			// add player to the room
			room.Players = append(room.Players, player)

			t, _ := template.ParseFiles("static/room.html")
			p := &PageLobby{Code: c, ID: roomID, Player: player}
			t.Execute(w, p)
		} else {
			http.Redirect(w, r, "/", 303)
			return
		}
	})

	http.HandleFunc("/game", func(w http.ResponseWriter, r *http.Request) {
		roomIDStr := r.URL.Query().Get("id")
		roomID, err := strconv.Atoi(roomIDStr)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		if room, ok := rooms[roomID]; ok {
			if room.Started == false {
				room.setup()
				fmt.Println("Started room", roomID)
			}
			t, _ := template.ParseFiles("static/game.html")
			cookie := r.Cookies()[0]
			uuid := cookie.Value
			player := room.playerForUUID(uuid)

			p := &PageGame{Spy: player.Spy, Location: room.Location, Cards: cards, Countdown: room.Countdown}
			t.Execute(w, p)
		} else {
			http.NotFound(w, r)
			return
		}
	})

	http.HandleFunc("/players.json", func(w http.ResponseWriter, r *http.Request) {
		roomIDStr := r.URL.Query().Get("id")
		roomID, err := strconv.Atoi(roomIDStr)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		if room, ok := rooms[roomID]; ok {
			js, err := json.Marshal(room)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			w.Write(js)
		} else {
			http.NotFound(w, r)
			return
		}
	})

	http.Handle("/", http.FileServer(http.Dir("./static")))
	fmt.Println("starting on port 3000")
	http.ListenAndServe(":3000", nil)
}
<commit_msg>taking port from the cmd line and logging when port cant be bind<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)
import (
	"github.com/satori/go.uuid"
	"github.com/speps/go-hashids"
)

var rooms = make(map[int]*Room)
var counter int = 0
var cards []Card
var HashID *hashids.HashID
var colors = []string{"Magenta", "LightSlateGray", "PaleVioletRed", "Peru", "RebeccaPurple", "LightSeaGreen", "Tomato", "SeaGreen", "Maroon", "GoldenRod", "DarkSlateBlue"}

type Card struct {
	Name string `json:"name"`
	Path string `json:"path"`
}

type PageLobby struct {
	ID     int
	Code   string
	Player Player
}

type PageGame struct {
	Spy       bool
	Location  int
	Cards     []Card
	Countdown int
}

type Room struct {
	ID        int
	Players   []Player `json:"players"`
	Started   bool     `json:"started"`
	Location  int      `json:"-"`
	Countdown int      `json:"-"`
}

type Player struct {
	Name  string `json:"name"`
	Color string `json:"color"`
	Admin bool   `json:"-"`
	UUID  string `json:"-"`
	Spy   bool   `json:"-"`
}

func (r Room) playerForUUID(uuid string) Player {
	for _, value := range r.Players {
		if value.UUID == uuid {
			return value
		}
	}
	return Player{"Grey", "grey", false, "", false} // TODO: return error
}

func (r *Room) setup() {
	// select location
	r.Location = rand.Intn(len(cards))
	// assign a spy
	spy := rand.Intn(len(r.Players))
	r.Players[spy].Spy = true

	// set the countdown
	t := int(time.Now().Unix())
	t = t + (60 * 10)
	r.Countdown = t
	r.Started = true

}

func main() {
	var (
		httpAddr = flag.String("http", ":3000", "HTTP service address")
	)
	flag.Parse()

	cardsJSON, _ := ioutil.ReadFile("static/cards/cards.json")
	json.Unmarshal([]byte(cardsJSON), &cards)

	hd := hashids.NewData()
	hd.Salt = "super secret salt"
	hd.MinLength = 5
	hd.Alphabet = "abcdefghijklmnopqrstuvwxyz"
	HashID = hashids.NewWithData(hd)

	http.HandleFunc("/room/new", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.NotFound(w, r)
			return
		}
		c, _ := HashID.Encode([]int{counter})

		// create new room
		uuidS := uuid.NewV4().String()
		admin := Player{Name: colors[0], Color: colors[0], Admin: true, UUID: uuidS, Spy: false}
		players := []Player{admin}
		rooms[counter] = &Room{ID: counter, Players: players, Started: false}

		// give admin a cookie with UUID
		expiration := time.Now().Add(365 * 24 * time.Hour)
		cookie := http.Cookie{Name: "spyfall", Value: uuidS, Path: "/", Expires: expiration}
		http.SetCookie(w, &cookie)

		t, _ := template.ParseFiles("static/room.html")
		p := &PageLobby{Code: c, ID: counter, Player: admin}
		t.Execute(w, p)
		fmt.Println("created new room", c, counter)

		counter++
	})

	http.HandleFunc("/room/join", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.NotFound(w, r)
			return
		}
		r.ParseForm()
		c := r.Form["Code"][0]
		d := HashID.Decode(c)
		roomID := d[0]
		if room, ok := rooms[roomID]; ok {
			if room.Started {
				http.Redirect(w, r, "/", 303) // TODO: show message that room started
				return
			}

			// create player, with unused color
			uuidS := uuid.NewV4().String()
			color := colors[len(room.Players)]
			player := Player{Name: color, Color: color, Admin: false, UUID: uuidS, Spy: false}

			// set cookie with uuid
			expiration := time.Now().Add(365 * 24 * time.Hour)
			cookie := http.Cookie{Name: "spyfall", Value: uuidS, Path: "/", Expires: expiration}
			http.SetCookie(w, &cookie)

			// add player to the room
			room.Players = append(room.Players, player)

			t, _ := template.ParseFiles("static/room.html")
			p := &PageLobby{Code: c, ID: roomID, Player: player}
			t.Execute(w, p)
		} else {
			http.Redirect(w, r, "/", 303)
			return
		}
	})

	http.HandleFunc("/game", func(w http.ResponseWriter, r *http.Request) {
		roomIDStr := r.URL.Query().Get("id")
		roomID, err := strconv.Atoi(roomIDStr)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		if room, ok := rooms[roomID]; ok {
			if room.Started == false {
				room.setup()
				fmt.Println("Started room", roomID)
			}
			t, _ := template.ParseFiles("static/game.html")
			cookie := r.Cookies()[0]
			uuid := cookie.Value
			player := room.playerForUUID(uuid)

			p := &PageGame{Spy: player.Spy, Location: room.Location, Cards: cards, Countdown: room.Countdown}
			t.Execute(w, p)
		} else {
			http.NotFound(w, r)
			return
		}
	})

	http.HandleFunc("/players.json", func(w http.ResponseWriter, r *http.Request) {
		roomIDStr := r.URL.Query().Get("id")
		roomID, err := strconv.Atoi(roomIDStr)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		if room, ok := rooms[roomID]; ok {
			js, err := json.Marshal(room)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			w.Write(js)
		} else {
			http.NotFound(w, r)
			return
		}
	})

	http.Handle("/", http.FileServer(http.Dir("./static")))
	fmt.Println("listen on", *httpAddr)
	log.Fatal(http.ListenAndServe(*httpAddr, nil))
}
<|endoftext|>


<commit_before>package main

import (
  "github.com/codegangsta/martini"
  "encoding/json"
  "net/http"
  "os"
  "strconv"
  "github.com/jinzhu/gorm"
  _ "github.com/go-sql-driver/mysql"
)

func main() {
  m := martini.Classic()

  dbPassword := os.Getenv("OOPS_DB_PASS")
  db, err := gorm.Open("mysql", "oops:" + dbPassword + "@tcp(15.126.247.23:3306)/oops")
  if err != nil { panic(err) }
  //defer db.Close() //TODO what should this be?

  //db.SetPool(10)

  db.LogMode(true)

  m.Map(db)

  //Cached data
  var allSchools = []School{}
  db.Order("Name").Find(&allSchools)

  allDistricts := []District{}
  db.Order("Name").Find(&allDistricts)

  //These are all the years of active school data. We could also derive it from the DB, possibly
  years := []string{"20022003", "20032004", "20042005", "20052006", "20062007", "20072008", "20082009", "20092010", "20102011", "20112012", "20122013"}

  m.Get("/schools", func(res http.ResponseWriter) string {
    return render(res, allSchools)
  })
  m.Get("/districts", func(res http.ResponseWriter) string {
    compiledDistricts := []District{}
    for _, district := range allDistricts {

      schools := []School{}
      for _, school := range allSchools {
        if school.DistrictId == district.Id {
          schools = append(schools, school)
        }
      }

      copiedDistrict := District{
        Id:  district.Id,
        Name: district.Name,
        CountyId: district.CountyId,
        Schools: schools,
      }
      compiledDistricts = append(compiledDistricts, copiedDistrict)
    }

    return render(res, compiledDistricts)
  })

  //TODO need to also return stats by year -- needs to return []DistrictsByYear - district level stats for each year and each district
  m.Get("/old/districts", func(res http.ResponseWriter) string {
    allDistricts := []District{}
    db.Find(&allDistricts)

    districtsByYear := []DistrictsByYear{}
    for _, year := range years {
      districtByYear := DistrictsByYear{
        Year: year,
      }

      allDistrictsWithYears := []DistrictYear{}
      for _, district := range allDistricts {

        districtWithYear := DistrictYear {
          EnrollmentSize: 2555,
          District: district,
        }
        allDistrictsWithYears = append(allDistrictsWithYears, districtWithYear)
      }

      districtByYear.Districts = allDistrictsWithYears
      districtsByYear = append(districtsByYear, districtByYear)
    }

    return render(res, districtsByYear)
  })

  type YearAndSchool struct {
          SchoolId        int64
          Years                 string
  }

  //TODO need to also return stats by year for each school in the district
  m.Get("/district/:id", func(res http.ResponseWriter, params martini.Params) string {
    districtId := params["id"]

    district := District{}
    db.Where("id = ?", districtId).First(&district);

    var districtStats = []DistrictClassStats{}
    db.Where("district_id = ?", districtId).Find(&districtStats)

    yearsToEnrollments := map[string][]GradeEnrollment{}
    for _, row := range districtStats {
      if row.EnrollmentSize > 0 {
        yearsToEnrollments[row.Years] = append(yearsToEnrollments[row.Years], GradeEnrollment{
          Grade: row.Grade,
          EnrollmentSize: row.EnrollmentSize,
        })
      }
    }

    enrollmentsByYear := []EnrollmentByYear{}
    for _, year := range years {
      enrollment := yearsToEnrollments[year]
      var classSize int64 = 0
      for _, i := range enrollment { classSize += i.EnrollmentSize }

      enrollmentsByYear = append(enrollmentsByYear, EnrollmentByYear{
        Year: year,
        GradeEnrollment: enrollment,
        Students : classSize,
      })
    }

    return render(res, DistrictWithYears{
      District: district,
      EnrollmentsByYear: enrollmentsByYear,
    })
  })

  //This is the original district/:id. We probably can delete it.
  m.Get("/old/district/:id", func(res http.ResponseWriter, params martini.Params) string {

    schoolsByYear := []SchoolsByYear{}

    schoolsInDistrict := []School{}
    db.Where("district_id = ?", params["id"]).Find(&schoolsInDistrict)

    var schoolStats = []SchoolStat{}
    db.Where("school_id LIKE ?", params["id"] + "%").Find(&schoolStats)

    schoolAndYearToStats := map[YearAndSchool]SchoolStat{}
    for _, schoolStat := range schoolStats {
      schoolAndYearToStats[YearAndSchool{
        SchoolId: schoolStat.SchoolId,
        Years: schoolStat.Years,
      }] = schoolStat
    }

    //TODO Use years from db? - if so make sure they're in the right order
    for _, year := range years {

      schoolsInYear := []SchoolYear{}
      for _, school := range schoolsInDistrict {
        var enrollment = schoolAndYearToStats[YearAndSchool{
          SchoolId: school.Id,
          Years: year,
        }].EnrollmentSize
        if enrollment > 0 {
          schoolsInYear = append(schoolsInYear, SchoolYear {
            EnrollmentSize: enrollment,
            School: school,
          })
        }
      }

      schoolsByYear = append(schoolsByYear, SchoolsByYear{
        Year: year,
        Schools: schoolsInYear,
      })
    }

    return render(res, schoolsByYear)
  })

  //TODO Possible clean-up opportunities here, but it's looking pretty decent
  m.Get("/school/:id", func(res http.ResponseWriter, params martini.Params) string {
    var school = School{}
    schoolId := params["id"]
    db.Where("id = ?", schoolId).First(&school)

    var classStats = []ClassStat{}
    db.Where("school_id = ?", params["id"]).Find(&classStats)

    var yearsToEnrollments = map[string][]GradeEnrollment{}
    for _, row := range classStats {
      if row.EnrollmentSize > 0 {
        yearsToEnrollments[row.Years] = append(yearsToEnrollments[row.Years], GradeEnrollment{
          Grade: row.Grade,
          EnrollmentSize:    row.EnrollmentSize,
        })
      }
    }

    var schoolStats = []SchoolStat{}
    db.Where("school_id = ?", params["id"]).Find(&schoolStats)

    var yearsToTotalStats = map[string]SchoolStat{}
    for _, row := range schoolStats {
            yearsToTotalStats[row.Years] = row
    }

    var enrollmentData = []EnrollmentByYear{}
    //Using hard coded years to make sure they're returned in order:
    for _, year := range years {
        studentCount := yearsToTotalStats[year].EnrollmentSize
        if studentCount > 0 {
        	teachersAsFloat, _ := strconv.ParseFloat(yearsToTotalStats[year].TeacherSize, 64)
        	enrollmentData = append(enrollmentData, EnrollmentByYear{
        		Year: year,
        		GradeEnrollment: yearsToEnrollments[year],
        		Students: yearsToTotalStats[year].EnrollmentSize,
        		Teachers: int64(teachersAsFloat),
      		})
      }
    }

    //put it all together
    var allData = SchoolWithEnrollment {
      School: school,
      EnrollmentByYear: enrollmentData,
    }

    return render(res, allData)
  })

  m.Run();
}

func render(res http.ResponseWriter, data interface{}) string {
  thing, err := json.Marshal(data)
  if err != nil { panic(err) }
  return asJson(res, thing)
}

func asJson(res http.ResponseWriter, data []byte) string {
  res.Header().Set("Content-Type", "application/json")
  res.Header().Set("Access-Control-Allow-Origin", "*")
  return string(data[:])
}
<commit_msg>Update server.go<commit_after>package main

import (
  "github.com/codegangsta/martini"
  "encoding/json"
  "net/http"
  "os"
  "strconv"
  "github.com/jinzhu/gorm"
  _ "github.com/go-sql-driver/mysql"
)

func main() {
  m := martini.Classic()

  dbPassword := os.Getenv("OOPS_DB_PASS")
  db, err := gorm.Open("mysql", "oops:" + dbPassword + "@tcp(15.126.247.23:3306)/oops")
  if err != nil { panic(err) }
  //defer db.Close() //TODO what should this be?

  //db.SetPool(10)

  db.LogMode(true)

  m.Map(db)

  //Cached data
  var allSchools = []School{}
  db.Order("Name").Find(&allSchools)

  allDistricts := []District{}
  db.Order("Name").Find(&allDistricts)

  //These are all the years of active school data. We could also derive it from the DB, possibly
  years := []string{"20022003", "20032004", "20042005", "20052006", "20062007", "20072008", "20082009", "20092010", "20102011", "20112012", "20122013"}

  m.Get("/schools", func(res http.ResponseWriter) string {
    return render(res, allSchools)
  })
  m.Get("/districts", func(res http.ResponseWriter) string {
    compiledDistricts := []District{}
    for _, district := range allDistricts {

      schools := []School{}
      for _, school := range allSchools {
        if school.DistrictId == district.Id {
          schools = append(schools, school)
        }
      }

      copiedDistrict := District{
        Id:  district.Id,
        Name: district.Name,
        CountyId: district.CountyId,
        Schools: schools,
      }
      compiledDistricts = append(compiledDistricts, copiedDistrict)
    }

    return render(res, compiledDistricts)
  })

  type YearAndSchool struct {
          SchoolId        int64
          Years                 string
  }

  //TODO need to also return stats by year for each school in the district
  m.Get("/district/:id", func(res http.ResponseWriter, params martini.Params) string {
    districtId := params["id"]

    district := District{}
    db.Where("id = ?", districtId).First(&district);

    var districtStats = []DistrictClassStats{}
    db.Where("district_id = ?", districtId).Find(&districtStats)

    yearsToEnrollments := map[string][]GradeEnrollment{}
    for _, row := range districtStats {
      if row.EnrollmentSize > 0 {
        yearsToEnrollments[row.Years] = append(yearsToEnrollments[row.Years], GradeEnrollment{
          Grade: row.Grade,
          EnrollmentSize: row.EnrollmentSize,
        })
      }
    }

    enrollmentsByYear := []EnrollmentByYear{}
    for _, year := range years {
      enrollment := yearsToEnrollments[year]
      var classSize int64 = 0
      for _, i := range enrollment { classSize += i.EnrollmentSize }

      enrollmentsByYear = append(enrollmentsByYear, EnrollmentByYear{
        Year: year,
        GradeEnrollment: enrollment,
        Students : classSize,
      })
    }

    return render(res, DistrictWithYears{
      District: district,
      EnrollmentsByYear: enrollmentsByYear,
    })
  })

  //TODO Possible clean-up opportunities here, but it's looking pretty decent
  m.Get("/school/:id", func(res http.ResponseWriter, params martini.Params) string {
    var school = School{}
    schoolId := params["id"]
    db.Where("id = ?", schoolId).First(&school)

    var classStats = []ClassStat{}
    db.Where("school_id = ?", params["id"]).Find(&classStats)

    var yearsToEnrollments = map[string][]GradeEnrollment{}
    for _, row := range classStats {
      if row.EnrollmentSize > 0 {
        yearsToEnrollments[row.Years] = append(yearsToEnrollments[row.Years], GradeEnrollment{
          Grade: row.Grade,
          EnrollmentSize:    row.EnrollmentSize,
        })
      }
    }

    var schoolStats = []SchoolStat{}
    db.Where("school_id = ?", params["id"]).Find(&schoolStats)

    var yearsToTotalStats = map[string]SchoolStat{}
    for _, row := range schoolStats {
            yearsToTotalStats[row.Years] = row
    }

    var enrollmentData = []EnrollmentByYear{}
    //Using hard coded years to make sure they're returned in order:
    for _, year := range years {
        studentCount := yearsToTotalStats[year].EnrollmentSize
        if studentCount > 0 {
        	teachersAsFloat, _ := strconv.ParseFloat(yearsToTotalStats[year].TeacherSize, 64)
        	enrollmentData = append(enrollmentData, EnrollmentByYear{
        		Year: year,
        		GradeEnrollment: yearsToEnrollments[year],
        		Students: yearsToTotalStats[year].EnrollmentSize,
        		Teachers: int64(teachersAsFloat),
      		})
      }
    }

    //put it all together
    var allData = SchoolWithEnrollment {
      School: school,
      EnrollmentByYear: enrollmentData,
    }

    return render(res, allData)
  })

  m.Run();
}

func render(res http.ResponseWriter, data interface{}) string {
  thing, err := json.Marshal(data)
  if err != nil { panic(err) }
  return asJson(res, thing)
}

func asJson(res http.ResponseWriter, data []byte) string {
  res.Header().Set("Content-Type", "application/json")
  res.Header().Set("Access-Control-Allow-Origin", "*")
  return string(data[:])
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"github.com/PuerkitoBio/goquery"
	"github.com/stretchr/testify/assert"
	"os"
	"testing"
)

func TestGetPurolandNewsFromDocument(t *testing.T) {
	f, err := os.Open("data/puroland.html")
	defer f.Close()
	if err != nil {
		t.Fatal(err)
	}
	doc, err := goquery.NewDocumentFromReader(bufio.NewReader(f))
	if err != nil {
		t.Fatal(err)
	}
	feed, err := GetPurolandNewsFromDocument(doc)
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, len(feed.Items), 10)
}

func TestGetPurolandInfoFromDocument(t *testing.T) {
	f, err := os.Open("data/puroland.html")
	defer f.Close()
	if err != nil {
		t.Fatal(err)
	}
	doc, err := goquery.NewDocumentFromReader(bufio.NewReader(f))
	if err != nil {
		t.Fatal(err)
	}
	feed, err := GetPurolandInfoFromDocument(doc)
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, len(feed.Items), 49)
}
<commit_msg>Fix test<commit_after>package main

import (
	"bufio"
	"github.com/PuerkitoBio/goquery"
	"github.com/stretchr/testify/assert"
	"os"
	"testing"
)

func TestGetPurolandNewsFromDocument(t *testing.T) {
	f, err := os.Open("data/puroland.html")
	defer f.Close()
	if err != nil {
		t.Fatal(err)
	}
	doc, err := goquery.NewDocumentFromReader(bufio.NewReader(f))
	if err != nil {
		t.Fatal(err)
	}
	feed, err := GetPurolandNewsFromDocument(doc)
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, len(feed.Items), 10)
}

func TestGetPurolandInfoFromDocument(t *testing.T) {
	f, err := os.Open("data/puroland.html")
	defer f.Close()
	if err != nil {
		t.Fatal(err)
	}
	doc, err := goquery.NewDocumentFromReader(bufio.NewReader(f))
	if err != nil {
		t.Fatal(err)
	}
	feed, err := GetPurolandInfoFromDocument(doc)
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, len(feed.Items), 48)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/miekg/dns"
	"github.com/spf13/pflag"
	"k8s.io/klog"
	"k8s.io/kops/protokube/pkg/gossip/dns/hosts"
)

var (
	flags = pflag.NewFlagSet("", pflag.ExitOnError)
	// BuildVersion is overwritten during build. This can be used to resolve issues.
	BuildVersion = "0.1"
)

func main() {
	fmt.Printf("kube-discovery version %s\n", BuildVersion)

	if err := run(); err != nil {
		klog.Errorf("unexpected error: %v", err)
		os.Exit(1)
	}
	os.Exit(0)
}

type Options struct {
	Containerized       bool
	ClusterID           string
	DnsDiscoveryTimeout time.Duration
	Interval            time.Duration
	Prefixes            []string
}

func (o *Options) InitDefaults() {
	o.DnsDiscoveryTimeout = 5 * time.Second
	o.Interval = 60 * time.Second
	o.Prefixes = []string{"api.internal."}
}

// run is responsible for running the protokube service controller
func run() error {
	var o Options
	o.InitDefaults()

	flags.BoolVar(&o.Containerized, "containerized", o.Containerized, "Set if we are running containerized.")
	flags.StringVar(&o.ClusterID, "cluster-id", o.ClusterID, "Cluster ID")

	// Trick to avoid 'logging before flag.Parse' warning
	flag.CommandLine.Parse([]string{})

	flag.Set("logtostderr", "true")
	flags.AddGoFlagSet(flag.CommandLine)
	flags.Parse(os.Args)

	if o.ClusterID == "" {
		klog.Infof("updating records for all discovered clusters")
	} else {
		klog.Infof("updating records for cluster %q", o.ClusterID)
	}

	c := &DiscoveryController{
		Options: o,
	}
	return c.Run()
}

type DiscoveryController struct {
	Options Options
}

func (c *DiscoveryController) Run() error {
	for {
		err := c.runOnce()
		if err != nil {
			klog.Warningf("error updating records: %v", err)
		}
		time.Sleep(c.Options.Interval)
	}
}

func (c *DiscoveryController) runOnce() error {
	o := &c.Options

	rootfs := "/"
	if o.Containerized {
		rootfs = "/rootfs/"
	}

	clusters, err := discoverKubernetesClusters(o.DnsDiscoveryTimeout)
	if err != nil {
		return fmt.Errorf("error from dns resolve: %v", err)
	}

	klog.Infof("clusters: %v", clusters)

	hostsPath := filepath.Join(rootfs, "etc/hosts")

	addrToHosts := make(map[string][]string)
	for k, addrs := range clusters {
		if o.ClusterID != "" {
			if k != o.ClusterID {
				klog.V(2).Infof("skipping discovered cluster %q as does not match configured %q", k, o.ClusterID)
				continue
			}
		}
		for _, addr := range addrs {
			addrString := addr.String()
			for _, prefix := range o.Prefixes {
				addrToHosts[addrString] = append(addrToHosts[addrString], prefix+k)
			}
		}
	}

	if len(addrToHosts) == 0 {
		// We don't update if there are no records remaining, just in case it is a transient blip
		klog.Warningf("no records found; skipping update")
		return nil
	}

	// TODO: Combined with previously discovered records (with a sliding window?)
	// TODO: Support an iptables / ipvs backend?
	// TODO: Verify resolved records against certificates?
	if err := hosts.UpdateHostsFileWithRecords(hostsPath, addrToHosts); err != nil {
		return fmt.Errorf("error updating hosts file: %v", err)
	}
	klog.Infof("updated %s", hostsPath)

	return nil
}

func discoverKubernetesClusters(timeout time.Duration) (map[string][]net.IP, error) {
	addr := &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251), Port: 5353}
	connection, err := net.ListenMulticastUDP("udp4", nil, addr)
	if err != nil {
		return nil, fmt.Errorf("error lisening for multicast: %v", err)
	}

	defer func() {
		err := connection.Close()
		if err != nil {
			klog.Warningf("error closing multicast connection: %v", err)
		}
	}()

	serviceName := "_kubernetes._tcp.local."

	{
		m := new(dns.Msg)
		m.SetQuestion(serviceName, dns.TypePTR)
		m.RecursionDesired = false
		buf, err := m.Pack()
		if err != nil {
			return nil, fmt.Errorf("error building DNS query: %v", err)
		}
		if _, err := connection.WriteToUDP(buf, addr); err != nil {
			return nil, fmt.Errorf("error sending DNS query: %v", err)
		}
	}

	stopAt := time.Now().Add(timeout)

	if err := connection.SetReadDeadline(stopAt); err != nil {
		return nil, fmt.Errorf("error setting socket read deadline: %v", err)
	}

	ptrs := make(map[string][]*dns.PTR)
	srvs := make(map[string][]*dns.SRV)
	txts := make(map[string][]*dns.TXT)
	aaaas := make(map[string][]*dns.AAAA)
	as := make(map[string][]*dns.A)

	buf := make([]byte, 65536)
	for {
		n, err := connection.Read(buf)
		if err != nil {
			if err, ok := err.(net.Error); ok && err.Timeout() {
				break
			}
			return nil, fmt.Errorf("error reading UDP response: %v", err)
		}
		msg := new(dns.Msg)
		if err := msg.Unpack(buf[:n]); err != nil {
			klog.Warningf("got unparseable DNS packet: %v", err)
			continue
		}
		klog.V(4).Infof("got response: %v", msg)

		for _, rr := range msg.Answer {
			switch rr := rr.(type) {
			case *dns.PTR:
				klog.V(4).Infof("PTR %v", rr)
				ptrs[rr.Hdr.Name] = append(ptrs[rr.Hdr.Name], rr)
			case *dns.TXT:
				klog.V(4).Infof("TXT %v", rr)
				txts[rr.Hdr.Name] = append(txts[rr.Hdr.Name], rr)
			case *dns.SRV:
				klog.V(4).Infof("SRV %v", rr)
				srvs[rr.Hdr.Name] = append(srvs[rr.Hdr.Name], rr)
			case *dns.AAAA:
				klog.V(4).Infof("AAAA %v", rr)
				aaaas[rr.Hdr.Name] = append(aaaas[rr.Hdr.Name], rr)
			case *dns.A:
				klog.V(4).Infof("A %v", rr)
				as[rr.Hdr.Name] = append(as[rr.Hdr.Name], rr)
			default:
				klog.V(2).Infof("ignoring answer of unknown type %T: %v", rr, rr)
			}
		}
	}

	addrs := make(map[string][]net.IP)

	for _, ptr := range ptrs[serviceName] {
		instance := strings.TrimSuffix(ptr.Ptr, serviceName)
		instance = strings.TrimSuffix(instance, ".")

		// Dots in the instance name are escaped
		instance = strings.Replace(instance, "\\.", ".", -1)

		for _, srv := range srvs[ptr.Ptr] {
			// TODO: Ignore if port is not 443?
			for _, a := range as[srv.Target] {
				ensureInMap(addrs, instance, a.A)
			}
			for _, aaaa := range aaaas[srv.Target] {
				ensureInMap(addrs, instance, aaaa.AAAA)
			}
		}
	}

	return addrs, nil
}

func ensureInMap(addrs map[string][]net.IP, name string, ip net.IP) {
	// Avoid duplicates
	for _, existing := range addrs[name] {
		if ip.Equal(existing) {
			return
		}
	}
	addrs[name] = append(addrs[name], ip)
}
<commit_msg>cleanup: fix error message typos<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/miekg/dns"
	"github.com/spf13/pflag"
	"k8s.io/klog"
	"k8s.io/kops/protokube/pkg/gossip/dns/hosts"
)

var (
	flags = pflag.NewFlagSet("", pflag.ExitOnError)
	// BuildVersion is overwritten during build. This can be used to resolve issues.
	BuildVersion = "0.1"
)

func main() {
	fmt.Printf("kube-discovery version %s\n", BuildVersion)

	if err := run(); err != nil {
		klog.Errorf("unexpected error: %v", err)
		os.Exit(1)
	}
	os.Exit(0)
}

type Options struct {
	Containerized       bool
	ClusterID           string
	DnsDiscoveryTimeout time.Duration
	Interval            time.Duration
	Prefixes            []string
}

func (o *Options) InitDefaults() {
	o.DnsDiscoveryTimeout = 5 * time.Second
	o.Interval = 60 * time.Second
	o.Prefixes = []string{"api.internal."}
}

// run is responsible for running the protokube service controller
func run() error {
	var o Options
	o.InitDefaults()

	flags.BoolVar(&o.Containerized, "containerized", o.Containerized, "Set if we are running containerized.")
	flags.StringVar(&o.ClusterID, "cluster-id", o.ClusterID, "Cluster ID")

	// Trick to avoid 'logging before flag.Parse' warning
	flag.CommandLine.Parse([]string{})

	flag.Set("logtostderr", "true")
	flags.AddGoFlagSet(flag.CommandLine)
	flags.Parse(os.Args)

	if o.ClusterID == "" {
		klog.Infof("updating records for all discovered clusters")
	} else {
		klog.Infof("updating records for cluster %q", o.ClusterID)
	}

	c := &DiscoveryController{
		Options: o,
	}
	return c.Run()
}

type DiscoveryController struct {
	Options Options
}

func (c *DiscoveryController) Run() error {
	for {
		err := c.runOnce()
		if err != nil {
			klog.Warningf("error updating records: %v", err)
		}
		time.Sleep(c.Options.Interval)
	}
}

func (c *DiscoveryController) runOnce() error {
	o := &c.Options

	rootfs := "/"
	if o.Containerized {
		rootfs = "/rootfs/"
	}

	clusters, err := discoverKubernetesClusters(o.DnsDiscoveryTimeout)
	if err != nil {
		return fmt.Errorf("error from dns resolve: %v", err)
	}

	klog.Infof("clusters: %v", clusters)

	hostsPath := filepath.Join(rootfs, "etc/hosts")

	addrToHosts := make(map[string][]string)
	for k, addrs := range clusters {
		if o.ClusterID != "" {
			if k != o.ClusterID {
				klog.V(2).Infof("skipping discovered cluster %q as does not match configured %q", k, o.ClusterID)
				continue
			}
		}
		for _, addr := range addrs {
			addrString := addr.String()
			for _, prefix := range o.Prefixes {
				addrToHosts[addrString] = append(addrToHosts[addrString], prefix+k)
			}
		}
	}

	if len(addrToHosts) == 0 {
		// We don't update if there are no records remaining, just in case it is a transient blip
		klog.Warningf("no records found; skipping update")
		return nil
	}

	// TODO: Combined with previously discovered records (with a sliding window?)
	// TODO: Support an iptables / ipvs backend?
	// TODO: Verify resolved records against certificates?
	if err := hosts.UpdateHostsFileWithRecords(hostsPath, addrToHosts); err != nil {
		return fmt.Errorf("error updating hosts file: %v", err)
	}
	klog.Infof("updated %s", hostsPath)

	return nil
}

func discoverKubernetesClusters(timeout time.Duration) (map[string][]net.IP, error) {
	addr := &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251), Port: 5353}
	connection, err := net.ListenMulticastUDP("udp4", nil, addr)
	if err != nil {
		return nil, fmt.Errorf("error listening for multicast: %v", err)
	}

	defer func() {
		err := connection.Close()
		if err != nil {
			klog.Warningf("error closing multicast connection: %v", err)
		}
	}()

	serviceName := "_kubernetes._tcp.local."

	{
		m := new(dns.Msg)
		m.SetQuestion(serviceName, dns.TypePTR)
		m.RecursionDesired = false
		buf, err := m.Pack()
		if err != nil {
			return nil, fmt.Errorf("error building DNS query: %v", err)
		}
		if _, err := connection.WriteToUDP(buf, addr); err != nil {
			return nil, fmt.Errorf("error sending DNS query: %v", err)
		}
	}

	stopAt := time.Now().Add(timeout)

	if err := connection.SetReadDeadline(stopAt); err != nil {
		return nil, fmt.Errorf("error setting socket read deadline: %v", err)
	}

	ptrs := make(map[string][]*dns.PTR)
	srvs := make(map[string][]*dns.SRV)
	txts := make(map[string][]*dns.TXT)
	aaaas := make(map[string][]*dns.AAAA)
	as := make(map[string][]*dns.A)

	buf := make([]byte, 65536)
	for {
		n, err := connection.Read(buf)
		if err != nil {
			if err, ok := err.(net.Error); ok && err.Timeout() {
				break
			}
			return nil, fmt.Errorf("error reading UDP response: %v", err)
		}
		msg := new(dns.Msg)
		if err := msg.Unpack(buf[:n]); err != nil {
			klog.Warningf("got unparseable DNS packet: %v", err)
			continue
		}
		klog.V(4).Infof("got response: %v", msg)

		for _, rr := range msg.Answer {
			switch rr := rr.(type) {
			case *dns.PTR:
				klog.V(4).Infof("PTR %v", rr)
				ptrs[rr.Hdr.Name] = append(ptrs[rr.Hdr.Name], rr)
			case *dns.TXT:
				klog.V(4).Infof("TXT %v", rr)
				txts[rr.Hdr.Name] = append(txts[rr.Hdr.Name], rr)
			case *dns.SRV:
				klog.V(4).Infof("SRV %v", rr)
				srvs[rr.Hdr.Name] = append(srvs[rr.Hdr.Name], rr)
			case *dns.AAAA:
				klog.V(4).Infof("AAAA %v", rr)
				aaaas[rr.Hdr.Name] = append(aaaas[rr.Hdr.Name], rr)
			case *dns.A:
				klog.V(4).Infof("A %v", rr)
				as[rr.Hdr.Name] = append(as[rr.Hdr.Name], rr)
			default:
				klog.V(2).Infof("ignoring answer of unknown type %T: %v", rr, rr)
			}
		}
	}

	addrs := make(map[string][]net.IP)

	for _, ptr := range ptrs[serviceName] {
		instance := strings.TrimSuffix(ptr.Ptr, serviceName)
		instance = strings.TrimSuffix(instance, ".")

		// Dots in the instance name are escaped
		instance = strings.Replace(instance, "\\.", ".", -1)

		for _, srv := range srvs[ptr.Ptr] {
			// TODO: Ignore if port is not 443?
			for _, a := range as[srv.Target] {
				ensureInMap(addrs, instance, a.A)
			}
			for _, aaaa := range aaaas[srv.Target] {
				ensureInMap(addrs, instance, aaaa.AAAA)
			}
		}
	}

	return addrs, nil
}

func ensureInMap(addrs map[string][]net.IP, name string, ip net.IP) {
	// Avoid duplicates
	for _, existing := range addrs[name] {
		if ip.Equal(existing) {
			return
		}
	}
	addrs[name] = append(addrs[name], ip)
}
<|endoftext|>


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

package raft

import "fmt"

const (
	ProgressStateProbe ProgressStateType = iota
	ProgressStateReplicate
	ProgressStateSnapshot
)

type ProgressStateType uint64

var prstmap = [...]string{
	"ProgressStateProbe",
	"ProgressStateReplicate",
	"ProgressStateSnapshot",
}

func (st ProgressStateType) String() string { return prstmap[uint64(st)] }

// Progress represents a follower’s progress in the view of the leader. Leader maintains
// progresses of all followers, and sends entries to the follower based on its progress.
type Progress struct {
	Match, Next uint64
	// State defines how the leader should interact with the follower.
	//
	// When in ProgressStateProbe, leader sends at most one replication message
	// per heartbeat interval. It also probes actual progress of the follower.
	//
	// When in ProgressStateReplicate, leader optimistically increases next
	// to the latest entry sent after sending replication message. This is
	// an optimized state for fast replicating log entries to the follower.
	//
	// When in ProgressStateSnapshot, leader should have sent out snapshot
	// before and stops sending any replication message.
	State ProgressStateType
	// Paused is used in ProgressStateProbe.
	// When Paused is true, raft should pause sending replication message to this peer.
	Paused bool
	// PendingSnapshot is used in ProgressStateSnapshot.
	// If there is a pending snapshot, the pendingSnapshot will be set to the
	// index of the snapshot. If pendingSnapshot is set, the replication process of
	// this Progress will be paused. raft will not resend snapshot until the pending one
	// is reported to be failed.
	PendingSnapshot uint64

	// RecentActive is true if the progress is recently active. Receiving any messages
	// from the corresponding follower indicates the progress is active.
	// RecentActive can be reset to false after an election timeout.
	RecentActive bool

	// inflights is a sliding window for the inflight messages.
	// When inflights is full, no more message should be sent.
	// When a leader sends out a message, the index of the last
	// entry should be added to inflights. The index MUST be added
	// into inflights in order.
	// When a leader receives a reply, the previous inflights should
	// be freed by calling inflights.freeTo.
	ins *inflights
}

func (pr *Progress) resetState(state ProgressStateType) {
	pr.Paused = false
	pr.RecentActive = false
	pr.PendingSnapshot = 0
	pr.State = state
	pr.ins.reset()
}

func (pr *Progress) becomeProbe() {
	// If the original state is ProgressStateSnapshot, progress knows that
	// the pending snapshot has been sent to this peer successfully, then
	// probes from pendingSnapshot + 1.
	if pr.State == ProgressStateSnapshot {
		pendingSnapshot := pr.PendingSnapshot
		pr.resetState(ProgressStateProbe)
		pr.Next = max(pr.Match+1, pendingSnapshot+1)
	} else {
		pr.resetState(ProgressStateProbe)
		pr.Next = pr.Match + 1
	}
}

func (pr *Progress) becomeReplicate() {
	pr.resetState(ProgressStateReplicate)
	pr.Next = pr.Match + 1
}

func (pr *Progress) becomeSnapshot(snapshoti uint64) {
	pr.resetState(ProgressStateSnapshot)
	pr.PendingSnapshot = snapshoti
}

// maybeUpdate returns false if the given n index comes from an outdated message.
// Otherwise it updates the progress and returns true.
func (pr *Progress) maybeUpdate(n uint64) bool {
	var updated bool
	if pr.Match < n {
		pr.Match = n
		updated = true
		pr.resume()
	}
	if pr.Next < n+1 {
		pr.Next = n + 1
	}
	return updated
}

func (pr *Progress) optimisticUpdate(n uint64) { pr.Next = n + 1 }

// maybeDecrTo returns false if the given to index comes from an out of order message.
// Otherwise it decreases the progress next index to min(rejected, last) and returns true.
func (pr *Progress) maybeDecrTo(rejected, last uint64) bool {
	if pr.State == ProgressStateReplicate {
		// the rejection must be stale if the progress has matched and "rejected"
		// is smaller than "match".
		if rejected <= pr.Match {
			return false
		}
		// directly decrease next to match + 1
		pr.Next = pr.Match + 1
		return true
	}

	// the rejection must be stale if "rejected" does not match next - 1
	if pr.Next-1 != rejected {
		return false
	}

	if pr.Next = min(rejected, last+1); pr.Next < 1 {
		pr.Next = 1
	}
	pr.resume()
	return true
}

func (pr *Progress) pause()  { pr.Paused = true }
func (pr *Progress) resume() { pr.Paused = false }

// isPaused returns whether progress stops sending message.
func (pr *Progress) isPaused() bool {
	switch pr.State {
	case ProgressStateProbe:
		return pr.Paused
	case ProgressStateReplicate:
		return pr.ins.full()
	case ProgressStateSnapshot:
		return true
	default:
		panic("unexpected state")
	}
}

func (pr *Progress) snapshotFailure() { pr.PendingSnapshot = 0 }

// needSnapshotAbort returns true if snapshot progress's Match
// is equal or higher than the pendingSnapshot.
func (pr *Progress) needSnapshotAbort() bool {
	return pr.State == ProgressStateSnapshot && pr.Match >= pr.PendingSnapshot
}

func (pr *Progress) String() string {
	return fmt.Sprintf("next = %d, match = %d, state = %s, waiting = %v, pendingSnapshot = %d", pr.Next, pr.Match, pr.State, pr.isPaused(), pr.PendingSnapshot)
}

type inflights struct {
	// the starting index in the buffer
	start int
	// number of inflights in the buffer
	count int

	// the size of the buffer
	size   int
	buffer []uint64
}

func newInflights(size int) *inflights {
	return &inflights{
		size:   size,
		buffer: make([]uint64, size),
	}
}

// add adds an inflight into inflights
func (in *inflights) add(inflight uint64) {
	if in.full() {
		panic("cannot add into a full inflights")
	}
	next := in.start + in.count
	if next >= in.size {
		next -= in.size
	}
	in.buffer[next] = inflight
	in.count++
}

// freeTo frees the inflights smaller or equal to the given `to` flight.
func (in *inflights) freeTo(to uint64) {
	if in.count == 0 || to < in.buffer[in.start] {
		// out of the left side of the window
		return
	}

	i, idx := 0, in.start
	for i = 0; i < in.count; i++ {
		if to < in.buffer[idx] { // found the first large inflight
			break
		}

		// increase index and maybe rotate
		if idx++; idx >= in.size {
			idx -= in.size
		}
	}
	// free i inflights and set new start index
	in.count -= i
	in.start = idx
}

func (in *inflights) freeFirstOne() { in.freeTo(in.buffer[in.start]) }

// full returns true if the inflights is full.
func (in *inflights) full() bool {
	return in.count == in.size
}

// resets frees all inflights.
func (in *inflights) reset() {
	in.count = 0
	in.start = 0
}
<commit_msg>raft: do not change RecentActive when resetState for progress<commit_after>// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package raft

import "fmt"

const (
	ProgressStateProbe ProgressStateType = iota
	ProgressStateReplicate
	ProgressStateSnapshot
)

type ProgressStateType uint64

var prstmap = [...]string{
	"ProgressStateProbe",
	"ProgressStateReplicate",
	"ProgressStateSnapshot",
}

func (st ProgressStateType) String() string { return prstmap[uint64(st)] }

// Progress represents a follower’s progress in the view of the leader. Leader maintains
// progresses of all followers, and sends entries to the follower based on its progress.
type Progress struct {
	Match, Next uint64
	// State defines how the leader should interact with the follower.
	//
	// When in ProgressStateProbe, leader sends at most one replication message
	// per heartbeat interval. It also probes actual progress of the follower.
	//
	// When in ProgressStateReplicate, leader optimistically increases next
	// to the latest entry sent after sending replication message. This is
	// an optimized state for fast replicating log entries to the follower.
	//
	// When in ProgressStateSnapshot, leader should have sent out snapshot
	// before and stops sending any replication message.
	State ProgressStateType
	// Paused is used in ProgressStateProbe.
	// When Paused is true, raft should pause sending replication message to this peer.
	Paused bool
	// PendingSnapshot is used in ProgressStateSnapshot.
	// If there is a pending snapshot, the pendingSnapshot will be set to the
	// index of the snapshot. If pendingSnapshot is set, the replication process of
	// this Progress will be paused. raft will not resend snapshot until the pending one
	// is reported to be failed.
	PendingSnapshot uint64

	// RecentActive is true if the progress is recently active. Receiving any messages
	// from the corresponding follower indicates the progress is active.
	// RecentActive can be reset to false after an election timeout.
	RecentActive bool

	// inflights is a sliding window for the inflight messages.
	// When inflights is full, no more message should be sent.
	// When a leader sends out a message, the index of the last
	// entry should be added to inflights. The index MUST be added
	// into inflights in order.
	// When a leader receives a reply, the previous inflights should
	// be freed by calling inflights.freeTo.
	ins *inflights
}

func (pr *Progress) resetState(state ProgressStateType) {
	pr.Paused = false
	pr.PendingSnapshot = 0
	pr.State = state
	pr.ins.reset()
}

func (pr *Progress) becomeProbe() {
	// If the original state is ProgressStateSnapshot, progress knows that
	// the pending snapshot has been sent to this peer successfully, then
	// probes from pendingSnapshot + 1.
	if pr.State == ProgressStateSnapshot {
		pendingSnapshot := pr.PendingSnapshot
		pr.resetState(ProgressStateProbe)
		pr.Next = max(pr.Match+1, pendingSnapshot+1)
	} else {
		pr.resetState(ProgressStateProbe)
		pr.Next = pr.Match + 1
	}
}

func (pr *Progress) becomeReplicate() {
	pr.resetState(ProgressStateReplicate)
	pr.Next = pr.Match + 1
}

func (pr *Progress) becomeSnapshot(snapshoti uint64) {
	pr.resetState(ProgressStateSnapshot)
	pr.PendingSnapshot = snapshoti
}

// maybeUpdate returns false if the given n index comes from an outdated message.
// Otherwise it updates the progress and returns true.
func (pr *Progress) maybeUpdate(n uint64) bool {
	var updated bool
	if pr.Match < n {
		pr.Match = n
		updated = true
		pr.resume()
	}
	if pr.Next < n+1 {
		pr.Next = n + 1
	}
	return updated
}

func (pr *Progress) optimisticUpdate(n uint64) { pr.Next = n + 1 }

// maybeDecrTo returns false if the given to index comes from an out of order message.
// Otherwise it decreases the progress next index to min(rejected, last) and returns true.
func (pr *Progress) maybeDecrTo(rejected, last uint64) bool {
	if pr.State == ProgressStateReplicate {
		// the rejection must be stale if the progress has matched and "rejected"
		// is smaller than "match".
		if rejected <= pr.Match {
			return false
		}
		// directly decrease next to match + 1
		pr.Next = pr.Match + 1
		return true
	}

	// the rejection must be stale if "rejected" does not match next - 1
	if pr.Next-1 != rejected {
		return false
	}

	if pr.Next = min(rejected, last+1); pr.Next < 1 {
		pr.Next = 1
	}
	pr.resume()
	return true
}

func (pr *Progress) pause()  { pr.Paused = true }
func (pr *Progress) resume() { pr.Paused = false }

// isPaused returns whether progress stops sending message.
func (pr *Progress) isPaused() bool {
	switch pr.State {
	case ProgressStateProbe:
		return pr.Paused
	case ProgressStateReplicate:
		return pr.ins.full()
	case ProgressStateSnapshot:
		return true
	default:
		panic("unexpected state")
	}
}

func (pr *Progress) snapshotFailure() { pr.PendingSnapshot = 0 }

// needSnapshotAbort returns true if snapshot progress's Match
// is equal or higher than the pendingSnapshot.
func (pr *Progress) needSnapshotAbort() bool {
	return pr.State == ProgressStateSnapshot && pr.Match >= pr.PendingSnapshot
}

func (pr *Progress) String() string {
	return fmt.Sprintf("next = %d, match = %d, state = %s, waiting = %v, pendingSnapshot = %d", pr.Next, pr.Match, pr.State, pr.isPaused(), pr.PendingSnapshot)
}

type inflights struct {
	// the starting index in the buffer
	start int
	// number of inflights in the buffer
	count int

	// the size of the buffer
	size   int
	buffer []uint64
}

func newInflights(size int) *inflights {
	return &inflights{
		size:   size,
		buffer: make([]uint64, size),
	}
}

// add adds an inflight into inflights
func (in *inflights) add(inflight uint64) {
	if in.full() {
		panic("cannot add into a full inflights")
	}
	next := in.start + in.count
	if next >= in.size {
		next -= in.size
	}
	in.buffer[next] = inflight
	in.count++
}

// freeTo frees the inflights smaller or equal to the given `to` flight.
func (in *inflights) freeTo(to uint64) {
	if in.count == 0 || to < in.buffer[in.start] {
		// out of the left side of the window
		return
	}

	i, idx := 0, in.start
	for i = 0; i < in.count; i++ {
		if to < in.buffer[idx] { // found the first large inflight
			break
		}

		// increase index and maybe rotate
		if idx++; idx >= in.size {
			idx -= in.size
		}
	}
	// free i inflights and set new start index
	in.count -= i
	in.start = idx
}

func (in *inflights) freeFirstOne() { in.freeTo(in.buffer[in.start]) }

// full returns true if the inflights is full.
func (in *inflights) full() bool {
	return in.count == in.size
}

// resets frees all inflights.
func (in *inflights) reset() {
	in.count = 0
	in.start = 0
}
<|endoftext|>


<commit_before>package rational

import (
	"math/big"

	"github.com/alex-ant/gomath/misc"
)

// Simplify simplifies the rational number by dividing it's numerator and
// denominator by the GCD.
func (ev *Rational) Simplify() {
	if ev.denominator < 0 {
		ev.numerator *= -1
		ev.denominator *= -1
	}

	currentNumerator := ev.numerator
	currentDenominator := ev.denominator

	if currentNumerator < 0 && currentDenominator > 0 {
		currentNumerator *= -1
	} else if currentDenominator < 0 && currentNumerator >= 0 {
		currentDenominator *= -1
	}

	n := big.NewInt(currentNumerator)
	d := big.NewInt(currentDenominator)

	gcd := new(big.Int).GCD(nil, nil, n, d).Int64()

	if gcd > 1 {
		ev.numerator /= gcd
		ev.denominator /= gcd
	}
}

// SimplifyLine miltiplies all the rationals in the line by the
// Greatest Common Delimiter of denominators if one is greater than 1.
func SimplifyLine(line []Rational) []Rational {
	var denominators []int64

	for i, v := range line {
		solveNegatives(&v.numerator, &v.denominator)
		line[i] = v
		denominators = append(denominators, v.GetDenominator())
	}

	gcd := misc.MultiGCD(denominators)

	if gcd > 1 {
		for i, v := range line {
			line[i] = v.MultiplyByNum(gcd)
		}
	}

	return line
}

// Float64 returns the float64 representation of a rational number.
func (ev Rational) Float64() float64 {
	return float64(ev.numerator) / float64(ev.denominator)
}

// Get returns a value.
func (ev Rational) Get() (numerator, denominator int64) {
	return ev.numerator, ev.denominator
}

// GetNumerator returns a numerator.
func (ev Rational) GetNumerator() int64 {
	return ev.numerator
}

// GetDenominator returns a denominator.
func (ev Rational) GetDenominator() int64 {
	return ev.denominator
}

// GetModule returns rational number's module.
func (ev Rational) GetModule() Rational {
	if ev.LessThanNum(0) {
		ev = ev.MultiplyByNum(-1)
	}
	return ev
}

// IsNull determines whether the value is zero.
func (ev Rational) IsNull() (n bool) {
	if ev.numerator == 0 {
		n = true
	}
	return
}

// RationalsAreNull determines whether the slice of Rationals contains only zero values.
func RationalsAreNull(l []Rational) (isNull bool) {
	isNull = true
	for _, v := range l {
		if !v.IsNull() {
			isNull = false
		}
	}
	return
}

func solveNegatives(n, d *int64) {
	if *d < 0 {
		*n *= -1
		*d *= -1
	}
}
<commit_msg>solving negatives before retrieving rational's module<commit_after>package rational

import (
	"math/big"

	"github.com/alex-ant/gomath/misc"
)

// Simplify simplifies the rational number by dividing it's numerator and
// denominator by the GCD.
func (ev *Rational) Simplify() {
	if ev.denominator < 0 {
		ev.numerator *= -1
		ev.denominator *= -1
	}

	currentNumerator := ev.numerator
	currentDenominator := ev.denominator

	if currentNumerator < 0 && currentDenominator > 0 {
		currentNumerator *= -1
	} else if currentDenominator < 0 && currentNumerator >= 0 {
		currentDenominator *= -1
	}

	n := big.NewInt(currentNumerator)
	d := big.NewInt(currentDenominator)

	gcd := new(big.Int).GCD(nil, nil, n, d).Int64()

	if gcd > 1 {
		ev.numerator /= gcd
		ev.denominator /= gcd
	}
}

// SimplifyLine miltiplies all the rationals in the line by the
// Greatest Common Delimiter of denominators if one is greater than 1.
func SimplifyLine(line []Rational) []Rational {
	var denominators []int64

	for i, v := range line {
		solveNegatives(&v.numerator, &v.denominator)
		line[i] = v
		denominators = append(denominators, v.GetDenominator())
	}

	gcd := misc.MultiGCD(denominators)

	if gcd > 1 {
		for i, v := range line {
			line[i] = v.MultiplyByNum(gcd)
		}
	}

	return line
}

// Float64 returns the float64 representation of a rational number.
func (ev Rational) Float64() float64 {
	return float64(ev.numerator) / float64(ev.denominator)
}

// Get returns a value.
func (ev Rational) Get() (numerator, denominator int64) {
	return ev.numerator, ev.denominator
}

// GetNumerator returns a numerator.
func (ev Rational) GetNumerator() int64 {
	return ev.numerator
}

// GetDenominator returns a denominator.
func (ev Rational) GetDenominator() int64 {
	return ev.denominator
}

// GetModule returns rational number's module.
func (ev Rational) GetModule() Rational {
	solveNegatives(&ev.numerator, &ev.denominator)
	if ev.LessThanNum(0) {
		ev = ev.MultiplyByNum(-1)
	}
	return ev
}

// IsNull determines whether the value is zero.
func (ev Rational) IsNull() (n bool) {
	if ev.numerator == 0 {
		n = true
	}
	return
}

// RationalsAreNull determines whether the slice of Rationals contains only zero values.
func RationalsAreNull(l []Rational) (isNull bool) {
	isNull = true
	for _, v := range l {
		if !v.IsNull() {
			isNull = false
		}
	}
	return
}

func solveNegatives(n, d *int64) {
	if *d < 0 {
		*n *= -1
		*d *= -1
	}
}
<|endoftext|>