1package sessionwire
2
3import (
4 "errors"
5 "time"
6
7 "google.golang.org/protobuf/proto"
8
9 "github.com/bnema/vev/internal/ports"
10 "github.com/bnema/vev/internal/protocol"
11 "github.com/bnema/vev/internal/protocol/wire"
12)
13
14type clientConnection struct {
15 handshake
16}
17
18var _ ports.ClientConnection = (*clientConnection)(nil)
19
20// NewClientConnection wraps one raw client connection incarnation. The
21// preamble runs lazily on first use, bounded by the handshake deadline
22// started here.
23func NewClientConnection(raw wire.Transport) ports.ClientConnection {
24 if raw == nil {
25 return nil
26 }
27 return &clientConnection{handshake: newHandshake(raw, time.Now().Add(protocol.HandshakeTimeout))}
28}
29
30func (c *clientConnection) ensurePreamble() error {
31 return c.handshake.ensurePreamble(runProtoClientPreamble)
32}
33
34func (c *clientConnection) SendClient(message protocol.ClientMessage) error {
35 if err := c.ensurePreamble(); err != nil {
36 return err
37 }
38 envelope, err := encodeProtoClient(message)
39 if err != nil {
40 return err
41 }
42 raw, err := proto.Marshal(envelope)
43 if err != nil {
44 return err
45 }
46 if err := checkCategoryCeiling(raw, clientEnvelopeCategory(raw), c.limits().maxReceiveEnvelopeBytes); err != nil {
47 return err
48 }
49 return c.raw.Send(wire.Envelope{Payload: raw})
50}
51
52func (c *clientConnection) ReceiveServer() (protocol.ServerMessage, error) {
53 if err := c.ensurePreamble(); err != nil {
54 return nil, err
55 }
56 envelope, err := c.raw.Recv()
57 if err != nil {
58 return nil, err
59 }
60 limits := c.limits()
61 message, decodeErr := decodeServerEnvelopeWithin(envelope.Payload, limits.maxReceiveEnvelopeBytes, limits.outputDataLimit)
62 if decodeErr == nil {
63 return message, nil
64 }
65 return nil, decodeErr
66}
67
68func (c *clientConnection) Capabilities() protocol.ConnectionCapabilities {
69 return rawCapabilities(c.raw, c.limits().outputDataLimit)
70}
71
72func (c *clientConnection) LinkState() ports.LinkState { return rawLinkState(c.raw) }
73func (c *clientConnection) LinkEvents() <-chan ports.LinkEvent { return rawLinkEvents(c.raw) }
74func (c *clientConnection) Close() error { return c.raw.Close() }
75
76// DecodeServerEnvelope unwraps one scanned server envelope payload into
77// its semantic message for hidden one-shot carriages.
78func DecodeServerEnvelope(payload []byte) (protocol.ServerMessage, error) {
79 return decodeServerEnvelope(payload)
80}
81
82func decodeServerEnvelope(payload []byte) (protocol.ServerMessage, error) {
83 return decodeServerEnvelopeWithin(payload, wire.AbsoluteEnvelopeLimit, uint64(protocol.MaxOutputDataLen))
84}
85
86// decodeServerEnvelopeWithin unwraps one server envelope under the
87// negotiated receive ceilings: the serialized envelope must fit
88// maxReceiveEnvelopeBytes and any Output must declare uncompressed data at
89// or below outputDataLimit. The one-shot exported decoder uses the absolute
90// ceilings because no preamble was negotiated there.
91func decodeServerEnvelopeWithin(payload []byte, envelopeLimit, outputDataLimit uint64) (protocol.ServerMessage, error) {
92 envelope := &wire.ServerEnvelope{}
93 if err := checkCategoryCeiling(payload, serverEnvelopeCategory(payload), envelopeLimit); err != nil {
94 return nil, classifyEnvelopeError(err, envelope)
95 }
96 if err := wire.ScanEnvelope(envelope, payload); err != nil {
97 return nil, classifyEnvelopeError(err, envelope)
98 }
99 if err := (proto.UnmarshalOptions{DiscardUnknown: false}.Unmarshal(payload, envelope)); err != nil {
100 return nil, &protocol.DecodeFailure{Category: protocol.DecodeMalformed, Err: err}
101 }
102 if err := checkOutputDataCeiling(envelope, outputDataLimit); err != nil {
103 return nil, &protocol.DecodeFailure{Category: protocol.DecodeMalformed, Err: err}
104 }
105 message, err := decodeProtoServer(envelope)
106 if err != nil {
107 return nil, classifyEnvelopeError(err, envelope)
108 }
109 return message, nil
110}
111
112func classifyEnvelopeError(err error, _ *wire.ServerEnvelope) *protocol.DecodeFailure {
113 failure := &protocol.DecodeFailure{Category: protocol.DecodeMalformed, Err: err}
114 if errors.Is(err, wire.ErrScanUnknown) {
115 failure.Category = protocol.DecodeUnknownType
116 } else if errors.Is(err, ErrWrongDirection) {
117 failure.Category = protocol.DecodeWrongDirection
118 }
119 return failure
120}