api-stat.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * Minio Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2017 Minio, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "context"
  20. "net/http"
  21. "strconv"
  22. "strings"
  23. "time"
  24. "github.com/minio/minio-go/pkg/s3utils"
  25. )
  26. // BucketExists verify if bucket exists and you have permission to access it.
  27. func (c Client) BucketExists(bucketName string) (bool, error) {
  28. // Input validation.
  29. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  30. return false, err
  31. }
  32. // Execute HEAD on bucketName.
  33. resp, err := c.executeMethod(context.Background(), "HEAD", requestMetadata{
  34. bucketName: bucketName,
  35. contentSHA256Hex: emptySHA256Hex,
  36. })
  37. defer closeResponse(resp)
  38. if err != nil {
  39. if ToErrorResponse(err).Code == "NoSuchBucket" {
  40. return false, nil
  41. }
  42. return false, err
  43. }
  44. if resp != nil {
  45. if resp.StatusCode != http.StatusOK {
  46. return false, httpRespToErrorResponse(resp, bucketName, "")
  47. }
  48. }
  49. return true, nil
  50. }
  51. // List of header keys to be filtered, usually
  52. // from all S3 API http responses.
  53. var defaultFilterKeys = []string{
  54. "Connection",
  55. "Transfer-Encoding",
  56. "Accept-Ranges",
  57. "Date",
  58. "Server",
  59. "Vary",
  60. "x-amz-bucket-region",
  61. "x-amz-request-id",
  62. "x-amz-id-2",
  63. // Add new headers to be ignored.
  64. }
  65. // Extract only necessary metadata header key/values by
  66. // filtering them out with a list of custom header keys.
  67. func extractObjMetadata(header http.Header) http.Header {
  68. filterKeys := append([]string{
  69. "ETag",
  70. "Content-Length",
  71. "Last-Modified",
  72. "Content-Type",
  73. }, defaultFilterKeys...)
  74. return filterHeader(header, filterKeys)
  75. }
  76. // StatObject verifies if object exists and you have permission to access.
  77. func (c Client) StatObject(bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
  78. // Input validation.
  79. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  80. return ObjectInfo{}, err
  81. }
  82. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  83. return ObjectInfo{}, err
  84. }
  85. return c.statObject(context.Background(), bucketName, objectName, opts)
  86. }
  87. // Lower level API for statObject supporting pre-conditions and range headers.
  88. func (c Client) statObject(ctx context.Context, bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
  89. // Input validation.
  90. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  91. return ObjectInfo{}, err
  92. }
  93. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  94. return ObjectInfo{}, err
  95. }
  96. // Execute HEAD on objectName.
  97. resp, err := c.executeMethod(ctx, "HEAD", requestMetadata{
  98. bucketName: bucketName,
  99. objectName: objectName,
  100. contentSHA256Hex: emptySHA256Hex,
  101. customHeader: opts.Header(),
  102. })
  103. defer closeResponse(resp)
  104. if err != nil {
  105. return ObjectInfo{}, err
  106. }
  107. if resp != nil {
  108. if resp.StatusCode != http.StatusOK {
  109. return ObjectInfo{}, httpRespToErrorResponse(resp, bucketName, objectName)
  110. }
  111. }
  112. // Trim off the odd double quotes from ETag in the beginning and end.
  113. md5sum := strings.TrimPrefix(resp.Header.Get("ETag"), "\"")
  114. md5sum = strings.TrimSuffix(md5sum, "\"")
  115. // Parse content length is exists
  116. var size int64 = -1
  117. contentLengthStr := resp.Header.Get("Content-Length")
  118. if contentLengthStr != "" {
  119. size, err = strconv.ParseInt(contentLengthStr, 10, 64)
  120. if err != nil {
  121. // Content-Length is not valid
  122. return ObjectInfo{}, ErrorResponse{
  123. Code: "InternalError",
  124. Message: "Content-Length is invalid. " + reportIssue,
  125. BucketName: bucketName,
  126. Key: objectName,
  127. RequestID: resp.Header.Get("x-amz-request-id"),
  128. HostID: resp.Header.Get("x-amz-id-2"),
  129. Region: resp.Header.Get("x-amz-bucket-region"),
  130. }
  131. }
  132. }
  133. // Parse Last-Modified has http time format.
  134. date, err := time.Parse(http.TimeFormat, resp.Header.Get("Last-Modified"))
  135. if err != nil {
  136. return ObjectInfo{}, ErrorResponse{
  137. Code: "InternalError",
  138. Message: "Last-Modified time format is invalid. " + reportIssue,
  139. BucketName: bucketName,
  140. Key: objectName,
  141. RequestID: resp.Header.Get("x-amz-request-id"),
  142. HostID: resp.Header.Get("x-amz-id-2"),
  143. Region: resp.Header.Get("x-amz-bucket-region"),
  144. }
  145. }
  146. // Fetch content type if any present.
  147. contentType := strings.TrimSpace(resp.Header.Get("Content-Type"))
  148. if contentType == "" {
  149. contentType = "application/octet-stream"
  150. }
  151. // Save object metadata info.
  152. return ObjectInfo{
  153. ETag: md5sum,
  154. Key: objectName,
  155. Size: size,
  156. LastModified: date,
  157. ContentType: contentType,
  158. // Extract only the relevant header keys describing the object.
  159. // following function filters out a list of standard set of keys
  160. // which are not part of object metadata.
  161. Metadata: extractObjMetadata(resp.Header),
  162. }, nil
  163. }