Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor gateway api to operate on higher level semantics #176

Merged
merged 70 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
8ba923b
feat: refactor gateway api to operate on higher level semantics
aschmahmann Feb 16, 2023
0ead6cd
fix: respond to comments
aschmahmann Feb 20, 2023
8704fef
next iteration: moved _redirects and index.html back to being the gat…
aschmahmann Feb 26, 2023
0b6c1c5
rename GatewayMetadata -> ContentPathMetadata
aschmahmann Feb 26, 2023
32eeec6
Merge branch 'main' into feat/gateway-refactor
aschmahmann Feb 27, 2023
9e32875
go mod tidy
aschmahmann Feb 27, 2023
7d145b5
make tests mostly pass, includes change to go-unixfs
aschmahmann Feb 28, 2023
ebbe85d
Merge branch 'main' into feat/gateway-refactor
aschmahmann Mar 1, 2023
8413c2b
use shared BlocksGatway
aschmahmann Mar 1, 2023
2b172e7
fixup most errors, expand definition of NotFound
aschmahmann Mar 1, 2023
461fca2
change error reporting to pass a test
aschmahmann Mar 1, 2023
8fca1ae
tmp: point at go-unixfs commit hash
aschmahmann Mar 1, 2023
2bba208
downgrade go-car+otel dependencies and tidy
aschmahmann Mar 1, 2023
2631a5c
fix: bump go-unixfs dep that was missed previously
aschmahmann Mar 1, 2023
6652355
bump go-unixfs to support cid metadata in symlinks
aschmahmann Mar 1, 2023
3f135ce
fix it so we don't cache /ipns/somedir/index.html
aschmahmann Mar 1, 2023
bbdc88f
feat(gateway): allow content-type for UnixFS data to be determined by…
aschmahmann Mar 3, 2023
f8b96e5
refactor(gateway): move public sentinel errors to the same file as th…
aschmahmann Mar 3, 2023
146bbff
docs(gateway): add clarification to ResolveMutable
aschmahmann Mar 3, 2023
707bec5
feat: fix errors and expose ErrServiceUnavailable instead of go-ipfs-…
aschmahmann Mar 3, 2023
382ceaa
fix more errors, assert index.html content type as text/html
aschmahmann Mar 3, 2023
526e9e0
cleanup some todos
aschmahmann Mar 3, 2023
ba19090
refactor dirEntryMetadata name
aschmahmann Mar 3, 2023
80bebc1
explicitly handle an impossible error
aschmahmann Mar 3, 2023
09dc72d
fixup some text
aschmahmann Mar 3, 2023
f4b3232
more 500s and removing todos
aschmahmann Mar 3, 2023
54b7644
docs: improve gateway API docs
aschmahmann Mar 3, 2023
42dfc9d
blocks-gateway: remove extraneous methods
aschmahmann Mar 5, 2023
b4a6bb7
blocks-gateway: return non-UnixFS codecs from Get, except for non-Uni…
aschmahmann Mar 5, 2023
61f233c
fix _redirects caching/mutability
aschmahmann Mar 5, 2023
0caed11
fix gateway car requests
aschmahmann Mar 5, 2023
8ff5626
switch multierr lib
aschmahmann Mar 5, 2023
7275da3
reorder some imports
aschmahmann Mar 5, 2023
7af0fc6
fix: set X-Ipfs-Roots for CARs
aschmahmann Mar 5, 2023
ac23a54
Merge branch 'main' into feat/gateway-refactor
aschmahmann Mar 6, 2023
5efb46f
fix: restore error handling lost in merge commit
aschmahmann Mar 6, 2023
ea590f1
feat: switch Get to be multiple functions and implement range requests
aschmahmann Mar 6, 2023
4a49a38
feat: add ResolvePath to gateway API
aschmahmann Mar 6, 2023
36c78c0
fix error shadowing
aschmahmann Mar 7, 2023
62c6100
chore: cleanup linter errors
aschmahmann Mar 7, 2023
8c1c4d0
change GetCAR return signature to make the linter happy
aschmahmann Mar 7, 2023
14e7ffb
fix: uint64 cannot be < 0
hacdias Mar 7, 2023
17f8d70
chore: go mod tidy
hacdias Mar 7, 2023
e7366ca
apply some suggestions by @lidel
hacdias Mar 9, 2023
cde04e5
Merge branch 'main' into feat/gateway-refactor
hacdias Mar 9, 2023
db8b79e
feat: use rawRecord value for ipns record etag
hacdias Mar 9, 2023
2411c38
refactor: call addUserHeaders and set X-Ipfs-Path earlier
hacdias Mar 9, 2023
e0a3b8b
docs: apply suggestions
hacdias Mar 9, 2023
2615467
docs: clean explanation in isErrNotFound
hacdias Mar 9, 2023
524f4fd
fix: panic on invalid ipns
hacdias Mar 10, 2023
c9bed98
improved errors, renamed variables, remove comments (lidel feedback)
hacdias Mar 10, 2023
2eb8fb6
wrap directory entries for fast listing 🌮
hacdias Mar 13, 2023
ae65e02
feat: add default dns resolvers
hacdias Mar 14, 2023
167a8a8
Merge branch 'main' into feat/gateway-refactor
hacdias Mar 15, 2023
cbf04b3
feat: switch Get to return a union of files.File and a directory list…
aschmahmann Mar 14, 2023
cd0193d
docs: change contract on GetRange to indicate the full file must stil…
aschmahmann Mar 14, 2023
5f7a833
fix: switch _redirects to properly use root path instead of a partial…
aschmahmann Mar 15, 2023
e928893
refactor: remove unused types
hacdias Mar 15, 2023
c3d7b6f
refactor: make unixfs dir handling clearer
hacdias Mar 16, 2023
a05f54c
docs: handleWebRequestErrors
lidel Mar 18, 2023
47d3e19
chore: rename to directoryMetadata
lidel Mar 18, 2023
3de5712
change errors: graph size -> DAG size
aschmahmann Mar 20, 2023
1c6d038
Merge branch 'main' into feat/gateway-refactor
hacdias Mar 21, 2023
784eed6
fix merge
hacdias Mar 21, 2023
1c4716f
fix: ensure basic ipld codecs are registered
hacdias Mar 21, 2023
925f76d
Merge branch 'main' into feat/gateway-refactor
aschmahmann Mar 27, 2023
b5beaa2
more boxo related fixups
aschmahmann Mar 27, 2023
2e4b7c3
more boxo related fixups
aschmahmann Mar 28, 2023
ffa3aad
cleanup variable name in example
aschmahmann Mar 28, 2023
360b031
Merge branch 'main' into feat/gateway-refactor
hacdias Mar 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions examples/gateway/common/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"io"
"net/http"
gopath "path"

