Last active
April 30, 2018 06:43
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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