Fix handing 206 responses from source with full Content-Range

This commit is contained in:
DarthSim
2025-02-06 17:25:27 +03:00
parent ac2cb25cfc
commit f100d3910b
2 changed files with 47 additions and 3 deletions

View File

@@ -4,6 +4,9 @@
### Added
- Add [IMGPROXY_BASE64_URL_INCLUDES_FILENAME](https://docs.imgproxy.net/latest/configuration/options#IMGPROXY_BASE64_URL_INCLUDES_FILENAME) config.
### Changed
- Treat 206 (Partial Content) responses from a source server as 200 (OK) when they contain a full content range.
## [3.27.2] - 2025-01-27
### Fixed
- Fix preventing requests to `0.0.0.0` when imgproxy is configured to deny loopback addresses.

View File

@@ -7,6 +7,8 @@ import (
"io"
"net/http"
"net/http/cookiejar"
"regexp"
"strconv"
"strings"
"time"
@@ -38,6 +40,8 @@ var (
"Last-Modified",
}
contentRangeRe = regexp.MustCompile(`^bytes ((\d+)-(\d+)|\*)/(\d+|\*)$`)
// For tests
redirectAllRequestsTo string
)
@@ -225,8 +229,46 @@ func requestImage(ctx context.Context, imageURL string, opts DownloadOptions) (*
return nil, func() {}, &ErrorNotModified{Message: "Not Modified", Headers: headersToStore(res)}
}
if res.StatusCode != 200 {
body, _ := io.ReadAll(res.Body)
// If the source responds with 206, check if the response contains entire image.
// If not, return an error.
if res.StatusCode == http.StatusPartialContent {
contentRange := res.Header.Get("Content-Range")
rangeParts := contentRangeRe.FindStringSubmatch(contentRange)
if len(rangeParts) == 0 {
res.Body.Close()
reqCancel()
return nil, func() {}, ierrors.New(404, "Partial response with invalid Content-Range header", msgSourceImageIsUnreachable)
}
if rangeParts[1] == "*" || rangeParts[2] != "0" {
res.Body.Close()
reqCancel()
return nil, func() {}, ierrors.New(404, "Partial response with incomplete content", msgSourceImageIsUnreachable)
}
contentLengthStr := rangeParts[4]
if contentLengthStr == "*" {
contentLengthStr = res.Header.Get("Content-Length")
}
contentLength, _ := strconv.Atoi(contentLengthStr)
rangeEnd, _ := strconv.Atoi(rangeParts[3])
if contentLength <= 0 || rangeEnd != contentLength-1 {
res.Body.Close()
reqCancel()
return nil, func() {}, ierrors.New(404, "Partial response with incomplete content", msgSourceImageIsUnreachable)
}
} else if res.StatusCode != http.StatusOK {
var msg string
if strings.HasPrefix(res.Header.Get("Content-Type"), "text/") {
body, _ := io.ReadAll(io.LimitReader(res.Body, 1024))
msg = fmt.Sprintf("Status: %d; %s", res.StatusCode, string(body))
} else {
msg = fmt.Sprintf("Status: %d", res.StatusCode)
}
res.Body.Close()
reqCancel()
@@ -235,7 +277,6 @@ func requestImage(ctx context.Context, imageURL string, opts DownloadOptions) (*
status = 500
}
msg := fmt.Sprintf("Status: %d; %s", res.StatusCode, string(body))
return nil, func() {}, ierrors.New(status, msg, msgSourceImageIsUnreachable)
}