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