Ejemplo n.º 1
0
smcp_status_t
resend_get_request(void* context) {
    ThingMLCOAPContext * thingml_context = (ThingMLCOAPContext*) context;
    smcp_status_t status = 0;

    status = smcp_outbound_begin(smcp_get_current_instance(),COAP_METHOD_GET, get_tt);
    require_noerr(status,bail);

    status = smcp_outbound_set_uri(thingml_context->url, 0);
    require_noerr(status,bail);

    if(request_accept_type!=COAP_CONTENT_TYPE_UNKNOWN) {
        status = smcp_outbound_add_option_uint(COAP_OPTION_ACCEPT, request_accept_type);
        require_noerr(status,bail);
    }

    status = smcp_outbound_send();

    if(status) {
        check_noerr(status);
        fprintf(stderr,
                "smcp_outbound_send() returned error %d(%s).\n",
                status,
                smcp_status_to_cstr(status));
        goto bail;
    }

bail:
    return status;
}
Ejemplo n.º 2
0
smcp_status_t
resend_async_response(void* context) {
	smcp_status_t ret = 0;
	struct smcp_async_response_s* async_response = (void*)context;

	ret = smcp_outbound_begin_async_response(COAP_RESULT_205_CONTENT,async_response);
	require_noerr(ret,bail);

	ret = smcp_outbound_add_option_uint(COAP_OPTION_CONTENT_TYPE, COAP_CONTENT_TYPE_TEXT_PLAIN);
	require_noerr(ret,bail);

	ret = smcp_outbound_set_content_formatted("This was an asynchronous response!");
	require_noerr(ret,bail);

	ret = smcp_outbound_send();
	require_noerr(ret,bail);

	if(ret) {
		assert_printf(
			"smcp_outbound_send() returned error %d(%s).\n",
			ret,
			smcp_status_to_cstr(ret)
		);
		goto bail;
	}

bail:
	return ret;
}
Ejemplo n.º 3
0
static smcp_status_t
delete_response_handler(
	int			statuscode,
	void*		context
) {
	if (statuscode >= 0) {
		char* content = (char*)smcp_inbound_get_content_ptr();
		coap_size_t content_length = smcp_inbound_get_content_len();

		if(content_length>(smcp_inbound_get_packet_length()-4)) {
			fprintf(stderr, "INTERNAL ERROR: CONTENT_LENGTH LARGER THAN PACKET_LENGTH-4! (content_length=%u, packet_length=%u)\n",content_length,smcp_inbound_get_packet_length());
			gRet = ERRORCODE_UNKNOWN;
			goto bail;
		}

		if (delete_show_headers) {
			coap_dump_header(
				stdout,
				NULL,
				smcp_inbound_get_packet(),
				smcp_inbound_get_packet_length()
			);
		}

		if(!coap_verify_packet((void*)smcp_inbound_get_packet(), smcp_inbound_get_packet_length())) {
			fprintf(stderr, "INTERNAL ERROR: CALLBACK GIVEN INVALID PACKET!\n");
			gRet = ERRORCODE_UNKNOWN;
			goto bail;
		}


		if (statuscode != COAP_RESULT_202_DELETED) {
			fprintf(stderr, "delete: Result code = %d (%s)\n", statuscode,
					(statuscode < 0) ? smcp_status_to_cstr(
					statuscode) : coap_code_to_cstr(statuscode));
		}

		if(content && content_length) {
			char contentBuffer[500];

			content_length =
				((content_length > sizeof(contentBuffer) -
					2) ? sizeof(contentBuffer) - 2 : content_length);
			memcpy(contentBuffer, content, content_length);
			contentBuffer[content_length] = 0;

			if(content_length && (contentBuffer[content_length - 1] == '\n'))
				contentBuffer[--content_length] = 0;

			printf("%s\n", contentBuffer);
		}
	}

bail:
	if (gRet == ERRORCODE_INPROGRESS) {
		gRet = 0;
	}
	return SMCP_STATUS_OK;
}
Ejemplo n.º 4
0
static void
async_response_ack_handler(int statuscode, void* context) {
	struct smcp_async_response_s* async_response = (void*)context;

	printf(" *** Finished sending async response.\n");
	printf("   * RESULT CODE = %d (%s)\n", statuscode,
		(statuscode>0)?coap_code_to_cstr(statuscode):smcp_status_to_cstr(statuscode));

	smcp_finish_async_response(async_response);
}
Ejemplo n.º 5
0
static smcp_status_t
resend_async_response(void* context) {
    smcp_status_t ret = 0;
    smcp_curl_request_t request = (smcp_curl_request_t)context;
    struct smcp_async_response_s* async_response = &request->async_response;

    coap_size_t len = (coap_size_t)request->content_len;
    if(len>512) {
        len = 512;
    }

    {
        uint16_t code;
        curl_easy_getinfo(request->curl, CURLINFO_RESPONSE_CODE,&code);
        code = http_to_coap_code(code);

        ret = smcp_outbound_begin_async_response(code,async_response);
        require_noerr(ret,bail);
    }

    {
        const char* content_type_string;
        curl_easy_getinfo(request->curl, CURLINFO_CONTENT_TYPE,&content_type_string);
        coap_content_type_t content_type = coap_content_type_from_cstr(content_type_string);
        if(content_type!=COAP_CONTENT_TYPE_UNKNOWN) {
            ret = smcp_outbound_add_option_uint(COAP_OPTION_CONTENT_TYPE, content_type);
            check_noerr(ret);
        } else {
            DEBUG_PRINTF("Unrecognised content-type: %s",content_type_string);
        }
    }

    ret = smcp_outbound_append_content(request->content, len);
    require_noerr(ret,bail);

    ret = smcp_outbound_send();
    require_noerr(ret,bail);

    if(ret) {
        assert_printf(
            "smcp_outbound_send() returned error %d(%s).\n",
            ret,
            smcp_status_to_cstr(ret)
        );
        goto bail;
    }

bail:
    return ret;
}
Ejemplo n.º 6
0
static smcp_status_t
list_response_handler(
	int			statuscode,
	void*		context
) {
//	smcp_t const self = smcp_get_current_instance();
	const char* content = smcp_inbound_get_content_ptr();
	printf(" *** GOT LIST RESPONSE!!! ***\n");
	printf("   * RESULT CODE = %d (%s)\n", statuscode,
		(statuscode>0)?coap_code_to_cstr(statuscode):smcp_status_to_cstr(statuscode));

	if(content) {
		printf("   * CONTENT = \"%s\"\n", content);
	}
	return SMCP_STATUS_OK;
}
Ejemplo n.º 7
0
static smcp_status_t
pair_response_handler(
	int			statuscode,
	void*		context
) {
//	smcp_t const self = smcp_get_current_instance();
	char* content = (char*)smcp_inbound_get_content_ptr();
	coap_size_t content_length = smcp_inbound_get_content_len();
	if((statuscode >= COAP_RESULT_100) && show_headers) {
		fprintf(stdout, "\n");
		coap_dump_header(
			stdout,
			"NULL",
			smcp_inbound_get_packet(),
			0
		);
	}
	if(((statuscode < COAP_RESULT_200) ||
	            (statuscode >= COAP_RESULT_300)) &&
	        (statuscode != SMCP_STATUS_TRANSACTION_INVALIDATED))
		fprintf(stderr, "pair: Result code = %d (%s)\n", statuscode,
			    (statuscode < 0) ? smcp_status_to_cstr(
				statuscode) : coap_code_to_cstr(statuscode));
	if(content &&
	    content_length) {
		char contentBuffer[500];

		content_length =
		    ((content_length > sizeof(contentBuffer) -
		        2) ? sizeof(contentBuffer) - 2 : content_length);
		memcpy(contentBuffer, content, content_length);
		contentBuffer[content_length] = 0;

		if(content_length && (contentBuffer[content_length - 1] == '\n'))
			contentBuffer[--content_length] = 0;

		printf("%s\n", contentBuffer);
	}

	if(gRet == ERRORCODE_INPROGRESS)
		gRet = ERRORCODE_OK;
	free(context);
	return SMCP_STATUS_OK;
}
Ejemplo n.º 8
0
void
dump_test_results(const test_data_s *test_data) {
	printf("\tURL: %s\n",test_data->url);
	if(test_data->inbound_code)
		printf("\tinbound_code: %s (%d)\n", coap_code_to_cstr(test_data->inbound_code), COAP_TO_HTTP_CODE(test_data->inbound_code));

	if(test_data->error || test_data->failed) {
		if(test_data->error)
			printf("\terror: %s (%d)\n", smcp_status_to_cstr(test_data->error), test_data->error);
	}
	{
		smcp_cms_t duration = smcp_plat_timestamp_diff(test_data->stop_time,test_data->start_time);
		printf("\tduration: %dms\n", (int)duration);

	}
	if(test_data->has_block1_option) {
		struct coap_block_info_s block_info;
		coap_decode_block(&block_info, test_data->block1_option);
		printf("\tblock1: %d/%d/%d\n", block_info.block_offset,block_info.block_m,block_info.block_size);
	}
	if(test_data->has_block2_option) {
		struct coap_block_info_s block_info;
		coap_decode_block(&block_info, test_data->block2_option);
		printf("\tblock2: %d/%d/%d\n", block_info.block_offset,block_info.block_m,block_info.block_size);
	}
	printf("\tlast_msg_id: 0x%04X\n", (int)test_data->msg_id);
	printf("\tinbound_content_len: %d\n", (int)test_data->inbound_content_len);
	if(test_data->inbound_packets!=1) {
		printf("\tinbound_packets: %d\n", test_data->inbound_packets);
	}
	if(test_data->inbound_dupe_packets!=0) {
		printf("\tinbound_dupes: %d\n", test_data->inbound_dupe_packets);
	}
	if(test_data->outbound_attempts!=1) {
		printf("\toutbound_attempts: %d\n", test_data->outbound_attempts);
	}
	if(test_data->response[0]!=0) {
		printf("\tresponse: \"%s\"\n", test_data->response);
	}
}
Ejemplo n.º 9
0
bool send_get_request(smcp_t smcp, const char* next, coap_size_t nextlen, void *thingML_context) {
    bool ret = false;
    smcp_status_t status = 0;
    int flags = SMCP_TRANSACTION_ALWAYS_INVALIDATE;
    gRet = ERRORCODE_INPROGRESS;

    retries = 0;

    if(get_observe)
        flags |= SMCP_TRANSACTION_OBSERVE;
    if(get_keep_alive)
        flags |= SMCP_TRANSACTION_KEEPALIVE;

    smcp_transaction_end(smcp,&transaction);
    smcp_transaction_init(
        &transaction,
        flags, // Flags
        (void*)&resend_get_request,
        (void*)&get_response_handler,
        thingML_context
    );

    status = smcp_transaction_begin(smcp, &transaction, get_timeout);

    if(status) {
        check(!status);
        fprintf(stderr,
                "smcp_begin_transaction_old() returned %d(%s).\n",
                status,
                smcp_status_to_cstr(status));
        goto bail;
    }

    ret = true;

bail:
    return ret;
}
Ejemplo n.º 10
0
smcp_status_t
resend_test_request(void* context) {
	test_data_s* test_data = context;
	smcp_status_t status = 0;

	status = smcp_outbound_begin(smcp_get_current_instance(),test_data->outbound_code, test_data->outbound_tt);
	require_noerr(status,bail);

	status = smcp_outbound_set_uri(test_data->url, 0);
	require_noerr(status,bail);

	if (test_data->outbound_content_type != COAP_CONTENT_TYPE_UNKNOWN) {
		status = smcp_outbound_add_option_uint(COAP_OPTION_CONTENT_TYPE, test_data->outbound_content_type);
		require_noerr(status,bail);
	}

	if (test_data->extra & EXT_BLOCK_01) {
		struct coap_block_info_s block1_info;
		uint32_t resource_length = 200;
		uint32_t block_stop;

		coap_decode_block(&block1_info, test_data->block1_option + (1<<4));

		block_stop = block1_info.block_offset+block1_info.block_size;

		if (block1_info.block_offset < resource_length) {
			if (block_stop >= resource_length) {
				test_data->block1_option &= ~(1<<3);
			} else {
				test_data->block1_option |= (1<<3);
			}

			if (test_data->has_block1_option) {
				test_data->block1_option += (1<<4);
			}

			smcp_outbound_add_option_uint(COAP_OPTION_BLOCK1, test_data->block1_option);

			if (block1_info.block_m) {
				coap_size_t max_len = 0;
				uint32_t i;
				uint32_t block_stop;
				char* content = NULL;
				content = smcp_outbound_get_content_ptr(&max_len);

				if (!content) {
					status = SMCP_STATUS_FAILURE;
					goto bail;
				}

				block_stop = block1_info.block_offset+block1_info.block_size;

				for (i = block1_info.block_offset; i < block_stop; i++) {
					if (!((i + 1) % 64)) {
						content[i-block1_info.block_offset] = '\n';
					} else {
						content[i-block1_info.block_offset] = '0'+(i%10);
					}
				}

				status = smcp_outbound_set_content_len(MIN((coap_code_t)(block_stop-block1_info.block_offset),(coap_code_t)(resource_length-block1_info.block_offset)));
				require_noerr(status,bail);
			}
		}
	}

	if(test_data->extra & EXT_BLOCK_02) {
		smcp_outbound_add_option_uint(COAP_OPTION_BLOCK2, 1);	// 32 byte block size.
	}

	status = smcp_outbound_send();

	if(status) {
		check_noerr(status);
		fprintf(stderr,
			"smcp_outbound_send() returned error %d(%s).\n",
			status,
			smcp_status_to_cstr(status));
		goto bail;
	} else {
		test_data->outbound_attempts++;
	}

bail:
	return status;
}
Ejemplo n.º 11
0
smcp_status_t
smcp_variable_node_request_handler(
	smcp_variable_node_t		node
) {
	// TODO: Make this function use less stack space!

	smcp_method_t method = smcp_inbound_get_code();
	smcp_status_t ret = SMCP_STATUS_NOT_FOUND;
	SMCP_NON_RECURSIVE coap_content_type_t content_type;
	SMCP_NON_RECURSIVE char* content_ptr;
	SMCP_NON_RECURSIVE size_t content_len;
	SMCP_NON_RECURSIVE char buffer[SMCP_VARIABLE_MAX_VALUE_LENGTH+1];
	SMCP_NON_RECURSIVE coap_content_type_t reply_content_type;
	uint8_t key_index = BAD_KEY_INDEX;
	size_t value_len;
	bool needs_prefix = true;
	char* prefix_name = "";

	content_type = smcp_inbound_get_content_type();
	content_ptr = (char*)smcp_inbound_get_content_ptr();
	content_len = smcp_inbound_get_content_len();

	reply_content_type = SMCP_CONTENT_TYPE_APPLICATION_FORM_URLENCODED;

	require(node, bail);

	// Look up the key index.
	if(smcp_inbound_peek_option(NULL,&value_len)==COAP_OPTION_URI_PATH) {
		if(!value_len) {
			needs_prefix = false;
			smcp_inbound_next_option(NULL,NULL);
		} else for(key_index=0;key_index<BAD_KEY_INDEX;key_index++) {
			ret = node->func(node,SMCP_VAR_GET_KEY,key_index,buffer);
			require_action(ret==0,bail,ret=SMCP_STATUS_NOT_FOUND);
			if(smcp_inbound_option_strequal(COAP_OPTION_URI_PATH, buffer)) {
				smcp_inbound_next_option(NULL,NULL);
				break;
			}
		}
	}

	{
		coap_option_key_t key;
		const uint8_t* value;
		while((key=smcp_inbound_next_option(&value, &value_len))!=COAP_OPTION_INVALID) {
			require_action(key!=COAP_OPTION_URI_PATH,bail,ret=SMCP_STATUS_NOT_FOUND);
			if(key==COAP_OPTION_URI_QUERY) {
				if(	method == COAP_METHOD_POST
					&& value_len>=2
					&& strhasprefix_const((const char*)value,"v=")
				) {
					DEBUG_PRINTF("variable-node: value is in the query.");
					content_type = COAP_CONTENT_TYPE_TEXT_PLAIN;
					content_ptr = (char*)value+2;
					content_len = value_len-2;
				}
//			} else if(key==COAP_OPTION_ETAG) {
//			} else if(key==COAP_OPTION_IF_MATCH) {
//			} else if(key==COAP_OPTION_IF_NONE_MATCH) {
			} else if(key==COAP_OPTION_ACCEPT) {
				reply_content_type = 0;
				if(value_len==1)
					reply_content_type = value[0];
				else {
					// Unsupported type.
				}
			} else if(COAP_OPTION_IS_CRITICAL(key)) {
				ret=SMCP_STATUS_BAD_OPTION;
				assert_printf("Unrecognized option %d, \"%s\"",
					key,
					coap_option_key_to_cstr(key, false)
				);
				goto bail;
			}
		}
	}

	// TODO: Implement me!
	if(method == COAP_METHOD_PUT)
		method = COAP_METHOD_POST;

	if(method == COAP_METHOD_POST) {
		require_action(!smcp_inbound_is_dupe(),bail,ret=0);

		require_action(
			key_index!=BAD_KEY_INDEX,
			bail,
			ret=SMCP_STATUS_NOT_ALLOWED
		);
		if(content_type==SMCP_CONTENT_TYPE_APPLICATION_FORM_URLENCODED) {
			char* key = NULL;
			char* value = NULL;
			content_len = 0;
			while(
				url_form_next_value(
					(char**)&content_ptr,
					&key,
					&value
				)
				&& key
				&& value
			) {
				if(strequal_const(key, "v")) {
					content_ptr = value;
					content_len = strlen(value);
					break;
				}
			}
		}

		// Make sure our content is zero terminated.
		((char*)content_ptr)[content_len] = 0;

		ret = node->func(node,SMCP_VAR_SET_VALUE,key_index,(char*)content_ptr);
		require_noerr(ret,bail);

		ret = smcp_outbound_begin_response(COAP_RESULT_204_CHANGED);
		require_noerr(ret,bail);

		ret = smcp_outbound_send();
		require_noerr(ret,bail);
	} else if(method == COAP_METHOD_GET) {

		if(key_index==BAD_KEY_INDEX) {
			char* content_end_ptr;

			ret = smcp_outbound_begin_response(COAP_RESULT_205_CONTENT);
			require_noerr(ret,bail);

			smcp_outbound_add_option_uint(COAP_OPTION_CONTENT_TYPE, COAP_CONTENT_TYPE_APPLICATION_LINK_FORMAT);

			ret = smcp_observable_update(&node->observable, SMCP_OBSERVABLE_BROADCAST_KEY);
			check_string(ret==0,smcp_status_to_cstr(ret));

			content_ptr = smcp_outbound_get_content_ptr(&content_len);
			content_end_ptr = content_ptr+content_len;

			for(key_index=0;key_index<BAD_KEY_INDEX;key_index++) {
				ret = node->func(node,SMCP_VAR_GET_KEY,key_index,buffer);
				if(ret) break;

				if(content_ptr+2>=content_end_ptr) {
					// No more room for content.
					break;
				}

				*content_ptr++ = '<';
				if(needs_prefix) {
					content_ptr += url_encode_cstr(content_ptr, prefix_name, (content_end_ptr-content_ptr)-1);
					content_ptr = stpncpy(content_ptr,"/",MIN(1,(content_end_ptr-content_ptr)-1));
				}
				content_ptr += url_encode_cstr(content_ptr, buffer, (content_end_ptr-content_ptr)-1);
				*content_ptr++ = '>';

				ret = node->func(node,SMCP_VAR_GET_VALUE,key_index,buffer);

				if(content_ptr+4>=content_end_ptr) {
					// No more room for content.
					break;
				}

				if(!ret) {
					strcpy(content_ptr,";v=");
					content_ptr += 3;
					content_ptr += quoted_cstr(content_ptr, buffer, (content_end_ptr-content_ptr)-1);
				}

				ret = node->func(node,SMCP_VAR_GET_LF_TITLE,key_index,buffer);

				if(content_ptr+8>=content_end_ptr) {
					// No more room for content.
					break;
				}

				if(!ret) {
					strcpy(content_ptr,";title=");
					content_ptr += 7;
					content_ptr += quoted_cstr(content_ptr, buffer, (content_end_ptr-content_ptr)-1);
				}

				// Observation flag
				if(0==node->func(node,SMCP_VAR_GET_OBSERVABLE,key_index,NULL)) {
					content_ptr = stpncpy(content_ptr,";obs",MIN(4,(content_end_ptr-content_ptr)-1));
				}

				*content_ptr++ = ',';
			}
			ret = smcp_outbound_set_content_len(content_len-(content_end_ptr-content_ptr));
			require_noerr(ret,bail);

			ret = smcp_outbound_send();
		} else {
			size_t replyContentLength = 0;
			char *replyContent;

			ret = smcp_outbound_begin_response(COAP_RESULT_205_CONTENT);
			require_noerr(ret,bail);

			if(0==node->func(node,SMCP_VAR_GET_OBSERVABLE,key_index,buffer)) {
				ret = smcp_observable_update(&node->observable, key_index);
				check_string(ret==0,smcp_status_to_cstr(ret));
			}

			if(reply_content_type == SMCP_CONTENT_TYPE_APPLICATION_FORM_URLENCODED) {
				uint32_t etag;

				if(0==node->func(node,SMCP_VAR_GET_MAX_AGE,key_index,buffer)) {
#if HAVE_STRTOL
					uint32_t max_age = strtol(buffer,NULL,0)&0xFFFFFF;
#else
					uint32_t max_age = atoi(buffer)&0xFFFFFF;
#endif
					smcp_outbound_add_option_uint(COAP_OPTION_MAX_AGE, max_age);
				}

				ret = node->func(node,SMCP_VAR_GET_VALUE,key_index,buffer);
				require_noerr(ret,bail);

				fasthash_start(0);
				fasthash_feed((const uint8_t*)buffer,strlen(buffer));
				etag = fasthash_finish_uint32();

				smcp_outbound_add_option_uint(COAP_OPTION_CONTENT_TYPE, SMCP_CONTENT_TYPE_APPLICATION_FORM_URLENCODED);

				smcp_outbound_add_option_uint(COAP_OPTION_ETAG, etag);

				replyContent = smcp_outbound_get_content_ptr(&replyContentLength);

				*replyContent++ = 'v';
				*replyContent++ = '=';
				replyContentLength -= 2;
				replyContentLength = url_encode_cstr(
					replyContent,
					buffer,
					replyContentLength
				);
				ret = smcp_outbound_set_content_len(replyContentLength+2);
			} else {
				ret = node->func(node,SMCP_VAR_GET_VALUE,key_index,buffer);
				require_noerr(ret,bail);

				ret = smcp_outbound_append_content(buffer, SMCP_CSTR_LEN);
			}

			require_noerr(ret,bail);

			ret = smcp_outbound_send();
		}
	} else {
		ret = smcp_default_request_handler(
			(void*)node
		);
		check_string(ret == SMCP_STATUS_OK, smcp_status_to_cstr(ret));
	}

bail:
	check_string(ret == SMCP_STATUS_OK, smcp_status_to_cstr(ret));
	return ret;
}
Ejemplo n.º 12
0
static smcp_status_t
get_response_handler(int statuscode, void* context) {
    ThingMLCOAPContext * thingml_context = (ThingMLCOAPContext *) context;

    const char* content = (const char*)smcp_inbound_get_content_ptr();
    coap_size_t content_length = smcp_inbound_get_content_len();

    if(statuscode>=0) {
        if(content_length>(smcp_inbound_get_packet_length()-4)) {
            fprintf(stderr, "INTERNAL ERROR: CONTENT_LENGTH LARGER THAN PACKET_LENGTH-4! (content_length=%u, packet_length=%u)\n",content_length,smcp_inbound_get_packet_length());
            gRet = ERRORCODE_UNKNOWN;
            thingml_context->fn_onerror_callback(thingml_context->thing_instance, gRet);
            goto bail;
        }

        if((statuscode >= 0) && get_show_headers) {
            if(next_len != ((coap_size_t)(-1)))
                fprintf(stdout, "\n\n");
            coap_dump_header(
                stdout,
                NULL,
                smcp_inbound_get_packet(),
                smcp_inbound_get_packet_length()
            );
        }

        if(!coap_verify_packet((void*)smcp_inbound_get_packet(), smcp_inbound_get_packet_length())) {
            fprintf(stderr, "INTERNAL ERROR: CALLBACK GIVEN INVALID PACKET!\n");
            gRet = ERRORCODE_UNKNOWN;
            thingml_context->fn_onerror_callback(thingml_context->thing_instance, gRet);
            goto bail;
        }
    }

    if(statuscode == SMCP_STATUS_TRANSACTION_INVALIDATED) {
        gRet = 0;
    }

    if(observe_ignore_first) {
        observe_ignore_first = false;
        goto bail;
    }

    if(	((statuscode < COAP_RESULT_200) ||(statuscode >= COAP_RESULT_400))
            && (statuscode != SMCP_STATUS_TRANSACTION_INVALIDATED)
            && (statuscode != HTTP_TO_COAP_CODE(HTTP_RESULT_CODE_PARTIAL_CONTENT))
      ) {
        if(get_observe && statuscode == SMCP_STATUS_TIMEOUT) {
            gRet = 0;
        } else {
            gRet = (statuscode == SMCP_STATUS_TIMEOUT)?ERRORCODE_TIMEOUT:ERRORCODE_COAP_ERROR;
            fprintf(stderr, "get: Result code = %d (%s)\n", statuscode,
                    (statuscode < 0) ? smcp_status_to_cstr(
                        statuscode) : coap_code_to_cstr(statuscode));
            thingml_context->fn_onerror_callback(thingml_context->thing_instance, gRet);
        }
    }

    if((statuscode>0) && content && content_length) {
        coap_option_key_t key;
        const uint8_t* value;
        coap_size_t value_len;
        bool last_block = true;
        int32_t observe_value = -1;

        while((key=smcp_inbound_next_option(&value, &value_len))!=COAP_OPTION_INVALID) {

            if(key == COAP_OPTION_BLOCK2) {
                last_block = !(value[value_len-1]&(1<<3));
            } else if(key == COAP_OPTION_OBSERVE) {
                if(value_len)
                    observe_value = value[0];
                else observe_value = 0;
            }

        }

        thingml_context->fn_onmsgrcv_callback(thingml_context->thing_instance, content, content_length);

        last_observe_value = observe_value;
    }

    if(observe_once) {
        gRet = 0;
        goto bail;
    }

bail:
    return SMCP_STATUS_OK;
}