api-stat.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. resperr := httpRespToErrorResponse(resp, bucketName, "")
  46. if ToErrorResponse(resperr).Code == "NoSuchBucket" {
  47. return false, nil
  48. }
  49. if resp.StatusCode != http.StatusOK {
  50. return false, httpRespToErrorResponse(resp, bucketName, "")
  51. }
  52. }
  53. return true, nil
  54. }
  55. // List of header keys to be filtered, usually
  56. // from all S3 API http responses.
  57. var defaultFilterKeys = []string{
  58. "Connection",
  59. "Transfer-Encoding",
  60. "Accept-Ranges",
  61. "Date",
  62. "Server",
  63. "Vary",
  64. "x-amz-bucket-region",
  65. "x-amz-request-id",
  66. "x-amz-id-2",
  67. "Content-Security-Policy",
  68. "X-Xss-Protection",
  69. // Add new headers to be ignored.
  70. }
  71. // Extract only necessary metadata header key/values by
  72. // filtering them out with a list of custom header keys.
  73. func extractObjMetadata(header http.Header) http.Header {
  74. filterKeys := append([]string{
  75. "ETag",
  76. "Content-Length",
  77. "Last-Modified",
  78. "Content-Type",
  79. }, defaultFilterKeys...)
  80. return filterHeader(header, filterKeys)
  81. }
  82. // StatObject verifies if object exists and you have permission to access.
  83. func (c Client) StatObject(bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
  84. // Input validation.
  85. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  86. return ObjectInfo{}, err
  87. }
  88. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  89. return ObjectInfo{}, err
  90. }
  91. return c.statObject(context.Background(), bucketName, objectName, opts)
  92. }
  93. // Lower level API for statObject supporting pre-conditions and range headers.
  94. func (c Client) statObject(ctx context.Context, bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
  95. // Input validation.
  96. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  97. return ObjectInfo{}, err
  98. }
  99. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  100. return ObjectInfo{}, err
  101. }
  102. // Execute HEAD on objectName.
  103. resp, err := c.executeMethod(ctx, "HEAD", requestMetadata{
  104. bucketName: bucketName,
  105. objectName: objectName,
  106. contentSHA256Hex: emptySHA256Hex,
  107. customHeader: opts.Header(),
  108. })
  109. defer closeResponse(resp)
  110. if err != nil {
  111. return ObjectInfo{}, err
  112. }
  113. if resp != nil {
  114. if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
  115. return ObjectInfo{}, httpRespToErrorResponse(resp, bucketName, objectName)
  116. }
  117. }
  118. // Trim off the odd double quotes from ETag in the beginning and end.
  119. md5sum := strings.TrimPrefix(resp.Header.Get("ETag"), "\"")
  120. md5sum = strings.TrimSuffix(md5sum, "\"")
  121. // Parse content length is exists
  122. var size int64 = -1
  123. contentLengthStr := resp.Header.Get("Content-Length")
  124. if contentLengthStr != "" {
  125. size, err = strconv.ParseInt(contentLengthStr, 10, 64)
  126. if err != nil {
  127. // Content-Length is not valid
  128. return ObjectInfo{}, ErrorResponse{
  129. Code: "InternalError",
  130. Message: "Content-Length is invalid. " + reportIssue,
  131. BucketName: bucketName,
  132. Key: objectName,
  133. RequestID: resp.Header.Get("x-amz-request-id"),
  134. HostID: resp.Header.Get("x-amz-id-2"),
  135. Region: resp.Header.Get("x-amz-bucket-region"),
  136. }
  137. }
  138. }
  139. // Parse Last-Modified has http time format.
  140. date, err := time.Parse(http.TimeFormat, resp.Header.Get("Last-Modified"))
  141. if err != nil {
  142. return ObjectInfo{}, ErrorResponse{
  143. Code: "InternalError",
  144. Message: "Last-Modified time format is invalid. " + reportIssue,
  145. BucketName: bucketName,
  146. Key: objectName,
  147. RequestID: resp.Header.Get("x-amz-request-id"),
  148. HostID: resp.Header.Get("x-amz-id-2"),
  149. Region: resp.Header.Get("x-amz-bucket-region"),
  150. }
  151. }
  152. // Fetch content type if any present.
  153. contentType := strings.TrimSpace(resp.Header.Get("Content-Type"))
  154. if contentType == "" {
  155. contentType = "application/octet-stream"
  156. }
  157. // Save object metadata info.
  158. return ObjectInfo{
  159. ETag: md5sum,
  160. Key: objectName,
  161. Size: size,
  162. LastModified: date,
  163. ContentType: contentType,
  164. // Extract only the relevant header keys describing the object.
  165. // following function filters out a list of standard set of keys
  166. // which are not part of object metadata.
  167. Metadata: extractObjMetadata(resp.Header),
  168. }, nil
  169. }