diff --git a/gateway/core/corehttp/gateway.go b/gateway/core/corehttp/gateway.go index 78e0571f2..d85d9e0a5 100644 --- a/gateway/core/corehttp/gateway.go +++ b/gateway/core/corehttp/gateway.go @@ -6,6 +6,7 @@ import ( "net/http" core "github.com/ipfs/go-ipfs/core" + coreapi "github.com/ipfs/go-ipfs/core/coreapi" config "github.com/ipfs/go-ipfs/repo/config" id "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/protocol/identify" ) @@ -27,7 +28,7 @@ func GatewayOption(writable bool, paths ...string) ServeOption { Headers: cfg.Gateway.HTTPHeaders, Writable: writable, PathPrefixes: cfg.Gateway.PathPrefixes, - }) + }, coreapi.NewUnixfsAPI(n)) for _, p := range paths { mux.Handle(p+"/", gateway) @@ -37,7 +38,7 @@ func GatewayOption(writable bool, paths ...string) ServeOption { } func VersionOption() ServeOption { - return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Commit: %s\n", config.CurrentCommit) fmt.Fprintf(w, "Client Version: %s\n", id.ClientVersion) diff --git a/gateway/core/corehttp/gateway_handler.go b/gateway/core/corehttp/gateway_handler.go index ea0ef5c5d..8bf781ef7 100644 --- a/gateway/core/corehttp/gateway_handler.go +++ b/gateway/core/corehttp/gateway_handler.go @@ -16,9 +16,10 @@ import ( chunk "github.com/ipfs/go-ipfs/importer/chunk" dag "github.com/ipfs/go-ipfs/merkledag" dagutils "github.com/ipfs/go-ipfs/merkledag/utils" + + coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface" path "github.com/ipfs/go-ipfs/path" ft "github.com/ipfs/go-ipfs/unixfs" - uio "github.com/ipfs/go-ipfs/unixfs/io" humanize "gx/ipfs/QmPSBJL4momYnE7DcUyk2DVhD6rH488ZmHBGLbxNdhU44K/go-humanize" routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing" @@ -36,12 +37,14 @@ const ( type gatewayHandler struct { node *core.IpfsNode config GatewayConfig + api coreiface.UnixfsAPI } -func newGatewayHandler(node *core.IpfsNode, conf GatewayConfig) *gatewayHandler { +func newGatewayHandler(n *core.IpfsNode, c GatewayConfig, api coreiface.UnixfsAPI) *gatewayHandler { i := &gatewayHandler{ - node: node, - config: conf, + node: n, + config: c, + api: api, } return i } @@ -57,6 +60,21 @@ func (i *gatewayHandler) newDagFromReader(r io.Reader) (node.Node, error) { // TODO(btc): break this apart into separate handlers using a more expressive muxer func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ctx, cancel := context.WithTimeout(i.node.Context(), time.Hour) + // the hour is a hard fallback, we don't expect it to happen, but just in case + defer cancel() + + if cn, ok := w.(http.CloseNotifier); ok { + clientGone := cn.CloseNotify() + go func() { + select { + case <-clientGone: + case <-ctx.Done(): + } + cancel() + }() + } + defer func() { if r := recover(); r != nil { log.Error("A panic occurred in the gateway handler!") @@ -68,7 +86,7 @@ func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if i.config.Writable { switch r.Method { case "POST": - i.postHandler(w, r) + i.postHandler(ctx, w, r) return case "PUT": i.putHandler(w, r) @@ -80,7 +98,7 @@ func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if r.Method == "GET" || r.Method == "HEAD" { - i.getOrHeadHandler(w, r) + i.getOrHeadHandler(ctx, w, r) return } @@ -110,21 +128,7 @@ func (i *gatewayHandler) optionsHandler(w http.ResponseWriter, r *http.Request) i.addUserHeaders(w) // return all custom headers (including CORS ones, if set) } -func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { - ctx, cancel := context.WithTimeout(i.node.Context(), time.Hour) - // the hour is a hard fallback, we don't expect it to happen, but just in case - defer cancel() - - if cn, ok := w.(http.CloseNotifier); ok { - clientGone := cn.CloseNotify() - go func() { - select { - case <-clientGone: - case <-ctx.Done(): - } - cancel() - }() - } +func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { urlPath := r.URL.Path @@ -154,27 +158,19 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request ipnsHostname = true } - p, err := path.ParsePath(urlPath) - if err != nil { - webError(w, "Invalid Path Error", err, http.StatusBadRequest) - return - } - - nd, err := core.Resolve(ctx, i.node.Namesys, i.node.Resolver, p) - // If node is in offline mode the error code and message should be different - if err == core.ErrNoNamesys && !i.node.OnlineMode() { + dr, err := i.api.Cat(ctx, urlPath) + dir := false + if err == coreiface.ErrIsDir { + dir = true + } else if err == coreiface.ErrOffline { w.WriteHeader(http.StatusServiceUnavailable) fmt.Fprint(w, "Could not resolve path. Node is in offline mode.") return } else if err != nil { webError(w, "Path Resolve error", err, http.StatusBadRequest) return - } - - pbnd, ok := nd.(*dag.ProtoNode) - if !ok { - webError(w, "Cannot read non protobuf nodes through gateway", dag.ErrNotProtobuf, http.StatusBadRequest) - return + } else { + defer dr.Close() } etag := gopath.Base(urlPath) @@ -204,13 +200,6 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request w.Header().Set("Suborigin", pathRoot) } - dr, err := uio.NewDagReader(ctx, pbnd, i.node.DAG) - if err != nil && err != uio.ErrIsDir { - // not a directory and still an error - internalWebError(w, err) - return - } - // set these headers _after_ the error, for we may just not have it // and dont want the client to cache a 500 response... // and only if it's /ipfs! @@ -224,18 +213,23 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request modtime = time.Unix(1, 0) } - if err == nil { - defer dr.Close() + if !dir { name := gopath.Base(urlPath) http.ServeContent(w, r, name, modtime, dr) return } + links, err := i.api.Ls(ctx, urlPath) + if err != nil { + internalWebError(w, err) + return + } + // storage for directory listing var dirListing []directoryItem // loop through files foundIndex := false - for _, link := range nd.Links() { + for _, link := range links { if link.Name == "index.html" { log.Debugf("found index.html link for %s", urlPath) foundIndex = true @@ -254,19 +248,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } // return index page instead. - nd, err := core.Resolve(ctx, i.node.Namesys, i.node.Resolver, p) - if err != nil { - internalWebError(w, err) - return - } - - pbnd, ok := nd.(*dag.ProtoNode) - if !ok { - internalWebError(w, dag.ErrNotProtobuf) - return - } - - dr, err := uio.NewDagReader(ctx, pbnd, i.node.DAG) + dr, err := i.api.Cat(ctx, p.String()) if err != nil { internalWebError(w, err) return @@ -332,14 +314,8 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } } -func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) { - nd, err := i.newDagFromReader(r.Body) - if err != nil { - internalWebError(w, err) - return - } - - k, err := i.node.DAG.Add(nd) +func (i *gatewayHandler) postHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { + k, err := i.api.Add(ctx, r.Body) if err != nil { internalWebError(w, err) return