server.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  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. "encoding/json"
  23. "errors"
  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/log"
  37. "github.com/ethereum/go-ethereum/metrics"
  38. "github.com/ethereum/go-ethereum/swarm/api"
  39. "github.com/ethereum/go-ethereum/swarm/storage"
  40. "github.com/rs/cors"
  41. )
  42. //setup metrics
  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. getFilesCount = metrics.NewRegisteredCounter("api.http.get.files.count", nil)
  56. getFilesFail = metrics.NewRegisteredCounter("api.http.get.files.fail", nil)
  57. getListCount = metrics.NewRegisteredCounter("api.http.get.list.count", nil)
  58. getListFail = metrics.NewRegisteredCounter("api.http.get.list.fail", nil)
  59. requestCount = metrics.NewRegisteredCounter("http.request.count", nil)
  60. htmlRequestCount = metrics.NewRegisteredCounter("http.request.html.count", nil)
  61. jsonRequestCount = metrics.NewRegisteredCounter("http.request.json.count", nil)
  62. requestTimer = metrics.NewRegisteredResettingTimer("http.request.time", nil)
  63. )
  64. // ServerConfig is the basic configuration needed for the HTTP server and also
  65. // includes CORS settings.
  66. type ServerConfig struct {
  67. Addr string
  68. CorsString string
  69. }
  70. // browser API for registering bzz url scheme handlers:
  71. // https://developer.mozilla.org/en/docs/Web-based_protocol_handlers
  72. // electron (chromium) api for registering bzz url scheme handlers:
  73. // https://github.com/atom/electron/blob/master/docs/api/protocol.md
  74. // starts up http server
  75. func StartHttpServer(api *api.Api, config *ServerConfig) {
  76. var allowedOrigins []string
  77. for _, domain := range strings.Split(config.CorsString, ",") {
  78. allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
  79. }
  80. c := cors.New(cors.Options{
  81. AllowedOrigins: allowedOrigins,
  82. AllowedMethods: []string{"POST", "GET", "DELETE", "PATCH", "PUT"},
  83. MaxAge: 600,
  84. AllowedHeaders: []string{"*"},
  85. })
  86. hdlr := c.Handler(NewServer(api))
  87. go http.ListenAndServe(config.Addr, hdlr)
  88. }
  89. func NewServer(api *api.Api) *Server {
  90. return &Server{api}
  91. }
  92. type Server struct {
  93. api *api.Api
  94. }
  95. // Request wraps http.Request and also includes the parsed bzz URI
  96. type Request struct {
  97. http.Request
  98. uri *api.URI
  99. }
  100. // HandlePostRaw handles a POST request to a raw bzz-raw:/ URI, stores the request
  101. // body in swarm and returns the resulting storage key as a text/plain response
  102. func (s *Server) HandlePostRaw(w http.ResponseWriter, r *Request) {
  103. postRawCount.Inc(1)
  104. if r.uri.Path != "" {
  105. postRawFail.Inc(1)
  106. s.BadRequest(w, r, "raw POST request cannot contain a path")
  107. return
  108. }
  109. if r.Header.Get("Content-Length") == "" {
  110. postRawFail.Inc(1)
  111. s.BadRequest(w, r, "missing Content-Length header in request")
  112. return
  113. }
  114. key, err := s.api.Store(r.Body, r.ContentLength, nil)
  115. if err != nil {
  116. postRawFail.Inc(1)
  117. s.Error(w, r, err)
  118. return
  119. }
  120. s.logDebug("content for %s stored", key.Log())
  121. w.Header().Set("Content-Type", "text/plain")
  122. w.WriteHeader(http.StatusOK)
  123. fmt.Fprint(w, key)
  124. }
  125. // HandlePostFiles handles a POST request (or deprecated PUT request) to
  126. // bzz:/<hash>/<path> which contains either a single file or multiple files
  127. // (either a tar archive or multipart form), adds those files either to an
  128. // existing manifest or to a new manifest under <path> and returns the
  129. // resulting manifest hash as a text/plain response
  130. func (s *Server) HandlePostFiles(w http.ResponseWriter, r *Request) {
  131. postFilesCount.Inc(1)
  132. contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
  133. if err != nil {
  134. postFilesFail.Inc(1)
  135. s.BadRequest(w, r, err.Error())
  136. return
  137. }
  138. var key storage.Key
  139. if r.uri.Addr != "" {
  140. key, err = s.api.Resolve(r.uri)
  141. if err != nil {
  142. postFilesFail.Inc(1)
  143. s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  144. return
  145. }
  146. } else {
  147. key, err = s.api.NewManifest()
  148. if err != nil {
  149. postFilesFail.Inc(1)
  150. s.Error(w, r, err)
  151. return
  152. }
  153. }
  154. newKey, err := s.updateManifest(key, func(mw *api.ManifestWriter) error {
  155. switch contentType {
  156. case "application/x-tar":
  157. return s.handleTarUpload(r, mw)
  158. case "multipart/form-data":
  159. return s.handleMultipartUpload(r, params["boundary"], mw)
  160. default:
  161. return s.handleDirectUpload(r, mw)
  162. }
  163. })
  164. if err != nil {
  165. postFilesFail.Inc(1)
  166. s.Error(w, r, fmt.Errorf("error creating manifest: %s", err))
  167. return
  168. }
  169. w.Header().Set("Content-Type", "text/plain")
  170. w.WriteHeader(http.StatusOK)
  171. fmt.Fprint(w, newKey)
  172. }
  173. func (s *Server) handleTarUpload(req *Request, mw *api.ManifestWriter) error {
  174. tr := tar.NewReader(req.Body)
  175. for {
  176. hdr, err := tr.Next()
  177. if err == io.EOF {
  178. return nil
  179. } else if err != nil {
  180. return fmt.Errorf("error reading tar stream: %s", err)
  181. }
  182. // only store regular files
  183. if !hdr.FileInfo().Mode().IsRegular() {
  184. continue
  185. }
  186. // add the entry under the path from the request
  187. path := path.Join(req.uri.Path, hdr.Name)
  188. entry := &api.ManifestEntry{
  189. Path: path,
  190. ContentType: hdr.Xattrs["user.swarm.content-type"],
  191. Mode: hdr.Mode,
  192. Size: hdr.Size,
  193. ModTime: hdr.ModTime,
  194. }
  195. s.logDebug("adding %s (%d bytes) to new manifest", entry.Path, entry.Size)
  196. contentKey, err := mw.AddEntry(tr, entry)
  197. if err != nil {
  198. return fmt.Errorf("error adding manifest entry from tar stream: %s", err)
  199. }
  200. s.logDebug("content for %s stored", contentKey.Log())
  201. }
  202. }
  203. func (s *Server) handleMultipartUpload(req *Request, boundary string, mw *api.ManifestWriter) error {
  204. mr := multipart.NewReader(req.Body, boundary)
  205. for {
  206. part, err := mr.NextPart()
  207. if err == io.EOF {
  208. return nil
  209. } else if err != nil {
  210. return fmt.Errorf("error reading multipart form: %s", err)
  211. }
  212. var size int64
  213. var reader io.Reader = part
  214. if contentLength := part.Header.Get("Content-Length"); contentLength != "" {
  215. size, err = strconv.ParseInt(contentLength, 10, 64)
  216. if err != nil {
  217. return fmt.Errorf("error parsing multipart content length: %s", err)
  218. }
  219. reader = part
  220. } else {
  221. // copy the part to a tmp file to get its size
  222. tmp, err := ioutil.TempFile("", "swarm-multipart")
  223. if err != nil {
  224. return err
  225. }
  226. defer os.Remove(tmp.Name())
  227. defer tmp.Close()
  228. size, err = io.Copy(tmp, part)
  229. if err != nil {
  230. return fmt.Errorf("error copying multipart content: %s", err)
  231. }
  232. if _, err := tmp.Seek(0, io.SeekStart); err != nil {
  233. return fmt.Errorf("error copying multipart content: %s", err)
  234. }
  235. reader = tmp
  236. }
  237. // add the entry under the path from the request
  238. name := part.FileName()
  239. if name == "" {
  240. name = part.FormName()
  241. }
  242. path := path.Join(req.uri.Path, name)
  243. entry := &api.ManifestEntry{
  244. Path: path,
  245. ContentType: part.Header.Get("Content-Type"),
  246. Size: size,
  247. ModTime: time.Now(),
  248. }
  249. s.logDebug("adding %s (%d bytes) to new manifest", entry.Path, entry.Size)
  250. contentKey, err := mw.AddEntry(reader, entry)
  251. if err != nil {
  252. return fmt.Errorf("error adding manifest entry from multipart form: %s", err)
  253. }
  254. s.logDebug("content for %s stored", contentKey.Log())
  255. }
  256. }
  257. func (s *Server) handleDirectUpload(req *Request, mw *api.ManifestWriter) error {
  258. key, err := mw.AddEntry(req.Body, &api.ManifestEntry{
  259. Path: req.uri.Path,
  260. ContentType: req.Header.Get("Content-Type"),
  261. Mode: 0644,
  262. Size: req.ContentLength,
  263. ModTime: time.Now(),
  264. })
  265. if err != nil {
  266. return err
  267. }
  268. s.logDebug("content for %s stored", key.Log())
  269. return nil
  270. }
  271. // HandleDelete handles a DELETE request to bzz:/<manifest>/<path>, removes
  272. // <path> from <manifest> and returns the resulting manifest hash as a
  273. // text/plain response
  274. func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) {
  275. deleteCount.Inc(1)
  276. key, err := s.api.Resolve(r.uri)
  277. if err != nil {
  278. deleteFail.Inc(1)
  279. s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  280. return
  281. }
  282. newKey, err := s.updateManifest(key, func(mw *api.ManifestWriter) error {
  283. s.logDebug("removing %s from manifest %s", r.uri.Path, key.Log())
  284. return mw.RemoveEntry(r.uri.Path)
  285. })
  286. if err != nil {
  287. deleteFail.Inc(1)
  288. s.Error(w, r, fmt.Errorf("error updating manifest: %s", err))
  289. return
  290. }
  291. w.Header().Set("Content-Type", "text/plain")
  292. w.WriteHeader(http.StatusOK)
  293. fmt.Fprint(w, newKey)
  294. }
  295. // HandleGet handles a GET request to
  296. // - bzz-raw://<key> and responds with the raw content stored at the
  297. // given storage key
  298. // - bzz-hash://<key> and responds with the hash of the content stored
  299. // at the given storage key as a text/plain response
  300. func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
  301. getCount.Inc(1)
  302. key, err := s.api.Resolve(r.uri)
  303. if err != nil {
  304. getFail.Inc(1)
  305. s.NotFound(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  306. return
  307. }
  308. // if path is set, interpret <key> as a manifest and return the
  309. // raw entry at the given path
  310. if r.uri.Path != "" {
  311. walker, err := s.api.NewManifestWalker(key, nil)
  312. if err != nil {
  313. getFail.Inc(1)
  314. s.BadRequest(w, r, fmt.Sprintf("%s is not a manifest", key))
  315. return
  316. }
  317. var entry *api.ManifestEntry
  318. walker.Walk(func(e *api.ManifestEntry) error {
  319. // if the entry matches the path, set entry and stop
  320. // the walk
  321. if e.Path == r.uri.Path {
  322. entry = e
  323. // return an error to cancel the walk
  324. return errors.New("found")
  325. }
  326. // ignore non-manifest files
  327. if e.ContentType != api.ManifestType {
  328. return nil
  329. }
  330. // if the manifest's path is a prefix of the
  331. // requested path, recurse into it by returning
  332. // nil and continuing the walk
  333. if strings.HasPrefix(r.uri.Path, e.Path) {
  334. return nil
  335. }
  336. return api.SkipManifest
  337. })
  338. if entry == nil {
  339. getFail.Inc(1)
  340. s.NotFound(w, r, fmt.Errorf("Manifest entry could not be loaded"))
  341. return
  342. }
  343. key = storage.Key(common.Hex2Bytes(entry.Hash))
  344. }
  345. // check the root chunk exists by retrieving the file's size
  346. reader := s.api.Retrieve(key)
  347. if _, err := reader.Size(nil); err != nil {
  348. getFail.Inc(1)
  349. s.NotFound(w, r, fmt.Errorf("Root chunk not found %s: %s", key, err))
  350. return
  351. }
  352. switch {
  353. case r.uri.Raw() || r.uri.DeprecatedRaw():
  354. // allow the request to overwrite the content type using a query
  355. // parameter
  356. contentType := "application/octet-stream"
  357. if typ := r.URL.Query().Get("content_type"); typ != "" {
  358. contentType = typ
  359. }
  360. w.Header().Set("Content-Type", contentType)
  361. http.ServeContent(w, &r.Request, "", time.Now(), reader)
  362. case r.uri.Hash():
  363. w.Header().Set("Content-Type", "text/plain")
  364. w.WriteHeader(http.StatusOK)
  365. fmt.Fprint(w, key)
  366. }
  367. }
  368. // HandleGetFiles handles a GET request to bzz:/<manifest> with an Accept
  369. // header of "application/x-tar" and returns a tar stream of all files
  370. // contained in the manifest
  371. func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) {
  372. getFilesCount.Inc(1)
  373. if r.uri.Path != "" {
  374. getFilesFail.Inc(1)
  375. s.BadRequest(w, r, "files request cannot contain a path")
  376. return
  377. }
  378. key, err := s.api.Resolve(r.uri)
  379. if err != nil {
  380. getFilesFail.Inc(1)
  381. s.NotFound(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  382. return
  383. }
  384. walker, err := s.api.NewManifestWalker(key, nil)
  385. if err != nil {
  386. getFilesFail.Inc(1)
  387. s.Error(w, r, err)
  388. return
  389. }
  390. tw := tar.NewWriter(w)
  391. defer tw.Close()
  392. w.Header().Set("Content-Type", "application/x-tar")
  393. w.WriteHeader(http.StatusOK)
  394. err = walker.Walk(func(entry *api.ManifestEntry) error {
  395. // ignore manifests (walk will recurse into them)
  396. if entry.ContentType == api.ManifestType {
  397. return nil
  398. }
  399. // retrieve the entry's key and size
  400. reader := s.api.Retrieve(storage.Key(common.Hex2Bytes(entry.Hash)))
  401. size, err := reader.Size(nil)
  402. if err != nil {
  403. return err
  404. }
  405. // write a tar header for the entry
  406. hdr := &tar.Header{
  407. Name: entry.Path,
  408. Mode: entry.Mode,
  409. Size: size,
  410. ModTime: entry.ModTime,
  411. Xattrs: map[string]string{
  412. "user.swarm.content-type": entry.ContentType,
  413. },
  414. }
  415. if err := tw.WriteHeader(hdr); err != nil {
  416. return err
  417. }
  418. // copy the file into the tar stream
  419. n, err := io.Copy(tw, io.LimitReader(reader, hdr.Size))
  420. if err != nil {
  421. return err
  422. } else if n != size {
  423. return fmt.Errorf("error writing %s: expected %d bytes but sent %d", entry.Path, size, n)
  424. }
  425. return nil
  426. })
  427. if err != nil {
  428. getFilesFail.Inc(1)
  429. s.logError("error generating tar stream: %s", err)
  430. }
  431. }
  432. // HandleGetList handles a GET request to bzz-list:/<manifest>/<path> and returns
  433. // a list of all files contained in <manifest> under <path> grouped into
  434. // common prefixes using "/" as a delimiter
  435. func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
  436. getListCount.Inc(1)
  437. // ensure the root path has a trailing slash so that relative URLs work
  438. if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
  439. http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
  440. return
  441. }
  442. key, err := s.api.Resolve(r.uri)
  443. if err != nil {
  444. getListFail.Inc(1)
  445. s.NotFound(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  446. return
  447. }
  448. list, err := s.getManifestList(key, r.uri.Path)
  449. if err != nil {
  450. getListFail.Inc(1)
  451. s.Error(w, r, err)
  452. return
  453. }
  454. // if the client wants HTML (e.g. a browser) then render the list as a
  455. // HTML index with relative URLs
  456. if strings.Contains(r.Header.Get("Accept"), "text/html") {
  457. w.Header().Set("Content-Type", "text/html")
  458. err := htmlListTemplate.Execute(w, &htmlListData{
  459. URI: &api.URI{
  460. Scheme: "bzz",
  461. Addr: r.uri.Addr,
  462. Path: r.uri.Path,
  463. },
  464. List: &list,
  465. })
  466. if err != nil {
  467. getListFail.Inc(1)
  468. s.logError("error rendering list HTML: %s", err)
  469. }
  470. return
  471. }
  472. w.Header().Set("Content-Type", "application/json")
  473. json.NewEncoder(w).Encode(&list)
  474. }
  475. func (s *Server) getManifestList(key storage.Key, prefix string) (list api.ManifestList, err error) {
  476. walker, err := s.api.NewManifestWalker(key, nil)
  477. if err != nil {
  478. return
  479. }
  480. err = walker.Walk(func(entry *api.ManifestEntry) error {
  481. // handle non-manifest files
  482. if entry.ContentType != api.ManifestType {
  483. // ignore the file if it doesn't have the specified prefix
  484. if !strings.HasPrefix(entry.Path, prefix) {
  485. return nil
  486. }
  487. // if the path after the prefix contains a slash, add a
  488. // common prefix to the list, otherwise add the entry
  489. suffix := strings.TrimPrefix(entry.Path, prefix)
  490. if index := strings.Index(suffix, "/"); index > -1 {
  491. list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1])
  492. return nil
  493. }
  494. if entry.Path == "" {
  495. entry.Path = "/"
  496. }
  497. list.Entries = append(list.Entries, entry)
  498. return nil
  499. }
  500. // if the manifest's path is a prefix of the specified prefix
  501. // then just recurse into the manifest by returning nil and
  502. // continuing the walk
  503. if strings.HasPrefix(prefix, entry.Path) {
  504. return nil
  505. }
  506. // if the manifest's path has the specified prefix, then if the
  507. // path after the prefix contains a slash, add a common prefix
  508. // to the list and skip the manifest, otherwise recurse into
  509. // the manifest by returning nil and continuing the walk
  510. if strings.HasPrefix(entry.Path, prefix) {
  511. suffix := strings.TrimPrefix(entry.Path, prefix)
  512. if index := strings.Index(suffix, "/"); index > -1 {
  513. list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1])
  514. return api.SkipManifest
  515. }
  516. return nil
  517. }
  518. // the manifest neither has the prefix or needs recursing in to
  519. // so just skip it
  520. return api.SkipManifest
  521. })
  522. return list, nil
  523. }
  524. // HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
  525. // with the content of the file at <path> from the given <manifest>
  526. func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
  527. getFileCount.Inc(1)
  528. // ensure the root path has a trailing slash so that relative URLs work
  529. if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
  530. http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
  531. return
  532. }
  533. key, err := s.api.Resolve(r.uri)
  534. if err != nil {
  535. getFileFail.Inc(1)
  536. s.NotFound(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  537. return
  538. }
  539. reader, contentType, status, err := s.api.Get(key, r.uri.Path)
  540. if err != nil {
  541. switch status {
  542. case http.StatusNotFound:
  543. getFileNotFound.Inc(1)
  544. s.NotFound(w, r, err)
  545. default:
  546. getFileFail.Inc(1)
  547. s.Error(w, r, err)
  548. }
  549. return
  550. }
  551. //the request results in ambiguous files
  552. //e.g. /read with readme.md and readinglist.txt available in manifest
  553. if status == http.StatusMultipleChoices {
  554. list, err := s.getManifestList(key, r.uri.Path)
  555. if err != nil {
  556. getFileFail.Inc(1)
  557. s.Error(w, r, err)
  558. return
  559. }
  560. s.logDebug(fmt.Sprintf("Multiple choices! --> %v", list))
  561. //show a nice page links to available entries
  562. ShowMultipleChoices(w, r, list)
  563. return
  564. }
  565. // check the root chunk exists by retrieving the file's size
  566. if _, err := reader.Size(nil); err != nil {
  567. getFileNotFound.Inc(1)
  568. s.NotFound(w, r, fmt.Errorf("File not found %s: %s", r.uri, err))
  569. return
  570. }
  571. w.Header().Set("Content-Type", contentType)
  572. http.ServeContent(w, &r.Request, "", time.Now(), reader)
  573. }
  574. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  575. if metrics.Enabled {
  576. //The increment for request count and request timer themselves have a flag check
  577. //for metrics.Enabled. Nevertheless, we introduce the if here because we
  578. //are looking into the header just to see what request type it is (json/html).
  579. //So let's take advantage and add all metrics related stuff here
  580. requestCount.Inc(1)
  581. defer requestTimer.UpdateSince(time.Now())
  582. if r.Header.Get("Accept") == "application/json" {
  583. jsonRequestCount.Inc(1)
  584. } else {
  585. htmlRequestCount.Inc(1)
  586. }
  587. }
  588. s.logDebug("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, r.URL.Host, r.URL.Path, r.Referer(), r.Header.Get("Accept"))
  589. if r.RequestURI == "/" && strings.Contains(r.Header.Get("Accept"), "text/html") {
  590. err := landingPageTemplate.Execute(w, nil)
  591. if err != nil {
  592. s.logError("error rendering landing page: %s", err)
  593. }
  594. return
  595. }
  596. uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/"))
  597. req := &Request{Request: *r, uri: uri}
  598. if err != nil {
  599. s.logError("Invalid URI %q: %s", r.URL.Path, err)
  600. s.BadRequest(w, req, fmt.Sprintf("Invalid URI %q: %s", r.URL.Path, err))
  601. return
  602. }
  603. s.logDebug("%s request received for %s", r.Method, uri)
  604. switch r.Method {
  605. case "POST":
  606. if uri.Raw() || uri.DeprecatedRaw() {
  607. s.HandlePostRaw(w, req)
  608. } else {
  609. s.HandlePostFiles(w, req)
  610. }
  611. case "PUT":
  612. // DEPRECATED:
  613. // clients should send a POST request (the request creates a
  614. // new manifest leaving the existing one intact, so it isn't
  615. // strictly a traditional PUT request which replaces content
  616. // at a URI, and POST is more ubiquitous)
  617. if uri.Raw() || uri.DeprecatedRaw() {
  618. ShowError(w, req, fmt.Sprintf("No PUT to %s allowed.", uri), http.StatusBadRequest)
  619. return
  620. } else {
  621. s.HandlePostFiles(w, req)
  622. }
  623. case "DELETE":
  624. if uri.Raw() || uri.DeprecatedRaw() {
  625. ShowError(w, req, fmt.Sprintf("No DELETE to %s allowed.", uri), http.StatusBadRequest)
  626. return
  627. }
  628. s.HandleDelete(w, req)
  629. case "GET":
  630. if uri.Raw() || uri.Hash() || uri.DeprecatedRaw() {
  631. s.HandleGet(w, req)
  632. return
  633. }
  634. if uri.List() {
  635. s.HandleGetList(w, req)
  636. return
  637. }
  638. if r.Header.Get("Accept") == "application/x-tar" {
  639. s.HandleGetFiles(w, req)
  640. return
  641. }
  642. s.HandleGetFile(w, req)
  643. default:
  644. ShowError(w, req, fmt.Sprintf("Method "+r.Method+" is not supported.", uri), http.StatusMethodNotAllowed)
  645. }
  646. }
  647. func (s *Server) updateManifest(key storage.Key, update func(mw *api.ManifestWriter) error) (storage.Key, error) {
  648. mw, err := s.api.NewManifestWriter(key, nil)
  649. if err != nil {
  650. return nil, err
  651. }
  652. if err := update(mw); err != nil {
  653. return nil, err
  654. }
  655. key, err = mw.Store()
  656. if err != nil {
  657. return nil, err
  658. }
  659. s.logDebug("generated manifest %s", key)
  660. return key, nil
  661. }
  662. func (s *Server) logDebug(format string, v ...interface{}) {
  663. log.Debug(fmt.Sprintf("[BZZ] HTTP: "+format, v...))
  664. }
  665. func (s *Server) logError(format string, v ...interface{}) {
  666. log.Error(fmt.Sprintf("[BZZ] HTTP: "+format, v...))
  667. }
  668. func (s *Server) BadRequest(w http.ResponseWriter, r *Request, reason string) {
  669. ShowError(w, r, fmt.Sprintf("Bad request %s %s: %s", r.Request.Method, r.uri, reason), http.StatusBadRequest)
  670. }
  671. func (s *Server) Error(w http.ResponseWriter, r *Request, err error) {
  672. ShowError(w, r, fmt.Sprintf("Error serving %s %s: %s", r.Request.Method, r.uri, err), http.StatusInternalServerError)
  673. }
  674. func (s *Server) NotFound(w http.ResponseWriter, r *Request, err error) {
  675. ShowError(w, r, fmt.Sprintf("NOT FOUND error serving %s %s: %s", r.Request.Method, r.uri, err), http.StatusNotFound)
  676. }