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