import org.iotivity.cloud.base.device.IRequestChannel;
import org.iotivity.cloud.base.exception.ClientException;
import org.iotivity.cloud.base.exception.ServerException;
+import org.iotivity.cloud.base.exception.ServerException.BadOptionException;
import org.iotivity.cloud.base.exception.ServerException.BadRequestException;
import org.iotivity.cloud.base.exception.ServerException.UnAuthorizedException;
import org.iotivity.cloud.base.protocols.MessageBuilder;
import org.iotivity.cloud.base.protocols.coap.CoapRequest;
import org.iotivity.cloud.base.protocols.coap.CoapResponse;
+import org.iotivity.cloud.base.protocols.coap.CoapSignaling;
import org.iotivity.cloud.base.protocols.enums.ContentFormat;
import org.iotivity.cloud.base.protocols.enums.RequestMethod;
import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
+import org.iotivity.cloud.base.protocols.enums.SignalingMethod;
import org.iotivity.cloud.base.server.CoapServer;
import org.iotivity.cloud.base.server.HttpServer;
import org.iotivity.cloud.base.server.Server;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
/**
public class DeviceServerSystem extends ServerSystem {
- private Cbor<HashMap<String, Object>> mCbor = new Cbor<HashMap<String, Object>>();
+ private Cbor<HashMap<String, Object>> mCbor = new Cbor<HashMap<String, Object>>();
+ private HashMap<ChannelHandlerContext, CoapSignaling> mCsmMap = new HashMap<>();
- IRequestChannel mRDServer = null;
+ IRequestChannel mRDServer = null;
public DeviceServerSystem() {
mRDServer = ConnectorPool.getConnection("rd");
public void channelInactive(ChannelHandlerContext ctx)
throws ClientException {
Device device = ctx.channel().attr(keyDevice).get();
+
// Some cases, this event occurs after new device connected using
// same di.
// So compare actual value, and remove if same.
@Override
public void write(ChannelHandlerContext ctx, Object msg,
ChannelPromise promise) {
-
try {
if (!(msg instanceof CoapResponse)) {
}
// This is CoapResponse
// Once the response is valid, add this to deviceList
+
CoapResponse response = (CoapResponse) msg;
switch (response.getUriPath()) {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
-
try {
if (!(msg instanceof CoapRequest)) {
throw new BadRequestException(
}
}
+ @Sharable
+ class CoapSignalingHandler extends ChannelInboundHandlerAdapter {
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx)
+ throws Exception {
+ // delete csm information from the map
+ mCsmMap.remove(ctx);
+ ctx.fireChannelInactive();
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ Device device = ctx.channel().attr(keyDevice).get();
+ mDevicePool.addDevice(device);
+ device.onConnected();
+ // Authenticated device connected
+ // Actual channel active should decided after authentication.
+ CoapSignaling signaling = (CoapSignaling) MessageBuilder
+ .createSignaling(SignalingMethod.CSM);
+ signaling.setCsmMaxMessageSize(4294967295L);
+ ctx.writeAndFlush(signaling);
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ try {
+ if (msg instanceof CoapSignaling) {
+ CoapSignaling signaling = (CoapSignaling) msg;
+ switch (signaling.getSignalingMethod()) {
+ case CSM:
+ // get existing CSM from the map
+ CoapSignaling existingCsm = mCsmMap.get(ctx);
+ if (existingCsm == null) {
+ existingCsm = signaling;
+ } else {
+ // replace and cumulate CSM options
+ existingCsm.setCsmBlockWiseTransfer(
+ signaling.getCsmBlockWiseTransfer());
+ existingCsm.setCsmMaxMessageSize(
+ signaling.getCsmMaxMessageSize());
+ existingCsm.setCsmServerName(
+ signaling.getCsmServerName());
+ }
+ mCsmMap.put(ctx, existingCsm);
+ break;
+ case PING:
+ // TODO process PING signaling option
+ break;
+ case PONG:
+ // TODO process PONG signaling option
+ break;
+ case RELEASE:
+ case ABORT:
+ mCsmMap.remove(ctx);
+ ctx.close();
+ break;
+ default:
+ throw new BadOptionException(
+ "unsupported CoAP Signaling option");
+ }
+
+ ctx.fireChannelRead(msg);
+ } else {
+ ctx.fireChannelRead(msg);
+ // TODO annotated codes must be removed to follow
+ // the CSM specification of draft-ietf-core-coap-tcp-tls-05
+
+ // if (mCsmMap.get(ctx) != null) {
+ // ctx.fireChannelRead(msg);
+ // } else {
+ // // send ABORT signaling and close the connection
+ // ctx.writeAndFlush(MessageBuilder.createSignaling(
+ // SignalingMethod.ABORT,
+ // new String(
+ // "Capability and Settings message (CSM) is not received
+ // yet")
+ // .getBytes()));
+ // ctx.close();
+ // }
+ }
+ } catch (Throwable t) {
+ ResponseStatus responseStatus = t instanceof ServerException
+ ? ((ServerException) t).getErrorResponse()
+ : ResponseStatus.BAD_OPTION;
+ if (msg instanceof CoapRequest) {
+ ctx.writeAndFlush(MessageBuilder
+ .createResponse((CoapRequest) msg, responseStatus));
+ } else if (msg instanceof CoapSignaling) {
+ ctx.writeAndFlush(MessageBuilder.createSignalingResponse(
+ (CoapSignaling) msg, responseStatus));
+ }
+ Log.f(ctx.channel(), t);
+ }
+ }
+
+ }
+
@Override
public void addServer(Server server) {
if (server instanceof CoapServer) {
+ server.addHandler(new CoapSignalingHandler());
server.addHandler(new CoapAuthHandler());
}
import org.iotivity.cloud.base.device.IRequestChannel;
import org.iotivity.cloud.base.protocols.IRequest;
import org.iotivity.cloud.base.protocols.IResponse;
+import org.iotivity.cloud.base.protocols.ISignaling;
import org.iotivity.cloud.base.protocols.MessageBuilder;
import org.iotivity.cloud.base.protocols.coap.CoapRequest;
import org.iotivity.cloud.base.protocols.coap.CoapResponse;
+import org.iotivity.cloud.base.protocols.coap.CoapSignaling;
import org.iotivity.cloud.base.protocols.enums.ContentFormat;
import org.iotivity.cloud.base.protocols.enums.RequestMethod;
import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
+import org.iotivity.cloud.base.protocols.enums.SignalingMethod;
import org.iotivity.cloud.base.server.CoapServer;
import org.iotivity.cloud.base.server.HttpServer;
import org.iotivity.cloud.ciserver.DeviceServerSystem.CoapDevicePool;
+import org.iotivity.cloud.ciserver.DeviceServerSystem.CoapSignalingHandler;
import org.iotivity.cloud.ciserver.resources.proxy.account.Account;
import org.iotivity.cloud.ciserver.resources.proxy.mq.MessageQueue;
import org.iotivity.cloud.ciserver.resources.proxy.rd.ResourceDirectory;
public class DeviceServerSystemTest {
private ChannelHandlerContext mCtx = null;
+ private ChannelHandlerContext mCtxSignal = null;
private String mDi = "B371C481-38E6-4D47-8320-7688D8A5B58C";
private String mUserId = "testuser";
private String mAccessToken = "1689c70ffa245effc563017fee36d250";
CoapDevice.class);
private IResponse mRes = null;
private IRequest mReq = null;
+ private ISignaling mSig = null;
final CountDownLatch mLatch = new CountDownLatch(
1);
private Cbor<HashMap<Object, Object>> mCbor = new Cbor<>();
private DeviceServerSystem.CoapLifecycleHandler mCoapLifecycleHandler = mDeviceServerSystem.new CoapLifecycleHandler();
@InjectMocks
private DeviceServerSystem.CoapAuthHandler mCoapAuthHandler = mDeviceServerSystem.new CoapAuthHandler();
+ @InjectMocks
+ private CoapSignalingHandler mCoapSignalingHandler = mDeviceServerSystem.new CoapSignalingHandler();
@Before
public void setUp() throws Exception {
mRes = null;
mReq = null;
mCtx = mock(ChannelHandlerContext.class);
+ mCtxSignal = mock(ChannelHandlerContext.class);
Cbor<HashMap<Object, Object>> cbor = new Cbor<>();
Channel channel = mock(Channel.class);
Attribute<Device> attribute = mock(Attribute.class);
Mockito.doAnswer(new Answer<Object>() {
@Override
+ public CoapSignaling answer(InvocationOnMock invocation)
+ throws Throwable {
+ Object[] args = invocation.getArguments();
+ CoapSignaling signaling = (CoapSignaling) args[0];
+ System.out.println("\t----------payload : "
+ + signaling.getPayloadString());
+ System.out.println("\t----------signaling method : "
+ + signaling.getSignalingMethod());
+ if (signaling.getSignalingMethod()
+ .equals(SignalingMethod.CSM)) {
+ System.out.println("\t----------CSM Blockwise Transfer : "
+ + signaling.getCsmBlockWiseTransfer());
+ System.out.println("\t----------CSM Max Message Size : "
+ + signaling.getCsmMaxMessageSize());
+ System.out.println("\t----------CSM Server Name : "
+ + signaling.getCsmServerName());
+ }
+ mSig = signaling;
+ mLatch.countDown();
+ return null;
+ }
+ }).when(mCtxSignal).fireChannelRead(Mockito.any());
+
+ Mockito.doAnswer(new Answer<Object>() {
+ @Override
public CoapResponse answer(InvocationOnMock invocation)
throws Throwable {
Object[] args = invocation.getArguments();
}
@Test
+ public void coapAuthHandlerCSMSignaling() throws Exception {
+
+ System.out.println(
+ "\t--------------coapAuthHandler coapAuthHandlerCSMSignaling Test------------");
+ ISignaling signal = MessageBuilder.createSignaling(SignalingMethod.CSM);
+ CoapSignaling signaling = (CoapSignaling) signal;
+ signaling.setCsmBlockWiseTransfer(true);
+ signaling.setCsmMaxMessageSize(1124);
+ signaling.setCsmServerName("default_server_name");
+ mCoapSignalingHandler.channelRead(mCtxSignal, signal);
+ assertEquals(mSig.getSignalingMethod(), SignalingMethod.CSM);
+ }
+
+ @Test
public void CoapLifecycleHandlerChannelReadRequest()
throws InterruptedException {
System.out.println(
}
}
+ public static class BadOptionException extends ServerException {
+ private static final long serialVersionUID = -1133293352997486233L;
+
+ public BadOptionException() {
+ super(ResponseStatus.BAD_OPTION);
+ }
+
+ public BadOptionException(String msg) {
+ super(ResponseStatus.BAD_OPTION, msg);
+ }
+ }
+
public static class NotFoundException extends ServerException {
private static final long serialVersionUID = 775328915430229701L;
}
}
+ public static class MethodNotAllowedException extends ServerException {
+ private static final long serialVersionUID = -3491771205478082847L;
+
+ public MethodNotAllowedException() {
+ super(ResponseStatus.METHOD_NOT_ALLOWED);
+ }
+
+ public MethodNotAllowedException(String msg) {
+ super(ResponseStatus.METHOD_NOT_ALLOWED, msg);
+ }
+ }
+
public static class PreconditionFailedException extends ServerException {
private static final long serialVersionUID = -4139595328143251734L;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2016 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+package org.iotivity.cloud.base.protocols;
+
+import org.iotivity.cloud.base.protocols.enums.SignalingMethod;
+
+public interface ISignaling {
+
+ public SignalingMethod getSignalingMethod();
+
+ public int getPayloadSize();
+
+ public byte[] getPayload();
+}
import java.util.List;
import java.util.StringTokenizer;
-public abstract class Message implements IRequest, IResponse {
+public abstract class Message implements IRequest, IResponse, ISignaling {
protected List<byte[]> uri_path = null;
protected List<byte[]> uri_query = null;
protected byte[] payload = null;
import org.iotivity.cloud.base.protocols.coap.CoapRequest;
import org.iotivity.cloud.base.protocols.coap.CoapResponse;
+import org.iotivity.cloud.base.protocols.coap.CoapSignaling;
import org.iotivity.cloud.base.protocols.enums.ContentFormat;
import org.iotivity.cloud.base.protocols.enums.RequestMethod;
import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
+import org.iotivity.cloud.base.protocols.enums.SignalingMethod;
public class MessageBuilder {
null);
}
+ public static IResponse createSignalingResponse(ISignaling signaling,
+ ResponseStatus responseStatus) {
+ return createSignalingResponse(signaling, responseStatus, null);
+ }
+
+ public static IResponse createSignalingResponse(ISignaling signaling,
+ ResponseStatus responseStatus, byte[] payload) {
+ IResponse response = null;
+
+ if (signaling instanceof CoapSignaling) {
+ CoapSignaling coapSignaling = (CoapSignaling) signaling;
+ CoapResponse coapResponse = new CoapResponse(responseStatus);
+ coapResponse.setToken(coapSignaling.getToken());
+ if (payload != null) {
+ coapResponse.setPayload(payload);
+ }
+
+ response = coapResponse;
+ }
+
+ return response;
+ }
+
public static IResponse createResponse(IRequest request,
ResponseStatus responseStatus, ContentFormat format,
byte[] payload) {
ContentFormat.NO_CONTENT, null);
}
+ public static ISignaling createSignaling(SignalingMethod signalingMethod,
+ byte[] diagnosticPayload) {
+
+ CoapSignaling coapSignaling = null;
+
+ coapSignaling = new CoapSignaling(signalingMethod);
+ // TODO: Random token generation required
+ coapSignaling.setToken("tmptoken".getBytes());
+
+ if (diagnosticPayload != null) {
+ coapSignaling.setPayload(diagnosticPayload);
+ }
+
+ return coapSignaling;
+ }
+
+ public static ISignaling createSignaling(SignalingMethod signalingMethod) {
+ return createSignaling(signalingMethod, null);
+ }
+
public static IRequest createRequest(RequestMethod requestMethod,
String uriPath, String uriQuery, ContentFormat contentFormat,
byte[] payload) {
import java.util.List;
+import org.iotivity.cloud.base.exception.ServerException;
+import org.iotivity.cloud.base.protocols.MessageBuilder;
+import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
+import org.iotivity.cloud.util.Log;
+
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,
- List<Object> out) throws Exception {
-
- // TODO: need exceptional case handling
- while (in.isReadable(bufferToRead)) {
-
- switch (nextState) {
- case SHIM_HEADER:
- int shimHeader = in.readByte();
- bufferToRead = (shimHeader >>> 4) & 0x0F;
- tokenLength = (shimHeader) & 0x0F;
- switch (bufferToRead) {
- case 13:
- bufferToRead = 1;
- nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
- break;
- case 14:
- bufferToRead = 2;
- nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
- break;
- case 15:
- bufferToRead = 4;
- nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
- break;
- default:
- optionPayloadLength = bufferToRead;
- bufferToRead += 1 + tokenLength; // code + tkl
- nextState = ParsingState.CODE_TOKEN_OPTION;
- break;
- }
- break;
-
- case OPTION_PAYLOAD_LENGTH:
- switch (bufferToRead) {
- case 1:
- optionPayloadLength = 13 + (in.readByte() & 0xFF);
- break;
-
- case 2:
- optionPayloadLength = 269
- + (((in.readByte() & 0xFF) << 8)
- + (in.readByte() & 0xFF));
- break;
-
- case 4:
- optionPayloadLength = 65805
- + (((in.readByte() & 0xFF) << 24)
- + ((in.readByte() & 0xFF) << 16)
- + ((in.readByte() & 0xFF) << 8)
- + (in.readByte() & 0xFF));
- break;
- }
- nextState = ParsingState.CODE_TOKEN_OPTION;
- bufferToRead = 1 + tokenLength + optionPayloadLength; // code
- // +
- // tkl
- break;
-
- case CODE_TOKEN_OPTION:
- int code = in.readByte() & 0xFF;
-
- if (code <= 31) {
- partialMsg = new CoapRequest(code);
- } else {
- partialMsg = new CoapResponse(code);
- }
-
- if (tokenLength > 0) {
- byte[] token = new byte[tokenLength];
- in.readBytes(token);
- partialMsg.setToken(token);
- }
-
- if (optionPayloadLength > 0) {
- int optionLen = parseOptions(partialMsg, in,
- optionPayloadLength);
- if (optionPayloadLength > optionLen) {
- nextState = ParsingState.PAYLOAD;
- bufferToRead = optionPayloadLength - optionLen;
- continue;
+ List<Object> out) {
+ try {
+
+ // TODO: need exceptional case handling
+ while (in.isReadable(bufferToRead)) {
+
+ switch (nextState) {
+ case SHIM_HEADER:
+ int shimHeader = in.readByte();
+ bufferToRead = (shimHeader >>> 4) & 0x0F;
+ tokenLength = (shimHeader) & 0x0F;
+ switch (bufferToRead) {
+ case 13:
+ bufferToRead = 1;
+ nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
+ break;
+ case 14:
+ bufferToRead = 2;
+ nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
+ break;
+ case 15:
+ bufferToRead = 4;
+ nextState = ParsingState.OPTION_PAYLOAD_LENGTH;
+ break;
+ default:
+ optionPayloadLength = bufferToRead;
+ bufferToRead += 1 + tokenLength; // code + tkl
+ nextState = ParsingState.CODE_TOKEN_OPTION;
+ break;
+ }
+ break;
+
+ case OPTION_PAYLOAD_LENGTH:
+ switch (bufferToRead) {
+ case 1:
+ optionPayloadLength = 13
+ + (in.readByte() & 0xFF);
+ break;
+
+ case 2:
+ optionPayloadLength = 269
+ + (((in.readByte() & 0xFF) << 8)
+ + (in.readByte() & 0xFF));
+ break;
+
+ case 4:
+ optionPayloadLength = 65805
+ + (((in.readByte() & 0xFF) << 24)
+ + ((in.readByte() & 0xFF) << 16)
+ + ((in.readByte() & 0xFF) << 8)
+ + (in.readByte() & 0xFF));
+ break;
+ }
+ nextState = ParsingState.CODE_TOKEN_OPTION;
+ bufferToRead = 1 + tokenLength + optionPayloadLength; // code
+ // +
+ // tkl
+ break;
+
+ case CODE_TOKEN_OPTION:
+ int code = in.readByte() & 0xFF;
+ if (code <= 31) {
+ partialMsg = new CoapRequest(code);
+ } else if (code > 224) {
+ partialMsg = new CoapSignaling(code);
+ } else {
+ partialMsg = new CoapResponse(code);
+ }
+
+ if (tokenLength > 0) {
+ byte[] token = new byte[tokenLength];
+ in.readBytes(token);
+ partialMsg.setToken(token);
+ }
+
+ if (optionPayloadLength > 0) {
+ int optionLen = parseOptions(partialMsg, in,
+ optionPayloadLength);
+ if (optionPayloadLength > optionLen) {
+ nextState = ParsingState.PAYLOAD;
+ bufferToRead = optionPayloadLength - optionLen;
+ continue;
+ }
}
- }
- nextState = ParsingState.FINISH;
- bufferToRead = 0;
+ nextState = ParsingState.FINISH;
+ bufferToRead = 0;
- break;
+ break;
- case PAYLOAD:
- byte[] payload = new byte[bufferToRead];
- in.readBytes(payload);
- partialMsg.setPayload(payload);
- nextState = ParsingState.FINISH;
- bufferToRead = 0;
- break;
+ case PAYLOAD:
+ byte[] payload = new byte[bufferToRead];
+ in.readBytes(payload);
+ partialMsg.setPayload(payload);
+ nextState = ParsingState.FINISH;
+ bufferToRead = 0;
+ break;
- case FINISH:
- nextState = ParsingState.SHIM_HEADER;
- bufferToRead = 1;
- out.add(partialMsg);
- break;
+ case FINISH:
+ nextState = ParsingState.SHIM_HEADER;
+ bufferToRead = 1;
+ out.add(partialMsg);
+ break;
- default:
- break;
+ default:
+ break;
+ }
}
+ in.discardReadBytes();
+ } catch (Throwable t) {
+ ResponseStatus responseStatus = t instanceof ServerException
+ ? ((ServerException) t).getErrorResponse()
+ : ResponseStatus.INTERNAL_SERVER_ERROR;
+ Log.f(ctx.channel(), t);
+ ctx.writeAndFlush(
+ MessageBuilder.createResponse(partialMsg, responseStatus));
+ ctx.close();
}
-
- in.discardReadBytes();
}
private int parseOptions(CoapMessage coapMessage, ByteBuf byteBuf,
public void write(ChannelHandlerContext ctx, Object msg,
ChannelPromise promise) {
- String log = null;
-
- if (msg instanceof CoapRequest) {
- log = composeCoapRequest(
- ctx.channel().id().asLongText().substring(26),
- (CoapRequest) msg);
- } else {
- log = composeCoapResponse(
- ctx.channel().id().asLongText().substring(26),
- (CoapResponse) msg);
- }
+ String log = getCoapLog(ctx, msg);
Log.v(log);
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
- String log = null;
+ String log = getCoapLog(ctx, msg);
+ Log.v(log);
+
+ ctx.fireChannelRead(msg);
+
+ }
+
+ private String getCoapLog(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof CoapRequest) {
- log = composeCoapRequest(
+ return composeCoapRequest(
ctx.channel().id().asLongText().substring(26),
(CoapRequest) msg);
+ } else if (msg instanceof CoapSignaling) {
+ return composeCoapSignaling(
+ ctx.channel().id().asLongText().substring(26),
+ (CoapSignaling) msg);
} else {
- log = composeCoapResponse(
+ return composeCoapResponse(
ctx.channel().id().asLongText().substring(26),
(CoapResponse) msg);
}
+ }
- Log.v(log);
+ private String composeCoapSignaling(String channelId,
+ CoapSignaling signaling) {
+ StringBuilder strBuilder = new StringBuilder();
- ctx.fireChannelRead(msg);
+ strBuilder.append(channelId);
+ strBuilder.append(" " + signaling.getTokenString());
+
+ switch (signaling.getSignalingMethod()) {
+ case CSM:
+ strBuilder.append(" 7.01 CSM");
+ strBuilder.append(" SERVER-NAME:");
+ strBuilder.append(signaling.getCsmServerName());
+ strBuilder.append(" MAX-MESSAGE-SIZE:");
+ strBuilder.append(signaling.getCsmMaxMessageSize());
+ strBuilder.append(" BLOCK-WISE-TRANSFER:");
+ strBuilder.append(signaling.getCsmBlockWiseTransfer());
+ break;
+ case PING:
+ strBuilder.append(" 7.02 PING");
+ break;
+ case PONG:
+ strBuilder.append(" 7.03 PONG");
+ break;
+ case RELEASE:
+ strBuilder.append(" 7.04 RELEASE");
+ break;
+ case ABORT:
+ strBuilder.append(" 7.05 ABORT");
+ break;
+ default:
+ break;
+ }
+
+ if (signaling.getPayloadSize() > 0) {
+ strBuilder.append(" SZ:" + signaling.getPayloadSize() + " P:"
+ + new String(signaling.getPayload(), 0,
+ signaling.getPayloadSize() > MAX_LOGLEN ? MAX_LOGLEN
+ : signaling.getPayloadSize()));
+ }
+ return strBuilder.toString();
}
private String composeCoapRequest(String channelId, CoapRequest request) {
}
if (request.getPayloadSize() > 0) {
- strBuilder.append(" SZ:" + request.getPayloadSize() + " P:"
- + new String(request.getPayload(), 0,
- request.getPayloadSize() > MAX_LOGLEN ? MAX_LOGLEN
- : request.getPayloadSize()));
+ strBuilder
+ .append(" SZ:" + request.getPayloadSize() + " P:"
+ + new String(request.getPayload(), 0,
+ request.getPayloadSize() > MAX_LOGLEN
+ ? MAX_LOGLEN
+ : request.getPayloadSize()));
}
return strBuilder.toString();
}
if (response.getPayloadSize() > 0) {
- strBuilder.append(" SZ:" + response.getPayloadSize() + " P:"
- + new String(response.getPayload(), 0,
- response.getPayloadSize() > MAX_LOGLEN ? MAX_LOGLEN
- : response.getPayloadSize()));
+ strBuilder
+ .append(" SZ:" + response.getPayloadSize() + " P:"
+ + new String(response.getPayload(), 0,
+ response.getPayloadSize() > MAX_LOGLEN
+ ? MAX_LOGLEN
+ : response.getPayloadSize()));
}
return strBuilder.toString();
import java.util.Arrays;
import java.util.List;
+import org.iotivity.cloud.base.exception.ServerException.BadOptionException;
import org.iotivity.cloud.base.protocols.Message;
import org.iotivity.cloud.base.protocols.enums.ContentFormat;
import org.iotivity.cloud.base.protocols.enums.Observe;
case 6:
mObserve = Bytes.bytesToInt(value);
break;
+ default: {
+ if (optnum % 2 == 1) {
+ throw new BadOptionException(
+ "unrecognized critical option # " + optnum
+ + " received");
+ }
+ }
}
}
*/
package org.iotivity.cloud.base.protocols.coap;
+import org.iotivity.cloud.base.exception.ServerException.MethodNotAllowedException;
import org.iotivity.cloud.base.protocols.enums.RequestMethod;
import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
+import org.iotivity.cloud.base.protocols.enums.SignalingMethod;
public class CoapRequest extends CoapMessage {
private RequestMethod mRequestMethod;
mRequestMethod = RequestMethod.DELETE;
break;
default:
- throw new IllegalArgumentException("Invalid CoapRequest code");
+ // unrecognized or unsupported Method Code MUST generate
+ // a 4.05 (Method Not Allowed) piggybacked response. (RFC7252)
+ throw new MethodNotAllowedException("Invalid CoapRequest code");
}
}
public ResponseStatus getStatus() {
return ResponseStatus.METHOD_NOT_ALLOWED;
}
+
+ // This request object does not support signaling status
+ @Override
+ public SignalingMethod getSignalingMethod() {
+ return null;
+ }
}
\ No newline at end of file
import org.iotivity.cloud.base.protocols.enums.RequestMethod;
import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
+import org.iotivity.cloud.base.protocols.enums.SignalingMethod;
public class CoapResponse extends CoapMessage {
private ResponseStatus mResponseStatus;
mResponseStatus = ResponseStatus.PROXY_NOT_SUPPORTED;
break;
default:
- throw new IllegalArgumentException("Invalid CoapResponse code");
+ // unrecognized response code is treated as being equivalent to
+ // the generic response code of 4.00 (RFC7252)
+ mResponseStatus = ResponseStatus.BAD_REQUEST;
}
}
public ResponseStatus getStatus() {
return mResponseStatus;
}
+
+ // This response object does not support signaling method
+ @Override
+ public SignalingMethod getSignalingMethod() {
+ return null;
+ }
}
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2016 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+package org.iotivity.cloud.base.protocols.coap;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.iotivity.cloud.base.exception.ServerException.BadOptionException;
+import org.iotivity.cloud.base.protocols.enums.RequestMethod;
+import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
+import org.iotivity.cloud.base.protocols.enums.SignalingMethod;
+
+public class CoapSignaling extends CoapMessage {
+ private SignalingMethod mSignalingMethod;
+
+ // Option fields (for CSM signaling)
+ protected byte[] server_name = null; // CSM option 1
+ protected byte[] max_message_size = null; // CSM option 2
+ protected boolean block_wise_tranfer = false; // CSM option 4
+
+ // Option fields (for PING signaling and PONG signaling)
+ protected boolean custody = false; // PING / PONG option 2
+
+ // Option fields (for Release signaling)
+ protected boolean bad_server_name = false; // Release option 2
+ protected byte[] alternative_address = null; // Release option 4
+ protected byte[] hold_off = null; // Release option 6
+
+ // Option fields (for Abort signaling)
+ protected byte[] bad_csm_option = null; // Abort option 2
+
+ public CoapSignaling(SignalingMethod signalingMethod) {
+ mSignalingMethod = signalingMethod;
+ }
+
+ public CoapSignaling(int code) {
+ switch (code) {
+ case 225: // code 7.01
+ mSignalingMethod = SignalingMethod.CSM;
+ break;
+ case 226: // code 7.02
+ mSignalingMethod = SignalingMethod.PING;
+ break;
+ case 227: // code 7.03
+ mSignalingMethod = SignalingMethod.PONG;
+ break;
+ case 228: // code 7.04
+ mSignalingMethod = SignalingMethod.RELEASE;
+ break;
+ case 229: // code 7.05
+ mSignalingMethod = SignalingMethod.ABORT;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported CoAP signaling code " + code);
+ }
+ }
+
+ @Override
+ public int getCode() {
+ switch (mSignalingMethod) {
+ case CSM: // code 7.01
+ return 225;
+ case PING: // code 7.02
+ return 226;
+ case PONG: // code 7.03
+ return 227;
+ case RELEASE: // code 7.04
+ return 228;
+ case ABORT: // code 7.05
+ return 229;
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ public SignalingMethod getSignalingMethod() {
+ return mSignalingMethod;
+ }
+
+ @Override
+ public void addOption(int optnum, byte[] value) {
+ switch (mSignalingMethod) {
+ case CSM: // code 7.01
+ addCsmOption(optnum, value);
+ break;
+ case PING: // code 7.02
+ case PONG: // code 7.03
+ addPingPongOption(optnum, value);
+ break;
+ case RELEASE: // code 7.04
+ addReleaseOption(optnum, value);
+ break;
+ case ABORT: // code 7.05
+ addAbortOption(optnum, value);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void addCsmOption(int optnum, byte[] value) {
+ switch (optnum) {
+ // SERVER_NAME
+ case 1:
+ server_name = value;
+ break;
+ // MAX_MESSAGE_SIZE
+ case 2:
+ max_message_size = value;
+ break;
+ // BLOCK_WISE_TRANSFER
+ case 4:
+ block_wise_tranfer = true;
+ break;
+ default: {
+ if (optnum % 2 == 1) {
+ throw new BadOptionException(
+ "unrecognized critical option # " + optnum
+ + " received");
+ }
+ }
+ }
+ }
+
+ private void addPingPongOption(int optnum, byte[] value) {
+ switch (optnum) {
+ // CUSTODY
+ case 2:
+ custody = true;
+ break;
+ default: {
+ if (optnum % 2 == 1) {
+ throw new BadOptionException(
+ "unrecognized critical option # " + optnum
+ + " received");
+ }
+ }
+ }
+ }
+
+ private void addReleaseOption(int optnum, byte[] value) {
+ switch (optnum) {
+ // BAD_SERVER_NAME
+ case 2:
+ bad_server_name = true;
+ break;
+ // ALTERNATIVE_ADDRESS
+ case 4:
+ alternative_address = value;
+ break;
+ // HOLD_OFF
+ case 6:
+ hold_off = value;
+ break;
+ default: {
+ if (optnum % 2 == 1) {
+ throw new BadOptionException(
+ "unrecognized critical option # " + optnum
+ + " received");
+ }
+ }
+ }
+ }
+
+ private void addAbortOption(int optnum, byte[] value) {
+ switch (optnum) {
+ // BAD_CSM_OPTION
+ case 2:
+ bad_csm_option = value;
+ break;
+ default: {
+ if (optnum % 2 == 1) {
+ throw new BadOptionException(
+ "unrecognized critical option # " + optnum
+ + " received");
+ }
+ }
+ }
+ }
+
+ public void setCsmServerName(String serverName) {
+ addOption(1, serverName.getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void setCsmMaxMessageSize(long maxMessageSize) {
+ ByteBuffer buf = ByteBuffer.wrap(new byte[4]);
+ max_message_size = buf.putInt(0, (int) maxMessageSize).array();
+ }
+
+ public void setCsmBlockWiseTransfer(boolean blockWiseTransferOption) {
+ block_wise_tranfer = blockWiseTransferOption;
+ }
+
+ public String getCsmServerName() {
+ if (server_name == null)
+ return "";
+ return new String(server_name, Charset.forName("UTF-8"));
+ }
+
+ public long getCsmMaxMessageSize() {
+ return unsignedIntToLong(max_message_size);
+ }
+
+ public boolean getCsmBlockWiseTransfer() {
+ return block_wise_tranfer;
+ }
+
+ @Override
+ public List<byte[]> getOption(int optnum) {
+ switch (mSignalingMethod) {
+ case CSM: // code 7.01
+ return getCsmOption(optnum);
+ case PING: // code 7.02
+ case PONG: // code 7.03
+ return getPingPongOption(optnum);
+ case RELEASE: // code 7.04
+ return getReleaseOption(optnum);
+ case ABORT: // code 7.05
+ return getAbortOption(optnum);
+ default:
+ break;
+ }
+ return null;
+ }
+
+ public List<byte[]> getCsmOption(int optnum) {
+ switch (optnum) {
+ // SERVER_NAME
+ case 1:
+ return server_name != null ? Arrays.asList(server_name) : null;
+ // MAX_MESSAGE_SIZE
+ case 2:
+ return max_message_size != null
+ ? Arrays.asList(max_message_size) : null;
+ // BLOCK_WISE_TRANSFER
+ case 4:
+ return block_wise_tranfer == true ? new ArrayList<byte[]>()
+ : null;
+ }
+ return null;
+ }
+
+ public List<byte[]> getPingPongOption(int optnum) {
+ switch (optnum) {
+ // CUSTODY
+ case 2:
+ return custody == true ? new ArrayList<byte[]>() : null;
+ }
+ return null;
+ }
+
+ public List<byte[]> getReleaseOption(int optnum) {
+ switch (optnum) {
+ // BAD_SERVER_NAME
+ case 2:
+ return bad_server_name == true ? new ArrayList<byte[]>() : null;
+ // ALTERNATIVE_ADDRESS
+ case 4:
+ return alternative_address != null
+ ? Arrays.asList(alternative_address) : null;
+ // HOLD_OFF
+ case 6:
+ return hold_off != null ? Arrays.asList(hold_off) : null;
+ }
+ return null;
+ }
+
+ public List<byte[]> getAbortOption(int optnum) {
+ switch (optnum) {
+ // BAD_CSM_OPTION
+ case 2:
+ return bad_csm_option != null ? Arrays.asList(bad_csm_option)
+ : null;
+ }
+ return null;
+ }
+
+ // This signal object does not support request status
+ @Override
+ public RequestMethod getMethod() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ // This signal object does not support response status
+ @Override
+ public ResponseStatus getStatus() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private static final long unsignedIntToLong(byte[] b) {
+ long value = 0;
+ for (byte data : b) {
+ value <<= 8;
+ value += data & 0xFF;
+ }
+ return value;
+ }
+}
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2016 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+package org.iotivity.cloud.base.protocols.enums;
+
+public enum SignalingMethod {
+ CSM, PING, PONG, RELEASE, ABORT
+}