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.

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