Skip to content

Commit 4cb72e4

Browse files
committed
feat: migrate subdomain and dnslink code
1 parent a4e0934 commit 4cb72e4

File tree

10 files changed

+1069
-14
lines changed

10 files changed

+1069
-14
lines changed

examples/gateway/car/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Then, you can start the gateway with:
2626
./gateway -c data.car -p 8040
2727
```
2828

29-
Now you can access the gateway in [127.0.0.1:8040](http://127.0.0.1:8040). It will
29+
Now you can access the gateway in [localhost:8040](http://localhost:8040). It will
3030
behave like a regular IPFS Gateway, except for the fact that all contents are provided
3131
from the CAR file. Therefore, things such as IPNS resolution and fetching contents
3232
from nodes in the IPFS network won't work.

examples/gateway/car/main.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/ipfs/go-cid"
1313
offline "github.com/ipfs/go-ipfs-exchange-offline"
1414
"github.com/ipfs/go-libipfs/examples/gateway/common"
15+
"github.com/ipfs/go-libipfs/gateway"
1516
carblockstore "github.com/ipld/go-car/v2/blockstore"
1617
)
1718

@@ -26,20 +27,32 @@ func main() {
2627
}
2728
defer f.Close()
2829

29-
gateway, err := common.NewBlocksGateway(blockService, nil)
30+
gwAPI, err := common.NewBlocksGateway(blockService, nil)
3031
if err != nil {
3132
log.Fatal(err)
3233
}
34+
handler := common.NewBlocksHandler(gwAPI, *portPtr)
3335

34-
handler := common.NewBlocksHandler(gateway, *portPtr)
36+
// Initialize the public gateways that we will want to have available through
37+
// Host header rewritting. This step is optional and only required if you're
38+
// running multiple public gateways and want different settings and support
39+
// for DNSLink and Subdomain Gateways.
40+
publicGateways := map[string]*gateway.Specification{
41+
"localhost": {
42+
Paths: []string{"/ipfs", "/ipns"},
43+
NoDNSLink: true,
44+
UseSubdomains: true,
45+
},
46+
}
47+
noDNSLink := true
48+
handler = gateway.WithHostname(handler, gwAPI, publicGateways, noDNSLink)
3549

36-
address := "127.0.0.1:" + strconv.Itoa(*portPtr)
37-
log.Printf("Listening on http://%s", address)
50+
log.Printf("Listening on http://localhost:%d", *portPtr)
3851
for _, cid := range roots {
39-
log.Printf("Hosting CAR root at http://%s/ipfs/%s", address, cid.String())
52+
log.Printf("Hosting CAR root at http://localhost:%d/ipfs/%s", *portPtr, cid.String())
4053
}
4154

42-
if err := http.ListenAndServe(address, handler); err != nil {
55+
if err := http.ListenAndServe(":"+strconv.Itoa(*portPtr), handler); err != nil {
4356
log.Fatal(err)
4457
}
4558
}

examples/gateway/common/blocks.go

+14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package common
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"net/http"
78
gopath "path"
@@ -24,6 +25,7 @@ import (
2425
uio "github.com/ipfs/go-unixfs/io"
2526
"github.com/ipfs/go-unixfsnode"
2627
iface "github.com/ipfs/interface-go-ipfs-core"
28+
nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
2729
ifacepath "github.com/ipfs/interface-go-ipfs-core/path"
2830
dagpb "github.com/ipld/go-codec-dagpb"
2931
"github.com/ipld/go-ipld-prime"
@@ -143,6 +145,18 @@ func (api *BlocksGateway) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte,
143145
return nil, routing.ErrNotSupported
144146
}
145147

148+
func (api *BlocksGateway) GetDNSLinkRecord(ctx context.Context, hostname string) (ifacepath.Path, error) {
149+
if api.namesys != nil {
150+
p, err := api.namesys.Resolve(ctx, "/ipns/"+hostname, nsopts.Depth(1))
151+
if err == namesys.ErrResolveRecursion {
152+
err = nil
153+
}
154+
return ifacepath.New(p.String()), err
155+
}
156+
157+
return nil, errors.New("not implemented")
158+
}
159+
146160
func (api *BlocksGateway) IsCached(ctx context.Context, p ifacepath.Path) bool {
147161
rp, err := api.ResolvePath(ctx, p)
148162
if err != nil {

examples/gateway/proxy/main.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/ipfs/go-blockservice"
1010
offline "github.com/ipfs/go-ipfs-exchange-offline"
1111
"github.com/ipfs/go-libipfs/examples/gateway/common"
12+
"github.com/ipfs/go-libipfs/gateway"
1213
)
1314

1415
func main() {
@@ -24,16 +25,28 @@ func main() {
2425
routing := newProxyRouting(*gatewayUrlPtr, nil)
2526

2627
// Creates the gateway with the block service and the routing.
27-
gateway, err := common.NewBlocksGateway(blockService, routing)
28+
gwAPI, err := common.NewBlocksGateway(blockService, routing)
2829
if err != nil {
2930
log.Fatal(err)
3031
}
32+
handler := common.NewBlocksHandler(gwAPI, *portPtr)
33+
34+
// Initialize the public gateways that we will want to have available through
35+
// Host header rewritting. This step is optional and only required if you're
36+
// running multiple public gateways and want different settings and support
37+
// for DNSLink and Subdomain Gateways.
38+
publicGateways := map[string]*gateway.Specification{
39+
"localhost": {
40+
Paths: []string{"/ipfs", "/ipns"},
41+
NoDNSLink: true,
42+
UseSubdomains: true,
43+
},
44+
}
45+
noDNSLink := true
46+
handler = gateway.WithHostname(handler, gwAPI, publicGateways, noDNSLink)
3147

32-
handler := common.NewBlocksHandler(gateway, *portPtr)
33-
address := "127.0.0.1:" + strconv.Itoa(*portPtr)
34-
log.Printf("Listening on http://%s", address)
35-
36-
if err := http.ListenAndServe(address, handler); err != nil {
48+
log.Printf("Listening on http://localhost:%d", *portPtr)
49+
if err := http.ListenAndServe(":"+strconv.Itoa(*portPtr), handler); err != nil {
3750
log.Fatal(err)
3851
}
3952
}

gateway/gateway.go

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ type API interface {
3232
// from the routing system.
3333
GetIPNSRecord(context.Context, cid.Cid) ([]byte, error)
3434

35+
// GetDNSLinkRecord returns the DNSLink TXT record for the provided FQDN.
36+
// Unlike ResolvePath, it does not perform recursive resolution. It only
37+
// checks for the existence of a DNSLink TXT record with path starting with
38+
// /ipfs/ or /ipns/ and returns the path as-is.
39+
GetDNSLinkRecord(context.Context, string) (path.Path, error)
40+
3541
// IsCached returns whether or not the path exists locally.
3642
IsCached(context.Context, path.Path) bool
3743

gateway/gateway_test.go

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package gateway
2+
3+
import (
4+
"context"
5+
"errors"
6+
"strings"
7+
8+
cid "github.com/ipfs/go-cid"
9+
"github.com/ipfs/go-libipfs/blocks"
10+
"github.com/ipfs/go-libipfs/files"
11+
"github.com/ipfs/go-namesys"
12+
path "github.com/ipfs/go-path"
13+
iface "github.com/ipfs/interface-go-ipfs-core"
14+
nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
15+
ipath "github.com/ipfs/interface-go-ipfs-core/path"
16+
"github.com/libp2p/go-libp2p/core/crypto"
17+
)
18+
19+
type mockNamesys map[string]path.Path
20+
21+
func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...nsopts.ResolveOpt) (value path.Path, err error) {
22+
cfg := nsopts.DefaultResolveOpts()
23+
for _, o := range opts {
24+
o(&cfg)
25+
}
26+
depth := cfg.Depth
27+
if depth == nsopts.UnlimitedDepth {
28+
// max uint
29+
depth = ^uint(0)
30+
}
31+
for strings.HasPrefix(name, "/ipns/") {
32+
if depth == 0 {
33+
return value, namesys.ErrResolveRecursion
34+
}
35+
depth--
36+
37+
var ok bool
38+
value, ok = m[name]
39+
if !ok {
40+
return "", namesys.ErrResolveFailed
41+
}
42+
name = value.String()
43+
}
44+
return value, nil
45+
}
46+
47+
func (m mockNamesys) ResolveAsync(ctx context.Context, name string, opts ...nsopts.ResolveOpt) <-chan namesys.Result {
48+
out := make(chan namesys.Result, 1)
49+
v, err := m.Resolve(ctx, name, opts...)
50+
out <- namesys.Result{Path: v, Err: err}
51+
close(out)
52+
return out
53+
}
54+
55+
func (m mockNamesys) Publish(ctx context.Context, name crypto.PrivKey, value path.Path, opts ...nsopts.PublishOption) error {
56+
return errors.New("not implemented for mockNamesys")
57+
}
58+
59+
func (m mockNamesys) GetResolver(subs string) (namesys.Resolver, bool) {
60+
return nil, false
61+
}
62+
63+
type mockApi struct {
64+
ns mockNamesys
65+
}
66+
67+
func newMockApi() *mockApi {
68+
return &mockApi{
69+
ns: mockNamesys{},
70+
}
71+
}
72+
73+
func (m *mockApi) GetUnixFsNode(context.Context, ipath.Resolved) (files.Node, error) {
74+
return nil, errors.New("not implemented")
75+
}
76+
77+
func (m *mockApi) LsUnixFsDir(context.Context, ipath.Resolved) (<-chan iface.DirEntry, error) {
78+
return nil, errors.New("not implemented")
79+
}
80+
81+
func (m *mockApi) GetBlock(context.Context, cid.Cid) (blocks.Block, error) {
82+
return nil, errors.New("not implemented")
83+
}
84+
85+
func (m *mockApi) GetIPNSRecord(context.Context, cid.Cid) ([]byte, error) {
86+
return nil, errors.New("not implemented")
87+
}
88+
89+
func (m *mockApi) GetDNSLinkRecord(ctx context.Context, hostname string) (ipath.Path, error) {
90+
p, err := m.ns.Resolve(ctx, "/ipns/"+hostname, nsopts.Depth(1))
91+
if err == namesys.ErrResolveRecursion {
92+
err = nil
93+
}
94+
return ipath.New(p.String()), err
95+
}
96+
97+
func (m *mockApi) IsCached(context.Context, ipath.Path) bool {
98+
return false
99+
}
100+
101+
func (m *mockApi) ResolvePath(context.Context, ipath.Path) (ipath.Resolved, error) {
102+
return nil, errors.New("not implemented")
103+
}

0 commit comments

Comments
 (0)