github.com/vnforks/kid@v5.11.1+incompatible/services/imageproxy/local_test.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package imageproxy 5 6 import ( 7 "io/ioutil" 8 "net/http" 9 "net/http/httptest" 10 "testing" 11 "time" 12 13 "github.com/mattermost/mattermost-server/model" 14 "github.com/mattermost/mattermost-server/services/httpservice" 15 "github.com/mattermost/mattermost-server/utils/testutils" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func makeTestLocalProxy() *ImageProxy { 21 configService := &testutils.StaticConfigService{ 22 Cfg: &model.Config{ 23 ServiceSettings: model.ServiceSettings{ 24 SiteURL: model.NewString("https://mattermost.example.com"), 25 AllowedUntrustedInternalConnections: model.NewString("127.0.0.1"), 26 }, 27 ImageProxySettings: model.ImageProxySettings{ 28 Enable: model.NewBool(true), 29 ImageProxyType: model.NewString(model.IMAGE_PROXY_TYPE_LOCAL), 30 }, 31 }, 32 } 33 34 return MakeImageProxy(configService, httpservice.MakeHTTPService(configService)) 35 } 36 37 func TestLocalBackend_GetImage(t *testing.T) { 38 t.Run("image", func(t *testing.T) { 39 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 40 w.Header().Set("Cache-Control", "max-age=2592000, private") 41 w.Header().Set("Content-Type", "image/png") 42 w.Header().Set("Content-Length", "10") 43 44 w.WriteHeader(http.StatusOK) 45 w.Write([]byte("1111111111")) 46 }) 47 48 mock := httptest.NewServer(handler) 49 defer mock.Close() 50 51 proxy := makeTestLocalProxy() 52 53 recorder := httptest.NewRecorder() 54 request, _ := http.NewRequest(http.MethodGet, "", nil) 55 proxy.GetImage(recorder, request, mock.URL+"/image.png") 56 resp := recorder.Result() 57 58 require.Equal(t, http.StatusOK, resp.StatusCode) 59 assert.Equal(t, "max-age=2592000, private", resp.Header.Get("Cache-Control")) 60 assert.Equal(t, "10", resp.Header.Get("Content-Length")) 61 62 respBody, _ := ioutil.ReadAll(resp.Body) 63 assert.Equal(t, []byte("1111111111"), respBody) 64 }) 65 66 t.Run("not an image", func(t *testing.T) { 67 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 w.WriteHeader(http.StatusNotAcceptable) 69 }) 70 71 mock := httptest.NewServer(handler) 72 defer mock.Close() 73 74 proxy := makeTestLocalProxy() 75 76 recorder := httptest.NewRecorder() 77 request, _ := http.NewRequest(http.MethodGet, "", nil) 78 proxy.GetImage(recorder, request, mock.URL+"/file.pdf") 79 resp := recorder.Result() 80 81 require.Equal(t, http.StatusNotAcceptable, resp.StatusCode) 82 }) 83 84 t.Run("not an image, but remote server ignores accept header", func(t *testing.T) { 85 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 86 w.Header().Set("Cache-Control", "max-age=2592000, private") 87 w.Header().Set("Content-Type", "application/pdf") 88 w.Header().Set("Content-Length", "10") 89 90 w.WriteHeader(http.StatusOK) 91 w.Write([]byte("1111111111")) 92 }) 93 94 mock := httptest.NewServer(handler) 95 defer mock.Close() 96 97 proxy := makeTestLocalProxy() 98 99 recorder := httptest.NewRecorder() 100 request, _ := http.NewRequest(http.MethodGet, "", nil) 101 proxy.GetImage(recorder, request, mock.URL+"/file.pdf") 102 resp := recorder.Result() 103 104 require.Equal(t, http.StatusForbidden, resp.StatusCode) 105 }) 106 107 t.Run("not found", func(t *testing.T) { 108 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 109 w.WriteHeader(http.StatusNotFound) 110 }) 111 112 mock := httptest.NewServer(handler) 113 defer mock.Close() 114 115 proxy := makeTestLocalProxy() 116 117 recorder := httptest.NewRecorder() 118 request, _ := http.NewRequest(http.MethodGet, "", nil) 119 proxy.GetImage(recorder, request, mock.URL+"/image.png") 120 resp := recorder.Result() 121 122 require.Equal(t, http.StatusNotFound, resp.StatusCode) 123 }) 124 125 t.Run("other server error", func(t *testing.T) { 126 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 127 w.WriteHeader(http.StatusInternalServerError) 128 }) 129 130 mock := httptest.NewServer(handler) 131 defer mock.Close() 132 133 proxy := makeTestLocalProxy() 134 135 recorder := httptest.NewRecorder() 136 request, _ := http.NewRequest(http.MethodGet, "", nil) 137 proxy.GetImage(recorder, request, mock.URL+"/image.png") 138 resp := recorder.Result() 139 140 require.Equal(t, http.StatusInternalServerError, resp.StatusCode) 141 }) 142 143 t.Run("timeout", func(t *testing.T) { 144 wait := make(chan bool, 1) 145 146 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 147 <-wait 148 }) 149 150 mock := httptest.NewServer(handler) 151 defer mock.Close() 152 153 proxy := makeTestLocalProxy() 154 155 // Modify the timeout to be much shorter than the default 30 seconds 156 proxy.backend.(*LocalBackend).impl.Timeout = time.Millisecond 157 158 recorder := httptest.NewRecorder() 159 request, _ := http.NewRequest(http.MethodGet, "", nil) 160 proxy.GetImage(recorder, request, mock.URL+"/image.png") 161 resp := recorder.Result() 162 163 require.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) 164 165 wait <- true 166 }) 167 } 168 169 func TestLocalBackend_GetImageDirect(t *testing.T) { 170 t.Run("image", func(t *testing.T) { 171 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 172 w.Header().Set("Cache-Control", "max-age=2592000, private") 173 w.Header().Set("Content-Type", "image/png") 174 w.Header().Set("Content-Length", "10") 175 176 w.WriteHeader(http.StatusOK) 177 w.Write([]byte("1111111111")) 178 }) 179 180 mock := httptest.NewServer(handler) 181 defer mock.Close() 182 183 proxy := makeTestLocalProxy() 184 185 body, contentType, err := proxy.GetImageDirect(mock.URL + "/image.png") 186 187 assert.Nil(t, err) 188 assert.Equal(t, "image/png", contentType) 189 190 respBody, _ := ioutil.ReadAll(body) 191 assert.Equal(t, []byte("1111111111"), respBody) 192 }) 193 194 t.Run("not an image", func(t *testing.T) { 195 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 196 w.WriteHeader(http.StatusNotAcceptable) 197 }) 198 199 mock := httptest.NewServer(handler) 200 defer mock.Close() 201 202 proxy := makeTestLocalProxy() 203 204 body, contentType, err := proxy.GetImageDirect(mock.URL + "/file.pdf") 205 206 assert.NotNil(t, err) 207 assert.Equal(t, "", contentType) 208 assert.Equal(t, ErrLocalRequestFailed, err) 209 assert.Nil(t, body) 210 }) 211 212 t.Run("not an image, but remote server ignores accept header", func(t *testing.T) { 213 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 214 w.Header().Set("Cache-Control", "max-age=2592000, private") 215 w.Header().Set("Content-Type", "application/pdf") 216 w.Header().Set("Content-Length", "10") 217 218 w.WriteHeader(http.StatusOK) 219 w.Write([]byte("1111111111")) 220 }) 221 222 mock := httptest.NewServer(handler) 223 defer mock.Close() 224 225 proxy := makeTestLocalProxy() 226 227 body, contentType, err := proxy.GetImageDirect(mock.URL + "/file.pdf") 228 229 assert.NotNil(t, err) 230 assert.Equal(t, "", contentType) 231 assert.Equal(t, ErrLocalRequestFailed, err) 232 assert.Nil(t, body) 233 }) 234 235 t.Run("not found", func(t *testing.T) { 236 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 237 w.WriteHeader(http.StatusNotFound) 238 }) 239 240 mock := httptest.NewServer(handler) 241 defer mock.Close() 242 243 proxy := makeTestLocalProxy() 244 245 body, contentType, err := proxy.GetImageDirect(mock.URL + "/image.png") 246 247 assert.NotNil(t, err) 248 assert.Equal(t, "", contentType) 249 assert.Equal(t, ErrLocalRequestFailed, err) 250 assert.Nil(t, body) 251 }) 252 253 t.Run("other server error", func(t *testing.T) { 254 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 255 w.WriteHeader(http.StatusInternalServerError) 256 }) 257 258 mock := httptest.NewServer(handler) 259 defer mock.Close() 260 261 proxy := makeTestLocalProxy() 262 263 body, contentType, err := proxy.GetImageDirect(mock.URL + "/image.png") 264 265 assert.NotNil(t, err) 266 assert.Equal(t, "", contentType) 267 assert.Equal(t, ErrLocalRequestFailed, err) 268 assert.Nil(t, body) 269 }) 270 271 t.Run("timeout", func(t *testing.T) { 272 wait := make(chan bool, 1) 273 274 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 275 <-wait 276 }) 277 278 mock := httptest.NewServer(handler) 279 defer mock.Close() 280 281 proxy := makeTestLocalProxy() 282 283 // Modify the timeout to be much shorter than the default 30 seconds 284 proxy.backend.(*LocalBackend).impl.Timeout = time.Millisecond 285 286 body, contentType, err := proxy.GetImageDirect(mock.URL + "/image.png") 287 288 assert.NotNil(t, err) 289 assert.Equal(t, "", contentType) 290 assert.Equal(t, ErrLocalRequestFailed, err) 291 assert.Nil(t, body) 292 293 wait <- true 294 }) 295 } 296 297 func TestLocalBackend_GetProxiedImageURL(t *testing.T) { 298 imageURL := "http://www.mattermost.org/wp-content/uploads/2016/03/logoHorizontal.png" 299 proxiedURL := "https://mattermost.example.com/api/v4/image?url=http%3A%2F%2Fwww.mattermost.org%2Fwp-content%2Fuploads%2F2016%2F03%2FlogoHorizontal.png" 300 301 proxy := makeTestLocalProxy() 302 303 for _, test := range []struct { 304 Name string 305 Input string 306 Expected string 307 }{ 308 { 309 Name: "should proxy image", 310 Input: imageURL, 311 Expected: proxiedURL, 312 }, 313 { 314 Name: "should not proxy a relative image", 315 Input: "/static/logo.png", 316 Expected: "/static/logo.png", 317 }, 318 { 319 Name: "should not proxy an image on the Mattermost server", 320 Input: "https://mattermost.example.com/static/logo.png", 321 Expected: "https://mattermost.example.com/static/logo.png", 322 }, 323 { 324 Name: "should not proxy an image that has already been proxied", 325 Input: proxiedURL, 326 Expected: proxiedURL, 327 }, 328 } { 329 t.Run(test.Name, func(t *testing.T) { 330 assert.Equal(t, test.Expected, proxy.GetProxiedImageURL(test.Input)) 331 }) 332 } 333 } 334 335 func TestLocalBackend_GetUnproxiedImageURL(t *testing.T) { 336 imageURL := "http://www.mattermost.org/wp-content/uploads/2016/03/logoHorizontal.png" 337 proxiedURL := "https://mattermost.example.com/api/v4/image?url=http%3A%2F%2Fwww.mattermost.org%2Fwp-content%2Fuploads%2F2016%2F03%2FlogoHorizontal.png" 338 339 proxy := makeTestLocalProxy() 340 341 for _, test := range []struct { 342 Name string 343 Input string 344 Expected string 345 }{ 346 { 347 Name: "should remove proxy", 348 Input: proxiedURL, 349 Expected: imageURL, 350 }, 351 { 352 Name: "should not remove proxy from a relative image", 353 Input: "/static/logo.png", 354 Expected: "/static/logo.png", 355 }, 356 { 357 Name: "should not remove proxy from an image on the Mattermost server", 358 Input: "https://mattermost.example.com/static/logo.png", 359 Expected: "https://mattermost.example.com/static/logo.png", 360 }, 361 { 362 Name: "should not remove proxy from a non-proxied image", 363 Input: imageURL, 364 Expected: imageURL, 365 }, 366 } { 367 t.Run(test.Name, func(t *testing.T) { 368 assert.Equal(t, test.Expected, proxy.GetUnproxiedImageURL(test.Input)) 369 }) 370 } 371 }