1package transport
2
3import "io"
4import "encoding/binary"
5import "github.com/couchbase/indexing/secondary/logging"
6
7func Send(conn transporter, buf []byte, flags TransportFlag, payload []byte, addChksm bool) (err error) {
8	// transport framing
9	l := pktLenSize + pktFlagSize
10	if maxLen := len(buf); l > maxLen {
11		logging.Errorf("sending packet length %v > %v\n", l, maxLen)
12		err = ErrorPacketOverflow
13		return
14	}
15
16	a, b := pktLenOffset, pktLenOffset+pktLenSize
17	binary.BigEndian.PutUint32(buf[a:b], uint32(len(payload)))
18
19	if payload != nil && addChksm {
20		chksm := computeChecksum(buf[a:b])
21		flags = flags.SetChecksum(chksm)
22	}
23
24	a, b = pktFlagOffset, pktFlagOffset+pktFlagSize
25	binary.BigEndian.PutUint16(buf[a:b], uint16(flags))
26	if err = connWrite(conn, buf[:pktDataOffset]); err != nil {
27		return err
28	}
29	if err = connWrite(conn, payload); err != nil {
30		return err
31	}
32	laddr, raddr := conn.LocalAddr(), conn.RemoteAddr()
33	logging.Tracef("wrote %v bytes on connection %v->%v", len(payload), laddr, raddr)
34	return nil
35}
36
37func connWrite(conn transporter, buf []byte) error {
38	laddr, raddr := conn.LocalAddr(), conn.RemoteAddr()
39	if n, err := conn.Write(buf); err != nil {
40		logging.Errorf("transport error between %v->%v: %v\n", laddr, raddr, err)
41		return err
42
43	} else if n != len(buf) {
44		err = ErrorPacketWrite
45		logging.Errorf("transport error between %v->%v: %v\n", laddr, raddr, err)
46		return err
47	}
48	return nil
49}
50
51func SendResponseEnd(conn transporter) error {
52	buf := make([]byte, pktLenSize+pktFlagSize)
53	// Special 0 byte payload and flag to indicate end of response
54	return Send(conn, buf, 0, nil, false)
55}
56
57func Receive(conn transporter, buf []byte) (flags TransportFlag, payload []byte, err error) {
58	// transport de-framing
59	bufHeader := safeBufSlice(buf, pktDataOffset)
60	if err = fullRead(conn, bufHeader); err != nil {
61		if err == io.EOF {
62			logging.Tracef("receiving packet: %v\n", err)
63		} else {
64			logging.Errorf("receiving packet: %v\n", err)
65		}
66		return
67	}
68	a, b := pktLenOffset, pktLenOffset+pktLenSize
69	pktlen := binary.BigEndian.Uint32(bufHeader[a:b])
70
71	bufLen := bufHeader[a:b]
72
73	a, b = pktFlagOffset, pktFlagOffset+pktFlagSize
74	flags = TransportFlag(binary.BigEndian.Uint16(bufHeader[a:b]))
75
76	if int(pktlen) != 0 && !flags.IsValidEncoding() {
77		logging.Errorf("transport - unknown encoding scheme %#v flags %#v", flags.GetEncoding(), flags)
78		err = ErrorDecoderUnknown
79		return
80	}
81
82	pktChksm := flags.GetChecksum()
83
84	if uint8(pktChksm) != 0 {
85		chksm := computeChecksum(bufLen)
86		if chksm != pktChksm {
87			logging.Errorf("checksum mismatch: expected %v got %v", pktChksm, chksm)
88			logging.Errorf("packet header %#v, flags %#v", bufLen, bufHeader[a:b])
89			err = ErrorChecksumMismatch
90			return
91		}
92	}
93
94	bufPkt := safeBufSlice(buf, int(pktlen))
95	if err = fullRead(conn, bufPkt); err != nil {
96		if err == io.EOF {
97			logging.Tracef("receiving packet: %v\n", err)
98		} else {
99			logging.Errorf("receiving packet: %v\n", err)
100		}
101		return
102	}
103
104	return flags, bufPkt, err
105}
106
107func safeBufSlice(b []byte, l int) []byte {
108	if cap(b) >= l {
109		return b[:l]
110	}
111
112	return make([]byte, l)
113}
114
115//checksum is 7bits
116func computeChecksum(pktLen []byte) byte {
117
118	var checksum byte
119
120	//use last 2 bytes from pktLen to
121	//compute checksum
122	checksum |= pktLen[3] & 0x0F
123	checksum <<= 4
124	checksum |= pktLen[2] & 0x0F
125	checksum &= 0x7F
126
127	return checksum
128}
129