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