diff --git a/pkg/rove/rove.go b/pkg/rove/rove.go deleted file mode 100644 index cdfed09..0000000 --- a/pkg/rove/rove.go +++ /dev/null @@ -1,77 +0,0 @@ -package rove - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/url" - - "github.com/mdiluz/rove/pkg/server" -) - -// Connection is the container for a simple connection to the server -type Connection struct { - host string -} - -// NewConnection sets up a new connection to a server host -func NewConnection(host string) *Connection { - return &Connection{ - host: host, - } -} - -// Status returns the current status of the server -func (c *Connection) Status() (status server.StatusResponse, err error) { - url := url.URL{ - Scheme: "http", - Host: c.host, - Path: "status", - } - - if resp, err := http.Get(url.String()); err != nil { - return server.StatusResponse{}, err - } else if resp.StatusCode != http.StatusOK { - return server.StatusResponse{}, fmt.Errorf("Status request returned %d", resp.StatusCode) - } else { - err = json.NewDecoder(resp.Body).Decode(&status) - } - - return -} - -// Register registers a new account on the server -func (c *Connection) Register(name string) (register server.RegisterResponse, err error) { - url := url.URL{ - Scheme: "http", - Host: c.host, - Path: "register", - } - - // Marshal the register data struct - data := server.RegisterData{Name: name} - marshalled, err := json.Marshal(data) - - // Set up the request - req, err := http.NewRequest("POST", url.String(), bytes.NewReader(marshalled)) - req.Header.Set("Content-Type", "application/json") - - // Do the request - client := &http.Client{} - if resp, err := client.Do(req); err != nil { - return server.RegisterResponse{}, err - } else { - defer resp.Body.Close() - - // Handle any errors - if resp.StatusCode != http.StatusOK { - return server.RegisterResponse{}, fmt.Errorf("Status request returned %d", resp.StatusCode) - } else { - // Decode the reply - err = json.NewDecoder(resp.Body).Decode(®ister) - } - } - - return -} diff --git a/pkg/rove/rove_test.go b/pkg/rove/rove_test.go deleted file mode 100644 index d5362dd..0000000 --- a/pkg/rove/rove_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// +build integration - -package rove - -import ( - "testing" - - "github.com/google/uuid" -) - -var serverUrl = "localhost:80" - -func TestStatus(t *testing.T) { - conn := NewConnection(serverUrl) - - if status, err := conn.Status(); err != nil { - t.Errorf("Status returned error: %s", err) - } else if !status.Ready { - t.Error("Server did not return that it was ready") - } else if len(status.Version) == 0 { - t.Error("Server returned blank version") - } -} - -func TestRegister(t *testing.T) { - conn := NewConnection(serverUrl) - - a := uuid.New().String() - reg1, err := conn.Register(a) - if err != nil { - t.Errorf("Register returned error: %s", err) - } else if !reg1.Success { - t.Error("Server did not success for Register") - } else if len(reg1.Id) == 0 { - t.Error("Server returned empty registration ID") - } - - b := uuid.New().String() - reg2, err := conn.Register(b) - if err != nil { - t.Errorf("Register returned error: %s", err) - } else if !reg2.Success { - t.Error("Server did not success for Register") - } else if len(reg2.Id) == 0 { - t.Error("Server returned empty registration ID") - } - - if reg2, err := conn.Register(a); err != nil { - t.Errorf("Register returned error: %s", err) - } else if reg2.Success { - t.Error("Server should have failed to register duplicate name") - } -} diff --git a/pkg/server/integration_test.go b/pkg/server/integration_test.go new file mode 100644 index 0000000..7448abf --- /dev/null +++ b/pkg/server/integration_test.go @@ -0,0 +1,124 @@ +// +build integration + +package server + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + "testing" + + "github.com/google/uuid" +) + +var serverUrl = "localhost:80" + +// Connection is the container for a simple connection to the server +type Connection struct { + host string +} + +// NewConnection sets up a new connection to a server host +func NewConnection(host string) *Connection { + return &Connection{ + host: host, + } +} + +// Status returns the current status of the server +func (c *Connection) Status() (status StatusResponse, err error) { + url := url.URL{ + Scheme: "http", + Host: c.host, + Path: "status", + } + + if resp, err := http.Get(url.String()); err != nil { + return StatusResponse{}, err + } else if resp.StatusCode != http.StatusOK { + return StatusResponse{}, fmt.Errorf("Status request returned %d", resp.StatusCode) + } else { + err = json.NewDecoder(resp.Body).Decode(&status) + } + + return +} + +// Register registers a new account on the server +func (c *Connection) Register(name string) (register RegisterResponse, err error) { + url := url.URL{ + Scheme: "http", + Host: c.host, + Path: "register", + } + + // Marshal the register data struct + data := RegisterData{Name: name} + marshalled, err := json.Marshal(data) + + // Set up the request + req, err := http.NewRequest("POST", url.String(), bytes.NewReader(marshalled)) + req.Header.Set("Content-Type", "application/json") + + // Do the request + client := &http.Client{} + if resp, err := client.Do(req); err != nil { + return RegisterResponse{}, err + } else { + defer resp.Body.Close() + + // Handle any errors + if resp.StatusCode != http.StatusOK { + return RegisterResponse{}, fmt.Errorf("Status request returned %d", resp.StatusCode) + } else { + // Decode the reply + err = json.NewDecoder(resp.Body).Decode(®ister) + } + } + + return +} + +func TestStatus(t *testing.T) { + conn := NewConnection(serverUrl) + + if status, err := conn.Status(); err != nil { + t.Errorf("Status returned error: %s", err) + } else if !status.Ready { + t.Error("Server did not return that it was ready") + } else if len(status.Version) == 0 { + t.Error("Server returned blank version") + } +} + +func TestRegister(t *testing.T) { + conn := NewConnection(serverUrl) + + a := uuid.New().String() + reg1, err := conn.Register(a) + if err != nil { + t.Errorf("Register returned error: %s", err) + } else if !reg1.Success { + t.Error("Server did not success for Register") + } else if len(reg1.Id) == 0 { + t.Error("Server returned empty registration ID") + } + + b := uuid.New().String() + reg2, err := conn.Register(b) + if err != nil { + t.Errorf("Register returned error: %s", err) + } else if !reg2.Success { + t.Error("Server did not success for Register") + } else if len(reg2.Id) == 0 { + t.Error("Server returned empty registration ID") + } + + if reg2, err := conn.Register(a); err != nil { + t.Errorf("Register returned error: %s", err) + } else if reg2.Success { + t.Error("Server should have failed to register duplicate name") + } +} diff --git a/pkg/server/router.go b/pkg/server/router.go deleted file mode 100644 index 8a285b9..0000000 --- a/pkg/server/router.go +++ /dev/null @@ -1,81 +0,0 @@ -package server - -import ( - "fmt" - "io" - "net/http" -) - -// RequestHandler describes a function that handles any incoming request and can respond -type RequestHandler func(io.ReadCloser, io.Writer) error - -// Route defines the information for a single path->function route -type Route struct { - path string - method string - handler RequestHandler -} - -// requestHandlerHTTP wraps a request handler in http checks -func requestHandlerHTTP(method string, handler RequestHandler) func(w http.ResponseWriter, r *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - // Log the request - fmt.Printf("%s\t%s\n", r.Method, r.RequestURI) - - // Verify we're hit with the right method - if r.Method != method { - w.WriteHeader(http.StatusMethodNotAllowed) - - } else if err := handler(r.Body, w); err != nil { - // Log the error - fmt.Printf("Failed to handle http request: %s", err) - - // Respond that we've had an error - w.WriteHeader(http.StatusInternalServerError) - - } else { - // Be a good citizen and set the header for the return - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) - - } - } -} - -// NewRouter sets up the server mux -func (s *Server) SetUpRouter() { - - // Array of all our routes - var routes = []Route{ - { - path: "/status", - method: http.MethodGet, - handler: s.HandleStatus, - }, - { - path: "/register", - method: http.MethodPost, - handler: s.HandleRegister, - }, - { - path: "/spawn", - method: http.MethodPost, - handler: s.HandleSpawn, - }, - { - path: "/commands", - method: http.MethodPost, - handler: s.HandleCommands, - }, - { - path: "/view", - method: http.MethodPost, - handler: s.HandleView, - }, - } - - // Set up the handlers - for _, route := range routes { - s.router.HandleFunc(route.path, requestHandlerHTTP(route.method, route.handler)) - } -} diff --git a/pkg/server/router_test.go b/pkg/server/router_test.go index 19664b0..eb26864 100644 --- a/pkg/server/router_test.go +++ b/pkg/server/router_test.go @@ -16,7 +16,7 @@ func TestHandleStatus(t *testing.T) { response := httptest.NewRecorder() s := NewServer() - RequestHandlerHTTP(http.MethodGet, s.HandleStatus)(response, request) + s.wrapHandler(http.MethodGet, HandleStatus)(response, request) var status StatusResponse json.NewDecoder(response.Body).Decode(&status) @@ -41,7 +41,7 @@ func TestHandleRegister(t *testing.T) { response := httptest.NewRecorder() s := NewServer() - RequestHandlerHTTP(http.MethodPost, s.HandleRegister)(response, request) + s.wrapHandler(http.MethodPost, HandleRegister)(response, request) var status RegisterResponse json.NewDecoder(response.Body).Decode(&status) @@ -63,7 +63,7 @@ func TestHandleSpawn(t *testing.T) { request, _ := http.NewRequest(http.MethodPost, "/spawn", bytes.NewReader(b)) response := httptest.NewRecorder() - RequestHandlerHTTP(http.MethodPost, s.HandleSpawn)(response, request) + s.wrapHandler(http.MethodPost, HandleSpawn)(response, request) var status SpawnResponse json.NewDecoder(response.Body).Decode(&status) @@ -99,7 +99,7 @@ func TestHandleCommands(t *testing.T) { request, _ := http.NewRequest(http.MethodPost, "/commands", bytes.NewReader(b)) response := httptest.NewRecorder() - RequestHandlerHTTP(http.MethodPost, s.HandleCommands)(response, request) + s.wrapHandler(http.MethodPost, HandleCommands)(response, request) var status BasicResponse json.NewDecoder(response.Body).Decode(&status) diff --git a/pkg/server/handlers.go b/pkg/server/routes.go similarity index 85% rename from pkg/server/handlers.go rename to pkg/server/routes.go index a22c9da..c18018d 100644 --- a/pkg/server/handlers.go +++ b/pkg/server/routes.go @@ -4,12 +4,52 @@ import ( "encoding/json" "fmt" "io" + "net/http" "github.com/google/uuid" "github.com/mdiluz/rove/pkg/game" "github.com/mdiluz/rove/pkg/version" ) +// Handler describes a function that handles any incoming request and can respond +type Handler func(*Server, io.ReadCloser, io.Writer) error + +// Route defines the information for a single path->function route +type Route struct { + path string + method string + handler Handler +} + +// Routes is an array of all the Routes +var Routes = []Route{ + { + path: "/status", + method: http.MethodGet, + handler: HandleStatus, + }, + { + path: "/register", + method: http.MethodPost, + handler: HandleRegister, + }, + { + path: "/spawn", + method: http.MethodPost, + handler: HandleSpawn, + }, + { + path: "/commands", + method: http.MethodPost, + handler: HandleCommands, + }, + { + path: "/view", + method: http.MethodPost, + handler: HandleView, + }, +} + // StatusResponse is a struct that contains information on the status of the server type StatusResponse struct { Ready bool `json:"ready"` @@ -17,7 +57,7 @@ type StatusResponse struct { } // HandleStatus handles the /status request -func (s *Server) HandleStatus(b io.ReadCloser, w io.Writer) error { +func HandleStatus(s *Server, b io.ReadCloser, w io.Writer) error { // Simply encode the current status var response = StatusResponse{ @@ -55,7 +95,7 @@ type RegisterResponse struct { } // HandleRegister handles /register endpoint -func (s *Server) HandleRegister(b io.ReadCloser, w io.Writer) error { +func HandleRegister(s *Server, b io.ReadCloser, w io.Writer) error { // Set up the response var response = RegisterResponse{ @@ -111,7 +151,7 @@ type SpawnResponse struct { } // HandleSpawn will spawn the player entity for the associated account -func (s *Server) HandleSpawn(b io.ReadCloser, w io.Writer) error { +func HandleSpawn(s *Server, b io.ReadCloser, w io.Writer) error { // Set up the response var response = SpawnResponse{ BasicResponse: BasicResponse{ @@ -175,7 +215,7 @@ type CommandsData struct { } // HandleSpawn will spawn the player entity for the associated account -func (s *Server) HandleCommands(b io.ReadCloser, w io.Writer) error { +func HandleCommands(s *Server, b io.ReadCloser, w io.Writer) error { // Set up the response var response = BasicResponse{ Success: false, @@ -237,7 +277,7 @@ type ViewResponse struct { } // HandleView handles the view request -func (s *Server) HandleView(b io.ReadCloser, w io.Writer) error { +func HandleView(s *Server, b io.ReadCloser, w io.Writer) error { // Set up the response var response = ViewResponse{ BasicResponse: BasicResponse{ diff --git a/pkg/server/server.go b/pkg/server/server.go index a4e4270..6d2eb69 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -93,7 +93,7 @@ func (s *Server) Initialise() error { } // Create a new router - s.SetUpRouter() + s.CreateRoutes() fmt.Printf("Routes Created\n") // Add to our sync @@ -134,6 +134,40 @@ func (s *Server) Close() error { return nil } +// wrapHandler wraps a request handler in http checks +func (s *Server) wrapHandler(method string, handler Handler) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + // Log the request + fmt.Printf("%s\t%s\n", r.Method, r.RequestURI) + + // Verify we're hit with the right method + if r.Method != method { + w.WriteHeader(http.StatusMethodNotAllowed) + + } else if err := handler(s, r.Body, w); err != nil { + // Log the error + fmt.Printf("Failed to handle http request: %s", err) + + // Respond that we've had an error + w.WriteHeader(http.StatusInternalServerError) + + } else { + // Be a good citizen and set the header for the return + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusOK) + + } + } +} + +// CreateRoutes sets up the server mux +func (s *Server) CreateRoutes() { + // Set up the handlers + for _, route := range Routes { + s.router.HandleFunc(route.path, s.wrapHandler(route.method, route.handler)) + } +} + // SpawnPrimary spawns the primary instance for an account func (s *Server) SpawnPrimary(accountid uuid.UUID) (game.Vector, uuid.UUID, error) { inst := uuid.New()