mosn

Форк
0
217 строк · 6.3 Кб
1
package boltv1
2

3
import (
4
	"bytes"
5
	"context"
6
	"encoding/json"
7
	"strings"
8
	"time"
9

10
	"mosn.io/api"
11
	"mosn.io/pkg/buffer"
12

13
	"mosn.io/mosn/pkg/log"
14
	"mosn.io/mosn/pkg/protocol"
15
	"mosn.io/mosn/pkg/protocol/xprotocol/bolt"
16
)
17

18
type BoltServerConfig struct {
19
	Addr string                     `json:"address"`
20
	Mux  map[string]*ResponseConfig `json:"mux_config"`
21
}
22

23
func NewBoltServerConfig(config interface{}) (*BoltServerConfig, error) {
24
	b, err := json.Marshal(config)
25
	if err != nil {
26
		return nil, err
27
	}
28
	cfg := &BoltServerConfig{}
29
	if err := json.Unmarshal(b, cfg); err != nil {
30
		return nil, err
31
	}
32
	return cfg, nil
33
}
34

35
type ResponseConfig struct {
36
	Condition     *Condition       `json:"condition"`
37
	CommonBuilder *ResponseBuilder `json:"common_builder"`
38
	ErrorBuilder  *ResponseBuilder `json:"error_buidler"`
39
}
40

41
func (c *ResponseConfig) HandleRequest(req *bolt.Request, engine api.XProtocol) (resp api.XRespFrame, status int16) {
42
	switch req.CmdCode {
43
	case bolt.CmdCodeHeartbeat:
44
		// heartbeat
45
		resp = engine.Reply(context.TODO(), req)
46
		status = int16(bolt.ResponseStatusSuccess) // heartbeat must be success
47
	case bolt.CmdCodeRpcRequest:
48
		if c == nil {
49
			resp, status = DefaultErrorBuilder.Build(req)
50
			return
51
		}
52
		if c.Condition.Match(req) {
53
			resp, status = c.CommonBuilder.Build(req)
54
		} else {
55
			resp, status = c.ErrorBuilder.Build(req)
56
		}
57
	default:
58
		log.DefaultLogger.Errorf("invalid cmd code: %d", req.CmdCode)
59
	}
60
	return
61
}
62

63
type Condition struct {
64
	// If a request contains the expected header, matched this condition.
65
	// A request must have the configured header key and value, and can have others headers
66
	// which will be ingored.
67
	ExpectedHeader map[string]string `json:"expected_header"`
68
	// If a request contains the unexpected header, matched failed.
69
	UnexpectedHeaderKey []string `json:"unexpected_header_key"`
70
	// TODO: Add more condition
71
}
72

73
func (cond *Condition) Match(req *bolt.Request) bool {
74
	if cond == nil {
75
		return true
76
	}
77
	for key, value := range cond.ExpectedHeader {
78
		if v, ok := req.Get(key); !ok || !strings.EqualFold(v, value) {
79
			return false
80
		}
81
	}
82
	for _, key := range cond.UnexpectedHeaderKey {
83
		if _, ok := req.Get(key); ok {
84
			return false
85
		}
86
	}
87
	return true
88
}
89

90
// ResponseBuilder builds a bolt response based on the bolt request
91
type ResponseBuilder struct {
92
	// The response status code
93
	StatusCode int16 `json:"status_code"`
94
	// The response header that not contains the protocol header part.
95
	Header map[string]string `json:"header"`
96
	// The repsonse body content
97
	Body json.RawMessage `json:"body"`
98
}
99

100
func (b *ResponseBuilder) Build(req *bolt.Request) (*bolt.Response, int16) {
101
	reqId := uint32(req.GetRequestId())
102
	cmd := bolt.NewRpcResponse(reqId, uint16(b.StatusCode), protocol.CommonHeader(b.Header), nil)
103
	if len(b.Body) > 0 {
104
		cmd.Content = buffer.NewIoBufferBytes(b.Body)
105
	}
106
	return cmd, b.StatusCode
107
}
108

109
var DefaultSucessBuilder = &ResponseBuilder{
110
	StatusCode: int16(bolt.ResponseStatusSuccess),
111
}
112

113
var DefaultErrorBuilder = &ResponseBuilder{
114
	StatusCode: int16(bolt.ResponseStatusError),
115
}
116

