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

316 lines
7.2 KiB
Go

package tcp
import (
"bytes"
"context"
"fmt"
"sync"
"sync/atomic"
"github.com/plgd-dev/go-coap/v2/message"
"github.com/plgd-dev/go-coap/v2/message/codes"
coapNet "github.com/plgd-dev/go-coap/v2/net"
"github.com/plgd-dev/go-coap/v2/net/blockwise"
coapTCP "github.com/plgd-dev/go-coap/v2/tcp/message"
"github.com/plgd-dev/go-coap/v2/tcp/message/pool"
)
type EventFunc func()
type Session struct {
connection *coapNet.Conn
maxMessageSize int
peerMaxMessageSize uint32
peerBlockWiseTranferEnabled uint32
disablePeerTCPSignalMessageCSMs bool
disableTCPSignalMessageCSM bool
goPool GoPoolFunc
errors ErrorFunc
sequence uint64
tokenHandlerContainer *HandlerContainer
midHandlerContainer *HandlerContainer
handler HandlerFunc
blockwiseSZX blockwise.SZX
blockWise *blockwise.BlockWise
mutex sync.Mutex
onClose []EventFunc
cancel context.CancelFunc
ctx context.Context
errSendCSM error
}
func NewSession(
ctx context.Context,
connection *coapNet.Conn,
handler HandlerFunc,
maxMessageSize int,
goPool GoPoolFunc,
errors ErrorFunc,
blockwiseSZX blockwise.SZX,
blockWise *blockwise.BlockWise,
disablePeerTCPSignalMessageCSMs bool,
disableTCPSignalMessageCSM bool,
) *Session {
ctx, cancel := context.WithCancel(ctx)
if errors == nil {
errors = func(error) {}
}
s := &Session{
ctx: ctx,
cancel: cancel,
connection: connection,
handler: handler,
maxMessageSize: maxMessageSize,
tokenHandlerContainer: NewHandlerContainer(),
midHandlerContainer: NewHandlerContainer(),
goPool: goPool,
errors: errors,
blockWise: blockWise,
blockwiseSZX: blockwiseSZX,
disablePeerTCPSignalMessageCSMs: disablePeerTCPSignalMessageCSMs,
disableTCPSignalMessageCSM: disableTCPSignalMessageCSM,
}
if !disableTCPSignalMessageCSM {
err := s.sendCSM()
if err != nil {
s.errSendCSM = fmt.Errorf("cannot send CSM: %w", err)
}
}
return s
}
func (s *Session) Done() <-chan struct{} {
return s.ctx.Done()
}
func (s *Session) AddOnClose(f EventFunc) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.onClose = append(s.onClose, f)
}
func (s *Session) popOnClose() []EventFunc {
s.mutex.Lock()
defer s.mutex.Unlock()
tmp := s.onClose
s.onClose = nil
return tmp
}
func (s *Session) close() {
for _, f := range s.popOnClose() {
f()
}
}
func (s *Session) Close() error {
s.cancel()
return nil
}
func (s *Session) Sequence() uint64 {
return atomic.AddUint64(&s.sequence, 1)
}
func (s *Session) Context() context.Context {
return s.ctx
}
func (s *Session) PeerMaxMessageSize() uint32 {
return atomic.LoadUint32(&s.peerMaxMessageSize)
}
func (s *Session) PeerBlockWiseTransferEnabled() bool {
return atomic.LoadUint32(&s.peerBlockWiseTranferEnabled) == 1
}
func (s *Session) handleBlockwise(w *ResponseWriter, r *pool.Message) {
if s.blockWise != nil && s.PeerBlockWiseTransferEnabled() {
bwr := bwResponseWriter{
w: w,
}
s.blockWise.Handle(&bwr, r, s.blockwiseSZX, s.maxMessageSize, func(bw blockwise.ResponseWriter, br blockwise.Message) {
h, err := s.tokenHandlerContainer.Pop(r.Token())
w := bw.(*bwResponseWriter).w
r := br.(*pool.Message)
if err == nil {
h(w, r)
return
}
s.handler(w, r)
})
return
}
h, err := s.tokenHandlerContainer.Pop(r.Token())
if err == nil {
h(w, r)
return
}
s.handler(w, r)
}
func (s *Session) handleSignals(w *ResponseWriter, r *pool.Message) {
switch r.Code() {
case codes.CSM:
if s.disablePeerTCPSignalMessageCSMs {
return
}
if size, err := r.GetOptionUint32(coapTCP.MaxMessageSize); err == nil {
atomic.StoreUint32(&s.peerMaxMessageSize, size)
}
if r.HasOption(coapTCP.BlockWiseTransfer) {
atomic.StoreUint32(&s.peerBlockWiseTranferEnabled, 1)
}
return
case codes.Ping:
if r.HasOption(coapTCP.Custody) {
//TODO
}
s.sendPong(w, r)
return
case codes.Release:
if r.HasOption(coapTCP.AlternativeAddress) {
//TODO
}
return
case codes.Abort:
if r.HasOption(coapTCP.BadCSMOption) {
//TODO
}
return
}
s.handleBlockwise(w, r)
}
type bwResponseWriter struct {
w *ResponseWriter
}
func (b *bwResponseWriter) Message() blockwise.Message {
return b.w.response
}
func (b *bwResponseWriter) SetMessage(m blockwise.Message) {
pool.ReleaseMessage(b.w.response)
b.w.response = m.(*pool.Message)
}
func (s *Session) Handle(w *ResponseWriter, r *pool.Message) {
s.handleSignals(w, r)
}
func (s *Session) TokenHandler() *HandlerContainer {
return s.tokenHandlerContainer
}
func (s *Session) processBuffer(buffer *bytes.Buffer, cc *ClientConn) error {
for buffer.Len() > 0 {
var hdr coapTCP.MessageHeader
err := hdr.Unmarshal(buffer.Bytes())
if err == message.ErrShortRead {
return nil
}
if s.maxMessageSize >= 0 && hdr.TotalLen > s.maxMessageSize {
return fmt.Errorf("max message size(%v) was exceeded %v", s.maxMessageSize, hdr.TotalLen)
}
if buffer.Len() < hdr.TotalLen {
return nil
}
msgRaw := make([]byte, hdr.TotalLen)
n, err := buffer.Read(msgRaw)
if err != nil {
return fmt.Errorf("cannot read full: %w", err)
}
if n != hdr.TotalLen {
return fmt.Errorf("invalid data: %w", err)
}
req := pool.AcquireMessage(s.ctx)
_, err = req.Unmarshal(msgRaw)
if err != nil {
pool.ReleaseMessage(req)
return fmt.Errorf("cannot unmarshal with header: %w", err)
}
req.SetSequence(s.Sequence())
s.goPool(func() {
origResp := pool.AcquireMessage(s.ctx)
origResp.SetToken(req.Token())
w := NewResponseWriter(origResp, cc, req.Options())
s.Handle(w, req)
defer pool.ReleaseMessage(w.response)
if !req.IsHijacked() {
pool.ReleaseMessage(req)
}
if w.response.IsModified() {
err := s.WriteMessage(w.response)
if err != nil {
s.Close()
s.errors(fmt.Errorf("cannot write response: %w", err))
}
}
})
}
return nil
}
func (s *Session) WriteMessage(req *pool.Message) error {
data, err := req.Marshal()
if err != nil {
return fmt.Errorf("cannot marshal: %w", err)
}
return s.connection.WriteWithContext(req.Context(), data)
}
func (s *Session) sendCSM() error {
token, err := message.GetToken()
if err != nil {
return fmt.Errorf("cannot get token: %w", err)
}
req := pool.AcquireMessage(s.ctx)
defer pool.ReleaseMessage(req)
req.SetCode(codes.CSM)
req.SetToken(token)
return s.WriteMessage(req)
}
func (s *Session) sendPong(w *ResponseWriter, r *pool.Message) {
w.SetResponse(codes.Pong, message.TextPlain, nil)
}
// Run reads and process requests from a connection, until the connection is not closed.
func (s *Session) Run(cc *ClientConn) (err error) {
defer func() {
err1 := s.Close()
if err == nil {
err = err1
}
s.close()
}()
if s.errSendCSM != nil {
return s.errSendCSM
}
buffer := bytes.NewBuffer(make([]byte, 0, 1024))
readBuf := make([]byte, 1024)
for {
err = s.processBuffer(buffer, cc)
if err != nil {
return err
}
readLen, err := s.connection.ReadWithContext(s.ctx, readBuf)
if err != nil {
return err
}
if readLen > 0 {
buffer.Write(readBuf[:readLen])
}
}
}