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.Charset;
26 import java.nio.charset.StandardCharsets;
27 import java.util.ArrayList;
28 import java.util.List;
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;
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;
47 * This class is relay handler relating Cloud Interface
51 public class CoapRelayHandler extends ChannelDuplexHandler {
53 /////////// Handler for Resource Directory
54 private static final AttributeKey<ChannelHandlerContext> keyRDClient = AttributeKey
55 .newInstance("rdCtx");
57 private static class RDHandler
58 extends SimpleChannelInboundHandler<CoapResponse> {
60 public void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
62 Logger.d("Receive response from RD, forward to client");
64 ChannelHandlerContext ctxToDevice = ctx.channel().attr(keyRDClient)
66 ctxToDevice.writeAndFlush(msg);
70 public void exceptionCaught(ChannelHandlerContext ctx,
72 cause.printStackTrace();
77 private CoapClient rdClient = null;
80 ////////// Handler for Account Server
81 private static final AttributeKey<List<CoapRequest>> keyAccountClient = AttributeKey
82 .newInstance("accountCtx");
84 private class AccountHandler
85 extends SimpleChannelInboundHandler<CoapResponse> {
87 public void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
89 Logger.d("Receive response from AS, make request to RD");
91 CoapRequest rdRequest = null;
93 switch (msg.getResponseCode()) {
95 // response of POST request
96 rdRequest = ctx.channel().attr(keyAccountClient).get()
98 rdClient.sendRequest(rdRequest);
102 // response of GET request, contains did list
103 rdRequest = ctx.channel().attr(keyAccountClient).get()
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);
118 public void exceptionCaught(ChannelHandlerContext ctx,
120 cause.printStackTrace();
125 private CoapClient asClient = null;
128 private SessionManager sessionManager = null;
130 public CoapRelayHandler(SessionManager sessionManager) {
131 this.sessionManager = sessionManager;
133 rdClient = new CoapClient();
135 rdClient.addHandler(new RDHandler());
137 asClient = new CoapClient();
139 asClient.addHandler(new AccountHandler());
142 public void startHandler(String rdAddress, int rdPort, String acAddress,
143 int acPort) throws Exception {
144 rdClient.startClient(new InetSocketAddress(rdAddress, rdPort));
146 asClient.startClient(new InetSocketAddress(acAddress, acPort));
148 asClient.getChannelFuture().channel().attr(keyAccountClient)
149 .set(new ArrayList<CoapRequest>());
152 public void stopHandler() throws Exception {
153 asClient.stopClient();
155 rdClient.stopClient();
158 private static final AttributeKey<ChannelHandlerContext> keyDevice = AttributeKey
159 .newInstance("deviceCtx");
162 public void channelRead(ChannelHandlerContext ctx, Object msg)
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;
173 Logger.d("Request received, URI: " + uriPath);
174 if (uriPath != null) {
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)
183 response = new CoapResponse(CoapStatus.DELETED);
185 response = new CoapResponse(
186 CoapStatus.BAD_REQUEST);
188 ctx.writeAndFlush(response);
192 case Constants.RD_URI:
193 // RD POST means publish device to server
194 switch (request.getRequestMethod()) {
196 userId = ctx.channel()
197 .attr(Constants.Attribute_UserId).get();
198 deviceId = request.decodeDeviceId();
199 authPayload = String.format(
200 "{\"userid\":\"%s\",\"deviceid\":\"%s\"}",
202 accountRequest = new CoapRequest(
205 .setUriPath(Constants.ACCOUNT_URI);
206 accountRequest.setUriQuery("reqtype=publish");
207 accountRequest.setToken(request.getToken());
208 accountRequest.setPayload(authPayload
209 .getBytes(StandardCharsets.UTF_8));
211 // TODO: deviceId must be registered after
214 Logger.d("Adding deviceId to session: "
216 sessionManager.addSession(deviceId, ctx);
220 Logger.e("Unsupported request type");
224 rdClient.getChannelFuture().channel().attr(keyRDClient)
227 // Add original request to list for future use
228 asClient.getChannelFuture().channel()
229 .attr(keyAccountClient).get().add(request);
230 asClient.sendRequest(accountRequest);
233 case Constants.WELL_KNOWN_URI:
234 switch (request.getRequestMethod()) {
236 userId = ctx.channel()
237 .attr(Constants.Attribute_UserId).get();
239 .format("{\"userid\":\"%s\"}", userId);
240 accountRequest = new CoapRequest(
243 .setUriPath(Constants.ACCOUNT_URI);
244 accountRequest.setUriQuery("reqtype=find");
245 accountRequest.setToken(request.getToken());
246 accountRequest.setPayload(authPayload
247 .getBytes(StandardCharsets.UTF_8));
251 Logger.e("Unsupported request type");
255 rdClient.getChannelFuture().channel().attr(keyRDClient)
258 // Add original request to list for future use
259 asClient.getChannelFuture().channel()
260 .attr(keyAccountClient).get().add(request);
261 asClient.sendRequest(accountRequest);
264 case Constants.KEEP_ALIVE_URI:
268 List<String> uriPathList = request.getUriPathSegments();
269 if (uriPathList != null) {
270 Logger.i("uriPahtList: " + uriPathList.toString());
272 String did = uriPathList.get(0);
274 Logger.i("did: " + did);
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,
281 for (String path : pathSegments) {
282 resource.append("/");
283 resource.append(path);
285 Logger.i("resource: " + resource);
286 request.setUriPath(resource.toString());
288 ChannelHandlerContext deviceCtx = sessionManager
290 if (deviceCtx != null) {
291 deviceCtx.attr(keyDevice).set(ctx);
292 deviceCtx.writeAndFlush(request);
294 Logger.e("deviceCtx is null");
295 response = new CoapResponse(
296 CoapStatus.FORBIDDEN);
297 response.setToken(request.getToken());
298 ctx.writeAndFlush(response);
305 } else if (msg instanceof CoapResponse) {
306 ChannelHandlerContext resourceClient = ctx.attr(keyDevice).get();
307 if (resourceClient != null) {
308 Logger.i("Forwards message to client");
310 CoapResponse response = (CoapResponse) msg;
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));
320 "ctx.channel : " + resourceClient.channel().toString());
321 resourceClient.writeAndFlush(response);
326 super.channelRead(ctx, msg);
330 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
331 Logger.d("Channel Inactive");
332 sessionManager.removeSessionByChannel(ctx);
333 super.channelInactive(ctx);
337 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
339 cause.printStackTrace();