github.com/bestchains/fabric-ca@v2.0.0-alpha+incompatible/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/hyperledger/fabric-ca/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 curErr := caerrors.GetCause(err) 106 if curErr == nil { 107 return caerrors.CreateHTTPErr(500, caerrors.ErrUnknown, err.Error()) 108 } 109 return curErr 110 111 } 112 113 func writeJSON(obj interface{}, w http.ResponseWriter) { 114 enc := json.NewEncoder(w) 115 err := enc.Encode(obj) 116 if err != nil { 117 log.Errorf("Failed encoding response to JSON: %s", err) 118 } 119 } 120 func newHTTPResponseWriter(r *http.Request, w http.ResponseWriter, se *serverEndpoint) *httpResponseWriter { 121 return &httpResponseWriter{r: r, w: w, se: se} 122 } 123 124 type httpResponseWriter struct { 125 r *http.Request 126 w http.ResponseWriter 127 se *serverEndpoint 128 writeHeaderCalled bool 129 writeCalled bool 130 } 131 132 // Header returns the header map that will be sent by WriteHeader. 133 func (hrw *httpResponseWriter) Header() http.Header { 134 return hrw.w.Header() 135 } 136 137 // WriteHeader sends an HTTP response header with status code. 138 func (hrw *httpResponseWriter) WriteHeader(scode int) { 139 if !hrw.writeHeaderCalled { 140 w := hrw.w 141 w.Header().Set("Connection", "Keep-Alive") 142 if hrw.isHead() { 143 w.Header().Set("Content-Length", "0") 144 } else { 145 w.Header().Set("Transfer-Encoding", "chunked") 146 w.Header().Set("Content-Type", "application/json") 147 } 148 // Write the appropriate successful status code for this endpoint 149 if scode == http.StatusOK { 150 scode = hrw.se.getSuccessRC() 151 } 152 w.WriteHeader(scode) 153 hrw.writeHeaderCalled = true 154 } 155 } 156 157 // Write writes the data to the connection as part of an HTTP reply. 158 func (hrw *httpResponseWriter) Write(buf []byte) (int, error) { 159 if hrw.isHead() { 160 return 0, nil 161 } 162 w := hrw.w 163 hrw.WriteHeader(http.StatusOK) 164 if !hrw.writeCalled { 165 // Write the header of the body of the result 166 b, err := w.Write([]byte(`{"result":`)) 167 if err != nil { 168 return b, err 169 } 170 hrw.writeCalled = true 171 } 172 if buf == nil { 173 return 0, nil 174 } 175 return w.Write(buf) 176 } 177 178 func (hrw *httpResponseWriter) Flush() { 179 hrw.w.(http.Flusher).Flush() 180 } 181 182 func (hrw *httpResponseWriter) isHead() bool { 183 return hrw.r.Method == "HEAD" 184 }