0

I am using JSON-RPC over Websocket. And, in an RPC method (say, Multiply in the example below), I need to know which connection called this method. The part below that says "// Need Websocket connection information here". How do I do so?

package main

import (
    "code.google.com/p/go.net/websocket"
    "net/http"
    "net/rpc"
    "net/rpc/jsonrpc"
)

type Args struct {
    A int
    B int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    // Need Websocket connection information here
    return nil
}

func main() {
    rpc.Register(new(Arith))
    http.Handle("/conn", websocket.Handler(serve))
    http.ListenAndServe("localhost:7000", nil)
}

func serve(ws *websocket.Conn) {
    jsonrpc.ServeConn(ws)
}
Ayyappan Subramanian
  • 5,348
  • 1
  • 22
  • 44
Sinatra
  • 820
  • 6
  • 13

1 Answers1

0

This will be challenging because it violates the abstraction that RPC provides. Here's a strategy suggestion:

Google uses a context object for lots of their APIs: https://blog.golang.org/context. Part of that interface is a Value method for arbitrary data:

Value(key interface{}) interface{}

That will give you something like thread-local storage, which is often used for this purpose in other programming languages.

So how do you add a context object to the request? One way would be to create a new, custom ServerCodec:

type ServerCodec interface {
    ReadRequestHeader(*Request) error
    ReadRequestBody(interface{}) error
    // WriteResponse must be safe for concurrent use by multiple goroutines.
    WriteResponse(*Response, interface{}) error

    Close() error
}

Your implementation can mostly mirror jsonrpc's:

var params [1]interface{}
params[0] = x
return json.Unmarshal(*c.req.Params, &params)

But before returning, you can use a bit of reflection and look for a field in params with name/type Context and then fill it. Something like:

ctx := createContextSomehow()
v := reflect.ValueOf(x)
if v.Kind() == reflect.Ptr {
    v = v.Elem()
    if v.Kind() == reflect.Struct {
        ctxv := v.FieldByName("Context")
        ctxv.Set(ctx)
    }
}

Then change your request:

type Args struct {
    A int
    B int
}

Change it to:

type Args struct {
    A int
    B int
    Context context.Context
}

Kinda clumsy but I think you could make that work.

Caleb
  • 9,272
  • 38
  • 30