isle/go/daemon/jsonrpc2/server_http.go

73 lines
1.8 KiB
Go
Raw Permalink Normal View History

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))
}
}