Add CoAP over Websocket interface in cloud
[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     public void encode(CoapMessage msg, ByteBuf out) throws Exception {
74         encode(null, msg, out);
75     }
76
77     private void calcShimHeader(CoapMessage coapMessage, ByteBuf byteBuf,
78             long length) {
79         if (length < 13) {
80             byteBuf.writeByte(((int) length & 0x0F) << 4
81                     | (coapMessage.getTokenLength() & 0x0F));
82         } else if (length < 269) {
83             byteBuf.writeShort((13 & 0x0F) << 12
84                     | (coapMessage.getTokenLength() & 0x0F) << 8
85                     | (((int) length - 13) & 0xFF));
86         } else if (length < 65805) {
87             byteBuf.writeByte(
88                     (14 & 0x0F) << 4 | (coapMessage.getTokenLength() & 0x0F));
89             byteBuf.writeShort(((int) length - 269) & 0xFFFF);
90         } else if (length < 4294967294L) {
91             byteBuf.writeByte(
92                     (15 & 0x0F) << 4 | (coapMessage.getTokenLength() & 0x0F));
93             byte[] size = new byte[4];
94             long payload = length - 65805;
95             for (int i = 3; i > -1; i--) {
96                 size[i] = (byte) (payload & 0xFF);
97                 payload >>= 8;
98             }
99             byteBuf.writeBytes(size);
100         } else {
101             throw new IllegalArgumentException(
102                     "Length must be less than 4GB " + length);
103         }
104     }
105
106     private void encodeOptions(ByteBuf byteBuf, CoapMessage coapMessage)
107             throws Exception {
108         int preOptionNum = 0;
109
110         for (int i = 0; i < 40; i++) {
111             List<byte[]> values = coapMessage.getOption(i);
112             if (values != null) {
113                 if (values.size() > 0) {
114                     for (byte[] value : values) {
115                         writeOption(i - preOptionNum,
116                                 value != null ? value.length : 0, byteBuf,
117                                 value);
118                         preOptionNum = i;
119                     }
120
121                 } else {
122                     writeOption(i - preOptionNum, 0, byteBuf, null);
123                     preOptionNum = i;
124                 }
125             }
126         }
127     }
128
129     private void writeOption(int optionDelta, int optionLength, ByteBuf byteBuf,
130             byte[] value) {
131
132         if (optionDelta < 13) {
133             if (optionLength < 13) {
134                 byteBuf.writeByte(
135                         ((optionDelta & 0xFF) << 4) | (optionLength & 0xFF));
136             } else if (optionLength < 269) {
137                 byteBuf.writeByte(((optionDelta << 4) & 0xFF) | (13 & 0xFF));
138                 byteBuf.writeByte((optionLength - 13) & 0xFF);
139             } else {
140                 byteBuf.writeByte(((optionDelta << 4) & 0xFF) | (14 & 0xFF));
141                 byteBuf.writeByte(((optionLength - 269) & 0xFF00) >>> 8);
142                 byteBuf.writeByte((optionLength - 269) & 0xFF);
143             }
144         }
145
146         else if (optionDelta < 269) {
147             if (optionLength < 13) {
148                 byteBuf.writeByte(((13 & 0xFF) << 4) | (optionLength & 0xFF));
149                 byteBuf.writeByte((optionDelta - 13) & 0xFF);
150             } else if (optionLength < 269) {
151                 byteBuf.writeByte(((13 & 0xFF) << 4) | (13 & 0xFF));
152                 byteBuf.writeByte((optionDelta - 13) & 0xFF);
153                 byteBuf.writeByte((optionLength - 13) & 0xFF);
154             } else {
155                 byteBuf.writeByte((13 & 0xFF) << 4 | (14 & 0xFF));
156                 byteBuf.writeByte((optionDelta - 13) & 0xFF);
157                 byteBuf.writeByte(((optionLength - 269) & 0xFF00) >>> 8);
158                 byteBuf.writeByte((optionLength - 269) & 0xFF);
159             }
160         }
161
162         else if (optionDelta < 65805) {
163
164             if (optionLength < 13) {
165                 byteBuf.writeByte(((14 & 0xFF) << 4) | (optionLength & 0xFF));
166                 byteBuf.writeByte(((optionDelta - 269) & 0xFF00) >>> 8);
167                 byteBuf.writeByte((optionDelta - 269) & 0xFF);
168             }
169
170             else if (optionLength < 269) {
171                 byteBuf.writeByte(((14 & 0xFF) << 4) | (13 & 0xFF));
172                 byteBuf.writeByte(((optionDelta - 269) & 0xFF00) >>> 8);
173                 byteBuf.writeByte((optionDelta - 269) & 0xFF);
174                 byteBuf.writeByte((optionLength - 13) & 0xFF);
175             }
176
177             else {
178                 byteBuf.writeByte(((14 & 0xFF) << 4) | (14 & 0xFF));
179                 byteBuf.writeByte(((optionDelta - 269) & 0xFF00) >>> 8);
180                 byteBuf.writeByte((optionDelta - 269) & 0xFF);
181                 byteBuf.writeByte(((optionLength - 269) & 0xFF00) >>> 8);
182                 byteBuf.writeByte((optionLength - 269) & 0xFF);
183             }
184         } else {
185             throw new IllegalArgumentException(
186                     "Unsupported option delta " + optionDelta);
187         }
188
189         if (optionLength > 0) {
190             byteBuf.writeBytes(value);
191         }
192     }
193 }