Files
higress/plugins/wasm-go/pkg/wrapper/jsonrpc_wrapper.go
2025-03-24 22:11:45 +08:00

124 lines
4.5 KiB
Go

// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package wrapper
import (
"fmt"
"github.com/alibaba/higress/plugins/wasm-go/pkg/log"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
const (
CtxJsonRpcID = "jsonRpcID"
JError = "error"
JCode = "code"
JMessage = "message"
JResult = "result"
ErrParseError = -32700
ErrInvalidRequest = -32600
ErrMethodNotFound = -32601
ErrInvalidParams = -32602
ErrInternalError = -32603
)
type JsonRpcRequestHandler[PluginConfig any] func(context HttpContext, config PluginConfig, id int64, method string, params gjson.Result) types.Action
type JsonRpcResponseHandler[PluginConfig any] func(context HttpContext, config PluginConfig, id int64, result gjson.Result, error gjson.Result) types.Action
type JsonRpcMethodHandler[PluginConfig any] func(context HttpContext, config PluginConfig, id int64, params gjson.Result) error
type MethodHandlers[PluginConfig any] map[string]JsonRpcMethodHandler[PluginConfig]
func sendJsonRpcResponse(id int64, extras map[string]any, debugInfo string) {
body := []byte(`{"jsonrpc": "2.0"}`)
body, _ = sjson.SetBytes(body, "id", id)
for key, value := range extras {
body, _ = sjson.SetBytes(body, key, value)
}
proxywasm.SendHttpResponseWithDetail(200, debugInfo, [][2]string{{"Content-Type", "application/json; charset=utf-8"}}, body, -1)
}
func (ctx *CommonHttpCtx[PluginConfig]) OnJsonRpcResponseSuccess(result map[string]any) {
var (
id int64
ok bool
)
if id, ok = ctx.userContext[CtxJsonRpcID].(int64); !ok {
proxywasm.SendHttpResponseWithDetail(500, "not_found_json_rpc_id", nil, []byte("not found json rpc id"), -1)
return
}
sendJsonRpcResponse(id, map[string]any{JResult: result}, "json_rpc_success")
}
func (ctx *CommonHttpCtx[PluginConfig]) OnJsonRpcResponseError(err error, code ...int) {
var (
id int64
ok bool
)
if id, ok = ctx.userContext[CtxJsonRpcID].(int64); !ok {
proxywasm.SendHttpResponseWithDetail(500, "not_found_json_rpc_id", nil, []byte("not found json rpc id"), -1)
return
}
errorCode := ErrInternalError
if len(code) > 0 {
errorCode = code[0]
}
sendJsonRpcResponse(id, map[string]any{JError: map[string]any{
JMessage: err.Error(),
JCode: errorCode,
}}, "json_rpc_error")
}
func (ctx *CommonHttpCtx[PluginConfig]) HandleJsonRpcMethod(context HttpContext, config PluginConfig, body []byte, handles MethodHandlers[PluginConfig]) types.Action {
id := gjson.GetBytes(body, "id").Int()
ctx.userContext[CtxJsonRpcID] = id
method := gjson.GetBytes(body, "method").String()
params := gjson.GetBytes(body, "params")
if handle, ok := handles[method]; ok {
log.Debugf("json rpc call id[%d] method[%s] with params[%s]", id, method, params.Raw)
err := handle(context, config, id, params)
if err != nil {
ctx.OnJsonRpcResponseError(err)
return types.ActionContinue
}
// Waiting for the response
return types.ActionPause
}
ctx.OnJsonRpcResponseError(fmt.Errorf("method not found:%s", method), ErrMethodNotFound)
return types.ActionContinue
}
func (ctx *CommonHttpCtx[PluginConfig]) HandleJsonRpcRequest(context HttpContext, config PluginConfig, body []byte, handle JsonRpcRequestHandler[PluginConfig]) types.Action {
id := gjson.GetBytes(body, "id").Int()
ctx.userContext[CtxJsonRpcID] = id
method := gjson.GetBytes(body, "method").String()
params := gjson.GetBytes(body, "params")
log.Debugf("json rpc call id[%d] method[%s] with params[%s]", id, method, params.Raw)
return handle(context, config, id, method, params)
}
func (ctx *CommonHttpCtx[PluginConfig]) HandleJsonRpcResponse(context HttpContext, config PluginConfig, body []byte, handle JsonRpcResponseHandler[PluginConfig]) types.Action {
id := gjson.GetBytes(body, "id").Int()
error := gjson.GetBytes(body, "error")
result := gjson.GetBytes(body, "result")
log.Debugf("json rpc response id[%d] error[%s] result[%s]", id, error.Raw, result.Raw)
return handle(context, config, id, result, error)
}