Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ab79a3c

Browse files
committedJan 10, 2020
add automatic hostname redirection
License: MIT Signed-off-by: Steven Allen <steven@stebalien.com>
1 parent 68c7df3 commit ab79a3c

File tree

2 files changed

+97
-23
lines changed

2 files changed

+97
-23
lines changed
 

‎core/corehttp/gateway_handler.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,20 @@ type GatewaySpec struct {
3636
// the gateway logic.
3737
PathPrefixes []string
3838

39-
// SupportsSubdomains indicates whether or not this gateway supports
40-
// requests of the form http://CID.ipfs.GATEWAY/...
41-
SupportsSubdomains bool
39+
// UseSubdomains indicates whether or not this gateway uses subdomains
40+
// for IPFS resources instead of paths. That is: http://CID.ipfs.GATEWAY/...
41+
//
42+
// If this flag is set, any /ipns/$id and/or /ipfs/$id paths in PathPrefixes
43+
// will be permanently redirected to http://$id.[ipns|ipfs].$gateway/.
44+
//
45+
// We do not support using both paths and subdomains for a single domain
46+
// for security reasons.
47+
UseSubdomains bool
4248
}
4349

4450
var DefaultGatewaySpec = GatewaySpec{
45-
PathPrefixes: []string{ipfsPathPrefix, ipnsPathPrefix, "/api/"},
46-
SupportsSubdomains: true,
51+
PathPrefixes: []string{ipfsPathPrefix, ipnsPathPrefix, "/api/"},
52+
UseSubdomains: false,
4753
}
4854

4955
// TODO(steb): Configurable

‎core/corehttp/hostname.go

+86-18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package corehttp
22

33
import (
44
"context"
5+
"fmt"
56
"net"
67
"net/http"
78
"strings"
@@ -21,51 +22,118 @@ func HostnameOption() ServeOption {
2122
ctx, cancel := context.WithCancel(n.Context())
2223
defer cancel()
2324

25+
// Unfortunately, many (well, ipfs.io) gateways use
26+
// DNSLink so if we blindly rewrite with DNSLink, we'll
27+
// break /ipfs links.
28+
//
29+
// We fix this by maintaining a list of known gateways
30+
// and the paths that they serve "gateway" content on.
31+
// That way, we can use DNSLink for everything else.
32+
//
33+
// TODO: We wouldn't need _any_ of this if we
34+
// supported transparent symlink resolution on
35+
// the gateway. If we had that, such gateways could add
36+
// symlinks to `/ipns`, `/ipfs`, `/api`, etc. to their
37+
// home directories. That way, `/ipfs/QmA/ipfs/QmB`
38+
// would "just work". Should we try this?
39+
2440
// Is this one of our "known gateways"?
2541
if gw, ok := KnownGateways[r.Host]; ok {
42+
// This is a known gateway but we're not using
43+
// the subdomain feature.
44+
45+
// Does this gateway _handle_ this path?
2646
if hasPrefix(r.URL.Path, gw.PathPrefixes...) {
47+
// It does.
48+
49+
// Does this gateway use subdomains?
50+
if gw.UseSubdomains {
51+
// Yes, redirect if applicable (pretty much everything except `/api`).
52+
if newURL, ok := toSubdomainURL(r.Host, r.URL.Path); ok {
53+
http.Redirect(
54+
w, r, newURL, http.StatusMovedPermanently,
55+
)
56+
return
57+
}
58+
}
2759
childMux.ServeHTTP(w, r)
2860
return
2961
}
3062
} else if host, pathPrefix, ok := parseSubdomains(r.Host); ok {
31-
if gw, ok := KnownGateways[host]; ok && gw.SupportsSubdomains {
32-
// Always handle this with the gateway.
33-
// We don't care if it's one of the
34-
// valid path-prefixes.
63+
// Looks like we're using subdomains.
64+
65+
// Again, is this a known gateway that supports subdomains?
66+
if gw, ok := KnownGateways[host]; ok && gw.UseSubdomains {
3567

68+
// Yes, serve the request (and rewrite the path to not use subdomains).
3669
r.URL.Path = pathPrefix + r.URL.Path
3770
childMux.ServeHTTP(w, r)
3871
return
3972
}
4073
}
4174

75+
// We don't have a known gateway. Fallback on dnslink.
4276
host := strings.SplitN(r.Host, ":", 2)[0]
43-
if len(host) == 0 || !isd.IsDomain(host) {
44-
childMux.ServeHTTP(w, r)
45-
return
46-
}
77+
if len(host) > 0 && isd.IsDomain(host) {
78+
name := "/ipns/" + host
79+
_, err := n.Namesys.Resolve(ctx, name, nsopts.Depth(1))
80+
if err == nil || err == namesys.ErrResolveRecursion {
81+
// The domain supports dnslink, rewrite.
82+
r.URL.Path = name + r.URL.Path
83+
}
84+
} // else, just treat it as a gateway, I guess.
4785

48-
name := "/ipns/" + host
49-
_, err := n.Namesys.Resolve(ctx, name, nsopts.Depth(1))
50-
if err == nil || err == namesys.ErrResolveRecursion {
51-
r.URL.Path = name + r.URL.Path
52-
}
5386
childMux.ServeHTTP(w, r)
5487
})
5588
return childMux, nil
5689
}
5790
}
5891

92+
func isSubdomainNamespace(ns string) bool {
93+
switch ns {
94+
case "ipfs", "ipns", "p2p", "ipld":
95+
return true
96+
default:
97+
return false
98+
}
99+
}
100+
101+
// Parses a subdomain-based URL and returns it's components
59102
func parseSubdomains(host string) (newHost, pathPrefix string, ok bool) {
60103
parts := strings.SplitN(host, ".", 3)
61-
if len(parts) < 3 {
104+
if len(parts) < 3 || !isSubdomainNamespace(parts[1]) {
62105
return "", "", false
63106
}
64-
switch parts[1] {
65-
case "ipfs", "ipns", "p2p":
66-
return parts[2], "/" + parts[1] + "/" + parts[0], true
107+
return parts[2], "/" + parts[1] + "/" + parts[0], true
108+
}
109+
110+
// Converts a host/path to a subdomain-based URL, if applicable.
111+
func toSubdomainURL(host, path string) (url string, ok bool) {
112+
parts := strings.SplitN(path, "/", 4)
113+
114+
var ns, object, rest string
115+
switch len(parts) {
116+
case 4:
117+
rest = parts[3]
118+
fallthrough
119+
case 3:
120+
ns = parts[1]
121+
object = parts[2]
122+
default:
123+
return "", false
124+
}
125+
126+
if !isSubdomainNamespace(ns) {
127+
return "", false
67128
}
68-
return "", "", false
129+
130+
return fmt.Sprintf(
131+
"http://%s.%s.%s/%s",
132+
object,
133+
ns,
134+
host,
135+
rest,
136+
), true
69137
}
70138

71139
func hasPrefix(s string, prefixes ...string) bool {

0 commit comments

Comments
 (0)
Please sign in to comment.