mosn

Форк
0
/
config.go 
248 строк · 6.8 Кб
1
package http
2

3
import (
4
	"bytes"
5
	"context"
6
	"encoding/json"
7
	"net/http"
8
	"reflect"
9
	"strings"
10
	"time"
11

12
	"github.com/valyala/fasthttp"
13
	"mosn.io/api"
14
	"mosn.io/pkg/buffer"
15
	"mosn.io/pkg/variable"
16

17
	"mosn.io/mosn/pkg/log"
18
	mosnhttp "mosn.io/mosn/pkg/protocol/http"
19
	"mosn.io/mosn/pkg/types"
20
	"mosn.io/mosn/test/lib/utils"
21
)
22

23
type HttpServerConfig struct {
24
	Addr    string                    `json:"address"`
25
	Configs map[string]*ResonseConfig `json:"response_configs"`
26
}
27

28
func NewHttpServerConfig(config interface{}) (*HttpServerConfig, error) {
29
	b, err := json.Marshal(config)
30
	if err != nil {
31
		return nil, err
32
	}
33
	cfg := &HttpServerConfig{}
34
	if err := json.Unmarshal(b, cfg); err != nil {
35
		return nil, err
36
	}
37
	return cfg, nil
38
}
39

40
// ResponseConfig decides what response to send.
41
// If a request matches the Condition, send a common repsonse.
42
// If not, send an error response
43
type ResonseConfig struct {
44
	Condition     *Condition       `json:"condition"`
45
	CommonBuilder *ResponseBuilder `json:"common_builder"`
46
	ErrorBuilder  *ResponseBuilder `json:"error_buidler"`
47
}
48

49
func (cfg *ResonseConfig) ServeHTTP(w http.ResponseWriter, r *http.Request) {
50
	if cfg.Condition.Match(r) {
51
		cfg.CommonBuilder.Build(w)
52
	} else {
53
		cfg.ErrorBuilder.Build(w)
54
	}
55
}
56

57
type Condition struct {
58
	// If a request contains the expected header, matched this condition.
59
	// A request must have the configured header key and value, and can have others headers
60
	// which will be ingored.
61
	ExpectedHeader map[string][]string `json:"expected_header"`
62
	// If a request contains the unexpected header, matched failed.
63
	UnexpectedHeaderKey []string `json"unexpected_headerkey"`
64
	// If a request's method is in the expected method, matched this condition.
65
	ExpectedMethod []string `json:"expected_method"`
66
	// TODO: Add more condition
67
}
68

69
func (c *Condition) Match(r *http.Request) bool {
70
	// empty condition always matched
71
	if c == nil {
72
		return true
73
	}
74
	// verify method if configured
75
	if len(c.ExpectedMethod) > 0 {
76
		if !utils.StringIn(r.Method, c.ExpectedMethod) {
77
			return false
78
		}
79
	}
80
	// verify header
81
	header := r.Header
82
	if len(header) < len(c.ExpectedHeader) {
83
		return false
84
	}
85
	for key, value := range c.ExpectedHeader {
86
		// needs to verify the string slice sequence
87
		key = strings.Title(key)
88
		if !reflect.DeepEqual(header[key], value) {
89
			return false
90
		}
91
	}
92
	for _, key := range c.UnexpectedHeaderKey {
93
		if _, ok := header[key]; ok {
94
			return false
95
		}
96
	}
97
	return true
98
}
99

100
type ResponseBuilder struct {
101
	// The response status code
102
	StatusCode int `json:"status_code"`
103
	// The response header
104
	Header map[string][]string `json:"header"`
105
	// The repsonse body content
106
	Body string `json:"body"`
107
}
108

109
func (b *ResponseBuilder) Build(w http.ResponseWriter) (int, error) {
110
	// empty builder always try to write success
111
	if b == nil {
112
		return w.Write([]byte("mosn mock http server response"))
113
	}
114
	for k, vs := range b.Header {
115
		for _, v := range vs {
116
			w.Header().Add(k, v)
117
		}
118
	}
119
	// WriteHeader should be called after Header.Set/Add
120
	w.WriteHeader(b.StatusCode)
121
	return w.Write([]byte(b.Body))
122
}
123

124
type HttpClientConfig struct {
125
	TargetAddr   string         `json:"target_address"`
126
	ProtocolName string         `json:"protocol_name"`
127
	MaxConn      uint32         `json:"max_connection"`
128
	Request      *RequestConfig `json:"request_config"`
129
	Verify       *VerifyConfig  `json:"verify_config"`
130
}
131

