-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrootpath.go
More file actions
205 lines (170 loc) · 4.75 KB
/
rootpath.go
File metadata and controls
205 lines (170 loc) · 4.75 KB
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// domain rootpath Handler support for Martini and TypePress
// 根据域名匹配相关目录
package rootpath
import (
"net/http"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
"github.com/go-martini/martini"
"github.com/typepress/types"
)
const (
// flags of category for RootPath.Flag
FStatic = 1 << iota // category name is _static
FContent // category name is _content
FTemplate // category name is _template
fCategoryCount = iota
FAll = FStatic | FContent | FTemplate
// flags of target rootpath for RootPath.Flag
FDontJoinCategoryName = 0x40000000
FDontJoinDomain = 0x80000000
)
var (
pathedType reflect.Type = reflect.TypeOf(rootPathed(true))
categoryType = [fCategoryCount]func(string) interface{}{
func(s string) interface{} { return http.Dir(s) },
func(s string) interface{} { return types.ContentDir(s) },
func(s string) interface{} { return types.TemplateDir(s) },
}
categoryFlag = [fCategoryCount]int{
FStatic, FContent, FTemplate,
}
categoryName = [fCategoryCount]string{
"_static", "_content", "_template",
}
)
type rootPathed bool
/**
返回多域名路径设置 handler. 依据 roots 匹配 Request.Host.
匹配成功设置相应路径, 无匹配时依据 statusCode 进行操作.
statusCode:
- 0 无操作
- 其他 WriteHeader(statusCode)
*/
func Handler(statusCode int, root ...RootPath) martini.Handler {
lock := sync.RWMutex{}
cacheDir := map[string]int{} // save file exist status for "?" pattern
// clone
root = append([]RootPath{}, root...)
for i, _ := range root {
root[i].CategoryName = append([]string{}, root[i].CategoryName...)
l := len(root[i].CategoryName)
if l < fCategoryCount {
root[i].CategoryName = append(root[i].CategoryName, categoryName[l:]...)
}
}
return func(res http.ResponseWriter, req *http.Request, c martini.Context) {
v := c.Get(pathedType)
var pathed bool
if v.IsValid() && v.Bool() {
return
}
host := strings.SplitN(req.Host, ":", 2)[0]
for _, rp := range root {
ok, prefix := rp.Match(host)
if !ok {
continue
}
for i := 0; i < fCategoryCount; i++ {
if rp.Flag&categoryFlag[i] == 0 {
continue
}
var domain, category string
var exist int
if 0 == FDontJoinDomain&rp.Flag {
if len(prefix) == 0 {
domain = rp.Domain
} else {
domain = prefix + "." + rp.Domain
}
}
if 0 == FDontJoinCategoryName&rp.Flag {
category = rp.CategoryName[i]
}
dir := filepath.Join(rp.Root, domain, category)
if rp.Pattern == "?" {
lock.RLock()
exist = cacheDir[dir]
lock.RUnlock()
if exist == 0 {
_, err := os.Stat(dir)
if err == nil {
exist = 1
} else {
exist = -1
}
lock.Lock()
cacheDir[dir] = exist // dir exist
lock.Unlock()
}
if exist == -1 {
if 0 == FDontJoinDomain&rp.Flag {
dir = filepath.Join(rp.Root, rp.Domain, category)
} else {
dir = filepath.Join(rp.Root, category)
}
}
}
c.Map(categoryType[i](dir))
pathed = true
}
if pathed {
c.Map(rootPathed(true))
return
}
}
if statusCode != 0 {
res.WriteHeader(statusCode)
return
}
}
}
/**
RootPath 使用简单的规则来确定域名根目录
Pattern: 域名匹配规则, "?" 规则多了一次目录检查, 是否 join example.com 部分由 FDontJoinDomain 标记决定
- "" 完全匹配 example.com , Root/[example.com]/[category]
- "." 泛域名相同目录 [foo.]example.com , Root/[example.com]/[category]
- "*" 泛域名独立目录 [foo.]example.com , Root/[foo.][example.com]/[category]
- "?" 泛域名泛目录 [foo.]example.com , Root/[foo.][example.com]/[category] 或 Root/[example.com]/[category]
Domain: 域名
Root: 基本目录
Flag: 根目录的分类标记, 参见 FStatic 等常量, 0 特指所有类型
CategoryName: 自定义 category 的名字, 顺序对应 static,content,template. 缺省使用 "_static","_content","_template"
*/
type RootPath struct {
Pattern string
Domain string
Root string
Flag int
CategoryName []string
}
/**
匹配 host, 要求 host 不包含 port 部分. 返回:
是否匹配成功, 子域名部分(尾部不包括".")
*/
func (r RootPath) Match(host string) (bool, string) {
if !strings.HasSuffix(host, r.Domain) {
return false, ""
}
last := len(host) - len(r.Domain)
switch r.Pattern {
case "":
return last == 0, ""
case ".":
return last == 0 || host[last-1] == '.', ""
case "*":
if last > 1 && host[last-1] == '.' {
return true, string(host[:last-1])
}
return last == 0, ""
case "?":
if last > 1 && host[last-1] == '.' {
return true, string(host[:last-1])
}
return last == 0, ""
}
return false, ""
}