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.

209 lines
5.5 KiB

  1. /*
  2. * Minio Cloud Storage, (C) 2015 Minio, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package cmd
  17. import (
  18. "encoding/base64"
  19. "encoding/xml"
  20. "fmt"
  21. "io"
  22. "net/http"
  23. "net/url"
  24. "os"
  25. "strings"
  26. "encoding/json"
  27. "github.com/pkg/profile"
  28. )
  29. // make a copy of http.Header
  30. func cloneHeader(h http.Header) http.Header {
  31. h2 := make(http.Header, len(h))
  32. for k, vv := range h {
  33. vv2 := make([]string, len(vv))
  34. copy(vv2, vv)
  35. h2[k] = vv2
  36. }
  37. return h2
  38. }
  39. // checkDuplicates - function to validate if there are duplicates in a slice of strings.
  40. func checkDuplicateStrings(list []string) error {
  41. // Empty lists are not allowed.
  42. if len(list) == 0 {
  43. return errInvalidArgument
  44. }
  45. // Empty keys are not allowed.
  46. for _, key := range list {
  47. if key == "" {
  48. return errInvalidArgument
  49. }
  50. }
  51. listMaps := make(map[string]int)
  52. // Navigate through each configs and count the entries.
  53. for _, key := range list {
  54. listMaps[key]++
  55. }
  56. // Validate if there are any duplicate counts.
  57. for key, count := range listMaps {
  58. if count != 1 {
  59. return fmt.Errorf("Duplicate key: \"%s\" found of count: \"%d\"", key, count)
  60. }
  61. }
  62. // No duplicates.
  63. return nil
  64. }
  65. // checkDuplicates - function to validate if there are duplicates in a slice of endPoints.
  66. func checkDuplicateEndpoints(endpoints []*url.URL) error {
  67. var strs []string
  68. for _, ep := range endpoints {
  69. strs = append(strs, ep.String())
  70. }
  71. return checkDuplicateStrings(strs)
  72. }
  73. // Find local node through the command line arguments. Returns in `host:port` format.
  74. func getLocalAddress(srvCmdConfig serverCmdConfig) string {
  75. if !globalIsDistXL {
  76. return srvCmdConfig.serverAddr
  77. }
  78. for _, ep := range srvCmdConfig.endpoints {
  79. // Validates if remote endpoint is local.
  80. if isLocalStorage(ep) {
  81. return ep.Host
  82. }
  83. }
  84. return ""
  85. }
  86. // xmlDecoder provide decoded value in xml.
  87. func xmlDecoder(body io.Reader, v interface{}, size int64) error {
  88. var lbody io.Reader
  89. if size > 0 {
  90. lbody = io.LimitReader(body, size)
  91. } else {
  92. lbody = body
  93. }
  94. d := xml.NewDecoder(lbody)
  95. return d.Decode(v)
  96. }
  97. // checkValidMD5 - verify if valid md5, returns md5 in bytes.
  98. func checkValidMD5(md5 string) ([]byte, error) {
  99. return base64.StdEncoding.DecodeString(strings.TrimSpace(md5))
  100. }
  101. /// http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html
  102. const (
  103. // maximum object size per PUT request is 5GiB
  104. maxObjectSize = 1024 * 1024 * 1024 * 5
  105. // minimum Part size for multipart upload is 5MB
  106. minPartSize = 1024 * 1024 * 5
  107. // maximum Part ID for multipart upload is 10000 (Acceptable values range from 1 to 10000 inclusive)
  108. maxPartID = 10000
  109. )
  110. // isMaxObjectSize - verify if max object size
  111. func isMaxObjectSize(size int64) bool {
  112. return size > maxObjectSize
  113. }
  114. // Check if part size is more than or equal to minimum allowed size.
  115. func isMinAllowedPartSize(size int64) bool {
  116. return size >= minPartSize
  117. }
  118. // isMaxPartNumber - Check if part ID is greater than the maximum allowed ID.
  119. func isMaxPartID(partID int) bool {
  120. return partID > maxPartID
  121. }
  122. func contains(stringList []string, element string) bool {
  123. for _, e := range stringList {
  124. if e == element {
  125. return true
  126. }
  127. }
  128. return false
  129. }
  130. // Contains endpoint returns true if endpoint found in the list of input endpoints.
  131. func containsEndpoint(endpoints []*url.URL, endpoint *url.URL) bool {
  132. for _, ep := range endpoints {
  133. if *ep == *endpoint {
  134. return true
  135. }
  136. }
  137. return false
  138. }
  139. // urlPathSplit - split url path into bucket and object components.
  140. func urlPathSplit(urlPath string) (bucketName, prefixName string) {
  141. if urlPath == "" {
  142. return urlPath, ""
  143. }
  144. urlPath = strings.TrimPrefix(urlPath, "/")
  145. i := strings.Index(urlPath, "/")
  146. if i != -1 {
  147. return urlPath[:i], urlPath[i+1:]
  148. }
  149. return urlPath, ""
  150. }
  151. // Starts a profiler returns nil if profiler is not enabled, caller needs to handle this.
  152. func startProfiler(profiler string) interface {
  153. Stop()
  154. } {
  155. // Set ``MINIO_PROFILE_DIR`` to the directory where profiling information should be persisted
  156. profileDir := os.Getenv("MINIO_PROFILE_DIR")
  157. // Enable profiler if ``MINIO_PROFILER`` is set. Supported options are [cpu, mem, block].
  158. switch profiler {
  159. case "cpu":
  160. return profile.Start(profile.CPUProfile, profile.NoShutdownHook, profile.ProfilePath(profileDir))
  161. case "mem":
  162. return profile.Start(profile.MemProfile, profile.NoShutdownHook, profile.ProfilePath(profileDir))
  163. case "block":
  164. return profile.Start(profile.BlockProfile, profile.NoShutdownHook, profile.ProfilePath(profileDir))
  165. default:
  166. return nil
  167. }
  168. }
  169. // Global profiler to be used by service go-routine.
  170. var globalProfiler interface {
  171. Stop()
  172. }
  173. // dump the request into a string in JSON format.
  174. func dumpRequest(r *http.Request) string {
  175. header := cloneHeader(r.Header)
  176. header.Set("Host", r.Host)
  177. req := struct {
  178. Method string `json:"method"`
  179. Path string `json:"path"`
  180. Query string `json:"query"`
  181. Header http.Header `json:"header"`
  182. }{r.Method, r.URL.Path, r.URL.RawQuery, header}
  183. jsonBytes, err := json.Marshal(req)
  184. if err != nil {
  185. return "<error dumping request>"
  186. }
  187. return string(jsonBytes)
  188. }