func readBootstrap(t *testing.T, ctx context.Context, conn *rpc.Conn, p rpc.Transport) (client capnp.Client, questionID uint32) { clientCh := make(chan capnp.Client, 1) go func() { clientCh <- conn.Bootstrap(ctx) }() msg, err := p.RecvMessage(ctx) if err != nil { t.Fatal("Read Bootstrap failed:", err) } if msg.Which() != rpccapnp.Message_Which_bootstrap { t.Fatalf("Received %v message from bootstrap, want Message_Which_bootstrap", msg.Which()) } boot, err := msg.Bootstrap() if err != nil { t.Fatal("Read Bootstrap failed:", err) } questionID = boot.QuestionId() // If this deadlocks, then Bootstrap isn't using a promised client. client = <-clientCh if client == nil { t.Fatal("Bootstrap client is nil") } return }
func startRecvMessage(t rpc.Transport) <-chan asyncRecv { ch := make(chan asyncRecv, 1) go func() { msg, err := t.RecvMessage(context.TODO()) ch <- asyncRecv{msg, err} }() return ch }
func bootstrapRoundtrip(t *testing.T, p rpc.Transport) (importID, questionID uint32) { questionID = 54 err := sendMessage(context.TODO(), p, func(msg rpccapnp.Message) error { bootstrap, err := msg.NewBootstrap() if err != nil { return err } bootstrap.SetQuestionId(questionID) return nil }) if err != nil { t.Fatal("Write Bootstrap failed:", err) } msg, err := p.RecvMessage(context.TODO()) if err != nil { t.Fatal("Read Bootstrap response failed:", err) } if msg.Which() != rpccapnp.Message_Which_return { t.Fatalf("Conn sent %v message, want Message_Which_return", msg.Which()) } ret, err := msg.Return() if err != nil { t.Fatal("return error:", err) } if id := ret.AnswerId(); id != questionID { t.Fatalf("msg.Return().AnswerId() = %d; want %d", id, questionID) } if ret.Which() != rpccapnp.Return_Which_results { t.Fatalf("msg.Return().Which() = %v; want Return_Which_results", ret.Which()) } payload, err := ret.Results() if err != nil { t.Fatal("return.results error:", err) } content, err := payload.Content() if err != nil { t.Fatal("return.results.content error:", err) } in := capnp.ToInterface(content) if !capnp.IsValid(in) { t.Fatalf("Result payload contains %v; want interface", content) } capIdx := int(in.Capability()) capTable, err := payload.CapTable() if err != nil { t.Fatal("return.results.capTable error:", err) } if n := capTable.Len(); capIdx >= n { t.Fatalf("Payload capTable has size %d, but capability index = %d", n, capIdx) } if cw := capTable.At(capIdx).Which(); cw != rpccapnp.CapDescriptor_Which_senderHosted { t.Fatalf("Capability type is %d; want CapDescriptor_Which_senderHosted", cw) } return capTable.At(capIdx).SenderHosted(), questionID }
func sendMessage(ctx context.Context, t rpc.Transport, f func(rpccapnp.Message) error) error { _, s, err := capnp.NewMessage(capnp.SingleSegment(nil)) m, err := rpccapnp.NewRootMessage(s) if err != nil { return err } if err := f(m); err != nil { return err } return t.SendMessage(ctx, m) }
func bootstrapAndFulfill(t *testing.T, ctx context.Context, conn *rpc.Conn, p rpc.Transport) capnp.Client { client, bootstrapID := readBootstrap(t, ctx, conn, p) err := sendMessage(ctx, p, func(msg rpccapnp.Message) error { ret, err := msg.NewReturn() if err != nil { return err } ret.SetAnswerId(bootstrapID) payload, err := ret.NewResults() if err != nil { return err } payload.SetContent(capnp.NewInterface(msg.Segment(), 0)) capTable, err := rpccapnp.NewCapDescriptor_List(msg.Segment(), 1) if err != nil { return err } capTable.At(0).SetSenderHosted(bootstrapExportID) payload.SetCapTable(capTable) return nil }) if err != nil { t.Fatal("error writing Return:", err) } if finish, err := p.RecvMessage(ctx); err != nil { t.Fatal("error reading Finish:", err) } else if finish.Which() != rpccapnp.Message_Which_finish { t.Fatalf("message sent is %v; want Message_Which_finish", finish.Which()) } else { f, err := finish.Finish() if err != nil { t.Fatal(err) } if id := f.QuestionId(); id != bootstrapID { t.Fatalf("finish question ID is %d; want %d", id, bootstrapID) } if f.ReleaseResultCaps() { t.Fatal("finish released bootstrap capability") } } return client }
// Receive a Finish message for the given question ID. // // Immediately releases any capabilities in the message. // // An error is returned if any of the following occur: // // * An error occurs when reading the message // * The message is not of type `Finish` // * The message's question ID is not equal to `questionID`. // // Parameters: // // ctx: The context to be used when sending the message. // p: The rpc.Transport to send the message on. // questionID: The expected question ID. func recvFinish(ctx context.Context, p rpc.Transport, questionID uint32) error { if finish, err := p.RecvMessage(ctx); err != nil { return err } else if finish.Which() != rpccapnp.Message_Which_finish { return fmt.Errorf("message sent is %v; want Message_Which_finish", finish.Which()) } else { f, err := finish.Finish() if err != nil { return err } if id := f.QuestionId(); id != questionID { return fmt.Errorf("finish question ID is %d; want %d", id, questionID) } if f.ReleaseResultCaps() { return fmt.Errorf("finish released bootstrap capability") } } return nil }