diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 86d02c6c..c7fb6c85 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -2,6 +2,7 @@ package auth import ( "bytes" + "fmt" "io/ioutil" "net/http" "net/url" @@ -30,9 +31,8 @@ type Auth interface { Check(body string, sign string) error } -// SignRequest 对PUT\POST等复杂HTTP请求签名,如果请求Header中 -// 包含 X-Policy, 则此请求会被认定为上传请求,只会对URI部分和 -// Policy部分进行签名。其他请求则会对URI和Body部分进行签名。 +// SignRequest 对PUT\POST等复杂HTTP请求签名,只会对URI部分、 +// 请求正文、`X-`开头的header进行签名 func SignRequest(instance Auth, r *http.Request, expires int64) *http.Request { // 处理有效期 if expires > 0 { @@ -61,20 +61,28 @@ func CheckRequest(instance Auth, r *http.Request) error { return instance.Check(getSignContent(r), sign[0]) } -// getSignContent 根据请求Header中是否包含X-Policy判断是否为上传请求, +// getSignContent 签名请求path、正文、以`X-`开头的header // 返回待签名/验证的字符串 func getSignContent(r *http.Request) (rawSignString string) { - if policy, ok := r.Header["X-Policy"]; ok { - rawSignString = serializer.NewRequestSignString(r.URL.Path, policy[0], "") - } else { - var body = []byte{} - if r.Body != nil { - body, _ = ioutil.ReadAll(r.Body) - _ = r.Body.Close() - r.Body = ioutil.NopCloser(bytes.NewReader(body)) + // 读取所有body正文 + var body = []byte{} + if r.Body != nil { + body, _ = ioutil.ReadAll(r.Body) + _ = r.Body.Close() + r.Body = ioutil.NopCloser(bytes.NewReader(body)) + } + + // 决定要签名的header + var signedHeader []string + for k, _ := range r.Header { + if strings.HasPrefix(k, "X-") { + signedHeader = append(signedHeader, fmt.Sprintf("%s=%s", k, r.Header.Get(k))) } - rawSignString = serializer.NewRequestSignString(r.URL.Path, "", string(body)) } + + // 读取所有待签名Header + rawSignString = serializer.NewRequestSignString(r.URL.Path, strings.Join(signedHeader, "&"), string(body)) + return rawSignString } diff --git a/pkg/request/request.go b/pkg/request/request.go index 36195c8d..d6f93afa 100644 --- a/pkg/request/request.go +++ b/pkg/request/request.go @@ -12,6 +12,7 @@ import ( model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/auth" + "github.com/cloudreve/Cloudreve/v3/pkg/conf" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/util" ) @@ -46,6 +47,7 @@ type options struct { signTTL int64 ctx context.Context contentLength int64 + masterMeta bool } type optionFunc func(*options) @@ -110,6 +112,13 @@ func WithContentLength(s int64) Option { }) } +// WithMasterMeta 请求时携带主机信息 +func WithMasterMeta() Option { + return optionFunc(func(o *options) { + o.masterMeta = true + }) +} + // Request 发送HTTP请求 func (c HTTPClient) Request(method, target string, body io.Reader, opts ...Option) *Response { // 应用额外设置 @@ -142,6 +151,12 @@ func (c HTTPClient) Request(method, target string, body io.Reader, opts ...Optio // 添加请求相关设置 req.Header = options.header + + if options.masterMeta { + req.Header.Add("X-Site-Url", model.GetSiteURL().String()) + req.Header.Add("X-Cloudreve-Version", conf.BackendVersion) + } + if options.contentLength != -1 { req.ContentLength = options.contentLength } diff --git a/pkg/serializer/auth.go b/pkg/serializer/auth.go index 7d11dffe..c8b348e5 100644 --- a/pkg/serializer/auth.go +++ b/pkg/serializer/auth.go @@ -5,16 +5,15 @@ import "encoding/json" // RequestRawSign 待签名的HTTP请求 type RequestRawSign struct { Path string - Policy string + Header string Body string } // NewRequestSignString 返回JSON格式的待签名字符串 -// TODO 测试 -func NewRequestSignString(path, policy, body string) string { +func NewRequestSignString(path, header, body string) string { req := RequestRawSign{ Path: path, - Policy: policy, + Header: header, Body: body, } res, _ := json.Marshal(req) diff --git a/routers/controllers/slave.go b/routers/controllers/slave.go index 6d7e7f0c..464092dd 100644 --- a/routers/controllers/slave.go +++ b/routers/controllers/slave.go @@ -10,6 +10,7 @@ import ( "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/service/admin" + "github.com/cloudreve/Cloudreve/v3/service/aria2" "github.com/cloudreve/Cloudreve/v3/service/explorer" "github.com/cloudreve/Cloudreve/v3/service/node" "github.com/gin-gonic/gin" @@ -187,3 +188,14 @@ func SlaveHeartbeat(c *gin.Context) { c.JSON(200, ErrorResponse(err)) } } + +// SlaveAria2Create 创建 Aria2 任务 +func SlaveAria2Create(c *gin.Context) { + var service aria2.SlaveAria2Call + if err := c.ShouldBindJSON(&service); err == nil { + res := service.Add(c) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} diff --git a/routers/router.go b/routers/router.go index 3209a985..2656e422 100644 --- a/routers/router.go +++ b/routers/router.go @@ -51,6 +51,12 @@ func InitSlaveRouter() *gin.Engine { v3.POST("delete", controllers.SlaveDelete) // 列出文件 v3.POST("list", controllers.SlaveList) + + // 离线下载 + aria2 := v3.Group("aria2") + { + aria2.POST("task", controllers.SlaveList) + } } return r } diff --git a/service/aria2/add.go b/service/aria2/add.go index 50ad4de0..fa48d439 100644 --- a/service/aria2/add.go +++ b/service/aria2/add.go @@ -17,7 +17,14 @@ type AddURLService struct { Dst string `json:"dst" binding:"required,min=1"` } -// Add 创建新的链接离线下载任务 +// SlaveAria2Call 从机有关Aria2的请求正文 +type SlaveAria2Call struct { + Task *model.Download `json:"task"` + GroupOptions map[string]interface{} `json:"group_options"` + Files []uint `json:"files"` +} + +// Add 主机创建新的链接离线下载任务 func (service *AddURLService) Add(c *gin.Context, taskType int) serializer.Response { // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) @@ -74,3 +81,8 @@ func (service *AddURLService) Add(c *gin.Context, taskType int) serializer.Respo return serializer.Response{} } + +// Add 从机创建新的链接离线下载任务 +func (service *SlaveAria2Call) Add(c *gin.Context) serializer.Response { + return serializer.Response{} +}