Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions gov2/s3/actions/presigner.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,22 @@ func (presigner Presigner) DeleteObject(bucketName string, objectKey string) (*v
}

// snippet-end:[gov2.s3.PresignDeleteObject]

// snippet-start:[gov2.s3.PresignPostObject]

func (presigner Presigner) PresignPostObject(bucketName string, objectKey string, lifetimeSecs int64) (*s3.PresignedPostRequest, error) {
request, err := presigner.PresignClient.PresignPostObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(objectKey),
}, func(options *s3.PresignPostOptions) {
options.Expires = time.Duration(lifetimeSecs) * time.Second
})
if err != nil {
log.Printf("Couldn't get a presigned post request to put %v:%v. Here's why: %v\n", bucketName, objectKey, err)
}
return request, nil
}

// snippet-end:[gov2.s3.PresignPostObject]

// snippet-end:[gov2.s3.Presigner.complete]
12 changes: 6 additions & 6 deletions gov2/s3/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import (
//
// `-scenario` can be one of the following:
//
// * `getstarted` - Runs the interactive get started scenario that shows you how to use
// Amazon Simple Storage Service (Amazon S3) actions to work with
// S3 buckets and objects.
// * `presigning` - Runs the interactive presigning scenario that shows you how to
// get presigned requests that contain temporary credentials
// and can be used to make requests from any HTTP client.
// - `getstarted` - Runs the interactive get started scenario that shows you how to use
// Amazon Simple Storage Service (Amazon S3) actions to work with
// S3 buckets and objects.
// - `presigning` - Runs the interactive presigning scenario that shows you how to
// get presigned requests that contain temporary credentials
// and can be used to make requests from any HTTP client.
func main() {
scenarioMap := map[string]func(sdkConfig aws.Config){
"getstarted": runGetStartedScenario,
Expand Down
14 changes: 0 additions & 14 deletions gov2/s3/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,7 @@ github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools v0.0.0-20240907001412-a93
github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools v0.0.0-20240907001412-a9375541143b/go.mod h1:iBzksyiv5HVU+cymGDQbbvcecca+rsARJlDFL8np8oE=
github.com/awsdocs/aws-doc-sdk-examples/gov2/testtools v0.0.0-20240907001412-a9375541143b h1:UmPy4pArM7SIhTX2Xn5bhOkgI9onSUQ1Y9fxgDJ3pHU=
github.com/awsdocs/aws-doc-sdk-examples/gov2/testtools v0.0.0-20240907001412-a9375541143b/go.mod h1:9Oj/8PZn3D5Ftp/Z1QWrIEFE0daERMqfJawL9duHRfc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
69 changes: 68 additions & 1 deletion gov2/s3/scenarios/scenario_presigning.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
package scenarios

import (
"bytes"
"fmt"
"io"
"log"
"mime/multipart"
"net/http"
"os"
"strings"
Expand All @@ -23,6 +25,7 @@ import (
// unit testing.
type IHttpRequester interface {
Get(url string) (resp *http.Response, err error)
Post(url, contentType string, body io.Reader) (resp *http.Response, err error)
Put(url string, contentLength int64, body io.Reader) (resp *http.Response, err error)
Delete(url string) (resp *http.Response, err error)
}
Expand All @@ -33,6 +36,15 @@ type HttpRequester struct{}
func (httpReq HttpRequester) Get(url string) (resp *http.Response, err error) {
return http.Get(url)
}
func (httpReq HttpRequester) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
postRequest, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
}
postRequest.Header.Set("Content-Type", contentType)
return http.DefaultClient.Do(postRequest)
}

func (httpReq HttpRequester) Put(url string, contentLength int64, body io.Reader) (resp *http.Response, err error) {
putRequest, err := http.NewRequest("PUT", url, body)
if err != nil {
Expand All @@ -51,6 +63,43 @@ func (httpReq HttpRequester) Delete(url string) (resp *http.Response, err error)

// snippet-end:[gov2.s3.IHttpRequester.helper]

// snippet-start:[gov2.s3.MultipartUpload.helper]
func sendMultipartRequest(url string, fields map[string]string, file *os.File, filePath string, httpRequester IHttpRequester) (*http.Response, error) {
// Create a buffer to hold the multipart data
var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody)

// Add form fields
for key, val := range fields {
err := writer.WriteField(key, val)
if err != nil {
return nil, err
}
}

// Always has to be named like this, and always has to be the last one
fileField := "file"
part, err := writer.CreateFormFile(fileField, filePath)
if err != nil {
return nil, err
}
_, err = io.Copy(part, file)
if err != nil {
return nil, err
}

// Close the writer to finalize the multipart message
err = writer.Close()
if err != nil {
return nil, err
}

// make the request
return httpRequester.Post(url, writer.FormDataContentType(), &requestBody)
}

// snippet-end:[gov2.s3.MultipartUpload.helper]

// snippet-start:[gov2.s3.Scenario_Presigning]

// RunPresigningScenario is an interactive example that shows you how to get presigned
Expand All @@ -76,7 +125,7 @@ func (httpReq HttpRequester) Delete(url string) (resp *http.Response, err error)
func RunPresigningScenario(sdkConfig aws.Config, questioner demotools.IQuestioner, httpRequester IHttpRequester) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Something went wrong with the demo.")
fmt.Printf("Something went wrong with the demo")
}
}()

Expand Down Expand Up @@ -159,6 +208,24 @@ func RunPresigningScenario(sdkConfig aws.Config, questioner demotools.IQuestione
log.Println(string(downloadBody[:100]))
log.Println(strings.Repeat("-", 88))

log.Println("Now we'll create a new request to put the same object using a presigned post request")
presignPostRequest, err := presigner.PresignPostObject(bucketName, uploadKey, 60)
if err != nil {
panic(err)
}
log.Printf("Got a presigned post request to url %v with values %v\n", presignPostRequest.URL, presignPostRequest.Values)
log.Println("Using net/http multipart to send the request...")
uploadFile, err = os.Open(uploadFilename)
if err != nil {
panic(err)
}
defer uploadFile.Close()
multiPartResponse, err := sendMultipartRequest(presignPostRequest.URL, presignPostRequest.Values, uploadFile, uploadKey, httpRequester)
if err != nil {
panic(err)
}
log.Printf("Presign post object %v with presigned URL returned %v.", uploadKey, multiPartResponse.StatusCode)

log.Println("Let's presign a request to delete the object.")
questioner.Ask("Press Enter when you're ready.")
presignedDelRequest, err := presigner.DeleteObject(bucketName, uploadKey)
Expand Down
4 changes: 4 additions & 0 deletions gov2/s3/scenarios/scenario_presigning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type MockHttpRequester struct {
func (httpReq MockHttpRequester) Get(url string) (resp *http.Response, err error) {
return &http.Response{Status: "Testing", StatusCode: 200, Body: httpReq.GetBody}, nil
}
func (httpReq MockHttpRequester) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
return &http.Response{Status: "Testing", StatusCode: 200}, nil
}
func (httpReq MockHttpRequester) Put(url string, contentLength int64, body io.Reader) (resp *http.Response, err error) {
return &http.Response{Status: "Testing", StatusCode: 200}, nil
}
Expand Down Expand Up @@ -68,6 +71,7 @@ func (scenTest *PresigningScenarioTest) SetupDataAndStubs() []testtools.Stub {
stubList = append(stubList, stubs.StubCreateBucket(bucketName, testConfig.Region, nil))
stubList = append(stubList, stubs.StubPresignedRequest("PUT", bucketName, objectKey, nil))
stubList = append(stubList, stubs.StubPresignedRequest("GET", bucketName, objectKey, nil))
stubList = append(stubList, stubs.StubPresignedRequest("POST", bucketName, objectKey, nil))
stubList = append(stubList, stubs.StubPresignedRequest("DELETE", bucketName, objectKey, nil))

return stubList
Expand Down
10 changes: 10 additions & 0 deletions gov2/s3/stubs/bucket_basics_stubs.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,16 @@ func StubPresignedRequest(method string, bucketName string, objectKey string, ra
case "DELETE":
opName = "DeleteObject"
input = &s3.DeleteObjectInput{Bucket: aws.String(bucketName), Key: aws.String(objectKey)}
case "POST":
opName = "PutObject"
input = &s3.PutObjectInput{Bucket: aws.String(bucketName), Key: aws.String(objectKey)}
// special case since the object here is different
return testtools.Stub{
OperationName: opName,
Input: input,
Output: &s3.PresignedPostRequest{URL: "test-url", Values: map[string]string{}},
Error: raiseErr,
}
}
return testtools.Stub{
OperationName: opName,
Expand Down
Loading