github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/cloudflare/cfssl/api/api.go (about)

     1  // Package api implements an HTTP-based API and server for CFSSL.
     2  package api
     3  
     4  import (
     5  	"encoding/json"
     6  	"github.com/hellobchain/newcryptosm/http"
     7  	"io/ioutil"
     8  
     9  	"github.com/hellobchain/third_party/cloudflare/cfssl/errors"
    10  	"github.com/hellobchain/third_party/cloudflare/cfssl/log"
    11  )
    12  
    13  // Handler is an interface providing a generic mechanism for handling HTTP requests.
    14  type Handler interface {
    15  	Handle(w http.ResponseWriter, r *http.Request) error
    16  }
    17  
    18  // HTTPHandler is a wrapper that encapsulates Handler interface as http.Handler.
    19  // HTTPHandler also enforces that the Handler only responds to requests with registered HTTP methods.
    20  type HTTPHandler struct {
    21  	Handler          // CFSSL handler
    22  	Methods []string // The associated HTTP methods
    23  }
    24  
    25  // HandlerFunc is similar to the http.HandlerFunc type; it serves as
    26  // an adapter allowing the use of ordinary functions as Handlers. If
    27  // f is a function with the appropriate signature, HandlerFunc(f) is a
    28  // Handler object that calls f.
    29  type HandlerFunc func(http.ResponseWriter, *http.Request) error
    30  
    31  // Handle calls f(w, r)
    32  func (f HandlerFunc) Handle(w http.ResponseWriter, r *http.Request) error {
    33  	w.Header().Set("Content-Type", "application/json")
    34  	return f(w, r)
    35  }
    36  
    37  // HandleError is the centralised error handling and reporting.
    38  func HandleError(w http.ResponseWriter, err error) (code int) {
    39  	if err == nil {
    40  		return http.StatusOK
    41  	}
    42  	msg := err.Error()
    43  	httpCode := http.StatusInternalServerError
    44  
    45  	// If it is recognized as HttpError emitted from cfssl,
    46  	// we rewrite the status code accordingly. If it is a
    47  	// cfssl error, set the http status to StatusBadRequest
    48  	switch err := err.(type) {
    49  	case *errors.HTTPError:
    50  		httpCode = err.StatusCode
    51  		code = err.StatusCode
    52  	case *errors.Error:
    53  		httpCode = http.StatusBadRequest
    54  		code = err.ErrorCode
    55  		msg = err.Message
    56  	}
    57  
    58  	response := NewErrorResponse(msg, code)
    59  	jsonMessage, err := json.Marshal(response)
    60  	if err != nil {
    61  		log.Errorf("Failed to marshal JSON: %v", err)
    62  	} else {
    63  		msg = string(jsonMessage)
    64  	}
    65  	http.Error(w, msg, httpCode)
    66  	return code
    67  }
    68  
    69  // ServeHTTP encapsulates the call to underlying Handler to handle the request
    70  // and return the response with proper HTTP status code
    71  func (h HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    72  	var err error
    73  	var match bool
    74  	// Throw 405 when requested with an unsupported verb.
    75  	for _, m := range h.Methods {
    76  		if m == r.Method {
    77  			match = true
    78  		}
    79  	}
    80  	if match {
    81  		err = h.Handle(w, r)
    82  	} else {
    83  		err = errors.NewMethodNotAllowed(r.Method)
    84  	}
    85  	status := HandleError(w, err)
    86  	log.Infof("%s - \"%s %s\" %d", r.RemoteAddr, r.Method, r.URL, status)
    87  }
    88  
    89  // readRequestBlob takes a JSON-blob-encoded response body in the form
    90  // map[string]string and returns it, the list of keywords presented,
    91  // and any error that occurred.
    92  func readRequestBlob(r *http.Request) (map[string]string, error) {
    93  	var blob map[string]string
    94  
    95  	body, err := ioutil.ReadAll(r.Body)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	r.Body.Close()
   100  
   101  	err = json.Unmarshal(body, &blob)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	return blob, nil
   106  }
   107  
   108  // ProcessRequestOneOf reads a JSON blob for the request and makes
   109  // sure it contains one of a set of keywords. For example, a request
   110  // might have the ('foo' && 'bar') keys, OR it might have the 'baz'
   111  // key.  In either case, we want to accept the request; however, if
   112  // none of these sets shows up, the request is a bad request, and it
   113  // should be returned.
   114  func ProcessRequestOneOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
   115  	blob, err := readRequestBlob(r)
   116  	if err != nil {
   117  		return nil, nil, err
   118  	}
   119  
   120  	var matched []string
   121  	for _, set := range keywordSets {
   122  		if matchKeywords(blob, set) {
   123  			if matched != nil {
   124  				return nil, nil, errors.NewBadRequestString("mismatched parameters")
   125  			}
   126  			matched = set
   127  		}
   128  	}
   129  	if matched == nil {
   130  		return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
   131  	}
   132  	return blob, matched, nil
   133  }
   134  
   135  // ProcessRequestFirstMatchOf reads a JSON blob for the request and returns
   136  // the first match of a set of keywords. For example, a request
   137  // might have one of the following combinations: (foo=1, bar=2), (foo=1), and (bar=2)
   138  // By giving a specific ordering of those combinations, we could decide how to accept
   139  // the request.
   140  func ProcessRequestFirstMatchOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
   141  	blob, err := readRequestBlob(r)
   142  	if err != nil {
   143  		return nil, nil, err
   144  	}
   145  
   146  	for _, set := range keywordSets {
   147  		if matchKeywords(blob, set) {
   148  			return blob, set, nil
   149  		}
   150  	}
   151  	return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
   152  }
   153  
   154  func matchKeywords(blob map[string]string, keywords []string) bool {
   155  	for _, keyword := range keywords {
   156  		if _, ok := blob[keyword]; !ok {
   157  			return false
   158  		}
   159  	}
   160  	return true
   161  }
   162  
   163  // ResponseMessage implements the standard for response errors and
   164  // messages. A message has a code and a string message.
   165  type ResponseMessage struct {
   166  	Code    int    `json:"code"`
   167  	Message string `json:"message"`
   168  }
   169  
   170  // Response implements the CloudFlare standard for API
   171  // responses.
   172  type Response struct {
   173  	Success  bool              `json:"success"`
   174  	Result   interface{}       `json:"result"`
   175  	Errors   []ResponseMessage `json:"errors"`
   176  	Messages []ResponseMessage `json:"messages"`
   177  }
   178  
   179  // NewSuccessResponse is a shortcut for creating new successul API
   180  // responses.
   181  func NewSuccessResponse(result interface{}) Response {
   182  	return Response{
   183  		Success:  true,
   184  		Result:   result,
   185  		Errors:   []ResponseMessage{},
   186  		Messages: []ResponseMessage{},
   187  	}
   188  }
   189  
   190  // NewSuccessResponseWithMessage is a shortcut for creating new successul API
   191  // responses that includes a message.
   192  func NewSuccessResponseWithMessage(result interface{}, message string, code int) Response {
   193  	return Response{
   194  		Success:  true,
   195  		Result:   result,
   196  		Errors:   []ResponseMessage{},
   197  		Messages: []ResponseMessage{{code, message}},
   198  	}
   199  }
   200  
   201  // NewErrorResponse is a shortcut for creating an error response for a
   202  // single error.
   203  func NewErrorResponse(message string, code int) Response {
   204  	return Response{
   205  		Success:  false,
   206  		Result:   nil,
   207  		Errors:   []ResponseMessage{{code, message}},
   208  		Messages: []ResponseMessage{},
   209  	}
   210  }
   211  
   212  // SendResponse builds a response from the result, sets the JSON
   213  // header, and writes to the http.ResponseWriter.
   214  func SendResponse(w http.ResponseWriter, result interface{}) error {
   215  	response := NewSuccessResponse(result)
   216  	w.Header().Set("Content-Type", "application/json")
   217  	enc := json.NewEncoder(w)
   218  	err := enc.Encode(response)
   219  	return err
   220  }
   221  
   222  // SendResponseWithMessage builds a response from the result and the
   223  // provided message, sets the JSON header, and writes to the
   224  // http.ResponseWriter.
   225  func SendResponseWithMessage(w http.ResponseWriter, result interface{}, message string, code int) error {
   226  	response := NewSuccessResponseWithMessage(result, message, code)
   227  	w.Header().Set("Content-Type", "application/json")
   228  	enc := json.NewEncoder(w)
   229  	err := enc.Encode(response)
   230  	return err
   231  }