Imported Upstream version 1.1.0
[platform/upstream/iotivity.git] / cloud / stack / src / main / java / org / iotivity / cloud / base / protocols / coap / CoapDecoder.java
1 /*
2  * //******************************************************************
3  * //
4  * // Copyright 2016 Samsung Electronics All Rights Reserved.
5  * //
6  * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7  * //
8  * // Licensed under the Apache License, Version 2.0 (the "License");
9  * // you may not use this file except in compliance with the License.
10  * // You may obtain a copy of the License at
11  * //
12  * //      http://www.apache.org/licenses/LICENSE-2.0
13  * //
14  * // Unless required by applicable law or agreed to in writing, software
15  * // distributed under the License is distributed on an "AS IS" BASIS,
16  * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * // See the License for the specific language governing permissions and
18  * // limitations under the License.
19  * //
20  * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21  */
22 package org.iotivity.cloud.base.protocols.coap;
23
24 import java.util.List;
25
26 import org.iotivity.cloud.base.protocols.coap.enums.CoapMethod;
27 import org.iotivity.cloud.base.protocols.coap.enums.CoapStatus;
28
29 import io.netty.buffer.ByteBuf;
30 import io.netty.channel.ChannelHandlerContext;
31 import io.netty.handler.codec.ByteToMessageDecoder;
32
33 public class CoapDecoder extends ByteToMessageDecoder {
34
35     private enum ParsingState {
36         SHIM_HEADER, OPTION_PAYLOAD_LENGTH, CODE_TOKEN_OPTION, PAYLOAD, FINISH
37     }
38
39     private ParsingState nextState           = ParsingState.SHIM_HEADER;
40     private int          bufferToRead        = 1;
41     private int          tokenLength         = 0;
42     private int          optionPayloadLength = 0;
43     private CoapMessage  partialMsg          = null;
44
45     @Override
46     protected void decode(ChannelHandlerContext ctx, ByteBuf in,
47             List<Object> out) throws Exception {
48
49         while (in.isReadable(bufferToRead)) {
50
51             switch (nextState) {
52                 case SHIM_HEADER:
53                     int shimHeader = in.readByte();
54                     bufferToRead = (shimHeader >>> 4) & 0x0F;
55                     tokenLength = (shimHeader) & 0x0F;
56                     switch (bufferToRead) {
57                         case 13:
58                             bufferToRead = 1;
59                             nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
60                             break;
61                         case 14:
62                             bufferToRead = 2;
63                             nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
64                             break;
65                         case 15:
66                             bufferToRead = 4;
67                             nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
68                             break;
69                         default:
70                             optionPayloadLength = bufferToRead;
71                             bufferToRead += 1 + tokenLength; // code + tkl
72                             nextState = ParsingState.CODE_TOKEN_OPTION;
73                             break;
74                     }
75                     break;
76
77                 case OPTION_PAYLOAD_LENGTH:
78                     switch (bufferToRead) {
79                         case 1:
80                             optionPayloadLength = 13 + (in.readByte() & 0xFF);
81                             break;
82
83                         case 2:
84                             optionPayloadLength = 269
85                                     + (((in.readByte() & 0xFF) << 8)
86                                             + (in.readByte() & 0xFF));
87                             break;
88
89                         case 4:
90                             optionPayloadLength = 65805
91                                     + (((in.readByte() & 0xFF) << 24)
92                                             + ((in.readByte() & 0xFF) << 16)
93                                             + ((in.readByte() & 0xFF) << 8)
94                                             + (in.readByte() & 0xFF));
95                             break;
96                     }
97                     nextState = ParsingState.CODE_TOKEN_OPTION;
98                     bufferToRead = 1 + tokenLength + optionPayloadLength; // code
99                                                                           // +
100                                                                           // tkl
101                     break;
102
103                 case CODE_TOKEN_OPTION:
104                     int code = in.readByte() & 0xFF;
105
106                     if (code <= 31) {
107                         partialMsg = new CoapRequest(CoapMethod.valueOf(code));
108                     } else {
109                         partialMsg = new CoapResponse(CoapStatus.valueOf(code));
110                     }
111
112                     if (tokenLength > 0) {
113                         byte[] token = new byte[tokenLength];
114                         in.readBytes(token);
115                         partialMsg.setToken(token);
116                     }
117
118                     if (optionPayloadLength > 0) {
119                         int optionLen = parseOptions(partialMsg, in,
120                                 optionPayloadLength);
121                         if (optionPayloadLength > optionLen) {
122                             nextState = ParsingState.PAYLOAD;
123                             bufferToRead = optionPayloadLength - optionLen;
124                             continue;
125                         }
126                     }
127
128                     nextState = ParsingState.FINISH;
129                     bufferToRead = 0;
130
131                     break;
132
133                 case PAYLOAD:
134                     byte[] payload = new byte[bufferToRead];
135                     in.readBytes(payload);
136                     partialMsg.setPayload(payload);
137                     nextState = ParsingState.FINISH;
138                     bufferToRead = 0;
139                     break;
140
141                 case FINISH:
142                     nextState = ParsingState.SHIM_HEADER;
143                     bufferToRead = 1;
144                     out.add(partialMsg);
145                     break;
146
147                 default:
148                     break;
149             }
150         }
151
152         in.discardReadBytes();
153     }
154
155     private int parseOptions(CoapMessage coapMessage, ByteBuf byteBuf,
156             int maxLength) {
157
158         int preOptionNum = 0;
159
160         int startPos = byteBuf.readerIndex();
161
162         int firstByte = byteBuf.readByte() & 0xFF;
163
164         while (firstByte != 0xFF && maxLength > 0) {
165             int optionDelta = (firstByte & 0xF0) >>> 4;
166             int optionLength = firstByte & 0x0F;
167
168             if (optionDelta == 13) {
169                 optionDelta = 13 + byteBuf.readByte() & 0xFF;
170             } else if (optionDelta == 14) {
171                 optionDelta = 269 + ((byteBuf.readByte() & 0xFF) << 8)
172                         + (byteBuf.readByte() & 0xFF);
173             }
174
175             if (optionLength == 13) {
176                 optionLength = 13 + byteBuf.readByte() & 0xFF;
177             } else if (optionLength == 14) {
178                 optionLength = 269 + ((byteBuf.readByte() & 0xFF) << 8)
179                         + (byteBuf.readByte() & 0xFF);
180             }
181
182             int curOptionNum = preOptionNum + optionDelta;
183             byte[] optionValue = new byte[optionLength];
184             byteBuf.readBytes(optionValue);
185
186             coapMessage.addOption(curOptionNum, optionValue);
187
188             preOptionNum = curOptionNum;
189             if (maxLength > byteBuf.readerIndex() - startPos) {
190                 firstByte = byteBuf.readByte() & 0xFF;
191             } else {
192                 break;
193             }
194         }
195
196         /// return option length
197         return byteBuf.readerIndex() - startPos;
198     }
199 }