-
Notifications
You must be signed in to change notification settings - Fork 0
/
handler.go
101 lines (85 loc) · 2.45 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package main
import (
"bytes"
"fmt"
"os"
"path/filepath"
fhttp "github.com/valyala/fasthttp"
)
// HandleFastHTTP implements fasthttp.Handler for Config.
func (cfg *Config) HandleFastHTTP(ctx *fhttp.RequestCtx) {
var (
uri = ctx.URI()
path = uri.Path()
lseg = uri.LastPathSegment()
)
// Perform trailing slash redirection if necessary.
if len(path) > 1 {
switch cfg.TrailingSlash {
case "true", "1":
if len(lseg) > 0 && bytes.IndexRune(lseg, '.') == -1 {
path = append(path, '/')
RedirectRel(ctx, path) // redirect with trailing slash
return
}
case "false", "0":
if len(lseg) == 0 { // path ends in a '/'
path = path[:len(path)-1] // pop last element
RedirectRel(ctx, path) // redirect without trailing slash
return
}
}
}
// Interpret path to target file.
fpath := filepath.Join(cfg.RootDir, string(path))
if len(lseg) == 0 || bytes.IndexRune(lseg, '.') == -1 {
fpath = filepath.Join(fpath, DefaultFile)
}
// Return 404 if target file does not exist.
if _, err := os.Stat(fpath); err != nil {
if os.IsNotExist(err) {
ctx.SetStatusCode(fhttp.StatusNotFound)
if _, err := os.Stat(cfg.NotFound); err != nil {
if os.IsNotExist(err) {
ctx.SetContentType(NotFoundType)
ctx.WriteString(NotFoundBody)
return
}
ctx.Response.SetStatusCode(fhttp.StatusInternalServerError)
fmt.Fprintf(ctx, "Failed to check 404 file: %v", err)
return
}
ctx.SendFile(cfg.NotFound)
return
}
ctx.Response.SetStatusCode(fhttp.StatusInternalServerError)
fmt.Fprintf(ctx, "Failed to check file: %v", err)
return
}
ctx.SendFile(fpath)
}
const strProto = "proto="
// RedirectRel performs a relative redirect, taking the X-Forwarded-Proto
// and Forwarded headers into account.
func RedirectRel(ctx *fhttp.RequestCtx, path []byte) {
header := ctx.Request.Header.Peek("Forwarded")
if index := bytes.Index(header, []byte(strProto)); index != -1 {
header = header[index+len(strProto):]
if index = bytes.IndexRune(header, ';'); index != -1 {
header = header[:index]
}
}
if len(header) == 0 {
header = ctx.Request.Header.Peek("X-Forwarded-Proto")
}
var proto []byte
if len(header) > 0 {
header = bytes.TrimSpace(header)
proto = append(header, "://"...)
} else {
proto = append(ctx.URI().Scheme(), "://"...)
}
uri := append(proto, ctx.URI().Host()...)
ctx.Response.Header.SetCanonical([]byte("Location"), append(uri, path...))
ctx.SetStatusCode(fhttp.StatusMovedPermanently)
}