github.com/tw-bc-group/fabric-ca-gm@v0.0.0-20201218004200-3b690512bd5a/lib/serverendpoint.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package lib
     8  
     9  import (
    10  	"encoding/json"
    11  	"net/http"
    12  
    13  	"github.com/cloudflare/cfssl/api"
    14  	"github.com/cloudflare/cfssl/log"
    15  	"github.com/tw-bc-group/fabric-ca-gm/lib/caerrors"
    16  )
    17  
    18  // serverEndpoint represents a particular endpoint (e.g. to "/api/v1/enroll")
    19  type serverEndpoint struct {
    20  	// Path is the path to the endpoint
    21  	Path string
    22  	// The HTTP methods ("GET", "POST", etc) which the function will handle
    23  	Methods []string
    24  	// The HTTP status code for a successful response
    25  	successRC int
    26  	// Handler is the handler function for this endpoint
    27  	Handler func(ctx *serverRequestContextImpl) (interface{}, error)
    28  	// Server which hosts this endpoint
    29  	Server *Server
    30  }
    31  
    32  // ServeHTTP encapsulates the call to underlying Handlers to handle the request
    33  // and return the response with a proper HTTP status code
    34  func (se *serverEndpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    35  	var resp interface{}
    36  	url := r.URL.String()
    37  	log.Debugf("Received request for %s", url)
    38  	w = newHTTPResponseWriter(r, w, se)
    39  	err := se.validateMethod(r)
    40  	if err == nil {
    41  		// Call the endpoint handler to handle the request.  The handler may
    42  		// a) return the response in the 'resp' variable below, or
    43  		// b) write the response one chunk at a time, which is appropriate if the response may be large
    44  		//    and we don't want the server to buffer the entire response in memory.
    45  		resp, err = se.Handler(newServerRequestContext(r, w, se))
    46  	}
    47  	he := getHTTPErr(err)
    48  	if he != nil {
    49  		// An error occurred
    50  		w.WriteHeader(he.GetStatusCode())
    51  		log.Infof(`%s %s %s %d %d "%s"`, r.RemoteAddr, r.Method, r.URL, he.GetStatusCode(), he.GetLocalCode(), he.GetLocalMsg())
    52  	} else {
    53  		// No error occurred
    54  		scode := se.getSuccessRC()
    55  		w.WriteHeader(scode)
    56  		log.Infof(`%s %s %s %d 0 "OK"`, r.RemoteAddr, r.Method, r.URL, scode)
    57  	}
    58  	// If a response was returned by the handler, write it now.
    59  	if resp != nil {
    60  		writeJSON(resp, w)
    61  	}
    62  	// If nothing has been written, write an empty string for the response
    63  	if !w.(*httpResponseWriter).writeCalled {
    64  		w.Write([]byte(`""`))
    65  	}
    66  	// If an error was returned by the handler, write it now.
    67  	w.Write([]byte(`,"errors":[`))
    68  	if he != nil {
    69  		rm := &api.ResponseMessage{Code: he.GetRemoteCode(), Message: he.GetRemoteMsg()}
    70  		writeJSON(rm, w)
    71  	}
    72  	// Write true or false for success
    73  	w.Write([]byte(`],"messages":[],"success":`))
    74  	if he != nil {
    75  		w.Write([]byte(`false}`))
    76  	} else {
    77  		w.Write([]byte(`true}`))
    78  	}
    79  	w.(http.Flusher).Flush()
    80  }
    81  
    82  func (se *serverEndpoint) getSuccessRC() int {
    83  	if se.successRC == 0 {
    84  		return 200
    85  	}
    86  	return se.successRC
    87  }
    88  
    89  // Validate that the HTTP method is supported for this endpoint
    90  func (se *serverEndpoint) validateMethod(r *http.Request) error {
    91  	for _, m := range se.Methods {
    92  		if m == r.Method {
    93  			return nil
    94  		}
    95  	}
    96  	return caerrors.NewHTTPErr(405, caerrors.ErrMethodNotAllowed, "Method %s is not allowed", r.Method)
    97  }
    98  
    99  // Get the top-most HTTP error from the cause stack.
   100  // If not found, create one with an unknown error code.
   101  func getHTTPErr(err error) *caerrors.HTTPErr {
   102  	if err == nil {
   103  		return nil
   104  	}
   105  	type causer interface {
   106  		Cause() error
   107  	}
   108  	curErr := err
   109  	for curErr != nil {
   110  		switch curErr.(type) {
   111  		case *caerrors.HTTPErr:
   112  			return curErr.(*caerrors.HTTPErr)
   113  		case causer:
   114  			curErr = curErr.(causer).Cause()
   115  		default:
   116  			return caerrors.CreateHTTPErr(500, caerrors.ErrUnknown, err.Error())
   117  		}
   118  	}
   119  	return caerrors.CreateHTTPErr(500, caerrors.ErrUnknown, "nil error")
   120  }
   121  
   122  func writeJSON(obj interface{}, w http.ResponseWriter) {
   123  	enc := json.NewEncoder(w)
   124  	err := enc.Encode(obj)
   125  	if err != nil {
   126  		log.Errorf("Failed encoding response to JSON: %s", err)
   127  	}
   128  }
   129  func newHTTPResponseWriter(r *http.Request, w http.ResponseWriter, se *serverEndpoint) *httpResponseWriter {
   130  	return &httpResponseWriter{r: r, w: w, se: se}
   131  }
   132  
   133  type httpResponseWriter struct {
   134  	r                 *http.Request
   135  	w                 http.ResponseWriter
   136  	se                *serverEndpoint
   137  	writeHeaderCalled bool
   138  	writeCalled       bool
   139  }
   140  
   141  // Header returns the header map that will be sent by WriteHeader.
   142  func (hrw *httpResponseWriter) Header() http.Header {
   143  	return hrw.w.Header()
   144  }
   145  
   146  // WriteHeader sends an HTTP response header with status code.
   147  func (hrw *httpResponseWriter) WriteHeader(scode int) {
   148  	if !hrw.writeHeaderCalled {
   149  		w := hrw.w
   150  		w.Header().Set("Connection", "Keep-Alive")
   151  		if hrw.isHead() {
   152  			w.Header().Set("Content-Length", "0")
   153  		} else {
   154  			w.Header().Set("Transfer-Encoding", "chunked")
   155  			w.Header().Set("Content-Type", "application/json")
   156  		}
   157  		// Write the appropriate successful status code for this endpoint
   158  		if scode == http.StatusOK {
   159  			scode = hrw.se.getSuccessRC()
   160  		}
   161  		w.WriteHeader(scode)
   162  		hrw.writeHeaderCalled = true
   163  	}
   164  }
   165  
   166  // Write writes the data to the connection as part of an HTTP reply.
   167  func (hrw *httpResponseWriter) Write(buf []byte) (int, error) {
   168  	if hrw.isHead() {
   169  		return 0, nil
   170  	}
   171  	w := hrw.w
   172  	hrw.WriteHeader(http.StatusOK)
   173  	if !hrw.writeCalled {
   174  		// Write the header of the body of the result
   175  		b, err := w.Write([]byte(`{"result":`))
   176  		if err != nil {
   177  			return b, err
   178  		}
   179  		hrw.writeCalled = true
   180  	}
   181  	if buf == nil {
   182  		return 0, nil
   183  	}
   184  	return w.Write(buf)
   185  }
   186  
   187  func (hrw *httpResponseWriter) Flush() {
   188  	hrw.w.(http.Flusher).Flush()
   189  }
   190  
   191  func (hrw *httpResponseWriter) isHead() bool {
   192  	return hrw.r.Method == "HEAD"
   193  }