132
func NewHttpClientConfig(config interface{}) (*HttpClientConfig, error) {
133
	b, err := json.Marshal(config)
134
	if err != nil {
135
		return nil, err
136
	}
137
	cfg := &HttpClientConfig{}
138
	if err := json.Unmarshal(b, cfg); err != nil {
139
		return nil, err
140
	}
141
	return cfg, nil
142

143
}
144

145
// RequestConfig decides what request to send
146
type RequestConfig struct {
147
	Method  string              `json:"method"`
148
	Path    string              `json:"path"`
149
	Header  map[string][]string `json:"header"`
150
	Body    json.RawMessage     `json:"body"`
151
	Timeout time.Duration       `json:"timeout"` // request timeout
152
}
153

154
func (c *RequestConfig) BuildRequest(ctx context.Context) (api.HeaderMap, buffer.IoBuffer) {
155
	if c == nil {
156
		return buildRequest(ctx, "GET", "/", nil, nil)
157
	}
158
	if c.Method == "" {
159
		c.Method = "GET"
160
	}
161
	if c.Path == "" {
162
		c.Path = "/"
163
	}
164
	return buildRequest(ctx, c.Method, c.Path, c.Header, c.Body)
165
}
166

167
func buildRequest(ctx context.Context, method string, path string, header map[string][]string, body []byte) (api.HeaderMap, buffer.IoBuffer) {
168
	fh := &fasthttp.RequestHeader{}
169
	// to simulate pkg/stream/http/stream.go injectInternalHeaders
170
	// headers will be setted in pkg/stream/http/stream.go
171
	variable.SetString(ctx, types.VarMethod, method)
172
	variable.SetString(ctx, types.VarPath, path)
173
	h := mosnhttp.RequestHeader{
174
		RequestHeader: fh,
175
	}
176
	for k, vs := range header {
177
		for _, v := range vs {
178
			h.Add(k, v)
179
		}
180
	}
181
	if len(body) > 0 {
182
		buf := buffer.NewIoBufferBytes(body)
183
		return h, buf
184
	}
185
	return h, nil
186

187
}
188

189
// VerifyConfig describes what response want
190
type VerifyConfig struct {
191
	ExpectedStatusCode int
192
	// if ExepctedHeader is nil, means do not care about header
193
	// if ExepctedHeader is exists, means resposne header should contain all the ExpectedHeader
194
	// If response header contain keys not in ExpectedHeader, we ignore it.
195
	// TODO: support regex
196
	ExpectedHeader map[string][]string
197
	// if ExpectedBody is nil, means do not care about body
198
	// TODO: support regex
199
	ExpectedBody []byte
200
	// if MaxExpectedRT is zero, means do not care about rt
201
	// if MaxExpectedRT is not zero, means response's rt should no more than it
202
	MaxExpectedRT time.Duration
203
	// if MinExpectedRT is zero means do not care about it
204
	// if MinExpectedRT is not zero, means response's rt should more than it
205
	MinExpectedRT time.Duration
206
}
207

208
func (c *VerifyConfig) Verify(resp *Response) bool {
209
	if c == nil {
210
		return true
211
	}
212
	if resp.StatusCode != c.ExpectedStatusCode {
213
		log.DefaultLogger.Errorf("status is not expected: %d, %d", resp.StatusCode, c.ExpectedStatusCode)
214
		return false
215
	}
216
	for k, vs := range c.ExpectedHeader {
217
		k = strings.Title(k)
218
		values, ok := resp.Header[k]
219
		if !ok {
220
			log.DefaultLogger.Errorf("header key %s is not expected, got: %v", k, ok)
221
			return false
222
		}
223
		if !utils.SliceEqual(vs, values) {
224
			log.DefaultLogger.Errorf("header key %s is not expected, want: %v, got: %v", k, vs, values)
225
			return false
226
		}
227
	}
228
	//  if ExpectedBody is not nil, but length is zero, means expected empty content
229
	if c.ExpectedBody != nil {
230
		if !bytes.Equal(c.ExpectedBody, resp.Body) {
231
			log.DefaultLogger.Errorf("body is not expected: %v, %v", c.ExpectedBody, resp.Body)
232
			return false
233
		}
234
	}
235
	if c.MaxExpectedRT > 0 {
236
		if resp.Cost > c.MaxExpectedRT {
237
			log.DefaultLogger.Errorf("rt is %s, max expected is %s", resp.Cost, c.MaxExpectedRT)
238
			return false
239
		}
240
	}
241
	if c.MinExpectedRT > 0 {
242
		if resp.Cost > c.MinExpectedRT {
243
			log.DefaultLogger.Errorf("rt is %s, min expected is %s", resp.Cost, c.MaxExpectedRT)
244
			return false
245
		}
246
	}
247
	return true
248
}
249

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

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

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

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