Expand Down Expand Up @@ -62,6 +63,8 @@ type BlocksGateway struct {
routing routing.ValueStore
}

var _ gateway.API = (*BlocksGateway)(nil)

func NewBlocksGateway(blockService blockservice.BlockService, routing routing.ValueStore) (*BlocksGateway, error) {
// Setup the DAG services, which use the CAR block store.
dagService := merkledag.NewDAGService(blockService)
Expand Down Expand Up @@ -99,6 +102,26 @@ func NewBlocksGateway(blockService blockservice.BlockService, routing routing.Va
}, nil
}

func (api *BlocksGateway) Get(ctx context.Context, path gateway.ImmutablePath, opt ...gateway.GetOpt) (gateway.GatewayMetadata, files.Node, error) {
//TODO implement me
panic("implement me")
}

func (api *BlocksGateway) Head(ctx context.Context, path gateway.ImmutablePath) (gateway.GatewayMetadata, files.Node, error) {
//TODO implement me
panic("implement me")
}

func (api *BlocksGateway) GetCAR(ctx context.Context, path gateway.ImmutablePath) (gateway.GatewayMetadata, io.ReadSeekCloser, error) {
//TODO implement me
panic("implement me")
}

func (api *BlocksGateway) ResolveMutable(ctx context.Context, path ifacepath.Path) (gateway.ImmutablePath, error) {
//TODO implement me
panic("implement me")
}

