github.com/nsqio/nsq@v1.3.0/internal/http_api/api_response.go (about)

     1  package http_api
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"time"
     9  
    10  	"github.com/julienschmidt/httprouter"
    11  	"github.com/nsqio/nsq/internal/lg"
    12  )
    13  
    14  type Decorator func(APIHandler) APIHandler
    15  
    16  type APIHandler func(http.ResponseWriter, *http.Request, httprouter.Params) (interface{}, error)
    17  
    18  type Err struct {
    19  	Code int
    20  	Text string
    21  }
    22  
    23  func (e Err) Error() string {
    24  	return e.Text
    25  }
    26  
    27  func PlainText(f APIHandler) APIHandler {
    28  	return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
    29  		code := 200
    30  		data, err := f(w, req, ps)
    31  		if err != nil {
    32  			code = err.(Err).Code
    33  			data = err.Error()
    34  		}
    35  		switch d := data.(type) {
    36  		case string:
    37  			w.WriteHeader(code)
    38  			io.WriteString(w, d)
    39  		case []byte:
    40  			w.WriteHeader(code)
    41  			w.Write(d)
    42  		default:
    43  			panic(fmt.Sprintf("unknown response type %T", data))
    44  		}
    45  		return nil, nil
    46  	}
    47  }
    48  
    49  func V1(f APIHandler) APIHandler {
    50  	return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
    51  		data, err := f(w, req, ps)
    52  		if err != nil {
    53  			RespondV1(w, err.(Err).Code, err)
    54  			return nil, nil
    55  		}
    56  		RespondV1(w, 200, data)
    57  		return nil, nil
    58  	}
    59  }
    60  
    61  func RespondV1(w http.ResponseWriter, code int, data interface{}) {
    62  	var response []byte
    63  	var err error
    64  	var isJSON bool
    65  
    66  	if code == 200 {
    67  		switch data := data.(type) {
    68  		case string:
    69  			response = []byte(data)
    70  		case []byte:
    71  			response = data
    72  		case nil:
    73  			response = []byte{}
    74  		default:
    75  			isJSON = true
    76  			response, err = json.Marshal(data)
    77  			if err != nil {
    78  				code = 500
    79  				data = err
    80  			}
    81  		}
    82  	}
    83  
    84  	if code != 200 {
    85  		isJSON = true
    86  		response, _ = json.Marshal(struct {
    87  			Message string `json:"message"`
    88  		}{fmt.Sprintf("%s", data)})
    89  	}
    90  
    91  	if isJSON {
    92  		w.Header().Set("Content-Type", "application/json; charset=utf-8")
    93  	}
    94  	w.Header().Set("X-NSQ-Content-Type", "nsq; version=1.0")
    95  	w.WriteHeader(code)
    96  	w.Write(response)
    97  }
    98  
    99  func Decorate(f APIHandler, ds ...Decorator) httprouter.Handle {
   100  	decorated := f
   101  	for _, decorate := range ds {
   102  		decorated = decorate(decorated)
   103  	}
   104  	return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
   105  		decorated(w, req, ps)
   106  	}
   107  }
   108  
   109  func Log(logf lg.AppLogFunc) Decorator {
   110  	return func(f APIHandler) APIHandler {
   111  		return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
   112  			start := time.Now()
   113  			response, err := f(w, req, ps)
   114  			elapsed := time.Since(start)
   115  			status := 200
   116  			if e, ok := err.(Err); ok {
   117  				status = e.Code
   118  			}
   119  			logf(lg.INFO, "%d %s %s (%s) %s",
   120  				status, req.Method, req.URL.RequestURI(), req.RemoteAddr, elapsed)
   121  			return response, err
   122  		}
   123  	}
   124  }
   125  
   126  func LogPanicHandler(logf lg.AppLogFunc) func(w http.ResponseWriter, req *http.Request, p interface{}) {
   127  	return func(w http.ResponseWriter, req *http.Request, p interface{}) {
   128  		logf(lg.ERROR, "panic in HTTP handler - %s", p)
   129  		Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
   130  			return nil, Err{500, "INTERNAL_ERROR"}
   131  		}, Log(logf), V1)(w, req, nil)
   132  	}
   133  }
   134  
   135  func LogNotFoundHandler(logf lg.AppLogFunc) http.Handler {
   136  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   137  		Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
   138  			return nil, Err{404, "NOT_FOUND"}
   139  		}, Log(logf), V1)(w, req, nil)
   140  	})
   141  }
   142  
   143  func LogMethodNotAllowedHandler(logf lg.AppLogFunc) http.Handler {
   144  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   145  		Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
   146  			return nil, Err{405, "METHOD_NOT_ALLOWED"}
   147  		}, Log(logf), V1)(w, req, nil)
   148  	})
   149  }