mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-10 18:38:22 +00:00
100 lines
2.7 KiB
Go
100 lines
2.7 KiB
Go
package jsonrpc
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"runtime/debug"
|
|
|
|
"golang.org/x/exp/slog"
|
|
"nfsense.net/nfsense/internal/session"
|
|
)
|
|
|
|
type Handler struct {
|
|
methods map[string]method
|
|
|
|
maxRequestSize int64
|
|
}
|
|
|
|
func NewHandler(maxRequestSize int64) *Handler {
|
|
return &Handler{
|
|
methods: map[string]method{},
|
|
maxRequestSize: maxRequestSize,
|
|
}
|
|
}
|
|
|
|
func (h *Handler) HandleRequest(ctx context.Context, s *session.Session, r io.Reader, w io.Writer) error {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
slog.Error("Recovered Panic Handling JSONRPC Request", "err", fmt.Errorf("%v", r), "stack", debug.Stack())
|
|
}
|
|
}()
|
|
var req request
|
|
bufferedRequest := new(bytes.Buffer)
|
|
reqSize, err := bufferedRequest.ReadFrom(io.LimitReader(r, h.maxRequestSize+1))
|
|
if err != nil {
|
|
return respondError(w, "", ErrInternalError, fmt.Errorf("Reading Request: %w", err))
|
|
}
|
|
if reqSize > h.maxRequestSize {
|
|
return respondError(w, "", ErrParse, fmt.Errorf("Request exceeds Max Request Size"))
|
|
}
|
|
|
|
dec := json.NewDecoder(bufferedRequest)
|
|
dec.DisallowUnknownFields()
|
|
err = dec.Decode(&req)
|
|
if err != nil {
|
|
return respondError(w, "", ErrParse, fmt.Errorf("Decodeing Request: %w", err))
|
|
}
|
|
|
|
if req.Jsonrpc != "2.0" {
|
|
return respondError(w, req.ID, ErrMethodNotFound, fmt.Errorf("Unsupported Jsonrpc version %v", req.Jsonrpc))
|
|
}
|
|
|
|
if s == nil {
|
|
return respondError(w, req.ID, 401, fmt.Errorf("Unauthorized"))
|
|
}
|
|
|
|
method, ok := h.methods[req.Method]
|
|
if !ok {
|
|
return respondError(w, req.ID, ErrMethodNotFound, fmt.Errorf("Unknown Method %v", req.Method))
|
|
}
|
|
|
|
p := reflect.New(method.inType)
|
|
paramPointer := p.Interface()
|
|
|
|
if len(req.Params) != 0 {
|
|
dec = json.NewDecoder(bytes.NewReader(req.Params))
|
|
dec.DisallowUnknownFields()
|
|
err = dec.Decode(paramPointer)
|
|
if err != nil {
|
|
return respondError(w, req.ID, ErrInvalidParams, fmt.Errorf("Decoding Parameters: %w", err))
|
|
}
|
|
}
|
|
|
|
params := make([]reflect.Value, 3)
|
|
params[0] = method.subSystem
|
|
params[1] = reflect.ValueOf(ctx)
|
|
params[2] = reflect.ValueOf(paramPointer).Elem()
|
|
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
slog.Error("Recovered Panic Executing API Method", "err", fmt.Errorf("%v", r), "method", req.Method, "params", fmt.Sprintf("%+v", params[2]), "id", req.ID, "stack", debug.Stack())
|
|
respondError(w, req.ID, ErrInternalError, fmt.Errorf("%v", r))
|
|
}
|
|
}()
|
|
res := method.handlerFunc.Call(params)
|
|
result := res[0].Interface()
|
|
|
|
if !res[1].IsNil() {
|
|
reqerr := res[1].Interface().(error)
|
|
slog.Error("API Method", "err", reqerr, "method", req.Method, "id", req.ID, "params", fmt.Sprintf("%+v", params[2]))
|
|
respondError(w, req.ID, ErrInternalError, reqerr)
|
|
return nil
|
|
}
|
|
|
|
respondResult(w, req.ID, result)
|
|
return nil
|
|
}
|