Repository: rclone/rclone
File: backend/s3/s3.go (Go)

Code before:
	}
	if o.fs.opt.RequesterPays {
		req.RequestPayer = aws.String(s3.RequestPayerRequester)
	}
	if o.fs.opt.SSECustomerAlgorithm != "" {
		req.SSECustomerAlgorithm = &o.fs.opt.SSECustomerAlgorithm
	}
	if o.fs.opt.SSECustomerKey != "" {
		req.SSECustomerKey = &o.fs.opt.SSECustomerKey
	}
	if o.fs.opt.SSECustomerKeyMD5 != "" {
		req.SSECustomerKeyMD5 = &o.fs.opt.SSECustomerKeyMD5
	}
	httpReq, resp := o.fs.c.GetObjectRequest(&req)
	fs.FixRangeOption(options, o.bytes)
	for _, option := range options {
		switch option.(type) {
		case *fs.RangeOption, *fs.SeekOption:
			_, value := option.Header()
			req.Range = &value
		case *fs.HTTPOption:
			key, value := option.Header()
			httpReq.HTTPRequest.Header.Add(key, value)
		default:
			if option.Mandatory() {
				fs.Logf(o, "Unsupported mandatory option: %v", option)
			}
		}
	}
	err = o.fs.pacer.Call(func() (bool, error) {
		var err error
		httpReq.HTTPRequest = httpReq.HTTPRequest.WithContext(ctx)
		err = httpReq.Send()
		return o.fs.shouldRetry(ctx, err)
	})
	if err, ok := err.(awserr.RequestFailure); ok {
		if err.Code() == "InvalidObjectState" {
			return nil, errors.Errorf("Object in GLACIER, restore first: bucket=%q, key=%q", bucket, bucketPath)
		}
	}
	if err != nil {
		return nil, err
	}
	if resp.LastModified == nil {
		fs.Logf(o, "Failed to read last modified: %v", err)
	}
	// read size from ContentLength or ContentRange
	size := resp.ContentLength
	if resp.ContentRange != nil {
		var contentRange = *resp.ContentRange
		slash := strings.IndexRune(contentRange, '/')

Review comment (suggestion):
This should be setting the metadata as is done here if possible.

https://github.com/rclone/rclone/blob/bb0c4ad2d8af8af1ff9abea54dbdf97eadc9018f/backend/s3/s3.go#L3013-L3029

I don't know whether the metadata is returned when objects are fetched via the CDN - I need your advice there!

Code after:
				fs.Debugf(o, "Failed to find parse integer from in %q: %v", contentRange, err)
			}
		} else {
			fs.Debugf(o, "Failed to find length in %q", contentRange)
		}
	}

	lastModified, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
	if err != nil {
		fs.Debugf(o, "Failed to parse last modified from string %s, %v", resp.Header.Get("Last-Modified"), err)
	}

	metaData := make(map[string]*string)
	for key, value := range resp.Header {
		if strings.HasPrefix(key, "x-amz-meta") {
			metaKey := strings.TrimPrefix(key, "x-amz-meta-")
			metaData[strings.Title(metaKey)] = &value[0]
		}
	}

	storageClass := resp.Header.Get("X-Amz-Storage-Class")
	contentType := resp.Header.Get("Content-Type")
	etag := resp.Header.Get("Etag")

	o.setMetaData(&etag, contentLength, &lastModified, metaData, &contentType, &storageClass)
	return resp.Body, err
}

// Open an object for read
func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
	bucket, bucketPath := o.split()

	if o.fs.opt.DownloadURL != "" {
		return o.downloadFromURL(ctx, bucketPath, options...)
	}

	req := s3.GetObjectInput{
		Bucket: &bucket,
		Key:    &bucketPath,
	}
	if o.fs.opt.RequesterPays {
		req.RequestPayer = aws.String(s3.RequestPayerRequester)
	}
	if o.fs.opt.SSECustomerAlgorithm != "" {
		req.SSECustomerAlgorithm = &o.fs.opt.SSECustomerAlgorithm
	}
	if o.fs.opt.SSECustomerKey != "" {
		req.SSECustomerKey = &o.fs.opt.SSECustomerKey
	}
	if o.fs.opt.SSECustomerKeyMD5 != "" {
		req.SSECustomerKeyMD5 = &o.fs.opt.SSECustomerKeyMD5