package server import ( "net/http" "path/filepath" "git.netflux.io/rob/clipper/config" "git.netflux.io/rob/clipper/filestore" "github.com/gorilla/mux" "github.com/improbable-eng/grpc-web/go/grpcweb" "go.uber.org/zap" ) type httpHandler struct { grpcHandler *grpcweb.WrappedGrpcServer router http.Handler mediaSetService MediaSetService logger *zap.SugaredLogger } func newHTTPHandler(grpcHandler *grpcweb.WrappedGrpcServer, mediaSetService MediaSetService, c config.Config, logger *zap.SugaredLogger) *httpHandler { fileStoreHandler := http.NotFoundHandler() if c.FileStoreHTTPRoot != "" { logger.With("root", c.FileStoreHTTPRoot, "baseURL", c.FileStoreHTTPBaseURL.String()).Info("Configured to serve file store over HTTP") fileStoreHandler = http.FileServer(&indexedFileSystem{http.Dir(c.FileStoreHTTPRoot)}) } assetsHandler := http.NotFoundHandler() if c.AssetsHTTPRoot != "" { logger.With("root", c.AssetsHTTPRoot).Info("Configured to serve assets over HTTP") assetsHandler = http.FileServer(&indexedFileSystem{http.Dir(c.AssetsHTTPRoot)}) } // If FileSystemStore AND assets serving are both enabled, // FileStoreHTTPBaseURL *must* be set to a value other than "/" to avoid // clobbering the assets routes. router := mux.NewRouter() if c.FileStore == config.FileSystemStore { router. Methods("GET"). PathPrefix(c.FileStoreHTTPBaseURL.Path). Handler(filestore.NewFileSystemStoreHTTPMiddleware(c.FileStoreHTTPBaseURL, fileStoreHandler)) } router. Methods("GET"). Handler(assetsHandler) return &httpHandler{ grpcHandler: grpcHandler, router: router, mediaSetService: mediaSetService, logger: logger, } } func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !h.grpcHandler.IsGrpcWebRequest(r) && !h.grpcHandler.IsAcceptableGrpcCorsRequest(r) { h.router.ServeHTTP(w, r) return } h.grpcHandler.ServeHTTP(w, r) } // indexedFileSystem is an HTTP file system which handles index.html files if // they exist, but does not serve directory listings. // // Ref: https://www.alexedwards.net/blog/disable-http-fileserver-directory-listings type indexedFileSystem struct { httpFS http.FileSystem } func (ifs *indexedFileSystem) Open(path string) (http.File, error) { f, err := ifs.httpFS.Open(path) if err != nil { return nil, err } s, err := f.Stat() if err != nil { return nil, err } if !s.IsDir() { return f, nil } index := filepath.Join(path, "index.html") if _, err := ifs.httpFS.Open(index); err != nil { _ = f.Close() // ignore error return nil, err } return f, nil }