73 lines
1.8 KiB
Go
73 lines
1.8 KiB
Go
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))
|
|
}
|
|
}
|