func (api *BlocksGateway) GetUnixFsNode(ctx context.Context, p ifacepath.Resolved) (files.Node, error) {
nd, err := api.resolveNode(ctx, p)
if err != nil {
Expand Down
109 changes: 92 additions & 17 deletions gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package gateway

import (
"context"
"io"
"net/http"
"sort"

cid "github.com/ipfs/go-cid"
"github.com/ipfs/go-libipfs/blocks"
"github.com/ipfs/go-libipfs/files"
iface "github.com/ipfs/interface-go-ipfs-core"
"github.com/ipfs/interface-go-ipfs-core/path"
)

Expand All @@ -17,34 +16,110 @@ type Config struct {
Headers map[string][]string
}

// API defines the minimal set of API services required for a gateway handler.
// ImmutablePath TODO: This isn't great, as its just a signal and mutable paths fulfill the same interface
type ImmutablePath interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why interface ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was matching the other path types, doesn't have to be though. This was mostly a placeholder

// String returns the path as a string.
String() string

// Namespace returns the first component of the path.
//
// For example path "/ipfs/QmHash", calling Namespace() will return "ipfs"
//
// Calling this method on invalid paths (IsValid() != nil) will result in
// empty string
Namespace() string

// IsValid checks if this path is a valid ipfs Path, returning nil iff it is
// valid
IsValid() error
}

type GatewayMetadata struct {
aschmahmann marked this conversation as resolved.
Show resolved Hide resolved
PathSegmentRoots []cid.Cid
LastSegment path.Resolved
RedirectInfo RedirectInfo
}

// RedirectInfo is used to help the gateway figure out what to happen if _redirects were leveraged
type RedirectInfo struct {
RedirectUsed bool
StatusCode int
Redirect4xxPage files.File
Redirect4xxTo path.Path
Redirect4xxPageCid cid.Cid
Redirect3xxTo string
}
aschmahmann marked this conversation as resolved.
Show resolved Hide resolved

type GetOpt func(*getOpts) error
type getOpts struct {
rangeFrom int
rangeTo int

fullDepth bool
rawBlock bool
}

type dummyGetOpts struct{}

var GetOptions dummyGetOpts

// GetRange is a range request for some file data
// Note: currently a full file object should be returned but reading from values outside the range can error
func (o dummyGetOpts) GetRange(from, to int) GetOpt {
return func(options *getOpts) error {
options.rangeFrom = from
options.rangeTo = to
return nil
}
}

// GetFullDepth fetches all data linked from the last logical node in the path. In particular, recursively fetch an
// entire UnixFS directory.
func (o dummyGetOpts) GetFullDepth() GetOpt {
return func(options *getOpts) error {
options.fullDepth = true
return nil
}
}

// GetRawBlock fetches the data at the last path element as if it were a raw block rather than something more complex
// such as a UnixFS file or directory.
func (o dummyGetOpts) GetRawBlock() GetOpt {
return func(options *getOpts) error {
options.rawBlock = true
return nil
}
}

type API interface {
// GetUnixFsNode returns a read-only handle to a file tree referenced by a path.
GetUnixFsNode(context.Context, path.Resolved) (files.Node, error)
// Get returns a file or directory depending on what the path is that has been requested.
// This Get follows the ipfs:// web semantics which means handling of index.html and _redirects files.
// There are multiple options passable to this function, read them for more information.
Get(context.Context, ImmutablePath, ...GetOpt) (GatewayMetadata, files.Node, error)

// Head returns a file or directory depending on what the path is that has been requested.
// This Head follows the ipfs:// web semantics which means handling of index.html and _redirects files.
Head(context.Context, ImmutablePath) (GatewayMetadata, files.Node, error)

// LsUnixFsDir returns the list of links in a directory.
LsUnixFsDir(context.Context, path.Resolved) (<-chan iface.DirEntry, error)
// GetCAR returns a CAR file for the given immutable path
GetCAR(context.Context, ImmutablePath) (GatewayMetadata, io.ReadSeekCloser, error)
aschmahmann marked this conversation as resolved.
Show resolved Hide resolved

// GetBlock return a block from a certain CID.
GetBlock(context.Context, cid.Cid) (blocks.Block, error)
// IsCached returns whether or not the path exists locally.
IsCached(context.Context, path.Path) bool

// GetIPNSRecord retrieves the best IPNS record for a given CID (libp2p-key)
// from the routing system.
GetIPNSRecord(context.Context, cid.Cid) ([]byte, error)

// ResolveMutable takes a mutable path and resolves it into an immutable one. This means recursively resolving any
// DNSLink or IPNS records.
aschmahmann marked this conversation as resolved.
Show resolved Hide resolved
ResolveMutable(context.Context, path.Path) (ImmutablePath, error)

// GetDNSLinkRecord returns the DNSLink TXT record for the provided FQDN.
// Unlike ResolvePath, it does not perform recursive resolution. It only
// checks for the existence of a DNSLink TXT record with path starting with
// /ipfs/ or /ipns/ and returns the path as-is.
GetDNSLinkRecord(context.Context, string) (path.Path, error)

// IsCached returns whether or not the path exists locally.
IsCached(context.Context, path.Path) bool

// ResolvePath resolves the path using UnixFS resolver. If the path does not
// exist due to a missing link, it should return an error of type:
// https://pkg.go.dev/github.com/ipfs/go-path@v0.3.0/resolver#ErrNoLink
ResolvePath(context.Context, path.Path) (path.Resolved, error)
}

// A helper function to clean up a set of headers:
Expand Down
23 changes: 12 additions & 11 deletions gateway/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ package gateway
import (
"context"
"errors"
"io"
"strings"

cid "github.com/ipfs/go-cid"
"github.com/ipfs/go-libipfs/blocks"
"github.com/ipfs/go-libipfs/files"
"github.com/ipfs/go-namesys"
path "github.com/ipfs/go-path"
iface "github.com/ipfs/interface-go-ipfs-core"
nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
ipath "github.com/ipfs/interface-go-ipfs-core/path"
"github.com/libp2p/go-libp2p/core/crypto"
Expand Down Expand Up @@ -64,21 +63,27 @@ type mockApi struct {
ns mockNamesys
}

var _ API = (*mockApi)(nil)

func newMockApi() *mockApi {
return &mockApi{
ns: mockNamesys{},
}
}

func (m *mockApi) GetUnixFsNode(context.Context, ipath.Resolved) (files.Node, error) {
return nil, errors.New("not implemented")
func (m *mockApi) Get(ctx context.Context, immutablePath ImmutablePath, opt ...GetOpt) (GatewayMetadata, files.Node, error) {
return GatewayMetadata{}, nil, errors.New("not implemented")
}

func (m *mockApi) LsUnixFsDir(context.Context, ipath.Resolved) (<-chan iface.DirEntry, error) {
return nil, errors.New("not implemented")
func (m *mockApi) Head(ctx context.Context, immutablePath ImmutablePath) (GatewayMetadata, files.Node, error) {
return GatewayMetadata{}, nil, errors.New("not implemented")
}

func (m *mockApi) GetCAR(ctx context.Context, immutablePath ImmutablePath) (GatewayMetadata, io.ReadSeekCloser, error) {
return GatewayMetadata{}, nil, errors.New("not implemented")
}

func (m *mockApi) GetBlock(context.Context, cid.Cid) (blocks.Block, error) {
func (m *mockApi) ResolveMutable(ctx context.Context, p ipath.Path) (ImmutablePath, error) {
return nil, errors.New("not implemented")
}

Expand All @@ -97,7 +102,3 @@ func (m *mockApi) GetDNSLinkRecord(ctx context.Context, hostname string) (ipath.
func (m *mockApi) IsCached(context.Context, ipath.Path) bool {
return false
}

func (m *mockApi) ResolvePath(context.Context, ipath.Path) (ipath.Resolved, error) {
return nil, errors.New("not implemented")
}
Loading