~fnux/yggdrasil-go-coap

ref: 614f652b70b2 yggdrasil-go-coap/responsewriter.go -rw-r--r-- 4.4 KiB
614f652bTimothée Floure Add syntax highlighting to code snippets in README 1 year, 4 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package coap

import (
	"context"
)

// A ResponseWriter interface is used by an CAOP handler to construct an COAP response.
// For Obsevation (GET+option observe) it can be stored and used in another go-routine
// with using calls NewResponse, WriteContextMsg
type ResponseWriter interface {
	Write(p []byte) (n int, err error)
	// WriteContext response with payload.
	// If p is nil it writes response without payload.
	// If p is non-nil then SetContentFormat must be called before Write otherwise Write fails.
	WriteWithContext(ctx context.Context, p []byte) (n int, err error)
	// SetCode for response that is send via Write call.
	//
	// If SetCode is not called explicitly, the first call to Write
	// will trigger an implicit SetCode(Content/Changed/Deleted/Created - depends on request).
	// Thus explicit calls to SetCode are mainly used to send error codes.
	SetCode(code COAPCode)
	// SetContentFormat of payload for response that is send via Write call.
	//
	// If SetContentFormat is not called and Write is called with non-nil argumet
	// If SetContentFormat is set but Write is called with nil argument it fails
	SetContentFormat(contentFormat MediaType)

	//NewResponse create response with code and token, messageid against request
	NewResponse(code COAPCode) Message

	WriteMsg(msg Message) error
	//WriteContextMsg to client.
	//If Option ContentFormat is set and Payload is not set then call will failed.
	//If Option ContentFormat is not set and Payload is set then call will failed.
	WriteMsgWithContext(ctx context.Context, msg Message) error

	getCode() *COAPCode
	getReq() *Request
	getContentFormat() *MediaType
}

type responseWriter struct {
	req           *Request
	code          *COAPCode
	contentFormat *MediaType
}

func responseWriterFromRequest(r *Request) ResponseWriter {
	w := ResponseWriter(&responseWriter{req: r})
	switch {
	case r.Msg.Code() == GET:
		switch {
		// set blockwise notice writer for observe
		case r.Client.networkSession().blockWiseEnabled() && r.Msg.Option(Observe) != nil:
			w = &blockWiseNoticeWriter{responseWriter: w}
		// set blockwise if it is enabled
		case r.Client.networkSession().blockWiseEnabled():
			w = &blockWiseResponseWriter{responseWriter: w}
		}
		w = &getResponseWriter{w}
	case r.Client.networkSession().blockWiseEnabled():
		w = &blockWiseResponseWriter{responseWriter: w}
	}
	if r.Msg.Option(NoResponse) != nil {
		w = newNoResponseWriter(w)
	}

	return w
}

// NewResponse creates reponse for request
func (r *responseWriter) NewResponse(code COAPCode) Message {
	typ := NonConfirmable
	if r.req.Msg.Type() == Confirmable {
		typ = Acknowledgement
	}
	resp := r.req.Client.NewMessage(MessageParams{
		Type:      typ,
		Code:      code,
		MessageID: r.req.Msg.MessageID(),
		Token:     r.req.Msg.Token(),
	})
	return resp
}

// Write send response without to peer
func (r *responseWriter) WriteMsg(msg Message) error {
	return r.WriteMsgWithContext(context.Background(), msg)
}

// Write send response with context to peer
func (r *responseWriter) WriteMsgWithContext(ctx context.Context, msg Message) error {
	switch msg.Code() {
	case GET, POST, PUT, DELETE:
		return ErrInvalidReponseCode
	}
	return r.req.Client.WriteMsgWithContext(ctx, msg)
}

func prepareReponse(w ResponseWriter, reqCode COAPCode, code *COAPCode, contentFormat *MediaType, payload []byte) (int, Message) {
	respCode := Content
	if code != nil {
		respCode = *code
	} else {
		switch reqCode {
		case POST:
			respCode = Changed
		case PUT:
			respCode = Created
		case DELETE:
			respCode = Deleted
		}
	}
	resp := w.NewResponse(respCode)
	if contentFormat != nil {
		resp.SetOption(ContentFormat, *contentFormat)
	}
	var l int
	if payload != nil {
		resp.SetPayload(payload)
		l = len(payload)
	}
	return l, resp
}

func (r *responseWriter) Write(p []byte) (n int, err error) {
	return r.WriteWithContext(context.Background(), p)
}

// Write send response to peer
func (r *responseWriter) WriteWithContext(ctx context.Context, p []byte) (n int, err error) {
	l, resp := prepareReponse(r, r.req.Msg.Code(), r.code, r.contentFormat, p)
	err = r.WriteMsgWithContext(ctx, resp)
	return l, err
}

func (r *responseWriter) SetCode(code COAPCode) {
	r.code = &code
}

func (r *responseWriter) SetContentFormat(contentFormat MediaType) {
	r.contentFormat = &contentFormat
}

func (r *responseWriter) getCode() *COAPCode {
	return r.code
}

func (r *responseWriter) getReq() *Request {
	return r.req
}

func (r *responseWriter) getContentFormat() *MediaType {
	return r.contentFormat
}