server.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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. "bytes"
  22. "io"
  23. "net/http"
  24. "regexp"
  25. "strings"
  26. "sync"
  27. "time"
  28. "github.com/ethereum/go-ethereum/common"
  29. "github.com/ethereum/go-ethereum/logger"
  30. "github.com/ethereum/go-ethereum/logger/glog"
  31. "github.com/ethereum/go-ethereum/swarm/api"
  32. "github.com/ethereum/go-ethereum/swarm/storage"
  33. "github.com/rs/cors"
  34. )
  35. const (
  36. rawType = "application/octet-stream"
  37. )
  38. var (
  39. // accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw)
  40. bzzPrefix = regexp.MustCompile("^/+bzz[ir]?:/+")
  41. trailingSlashes = regexp.MustCompile("/+$")
  42. rootDocumentUri = regexp.MustCompile("^/+bzz[i]?:/+[^/]+$")
  43. // forever = func() time.Time { return time.Unix(0, 0) }
  44. forever = time.Now
  45. )
  46. type sequentialReader struct {
  47. reader io.Reader
  48. pos int64
  49. ahead map[int64](chan bool)
  50. lock sync.Mutex
  51. }
  52. // Server is the basic configuration needs for the HTTP server and also
  53. // includes CORS settings.
  54. type Server struct {
  55. Addr string
  56. CorsString string
  57. }
  58. // browser API for registering bzz url scheme handlers:
  59. // https://developer.mozilla.org/en/docs/Web-based_protocol_handlers
  60. // electron (chromium) api for registering bzz url scheme handlers:
  61. // https://github.com/atom/electron/blob/master/docs/api/protocol.md
  62. // starts up http server
  63. func StartHttpServer(api *api.Api, server *Server) {
  64. serveMux := http.NewServeMux()
  65. serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  66. handler(w, r, api)
  67. })
  68. var allowedOrigins []string
  69. for _, domain := range strings.Split(server.CorsString, ",") {
  70. allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
  71. }
  72. c := cors.New(cors.Options{
  73. AllowedOrigins: allowedOrigins,
  74. AllowedMethods: []string{"POST", "GET", "DELETE", "PATCH", "PUT"},
  75. MaxAge: 600,
  76. })
  77. hdlr := c.Handler(serveMux)
  78. go http.ListenAndServe(server.Addr, hdlr)
  79. glog.V(logger.Info).Infof("Swarm HTTP proxy started on localhost:%s", server.Addr)
  80. }
  81. func handler(w http.ResponseWriter, r *http.Request, a *api.Api) {
  82. requestURL := r.URL
  83. // This is wrong
  84. // if requestURL.Host == "" {
  85. // var err error
  86. // requestURL, err = url.Parse(r.Referer() + requestURL.String())
  87. // if err != nil {
  88. // http.Error(w, err.Error(), http.StatusBadRequest)
  89. // return
  90. // }
  91. // }
  92. glog.V(logger.Debug).Infof("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept"))
  93. uri := requestURL.Path
  94. var raw, nameresolver bool
  95. var proto string
  96. // HTTP-based URL protocol handler
  97. glog.V(logger.Debug).Infof("BZZ request URI: '%s'", uri)
  98. path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string {
  99. proto = p
  100. return ""
  101. })
  102. // protocol identification (ugly)
  103. if proto == "" {
  104. if glog.V(logger.Error) {
  105. glog.Errorf(
  106. "[BZZ] Swarm: Protocol error in request `%s`.",
  107. uri,
  108. )
  109. http.Error(w, "Invalid request URL: need access protocol (bzz:/, bzzr:/, bzzi:/) as first element in path.", http.StatusBadRequest)
  110. return
  111. }
  112. }
  113. if len(proto) > 4 {
  114. raw = proto[1:5] == "bzzr"
  115. nameresolver = proto[1:5] != "bzzi"
  116. }
  117. glog.V(logger.Debug).Infof(
  118. "[BZZ] Swarm: %s request over protocol %s '%s' received.",
  119. r.Method, proto, path,
  120. )
  121. switch {
  122. case r.Method == "POST" || r.Method == "PUT":
  123. if r.Header.Get("content-length") == "" {
  124. http.Error(w, "Missing Content-Length header in request.", http.StatusBadRequest)
  125. return
  126. }
  127. key, err := a.Store(io.LimitReader(r.Body, r.ContentLength), r.ContentLength, nil)
  128. if err == nil {
  129. glog.V(logger.Debug).Infof("Content for %v stored", key.Log())
  130. } else {
  131. http.Error(w, err.Error(), http.StatusBadRequest)
  132. return
  133. }
  134. if r.Method == "POST" {
  135. if raw {
  136. w.Header().Set("Content-Type", "text/plain")
  137. http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(common.Bytes2Hex(key))))
  138. } else {
  139. http.Error(w, "No POST to "+uri+" allowed.", http.StatusBadRequest)
  140. return
  141. }
  142. } else {
  143. // PUT
  144. if raw {
  145. http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest)
  146. return
  147. } else {
  148. path = api.RegularSlashes(path)
  149. mime := r.Header.Get("Content-Type")
  150. // TODO proper root hash separation
  151. glog.V(logger.Debug).Infof("Modify '%s' to store %v as '%s'.", path, key.Log(), mime)
  152. newKey, err := a.Modify(path, common.Bytes2Hex(key), mime, nameresolver)
  153. if err == nil {
  154. glog.V(logger.Debug).Infof("Swarm replaced manifest by '%s'", newKey)
  155. w.Header().Set("Content-Type", "text/plain")
  156. http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey)))
  157. } else {
  158. http.Error(w, "PUT to "+path+"failed.", http.StatusBadRequest)
  159. return
  160. }
  161. }
  162. }
  163. case r.Method == "DELETE":
  164. if raw {
  165. http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest)
  166. return
  167. } else {
  168. path = api.RegularSlashes(path)
  169. glog.V(logger.Debug).Infof("Delete '%s'.", path)
  170. newKey, err := a.Modify(path, "", "", nameresolver)
  171. if err == nil {
  172. glog.V(logger.Debug).Infof("Swarm replaced manifest by '%s'", newKey)
  173. w.Header().Set("Content-Type", "text/plain")
  174. http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey)))
  175. } else {
  176. http.Error(w, "DELETE to "+path+"failed.", http.StatusBadRequest)
  177. return
  178. }
  179. }
  180. case r.Method == "GET" || r.Method == "HEAD":
  181. path = trailingSlashes.ReplaceAllString(path, "")
  182. if path == "" {
  183. http.Error(w, "Empty path not allowed", http.StatusBadRequest)
  184. return
  185. }
  186. if raw {
  187. var reader storage.LazySectionReader
  188. parsedurl, _ := api.Parse(path)
  189. if parsedurl == path {
  190. key, err := a.Resolve(parsedurl, nameresolver)
  191. if err != nil {
  192. glog.V(logger.Error).Infof("%v", err)
  193. http.Error(w, err.Error(), http.StatusBadRequest)
  194. return
  195. }
  196. reader = a.Retrieve(key)
  197. } else {
  198. var status int
  199. readertmp, _, status, err := a.Get(path, nameresolver)
  200. if err != nil {
  201. http.Error(w, err.Error(), status)
  202. return
  203. }
  204. reader = readertmp
  205. }
  206. // retrieving content
  207. quitC := make(chan bool)
  208. size, err := reader.Size(quitC)
  209. if err != nil {
  210. glog.V(logger.Debug).Infof("Could not determine size: %v", err.Error())
  211. //An error on call to Size means we don't have the root chunk
  212. http.Error(w, err.Error(), http.StatusNotFound)
  213. return
  214. }
  215. glog.V(logger.Debug).Infof("Reading %d bytes.", size)
  216. // setting mime type
  217. qv := requestURL.Query()
  218. mimeType := qv.Get("content_type")
  219. if mimeType == "" {
  220. mimeType = rawType
  221. }
  222. w.Header().Set("Content-Type", mimeType)
  223. http.ServeContent(w, r, uri, forever(), reader)
  224. glog.V(logger.Debug).Infof("Serve raw content '%s' (%d bytes) as '%s'", uri, size, mimeType)
  225. // retrieve path via manifest
  226. } else {
  227. glog.V(logger.Debug).Infof("Structured GET request '%s' received.", uri)
  228. // add trailing slash, if missing
  229. if rootDocumentUri.MatchString(uri) {
  230. http.Redirect(w, r, path+"/", http.StatusFound)
  231. return
  232. }
  233. reader, mimeType, status, err := a.Get(path, nameresolver)
  234. if err != nil {
  235. if _, ok := err.(api.ErrResolve); ok {
  236. glog.V(logger.Debug).Infof("%v", err)
  237. status = http.StatusBadRequest
  238. } else {
  239. glog.V(logger.Debug).Infof("error retrieving '%s': %v", uri, err)
  240. status = http.StatusNotFound
  241. }
  242. http.Error(w, err.Error(), status)
  243. return
  244. }
  245. // set mime type and status headers
  246. w.Header().Set("Content-Type", mimeType)
  247. if status > 0 {
  248. w.WriteHeader(status)
  249. } else {
  250. status = 200
  251. }
  252. quitC := make(chan bool)
  253. size, err := reader.Size(quitC)
  254. if err != nil {
  255. glog.V(logger.Debug).Infof("Could not determine size: %v", err.Error())
  256. //An error on call to Size means we don't have the root chunk
  257. http.Error(w, err.Error(), http.StatusNotFound)
  258. return
  259. }
  260. glog.V(logger.Debug).Infof("Served '%s' (%d bytes) as '%s' (status code: %v)", uri, size, mimeType, status)
  261. http.ServeContent(w, r, path, forever(), reader)
  262. }
  263. default:
  264. http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed)
  265. }
  266. }
  267. func (self *sequentialReader) ReadAt(target []byte, off int64) (n int, err error) {
  268. self.lock.Lock()
  269. // assert self.pos <= off
  270. if self.pos > off {
  271. glog.V(logger.Error).Infof("non-sequential read attempted from sequentialReader; %d > %d",
  272. self.pos, off)
  273. panic("Non-sequential read attempt")
  274. }
  275. if self.pos != off {
  276. glog.V(logger.Debug).Infof("deferred read in POST at position %d, offset %d.",
  277. self.pos, off)
  278. wait := make(chan bool)
  279. self.ahead[off] = wait
  280. self.lock.Unlock()
  281. if <-wait {
  282. // failed read behind
  283. n = 0
  284. err = io.ErrUnexpectedEOF
  285. return
  286. }
  287. self.lock.Lock()
  288. }
  289. localPos := 0
  290. for localPos < len(target) {
  291. n, err = self.reader.Read(target[localPos:])
  292. localPos += n
  293. glog.V(logger.Debug).Infof("Read %d bytes into buffer size %d from POST, error %v.",
  294. n, len(target), err)
  295. if err != nil {
  296. glog.V(logger.Debug).Infof("POST stream's reading terminated with %v.", err)
  297. for i := range self.ahead {
  298. self.ahead[i] <- true
  299. delete(self.ahead, i)
  300. }
  301. self.lock.Unlock()
  302. return localPos, err
  303. }
  304. self.pos += int64(n)
  305. }
  306. wait := self.ahead[self.pos]
  307. if wait != nil {
  308. glog.V(logger.Debug).Infof("deferred read in POST at position %d triggered.",
  309. self.pos)
  310. delete(self.ahead, self.pos)
  311. close(wait)
  312. }
  313. self.lock.Unlock()
  314. return localPos, err
  315. }