type Dir … // mapOpenError maps the provided non-nil error from opening name // to a possibly better non-nil error. In particular, it turns OS-specific errors // about opening files in non-directories into fs.ErrNotExist. See Issues 18984 and 49552. func mapOpenError(originalErr error, name string, sep rune, stat func(string) (fs.FileInfo, error)) error { … } // Open implements [FileSystem] using [os.Open], opening files for reading rooted // and relative to the directory d. func (d Dir) Open(name string) (File, error) { … } type FileSystem … type File … type anyDirs … type fileInfoDirs … func (d fileInfoDirs) len() int { … } func (d fileInfoDirs) isDir(i int) bool { … } func (d fileInfoDirs) name(i int) string { … } type dirEntryDirs … func (d dirEntryDirs) len() int { … } func (d dirEntryDirs) isDir(i int) bool { … } func (d dirEntryDirs) name(i int) string { … } func dirList(w ResponseWriter, r *Request, f File) { … } var httpservecontentkeepheaders … // serveError serves an error from ServeFile, ServeFileFS, and ServeContent. // Because those can all be configured by the caller by setting headers like // Etag, Last-Modified, and Cache-Control to send on a successful response, // the error path needs to clear them, since they may not be meant for errors. func serveError(w ResponseWriter, text string, code int) { … } // ServeContent replies to the request using the content in the // provided ReadSeeker. The main benefit of ServeContent over [io.Copy] // is that it handles Range requests properly, sets the MIME type, and // handles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since, // and If-Range requests. // // If the response's Content-Type header is not set, ServeContent // first tries to deduce the type from name's file extension and, // if that fails, falls back to reading the first block of the content // and passing it to [DetectContentType]. // The name is otherwise unused; in particular it can be empty and is // never sent in the response. // // If modtime is not the zero time or Unix epoch, ServeContent // includes it in a Last-Modified header in the response. If the // request includes an If-Modified-Since header, ServeContent uses // modtime to decide whether the content needs to be sent at all. // // The content's Seek method must work: ServeContent uses // a seek to the end of the content to determine its size. // Note that [*os.File] implements the [io.ReadSeeker] interface. // // If the caller has set w's ETag header formatted per RFC 7232, section 2.3, // ServeContent uses it to handle requests using If-Match, If-None-Match, or If-Range. // // If an error occurs when serving the request (for example, when // handling an invalid range request), ServeContent responds with an // error message. By default, ServeContent strips the Cache-Control, // Content-Encoding, ETag, and Last-Modified headers from error responses. // The GODEBUG setting httpservecontentkeepheaders=1 causes ServeContent // to preserve these headers. func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) { … } var errSeeker … var errNoOverlap … // if name is empty, filename is unknown. (used for mime type, before sniffing) // if modtime.IsZero(), modtime is unknown. // content must be seeked to the beginning of the file. // The sizeFunc is called at most once. Its error, if any, is sent in the HTTP response. func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) { … } // scanETag determines if a syntactically valid ETag is present at s. If so, // the ETag and remaining text after consuming ETag is returned. Otherwise, // it returns "", "". func scanETag(s string) (etag string, remain string) { … } // etagStrongMatch reports whether a and b match using strong ETag comparison. // Assumes a and b are valid ETags. func etagStrongMatch(a, b string) bool { … } // etagWeakMatch reports whether a and b match using weak ETag comparison. // Assumes a and b are valid ETags. func etagWeakMatch(a, b string) bool { … } type condResult … const condNone … const condTrue … const condFalse … func checkIfMatch(w ResponseWriter, r *Request) condResult { … } func checkIfUnmodifiedSince(r *Request, modtime time.Time) condResult { … } func checkIfNoneMatch(w ResponseWriter, r *Request) condResult { … } func checkIfModifiedSince(r *Request, modtime time.Time) condResult { … } func checkIfRange(w ResponseWriter, r *Request, modtime time.Time) condResult { … } var unixEpochTime … // isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0). func isZeroTime(t time.Time) bool { … } func setLastModified(w ResponseWriter, modtime time.Time) { … } func writeNotModified(w ResponseWriter) { … } // checkPreconditions evaluates request preconditions and reports whether a precondition // resulted in sending StatusNotModified or StatusPreconditionFailed. func checkPreconditions(w ResponseWriter, r *Request, modtime time.Time) (done bool, rangeHeader string) { … } // name is '/'-separated, not filepath.Separator. func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) { … } // toHTTPError returns a non-specific HTTP error message and status code // for a given non-nil error value. It's important that toHTTPError does not // actually return err.Error(), since msg and httpStatus are returned to users, // and historically Go's ServeContent always returned just "404 Not Found" for // all errors. We don't want to start leaking information in error messages. func toHTTPError(err error) (msg string, httpStatus int) { … } // localRedirect gives a Moved Permanently response. // It does not convert relative paths to absolute paths like Redirect does. func localRedirect(w ResponseWriter, r *Request, newPath string) { … } // ServeFile replies to the request with the contents of the named // file or directory. // // If the provided file or directory name is a relative path, it is // interpreted relative to the current directory and may ascend to // parent directories. If the provided name is constructed from user // input, it should be sanitized before calling [ServeFile]. // // As a precaution, ServeFile will reject requests where r.URL.Path // contains a ".." path element; this protects against callers who // might unsafely use [filepath.Join] on r.URL.Path without sanitizing // it and then use that filepath.Join result as the name argument. // // As another special case, ServeFile redirects any request where r.URL.Path // ends in "/index.html" to the same path, without the final // "index.html". To avoid such redirects either modify the path or // use [ServeContent]. // // Outside of those two special cases, ServeFile does not use // r.URL.Path for selecting the file or directory to serve; only the // file or directory provided in the name argument is used. func ServeFile(w ResponseWriter, r *Request, name string) { … } // ServeFileFS replies to the request with the contents // of the named file or directory from the file system fsys. // The files provided by fsys must implement [io.Seeker]. // // If the provided name is constructed from user input, it should be // sanitized before calling [ServeFileFS]. // // As a precaution, ServeFileFS will reject requests where r.URL.Path // contains a ".." path element; this protects against callers who // might unsafely use [filepath.Join] on r.URL.Path without sanitizing // it and then use that filepath.Join result as the name argument. // // As another special case, ServeFileFS redirects any request where r.URL.Path // ends in "/index.html" to the same path, without the final // "index.html". To avoid such redirects either modify the path or // use [ServeContent]. // // Outside of those two special cases, ServeFileFS does not use // r.URL.Path for selecting the file or directory to serve; only the // file or directory provided in the name argument is used. func ServeFileFS(w ResponseWriter, r *Request, fsys fs.FS, name string) { … } func containsDotDot(v string) bool { … } func isSlashRune(r rune) bool { … } type fileHandler … type ioFS … type ioFile … func (f ioFS) Open(name string) (File, error) { … } func (f ioFile) Close() error { … } func (f ioFile) Read(b []byte) (int, error) { … } func (f ioFile) Stat() (fs.FileInfo, error) { … } var errMissingSeek … var errMissingReadDir … func (f ioFile) Seek(offset int64, whence int) (int64, error) { … } func (f ioFile) ReadDir(count int) ([]fs.DirEntry, error) { … } func (f ioFile) Readdir(count int) ([]fs.FileInfo, error) { … } // FS converts fsys to a [FileSystem] implementation, // for use with [FileServer] and [NewFileTransport]. // The files provided by fsys must implement [io.Seeker]. func FS(fsys fs.FS) FileSystem { … } // FileServer returns a handler that serves HTTP requests // with the contents of the file system rooted at root. // // As a special case, the returned file server redirects any request // ending in "/index.html" to the same path, without the final // "index.html". // // To use the operating system's file system implementation, // use [http.Dir]: // // http.Handle("/", http.FileServer(http.Dir("/tmp"))) // // To use an [fs.FS] implementation, use [http.FileServerFS] instead. func FileServer(root FileSystem) Handler { … } // FileServerFS returns a handler that serves HTTP requests // with the contents of the file system fsys. // The files provided by fsys must implement [io.Seeker]. // // As a special case, the returned file server redirects any request // ending in "/index.html" to the same path, without the final // "index.html". // // http.Handle("/", http.FileServerFS(fsys)) func FileServerFS(root fs.FS) Handler { … } func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { … } type httpRange … func (r httpRange) contentRange(size int64) string { … } func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader { … } // parseRange parses a Range header string as per RFC 7233. // errNoOverlap is returned if none of the ranges overlap. func parseRange(s string, size int64) ([]httpRange, error) { … } type countingWriter … func (w *countingWriter) Write(p []byte) (n int, err error) { … } // rangesMIMESize returns the number of bytes it takes to encode the // provided ranges as a multipart response. func rangesMIMESize(ranges []httpRange, contentType string, contentSize int64) (encSize int64) { … } func sumRangesSize(ranges []httpRange) (size int64) { … }