提交 3632a6d4 作者: Steven Allen

add automatic hostname redirection

License: MIT
Signed-off-by: 's avatarSteven Allen <steven@stebalien.com>
上级 4af104ba
......@@ -39,14 +39,20 @@ type GatewaySpec struct {
// the gateway logic.
PathPrefixes []string
// SupportsSubdomains indicates whether or not this gateway supports
// requests of the form http://CID.ipfs.GATEWAY/...
SupportsSubdomains bool
// UseSubdomains indicates whether or not this gateway uses subdomains
// for IPFS resources instead of paths. That is: http://CID.ipfs.GATEWAY/...
//
// If this flag is set, any /ipns/$id and/or /ipfs/$id paths in PathPrefixes
// will be permanently redirected to http://$id.[ipns|ipfs].$gateway/.
//
// We do not support using both paths and subdomains for a single domain
// for security reasons.
UseSubdomains bool
}
var DefaultGatewaySpec = GatewaySpec{
PathPrefixes: []string{ipfsPathPrefix, ipnsPathPrefix, "/api/"},
SupportsSubdomains: true,
PathPrefixes: []string{ipfsPathPrefix, ipnsPathPrefix, "/api/"},
UseSubdomains: false,
}
// TODO(steb): Configurable
......
......@@ -2,6 +2,7 @@ package corehttp
import (
"context"
"fmt"
"net"
"net/http"
"strings"
......@@ -21,51 +22,118 @@ func HostnameOption() ServeOption {
ctx, cancel := context.WithCancel(n.Context())
defer cancel()
// Unfortunately, many (well, ipfs.io) gateways use
// DNSLink so if we blindly rewrite with DNSLink, we'll
// break /ipfs links.
//
// We fix this by maintaining a list of known gateways
// and the paths that they serve "gateway" content on.
// That way, we can use DNSLink for everything else.
//
// TODO: We wouldn't need _any_ of this if we
// supported transparent symlink resolution on
// the gateway. If we had that, such gateways could add
// symlinks to `/ipns`, `/ipfs`, `/api`, etc. to their
// home directories. That way, `/ipfs/QmA/ipfs/QmB`
// would "just work". Should we try this?
// Is this one of our "known gateways"?
if gw, ok := KnownGateways[r.Host]; ok {
// This is a known gateway but we're not using
// the subdomain feature.
// Does this gateway _handle_ this path?
if hasPrefix(r.URL.Path, gw.PathPrefixes...) {
// It does.
// Does this gateway use subdomains?
if gw.UseSubdomains {
// Yes, redirect if applicable (pretty much everything except `/api`).
if newURL, ok := toSubdomainURL(r.Host, r.URL.Path); ok {
http.Redirect(
w, r, newURL, http.StatusMovedPermanently,
)
return
}
}
childMux.ServeHTTP(w, r)
return
}
} else if host, pathPrefix, ok := parseSubdomains(r.Host); ok {
if gw, ok := KnownGateways[host]; ok && gw.SupportsSubdomains {
// Always handle this with the gateway.
// We don't care if it's one of the
// valid path-prefixes.
// Looks like we're using subdomains.
// Again, is this a known gateway that supports subdomains?
if gw, ok := KnownGateways[host]; ok && gw.UseSubdomains {
// Yes, serve the request (and rewrite the path to not use subdomains).
r.URL.Path = pathPrefix + r.URL.Path
childMux.ServeHTTP(w, r)
return
}
}
// We don't have a known gateway. Fallback on dnslink.
host := strings.SplitN(r.Host, ":", 2)[0]
if len(host) == 0 || !isd.IsDomain(host) {
childMux.ServeHTTP(w, r)
return
}
if len(host) > 0 && isd.IsDomain(host) {
name := "/ipns/" + host
_, err := n.Namesys.Resolve(ctx, name, nsopts.Depth(1))
if err == nil || err == namesys.ErrResolveRecursion {
// The domain supports dnslink, rewrite.
r.URL.Path = name + r.URL.Path
}
} // else, just treat it as a gateway, I guess.
name := "/ipns/" + host
_, err := n.Namesys.Resolve(ctx, name, nsopts.Depth(1))
if err == nil || err == namesys.ErrResolveRecursion {
r.URL.Path = name + r.URL.Path
}
childMux.ServeHTTP(w, r)
})
return childMux, nil
}
}
func isSubdomainNamespace(ns string) bool {
switch ns {
case "ipfs", "ipns", "p2p", "ipld":
return true
default:
return false
}
}
// Parses a subdomain-based URL and returns it's components
func parseSubdomains(host string) (newHost, pathPrefix string, ok bool) {
parts := strings.SplitN(host, ".", 3)
if len(parts) < 3 {
if len(parts) < 3 || !isSubdomainNamespace(parts[1]) {
return "", "", false
}
switch parts[1] {
case "ipfs", "ipns", "p2p":
return parts[2], "/" + parts[1] + "/" + parts[0], true
return parts[2], "/" + parts[1] + "/" + parts[0], true
}
// Converts a host/path to a subdomain-based URL, if applicable.
func toSubdomainURL(host, path string) (url string, ok bool) {
parts := strings.SplitN(path, "/", 4)
var ns, object, rest string
switch len(parts) {
case 4:
rest = parts[3]
fallthrough
case 3:
ns = parts[1]
object = parts[2]
default:
return "", false
}
if !isSubdomainNamespace(ns) {
return "", false
}
return "", "", false
return fmt.Sprintf(
"http://%s.%s.%s/%s",
object,
ns,
host,
rest,
), true
}
func hasPrefix(s string, prefixes ...string) bool {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论