zidsal
zidsal

Reputation: 587

golang rpc not returning when struct has a nested struct

UPDATE

moving from gob encoding to json fixed the issue. However I would still like to know why this was failing to work with gob.

so my client code looks like this

account := new(database.Account)

err := client.Call("AccountDb.FindAccount", "username", account)

if err != nil {
    logger.FATAL.Print(err.Error())
    return
}

logger.INFO.Print(account)

On the server side AccountDb.FindAccount looks like this

func (t *AccountDb) FindAccount(args *string, reply *Account) error {

    reply.Username = "this is a test"

    return nil
}

The struct for Account looks like this

type Account struct {
    Id           int
    Username     string
    Password     string
    Email        string
    Created      time.Time
    LastLoggedIn time.Time
    AccessLevel  int

    Banned struct {
        reason  string
        expires time.Time
    }
}

if I attempt to perform the rpc the requests starts and the server executes the procedure. However the program then hangs and the procedure does not return! If however I remove the Banned anonymous struct from the Account struct it works fine! Why is this? is there a solution to this problem?

edit the client and server register code looks like this

Client

client, err = rpc.DialHTTP("tcp", "127.0.0.1:9001")

if err != nil {
    logger.FATAL.Panic(err.Error())
}

Server

defer db.Close()

account := new(database.AccountDb)
account.Database = db

rpc.Register(account)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":9001")

if e != nil {
    logger.FATAL.Fatal("listen error:", e)
}
http.Serve(l, nil)

Upvotes: 2

Views: 4461

Answers (1)

lnmx
lnmx

Reputation: 11164

Evidently, the gob encoder fails to marshal the RPC response because the Banned struct has no exported fields. This playground example exhibits the error:

encode error: gob: type struct { reason string; expires time.Time } 
has no exported fields

If you export the reason/expires fields by capitalizing them, the gob round-trip works OK (see http://play.golang.org/p/YrYFsk6trQ).

The JSON encoder also requires that serialized fields are exported, but it does not return an error if a struct has none. The decode just returns default/zero values. Run http://play.golang.org/p/OBBkB4tPcZ and note that the Banned information is lost on the round-trip.

If you need to detect these kinds of errors in the rpc package, there are two options:

  1. Edit rpc/debug.go, set the logDebug flag to true, and rebuild the rpc package, or
  2. Implement a ServerCodec that wraps the existing implementation and logs any errors returned by ReadRequestBody/WriteResponse.

Upvotes: 6

Related Questions