package jsonrpc2 import ( "encoding/json" "errors" "fmt" "net/http" ) type httpHandler struct { handler Handler } // NewHTTPHandler returns an http.Handler which will decode RPC calls out of the // HTTP request body, and return the result as the response body. It will try to // intelligently set the status code on the response as well. func NewHTTPHandler(h Handler) http.Handler { return httpHandler{h} } func (h httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { rw.WriteHeader(http.StatusMethodNotAllowed) return } var ( ctx = r.Context() enc = json.NewEncoder(rw) dec = json.NewDecoder(r.Body) ) rw.Header().Set("Content-Type", "application/json") req, decErr := decodeRequest(dec) if decErr.Code != 0 { rw.WriteHeader(http.StatusBadRequest) _ = encodeErrorResponse(enc, req.ID, decErr) return } var rpcErr Error res, err := h.handler.ServeRPC(ctx, req) if err != nil && !errors.As(err, &rpcErr) { // We deliberately don't include the actual error in the response. If // doing so is desired this can be achieved using a middleware which // translates the non-Error into an Error prior to it reaching this // point. rpcErr = NewError(errCodeServerError, "unexpected server-side error") } switch rpcErr.Code { case 0: // no error if err := encodeResponse(enc, req.ID, res); err != nil { panic(fmt.Errorf("encoding response %#v: %w", res, err)) } return case errCodeMethodNotFound: rw.WriteHeader(http.StatusNotFound) case errCodeServerError: rw.WriteHeader(http.StatusInternalServerError) default: rw.WriteHeader(http.StatusBadRequest) } if err := encodeErrorResponse(enc, req.ID, rpcErr); err != nil { panic(fmt.Errorf("encoding error %+v: %w", rpcErr, err)) } }