@@ -2,6 +2,7 @@ package corehttp
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"net"
6
7
"net/http"
7
8
"strings"
@@ -21,51 +22,118 @@ func HostnameOption() ServeOption {
21
22
ctx , cancel := context .WithCancel (n .Context ())
22
23
defer cancel ()
23
24
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
+
24
40
// Is this one of our "known gateways"?
25
41
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?
26
46
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
+ }
27
59
childMux .ServeHTTP (w , r )
28
60
return
29
61
}
30
62
} 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 {
35
67
68
+ // Yes, serve the request (and rewrite the path to not use subdomains).
36
69
r .URL .Path = pathPrefix + r .URL .Path
37
70
childMux .ServeHTTP (w , r )
38
71
return
39
72
}
40
73
}
41
74
75
+ // We don't have a known gateway. Fallback on dnslink.
42
76
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.
47
85
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
- }
53
86
childMux .ServeHTTP (w , r )
54
87
})
55
88
return childMux , nil
56
89
}
57
90
}
58
91
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
59
102
func parseSubdomains (host string ) (newHost , pathPrefix string , ok bool ) {
60
103
parts := strings .SplitN (host , "." , 3 )
61
- if len (parts ) < 3 {
104
+ if len (parts ) < 3 || ! isSubdomainNamespace ( parts [ 1 ]) {
62
105
return "" , "" , false
63
106
}
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
67
128
}
68
- return "" , "" , false
129
+
130
+ return fmt .Sprintf (
131
+ "http://%s.%s.%s/%s" ,
132
+ object ,
133
+ ns ,
134
+ host ,
135
+ rest ,
136
+ ), true
69
137
}
70
138
71
139
func hasPrefix (s string , prefixes ... string ) bool {
0 commit comments