2 *******************************************************************
4 * Copyright 2016 Samsung Electronics All Rights Reserved.
6 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
22 package org.iotivity.cloud.ciserver.protocols;
24 import java.net.InetSocketAddress;
25 import java.nio.charset.StandardCharsets;
26 import java.util.ArrayList;
27 import java.util.List;
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;
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;
46 * This class is relay handler relating Cloud Interface
50 public class CoapRelayHandler extends ChannelDuplexHandler {
52 /////////// Handler for Resource Directory
53 private static final AttributeKey<ChannelHandlerContext> keyRDClient = AttributeKey
54 .newInstance("rdCtx");
56 private static class RDHandler
57 extends SimpleChannelInboundHandler<CoapResponse> {
59 public void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
61 Logger.d("Receive response from RD, forward to client");
63 ChannelHandlerContext ctxToDevice = ctx.channel().attr(keyRDClient)
65 ctxToDevice.writeAndFlush(msg);
69 public void exceptionCaught(ChannelHandlerContext ctx,
71 cause.printStackTrace();
76 private CoapClient rdClient = null;
79 ////////// Handler for Account Server
80 private static final AttributeKey<List<CoapRequest>> keyAccountClient = AttributeKey
81 .newInstance("accountCtx");
83 private class AccountHandler
84 extends SimpleChannelInboundHandler<CoapResponse> {
86 public void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
88 Logger.d("Receive response from AS, make request to RD");
90 CoapRequest rdRequest = null;
92 switch (msg.getResponseCode()) {
94 // response of POST request
95 rdRequest = ctx.channel().attr(keyAccountClient).get()
97 rdClient.sendRequest(rdRequest);
101 // response of GET request, contains did list
102 rdRequest = ctx.channel().attr(keyAccountClient).get()
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);
117 public void exceptionCaught(ChannelHandlerContext ctx,
119 cause.printStackTrace();
124 private CoapClient asClient = null;
127 private SessionManager sessionManager = null;
129 public CoapRelayHandler(SessionManager sessionManager) {
130 this.sessionManager = sessionManager;
132 rdClient = new CoapClient();
134 rdClient.addHandler(new RDHandler());
136 asClient = new CoapClient();
138 asClient.addHandler(new AccountHandler());
141 public void startHandler(String rdAddress, int rdPort, String acAddress,
142 int acPort) throws Exception {
143 rdClient.startClient(new InetSocketAddress(rdAddress, rdPort));
145 asClient.startClient(new InetSocketAddress(acAddress, acPort));
147 asClient.getChannelFuture().channel().attr(keyAccountClient)
148 .set(new ArrayList<CoapRequest>());
151 public void stopHandler() throws Exception {
152 asClient.stopClient();
154 rdClient.stopClient();
157 private static final AttributeKey<ChannelHandlerContext> keyDevice = AttributeKey
158 .newInstance("deviceCtx");
161 public void channelRead(ChannelHandlerContext ctx, Object msg)
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;
172 Logger.d("Request received, URI: " + uriPath);
173 if (uriPath != null) {
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)
182 response = new CoapResponse(CoapStatus.DELETED);
184 response = new CoapResponse(
185 CoapStatus.BAD_REQUEST);
187 ctx.writeAndFlush(response);
191 case Constants.RD_URI:
192 // RD POST means publish device to server
193 switch (request.getRequestMethod()) {
195 userId = ctx.channel()
196 .attr(Constants.Attribute_UserId).get();
197 deviceId = request.decodeDeviceId();
198 authPayload = String.format(
199 "{\"userid\":\"%s\",\"deviceid\":\"%s\"}",
201 accountRequest = new CoapRequest(
204 .setUriPath(Constants.ACCOUNT_URI);
205 accountRequest.setUriQuery("reqtype=publish");
206 accountRequest.setToken(request.getToken());
207 accountRequest.setPayload(authPayload
208 .getBytes(StandardCharsets.UTF_8));
210 // TODO: deviceId must be registered after
213 Logger.d("Adding deviceId to session: "
215 sessionManager.addSession(deviceId, ctx);
219 Logger.e("Unsupported request type");
223 rdClient.getChannelFuture().channel().attr(keyRDClient)
226 // Add original request to list for future use
227 asClient.getChannelFuture().channel()
228 .attr(keyAccountClient).get().add(request);
229 asClient.sendRequest(accountRequest);
232 case Constants.WELL_KNOWN_URI:
233 switch (request.getRequestMethod()) {
235 userId = ctx.channel()
236 .attr(Constants.Attribute_UserId).get();
238 .format("{\"userid\":\"%s\"}", userId);
239 accountRequest = new CoapRequest(
242 .setUriPath(Constants.ACCOUNT_URI);
243 accountRequest.setUriQuery("reqtype=find");
244 accountRequest.setToken(request.getToken());
245 accountRequest.setPayload(authPayload
246 .getBytes(StandardCharsets.UTF_8));
250 Logger.e("Unsupported request type");
254 rdClient.getChannelFuture().channel().attr(keyRDClient)
257 // Add original request to list for future use
258 asClient.getChannelFuture().channel()
259 .attr(keyAccountClient).get().add(request);
260 asClient.sendRequest(accountRequest);
263 case Constants.KEEP_ALIVE_URI:
267 List<String> uriPathList = request.getUriPathSegments();
268 if (uriPathList != null) {
269 Logger.i("uriPahtList: " + uriPathList.toString());
271 String did = uriPathList.get(0);
273 Logger.i("did: " + did);
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,
280 for (String path : pathSegments) {
281 resource.append("/");
282 resource.append(path);
284 Logger.i("resource: " + resource);
285 request.setUriPath(resource.toString());
287 ChannelHandlerContext deviceCtx = sessionManager
289 if (deviceCtx != null) {
290 deviceCtx.attr(keyDevice).set(ctx);
291 deviceCtx.writeAndFlush(request);
293 Logger.e("deviceCtx is null");
294 response = new CoapResponse(
295 CoapStatus.FORBIDDEN);
296 response.setToken(request.getToken());
297 ctx.writeAndFlush(response);
304 } else if (msg instanceof CoapResponse) {
305 ChannelHandlerContext resourceClient = ctx.attr(keyDevice).get();
306 if (resourceClient != null) {
307 Logger.i("Forwards message to client");
309 CoapResponse response = (CoapResponse) msg;
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));
319 "ctx.channel : " + resourceClient.channel().toString());
320 resourceClient.writeAndFlush(response);
325 super.channelRead(ctx, msg);
329 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
330 Logger.d("Channel Inactive");
331 sessionManager.removeSessionByChannel(ctx);
332 super.channelInactive(ctx);
336 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
338 cause.printStackTrace();