Skip to content

Commit

Permalink
gateway: use core api for serving GET/HEAD requests
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: Lars Gierth <larsg@systemli.org>
  • Loading branch information
Lars Gierth committed Sep 11, 2016
1 parent 88187f4 commit c7d0f8a
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 43 deletions.
13 changes: 11 additions & 2 deletions core/corehttp/gateway.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package corehttp

import (
"context"
"fmt"
"net"
"net/http"

core "github.com/ipfs/go-ipfs/core"
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
config "github.com/ipfs/go-ipfs/repo/config"
id "gx/ipfs/QmXnaDLonE9YBTVDdWBM6Jb5YxxmW1MHMkXzgsnu1jTEmK/go-libp2p/p2p/protocol/identify"
)
Expand All @@ -16,18 +19,24 @@ type GatewayConfig struct {
PathPrefixes []string
}

type apiOption func(context.Context) coreiface.UnixfsAPI

func GatewayOption(paths ...string) ServeOption {
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
cfg, err := n.Repo.Config()
if err != nil {
return nil, err
}

apiOpt := func(ctx context.Context) coreiface.UnixfsAPI {
api := &coreapi.UnixfsAPI{Context: ctx, Node: n}
return api
}
gateway := newGatewayHandler(n, GatewayConfig{
Headers: cfg.Gateway.HTTPHeaders,
Writable: cfg.Gateway.Writable,
PathPrefixes: cfg.Gateway.PathPrefixes,
})
}, apiOpt)

for _, p := range paths {
mux.Handle(p+"/", gateway)
Expand All @@ -37,7 +46,7 @@ func GatewayOption(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)
Expand Down
70 changes: 29 additions & 41 deletions core/corehttp/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ 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"
"github.com/ipfs/go-ipfs/routing"
uio "github.com/ipfs/go-ipfs/unixfs/io"
Expand All @@ -32,14 +34,16 @@ const (
// gatewayHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/<path>)
// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link)
type gatewayHandler struct {
node *core.IpfsNode
config GatewayConfig
node *core.IpfsNode
config GatewayConfig
apiOption apiOption
}

func newGatewayHandler(node *core.IpfsNode, conf GatewayConfig) *gatewayHandler {
func newGatewayHandler(n *core.IpfsNode, c GatewayConfig, opt apiOption) *gatewayHandler {
i := &gatewayHandler{
node: node,
config: conf,
node: n,
config: c,
apiOption: opt,
}
return i
}
Expand All @@ -63,6 +67,8 @@ func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}()

api := i.apiOption(r.Context())

if i.config.Writable {
switch r.Method {
case "POST":
Expand All @@ -78,7 +84,7 @@ func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

if r.Method == "GET" || r.Method == "HEAD" {
i.getOrHeadHandler(w, r)
i.getOrHeadHandler(w, r, api)
return
}

Expand Down Expand Up @@ -108,22 +114,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(w http.ResponseWriter, r *http.Request, api coreiface.UnixfsAPI) {
urlPath := r.URL.Path

// If the gateway is behind a reverse proxy and mounted at a sub-path,
Expand Down Expand Up @@ -152,15 +143,19 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
ipnsHostname = true
}

nd, err := core.Resolve(ctx, i.node, path.Path(urlPath))
// If node is in offline mode the error code and message should be different
if err == core.ErrNoNamesys && !i.node.OnlineMode() {
dr, err := api.Cat(urlPath)
dir := false
if err == coreiface.ErrOffline {
w.WriteHeader(http.StatusServiceUnavailable)
fmt.Fprint(w, "Could not resolve path. Node is in offline mode.")
return
} else if err == coreiface.ErrIsDir {
dir = true
} else if err != nil {
webError(w, "Path Resolve error", err, http.StatusBadRequest)
return
} else {
defer dr.Close()
}

etag := gopath.Base(urlPath)
Expand Down Expand Up @@ -190,13 +185,6 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
w.Header().Set("Suborigin", pathRoot)
}

dr, err := uio.NewDagReader(ctx, nd, 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!
Expand All @@ -210,18 +198,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 := api.Ls(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
Expand All @@ -234,12 +227,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
}

// return index page instead.
nd, err := core.Resolve(ctx, i.node, path.Path(urlPath+"/index.html"))
if err != nil {
internalWebError(w, err)
return
}
dr, err := uio.NewDagReader(ctx, nd, i.node.DAG)
dr, err := api.Cat(urlPath + "/index.html")
if err != nil {
internalWebError(w, err)
return
Expand Down

0 comments on commit c7d0f8a

Please # to comment.