github.com/goplus/yap@v0.8.1/noredirect/noredirect.go (about) 1 /* 2 * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package noredirect 18 19 import ( 20 "io" 21 "net/http" 22 "strings" 23 "time" 24 _ "unsafe" 25 ) 26 27 // FileServer returns a handler that serves HTTP requests 28 // with the contents of the file system rooted at root. 29 func FileServer(root http.FileSystem) http.Handler { 30 return &fileHandler{root} 31 } 32 33 type fileHandler struct { 34 root http.FileSystem 35 } 36 37 func (f *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 38 upath := r.URL.Path 39 if !strings.HasPrefix(upath, "/") { 40 upath = "/" + upath 41 r.URL.Path = upath 42 } 43 serveFile(w, r, f.root, upath) 44 } 45 46 //go:linkname serveContent net/http.serveContent 47 func serveContent(w http.ResponseWriter, r *http.Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) 48 49 //go:linkname toHTTPError net/http.toHTTPError 50 func toHTTPError(err error) (msg string, httpStatus int) 51 52 // name is '/'-separated, not filepath.Separator. 53 func serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string) { 54 f, err := fs.Open(name) 55 if err != nil { 56 msg, code := toHTTPError(err) 57 http.Error(w, msg, code) 58 return 59 } 60 defer f.Close() 61 62 d, err := f.Stat() 63 if err != nil { 64 msg, code := toHTTPError(err) 65 http.Error(w, msg, code) 66 return 67 } 68 69 // serveContent will check modification time 70 sizeFunc := func() (int64, error) { 71 if size := d.Size(); size >= 0 { 72 return size, nil 73 } 74 return fileSize(f) 75 } 76 serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f) 77 } 78 79 func fileSize(content http.File) (size int64, err error) { 80 size, err = content.Seek(0, io.SeekEnd) 81 if err != nil { 82 return 83 } 84 _, err = content.Seek(0, io.SeekStart) 85 if err != nil { 86 return 87 } 88 return size, nil 89 }