Skip to content

Commit 3ed7796

Browse files
committed
implement basic redirect
1 parent da35610 commit 3ed7796

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

core/corehttp/gateway_handler.go

+107
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package corehttp
22

33
import (
4+
"bufio"
45
"context"
56
"fmt"
67
"html/template"
@@ -270,6 +271,18 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
270271
return
271272
}
272273

274+
redirects, err := i.searchUpTreeForRedirects(r, parsedPath)
275+
if err == nil {
276+
redirected, err := i.redirect(w, r, redirects)
277+
if err != nil {
278+
// FIXME what to do here with errors ...
279+
}
280+
281+
if redirected {
282+
return
283+
}
284+
}
285+
273286
// Resolve path to the final DAG node for the ETag
274287
resolvedPath, err := i.api.ResolvePath(r.Context(), parsedPath)
275288
switch err {
@@ -497,6 +510,81 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
497510
}
498511
}
499512

513+
type redirLine struct {
514+
matcher string
515+
to string
516+
code int
517+
}
518+
519+
type redirs []redirLine
520+
521+
func newRedirs(f io.Reader) *redirs {
522+
ret := redirs{}
523+
scanner := bufio.NewScanner(f)
524+
scanner.Split(bufio.ScanLines)
525+
for scanner.Scan() {
526+
groups := strings.Split(scanner.Text(), " ")
527+
if len(groups) >= 2 {
528+
matcher := groups[0]
529+
to := groups[1]
530+
// default to 302 (temporary redirect)
531+
code := 302
532+
if len(groups) >= 3 {
533+
c, err := strconv.Atoi(groups[2])
534+
if err == nil {
535+
code = c
536+
}
537+
}
538+
ret = append(ret, redirLine{matcher, to, code})
539+
}
540+
}
541+
542+
return &ret
543+
}
544+
545+
// returns "" if no redir
546+
func (r redirs) search(path string) (string, int) {
547+
for _, rdir := range r {
548+
if path == rdir.matcher {
549+
return rdir.to, rdir.code
550+
}
551+
}
552+
553+
return "", 0
554+
}
555+
556+
func (i *gatewayHandler) redirect(w http.ResponseWriter, r *http.Request, path ipath.Resolved) (bool, error) {
557+
node, err := i.api.Unixfs().Get(r.Context(), path)
558+
if err != nil {
559+
return false, fmt.Errorf("could not get redirects file: %v", err)
560+
}
561+
562+
defer node.Close()
563+
564+
f, ok := node.(files.File)
565+
566+
if !ok {
567+
return false, fmt.Errorf("redirect, could not convert node to file")
568+
}
569+
570+
redirs := newRedirs(f)
571+
572+
// extract "file" part of URL, typically the part after /ipfs/CID/...
573+
g := strings.Split(r.URL.Path, "/")
574+
575+
if len(g) > 3 {
576+
filePartPath := strings.Join(g[3:], "/")
577+
578+
to, code := redirs.search(filePartPath)
579+
if code > 0 {
580+
http.Redirect(w, r, to, code)
581+
return true, nil
582+
}
583+
}
584+
585+
return false, nil
586+
}
587+
500588
func (i *gatewayHandler) serveFile(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, file files.File) {
501589
size, err := file.Size()
502590
if err != nil {
@@ -790,6 +878,25 @@ func getFilename(s string) string {
790878
return gopath.Base(s)
791879
}
792880

881+
func (i *gatewayHandler) searchUpTreeForRedirects(r *http.Request, parsedPath ipath.Path) (ipath.Resolved, error) {
882+
pathComponents := strings.Split(parsedPath.String(), "/")
883+
884+
for idx := len(pathComponents); idx >= 3; idx-- {
885+
rdir := gopath.Join(append(pathComponents[0:idx], "_redirects")...)
886+
rdirPath := ipath.New("/" + rdir)
887+
if rdirPath.IsValid() != nil {
888+
break
889+
}
890+
resolvedPath, err := i.api.ResolvePath(r.Context(), rdirPath)
891+
if err != nil {
892+
continue
893+
}
894+
return resolvedPath, nil
895+
}
896+
897+
return nil, fmt.Errorf("no redirects in any parent folder")
898+
}
899+
793900
func (i *gatewayHandler) searchUpTreeFor404(r *http.Request, parsedPath ipath.Path) (ipath.Resolved, string, error) {
794901
filename404, ctype, err := preferred404Filename(r.Header.Values("Accept"))
795902
if err != nil {

0 commit comments

Comments
 (0)