aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2020-06-19 21:13:06 +0200
committerMartin Polden <mpolden@mpolden.no>2020-06-19 21:13:06 +0200
commita197f906c275a35db82b1f1b634337cc9dc0c273 (patch)
tree7e57f76c6cc792c6cd56d429400e634e06ad2999
parentb5b4353ad8b1c5535fe592dc6bed03853ba73ccf (diff)
http: Support non-JSON handlers
-rw-r--r--http/http.go61
-rw-r--r--http/router.go31
2 files changed, 58 insertions, 34 deletions
diff --git a/http/http.go b/http/http.go
index 8a219dc..d547010 100644
--- a/http/http.go
+++ b/http/http.go
@@ -2,6 +2,7 @@ package http
import (
"context"
+ "encoding/json"
"fmt"
"log"
"net"
@@ -15,6 +16,10 @@ import (
"github.com/mpolden/zdns/sql"
)
+const (
+ jsonMediaType = "application/json"
+)
+
// A Server defines paramaters for running an HTTP server. The HTTP server serves an API for inspecting cache contents
// and request log.
type Server struct {
@@ -130,10 +135,22 @@ func resolutionFrom(r *http.Request) (time.Duration, error) {
return time.ParseDuration(param)
}
-func (s *Server) cacheHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
+func writeJSONHeader(w http.ResponseWriter) { w.Header().Set("Content-Type", jsonMediaType) }
+
+func writeJSON(w http.ResponseWriter, data interface{}) {
+ b, err := json.Marshal(data)
+ if err != nil {
+ panic(err)
+ }
+ writeJSONHeader(w)
+ w.Write(b)
+}
+
+func (s *Server) cacheHandler(w http.ResponseWriter, r *http.Request) *httpError {
count, err := countFrom(r)
if err != nil {
- return nil, newHTTPBadRequest(err)
+ writeJSONHeader(w)
+ return newHTTPBadRequest(err)
}
cacheValues := s.cache.List(count)
entries := make([]entry, 0, len(cacheValues))
@@ -147,24 +164,28 @@ func (s *Server) cacheHandler(w http.ResponseWriter, r *http.Request) (interface
Rcode: dnsutil.RcodeToString[v.Rcode()],
})
}
- return entries, nil
+ writeJSON(w, entries)
+ return nil
}
-func (s *Server) cacheResetHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
+func (s *Server) cacheResetHandler(w http.ResponseWriter, r *http.Request) *httpError {
s.cache.Reset()
- return struct {
+ writeJSON(w, struct {
Message string `json:"message"`
- }{"Cleared cache."}, nil
+ }{"Cleared cache."})
+ return nil
}
-func (s *Server) logHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
+func (s *Server) logHandler(w http.ResponseWriter, r *http.Request) *httpError {
count, err := countFrom(r)
if err != nil {
- return nil, newHTTPBadRequest(err)
+ writeJSONHeader(w)
+ return newHTTPBadRequest(err)
}
logEntries, err := s.logger.Read(count)
if err != nil {
- return nil, newHTTPError(err)
+ writeJSONHeader(w)
+ return newHTTPError(err)
}
entries := make([]entry, 0, len(logEntries))
for _, le := range logEntries {
@@ -178,17 +199,20 @@ func (s *Server) logHandler(w http.ResponseWriter, r *http.Request) (interface{}
Answers: le.Answers,
})
}
- return entries, nil
+ writeJSON(w, entries)
+ return nil
}
-func (s *Server) basicMetricHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
+func (s *Server) basicMetricHandler(w http.ResponseWriter, r *http.Request) *httpError {
resolution, err := resolutionFrom(r)
if err != nil {
- return nil, newHTTPBadRequest(err)
+ writeJSONHeader(w)
+ return newHTTPBadRequest(err)
}
lstats, err := s.logger.Stats(resolution)
if err != nil {
- return nil, newHTTPError(err)
+ writeJSONHeader(w)
+ return newHTTPError(err)
}
requests := make([]request, 0, len(lstats.Events))
for _, e := range lstats.Events {
@@ -202,7 +226,7 @@ func (s *Server) basicMetricHandler(w http.ResponseWriter, r *http.Request) (int
if s.sqlCache != nil {
bstats = &backendStats{PendingTasks: s.sqlCache.Stats().PendingTasks}
}
- return stats{
+ stats := stats{
Summary: summary{
Log: logStats{
Since: lstats.Since.Format(time.RFC3339),
@@ -217,10 +241,12 @@ func (s *Server) basicMetricHandler(w http.ResponseWriter, r *http.Request) (int
},
},
Requests: requests,
- }, nil
+ }
+ writeJSON(w, stats)
+ return nil
}
-func (s *Server) metricHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
+func (s *Server) metricHandler(w http.ResponseWriter, r *http.Request) *httpError {
format := ""
if formatParams := r.URL.Query()["format"]; len(formatParams) > 0 {
format = formatParams[0]
@@ -229,7 +255,8 @@ func (s *Server) metricHandler(w http.ResponseWriter, r *http.Request) (interfac
case "", "basic":
return s.basicMetricHandler(w, r)
}
- return nil, newHTTPBadRequest(fmt.Errorf("invalid metric format: %s", format))
+ writeJSONHeader(w)
+ return newHTTPBadRequest(fmt.Errorf("invalid metric format: %s", format))
}
// Close shuts down the HTTP server.
diff --git a/http/router.go b/http/router.go
index 27adc33..2e6b77b 100644
--- a/http/router.go
+++ b/http/router.go
@@ -15,32 +15,29 @@ type route struct {
handler appHandler
}
-type appHandler func(http.ResponseWriter, *http.Request) (interface{}, *httpError)
+type appHandler func(http.ResponseWriter, *http.Request) *httpError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- data, e := fn(w, r)
- w.Header().Set("Content-Type", "application/json")
- if e != nil { // e is *Error, not os.Error.
+ if e := fn(w, r); e != nil { // e is *httpError, not os.Error.
if e.Message == "" {
e.Message = e.err.Error()
}
- out, err := json.Marshal(e)
- if err != nil {
- panic(err)
- }
w.WriteHeader(e.Status)
- w.Write(out)
- } else if data != nil {
- out, err := json.Marshal(data)
- if err != nil {
- panic(err)
+ if w.Header().Get("Content-Type") == jsonMediaType {
+ out, err := json.Marshal(e)
+ if err != nil {
+ panic(err)
+ }
+ w.Write(out)
+ } else {
+ w.Write([]byte(e.Message))
}
- w.Write(out)
}
}
-func notFoundHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
- return nil, &httpError{
+func notFoundHandler(w http.ResponseWriter, r *http.Request) *httpError {
+ writeJSONHeader(w)
+ return &httpError{
Status: http.StatusNotFound,
Message: "Resource not found",
}
@@ -57,7 +54,7 @@ func (r *router) route(method, path string, handler appHandler) *route {
}
func (r *router) handler() http.Handler {
- return appHandler(func(w http.ResponseWriter, req *http.Request) (interface{}, *httpError) {
+ return appHandler(func(w http.ResponseWriter, req *http.Request) *httpError {
for _, route := range r.routes {
if route.match(req) {
return route.handler(w, req)