server.go 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. /*
  17. A simple http server interface to Swarm
  18. */
  19. package http
  20. import (
  21. "archive/tar"
  22. "bufio"
  23. "bytes"
  24. "context"
  25. "encoding/json"
  26. "errors"
  27. "fmt"
  28. "io"
  29. "io/ioutil"
  30. "mime"
  31. "mime/multipart"
  32. "net/http"
  33. "os"
  34. "path"
  35. "regexp"
  36. "strconv"
  37. "strings"
  38. "time"
  39. "github.com/ethereum/go-ethereum/common"
  40. "github.com/ethereum/go-ethereum/common/hexutil"
  41. "github.com/ethereum/go-ethereum/metrics"
  42. "github.com/ethereum/go-ethereum/swarm/api"
  43. "github.com/ethereum/go-ethereum/swarm/log"
  44. "github.com/ethereum/go-ethereum/swarm/storage"
  45. "github.com/ethereum/go-ethereum/swarm/storage/mru"
  46. "github.com/pborman/uuid"
  47. "github.com/rs/cors"
  48. )
  49. type resourceResponse struct {
  50. Manifest storage.Address `json:"manifest"`
  51. Resource string `json:"resource"`
  52. Update storage.Address `json:"update"`
  53. }
  54. var (
  55. postRawCount = metrics.NewRegisteredCounter("api.http.post.raw.count", nil)
  56. postRawFail = metrics.NewRegisteredCounter("api.http.post.raw.fail", nil)
  57. postFilesCount = metrics.NewRegisteredCounter("api.http.post.files.count", nil)
  58. postFilesFail = metrics.NewRegisteredCounter("api.http.post.files.fail", nil)
  59. deleteCount = metrics.NewRegisteredCounter("api.http.delete.count", nil)
  60. deleteFail = metrics.NewRegisteredCounter("api.http.delete.fail", nil)
  61. getCount = metrics.NewRegisteredCounter("api.http.get.count", nil)
  62. getFail = metrics.NewRegisteredCounter("api.http.get.fail", nil)
  63. getFileCount = metrics.NewRegisteredCounter("api.http.get.file.count", nil)
  64. getFileNotFound = metrics.NewRegisteredCounter("api.http.get.file.notfound", nil)
  65. getFileFail = metrics.NewRegisteredCounter("api.http.get.file.fail", nil)
  66. getFilesCount = metrics.NewRegisteredCounter("api.http.get.files.count", nil)
  67. getFilesFail = metrics.NewRegisteredCounter("api.http.get.files.fail", nil)
  68. getListCount = metrics.NewRegisteredCounter("api.http.get.list.count", nil)
  69. getListFail = metrics.NewRegisteredCounter("api.http.get.list.fail", nil)
  70. )
  71. // ServerConfig is the basic configuration needed for the HTTP server and also
  72. // includes CORS settings.
  73. type ServerConfig struct {
  74. Addr string
  75. CorsString string
  76. }
  77. // browser API for registering bzz url scheme handlers:
  78. // https://developer.mozilla.org/en/docs/Web-based_protocol_handlers
  79. // electron (chromium) api for registering bzz url scheme handlers:
  80. // https://github.com/atom/electron/blob/master/docs/api/protocol.md
  81. // starts up http server
  82. func StartHTTPServer(api *api.API, config *ServerConfig) {
  83. var allowedOrigins []string
  84. for _, domain := range strings.Split(config.CorsString, ",") {
  85. allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
  86. }
  87. c := cors.New(cors.Options{
  88. AllowedOrigins: allowedOrigins,
  89. AllowedMethods: []string{"POST", "GET", "DELETE", "PATCH", "PUT"},
  90. MaxAge: 600,
  91. AllowedHeaders: []string{"*"},
  92. })
  93. hdlr := c.Handler(NewServer(api))
  94. go http.ListenAndServe(config.Addr, hdlr)
  95. }
  96. func NewServer(api *api.API) *Server {
  97. return &Server{api}
  98. }
  99. type Server struct {
  100. api *api.API
  101. }
  102. // Request wraps http.Request and also includes the parsed bzz URI
  103. type Request struct {
  104. http.Request
  105. uri *api.URI
  106. ruid string // request unique id
  107. }
  108. // HandlePostRaw handles a POST request to a raw bzz-raw:/ URI, stores the request
  109. // body in swarm and returns the resulting storage address as a text/plain response
  110. func (s *Server) HandlePostRaw(ctx context.Context, w http.ResponseWriter, r *Request) {
  111. log.Debug("handle.post.raw", "ruid", r.ruid)
  112. postRawCount.Inc(1)
  113. toEncrypt := false
  114. if r.uri.Addr == "encrypt" {
  115. toEncrypt = true
  116. }
  117. if r.uri.Path != "" {
  118. postRawFail.Inc(1)
  119. Respond(w, r, "raw POST request cannot contain a path", http.StatusBadRequest)
  120. return
  121. }
  122. if r.uri.Addr != "" && r.uri.Addr != "encrypt" {
  123. postRawFail.Inc(1)
  124. Respond(w, r, "raw POST request addr can only be empty or \"encrypt\"", http.StatusBadRequest)
  125. return
  126. }
  127. if r.Header.Get("Content-Length") == "" {
  128. postRawFail.Inc(1)
  129. Respond(w, r, "missing Content-Length header in request", http.StatusBadRequest)
  130. return
  131. }
  132. addr, _, err := s.api.Store(ctx, r.Body, r.ContentLength, toEncrypt)
  133. if err != nil {
  134. postRawFail.Inc(1)
  135. Respond(w, r, err.Error(), http.StatusInternalServerError)
  136. return
  137. }
  138. log.Debug("stored content", "ruid", r.ruid, "key", addr)
  139. w.Header().Set("Content-Type", "text/plain")
  140. w.WriteHeader(http.StatusOK)
  141. fmt.Fprint(w, addr)
  142. }
  143. // HandlePostFiles handles a POST request to
  144. // bzz:/<hash>/<path> which contains either a single file or multiple files
  145. // (either a tar archive or multipart form), adds those files either to an
  146. // existing manifest or to a new manifest under <path> and returns the
  147. // resulting manifest hash as a text/plain response
  148. func (s *Server) HandlePostFiles(ctx context.Context, w http.ResponseWriter, r *Request) {
  149. log.Debug("handle.post.files", "ruid", r.ruid)
  150. postFilesCount.Inc(1)
  151. contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
  152. if err != nil {
  153. postFilesFail.Inc(1)
  154. Respond(w, r, err.Error(), http.StatusBadRequest)
  155. return
  156. }
  157. toEncrypt := false
  158. if r.uri.Addr == "encrypt" {
  159. toEncrypt = true
  160. }
  161. var addr storage.Address
  162. if r.uri.Addr != "" && r.uri.Addr != "encrypt" {
  163. addr, err = s.api.Resolve(ctx, r.uri)
  164. if err != nil {
  165. postFilesFail.Inc(1)
  166. Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusInternalServerError)
  167. return
  168. }
  169. log.Debug("resolved key", "ruid", r.ruid, "key", addr)
  170. } else {
  171. addr, err = s.api.NewManifest(ctx, toEncrypt)
  172. if err != nil {
  173. postFilesFail.Inc(1)
  174. Respond(w, r, err.Error(), http.StatusInternalServerError)
  175. return
  176. }
  177. log.Debug("new manifest", "ruid", r.ruid, "key", addr)
  178. }
  179. newAddr, err := s.updateManifest(ctx, addr, func(mw *api.ManifestWriter) error {
  180. switch contentType {
  181. case "application/x-tar":
  182. return s.handleTarUpload(ctx, r, mw)
  183. case "multipart/form-data":
  184. return s.handleMultipartUpload(ctx, r, params["boundary"], mw)
  185. default:
  186. return s.handleDirectUpload(ctx, r, mw)
  187. }
  188. })
  189. if err != nil {
  190. postFilesFail.Inc(1)
  191. Respond(w, r, fmt.Sprintf("cannot create manifest: %s", err), http.StatusInternalServerError)
  192. return
  193. }
  194. log.Debug("stored content", "ruid", r.ruid, "key", newAddr)
  195. w.Header().Set("Content-Type", "text/plain")
  196. w.WriteHeader(http.StatusOK)
  197. fmt.Fprint(w, newAddr)
  198. }
  199. func (s *Server) handleTarUpload(ctx context.Context, req *Request, mw *api.ManifestWriter) error {
  200. log.Debug("handle.tar.upload", "ruid", req.ruid)
  201. tr := tar.NewReader(req.Body)
  202. for {
  203. hdr, err := tr.Next()
  204. if err == io.EOF {
  205. return nil
  206. } else if err != nil {
  207. return fmt.Errorf("error reading tar stream: %s", err)
  208. }
  209. // only store regular files
  210. if !hdr.FileInfo().Mode().IsRegular() {
  211. continue
  212. }
  213. // add the entry under the path from the request
  214. path := path.Join(req.uri.Path, hdr.Name)
  215. entry := &api.ManifestEntry{
  216. Path: path,
  217. ContentType: hdr.Xattrs["user.swarm.content-type"],
  218. Mode: hdr.Mode,
  219. Size: hdr.Size,
  220. ModTime: hdr.ModTime,
  221. }
  222. log.Debug("adding path to new manifest", "ruid", req.ruid, "bytes", entry.Size, "path", entry.Path)
  223. contentKey, err := mw.AddEntry(ctx, tr, entry)
  224. if err != nil {
  225. return fmt.Errorf("error adding manifest entry from tar stream: %s", err)
  226. }
  227. log.Debug("stored content", "ruid", req.ruid, "key", contentKey)
  228. }
  229. }
  230. func (s *Server) handleMultipartUpload(ctx context.Context, req *Request, boundary string, mw *api.ManifestWriter) error {
  231. log.Debug("handle.multipart.upload", "ruid", req.ruid)
  232. mr := multipart.NewReader(req.Body, boundary)
  233. for {
  234. part, err := mr.NextPart()
  235. if err == io.EOF {
  236. return nil
  237. } else if err != nil {
  238. return fmt.Errorf("error reading multipart form: %s", err)
  239. }
  240. var size int64
  241. var reader io.Reader = part
  242. if contentLength := part.Header.Get("Content-Length"); contentLength != "" {
  243. size, err = strconv.ParseInt(contentLength, 10, 64)
  244. if err != nil {
  245. return fmt.Errorf("error parsing multipart content length: %s", err)
  246. }
  247. reader = part
  248. } else {
  249. // copy the part to a tmp file to get its size
  250. tmp, err := ioutil.TempFile("", "swarm-multipart")
  251. if err != nil {
  252. return err
  253. }
  254. defer os.Remove(tmp.Name())
  255. defer tmp.Close()
  256. size, err = io.Copy(tmp, part)
  257. if err != nil {
  258. return fmt.Errorf("error copying multipart content: %s", err)
  259. }
  260. if _, err := tmp.Seek(0, io.SeekStart); err != nil {
  261. return fmt.Errorf("error copying multipart content: %s", err)
  262. }
  263. reader = tmp
  264. }
  265. // add the entry under the path from the request
  266. name := part.FileName()
  267. if name == "" {
  268. name = part.FormName()
  269. }
  270. path := path.Join(req.uri.Path, name)
  271. entry := &api.ManifestEntry{
  272. Path: path,
  273. ContentType: part.Header.Get("Content-Type"),
  274. Size: size,
  275. ModTime: time.Now(),
  276. }
  277. log.Debug("adding path to new manifest", "ruid", req.ruid, "bytes", entry.Size, "path", entry.Path)
  278. contentKey, err := mw.AddEntry(ctx, reader, entry)
  279. if err != nil {
  280. return fmt.Errorf("error adding manifest entry from multipart form: %s", err)
  281. }
  282. log.Debug("stored content", "ruid", req.ruid, "key", contentKey)
  283. }
  284. }
  285. func (s *Server) handleDirectUpload(ctx context.Context, req *Request, mw *api.ManifestWriter) error {
  286. log.Debug("handle.direct.upload", "ruid", req.ruid)
  287. key, err := mw.AddEntry(ctx, req.Body, &api.ManifestEntry{
  288. Path: req.uri.Path,
  289. ContentType: req.Header.Get("Content-Type"),
  290. Mode: 0644,
  291. Size: req.ContentLength,
  292. ModTime: time.Now(),
  293. })
  294. if err != nil {
  295. return err
  296. }
  297. log.Debug("stored content", "ruid", req.ruid, "key", key)
  298. return nil
  299. }
  300. // HandleDelete handles a DELETE request to bzz:/<manifest>/<path>, removes
  301. // <path> from <manifest> and returns the resulting manifest hash as a
  302. // text/plain response
  303. func (s *Server) HandleDelete(ctx context.Context, w http.ResponseWriter, r *Request) {
  304. log.Debug("handle.delete", "ruid", r.ruid)
  305. deleteCount.Inc(1)
  306. key, err := s.api.Resolve(ctx, r.uri)
  307. if err != nil {
  308. deleteFail.Inc(1)
  309. Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusInternalServerError)
  310. return
  311. }
  312. newKey, err := s.updateManifest(ctx, key, func(mw *api.ManifestWriter) error {
  313. log.Debug(fmt.Sprintf("removing %s from manifest %s", r.uri.Path, key.Log()), "ruid", r.ruid)
  314. return mw.RemoveEntry(r.uri.Path)
  315. })
  316. if err != nil {
  317. deleteFail.Inc(1)
  318. Respond(w, r, fmt.Sprintf("cannot update manifest: %s", err), http.StatusInternalServerError)
  319. return
  320. }
  321. w.Header().Set("Content-Type", "text/plain")
  322. w.WriteHeader(http.StatusOK)
  323. fmt.Fprint(w, newKey)
  324. }
  325. // Parses a resource update post url to corresponding action
  326. // possible combinations:
  327. // / add multihash update to existing hash
  328. // /raw add raw update to existing hash
  329. // /# create new resource with first update as mulitihash
  330. // /raw/# create new resource with first update raw
  331. func resourcePostMode(path string) (isRaw bool, frequency uint64, err error) {
  332. re, err := regexp.Compile("^(raw)?/?([0-9]+)?$")
  333. if err != nil {
  334. return isRaw, frequency, err
  335. }
  336. m := re.FindAllStringSubmatch(path, 2)
  337. var freqstr = "0"
  338. if len(m) > 0 {
  339. if m[0][1] != "" {
  340. isRaw = true
  341. }
  342. if m[0][2] != "" {
  343. freqstr = m[0][2]
  344. }
  345. } else if len(path) > 0 {
  346. return isRaw, frequency, fmt.Errorf("invalid path")
  347. }
  348. frequency, err = strconv.ParseUint(freqstr, 10, 64)
  349. return isRaw, frequency, err
  350. }
  351. // Handles creation of new mutable resources and adding updates to existing mutable resources
  352. // There are two types of updates available, "raw" and "multihash."
  353. // If the latter is used, a subsequent bzz:// GET call to the manifest of the resource will return
  354. // the page that the multihash is pointing to, as if it held a normal swarm content manifest
  355. //
  356. // The resource name will be verbatim what is passed as the address part of the url.
  357. // For example, if a POST is made to /bzz-resource:/foo.eth/raw/13 a new resource with frequency 13
  358. // and name "foo.eth" will be created
  359. func (s *Server) HandlePostResource(ctx context.Context, w http.ResponseWriter, r *Request) {
  360. log.Debug("handle.post.resource", "ruid", r.ruid)
  361. var err error
  362. var addr storage.Address
  363. var name string
  364. var outdata []byte
  365. isRaw, frequency, err := resourcePostMode(r.uri.Path)
  366. if err != nil {
  367. Respond(w, r, err.Error(), http.StatusBadRequest)
  368. return
  369. }
  370. // new mutable resource creation will always have a frequency field larger than 0
  371. if frequency > 0 {
  372. name = r.uri.Addr
  373. // the key is the content addressed root chunk holding mutable resource metadata information
  374. addr, err = s.api.ResourceCreate(r.Context(), name, frequency)
  375. if err != nil {
  376. code, err2 := s.translateResourceError(w, r, "resource creation fail", err)
  377. Respond(w, r, err2.Error(), code)
  378. return
  379. }
  380. // we create a manifest so we can retrieve the resource with bzz:// later
  381. // this manifest has a special "resource type" manifest, and its hash is the key of the mutable resource
  382. // root chunk
  383. m, err := s.api.NewResourceManifest(ctx, addr.Hex())
  384. if err != nil {
  385. Respond(w, r, fmt.Sprintf("failed to create resource manifest: %v", err), http.StatusInternalServerError)
  386. return
  387. }
  388. // the key to the manifest will be passed back to the client
  389. // the client can access the root chunk key directly through its Hash member
  390. // the manifest key should be set as content in the resolver of the ENS name
  391. // \TODO update manifest key automatically in ENS
  392. outdata, err = json.Marshal(m)
  393. if err != nil {
  394. Respond(w, r, fmt.Sprintf("failed to create json response: %s", err), http.StatusInternalServerError)
  395. return
  396. }
  397. } else {
  398. // to update the resource through http we need to retrieve the key for the mutable resource root chunk
  399. // that means that we retrieve the manifest and inspect its Hash member.
  400. manifestAddr := r.uri.Address()
  401. if manifestAddr == nil {
  402. manifestAddr, err = s.api.Resolve(ctx, r.uri)
  403. if err != nil {
  404. getFail.Inc(1)
  405. Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
  406. return
  407. }
  408. } else {
  409. w.Header().Set("Cache-Control", "max-age=2147483648")
  410. }
  411. // get the root chunk key from the manifest
  412. addr, err = s.api.ResolveResourceManifest(ctx, manifestAddr)
  413. if err != nil {
  414. getFail.Inc(1)
  415. Respond(w, r, fmt.Sprintf("error resolving resource root chunk for %s: %s", r.uri.Addr, err), http.StatusNotFound)
  416. return
  417. }
  418. log.Debug("handle.post.resource: resolved", "ruid", r.ruid, "manifestkey", manifestAddr, "rootchunkkey", addr)
  419. name, _, err = s.api.ResourceLookup(r.Context(), addr, 0, 0, &mru.LookupParams{})
  420. if err != nil {
  421. Respond(w, r, err.Error(), http.StatusNotFound)
  422. return
  423. }
  424. }
  425. // Creation and update must send data aswell. This data constitutes the update data itself.
  426. data, err := ioutil.ReadAll(r.Body)
  427. if err != nil {
  428. Respond(w, r, err.Error(), http.StatusInternalServerError)
  429. return
  430. }
  431. // Multihash will be passed as hex-encoded data, so we need to parse this to bytes
  432. if isRaw {
  433. _, _, _, err = s.api.ResourceUpdate(r.Context(), name, data)
  434. if err != nil {
  435. Respond(w, r, err.Error(), http.StatusBadRequest)
  436. return
  437. }
  438. } else {
  439. bytesdata, err := hexutil.Decode(string(data))
  440. if err != nil {
  441. Respond(w, r, err.Error(), http.StatusBadRequest)
  442. return
  443. }
  444. _, _, _, err = s.api.ResourceUpdateMultihash(r.Context(), name, bytesdata)
  445. if err != nil {
  446. Respond(w, r, err.Error(), http.StatusBadRequest)
  447. return
  448. }
  449. }
  450. // If we have data to return, write this now
  451. // \TODO there should always be data to return here
  452. if len(outdata) > 0 {
  453. w.Header().Add("Content-type", "text/plain")
  454. w.WriteHeader(http.StatusOK)
  455. fmt.Fprint(w, string(outdata))
  456. return
  457. }
  458. w.WriteHeader(http.StatusOK)
  459. }
  460. // Retrieve mutable resource updates:
  461. // bzz-resource://<id> - get latest update
  462. // bzz-resource://<id>/<n> - get latest update on period n
  463. // bzz-resource://<id>/<n>/<m> - get update version m of period n
  464. // <id> = ens name or hash
  465. func (s *Server) HandleGetResource(ctx context.Context, w http.ResponseWriter, r *Request) {
  466. s.handleGetResource(ctx, w, r)
  467. }
  468. // TODO: Enable pass maxPeriod parameter
  469. func (s *Server) handleGetResource(ctx context.Context, w http.ResponseWriter, r *Request) {
  470. log.Debug("handle.get.resource", "ruid", r.ruid)
  471. var err error
  472. // resolve the content key.
  473. manifestAddr := r.uri.Address()
  474. if manifestAddr == nil {
  475. manifestAddr, err = s.api.Resolve(ctx, r.uri)
  476. if err != nil {
  477. getFail.Inc(1)
  478. Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
  479. return
  480. }
  481. } else {
  482. w.Header().Set("Cache-Control", "max-age=2147483648")
  483. }
  484. // get the root chunk key from the manifest
  485. key, err := s.api.ResolveResourceManifest(ctx, manifestAddr)
  486. if err != nil {
  487. getFail.Inc(1)
  488. Respond(w, r, fmt.Sprintf("error resolving resource root chunk for %s: %s", r.uri.Addr, err), http.StatusNotFound)
  489. return
  490. }
  491. log.Debug("handle.get.resource: resolved", "ruid", r.ruid, "manifestkey", manifestAddr, "rootchunk key", key)
  492. // determine if the query specifies period and version
  493. var params []string
  494. if len(r.uri.Path) > 0 {
  495. params = strings.Split(r.uri.Path, "/")
  496. }
  497. var name string
  498. var period uint64
  499. var version uint64
  500. var data []byte
  501. now := time.Now()
  502. switch len(params) {
  503. case 0: // latest only
  504. name, data, err = s.api.ResourceLookup(r.Context(), key, 0, 0, nil)
  505. case 2: // specific period and version
  506. version, err = strconv.ParseUint(params[1], 10, 32)
  507. if err != nil {
  508. break
  509. }
  510. period, err = strconv.ParseUint(params[0], 10, 32)
  511. if err != nil {
  512. break
  513. }
  514. name, data, err = s.api.ResourceLookup(r.Context(), key, uint32(period), uint32(version), nil)
  515. case 1: // last version of specific period
  516. period, err = strconv.ParseUint(params[0], 10, 32)
  517. if err != nil {
  518. break
  519. }
  520. name, data, err = s.api.ResourceLookup(r.Context(), key, uint32(period), uint32(version), nil)
  521. default: // bogus
  522. err = mru.NewError(storage.ErrInvalidValue, "invalid mutable resource request")
  523. }
  524. // any error from the switch statement will end up here
  525. if err != nil {
  526. code, err2 := s.translateResourceError(w, r, "mutable resource lookup fail", err)
  527. Respond(w, r, err2.Error(), code)
  528. return
  529. }
  530. // All ok, serve the retrieved update
  531. log.Debug("Found update", "name", name, "ruid", r.ruid)
  532. w.Header().Set("Content-Type", "application/octet-stream")
  533. http.ServeContent(w, &r.Request, "", now, bytes.NewReader(data))
  534. }
  535. func (s *Server) translateResourceError(w http.ResponseWriter, r *Request, supErr string, err error) (int, error) {
  536. code := 0
  537. defaultErr := fmt.Errorf("%s: %v", supErr, err)
  538. rsrcErr, ok := err.(*mru.Error)
  539. if !ok && rsrcErr != nil {
  540. code = rsrcErr.Code()
  541. }
  542. switch code {
  543. case storage.ErrInvalidValue:
  544. return http.StatusBadRequest, defaultErr
  545. case storage.ErrNotFound, storage.ErrNotSynced, storage.ErrNothingToReturn, storage.ErrInit:
  546. return http.StatusNotFound, defaultErr
  547. case storage.ErrUnauthorized, storage.ErrInvalidSignature:
  548. return http.StatusUnauthorized, defaultErr
  549. case storage.ErrDataOverflow:
  550. return http.StatusRequestEntityTooLarge, defaultErr
  551. }
  552. return http.StatusInternalServerError, defaultErr
  553. }
  554. // HandleGet handles a GET request to
  555. // - bzz-raw://<key> and responds with the raw content stored at the
  556. // given storage key
  557. // - bzz-hash://<key> and responds with the hash of the content stored
  558. // at the given storage key as a text/plain response
  559. func (s *Server) HandleGet(ctx context.Context, w http.ResponseWriter, r *Request) {
  560. log.Debug("handle.get", "ruid", r.ruid, "uri", r.uri)
  561. getCount.Inc(1)
  562. var err error
  563. addr := r.uri.Address()
  564. if addr == nil {
  565. addr, err = s.api.Resolve(ctx, r.uri)
  566. if err != nil {
  567. getFail.Inc(1)
  568. Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
  569. return
  570. }
  571. } else {
  572. w.Header().Set("Cache-Control", "max-age=2147483648, immutable") // url was of type bzz://<hex key>/path, so we are sure it is immutable.
  573. }
  574. log.Debug("handle.get: resolved", "ruid", r.ruid, "key", addr)
  575. // if path is set, interpret <key> as a manifest and return the
  576. // raw entry at the given path
  577. if r.uri.Path != "" {
  578. walker, err := s.api.NewManifestWalker(ctx, addr, nil)
  579. if err != nil {
  580. getFail.Inc(1)
  581. Respond(w, r, fmt.Sprintf("%s is not a manifest", addr), http.StatusBadRequest)
  582. return
  583. }
  584. var entry *api.ManifestEntry
  585. walker.Walk(func(e *api.ManifestEntry) error {
  586. // if the entry matches the path, set entry and stop
  587. // the walk
  588. if e.Path == r.uri.Path {
  589. entry = e
  590. // return an error to cancel the walk
  591. return errors.New("found")
  592. }
  593. // ignore non-manifest files
  594. if e.ContentType != api.ManifestType {
  595. return nil
  596. }
  597. // if the manifest's path is a prefix of the
  598. // requested path, recurse into it by returning
  599. // nil and continuing the walk
  600. if strings.HasPrefix(r.uri.Path, e.Path) {
  601. return nil
  602. }
  603. return api.ErrSkipManifest
  604. })
  605. if entry == nil {
  606. getFail.Inc(1)
  607. Respond(w, r, fmt.Sprintf("manifest entry could not be loaded"), http.StatusNotFound)
  608. return
  609. }
  610. addr = storage.Address(common.Hex2Bytes(entry.Hash))
  611. }
  612. etag := common.Bytes2Hex(addr)
  613. noneMatchEtag := r.Header.Get("If-None-Match")
  614. w.Header().Set("ETag", fmt.Sprintf("%q", etag)) // set etag to manifest key or raw entry key.
  615. if noneMatchEtag != "" {
  616. if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), addr) {
  617. Respond(w, r, "Not Modified", http.StatusNotModified)
  618. return
  619. }
  620. }
  621. // check the root chunk exists by retrieving the file's size
  622. reader, isEncrypted := s.api.Retrieve(ctx, addr)
  623. if _, err := reader.Size(nil); err != nil {
  624. getFail.Inc(1)
  625. Respond(w, r, fmt.Sprintf("root chunk not found %s: %s", addr, err), http.StatusNotFound)
  626. return
  627. }
  628. w.Header().Set("X-Decrypted", fmt.Sprintf("%v", isEncrypted))
  629. switch {
  630. case r.uri.Raw():
  631. // allow the request to overwrite the content type using a query
  632. // parameter
  633. contentType := "application/octet-stream"
  634. if typ := r.URL.Query().Get("content_type"); typ != "" {
  635. contentType = typ
  636. }
  637. w.Header().Set("Content-Type", contentType)
  638. http.ServeContent(w, &r.Request, "", time.Now(), reader)
  639. case r.uri.Hash():
  640. w.Header().Set("Content-Type", "text/plain")
  641. w.WriteHeader(http.StatusOK)
  642. fmt.Fprint(w, addr)
  643. }
  644. }
  645. // HandleGetFiles handles a GET request to bzz:/<manifest> with an Accept
  646. // header of "application/x-tar" and returns a tar stream of all files
  647. // contained in the manifest
  648. func (s *Server) HandleGetFiles(ctx context.Context, w http.ResponseWriter, r *Request) {
  649. log.Debug("handle.get.files", "ruid", r.ruid, "uri", r.uri)
  650. getFilesCount.Inc(1)
  651. if r.uri.Path != "" {
  652. getFilesFail.Inc(1)
  653. Respond(w, r, "files request cannot contain a path", http.StatusBadRequest)
  654. return
  655. }
  656. addr, err := s.api.Resolve(ctx, r.uri)
  657. if err != nil {
  658. getFilesFail.Inc(1)
  659. Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
  660. return
  661. }
  662. log.Debug("handle.get.files: resolved", "ruid", r.ruid, "key", addr)
  663. walker, err := s.api.NewManifestWalker(ctx, addr, nil)
  664. if err != nil {
  665. getFilesFail.Inc(1)
  666. Respond(w, r, err.Error(), http.StatusInternalServerError)
  667. return
  668. }
  669. tw := tar.NewWriter(w)
  670. defer tw.Close()
  671. w.Header().Set("Content-Type", "application/x-tar")
  672. w.WriteHeader(http.StatusOK)
  673. err = walker.Walk(func(entry *api.ManifestEntry) error {
  674. // ignore manifests (walk will recurse into them)
  675. if entry.ContentType == api.ManifestType {
  676. return nil
  677. }
  678. // retrieve the entry's key and size
  679. reader, isEncrypted := s.api.Retrieve(ctx, storage.Address(common.Hex2Bytes(entry.Hash)))
  680. size, err := reader.Size(nil)
  681. if err != nil {
  682. return err
  683. }
  684. w.Header().Set("X-Decrypted", fmt.Sprintf("%v", isEncrypted))
  685. // write a tar header for the entry
  686. hdr := &tar.Header{
  687. Name: entry.Path,
  688. Mode: entry.Mode,
  689. Size: size,
  690. ModTime: entry.ModTime,
  691. Xattrs: map[string]string{
  692. "user.swarm.content-type": entry.ContentType,
  693. },
  694. }
  695. if err := tw.WriteHeader(hdr); err != nil {
  696. return err
  697. }
  698. // copy the file into the tar stream
  699. n, err := io.Copy(tw, io.LimitReader(reader, hdr.Size))
  700. if err != nil {
  701. return err
  702. } else if n != size {
  703. return fmt.Errorf("error writing %s: expected %d bytes but sent %d", entry.Path, size, n)
  704. }
  705. return nil
  706. })
  707. if err != nil {
  708. getFilesFail.Inc(1)
  709. log.Error(fmt.Sprintf("error generating tar stream: %s", err))
  710. }
  711. }
  712. // HandleGetList handles a GET request to bzz-list:/<manifest>/<path> and returns
  713. // a list of all files contained in <manifest> under <path> grouped into
  714. // common prefixes using "/" as a delimiter
  715. func (s *Server) HandleGetList(ctx context.Context, w http.ResponseWriter, r *Request) {
  716. log.Debug("handle.get.list", "ruid", r.ruid, "uri", r.uri)
  717. getListCount.Inc(1)
  718. // ensure the root path has a trailing slash so that relative URLs work
  719. if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
  720. http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
  721. return
  722. }
  723. addr, err := s.api.Resolve(ctx, r.uri)
  724. if err != nil {
  725. getListFail.Inc(1)
  726. Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
  727. return
  728. }
  729. log.Debug("handle.get.list: resolved", "ruid", r.ruid, "key", addr)
  730. list, err := s.getManifestList(ctx, addr, r.uri.Path)
  731. if err != nil {
  732. getListFail.Inc(1)
  733. Respond(w, r, err.Error(), http.StatusInternalServerError)
  734. return
  735. }
  736. // if the client wants HTML (e.g. a browser) then render the list as a
  737. // HTML index with relative URLs
  738. if strings.Contains(r.Header.Get("Accept"), "text/html") {
  739. w.Header().Set("Content-Type", "text/html")
  740. err := htmlListTemplate.Execute(w, &htmlListData{
  741. URI: &api.URI{
  742. Scheme: "bzz",
  743. Addr: r.uri.Addr,
  744. Path: r.uri.Path,
  745. },
  746. List: &list,
  747. })
  748. if err != nil {
  749. getListFail.Inc(1)
  750. log.Error(fmt.Sprintf("error rendering list HTML: %s", err))
  751. }
  752. return
  753. }
  754. w.Header().Set("Content-Type", "application/json")
  755. json.NewEncoder(w).Encode(&list)
  756. }
  757. func (s *Server) getManifestList(ctx context.Context, addr storage.Address, prefix string) (list api.ManifestList, err error) {
  758. walker, err := s.api.NewManifestWalker(ctx, addr, nil)
  759. if err != nil {
  760. return
  761. }
  762. err = walker.Walk(func(entry *api.ManifestEntry) error {
  763. // handle non-manifest files
  764. if entry.ContentType != api.ManifestType {
  765. // ignore the file if it doesn't have the specified prefix
  766. if !strings.HasPrefix(entry.Path, prefix) {
  767. return nil
  768. }
  769. // if the path after the prefix contains a slash, add a
  770. // common prefix to the list, otherwise add the entry
  771. suffix := strings.TrimPrefix(entry.Path, prefix)
  772. if index := strings.Index(suffix, "/"); index > -1 {
  773. list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1])
  774. return nil
  775. }
  776. if entry.Path == "" {
  777. entry.Path = "/"
  778. }
  779. list.Entries = append(list.Entries, entry)
  780. return nil
  781. }
  782. // if the manifest's path is a prefix of the specified prefix
  783. // then just recurse into the manifest by returning nil and
  784. // continuing the walk
  785. if strings.HasPrefix(prefix, entry.Path) {
  786. return nil
  787. }
  788. // if the manifest's path has the specified prefix, then if the
  789. // path after the prefix contains a slash, add a common prefix
  790. // to the list and skip the manifest, otherwise recurse into
  791. // the manifest by returning nil and continuing the walk
  792. if strings.HasPrefix(entry.Path, prefix) {
  793. suffix := strings.TrimPrefix(entry.Path, prefix)
  794. if index := strings.Index(suffix, "/"); index > -1 {
  795. list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1])
  796. return api.ErrSkipManifest
  797. }
  798. return nil
  799. }
  800. // the manifest neither has the prefix or needs recursing in to
  801. // so just skip it
  802. return api.ErrSkipManifest
  803. })
  804. return list, nil
  805. }
  806. // HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
  807. // with the content of the file at <path> from the given <manifest>
  808. func (s *Server) HandleGetFile(ctx context.Context, w http.ResponseWriter, r *Request) {
  809. log.Debug("handle.get.file", "ruid", r.ruid)
  810. getFileCount.Inc(1)
  811. // ensure the root path has a trailing slash so that relative URLs work
  812. if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
  813. http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
  814. return
  815. }
  816. var err error
  817. manifestAddr := r.uri.Address()
  818. if manifestAddr == nil {
  819. manifestAddr, err = s.api.Resolve(ctx, r.uri)
  820. if err != nil {
  821. getFileFail.Inc(1)
  822. Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
  823. return
  824. }
  825. } else {
  826. w.Header().Set("Cache-Control", "max-age=2147483648, immutable") // url was of type bzz://<hex key>/path, so we are sure it is immutable.
  827. }
  828. log.Debug("handle.get.file: resolved", "ruid", r.ruid, "key", manifestAddr)
  829. reader, contentType, status, contentKey, err := s.api.Get(ctx, manifestAddr, r.uri.Path)
  830. etag := common.Bytes2Hex(contentKey)
  831. noneMatchEtag := r.Header.Get("If-None-Match")
  832. w.Header().Set("ETag", fmt.Sprintf("%q", etag)) // set etag to actual content key.
  833. if noneMatchEtag != "" {
  834. if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), contentKey) {
  835. Respond(w, r, "Not Modified", http.StatusNotModified)
  836. return
  837. }
  838. }
  839. if err != nil {
  840. switch status {
  841. case http.StatusNotFound:
  842. getFileNotFound.Inc(1)
  843. Respond(w, r, err.Error(), http.StatusNotFound)
  844. default:
  845. getFileFail.Inc(1)
  846. Respond(w, r, err.Error(), http.StatusInternalServerError)
  847. }
  848. return
  849. }
  850. //the request results in ambiguous files
  851. //e.g. /read with readme.md and readinglist.txt available in manifest
  852. if status == http.StatusMultipleChoices {
  853. list, err := s.getManifestList(ctx, manifestAddr, r.uri.Path)
  854. if err != nil {
  855. getFileFail.Inc(1)
  856. Respond(w, r, err.Error(), http.StatusInternalServerError)
  857. return
  858. }
  859. log.Debug(fmt.Sprintf("Multiple choices! --> %v", list), "ruid", r.ruid)
  860. //show a nice page links to available entries
  861. ShowMultipleChoices(w, r, list)
  862. return
  863. }
  864. // check the root chunk exists by retrieving the file's size
  865. if _, err := reader.Size(nil); err != nil {
  866. getFileNotFound.Inc(1)
  867. Respond(w, r, fmt.Sprintf("file not found %s: %s", r.uri, err), http.StatusNotFound)
  868. return
  869. }
  870. w.Header().Set("Content-Type", contentType)
  871. http.ServeContent(w, &r.Request, "", time.Now(), newBufferedReadSeeker(reader, getFileBufferSize))
  872. }
  873. // The size of buffer used for bufio.Reader on LazyChunkReader passed to
  874. // http.ServeContent in HandleGetFile.
  875. // Warning: This value influences the number of chunk requests and chunker join goroutines
  876. // per file request.
  877. // Recommended value is 4 times the io.Copy default buffer value which is 32kB.
  878. const getFileBufferSize = 4 * 32 * 1024
  879. // bufferedReadSeeker wraps bufio.Reader to expose Seek method
  880. // from the provied io.ReadSeeker in newBufferedReadSeeker.
  881. type bufferedReadSeeker struct {
  882. r io.Reader
  883. s io.Seeker
  884. }
  885. // newBufferedReadSeeker creates a new instance of bufferedReadSeeker,
  886. // out of io.ReadSeeker. Argument `size` is the size of the read buffer.
  887. func newBufferedReadSeeker(readSeeker io.ReadSeeker, size int) bufferedReadSeeker {
  888. return bufferedReadSeeker{
  889. r: bufio.NewReaderSize(readSeeker, size),
  890. s: readSeeker,
  891. }
  892. }
  893. func (b bufferedReadSeeker) Read(p []byte) (n int, err error) {
  894. return b.r.Read(p)
  895. }
  896. func (b bufferedReadSeeker) Seek(offset int64, whence int) (int64, error) {
  897. return b.s.Seek(offset, whence)
  898. }
  899. func (s *Server) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
  900. ctx := context.TODO()
  901. defer metrics.GetOrRegisterResettingTimer(fmt.Sprintf("http.request.%s.time", r.Method), nil).UpdateSince(time.Now())
  902. req := &Request{Request: *r, ruid: uuid.New()[:8]}
  903. metrics.GetOrRegisterCounter(fmt.Sprintf("http.request.%s", r.Method), nil).Inc(1)
  904. log.Info("serving request", "ruid", req.ruid, "method", r.Method, "url", r.RequestURI)
  905. // wrapping the ResponseWriter, so that we get the response code set by http.ServeContent
  906. w := newLoggingResponseWriter(rw)
  907. if r.RequestURI == "/" && strings.Contains(r.Header.Get("Accept"), "text/html") {
  908. err := landingPageTemplate.Execute(w, nil)
  909. if err != nil {
  910. log.Error(fmt.Sprintf("error rendering landing page: %s", err))
  911. }
  912. return
  913. }
  914. if r.URL.Path == "/robots.txt" {
  915. w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))
  916. fmt.Fprintf(w, "User-agent: *\nDisallow: /")
  917. return
  918. }
  919. if r.RequestURI == "/" && strings.Contains(r.Header.Get("Accept"), "application/json") {
  920. w.Header().Set("Content-Type", "application/json")
  921. w.WriteHeader(http.StatusOK)
  922. json.NewEncoder(w).Encode("Welcome to Swarm!")
  923. return
  924. }
  925. uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/"))
  926. if err != nil {
  927. Respond(w, req, fmt.Sprintf("invalid URI %q", r.URL.Path), http.StatusBadRequest)
  928. return
  929. }
  930. req.uri = uri
  931. log.Debug("parsed request path", "ruid", req.ruid, "method", req.Method, "uri.Addr", req.uri.Addr, "uri.Path", req.uri.Path, "uri.Scheme", req.uri.Scheme)
  932. switch r.Method {
  933. case "POST":
  934. if uri.Raw() {
  935. log.Debug("handlePostRaw")
  936. s.HandlePostRaw(ctx, w, req)
  937. } else if uri.Resource() {
  938. log.Debug("handlePostResource")
  939. s.HandlePostResource(ctx, w, req)
  940. } else if uri.Immutable() || uri.List() || uri.Hash() {
  941. log.Debug("POST not allowed on immutable, list or hash")
  942. Respond(w, req, fmt.Sprintf("POST method on scheme %s not allowed", uri.Scheme), http.StatusMethodNotAllowed)
  943. } else {
  944. log.Debug("handlePostFiles")
  945. s.HandlePostFiles(ctx, w, req)
  946. }
  947. case "PUT":
  948. Respond(w, req, fmt.Sprintf("PUT method to %s not allowed", uri), http.StatusBadRequest)
  949. return
  950. case "DELETE":
  951. if uri.Raw() {
  952. Respond(w, req, fmt.Sprintf("DELETE method to %s not allowed", uri), http.StatusBadRequest)
  953. return
  954. }
  955. s.HandleDelete(ctx, w, req)
  956. case "GET":
  957. if uri.Resource() {
  958. s.HandleGetResource(ctx, w, req)
  959. return
  960. }
  961. if uri.Raw() || uri.Hash() {
  962. s.HandleGet(ctx, w, req)
  963. return
  964. }
  965. if uri.List() {
  966. s.HandleGetList(ctx, w, req)
  967. return
  968. }
  969. if r.Header.Get("Accept") == "application/x-tar" {
  970. s.HandleGetFiles(ctx, w, req)
  971. return
  972. }
  973. s.HandleGetFile(ctx, w, req)
  974. default:
  975. Respond(w, req, fmt.Sprintf("%s method is not supported", r.Method), http.StatusMethodNotAllowed)
  976. }
  977. log.Info("served response", "ruid", req.ruid, "code", w.statusCode)
  978. }
  979. func (s *Server) updateManifest(ctx context.Context, addr storage.Address, update func(mw *api.ManifestWriter) error) (storage.Address, error) {
  980. mw, err := s.api.NewManifestWriter(ctx, addr, nil)
  981. if err != nil {
  982. return nil, err
  983. }
  984. if err := update(mw); err != nil {
  985. return nil, err
  986. }
  987. addr, err = mw.Store()
  988. if err != nil {
  989. return nil, err
  990. }
  991. log.Debug(fmt.Sprintf("generated manifest %s", addr))
  992. return addr, nil
  993. }
  994. type loggingResponseWriter struct {
  995. http.ResponseWriter
  996. statusCode int
  997. }
  998. func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
  999. return &loggingResponseWriter{w, http.StatusOK}
  1000. }
  1001. func (lrw *loggingResponseWriter) WriteHeader(code int) {
  1002. lrw.statusCode = code
  1003. lrw.ResponseWriter.WriteHeader(code)
  1004. }