654ce1ac8c4ed6f06150c52ba1e0863ac0c37841
[platform/upstream/iotivity.git] / cloud / stack / src / main / java / org / iotivity / cloud / base / protocols / coap / CoapEncoder.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.buffer.Unpooled;
28 import io.netty.channel.ChannelHandlerContext;
29 import io.netty.handler.codec.MessageToByteEncoder;
30
31 public class CoapEncoder extends MessageToByteEncoder<CoapMessage> {
32
33     @Override
34     protected void encode(ChannelHandlerContext ctx, CoapMessage msg,
35             ByteBuf out) throws Exception {
36
37         CoapMessage coapMessage = (CoapMessage) msg;
38
39         ByteBuf optBuf = Unpooled.directBuffer();
40
41         /**
42          * encode options
43          */
44         encodeOptions(optBuf, coapMessage);
45         long length = optBuf.readableBytes();
46
47         if (coapMessage.getPayloadSize() > 0) {
48             // + 1 means 8bits delimiter
49             length += 1 + coapMessage.getPayloadSize();
50         }
51
52         calcShimHeader(coapMessage, out, length);
53
54         out.writeByte(coapMessage.getCode());
55
56         if (coapMessage.getTokenLength() > 0) {
57             out.writeBytes(coapMessage.getToken());
58         }
59
60         // Write option
61         if (optBuf.readableBytes() > 0) {
62             out.writeBytes(optBuf);
63         }
64
65         optBuf.release();
66
67         if (coapMessage.getPayloadSize() > 0) {
68             out.writeByte(255);
69             out.writeBytes(coapMessage.getPayload());
70         }
71     }
72
73     private void calcShimHeader(CoapMessage coapMessage, ByteBuf byteBuf,
74             long length) {
75         if (length < 13) {
76             byteBuf.writeByte(((int) length & 0x0F) << 4
77                     | (coapMessage.getTokenLength() & 0x0F));
78         } else if (length < 269) {
79             byteBuf.writeShort((13 & 0x0F) << 12
80                     | (coapMessage.getTokenLength() & 0x0F) << 8
81                     | (((int) length - 13) & 0xFF));
82         } else if (length < 65805) {
83             byteBuf.writeByte(
84                     (14 & 0x0F) << 4 | (coapMessage.getTokenLength() & 0x0F));
85             byteBuf.writeShort(((int) length - 269) & 0xFFFF);
86         } else if (length < Integer.MAX_VALUE * 2) {
87             byteBuf.writeByte(
88                     (15 & 0x0F) << 4 | (coapMessage.getTokenLength() & 0x0F));
89             byteBuf.writeLong((length - 65805) & 0xFFFFFFFF);
90         } else {
91             throw new IllegalArgumentException(
92                     "Length must be less than 4GB " + length);
93         }
94     }
95
96     private void encodeOptions(ByteBuf byteBuf, CoapMessage coapMessage)
97             throws Exception {
98         int preOptionNum = 0;
99
100         for (int i = 0; i < 40; i++) {
101             List<byte[]> values = coapMessage.getOption(i);
102             if (values != null) {
103                 if (values.size() > 0) {
104                     for (byte[] value : values) {
105                         writeOption(i - preOptionNum,
106                                 value != null ? value.length : 0, byteBuf,
107                                 value);
108                         preOptionNum = i;
109                     }
110
111                 } else {
112                     writeOption(i - preOptionNum, 0, byteBuf, null);
113                     preOptionNum = i;
114                 }
115             }
116         }
117     }
118
119     private void writeOption(int optionDelta, int optionLength, ByteBuf byteBuf,
120             byte[] value) {
121
122         if (optionDelta < 13) {
123             if (optionLength < 13) {
124                 byteBuf.writeByte(
125                         ((optionDelta & 0xFF) << 4) | (optionLength & 0xFF));
126             } else if (optionLength < 269) {
127                 byteBuf.writeByte(((optionDelta << 4) & 0xFF) | (13 & 0xFF));
128                 byteBuf.writeByte((optionLength - 13) & 0xFF);
129             } else {
130                 byteBuf.writeByte(((optionDelta << 4) & 0xFF) | (14 & 0xFF));
131                 byteBuf.writeByte(((optionLength - 269) & 0xFF00) >>> 8);
132                 byteBuf.writeByte((optionLength - 269) & 0xFF);
133             }
134         }
135
136         else if (optionDelta < 269) {
137             if (optionLength < 13) {
138                 byteBuf.writeByte(((13 & 0xFF) << 4) | (optionLength & 0xFF));
139                 byteBuf.writeByte((optionDelta - 13) & 0xFF);
140             } else if (optionLength < 269) {
141                 byteBuf.writeByte(((13 & 0xFF) << 4) | (13 & 0xFF));
142                 byteBuf.writeByte((optionDelta - 13) & 0xFF);
143                 byteBuf.writeByte((optionLength - 13) & 0xFF);
144             } else {
145                 byteBuf.writeByte((13 & 0xFF) << 4 | (14 & 0xFF));
146                 byteBuf.writeByte((optionDelta - 13) & 0xFF);
147                 byteBuf.writeByte(((optionLength - 269) & 0xFF00) >>> 8);
148                 byteBuf.writeByte((optionLength - 269) & 0xFF);
149             }
150         }
151
152         else if (optionDelta < 65805) {
153
154             if (optionLength < 13) {
155                 byteBuf.writeByte(((14 & 0xFF) << 4) | (optionLength & 0xFF));
156                 byteBuf.writeByte(((optionDelta - 269) & 0xFF00) >>> 8);
157                 byteBuf.writeByte((optionDelta - 269) & 0xFF);
158             }
159
160             else if (optionLength < 269) {
161                 byteBuf.writeByte(((14 & 0xFF) << 4) | (13 & 0xFF));
162                 byteBuf.writeByte(((optionDelta - 269) & 0xFF00) >>> 8);
163                 byteBuf.writeByte((optionDelta - 269) & 0xFF);
164                 byteBuf.writeByte((optionLength - 13) & 0xFF);
165             }
166
167             else {
168                 byteBuf.writeByte(((14 & 0xFF) << 4) | (14 & 0xFF));
169                 byteBuf.writeByte(((optionDelta - 269) & 0xFF00) >>> 8);
170                 byteBuf.writeByte((optionDelta - 269) & 0xFF);
171                 byteBuf.writeByte(((optionLength - 269) & 0xFF00) >>> 8);
172                 byteBuf.writeByte((optionLength - 269) & 0xFF);
173             }
174         } else {
175             throw new IllegalArgumentException(
176                     "Unsupported option delta " + optionDelta);
177         }
178
179         if (optionLength > 0) {
180             byteBuf.writeBytes(value);
181         }
182     }
183 }