✨ webdav direct proxy
This commit is contained in:
parent
7bb237d0ef
commit
4371c470b3
@ -111,6 +111,13 @@ func GetDrivers() map[string][]Item {
|
|||||||
Required: true,
|
Required: true,
|
||||||
Description: "Transfer the WebDAV of this account through the server",
|
Description: "Transfer the WebDAV of this account through the server",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "webdav_direct",
|
||||||
|
Label: "webdav direct",
|
||||||
|
Type: TypeBool,
|
||||||
|
Required: true,
|
||||||
|
Description: "Transfer the WebDAV of this account through the native",
|
||||||
|
},
|
||||||
}, v.Items()...)
|
}, v.Items()...)
|
||||||
}
|
}
|
||||||
res[k] = append([]Item{
|
res[k] = append([]Item{
|
||||||
|
@ -44,7 +44,8 @@ type Header struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Link struct {
|
type Link struct {
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Headers []Header `json:"headers"`
|
Headers []Header `json:"headers"`
|
||||||
Data io.ReadCloser
|
Data io.ReadCloser
|
||||||
|
FilePath string `json:"path"` // for native
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ func (driver Native) Link(args base.Args, account *model.Account) (*base.Link, e
|
|||||||
return nil, base.ErrNotFile
|
return nil, base.ErrNotFile
|
||||||
}
|
}
|
||||||
link := base.Link{
|
link := base.Link{
|
||||||
Url: fullPath,
|
FilePath: fullPath,
|
||||||
}
|
}
|
||||||
return &link, nil
|
return &link, nil
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,9 @@ type Account struct {
|
|||||||
SiteUrl string `json:"site_url"`
|
SiteUrl string `json:"site_url"`
|
||||||
SiteId string `json:"site_id"`
|
SiteId string `json:"site_id"`
|
||||||
InternalType string `json:"internal_type"`
|
InternalType string `json:"internal_type"`
|
||||||
WebdavProxy bool `json:"webdav_proxy"` // 开启之后只会webdav走中转
|
WebdavProxy bool `json:"webdav_proxy"` // 开启之后只会webdav走中转
|
||||||
Proxy bool `json:"proxy"` // 是否中转,开启之后web和webdav都会走中转
|
Proxy bool `json:"proxy"` // 是否中转,开启之后web和webdav都会走中转
|
||||||
|
WebdavDirect bool `json:"webdav_direct"` // webdav 下载不跳转
|
||||||
//AllowProxy bool `json:"allow_proxy"` // 是否允许中转下载
|
//AllowProxy bool `json:"allow_proxy"` // 是否允许中转下载
|
||||||
DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302
|
DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302
|
||||||
APIProxyUrl string `json:"api_proxy_url"` // 用于中转api的地址
|
APIProxyUrl string `json:"api_proxy_url"` // 用于中转api的地址
|
||||||
|
87
server/common/proxy.go
Normal file
87
server/common/proxy.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var HttpClient = &http.Client{}
|
||||||
|
|
||||||
|
func Proxy(w http.ResponseWriter, r *http.Request, link *base.Link, file *model.File) error {
|
||||||
|
// 本机读取数据
|
||||||
|
var err error
|
||||||
|
if link.Data != nil {
|
||||||
|
//c.Data(http.StatusOK, "application/octet-stream", link.Data)
|
||||||
|
defer func() {
|
||||||
|
_ = link.Data.Close()
|
||||||
|
}()
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
|
||||||
|
w.Header().Set("Content-Length", strconv.FormatInt(file.Size, 10))
|
||||||
|
_, err = io.Copy(w, link.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 本机文件直接返回文件
|
||||||
|
if link.FilePath != "" {
|
||||||
|
f, err := os.Open(link.FilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
|
fileStat, err := os.Stat(link.FilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
http.ServeContent(w, r, file.Name, fileStat.ModTime(), f)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
req, err := http.NewRequest(r.Method, link.Url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for h, val := range r.Header {
|
||||||
|
req.Header[h] = val
|
||||||
|
}
|
||||||
|
for _, header := range link.Headers {
|
||||||
|
req.Header.Set(header.Name, header.Value)
|
||||||
|
}
|
||||||
|
res, err := HttpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = res.Body.Close()
|
||||||
|
}()
|
||||||
|
log.Debugf("proxy status: %d", res.StatusCode)
|
||||||
|
w.WriteHeader(res.StatusCode)
|
||||||
|
if res.StatusCode >= 400 {
|
||||||
|
all, _ := ioutil.ReadAll(res.Body)
|
||||||
|
msg := string(all)
|
||||||
|
log.Debugln(msg)
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
for h, v := range res.Header {
|
||||||
|
w.Header()[h] = v
|
||||||
|
}
|
||||||
|
_, err = io.Copy(w, res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
@ -10,13 +10,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Proxy(c *gin.Context) {
|
func Proxy(c *gin.Context) {
|
||||||
@ -63,98 +57,13 @@ func Proxy(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 本机读取数据
|
err = common.Proxy(c.Writer, c.Request, link, file)
|
||||||
if link.Data != nil {
|
if err != nil {
|
||||||
//c.Data(http.StatusOK, "application/octet-stream", link.Data)
|
common.ErrorResp(c, err, 500)
|
||||||
defer func() {
|
|
||||||
_ = link.Data.Close()
|
|
||||||
}()
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
c.Header("Content-Type", "application/octet-stream")
|
|
||||||
c.Header("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
|
|
||||||
c.Header("Content-Length", strconv.FormatInt(file.Size, 10))
|
|
||||||
_, err = io.Copy(c.Writer, link.Data)
|
|
||||||
if err != nil {
|
|
||||||
_, _ = c.Writer.WriteString(err.Error())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 本机文件直接返回文件
|
|
||||||
if account.Type == "Native" {
|
|
||||||
// 对于名称为index.html的文件需要特殊处理
|
|
||||||
if utils.Base(rawPath) == "index.html" {
|
|
||||||
file, err := os.Open(link.Url)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = file.Close()
|
|
||||||
}()
|
|
||||||
fileStat, err := os.Stat(link.Url)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.ServeContent(c.Writer, c.Request, utils.Base(rawPath), fileStat.ModTime(), file)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.File(link.Url)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
//if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
|
|
||||||
// Text(c, link)
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
r := c.Request
|
|
||||||
w := c.Writer
|
|
||||||
//target, err := url.Parse(link.Url)
|
|
||||||
//if err != nil {
|
|
||||||
// common.ErrorResp(c, err, 500)
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
req, err := http.NewRequest("GET", link.Url, nil)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Debugf("%+v", r.Header)
|
|
||||||
for h, val := range r.Header {
|
|
||||||
req.Header[h] = val
|
|
||||||
}
|
|
||||||
for _, header := range link.Headers {
|
|
||||||
req.Header.Set(header.Name, header.Value)
|
|
||||||
}
|
|
||||||
log.Debugf("%+v", req.Header)
|
|
||||||
res, err := HttpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = res.Body.Close()
|
|
||||||
}()
|
|
||||||
log.Debugf("proxy status: %d", res.StatusCode)
|
|
||||||
w.WriteHeader(res.StatusCode)
|
|
||||||
if res.StatusCode >= 400 {
|
|
||||||
all, _ := ioutil.ReadAll(res.Body)
|
|
||||||
log.Debugln(string(all))
|
|
||||||
common.ErrorStrResp(c, string(all), 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for h, v := range res.Header {
|
|
||||||
w.Header()[h] = v
|
|
||||||
}
|
|
||||||
_, err = io.Copy(w, res.Body)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var client *resty.Client
|
var client *resty.Client
|
||||||
var HttpClient = &http.Client{}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
client = resty.New()
|
client = resty.New()
|
||||||
|
@ -95,7 +95,7 @@ func ClientIP(r *http.Request) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
|
func (fs *FileSystem) Link(w http.ResponseWriter, r *http.Request, rawPath string) (string, error) {
|
||||||
rawPath = utils.ParsePath(rawPath)
|
rawPath = utils.ParsePath(rawPath)
|
||||||
log.Debugf("get link path: %s", rawPath)
|
log.Debugf("get link path: %s", rawPath)
|
||||||
if model.AccountsCount() > 1 && rawPath == "/" {
|
if model.AccountsCount() > 1 && rawPath == "/" {
|
||||||
@ -110,6 +110,19 @@ func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
|
|||||||
if r.TLS != nil {
|
if r.TLS != nil {
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
}
|
}
|
||||||
|
// 直接返回
|
||||||
|
if account.WebdavDirect {
|
||||||
|
file, err := fs.File(rawPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
link_, err := driver.Link(base.Args{Path: path_}, account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = common.Proxy(w, r, link_, file)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
if driver.Config().OnlyProxy || account.WebdavProxy {
|
if driver.Config().OnlyProxy || account.WebdavProxy {
|
||||||
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
|
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
|
||||||
if conf.GetBool("check down link") {
|
if conf.GetBool("check down link") {
|
||||||
|
@ -237,11 +237,14 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *
|
|||||||
}
|
}
|
||||||
w.Header().Set("ETag", etag)
|
w.Header().Set("ETag", etag)
|
||||||
log.Debugf("url: %+v", r.URL)
|
log.Debugf("url: %+v", r.URL)
|
||||||
link, err := fs.Link(r, reqPath)
|
link, err := fs.Link(w, r, reqPath)
|
||||||
|
log.Debugf("webdav link error: %s", err.Error())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, link, 302)
|
if link != "" {
|
||||||
|
http.Redirect(w, r, link, 302)
|
||||||
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user