server.go 18 KB

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