server.go 8.4 KB

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