github.com/jfrerich/mattermost-server@v5.8.0-rc2+incompatible/services/imageproxy/local.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  	"errors"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"net/url"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/mattermost/mattermost-server/mlog"
    17  	"github.com/mattermost/mattermost-server/services/httpservice"
    18  	"willnorris.com/go/imageproxy"
    19  )
    20  
    21  var imageContentTypes = []string{
    22  	"image/bmp", "image/cgm", "image/g3fax", "image/gif", "image/ief", "image/jp2",
    23  	"image/jpeg", "image/jpg", "image/pict", "image/png", "image/prs.btif", "image/svg+xml",
    24  	"image/tiff", "image/vnd.adobe.photoshop", "image/vnd.djvu", "image/vnd.dwg",
    25  	"image/vnd.dxf", "image/vnd.fastbidsheet", "image/vnd.fpx", "image/vnd.fst",
    26  	"image/vnd.fujixerox.edmics-mmr", "image/vnd.fujixerox.edmics-rlc",
    27  	"image/vnd.microsoft.icon", "image/vnd.ms-modi", "image/vnd.net-fpx", "image/vnd.wap.wbmp",
    28  	"image/vnd.xiff", "image/webp", "image/x-cmu-raster", "image/x-cmx", "image/x-icon",
    29  	"image/x-macpaint", "image/x-pcx", "image/x-pict", "image/x-portable-anymap",
    30  	"image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap",
    31  	"image/x-quicktime", "image/x-rgb", "image/x-xbitmap", "image/x-xpixmap", "image/x-xwindowdump",
    32  }
    33  
    34  var ErrLocalRequestFailed = Error{errors.New("imageproxy.LocalBackend: failed to request proxied image")}
    35  
    36  type LocalBackend struct {
    37  	proxy *ImageProxy
    38  
    39  	// The underlying image proxy implementation provided by the third party library
    40  	impl *imageproxy.Proxy
    41  }
    42  
    43  func makeLocalBackend(proxy *ImageProxy) *LocalBackend {
    44  	impl := imageproxy.NewProxy(proxy.HTTPService.MakeTransport(false), nil)
    45  
    46  	baseURL, err := url.Parse(*proxy.ConfigService.Config().ServiceSettings.SiteURL)
    47  	if err != nil {
    48  		mlog.Error("Failed to set base URL for image proxy. Relative image links may not work.", mlog.Err(err))
    49  	} else {
    50  		impl.DefaultBaseURL = baseURL
    51  	}
    52  
    53  	impl.Timeout = time.Duration(httpservice.RequestTimeout)
    54  	impl.ContentTypes = imageContentTypes
    55  
    56  	return &LocalBackend{
    57  		proxy: proxy,
    58  		impl:  impl,
    59  	}
    60  }
    61  
    62  func (backend *LocalBackend) GetImage(w http.ResponseWriter, r *http.Request, imageURL string) {
    63  	// The interface to the proxy only exposes a ServeHTTP method, so fake a request to it
    64  	req, err := http.NewRequest(http.MethodGet, "/"+imageURL, nil)
    65  	if err != nil {
    66  		// http.NewRequest should only return an error on an invalid URL
    67  		mlog.Error("Failed to create request for proxied image", mlog.String("url", imageURL), mlog.Err(err))
    68  
    69  		w.WriteHeader(http.StatusBadRequest)
    70  		w.Write([]byte{})
    71  		return
    72  	}
    73  
    74  	backend.impl.ServeHTTP(w, req)
    75  }
    76  
    77  func (backend *LocalBackend) GetImageDirect(imageURL string) (io.ReadCloser, string, error) {
    78  	// The interface to the proxy only exposes a ServeHTTP method, so fake a request to it
    79  	req, err := http.NewRequest(http.MethodGet, "/"+imageURL, nil)
    80  	if err != nil {
    81  		return nil, "", Error{err}
    82  	}
    83  
    84  	recorder := httptest.NewRecorder()
    85  
    86  	backend.impl.ServeHTTP(recorder, req)
    87  
    88  	if recorder.Code != http.StatusOK {
    89  		return nil, "", ErrLocalRequestFailed
    90  	}
    91  
    92  	return ioutil.NopCloser(recorder.Body), recorder.Header().Get("Content-Type"), nil
    93  }
    94  
    95  func (backend *LocalBackend) GetProxiedImageURL(imageURL string) string {
    96  	siteURL := *backend.proxy.ConfigService.Config().ServiceSettings.SiteURL
    97  
    98  	if imageURL == "" || imageURL[0] == '/' || strings.HasPrefix(imageURL, siteURL) {
    99  		return imageURL
   100  	}
   101  
   102  	return siteURL + "/api/v4/image?url=" + url.QueryEscape(imageURL)
   103  }
   104  
   105  func (backend *LocalBackend) GetUnproxiedImageURL(proxiedURL string) string {
   106  	siteURL := *backend.proxy.ConfigService.Config().ServiceSettings.SiteURL
   107  
   108  	if !strings.HasPrefix(proxiedURL, siteURL+"/api/v4/image?url=") {
   109  		return proxiedURL
   110  	}
   111  
   112  	parsed, err := url.Parse(proxiedURL)
   113  	if err != nil {
   114  		return proxiedURL
   115  	}
   116  
   117  	u := parsed.Query()["url"]
   118  	if len(u) == 0 {
   119  		return proxiedURL
   120  	}
   121  
   122  	return u[0]
   123  }