server.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  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. // HandleGetRaw handles a GET request to bzz-raw://<key> and responds with
  261. // the raw content stored at the given storage key
  262. func (s *Server) HandleGetRaw(w http.ResponseWriter, r *Request) {
  263. key, err := s.api.Resolve(r.uri)
  264. if err != nil {
  265. s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  266. return
  267. }
  268. // if path is set, interpret <key> as a manifest and return the
  269. // raw entry at the given path
  270. if r.uri.Path != "" {
  271. walker, err := s.api.NewManifestWalker(key, nil)
  272. if err != nil {
  273. s.BadRequest(w, r, fmt.Sprintf("%s is not a manifest", key))
  274. return
  275. }
  276. var entry *api.ManifestEntry
  277. walker.Walk(func(e *api.ManifestEntry) error {
  278. // if the entry matches the path, set entry and stop
  279. // the walk
  280. if e.Path == r.uri.Path {
  281. entry = e
  282. // return an error to cancel the walk
  283. return errors.New("found")
  284. }
  285. // ignore non-manifest files
  286. if e.ContentType != api.ManifestType {
  287. return nil
  288. }
  289. // if the manifest's path is a prefix of the
  290. // requested path, recurse into it by returning
  291. // nil and continuing the walk
  292. if strings.HasPrefix(r.uri.Path, e.Path) {
  293. return nil
  294. }
  295. return api.SkipManifest
  296. })
  297. if entry == nil {
  298. s.NotFound(w, r, fmt.Errorf("Manifest entry could not be loaded"))
  299. return
  300. }
  301. key = storage.Key(common.Hex2Bytes(entry.Hash))
  302. }
  303. // check the root chunk exists by retrieving the file's size
  304. reader := s.api.Retrieve(key)
  305. if _, err := reader.Size(nil); err != nil {
  306. s.NotFound(w, r, fmt.Errorf("Root chunk not found %s: %s", key, err))
  307. return
  308. }
  309. // allow the request to overwrite the content type using a query
  310. // parameter
  311. contentType := "application/octet-stream"
  312. if typ := r.URL.Query().Get("content_type"); typ != "" {
  313. contentType = typ
  314. }
  315. w.Header().Set("Content-Type", contentType)
  316. http.ServeContent(w, &r.Request, "", time.Now(), reader)
  317. }
  318. // HandleGetFiles handles a GET request to bzz:/<manifest> with an Accept
  319. // header of "application/x-tar" and returns a tar stream of all files
  320. // contained in the manifest
  321. func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) {
  322. if r.uri.Path != "" {
  323. s.BadRequest(w, r, "files request cannot contain a path")
  324. return
  325. }
  326. key, err := s.api.Resolve(r.uri)
  327. if err != nil {
  328. s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  329. return
  330. }
  331. walker, err := s.api.NewManifestWalker(key, nil)
  332. if err != nil {
  333. s.Error(w, r, err)
  334. return
  335. }
  336. tw := tar.NewWriter(w)
  337. defer tw.Close()
  338. w.Header().Set("Content-Type", "application/x-tar")
  339. w.WriteHeader(http.StatusOK)
  340. err = walker.Walk(func(entry *api.ManifestEntry) error {
  341. // ignore manifests (walk will recurse into them)
  342. if entry.ContentType == api.ManifestType {
  343. return nil
  344. }
  345. // retrieve the entry's key and size
  346. reader := s.api.Retrieve(storage.Key(common.Hex2Bytes(entry.Hash)))
  347. size, err := reader.Size(nil)
  348. if err != nil {
  349. return err
  350. }
  351. // write a tar header for the entry
  352. hdr := &tar.Header{
  353. Name: entry.Path,
  354. Mode: entry.Mode,
  355. Size: size,
  356. ModTime: entry.ModTime,
  357. Xattrs: map[string]string{
  358. "user.swarm.content-type": entry.ContentType,
  359. },
  360. }
  361. if err := tw.WriteHeader(hdr); err != nil {
  362. return err
  363. }
  364. // copy the file into the tar stream
  365. n, err := io.Copy(tw, io.LimitReader(reader, hdr.Size))
  366. if err != nil {
  367. return err
  368. } else if n != size {
  369. return fmt.Errorf("error writing %s: expected %d bytes but sent %d", entry.Path, size, n)
  370. }
  371. return nil
  372. })
  373. if err != nil {
  374. s.logError("error generating tar stream: %s", err)
  375. }
  376. }
  377. // HandleGetList handles a GET request to bzz-list:/<manifest>/<path> and returns
  378. // a list of all files contained in <manifest> under <path> grouped into
  379. // common prefixes using "/" as a delimiter
  380. func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
  381. // ensure the root path has a trailing slash so that relative URLs work
  382. if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
  383. http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
  384. return
  385. }
  386. key, err := s.api.Resolve(r.uri)
  387. if err != nil {
  388. s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  389. return
  390. }
  391. list, err := s.getManifestList(key, r.uri.Path)
  392. if err != nil {
  393. s.Error(w, r, err)
  394. return
  395. }
  396. // if the client wants HTML (e.g. a browser) then render the list as a
  397. // HTML index with relative URLs
  398. if strings.Contains(r.Header.Get("Accept"), "text/html") {
  399. w.Header().Set("Content-Type", "text/html")
  400. err := htmlListTemplate.Execute(w, &htmlListData{
  401. URI: &api.URI{
  402. Scheme: "bzz",
  403. Addr: r.uri.Addr,
  404. Path: r.uri.Path,
  405. },
  406. List: &list,
  407. })
  408. if err != nil {
  409. s.logError("error rendering list HTML: %s", err)
  410. }
  411. return
  412. }
  413. w.Header().Set("Content-Type", "application/json")
  414. json.NewEncoder(w).Encode(&list)
  415. }
  416. func (s *Server) getManifestList(key storage.Key, prefix string) (list api.ManifestList, err error) {
  417. walker, err := s.api.NewManifestWalker(key, nil)
  418. if err != nil {
  419. return
  420. }
  421. err = walker.Walk(func(entry *api.ManifestEntry) error {
  422. // handle non-manifest files
  423. if entry.ContentType != api.ManifestType {
  424. // ignore the file if it doesn't have the specified prefix
  425. if !strings.HasPrefix(entry.Path, prefix) {
  426. return nil
  427. }
  428. // if the path after the prefix contains a slash, add a
  429. // common prefix to the list, otherwise add the entry
  430. suffix := strings.TrimPrefix(entry.Path, prefix)
  431. if index := strings.Index(suffix, "/"); index > -1 {
  432. list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1])
  433. return nil
  434. }
  435. if entry.Path == "" {
  436. entry.Path = "/"
  437. }
  438. list.Entries = append(list.Entries, entry)
  439. return nil
  440. }
  441. // if the manifest's path is a prefix of the specified prefix
  442. // then just recurse into the manifest by returning nil and
  443. // continuing the walk
  444. if strings.HasPrefix(prefix, entry.Path) {
  445. return nil
  446. }
  447. // if the manifest's path has the specified prefix, then if the
  448. // path after the prefix contains a slash, add a common prefix
  449. // to the list and skip the manifest, otherwise recurse into
  450. // the manifest by returning nil and continuing the walk
  451. if strings.HasPrefix(entry.Path, prefix) {
  452. suffix := strings.TrimPrefix(entry.Path, prefix)
  453. if index := strings.Index(suffix, "/"); index > -1 {
  454. list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1])
  455. return api.SkipManifest
  456. }
  457. return nil
  458. }
  459. // the manifest neither has the prefix or needs recursing in to
  460. // so just skip it
  461. return api.SkipManifest
  462. })
  463. return list, nil
  464. }
  465. // HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
  466. // with the content of the file at <path> from the given <manifest>
  467. func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
  468. // ensure the root path has a trailing slash so that relative URLs work
  469. if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
  470. http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
  471. return
  472. }
  473. key, err := s.api.Resolve(r.uri)
  474. if err != nil {
  475. s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
  476. return
  477. }
  478. reader, contentType, status, err := s.api.Get(key, r.uri.Path)
  479. if err != nil {
  480. switch status {
  481. case http.StatusNotFound:
  482. s.NotFound(w, r, err)
  483. default:
  484. s.Error(w, r, err)
  485. }
  486. return
  487. }
  488. //the request results in ambiguous files
  489. //e.g. /read with readme.md and readinglist.txt available in manifest
  490. if status == http.StatusMultipleChoices {
  491. list, err := s.getManifestList(key, r.uri.Path)
  492. if err != nil {
  493. s.Error(w, r, err)
  494. return
  495. }
  496. s.logDebug(fmt.Sprintf("Multiple choices! --> %v", list))
  497. //show a nice page links to available entries
  498. ShowMultipleChoices(w, &r.Request, list)
  499. return
  500. }
  501. // check the root chunk exists by retrieving the file's size
  502. if _, err := reader.Size(nil); err != nil {
  503. s.NotFound(w, r, fmt.Errorf("File not found %s: %s", r.uri, err))
  504. return
  505. }
  506. w.Header().Set("Content-Type", contentType)
  507. http.ServeContent(w, &r.Request, "", time.Now(), reader)
  508. }
  509. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  510. 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"))
  511. uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/"))
  512. req := &Request{Request: *r, uri: uri}
  513. if err != nil {
  514. s.logError("Invalid URI %q: %s", r.URL.Path, err)
  515. s.BadRequest(w, req, fmt.Sprintf("Invalid URI %q: %s", r.URL.Path, err))
  516. return
  517. }
  518. s.logDebug("%s request received for %s", r.Method, uri)
  519. switch r.Method {
  520. case "POST":
  521. if uri.Raw() || uri.DeprecatedRaw() {
  522. s.HandlePostRaw(w, req)
  523. } else {
  524. s.HandlePostFiles(w, req)
  525. }
  526. case "PUT":
  527. // DEPRECATED:
  528. // clients should send a POST request (the request creates a
  529. // new manifest leaving the existing one intact, so it isn't
  530. // strictly a traditional PUT request which replaces content
  531. // at a URI, and POST is more ubiquitous)
  532. if uri.Raw() || uri.DeprecatedRaw() {
  533. ShowError(w, r, fmt.Sprintf("No PUT to %s allowed.", uri), http.StatusBadRequest)
  534. return
  535. } else {
  536. s.HandlePostFiles(w, req)
  537. }
  538. case "DELETE":
  539. if uri.Raw() || uri.DeprecatedRaw() {
  540. ShowError(w, r, fmt.Sprintf("No DELETE to %s allowed.", uri), http.StatusBadRequest)
  541. return
  542. }
  543. s.HandleDelete(w, req)
  544. case "GET":
  545. if uri.Raw() || uri.DeprecatedRaw() {
  546. s.HandleGetRaw(w, req)
  547. return
  548. }
  549. if uri.List() {
  550. s.HandleGetList(w, req)
  551. return
  552. }
  553. if r.Header.Get("Accept") == "application/x-tar" {
  554. s.HandleGetFiles(w, req)
  555. return
  556. }
  557. s.HandleGetFile(w, req)
  558. default:
  559. ShowError(w, r, fmt.Sprintf("Method "+r.Method+" is not supported.", uri), http.StatusMethodNotAllowed)
  560. }
  561. }
  562. func (s *Server) updateManifest(key storage.Key, update func(mw *api.ManifestWriter) error) (storage.Key, error) {
  563. mw, err := s.api.NewManifestWriter(key, nil)
  564. if err != nil {
  565. return nil, err
  566. }
  567. if err := update(mw); err != nil {
  568. return nil, err
  569. }
  570. key, err = mw.Store()
  571. if err != nil {
  572. return nil, err
  573. }
  574. s.logDebug("generated manifest %s", key)
  575. return key, nil
  576. }
  577. func (s *Server) logDebug(format string, v ...interface{}) {
  578. log.Debug(fmt.Sprintf("[BZZ] HTTP: "+format, v...))
  579. }
  580. func (s *Server) logError(format string, v ...interface{}) {
  581. log.Error(fmt.Sprintf("[BZZ] HTTP: "+format, v...))
  582. }
  583. func (s *Server) BadRequest(w http.ResponseWriter, r *Request, reason string) {
  584. ShowError(w, &r.Request, fmt.Sprintf("Bad request %s %s: %s", r.Method, r.uri, reason), http.StatusBadRequest)
  585. }
  586. func (s *Server) Error(w http.ResponseWriter, r *Request, err error) {
  587. ShowError(w, &r.Request, fmt.Sprintf("Error serving %s %s: %s", r.Method, r.uri, err), http.StatusInternalServerError)
  588. }
  589. func (s *Server) NotFound(w http.ResponseWriter, r *Request, err error) {
  590. ShowError(w, &r.Request, fmt.Sprintf("NOT FOUND error serving %s %s: %s", r.Method, r.uri, err), http.StatusNotFound)
  591. }