mirror of
https://github.com/mainflux/mainflux.git
synced 2025-05-04 22:17:59 +08:00

* 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>
131 lines
3.0 KiB
Go
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
|
|
}
|
|
}
|