You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1162 lines
30 KiB

  1. //
  2. // goamz - Go packages to interact with the Amazon Web Services.
  3. //
  4. // https://wiki.ubuntu.com/goamz
  5. //
  6. // Copyright (c) 2011 Canonical Ltd.
  7. //
  8. // Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
  9. //
  10. package s3
  11. import (
  12. "bytes"
  13. "crypto/hmac"
  14. "crypto/md5"
  15. "crypto/sha1"
  16. "encoding/base64"
  17. "encoding/xml"
  18. "fmt"
  19. "io"
  20. "io/ioutil"
  21. "log"
  22. "net"
  23. "net/http"
  24. "net/http/httputil"
  25. "net/url"
  26. "strconv"
  27. "strings"
  28. "time"
  29. "github.com/goamz/goamz/aws"
  30. )
  31. const debug = false
  32. // The S3 type encapsulates operations with an S3 region.
  33. type S3 struct {
  34. aws.Auth
  35. aws.Region
  36. // ConnectTimeout is the maximum time a request attempt will
  37. // wait for a successful connection to be made.
  38. //
  39. // A value of zero means no timeout.
  40. ConnectTimeout time.Duration
  41. // ReadTimeout is the maximum time a request attempt will wait
  42. // for an individual read to complete.
  43. //
  44. // A value of zero means no timeout.
  45. ReadTimeout time.Duration
  46. // WriteTimeout is the maximum time a request attempt will
  47. // wait for an individual write to complete.
  48. //
  49. // A value of zero means no timeout.
  50. WriteTimeout time.Duration
  51. // RequestTimeout is the maximum time a request attempt can
  52. // take before operations return a timeout error.
  53. //
  54. // This includes connection time, any redirects, and reading
  55. // the response body. The timer remains running after the request
  56. // is made so it can interrupt reading of the response data.
  57. //
  58. // A Timeout of zero means no timeout.
  59. RequestTimeout time.Duration
  60. // AttemptStrategy is the attempt strategy used for requests.
  61. aws.AttemptStrategy
  62. // Reserve the right of using private data.
  63. private byte
  64. // client used for requests
  65. client *http.Client
  66. }
  67. // The Bucket type encapsulates operations with an S3 bucket.
  68. type Bucket struct {
  69. *S3
  70. Name string
  71. }
  72. // The Owner type represents the owner of the object in an S3 bucket.
  73. type Owner struct {
  74. ID string
  75. DisplayName string
  76. }
  77. // Fold options into an Options struct
  78. //
  79. type Options struct {
  80. SSE bool
  81. Meta map[string][]string
  82. ContentEncoding string
  83. CacheControl string
  84. RedirectLocation string
  85. ContentMD5 string
  86. // What else?
  87. // Content-Disposition string
  88. //// The following become headers so they are []strings rather than strings... I think
  89. // x-amz-storage-class []string
  90. }
  91. type CopyOptions struct {
  92. Options
  93. MetadataDirective string
  94. ContentType string
  95. }
  96. // CopyObjectResult is the output from a Copy request
  97. type CopyObjectResult struct {
  98. ETag string
  99. LastModified string
  100. }
  101. // DefaultAttemptStrategy is the default AttemptStrategy used by S3 objects created by New.
  102. var DefaultAttemptStrategy = aws.AttemptStrategy{
  103. Min: 5,
  104. Total: 5 * time.Second,
  105. Delay: 200 * time.Millisecond,
  106. }
  107. // New creates a new S3. Optional client argument allows for custom http.clients to be used.
  108. func New(auth aws.Auth, region aws.Region, client ...*http.Client) *S3 {
  109. var httpclient *http.Client
  110. if len(client) > 0 {
  111. httpclient = client[0]
  112. }
  113. return &S3{Auth: auth, Region: region, AttemptStrategy: DefaultAttemptStrategy, client: httpclient}
  114. }
  115. // Bucket returns a Bucket with the given name.
  116. func (s3 *S3) Bucket(name string) *Bucket {
  117. if s3.Region.S3BucketEndpoint != "" || s3.Region.S3LowercaseBucket {
  118. name = strings.ToLower(name)
  119. }
  120. return &Bucket{s3, name}
  121. }
  122. var createBucketConfiguration = `<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  123. <LocationConstraint>%s</LocationConstraint>
  124. </CreateBucketConfiguration>`
  125. // locationConstraint returns an io.Reader specifying a LocationConstraint if
  126. // required for the region.
  127. //
  128. // See http://goo.gl/bh9Kq for details.
  129. func (s3 *S3) locationConstraint() io.Reader {
  130. constraint := ""
  131. if s3.Region.S3LocationConstraint {
  132. constraint = fmt.Sprintf(createBucketConfiguration, s3.Region.Name)
  133. }
  134. return strings.NewReader(constraint)
  135. }
  136. type ACL string
  137. const (
  138. Private = ACL("private")
  139. PublicRead = ACL("public-read")
  140. PublicReadWrite = ACL("public-read-write")
  141. AuthenticatedRead = ACL("authenticated-read")
  142. BucketOwnerRead = ACL("bucket-owner-read")
  143. BucketOwnerFull = ACL("bucket-owner-full-control")
  144. )
  145. // PutBucket creates a new bucket.
  146. //
  147. // See http://goo.gl/ndjnR for details.
  148. func (b *Bucket) PutBucket(perm ACL) error {
  149. headers := map[string][]string{
  150. "x-amz-acl": {string(perm)},
  151. }
  152. req := &request{
  153. method: "PUT",
  154. bucket: b.Name,
  155. path: "/",
  156. headers: headers,
  157. payload: b.locationConstraint(),
  158. }
  159. return b.S3.query(req, nil)
  160. }
  161. // DelBucket removes an existing S3 bucket. All objects in the bucket must
  162. // be removed before the bucket itself can be removed.
  163. //
  164. // See http://goo.gl/GoBrY for details.
  165. func (b *Bucket) DelBucket() (err error) {
  166. req := &request{
  167. method: "DELETE",
  168. bucket: b.Name,
  169. path: "/",
  170. }
  171. for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
  172. err = b.S3.query(req, nil)
  173. if !shouldRetry(err) {
  174. break
  175. }
  176. }
  177. return err
  178. }
  179. // Get retrieves an object from an S3 bucket.
  180. //
  181. // See http://goo.gl/isCO7 for details.
  182. func (b *Bucket) Get(path string) (data []byte, err error) {
  183. body, err := b.GetReader(path)
  184. defer func() {
  185. if body != nil {
  186. body.Close()
  187. }
  188. }()
  189. if err != nil {
  190. return nil, err
  191. }
  192. data, err = ioutil.ReadAll(body)
  193. return data, err
  194. }
  195. // GetReader retrieves an object from an S3 bucket,
  196. // returning the body of the HTTP response.
  197. // It is the caller's responsibility to call Close on rc when
  198. // finished reading.
  199. func (b *Bucket) GetReader(path string) (rc io.ReadCloser, err error) {
  200. resp, err := b.GetResponse(path)
  201. if resp != nil {
  202. return resp.Body, err
  203. }
  204. return nil, err
  205. }
  206. // GetResponse retrieves an object from an S3 bucket,
  207. // returning the HTTP response.
  208. // It is the caller's responsibility to call Close on rc when
  209. // finished reading
  210. func (b *Bucket) GetResponse(path string) (resp *http.Response, err error) {
  211. return b.GetResponseWithHeaders(path, make(http.Header))
  212. }
  213. // GetReaderWithHeaders retrieves an object from an S3 bucket
  214. // Accepts custom headers to be sent as the second parameter
  215. // returning the body of the HTTP response.
  216. // It is the caller's responsibility to call Close on rc when
  217. // finished reading
  218. func (b *Bucket) GetResponseWithHeaders(path string, headers map[string][]string) (resp *http.Response, err error) {
  219. req := &request{
  220. bucket: b.Name,
  221. path: path,
  222. headers: headers,
  223. }
  224. err = b.S3.prepare(req)
  225. if err != nil {
  226. return nil, err
  227. }
  228. for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
  229. resp, err := b.S3.run(req, nil)
  230. if shouldRetry(err) && attempt.HasNext() {
  231. continue
  232. }
  233. if err != nil {
  234. return nil, err
  235. }
  236. return resp, nil
  237. }
  238. panic("unreachable")
  239. }
  240. // Exists checks whether or not an object exists on an S3 bucket using a HEAD request.
  241. func (b *Bucket) Exists(path string) (exists bool, err error) {
  242. req := &request{
  243. method: "HEAD",
  244. bucket: b.Name,
  245. path: path,
  246. }
  247. err = b.S3.prepare(req)
  248. if err != nil {
  249. return
  250. }
  251. for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
  252. resp, err := b.S3.run(req, nil)
  253. if shouldRetry(err) && attempt.HasNext() {
  254. continue
  255. }
  256. if err != nil {
  257. // We can treat a 403 or 404 as non existance
  258. if e, ok := err.(*Error); ok && (e.StatusCode == 403 || e.StatusCode == 404) {
  259. return false, nil
  260. }
  261. return false, err
  262. }
  263. if resp.StatusCode/100 == 2 {
  264. exists = true
  265. }
  266. return exists, err
  267. }
  268. return false, fmt.Errorf("S3 Currently Unreachable")
  269. }
  270. // Head HEADs an object in the S3 bucket, returns the response with
  271. // no body see http://bit.ly/17K1ylI
  272. func (b *Bucket) Head(path string, headers map[string][]string) (*http.Response, error) {
  273. req := &request{
  274. method: "HEAD",
  275. bucket: b.Name,
  276. path: path,
  277. headers: headers,
  278. }
  279. err := b.S3.prepare(req)
  280. if err != nil {
  281. return nil, err
  282. }
  283. for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
  284. resp, err := b.S3.run(req, nil)
  285. if shouldRetry(err) && attempt.HasNext() {
  286. continue
  287. }
  288. if err != nil {
  289. return nil, err
  290. }
  291. return resp, err
  292. }
  293. return nil, fmt.Errorf("S3 Currently Unreachable")
  294. }
  295. // Put inserts an object into the S3 bucket.
  296. //
  297. // See http://goo.gl/FEBPD for details.
  298. func (b *Bucket) Put(path string, data []byte, contType string, perm ACL, options Options) error {
  299. body := bytes.NewBuffer(data)
  300. return b.PutReader(path, body, int64(len(data)), contType, perm, options)
  301. }
  302. // PutCopy puts a copy of an object given by the key path into bucket b using b.Path as the target key
  303. func (b *Bucket) PutCopy(path string, perm ACL, options CopyOptions, source string) (result *CopyObjectResult, err error) {
  304. headers := map[string][]string{
  305. "x-amz-acl": {string(perm)},
  306. "x-amz-copy-source": {source},
  307. }
  308. options.addHeaders(headers)
  309. req := &request{
  310. method: "PUT",
  311. bucket: b.Name,
  312. path: path,
  313. headers: headers,
  314. }
  315. result = &CopyObjectResult{}
  316. for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
  317. err = b.S3.query(req, result)
  318. if !shouldRetry(err) {
  319. break
  320. }
  321. }
  322. if err != nil {
  323. return nil, err
  324. }
  325. return result, nil
  326. }
  327. /*
  328. PutHeader - like Put, inserts an object into the S3 bucket.
  329. Instead of Content-Type string, pass in custom headers to override defaults.
  330. */
  331. func (b *Bucket) PutHeader(path string, data []byte, customHeaders map[string][]string, perm ACL) error {
  332. body := bytes.NewBuffer(data)
  333. return b.PutReaderHeader(path, body, int64(len(data)), customHeaders, perm)
  334. }
  335. // PutReader inserts an object into the S3 bucket by consuming data
  336. // from r until EOF.
  337. func (b *Bucket) PutReader(path string, r io.Reader, length int64, contType string, perm ACL, options Options) error {
  338. headers := map[string][]string{
  339. "Content-Length": {strconv.FormatInt(length, 10)},
  340. "Content-Type": {contType},
  341. "x-amz-acl": {string(perm)},
  342. }
  343. options.addHeaders(headers)
  344. req := &request{
  345. method: "PUT",
  346. bucket: b.Name,
  347. path: path,
  348. headers: headers,
  349. payload: r,
  350. }
  351. return b.S3.query(req, nil)
  352. }
  353. /*
  354. PutReaderHeader - like PutReader, inserts an object into S3 from a reader.
  355. Instead of Content-Type string, pass in custom headers to override defaults.
  356. */
  357. func (b *Bucket) PutReaderHeader(path string, r io.Reader, length int64, customHeaders map[string][]string, perm ACL) error {
  358. // Default headers
  359. headers := map[string][]string{
  360. "Content-Length": {strconv.FormatInt(length, 10)},
  361. "Content-Type": {"application/text"},
  362. "x-amz-acl": {string(perm)},
  363. }
  364. // Override with custom headers
  365. for key, value := range customHeaders {
  366. headers[key] = value
  367. }
  368. req := &request{
  369. method: "PUT",
  370. bucket: b.Name,
  371. path: path,
  372. headers: headers,
  373. payload: r,
  374. }
  375. return b.S3.query(req, nil)
  376. }
  377. // addHeaders adds o's specified fields to headers
  378. func (o Options) addHeaders(headers map[string][]string) {
  379. if o.SSE {
  380. headers["x-amz-server-side-encryption"] = []string{"AES256"}
  381. }
  382. if len(o.ContentEncoding) != 0 {
  383. headers["Content-Encoding"] = []string{o.ContentEncoding}
  384. }
  385. if len(o.CacheControl) != 0 {
  386. headers["Cache-Control"] = []string{o.CacheControl}
  387. }
  388. if len(o.ContentMD5) != 0 {
  389. headers["Content-MD5"] = []string{o.ContentMD5}
  390. }
  391. if len(o.RedirectLocation) != 0 {
  392. headers["x-amz-website-redirect-location"] = []string{o.RedirectLocation}
  393. }
  394. for k, v := range o.Meta {
  395. headers["x-amz-meta-"+k] = v
  396. }
  397. }
  398. // addHeaders adds o's specified fields to headers
  399. func (o CopyOptions) addHeaders(headers map[string][]string) {
  400. o.Options.addHeaders(headers)
  401. if len(o.MetadataDirective) != 0 {
  402. headers["x-amz-metadata-directive"] = []string{o.MetadataDirective}
  403. }
  404. if len(o.ContentType) != 0 {
  405. headers["Content-Type"] = []string{o.ContentType}
  406. }
  407. }
  408. func makeXmlBuffer(doc []byte) *bytes.Buffer {
  409. buf := new(bytes.Buffer)
  410. buf.WriteString(xml.Header)
  411. buf.Write(doc)
  412. return buf
  413. }
  414. type RoutingRule struct {
  415. ConditionKeyPrefixEquals string `xml:"Condition>KeyPrefixEquals"`
  416. RedirectReplaceKeyPrefixWith string `xml:"Redirect>ReplaceKeyPrefixWith,omitempty"`
  417. RedirectReplaceKeyWith string `xml:"Redirect>ReplaceKeyWith,omitempty"`
  418. }
  419. type WebsiteConfiguration struct {
  420. XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ WebsiteConfiguration"`
  421. IndexDocumentSuffix string `xml:"IndexDocument>Suffix"`
  422. ErrorDocumentKey string `xml:"ErrorDocument>Key"`
  423. RoutingRules *[]RoutingRule `xml:"RoutingRules>RoutingRule,omitempty"`
  424. }
  425. func (b *Bucket) PutBucketWebsite(configuration WebsiteConfiguration) error {
  426. doc, err := xml.Marshal(configuration)
  427. if err != nil {
  428. return err
  429. }
  430. buf := makeXmlBuffer(doc)
  431. return b.PutBucketSubresource("website", buf, int64(buf.Len()))
  432. }
  433. func (b *Bucket) PutBucketSubresource(subresource string, r io.Reader, length int64) error {
  434. headers := map[string][]string{
  435. "Content-Length": {strconv.FormatInt(length, 10)},
  436. }
  437. req := &request{
  438. path: "/",
  439. method: "PUT",
  440. bucket: b.Name,
  441. headers: headers,
  442. payload: r,
  443. params: url.Values{subresource: {""}},
  444. }
  445. return b.S3.query(req, nil)
  446. }
  447. // Del removes an object from the S3 bucket.
  448. //
  449. // See http://goo.gl/APeTt for details.
  450. func (b *Bucket) Del(path string) error {
  451. req := &request{
  452. method: "DELETE",
  453. bucket: b.Name,
  454. path: path,
  455. }
  456. return b.S3.query(req, nil)
  457. }
  458. type Delete struct {
  459. Quiet bool `xml:"Quiet,omitempty"`
  460. Objects []Object `xml:"Object"`
  461. }
  462. type Object struct {
  463. Key string `xml:"Key"`
  464. VersionId string `xml:"VersionId,omitempty"`
  465. }
  466. // DelMulti removes up to 1000 objects from the S3 bucket.
  467. //
  468. // See http://goo.gl/jx6cWK for details.
  469. func (b *Bucket) DelMulti(objects Delete) error {
  470. doc, err := xml.Marshal(objects)
  471. if err != nil {
  472. return err
  473. }
  474. buf := makeXmlBuffer(doc)
  475. digest := md5.New()
  476. size, err := digest.Write(buf.Bytes())
  477. if err != nil {
  478. return err
  479. }
  480. headers := map[string][]string{
  481. "Content-Length": {strconv.FormatInt(int64(size), 10)},
  482. "Content-MD5": {base64.StdEncoding.EncodeToString(digest.Sum(nil))},
  483. "Content-Type": {"text/xml"},
  484. }
  485. req := &request{
  486. path: "/",
  487. method: "POST",
  488. params: url.Values{"delete": {""}},
  489. bucket: b.Name,
  490. headers: headers,
  491. payload: buf,
  492. }
  493. return b.S3.query(req, nil)
  494. }
  495. // The ListResp type holds the results of a List bucket operation.
  496. type ListResp struct {
  497. Name string
  498. Prefix string
  499. Delimiter string
  500. Marker string
  501. NextMarker string
  502. MaxKeys int
  503. // IsTruncated is true if the results have been truncated because
  504. // there are more keys and prefixes than can fit in MaxKeys.
  505. // N.B. this is the opposite sense to that documented (incorrectly) in
  506. // http://goo.gl/YjQTc
  507. IsTruncated bool
  508. Contents []Key
  509. CommonPrefixes []string `xml:">Prefix"`
  510. }
  511. // The Key type represents an item stored in an S3 bucket.
  512. type Key struct {
  513. Key string
  514. LastModified string
  515. Size int64
  516. // ETag gives the hex-encoded MD5 sum of the contents,
  517. // surrounded with double-quotes.
  518. ETag string
  519. StorageClass string
  520. Owner Owner
  521. }
  522. // List returns information about objects in an S3 bucket.
  523. //
  524. // The prefix parameter limits the response to keys that begin with the
  525. // specified prefix.
  526. //
  527. // The delim parameter causes the response to group all of the keys that
  528. // share a common prefix up to the next delimiter in a single entry within
  529. // the CommonPrefixes field. You can use delimiters to separate a bucket
  530. // into different groupings of keys, similar to how folders would work.
  531. //
  532. // The marker parameter specifies the key to start with when listing objects
  533. // in a bucket. Amazon S3 lists objects in alphabetical order and
  534. // will return keys alphabetically greater than the marker.
  535. //
  536. // The max parameter specifies how many keys + common prefixes to return in
  537. // the response. The default is 1000.
  538. //
  539. // For example, given these keys in a bucket:
  540. //
  541. // index.html
  542. // index2.html
  543. // photos/2006/January/sample.jpg
  544. // photos/2006/February/sample2.jpg
  545. // photos/2006/February/sample3.jpg
  546. // photos/2006/February/sample4.jpg
  547. //
  548. // Listing this bucket with delimiter set to "/" would yield the
  549. // following result:
  550. //
  551. // &ListResp{
  552. // Name: "sample-bucket",
  553. // MaxKeys: 1000,
  554. // Delimiter: "/",
  555. // Contents: []Key{
  556. // {Key: "index.html", "index2.html"},
  557. // },
  558. // CommonPrefixes: []string{
  559. // "photos/",
  560. // },
  561. // }
  562. //
  563. // Listing the same bucket with delimiter set to "/" and prefix set to
  564. // "photos/2006/" would yield the following result:
  565. //
  566. // &ListResp{
  567. // Name: "sample-bucket",
  568. // MaxKeys: 1000,
  569. // Delimiter: "/",
  570. // Prefix: "photos/2006/",
  571. // CommonPrefixes: []string{
  572. // "photos/2006/February/",
  573. // "photos/2006/January/",
  574. // },
  575. // }
  576. //
  577. // See http://goo.gl/YjQTc for details.
  578. func (b *Bucket) List(prefix, delim, marker string, max int) (result *ListResp, err error) {
  579. params := map[string][]string{
  580. "prefix": {prefix},
  581. "delimiter": {delim},
  582. "marker": {marker},
  583. }
  584. if max != 0 {
  585. params["max-keys"] = []string{strconv.FormatInt(int64(max), 10)}
  586. }
  587. req := &request{
  588. bucket: b.Name,
  589. params: params,
  590. }
  591. result = &ListResp{}
  592. for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
  593. err = b.S3.query(req, result)
  594. if !shouldRetry(err) {
  595. break
  596. }
  597. }
  598. if err != nil {
  599. return nil, err
  600. }
  601. return result, nil
  602. }
  603. // The VersionsResp type holds the results of a list bucket Versions operation.
  604. type VersionsResp struct {
  605. Name string
  606. Prefix string
  607. KeyMarker string
  608. VersionIdMarker string
  609. MaxKeys int
  610. Delimiter string
  611. IsTruncated bool
  612. Versions []Version
  613. CommonPrefixes []string `xml:">Prefix"`
  614. }
  615. // The Version type represents an object version stored in an S3 bucket.
  616. type Version struct {
  617. Key string
  618. VersionId string
  619. IsLatest bool
  620. LastModified string
  621. // ETag gives the hex-encoded MD5 sum of the contents,
  622. // surrounded with double-quotes.
  623. ETag string
  624. Size int64
  625. Owner Owner
  626. StorageClass string
  627. }
  628. func (b *Bucket) Versions(prefix, delim, keyMarker string, versionIdMarker string, max int) (result *VersionsResp, err error) {
  629. params := map[string][]string{
  630. "versions": {""},
  631. "prefix": {prefix},
  632. "delimiter": {delim},
  633. }
  634. if len(versionIdMarker) != 0 {
  635. params["version-id-marker"] = []string{versionIdMarker}
  636. }
  637. if len(keyMarker) != 0 {
  638. params["key-marker"] = []string{keyMarker}
  639. }
  640. if max != 0 {
  641. params["max-keys"] = []string{strconv.FormatInt(int64(max), 10)}
  642. }
  643. req := &request{
  644. bucket: b.Name,
  645. params: params,
  646. }
  647. result = &VersionsResp{}
  648. for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
  649. err = b.S3.query(req, result)
  650. if !shouldRetry(err) {
  651. break
  652. }
  653. }
  654. if err != nil {
  655. return nil, err
  656. }
  657. return result, nil
  658. }
  659. // Returns a mapping of all key names in this bucket to Key objects
  660. func (b *Bucket) GetBucketContents() (*map[string]Key, error) {
  661. bucket_contents := map[string]Key{}
  662. prefix := ""
  663. path_separator := ""
  664. marker := ""
  665. for {
  666. contents, err := b.List(prefix, path_separator, marker, 1000)
  667. if err != nil {
  668. return &bucket_contents, err
  669. }
  670. for _, key := range contents.Contents {
  671. bucket_contents[key.Key] = key
  672. }
  673. if contents.IsTruncated {
  674. marker = contents.NextMarker
  675. } else {
  676. break
  677. }
  678. }
  679. return &bucket_contents, nil
  680. }
  681. // URL returns a non-signed URL that allows retriving the
  682. // object at path. It only works if the object is publicly
  683. // readable (see SignedURL).
  684. func (b *Bucket) URL(path string) string {
  685. req := &request{
  686. bucket: b.Name,
  687. path: path,
  688. }
  689. err := b.S3.prepare(req)
  690. if err != nil {
  691. panic(err)
  692. }
  693. u, err := req.url()
  694. if err != nil {
  695. panic(err)
  696. }
  697. u.RawQuery = ""
  698. return u.String()
  699. }
  700. // SignedURL returns a signed URL that allows anyone holding the URL
  701. // to retrieve the object at path. The signature is valid until expires.
  702. func (b *Bucket) SignedURL(path string, expires time.Time) string {
  703. req := &request{
  704. bucket: b.Name,
  705. path: path,
  706. params: url.Values{"Expires": {strconv.FormatInt(expires.Unix(), 10)}},
  707. }
  708. err := b.S3.prepare(req)
  709. if err != nil {
  710. panic(err)
  711. }
  712. u, err := req.url()
  713. if err != nil {
  714. panic(err)
  715. }
  716. if b.S3.Auth.Token() != "" {
  717. return u.String() + "&x-amz-security-token=" + url.QueryEscape(req.headers["X-Amz-Security-Token"][0])
  718. } else {
  719. return u.String()
  720. }
  721. }
  722. // UploadSignedURL returns a signed URL that allows anyone holding the URL
  723. // to upload the object at path. The signature is valid until expires.
  724. // contenttype is a string like image/png
  725. // path is the resource name in s3 terminalogy like images/ali.png [obviously exclusing the bucket name itself]
  726. func (b *Bucket) UploadSignedURL(path, method, content_type string, expires time.Time) string {
  727. expire_date := expires.Unix()
  728. if method != "POST" {
  729. method = "PUT"
  730. }
  731. stringToSign := method + "\n\n" + content_type + "\n" + strconv.FormatInt(expire_date, 10) + "\n/" + b.Name + "/" + path
  732. fmt.Println("String to sign:\n", stringToSign)
  733. a := b.S3.Auth
  734. secretKey := a.SecretKey
  735. accessId := a.AccessKey
  736. mac := hmac.New(sha1.New, []byte(secretKey))
  737. mac.Write([]byte(stringToSign))
  738. macsum := mac.Sum(nil)
  739. signature := base64.StdEncoding.EncodeToString([]byte(macsum))
  740. signature = strings.TrimSpace(signature)
  741. signedurl, err := url.Parse("https://" + b.Name + ".s3.amazonaws.com/")
  742. if err != nil {
  743. log.Println("ERROR sining url for S3 upload", err)
  744. return ""
  745. }
  746. signedurl.Path += path
  747. params := url.Values{}
  748. params.Add("AWSAccessKeyId", accessId)
  749. params.Add("Expires", strconv.FormatInt(expire_date, 10))
  750. params.Add("Signature", signature)
  751. if a.Token() != "" {
  752. params.Add("token", a.Token())
  753. }
  754. signedurl.RawQuery = params.Encode()
  755. return signedurl.String()
  756. }
  757. // PostFormArgs returns the action and input fields needed to allow anonymous
  758. // uploads to a bucket within the expiration limit
  759. func (b *Bucket) PostFormArgs(path string, expires time.Time, redirect string) (action string, fields map[string]string) {
  760. conditions := make([]string, 0)
  761. fields = map[string]string{
  762. "AWSAccessKeyId": b.Auth.AccessKey,
  763. "key": path,
  764. }
  765. conditions = append(conditions, fmt.Sprintf("{\"key\": \"%s\"}", path))
  766. conditions = append(conditions, fmt.Sprintf("{\"bucket\": \"%s\"}", b.Name))
  767. if redirect != "" {
  768. conditions = append(conditions, fmt.Sprintf("{\"success_action_redirect\": \"%s\"}", redirect))
  769. fields["success_action_redirect"] = redirect
  770. }
  771. vExpiration := expires.Format("2006-01-02T15:04:05Z")
  772. vConditions := strings.Join(conditions, ",")
  773. policy := fmt.Sprintf("{\"expiration\": \"%s\", \"conditions\": [%s]}", vExpiration, vConditions)
  774. policy64 := base64.StdEncoding.EncodeToString([]byte(policy))
  775. fields["policy"] = policy64
  776. signer := hmac.New(sha1.New, []byte(b.Auth.SecretKey))
  777. signer.Write([]byte(policy64))
  778. fields["signature"] = base64.StdEncoding.EncodeToString(signer.Sum(nil))
  779. action = fmt.Sprintf("%s/%s/", b.S3.Region.S3Endpoint, b.Name)
  780. return
  781. }
  782. type request struct {
  783. method string
  784. bucket string
  785. path string
  786. signpath string
  787. params url.Values
  788. headers http.Header
  789. baseurl string
  790. payload io.Reader
  791. prepared bool
  792. }
  793. func (req *request) url() (*url.URL, error) {
  794. u, err := url.Parse(req.baseurl)
  795. if err != nil {
  796. return nil, fmt.Errorf("bad S3 endpoint URL %q: %v", req.baseurl, err)
  797. }
  798. u.RawQuery = req.params.Encode()
  799. u.Path = req.path
  800. return u, nil
  801. }
  802. // query prepares and runs the req request.
  803. // If resp is not nil, the XML data contained in the response
  804. // body will be unmarshalled on it.
  805. func (s3 *S3) query(req *request, resp interface{}) error {
  806. err := s3.prepare(req)
  807. if err == nil {
  808. var httpResponse *http.Response
  809. httpResponse, err = s3.run(req, resp)
  810. if resp == nil && httpResponse != nil {
  811. httpResponse.Body.Close()
  812. }
  813. }
  814. return err
  815. }
  816. // prepare sets up req to be delivered to S3.
  817. func (s3 *S3) prepare(req *request) error {
  818. var signpath = req.path
  819. if !req.prepared {
  820. req.prepared = true
  821. if req.method == "" {
  822. req.method = "GET"
  823. }
  824. // Copy so they can be mutated without affecting on retries.
  825. params := make(url.Values)
  826. headers := make(http.Header)
  827. for k, v := range req.params {
  828. params[k] = v
  829. }
  830. for k, v := range req.headers {
  831. headers[k] = v
  832. }
  833. req.params = params
  834. req.headers = headers
  835. if !strings.HasPrefix(req.path, "/") {
  836. req.path = "/" + req.path
  837. }
  838. signpath = req.path
  839. if req.bucket != "" {
  840. req.baseurl = s3.Region.S3BucketEndpoint
  841. if req.baseurl == "" {
  842. // Use the path method to address the bucket.
  843. req.baseurl = s3.Region.S3Endpoint
  844. req.path = "/" + req.bucket + req.path
  845. } else {
  846. // Just in case, prevent injection.
  847. if strings.IndexAny(req.bucket, "/:@") >= 0 {
  848. return fmt.Errorf("bad S3 bucket: %q", req.bucket)
  849. }
  850. req.baseurl = strings.Replace(req.baseurl, "${bucket}", req.bucket, -1)
  851. }
  852. signpath = "/" + req.bucket + signpath
  853. }
  854. }
  855. // Always sign again as it's not clear how far the
  856. // server has handled a previous attempt.
  857. u, err := url.Parse(req.baseurl)
  858. if err != nil {
  859. return fmt.Errorf("bad S3 endpoint URL %q: %v", req.baseurl, err)
  860. }
  861. reqSignpathSpaceFix := (&url.URL{Path: signpath}).String()
  862. req.headers["Host"] = []string{u.Host}
  863. req.headers["Date"] = []string{time.Now().In(time.UTC).Format(time.RFC1123)}
  864. if s3.Auth.Token() != "" {
  865. req.headers["X-Amz-Security-Token"] = []string{s3.Auth.Token()}
  866. }
  867. sign(s3.Auth, req.method, reqSignpathSpaceFix, req.params, req.headers)
  868. return nil
  869. }
  870. // run sends req and returns the http response from the server.
  871. // If resp is not nil, the XML data contained in the response
  872. // body will be unmarshalled on it.
  873. func (s3 *S3) run(req *request, resp interface{}) (*http.Response, error) {
  874. if debug {
  875. log.Printf("Running S3 request: %#v", req)
  876. }
  877. u, err := req.url()
  878. if err != nil {
  879. return nil, err
  880. }
  881. hreq := http.Request{
  882. URL: u,
  883. Method: req.method,
  884. ProtoMajor: 1,
  885. ProtoMinor: 1,
  886. Close: true,
  887. Header: req.headers,
  888. }
  889. if v, ok := req.headers["Content-Length"]; ok {
  890. hreq.ContentLength, _ = strconv.ParseInt(v[0], 10, 64)
  891. delete(req.headers, "Content-Length")
  892. }
  893. if req.payload != nil {
  894. hreq.Body = ioutil.NopCloser(req.payload)
  895. }
  896. if s3.client == nil {
  897. s3.client = &http.Client{
  898. Transport: &http.Transport{
  899. Dial: func(netw, addr string) (c net.Conn, err error) {
  900. c, err = net.DialTimeout(netw, addr, s3.ConnectTimeout)
  901. if err != nil {
  902. return
  903. }
  904. var deadline time.Time
  905. if s3.RequestTimeout > 0 {
  906. deadline = time.Now().Add(s3.RequestTimeout)
  907. c.SetDeadline(deadline)
  908. }
  909. if s3.ReadTimeout > 0 || s3.WriteTimeout > 0 {
  910. c = &ioTimeoutConn{
  911. TCPConn: c.(*net.TCPConn),
  912. readTimeout: s3.ReadTimeout,
  913. writeTimeout: s3.WriteTimeout,
  914. requestDeadline: deadline,
  915. }
  916. }
  917. return
  918. },
  919. },
  920. }
  921. }
  922. hresp, err := s3.client.Do(&hreq)
  923. if err != nil {
  924. return nil, err
  925. }
  926. if debug {
  927. dump, _ := httputil.DumpResponse(hresp, true)
  928. log.Printf("} -> %s\n", dump)
  929. }
  930. if hresp.StatusCode != 200 && hresp.StatusCode != 204 && hresp.StatusCode != 206 {
  931. defer hresp.Body.Close()
  932. return nil, buildError(hresp)
  933. }
  934. if resp != nil {
  935. err = xml.NewDecoder(hresp.Body).Decode(resp)
  936. hresp.Body.Close()
  937. if debug {
  938. log.Printf("goamz.s3> decoded xml into %#v", resp)
  939. }
  940. }
  941. return hresp, err
  942. }
  943. // Error represents an error in an operation with S3.
  944. type Error struct {
  945. StatusCode int // HTTP status code (200, 403, ...)
  946. Code string // EC2 error code ("UnsupportedOperation", ...)
  947. Message string // The human-oriented error message
  948. BucketName string
  949. RequestId string
  950. HostId string
  951. }
  952. func (e *Error) Error() string {
  953. return e.Message
  954. }
  955. func buildError(r *http.Response) error {
  956. if debug {
  957. log.Printf("got error (status code %v)", r.StatusCode)
  958. data, err := ioutil.ReadAll(r.Body)
  959. if err != nil {
  960. log.Printf("\tread error: %v", err)
  961. } else {
  962. log.Printf("\tdata:\n%s\n\n", data)
  963. }
  964. r.Body = ioutil.NopCloser(bytes.NewBuffer(data))
  965. }
  966. err := Error{}
  967. // TODO return error if Unmarshal fails?
  968. xml.NewDecoder(r.Body).Decode(&err)
  969. r.Body.Close()
  970. err.StatusCode = r.StatusCode
  971. if err.Message == "" {
  972. err.Message = r.Status
  973. }
  974. if debug {
  975. log.Printf("err: %#v\n", err)
  976. }
  977. return &err
  978. }
  979. func shouldRetry(err error) bool {
  980. if err == nil {
  981. return false
  982. }
  983. if e, ok := err.(*url.Error); ok {
  984. // Transport returns this string if it detects a write on a connection which
  985. // has already had an error
  986. if e.Err.Error() == "http: can't write HTTP request on broken connection" {
  987. return true
  988. }
  989. err = e.Err
  990. }
  991. switch err {
  992. case io.ErrUnexpectedEOF, io.EOF:
  993. return true
  994. }
  995. switch e := err.(type) {
  996. case *net.DNSError:
  997. return true
  998. case *net.OpError:
  999. switch e.Op {
  1000. case "read", "write", "WSARecv", "WSASend", "ConnectEx":
  1001. return true
  1002. }
  1003. case *Error:
  1004. switch e.Code {
  1005. case "InternalError", "NoSuchUpload", "NoSuchBucket", "RequestTimeout":
  1006. return true
  1007. }
  1008. // let's handle tls handshake timeout issues and similar temporary errors
  1009. case net.Error:
  1010. return e.Temporary()
  1011. }
  1012. return false
  1013. }
  1014. func hasCode(err error, code string) bool {
  1015. s3err, ok := err.(*Error)
  1016. return ok && s3err.Code == code
  1017. }
  1018. // ioTimeoutConn is a net.Conn which sets a deadline for each Read or Write operation
  1019. type ioTimeoutConn struct {
  1020. *net.TCPConn
  1021. readTimeout time.Duration
  1022. writeTimeout time.Duration
  1023. requestDeadline time.Time
  1024. }
  1025. func (c *ioTimeoutConn) deadline(timeout time.Duration) time.Time {
  1026. dl := time.Now().Add(timeout)
  1027. if c.requestDeadline.IsZero() || dl.Before(c.requestDeadline) {
  1028. return dl
  1029. }
  1030. return c.requestDeadline
  1031. }
  1032. func (c *ioTimeoutConn) Read(b []byte) (int, error) {
  1033. if c.readTimeout > 0 {
  1034. err := c.TCPConn.SetReadDeadline(c.deadline(c.readTimeout))
  1035. if err != nil {
  1036. return 0, err
  1037. }
  1038. }
  1039. return c.TCPConn.Read(b)
  1040. }
  1041. func (c *ioTimeoutConn) Write(b []byte) (int, error) {
  1042. if c.writeTimeout > 0 {
  1043. err := c.TCPConn.SetWriteDeadline(c.deadline(c.writeTimeout))
  1044. if err != nil {
  1045. return 0, err
  1046. }
  1047. }
  1048. return c.TCPConn.Write(b)
  1049. }