Add CoAP over Websocket interface in cloud
[platform/upstream/iotivity.git] / cloud / stack / src / main / java / org / iotivity / cloud / base / protocols / coap / CoapLogHandler.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.ArrayList;
25
26 import org.iotivity.cloud.base.OICConstants;
27 import org.iotivity.cloud.base.protocols.enums.ContentFormat;
28 import org.iotivity.cloud.util.Cbor;
29 import org.iotivity.cloud.util.Log;
30
31 import com.google.gson.Gson;
32
33 import io.netty.channel.ChannelDuplexHandler;
34 import io.netty.channel.ChannelHandler.Sharable;
35 import io.netty.channel.ChannelHandlerContext;
36 import io.netty.channel.ChannelPromise;
37
38 /**
39  *
40  * This class provides a set of APIs to print out logs for CoAP request and
41  * response.
42  *
43  */
44 @Sharable
45 public class CoapLogHandler extends ChannelDuplexHandler {
46
47     private Cbor<Object> mCbor      = new Cbor<>();
48     private Gson         mGson      = new Gson();
49
50     static final int     MAX_LOGLEN = 100;
51
52     @Override
53     public void channelActive(ChannelHandlerContext ctx) throws Exception {
54         Log.v(ctx.channel().id().asLongText().substring(26)
55                 + " Connected, Address: "
56                 + ctx.channel().remoteAddress().toString());
57
58         ctx.fireChannelActive();
59     }
60
61     @Override
62     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
63         Log.v(ctx.channel().id().asLongText().substring(26)
64                 + " Disconnected, Address: "
65                 + ctx.channel().remoteAddress().toString());
66
67         ctx.fireChannelInactive();
68     }
69
70     @Override
71     public void write(ChannelHandlerContext ctx, Object msg,
72             ChannelPromise promise) {
73
74         String log = null;
75
76         if (msg instanceof CoapRequest) {
77             log = composeCoapRequest(
78                     ctx.channel().id().asLongText().substring(26),
79                     (CoapRequest) msg);
80         } else if (msg instanceof CoapResponse) {
81             log = composeCoapResponse(ctx.channel().id().asLongText()
82                     .substring(26), (CoapResponse) msg);
83         }
84
85         if (log != null) {
86             Log.v(log);
87         }
88
89         ctx.writeAndFlush(msg);
90     }
91
92     @Override
93     public void channelRead(ChannelHandlerContext ctx, Object msg)
94             throws Exception {
95
96         String log = null;
97
98         if (msg instanceof CoapRequest) {
99             log = composeCoapRequest(
100                     ctx.channel().id().asLongText().substring(26),
101                     (CoapRequest) msg);
102         } else if (msg instanceof CoapResponse) {
103             log = composeCoapResponse(ctx.channel().id().asLongText()
104                     .substring(26), (CoapResponse) msg);
105         }
106
107         if (log != null) {
108             Log.v(log);
109         }
110
111         ctx.fireChannelRead(msg);
112     }
113
114     private String composeCoapRequest(String channelId, CoapRequest request) {
115         StringBuilder strBuilder = new StringBuilder();
116
117         strBuilder.append(channelId);
118         strBuilder.append(" " + request.getTokenString());
119
120         switch (request.getMethod()) {
121             case DELETE:
122                 strBuilder.append(" DELETE ");
123                 break;
124             case GET:
125                 switch (request.getObserve()) {
126                     case SUBSCRIBE:
127                         strBuilder.append(" GET OBSERVE ");
128                         break;
129                     case UNSUBSCRIBE:
130                         strBuilder.append(" GET OBSERVE CANCEL ");
131                         break;
132                     default:
133                         strBuilder.append(" GET ");
134                         break;
135                 }
136                 break;
137             case POST:
138                 strBuilder.append(" POST ");
139                 break;
140             case PUT:
141                 strBuilder.append(" PUT ");
142                 break;
143         }
144
145         strBuilder.append(request.getUriPath());
146         String query = request.getUriQuery();
147         if (query != null) {
148             strBuilder.append("/?" + query);
149         }
150
151         if (request.getPayloadSize() > 0) {
152             strBuilder.append(" CT:" + request.getContentFormat());
153             strBuilder.append(" SZ:" + request.getPayloadSize() + " P:"
154                     + getJsonPayloadString(request));
155         }
156
157         return strBuilder.toString();
158     }
159
160     private String composeCoapResponse(String channelId,
161             CoapResponse response) {
162         StringBuilder strBuilder = new StringBuilder();
163
164         strBuilder.append(channelId);
165         strBuilder.append(" " + response.getTokenString());
166
167         switch (response.getStatus()) {
168             case BAD_GATEWAY:
169                 strBuilder.append(" 5.02 Bad Gateway");
170                 break;
171             case BAD_OPTION:
172                 strBuilder.append(" 4.02 Bad Option");
173                 break;
174             case BAD_REQUEST:
175                 strBuilder.append(" 4.00 Bad Request");
176                 break;
177             case CHANGED:
178                 strBuilder.append(" 2.04 Changed");
179                 break;
180             case CONTENT:
181                 strBuilder.append(" 2.05 Content");
182                 break;
183             case CREATED:
184                 strBuilder.append(" 2.01 Created");
185                 break;
186             case DELETED:
187                 strBuilder.append(" 2.02 Deleted");
188                 break;
189             case FORBIDDEN:
190                 strBuilder.append(" 4.03 Forbidden");
191                 break;
192             case GATEWAY_TIMEOUT:
193                 strBuilder.append(" 5.04 Gateway Timeout");
194                 break;
195             case INTERNAL_SERVER_ERROR:
196                 strBuilder.append(" 5.00 Internal Server Error");
197                 break;
198             case METHOD_NOT_ALLOWED:
199                 strBuilder.append(" 4.05 Method Not Allowed");
200                 break;
201             case NOT_ACCEPTABLE:
202                 strBuilder.append(" 4.06 Not Acceptable");
203                 break;
204             case NOT_FOUND:
205                 strBuilder.append(" 4.04 Not Found");
206                 break;
207             case NOT_IMPLEMENTED:
208                 strBuilder.append(" 5.01 Not Implemented");
209                 break;
210             case PRECONDITION_FAILED:
211                 strBuilder.append(" 4.12 Precondition Failed");
212                 break;
213             case PROXY_NOT_SUPPORTED:
214                 strBuilder.append(" 5.05 Proxying Not Supported");
215                 break;
216             case REQUEST_ENTITY_TOO_LARGE:
217                 strBuilder.append(" 4.13 Request Entity Too Large");
218                 break;
219             case SERVICE_UNAVAILABLE:
220                 strBuilder.append(" 5.03 Service Unavailable");
221                 break;
222             case UNAUTHORIZED:
223                 strBuilder.append(" 4.01 Unauthorized");
224                 break;
225             case UNSUPPORTED_CONTENT_FORMAT:
226                 strBuilder.append(" 4.15 Unsupported Content-Format");
227                 break;
228             case VALID:
229                 strBuilder.append(" 2.03 Valid");
230                 break;
231             default:
232                 break;
233         }
234
235         switch (response.getObserve()) {
236             case SUBSCRIBE:
237                 strBuilder.append(" OBSERVE");
238                 break;
239             case UNSUBSCRIBE:
240                 strBuilder.append(" OBSERVE CANCEL");
241                 break;
242             case SEQUENCE_NUMBER:
243                 strBuilder.append(" OBSERVE SEQ:");
244                 strBuilder.append(response.getSequenceNumber());
245                 break;
246             default:
247                 break;
248         }
249
250         if (response.getPayloadSize() > 0) {
251             strBuilder.append(" CT:" + response.getContentFormat());
252             strBuilder.append(" SZ:" + response.getPayloadSize() + " P:"
253                     + getJsonPayloadString(response));
254         }
255
256         return strBuilder.toString();
257     }
258
259     private String getJsonPayloadString(CoapMessage coapMessage) {
260         String jsonPayload = null;
261         if (coapMessage.getContentFormat() == ContentFormat.APPLICATION_CBOR) {
262
263             Object mapPayload = mCbor.parsePayloadFromCbor(
264                     coapMessage.getPayload(), Object.class);
265             if (coapMessage.getUriPath()
266                     .contains(OICConstants.WELL_KNOWN_FULL_URI)) {
267                 jsonPayload = mGson.toJson((ArrayList<Object>) mapPayload);
268                 return jsonPayload.length() <= MAX_LOGLEN ? jsonPayload
269                         : jsonPayload.substring(0, MAX_LOGLEN);
270             }
271             jsonPayload = mGson.toJson(mapPayload);
272             return jsonPayload.length() <= MAX_LOGLEN ? jsonPayload
273                     : jsonPayload.substring(0, MAX_LOGLEN);
274         }
275         return jsonPayload;
276     }
277 }