server.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  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. "bufio"
  22. "bytes"
  23. "encoding/json"
  24. "fmt"
  25. "io"
  26. "io/ioutil"
  27. "mime"
  28. "mime/multipart"
  29. "net/http"
  30. "os"
  31. "path"
  32. "strconv"
  33. "strings"
  34. "time"
  35. "github.com/ethereum/go-ethereum/common"
  36. "github.com/ethereum/go-ethereum/metrics"
  37. "github.com/ethereum/go-ethereum/swarm/api"
  38. "github.com/ethereum/go-ethereum/swarm/log"
  39. "github.com/ethereum/go-ethereum/swarm/storage"
  40. "github.com/ethereum/go-ethereum/swarm/storage/feed"
  41. "github.com/rs/cors"
  42. )
  43. var (
  44. postRawCount = metrics.NewRegisteredCounter("api.http.post.raw.count", nil)
  45. postRawFail = metrics.NewRegisteredCounter("api.http.post.raw.fail", nil)
  46. postFilesCount = metrics.NewRegisteredCounter("api.http.post.files.count", nil)
  47. postFilesFail = metrics.NewRegisteredCounter("api.http.post.files.fail", nil)
  48. deleteCount = metrics.NewRegisteredCounter("api.http.delete.count", nil)
  49. deleteFail = metrics.NewRegisteredCounter("api.http.delete.fail", nil)
  50. getCount = metrics.NewRegisteredCounter("api.http.get.count", nil)
  51. getFail = metrics.NewRegisteredCounter("api.http.get.fail", nil)
  52. getFileCount = metrics.NewRegisteredCounter("api.http.get.file.count", nil)
  53. getFileNotFound = metrics.NewRegisteredCounter("api.http.get.file.notfound", nil)
  54. getFileFail = metrics.NewRegisteredCounter("api.http.get.file.fail", nil)
  55. getListCount = metrics.NewRegisteredCounter("api.http.get.list.count", nil)
  56. getListFail = metrics.NewRegisteredCounter("api.http.get.list.fail", nil)
  57. )
  58. type methodHandler map[string]http.Handler
  59. func (m methodHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
  60. v, ok := m[r.Method]
  61. if ok {
  62. v.ServeHTTP(rw, r)
  63. return
  64. }
  65. rw.WriteHeader(http.StatusMethodNotAllowed)
  66. }
  67. func NewServer(api *api.API, corsString string) *Server {
  68. var allowedOrigins []string
  69. for _, domain := range strings.Split(corsString, ",") {
  70. allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
  71. }
  72. c := cors.New(cors.Options{
  73. AllowedOrigins: allowedOrigins,
  74. AllowedMethods: []string{http.MethodPost, http.MethodGet, http.MethodDelete, http.MethodPatch, http.MethodPut},
  75. MaxAge: 600,
  76. AllowedHeaders: []string{"*"},
  77. })
  78. server := &Server{api: api}
  79. defaultMiddlewares := []Adapter{
  80. RecoverPanic,
  81. SetRequestID,
  82. SetRequestHost,
  83. InitLoggingResponseWriter,
  84. ParseURI,
  85. InstrumentOpenTracing,
  86. }
  87. mux := http.NewServeMux()
  88. mux.Handle("/bzz:/", methodHandler{
  89. "GET": Adapt(
  90. http.HandlerFunc(server.HandleBzzGet),
  91. defaultMiddlewares...,
  92. ),
  93. "POST": Adapt(
  94. http.HandlerFunc(server.HandlePostFiles),
  95. defaultMiddlewares...,
  96. ),
  97. "DELETE": Adapt(
  98. http.HandlerFunc(server.HandleDelete),
  99. defaultMiddlewares...,
  100. ),
  101. })
  102. mux.Handle("/bzz-raw:/", methodHandler{
  103. "GET": Adapt(
  104. http.HandlerFunc(server.HandleGet),
  105. defaultMiddlewares...,
  106. ),
  107. "POST": Adapt(
  108. http.HandlerFunc(server.HandlePostRaw),
  109. defaultMiddlewares...,
  110. ),
  111. })
  112. mux.Handle("/bzz-immutable:/", methodHandler{
  113. "GET": Adapt(
  114. http.HandlerFunc(server.HandleBzzGet),
  115. defaultMiddlewares...,
  116. ),
  117. })
  118. mux.Handle("/bzz-hash:/", methodHandler{
  119. "GET": Adapt(
  120. http.HandlerFunc(server.HandleGet),
  121. defaultMiddlewares...,
  122. ),
  123. })
  124. mux.Handle("/bzz-list:/", methodHandler{
  125. "GET": Adapt(
  126. http.HandlerFunc(server.HandleGetList),
  127. defaultMiddlewares...,
  128. ),
  129. })
  130. mux.Handle("/bzz-feed:/", methodHandler{
  131. "GET": Adapt(
  132. http.HandlerFunc(server.HandleGetFeed),
  133. defaultMiddlewares...,
  134. ),
  135. "POST": Adapt(
  136. http.HandlerFunc(server.HandlePostFeed),
  137. defaultMiddlewares...,
  138. ),
  139. })
  140. mux.Handle("/", methodHandler{
  141. "GET": Adapt(
  142. http.HandlerFunc(server.HandleRootPaths),
  143. SetRequestID,
  144. InitLoggingResponseWriter,
  145. ),
  146. })
  147. server.Handler = c.Handler(mux)
  148. return server
  149. }
  150. func (s *Server) ListenAndServe(addr string) error {
  151. s.listenAddr = addr
  152. return http.ListenAndServe(addr, s)
  153. }
  154. // browser API for registering bzz url scheme handlers:
  155. // https://developer.mozilla.org/en/docs/Web-based_protocol_handlers
  156. // electron (chromium) api for registering bzz url scheme handlers:
  157. // https://github.com/atom/electron/blob/master/docs/api/protocol.md
  158. type Server struct {
  159. http.Handler
  160. api *api.API
  161. listenAddr string
  162. }
  163. func (s *Server) HandleBzzGet(w http.ResponseWriter, r *http.Request) {
  164. log.Debug("handleBzzGet", "ruid", GetRUID(r.Context()), "uri", r.RequestURI)
  165. if r.Header.Get("Accept") == "application/x-tar" {
  166. uri := GetURI(r.Context())
  167. _, credentials, _ := r.BasicAuth()
  168. reader, err := s.api.GetDirectoryTar(r.Context(), s.api.Decryptor(r.Context(), credentials), uri)
  169. if err != nil {
  170. if isDecryptError(err) {
  171. w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", uri.Address().String()))
  172. respondError(w, r, err.Error(), http.StatusUnauthorized)
  173. return
  174. }
  175. respondError(w, r, fmt.Sprintf("Had an error building the tarball: %v", err), http.StatusInternalServerError)
  176. return
  177. }
  178. defer reader.Close()
  179. w.Header().Set("Content-Type", "application/x-tar")
  180. fileName := uri.Addr
  181. if found := path.Base(uri.Path); found != "" && found != "." && found != "/" {
  182. fileName = found
  183. }
  184. w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s.tar\"", fileName))
  185. w.WriteHeader(http.StatusOK)
  186. io.Copy(w, reader)
  187. return
  188. }
  189. s.HandleGetFile(w, r)
  190. }
  191. func (s *Server) HandleRootPaths(w http.ResponseWriter, r *http.Request) {
  192. switch r.RequestURI {
  193. case "/":
  194. respondTemplate(w, r, "landing-page", "Swarm: Please request a valid ENS or swarm hash with the appropriate bzz scheme", 200)
  195. return
  196. case "/robots.txt":
  197. w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))
  198. fmt.Fprintf(w, "User-agent: *\nDisallow: /")
  199. case "/favicon.ico":
  200. w.WriteHeader(http.StatusOK)
  201. w.Write(faviconBytes)
  202. default:
  203. respondError(w, r, "Not Found", http.StatusNotFound)
  204. }
  205. }
  206. // HandlePostRaw handles a POST request to a raw bzz-raw:/ URI, stores the request
  207. // body in swarm and returns the resulting storage address as a text/plain response
  208. func (s *Server) HandlePostRaw(w http.ResponseWriter, r *http.Request) {
  209. ruid := GetRUID(r.Context())
  210. log.Debug("handle.post.raw", "ruid", ruid)
  211. postRawCount.Inc(1)
  212. toEncrypt := false
  213. uri := GetURI(r.Context())
  214. if uri.Addr == "encrypt" {
  215. toEncrypt = true
  216. }
  217. if uri.Path != "" {
  218. postRawFail.Inc(1)
  219. respondError(w, r, "raw POST request cannot contain a path", http.StatusBadRequest)
  220. return
  221. }
  222. if uri.Addr != "" && uri.Addr != "encrypt" {
  223. postRawFail.Inc(1)
  224. respondError(w, r, "raw POST request addr can only be empty or \"encrypt\"", http.StatusBadRequest)
  225. return
  226. }
  227. if r.Header.Get("Content-Length") == "" {
  228. postRawFail.Inc(1)
  229. respondError(w, r, "missing Content-Length header in request", http.StatusBadRequest)
  230. return
  231. }
  232. addr, _, err := s.api.Store(r.Context(), r.Body, r.ContentLength, toEncrypt)
  233. if err != nil {
  234. postRawFail.Inc(1)
  235. respondError(w, r, err.Error(), http.StatusInternalServerError)
  236. return
  237. }
  238. log.Debug("stored content", "ruid", ruid, "key", addr)
  239. w.Header().Set("Content-Type", "text/plain")
  240. w.WriteHeader(http.StatusOK)
  241. fmt.Fprint(w, addr)
  242. }
  243. // HandlePostFiles handles a POST request to
  244. // bzz:/<hash>/<path> which contains either a single file or multiple files
  245. // (either a tar archive or multipart form), adds those files either to an
  246. // existing manifest or to a new manifest under <path> and returns the
  247. // resulting manifest hash as a text/plain response
  248. func (s *Server) HandlePostFiles(w http.ResponseWriter, r *http.Request) {
  249. ruid := GetRUID(r.Context())
  250. log.Debug("handle.post.files", "ruid", ruid)
  251. postFilesCount.Inc(1)
  252. contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
  253. if err != nil {
  254. postFilesFail.Inc(1)
  255. respondError(w, r, err.Error(), http.StatusBadRequest)
  256. return
  257. }
  258. toEncrypt := false
  259. uri := GetURI(r.Context())
  260. if uri.Addr == "encrypt" {
  261. toEncrypt = true
  262. }
  263. var addr storage.Address
  264. if uri.Addr != "" && uri.Addr != "encrypt" {
  265. addr, err = s.api.Resolve(r.Context(), uri.Addr)
  266. if err != nil {
  267. postFilesFail.Inc(1)
  268. respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusInternalServerError)
  269. return
  270. }
  271. log.Debug("resolved key", "ruid", ruid, "key", addr)
  272. } else {
  273. addr, err = s.api.NewManifest(r.Context(), toEncrypt)
  274. if err != nil {
  275. postFilesFail.Inc(1)
  276. respondError(w, r, err.Error(), http.StatusInternalServerError)
  277. return
  278. }
  279. log.Debug("new manifest", "ruid", ruid, "key", addr)
  280. }
  281. newAddr, err := s.api.UpdateManifest(r.Context(), addr, func(mw *api.ManifestWriter) error {
  282. switch contentType {
  283. case "application/x-tar":
  284. _, err := s.handleTarUpload(r, mw)
  285. if err != nil {
  286. respondError(w, r, fmt.Sprintf("error uploading tarball: %v", err), http.StatusInternalServerError)
  287. return err
  288. }
  289. return nil
  290. case "multipart/form-data":
  291. return s.handleMultipartUpload(r, params["boundary"], mw)
  292. default:
  293. return s.handleDirectUpload(r, mw)
  294. }
  295. })
  296. if err != nil {
  297. postFilesFail.Inc(1)
  298. respondError(w, r, fmt.Sprintf("cannot create manifest: %s", err), http.StatusInternalServerError)
  299. return
  300. }
  301. log.Debug("stored content", "ruid", ruid, "key", newAddr)
  302. w.Header().Set("Content-Type", "text/plain")
  303. w.WriteHeader(http.StatusOK)
  304. fmt.Fprint(w, newAddr)
  305. }
  306. func (s *Server) handleTarUpload(r *http.Request, mw *api.ManifestWriter) (storage.Address, error) {
  307. log.Debug("handle.tar.upload", "ruid", GetRUID(r.Context()))
  308. defaultPath := r.URL.Query().Get("defaultpath")
  309. key, err := s.api.UploadTar(r.Context(), r.Body, GetURI(r.Context()).Path, defaultPath, mw)
  310. if err != nil {
  311. return nil, err
  312. }
  313. return key, nil
  314. }
  315. func (s *Server) handleMultipartUpload(r *http.Request, boundary string, mw *api.ManifestWriter) error {
  316. ruid := GetRUID(r.Context())
  317. log.Debug("handle.multipart.upload", "ruid", ruid)
  318. mr := multipart.NewReader(r.Body, boundary)
  319. for {
  320. part, err := mr.NextPart()
  321. if err == io.EOF {
  322. return nil
  323. } else if err != nil {
  324. return fmt.Errorf("error reading multipart form: %s", err)
  325. }
  326. var size int64
  327. var reader io.Reader = part
  328. if contentLength := part.Header.Get("Content-Length"); contentLength != "" {
  329. size, err = strconv.ParseInt(contentLength, 10, 64)
  330. if err != nil {
  331. return fmt.Errorf("error parsing multipart content length: %s", err)
  332. }
  333. reader = part
  334. } else {
  335. // copy the part to a tmp file to get its size
  336. tmp, err := ioutil.TempFile("", "swarm-multipart")
  337. if err != nil {
  338. return err
  339. }
  340. defer os.Remove(tmp.Name())
  341. defer tmp.Close()
  342. size, err = io.Copy(tmp, part)
  343. if err != nil {
  344. return fmt.Errorf("error copying multipart content: %s", err)
  345. }
  346. if _, err := tmp.Seek(0, io.SeekStart); err != nil {
  347. return fmt.Errorf("error copying multipart content: %s", err)
  348. }
  349. reader = tmp
  350. }
  351. // add the entry under the path from the request
  352. name := part.FileName()
  353. if name == "" {
  354. name = part.FormName()
  355. }
  356. uri := GetURI(r.Context())
  357. path := path.Join(uri.Path, name)
  358. entry := &api.ManifestEntry{
  359. Path: path,
  360. ContentType: part.Header.Get("Content-Type"),
  361. Size: size,
  362. }
  363. log.Debug("adding path to new manifest", "ruid", ruid, "bytes", entry.Size, "path", entry.Path)
  364. contentKey, err := mw.AddEntry(r.Context(), reader, entry)
  365. if err != nil {
  366. return fmt.Errorf("error adding manifest entry from multipart form: %s", err)
  367. }
  368. log.Debug("stored content", "ruid", ruid, "key", contentKey)
  369. }
  370. }
  371. func (s *Server) handleDirectUpload(r *http.Request, mw *api.ManifestWriter) error {
  372. ruid := GetRUID(r.Context())
  373. log.Debug("handle.direct.upload", "ruid", ruid)
  374. key, err := mw.AddEntry(r.Context(), r.Body, &api.ManifestEntry{
  375. Path: GetURI(r.Context()).Path,
  376. ContentType: r.Header.Get("Content-Type"),
  377. Mode: 0644,
  378. Size: r.ContentLength,
  379. })
  380. if err != nil {
  381. return err
  382. }
  383. log.Debug("stored content", "ruid", ruid, "key", key)
  384. return nil
  385. }
  386. // HandleDelete handles a DELETE request to bzz:/<manifest>/<path>, removes
  387. // <path> from <manifest> and returns the resulting manifest hash as a
  388. // text/plain response
  389. func (s *Server) HandleDelete(w http.ResponseWriter, r *http.Request) {
  390. ruid := GetRUID(r.Context())
  391. uri := GetURI(r.Context())
  392. log.Debug("handle.delete", "ruid", ruid)
  393. deleteCount.Inc(1)
  394. newKey, err := s.api.Delete(r.Context(), uri.Addr, uri.Path)
  395. if err != nil {
  396. deleteFail.Inc(1)
  397. respondError(w, r, fmt.Sprintf("could not delete from manifest: %v", err), http.StatusInternalServerError)
  398. return
  399. }
  400. w.Header().Set("Content-Type", "text/plain")
  401. w.WriteHeader(http.StatusOK)
  402. fmt.Fprint(w, newKey)
  403. }
  404. // Handles feed manifest creation and feed updates
  405. // The POST request admits a JSON structure as defined in the feeds package: `feed.updateRequestJSON`
  406. // The requests can be to a) create a feed manifest, b) update a feed or c) both a+b: create a feed manifest and publish a first update
  407. func (s *Server) HandlePostFeed(w http.ResponseWriter, r *http.Request) {
  408. ruid := GetRUID(r.Context())
  409. uri := GetURI(r.Context())
  410. log.Debug("handle.post.feed", "ruid", ruid)
  411. var err error
  412. // Creation and update must send feed.updateRequestJSON JSON structure
  413. body, err := ioutil.ReadAll(r.Body)
  414. if err != nil {
  415. respondError(w, r, err.Error(), http.StatusInternalServerError)
  416. return
  417. }
  418. fd, err := s.api.ResolveFeed(r.Context(), uri, r.URL.Query())
  419. if err != nil { // couldn't parse query string or retrieve manifest
  420. getFail.Inc(1)
  421. httpStatus := http.StatusBadRequest
  422. if err == api.ErrCannotLoadFeedManifest || err == api.ErrCannotResolveFeedURI {
  423. httpStatus = http.StatusNotFound
  424. }
  425. respondError(w, r, fmt.Sprintf("cannot retrieve feed from manifest: %s", err), httpStatus)
  426. return
  427. }
  428. var updateRequest feed.Request
  429. updateRequest.Feed = *fd
  430. query := r.URL.Query()
  431. if err := updateRequest.FromValues(query, body); err != nil { // decodes request from query parameters
  432. respondError(w, r, err.Error(), http.StatusBadRequest)
  433. return
  434. }
  435. switch {
  436. case updateRequest.IsUpdate():
  437. // Verify that the signature is intact and that the signer is authorized
  438. // to update this feed
  439. // Check this early, to avoid creating a feed and then not being able to set its first update.
  440. if err = updateRequest.Verify(); err != nil {
  441. respondError(w, r, err.Error(), http.StatusForbidden)
  442. return
  443. }
  444. _, err = s.api.FeedsUpdate(r.Context(), &updateRequest)
  445. if err != nil {
  446. respondError(w, r, err.Error(), http.StatusInternalServerError)
  447. return
  448. }
  449. fallthrough
  450. case query.Get("manifest") == "1":
  451. // we create a manifest so we can retrieve feed updates with bzz:// later
  452. // this manifest has a special "feed type" manifest, and saves the
  453. // feed identification used to retrieve feed updates later
  454. m, err := s.api.NewFeedManifest(r.Context(), &updateRequest.Feed)
  455. if err != nil {
  456. respondError(w, r, fmt.Sprintf("failed to create feed manifest: %v", err), http.StatusInternalServerError)
  457. return
  458. }
  459. // the key to the manifest will be passed back to the client
  460. // the client can access the feed directly through its Feed member
  461. // the manifest key can be set as content in the resolver of the ENS name
  462. outdata, err := json.Marshal(m)
  463. if err != nil {
  464. respondError(w, r, fmt.Sprintf("failed to create json response: %s", err), http.StatusInternalServerError)
  465. return
  466. }
  467. fmt.Fprint(w, string(outdata))
  468. w.Header().Add("Content-type", "application/json")
  469. default:
  470. respondError(w, r, "Missing signature in feed update request", http.StatusBadRequest)
  471. }
  472. }
  473. // HandleGetFeed retrieves Swarm feeds updates:
  474. // bzz-feed://<manifest address or ENS name> - get latest feed update, given a manifest address
  475. // - or -
  476. // specify user + topic (optional), subtopic name (optional) directly, without manifest:
  477. // bzz-feed://?user=0x...&topic=0x...&name=subtopic name
  478. // topic defaults to 0x000... if not specified.
  479. // name defaults to empty string if not specified.
  480. // thus, empty name and topic refers to the user's default feed.
  481. //
  482. // Optional parameters:
  483. // time=xx - get the latest update before time (in epoch seconds)
  484. // hint.time=xx - hint the lookup algorithm looking for updates at around that time
  485. // hint.level=xx - hint the lookup algorithm looking for updates at around this frequency level
  486. // meta=1 - get feed metadata and status information instead of performing a feed query
  487. // NOTE: meta=1 will be deprecated in the near future
  488. func (s *Server) HandleGetFeed(w http.ResponseWriter, r *http.Request) {
  489. ruid := GetRUID(r.Context())
  490. uri := GetURI(r.Context())
  491. log.Debug("handle.get.feed", "ruid", ruid)
  492. var err error
  493. fd, err := s.api.ResolveFeed(r.Context(), uri, r.URL.Query())
  494. if err != nil { // couldn't parse query string or retrieve manifest
  495. getFail.Inc(1)
  496. httpStatus := http.StatusBadRequest
  497. if err == api.ErrCannotLoadFeedManifest || err == api.ErrCannotResolveFeedURI {
  498. httpStatus = http.StatusNotFound
  499. }
  500. respondError(w, r, fmt.Sprintf("cannot retrieve feed information from manifest: %s", err), httpStatus)
  501. return
  502. }
  503. // determine if the query specifies period and version or it is a metadata query
  504. if r.URL.Query().Get("meta") == "1" {
  505. unsignedUpdateRequest, err := s.api.FeedsNewRequest(r.Context(), fd)
  506. if err != nil {
  507. getFail.Inc(1)
  508. respondError(w, r, fmt.Sprintf("cannot retrieve feed metadata for feed=%s: %s", fd.Hex(), err), http.StatusNotFound)
  509. return
  510. }
  511. rawResponse, err := unsignedUpdateRequest.MarshalJSON()
  512. if err != nil {
  513. respondError(w, r, fmt.Sprintf("cannot encode unsigned feed update request: %v", err), http.StatusInternalServerError)
  514. return
  515. }
  516. w.Header().Add("Content-type", "application/json")
  517. w.WriteHeader(http.StatusOK)
  518. fmt.Fprint(w, string(rawResponse))
  519. return
  520. }
  521. lookupParams := &feed.Query{Feed: *fd}
  522. if err = lookupParams.FromValues(r.URL.Query()); err != nil { // parse period, version
  523. respondError(w, r, fmt.Sprintf("invalid feed update request:%s", err), http.StatusBadRequest)
  524. return
  525. }
  526. data, err := s.api.FeedsLookup(r.Context(), lookupParams)
  527. // any error from the switch statement will end up here
  528. if err != nil {
  529. code, err2 := s.translateFeedError(w, r, "feed lookup fail", err)
  530. respondError(w, r, err2.Error(), code)
  531. return
  532. }
  533. // All ok, serve the retrieved update
  534. log.Debug("Found update", "feed", fd.Hex(), "ruid", ruid)
  535. w.Header().Set("Content-Type", api.MimeOctetStream)
  536. http.ServeContent(w, r, "", time.Now(), bytes.NewReader(data))
  537. }
  538. func (s *Server) translateFeedError(w http.ResponseWriter, r *http.Request, supErr string, err error) (int, error) {
  539. code := 0
  540. defaultErr := fmt.Errorf("%s: %v", supErr, err)
  541. rsrcErr, ok := err.(*feed.Error)
  542. if !ok && rsrcErr != nil {
  543. code = rsrcErr.Code()
  544. }
  545. switch code {
  546. case storage.ErrInvalidValue:
  547. return http.StatusBadRequest, defaultErr
  548. case storage.ErrNotFound, storage.ErrNotSynced, storage.ErrNothingToReturn, storage.ErrInit:
  549. return http.StatusNotFound, defaultErr
  550. case storage.ErrUnauthorized, storage.ErrInvalidSignature:
  551. return http.StatusUnauthorized, defaultErr
  552. case storage.ErrDataOverflow:
  553. return http.StatusRequestEntityTooLarge, defaultErr
  554. }
  555. return http.StatusInternalServerError, defaultErr
  556. }
  557. // HandleGet handles a GET request to
  558. // - bzz-raw://<key> and responds with the raw content stored at the
  559. // given storage key
  560. // - bzz-hash://<key> and responds with the hash of the content stored
  561. // at the given storage key as a text/plain response
  562. func (s *Server) HandleGet(w http.ResponseWriter, r *http.Request) {
  563. ruid := GetRUID(r.Context())
  564. uri := GetURI(r.Context())
  565. log.Debug("handle.get", "ruid", ruid, "uri", uri)
  566. getCount.Inc(1)
  567. _, pass, _ := r.BasicAuth()
  568. addr, err := s.api.ResolveURI(r.Context(), uri, pass)
  569. if err != nil {
  570. getFail.Inc(1)
  571. respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
  572. return
  573. }
  574. w.Header().Set("Cache-Control", "max-age=2147483648, immutable") // url was of type bzz://<hex key>/path, so we are sure it is immutable.
  575. log.Debug("handle.get: resolved", "ruid", ruid, "key", addr)
  576. // if path is set, interpret <key> as a manifest and return the
  577. // raw entry at the given path
  578. etag := common.Bytes2Hex(addr)
  579. noneMatchEtag := r.Header.Get("If-None-Match")
  580. w.Header().Set("ETag", fmt.Sprintf("%q", etag)) // set etag to manifest key or raw entry key.
  581. if noneMatchEtag != "" {
  582. if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), addr) {
  583. w.WriteHeader(http.StatusNotModified)
  584. return
  585. }
  586. }
  587. // check the root chunk exists by retrieving the file's size
  588. reader, isEncrypted := s.api.Retrieve(r.Context(), addr)
  589. if _, err := reader.Size(r.Context(), nil); err != nil {
  590. getFail.Inc(1)
  591. respondError(w, r, fmt.Sprintf("root chunk not found %s: %s", addr, err), http.StatusNotFound)
  592. return
  593. }
  594. w.Header().Set("X-Decrypted", fmt.Sprintf("%v", isEncrypted))
  595. switch {
  596. case uri.Raw():
  597. // allow the request to overwrite the content type using a query
  598. // parameter
  599. if typ := r.URL.Query().Get("content_type"); typ != "" {
  600. w.Header().Set("Content-Type", typ)
  601. }
  602. http.ServeContent(w, r, "", time.Now(), reader)
  603. case uri.Hash():
  604. w.Header().Set("Content-Type", "text/plain")
  605. w.WriteHeader(http.StatusOK)
  606. fmt.Fprint(w, addr)
  607. }
  608. }
  609. // HandleGetList handles a GET request to bzz-list:/<manifest>/<path> and returns
  610. // a list of all files contained in <manifest> under <path> grouped into
  611. // common prefixes using "/" as a delimiter
  612. func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) {
  613. ruid := GetRUID(r.Context())
  614. uri := GetURI(r.Context())
  615. _, credentials, _ := r.BasicAuth()
  616. log.Debug("handle.get.list", "ruid", ruid, "uri", uri)
  617. getListCount.Inc(1)
  618. // ensure the root path has a trailing slash so that relative URLs work
  619. if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
  620. http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently)
  621. return
  622. }
  623. addr, err := s.api.Resolve(r.Context(), uri.Addr)
  624. if err != nil {
  625. getListFail.Inc(1)
  626. respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
  627. return
  628. }
  629. log.Debug("handle.get.list: resolved", "ruid", ruid, "key", addr)
  630. list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), addr, uri.Path)
  631. if err != nil {
  632. getListFail.Inc(1)
  633. if isDecryptError(err) {
  634. w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", addr.String()))
  635. respondError(w, r, err.Error(), http.StatusUnauthorized)
  636. return
  637. }
  638. respondError(w, r, err.Error(), http.StatusInternalServerError)
  639. return
  640. }
  641. // if the client wants HTML (e.g. a browser) then render the list as a
  642. // HTML index with relative URLs
  643. if strings.Contains(r.Header.Get("Accept"), "text/html") {
  644. w.Header().Set("Content-Type", "text/html")
  645. err := TemplatesMap["bzz-list"].Execute(w, &htmlListData{
  646. URI: &api.URI{
  647. Scheme: "bzz",
  648. Addr: uri.Addr,
  649. Path: uri.Path,
  650. },
  651. List: &list,
  652. })
  653. if err != nil {
  654. getListFail.Inc(1)
  655. log.Error(fmt.Sprintf("error rendering list HTML: %s", err))
  656. }
  657. return
  658. }
  659. w.Header().Set("Content-Type", "application/json")
  660. json.NewEncoder(w).Encode(&list)
  661. }
  662. // HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
  663. // with the content of the file at <path> from the given <manifest>
  664. func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
  665. ruid := GetRUID(r.Context())
  666. uri := GetURI(r.Context())
  667. _, credentials, _ := r.BasicAuth()
  668. log.Debug("handle.get.file", "ruid", ruid, "uri", r.RequestURI)
  669. getFileCount.Inc(1)
  670. // ensure the root path has a trailing slash so that relative URLs work
  671. if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
  672. http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently)
  673. return
  674. }
  675. var err error
  676. manifestAddr := uri.Address()
  677. if manifestAddr == nil {
  678. manifestAddr, err = s.api.Resolve(r.Context(), uri.Addr)
  679. if err != nil {
  680. getFileFail.Inc(1)
  681. respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
  682. return
  683. }
  684. } else {
  685. w.Header().Set("Cache-Control", "max-age=2147483648, immutable") // url was of type bzz://<hex key>/path, so we are sure it is immutable.
  686. }
  687. log.Debug("handle.get.file: resolved", "ruid", ruid, "key", manifestAddr)
  688. reader, contentType, status, contentKey, err := s.api.Get(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path)
  689. etag := common.Bytes2Hex(contentKey)
  690. noneMatchEtag := r.Header.Get("If-None-Match")
  691. w.Header().Set("ETag", fmt.Sprintf("%q", etag)) // set etag to actual content key.
  692. if noneMatchEtag != "" {
  693. if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), contentKey) {
  694. w.WriteHeader(http.StatusNotModified)
  695. return
  696. }
  697. }
  698. if err != nil {
  699. if isDecryptError(err) {
  700. w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr))
  701. respondError(w, r, err.Error(), http.StatusUnauthorized)
  702. return
  703. }
  704. switch status {
  705. case http.StatusNotFound:
  706. getFileNotFound.Inc(1)
  707. respondError(w, r, err.Error(), http.StatusNotFound)
  708. default:
  709. getFileFail.Inc(1)
  710. respondError(w, r, err.Error(), http.StatusInternalServerError)
  711. }
  712. return
  713. }
  714. //the request results in ambiguous files
  715. //e.g. /read with readme.md and readinglist.txt available in manifest
  716. if status == http.StatusMultipleChoices {
  717. list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path)
  718. if err != nil {
  719. getFileFail.Inc(1)
  720. if isDecryptError(err) {
  721. w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr))
  722. respondError(w, r, err.Error(), http.StatusUnauthorized)
  723. return
  724. }
  725. respondError(w, r, err.Error(), http.StatusInternalServerError)
  726. return
  727. }
  728. log.Debug(fmt.Sprintf("Multiple choices! --> %v", list), "ruid", ruid)
  729. //show a nice page links to available entries
  730. ShowMultipleChoices(w, r, list)
  731. return
  732. }
  733. // check the root chunk exists by retrieving the file's size
  734. if _, err := reader.Size(r.Context(), nil); err != nil {
  735. getFileNotFound.Inc(1)
  736. respondError(w, r, fmt.Sprintf("file not found %s: %s", uri, err), http.StatusNotFound)
  737. return
  738. }
  739. if contentType != "" {
  740. w.Header().Set("Content-Type", contentType)
  741. }
  742. fileName := uri.Addr
  743. if found := path.Base(uri.Path); found != "" && found != "." && found != "/" {
  744. fileName = found
  745. }
  746. w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", fileName))
  747. http.ServeContent(w, r, fileName, time.Now(), newBufferedReadSeeker(reader, getFileBufferSize))
  748. }
  749. // The size of buffer used for bufio.Reader on LazyChunkReader passed to
  750. // http.ServeContent in HandleGetFile.
  751. // Warning: This value influences the number of chunk requests and chunker join goroutines
  752. // per file request.
  753. // Recommended value is 4 times the io.Copy default buffer value which is 32kB.
  754. const getFileBufferSize = 4 * 32 * 1024
  755. // bufferedReadSeeker wraps bufio.Reader to expose Seek method
  756. // from the provied io.ReadSeeker in newBufferedReadSeeker.
  757. type bufferedReadSeeker struct {
  758. r io.Reader
  759. s io.Seeker
  760. }
  761. // newBufferedReadSeeker creates a new instance of bufferedReadSeeker,
  762. // out of io.ReadSeeker. Argument `size` is the size of the read buffer.
  763. func newBufferedReadSeeker(readSeeker io.ReadSeeker, size int) bufferedReadSeeker {
  764. return bufferedReadSeeker{
  765. r: bufio.NewReaderSize(readSeeker, size),
  766. s: readSeeker,
  767. }
  768. }
  769. func (b bufferedReadSeeker) Read(p []byte) (n int, err error) {
  770. return b.r.Read(p)
  771. }
  772. func (b bufferedReadSeeker) Seek(offset int64, whence int) (int64, error) {
  773. return b.s.Seek(offset, whence)
  774. }
  775. type loggingResponseWriter struct {
  776. http.ResponseWriter
  777. statusCode int
  778. }
  779. func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
  780. return &loggingResponseWriter{w, http.StatusOK}
  781. }
  782. func (lrw *loggingResponseWriter) WriteHeader(code int) {
  783. lrw.statusCode = code
  784. lrw.ResponseWriter.WriteHeader(code)
  785. }
  786. func isDecryptError(err error) bool {
  787. return strings.Contains(err.Error(), api.ErrDecrypt.Error())
  788. }