diff --git a/cmd/api-headers.go b/cmd/api-headers.go index 66ff7c078..7a7b6b320 100644 --- a/cmd/api-headers.go +++ b/cmd/api-headers.go @@ -84,7 +84,7 @@ func setPartsCountHeaders(w http.ResponseWriter, objInfo ObjectInfo) { } // Write object header -func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSpec) (err error) { +func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSpec, opts ObjectOptions) (err error) { // set common headers setCommonHeaders(w) @@ -147,15 +147,26 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSp } } + var start, rangeLen int64 totalObjectSize, err := objInfo.GetActualSize() if err != nil { return err } - // for providing ranged content - start, rangeLen, err := rs.GetOffsetLength(totalObjectSize) - if err != nil { - return err + if opts.PartNumber > 0 { + var start, end int64 + for i := 0; i < len(objInfo.Parts) && i < opts.PartNumber; i++ { + start = end + end = start + objInfo.Parts[i].ActualSize - 1 + } + rs = &HTTPRangeSpec{Start: start, End: end} + rangeLen = end - start + 1 + } else { + // for providing ranged content + start, rangeLen, err = rs.GetOffsetLength(totalObjectSize) + if err != nil { + return err + } } // Set content length. diff --git a/cmd/object-api-utils.go b/cmd/object-api-utils.go index 55543671e..9f0c48d56 100644 --- a/cmd/object-api-utils.go +++ b/cmd/object-api-utils.go @@ -562,6 +562,15 @@ type ObjReaderFn func(inputReader io.Reader, h http.Header, pcfn CheckPreconditi func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, opts ObjectOptions, cleanUpFns ...func()) ( fn ObjReaderFn, off, length int64, err error) { + if rs == nil && opts.PartNumber > 0 { + var start, end int64 + for i := 0; i < len(oi.Parts) && i < opts.PartNumber; i++ { + start = end + end = start + oi.Parts[i].ActualSize - 1 + } + rs = &HTTPRangeSpec{Start: start, End: end} + } + // Call the clean-up functions immediately in case of exit // with error defer func() { diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 377265601..84e5fdd93 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -378,6 +378,22 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req rangeHeader := r.Header.Get(xhttp.Range) if rangeHeader != "" { rs, rangeErr = parseRequestRangeSpec(rangeHeader) + // Handle only errInvalidRange. Ignore other + // parse error and treat it as regular Get + // request like Amazon S3. + if rangeErr == errInvalidRange { + writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRange), r.URL, guessIsBrowserReq(r)) + return + } + if rangeErr != nil { + logger.LogIf(ctx, rangeErr, logger.Application) + } + } + + // Both 'bytes' and 'partNumber' cannot be specified at the same time + if rs != nil && opts.PartNumber > 0 { + writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrBadRequest)) + return } // Validate pre-conditions if any. @@ -393,16 +409,6 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req return true } - // Handle only errInvalidRange. Ignore other - // parse error and treat it as regular Get - // request like Amazon S3. - if rangeErr == errInvalidRange { - writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRange), r.URL, guessIsBrowserReq(r)) - return true - } - if rangeErr != nil { - logger.LogIf(ctx, rangeErr, logger.Application) - } return false } @@ -445,7 +451,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req } } - if err = setObjectHeaders(w, objInfo, rs); err != nil { + if err = setObjectHeaders(w, objInfo, rs, opts); err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) return } @@ -459,7 +465,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req statusCodeWritten := false httpWriter := ioutil.WriteOnClose(w) - if rs != nil { + if rs != nil || opts.PartNumber > 0 { statusCodeWritten = true w.WriteHeader(http.StatusPartialContent) } @@ -613,6 +619,12 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re } } + // Both 'bytes' and 'partNumber' cannot be specified at the same time + if rs != nil && opts.PartNumber > 0 { + writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrBadRequest)) + return + } + // Set encryption response headers if objectAPI.IsEncryptionSupported() { if crypto.IsEncrypted(objInfo.UserDefined) { @@ -632,7 +644,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re } // Set standard object headers. - if err = setObjectHeaders(w, objInfo, rs); err != nil { + if err = setObjectHeaders(w, objInfo, rs, opts); err != nil { writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) return } @@ -646,7 +658,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re setHeadGetRespHeaders(w, r.URL.Query()) // Successful response. - if rs != nil { + if rs != nil || opts.PartNumber > 0 { w.WriteHeader(http.StatusPartialContent) } else { w.WriteHeader(http.StatusOK) diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index 94349bd31..7eb4717bd 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -1330,7 +1330,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) { setPartsCountHeaders(w, objInfo) } - if err = setObjectHeaders(w, objInfo, nil); err != nil { + if err = setObjectHeaders(w, objInfo, nil, opts); err != nil { writeWebErrorResponse(w, err) return }