github.com/ashishbhate/mattermost-server@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  }