@ -34,11 +34,19 @@ static void *_event_loop(void *arg);
static void _listen ( sock_udp_t * sock ) ;
static ssize_t _well_known_core_handler ( coap_pkt_t * pdu , uint8_t * buf , size_t len ) ;
static ssize_t _write_options ( coap_pkt_t * pdu , uint8_t * buf , size_t len ) ;
static size_t _handle_req ( coap_pkt_t * pdu , uint8_t * buf , size_t len ) ;
static size_t _handle_req ( coap_pkt_t * pdu , uint8_t * buf , size_t len ,
sock_udp_ep_t * remote ) ;
static ssize_t _finish_pdu ( coap_pkt_t * pdu , uint8_t * buf , size_t len ) ;
static void _expire_request ( gcoap_request_memo_t * memo ) ;
static void _find_req_memo ( gcoap_request_memo_t * * memo_ptr , coap_pkt_t * pdu ,
uint8_t * buf , size_t len ) ;
static void _find_resource ( coap_pkt_t * pdu , coap_resource_t * * resource_ptr ,
gcoap_listener_t * * listener_ptr ) ;
static int _find_observer ( sock_udp_ep_t * * observer , sock_udp_ep_t * remote ) ;
static int _find_obs_memo ( gcoap_observe_memo_t * * memo , sock_udp_ep_t * remote ,
coap_pkt_t * pdu ) ;
static void _find_obs_memo_resource ( gcoap_observe_memo_t * * memo ,
const coap_resource_t * resource ) ;
/* Internal variables */
const coap_resource_t _default_resources [ ] = {
@ -132,7 +140,7 @@ static void _listen(sock_udp_t *sock)
/* incoming request */
if ( coap_get_code_class ( & pdu ) = = COAP_CLASS_REQ ) {
size_t pdu_len = _handle_req ( & pdu , buf , sizeof ( buf ) ) ;
size_t pdu_len = _handle_req ( & pdu , buf , sizeof ( buf ) , & remote ) ;
if ( pdu_len > 0 ) {
sock_udp_send ( sock , buf , pdu_len , & remote ) ;
}
@ -152,8 +160,104 @@ static void _listen(sock_udp_t *sock)
* Main request handler : generates response PDU in the provided buffer .
*
* Caller must finish the PDU and send it .
*
* return length of response pdu , or < 0 if can ' t handle
*/
static size_t _handle_req ( coap_pkt_t * pdu , uint8_t * buf , size_t len ,
sock_udp_ep_t * remote )
{
coap_resource_t * resource ;
gcoap_listener_t * listener ;
sock_udp_ep_t * observer = NULL ;
gcoap_observe_memo_t * memo = NULL ;
gcoap_observe_memo_t * resource_memo = NULL ;
_find_resource ( pdu , & resource , & listener ) ;
if ( resource = = NULL ) {
return gcoap_response ( pdu , buf , len , COAP_CODE_PATH_NOT_FOUND ) ;
}
else {
/* used below to ensure a memo not already recorded for the resource */
_find_obs_memo_resource ( & resource_memo , resource ) ;
}
if ( coap_get_observe ( pdu ) = = COAP_OBS_REGISTER ) {
int empty_slot = _find_obs_memo ( & memo , remote , pdu ) ;
/* record observe memo */
if ( memo = = NULL ) {
if ( empty_slot > = 0 & & resource_memo = = NULL ) {
int obs_slot = _find_observer ( & observer , remote ) ;
/* cache new observer */
if ( observer = = NULL ) {
if ( obs_slot > = 0 ) {
observer = & _coap_state . observers [ obs_slot ] ;
memcpy ( observer , remote , sizeof ( sock_udp_ep_t ) ) ;
} else {
DEBUG ( " gcoap: can't register observer \n " ) ;
}
}
if ( observer ! = NULL ) {
memo = & _coap_state . observe_memos [ empty_slot ] ;
}
}
if ( memo = = NULL ) {
coap_clear_observe ( pdu ) ;
DEBUG ( " gcoap: can't register observe memo \n " ) ;
}
}
if ( memo ! = NULL ) {
memo - > observer = observer ;
memo - > resource = resource ;
memo - > token_len = coap_get_token_len ( pdu ) ;
if ( memo - > token_len ) {
memcpy ( & memo - > token [ 0 ] , pdu - > token , memo - > token_len ) ;
}
DEBUG ( " gcoap: Registered observer for: %s \n " , memo - > resource - > path ) ;
/* generate initial notification value */
uint32_t now = xtimer_now_usec ( ) ;
pdu - > observe_value = ( now > > GCOAP_OBS_TICK_EXPONENT ) & 0xFFFFFF ;
}
} else if ( coap_get_observe ( pdu ) = = COAP_OBS_DEREGISTER ) {
_find_obs_memo ( & memo , remote , pdu ) ;
/* clear memo, and clear observer if no other memos */
if ( memo ! = NULL ) {
DEBUG ( " gcoap: Deregistering observer for: %s \n " , memo - > resource - > path ) ;
memo - > observer = NULL ;
memo = NULL ;
_find_obs_memo ( & memo , remote , NULL ) ;
if ( memo = = NULL ) {
_find_observer ( & observer , remote ) ;
if ( observer ! = NULL ) {
observer - > family = AF_UNSPEC ;
}
}
}
coap_clear_observe ( pdu ) ;
} else if ( coap_has_observe ( pdu ) ) {
/* bogus request; don't respond */
DEBUG ( " gcoap: Observe value unexpected: % " PRIu32 " \n " , coap_get_observe ( pdu ) ) ;
return - 1 ;
}
ssize_t pdu_len = resource - > handler ( pdu , buf , len ) ;
if ( pdu_len < 0 ) {
pdu_len = gcoap_response ( pdu , buf , len ,
COAP_CODE_INTERNAL_SERVER_ERROR ) ;
}
return pdu_len ;
}
/*
* Searches listener registrations for the resource matching the path in a PDU .
*
* param [ out ] resource_ptr - - found resource
* param [ out ] listener_ptr - - listener for found resource
*/
static size_t _handle_req ( coap_pkt_t * pdu , uint8_t * buf , size_t len )
static void _find_resource ( coap_pkt_t * pdu , coap_resource_t * * resource_ptr ,
gcoap_listener_t * * listener_ptr )
{
unsigned method_flag = coap_method2flag ( coap_get_code_detail ( pdu ) ) ;
@ -178,18 +282,16 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len)
break ;
}
else {
ssize_t pdu_len = resource - > handler ( pdu , buf , len ) ;
if ( pdu_len < 0 ) {
pdu_len = gcoap_response ( pdu , buf , len ,
COAP_CODE_INTERNAL_SERVER_ERROR ) ;
}
return pdu_len ;
* resource_ptr = resource ;
* listener_ptr = listener ;
return ;
}
}
listener = listener - > next ;
}
/* resource not found */
return gcoap_response ( pdu , buf , len , COAP_CODE_PATH_NOT_FOUND ) ;
* resource_ptr = NULL ;
* listener_ptr = NULL ;
}
/*
@ -329,6 +431,22 @@ static ssize_t _write_options(coap_pkt_t *pdu, uint8_t *buf, size_t len)
uint8_t * bufpos = buf + coap_get_total_hdr_len ( pdu ) ; /* position for write */
/* Observe for notification or registration response */
if ( coap_get_code_class ( pdu ) = = COAP_CLASS_SUCCESS & & coap_has_observe ( pdu ) ) {
uint32_t nval = htonl ( pdu - > observe_value ) ;
uint8_t * nbyte = ( uint8_t * ) & nval ;
unsigned i ;
/* find address of non-zero MSB; max 3 bytes */
for ( i = 1 ; i < 4 ; i + + ) {
if ( * ( nbyte + i ) > 0 ) {
break ;
}
}
bufpos + = coap_put_option ( bufpos , last_optnum , COAP_OPT_OBSERVE ,
nbyte + i , 4 - i ) ;
last_optnum = COAP_OPT_OBSERVE ;
}
/* Uri-Path for request */
if ( coap_get_code_class ( pdu ) = = COAP_CLASS_REQ ) {
size_t url_len = strlen ( ( char * ) pdu - > url ) ;
@ -355,6 +473,108 @@ static ssize_t _write_options(coap_pkt_t *pdu, uint8_t *buf, size_t len)
return bufpos - buf ;
}
/*
* Find registered observer for a remote address and port .
*
* observer [ out ] - - Registered observer , or NULL if not found
* remote [ in ] - - Endpoint to match
*
* return Index of empty slot , suitable for registering new observer ; or - 1
* if no empty slots . Undefined if observer found .
*/
static int _find_observer ( sock_udp_ep_t * * observer , sock_udp_ep_t * remote )
{
int empty_slot = - 1 ;
* observer = NULL ;
for ( unsigned i = 0 ; i < GCOAP_OBS_CLIENTS_MAX ; i + + ) {
unsigned cmplen = 0 ;
if ( _coap_state . observers [ i ] . family = = AF_UNSPEC ) {
cmplen = 0 ;
empty_slot = i ;
}
else if ( _coap_state . observers [ i ] . family = = AF_INET6 ) {
cmplen = 16 ;
}
else {
cmplen = 4 ;
}
if ( cmplen & &
memcmp ( & _coap_state . observers [ i ] . addr . ipv6 [ 0 ] , & remote - > addr . ipv6 [ 0 ] ,
cmplen ) = = 0
& & _coap_state . observers [ i ] . port = = remote - > port ) {
* observer = & _coap_state . observers [ i ] ;
break ;
}
}
return empty_slot ;
}
/*
* Find registered observe memo for a remote address and token .
*
* memo [ out ] - - Registered observe memo , or NULL if not found
* remote [ in ] - - Endpoint for address to match
* pdu [ in ] - - PDU for token to match , or NULL to match only on remote address
*
* return Index of empty slot , suitable for registering new memo ; or - 1 if no
* empty slots . Undefined if memo found .
*/
static int _find_obs_memo ( gcoap_observe_memo_t * * memo , sock_udp_ep_t * remote ,
coap_pkt_t * pdu )
{
int empty_slot = - 1 ;
* memo = NULL ;
sock_udp_ep_t * remote_observer = NULL ;
_find_observer ( & remote_observer , remote ) ;
for ( unsigned i = 0 ; i < GCOAP_OBS_REGISTRATIONS_MAX ; i + + ) {
if ( _coap_state . observe_memos [ i ] . observer = = NULL ) {
empty_slot = i ;
continue ;
}
if ( _coap_state . observe_memos [ i ] . observer = = remote_observer ) {
if ( pdu = = NULL ) {
* memo = & _coap_state . observe_memos [ i ] ;
break ;
}
if ( _coap_state . observe_memos [ i ] . token_len = = coap_get_token_len ( pdu ) ) {
unsigned cmplen = _coap_state . observe_memos [ i ] . token_len ;
if ( cmplen & &
memcmp ( & _coap_state . observe_memos [ i ] . token [ 0 ] , & pdu - > token [ 0 ] ,
cmplen ) = = 0 ) {
* memo = & _coap_state . observe_memos [ i ] ;
break ;
}
}
}
}
return empty_slot ;
}
/*
* Find registered observe memo for a resource .
*
* memo [ out ] - - Registered observe memo , or NULL if not found
* resource [ in ] - - Resource to match
*/
static void _find_obs_memo_resource ( gcoap_observe_memo_t * * memo ,
const coap_resource_t * resource )
{
* memo = NULL ;
for ( int i = 0 ; i < GCOAP_OBS_REGISTRATIONS_MAX ; i + + ) {
if ( _coap_state . observe_memos [ i ] . observer ! = NULL
& & _coap_state . observe_memos [ i ] . resource = = resource ) {
* memo = & _coap_state . observe_memos [ i ] ;
break ;
}
}
}
/*
* gcoap interface functions
*/
@ -367,8 +587,10 @@ kernel_pid_t gcoap_init(void)
_pid = thread_create ( _msg_stack , sizeof ( _msg_stack ) , THREAD_PRIORITY_MAIN - 1 ,
THREAD_CREATE_STACKTEST , _event_loop , NULL , " coap " ) ;
/* Blank list of open request s so we know if an entry is available. */
/* Blank lists so we know if an entry is available. */
memset ( & _coap_state . open_reqs [ 0 ] , 0 , sizeof ( _coap_state . open_reqs ) ) ;
memset ( & _coap_state . observers [ 0 ] , 0 , sizeof ( _coap_state . observers ) ) ;
memset ( & _coap_state . observe_memos [ 0 ] , 0 , sizeof ( _coap_state . observe_memos ) ) ;
/* randomize initial value */
_coap_state . last_message_id = random_uint32 ( ) & 0xFFFF ;
@ -518,6 +740,55 @@ int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code)
return 0 ;
}
int gcoap_obs_init ( coap_pkt_t * pdu , uint8_t * buf , size_t len ,
const coap_resource_t * resource )
{
ssize_t hdrlen ;
gcoap_observe_memo_t * memo = NULL ;
_find_obs_memo_resource ( & memo , resource ) ;
if ( memo = = NULL ) {
/* Unique return value to specify there is not an observer */
return GCOAP_OBS_INIT_UNUSED ;
}
pdu - > hdr = ( coap_hdr_t * ) buf ;
hdrlen = coap_build_hdr ( pdu - > hdr , COAP_TYPE_NON , & memo - > token [ 0 ] ,
memo - > token_len , COAP_CODE_CONTENT ,
+ + _coap_state . last_message_id ) ;
if ( hdrlen > 0 ) {
uint32_t now = xtimer_now_usec ( ) ;
pdu - > observe_value = ( now > > GCOAP_OBS_TICK_EXPONENT ) & 0xFFFFFF ;
/* Reserve some space between the header and payload to write options later */
pdu - > payload = buf + coap_get_total_hdr_len ( pdu ) + GCOAP_OBS_OPTIONS_BUF ;
/* Payload length really zero at this point, but we set this to the available
* length in the buffer . Allows us to reconstruct buffer length later . */
pdu - > payload_len = len - ( pdu - > payload - buf ) ;
pdu - > content_type = COAP_FORMAT_NONE ;
return GCOAP_OBS_INIT_OK ;
}
else {
/* reason for negative hdrlen is not defined, so we also are vague */
return GCOAP_OBS_INIT_ERR ;
}
}
size_t gcoap_obs_send ( uint8_t * buf , size_t len , const coap_resource_t * resource )
{
gcoap_observe_memo_t * memo = NULL ;
_find_obs_memo_resource ( & memo , resource ) ;
if ( memo ) {
return sock_udp_send ( & _sock , buf , len , memo - > observer ) ;
}
else {
return 0 ;
}
}
uint8_t gcoap_op_state ( void )
{
uint8_t count = 0 ;