chore(deps): bump github.com/tus/tusd/v2 from 2.5.0 to 2.6.0
Bumps [github.com/tus/tusd/v2](https://github.com/tus/tusd) from 2.5.0 to 2.6.0. - [Release notes](https://github.com/tus/tusd/releases) - [Commits](https://github.com/tus/tusd/compare/v2.5.0...v2.6.0) --- updated-dependencies: - dependency-name: github.com/tus/tusd/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
293aff1b53
commit
6782c2473d
+20
-5
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
@@ -28,8 +29,11 @@ type bodyReader struct {
|
||||
bytesCounter int64
|
||||
ctx *httpContext
|
||||
reader io.ReadCloser
|
||||
err error
|
||||
onReadDone func()
|
||||
|
||||
// lock protects concurrent access to err.
|
||||
lock sync.RWMutex
|
||||
err error
|
||||
}
|
||||
|
||||
func newBodyReader(c *httpContext, maxSize int64) *bodyReader {
|
||||
@@ -41,7 +45,10 @@ func newBodyReader(c *httpContext, maxSize int64) *bodyReader {
|
||||
}
|
||||
|
||||
func (r *bodyReader) Read(b []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
r.lock.RLock()
|
||||
hasErrored := r.err != nil
|
||||
r.lock.RUnlock()
|
||||
if hasErrored {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
@@ -99,20 +106,26 @@ func (r *bodyReader) Read(b []byte) (int, error) {
|
||||
|
||||
// Other errors are stored for retrival with hasError, but is not returned
|
||||
// to the consumer. We do not overwrite an error if it has been set already.
|
||||
r.lock.Lock()
|
||||
if r.err == nil {
|
||||
r.err = err
|
||||
}
|
||||
r.lock.Unlock()
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (r bodyReader) hasError() error {
|
||||
if r.err == io.EOF {
|
||||
func (r *bodyReader) hasError() error {
|
||||
r.lock.RLock()
|
||||
err := r.err
|
||||
r.lock.RUnlock()
|
||||
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.err
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *bodyReader) bytesRead() int64 {
|
||||
@@ -120,7 +133,9 @@ func (r *bodyReader) bytesRead() int64 {
|
||||
}
|
||||
|
||||
func (r *bodyReader) closeWithError(err error) {
|
||||
r.lock.Lock()
|
||||
r.err = err
|
||||
r.lock.Unlock()
|
||||
|
||||
// SetReadDeadline with the current time causes concurrent reads to the body to time out,
|
||||
// so the body will be closed sooner with less delay.
|
||||
|
||||
+101
-10
@@ -28,6 +28,7 @@ const (
|
||||
interopVersion3 draftVersion = "3" // From draft version -01
|
||||
interopVersion4 draftVersion = "4" // From draft version -02
|
||||
interopVersion5 draftVersion = "5" // From draft version -03
|
||||
interopVersion6 draftVersion = "6" // From draft version -04 and -05
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -235,8 +236,16 @@ func (handler *UnroutedHandler) Middleware(h http.Handler) http.Handler {
|
||||
// Set appropriated headers in case of OPTIONS method allowing protocol
|
||||
// discovery and end with an 204 No Content
|
||||
if r.Method == "OPTIONS" {
|
||||
ietfDraftLimits := "min-size=0"
|
||||
|
||||
if handler.config.MaxSize > 0 {
|
||||
header.Set("Tus-Max-Size", strconv.FormatInt(handler.config.MaxSize, 10))
|
||||
maxSizeStr := strconv.FormatInt(handler.config.MaxSize, 10)
|
||||
header.Set("Tus-Max-Size", maxSizeStr)
|
||||
ietfDraftLimits += ",max-size=" + maxSizeStr
|
||||
}
|
||||
|
||||
if handler.usesIETFDraft(r) {
|
||||
header.Set("Upload-Limit", ietfDraftLimits)
|
||||
}
|
||||
|
||||
header.Set("Tus-Version", "1.0.0")
|
||||
@@ -462,9 +471,15 @@ func (handler *UnroutedHandler) PostFileV2(w http.ResponseWriter, r *http.Reques
|
||||
info := FileInfo{
|
||||
MetaData: make(MetaData),
|
||||
}
|
||||
if willCompleteUpload && r.ContentLength != -1 {
|
||||
// If the client wants to perform the upload in one request with Content-Length, we know the final upload size.
|
||||
info.Size = r.ContentLength
|
||||
|
||||
size, sizeIsDeferred, err := getIETFDraftUploadLength(r)
|
||||
if err != nil {
|
||||
handler.sendError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !sizeIsDeferred {
|
||||
info.Size = size
|
||||
} else {
|
||||
// Error out if the storage does not support upload length deferring, but we need it.
|
||||
if !handler.composer.UsesLengthDeferrer {
|
||||
@@ -545,11 +560,14 @@ func (handler *UnroutedHandler) PostFileV2(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
id := info.ID
|
||||
url := handler.absFileURL(r, id)
|
||||
limits := handler.getIETFDraftUploadLimits(info)
|
||||
resp.Header["Location"] = url
|
||||
resp.Header["Upload-Limit"] = limits
|
||||
|
||||
// Send 104 response
|
||||
w.Header().Set("Location", url)
|
||||
w.Header().Set("Upload-Draft-Interop-Version", string(currentUploadDraftInteropVersion))
|
||||
w.Header().Set("Upload-Limit", limits)
|
||||
w.WriteHeader(104)
|
||||
|
||||
handler.Metrics.incUploadsCreated()
|
||||
@@ -673,6 +691,8 @@ func (handler *UnroutedHandler) HeadFile(w http.ResponseWriter, r *http.Request)
|
||||
resp.Header["Upload-Defer-Length"] = UploadLengthDeferred
|
||||
} else {
|
||||
resp.Header["Upload-Length"] = strconv.FormatInt(info.Size, 10)
|
||||
// TODO: Shouldn't this rather be offset? Basically, whatever GET would return.
|
||||
// But this then also depends on the storage backend if that's even supported.
|
||||
resp.Header["Content-Length"] = strconv.FormatInt(info.Size, 10)
|
||||
}
|
||||
|
||||
@@ -682,6 +702,12 @@ func (handler *UnroutedHandler) HeadFile(w http.ResponseWriter, r *http.Request)
|
||||
setIETFDraftUploadComplete(r, resp, isUploadCompleteNow)
|
||||
resp.Header["Upload-Draft-Interop-Version"] = string(getIETFDraftInteropVersion(r))
|
||||
|
||||
if !info.SizeIsDeferred {
|
||||
resp.Header["Upload-Length"] = strconv.FormatInt(info.Size, 10)
|
||||
}
|
||||
|
||||
resp.Header["Upload-Limit"] = handler.getIETFDraftUploadLimits(info)
|
||||
|
||||
// Draft -01 and -02 require a 204 No Content response. Version -03 allows 200 OK as well,
|
||||
// but we stick to 204 to not make the logic less complex.
|
||||
resp.StatusCode = http.StatusNoContent
|
||||
@@ -697,12 +723,20 @@ func (handler *UnroutedHandler) PatchFile(w http.ResponseWriter, r *http.Request
|
||||
|
||||
isTusV1 := !handler.usesIETFDraft(r)
|
||||
|
||||
// Check for presence of application/offset+octet-stream
|
||||
// Check for presence of application/offset+octet-stream (tus v1) or application/partial-upload (IETF draft since -04)
|
||||
if isTusV1 && r.Header.Get("Content-Type") != "application/offset+octet-stream" {
|
||||
handler.sendError(c, ErrInvalidContentType)
|
||||
return
|
||||
}
|
||||
|
||||
if !isTusV1 {
|
||||
currentInteropVersion := getIETFDraftInteropVersion(r)
|
||||
if currentInteropVersion != interopVersion3 && currentInteropVersion != interopVersion4 && currentInteropVersion != interopVersion5 && r.Header.Get("Content-Type") != "application/partial-upload" {
|
||||
handler.sendError(c, ErrInvalidContentType)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check for presence of a valid Upload-Offset Header
|
||||
offset, err := strconv.ParseInt(r.Header.Get("Upload-Offset"), 10, 64)
|
||||
if err != nil || offset < 0 {
|
||||
@@ -1185,8 +1219,9 @@ func (handler *UnroutedHandler) terminateUpload(c *httpContext, upload Upload, i
|
||||
func (handler *UnroutedHandler) sendError(c *httpContext, err error) {
|
||||
r := c.req
|
||||
|
||||
detailedErr, ok := err.(Error)
|
||||
if !ok {
|
||||
var detailedErr Error
|
||||
|
||||
if !errors.As(err, &detailedErr) {
|
||||
c.log.Error("InternalServerError", "message", err.Error())
|
||||
detailedErr = NewError("ERR_INTERNAL_SERVER_ERROR", err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -1376,11 +1411,24 @@ func (handler UnroutedHandler) usesIETFDraft(r *http.Request) bool {
|
||||
return handler.config.EnableExperimentalProtocol && interopVersionHeader != ""
|
||||
}
|
||||
|
||||
// getIETFDraftUploadLimits returns the Upload-Limit header for a given upload
|
||||
// according to the set resumable upload draft version from IETF.
|
||||
func (handler UnroutedHandler) getIETFDraftUploadLimits(info FileInfo) string {
|
||||
limits := "min-size=0"
|
||||
if handler.config.MaxSize > 0 {
|
||||
limits += ",max-size=" + strconv.FormatInt(handler.config.MaxSize, 10)
|
||||
} else if !info.SizeIsDeferred {
|
||||
limits += ",max-size=" + strconv.FormatInt(info.Size, 10)
|
||||
}
|
||||
|
||||
return limits
|
||||
}
|
||||
|
||||
// getIETFDraftInteropVersion returns the resumable upload draft interop version from the headers.
|
||||
func getIETFDraftInteropVersion(r *http.Request) draftVersion {
|
||||
version := draftVersion(r.Header.Get("Upload-Draft-Interop-Version"))
|
||||
switch version {
|
||||
case interopVersion3, interopVersion4, interopVersion5:
|
||||
case interopVersion3, interopVersion4, interopVersion5, interopVersion6:
|
||||
return version
|
||||
default:
|
||||
return ""
|
||||
@@ -1392,7 +1440,7 @@ func getIETFDraftInteropVersion(r *http.Request) draftVersion {
|
||||
func isIETFDraftUploadComplete(r *http.Request) bool {
|
||||
currentUploadDraftInteropVersion := getIETFDraftInteropVersion(r)
|
||||
switch currentUploadDraftInteropVersion {
|
||||
case interopVersion4, interopVersion5:
|
||||
case interopVersion4, interopVersion5, interopVersion6:
|
||||
return r.Header.Get("Upload-Complete") == "?1"
|
||||
case interopVersion3:
|
||||
return r.Header.Get("Upload-Incomplete") == "?0"
|
||||
@@ -1413,7 +1461,7 @@ func setIETFDraftUploadComplete(r *http.Request, resp HTTPResponse, isComplete b
|
||||
} else {
|
||||
resp.Header["Upload-Incomplete"] = "?1"
|
||||
}
|
||||
case interopVersion4, interopVersion5:
|
||||
case interopVersion4, interopVersion5, interopVersion6:
|
||||
if isComplete {
|
||||
resp.Header["Upload-Complete"] = "?1"
|
||||
} else {
|
||||
@@ -1422,6 +1470,49 @@ func setIETFDraftUploadComplete(r *http.Request, resp HTTPResponse, isComplete b
|
||||
}
|
||||
}
|
||||
|
||||
// getIETFDraftUploadLength returns the length of an upload as defined in the
|
||||
// resumable upload draft from IETF. This can either be in the Upload-Length
|
||||
// header or in the Content-Length header.
|
||||
func getIETFDraftUploadLength(r *http.Request) (length int64, lengthIsDeferred bool, err error) {
|
||||
var lengthFromUploadLength int64
|
||||
hasLengthFromUploadLength := false
|
||||
var lengthFromContentLength int64
|
||||
hasLengthFromContentLength := false
|
||||
|
||||
willCompleteUpload := isIETFDraftUploadComplete(r)
|
||||
if willCompleteUpload && r.ContentLength != -1 {
|
||||
lengthFromContentLength = r.ContentLength
|
||||
hasLengthFromContentLength = true
|
||||
}
|
||||
|
||||
uploadLengthStr := r.Header.Get("Upload-Length")
|
||||
if uploadLengthStr != "" {
|
||||
var err error
|
||||
lengthFromUploadLength, err = strconv.ParseInt(uploadLengthStr, 10, 64)
|
||||
if err != nil {
|
||||
return 0, false, ErrInvalidUploadLength
|
||||
}
|
||||
|
||||
hasLengthFromUploadLength = true
|
||||
}
|
||||
|
||||
// If both lengths are set, they must match
|
||||
if hasLengthFromContentLength && hasLengthFromUploadLength && lengthFromUploadLength != lengthFromContentLength {
|
||||
return 0, false, ErrInvalidUploadLength
|
||||
}
|
||||
|
||||
// Return whichever length is set
|
||||
if hasLengthFromUploadLength {
|
||||
return lengthFromUploadLength, false, nil
|
||||
}
|
||||
if hasLengthFromContentLength {
|
||||
return lengthFromContentLength, false, nil
|
||||
}
|
||||
|
||||
// No length set, so it's deferred
|
||||
return 0, true, nil
|
||||
}
|
||||
|
||||
// ParseMetadataHeader parses the Upload-Metadata header as defined in the
|
||||
// File Creation extension.
|
||||
// e.g. Upload-Metadata: name bHVucmpzLnBuZw==,type aW1hZ2UvcG5n
|
||||
|
||||
Reference in New Issue
Block a user