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.

339 lines
9.8 KiB

9 years ago
6 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
7 years ago
6 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
6 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package weed_server
  2. import (
  3. "context"
  4. "crypto/md5"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "mime"
  11. "net/http"
  12. "net/url"
  13. "os"
  14. filenamePath "path"
  15. "strconv"
  16. "strings"
  17. "time"
  18. "github.com/chrislusf/seaweedfs/weed/filer2"
  19. "github.com/chrislusf/seaweedfs/weed/glog"
  20. "github.com/chrislusf/seaweedfs/weed/operation"
  21. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  22. "github.com/chrislusf/seaweedfs/weed/security"
  23. "github.com/chrislusf/seaweedfs/weed/stats"
  24. "github.com/chrislusf/seaweedfs/weed/util"
  25. )
  26. var (
  27. OS_UID = uint32(os.Getuid())
  28. OS_GID = uint32(os.Getgid())
  29. )
  30. type FilerPostResult struct {
  31. Name string `json:"name,omitempty"`
  32. Size int64 `json:"size,omitempty"`
  33. Error string `json:"error,omitempty"`
  34. Fid string `json:"fid,omitempty"`
  35. Url string `json:"url,omitempty"`
  36. }
  37. func (fs *FilerServer) assignNewFileInfo(w http.ResponseWriter, r *http.Request, replication, collection string, dataCenter string) (fileId, urlLocation string, auth security.EncodedJwt, err error) {
  38. stats.FilerRequestCounter.WithLabelValues("assign").Inc()
  39. start := time.Now()
  40. defer func() { stats.FilerRequestHistogram.WithLabelValues("assign").Observe(time.Since(start).Seconds()) }()
  41. ar := &operation.VolumeAssignRequest{
  42. Count: 1,
  43. Replication: replication,
  44. Collection: collection,
  45. Ttl: r.URL.Query().Get("ttl"),
  46. DataCenter: dataCenter,
  47. }
  48. var altRequest *operation.VolumeAssignRequest
  49. if dataCenter != "" {
  50. altRequest = &operation.VolumeAssignRequest{
  51. Count: 1,
  52. Replication: replication,
  53. Collection: collection,
  54. Ttl: r.URL.Query().Get("ttl"),
  55. DataCenter: "",
  56. }
  57. }
  58. assignResult, ae := operation.Assign(fs.filer.GetMaster(), fs.grpcDialOption, ar, altRequest)
  59. if ae != nil {
  60. glog.Errorf("failing to assign a file id: %v", ae)
  61. writeJsonError(w, r, http.StatusInternalServerError, ae)
  62. err = ae
  63. return
  64. }
  65. fileId = assignResult.Fid
  66. urlLocation = "http://" + assignResult.Url + "/" + assignResult.Fid
  67. auth = assignResult.Auth
  68. return
  69. }
  70. func (fs *FilerServer) PostHandler(w http.ResponseWriter, r *http.Request) {
  71. ctx := context.Background()
  72. query := r.URL.Query()
  73. collection, replication := fs.detectCollection(r.RequestURI, query.Get("collection"), query.Get("replication"))
  74. dataCenter := query.Get("dataCenter")
  75. if dataCenter == "" {
  76. dataCenter = fs.option.DataCenter
  77. }
  78. if autoChunked := fs.autoChunk(ctx, w, r, replication, collection, dataCenter); autoChunked {
  79. return
  80. }
  81. if fs.option.Cipher {
  82. reply, err := fs.encrypt(ctx, w, r, replication, collection, dataCenter)
  83. if err != nil {
  84. writeJsonError(w, r, http.StatusInternalServerError, err)
  85. } else if reply != nil {
  86. writeJsonQuiet(w, r, http.StatusCreated, reply)
  87. }
  88. return
  89. }
  90. fileId, urlLocation, auth, err := fs.assignNewFileInfo(w, r, replication, collection, dataCenter)
  91. if err != nil || fileId == "" || urlLocation == "" {
  92. glog.V(0).Infof("fail to allocate volume for %s, collection:%s, datacenter:%s", r.URL.Path, collection, dataCenter)
  93. writeJsonError(w, r, http.StatusInternalServerError, fmt.Errorf("fail to allocate volume for %s, collection:%s, datacenter:%s", r.URL.Path, collection, dataCenter))
  94. return
  95. }
  96. glog.V(4).Infof("write %s to %v", r.URL.Path, urlLocation)
  97. u, _ := url.Parse(urlLocation)
  98. ret, err := fs.uploadToVolumeServer(r, u, auth, w, fileId)
  99. if err != nil {
  100. return
  101. }
  102. if err = fs.updateFilerStore(ctx, r, w, replication, collection, ret, fileId); err != nil {
  103. return
  104. }
  105. // send back post result
  106. reply := FilerPostResult{
  107. Name: ret.Name,
  108. Size: int64(ret.Size),
  109. Error: ret.Error,
  110. Fid: fileId,
  111. Url: urlLocation,
  112. }
  113. setEtag(w, ret.ETag)
  114. writeJsonQuiet(w, r, http.StatusCreated, reply)
  115. }
  116. // update metadata in filer store
  117. func (fs *FilerServer) updateFilerStore(ctx context.Context, r *http.Request, w http.ResponseWriter,
  118. replication string, collection string, ret *operation.UploadResult, fileId string) (err error) {
  119. stats.FilerRequestCounter.WithLabelValues("postStoreWrite").Inc()
  120. start := time.Now()
  121. defer func() {
  122. stats.FilerRequestHistogram.WithLabelValues("postStoreWrite").Observe(time.Since(start).Seconds())
  123. }()
  124. modeStr := r.URL.Query().Get("mode")
  125. if modeStr == "" {
  126. modeStr = "0660"
  127. }
  128. mode, err := strconv.ParseUint(modeStr, 8, 32)
  129. if err != nil {
  130. glog.Errorf("Invalid mode format: %s, use 0660 by default", modeStr)
  131. mode = 0660
  132. }
  133. path := r.URL.Path
  134. if strings.HasSuffix(path, "/") {
  135. if ret.Name != "" {
  136. path += ret.Name
  137. }
  138. }
  139. existingEntry, err := fs.filer.FindEntry(ctx, filer2.FullPath(path))
  140. crTime := time.Now()
  141. if err == nil && existingEntry != nil {
  142. crTime = existingEntry.Crtime
  143. }
  144. entry := &filer2.Entry{
  145. FullPath: filer2.FullPath(path),
  146. Attr: filer2.Attr{
  147. Mtime: time.Now(),
  148. Crtime: crTime,
  149. Mode: os.FileMode(mode),
  150. Uid: OS_UID,
  151. Gid: OS_GID,
  152. Replication: replication,
  153. Collection: collection,
  154. TtlSec: int32(util.ParseInt(r.URL.Query().Get("ttl"), 0)),
  155. Mime: ret.Mime,
  156. },
  157. Chunks: []*filer_pb.FileChunk{{
  158. FileId: fileId,
  159. Size: uint64(ret.Size),
  160. Mtime: time.Now().UnixNano(),
  161. ETag: ret.ETag,
  162. }},
  163. }
  164. if entry.Attr.Mime == "" {
  165. if ext := filenamePath.Ext(path); ext != "" {
  166. entry.Attr.Mime = mime.TypeByExtension(ext)
  167. }
  168. }
  169. // glog.V(4).Infof("saving %s => %+v", path, entry)
  170. if dbErr := fs.filer.CreateEntry(ctx, entry, false); dbErr != nil {
  171. fs.filer.DeleteChunks(entry.Chunks)
  172. glog.V(0).Infof("failing to write %s to filer server : %v", path, dbErr)
  173. writeJsonError(w, r, http.StatusInternalServerError, dbErr)
  174. err = dbErr
  175. return
  176. }
  177. return nil
  178. }
  179. // send request to volume server
  180. func (fs *FilerServer) uploadToVolumeServer(r *http.Request, u *url.URL, auth security.EncodedJwt, w http.ResponseWriter, fileId string) (ret *operation.UploadResult, err error) {
  181. stats.FilerRequestCounter.WithLabelValues("postUpload").Inc()
  182. start := time.Now()
  183. defer func() { stats.FilerRequestHistogram.WithLabelValues("postUpload").Observe(time.Since(start).Seconds()) }()
  184. ret = &operation.UploadResult{}
  185. hash := md5.New()
  186. var body = ioutil.NopCloser(io.TeeReader(r.Body, hash))
  187. request := &http.Request{
  188. Method: r.Method,
  189. URL: u,
  190. Proto: r.Proto,
  191. ProtoMajor: r.ProtoMajor,
  192. ProtoMinor: r.ProtoMinor,
  193. Header: r.Header,
  194. Body: body,
  195. Host: r.Host,
  196. ContentLength: r.ContentLength,
  197. }
  198. if auth != "" {
  199. request.Header.Set("Authorization", "BEARER "+string(auth))
  200. }
  201. resp, doErr := util.Do(request)
  202. if doErr != nil {
  203. glog.Errorf("failing to connect to volume server %s: %v, %+v", r.RequestURI, doErr, r.Method)
  204. writeJsonError(w, r, http.StatusInternalServerError, doErr)
  205. err = doErr
  206. return
  207. }
  208. defer func() {
  209. io.Copy(ioutil.Discard, resp.Body)
  210. resp.Body.Close()
  211. }()
  212. respBody, raErr := ioutil.ReadAll(resp.Body)
  213. if raErr != nil {
  214. glog.V(0).Infoln("failing to upload to volume server", r.RequestURI, raErr.Error())
  215. writeJsonError(w, r, http.StatusInternalServerError, raErr)
  216. err = raErr
  217. return
  218. }
  219. glog.V(4).Infoln("post result", string(respBody))
  220. unmarshalErr := json.Unmarshal(respBody, &ret)
  221. if unmarshalErr != nil {
  222. glog.V(0).Infoln("failing to read upload resonse", r.RequestURI, string(respBody))
  223. writeJsonError(w, r, http.StatusInternalServerError, unmarshalErr)
  224. err = unmarshalErr
  225. return
  226. }
  227. if ret.Error != "" {
  228. err = errors.New(ret.Error)
  229. glog.V(0).Infoln("failing to post to volume server", r.RequestURI, ret.Error)
  230. writeJsonError(w, r, http.StatusInternalServerError, err)
  231. return
  232. }
  233. // find correct final path
  234. path := r.URL.Path
  235. if strings.HasSuffix(path, "/") {
  236. if ret.Name != "" {
  237. path += ret.Name
  238. } else {
  239. err = fmt.Errorf("can not to write to folder %s without a file name", path)
  240. fs.filer.DeleteFileByFileId(fileId)
  241. glog.V(0).Infoln("Can not to write to folder", path, "without a file name!")
  242. writeJsonError(w, r, http.StatusInternalServerError, err)
  243. return
  244. }
  245. }
  246. // use filer calculated md5 ETag, instead of the volume server crc ETag
  247. ret.ETag = fmt.Sprintf("%x", hash.Sum(nil))
  248. return
  249. }
  250. // curl -X DELETE http://localhost:8888/path/to
  251. // curl -X DELETE http://localhost:8888/path/to?recursive=true
  252. // curl -X DELETE http://localhost:8888/path/to?recursive=true&ignoreRecursiveError=true
  253. // curl -X DELETE http://localhost:8888/path/to?recursive=true&skipChunkDeletion=true
  254. func (fs *FilerServer) DeleteHandler(w http.ResponseWriter, r *http.Request) {
  255. isRecursive := r.FormValue("recursive") == "true"
  256. if !isRecursive && fs.option.recursiveDelete {
  257. if r.FormValue("recursive") != "false" {
  258. isRecursive = true
  259. }
  260. }
  261. ignoreRecursiveError := r.FormValue("ignoreRecursiveError") == "true"
  262. skipChunkDeletion := r.FormValue("skipChunkDeletion") == "true"
  263. err := fs.filer.DeleteEntryMetaAndData(context.Background(), filer2.FullPath(r.URL.Path), isRecursive, ignoreRecursiveError, !skipChunkDeletion)
  264. if err != nil {
  265. glog.V(1).Infoln("deleting", r.URL.Path, ":", err.Error())
  266. httpStatus := http.StatusInternalServerError
  267. if err == filer_pb.ErrNotFound {
  268. httpStatus = http.StatusNotFound
  269. }
  270. writeJsonError(w, r, httpStatus, err)
  271. return
  272. }
  273. w.WriteHeader(http.StatusNoContent)
  274. }
  275. func (fs *FilerServer) detectCollection(requestURI, qCollection, qReplication string) (collection, replication string) {
  276. // default
  277. collection = fs.option.Collection
  278. replication = fs.option.DefaultReplication
  279. // get default collection settings
  280. if qCollection != "" {
  281. collection = qCollection
  282. }
  283. if qReplication != "" {
  284. replication = qReplication
  285. }
  286. // required by buckets folder
  287. if strings.HasPrefix(requestURI, fs.filer.DirBucketsPath+"/") {
  288. bucketAndObjectKey := requestURI[len(fs.filer.DirBucketsPath)+1:]
  289. t := strings.Index(bucketAndObjectKey, "/")
  290. if t < 0 {
  291. collection = bucketAndObjectKey
  292. }
  293. if t > 0 {
  294. collection = bucketAndObjectKey[:t]
  295. }
  296. replication = fs.filer.ReadBucketOption(collection)
  297. }
  298. return
  299. }