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.

224 lines
6.3 KiB

9 years ago
7 years ago
9 years ago
7 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
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
9 years ago
  1. package weed_server
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "io/ioutil"
  6. "net/http"
  7. "net/url"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/chrislusf/seaweedfs/weed/filer2"
  12. "github.com/chrislusf/seaweedfs/weed/glog"
  13. "github.com/chrislusf/seaweedfs/weed/operation"
  14. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  15. "github.com/chrislusf/seaweedfs/weed/util"
  16. "os"
  17. )
  18. var (
  19. OS_UID = uint32(os.Getuid())
  20. OS_GID = uint32(os.Getgid())
  21. )
  22. type FilerPostResult struct {
  23. Name string `json:"name,omitempty"`
  24. Size uint32 `json:"size,omitempty"`
  25. Error string `json:"error,omitempty"`
  26. Fid string `json:"fid,omitempty"`
  27. Url string `json:"url,omitempty"`
  28. }
  29. func (fs *FilerServer) assignNewFileInfo(w http.ResponseWriter, r *http.Request, replication, collection string, dataCenter string) (fileId, urlLocation string, err error) {
  30. ar := &operation.VolumeAssignRequest{
  31. Count: 1,
  32. Replication: replication,
  33. Collection: collection,
  34. Ttl: r.URL.Query().Get("ttl"),
  35. DataCenter: dataCenter,
  36. }
  37. var altRequest *operation.VolumeAssignRequest
  38. if dataCenter != "" {
  39. altRequest = &operation.VolumeAssignRequest{
  40. Count: 1,
  41. Replication: replication,
  42. Collection: collection,
  43. Ttl: r.URL.Query().Get("ttl"),
  44. DataCenter: "",
  45. }
  46. }
  47. assignResult, ae := operation.Assign(fs.filer.GetMaster(), ar, altRequest)
  48. if ae != nil {
  49. glog.Errorf("failing to assign a file id: %v", ae)
  50. writeJsonError(w, r, http.StatusInternalServerError, ae)
  51. err = ae
  52. return
  53. }
  54. fileId = assignResult.Fid
  55. urlLocation = "http://" + assignResult.Url + "/" + assignResult.Fid
  56. return
  57. }
  58. func (fs *FilerServer) PostHandler(w http.ResponseWriter, r *http.Request) {
  59. query := r.URL.Query()
  60. replication := query.Get("replication")
  61. if replication == "" {
  62. replication = fs.option.DefaultReplication
  63. }
  64. collection := query.Get("collection")
  65. if collection == "" {
  66. collection = fs.option.Collection
  67. }
  68. dataCenter := query.Get("dataCenter")
  69. if dataCenter == "" {
  70. dataCenter = fs.option.DataCenter
  71. }
  72. if autoChunked := fs.autoChunk(w, r, replication, collection, dataCenter); autoChunked {
  73. return
  74. }
  75. fileId, urlLocation, err := fs.assignNewFileInfo(w, r, replication, collection, dataCenter)
  76. if err != nil || fileId == "" || urlLocation == "" {
  77. glog.V(0).Infof("fail to allocate volume for %s, collection:%s, datacenter:%s", r.URL.Path, collection, dataCenter)
  78. return
  79. }
  80. glog.V(4).Infof("write %s to %v", r.URL.Path, urlLocation)
  81. u, _ := url.Parse(urlLocation)
  82. // This allows a client to generate a chunk manifest and submit it to the filer -- it is a little off
  83. // because they need to provide FIDs instead of file paths...
  84. cm, _ := strconv.ParseBool(query.Get("cm"))
  85. if cm {
  86. q := u.Query()
  87. q.Set("cm", "true")
  88. u.RawQuery = q.Encode()
  89. }
  90. glog.V(4).Infoln("post to", u)
  91. // send request to volume server
  92. request := &http.Request{
  93. Method: r.Method,
  94. URL: u,
  95. Proto: r.Proto,
  96. ProtoMajor: r.ProtoMajor,
  97. ProtoMinor: r.ProtoMinor,
  98. Header: r.Header,
  99. Body: r.Body,
  100. Host: r.Host,
  101. ContentLength: r.ContentLength,
  102. }
  103. resp, do_err := util.Do(request)
  104. if do_err != nil {
  105. glog.Errorf("failing to connect to volume server %s: %v, %+v", r.RequestURI, do_err, r.Method)
  106. writeJsonError(w, r, http.StatusInternalServerError, do_err)
  107. return
  108. }
  109. defer resp.Body.Close()
  110. etag := resp.Header.Get("ETag")
  111. resp_body, ra_err := ioutil.ReadAll(resp.Body)
  112. if ra_err != nil {
  113. glog.V(0).Infoln("failing to upload to volume server", r.RequestURI, ra_err.Error())
  114. writeJsonError(w, r, http.StatusInternalServerError, ra_err)
  115. return
  116. }
  117. glog.V(4).Infoln("post result", string(resp_body))
  118. var ret operation.UploadResult
  119. unmarshal_err := json.Unmarshal(resp_body, &ret)
  120. if unmarshal_err != nil {
  121. glog.V(0).Infoln("failing to read upload resonse", r.RequestURI, string(resp_body))
  122. writeJsonError(w, r, http.StatusInternalServerError, unmarshal_err)
  123. return
  124. }
  125. if ret.Error != "" {
  126. glog.V(0).Infoln("failing to post to volume server", r.RequestURI, ret.Error)
  127. writeJsonError(w, r, http.StatusInternalServerError, errors.New(ret.Error))
  128. return
  129. }
  130. // find correct final path
  131. path := r.URL.Path
  132. if strings.HasSuffix(path, "/") {
  133. if ret.Name != "" {
  134. path += ret.Name
  135. } else {
  136. fs.filer.DeleteFileByFileId(fileId)
  137. glog.V(0).Infoln("Can not to write to folder", path, "without a file name!")
  138. writeJsonError(w, r, http.StatusInternalServerError,
  139. errors.New("Can not to write to folder "+path+" without a file name"))
  140. return
  141. }
  142. }
  143. // update metadata in filer store
  144. existingEntry, err := fs.filer.FindEntry(filer2.FullPath(path))
  145. crTime := time.Now()
  146. if err == nil && existingEntry != nil {
  147. // glog.V(4).Infof("existing %s => %+v", path, existingEntry)
  148. if existingEntry.IsDirectory() {
  149. path += "/" + ret.Name
  150. } else {
  151. crTime = existingEntry.Crtime
  152. }
  153. }
  154. entry := &filer2.Entry{
  155. FullPath: filer2.FullPath(path),
  156. Attr: filer2.Attr{
  157. Mtime: time.Now(),
  158. Crtime: crTime,
  159. Mode: 0660,
  160. Uid: OS_UID,
  161. Gid: OS_GID,
  162. Replication: replication,
  163. Collection: collection,
  164. TtlSec: int32(util.ParseInt(r.URL.Query().Get("ttl"), 0)),
  165. },
  166. Chunks: []*filer_pb.FileChunk{{
  167. FileId: fileId,
  168. Size: uint64(ret.Size),
  169. Mtime: time.Now().UnixNano(),
  170. ETag: etag,
  171. }},
  172. }
  173. // glog.V(4).Infof("saving %s => %+v", path, entry)
  174. if db_err := fs.filer.CreateEntry(entry); db_err != nil {
  175. fs.filer.DeleteFileByFileId(fileId)
  176. glog.V(0).Infof("failing to write %s to filer server : %v", path, db_err)
  177. writeJsonError(w, r, http.StatusInternalServerError, db_err)
  178. return
  179. }
  180. // send back post result
  181. reply := FilerPostResult{
  182. Name: ret.Name,
  183. Size: ret.Size,
  184. Error: ret.Error,
  185. Fid: fileId,
  186. Url: urlLocation,
  187. }
  188. setEtag(w, etag)
  189. writeJsonQuiet(w, r, http.StatusCreated, reply)
  190. }
  191. // curl -X DELETE http://localhost:8888/path/to
  192. // curl -X DELETE http://localhost:8888/path/to?recursive=true
  193. func (fs *FilerServer) DeleteHandler(w http.ResponseWriter, r *http.Request) {
  194. isRecursive := r.FormValue("recursive") == "true"
  195. err := fs.filer.DeleteEntryMetaAndData(filer2.FullPath(r.URL.Path), isRecursive, true)
  196. if err != nil {
  197. glog.V(1).Infoln("deleting", r.URL.Path, ":", err.Error())
  198. writeJsonError(w, r, http.StatusInternalServerError, err)
  199. return
  200. }
  201. w.WriteHeader(http.StatusNoContent)
  202. }