2 * //******************************************************************
4 * // Copyright 2016 Samsung Electronics All Rights Reserved.
6 * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
12 * // http://www.apache.org/licenses/LICENSE-2.0
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.
20 * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
22 package org.iotivity.cloud.base.protocols.coap;
24 import java.util.List;
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;
31 import io.netty.buffer.ByteBuf;
32 import io.netty.channel.ChannelHandlerContext;
33 import io.netty.handler.codec.ByteToMessageDecoder;
35 public class CoapDecoder extends ByteToMessageDecoder {
37 private enum ParsingState {
38 SHIM_HEADER, OPTION_PAYLOAD_LENGTH, CODE_TOKEN_OPTION, PAYLOAD, FINISH
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;
48 protected void decode(ChannelHandlerContext ctx, ByteBuf in,
52 // TODO: need exceptional case handling
53 while (in.isReadable(bufferToRead)) {
57 int shimHeader = in.readByte();
58 bufferToRead = (shimHeader >>> 4) & 0x0F;
59 tokenLength = (shimHeader) & 0x0F;
60 switch (bufferToRead) {
63 nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
67 nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
71 nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
74 optionPayloadLength = bufferToRead;
75 bufferToRead += 1 + tokenLength; // code + tkl
76 nextState = ParsingState.CODE_TOKEN_OPTION;
81 case OPTION_PAYLOAD_LENGTH:
82 switch (bufferToRead) {
84 optionPayloadLength = 13
85 + (in.readByte() & 0xFF);
89 optionPayloadLength = 269
90 + (((in.readByte() & 0xFF) << 8)
91 + (in.readByte() & 0xFF));
95 optionPayloadLength = 65805
96 + (((in.readByte() & 0xFF) << 24)
97 + ((in.readByte() & 0xFF) << 16)
98 + ((in.readByte() & 0xFF) << 8)
99 + (in.readByte() & 0xFF));
102 nextState = ParsingState.CODE_TOKEN_OPTION;
103 bufferToRead = 1 + tokenLength + optionPayloadLength; // code
108 case CODE_TOKEN_OPTION:
109 int code = in.readByte() & 0xFF;
111 partialMsg = new CoapRequest(code);
112 } else if (code > 224) {
113 partialMsg = new CoapSignaling(code);
115 partialMsg = new CoapResponse(code);
118 if (tokenLength > 0) {
119 byte[] token = new byte[tokenLength];
121 partialMsg.setToken(token);
124 if (optionPayloadLength > 0) {
125 int optionLen = parseOptions(partialMsg, in,
126 optionPayloadLength);
127 if (optionPayloadLength > optionLen) {
128 nextState = ParsingState.PAYLOAD;
129 bufferToRead = optionPayloadLength - optionLen;
134 nextState = ParsingState.FINISH;
140 byte[] payload = new byte[bufferToRead];
141 in.readBytes(payload);
142 partialMsg.setPayload(payload);
143 nextState = ParsingState.FINISH;
148 nextState = ParsingState.SHIM_HEADER;
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);
164 MessageBuilder.createResponse(partialMsg, responseStatus));
169 public void decode(ByteBuf in, List<Object> out) throws Exception {
170 decode(null, in, out);
173 private int parseOptions(CoapMessage coapMessage, ByteBuf byteBuf,
176 int preOptionNum = 0;
178 int startPos = byteBuf.readerIndex();
180 int firstByte = byteBuf.readByte() & 0xFF;
182 while (firstByte != 0xFF && maxLength > 0) {
183 int optionDelta = (firstByte & 0xF0) >>> 4;
184 int optionLength = firstByte & 0x0F;
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);
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);
200 int curOptionNum = preOptionNum + optionDelta;
201 byte[] optionValue = new byte[optionLength];
202 byteBuf.readBytes(optionValue);
204 coapMessage.addOption(curOptionNum, optionValue);
206 preOptionNum = curOptionNum;
207 if (maxLength > byteBuf.readerIndex() - startPos) {
208 firstByte = byteBuf.readByte() & 0xFF;
214 // return option length
215 return byteBuf.readerIndex() - startPos;