1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package http_test
6
7import (
8	"io/fs"
9	"log"
10	"net/http"
11	"strings"
12)
13
14// containsDotFile reports whether name contains a path element starting with a period.
15// The name is assumed to be a delimited by forward slashes, as guaranteed
16// by the http.FileSystem interface.
17func containsDotFile(name string) bool {
18	parts := strings.Split(name, "/")
19	for _, part := range parts {
20		if strings.HasPrefix(part, ".") {
21			return true
22		}
23	}
24	return false
25}
26
27// dotFileHidingFile is the http.File use in dotFileHidingFileSystem.
28// It is used to wrap the Readdir method of http.File so that we can
29// remove files and directories that start with a period from its output.
30type dotFileHidingFile struct {
31	http.File
32}
33
34// Readdir is a wrapper around the Readdir method of the embedded File
35// that filters out all files that start with a period in their name.
36func (f dotFileHidingFile) Readdir(n int) (fis []fs.FileInfo, err error) {
37	files, err := f.File.Readdir(n)
38	for _, file := range files { // Filters out the dot files
39		if !strings.HasPrefix(file.Name(), ".") {
40			fis = append(fis, file)
41		}
42	}
43	return
44}
45
46// dotFileHidingFileSystem is an http.FileSystem that hides
47// hidden "dot files" from being served.
48type dotFileHidingFileSystem struct {
49	http.FileSystem
50}
51
52// Open is a wrapper around the Open method of the embedded FileSystem
53// that serves a 403 permission error when name has a file or directory
54// with whose name starts with a period in its path.
55func (fsys dotFileHidingFileSystem) Open(name string) (http.File, error) {
56	if containsDotFile(name) { // If dot file, return 403 response
57		return nil, fs.ErrPermission
58	}
59
60	file, err := fsys.FileSystem.Open(name)
61	if err != nil {
62		return nil, err
63	}
64	return dotFileHidingFile{file}, err
65}
66
67func ExampleFileServer_dotFileHiding() {
68	fsys := dotFileHidingFileSystem{http.Dir(".")}
69	http.Handle("/", http.FileServer(fsys))
70	log.Fatal(http.ListenAndServe(":8080", nil))
71}
72