2020-09-09 00:18:07 +02:00
|
|
|
package store
|
|
|
|
|
|
|
|
import (
|
2020-10-22 19:12:31 +02:00
|
|
|
"io"
|
2020-09-09 00:18:07 +02:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
2020-10-15 02:59:12 +02:00
|
|
|
"github.com/lbryio/reflector.go/internal/metrics"
|
2020-09-09 00:18:07 +02:00
|
|
|
"github.com/lbryio/reflector.go/meta"
|
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
"github.com/lbryio/lbry.go/v2/extras/errors"
|
|
|
|
"github.com/lbryio/lbry.go/v2/stream"
|
|
|
|
|
2020-09-09 00:18:07 +02:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
// CloudFrontStore wraps an S3 store. Reads go to Cloudfront, writes go to S3.
|
|
|
|
type CloudFrontStore struct {
|
|
|
|
s3 *S3Store
|
|
|
|
endpoint string // cloudflare endpoint
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
// NewCloudFrontStore returns an initialized CloudFrontStore store pointer.
|
|
|
|
// NOTE: It panics if S3Store is nil.
|
|
|
|
func NewCloudFrontStore(s3 *S3Store, cfEndpoint string) *CloudFrontStore {
|
|
|
|
if s3 == nil {
|
|
|
|
panic("S3Store must not be nil")
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
return &CloudFrontStore{
|
|
|
|
endpoint: cfEndpoint,
|
|
|
|
s3: s3,
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
2020-10-22 19:12:31 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 19:49:02 +02:00
|
|
|
const nameCloudFront = "cloudfront"
|
|
|
|
|
|
|
|
// Name is the cache type name
|
|
|
|
func (c *CloudFrontStore) Name() string { return nameCloudFront }
|
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
// Has checks if the hash is in the store.
|
|
|
|
func (c *CloudFrontStore) Has(hash string) (bool, error) {
|
|
|
|
status, body, err := c.cfRequest(http.MethodHead, hash)
|
2020-09-09 00:18:07 +02:00
|
|
|
if err != nil {
|
2020-10-22 19:12:31 +02:00
|
|
|
return false, err
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
2020-10-22 19:12:31 +02:00
|
|
|
defer body.Close()
|
2020-09-09 00:18:07 +02:00
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
switch status {
|
2020-09-09 00:18:07 +02:00
|
|
|
case http.StatusNotFound, http.StatusForbidden:
|
|
|
|
return false, nil
|
|
|
|
case http.StatusOK:
|
|
|
|
return true, nil
|
|
|
|
default:
|
2020-10-22 19:12:31 +02:00
|
|
|
return false, errors.Err("unexpected status %d", status)
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
// Get gets the blob from Cloudfront.
|
|
|
|
func (c *CloudFrontStore) Get(hash string) (stream.Blob, error) {
|
2020-09-09 00:18:07 +02:00
|
|
|
log.Debugf("Getting %s from S3", hash[:8])
|
|
|
|
defer func(t time.Time) {
|
|
|
|
log.Debugf("Getting %s from S3 took %s", hash[:8], time.Since(t).String())
|
|
|
|
}(time.Now())
|
2020-10-22 19:12:31 +02:00
|
|
|
|
|
|
|
status, body, err := c.cfRequest(http.MethodGet, hash)
|
2020-09-09 00:18:07 +02:00
|
|
|
if err != nil {
|
2020-10-22 19:12:31 +02:00
|
|
|
return nil, err
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
2020-10-22 19:12:31 +02:00
|
|
|
defer body.Close()
|
2020-09-09 00:18:07 +02:00
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
switch status {
|
2020-09-09 00:18:07 +02:00
|
|
|
case http.StatusNotFound, http.StatusForbidden:
|
|
|
|
return nil, errors.Err(ErrBlobNotFound)
|
|
|
|
case http.StatusOK:
|
2020-10-22 19:12:31 +02:00
|
|
|
b, err := ioutil.ReadAll(body)
|
2020-09-09 00:18:07 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Err(err)
|
|
|
|
}
|
2020-10-15 02:59:12 +02:00
|
|
|
metrics.MtrInBytesS3.Add(float64(len(b)))
|
2020-09-09 00:18:07 +02:00
|
|
|
return b, nil
|
|
|
|
default:
|
2020-10-22 19:12:31 +02:00
|
|
|
return nil, errors.Err("unexpected status %d", status)
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
func (c *CloudFrontStore) cfRequest(method, hash string) (int, io.ReadCloser, error) {
|
|
|
|
url := c.endpoint + hash
|
|
|
|
req, err := http.NewRequest(method, url, nil)
|
|
|
|
if err != nil {
|
|
|
|
return 0, nil, errors.Err(err)
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
2020-10-22 19:12:31 +02:00
|
|
|
req.Header.Add("User-Agent", "reflector.go/"+meta.Version)
|
2020-09-09 00:18:07 +02:00
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return 0, nil, errors.Err(err)
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
2020-10-22 19:12:31 +02:00
|
|
|
|
|
|
|
return res.StatusCode, res.Body, nil
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 19:12:31 +02:00
|
|
|
// Put stores the blob on S3
|
|
|
|
func (c *CloudFrontStore) Put(hash string, blob stream.Blob) error {
|
|
|
|
return c.s3.Put(hash, blob)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PutSD stores the sd blob on S3
|
|
|
|
func (c *CloudFrontStore) PutSD(hash string, blob stream.Blob) error {
|
|
|
|
return c.s3.PutSD(hash, blob)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete deletes the blob from S3
|
|
|
|
func (c *CloudFrontStore) Delete(hash string) error {
|
|
|
|
return c.s3.Delete(hash)
|
2020-09-09 00:18:07 +02:00
|
|
|
}
|