diff --git a/callback.go b/callback.go index 2b076348d55870b4af7f7d3763becac359a12fd8..eaff16a0116c2501414e20b9fcdb5b71961e2c2b 100644 --- a/callback.go +++ b/callback.go @@ -36,6 +36,16 @@ func (r *Request) Params() json.RawMessage { return r.msg.Params } +func (r *Request) ParamSlice() []any { + var params []any + jsoniter.Unmarshal(r.msg.Params, ¶ms) + return params +} + +func (r *Request) ParamInto(v any) error { + return jsoniter.Unmarshal(r.msg.Params, &v) +} + func (r *Request) Context() context.Context { return r.ctx } diff --git a/example/echo/main.go b/example/echo/main.go new file mode 100644 index 0000000000000000000000000000000000000000..ee59cee26476ecfadc14cde500d39b1b64ffc51f --- /dev/null +++ b/example/echo/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "log" + "net/http" + + "gfx.cafe/open/jrpc" +) + +func main() { + + r := jrpc.NewRouter() + srv := jrpc.NewServer(r) + + r.HandleFunc("echo", func(w jrpc.ResponseWriter, r *jrpc.Request) { + w.Send(r.Params(), nil) + }) + + log.Println("running on 8855") + log.Println(http.ListenAndServe(":8855", srv)) +} + +// http://localhost:8855/?method=echo¶ms=[1,2,3] diff --git a/example/proxy/main.go b/example/proxy/main.go new file mode 100644 index 0000000000000000000000000000000000000000..d6e6651c5a8d48d5e2cc6f9aaac54e9929e35d9a --- /dev/null +++ b/example/proxy/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + + "gfx.cafe/open/jrpc" +) + +func main() { + + r := jrpc.NewRouter() + srv := jrpc.NewServer(r) + + c, err := jrpc.Dial("wss://mainnet.rpc.gfx.xyz") + if err != nil { + panic(err) + } + + r.HandleFunc("eth_*", func(w jrpc.ResponseWriter, r *jrpc.Request) { + var res json.RawMessage + err := c.Call(&res, r.Method(), r.ParamSlice()...) + w.Send(res, err) + }) + + log.Println("running on 8855") + log.Println(http.ListenAndServe(":8855", srv)) +} + +// http://localhost:8855/?method=echo¶ms=[1,2,3] diff --git a/http.go b/http.go index 5a187f4c1b81e52445913dcf8417aa811cdea18e..24c62cbc0273c586a17c1240a5e256872dda9a70 100644 --- a/http.go +++ b/http.go @@ -19,6 +19,7 @@ package jrpc import ( "bytes" "context" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -36,7 +37,12 @@ const ( ) // https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 -var acceptedContentTypes = []string{contentType, "application/json-rpc", "application/jsonrequest"} +var acceptedContentTypes = []string{ + // https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 + contentType, "application/json-rpc", "application/jsonrequest", + // these are added because they make sense + "application/jsonrpc2", "application/json-rpc2", "application/jrpc", +} type httpConn struct { client *http.Client @@ -216,8 +222,36 @@ type httpServerConn struct { } func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { - body := io.LimitReader(r.Body, maxRequestContentLength) - conn := &httpServerConn{Reader: body, Writer: w, r: r} + conn := &httpServerConn{Writer: w, r: r} + // if the request is a GET request, and the body is empty, we turn the request into fake json rpc request, see below + // https://www.jsonrpc.org/historical/json-rpc-over-http.html#encoded-parameters + // we however allow for non base64 encoded parameters to be passed + if r.Method == http.MethodGet { + // default id 1 + id := `1` + id_up := r.URL.Query().Get("id") + if id_up != "" { + id = id_up + } + method_up := r.URL.Query().Get("method") + params, _ := url.QueryUnescape(r.URL.Query().Get("params")) + param := []byte(params) + if pb, err := base64.URLEncoding.DecodeString(params); err == nil { + param = pb + } + buf := new(bytes.Buffer) + buf.Grow(64) + json.NewEncoder(buf).Encode(jsonrpcMessage{ + Version: "2.0", + ID: []byte(id), + Method: method_up, + Params: param, + }) + conn.Reader = buf + } else { + // it's a post request or whatever, so just process it like normal + conn.Reader = io.LimitReader(r.Body, maxRequestContentLength) + } return NewCodec(conn) } @@ -284,7 +318,8 @@ func validateRequest(r *http.Request) (int, error) { } } } - // Invalid content-type - err := fmt.Errorf("invalid content type, only %s is supported", contentType) - return http.StatusUnsupportedMediaType, err + // Invalid content-type ignored for now + return 0, nil + //err := fmt.Errorf("invalid content type, only %s is supported", contentType) + //return http.StatusUnsupportedMediaType, err }