123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- package http_api
- import (
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "time"
- "github.com/julienschmidt/httprouter"
- "github.com/nsqio/nsq/internal/lg"
- )
- type Decorator func(APIHandler) APIHandler
- type APIHandler func(http.ResponseWriter, *http.Request, httprouter.Params) (interface{}, error)
- type Err struct {
- Code int
- Text string
- }
- func (e Err) Error() string {
- return e.Text
- }
- func acceptVersion(req *http.Request) int {
- if req.Header.Get("accept") == "application/vnd.nsq; version=1.0" {
- return 1
- }
- return 0
- }
- func PlainText(f APIHandler) APIHandler {
- return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
- code := 200
- data, err := f(w, req, ps)
- if err != nil {
- code = err.(Err).Code
- data = err.Error()
- }
- switch d := data.(type) {
- case string:
- w.WriteHeader(code)
- io.WriteString(w, d)
- case []byte:
- w.WriteHeader(code)
- w.Write(d)
- default:
- panic(fmt.Sprintf("unknown response type %T", data))
- }
- return nil, nil
- }
- }
- func V1(f APIHandler) APIHandler {
- return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
- data, err := f(w, req, ps)
- if err != nil {
- RespondV1(w, err.(Err).Code, err)
- return nil, nil
- }
- RespondV1(w, 200, data)
- return nil, nil
- }
- }
- func RespondV1(w http.ResponseWriter, code int, data interface{}) {
- var response []byte
- var err error
- var isJSON bool
- if code == 200 {
- switch data.(type) {
- case string:
- response = []byte(data.(string))
- case []byte:
- response = data.([]byte)
- case nil:
- response = []byte{}
- default:
- isJSON = true
- response, err = json.Marshal(data)
- if err != nil {
- code = 500
- data = err
- }
- }
- }
- if code != 200 {
- isJSON = true
- response, _ = json.Marshal(struct {
- Message string `json:"message"`
- }{fmt.Sprintf("%s", data)})
- }
- if isJSON {
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- }
- w.Header().Set("X-SMQ-Content-Type", "smq; version=1.0")
- w.WriteHeader(code)
- w.Write(response)
- }
- func Decorate(f APIHandler, ds ...Decorator) httprouter.Handle {
- decorated := f
- for _, decorate := range ds {
- decorated = decorate(decorated)
- }
- return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
- decorated(w, req, ps)
- }
- }
- func Log(logf lg.AppLogFunc) Decorator {
- return func(f APIHandler) APIHandler {
- return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
- start := time.Now()
- response, err := f(w, req, ps)
- elapsed := time.Since(start)
- status := 200
- if e, ok := err.(Err); ok {
- status = e.Code
- }
- logf(lg.INFO, "%d %s %s (%s) %s",
- status, req.Method, req.URL.RequestURI(), req.RemoteAddr, elapsed)
- return response, err
- }
- }
- }
- func LogPanicHandler(logf lg.AppLogFunc) func(w http.ResponseWriter, req *http.Request, p interface{}) {
- return func(w http.ResponseWriter, req *http.Request, p interface{}) {
- logf(lg.ERROR, "panic in HTTP handler - %s", p)
- Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
- return nil, Err{500, "INTERNAL_ERROR"}
- }, Log(logf), V1)(w, req, nil)
- }
- }
- func LogNotFoundHandler(logf lg.AppLogFunc) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
- return nil, Err{404, "NOT_FOUND"}
- }, Log(logf), V1)(w, req, nil)
- })
- }
- func LogMethodNotAllowedHandler(logf lg.AppLogFunc) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
- return nil, Err{405, "METHOD_NOT_ALLOWED"}
- }, Log(logf), V1)(w, req, nil)
- })
- }
|