remote_store.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package client
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "net/url"
  7. "path"
  8. "strconv"
  9. "strings"
  10. "time"
  11. )
  12. type HTTPRemoteOptions struct {
  13. MetadataPath string
  14. TargetsPath string
  15. UserAgent string
  16. Retries *HTTPRemoteRetries
  17. }
  18. type HTTPRemoteRetries struct {
  19. Delay time.Duration
  20. Total time.Duration
  21. }
  22. var DefaultHTTPRetries = &HTTPRemoteRetries{
  23. Delay: time.Second,
  24. Total: 10 * time.Second,
  25. }
  26. func HTTPRemoteStore(baseURL string, opts *HTTPRemoteOptions, client *http.Client) (RemoteStore, error) {
  27. if !strings.HasPrefix(baseURL, "http") {
  28. return nil, ErrInvalidURL{baseURL}
  29. }
  30. if opts == nil {
  31. opts = &HTTPRemoteOptions{}
  32. }
  33. if opts.TargetsPath == "" {
  34. opts.TargetsPath = "targets"
  35. }
  36. if client == nil {
  37. client = http.DefaultClient
  38. }
  39. return &httpRemoteStore{baseURL, opts, client}, nil
  40. }
  41. type httpRemoteStore struct {
  42. baseURL string
  43. opts *HTTPRemoteOptions
  44. cli *http.Client
  45. }
  46. func (h *httpRemoteStore) GetMeta(name string) (io.ReadCloser, int64, error) {
  47. return h.get(path.Join(h.opts.MetadataPath, name))
  48. }
  49. func (h *httpRemoteStore) GetTarget(name string) (io.ReadCloser, int64, error) {
  50. return h.get(path.Join(h.opts.TargetsPath, name))
  51. }
  52. func (h *httpRemoteStore) get(s string) (io.ReadCloser, int64, error) {
  53. u := h.url(s)
  54. req, err := http.NewRequest("GET", u, nil)
  55. if err != nil {
  56. return nil, 0, err
  57. }
  58. if h.opts.UserAgent != "" {
  59. req.Header.Set("User-Agent", h.opts.UserAgent)
  60. }
  61. var res *http.Response
  62. if r := h.opts.Retries; r != nil {
  63. for start := time.Now(); time.Since(start) < r.Total; time.Sleep(r.Delay) {
  64. res, err = h.cli.Do(req)
  65. if err == nil && (res.StatusCode < 500 || res.StatusCode > 599) {
  66. break
  67. }
  68. }
  69. } else {
  70. res, err = h.cli.Do(req)
  71. }
  72. if err != nil {
  73. return nil, 0, err
  74. }
  75. if res.StatusCode == http.StatusNotFound {
  76. res.Body.Close()
  77. return nil, 0, ErrNotFound{s}
  78. } else if res.StatusCode != http.StatusOK {
  79. res.Body.Close()
  80. return nil, 0, &url.Error{
  81. Op: "GET",
  82. URL: u,
  83. Err: fmt.Errorf("unexpected HTTP status %d", res.StatusCode),
  84. }
  85. }
  86. size, err := strconv.ParseInt(res.Header.Get("Content-Length"), 10, 0)
  87. if err != nil {
  88. return res.Body, -1, nil
  89. }
  90. return res.Body, size, nil
  91. }
  92. func (h *httpRemoteStore) url(path string) string {
  93. if !strings.HasPrefix(path, "/") {
  94. path = "/" + path
  95. }
  96. return h.baseURL + path
  97. }