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.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;
32 import io.netty.buffer.ByteBuf;
33 import io.netty.channel.ChannelHandlerContext;
34 import io.netty.handler.codec.ByteToMessageDecoder;
36 public class CoapDecoder extends ByteToMessageDecoder {
38 private enum ParsingState {
39 SHIM_HEADER, OPTION_PAYLOAD_LENGTH, CODE_TOKEN_OPTION, PAYLOAD, FINISH
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;
51 protected void decode(ChannelHandlerContext ctx, ByteBuf in,
55 // TODO: need exceptional case handling
56 while (in.isReadable(bufferToRead)) {
60 int shimHeader = in.readByte();
61 bufferToRead = (shimHeader >>> 4) & 0x0F;
62 tokenLength = (shimHeader) & 0x0F;
63 switch (bufferToRead) {
66 nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
70 nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
74 nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
77 optionPayloadLength = bufferToRead;
78 bufferToRead += 1 + tokenLength; // code + tkl
79 nextState = ParsingState.CODE_TOKEN_OPTION;
84 case OPTION_PAYLOAD_LENGTH:
85 switch (bufferToRead) {
87 optionPayloadLength = 13
88 + (in.readByte() & 0xFF);
92 optionPayloadLength = 269
93 + (((in.readByte() & 0xFF) << 8)
94 + (in.readByte() & 0xFF));
98 optionPayloadLength = 65805
99 + (((in.readByte() & 0xFF) << 24)
100 + ((in.readByte() & 0xFF) << 16)
101 + ((in.readByte() & 0xFF) << 8)
102 + (in.readByte() & 0xFF));
105 nextState = ParsingState.CODE_TOKEN_OPTION;
106 bufferToRead = 1 + tokenLength + optionPayloadLength; // code
111 case CODE_TOKEN_OPTION:
112 int code = in.readByte() & 0xFF;
114 partialMsg = new CoapRequest(code);
115 } else if (code > 224) {
116 partialMsg = new CoapSignaling(code);
118 partialMsg = new CoapResponse(code);
121 if (tokenLength > 0) {
122 byte[] token = new byte[tokenLength];
124 partialMsg.setToken(token);
127 if (websocketLength != -1) {
128 optionPayloadLength = websocketLength
129 - (bufferToRead + 1); // shimheader + code +
133 if (optionPayloadLength > 0) {
134 int optionLen = parseOptions(partialMsg, in,
135 optionPayloadLength);
136 if (optionPayloadLength > optionLen) {
137 nextState = ParsingState.PAYLOAD;
138 bufferToRead = optionPayloadLength - optionLen;
143 nextState = ParsingState.FINISH;
149 byte[] payload = new byte[bufferToRead];
150 in.readBytes(payload);
151 partialMsg.setPayload(payload);
152 nextState = ParsingState.FINISH;
157 nextState = ParsingState.SHIM_HEADER;
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);
173 MessageBuilder.createResponse(partialMsg, responseStatus));
178 public void decode(ByteBuf in, List<Object> out, int length)
180 websocketLength = length;
181 decode(null, in, out);
184 private int parseOptions(CoapMessage coapMessage, ByteBuf byteBuf,
187 int preOptionNum = 0;
189 int startPos = byteBuf.readerIndex();
191 int firstByte = byteBuf.readByte() & 0xFF;
193 while (firstByte != 0xFF && maxLength > 0) {
194 int optionDelta = (firstByte & 0xF0) >>> 4;
195 int optionLength = firstByte & 0x0F;
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);
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);
211 int curOptionNum = preOptionNum + optionDelta;
212 byte[] optionValue = new byte[optionLength];
213 byteBuf.readBytes(optionValue);
215 coapMessage.addOption(curOptionNum, optionValue);
217 preOptionNum = curOptionNum;
218 if (maxLength > byteBuf.readerIndex() - startPos) {
219 firstByte = byteBuf.readByte() & 0xFF;
225 // return option length
226 return byteBuf.readerIndex() - startPos;