123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- package swift
- import (
- "context"
- "errors"
- "fmt"
- "io"
- "net/http"
- "strings"
- "time"
- "github.com/ncw/swift/v2"
- "github.com/imgproxy/imgproxy/v3/config"
- "github.com/imgproxy/imgproxy/v3/ctxreader"
- )
- type transport struct {
- con *swift.Connection
- }
- func New() (http.RoundTripper, error) {
- c := &swift.Connection{
- UserName: config.SwiftUsername,
- ApiKey: config.SwiftAPIKey,
- AuthUrl: config.SwiftAuthURL,
- AuthVersion: config.SwiftAuthVersion,
- Domain: config.SwiftDomain, // v3 auth only
- Tenant: config.SwiftTenant, // v2 auth only
- Timeout: time.Duration(config.SwiftTimeoutSeconds) * time.Second,
- ConnectTimeout: time.Duration(config.SwiftConnectTimeoutSeconds) * time.Second,
- }
- ctx := context.Background()
- err := c.Authenticate(ctx)
- if err != nil {
- return nil, fmt.Errorf("swift authentication error: %s", err)
- }
- return transport{con: c}, nil
- }
- func (t transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
- // Users should have converted the object storage URL in the format of swift://{container}/{object}
- container := req.URL.Host
- objectName := strings.TrimPrefix(req.URL.Path, "/")
- reqHeaders := make(swift.Headers)
- if r := req.Header.Get("Range"); len(r) > 0 {
- reqHeaders["Range"] = r
- }
- object, objectHeaders, err := t.con.ObjectOpen(req.Context(), container, objectName, false, reqHeaders)
- header := make(http.Header)
- if err != nil {
- if errors.Is(err, swift.ObjectNotFound) || errors.Is(err, swift.ContainerNotFound) {
- return &http.Response{
- StatusCode: http.StatusNotFound,
- Proto: "HTTP/1.0",
- ProtoMajor: 1,
- ProtoMinor: 0,
- Header: header,
- Body: io.NopCloser(strings.NewReader(err.Error())),
- Close: false,
- Request: req,
- }, nil
- }
- return nil, fmt.Errorf("error opening object: %v", err)
- }
- if config.ETagEnabled {
- if etag, ok := objectHeaders["Etag"]; ok {
- header.Set("ETag", etag)
- if len(etag) > 0 && etag == req.Header.Get("If-None-Match") {
- object.Close()
- 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
- }
- }
- }
- for k, v := range objectHeaders {
- header.Set(k, v)
- }
- return &http.Response{
- Status: "200 OK",
- StatusCode: 200,
- Proto: "HTTP/1.0",
- ProtoMajor: 1,
- ProtoMinor: 0,
- Header: header,
- Body: ctxreader.New(req.Context(), object, true),
- Close: true,
- Request: req,
- }, nil
- }
|