mirror of
https://github.com/minio/minio.git
synced 2025-11-10 05:59:43 -05:00
getObject: Add support for special response headers.
Supports now response-content-type, response-content-disposition, response-cache-control, response-expires.
This commit is contained in:
2
vendor/github.com/minio/minio-go/CONTRIBUTING.md
generated
vendored
2
vendor/github.com/minio/minio-go/CONTRIBUTING.md
generated
vendored
@@ -14,7 +14,7 @@
|
||||
- Have test cases for the new code. If you have questions about how to do it, please ask in your pull request.
|
||||
- Run `go fmt`
|
||||
- Squash your commits into a single commit. `git rebase -i`. It's okay to force update your pull request.
|
||||
- Make sure `go test -race ./...` and `go build` completes.
|
||||
- Make sure `go test -short -race ./...` and `go build` completes.
|
||||
|
||||
* Read [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project
|
||||
- `minio-go` project is strictly conformant with Golang style
|
||||
|
||||
2
vendor/github.com/minio/minio-go/README.md
generated
vendored
2
vendor/github.com/minio/minio-go/README.md
generated
vendored
@@ -83,7 +83,7 @@ func main() {
|
||||
* [FGetObject(bucketName, objectName, filePath) error](examples/s3/fgetobject.go)
|
||||
|
||||
### Presigned Operations.
|
||||
* [PresignedGetObject(bucketName, objectName, time.Duration) (string, error)](examples/s3/presignedgetobject.go)
|
||||
* [PresignedGetObject(bucketName, objectName, time.Duration, url.Values) (string, error)](examples/s3/presignedgetobject.go)
|
||||
* [PresignedPutObject(bucketName, objectName, time.Duration) (string, error)](examples/s3/presignedputobject.go)
|
||||
* [PresignedPostPolicy(NewPostPolicy()) (map[string]string, error)](examples/s3/presignedpostpolicy.go)
|
||||
|
||||
|
||||
56
vendor/github.com/minio/minio-go/api-presigned.go
generated
vendored
56
vendor/github.com/minio/minio-go/api-presigned.go
generated
vendored
@@ -18,13 +18,26 @@ package minio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// supportedGetReqParams - supported request parameters for GET
|
||||
// presigned request.
|
||||
var supportedGetReqParams = map[string]struct{}{
|
||||
"response-expires": struct{}{},
|
||||
"response-content-type": struct{}{},
|
||||
"response-cache-control": struct{}{},
|
||||
"response-content-disposition": struct{}{},
|
||||
}
|
||||
|
||||
// presignURL - Returns a presigned URL for an input 'method'.
|
||||
// Expires maximum is 7days - ie. 604800 and minimum is 1.
|
||||
func (c Client) presignURL(method string, bucketName string, objectName string, expires time.Duration) (url string, err error) {
|
||||
func (c Client) presignURL(method string, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (urlStr string, err error) {
|
||||
// Input validation.
|
||||
if method == "" {
|
||||
return "", ErrInvalidArgument("method cannot be empty.")
|
||||
}
|
||||
if err := isValidBucketName(bucketName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -35,35 +48,50 @@ func (c Client) presignURL(method string, bucketName string, objectName string,
|
||||
return "", err
|
||||
}
|
||||
|
||||
if method == "" {
|
||||
return "", ErrInvalidArgument("method cannot be empty.")
|
||||
}
|
||||
|
||||
// Convert expires into seconds.
|
||||
expireSeconds := int64(expires / time.Second)
|
||||
// Instantiate a new request.
|
||||
// Since expires is set newRequest will presign the request.
|
||||
req, err := c.newRequest(method, requestMetadata{
|
||||
reqMetadata := requestMetadata{
|
||||
presignURL: true,
|
||||
bucketName: bucketName,
|
||||
objectName: objectName,
|
||||
expires: expireSeconds,
|
||||
})
|
||||
}
|
||||
|
||||
// For "GET" we are handling additional request parameters to
|
||||
// override its response headers.
|
||||
if method == "GET" {
|
||||
// Verify if input map has unsupported params, if yes exit.
|
||||
for k := range reqParams {
|
||||
if _, ok := supportedGetReqParams[k]; !ok {
|
||||
return "", ErrInvalidArgument(k + " unsupported request parameter for presigned GET.")
|
||||
}
|
||||
}
|
||||
// Save the request parameters to be used in presigning for
|
||||
// GET request.
|
||||
reqMetadata.queryValues = reqParams
|
||||
}
|
||||
|
||||
// Instantiate a new request.
|
||||
// Since expires is set newRequest will presign the request.
|
||||
req, err := c.newRequest(method, reqMetadata)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return req.URL.String(), nil
|
||||
}
|
||||
|
||||
// PresignedGetObject - Returns a presigned URL to access an object without credentials.
|
||||
// Expires maximum is 7days - ie. 604800 and minimum is 1.
|
||||
func (c Client) PresignedGetObject(bucketName string, objectName string, expires time.Duration) (url string, err error) {
|
||||
return c.presignURL("GET", bucketName, objectName, expires)
|
||||
// PresignedGetObject - Returns a presigned URL to access an object
|
||||
// without credentials. Expires maximum is 7days - ie. 604800 and
|
||||
// minimum is 1. Additionally you can override a set of response
|
||||
// headers using the query parameters.
|
||||
func (c Client) PresignedGetObject(bucketName string, objectName string, expires time.Duration, reqParams url.Values) (url string, err error) {
|
||||
return c.presignURL("GET", bucketName, objectName, expires, reqParams)
|
||||
}
|
||||
|
||||
// PresignedPutObject - Returns a presigned URL to upload an object without credentials.
|
||||
// Expires maximum is 7days - ie. 604800 and minimum is 1.
|
||||
func (c Client) PresignedPutObject(bucketName string, objectName string, expires time.Duration) (url string, err error) {
|
||||
return c.presignURL("PUT", bucketName, objectName, expires)
|
||||
return c.presignURL("PUT", bucketName, objectName, expires, nil)
|
||||
}
|
||||
|
||||
// PresignedPostPolicy - Returns POST form data to upload an object at a location.
|
||||
|
||||
2
vendor/github.com/minio/minio-go/api-put-object-common.go
generated
vendored
2
vendor/github.com/minio/minio-go/api-put-object-common.go
generated
vendored
@@ -55,7 +55,7 @@ func shouldUploadPart(objPart objectPart, objectParts map[int]objectPart) bool {
|
||||
return true
|
||||
}
|
||||
// if md5sum mismatches should upload the part.
|
||||
if objPart.ETag == uploadedPart.ETag {
|
||||
if objPart.ETag != uploadedPart.ETag {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
8
vendor/github.com/minio/minio-go/api.go
generated
vendored
8
vendor/github.com/minio/minio-go/api.go
generated
vendored
@@ -325,6 +325,12 @@ func (c Client) do(req *http.Request) (*http.Response, error) {
|
||||
// execute the request.
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
// Handle this specifically for now until future Golang
|
||||
// versions fix this issue properly.
|
||||
urlErr, ok := err.(*url.Error)
|
||||
if ok && strings.Contains(urlErr.Err.Error(), "EOF") {
|
||||
return nil, fmt.Errorf("Connection closed by foreign host %s. Retry again.", urlErr.URL)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
// If trace is enabled, dump http request and response.
|
||||
@@ -516,7 +522,7 @@ type CloudStorageClient interface {
|
||||
PutObjectWithProgress(bucketName, objectName string, reader io.Reader, contentType string, progress io.Reader) (n int64, err error)
|
||||
|
||||
// Presigned operations.
|
||||
PresignedGetObject(bucketName, objectName string, expires time.Duration) (presignedURL string, err error)
|
||||
PresignedGetObject(bucketName, objectName string, expires time.Duration, reqParams url.Values) (presignedURL string, err error)
|
||||
PresignedPutObject(bucketName, objectName string, expires time.Duration) (presignedURL string, err error)
|
||||
PresignedPostPolicy(*PostPolicy) (formData map[string]string, err error)
|
||||
|
||||
|
||||
34
vendor/github.com/minio/minio-go/api_functional_v2_test.go
generated
vendored
34
vendor/github.com/minio/minio-go/api_functional_v2_test.go
generated
vendored
@@ -24,6 +24,7 @@ import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -954,11 +955,12 @@ func TestFunctionalV2(t *testing.T) {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
|
||||
presignedGetURL, err := c.PresignedGetObject(bucketName, objectName, 3600*time.Second)
|
||||
// Generate presigned GET object url.
|
||||
presignedGetURL, err := c.PresignedGetObject(bucketName, objectName, 3600*time.Second, nil)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
|
||||
// Verify if presigned url works.
|
||||
resp, err := http.Get(presignedGetURL)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
@@ -974,6 +976,34 @@ func TestFunctionalV2(t *testing.T) {
|
||||
t.Fatal("Error: bytes mismatch.")
|
||||
}
|
||||
|
||||
// Set request parameters.
|
||||
reqParams := make(url.Values)
|
||||
reqParams.Set("response-content-disposition", "attachment; filename=\"test.txt\"")
|
||||
// Generate presigned GET object url.
|
||||
presignedGetURL, err = c.PresignedGetObject(bucketName, objectName, 3600*time.Second, reqParams)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
// Verify if presigned url works.
|
||||
resp, err = http.Get(presignedGetURL)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatal("Error: ", resp.Status)
|
||||
}
|
||||
newPresignedBytes, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
if !bytes.Equal(newPresignedBytes, buf) {
|
||||
t.Fatal("Error: bytes mismatch for presigned GET url.")
|
||||
}
|
||||
// Verify content disposition.
|
||||
if resp.Header.Get("Content-Disposition") != "attachment; filename=\"test.txt\"" {
|
||||
t.Fatalf("Error: wrong Content-Disposition received %s", resp.Header.Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
presignedPutURL, err := c.PresignedPutObject(bucketName, objectName+"-presigned", 3600*time.Second)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
|
||||
31
vendor/github.com/minio/minio-go/api_functional_v4_test.go
generated
vendored
31
vendor/github.com/minio/minio-go/api_functional_v4_test.go
generated
vendored
@@ -24,6 +24,7 @@ import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -983,11 +984,13 @@ func TestFunctional(t *testing.T) {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
|
||||
presignedGetURL, err := c.PresignedGetObject(bucketName, objectName, 3600*time.Second)
|
||||
// Generate presigned GET object url.
|
||||
presignedGetURL, err := c.PresignedGetObject(bucketName, objectName, 3600*time.Second, nil)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
|
||||
// Verify if presigned url works.
|
||||
resp, err := http.Get(presignedGetURL)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
@@ -1003,6 +1006,32 @@ func TestFunctional(t *testing.T) {
|
||||
t.Fatal("Error: bytes mismatch.")
|
||||
}
|
||||
|
||||
// Set request parameters.
|
||||
reqParams := make(url.Values)
|
||||
reqParams.Set("response-content-disposition", "attachment; filename=\"test.txt\"")
|
||||
presignedGetURL, err = c.PresignedGetObject(bucketName, objectName, 3600*time.Second, reqParams)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
// Verify if presigned url works.
|
||||
resp, err = http.Get(presignedGetURL)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatal("Error: ", resp.Status)
|
||||
}
|
||||
newPresignedBytes, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
}
|
||||
if !bytes.Equal(newPresignedBytes, buf) {
|
||||
t.Fatal("Error: bytes mismatch for presigned GET URL.")
|
||||
}
|
||||
if resp.Header.Get("Content-Disposition") != "attachment; filename=\"test.txt\"" {
|
||||
t.Fatalf("Error: wrong Content-Disposition received %s", resp.Header.Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
presignedPutURL, err := c.PresignedPutObject(bucketName, objectName+"-presigned", 3600*time.Second)
|
||||
if err != nil {
|
||||
t.Fatal("Error: ", err)
|
||||
|
||||
17
vendor/github.com/minio/minio-go/request-signature-v2.go
generated
vendored
17
vendor/github.com/minio/minio-go/request-signature-v2.go
generated
vendored
@@ -73,6 +73,11 @@ func preSignV2(req http.Request, accessKeyID, secretAccessKey string, expires in
|
||||
|
||||
// Get encoded URL path.
|
||||
path := encodeURL2Path(req.URL)
|
||||
if len(req.URL.Query()) > 0 {
|
||||
// Keep the usual queries unescaped for string to sign.
|
||||
query, _ := url.QueryUnescape(queryEncode(req.URL.Query()))
|
||||
path = path + "?" + query
|
||||
}
|
||||
|
||||
// Find epoch expires when the request will expire.
|
||||
epochExpires := d.Unix() + expires
|
||||
@@ -93,12 +98,16 @@ func preSignV2(req http.Request, accessKeyID, secretAccessKey string, expires in
|
||||
query.Set("AWSAccessKeyId", accessKeyID)
|
||||
}
|
||||
|
||||
// Fill in Expires and Signature for presigned query.
|
||||
// Fill in Expires for presigned query.
|
||||
query.Set("Expires", strconv.FormatInt(epochExpires, 10))
|
||||
query.Set("Signature", signature)
|
||||
|
||||
// Encode query and save.
|
||||
req.URL.RawQuery = query.Encode()
|
||||
req.URL.RawQuery = queryEncode(query)
|
||||
|
||||
// Save signature finally.
|
||||
req.URL.RawQuery += "&Signature=" + urlEncodePath(signature)
|
||||
|
||||
// Return.
|
||||
return &req
|
||||
}
|
||||
|
||||
@@ -283,7 +292,7 @@ func writeCanonicalizedResource(buf *bytes.Buffer, req http.Request) {
|
||||
// Request parameters
|
||||
if len(vv[0]) > 0 {
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(url.QueryEscape(vv[0]))
|
||||
buf.WriteString(strings.Replace(url.QueryEscape(vv[0]), "+", "%20", -1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user