/** * Returns true if the specified notification is newer than the current one. * * @param response the notification * @return true if the notification is new */ public synchronized boolean isNew(Response response) { if (!response.getOptions().hasObserve()) { // this is a final response, e.g., error or proactive cancellation return true; } // Multiple responses with different notification numbers might // arrive and be processed by different threads. We have to // ensure that only the most fresh one is being delivered. // We use the notation from the observe draft-08. long T1 = getTimestamp(); long T2 = System.currentTimeMillis(); int V1 = getCurrent(); int V2 = response.getOptions().getObserve(); int notifMaxAge = NetworkConfig.getStandard().getInt(NetworkConfigDefaults.NOTIFICATION_MAX_AGE); if (V1 < V2 && V2 - V1 < 1 << 23 || V1 > V2 && V1 - V2 > 1 << 23 || T2 > T1 + notifMaxAge) { setTimestamp(T2); number.set(V2); return true; } else { return false; } }
@Test public void testNonConfirmable() throws Exception { // send request Request req2acc = new Request(Code.POST); req2acc.setConfirmable(false); req2acc.setURI("localhost:" + serverPort + "/" + ACC_RESOURCE); req2acc.setPayload("client says hi"); req2acc.send(); // receive response and check Response response = req2acc.waitForResponse(100); assertNotNull(response); assertEquals(response.getPayloadString(), SERVER_RESPONSE); assertEquals(response.getType(), Type.NON); Request req2noacc = new Request(Code.POST); req2noacc.setConfirmable(false); req2noacc.setURI("coap://localhost:" + serverPort + "/" + NO_ACC_RESOURCE); req2noacc.setPayload("client says hi"); req2noacc.send(); // receive response and check response = req2noacc.waitForResponse(100); assertNotNull(response); assertEquals(response.getPayloadString(), SERVER_RESPONSE); assertEquals(response.getType(), Type.NON); }
@Override public void handleGET(CoapExchange exchange) { String payload = "Available commands:\n - GET: show statistics\n - POST write stats to file\n - DELETE: reset statistics\n\n"; payload += getStatString(); Response response = new Response(ResponseCode.CONTENT); response.setPayload(payload); response.getOptions().setContentFormat(MediaTypeRegistry.TEXT_PLAIN); exchange.respond(response); }
protected boolean checkResponse(Request request, Response response) { boolean success = true; success &= checkType(Type.NON, response.getType()); success &= checkInt(EXPECTED_RESPONSE_CODE.value, response.getCode().value, "code"); success &= hasContentType(response); success &= hasNonEmptyPalyoad(response); return success; }
@Override public void handlePOST(CoapExchange exchange) { String payload = exchange.getRequestText(); String[] parts = payload.split("\\?"); String[] path = parts[0].split("/"); Resource resource = create(new LinkedList<String>(Arrays.asList(path))); Response response = new Response(ResponseCode.CREATED); response.getOptions().setLocationPath(resource.getURI()); exchange.respond(response); }
@Override public void handlePOST(CoapExchange exchange) { // TODO include stopping the writing => make something for the whole // proxy // executor.shutdown(); // request.respond(CodeRegistry.RESP_DELETED, "Stopped", // MediaTypeRegistry.TEXT_PLAIN); // starting to log the stats on a new file // create the new file String logName = System.nanoTime() + CACHE_LOG_NAME; final File cacheLog = new File(logName); try { cacheLog.createNewFile(); // write the header com.google.common.io.Files.write( "hits%, avg. load, #evictions \n", cacheLog, Charset.defaultCharset()); } catch (IOException e) { } executor.scheduleWithFixedDelay( new Runnable() { @Override public void run() { CacheStats cacheStats = cacheResource.getCacheStats().minus(relativeCacheStats); String csvStats = String.format( "%.3f, %.3f, %d %n", cacheStats.hitRate(), cacheStats.averageLoadPenalty(), cacheStats.evictionCount()); try { com.google.common.io.Files.append(csvStats, cacheLog, Charset.defaultCharset()); } catch (IOException e) { } } }, 0, DEFAULT_LOGGING_DELAY, TimeUnit.SECONDS); Response response = new Response(ResponseCode.CREATED); response.setPayload("Creted log: " + logName); response.getOptions().setContentFormat(MediaTypeRegistry.TEXT_PLAIN); exchange.respond(response); }
@Override public void handlePOST(CoapExchange exchange) { Response response = new Response(CoAP.ResponseCode.CONTENT); int contentType = exchange.getRequestOptions().getContentFormat(); int acceptTypeVal = exchange.getRequestOptions().getAccept(); String acceptType = ""; switch (acceptTypeVal) { case MediaTypeRegistry.APPLICATION_EXI: break; case MediaTypeRegistry.APPLICATION_XML: acceptType = MediaType.APPLICATION_XML.getSubType(); break; case MediaTypeRegistry.APPLICATION_JSON: acceptType = MediaType.APPLICATION_JSON.getSubType(); break; } Resource02_Discovery rd = new Resource02_Discovery(); byte[] message = exchange.getRequestPayload(); InputStream isMsg = new ByteArrayInputStream(message); StringRepresentation sr = new StringRepresentation(""); try { switch (contentType) { case MediaTypeRegistry.APPLICATION_EXI: // decode first! TODO // sr = rc.registerXmlHandler(isMsg, acceptType); // byte[] exiMessage = codeSchemaLess(message); // response.setPayload(exiMessage); break; case MediaTypeRegistry.APPLICATION_JSON: sr = rd.discoveryJsonHandler(isMsg, acceptType); response.setPayload(sr.getText()); break; default: response.setPayload( "accept types supported: application/exi; application/xml; application/json"); } } catch (ResourceException ex) { Logger.getLogger(CoapR02_Discovery.class.getName()).log(Level.SEVERE, null, ex); } exchange.respond(response); }
/** * When we receive a Confirmable response, we acknowledge it and it also counts as acknowledgment * for the request. If the response is a duplicate, we stop it here and do not forward it to the * upper layer. */ @Override public void receiveResponse(Exchange exchange, Response response) { exchange.setFailedTransmissionCount(0); exchange.getCurrentRequest().setAcknowledged(true); LOGGER.finest("Cancel any retransmission"); exchange.setRetransmissionHandle(null); if (response.getType() == Type.CON && !exchange.getRequest().isCanceled()) { LOGGER.finer("Response is confirmable, send ACK"); EmptyMessage ack = EmptyMessage.newACK(response); sendEmptyMessage(exchange, ack); } if (response.isDuplicate()) { LOGGER.fine("Response is duplicate, ignore it"); } else { super.receiveResponse(exchange, response); } }
/** * Makes sure that the response type is correct. The response type for a NON can be NON or CON. * The response type for a CON should either be an ACK with a piggy-backed response or, if an * empty ACK has already be sent, a CON or NON with a separate response. */ @Override public void sendResponse(final Exchange exchange, final Response response) { LOGGER.finer("Send response, failed transmissions: " + exchange.getFailedTransmissionCount()); // If a response type is set, we do not mess around with it. // Only if none is set, we have to decide for one here. Type respType = response.getType(); if (respType == null) { Type reqType = exchange.getCurrentRequest().getType(); if (reqType == Type.CON) { if (exchange.getCurrentRequest().isAcknowledged()) { // send separate response response.setType(Type.CON); } else { exchange.getCurrentRequest().setAcknowledged(true); // send piggy-backed response response.setType(Type.ACK); response.setMID(exchange.getCurrentRequest().getMID()); } } else { // send NON response response.setType(Type.NON); } LOGGER.finest( "Switched response message type from " + respType + " to " + response.getType() + " (request was " + reqType + ")"); } else if (respType == Type.ACK || respType == Type.RST) { response.setMID(exchange.getCurrentRequest().getMID()); } if (response.getType() == Type.CON) { LOGGER.finer("Scheduling retransmission for " + response); prepareRetransmission( exchange, new RetransmissionTask(exchange, response) { public void retransmit() { sendResponse(exchange, response); } }); } super.sendResponse(exchange, response); }
@Override public void sendResponse(Exchange exchange, Response response) { if (response.getDestination() == null) throw new NullPointerException("Response has no destination address"); if (response.getDestinationPort() == 0) throw new NullPointerException("Response has no destination port"); matcher.sendResponse(exchange, response); /* * Logging here causes significant performance loss. * If necessary, add an interceptor that logs the messages, * e.g., the MessageTracer. */ for (MessageInterceptor interceptor : interceptors) interceptor.sendResponse(response); // MessageInterceptor might have canceled if (!response.isCanceled()) connector.send(serializer.serialize(response)); }
/** * This method is used to apply resource-specific knowledge on the exchange. If the request was * successful, it sets the Observe option for the response. It is important to use the * notificationOrderer of the resource here. Further down the layer, race conditions could cause * local reordering of notifications. If the response has an error code, no observe relation can * be established and if there was one previously it is canceled. When this resource allows to be * observed by clients and the request is a GET request with an observe option, the {@link * ServerMessageDeliverer} already created the relation, as it manages the observing endpoints * globally. * * @param exchange the exchange * @param response the response */ public void checkObserveRelation(Exchange exchange, Response response) { /* * If the request for the specified exchange tries to establish an observer * relation, then the ServerMessageDeliverer must have created such a relation * and added to the exchange. Otherwise, there is no such relation. * Remember that different paths might lead to this resource. */ ObserveRelation relation = exchange.getRelation(); if (relation == null) return; // because request did not try to establish a relation if (CoAP.ResponseCode.isSuccess(response.getCode())) { response.getOptions().setObserve(notificationOrderer.getCurrent()); if (!relation.isEstablished()) { relation.setEstablished(true); addObserveRelation(relation); } else if (observeType != null) { // The resource can control the message type of the notification response.setType(observeType); } } // ObserveLayer takes care of the else case }
/* * The endpoint's executor executes this method to convert the raw bytes * into a message, look for an associated exchange and forward it to * the stack of layers. */ private void receiveMessage(RawData raw) { DataParser parser = new DataParser(raw.getBytes()); if (parser.isRequest()) { // This is a request Request request; try { request = parser.parseRequest(); } catch (IllegalStateException e) { StringBuffer log = new StringBuffer("message format error caused by ") .append(raw.getInetSocketAddress()); if (!parser.isReply()) { // manually build RST from raw information EmptyMessage rst = new EmptyMessage(Type.RST); rst.setDestination(raw.getAddress()); rst.setDestinationPort(raw.getPort()); rst.setMID(parser.getMID()); for (MessageInterceptor interceptor : interceptors) interceptor.sendEmptyMessage(rst); connector.send(serializer.serialize(rst)); log.append(" and reset"); } if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info(log.toString()); } return; } request.setSource(raw.getAddress()); request.setSourcePort(raw.getPort()); request.setSenderIdentity(raw.getSenderIdentity()); /* * Logging here causes significant performance loss. * If necessary, add an interceptor that logs the messages, * e.g., the MessageTracer. */ for (MessageInterceptor interceptor : interceptors) interceptor.receiveRequest(request); // MessageInterceptor might have canceled if (!request.isCanceled()) { Exchange exchange = matcher.receiveRequest(request); if (exchange != null) { exchange.setEndpoint(CoAPEndpoint.this); coapstack.receiveRequest(exchange, request); } } } else if (parser.isResponse()) { // This is a response Response response = parser.parseResponse(); response.setSource(raw.getAddress()); response.setSourcePort(raw.getPort()); /* * Logging here causes significant performance loss. * If necessary, add an interceptor that logs the messages, * e.g., the MessageTracer. */ for (MessageInterceptor interceptor : interceptors) interceptor.receiveResponse(response); // MessageInterceptor might have canceled if (!response.isCanceled()) { Exchange exchange = matcher.receiveResponse(response); if (exchange != null) { exchange.setEndpoint(CoAPEndpoint.this); response.setRTT(System.currentTimeMillis() - exchange.getTimestamp()); coapstack.receiveResponse(exchange, response); } else if (response.getType() != Type.ACK) { LOGGER.fine("Rejecting unmatchable response from " + raw.getInetSocketAddress()); reject(response); } } } else if (parser.isEmpty()) { // This is an empty message EmptyMessage message = parser.parseEmptyMessage(); message.setSource(raw.getAddress()); message.setSourcePort(raw.getPort()); /* * Logging here causes significant performance loss. * If necessary, add an interceptor that logs the messages, * e.g., the MessageTracer. */ for (MessageInterceptor interceptor : interceptors) interceptor.receiveEmptyMessage(message); // MessageInterceptor might have canceled if (!message.isCanceled()) { // CoAP Ping if (message.getType() == Type.CON || message.getType() == Type.NON) { LOGGER.info("Responding to ping by " + raw.getInetSocketAddress()); reject(message); } else { Exchange exchange = matcher.receiveEmptyMessage(message); if (exchange != null) { exchange.setEndpoint(CoAPEndpoint.this); coapstack.receiveEmptyMessage(exchange, message); } } } } else { LOGGER.finest("Silently ignoring non-CoAP message from " + raw.getInetSocketAddress()); } }
/** * Checks if the response code is a successful code. * * @return true, if is success */ public boolean isSuccess() { return CoAP.ResponseCode.isSuccess(response.getCode()); }
@Override protected synchronized void executeRequest( Request request, String serverURI, String resourceUri) { // defensive check for slash if (!serverURI.endsWith("/") && !resourceUri.startsWith("/")) { resourceUri = "/" + resourceUri; } URI uri = null; try { uri = new URI(serverURI + resourceUri); } catch (URISyntaxException use) { throw new IllegalArgumentException("Invalid URI: " + use.getMessage()); } request.setURI(uri); // for observing int observeLoop = 2; // print request info if (verbose) { System.out.println("Request for test " + this.testName + " sent"); Utils.prettyPrint(request); } // execute the request try { Response response = null; boolean success = true; long time = 5000; request.send(); System.out.println(); System.out.println("**** TEST: " + testName + " ****"); System.out.println("**** BEGIN CHECK ****"); response = request.waitForResponse(time); if (response != null) { success &= checkType(Type.ACK, response.getType()); success &= checkInt(EXPECTED_RESPONSE_CODE.value, response.getCode().value, "code"); success &= checkToken(request.getToken(), response.getToken()); success &= hasContentType(response); success &= hasNonEmptyPalyoad(response); success &= hasObserve(response); if (success) { time = response.getOptions().getMaxAge() * 1000; System.out.println("+++++ Max-Age: " + time + " +++++"); if (time == 0) time = 5000; for (int l = 0; success && (l < observeLoop); ++l) { response = request.waitForResponse(time + 1000); // checking the response if (response != null) { System.out.println("Received notification " + l); // print response info if (verbose) { System.out.println("Response received"); System.out.println("Time elapsed (ms): " + response.getRTT()); Utils.prettyPrint(response); } success &= checkResponse(request, response); } else { System.out.println("FAIL: Notifications stopped"); success = false; break; } // response != null } // observeLoop if (response != null) { System.out.println("+++++++ Canceling +++++++"); request.cancel(); // stack should send RST Thread.sleep(time + time / 2); } else { System.out.println("FAIL: Notifications stopped"); success = false; } } } else { System.out.println("FAIL: No notification after registration"); success = false; } if (success) { System.out.println("**** TEST PASSED ****"); addSummaryEntry(testName + ": PASSED (conditionally)"); } else { System.out.println("**** TEST FAILED ****"); addSummaryEntry(testName + ": --FAILED--"); } tickOffTest(); } catch (InterruptedException e) { System.err.println("Interupted during receive: " + e.getMessage()); System.exit(-1); } }
/** * Gets the response code code. * * @return the response code */ public ResponseCode getCode() { return response.getCode(); }
/** * Gets the payload of this response as string. * * @return the response text */ public String getResponseText() { return response.getPayloadString(); }
/** * Gets the set of options of this response. * * @return the options */ public OptionSet getOptions() { return response.getOptions(); }
/** * Gets the payload of this response as byte array. * * @return the payload */ public byte[] getPayload() { return response.getPayload(); }