Skip to content

Commit

Permalink
Merge pull request #17 from kkrull/other-route-methods
Browse files Browse the repository at this point in the history
Standardized routes
  • Loading branch information
kkrull authored Apr 25, 2018
2 parents b6bb98e + 92d6e39 commit 7b37ad2
Show file tree
Hide file tree
Showing 25 changed files with 402 additions and 332 deletions.
6 changes: 3 additions & 3 deletions capability/capability_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ func TestCapability(t *testing.T) {
RunSpecs(t, "capability")
}

type ServerCapabilityControllerMock struct {
type ServerCapabilityServerMock struct {
optionsCalled bool
}

func (mock *ServerCapabilityControllerMock) Options(writer io.Writer) {
func (mock *ServerCapabilityServerMock) Options(writer io.Writer) {
mock.optionsCalled = true
}

func (mock *ServerCapabilityControllerMock) OptionsShouldHaveBeenCalled() {
func (mock *ServerCapabilityServerMock) OptionsShouldHaveBeenCalled() {
ExpectWithOffset(1, mock.optionsCalled).To(BeTrue())
}
19 changes: 11 additions & 8 deletions capability/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,41 @@ import (
"io"

"github.com/kkrull/gohttp/http"
"github.com/kkrull/gohttp/msg/clienterror"
)

func NewRoute() *ServerCapabilityRoute {
controller := &StaticCapabilityController{
controller := &StaticCapabilityServer{
AvailableMethods: []string{"GET", "HEAD"},
}

return &ServerCapabilityRoute{Controller: controller}
}

type ServerCapabilityRoute struct {
Controller ServerCapabilityController
Controller ServerResource
}

func (route *ServerCapabilityRoute) Route(requested *http.RequestLine) http.Request {
if requested.Method == "OPTIONS" && requested.Target == "*" {
return &optionsRequest{Controller: route.Controller}
if requested.Target != "*" {
return nil
} else if requested.Method != "OPTIONS" {
return clienterror.MethodNotAllowed("OPTIONS")
}

return nil
return &optionsRequest{Resource: route.Controller}
}

type optionsRequest struct {
Controller ServerCapabilityController
Resource ServerResource
}

func (request *optionsRequest) Handle(client io.Writer) error {
request.Controller.Options(client)
request.Resource.Options(client)
return nil
}

// Reports the global, generic capabilities of this server, without regard to resource or state
type ServerCapabilityController interface {
type ServerResource interface {
Options(writer io.Writer)
}
36 changes: 18 additions & 18 deletions capability/route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,21 @@ import (

"github.com/kkrull/gohttp/capability"
"github.com/kkrull/gohttp/http"
"github.com/kkrull/gohttp/msg/clienterror"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

//Panics due to incomplete wiring are easy to cause due to Go's permissive struct declaration
//and hard to root cause when the server goes down and starts refusing connections behind a FitNesse suite
//that swallows console output from the server
var _ = Describe("::NewRoute", func() {
It("configures the route with StaticCapabilityController", func() {
It("configures the route with StaticCapabilityServer", func() {
route := capability.NewRoute()
Expect(route.Controller).To(BeAssignableToTypeOf(&capability.StaticCapabilityController{}))
Expect(route.Controller).To(BeAssignableToTypeOf(&capability.StaticCapabilityServer{}))
})

It("configures available methods to the server as GET and HEAD", func() {
route := capability.NewRoute()
Expect(route.Controller).To(BeEquivalentTo(
&capability.StaticCapabilityController{
&capability.StaticCapabilityServer{
AvailableMethods: []string{"GET", "HEAD"},
},
))
Expand All @@ -32,27 +30,29 @@ var _ = Describe("ServerCapabilityRoute", func() {
Describe("#Route", func() {
var (
router http.Route
controller *ServerCapabilityControllerMock
controller *ServerCapabilityServerMock
requested *http.RequestLine
routedRequest http.Request
)

BeforeEach(func() {
controller = &ServerCapabilityControllerMock{}
controller = &ServerCapabilityServerMock{}
router = &capability.ServerCapabilityRoute{Controller: controller}
})

It("routes OPTIONS * to ServerCapabilityController#Options", func() {
requested = &http.RequestLine{Method: "OPTIONS", Target: "*"}
routedRequest = router.Route(requested)
routedRequest.Handle(&bufio.Writer{})
controller.OptionsShouldHaveBeenCalled()
})
Context("when the target is *", func() {
It("routes OPTIONS to ServerResource", func() {
requested = &http.RequestLine{Method: "OPTIONS", Target: "*"}
routedRequest = router.Route(requested)
routedRequest.Handle(&bufio.Writer{})
controller.OptionsShouldHaveBeenCalled()
})

It("returns nil to pass on any other method", func() {
requested = &http.RequestLine{Method: "GET", Target: "*"}
routedRequest = router.Route(requested)
Expect(routedRequest).To(BeNil())
It("returns MethodNotAllowed for any other method", func() {
requested = &http.RequestLine{Method: "GET", Target: "*"}
routedRequest = router.Route(requested)
Expect(routedRequest).To(BeEquivalentTo(clienterror.MethodNotAllowed("OPTIONS")))
})
})

It("returns nil to pass on any other target", func() {
Expand Down
4 changes: 2 additions & 2 deletions capability/server_capability.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
)

// Reports on server capabilities that are defined during startup and do not change after that
type StaticCapabilityController struct {
type StaticCapabilityServer struct {
AvailableMethods []string
}

func (controller *StaticCapabilityController) Options(client io.Writer) {
func (controller *StaticCapabilityServer) Options(client io.Writer) {
msg.WriteStatusLine(client, 200, "OK")
msg.WriteHeader(client, "Allow", strings.Join(controller.AvailableMethods, ","))
msg.WriteContentLengthHeader(client, 0)
Expand Down
10 changes: 5 additions & 5 deletions capability/server_capability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import (
. "github.com/onsi/gomega"
)

var _ = Describe("StaticCapabilityController", func() {
var _ = Describe("StaticCapabilityServer", func() {
var (
controller capability.ServerCapabilityController
controller capability.ServerResource
response *httptest.ResponseMessage
responseBuffer *bytes.Buffer
)

Describe("#Options", func() {
BeforeEach(func() {
responseBuffer = &bytes.Buffer{}
controller = &capability.StaticCapabilityController{
controller = &capability.StaticCapabilityServer{
AvailableMethods: []string{"CONNECT", "TRACE"},
}
controller.Options(responseBuffer)
Expand All @@ -39,7 +39,7 @@ var _ = Describe("StaticCapabilityController", func() {
Context("given 1 available method", func() {
BeforeEach(func() {
responseBuffer = &bytes.Buffer{}
controller = &capability.StaticCapabilityController{
controller = &capability.StaticCapabilityServer{
AvailableMethods: []string{"OPTIONS"},
}
controller.Options(responseBuffer)
Expand All @@ -54,7 +54,7 @@ var _ = Describe("StaticCapabilityController", func() {
Context("given 2 or more available methods", func() {
BeforeEach(func() {
responseBuffer = &bytes.Buffer{}
controller = &capability.StaticCapabilityController{
controller = &capability.StaticCapabilityServer{
AvailableMethods: []string{"CONNECT", "TRACE"},
}
controller.Options(responseBuffer)
Expand Down
4 changes: 4 additions & 0 deletions fs/fs_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type FileSystemResourceMock struct {
headTarget string
}

func (mock *FileSystemResourceMock) Name() string {
return "File system mock"
}

func (mock *FileSystemResourceMock) Get(client io.Writer, target string) {
mock.getTarget = target
}
Expand Down
12 changes: 8 additions & 4 deletions fs/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@ import (
"github.com/kkrull/gohttp/msg/clienterror"
)

type ReadOnlyFilesystem struct {
type ReadOnlyFileSystem struct {
BaseDirectory string
}

func (controller *ReadOnlyFilesystem) Get(client io.Writer, target string) {
func (controller *ReadOnlyFileSystem) Name() string {
return "Readonly file system"
}

func (controller *ReadOnlyFileSystem) Get(client io.Writer, target string) {
response := controller.determineResponse(target)
response.WriteTo(client)
}

func (controller *ReadOnlyFilesystem) Head(client io.Writer, target string) {
func (controller *ReadOnlyFileSystem) Head(client io.Writer, target string) {
response := controller.determineResponse(target)
response.WriteHeader(client)
}

func (controller *ReadOnlyFilesystem) determineResponse(requestedTarget string) http.Response {
func (controller *ReadOnlyFileSystem) determineResponse(requestedTarget string) http.Response {
resolvedTarget := path.Join(controller.BaseDirectory, requestedTarget)
info, err := os.Stat(resolvedTarget)
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions fs/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ import (
. "github.com/onsi/gomega"
)

var _ = Describe("ReadOnlyFilesystem", func() {
var _ = Describe("ReadOnlyFileSystem", func() {
var (
controller *fs.ReadOnlyFilesystem
controller *fs.ReadOnlyFileSystem
basePath string

response *httptest.ResponseMessage
responseBuffer *bytes.Buffer
)

BeforeEach(func() {
basePath = makeEmptyTestDirectory("ReadOnlyFilesystem", os.ModePerm)
controller = &fs.ReadOnlyFilesystem{BaseDirectory: basePath}
basePath = makeEmptyTestDirectory("ReadOnlyFileSystem", os.ModePerm)
controller = &fs.ReadOnlyFileSystem{BaseDirectory: basePath}
responseBuffer = &bytes.Buffer{}
})

Expand Down
53 changes: 4 additions & 49 deletions fs/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ package fs

import (
"io"
"strings"

"github.com/kkrull/gohttp/http"
"github.com/kkrull/gohttp/msg"
)

func NewRoute(contentRootPath string) http.Route {
return &FileSystemRoute{
ContentRootPath: contentRootPath,
Resource: &ReadOnlyFilesystem{BaseDirectory: contentRootPath},
Resource: &ReadOnlyFileSystem{BaseDirectory: contentRootPath},
}
}

Expand All @@ -21,55 +19,12 @@ type FileSystemRoute struct {
}

func (route FileSystemRoute) Route(requested *http.RequestLine) http.Request {
switch requested.Method {
case "GET":
return &GetRequest{
Controller: route.Resource,
Target: requested.Target,
}
case "HEAD":
return &HeadRequest{
Controller: route.Resource,
Target: requested.Target,
}
default:
return &MethodNotAllowedRequest{SupportedMethods: []string{"GET", "HEAD"}}
}
return http.MakeResourceRequest(requested, route.Resource)
}

// Represents files and directories on the file system
type FileSystemResource interface {
Name() string
Get(client io.Writer, target string)
Head(client io.Writer, target string)
}

type GetRequest struct {
Controller FileSystemResource
Target string
}

func (request *GetRequest) Handle(client io.Writer) error {
request.Controller.Get(client, request.Target)
return nil
}

type HeadRequest struct {
Controller FileSystemResource
Target string
}

func (request *HeadRequest) Handle(client io.Writer) error {
request.Controller.Head(client, request.Target)
return nil
}

type MethodNotAllowedRequest struct {
SupportedMethods []string
}

func (notAllowed *MethodNotAllowedRequest) Handle(client io.Writer) error {
msg.WriteStatusLine(client, 405, "Method Not Allowed")
msg.WriteContentLengthHeader(client, 0)
msg.WriteHeader(client, "Allow", strings.Join(notAllowed.SupportedMethods, ","))
msg.WriteEndOfMessageHeader(client)
return nil
}
17 changes: 6 additions & 11 deletions fs/route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (

"github.com/kkrull/gohttp/fs"
"github.com/kkrull/gohttp/http"
"github.com/kkrull/gohttp/httptest"
"github.com/kkrull/gohttp/msg/clienterror"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
Expand All @@ -16,7 +16,7 @@ var _ = Describe("::NewRoute", func() {
Expect(route).To(BeEquivalentTo(
&fs.FileSystemRoute{
ContentRootPath: "/public",
Resource: &fs.ReadOnlyFilesystem{BaseDirectory: "/public"},
Resource: &fs.ReadOnlyFileSystem{BaseDirectory: "/public"},
}))
})
})
Expand Down Expand Up @@ -51,15 +51,10 @@ var _ = Describe("FileSystemRoute", func() {
resource.HeadShouldHaveReceived("/foo")
})

Context("given any other method", func() {
BeforeEach(func() {
requested := &http.RequestLine{Method: "TRACE", Target: "/"}
routedRequest := route.Route(requested)
routedRequest.Handle(response)
})

It("responds 405 Method Not Allowed", httptest.ShouldHaveNoBody(response, 405, "Method Not Allowed"))
It("sets Allow to GET and HEAD", httptest.ShouldAllowMethods(response, "GET", "HEAD"))
It("routes any other method to MethodNotAllowed", func() {
requested := &http.RequestLine{Method: "TRACE", Target: "/"}
routedRequest := route.Route(requested)
Expect(routedRequest).To(BeEquivalentTo(clienterror.MethodNotAllowed("GET", "HEAD", "OPTIONS")))
})
})
})
Loading

0 comments on commit 7b37ad2

Please sign in to comment.