|
@@ -12,6 +12,7 @@ import (
|
|
|
"google.golang.org/api/option"
|
|
|
|
|
|
"github.com/imgproxy/imgproxy/v3/config"
|
|
|
+ "github.com/imgproxy/imgproxy/v3/httprange"
|
|
|
)
|
|
|
|
|
|
// For tests
|
|
@@ -58,40 +59,82 @@ func (t transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
obj = obj.Generation(g)
|
|
|
}
|
|
|
|
|
|
+ var (
|
|
|
+ reader *storage.Reader
|
|
|
+ statusCode int
|
|
|
+ size int64
|
|
|
+ )
|
|
|
+
|
|
|
header := make(http.Header)
|
|
|
|
|
|
- if config.ETagEnabled {
|
|
|
- attrs, err := obj.Attrs(req.Context())
|
|
|
+ if r := req.Header.Get("Range"); len(r) != 0 {
|
|
|
+ start, end, err := httprange.Parse(r)
|
|
|
if err != nil {
|
|
|
- return handleError(req, err)
|
|
|
+ return httprange.InvalidHTTPRangeResponse(req), nil
|
|
|
}
|
|
|
- header.Set("ETag", attrs.Etag)
|
|
|
-
|
|
|
- if etag := req.Header.Get("If-None-Match"); len(etag) > 0 && attrs.Etag == etag {
|
|
|
- return &http.Response{
|
|
|
- StatusCode: http.StatusNotModified,
|
|
|
- Proto: "HTTP/1.0",
|
|
|
- ProtoMajor: 1,
|
|
|
- ProtoMinor: 0,
|
|
|
- Header: header,
|
|
|
- ContentLength: 0,
|
|
|
- Body: nil,
|
|
|
- Close: false,
|
|
|
- Request: req,
|
|
|
- }, nil
|
|
|
+
|
|
|
+ if end != 0 {
|
|
|
+ length := end - start + 1
|
|
|
+ if end < 0 {
|
|
|
+ length = -1
|
|
|
+ }
|
|
|
+
|
|
|
+ reader, err = obj.NewRangeReader(req.Context(), start, length)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if end < 0 || end >= reader.Attrs.Size {
|
|
|
+ end = reader.Attrs.Size - 1
|
|
|
+ }
|
|
|
+
|
|
|
+ size = end - reader.Attrs.StartOffset + 1
|
|
|
+
|
|
|
+ statusCode = http.StatusPartialContent
|
|
|
+ header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", reader.Attrs.StartOffset, end, reader.Attrs.Size))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- reader, err := obj.NewReader(req.Context())
|
|
|
- if err != nil {
|
|
|
- return handleError(req, err)
|
|
|
+ // We haven't initialize reader yet, this means that we need non-ranged reader
|
|
|
+ if reader == nil {
|
|
|
+ if config.ETagEnabled {
|
|
|
+ attrs, err := obj.Attrs(req.Context())
|
|
|
+ if err != nil {
|
|
|
+ return handleError(req, err)
|
|
|
+ }
|
|
|
+ header.Set("ETag", attrs.Etag)
|
|
|
+
|
|
|
+ if etag := req.Header.Get("If-None-Match"); len(etag) > 0 && attrs.Etag == etag {
|
|
|
+ return &http.Response{
|
|
|
+ StatusCode: http.StatusNotModified,
|
|
|
+ Proto: "HTTP/1.0",
|
|
|
+ ProtoMajor: 1,
|
|
|
+ ProtoMinor: 0,
|
|
|
+ Header: header,
|
|
|
+ ContentLength: 0,
|
|
|
+ Body: nil,
|
|
|
+ Close: false,
|
|
|
+ Request: req,
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var err error
|
|
|
+ reader, err = obj.NewReader(req.Context())
|
|
|
+ if err != nil {
|
|
|
+ return handleError(req, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ statusCode = 200
|
|
|
+ size = reader.Attrs.Size
|
|
|
}
|
|
|
|
|
|
+ header.Set("Content-Length", strconv.Itoa(int(size)))
|
|
|
+ header.Set("Content-Type", reader.Attrs.ContentType)
|
|
|
header.Set("Cache-Control", reader.Attrs.CacheControl)
|
|
|
|
|
|
return &http.Response{
|
|
|
- Status: "200 OK",
|
|
|
- StatusCode: 200,
|
|
|
+ StatusCode: statusCode,
|
|
|
Proto: "HTTP/1.0",
|
|
|
ProtoMajor: 1,
|
|
|
ProtoMinor: 0,
|