Skip to content

Instantly share code, notes, and snippets.

@LouisRitchie
Last active April 30, 2018 06:43
Show Gist options
  • Save LouisRitchie/0f21b2347c38ad7fc5d1fc74bd76be67 to your computer and use it in GitHub Desktop.
Save LouisRitchie/0f21b2347c38ad7fc5d1fc74bd76be67 to your computer and use it in GitHub Desktop.
The modified server.go file that is required to use the Battlesnake 2019 Board https://github.com/LouisRitchie/battlesnake-2019-board
package api
import (
"context"
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"time"
"github.com/battlesnakeio/engine/controller/pb"
"github.com/julienschmidt/httprouter"
log "github.com/sirupsen/logrus"
)
// Server this is the api server
type Server struct {
hs *http.Server
}
// clientHandle is a function that handles an http route and accepts a ControllerClient
// in addition to the normal httprouter.Handle parameters.
type clientHandle func(http.ResponseWriter, *http.Request, httprouter.Params, pb.ControllerClient)
// New creates a new api server
func New(addr string, c pb.ControllerClient) *Server {
router := httprouter.New()
router.POST("/games", newClientHandle(c, createGame))
router.OPTIONS("/games", newClientHandle(c, corsResponse))
router.POST("/games/:id/start", newClientHandle(c, startGame))
router.OPTIONS("/games/:id/start", newClientHandle(c, corsResponse))
router.GET("/games/:id", newClientHandle(c, getStatus))
router.GET("/games/:id/frames", newClientHandle(c, getFrames))
return &Server{
hs: &http.Server{
Addr: addr,
Handler: router,
},
}
}
func newClientHandle(c pb.ControllerClient, innerHandle clientHandle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
innerHandle(w, r, p, c)
}
}
func createGame(w http.ResponseWriter, r *http.Request, _ httprouter.Params, c pb.ControllerClient) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.WithError(err).Error("Unable to read request body")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
req := &pb.CreateRequest{}
err = json.Unmarshal(body, req)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Invalid JSON: " + err.Error()))
return
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := c.Create(ctx, req)
if err != nil {
log.WithError(err).Error("Error creating game")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
j, err := json.Marshal(resp)
if err != nil {
log.WithError(err).WithField("resp", resp).Error("Error serializing to JSON")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("Access-Control-Allow-Method", "POST")
w.Write(j)
}
func startGame(w http.ResponseWriter, r *http.Request, ps httprouter.Params, c pb.ControllerClient) {
id := ps.ByName("id")
req := &pb.StartRequest{
ID: id,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_, err := c.Start(ctx, req)
if err != nil {
log.WithError(err).WithField("req", req).Error("Error while calling controller start")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("Access-Control-Allow-Method", "POST")
w.WriteHeader(http.StatusOK)
}
func getStatus(w http.ResponseWriter, r *http.Request, ps httprouter.Params, c pb.ControllerClient) {
id := ps.ByName("id")
req := &pb.StatusRequest{
ID: id,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := c.Status(ctx, req)
if err != nil {
log.WithError(err).WithField("req", req).Error("Error while calling controller status")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
j, err := json.Marshal(resp)
if err != nil {
log.WithError(err).WithField("resp", resp).Error("Error serializing response to JSON")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Write(j)
}
func getFrames(w http.ResponseWriter, r *http.Request, ps httprouter.Params, c pb.ControllerClient) {
id := ps.ByName("id")
offset, _ := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 0)
limit, _ := strconv.ParseInt(r.URL.Query().Get("limit"), 10, 0)
if limit == 0 {
limit = 100
}
req := &pb.ListGameFramesRequest{
ID: id,
Offset: int32(offset),
Limit: int32(limit),
}
// TODO: use a context with timeout
resp, err := c.ListGameFrames(r.Context(), req)
if err != nil {
log.WithError(err).WithField("req", req).Error("Error while calling controller status")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
j, err := json.Marshal(resp)
if err != nil {
log.WithError(err).WithField("resp", resp).Error("Error serializing response to JSON")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Write(j)
}
func corsResponse(w http.ResponseWriter, r *http.Request, ps httprouter.Params, c pb.ControllerClient) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("Access-Control-Allow-Method", "POST")
}
// WaitForExit starts up the server and blocks until the server shuts down.
func (s *Server) WaitForExit() {
log.Infof("Battlesnake engine api listening on %s", s.hs.Addr)
err := s.hs.ListenAndServe()
if err != nil {
log.Errorf("Error while listening: %v", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment