aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <m.shulhan@gmail.com>2019-09-12 00:59:53 +0700
committerShulhan <m.shulhan@gmail.com>2019-09-12 00:59:53 +0700
commitb938bb94426f71105d572df56b22528b7a76d96f (patch)
tree3bfcbd463450e7a94bcaccce9eab12314c6ca9f8
parentd8368c713d49ef0606a2c59baec0e55f0c61baea (diff)
downloadpakakeh.go-b938bb94426f71105d572df56b22528b7a76d96f.tar.xz
http: implement key binding in registered Endpoint's Path
Previously, only raw path can be registered on Endpoint. This changes implement key binding using colon ":" on path. For example, registering path "/:x/y" will bind key "x" to a string value that can be accessed on http.Request.Form using Get method.
-rw-r--r--CHANGELOG.adoc22
-rw-r--r--lib/http/endpoint.go15
-rw-r--r--lib/http/http.go66
-rw-r--r--lib/http/server.go334
-rw-r--r--lib/http/server_test.go220
5 files changed, 490 insertions, 167 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index d49abc7c..652de861 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -5,6 +5,28 @@
This library is released each month, usually at the first week of month.
+== share v0.9.0 (2019-10-xx)
+
+=== New Features
+
+* http: implement key binding in registered Endpoint's Path
++
+Previously, only raw path can be registered on Endpoint. This changes
+implement key binding using colon ":" on path. For example, registering
+path "/:x/y" will bind key "x" to a string value that can be accessed on
+http.Request.Form using Get method.
+
+
+=== Bug Fixes
+
+* ini: check for possible nil variable on Write
+
+* dns: allow message with non recursive-desired to be forwarded
++
+On macOS, turn out, all DNS queries have RD flag set to zero. This cause
+no DNS queries forwarded to parent server.
+
+
== share v0.8.2 (2019-09-05)
=== Enhancements
diff --git a/lib/http/endpoint.go b/lib/http/endpoint.go
index db80990c..c56f5e76 100644
--- a/lib/http/endpoint.go
+++ b/lib/http/endpoint.go
@@ -24,21 +24,29 @@ import (
type Endpoint struct {
// Method contains HTTP method, default to GET.
Method RequestMethod
+
// Path contains route to be served, default to "/" if its empty.
Path string
+
// RequestType contains type of request, default to RequestTypeNone.
RequestType RequestType
+
// ResponseType contains type of request, default to ResponseTypeNone.
ResponseType ResponseType
+
// Eval define evaluator for route that will be called after global
// evaluators and before callback.
Eval Evaluator
+
// Call is the main process of route.
Call Callback
}
-func (ep *Endpoint) call(res http.ResponseWriter, req *http.Request,
+func (ep *Endpoint) call(
+ res http.ResponseWriter,
+ req *http.Request,
evaluators []Evaluator,
+ vals map[string]string,
) {
reqBody, e := ioutil.ReadAll(req.Body)
if e != nil {
@@ -68,6 +76,11 @@ func (ep *Endpoint) call(res http.ResponseWriter, req *http.Request,
if debug.Value > 0 {
log.Printf("> request body: %s\n", reqBody)
}
+ for k, v := range vals {
+ if len(k) > 0 && len(v) > 0 {
+ req.Form.Set(k, v)
+ }
+ }
for _, eval := range evaluators {
e = eval(req, reqBody)
diff --git a/lib/http/http.go b/lib/http/http.go
index 5d9141cc..a87bed02 100644
--- a/lib/http/http.go
+++ b/lib/http/http.go
@@ -69,7 +69,7 @@
// ResponseType: libhttp.ResponseTypeJSON,
// Call: handleLogin,
// }
-// server.RequestPost(epAPILogin)
+// server.RegisterPost(epAPILogin)
//
// Upon receiving request to "/api/login", the library will call
// "req.ParseForm()", read the content of body and pass them to
@@ -81,14 +81,55 @@
// // Return response body and error.
// }
//
-// Known Bugs
+// Routing
+//
+// The Endpoint allow binding the unique key into path using colon ":" as the
+// first character.
+//
+// For example, after registering the following Endpoint,
+//
+// epBinding := &libhttp.Endpoint{
+// Path: "/category/:name",
+// RequestType: libhttp.RequestTypeQuery,
+// ResponseType: libhttp.ResponseTypeJSON,
+// Call: handleCategory,
+// }
+// server.RegisterGet(epBinding)
+//
+// when the server receiving GET request using path "/category/book?limit=10",
+// it will put the "book" and "10" into http.Request's Form with key is "name"
+// and "limit"
+//
+// fmt.Printf("request.Form:", req.Form)
+// // request.Form: map[name:[book] limit:[10]]
+//
+// The key binding must be unique between path and query. If query has the
+// same key then it will be overridden by value in path. For example, using
+// the above endpoint, request with "/category/book?name=Hitchiker" will
+// result in Request.Form:
+//
+// map[name:[book]]
+//
+// not
+//
+// map[name:[book Hitchiker]]
+//
+// Known Bugs and Limitations
//
// * The server does not handle CONNECT method
//
// * Missing test for request with content-type multipart-form
//
+// * We can not register path with ambigous route. For example, "/:x" and
+// "/y" are ambiguous because one is dynamic path using key binding "x" and
+// the last one is static path to "y".
+//
package http
+import (
+ "errors"
+)
+
const (
ContentEncoding = "Content-Encoding"
ContentLength = "Content-Length"
@@ -99,3 +140,24 @@ const (
ContentTypePlain = "text/plain; charset=utf-8"
HeaderLocation = "Location"
)
+
+var (
+ //
+ // ErrEndpointAmbiguous define an error when registering path that
+ // already exist. For example, after registering "/:x", registering
+ // "/:y" or "/z" on the same HTTP method will result in ambiguous.
+ //
+ ErrEndpointAmbiguous = errors.New("ambigous endpoint")
+
+ //
+ // ErrEndpointKeyDuplicate define an error when registering path with
+ // the same keys, for example "/:x/:x".
+ //
+ ErrEndpointKeyDuplicate = errors.New("duplicate key in route")
+
+ //
+ // ErrEndpointKeyEmpty define an error when path contains an empty
+ // key, for example "/:/y".
+ //
+ ErrEndpointKeyEmpty = errors.New("empty route's key")
+)
diff --git a/lib/http/server.go b/lib/http/server.go
index 0d8f9934..d313556b 100644
--- a/lib/http/server.go
+++ b/lib/http/server.go
@@ -23,14 +23,14 @@ import (
// Server define HTTP server.
//
type Server struct {
- mfs *memfs.MemFS
- evals []Evaluator
- conn *http.Server
- regDelete map[string]*Endpoint
- regGet map[string]*Endpoint
- regPatch map[string]*Endpoint
- regPost map[string]*Endpoint
- regPut map[string]*Endpoint
+ mfs *memfs.MemFS
+ evals []Evaluator
+ conn *http.Server
+ routeDeletes []*route
+ routeGets []*route
+ routePatches []*route
+ routePosts []*route
+ routePuts []*route
}
//
@@ -38,13 +38,7 @@ type Server struct {
// with custom connection.
//
func NewServer(opts *ServerOptions) (srv *Server, e error) {
- srv = &Server{
- regDelete: make(map[string]*Endpoint),
- regGet: make(map[string]*Endpoint),
- regPatch: make(map[string]*Endpoint),
- regPost: make(map[string]*Endpoint),
- regPut: make(map[string]*Endpoint),
- }
+ srv = &Server{}
if len(opts.Address) == 0 {
opts.Address = ":80"
@@ -90,14 +84,33 @@ func (srv *Server) RedirectTemp(res http.ResponseWriter, redirectURL string) {
}
//
-// RegisterDelete register HTTP method DELETE with callback to handle it.
+// RegisterDelete register HTTP method DELETE with specific endpoint to handle
+// it.
//
-func (srv *Server) RegisterDelete(ep *Endpoint) {
- if ep != nil {
- ep.Method = RequestMethodDelete
- ep.RequestType = RequestTypeQuery
- srv.register(ep)
+func (srv *Server) RegisterDelete(ep *Endpoint) (err error) {
+ if ep == nil || ep.Call == nil {
+ return nil
}
+
+ ep.Method = RequestMethodDelete
+ ep.RequestType = RequestTypeQuery
+
+ // Check if the same route already registered.
+ for _, rute := range srv.routeDeletes {
+ _, ok := rute.parse(ep.Path)
+ if ok {
+ return ErrEndpointAmbiguous
+ }
+ }
+
+ rute, err := newRoute(ep)
+ if err != nil {
+ return err
+ }
+
+ srv.routeDeletes = append(srv.routeDeletes, rute)
+
+ return nil
}
//
@@ -111,93 +124,152 @@ func (srv *Server) RegisterEvaluator(eval Evaluator) {
//
// RegisterGet register HTTP method GET with callback to handle it.
//
-func (srv *Server) RegisterGet(ep *Endpoint) {
- if ep != nil {
- ep.Method = RequestMethodGet
- ep.RequestType = RequestTypeQuery
- srv.register(ep)
+func (srv *Server) RegisterGet(ep *Endpoint) (err error) {
+ if ep == nil || ep.Call == nil {
+ return nil
}
+
+ ep.Method = RequestMethodGet
+ ep.RequestType = RequestTypeQuery
+
+ // Check if the same route already registered.
+ for _, rute := range srv.routeGets {
+ _, ok := rute.parse(ep.Path)
+ if ok {
+ return ErrEndpointAmbiguous
+ }
+ }
+
+ rute, err := newRoute(ep)
+ if err != nil {
+ return err
+ }
+
+ srv.routeGets = append(srv.routeGets, rute)
+
+ return nil
}
//
// RegisterPatch register HTTP method PATCH with callback to handle it.
//
-func (srv *Server) RegisterPatch(ep *Endpoint) {
- if ep != nil {
- ep.Method = RequestMethodPatch
- srv.register(ep)
+func (srv *Server) RegisterPatch(ep *Endpoint) (err error) {
+ if ep == nil || ep.Call == nil {
+ return nil
+ }
+
+ ep.Method = RequestMethodPatch
+
+ // Check if the same route already registered.
+ for _, rute := range srv.routePatches {
+ _, ok := rute.parse(ep.Path)
+ if ok {
+ return ErrEndpointAmbiguous
+ }
+ }
+
+ rute, err := newRoute(ep)
+ if err != nil {
+ return err
}
+
+ srv.routePatches = append(srv.routePatches, rute)
+
+ return nil
}
//
// RegisterPost register HTTP method POST with callback to handle it.
//
-func (srv *Server) RegisterPost(ep *Endpoint) {
- if ep != nil {
- ep.Method = RequestMethodPost
- srv.register(ep)
+func (srv *Server) RegisterPost(ep *Endpoint) (err error) {
+ if ep == nil || ep.Call == nil {
+ return nil
}
+
+ ep.Method = RequestMethodPost
+
+ // Check if the same route already registered.
+ for _, rute := range srv.routePosts {
+ _, ok := rute.parse(ep.Path)
+ if ok {
+ return ErrEndpointAmbiguous
+ }
+ }
+
+ rute, err := newRoute(ep)
+ if err != nil {
+ return err
+ }
+
+ srv.routePosts = append(srv.routePosts, rute)
+
+ return nil
+
}
//
// RegisterPut register HTTP method PUT with callback to handle it.
//
-func (srv *Server) RegisterPut(ep *Endpoint) {
- if ep != nil {
- ep.Method = RequestMethodPut
- ep.ResponseType = ResponseTypeNone
- srv.register(ep)
+func (srv *Server) RegisterPut(ep *Endpoint) (err error) {
+ if ep == nil || ep.Call == nil {
+ return nil
+ }
+
+ ep.Method = RequestMethodPut
+ ep.ResponseType = ResponseTypeNone
+
+ // Check if the same route already registered.
+ for _, rute := range srv.routePuts {
+ _, ok := rute.parse(ep.Path)
+ if ok {
+ return ErrEndpointAmbiguous
+ }
+ }
+
+ rute, err := newRoute(ep)
+ if err != nil {
+ return err
}
+
+ srv.routePuts = append(srv.routePuts, rute)
+
+ return nil
}
//
// ServeHTTP handle mapping of client request to registered endpoints.
//
func (srv *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
- var (
- ep *Endpoint
- ok bool
- )
-
if debug.Value > 0 {
log.Printf("> ServeHTTP: %s %+v\n", req.Method, req.URL)
}
switch req.Method {
case http.MethodDelete:
- ep, ok = srv.regDelete[req.URL.Path]
+ srv.handleDelete(res, req)
case http.MethodGet:
srv.handleGet(res, req)
- return
case http.MethodHead:
srv.handleHead(res, req)
- return
case http.MethodOptions:
srv.handleOptions(res, req)
- return
case http.MethodPatch:
- ep, ok = srv.regPatch[req.URL.Path]
+ srv.handlePatch(res, req)
case http.MethodPost:
- ep, ok = srv.regPost[req.URL.Path]
+ srv.handlePost(res, req)
case http.MethodPut:
- ep, ok = srv.regPut[req.URL.Path]
+ srv.handlePut(res, req)
default:
res.WriteHeader(http.StatusNotImplemented)
return
}
- if !ok {
- res.WriteHeader(http.StatusNotFound)
- return
- }
-
- ep.call(res, req, srv.evals)
}
//
@@ -242,6 +314,21 @@ func (srv *Server) getFSNode(reqPath string) (node *memfs.Node) {
return node
}
+//
+// handleDelete handle the DELETE request by searching the registered route
+// and calling the endpoint.
+//
+func (srv *Server) handleDelete(res http.ResponseWriter, req *http.Request) {
+ for _, rute := range srv.routeDeletes {
+ vals, ok := rute.parse(req.URL.Path)
+ if ok {
+ rute.endpoint.call(res, req, srv.evals, vals)
+ return
+ }
+ }
+ res.WriteHeader(http.StatusNotFound)
+}
+
func (srv *Server) handleFS(
res http.ResponseWriter, req *http.Request, method RequestMethod,
) {
@@ -285,24 +372,41 @@ func (srv *Server) handleFS(
}
}
+//
+// handleGet handle the GET request by searching the registered route and
+// calling the endpoint.
+//
func (srv *Server) handleGet(res http.ResponseWriter, req *http.Request) {
- ep, ok := srv.regGet[req.URL.Path]
- if ok {
- ep.call(res, req, srv.evals)
- return
+ for _, rute := range srv.routeGets {
+ vals, ok := rute.parse(req.URL.Path)
+ if ok {
+ log.Printf("handleGet: %s %s\n", req.URL.Path, vals)
+ rute.endpoint.call(res, req, srv.evals, vals)
+ return
+ }
}
srv.handleFS(res, req, RequestMethodGet)
}
func (srv *Server) handleHead(res http.ResponseWriter, req *http.Request) {
- ep, ok := srv.regGet[req.URL.Path]
+ var (
+ rute *route
+ ok bool
+ )
+
+ for _, rute = range srv.routeGets {
+ _, ok = rute.parse(req.URL.Path)
+ if ok {
+ break
+ }
+ }
if !ok {
srv.handleFS(res, req, RequestMethodHead)
return
}
- switch ep.ResponseType {
+ switch rute.endpoint.ResponseType {
case ResponseTypeNone:
res.WriteHeader(http.StatusNoContent)
return
@@ -331,25 +435,44 @@ func (srv *Server) handleOptions(res http.ResponseWriter, req *http.Request) {
methods[http.MethodHead] = true
}
- ep, ok := srv.regDelete[req.URL.Path]
- if ok && ep != nil {
- methods[http.MethodDelete] = true
+ for _, rute := range srv.routeDeletes {
+ _, ok := rute.parse(req.URL.Path)
+ if ok {
+ methods[http.MethodDelete] = true
+ break
+ }
}
- _, ok = srv.regGet[req.URL.Path]
- if ok && ep != nil {
- methods[http.MethodGet] = true
+
+ for _, rute := range srv.routeGets {
+ _, ok := rute.parse(req.URL.Path)
+ if ok {
+ methods[http.MethodGet] = true
+ break
+ }
}
- _, ok = srv.regPatch[req.URL.Path]
- if ok && ep != nil {
- methods[http.MethodPatch] = true
+
+ for _, rute := range srv.routePatches {
+ _, ok := rute.parse(req.URL.Path)
+ if ok {
+ methods[http.MethodPatch] = true
+ break
+ }
}
- _, ok = srv.regPost[req.URL.Path]
- if ok && ep != nil {
- methods[http.MethodPost] = true
+
+ for _, rute := range srv.routePosts {
+ _, ok := rute.parse(req.URL.Path)
+ if ok {
+ methods[http.MethodPost] = true
+ break
+ }
}
- ep, ok = srv.regPut[req.URL.Path]
- if ok && ep != nil {
- methods[http.MethodPut] = true
+
+ for _, rute := range srv.routePuts {
+ _, ok := rute.parse(req.URL.Path)
+ if ok {
+ methods[http.MethodPut] = true
+ break
+ }
}
if len(methods) == 0 {
@@ -375,27 +498,46 @@ func (srv *Server) handleOptions(res http.ResponseWriter, req *http.Request) {
}
//
-// register new endpoint with specific method, path, request type, and
-// response type.
+// handlePatch handle the PATCH request by searching the registered route and
+// calling the endpoint.
//
-func (srv *Server) register(ep *Endpoint) {
- if ep == nil || ep.Call == nil {
- return
+func (srv *Server) handlePatch(res http.ResponseWriter, req *http.Request) {
+ for _, rute := range srv.routePatches {
+ vals, ok := rute.parse(req.URL.Path)
+ if ok {
+ rute.endpoint.call(res, req, srv.evals, vals)
+ return
+ }
}
- if len(ep.Path) == 0 {
- ep.Path = "/"
+ res.WriteHeader(http.StatusNotFound)
+}
+
+//
+// handlePost handle the POST request by searching the registered route and
+// calling the endpoint.
+//
+func (srv *Server) handlePost(res http.ResponseWriter, req *http.Request) {
+ for _, rute := range srv.routePosts {
+ vals, ok := rute.parse(req.URL.Path)
+ if ok {
+ rute.endpoint.call(res, req, srv.evals, vals)
+ return
+ }
}
+ res.WriteHeader(http.StatusNotFound)
+}
- switch ep.Method {
- case RequestMethodDelete:
- srv.regDelete[ep.Path] = ep
- case RequestMethodGet:
- srv.regGet[ep.Path] = ep
- case RequestMethodPatch:
- srv.regPatch[ep.Path] = ep
- case RequestMethodPost:
- srv.regPost[ep.Path] = ep
- case RequestMethodPut:
- srv.regPut[ep.Path] = ep
+//
+// handlePut handle the PUT request by searching the registered route and
+// calling the endpoint.
+//
+func (srv *Server) handlePut(res http.ResponseWriter, req *http.Request) {
+ for _, rute := range srv.routePuts {
+ vals, ok := rute.parse(req.URL.Path)
+ if ok {
+ rute.endpoint.call(res, req, srv.evals, vals)
+ return
+ }
}
+ res.WriteHeader(http.StatusNotFound)
}
diff --git a/lib/http/server_test.go b/lib/http/server_test.go
index 89055608..1cc9c178 100644
--- a/lib/http/server_test.go
+++ b/lib/http/server_test.go
@@ -17,78 +17,118 @@ import (
func TestRegisterDelete(t *testing.T) {
cases := []struct {
- desc string
- reqURL string
- ep *Endpoint
- expStatusCode int
- expBody string
+ desc string
+ reqURL string
+ ep *Endpoint
+ expStatusCode int
+ expContentType string
+ expBody string
+ expError string
}{{
- desc: "With unknown path",
- reqURL: "http://127.0.0.1:8080/",
+ desc: "With new endpoint",
ep: &Endpoint{
Path: "/delete",
ResponseType: ResponseTypePlain,
Call: cbPlain,
},
- expStatusCode: http.StatusNotFound,
}, {
- desc: "With known path and subtree root",
- reqURL: "http://127.0.0.1:8080/delete/",
+ desc: "With duplicate endpoint",
ep: &Endpoint{
Path: "/delete",
ResponseType: ResponseTypePlain,
Call: cbPlain,
},
+ expError: ErrEndpointAmbiguous.Error(),
+ }, {
+ desc: "With unknown path",
+ reqURL: "http://127.0.0.1:8080/",
expStatusCode: http.StatusNotFound,
}, {
- desc: "With response type none",
- reqURL: "http://127.0.0.1:8080/delete?k=v",
+ desc: "With known path and subtree root",
+ reqURL: "http://127.0.0.1:8080/delete/",
+ expStatusCode: http.StatusOK,
+ expContentType: ContentTypePlain,
+ expBody: "map[]\nmap[]\n<nil>\n",
+ }, {
+ desc: "With response type none",
ep: &Endpoint{
- Path: "/delete",
+ Path: "/delete/none",
ResponseType: ResponseTypeNone,
Call: cbNone,
},
+ reqURL: "http://127.0.0.1:8080/delete/none?k=v",
expStatusCode: http.StatusNoContent,
}, {
- desc: "With response type binary",
- reqURL: "http://127.0.0.1:8080/delete?k=v",
+ desc: "With response type binary",
ep: &Endpoint{
- Path: "/delete",
+ Path: "/delete/bin",
ResponseType: ResponseTypeBinary,
Call: cbPlain,
},
- expStatusCode: http.StatusOK,
- expBody: "map[k:[v]]\nmap[]\n<nil>\n",
+ reqURL: "http://127.0.0.1:8080/delete/bin?k=v",
+ expStatusCode: http.StatusOK,
+ expContentType: ContentTypeBinary,
+ expBody: "map[k:[v]]\nmap[]\n<nil>\n",
}, {
- desc: "With response type plain",
- reqURL: "http://127.0.0.1:8080/delete?k=v",
- ep: &Endpoint{
- Path: "/delete",
- ResponseType: ResponseTypePlain,
- Call: cbPlain,
- },
- expStatusCode: http.StatusOK,
- expBody: "map[k:[v]]\nmap[]\n<nil>\n",
+ desc: "With response type plain",
+ reqURL: "http://127.0.0.1:8080/delete?k=v",
+ expStatusCode: http.StatusOK,
+ expContentType: ContentTypePlain,
+ expBody: "map[k:[v]]\nmap[]\n<nil>\n",
}, {
- desc: "With response type JSON",
- reqURL: "http://127.0.0.1:8080/delete?k=v",
+ desc: "With response type JSON",
ep: &Endpoint{
- Path: "/delete",
+ Path: "/delete/json",
ResponseType: ResponseTypeJSON,
Call: cbJSON,
},
- expStatusCode: http.StatusOK,
+ reqURL: "http://127.0.0.1:8080/delete/json?k=v",
+ expStatusCode: http.StatusOK,
+ expContentType: ContentTypeJSON,
expBody: `{
"form": "map[k:[v]]",
"multipartForm": "<nil>",
"body": ""
}`,
+ }, {
+ desc: "With ambigous path",
+ ep: &Endpoint{
+ Path: "/delete/:id",
+ ResponseType: ResponseTypePlain,
+ Call: cbPlain,
+ },
+ expError: ErrEndpointAmbiguous.Error(),
+ }, {
+ desc: "With key",
+ ep: &Endpoint{
+ Path: "/delete/:id/x",
+ ResponseType: ResponseTypePlain,
+ Call: cbPlain,
+ },
+ reqURL: "http://127.0.0.1:8080/delete/1/x?k=v",
+ expStatusCode: http.StatusOK,
+ expContentType: ContentTypePlain,
+ expBody: "map[id:[1] k:[v]]\nmap[]\n<nil>\n",
+ }, {
+ desc: "With duplicate key in query",
+ reqURL: "http://127.0.0.1:8080/delete/1/x?id=v",
+ expStatusCode: http.StatusOK,
+ expContentType: ContentTypePlain,
+ expBody: "map[id:[1]]\nmap[]\n<nil>\n",
}}
for _, c := range cases {
t.Log(c.desc)
- testServer.RegisterDelete(c.ep)
+ err := testServer.RegisterDelete(c.ep)
+ if err != nil {
+ test.Assert(t, "error", c.expError, err.Error(), true)
+ continue
+ }
+
+ if len(c.reqURL) == 0 {
+ continue
+ }
req, e := http.NewRequest(http.MethodDelete, c.reqURL, nil)
if e != nil {
@@ -118,20 +158,9 @@ func TestRegisterDelete(t *testing.T) {
test.Assert(t, "Body", c.expBody, string(body), true)
- var expContentType string
gotContentType := res.Header.Get(ContentType)
- switch c.ep.ResponseType {
- case ResponseTypeBinary:
- expContentType = ContentTypeBinary
- case ResponseTypeJSON:
- expContentType = ContentTypeJSON
- default:
- expContentType = ContentTypePlain
- }
-
- test.Assert(t, "Content-Type", expContentType, gotContentType,
- true)
+ test.Assert(t, "Content-Type", c.expContentType, gotContentType, true)
}
}
@@ -156,7 +185,11 @@ func TestRegisterEvaluator(t *testing.T) {
Call: cbPlain,
}
- testServer.RegisterDelete(epEvaluate)
+ err := testServer.RegisterDelete(epEvaluate)
+ if err != nil {
+ t.Fatal(err)
+ }
+
testServer.RegisterEvaluator(testEvaluator)
cases := []struct {
@@ -202,12 +235,18 @@ func TestRegisterEvaluator(t *testing.T) {
}
func TestRegisterGet(t *testing.T) {
+ testServer.evals = nil
+
epGet := &Endpoint{
Path: "/get",
ResponseType: ResponseTypePlain,
Call: cbPlain,
}
- testServer.RegisterGet(epGet)
+
+ err := testServer.RegisterGet(epGet)
+ if err != nil {
+ t.Fatal(err)
+ }
cases := []struct {
desc string
@@ -227,7 +266,8 @@ func TestRegisterGet(t *testing.T) {
}, {
desc: "With known path and subtree root",
reqURL: "http://127.0.0.1:8080/get/",
- expStatusCode: http.StatusNotFound,
+ expStatusCode: http.StatusOK,
+ expBody: "map[]\nmap[]\n<nil>\n",
}, {
desc: "With known path",
reqURL: "http://127.0.0.1:8080/get?k=v",
@@ -258,19 +298,24 @@ func TestRegisterGet(t *testing.T) {
t.Fatal(e)
}
- test.Assert(t, "StatusCode", c.expStatusCode, res.StatusCode,
- true)
+ test.Assert(t, "StatusCode", c.expStatusCode, res.StatusCode, true)
test.Assert(t, "Body", c.expBody, string(body), true)
}
}
func TestRegisterHead(t *testing.T) {
+ testServer.routeGets = nil
+
epAPI := &Endpoint{
Path: "/api",
ResponseType: ResponseTypeJSON,
Call: cbNone,
}
- testServer.RegisterGet(epAPI)
+
+ err := testServer.RegisterGet(epAPI)
+ if err != nil {
+ t.Fatal(err)
+ }
cases := []struct {
desc string
@@ -286,9 +331,10 @@ func TestRegisterHead(t *testing.T) {
expContentType: []string{"text/html; charset=utf-8"},
expContentLength: []string{"40"},
}, {
- desc: "With registered GET and subtree root",
- reqURL: "http://127.0.0.1:8080/api/",
- expStatusCode: http.StatusNotFound,
+ desc: "With registered GET and subtree root",
+ reqURL: "http://127.0.0.1:8080/api/",
+ expStatusCode: http.StatusOK,
+ expContentType: []string{ContentTypeJSON},
}, {
desc: "With registered GET",
reqURL: "http://127.0.0.1:8080/api?k=v",
@@ -336,7 +382,11 @@ func TestRegisterPatch(t *testing.T) {
ResponseType: ResponseTypePlain,
Call: cbPlain,
}
- testServer.RegisterPatch(ep)
+
+ err := testServer.RegisterPatch(ep)
+ if err != nil {
+ t.Fatal(err)
+ }
cases := []struct {
desc string
@@ -350,7 +400,8 @@ func TestRegisterPatch(t *testing.T) {
}, {
desc: "With registered PATCH and subtree root",
reqURL: "http://127.0.0.1:8080/patch/",
- expStatusCode: http.StatusNotFound,
+ expStatusCode: http.StatusOK,
+ expBody: "map[]\nmap[]\n<nil>\n",
}, {
desc: "With registered PATCH and query",
reqURL: "http://127.0.0.1:8080/patch?k=v",
@@ -395,7 +446,10 @@ func TestRegisterPost(t *testing.T) {
Call: cbPlain,
}
- testServer.RegisterPost(ep)
+ err := testServer.RegisterPost(ep)
+ if err != nil {
+ t.Fatal(err)
+ }
cases := []struct {
desc string
@@ -410,7 +464,8 @@ func TestRegisterPost(t *testing.T) {
}, {
desc: "With registered POST and subtree root",
reqURL: "http://127.0.0.1:8080/post/",
- expStatusCode: http.StatusNotFound,
+ expStatusCode: http.StatusOK,
+ expBody: "map[]\nmap[]\n<nil>\n",
}, {
desc: "With registered POST and query",
reqURL: "http://127.0.0.1:8080/post?k=v",
@@ -463,7 +518,10 @@ func TestRegisterPut(t *testing.T) {
Call: cbPlain,
}
- testServer.RegisterPut(ep)
+ err := testServer.RegisterPut(ep)
+ if err != nil {
+ t.Fatal(err)
+ }
cases := []struct {
desc string
@@ -477,7 +535,7 @@ func TestRegisterPut(t *testing.T) {
}, {
desc: "With registered PUT and subtree root",
reqURL: "http://127.0.0.1:8080/put/",
- expStatusCode: http.StatusNotFound,
+ expStatusCode: http.StatusNoContent,
}, {
desc: "With registered PUT and query",
reqURL: "http://127.0.0.1:8080/put?k=v",
@@ -526,8 +584,15 @@ func TestServeHTTPOptions(t *testing.T) {
Call: cbPlain,
}
- testServer.RegisterDelete(epDelete)
- testServer.RegisterPatch(epPatch)
+ err := testServer.RegisterDelete(epDelete)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = testServer.RegisterPatch(epPatch)
+ if err != nil {
+ t.Fatal(err)
+ }
cases := []struct {
desc string
@@ -542,7 +607,8 @@ func TestServeHTTPOptions(t *testing.T) {
}, {
desc: "With registered PATCH and subtree root",
reqURL: "http://127.0.0.1:8080/options/",
- expStatusCode: http.StatusNotFound,
+ expStatusCode: http.StatusOK,
+ expAllow: "DELETE, OPTIONS, PATCH",
}, {
desc: "With registered PATCH and query",
reqURL: "http://127.0.0.1:8080/options?k=v",
@@ -601,7 +667,10 @@ func TestStatusError(t *testing.T) {
ResponseType: ResponseTypeNone,
Call: cbError,
}
- testServer.RegisterPost(epErrNoBody)
+ err := testServer.RegisterPost(epErrNoBody)
+ if err != nil {
+ t.Fatal(err)
+ }
epErrBinary := &Endpoint{
Path: "/error/binary",
@@ -609,7 +678,10 @@ func TestStatusError(t *testing.T) {
ResponseType: ResponseTypeBinary,
Call: cbError,
}
- testServer.RegisterPost(epErrBinary)
+ err = testServer.RegisterPost(epErrBinary)
+ if err != nil {
+ t.Fatal(err)
+ }
epErrJSON := &Endpoint{
Path: "/error/json",
@@ -617,7 +689,10 @@ func TestStatusError(t *testing.T) {
ResponseType: ResponseTypeJSON,
Call: cbError,
}
- testServer.RegisterPost(epErrJSON)
+ err = testServer.RegisterPost(epErrJSON)
+ if err != nil {
+ t.Fatal(err)
+ }
epErrPlain := &Endpoint{
Path: "/error/plain",
@@ -625,7 +700,10 @@ func TestStatusError(t *testing.T) {
ResponseType: ResponseTypePlain,
Call: cbError,
}
- testServer.RegisterPost(epErrPlain)
+ err = testServer.RegisterPost(epErrPlain)
+ if err != nil {
+ t.Fatal(err)
+ }
epErrNoCode := &Endpoint{
Path: "/error/no-code",
@@ -633,7 +711,10 @@ func TestStatusError(t *testing.T) {
ResponseType: ResponseTypePlain,
Call: cbNoCode,
}
- testServer.RegisterPost(epErrNoCode)
+ err = testServer.RegisterPost(epErrNoCode)
+ if err != nil {
+ t.Fatal(err)
+ }
epErrCustom := &Endpoint{
Path: "/error/custom",
@@ -641,7 +722,10 @@ func TestStatusError(t *testing.T) {
ResponseType: ResponseTypePlain,
Call: cbCustomErr,
}
- testServer.RegisterPost(epErrCustom)
+ err = testServer.RegisterPost(epErrCustom)
+ if err != nil {
+ t.Fatal(err)
+ }
cases := []struct {
desc string