Blame


1 3bf22e58 2015-11-04 stephen.d package p9pnew
2 3bf22e58 2015-11-04 stephen.d
3 3bf22e58 2015-11-04 stephen.d import (
4 3bf22e58 2015-11-04 stephen.d "fmt"
5 3bf22e58 2015-11-04 stephen.d
6 3bf22e58 2015-11-04 stephen.d "golang.org/x/net/context"
7 3bf22e58 2015-11-04 stephen.d )
8 3bf22e58 2015-11-04 stephen.d
9 3bf22e58 2015-11-04 stephen.d // NOTE(stevvooe): This file contains functions for negotiating version on the
10 3bf22e58 2015-11-04 stephen.d // client and server. There are some nasty details to get right for
11 3bf22e58 2015-11-04 stephen.d // downgrading the connection on the server-side that are not present yet.
12 3bf22e58 2015-11-04 stephen.d // Really, these should be refactored into some sort of channel type that can
13 3bf22e58 2015-11-04 stephen.d // support resets through version messages during the protocol exchange.
14 3bf22e58 2015-11-04 stephen.d
15 3bf22e58 2015-11-04 stephen.d // clientnegotiate negiotiates the protocol version using channel, blocking
16 3bf22e58 2015-11-04 stephen.d // until a response is received. The received value will be the version
17 3bf22e58 2015-11-04 stephen.d // implemented by the server.
18 3bf22e58 2015-11-04 stephen.d func clientnegotiate(ctx context.Context, ch Channel, version string) (string, error) {
19 3bf22e58 2015-11-04 stephen.d req := newFcall(MessageTversion{
20 3bf22e58 2015-11-04 stephen.d MSize: uint32(ch.MSize()),
21 3bf22e58 2015-11-04 stephen.d Version: version,
22 3bf22e58 2015-11-04 stephen.d })
23 3bf22e58 2015-11-04 stephen.d
24 3bf22e58 2015-11-04 stephen.d if err := ch.WriteFcall(ctx, req); err != nil {
25 3bf22e58 2015-11-04 stephen.d return "", err
26 3bf22e58 2015-11-04 stephen.d }
27 3bf22e58 2015-11-04 stephen.d
28 3bf22e58 2015-11-04 stephen.d resp := new(Fcall)
29 3bf22e58 2015-11-04 stephen.d if err := ch.ReadFcall(ctx, resp); err != nil {
30 3bf22e58 2015-11-04 stephen.d return "", err
31 3bf22e58 2015-11-04 stephen.d }
32 3bf22e58 2015-11-04 stephen.d
33 3bf22e58 2015-11-04 stephen.d switch v := resp.Message.(type) {
34 3bf22e58 2015-11-04 stephen.d case *MessageRversion:
35 3bf22e58 2015-11-04 stephen.d
36 3bf22e58 2015-11-04 stephen.d if v.Version != version {
37 3bf22e58 2015-11-04 stephen.d // TODO(stevvooe): A stubborn client indeed!
38 3bf22e58 2015-11-04 stephen.d return "", fmt.Errorf("unsupported server version: %v", version)
39 3bf22e58 2015-11-04 stephen.d }
40 3bf22e58 2015-11-04 stephen.d
41 3bf22e58 2015-11-04 stephen.d if int(v.MSize) > ch.MSize() {
42 3bf22e58 2015-11-04 stephen.d // upgrade msize if server differs.
43 3bf22e58 2015-11-04 stephen.d ch.SetMSize(int(v.MSize))
44 3bf22e58 2015-11-04 stephen.d }
45 3bf22e58 2015-11-04 stephen.d
46 3bf22e58 2015-11-04 stephen.d return v.Version, nil
47 3bf22e58 2015-11-04 stephen.d case error:
48 3bf22e58 2015-11-04 stephen.d return "", v
49 3bf22e58 2015-11-04 stephen.d default:
50 3bf22e58 2015-11-04 stephen.d return "", fmt.Errorf("invalid rpc response for version message: %v", resp)
51 3bf22e58 2015-11-04 stephen.d }
52 3bf22e58 2015-11-04 stephen.d }
53 3bf22e58 2015-11-04 stephen.d
54 3bf22e58 2015-11-04 stephen.d // servernegotiate blocks until a version message is received or a timeout
55 3bf22e58 2015-11-04 stephen.d // occurs. The msize for the tranport will be set from the negotiation. If
56 3bf22e58 2015-11-04 stephen.d // negotiate returns nil, a server may proceed with the connection.
57 3bf22e58 2015-11-04 stephen.d //
58 3bf22e58 2015-11-04 stephen.d // In the future, it might be better to handle the version messages in a
59 3bf22e58 2015-11-04 stephen.d // separate object that manages the session. Each set of version requests
60 3bf22e58 2015-11-04 stephen.d // effectively "reset" a connection, meaning all fids get clunked and all
61 3bf22e58 2015-11-04 stephen.d // outstanding IO is aborted. This is probably slightly racy, in practice with
62 3bf22e58 2015-11-04 stephen.d // a misbehaved client. The main issue is that we cannot tell which session
63 3bf22e58 2015-11-04 stephen.d // messages belong to.
64 3bf22e58 2015-11-04 stephen.d func servernegotiate(ctx context.Context, ch Channel, version string) error {
65 3bf22e58 2015-11-04 stephen.d // wait for the version message over the transport.
66 3bf22e58 2015-11-04 stephen.d req := new(Fcall)
67 3bf22e58 2015-11-04 stephen.d if err := ch.ReadFcall(ctx, req); err != nil {
68 3bf22e58 2015-11-04 stephen.d return err
69 3bf22e58 2015-11-04 stephen.d }
70 3bf22e58 2015-11-04 stephen.d
71 3bf22e58 2015-11-04 stephen.d mv, ok := req.Message.(*MessageTversion)
72 3bf22e58 2015-11-04 stephen.d if !ok {
73 3bf22e58 2015-11-04 stephen.d return fmt.Errorf("expected version message: %v", mv)
74 3bf22e58 2015-11-04 stephen.d }
75 3bf22e58 2015-11-04 stephen.d
76 3bf22e58 2015-11-04 stephen.d respmsg := MessageRversion{
77 3bf22e58 2015-11-04 stephen.d Version: version,
78 3bf22e58 2015-11-04 stephen.d }
79 3bf22e58 2015-11-04 stephen.d
80 3bf22e58 2015-11-04 stephen.d if mv.Version != version {
81 3bf22e58 2015-11-04 stephen.d // TODO(stevvooe): Not the best place to do version handling. We need
82 3bf22e58 2015-11-04 stephen.d // to have a way to pass supported versions into this method then have
83 3bf22e58 2015-11-04 stephen.d // it return the actual version. For now, respond with unknown for
84 3bf22e58 2015-11-04 stephen.d // anything that doesn't match the provided version string.
85 3bf22e58 2015-11-04 stephen.d respmsg.Version = "unknown"
86 3bf22e58 2015-11-04 stephen.d }
87 3bf22e58 2015-11-04 stephen.d
88 3bf22e58 2015-11-04 stephen.d if int(mv.MSize) < ch.MSize() {
89 3bf22e58 2015-11-04 stephen.d // if the server msize is too large, use the client's suggested msize.
90 3bf22e58 2015-11-04 stephen.d ch.SetMSize(int(mv.MSize))
91 3bf22e58 2015-11-04 stephen.d respmsg.MSize = mv.MSize
92 3bf22e58 2015-11-04 stephen.d } else {
93 3bf22e58 2015-11-04 stephen.d respmsg.MSize = uint32(ch.MSize())
94 3bf22e58 2015-11-04 stephen.d }
95 3bf22e58 2015-11-04 stephen.d
96 3bf22e58 2015-11-04 stephen.d resp := newFcall(respmsg)
97 3bf22e58 2015-11-04 stephen.d if err := ch.WriteFcall(ctx, resp); err != nil {
98 3bf22e58 2015-11-04 stephen.d return err
99 3bf22e58 2015-11-04 stephen.d }
100 3bf22e58 2015-11-04 stephen.d
101 3bf22e58 2015-11-04 stephen.d if respmsg.Version == "unknown" {
102 3bf22e58 2015-11-04 stephen.d return fmt.Errorf("bad version negotiation")
103 3bf22e58 2015-11-04 stephen.d }
104 3bf22e58 2015-11-04 stephen.d
105 3bf22e58 2015-11-04 stephen.d return nil
106 3bf22e58 2015-11-04 stephen.d }