swift.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package swift
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "github.com/ncw/swift/v2"
  11. "github.com/imgproxy/imgproxy/v3/config"
  12. defaultTransport "github.com/imgproxy/imgproxy/v3/transport"
  13. "github.com/imgproxy/imgproxy/v3/transport/notmodified"
  14. )
  15. type transport struct {
  16. con *swift.Connection
  17. }
  18. func New() (http.RoundTripper, error) {
  19. trans, err := defaultTransport.New(false)
  20. if err != nil {
  21. return nil, err
  22. }
  23. c := &swift.Connection{
  24. UserName: config.SwiftUsername,
  25. ApiKey: config.SwiftAPIKey,
  26. AuthUrl: config.SwiftAuthURL,
  27. AuthVersion: config.SwiftAuthVersion,
  28. Domain: config.SwiftDomain, // v3 auth only
  29. Tenant: config.SwiftTenant, // v2 auth only
  30. Timeout: time.Duration(config.SwiftTimeoutSeconds) * time.Second,
  31. ConnectTimeout: time.Duration(config.SwiftConnectTimeoutSeconds) * time.Second,
  32. Transport: trans,
  33. }
  34. ctx := context.Background()
  35. err = c.Authenticate(ctx)
  36. if err != nil {
  37. return nil, fmt.Errorf("swift authentication error: %s", err)
  38. }
  39. return transport{con: c}, nil
  40. }
  41. func (t transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
  42. // Users should have converted the object storage URL in the format of swift://{container}/{object}
  43. container := req.URL.Host
  44. objectName := strings.TrimPrefix(req.URL.Path, "/")
  45. reqHeaders := make(swift.Headers)
  46. if r := req.Header.Get("Range"); len(r) > 0 {
  47. reqHeaders["Range"] = r
  48. }
  49. object, objectHeaders, err := t.con.ObjectOpen(req.Context(), container, objectName, false, reqHeaders)
  50. header := make(http.Header)
  51. if err != nil {
  52. if errors.Is(err, swift.ObjectNotFound) || errors.Is(err, swift.ContainerNotFound) {
  53. return &http.Response{
  54. StatusCode: http.StatusNotFound,
  55. Proto: "HTTP/1.0",
  56. ProtoMajor: 1,
  57. ProtoMinor: 0,
  58. Header: header,
  59. Body: io.NopCloser(strings.NewReader(err.Error())),
  60. Close: false,
  61. Request: req,
  62. }, nil
  63. }
  64. return nil, fmt.Errorf("error opening object: %v", err)
  65. }
  66. if config.ETagEnabled {
  67. if etag, ok := objectHeaders["Etag"]; ok {
  68. header.Set("ETag", etag)
  69. }
  70. }
  71. if config.LastModifiedEnabled {
  72. if lastModified, ok := objectHeaders["Last-Modified"]; ok {
  73. header.Set("Last-Modified", lastModified)
  74. }
  75. }
  76. if resp := notmodified.Response(req, header); resp != nil {
  77. object.Close()
  78. return resp, nil
  79. }
  80. for k, v := range objectHeaders {
  81. header.Set(k, v)
  82. }
  83. return &http.Response{
  84. Status: "200 OK",
  85. StatusCode: 200,
  86. Proto: "HTTP/1.0",
  87. ProtoMajor: 1,
  88. ProtoMinor: 0,
  89. Header: header,
  90. Body: object,
  91. Close: true,
  92. Request: req,
  93. }, nil
  94. }