mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-12-23 22:11:10 +02:00
127 lines
3.4 KiB
Go
127 lines
3.4 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/httpheaders"
|
|
)
|
|
|
|
// Reader represents a generic storage interface, which can read
|
|
// objects from a storage backend.
|
|
type Reader interface {
|
|
// GetObject retrieves an object from the storage and returns
|
|
// ObjectReader with the result.
|
|
GetObject(
|
|
ctx context.Context,
|
|
reqHeader http.Header,
|
|
bucket, key, query string,
|
|
) (*ObjectReader, error)
|
|
}
|
|
|
|
// ObjectReader represents a generic reader for a storage object.
|
|
// It can be in any state: success, error, not found, etc.
|
|
// It can be converted to HTTP response or used as-is.
|
|
type ObjectReader struct {
|
|
Status int // HTTP status code
|
|
Headers http.Header // Response headers harvested from the engine response
|
|
Body io.ReadCloser // Response body reader
|
|
contentLength int64
|
|
}
|
|
|
|
// NewObjectOK creates a new Reader with a 200 OK status.
|
|
func NewObjectOK(headers http.Header, body io.ReadCloser) *ObjectReader {
|
|
return &ObjectReader{
|
|
Status: http.StatusOK,
|
|
Headers: headers,
|
|
Body: body,
|
|
contentLength: -1, // is set in Response()
|
|
}
|
|
}
|
|
|
|
// NewObjectPartialContent creates a new Reader with a 206 Partial Content status.
|
|
func NewObjectPartialContent(headers http.Header, body io.ReadCloser) *ObjectReader {
|
|
return &ObjectReader{
|
|
Status: http.StatusPartialContent,
|
|
Headers: headers,
|
|
Body: body,
|
|
contentLength: -1, // is set in Response()
|
|
}
|
|
}
|
|
|
|
// NewObjectNotFound creates a new Reader with a 404 Not Found status.
|
|
func NewObjectNotFound(message string) *ObjectReader {
|
|
return NewObjectError(http.StatusNotFound, message)
|
|
}
|
|
|
|
// NewObjectError creates a new Reader with a custom status code
|
|
func NewObjectError(statusCode int, message string) *ObjectReader {
|
|
return &ObjectReader{
|
|
Status: statusCode,
|
|
Body: io.NopCloser(strings.NewReader(message)),
|
|
Headers: http.Header{httpheaders.ContentType: {"text/plain"}},
|
|
contentLength: int64(len(message)),
|
|
}
|
|
}
|
|
|
|
// NewObjectNotModified creates a new Reader with a 304 Not Modified status.
|
|
func NewObjectNotModified(headers http.Header) *ObjectReader {
|
|
// Copy headers relevant to NotModified response only
|
|
nmHeaders := make(http.Header)
|
|
httpheaders.Copy(
|
|
headers,
|
|
nmHeaders,
|
|
[]string{httpheaders.Etag, httpheaders.LastModified},
|
|
)
|
|
|
|
return &ObjectReader{
|
|
Status: http.StatusNotModified,
|
|
Headers: nmHeaders,
|
|
contentLength: 0,
|
|
}
|
|
}
|
|
|
|
// NewObjectInvalidRange creates a new Reader with a 416 Range Not Satisfiable status.
|
|
func NewObjectInvalidRange() *ObjectReader {
|
|
return &ObjectReader{
|
|
Status: http.StatusRequestedRangeNotSatisfiable,
|
|
contentLength: 0,
|
|
}
|
|
}
|
|
|
|
// ContentLength returns the content length of the response.
|
|
func (r *ObjectReader) ContentLength() int64 {
|
|
if r.contentLength > 0 {
|
|
return r.contentLength
|
|
}
|
|
|
|
h := r.Headers.Get(httpheaders.ContentLength)
|
|
if len(h) > 0 {
|
|
p, err := strconv.ParseInt(h, 10, 64)
|
|
if err != nil {
|
|
return p
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
// Response converts Reader to http.Response
|
|
func (r *ObjectReader) Response(req *http.Request) *http.Response {
|
|
return &http.Response{
|
|
Status: http.StatusText(r.Status),
|
|
StatusCode: r.Status,
|
|
Proto: "HTTP/1.0",
|
|
ProtoMajor: 1,
|
|
ProtoMinor: 0,
|
|
Header: r.Headers,
|
|
Body: r.Body,
|
|
Close: true,
|
|
Request: req,
|
|
ContentLength: r.ContentLength(),
|
|
}
|
|
}
|