cf384ea6d98e4c455d7887a73bfbe2a037f303ac
[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 io.netty.buffer.ByteBuf;
27 import io.netty.channel.ChannelHandlerContext;
28 import io.netty.handler.codec.ByteToMessageDecoder;
29
30 public class CoapDecoder extends ByteToMessageDecoder {
31
32     private enum ParsingState {
33         SHIM_HEADER, OPTION_PAYLOAD_LENGTH, CODE_TOKEN_OPTION, PAYLOAD, FINISH
34     }
35
36     private ParsingState nextState           = ParsingState.SHIM_HEADER;
37     private int          bufferToRead        = 1;
38     private int          tokenLength         = 0;
39     private int          optionPayloadLength = 0;
40     private CoapMessage  partialMsg          = null;
41
42     @Override
43     protected void decode(ChannelHandlerContext ctx, ByteBuf in,
44             List<Object> out) throws Exception {
45
46         // TODO: need exceptional case handling
47         while (in.isReadable(bufferToRead)) {
48
49             switch (nextState) {
50                 case SHIM_HEADER:
51                     int shimHeader = in.readByte();
52                     bufferToRead = (shimHeader >>> 4) & 0x0F;
53                     tokenLength = (shimHeader) & 0x0F;
54                     switch (bufferToRead) {
55                         case 13:
56                             bufferToRead = 1;
57                             nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
58                             break;
59                         case 14:
60                             bufferToRead = 2;
61                             nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
62                             break;
63                         case 15:
64                             bufferToRead = 4;
65                             nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
66                             break;
67                         default:
68                             optionPayloadLength = bufferToRead;
69                             bufferToRead += 1 + tokenLength; // code + tkl
70                             nextState = ParsingState.CODE_TOKEN_OPTION;
71                             break;
72                     }
73                     break;
74
75                 case OPTION_PAYLOAD_LENGTH:
76                     switch (bufferToRead) {
77                         case 1:
78                             optionPayloadLength = 13 + (in.readByte() & 0xFF);
79                             break;
80
81                         case 2:
82                             optionPayloadLength = 269
83                                     + (((in.readByte() & 0xFF) << 8)
84                                             + (in.readByte() & 0xFF));
85                             break;
86
87                         case 4:
88                             optionPayloadLength = 65805
89                                     + (((in.readByte() & 0xFF) << 24)
90                                             + ((in.readByte() & 0xFF) << 16)
91                                             + ((in.readByte() & 0xFF) << 8)
92                                             + (in.readByte() & 0xFF));
93                             break;
94                     }
95                     nextState = ParsingState.CODE_TOKEN_OPTION;
96                     bufferToRead = 1 + tokenLength + optionPayloadLength; // code
97                                                                           // +
98                                                                           // tkl
99                     break;
100
101                 case CODE_TOKEN_OPTION:
102                     int code = in.readByte() & 0xFF;
103
104                     if (code <= 31) {
105                         partialMsg = new CoapRequest(code);
106                     } else {
107                         partialMsg = new CoapResponse(code);
108                     }
109
110                     if (tokenLength > 0) {
111                         byte[] token = new byte[tokenLength];
112                         in.readBytes(token);
113                         partialMsg.setToken(token);
114                     }
115
116                     if (optionPayloadLength > 0) {
117                         int optionLen = parseOptions(partialMsg, in,
118                                 optionPayloadLength);
119                         if (optionPayloadLength > optionLen) {
120                             nextState = ParsingState.PAYLOAD;
121                             bufferToRead = optionPayloadLength - optionLen;
122                             continue;
123                         }
124                     }
125
126                     nextState = ParsingState.FINISH;
127                     bufferToRead = 0;
128
129                     break;
130
131                 case PAYLOAD:
132                     byte[] payload = new byte[bufferToRead];
133                     in.readBytes(payload);
134                     partialMsg.setPayload(payload);
135                     nextState = ParsingState.FINISH;
136                     bufferToRead = 0;
137                     break;
138
139                 case FINISH:
140                     nextState = ParsingState.SHIM_HEADER;
141                     bufferToRead = 1;
142                     out.add(partialMsg);
143                     break;
144
145                 default:
146                     break;
147             }
148         }
149
150         in.discardReadBytes();
151     }
152
153     private int parseOptions(CoapMessage coapMessage, ByteBuf byteBuf,
154             int maxLength) {
155
156         int preOptionNum = 0;
157
158         int startPos = byteBuf.readerIndex();
159
160         int firstByte = byteBuf.readByte() & 0xFF;
161
162         while (firstByte != 0xFF && maxLength > 0) {
163             int optionDelta = (firstByte & 0xF0) >>> 4;
164             int optionLength = firstByte & 0x0F;
165
166             if (optionDelta == 13) {
167                 optionDelta = 13 + byteBuf.readByte() & 0xFF;
168             } else if (optionDelta == 14) {
169                 optionDelta = 269 + ((byteBuf.readByte() & 0xFF) << 8)
170                         + (byteBuf.readByte() & 0xFF);
171             }
172
173             if (optionLength == 13) {
174                 optionLength = 13 + byteBuf.readByte() & 0xFF;
175             } else if (optionLength == 14) {
176                 optionLength = 269 + ((byteBuf.readByte() & 0xFF) << 8)
177                         + (byteBuf.readByte() & 0xFF);
178             }
179
180             int curOptionNum = preOptionNum + optionDelta;
181             byte[] optionValue = new byte[optionLength];
182             byteBuf.readBytes(optionValue);
183
184             coapMessage.addOption(curOptionNum, optionValue);
185
186             preOptionNum = curOptionNum;
187             if (maxLength > byteBuf.readerIndex() - startPos) {
188                 firstByte = byteBuf.readByte() & 0xFF;
189             } else {
190                 break;
191             }
192         }
193
194         /// return option length
195         return byteBuf.readerIndex() - startPos;
196     }
197 }