117
type BoltClientConfig struct {
118
	TargetAddr string         `json:"target_address"`
119
	MaxConn    uint32         `json:"max_connection"`
120
	Request    *RequestConfig `json:"request_config"`
121
	Verify     *VerifyConfig  `json:"verify_config"`
122
}
123

124
func NewBoltClientConfig(config interface{}) (*BoltClientConfig, error) {
125
	b, err := json.Marshal(config)
126
	if err != nil {
127
		return nil, err
128
	}
129
	cfg := &BoltClientConfig{}
130
	if err := json.Unmarshal(b, cfg); err != nil {
131
		return nil, err
132
	}
133
	return cfg, nil
134
}
135

136
// RequestConfig decides what request to send
137
type RequestConfig struct {
138
	Header  map[string]string `json:"header"`
139
	Body    json.RawMessage   `json:"body"`
140
	Timeout time.Duration     `json:"timeout"` // request timeout
141
}
142

143
func (c *RequestConfig) BuildRequest(id uint32) (api.HeaderMap, buffer.IoBuffer) {
144
	if c == nil {
145
		return buildRequest(id, map[string]string{
146
			"service": "mosn-test-default-service", // must have service
147
		}, nil, -1)
148
	}
149
	return buildRequest(id, c.Header, c.Body, c.Timeout)
150
}
151

152
func buildRequest(id uint32, header map[string]string, body []byte, timeout time.Duration) (api.HeaderMap, buffer.IoBuffer) {
153
	var buf buffer.IoBuffer
154
	if len(body) > 0 {
155
		buf = buffer.NewIoBufferBytes(body)
156
	}
157
	req := bolt.NewRpcRequest(id, protocol.CommonHeader(header), buf)
158
	if timeout > 0 {
159
		req.Timeout = int32(int64(timeout) / 1e6)
160
	}
161
	return req, req.Content
162
}
163

164
// VerifyConfig describes what response want
165
type VerifyConfig struct {
166
	ExpectedStatusCode int16
167
	// if ExepctedHeader is nil, means do not care about header
168
	// if ExepctedHeader is exists, means resposne header should contain all the ExpectedHeader
169
	// If response header contain keys not in ExpectedHeader, we ignore it.
170
	// TODO: support regex
171
	ExpectedHeader map[string]string
172
	// if ExpectedBody is nil, means do not care about body
173
	// TODO: support regex
174
	ExpectedBody []byte
175
	// if MaxExpectedRT is zero, means do not care about rt
176
	// if MaxExpectedRT is not zero, means response's rt should no more than it
177
	MaxExpectedRT time.Duration
178
	// if MinExpectedRT is zero means do not care about it
179
	// if MinExpectedRT is not zero, means response's rt should more than it
180
	MinExpectedRT time.Duration
181
}
182

183
func (c *VerifyConfig) Verify(resp *Response) bool {
184
	if c == nil {
185
		return true
186
	}
187
	if resp.GetResponseStatus() != c.ExpectedStatusCode {
188
		log.DefaultLogger.Errorf("status is not expected: %d, %d", resp.GetResponseStatus(), c.ExpectedStatusCode)
189
		return false
190
	}
191
	for k, v := range c.ExpectedHeader {
192
		if value, ok := resp.Header.Get(k); !ok || !strings.EqualFold(v, value) {
193
			log.DefaultLogger.Errorf("header key %s is not expected, got: %s, %t, value: %s", k, v, ok, value)
194
			return false
195
		}
196
	}
197
	// if ExpectedBody is not nil, but length is zero, means expected empty content
198
	if c.ExpectedBody != nil {
199
		if !bytes.Equal(c.ExpectedBody, resp.Content.Bytes()) {
200
			log.DefaultLogger.Errorf("body is not expected: %v, %v", string(c.ExpectedBody), string(resp.Content.Bytes()))
201
			return false
202
		}
203
	}
204
	if c.MaxExpectedRT > 0 {
205
		if resp.Cost > c.MaxExpectedRT {
206
			log.DefaultLogger.Errorf("rt is %s, max expected is %s", resp.Cost, c.MaxExpectedRT)
207
			return false
208
		}
209
	}
210
	if c.MinExpectedRT > 0 {
211
		if resp.Cost < c.MinExpectedRT {
212
			log.DefaultLogger.Errorf("rt is %s, min expected is %s", resp.Cost, c.MaxExpectedRT)
213
			return false
214
		}
215
	}
216
	return true
217
}
218

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.