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