github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/services/imageproxy/local.go (about) 1 // Copyright (c) 2017-present Xenia, 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 "time" 14 15 "github.com/xzl8028/xenia-server/mlog" 16 "github.com/xzl8028/xenia-server/services/httpservice" 17 "willnorris.com/go/imageproxy" 18 ) 19 20 var imageContentTypes = []string{ 21 "image/bmp", "image/cgm", "image/g3fax", "image/gif", "image/ief", "image/jp2", 22 "image/jpeg", "image/jpg", "image/pict", "image/png", "image/prs.btif", "image/svg+xml", 23 "image/tiff", "image/vnd.adobe.photoshop", "image/vnd.djvu", "image/vnd.dwg", 24 "image/vnd.dxf", "image/vnd.fastbidsheet", "image/vnd.fpx", "image/vnd.fst", 25 "image/vnd.fujixerox.edmics-mmr", "image/vnd.fujixerox.edmics-rlc", 26 "image/vnd.microsoft.icon", "image/vnd.ms-modi", "image/vnd.net-fpx", "image/vnd.wap.wbmp", 27 "image/vnd.xiff", "image/webp", "image/x-cmu-raster", "image/x-cmx", "image/x-icon", 28 "image/x-macpaint", "image/x-pcx", "image/x-pict", "image/x-portable-anymap", 29 "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap", 30 "image/x-quicktime", "image/x-rgb", "image/x-xbitmap", "image/x-xpixmap", "image/x-xwindowdump", 31 } 32 33 var ErrLocalRequestFailed = Error{errors.New("imageproxy.LocalBackend: failed to request proxied image")} 34 35 type LocalBackend struct { 36 proxy *ImageProxy 37 38 // The underlying image proxy implementation provided by the third party library 39 impl *imageproxy.Proxy 40 } 41 42 func makeLocalBackend(proxy *ImageProxy) *LocalBackend { 43 impl := imageproxy.NewProxy(proxy.HTTPService.MakeTransport(false), nil) 44 45 if proxy.Logger != nil { 46 logger, err := proxy.Logger.StdLogAt(mlog.LevelDebug, mlog.String("image_proxy", "local")) 47 if err != nil { 48 mlog.Error("Failed to initialize logger for image proxy", mlog.Err(err)) 49 } 50 51 impl.Logger = logger 52 } 53 54 baseURL, err := url.Parse(*proxy.ConfigService.Config().ServiceSettings.SiteURL) 55 if err != nil { 56 mlog.Error("Failed to set base URL for image proxy. Relative image links may not work.", mlog.Err(err)) 57 } else { 58 impl.DefaultBaseURL = baseURL 59 } 60 61 impl.Timeout = time.Duration(httpservice.RequestTimeout) 62 impl.ContentTypes = imageContentTypes 63 64 return &LocalBackend{ 65 proxy: proxy, 66 impl: impl, 67 } 68 } 69 70 func (backend *LocalBackend) GetImage(w http.ResponseWriter, r *http.Request, imageURL string) { 71 // The interface to the proxy only exposes a ServeHTTP method, so fake a request to it 72 req, err := http.NewRequest(http.MethodGet, "/"+imageURL, nil) 73 if err != nil { 74 // http.NewRequest should only return an error on an invalid URL 75 mlog.Error("Failed to create request for proxied image", mlog.String("url", imageURL), mlog.Err(err)) 76 77 w.WriteHeader(http.StatusBadRequest) 78 w.Write([]byte{}) 79 return 80 } 81 82 w.Header().Set("X-Frame-Options", "deny") 83 w.Header().Set("X-XSS-Protection", "1; mode=block") 84 w.Header().Set("X-Content-Type-Options", "text/plain") 85 w.Header().Set("Content-Security-Policy", "default-src 'none'; img-src data:; style-src 'unsafe-inline'") 86 87 backend.impl.ServeHTTP(w, req) 88 } 89 90 func (backend *LocalBackend) GetImageDirect(imageURL string) (io.ReadCloser, string, error) { 91 // The interface to the proxy only exposes a ServeHTTP method, so fake a request to it 92 req, err := http.NewRequest(http.MethodGet, "/"+imageURL, nil) 93 if err != nil { 94 return nil, "", Error{err} 95 } 96 97 recorder := httptest.NewRecorder() 98 99 backend.impl.ServeHTTP(recorder, req) 100 101 if recorder.Code != http.StatusOK { 102 return nil, "", ErrLocalRequestFailed 103 } 104 105 return ioutil.NopCloser(recorder.Body), recorder.Header().Get("Content-Type"), nil 106 }