mock adapter: Group all replies for one request under one call to MockReply
[govpp.git] / core / msg_codec.go
1 // Copyright (c) 2017 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package core
16
17 import (
18         "bytes"
19         "errors"
20         "fmt"
21         "reflect"
22
23         "github.com/lunixbochs/struc"
24         logger "github.com/sirupsen/logrus"
25
26         "git.fd.io/govpp.git/api"
27 )
28
29 // MsgCodec provides encoding and decoding functionality of `api.Message` structs into/from
30 // binary format as accepted by VPP.
31 type MsgCodec struct{}
32
33 // VppRequestHeader struct contains header fields implemented by all VPP requests.
34 type VppRequestHeader struct {
35         VlMsgID     uint16
36         ClientIndex uint32
37         Context     uint32
38 }
39
40 // VppReplyHeader struct contains header fields implemented by all VPP replies.
41 type VppReplyHeader struct {
42         VlMsgID uint16
43         Context uint32
44 }
45
46 // VppEventHeader struct contains header fields implemented by all VPP events.
47 type VppEventHeader struct {
48         VlMsgID uint16
49         Context uint32
50 }
51
52 // VppOtherHeader struct contains header fields implemented by other VPP messages (not requests nor replies).
53 type VppOtherHeader struct {
54         VlMsgID uint16
55 }
56
57 const (
58         vppRequestHeaderSize = 10 // size of a VPP request header
59         vppReplyHeaderSize   = 6  // size of a VPP reply header
60         vppEventHeaderSize   = 6  // size of a VPP event header
61         vppOtherHeaderSize   = 2  // size of the header of other VPP messages
62 )
63
64 // EncodeMsg encodes provided `Message` structure into its binary-encoded data representation.
65 func (*MsgCodec) EncodeMsg(msg api.Message, msgID uint16) ([]byte, error) {
66         if msg == nil {
67                 return nil, errors.New("nil message passed in")
68         }
69
70         buf := new(bytes.Buffer)
71
72         // encode message header
73         var header interface{}
74         if msg.GetMessageType() == api.RequestMessage {
75                 header = &VppRequestHeader{VlMsgID: msgID}
76         } else if msg.GetMessageType() == api.ReplyMessage {
77                 header = &VppReplyHeader{VlMsgID: msgID}
78         } else if msg.GetMessageType() == api.EventMessage {
79                 header = &VppEventHeader{VlMsgID: msgID}
80         } else {
81                 header = &VppOtherHeader{VlMsgID: msgID}
82         }
83         err := struc.Pack(buf, header)
84         if err != nil {
85                 log.WithFields(logger.Fields{
86                         "error":  err,
87                         "header": header,
88                 }).Error("Unable to encode the message header: ", err)
89                 return nil, fmt.Errorf("unable to encode the message header: %v", err)
90         }
91
92         // encode message content
93         if reflect.Indirect(reflect.ValueOf(msg)).NumField() > 0 {
94                 err := struc.Pack(buf, msg)
95                 if err != nil {
96                         log.WithFields(logger.Fields{
97                                 "error":   err,
98                                 "message": msg,
99                         }).Error("Unable to encode the message: ", err)
100                         return nil, fmt.Errorf("unable to encode the message: %v", err)
101                 }
102         }
103
104         return buf.Bytes(), nil
105 }
106
107 // DecodeMsg decodes binary-encoded data of a message into provided `Message` structure.
108 func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) error {
109         if msg == nil {
110                 return errors.New("nil message passed in")
111         }
112
113         buf := bytes.NewReader(data)
114
115         // check which header is expected
116         var header interface{}
117         if msg.GetMessageType() == api.RequestMessage {
118                 header = &VppRequestHeader{}
119         } else if msg.GetMessageType() == api.ReplyMessage {
120                 header = &VppReplyHeader{}
121         } else if msg.GetMessageType() == api.EventMessage {
122                 header = &VppEventHeader{}
123         } else {
124                 header = &VppOtherHeader{}
125         }
126
127         // decode message header
128         err := struc.Unpack(buf, header)
129         if err != nil {
130                 log.WithFields(logger.Fields{
131                         "error": err,
132                         "data":  data,
133                 }).Error("Unable to decode header of the message.")
134                 return fmt.Errorf("unable to decode the message header: %v", err)
135         }
136
137         // get rid of the message header
138         if msg.GetMessageType() == api.RequestMessage {
139                 buf = bytes.NewReader(data[vppRequestHeaderSize:])
140         } else if msg.GetMessageType() == api.ReplyMessage {
141                 buf = bytes.NewReader(data[vppReplyHeaderSize:])
142         } else if msg.GetMessageType() == api.EventMessage {
143                 buf = bytes.NewReader(data[vppEventHeaderSize:])
144         } else {
145                 buf = bytes.NewReader(data[vppOtherHeaderSize:])
146         }
147
148         // decode message content
149         err = struc.Unpack(buf, msg)
150         if err != nil {
151                 log.WithFields(logger.Fields{
152                         "error": err,
153                         "data":  buf,
154                 }).Error("Unable to decode the message.")
155                 return fmt.Errorf("unable to decode the message: %v", err)
156         }
157
158         return nil
159 }