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.

355 lines
10 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
5 years ago
5 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/storage/needle"
  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(replication, collection, dataCenter, ttlString string, fsync bool) (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: ttlString,
  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: ttlString,
  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. err = ae
  62. return
  63. }
  64. fileId = assignResult.Fid
  65. urlLocation = "http://" + assignResult.Url + "/" + assignResult.Fid
  66. if fsync {
  67. urlLocation += "?fsync=true"
  68. }
  69. auth = assignResult.Auth
  70. return
  71. }
  72. func (fs *FilerServer) PostHandler(w http.ResponseWriter, r *http.Request) {
  73. ctx := context.Background()
  74. query := r.URL.Query()
  75. collection, replication, fsync := fs.detectCollection(r.RequestURI, query.Get("collection"), query.Get("replication"))
  76. dataCenter := query.Get("dataCenter")
  77. if dataCenter == "" {
  78. dataCenter = fs.option.DataCenter
  79. }
  80. ttlString := r.URL.Query().Get("ttl")
  81. // read ttl in seconds
  82. ttl, err := needle.ReadTTL(ttlString)
  83. ttlSeconds := int32(0)
  84. if err == nil {
  85. ttlSeconds = int32(ttl.Minutes()) * 60
  86. }
  87. if autoChunked := fs.autoChunk(ctx, w, r, replication, collection, dataCenter, ttlSeconds, ttlString, fsync); autoChunked {
  88. return
  89. }
  90. if fs.option.Cipher {
  91. reply, err := fs.encrypt(ctx, w, r, replication, collection, dataCenter, ttlSeconds, ttlString, fsync)
  92. if err != nil {
  93. writeJsonError(w, r, http.StatusInternalServerError, err)
  94. } else if reply != nil {
  95. writeJsonQuiet(w, r, http.StatusCreated, reply)
  96. }
  97. return
  98. }
  99. fileId, urlLocation, auth, err := fs.assignNewFileInfo(replication, collection, dataCenter, ttlString, fsync)
  100. if err != nil || fileId == "" || urlLocation == "" {
  101. glog.V(0).Infof("fail to allocate volume for %s, collection:%s, datacenter:%s", r.URL.Path, collection, dataCenter)
  102. writeJsonError(w, r, http.StatusInternalServerError, fmt.Errorf("fail to allocate volume for %s, collection:%s, datacenter:%s", r.URL.Path, collection, dataCenter))
  103. return
  104. }
  105. glog.V(4).Infof("write %s to %v", r.URL.Path, urlLocation)
  106. u, _ := url.Parse(urlLocation)
  107. ret, err := fs.uploadToVolumeServer(r, u, auth, w, fileId)
  108. if err != nil {
  109. return
  110. }
  111. if err = fs.updateFilerStore(ctx, r, w, replication, collection, ret, fileId, ttlSeconds); err != nil {
  112. return
  113. }
  114. // send back post result
  115. reply := FilerPostResult{
  116. Name: ret.Name,
  117. Size: int64(ret.Size),
  118. Error: ret.Error,
  119. Fid: fileId,
  120. Url: urlLocation,
  121. }
  122. setEtag(w, ret.ETag)
  123. writeJsonQuiet(w, r, http.StatusCreated, reply)
  124. }
  125. // update metadata in filer store
  126. func (fs *FilerServer) updateFilerStore(ctx context.Context, r *http.Request, w http.ResponseWriter, replication string,
  127. collection string, ret *operation.UploadResult, fileId string, ttlSeconds int32) (err error) {
  128. stats.FilerRequestCounter.WithLabelValues("postStoreWrite").Inc()
  129. start := time.Now()
  130. defer func() {
  131. stats.FilerRequestHistogram.WithLabelValues("postStoreWrite").Observe(time.Since(start).Seconds())
  132. }()
  133. modeStr := r.URL.Query().Get("mode")
  134. if modeStr == "" {
  135. modeStr = "0660"
  136. }
  137. mode, err := strconv.ParseUint(modeStr, 8, 32)
  138. if err != nil {
  139. glog.Errorf("Invalid mode format: %s, use 0660 by default", modeStr)
  140. mode = 0660
  141. }
  142. path := r.URL.Path
  143. if strings.HasSuffix(path, "/") {
  144. if ret.Name != "" {
  145. path += ret.Name
  146. }
  147. }
  148. existingEntry, err := fs.filer.FindEntry(ctx, util.FullPath(path))
  149. crTime := time.Now()
  150. if err == nil && existingEntry != nil {
  151. crTime = existingEntry.Crtime
  152. }
  153. entry := &filer2.Entry{
  154. FullPath: util.FullPath(path),
  155. Attr: filer2.Attr{
  156. Mtime: time.Now(),
  157. Crtime: crTime,
  158. Mode: os.FileMode(mode),
  159. Uid: OS_UID,
  160. Gid: OS_GID,
  161. Replication: replication,
  162. Collection: collection,
  163. TtlSec: ttlSeconds,
  164. Mime: ret.Mime,
  165. Md5: util.Base64Md5ToBytes(ret.ContentMd5),
  166. },
  167. Chunks: []*filer_pb.FileChunk{{
  168. FileId: fileId,
  169. Size: uint64(ret.Size),
  170. Mtime: time.Now().UnixNano(),
  171. ETag: ret.ETag,
  172. }},
  173. }
  174. if entry.Attr.Mime == "" {
  175. if ext := filenamePath.Ext(path); ext != "" {
  176. entry.Attr.Mime = mime.TypeByExtension(ext)
  177. }
  178. }
  179. // glog.V(4).Infof("saving %s => %+v", path, entry)
  180. if dbErr := fs.filer.CreateEntry(ctx, entry, false, false); dbErr != nil {
  181. fs.filer.DeleteChunks(entry.Chunks)
  182. glog.V(0).Infof("failing to write %s to filer server : %v", path, dbErr)
  183. writeJsonError(w, r, http.StatusInternalServerError, dbErr)
  184. err = dbErr
  185. return
  186. }
  187. return nil
  188. }
  189. // send request to volume server
  190. func (fs *FilerServer) uploadToVolumeServer(r *http.Request, u *url.URL, auth security.EncodedJwt, w http.ResponseWriter, fileId string) (ret *operation.UploadResult, err error) {
  191. stats.FilerRequestCounter.WithLabelValues("postUpload").Inc()
  192. start := time.Now()
  193. defer func() { stats.FilerRequestHistogram.WithLabelValues("postUpload").Observe(time.Since(start).Seconds()) }()
  194. ret = &operation.UploadResult{}
  195. body := r.Body
  196. request := &http.Request{
  197. Method: r.Method,
  198. URL: u,
  199. Proto: r.Proto,
  200. ProtoMajor: r.ProtoMajor,
  201. ProtoMinor: r.ProtoMinor,
  202. Header: r.Header,
  203. Body: body,
  204. Host: r.Host,
  205. ContentLength: r.ContentLength,
  206. }
  207. if auth != "" {
  208. request.Header.Set("Authorization", "BEARER "+string(auth))
  209. }
  210. resp, doErr := util.Do(request)
  211. if doErr != nil {
  212. glog.Errorf("failing to connect to volume server %s: %v, %+v", r.RequestURI, doErr, r.Method)
  213. writeJsonError(w, r, http.StatusInternalServerError, doErr)
  214. err = doErr
  215. return
  216. }
  217. defer func() {
  218. io.Copy(ioutil.Discard, resp.Body)
  219. resp.Body.Close()
  220. }()
  221. respBody, raErr := ioutil.ReadAll(resp.Body)
  222. if raErr != nil {
  223. glog.V(0).Infoln("failing to upload to volume server", r.RequestURI, raErr.Error())
  224. writeJsonError(w, r, http.StatusInternalServerError, raErr)
  225. err = raErr
  226. return
  227. }
  228. glog.V(4).Infoln("post result", string(respBody))
  229. unmarshalErr := json.Unmarshal(respBody, &ret)
  230. if unmarshalErr != nil {
  231. glog.V(0).Infoln("failing to read upload resonse", r.RequestURI, string(respBody))
  232. writeJsonError(w, r, http.StatusInternalServerError, unmarshalErr)
  233. err = unmarshalErr
  234. return
  235. }
  236. if ret.Error != "" {
  237. err = errors.New(ret.Error)
  238. glog.V(0).Infoln("failing to post to volume server", r.RequestURI, ret.Error)
  239. writeJsonError(w, r, http.StatusInternalServerError, err)
  240. return
  241. }
  242. // find correct final path
  243. path := r.URL.Path
  244. if strings.HasSuffix(path, "/") {
  245. if ret.Name != "" {
  246. path += ret.Name
  247. } else {
  248. err = fmt.Errorf("can not to write to folder %s without a file name", path)
  249. fs.filer.DeleteFileByFileId(fileId)
  250. glog.V(0).Infoln("Can not to write to folder", path, "without a file name!")
  251. writeJsonError(w, r, http.StatusInternalServerError, err)
  252. return
  253. }
  254. }
  255. ret.ETag = getEtag(resp)
  256. ret.ContentMd5 = resp.Header.Get("Content-MD5")
  257. return
  258. }
  259. // curl -X DELETE http://localhost:8888/path/to
  260. // curl -X DELETE http://localhost:8888/path/to?recursive=true
  261. // curl -X DELETE http://localhost:8888/path/to?recursive=true&ignoreRecursiveError=true
  262. // curl -X DELETE http://localhost:8888/path/to?recursive=true&skipChunkDeletion=true
  263. func (fs *FilerServer) DeleteHandler(w http.ResponseWriter, r *http.Request) {
  264. isRecursive := r.FormValue("recursive") == "true"
  265. if !isRecursive && fs.option.recursiveDelete {
  266. if r.FormValue("recursive") != "false" {
  267. isRecursive = true
  268. }
  269. }
  270. ignoreRecursiveError := r.FormValue("ignoreRecursiveError") == "true"
  271. skipChunkDeletion := r.FormValue("skipChunkDeletion") == "true"
  272. objectPath := r.URL.Path
  273. if len(r.URL.Path) > 1 && strings.HasSuffix(objectPath, "/") {
  274. objectPath = objectPath[0 : len(objectPath)-1]
  275. }
  276. err := fs.filer.DeleteEntryMetaAndData(context.Background(), util.FullPath(objectPath), isRecursive, ignoreRecursiveError, !skipChunkDeletion, false)
  277. if err != nil {
  278. glog.V(1).Infoln("deleting", objectPath, ":", err.Error())
  279. httpStatus := http.StatusInternalServerError
  280. if err == filer_pb.ErrNotFound {
  281. httpStatus = http.StatusNotFound
  282. }
  283. writeJsonError(w, r, httpStatus, err)
  284. return
  285. }
  286. w.WriteHeader(http.StatusNoContent)
  287. }
  288. func (fs *FilerServer) detectCollection(requestURI, qCollection, qReplication string) (collection, replication string, fsync bool) {
  289. // default
  290. collection = fs.option.Collection
  291. replication = fs.option.DefaultReplication
  292. // get default collection settings
  293. if qCollection != "" {
  294. collection = qCollection
  295. }
  296. if qReplication != "" {
  297. replication = qReplication
  298. }
  299. // required by buckets folder
  300. if strings.HasPrefix(requestURI, fs.filer.DirBucketsPath+"/") {
  301. bucketAndObjectKey := requestURI[len(fs.filer.DirBucketsPath)+1:]
  302. t := strings.Index(bucketAndObjectKey, "/")
  303. if t < 0 {
  304. collection = bucketAndObjectKey
  305. }
  306. if t > 0 {
  307. collection = bucketAndObjectKey[:t]
  308. }
  309. replication, fsync = fs.filer.ReadBucketOption(collection)
  310. }
  311. return
  312. }