github.com/vicanso/pike@v1.0.1-0.20210630235453-9099e041f6ec/server/proxy_test.go (about) 1 // MIT License 2 3 // Copyright (c) 2020 Tree Xie 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 // SOFTWARE. 22 23 package server 24 25 import ( 26 "bytes" 27 "net" 28 "net/http" 29 "net/http/httptest" 30 "regexp" 31 "strconv" 32 "strings" 33 "testing" 34 "time" 35 36 "github.com/stretchr/testify/assert" 37 "github.com/vicanso/elton" 38 "github.com/vicanso/elton/middleware" 39 "github.com/vicanso/pike/cache" 40 "github.com/vicanso/pike/config" 41 "github.com/vicanso/pike/location" 42 "github.com/vicanso/pike/upstream" 43 ) 44 45 func TestGetCacheMaxAge(t *testing.T) { 46 assert := assert.New(t) 47 48 tests := []struct { 49 key string 50 value string 51 age int 52 existsAge int 53 }{ 54 // 设置了 set cookie 55 { 56 key: elton.HeaderSetCookie, 57 value: "set cookie", 58 age: 0, 59 }, 60 // 未设置cache control 61 { 62 age: 0, 63 }, 64 // 设置了cache control 为 no cache 65 { 66 key: elton.HeaderCacheControl, 67 value: "no-cache", 68 age: 0, 69 }, 70 // 设置了cache control 为 no store 71 { 72 key: elton.HeaderCacheControl, 73 value: "no-store", 74 age: 0, 75 }, 76 // 设置了cache control 为 private 77 { 78 key: elton.HeaderCacheControl, 79 value: "private, max-age=10", 80 age: 0, 81 }, 82 // 设置了max-age 83 { 84 key: elton.HeaderCacheControl, 85 value: "max-age=10", 86 age: 10, 87 }, 88 // 设置了s-maxage 89 { 90 key: elton.HeaderCacheControl, 91 value: "max-age=10, s-maxage=1 ", 92 age: 1, 93 }, 94 // 设置了age 95 { 96 key: elton.HeaderCacheControl, 97 value: "max-age=10", 98 age: 8, 99 existsAge: 2, 100 }, 101 } 102 103 for _, tt := range tests { 104 h := http.Header{} 105 h.Add(tt.key, tt.value) 106 if tt.existsAge != 0 { 107 h.Add("Age", strconv.Itoa(tt.existsAge)) 108 } 109 age := getCacheMaxAge(h) 110 assert.Equal(tt.age, age) 111 } 112 } 113 114 func TestProxyMiddleware(t *testing.T) { 115 assert := assert.New(t) 116 ln, err := net.Listen("tcp", "127.0.0.1:") 117 assert.Nil(err) 118 defer ln.Close() 119 120 cacheResp := []byte("cache response") 121 122 go func() { 123 e := elton.New() 124 e.Use(middleware.NewDefaultBodyParser()) 125 e.GET("/ping", func(c *elton.Context) error { 126 c.BodyBuffer = bytes.NewBufferString("pong") 127 return nil 128 }) 129 130 e.GET("/cache", func(c *elton.Context) error { 131 c.CacheMaxAge(time.Minute) 132 c.BodyBuffer = bytes.NewBuffer(cacheResp) 133 return nil 134 }) 135 136 e.GET("/accept-encoding", func(c *elton.Context) error { 137 c.BodyBuffer = bytes.NewBufferString(c.GetRequestHeader(elton.HeaderAcceptEncoding)) 138 return nil 139 }) 140 e.GET("/remove-304-header", func(c *elton.Context) error { 141 values := make([]string, 0) 142 for _, key := range []string{ 143 elton.HeaderIfModifiedSince, 144 elton.HeaderIfNoneMatch, 145 "X-Custom", 146 } { 147 values = append(values, c.GetRequestHeader(key)) 148 } 149 c.BodyBuffer = bytes.NewBufferString(strings.Join(values, ",")) 150 return nil 151 }) 152 153 // e.POST("/") 154 _ = e.Serve(ln) 155 }() 156 time.Sleep(50 * time.Millisecond) 157 reqHeader := http.Header{ 158 "X-Request-ID": []string{ 159 "1", 160 }, 161 } 162 respHeader := http.Header{ 163 "X-Response-ID": []string{ 164 "2", 165 }, 166 } 167 location.Reset([]config.LocationConfig{ 168 { 169 Name: "test", 170 Upstream: "test", 171 ReqHeaders: []string{ 172 "X-Request-ID:1", 173 }, 174 RespHeaders: []string{ 175 "X-Response-ID:2", 176 }, 177 Rewrites: []string{ 178 "/api/*:/$1", 179 }, 180 ProxyTimeout: "1s", 181 }, 182 }) 183 upstream.Reset([]config.UpstreamConfig{ 184 { 185 Name: "test", 186 Servers: []config.UpstreamServerConfig{ 187 { 188 Addr: "http://" + ln.Addr().String(), 189 }, 190 }, 191 AcceptEncoding: "snappy", 192 }, 193 }) 194 195 tests := []struct { 196 create func() *elton.Context 197 body string 198 age int 199 originalAcceptEncoding string 200 }{ 201 // 正常fetching,可缓存请求 202 { 203 create: func() *elton.Context { 204 req := httptest.NewRequest("GET", "/cache", nil) 205 c := elton.NewContext(httptest.NewRecorder(), req) 206 setCacheStatus(c, cache.StatusFetching) 207 return c 208 }, 209 body: string(cacheResp), 210 age: 60, 211 }, 212 // url rewrite 213 { 214 create: func() *elton.Context { 215 req := httptest.NewRequest("GET", "/api/cache", nil) 216 return elton.NewContext(httptest.NewRecorder(), req) 217 }, 218 body: string(cacheResp), 219 }, 220 // 修改accept encoding 221 { 222 create: func() *elton.Context { 223 req := httptest.NewRequest("GET", "/accept-encoding", nil) 224 req.Header.Set(elton.HeaderAcceptEncoding, "lz4") 225 return elton.NewContext(httptest.NewRecorder(), req) 226 }, 227 body: "snappy", 228 originalAcceptEncoding: "lz4", 229 }, 230 // remove 304 header 231 { 232 create: func() *elton.Context { 233 req := httptest.NewRequest("GET", "/remove-304-header", nil) 234 c := elton.NewContext(httptest.NewRecorder(), req) 235 // 设置为fetching的才会影响304的请求头 236 setCacheStatus(c, cache.StatusFetching) 237 c.SetRequestHeader(elton.HeaderIfModifiedSince, "if modified since") 238 c.SetRequestHeader(elton.HeaderIfNoneMatch, "if none match") 239 c.SetRequestHeader("X-Custom", "1") 240 return c 241 }, 242 body: ",,1", 243 }, 244 } 245 246 serverOption := ServerOption{ 247 Locations: []string{ 248 "test", 249 }, 250 Compress: "test-compress", 251 CompressMinLength: 100, 252 CompressContentTypeFilter: regexp.MustCompile(`text|json`), 253 } 254 fn := NewProxy(NewServer(serverOption)) 255 256 for _, tt := range tests { 257 c := tt.create() 258 c.Next = func() error { 259 return nil 260 } 261 err := fn(c) 262 assert.Nil(err) 263 httpResp := getHTTPResp(c) 264 for key, value := range reqHeader { 265 assert.Equal(value[0], c.GetRequestHeader(key)) 266 } 267 for key, value := range respHeader { 268 assert.Equal(value[0], httpResp.Header.Get(key)) 269 // 确认context中的header并没有设置 270 assert.Empty(c.GetHeader(key)) 271 } 272 assert.Equal(tt.originalAcceptEncoding, c.GetRequestHeader(elton.HeaderAcceptEncoding)) 273 assert.Equal(tt.body, string(httpResp.RawBody)) 274 assert.Equal(tt.age, getHTTPCacheMaxAge(c)) 275 assert.Equal(serverOption.CompressContentTypeFilter, httpResp.CompressContentTypeFilter) 276 assert.Equal(serverOption.CompressMinLength, httpResp.CompressMinLength) 277 assert.Equal(serverOption.Compress, httpResp.CompressSrv) 278 } 279 }