Dump: add output format tar and output to stdout (#10376)
* Dump: Use mholt/archive/v3 to support tar including many compressions Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: Allow dump output to stdout Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: Fixed bug present since #6677 where SessionConfig.Provider is never "file" Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: never pack RepoRootPath, LFS.ContentPath and LogRootPath when they are below AppDataPath Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: also dump LFS (fixes #10058) Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: never dump CustomPath if CustomPath is a subdir of or equal to AppDataPath (fixes #10365) Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Use log.Info instead of fmt.Fprintf Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * import ordering * make fmt Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Matti R <matti@mdranta.net>
This commit is contained in:
parent
209b17c4e2
commit
684b7a999f
303 changed files with 301317 additions and 1183 deletions
265
vendor/github.com/golang/gddo/httputil/static.go
generated
vendored
Normal file
265
vendor/github.com/golang/gddo/httputil/static.go
generated
vendored
Normal file
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang/gddo/httputil/header"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StaticServer serves static files.
|
||||
type StaticServer struct {
|
||||
// Dir specifies the location of the directory containing the files to serve.
|
||||
Dir string
|
||||
|
||||
// MaxAge specifies the maximum age for the cache control and expiration
|
||||
// headers.
|
||||
MaxAge time.Duration
|
||||
|
||||
// Error specifies the function used to generate error responses. If Error
|
||||
// is nil, then http.Error is used to generate error responses.
|
||||
Error Error
|
||||
|
||||
// MIMETypes is a map from file extensions to MIME types.
|
||||
MIMETypes map[string]string
|
||||
|
||||
mu sync.Mutex
|
||||
etags map[string]string
|
||||
}
|
||||
|
||||
func (ss *StaticServer) resolve(fname string) string {
|
||||
if path.IsAbs(fname) {
|
||||
panic("Absolute path not allowed when creating a StaticServer handler")
|
||||
}
|
||||
dir := ss.Dir
|
||||
if dir == "" {
|
||||
dir = "."
|
||||
}
|
||||
fname = filepath.FromSlash(fname)
|
||||
return filepath.Join(dir, fname)
|
||||
}
|
||||
|
||||
func (ss *StaticServer) mimeType(fname string) string {
|
||||
ext := path.Ext(fname)
|
||||
var mimeType string
|
||||
if ss.MIMETypes != nil {
|
||||
mimeType = ss.MIMETypes[ext]
|
||||
}
|
||||
if mimeType == "" {
|
||||
mimeType = mime.TypeByExtension(ext)
|
||||
}
|
||||
if mimeType == "" {
|
||||
mimeType = "application/octet-stream"
|
||||
}
|
||||
return mimeType
|
||||
}
|
||||
|
||||
func (ss *StaticServer) openFile(fname string) (io.ReadCloser, int64, string, error) {
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, 0, "", err
|
||||
}
|
||||
const modeType = os.ModeDir | os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
|
||||
if fi.Mode()&modeType != 0 {
|
||||
f.Close()
|
||||
return nil, 0, "", errors.New("not a regular file")
|
||||
}
|
||||
return f, fi.Size(), ss.mimeType(fname), nil
|
||||
}
|
||||
|
||||
// FileHandler returns a handler that serves a single file. The file is
|
||||
// specified by a slash separated path relative to the static server's Dir
|
||||
// field.
|
||||
func (ss *StaticServer) FileHandler(fileName string) http.Handler {
|
||||
id := fileName
|
||||
fileName = ss.resolve(fileName)
|
||||
return &staticHandler{
|
||||
ss: ss,
|
||||
id: func(_ string) string { return id },
|
||||
open: func(_ string) (io.ReadCloser, int64, string, error) { return ss.openFile(fileName) },
|
||||
}
|
||||
}
|
||||
|
||||
// DirectoryHandler returns a handler that serves files from a directory tree.
|
||||
// The directory is specified by a slash separated path relative to the static
|
||||
// server's Dir field.
|
||||
func (ss *StaticServer) DirectoryHandler(prefix, dirName string) http.Handler {
|
||||
if !strings.HasSuffix(prefix, "/") {
|
||||
prefix += "/"
|
||||
}
|
||||
idBase := dirName
|
||||
dirName = ss.resolve(dirName)
|
||||
return &staticHandler{
|
||||
ss: ss,
|
||||
id: func(p string) string {
|
||||
if !strings.HasPrefix(p, prefix) {
|
||||
return "."
|
||||
}
|
||||
return path.Join(idBase, p[len(prefix):])
|
||||
},
|
||||
open: func(p string) (io.ReadCloser, int64, string, error) {
|
||||
if !strings.HasPrefix(p, prefix) {
|
||||
return nil, 0, "", errors.New("request url does not match directory prefix")
|
||||
}
|
||||
p = p[len(prefix):]
|
||||
return ss.openFile(filepath.Join(dirName, filepath.FromSlash(p)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FilesHandler returns a handler that serves the concatentation of the
|
||||
// specified files. The files are specified by slash separated paths relative
|
||||
// to the static server's Dir field.
|
||||
func (ss *StaticServer) FilesHandler(fileNames ...string) http.Handler {
|
||||
|
||||
// todo: cache concatenated files on disk and serve from there.
|
||||
|
||||
mimeType := ss.mimeType(fileNames[0])
|
||||
var buf []byte
|
||||
var openErr error
|
||||
|
||||
for _, fileName := range fileNames {
|
||||
p, err := ioutil.ReadFile(ss.resolve(fileName))
|
||||
if err != nil {
|
||||
openErr = err
|
||||
buf = nil
|
||||
break
|
||||
}
|
||||
buf = append(buf, p...)
|
||||
}
|
||||
|
||||
id := strings.Join(fileNames, " ")
|
||||
|
||||
return &staticHandler{
|
||||
ss: ss,
|
||||
id: func(_ string) string { return id },
|
||||
open: func(p string) (io.ReadCloser, int64, string, error) {
|
||||
return ioutil.NopCloser(bytes.NewReader(buf)), int64(len(buf)), mimeType, openErr
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type staticHandler struct {
|
||||
id func(fname string) string
|
||||
open func(p string) (io.ReadCloser, int64, string, error)
|
||||
ss *StaticServer
|
||||
}
|
||||
|
||||
func (h *staticHandler) error(w http.ResponseWriter, r *http.Request, status int, err error) {
|
||||
http.Error(w, http.StatusText(status), status)
|
||||
}
|
||||
|
||||
func (h *staticHandler) etag(p string) (string, error) {
|
||||
id := h.id(p)
|
||||
|
||||
h.ss.mu.Lock()
|
||||
if h.ss.etags == nil {
|
||||
h.ss.etags = make(map[string]string)
|
||||
}
|
||||
etag := h.ss.etags[id]
|
||||
h.ss.mu.Unlock()
|
||||
|
||||
if etag != "" {
|
||||
return etag, nil
|
||||
}
|
||||
|
||||
// todo: if a concurrent goroutine is calculating the hash, then wait for
|
||||
// it instead of computing it again here.
|
||||
|
||||
rc, _, _, err := h.open(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer rc.Close()
|
||||
|
||||
w := sha1.New()
|
||||
_, err = io.Copy(w, rc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
etag = fmt.Sprintf(`"%x"`, w.Sum(nil))
|
||||
|
||||
h.ss.mu.Lock()
|
||||
h.ss.etags[id] = etag
|
||||
h.ss.mu.Unlock()
|
||||
|
||||
return etag, nil
|
||||
}
|
||||
|
||||
func (h *staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
p := path.Clean(r.URL.Path)
|
||||
if p != r.URL.Path {
|
||||
http.Redirect(w, r, p, 301)
|
||||
return
|
||||
}
|
||||
|
||||
etag, err := h.etag(p)
|
||||
if err != nil {
|
||||
h.error(w, r, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
maxAge := h.ss.MaxAge
|
||||
if maxAge == 0 {
|
||||
maxAge = 24 * time.Hour
|
||||
}
|
||||
if r.FormValue("v") != "" {
|
||||
maxAge = 365 * 24 * time.Hour
|
||||
}
|
||||
|
||||
cacheControl := fmt.Sprintf("public, max-age=%d", maxAge/time.Second)
|
||||
|
||||
for _, e := range header.ParseList(r.Header, "If-None-Match") {
|
||||
if e == etag {
|
||||
w.Header().Set("Cache-Control", cacheControl)
|
||||
w.Header().Set("Etag", etag)
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rc, cl, ct, err := h.open(p)
|
||||
if err != nil {
|
||||
h.error(w, r, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
w.Header().Set("Cache-Control", cacheControl)
|
||||
w.Header().Set("Etag", etag)
|
||||
if ct != "" {
|
||||
w.Header().Set("Content-Type", ct)
|
||||
}
|
||||
if cl != 0 {
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(cl, 10))
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if r.Method != "HEAD" {
|
||||
io.Copy(w, rc)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue