Skip to content

Commit 95af22a

Browse files
authored
Merge pull request ipfs/kubo#9395 from ipfs/release-v0.17.0
release v0.17.0 This commit was moved from ipfs/kubo@4485d6b
2 parents 134f098 + 9abf081 commit 95af22a

File tree

12 files changed

+163
-53
lines changed

12 files changed

+163
-53
lines changed

gateway/core/corehttp/gateway.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
7777

7878
AddAccessControlHeaders(headers)
7979

80-
offlineApi, err := api.WithOptions(options.Api.Offline(true))
80+
offlineAPI, err := api.WithOptions(options.Api.Offline(true))
8181
if err != nil {
8282
return nil, err
8383
}
@@ -86,7 +86,7 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
8686
Headers: headers,
8787
Writable: writable,
8888
FastDirIndexThreshold: int(cfg.Gateway.FastDirIndexThreshold.WithDefault(100)),
89-
}, api, offlineApi)
89+
}, api, offlineAPI)
9090

9191
gateway = otelhttp.NewHandler(gateway, "Gateway.Request")
9292

gateway/core/corehttp/gateway_handler.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const (
3939
)
4040

4141
var (
42-
onlyAscii = regexp.MustCompile("[[:^ascii:]]")
42+
onlyASCII = regexp.MustCompile("[[:^ascii:]]")
4343
noModtime = time.Unix(0, 0) // disables Last-Modified header if passed as modtime
4444
)
4545

@@ -68,7 +68,7 @@ type redirectTemplateData struct {
6868
type gatewayHandler struct {
6969
config GatewayConfig
7070
api NodeAPI
71-
offlineApi NodeAPI
71+
offlineAPI NodeAPI
7272

7373
// generic metrics
7474
firstContentBlockGetMetric *prometheus.HistogramVec
@@ -214,15 +214,15 @@ func newGatewayHistogramMetric(name string, help string) *prometheus.HistogramVe
214214

215215
// NewGatewayHandler returns an http.Handler that can act as a gateway to IPFS content
216216
// offlineApi is a version of the API that should not make network requests for missing data
217-
func NewGatewayHandler(c GatewayConfig, api NodeAPI, offlineApi NodeAPI) http.Handler {
218-
return newGatewayHandler(c, api, offlineApi)
217+
func NewGatewayHandler(c GatewayConfig, api NodeAPI, offlineAPI NodeAPI) http.Handler {
218+
return newGatewayHandler(c, api, offlineAPI)
219219
}
220220

221-
func newGatewayHandler(c GatewayConfig, api NodeAPI, offlineApi NodeAPI) *gatewayHandler {
221+
func newGatewayHandler(c GatewayConfig, api NodeAPI, offlineAPI NodeAPI) *gatewayHandler {
222222
i := &gatewayHandler{
223223
config: c,
224224
api: api,
225-
offlineApi: offlineApi,
225+
offlineAPI: offlineAPI,
226226
// Improved Metrics
227227
// ----------------------------
228228
// Time till the first content block (bar in /ipfs/cid/foo/bar)
@@ -430,6 +430,10 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
430430
carVersion := formatParams["version"]
431431
i.serveCAR(r.Context(), w, r, resolvedPath, contentPath, carVersion, begin)
432432
return
433+
case "application/x-tar":
434+
logger.Debugw("serving tar file", "path", contentPath)
435+
i.serveTAR(r.Context(), w, r, resolvedPath, contentPath, begin, logger)
436+
return
433437
default: // catch-all for unsuported application/vnd.*
434438
err := fmt.Errorf("unsupported format %q", responseFormat)
435439
webError(w, "failed respond with requested content type", err, http.StatusBadRequest)
@@ -683,7 +687,7 @@ func addContentDispositionHeader(w http.ResponseWriter, r *http.Request, content
683687
// Set Content-Disposition to arbitrary filename and disposition
684688
func setContentDispositionHeader(w http.ResponseWriter, filename string, disposition string) {
685689
utf8Name := url.PathEscape(filename)
686-
asciiName := url.PathEscape(onlyAscii.ReplaceAllLiteralString(filename, "_"))
690+
asciiName := url.PathEscape(onlyASCII.ReplaceAllLiteralString(filename, "_"))
687691
w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"; filename*=UTF-8''%s", disposition, asciiName, utf8Name))
688692
}
689693

@@ -842,9 +846,10 @@ func getEtag(r *http.Request, cid cid.Cid) string {
842846
responseFormat, _, err := customResponseFormat(r)
843847
if err == nil && responseFormat != "" {
844848
// application/vnd.ipld.foo → foo
845-
f := responseFormat[strings.LastIndex(responseFormat, ".")+1:]
846-
// Etag: "cid.foo" (gives us nice compression together with Content-Disposition in block (raw) and car responses)
847-
suffix = `.` + f + suffix
849+
// application/x-bar → x-bar
850+
shortFormat := responseFormat[strings.LastIndexAny(responseFormat, "/.")+1:]
851+
// Etag: "cid.shortFmt" (gives us nice compression together with Content-Disposition in block (raw) and car responses)
852+
suffix = `.` + shortFormat + suffix
848853
}
849854
// TODO: include selector suffix when https://github.com/ipfs/kubo/issues/8769 lands
850855
return prefix + cid.String() + suffix
@@ -859,14 +864,17 @@ func customResponseFormat(r *http.Request) (mediaType string, params map[string]
859864
return "application/vnd.ipld.raw", nil, nil
860865
case "car":
861866
return "application/vnd.ipld.car", nil, nil
867+
case "tar":
868+
return "application/x-tar", nil, nil
862869
}
863870
}
864871
// Browsers and other user agents will send Accept header with generic types like:
865872
// Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
866-
// We only care about explciit, vendor-specific content-types.
873+
// We only care about explicit, vendor-specific content-types.
867874
for _, accept := range r.Header.Values("Accept") {
868875
// respond to the very first ipld content type
869-
if strings.HasPrefix(accept, "application/vnd.ipld") {
876+
if strings.HasPrefix(accept, "application/vnd.ipld") ||
877+
strings.HasPrefix(accept, "application/x-tar") {
870878
mediatype, params, err := mime.ParseMediaType(accept)
871879
if err != nil {
872880
return "", nil, err
@@ -933,7 +941,7 @@ func (i *gatewayHandler) handlePathResolution(w http.ResponseWriter, r *http.Req
933941
// https://github.com/ipfs/specs/blob/main/http-gateways/PATH_GATEWAY.md#cache-control-request-header
934942
func (i *gatewayHandler) handleOnlyIfCached(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, logger *zap.SugaredLogger) (requestHandled bool) {
935943
if r.Header.Get("Cache-Control") == "only-if-cached" {
936-
_, err := i.offlineApi.Block().Stat(r.Context(), contentPath)
944+
_, err := i.offlineAPI.Block().Stat(r.Context(), contentPath)
937945
if err != nil {
938946
if r.Method == http.MethodHead {
939947
w.WriteHeader(http.StatusPreconditionFailed)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package corehttp
2+
3+
import (
4+
"context"
5+
"html"
6+
"net/http"
7+
"time"
8+
9+
files "github.com/ipfs/go-ipfs-files"
10+
ipath "github.com/ipfs/interface-go-ipfs-core/path"
11+
"github.com/ipfs/kubo/tracing"
12+
"go.opentelemetry.io/otel/attribute"
13+
"go.opentelemetry.io/otel/trace"
14+
"go.uber.org/zap"
15+
)
16+
17+
var unixEpochTime = time.Unix(0, 0)
18+
19+
func (i *gatewayHandler) serveTAR(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) {
20+
ctx, span := tracing.Span(ctx, "Gateway", "ServeTAR", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
21+
defer span.End()
22+
23+
ctx, cancel := context.WithCancel(ctx)
24+
defer cancel()
25+
26+
// Get Unixfs file
27+
file, err := i.api.Unixfs().Get(ctx, resolvedPath)
28+
if err != nil {
29+
webError(w, "ipfs cat "+html.EscapeString(contentPath.String()), err, http.StatusBadRequest)
30+
return
31+
}
32+
defer file.Close()
33+
34+
rootCid := resolvedPath.Cid()
35+
36+
// Set Cache-Control and read optional Last-Modified time
37+
modtime := addCacheControlHeaders(w, r, contentPath, rootCid)
38+
39+
// Weak Etag W/ because we can't guarantee byte-for-byte identical
40+
// responses, but still want to benefit from HTTP Caching. Two TAR
41+
// responses for the same CID will be logically equivalent,
42+
// but when TAR is streamed, then in theory, files and directories
43+
// may arrive in different order (depends on TAR lib and filesystem/inodes).
44+
etag := `W/` + getEtag(r, rootCid)
45+
w.Header().Set("Etag", etag)
46+
47+
// Finish early if Etag match
48+
if r.Header.Get("If-None-Match") == etag {
49+
w.WriteHeader(http.StatusNotModified)
50+
return
51+
}
52+
53+
// Set Content-Disposition
54+
var name string
55+
if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" {
56+
name = urlFilename
57+
} else {
58+
name = rootCid.String() + ".tar"
59+
}
60+
setContentDispositionHeader(w, name, "attachment")
61+
62+
// Construct the TAR writer
63+
tarw, err := files.NewTarWriter(w)
64+
if err != nil {
65+
webError(w, "could not build tar writer", err, http.StatusInternalServerError)
66+
return
67+
}
68+
defer tarw.Close()
69+
70+
// Sets correct Last-Modified header. This code is borrowed from the standard
71+
// library (net/http/server.go) as we cannot use serveFile without throwing the entire
72+
// TAR into the memory first.
73+
if !(modtime.IsZero() || modtime.Equal(unixEpochTime)) {
74+
w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat))
75+
}
76+
77+
w.Header().Set("Content-Type", "application/x-tar")
78+
w.Header().Set("X-Content-Type-Options", "nosniff") // no funny business in the browsers :^)
79+
80+
// The TAR has a top-level directory (or file) named by the CID.
81+
if err := tarw.WriteFile(file, rootCid.String()); err != nil {
82+
w.Header().Set("X-Stream-Error", err.Error())
83+
// Trailer headers do not work in web browsers
84+
// (see https://github.com/mdn/browser-compat-data/issues/14703)
85+
// and we have limited options around error handling in browser contexts.
86+
// To improve UX/DX, we finish response stream with error message, allowing client to
87+
// (1) detect error by having corrupted TAR
88+
// (2) be able to reason what went wrong by instecting the tail of TAR stream
89+
_, _ = w.Write([]byte(err.Error()))
90+
return
91+
}
92+
}

gateway/core/corehttp/gateway_handler_unixfs_dir.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
3939
webError(w, "failed to parse request path", err, http.StatusInternalServerError)
4040
return
4141
}
42-
originalUrlPath := requestURI.Path
42+
originalURLPath := requestURI.Path
4343

4444
// Ensure directory paths end with '/'
45-
if originalUrlPath[len(originalUrlPath)-1] != '/' {
45+
if originalURLPath[len(originalURLPath)-1] != '/' {
4646
// don't redirect to trailing slash if it's go get
4747
// https://github.com/ipfs/kubo/pull/3963
4848
goget := r.URL.Query().Get("go-get") == "1"
@@ -53,7 +53,7 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
5353
suffix = suffix + "?" + r.URL.RawQuery
5454
}
5555
// /ipfs/cid/foo?bar must be redirected to /ipfs/cid/foo/?bar
56-
redirectURL := originalUrlPath + suffix
56+
redirectURL := originalURLPath + suffix
5757
logger.Debugw("directory location moved permanently", "status", http.StatusMovedPermanently)
5858
http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
5959
return
@@ -125,7 +125,7 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
125125
di := directoryItem{
126126
Size: "", // no size because we did not fetch child nodes
127127
Name: link.Name,
128-
Path: gopath.Join(originalUrlPath, link.Name),
128+
Path: gopath.Join(originalURLPath, link.Name),
129129
Hash: hash,
130130
ShortHash: shortHash(hash),
131131
}
@@ -149,7 +149,7 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
149149

150150
// construct the correct back link
151151
// https://github.com/ipfs/kubo/issues/1365
152-
var backLink string = originalUrlPath
152+
backLink := originalURLPath
153153

154154
// don't go further up than /ipfs/$hash/
155155
pathSplit := path.SplitList(contentPath.String())

gateway/core/corehttp/gateway_indexPage.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ func init() {
117117

118118
// custom template-escaping function to escape a full path, including '#' and '?'
119119
urlEscape := func(rawUrl string) string {
120-
pathUrl := url.URL{Path: rawUrl}
121-
return pathUrl.String()
120+
pathURL := url.URL{Path: rawUrl}
121+
return pathURL.String()
122122
}
123123

124124
// Directory listing template

gateway/core/corehttp/hostname.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ func HostnameOption() ServeOption {
8484
if gw.UseSubdomains {
8585
// Yes, redirect if applicable
8686
// Example: dweb.link/ipfs/{cid} → {cid}.ipfs.dweb.link
87-
newURL, err := toSubdomainURL(host, r.URL.Path, r, coreAPI)
87+
useInlinedDNSLink := gw.InlineDNSLink.WithDefault(config.DefaultInlineDNSLink)
88+
newURL, err := toSubdomainURL(host, r.URL.Path, r, useInlinedDNSLink, coreAPI)
8889
if err != nil {
8990
http.Error(w, err.Error(), http.StatusBadRequest)
9091
return
@@ -132,6 +133,9 @@ func HostnameOption() ServeOption {
132133
// Assemble original path prefix.
133134
pathPrefix := "/" + ns + "/" + rootID
134135

136+
// Retrieve whether or not we should inline DNSLink.
137+
useInlinedDNSLink := gw.InlineDNSLink.WithDefault(config.DefaultInlineDNSLink)
138+
135139
// Does this gateway _handle_ subdomains AND this path?
136140
if !(gw.UseSubdomains && hasPrefix(pathPrefix, gw.Paths...)) {
137141
// If not, resource does not exist, return 404
@@ -149,7 +153,7 @@ func HostnameOption() ServeOption {
149153
}
150154
if !strings.HasPrefix(r.Host, dnsCID) {
151155
dnsPrefix := "/" + ns + "/" + dnsCID
152-
newURL, err := toSubdomainURL(gwHostname, dnsPrefix+r.URL.Path, r, coreAPI)
156+
newURL, err := toSubdomainURL(gwHostname, dnsPrefix+r.URL.Path, r, useInlinedDNSLink, coreAPI)
153157
if err != nil {
154158
http.Error(w, err.Error(), http.StatusBadRequest)
155159
return
@@ -165,7 +169,7 @@ func HostnameOption() ServeOption {
165169
// Do we need to fix multicodec in PeerID represented as CIDv1?
166170
if isPeerIDNamespace(ns) {
167171
if rootCID.Type() != cid.Libp2pKey {
168-
newURL, err := toSubdomainURL(gwHostname, pathPrefix+r.URL.Path, r, coreAPI)
172+
newURL, err := toSubdomainURL(gwHostname, pathPrefix+r.URL.Path, r, useInlinedDNSLink, coreAPI)
169173
if err != nil {
170174
http.Error(w, err.Error(), http.StatusBadRequest)
171175
return
@@ -451,7 +455,7 @@ func toDNSLinkFQDN(dnsLabel string) (fqdn string) {
451455
}
452456

453457
// Converts a hostname/path to a subdomain-based URL, if applicable.
454-
func toSubdomainURL(hostname, path string, r *http.Request, ipfs iface.CoreAPI) (redirURL string, err error) {
458+
func toSubdomainURL(hostname, path string, r *http.Request, inlineDNSLink bool, ipfs iface.CoreAPI) (redirURL string, err error) {
455459
var scheme, ns, rootID, rest string
456460

457461
query := r.URL.RawQuery
@@ -554,7 +558,7 @@ func toSubdomainURL(hostname, path string, r *http.Request, ipfs iface.CoreAPI)
554558
// can be loaded from a subdomain gateway with a wildcard TLS cert if
555559
// represented as a single DNS label:
556560
// https://my-v--long-example-com.ipns.dweb.link
557-
if isHTTPS && ns == "ipns" && strings.Contains(rootID, ".") {
561+
if (inlineDNSLink || isHTTPS) && ns == "ipns" && strings.Contains(rootID, ".") {
558562
if isDNSLinkName(r.Context(), ipfs, rootID) {
559563
// my.v-long.example.com → my-v--long-example-com
560564
dnsLabel, err := toDNSLinkDNSLabel(rootID)

gateway/core/corehttp/hostname_test.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,35 +36,39 @@ func TestToSubdomainURL(t *testing.T) {
3636

3737
for _, test := range []struct {
3838
// in:
39-
request *http.Request
40-
gwHostname string
41-
path string
39+
request *http.Request
40+
gwHostname string
41+
inlineDNSLink bool
42+
path string
4243
// out:
4344
url string
4445
err error
4546
}{
4647
// DNSLink
47-
{httpRequest, "localhost", "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost/", nil},
48+
{httpRequest, "localhost", false, "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost/", nil},
4849
// Hostname with port
49-
{httpRequest, "localhost:8080", "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost:8080/", nil},
50+
{httpRequest, "localhost:8080", false, "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost:8080/", nil},
5051
// CIDv0 → CIDv1base32
51-
{httpRequest, "localhost", "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.localhost/", nil},
52+
{httpRequest, "localhost", false, "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.localhost/", nil},
5253
// CIDv1 with long sha512
53-
{httpRequest, "localhost", "/ipfs/bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")},
54+
{httpRequest, "localhost", false, "/ipfs/bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")},
5455
// PeerID as CIDv1 needs to have libp2p-key multicodec
55-
{httpRequest, "localhost", "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "http://k2k4r8n0flx3ra0y5dr8fmyvwbzy3eiztmtq6th694k5a3rznayp3e4o.ipns.localhost/", nil},
56-
{httpRequest, "localhost", "/ipns/bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "http://k2k4r8l9ja7hkzynavdqup76ou46tnvuaqegbd04a4o1mpbsey0meucb.ipns.localhost/", nil},
56+
{httpRequest, "localhost", false, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "http://k2k4r8n0flx3ra0y5dr8fmyvwbzy3eiztmtq6th694k5a3rznayp3e4o.ipns.localhost/", nil},
57+
{httpRequest, "localhost", false, "/ipns/bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "http://k2k4r8l9ja7hkzynavdqup76ou46tnvuaqegbd04a4o1mpbsey0meucb.ipns.localhost/", nil},
5758
// PeerID: ed25519+identity multihash → CIDv1Base36
58-
{httpRequest, "localhost", "/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", "http://k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna.ipns.localhost/", nil},
59-
{httpRequest, "sub.localhost", "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.sub.localhost/", nil},
59+
{httpRequest, "localhost", false, "/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", "http://k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna.ipns.localhost/", nil},
60+
{httpRequest, "sub.localhost", false, "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.sub.localhost/", nil},
6061
// HTTPS requires DNSLink name to fit in a single DNS label – see "Option C" from https://github.com/ipfs/in-web-browsers/issues/169
61-
{httpRequest, "dweb.link", "/ipns/dnslink.long-name.example.com", "http://dnslink.long-name.example.com.ipns.dweb.link/", nil},
62-
{httpsRequest, "dweb.link", "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil},
63-
{httpsProxiedRequest, "dweb.link", "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil},
62+
{httpRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "http://dnslink.long-name.example.com.ipns.dweb.link/", nil},
63+
{httpsRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil},
64+
{httpsProxiedRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil},
65+
// HTTP requests can also be converted to fit into a single DNS label - https://github.com/ipfs/kubo/issues/9243
66+
{httpRequest, "localhost", true, "/ipns/dnslink.long-name.example.com", "http://dnslink-long--name-example-com.ipns.localhost/", nil},
67+
{httpRequest, "dweb.link", true, "/ipns/dnslink.long-name.example.com", "http://dnslink-long--name-example-com.ipns.dweb.link/", nil},
6468
} {
65-
url, err := toSubdomainURL(test.gwHostname, test.path, test.request, coreAPI)
69+
url, err := toSubdomainURL(test.gwHostname, test.path, test.request, test.inlineDNSLink, coreAPI)
6670
if url != test.url || !equalError(err, test.err) {
67-
t.Errorf("(%s, %s) returned (%s, %v), expected (%s, %v)", test.gwHostname, test.path, url, err, test.url, test.err)
71+
t.Errorf("(%s, %v, %s) returned (%s, %v), expected (%s, %v)", test.gwHostname, test.inlineDNSLink, test.path, url, err, test.url, test.err)
6872
}
6973
}
7074
}

0 commit comments

Comments
 (0)