server.go 19 KB

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