github.com/simranvc/fabric-ca@v0.0.0-20191030094829-acc364294dde/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 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 }