be472e353053f9f527ef0594bcaf659130290bb3
[platform/upstream/iotivity.git] / cloud / interface / src / main / java / org / iotivity / cloud / ciserver / protocols / CoapRelayHandler.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.ciserver.protocols;
23
24 import java.net.InetSocketAddress;
25 import java.nio.charset.Charset;
26 import java.nio.charset.StandardCharsets;
27 import java.util.ArrayList;
28 import java.util.List;
29
30 import org.iotivity.cloud.base.CoapClient;
31 import org.iotivity.cloud.base.SessionManager;
32 import org.iotivity.cloud.base.protocols.coap.CoapRequest;
33 import org.iotivity.cloud.base.protocols.coap.CoapResponse;
34 import org.iotivity.cloud.base.protocols.coap.enums.CoapMethod;
35 import org.iotivity.cloud.base.protocols.coap.enums.CoapStatus;
36 import org.iotivity.cloud.ciserver.Constants;
37 import org.iotivity.cloud.util.Logger;
38
39 import io.netty.channel.ChannelDuplexHandler;
40 import io.netty.channel.ChannelHandler.Sharable;
41 import io.netty.channel.ChannelHandlerContext;
42 import io.netty.channel.SimpleChannelInboundHandler;
43 import io.netty.util.AttributeKey;
44
45 /**
46  *
47  * This class is relay handler relating Cloud Interface
48  *
49  */
50 @Sharable
51 public class CoapRelayHandler extends ChannelDuplexHandler {
52
53     /////////// Handler for Resource Directory
54     private static final AttributeKey<ChannelHandlerContext> keyRDClient = AttributeKey
55             .newInstance("rdCtx");
56
57     private static class RDHandler
58             extends SimpleChannelInboundHandler<CoapResponse> {
59         @Override
60         public void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
61                 throws Exception {
62             Logger.d("Receive response from RD, forward to client");
63
64             ChannelHandlerContext ctxToDevice = ctx.channel().attr(keyRDClient)
65                     .get();
66             ctxToDevice.writeAndFlush(msg);
67         }
68
69         @Override
70         public void exceptionCaught(ChannelHandlerContext ctx,
71                 Throwable cause) {
72             cause.printStackTrace();
73             ctx.close();
74         }
75     }
76
77     private CoapClient                                   rdClient         = null;
78                                                                           ///////////
79
80     ////////// Handler for Account Server
81     private static final AttributeKey<List<CoapRequest>> keyAccountClient = AttributeKey
82             .newInstance("accountCtx");
83
84     private class AccountHandler
85             extends SimpleChannelInboundHandler<CoapResponse> {
86         @Override
87         public void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
88                 throws Exception {
89             Logger.d("Receive response from AS, make request to RD");
90
91             CoapRequest rdRequest = null;
92
93             switch (msg.getResponseCode()) {
94                 case CREATED:
95                     // response of POST request
96                     rdRequest = ctx.channel().attr(keyAccountClient).get()
97                             .remove(0);
98                     rdClient.sendRequest(rdRequest);
99                     break;
100
101                 case CONTENT:
102                     // response of GET request, contains did list
103                     rdRequest = ctx.channel().attr(keyAccountClient).get()
104                             .remove(0);
105                     // change uri to send RD
106                     rdRequest.setUriPath(Constants.RD_URI);
107                     rdRequest.setUriQuery(Constants.DEVICE_LIST);
108                     rdRequest.setPayload(msg.getPayload());
109                     rdClient.sendRequest(rdRequest);
110                     break;
111
112                 default:
113                     break;
114             }
115         }
116
117         @Override
118         public void exceptionCaught(ChannelHandlerContext ctx,
119                 Throwable cause) {
120             cause.printStackTrace();
121             ctx.close();
122         }
123     }
124
125     private CoapClient     asClient       = null;
126     //////////
127
128     private SessionManager sessionManager = null;
129
130     public CoapRelayHandler(SessionManager sessionManager) {
131         this.sessionManager = sessionManager;
132
133         rdClient = new CoapClient();
134
135         rdClient.addHandler(new RDHandler());
136
137         asClient = new CoapClient();
138
139         asClient.addHandler(new AccountHandler());
140     }
141
142     public void startHandler(String rdAddress, int rdPort, String acAddress,
143             int acPort) throws Exception {
144         rdClient.startClient(new InetSocketAddress(rdAddress, rdPort));
145
146         asClient.startClient(new InetSocketAddress(acAddress, acPort));
147
148         asClient.getChannelFuture().channel().attr(keyAccountClient)
149                 .set(new ArrayList<CoapRequest>());
150     }
151
152     public void stopHandler() throws Exception {
153         asClient.stopClient();
154
155         rdClient.stopClient();
156     }
157
158     private static final AttributeKey<ChannelHandlerContext> keyDevice = AttributeKey
159             .newInstance("deviceCtx");
160
161     @Override
162     public void channelRead(ChannelHandlerContext ctx, Object msg)
163             throws Exception {
164
165         if (msg instanceof CoapRequest) {
166             CoapRequest request = (CoapRequest) msg;
167             // Parse uri, send to RD
168             String uriPath = request.getUriPath();
169             CoapRequest accountRequest = null;
170             String userId, deviceId, authPayload;
171             CoapResponse response;
172
173             Logger.d("Request received, URI: " + uriPath);
174             if (uriPath != null) {
175                 switch (uriPath) {
176                     case Constants.AUTH_URI:
177                         // This case user wants to logout
178                         String uriQuery = request.getUriQuery();
179                         if (uriQuery != null) {
180                             if (uriQuery.endsWith("logout")) {
181                                 ctx.channel().attr(Constants.Attribute_UserId)
182                                         .remove();
183                                 response = new CoapResponse(CoapStatus.DELETED);
184                             } else {
185                                 response = new CoapResponse(
186                                         CoapStatus.BAD_REQUEST);
187                             }
188                             ctx.writeAndFlush(response);
189                         }
190                         break;
191
192                     case Constants.RD_URI:
193                         // RD POST means publish device to server
194                         switch (request.getRequestMethod()) {
195                             case POST:
196                                 userId = ctx.channel()
197                                         .attr(Constants.Attribute_UserId).get();
198                                 deviceId = request.decodeDeviceId();
199                                 authPayload = String.format(
200                                         "{\"userid\":\"%s\",\"deviceid\":\"%s\"}",
201                                         userId, deviceId);
202                                 accountRequest = new CoapRequest(
203                                         CoapMethod.POST);
204                                 accountRequest
205                                         .setUriPath(Constants.ACCOUNT_URI);
206                                 accountRequest.setUriQuery("reqtype=publish");
207                                 accountRequest.setToken(request.getToken());
208                                 accountRequest.setPayload(authPayload
209                                         .getBytes(StandardCharsets.UTF_8));
210
211                                 // TODO: deviceId must be registered after
212                                 // session
213                                 // granted
214                                 Logger.d("Adding deviceId to session: "
215                                         + deviceId);
216                                 sessionManager.addSession(deviceId, ctx);
217                                 break;
218
219                             default:
220                                 Logger.e("Unsupported request type");
221                                 break;
222                         }
223
224                         rdClient.getChannelFuture().channel().attr(keyRDClient)
225                                 .set(ctx);
226
227                         // Add original request to list for future use
228                         asClient.getChannelFuture().channel()
229                                 .attr(keyAccountClient).get().add(request);
230                         asClient.sendRequest(accountRequest);
231                         return;
232
233                     case Constants.WELL_KNOWN_URI:
234                         switch (request.getRequestMethod()) {
235                             case GET:
236                                 userId = ctx.channel()
237                                         .attr(Constants.Attribute_UserId).get();
238                                 authPayload = String
239                                         .format("{\"userid\":\"%s\"}", userId);
240                                 accountRequest = new CoapRequest(
241                                         CoapMethod.GET);
242                                 accountRequest
243                                         .setUriPath(Constants.ACCOUNT_URI);
244                                 accountRequest.setUriQuery("reqtype=find");
245                                 accountRequest.setToken(request.getToken());
246                                 accountRequest.setPayload(authPayload
247                                         .getBytes(StandardCharsets.UTF_8));
248                                 break;
249
250                             default:
251                                 Logger.e("Unsupported request type");
252                                 break;
253                         }
254
255                         rdClient.getChannelFuture().channel().attr(keyRDClient)
256                                 .set(ctx);
257
258                         // Add original request to list for future use
259                         asClient.getChannelFuture().channel()
260                                 .attr(keyAccountClient).get().add(request);
261                         asClient.sendRequest(accountRequest);
262                         return;
263
264                     case Constants.KEEP_ALIVE_URI:
265                         break;
266
267                     default:
268                         List<String> uriPathList = request.getUriPathSegments();
269                         if (uriPathList != null) {
270                             Logger.i("uriPahtList: " + uriPathList.toString());
271
272                             String did = uriPathList.get(0);
273
274                             Logger.i("did: " + did);
275
276                             // TODO: Clustering algorithm required
277                             // find ctx about did, and send msg
278                             StringBuffer resource = new StringBuffer();
279                             List<String> pathSegments = uriPathList.subList(1,
280                                     uriPathList.size());
281                             for (String path : pathSegments) {
282                                 resource.append("/");
283                                 resource.append(path);
284                             }
285                             Logger.i("resource: " + resource);
286                             request.setUriPath(resource.toString());
287
288                             ChannelHandlerContext deviceCtx = sessionManager
289                                     .querySession(did);
290                             if (deviceCtx != null) {
291                                 deviceCtx.attr(keyDevice).set(ctx);
292                                 deviceCtx.writeAndFlush(request);
293                             } else {
294                                 Logger.e("deviceCtx is null");
295                                 response = new CoapResponse(
296                                         CoapStatus.FORBIDDEN);
297                                 response.setToken(request.getToken());
298                                 ctx.writeAndFlush(response);
299                             }
300                         }
301                         return;
302                 }
303             }
304
305         } else if (msg instanceof CoapResponse) {
306             ChannelHandlerContext resourceClient = ctx.attr(keyDevice).get();
307             if (resourceClient != null) {
308                 Logger.i("Forwards message to client");
309
310                 CoapResponse response = (CoapResponse) msg;
311
312                 // If response contains path, add di
313                 String did = sessionManager.queryDid(ctx);
314                 if (response.getOption(11) != null && did != null) {
315                     response.getOption(11).add(0,
316                             did.getBytes(StandardCharsets.UTF_8));
317                 }
318
319                 Logger.i(
320                         "ctx.channel : " + resourceClient.channel().toString());
321                 resourceClient.writeAndFlush(response);
322                 return;
323             }
324         }
325
326         super.channelRead(ctx, msg);
327     }
328
329     @Override
330     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
331         Logger.d("Channel Inactive");
332         sessionManager.removeSessionByChannel(ctx);
333         super.channelInactive(ctx);
334     }
335
336     @Override
337     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
338
339         cause.printStackTrace();
340         ctx.close();
341     }
342 }