samples: net: sockets: coap_client: Make use of CoAP APIs for observe

The sample oversimplified the observe mechanism a bit - instead of
making use of CoAP APIs to verify the packets received (if they are
actually notifications for the observe) it blindly assumed that any
received packet was a notification. This could be misleading for
potential users of the CoAP library, as the sample had little use as
a reference for the Observer functionality.

Fix this by making use of `coap_reply` structure and a corresponding
`coap_response_received()` function.

Additionally, make the observe cancellation compliant with the CoAP
specification - it should be either an empty Reset message, or a GET
request with Observe option set to 1. The sample used invalid
construct of a Reset message with Observe option. For the purpose of
the sample, use the latter option.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
diff --git a/samples/net/sockets/coap_client/src/coap-client.c b/samples/net/sockets/coap_client/src/coap-client.c
index 79e2794..9dbaf70 100644
--- a/samples/net/sockets/coap_client/src/coap-client.c
+++ b/samples/net/sockets/coap_client/src/coap-client.c
@@ -388,7 +388,7 @@
 	return 0;
 }
 
-static int send_obs_reply_ack(uint16_t id)
+static void send_obs_reply_ack(uint16_t id)
 {
 	struct coap_packet request;
 	uint8_t *data;
@@ -396,7 +396,7 @@
 
 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
 	if (!data) {
-		return -ENOMEM;
+		return;
 	}
 
 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
@@ -406,21 +406,42 @@
 		goto end;
 	}
 
-	net_hexdump("Request", request.data, request.offset);
+	net_hexdump("ACK", request.data, request.offset);
 
 	r = send(sock, request.data, request.offset, 0);
+	if (r < 0) {
+		LOG_ERR("Failed to send CoAP ACK");
+	}
 end:
 	k_free(data);
-
-	return r;
 }
 
-static int process_obs_coap_reply(void)
+
+static int obs_notification_cb(const struct coap_packet *response,
+			       struct coap_reply *reply,
+			       const struct sockaddr *from)
 {
-	struct coap_packet reply;
-	uint16_t id;
+	uint16_t id = coap_header_get_id(response);
+	uint8_t type = coap_header_get_type(response);
+	uint8_t *counter = (uint8_t *)reply->user_data;
+
+	ARG_UNUSED(from);
+
+	printk("\nCoAP OBS Notification\n");
+
+	(*counter)++;
+
+	if (type == COAP_TYPE_CON) {
+		send_obs_reply_ack(id);
+	}
+
+	return 0;
+}
+
+static int process_obs_coap_reply(struct coap_reply *reply)
+{
+	struct coap_packet reply_msg;
 	uint8_t *data;
-	uint8_t type;
 	int rcvd;
 	int ret;
 
@@ -449,27 +470,23 @@
 
 	net_hexdump("Response", data, rcvd);
 
-	ret = coap_packet_parse(&reply, data, rcvd, NULL, 0);
+	ret = coap_packet_parse(&reply_msg, data, rcvd, NULL, 0);
 	if (ret < 0) {
 		LOG_ERR("Invalid data received");
 		goto end;
 	}
 
-	id = coap_header_get_id(&reply);
-
-	type = coap_header_get_type(&reply);
-	if (type == COAP_TYPE_ACK) {
-		ret = 0;
-	} else if (type == COAP_TYPE_CON) {
-		ret = send_obs_reply_ack(id);
+	if (coap_response_received(&reply_msg, NULL, reply, 1) == NULL) {
+		printk("\nOther response received\n");
 	}
+
 end:
 	k_free(data);
 
 	return ret;
 }
 
-static int send_obs_coap_request(void)
+static int send_obs_coap_request(struct coap_reply *reply, void *user_data)
 {
 	struct coap_packet request;
 	const char * const *p;
@@ -507,6 +524,10 @@
 
 	net_hexdump("Request", request.data, request.offset);
 
+	coap_reply_init(reply, &request);
+	reply->reply = obs_notification_cb;
+	reply->user_data = user_data;
+
 	r = send(sock, request.data, request.offset, 0);
 
 end:
@@ -515,7 +536,7 @@
 	return r;
 }
 
-static int send_obs_reset_coap_request(void)
+static int send_obs_reset_coap_request(struct coap_reply *reply)
 {
 	struct coap_packet request;
 	const char * const *p;
@@ -528,15 +549,15 @@
 	}
 
 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
-			     COAP_VERSION_1, COAP_TYPE_RESET,
-			     COAP_TOKEN_MAX_LEN, coap_next_token(),
-			     0, coap_next_id());
+			     COAP_VERSION_1, COAP_TYPE_CON,
+			     reply->tkl, reply->token,
+			     COAP_METHOD_GET, coap_next_id());
 	if (r < 0) {
 		LOG_ERR("Failed to init CoAP message");
 		goto end;
 	}
 
-	r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 0);
+	r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 1);
 	if (r < 0) {
 		LOG_ERR("Failed to append Observe option");
 		goto end;
@@ -563,36 +584,40 @@
 
 static int register_observer(void)
 {
-	uint8_t counter = 0U;
+	struct coap_reply reply;
+	uint8_t counter = 0;
 	int r;
 
-	while (1) {
-		/* Test CoAP OBS GET method */
-		if (!counter) {
-			printk("\nCoAP client OBS GET\n");
-			r = send_obs_coap_request();
-			if (r < 0) {
-				return r;
-			}
-		} else {
-			printk("\nCoAP OBS Notification\n");
-		}
+	printk("\nCoAP client OBS GET\n");
+	r = send_obs_coap_request(&reply, &counter);
+	if (r < 0) {
+		return r;
+	}
 
-		r = process_obs_coap_reply();
+	while (1) {
+		r = process_obs_coap_reply(&reply);
 		if (r < 0) {
 			return r;
 		}
 
-		counter++;
-
-		/* Unregister */
-		if (counter == 5U) {
+		if (counter >= 5) {
 			/* TODO: Functionality can be verified byt waiting for
 			 * some time and make sure client shouldn't receive
 			 * any notifications. If client still receives
 			 * notifications means, Observer is not removed.
 			 */
-			return send_obs_reset_coap_request();
+			r = send_obs_reset_coap_request(&reply);
+			if (r < 0) {
+				return r;
+			}
+
+			/* Wait for the final ACK */
+			r = process_obs_coap_reply(&reply);
+			if (r < 0) {
+				return r;
+			}
+
+			break;
 		}
 	}