1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-05-04 22:17:59 +08:00
Dušan Borovčanin f10e49e6b5
MF-928 - Change CoAP lib (#1233)
* Switch CoAP lib

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Revert removed adapter code

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* WIP CoAP refactor

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add auth key

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix observers map

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix reading message body

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix subtopic parsing

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix error handling

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix multi-protocol communication

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Separate client from observer

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Remove unused config

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Remove TCP option

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Inline error check

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add logging client errors

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Replace RWMutex since we're not using RLock

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Inline error handling

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Inline error handling

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
2020-09-22 11:59:10 +02:00

131 lines
3.0 KiB
Go

package tcp
import (
"context"
"fmt"
"sync"
"sync/atomic"
"github.com/plgd-dev/go-coap/v2/message"
"github.com/plgd-dev/go-coap/v2/message/codes"
"github.com/plgd-dev/go-coap/v2/tcp/message/pool"
)
//Observation represents subscription to resource on the server
type Observation struct {
token message.Token
path string
obsSequence uint32
etag []byte
cc *ClientConn
mutex sync.Mutex
}
func NewObservationHandler(obsertionTokenHandler *HandlerContainer, next HandlerFunc) HandlerFunc {
return func(w *ResponseWriter, r *pool.Message) {
v, err := obsertionTokenHandler.Get(r.Token())
if err != nil {
next(w, r)
return
}
v(w, r)
}
}
func (o *Observation) cleanUp() {
o.cc.observationTokenHandler.Pop(o.token)
registeredRequest, ok := o.cc.observationRequests.PullOut(o.token.String())
if ok {
pool.ReleaseMessage(registeredRequest.(*pool.Message))
}
}
// Cancel remove observation from server. For recreate observation use Observe.
func (o *Observation) Cancel(ctx context.Context) error {
o.cleanUp()
req, err := NewGetRequest(ctx, o.path)
if err != nil {
return fmt.Errorf("cannot cancel observation request: %w", err)
}
defer pool.ReleaseMessage(req)
req.SetObserve(1)
req.SetToken(o.token)
return o.cc.WriteMessage(req)
}
func (o *Observation) wantBeNotified(r *pool.Message) bool {
obsSequence, err := r.Observe()
if err != nil {
return true
}
o.mutex.Lock()
defer o.mutex.Unlock()
//obs starts with 0, after that check obsSequence
if obsSequence != 0 && o.obsSequence > obsSequence {
return false
}
o.obsSequence = obsSequence
return true
}
// Observe subscribes for every change of resource on path.
func (cc *ClientConn) Observe(ctx context.Context, path string, observeFunc func(req *pool.Message), opts ...message.Option) (*Observation, error) {
req, err := NewGetRequest(ctx, path, opts...)
if err != nil {
return nil, fmt.Errorf("cannot create observe request: %w", err)
}
token := req.Token()
req.SetObserve(0)
o := &Observation{
token: token,
path: path,
obsSequence: 0,
cc: cc,
}
respCodeChan := make(chan codes.Code, 1)
waitForReponse := uint32(1)
cc.observationRequests.Store(token.String(), req)
err = o.cc.observationTokenHandler.Insert(token.String(), func(w *ResponseWriter, r *pool.Message) {
code := r.Code()
if atomic.CompareAndSwapUint32(&waitForReponse, 1, 0) {
select {
case respCodeChan <- code:
default:
}
}
if o.wantBeNotified(r) {
observeFunc(r)
}
})
defer func(err *error) {
if *err != nil {
o.cleanUp()
}
}(&err)
if err != nil {
return nil, err
}
err = cc.WriteMessage(req)
if err != nil {
return nil, err
}
select {
case <-req.Context().Done():
err = req.Context().Err()
return nil, err
case <-cc.Context().Done():
err = fmt.Errorf("connection was closed: %w", cc.Context().Err())
return nil, err
case respCode := <-respCodeChan:
if respCode != codes.Content {
err = fmt.Errorf("unexected return code(%v)", respCode)
return nil, err
}
return o, nil
}
}