|
|
|
@ -37,11 +37,35 @@ import ( |
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
|
"github.com/ethereum/go-ethereum/log" |
|
|
|
|
"github.com/ethereum/go-ethereum/metrics" |
|
|
|
|
"github.com/ethereum/go-ethereum/swarm/api" |
|
|
|
|
"github.com/ethereum/go-ethereum/swarm/storage" |
|
|
|
|
"github.com/rs/cors" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
//setup metrics
|
|
|
|
|
var ( |
|
|
|
|
postRawCount = metrics.NewRegisteredCounter("api.http.post.raw.count", nil) |
|
|
|
|
postRawFail = metrics.NewRegisteredCounter("api.http.post.raw.fail", nil) |
|
|
|
|
postFilesCount = metrics.NewRegisteredCounter("api.http.post.files.count", nil) |
|
|
|
|
postFilesFail = metrics.NewRegisteredCounter("api.http.post.files.fail", nil) |
|
|
|
|
deleteCount = metrics.NewRegisteredCounter("api.http.delete.count", nil) |
|
|
|
|
deleteFail = metrics.NewRegisteredCounter("api.http.delete.fail", nil) |
|
|
|
|
getCount = metrics.NewRegisteredCounter("api.http.get.count", nil) |
|
|
|
|
getFail = metrics.NewRegisteredCounter("api.http.get.fail", nil) |
|
|
|
|
getFileCount = metrics.NewRegisteredCounter("api.http.get.file.count", nil) |
|
|
|
|
getFileNotFound = metrics.NewRegisteredCounter("api.http.get.file.notfound", nil) |
|
|
|
|
getFileFail = metrics.NewRegisteredCounter("api.http.get.file.fail", nil) |
|
|
|
|
getFilesCount = metrics.NewRegisteredCounter("api.http.get.files.count", nil) |
|
|
|
|
getFilesFail = metrics.NewRegisteredCounter("api.http.get.files.fail", nil) |
|
|
|
|
getListCount = metrics.NewRegisteredCounter("api.http.get.list.count", nil) |
|
|
|
|
getListFail = metrics.NewRegisteredCounter("api.http.get.list.fail", nil) |
|
|
|
|
requestCount = metrics.NewRegisteredCounter("http.request.count", nil) |
|
|
|
|
htmlRequestCount = metrics.NewRegisteredCounter("http.request.html.count", nil) |
|
|
|
|
jsonRequestCount = metrics.NewRegisteredCounter("http.request.json.count", nil) |
|
|
|
|
requestTimer = metrics.NewRegisteredResettingTimer("http.request.time", nil) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// ServerConfig is the basic configuration needed for the HTTP server and also
|
|
|
|
|
// includes CORS settings.
|
|
|
|
|
type ServerConfig struct { |
|
|
|
@ -89,18 +113,22 @@ type Request struct { |
|
|
|
|
// HandlePostRaw handles a POST request to a raw bzz-raw:/ URI, stores the request
|
|
|
|
|
// body in swarm and returns the resulting storage key as a text/plain response
|
|
|
|
|
func (s *Server) HandlePostRaw(w http.ResponseWriter, r *Request) { |
|
|
|
|
postRawCount.Inc(1) |
|
|
|
|
if r.uri.Path != "" { |
|
|
|
|
postRawFail.Inc(1) |
|
|
|
|
s.BadRequest(w, r, "raw POST request cannot contain a path") |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if r.Header.Get("Content-Length") == "" { |
|
|
|
|
postRawFail.Inc(1) |
|
|
|
|
s.BadRequest(w, r, "missing Content-Length header in request") |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
key, err := s.api.Store(r.Body, r.ContentLength, nil) |
|
|
|
|
if err != nil { |
|
|
|
|
postRawFail.Inc(1) |
|
|
|
|
s.Error(w, r, err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -117,8 +145,10 @@ func (s *Server) HandlePostRaw(w http.ResponseWriter, r *Request) { |
|
|
|
|
// existing manifest or to a new manifest under <path> and returns the
|
|
|
|
|
// resulting manifest hash as a text/plain response
|
|
|
|
|
func (s *Server) HandlePostFiles(w http.ResponseWriter, r *Request) { |
|
|
|
|
postFilesCount.Inc(1) |
|
|
|
|
contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) |
|
|
|
|
if err != nil { |
|
|
|
|
postFilesFail.Inc(1) |
|
|
|
|
s.BadRequest(w, r, err.Error()) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -127,12 +157,14 @@ func (s *Server) HandlePostFiles(w http.ResponseWriter, r *Request) { |
|
|
|
|
if r.uri.Addr != "" { |
|
|
|
|
key, err = s.api.Resolve(r.uri) |
|
|
|
|
if err != nil { |
|
|
|
|
postFilesFail.Inc(1) |
|
|
|
|
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
key, err = s.api.NewManifest() |
|
|
|
|
if err != nil { |
|
|
|
|
postFilesFail.Inc(1) |
|
|
|
|
s.Error(w, r, err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -152,6 +184,7 @@ func (s *Server) HandlePostFiles(w http.ResponseWriter, r *Request) { |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
postFilesFail.Inc(1) |
|
|
|
|
s.Error(w, r, fmt.Errorf("error creating manifest: %s", err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -270,8 +303,10 @@ func (s *Server) handleDirectUpload(req *Request, mw *api.ManifestWriter) error |
|
|
|
|
// <path> from <manifest> and returns the resulting manifest hash as a
|
|
|
|
|
// text/plain response
|
|
|
|
|
func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) { |
|
|
|
|
deleteCount.Inc(1) |
|
|
|
|
key, err := s.api.Resolve(r.uri) |
|
|
|
|
if err != nil { |
|
|
|
|
deleteFail.Inc(1) |
|
|
|
|
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -281,6 +316,7 @@ func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) { |
|
|
|
|
return mw.RemoveEntry(r.uri.Path) |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
deleteFail.Inc(1) |
|
|
|
|
s.Error(w, r, fmt.Errorf("error updating manifest: %s", err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -296,8 +332,10 @@ func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) { |
|
|
|
|
// - bzz-hash://<key> and responds with the hash of the content stored
|
|
|
|
|
// at the given storage key as a text/plain response
|
|
|
|
|
func (s *Server) HandleGet(w http.ResponseWriter, r *Request) { |
|
|
|
|
getCount.Inc(1) |
|
|
|
|
key, err := s.api.Resolve(r.uri) |
|
|
|
|
if err != nil { |
|
|
|
|
getFail.Inc(1) |
|
|
|
|
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -307,6 +345,7 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) { |
|
|
|
|
if r.uri.Path != "" { |
|
|
|
|
walker, err := s.api.NewManifestWalker(key, nil) |
|
|
|
|
if err != nil { |
|
|
|
|
getFail.Inc(1) |
|
|
|
|
s.BadRequest(w, r, fmt.Sprintf("%s is not a manifest", key)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -335,6 +374,7 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) { |
|
|
|
|
return api.SkipManifest |
|
|
|
|
}) |
|
|
|
|
if entry == nil { |
|
|
|
|
getFail.Inc(1) |
|
|
|
|
s.NotFound(w, r, fmt.Errorf("Manifest entry could not be loaded")) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -344,6 +384,7 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) { |
|
|
|
|
// check the root chunk exists by retrieving the file's size
|
|
|
|
|
reader := s.api.Retrieve(key) |
|
|
|
|
if _, err := reader.Size(nil); err != nil { |
|
|
|
|
getFail.Inc(1) |
|
|
|
|
s.NotFound(w, r, fmt.Errorf("Root chunk not found %s: %s", key, err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -370,19 +411,23 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) { |
|
|
|
|
// header of "application/x-tar" and returns a tar stream of all files
|
|
|
|
|
// contained in the manifest
|
|
|
|
|
func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) { |
|
|
|
|
getFilesCount.Inc(1) |
|
|
|
|
if r.uri.Path != "" { |
|
|
|
|
getFilesFail.Inc(1) |
|
|
|
|
s.BadRequest(w, r, "files request cannot contain a path") |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
key, err := s.api.Resolve(r.uri) |
|
|
|
|
if err != nil { |
|
|
|
|
getFilesFail.Inc(1) |
|
|
|
|
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
walker, err := s.api.NewManifestWalker(key, nil) |
|
|
|
|
if err != nil { |
|
|
|
|
getFilesFail.Inc(1) |
|
|
|
|
s.Error(w, r, err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -430,6 +475,7 @@ func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) { |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
getFilesFail.Inc(1) |
|
|
|
|
s.logError("error generating tar stream: %s", err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -438,6 +484,7 @@ func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) { |
|
|
|
|
// a list of all files contained in <manifest> under <path> grouped into
|
|
|
|
|
// common prefixes using "/" as a delimiter
|
|
|
|
|
func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) { |
|
|
|
|
getListCount.Inc(1) |
|
|
|
|
// ensure the root path has a trailing slash so that relative URLs work
|
|
|
|
|
if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") { |
|
|
|
|
http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently) |
|
|
|
@ -446,6 +493,7 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) { |
|
|
|
|
|
|
|
|
|
key, err := s.api.Resolve(r.uri) |
|
|
|
|
if err != nil { |
|
|
|
|
getListFail.Inc(1) |
|
|
|
|
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -453,6 +501,7 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) { |
|
|
|
|
list, err := s.getManifestList(key, r.uri.Path) |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
getListFail.Inc(1) |
|
|
|
|
s.Error(w, r, err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -470,6 +519,7 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) { |
|
|
|
|
List: &list, |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
getListFail.Inc(1) |
|
|
|
|
s.logError("error rendering list HTML: %s", err) |
|
|
|
|
} |
|
|
|
|
return |
|
|
|
@ -538,6 +588,7 @@ func (s *Server) getManifestList(key storage.Key, prefix string) (list api.Manif |
|
|
|
|
// HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
|
|
|
|
|
// with the content of the file at <path> from the given <manifest>
|
|
|
|
|
func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) { |
|
|
|
|
getFileCount.Inc(1) |
|
|
|
|
// ensure the root path has a trailing slash so that relative URLs work
|
|
|
|
|
if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") { |
|
|
|
|
http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently) |
|
|
|
@ -546,6 +597,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) { |
|
|
|
|
|
|
|
|
|
key, err := s.api.Resolve(r.uri) |
|
|
|
|
if err != nil { |
|
|
|
|
getFileFail.Inc(1) |
|
|
|
|
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -554,8 +606,10 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) { |
|
|
|
|
if err != nil { |
|
|
|
|
switch status { |
|
|
|
|
case http.StatusNotFound: |
|
|
|
|
getFileNotFound.Inc(1) |
|
|
|
|
s.NotFound(w, r, err) |
|
|
|
|
default: |
|
|
|
|
getFileFail.Inc(1) |
|
|
|
|
s.Error(w, r, err) |
|
|
|
|
} |
|
|
|
|
return |
|
|
|
@ -567,6 +621,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) { |
|
|
|
|
list, err := s.getManifestList(key, r.uri.Path) |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
getFileFail.Inc(1) |
|
|
|
|
s.Error(w, r, err) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -579,6 +634,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) { |
|
|
|
|
|
|
|
|
|
// check the root chunk exists by retrieving the file's size
|
|
|
|
|
if _, err := reader.Size(nil); err != nil { |
|
|
|
|
getFileNotFound.Inc(1) |
|
|
|
|
s.NotFound(w, r, fmt.Errorf("File not found %s: %s", r.uri, err)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -589,6 +645,19 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
if metrics.Enabled { |
|
|
|
|
//The increment for request count and request timer themselves have a flag check
|
|
|
|
|
//for metrics.Enabled. Nevertheless, we introduce the if here because we
|
|
|
|
|
//are looking into the header just to see what request type it is (json/html).
|
|
|
|
|
//So let's take advantage and add all metrics related stuff here
|
|
|
|
|
requestCount.Inc(1) |
|
|
|
|
defer requestTimer.UpdateSince(time.Now()) |
|
|
|
|
if r.Header.Get("Accept") == "application/json" { |
|
|
|
|
jsonRequestCount.Inc(1) |
|
|
|
|
} else { |
|
|
|
|
htmlRequestCount.Inc(1) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
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")) |
|
|
|
|
|
|
|
|
|
uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/")) |
|
|
|
|