正在显示
21 个修改的文件
包含
4895 行增加
和
0 行删除
vendor/github.com/astaxie/beego/.gitignore
0 → 100644
vendor/github.com/astaxie/beego/.travis.yml
0 → 100644
1 | +language: go | ||
2 | + | ||
3 | +go: | ||
4 | + - "1.9.2" | ||
5 | + - "1.10.3" | ||
6 | +services: | ||
7 | + - redis-server | ||
8 | + - mysql | ||
9 | + - postgresql | ||
10 | + - memcached | ||
11 | +env: | ||
12 | + - ORM_DRIVER=sqlite3 ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db | ||
13 | + - ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable" | ||
14 | +before_install: | ||
15 | + - git clone git://github.com/ideawu/ssdb.git | ||
16 | + - cd ssdb | ||
17 | + - make | ||
18 | + - cd .. | ||
19 | +install: | ||
20 | + - go get github.com/lib/pq | ||
21 | + - go get github.com/go-sql-driver/mysql | ||
22 | + - go get github.com/mattn/go-sqlite3 | ||
23 | + - go get github.com/bradfitz/gomemcache/memcache | ||
24 | + - go get github.com/gomodule/redigo/redis | ||
25 | + - go get github.com/beego/x2j | ||
26 | + - go get github.com/couchbase/go-couchbase | ||
27 | + - go get github.com/beego/goyaml2 | ||
28 | + - go get gopkg.in/yaml.v2 | ||
29 | + - go get github.com/belogik/goes | ||
30 | + - go get github.com/siddontang/ledisdb/config | ||
31 | + - go get github.com/siddontang/ledisdb/ledis | ||
32 | + - go get github.com/ssdb/gossdb/ssdb | ||
33 | + - go get github.com/cloudflare/golz4 | ||
34 | + - go get github.com/gogo/protobuf/proto | ||
35 | + - go get github.com/Knetic/govaluate | ||
36 | + - go get github.com/casbin/casbin | ||
37 | + - go get -u honnef.co/go/tools/cmd/gosimple | ||
38 | + - go get -u github.com/mdempsky/unconvert | ||
39 | + - go get -u github.com/gordonklaus/ineffassign | ||
40 | + - go get -u github.com/golang/lint/golint | ||
41 | + - go get -u github.com/go-redis/redis | ||
42 | +before_script: | ||
43 | + - psql --version | ||
44 | + - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" | ||
45 | + - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi" | ||
46 | + - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi" | ||
47 | + - sh -c "if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi" | ||
48 | + - sh -c "if [ $(go version) == *1.[5-9]* ]; then go tool vet .; fi" | ||
49 | + - mkdir -p res/var | ||
50 | + - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d | ||
51 | +after_script: | ||
52 | + -killall -w ssdb-server | ||
53 | + - rm -rf ./res/var/* | ||
54 | +script: | ||
55 | + - go test -v ./... | ||
56 | + - gosimple -ignore "$(cat .gosimpleignore)" $(go list ./... | grep -v /vendor/) | ||
57 | + - unconvert $(go list ./... | grep -v /vendor/) | ||
58 | + - ineffassign . | ||
59 | + - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s | ||
60 | + - golint ./... | ||
61 | +addons: | ||
62 | + postgresql: "9.4" |
1 | +# Contributing to beego | ||
2 | + | ||
3 | +beego is an open source project. | ||
4 | + | ||
5 | +It is the work of hundreds of contributors. We appreciate your help! | ||
6 | + | ||
7 | +Here are instructions to get you started. They are probably not perfect, | ||
8 | +please let us know if anything feels wrong or incomplete. | ||
9 | + | ||
10 | +## Contribution guidelines | ||
11 | + | ||
12 | +### Pull requests | ||
13 | + | ||
14 | +First of all. beego follow the gitflow. So please send you pull request | ||
15 | +to **develop** branch. We will close the pull request to master branch. | ||
16 | + | ||
17 | +We are always happy to receive pull requests, and do our best to | ||
18 | +review them as fast as possible. Not sure if that typo is worth a pull | ||
19 | +request? Do it! We will appreciate it. | ||
20 | + | ||
21 | +If your pull request is not accepted on the first try, don't be | ||
22 | +discouraged! Sometimes we can make a mistake, please do more explaining | ||
23 | +for us. We will appreciate it. | ||
24 | + | ||
25 | +We're trying very hard to keep beego simple and fast. We don't want it | ||
26 | +to do everything for everybody. This means that we might decide against | ||
27 | +incorporating a new feature. But we will give you some advice on how to | ||
28 | +do it in other way. | ||
29 | + | ||
30 | +### Create issues | ||
31 | + | ||
32 | +Any significant improvement should be documented as [a GitHub | ||
33 | +issue](https://github.com/astaxie/beego/issues) before anybody | ||
34 | +starts working on it. | ||
35 | + | ||
36 | +Also when filing an issue, make sure to answer these five questions: | ||
37 | + | ||
38 | +- What version of beego are you using (bee version)? | ||
39 | +- What operating system and processor architecture are you using? | ||
40 | +- What did you do? | ||
41 | +- What did you expect to see? | ||
42 | +- What did you see instead? | ||
43 | + | ||
44 | +### but check existing issues and docs first! | ||
45 | + | ||
46 | +Please take a moment to check that an issue doesn't already exist | ||
47 | +documenting your bug report or improvement proposal. If it does, it | ||
48 | +never hurts to add a quick "+1" or "I have this problem too". This will | ||
49 | +help prioritize the most common problems and requests. | ||
50 | + | ||
51 | +Also if you don't know how to use it. please make sure you have read though | ||
52 | +the docs in http://beego.me/docs |
vendor/github.com/astaxie/beego/LICENSE
0 → 100644
1 | +Copyright 2014 astaxie | ||
2 | + | ||
3 | +Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +you may not use this file except in compliance with the License. | ||
5 | +You may obtain a copy of the License at | ||
6 | + | ||
7 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | + | ||
9 | +Unless required by applicable law or agreed to in writing, software | ||
10 | +distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +See the License for the specific language governing permissions and | ||
13 | +limitations under the License. |
vendor/github.com/astaxie/beego/README.md
0 → 100644
1 | +# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) [![Go Report Card](https://goreportcard.com/badge/github.com/astaxie/beego)](https://goreportcard.com/report/github.com/astaxie/beego) | ||
2 | + | ||
3 | + | ||
4 | +beego is used for rapid development of RESTful APIs, web apps and backend services in Go. | ||
5 | +It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. | ||
6 | + | ||
7 | +###### More info at [beego.me](http://beego.me). | ||
8 | + | ||
9 | +## Quick Start | ||
10 | + | ||
11 | +#### Download and install | ||
12 | + | ||
13 | + go get github.com/astaxie/beego | ||
14 | + | ||
15 | +#### Create file `hello.go` | ||
16 | +```go | ||
17 | +package main | ||
18 | + | ||
19 | +import "github.com/astaxie/beego" | ||
20 | + | ||
21 | +func main(){ | ||
22 | + beego.Run() | ||
23 | +} | ||
24 | +``` | ||
25 | +#### Build and run | ||
26 | + | ||
27 | + go build hello.go | ||
28 | + ./hello | ||
29 | + | ||
30 | +#### Go to [http://localhost:8080](http://localhost:8080) | ||
31 | + | ||
32 | +Congratulations! You've just built your first **beego** app. | ||
33 | + | ||
34 | +###### Please see [Documentation](http://beego.me/docs) for more. | ||
35 | + | ||
36 | +## Features | ||
37 | + | ||
38 | +* RESTful support | ||
39 | +* MVC architecture | ||
40 | +* Modularity | ||
41 | +* Auto API documents | ||
42 | +* Annotation router | ||
43 | +* Namespace | ||
44 | +* Powerful development tools | ||
45 | +* Full stack for Web & API | ||
46 | + | ||
47 | +## Documentation | ||
48 | + | ||
49 | +* [English](http://beego.me/docs/intro/) | ||
50 | +* [中文文档](http://beego.me/docs/intro/) | ||
51 | +* [Русский](http://beego.me/docs/intro/) | ||
52 | + | ||
53 | +## Community | ||
54 | + | ||
55 | +* [http://beego.me/community](http://beego.me/community) | ||
56 | +* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232) | ||
57 | + | ||
58 | +## License | ||
59 | + | ||
60 | +beego source code is licensed under the Apache Licence, Version 2.0 | ||
61 | +(http://www.apache.org/licenses/LICENSE-2.0.html). |
vendor/github.com/astaxie/beego/admin.go
0 → 100644
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package beego | ||
16 | + | ||
17 | +import ( | ||
18 | + "bytes" | ||
19 | + "encoding/json" | ||
20 | + "fmt" | ||
21 | + "net/http" | ||
22 | + "os" | ||
23 | + "text/template" | ||
24 | + "time" | ||
25 | + | ||
26 | + "reflect" | ||
27 | + | ||
28 | + "github.com/astaxie/beego/grace" | ||
29 | + "github.com/astaxie/beego/logs" | ||
30 | + "github.com/astaxie/beego/toolbox" | ||
31 | + "github.com/astaxie/beego/utils" | ||
32 | +) | ||
33 | + | ||
34 | +// BeeAdminApp is the default adminApp used by admin module. | ||
35 | +var beeAdminApp *adminApp | ||
36 | + | ||
37 | +// FilterMonitorFunc is default monitor filter when admin module is enable. | ||
38 | +// if this func returns, admin module records qbs for this request by condition of this function logic. | ||
39 | +// usage: | ||
40 | +// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool { | ||
41 | +// if method == "POST" { | ||
42 | +// return false | ||
43 | +// } | ||
44 | +// if t.Nanoseconds() < 100 { | ||
45 | +// return false | ||
46 | +// } | ||
47 | +// if strings.HasPrefix(requestPath, "/astaxie") { | ||
48 | +// return false | ||
49 | +// } | ||
50 | +// return true | ||
51 | +// } | ||
52 | +// beego.FilterMonitorFunc = MyFilterMonitor. | ||
53 | +var FilterMonitorFunc func(string, string, time.Duration, string, int) bool | ||
54 | + | ||
55 | +func init() { | ||
56 | + beeAdminApp = &adminApp{ | ||
57 | + routers: make(map[string]http.HandlerFunc), | ||
58 | + } | ||
59 | + beeAdminApp.Route("/", adminIndex) | ||
60 | + beeAdminApp.Route("/qps", qpsIndex) | ||
61 | + beeAdminApp.Route("/prof", profIndex) | ||
62 | + beeAdminApp.Route("/healthcheck", healthcheck) | ||
63 | + beeAdminApp.Route("/task", taskStatus) | ||
64 | + beeAdminApp.Route("/listconf", listConf) | ||
65 | + FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true } | ||
66 | +} | ||
67 | + | ||
68 | +// AdminIndex is the default http.Handler for admin module. | ||
69 | +// it matches url pattern "/". | ||
70 | +func adminIndex(rw http.ResponseWriter, r *http.Request) { | ||
71 | + execTpl(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl) | ||
72 | +} | ||
73 | + | ||
74 | +// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter. | ||
75 | +// it's registered with url pattern "/qbs" in admin module. | ||
76 | +func qpsIndex(rw http.ResponseWriter, r *http.Request) { | ||
77 | + data := make(map[interface{}]interface{}) | ||
78 | + data["Content"] = toolbox.StatisticsMap.GetMap() | ||
79 | + | ||
80 | + // do html escape before display path, avoid xss | ||
81 | + if content, ok := (data["Content"]).(map[string]interface{}); ok { | ||
82 | + if resultLists, ok := (content["Data"]).([][]string); ok { | ||
83 | + for i := range resultLists { | ||
84 | + if len(resultLists[i]) > 0 { | ||
85 | + resultLists[i][0] = template.HTMLEscapeString(resultLists[i][0]) | ||
86 | + } | ||
87 | + } | ||
88 | + } | ||
89 | + } | ||
90 | + | ||
91 | + execTpl(rw, data, qpsTpl, defaultScriptsTpl) | ||
92 | +} | ||
93 | + | ||
94 | +// ListConf is the http.Handler of displaying all beego configuration values as key/value pair. | ||
95 | +// it's registered with url pattern "/listconf" in admin module. | ||
96 | +func listConf(rw http.ResponseWriter, r *http.Request) { | ||
97 | + r.ParseForm() | ||
98 | + command := r.Form.Get("command") | ||
99 | + if command == "" { | ||
100 | + rw.Write([]byte("command not support")) | ||
101 | + return | ||
102 | + } | ||
103 | + | ||
104 | + data := make(map[interface{}]interface{}) | ||
105 | + switch command { | ||
106 | + case "conf": | ||
107 | + m := make(map[string]interface{}) | ||
108 | + list("BConfig", BConfig, m) | ||
109 | + m["AppConfigPath"] = appConfigPath | ||
110 | + m["AppConfigProvider"] = appConfigProvider | ||
111 | + tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) | ||
112 | + tmpl = template.Must(tmpl.Parse(configTpl)) | ||
113 | + tmpl = template.Must(tmpl.Parse(defaultScriptsTpl)) | ||
114 | + | ||
115 | + data["Content"] = m | ||
116 | + | ||
117 | + tmpl.Execute(rw, data) | ||
118 | + | ||
119 | + case "router": | ||
120 | + content := PrintTree() | ||
121 | + content["Fields"] = []string{ | ||
122 | + "Router Pattern", | ||
123 | + "Methods", | ||
124 | + "Controller", | ||
125 | + } | ||
126 | + data["Content"] = content | ||
127 | + data["Title"] = "Routers" | ||
128 | + execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl) | ||
129 | + case "filter": | ||
130 | + var ( | ||
131 | + content = map[string]interface{}{ | ||
132 | + "Fields": []string{ | ||
133 | + "Router Pattern", | ||
134 | + "Filter Function", | ||
135 | + }, | ||
136 | + } | ||
137 | + filterTypes = []string{} | ||
138 | + filterTypeData = make(map[string]interface{}) | ||
139 | + ) | ||
140 | + | ||
141 | + if BeeApp.Handlers.enableFilter { | ||
142 | + var filterType string | ||
143 | + for k, fr := range map[int]string{ | ||
144 | + BeforeStatic: "Before Static", | ||
145 | + BeforeRouter: "Before Router", | ||
146 | + BeforeExec: "Before Exec", | ||
147 | + AfterExec: "After Exec", | ||
148 | + FinishRouter: "Finish Router"} { | ||
149 | + if bf := BeeApp.Handlers.filters[k]; len(bf) > 0 { | ||
150 | + filterType = fr | ||
151 | + filterTypes = append(filterTypes, filterType) | ||
152 | + resultList := new([][]string) | ||
153 | + for _, f := range bf { | ||
154 | + var result = []string{ | ||
155 | + f.pattern, | ||
156 | + utils.GetFuncName(f.filterFunc), | ||
157 | + } | ||
158 | + *resultList = append(*resultList, result) | ||
159 | + } | ||
160 | + filterTypeData[filterType] = resultList | ||
161 | + } | ||
162 | + } | ||
163 | + } | ||
164 | + | ||
165 | + content["Data"] = filterTypeData | ||
166 | + content["Methods"] = filterTypes | ||
167 | + | ||
168 | + data["Content"] = content | ||
169 | + data["Title"] = "Filters" | ||
170 | + execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl) | ||
171 | + default: | ||
172 | + rw.Write([]byte("command not support")) | ||
173 | + } | ||
174 | +} | ||
175 | + | ||
176 | +func list(root string, p interface{}, m map[string]interface{}) { | ||
177 | + pt := reflect.TypeOf(p) | ||
178 | + pv := reflect.ValueOf(p) | ||
179 | + if pt.Kind() == reflect.Ptr { | ||
180 | + pt = pt.Elem() | ||
181 | + pv = pv.Elem() | ||
182 | + } | ||
183 | + for i := 0; i < pv.NumField(); i++ { | ||
184 | + var key string | ||
185 | + if root == "" { | ||
186 | + key = pt.Field(i).Name | ||
187 | + } else { | ||
188 | + key = root + "." + pt.Field(i).Name | ||
189 | + } | ||
190 | + if pv.Field(i).Kind() == reflect.Struct { | ||
191 | + list(key, pv.Field(i).Interface(), m) | ||
192 | + } else { | ||
193 | + m[key] = pv.Field(i).Interface() | ||
194 | + } | ||
195 | + } | ||
196 | +} | ||
197 | + | ||
198 | +// PrintTree prints all registered routers. | ||
199 | +func PrintTree() map[string]interface{} { | ||
200 | + var ( | ||
201 | + content = map[string]interface{}{} | ||
202 | + methods = []string{} | ||
203 | + methodsData = make(map[string]interface{}) | ||
204 | + ) | ||
205 | + for method, t := range BeeApp.Handlers.routers { | ||
206 | + | ||
207 | + resultList := new([][]string) | ||
208 | + | ||
209 | + printTree(resultList, t) | ||
210 | + | ||
211 | + methods = append(methods, method) | ||
212 | + methodsData[method] = resultList | ||
213 | + } | ||
214 | + | ||
215 | + content["Data"] = methodsData | ||
216 | + content["Methods"] = methods | ||
217 | + return content | ||
218 | +} | ||
219 | + | ||
220 | +func printTree(resultList *[][]string, t *Tree) { | ||
221 | + for _, tr := range t.fixrouters { | ||
222 | + printTree(resultList, tr) | ||
223 | + } | ||
224 | + if t.wildcard != nil { | ||
225 | + printTree(resultList, t.wildcard) | ||
226 | + } | ||
227 | + for _, l := range t.leaves { | ||
228 | + if v, ok := l.runObject.(*ControllerInfo); ok { | ||
229 | + if v.routerType == routerTypeBeego { | ||
230 | + var result = []string{ | ||
231 | + v.pattern, | ||
232 | + fmt.Sprintf("%s", v.methods), | ||
233 | + v.controllerType.String(), | ||
234 | + } | ||
235 | + *resultList = append(*resultList, result) | ||
236 | + } else if v.routerType == routerTypeRESTFul { | ||
237 | + var result = []string{ | ||
238 | + v.pattern, | ||
239 | + fmt.Sprintf("%s", v.methods), | ||
240 | + "", | ||
241 | + } | ||
242 | + *resultList = append(*resultList, result) | ||
243 | + } else if v.routerType == routerTypeHandler { | ||
244 | + var result = []string{ | ||
245 | + v.pattern, | ||
246 | + "", | ||
247 | + "", | ||
248 | + } | ||
249 | + *resultList = append(*resultList, result) | ||
250 | + } | ||
251 | + } | ||
252 | + } | ||
253 | +} | ||
254 | + | ||
255 | +// ProfIndex is a http.Handler for showing profile command. | ||
256 | +// it's in url pattern "/prof" in admin module. | ||
257 | +func profIndex(rw http.ResponseWriter, r *http.Request) { | ||
258 | + r.ParseForm() | ||
259 | + command := r.Form.Get("command") | ||
260 | + if command == "" { | ||
261 | + return | ||
262 | + } | ||
263 | + | ||
264 | + var ( | ||
265 | + format = r.Form.Get("format") | ||
266 | + data = make(map[interface{}]interface{}) | ||
267 | + result bytes.Buffer | ||
268 | + ) | ||
269 | + toolbox.ProcessInput(command, &result) | ||
270 | + data["Content"] = result.String() | ||
271 | + | ||
272 | + if format == "json" && command == "gc summary" { | ||
273 | + dataJSON, err := json.Marshal(data) | ||
274 | + if err != nil { | ||
275 | + http.Error(rw, err.Error(), http.StatusInternalServerError) | ||
276 | + return | ||
277 | + } | ||
278 | + | ||
279 | + rw.Header().Set("Content-Type", "application/json") | ||
280 | + rw.Write(dataJSON) | ||
281 | + return | ||
282 | + } | ||
283 | + | ||
284 | + data["Title"] = command | ||
285 | + defaultTpl := defaultScriptsTpl | ||
286 | + if command == "gc summary" { | ||
287 | + defaultTpl = gcAjaxTpl | ||
288 | + } | ||
289 | + execTpl(rw, data, profillingTpl, defaultTpl) | ||
290 | +} | ||
291 | + | ||
292 | +// Healthcheck is a http.Handler calling health checking and showing the result. | ||
293 | +// it's in "/healthcheck" pattern in admin module. | ||
294 | +func healthcheck(rw http.ResponseWriter, req *http.Request) { | ||
295 | + var ( | ||
296 | + result []string | ||
297 | + data = make(map[interface{}]interface{}) | ||
298 | + resultList = new([][]string) | ||
299 | + content = map[string]interface{}{ | ||
300 | + "Fields": []string{"Name", "Message", "Status"}, | ||
301 | + } | ||
302 | + ) | ||
303 | + | ||
304 | + for name, h := range toolbox.AdminCheckList { | ||
305 | + if err := h.Check(); err != nil { | ||
306 | + result = []string{ | ||
307 | + "error", | ||
308 | + name, | ||
309 | + err.Error(), | ||
310 | + } | ||
311 | + } else { | ||
312 | + result = []string{ | ||
313 | + "success", | ||
314 | + name, | ||
315 | + "OK", | ||
316 | + } | ||
317 | + } | ||
318 | + *resultList = append(*resultList, result) | ||
319 | + } | ||
320 | + | ||
321 | + content["Data"] = resultList | ||
322 | + data["Content"] = content | ||
323 | + data["Title"] = "Health Check" | ||
324 | + execTpl(rw, data, healthCheckTpl, defaultScriptsTpl) | ||
325 | +} | ||
326 | + | ||
327 | +// TaskStatus is a http.Handler with running task status (task name, status and the last execution). | ||
328 | +// it's in "/task" pattern in admin module. | ||
329 | +func taskStatus(rw http.ResponseWriter, req *http.Request) { | ||
330 | + data := make(map[interface{}]interface{}) | ||
331 | + | ||
332 | + // Run Task | ||
333 | + req.ParseForm() | ||
334 | + taskname := req.Form.Get("taskname") | ||
335 | + if taskname != "" { | ||
336 | + if t, ok := toolbox.AdminTaskList[taskname]; ok { | ||
337 | + if err := t.Run(); err != nil { | ||
338 | + data["Message"] = []string{"error", fmt.Sprintf("%s", err)} | ||
339 | + } | ||
340 | + data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus())} | ||
341 | + } else { | ||
342 | + data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)} | ||
343 | + } | ||
344 | + } | ||
345 | + | ||
346 | + // List Tasks | ||
347 | + content := make(map[string]interface{}) | ||
348 | + resultList := new([][]string) | ||
349 | + var fields = []string{ | ||
350 | + "Task Name", | ||
351 | + "Task Spec", | ||
352 | + "Task Status", | ||
353 | + "Last Time", | ||
354 | + "", | ||
355 | + } | ||
356 | + for tname, tk := range toolbox.AdminTaskList { | ||
357 | + result := []string{ | ||
358 | + tname, | ||
359 | + tk.GetSpec(), | ||
360 | + tk.GetStatus(), | ||
361 | + tk.GetPrev().String(), | ||
362 | + } | ||
363 | + *resultList = append(*resultList, result) | ||
364 | + } | ||
365 | + | ||
366 | + content["Fields"] = fields | ||
367 | + content["Data"] = resultList | ||
368 | + data["Content"] = content | ||
369 | + data["Title"] = "Tasks" | ||
370 | + execTpl(rw, data, tasksTpl, defaultScriptsTpl) | ||
371 | +} | ||
372 | + | ||
373 | +func execTpl(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) { | ||
374 | + tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) | ||
375 | + for _, tpl := range tpls { | ||
376 | + tmpl = template.Must(tmpl.Parse(tpl)) | ||
377 | + } | ||
378 | + tmpl.Execute(rw, data) | ||
379 | +} | ||
380 | + | ||
381 | +// adminApp is an http.HandlerFunc map used as beeAdminApp. | ||
382 | +type adminApp struct { | ||
383 | + routers map[string]http.HandlerFunc | ||
384 | +} | ||
385 | + | ||
386 | +// Route adds http.HandlerFunc to adminApp with url pattern. | ||
387 | +func (admin *adminApp) Route(pattern string, f http.HandlerFunc) { | ||
388 | + admin.routers[pattern] = f | ||
389 | +} | ||
390 | + | ||
391 | +// Run adminApp http server. | ||
392 | +// Its addr is defined in configuration file as adminhttpaddr and adminhttpport. | ||
393 | +func (admin *adminApp) Run() { | ||
394 | + if len(toolbox.AdminTaskList) > 0 { | ||
395 | + toolbox.StartTask() | ||
396 | + } | ||
397 | + addr := BConfig.Listen.AdminAddr | ||
398 | + | ||
399 | + if BConfig.Listen.AdminPort != 0 { | ||
400 | + addr = fmt.Sprintf("%s:%d", BConfig.Listen.AdminAddr, BConfig.Listen.AdminPort) | ||
401 | + } | ||
402 | + for p, f := range admin.routers { | ||
403 | + http.Handle(p, f) | ||
404 | + } | ||
405 | + logs.Info("Admin server Running on %s", addr) | ||
406 | + | ||
407 | + var err error | ||
408 | + if BConfig.Listen.Graceful { | ||
409 | + err = grace.ListenAndServe(addr, nil) | ||
410 | + } else { | ||
411 | + err = http.ListenAndServe(addr, nil) | ||
412 | + } | ||
413 | + if err != nil { | ||
414 | + logs.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) | ||
415 | + } | ||
416 | +} |
vendor/github.com/astaxie/beego/adminui.go
0 → 100644
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package beego | ||
16 | + | ||
17 | +var indexTpl = ` | ||
18 | +{{define "content"}} | ||
19 | +<h1>Beego Admin Dashboard</h1> | ||
20 | +<p> | ||
21 | +For detail usage please check our document: | ||
22 | +</p> | ||
23 | +<p> | ||
24 | +<a target="_blank" href="http://beego.me/docs/module/toolbox.md">Toolbox</a> | ||
25 | +</p> | ||
26 | +<p> | ||
27 | +<a target="_blank" href="http://beego.me/docs/advantage/monitor.md">Live Monitor</a> | ||
28 | +</p> | ||
29 | +{{.Content}} | ||
30 | +{{end}}` | ||
31 | + | ||
32 | +var profillingTpl = ` | ||
33 | +{{define "content"}} | ||
34 | +<h1>{{.Title}}</h1> | ||
35 | +<pre id="content"> | ||
36 | +<div>{{.Content}}</div> | ||
37 | +</pre> | ||
38 | +{{end}}` | ||
39 | + | ||
40 | +var defaultScriptsTpl = `` | ||
41 | + | ||
42 | +var gcAjaxTpl = ` | ||
43 | +{{define "scripts"}} | ||
44 | +<script type="text/javascript"> | ||
45 | + var app = app || {}; | ||
46 | +(function() { | ||
47 | + app.$el = $('#content'); | ||
48 | + app.getGc = function() { | ||
49 | + var that = this; | ||
50 | + $.ajax("/prof?command=gc%20summary&format=json").done(function(data) { | ||
51 | + that.$el.append($('<p>' + data.Content + '</p>')); | ||
52 | + }); | ||
53 | + }; | ||
54 | + $(document).ready(function() { | ||
55 | + setInterval(function() { | ||
56 | + app.getGc(); | ||
57 | + }, 3000); | ||
58 | + }); | ||
59 | +})(); | ||
60 | +</script> | ||
61 | +{{end}} | ||
62 | +` | ||
63 | + | ||
64 | +var qpsTpl = `{{define "content"}} | ||
65 | +<h1>Requests statistics</h1> | ||
66 | +<table class="table table-striped table-hover "> | ||
67 | + <thead> | ||
68 | + <tr> | ||
69 | + {{range .Content.Fields}} | ||
70 | + <th> | ||
71 | + {{.}} | ||
72 | + </th> | ||
73 | + {{end}} | ||
74 | + </tr> | ||
75 | + </thead> | ||
76 | + | ||
77 | + <tbody> | ||
78 | + {{range $i, $elem := .Content.Data}} | ||
79 | + | ||
80 | + <tr> | ||
81 | + <td>{{index $elem 0}}</td> | ||
82 | + <td>{{index $elem 1}}</td> | ||
83 | + <td>{{index $elem 2}}</td> | ||
84 | + <td data-order="{{index $elem 3}}">{{index $elem 4}}</td> | ||
85 | + <td data-order="{{index $elem 5}}">{{index $elem 6}}</td> | ||
86 | + <td data-order="{{index $elem 7}}">{{index $elem 8}}</td> | ||
87 | + <td data-order="{{index $elem 9}}">{{index $elem 10}}</td> | ||
88 | + </tr> | ||
89 | + {{end}} | ||
90 | + </tbody> | ||
91 | + | ||
92 | +</table> | ||
93 | +{{end}}` | ||
94 | + | ||
95 | +var configTpl = ` | ||
96 | +{{define "content"}} | ||
97 | +<h1>Configurations</h1> | ||
98 | +<pre> | ||
99 | +{{range $index, $elem := .Content}} | ||
100 | +{{$index}}={{$elem}} | ||
101 | +{{end}} | ||
102 | +</pre> | ||
103 | +{{end}} | ||
104 | +` | ||
105 | + | ||
106 | +var routerAndFilterTpl = `{{define "content"}} | ||
107 | + | ||
108 | + | ||
109 | +<h1>{{.Title}}</h1> | ||
110 | + | ||
111 | +{{range .Content.Methods}} | ||
112 | + | ||
113 | +<div class="panel panel-default"> | ||
114 | +<div class="panel-heading lead success"><strong>{{.}}</strong></div> | ||
115 | +<div class="panel-body"> | ||
116 | +<table class="table table-striped table-hover "> | ||
117 | + <thead> | ||
118 | + <tr> | ||
119 | + {{range $.Content.Fields}} | ||
120 | + <th> | ||
121 | + {{.}} | ||
122 | + </th> | ||
123 | + {{end}} | ||
124 | + </tr> | ||
125 | + </thead> | ||
126 | + | ||
127 | + <tbody> | ||
128 | + {{$slice := index $.Content.Data .}} | ||
129 | + {{range $i, $elem := $slice}} | ||
130 | + | ||
131 | + <tr> | ||
132 | + {{range $elem}} | ||
133 | + <td> | ||
134 | + {{.}} | ||
135 | + </td> | ||
136 | + {{end}} | ||
137 | + </tr> | ||
138 | + | ||
139 | + {{end}} | ||
140 | + </tbody> | ||
141 | + | ||
142 | +</table> | ||
143 | +</div> | ||
144 | +</div> | ||
145 | +{{end}} | ||
146 | + | ||
147 | + | ||
148 | +{{end}}` | ||
149 | + | ||
150 | +var tasksTpl = `{{define "content"}} | ||
151 | + | ||
152 | +<h1>{{.Title}}</h1> | ||
153 | + | ||
154 | +{{if .Message }} | ||
155 | +{{ $messageType := index .Message 0}} | ||
156 | +<p class="message | ||
157 | +{{if eq "error" $messageType}} | ||
158 | +bg-danger | ||
159 | +{{else if eq "success" $messageType}} | ||
160 | +bg-success | ||
161 | +{{else}} | ||
162 | +bg-warning | ||
163 | +{{end}} | ||
164 | +"> | ||
165 | +{{index .Message 1}} | ||
166 | +</p> | ||
167 | +{{end}} | ||
168 | + | ||
169 | + | ||
170 | +<table class="table table-striped table-hover "> | ||
171 | +<thead> | ||
172 | +<tr> | ||
173 | +{{range .Content.Fields}} | ||
174 | +<th> | ||
175 | +{{.}} | ||
176 | +</th> | ||
177 | +{{end}} | ||
178 | +</tr> | ||
179 | +</thead> | ||
180 | + | ||
181 | +<tbody> | ||
182 | +{{range $i, $slice := .Content.Data}} | ||
183 | +<tr> | ||
184 | + {{range $slice}} | ||
185 | + <td> | ||
186 | + {{.}} | ||
187 | + </td> | ||
188 | + {{end}} | ||
189 | + <td> | ||
190 | + <a class="btn btn-primary btn-sm" href="/task?taskname={{index $slice 0}}">Run</a> | ||
191 | + </td> | ||
192 | +</tr> | ||
193 | +{{end}} | ||
194 | +</tbody> | ||
195 | +</table> | ||
196 | + | ||
197 | +{{end}}` | ||
198 | + | ||
199 | +var healthCheckTpl = ` | ||
200 | +{{define "content"}} | ||
201 | + | ||
202 | +<h1>{{.Title}}</h1> | ||
203 | +<table class="table table-striped table-hover "> | ||
204 | +<thead> | ||
205 | +<tr> | ||
206 | +{{range .Content.Fields}} | ||
207 | + <th> | ||
208 | + {{.}} | ||
209 | + </th> | ||
210 | +{{end}} | ||
211 | +</tr> | ||
212 | +</thead> | ||
213 | +<tbody> | ||
214 | +{{range $i, $slice := .Content.Data}} | ||
215 | + {{ $header := index $slice 0}} | ||
216 | + {{ if eq "success" $header}} | ||
217 | + <tr class="success"> | ||
218 | + {{else if eq "error" $header}} | ||
219 | + <tr class="danger"> | ||
220 | + {{else}} | ||
221 | + <tr> | ||
222 | + {{end}} | ||
223 | + {{range $j, $elem := $slice}} | ||
224 | + {{if ne $j 0}} | ||
225 | + <td> | ||
226 | + {{$elem}} | ||
227 | + </td> | ||
228 | + {{end}} | ||
229 | + {{end}} | ||
230 | + <td> | ||
231 | + {{$header}} | ||
232 | + </td> | ||
233 | + </tr> | ||
234 | +{{end}} | ||
235 | + | ||
236 | +</tbody> | ||
237 | +</table> | ||
238 | +{{end}}` | ||
239 | + | ||
240 | +// The base dashboardTpl | ||
241 | +var dashboardTpl = ` | ||
242 | +<!DOCTYPE html> | ||
243 | +<html lang="en"> | ||
244 | +<head> | ||
245 | +<!-- Meta, title, CSS, favicons, etc. --> | ||
246 | +<meta charset="utf-8"> | ||
247 | +<meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
248 | +<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
249 | + | ||
250 | +<title> | ||
251 | + | ||
252 | +Welcome to Beego Admin Dashboard | ||
253 | + | ||
254 | +</title> | ||
255 | + | ||
256 | +<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet"> | ||
257 | +<link href="//cdn.datatables.net/plug-ins/725b2a2115b/integration/bootstrap/3/dataTables.bootstrap.css" rel="stylesheet"> | ||
258 | + | ||
259 | +<style type="text/css"> | ||
260 | +ul.nav li.dropdown:hover > ul.dropdown-menu { | ||
261 | + display: block; | ||
262 | +} | ||
263 | +#logo { | ||
264 | + width: 102px; | ||
265 | + height: 32px; | ||
266 | + margin-top: 5px; | ||
267 | +} | ||
268 | +.message { | ||
269 | + padding: 15px; | ||
270 | +} | ||
271 | +</style> | ||
272 | + | ||
273 | +</head> | ||
274 | +<body> | ||
275 | + | ||
276 | +<header class="navbar navbar-default navbar-static-top bs-docs-nav" id="top" role="banner"> | ||
277 | +<div class="container"> | ||
278 | +<div class="navbar-header"> | ||
279 | +<button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".bs-navbar-collapse"> | ||
280 | +<span class="sr-only">Toggle navigation</span> | ||
281 | +<span class="icon-bar"></span> | ||
282 | +<span class="icon-bar"></span> | ||
283 | +<span class="icon-bar"></span> | ||
284 | +</button> | ||
285 | + | ||
286 | +<a href="/"> | ||
287 | +<img id="logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPYAAABNCAYAAACVH5l+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAV/QAAFf0BzXBRYQAAABZ0RVh0Q3JlYXRpb24gVGltZQAxMi8xMy8xM+ovEHIAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAQAklEQVR4nO2de7RcVXnAfzM3MckmhLdKtZYqoimtERVtXSjFNvYE6rsnFMFKy2MJC4tGdNECalFsKxRQ5GGDClUjzS6lltcuUmiryxRqFaEaCkgVqAkSakKSneTm3rn949uHOTNzzp1zZs5j7mT/1pqVmzMze39z73xnf/vb36OBx+OZFRuEvwusBp4fu7wROE0ZfUs9UiUzMzMDQLNmOTyeucB1dCo17v/XVS5JRrxiezz9OSDn9drxiu3xjCFesT2eMcQrtsczhnjF9njGEK/YHs8Y4hXb4xlDvGJ7PGOIV2yPZwzxiu3x9OfpnNdrxyu2x9Ofk5HY8Dgb3fWRpFG3AB6Ppzh8EojHM8Z4xfZ4xhCv2B7PGOIV2+MZQ+bVLYDHUzU2CE8AXg+8GHguMAW0gOmEn6PH7q6fdwOTsceurp+fca+1wJPAz4GfKaM3V/EZvVfcs0dhg/AC4D3AgcAixGrNqgczOZ9ruOuTiGLfBVygjH4is8A5ibziXrE9eww2CI8C1gL7INZq9P2PFLBIorGjcacRBf82sFIZvbXg+WQyp9jzbBBuRj7osDwFPALcA9wC3K2MbhUw7rMUKGsWHgMuUUZfMcib55Ksg2CDcCnwR8CbgJcCe2d5G3AzcJYyelOJ4vVOHISLgEuB/YAJqlvUonkmgAXAa4HfR4ojDo0NwiXAIcAPldFT0fUinWcHAb8BfAC4E3jQBuHxBY5fNS8CPmuD8Ji6BclAZbLaIFxgg/AzwH8B5wCvIptSAyjgeOCvShIvkW1vflcDkfXltL/zMxU/QJR8EXCWDcIDh/lMNgjn2SD8MLAB+D6w3gbhC6Lny/SKvxS4wQbhGhuEqsR5yua36xYgB6XK6v6OtwN/zHDfnaOLkSgbzWbz5cDpwHySla5VwCNpzDTlPgSxdgbCBuGrgXuBTyM3S4BDgS/tWLGyAdV4xU8AltggfJsyerqC+YrG1i1ADkqT1QbhBLI/LcIq2FnAGJlwJviFwL7uUlzJQH5n6921pntM9Pk3crg1ux646w3apvd8es3++cApNgi/pox+PMdnUcAngLPd+N0sB04FVld13HUc8FHgYxXNVxSTwI11C5GRsmW9APk7FsHagsbJwnGIhRApQuQom0FuMOcpo68pY2IbhPsD/wwcRvtmECn5wcCHEesny1i/A1yDrPazcfGOFSu/1khx8rSABzJJL+znBJ0/y2t2A0uV0T/KMW4HBcmahUngIeAKZfQ9gwwwl2Tthw3Co5GjmjTz+xngx/T3LD8J/CNwTRXWm9vH3gr8Cr2KPQ3co4wue/tyKvAXtE3m+I1lM3CsMvr7s7z/AOBy4KQc0y5LW7G3KqNfmWOgyExYDnwcSHrvfMSBcUaecTOQW9YamUuyAs8qxxrSlXo14uWerE6qzKyivVpGRHvfzYhJWzZrgROB19DpuANYDPypDcL3KqN7tic2COcDdwO/lmO+B4D1hTnPlNFWGf115AOkmVrvtkG4oKg5PeWydfk7G8D1wC+kvOTfgDNGUaltEL4GCUSZR69DaxdwgzL6B2XLoYx+BnFybaftaIP2nv4NwG+lvH0p+ZR6DXDMotvX7i7cK+5MrFPoTUwHWAIcVfScnnKYmJhYBRyb8vTTwAmj6BC1QbgYcZhFx3BxpZ4GfgJcVqFIdyJe7Gl6veQK+IA7j+7mUcSy6MdPgBXK6BMX3b72aSjpuEsZvY30hmWvL2NOT7HYIDwS+PNZXnKyMvqnVcmTk5XAEXRGlEXKZBF/RGWyK6N3A+cjvojuVbuBrMpvSXjfNuCDswzdQm5QhyujTfyJMs+x70i5fniJc3oKwAbhPsANpDtDLx+19rERLkjjTGAhvWGdU8B/Al+tWi5l9P3A3zsZIpkiL/lC4P1Jq7Yy+jrgtoQh7wNep4xepYze3v1kmYqdtn/5pRLn9BTD55HMpyS+B5xboSx5ORuJxGvSuVq3ELN2VY0+gUuRLWp38EoDCTA5JeV9pyHh2iAWx7nAkcro76RNVJpiK6N/hhxxdXNQWXN6hscG4WlI2GcS24HjldG7KhQpMzYIXwmEtCPMoK08u4AvKKMfqkk8lNGPIdbCLto3G2g70k63Qdiz8Lltw1JgGXCwMvov43HhSZRdaCEpEsrngI8oNggPBT4zy0vOUEY/XJU8ebBBuBA5L47HrUf72WngYQpKvBiSaxFnV7cjrYGkkp7lovw6UEZPKaPvd172vtRRQeU5Nczp6YNLlPhrJEkhia8oo79coUh5eTfwCnq/05HD7GpnRdaKk+FqJLAortQgQTTvQFbnoShNsW0QNklOW6wsTtiTnWazeRLpceCPUHxgUWHYIDwYOIvORSPax04B6xDH1aiwBniQ3r02yJHwh1xwysCUuWKneb+fKnFOzwC41fqClKcnkX31tgpFysu5SEhzPFwzOrP+OXCRMnpHfeJ14nwUH0csiUi5ob3XPgZJhx2YMhU7LWHgkZTrnppoNptvQNJsk/gzZfR3q5QnDy7C7DjEjI0UJFKW3cAXZ4vFrgtl9L8C36RdYy1elGQRcN4w45ei2DYI9yU9a+XeMub0DEWQcv1x4JIqBcmDDcJ5wEVIzHX8zDoywR8ErqpHukx8EthCr0neBF5tgzDt79KXwhXbBTesRUyjJG4tek7P0CxLuX79KMaBxzgZ2fLFTfBo9dsGXFpWbbEiUEavR8qITdF7/DUBfMLlk+emMMW2QXiYDcKzkeyS5Skvu0sZ7U3x0eOQlOv/VKUQebBB+DzEKoyKEsbDR6eRNNNv1CNdLi5D4u67U16byOL4vkEGTTtT3sflE2dlr1nGivMnOcbMShZZp5B84S8ro2c7py2bUZV1r5TrP6xo/kE4Ddif5Hjwp4BPj2ogTRxl9BM2CK9DYsKjwJroM80D3mOD8AZl9IY8486mjEVX2LxIGV3W/jqLrAcg+5bNyujrS5IjC6Moa1Ixwkll9P9VMPegHE1bAeLKPQWsbrVaAxf0qIGrkGi/F9Kbs30gUl7s0jwDVhWgsobRKYv0jroFyEFVsibVzxqZ46EUor1nd8HA7a1W68rFd9zYr5rLyOCO4i6kvdeOWyAN4DfzjlmVYttWq1VojXHPHk8UudXNvGaz+dqqhSmAbyA30+7Chw3St0qpVKXYpzabzTL214NwU90C5GAuyVo162gfE8X/XQCc547C5hLLkfTNpPLIuX0Fs334LTnG2Zv+N4kLbRDeWlKwQD9Z4w6pOvfXMLdk7YsNwrcAHwFeRvY8gC3K6GHTd/8GeBtSVrg773op8FZGK4w0FZeHfS6iQy16Pfyp6ZlppCn2FmX0vinPpQl3CFKw/iMkRzFNAJ+iuBK2EbllrZG5JGtfbBC+CvgH6kkmegT4OlK9M4o6ixR8AZICeWfWbKiaORN4Acke/i0MUBiiyGKGP1ZGXwv8KtKfKYkVNgh/sag5PbVzLDX1WHe11r6InAEnpUC+GHhXHbLlweVfn0D75hTfWuxGbpy5Yz/KKGY4iTQdS+pw0KD4Fduzh9JqtR4F/o7OPTbu5wXAScP2yKqA85GMLuhcracRHbpskIKRZRUztEhCeRKvK2NOTy3cRmfyQqW4I60vAP9Lsof8RUhFlZHEtfU9is72QHGH2SeV0XkCxZ6lTDPq9pTrh5Y4p6dCXNbX24FvIdFeWxIeZcvwJLIHjRcuiJR8HnD8KK7arsHGxxBPeES8RPK/K6MHDokt80jgwZTrI/dL9gyOMvpm0n0qVfUJ10gwz6H0Nsd7IfCHwMUly5AZV4Tk/UiMfpLDbCuzl37uS5nFDLcixe+6GShbxeNJQxm9CVm1d9J7BtxEVu1Rqo77y8DvkZxDPgXcpIxOWxgzUbZHM6mSom/x4ymDmxDvcdyBBrIi7guckVQksGpc4MwHkUaW3VFmLeCnwGeHnadsxV6YcG3kM248cw93Xr0aWbW7yw01gDcDL6lHug7e6B5xEzzKIZ8Eriwi+abMYoYHkbw6D+Tl83gyYJAm9klFAhcjgSC1YYNwb2S1jm9H42fX31NGF9I7vMwV+00p1x8tcU7PYFRtRZWSeeV6ZH0KSaboPtduAEfbIDysjLkzciLi4Euq+LIFKZVUCKUotmu/mtZM7D/KmNMzFElOziU2CIuoAZ9URre0YzB3BPct0jtbripr7tlwIdd/QHrRxduU0YUVtihcsbcuf2djYmLiYtIDUUaymdseTtKeroEUMxgYG4TLEGXq5slhxs3A5ciRUbQaxpX7FTYIjyh5/g5cjfDIYQadN5xppJ/XlUXOWWTNs4U2CIOJiYm7gA+lvOxeZfQDRc3pKYy0aiNXOOXMjQ3Cw5EMrCTuG2TMrLj+XN+k1+RvIOGbv17m/Am8keQIMxCH2bUu0KYw0gJU9rZBmOeXvxjpotkv4KWMKip5Zc3CJPAQ0kf5ngLHHVVZ70B6SnfzMuA+G4QbgKztcZpIEFJalVqYJaClQK5CCu93x01MIJ+rElzV3vfRtlzi3voW0lPsxqLnTVPEJuklaQfl+u7m3AVRhqwARwKhDcJlwwYLxBhVWf8WcTo9N+X5g5ldUfPwMMn9novmUeAJekOYZyjus2Th7Ugac7w+W7Q92IbUAizceVlVyt3d1HzUMCDPYQ6k/jkGltW176ni7zMDnKmMLj1xxGVEbSL56CspvqJwXE+x99LZ1jceYWbK6rJSRfmYrwCnj1LvpJwkOX9GlYFlVUbfaIPwTMrrnNFClPrOksZPIjr26o7w6vneu7iLw5Fwz+cjyr8L6a+13Y21M/bvM4iDbguy8u6I96x2EWbnIFZQd+XRGUpwmMUpU7HvB853SQJzmSq/iMMylKzK6KttED6OpEKmmeWD8CPk5n5XgWNmIR47HhHV6wbABuEBiH/hWOB5iEJHCSTx9yTRMa4NwvhcDcSK6h5nBvGLfFUZvTHn58lMkYq9EcnoWgfcrIxeV+DYdfAYcIky+u66BclAYbIqo2+xQfgSxIRcARyBKHme78pOYAPwbaSlk46vZhUSrdjdit10ZvJKZA+8P+JU617ZI9KuZ6F77hYS0164wyzOMAJ7PCONDcKPIllUcV/SDHLjsUhySJNOPYgfRQ1L92rdcvOeX9aWZGZGRJ9rJVo9njx0r9iRib0QyWOIVzft7tZZBN2r9W7gX6rwM3jF9owzu+hVLkhW3CRPfZqC57V0W4hS/w9QST82r9ieccaSrIRpjrG4uRx15ZhAzPXoEa360f+hM6Ksm0kko3E98Lm8zfUGxSu2Z5x5Glkp59OrdElm8n8jOd3fUUYnJcYk4o62FrrHXu6hEP2yyNHYJhcvUAlesT3jzEbkDHoJnUdYcZN8J3IcN3BIrvP4b3OPTcMIXBResT3jzONIqaGFtE3qiB3AD5AAqnUul3ts8IrtGWc2IDHw5yAtdOYjoZzfRRoN3FdGnPYo4M+xPWONK2C4H+1FbArYXFPATOlE59j/D6WId7YitGZUAAAAAElFTkSuQmCC"/> | ||
288 | +</a> | ||
289 | + | ||
290 | +</div> | ||
291 | +<nav class="collapse navbar-collapse bs-navbar-collapse" role="navigation"> | ||
292 | +<ul class="nav navbar-nav"> | ||
293 | +<li> | ||
294 | +<a href="/qps"> | ||
295 | +Requests statistics | ||
296 | +</a> | ||
297 | +</li> | ||
298 | +<li> | ||
299 | + | ||
300 | +<li class="dropdown"> | ||
301 | +<a href="#" class="dropdown-toggle disabled" data-toggle="dropdown">Performance profiling<span class="caret"></span></a> | ||
302 | +<ul class="dropdown-menu" role="menu"> | ||
303 | + | ||
304 | +<li><a href="/prof?command=lookup goroutine">lookup goroutine</a></li> | ||
305 | +<li><a href="/prof?command=lookup heap">lookup heap</a></li> | ||
306 | +<li><a href="/prof?command=lookup threadcreate">lookup threadcreate</a></li> | ||
307 | +<li><a href="/prof?command=lookup block">lookup block</a></li> | ||
308 | +<li><a href="/prof?command=get cpuprof">get cpuprof</a></li> | ||
309 | +<li><a href="/prof?command=get memprof">get memprof</a></li> | ||
310 | +<li><a href="/prof?command=gc summary">gc summary</a></li> | ||
311 | + | ||
312 | +</ul> | ||
313 | +</li> | ||
314 | + | ||
315 | +<li> | ||
316 | +<a href="/healthcheck"> | ||
317 | +Healthcheck | ||
318 | +</a> | ||
319 | +</li> | ||
320 | + | ||
321 | +<li> | ||
322 | +<a href="/task" class="dropdown-toggle disabled" data-toggle="dropdown">Tasks</a> | ||
323 | +</li> | ||
324 | + | ||
325 | +<li class="dropdown"> | ||
326 | +<a href="#" class="dropdown-toggle disabled" data-toggle="dropdown">Config Status<span class="caret"></span></a> | ||
327 | +<ul class="dropdown-menu" role="menu"> | ||
328 | +<li><a href="/listconf?command=conf">Configs</a></li> | ||
329 | +<li><a href="/listconf?command=router">Routers</a></li> | ||
330 | +<li><a href="/listconf?command=filter">Filters</a></li> | ||
331 | +</ul> | ||
332 | +</li> | ||
333 | +</ul> | ||
334 | +</nav> | ||
335 | +</div> | ||
336 | +</header> | ||
337 | + | ||
338 | +<div class="container"> | ||
339 | +{{template "content" .}} | ||
340 | +</div> | ||
341 | + | ||
342 | +<script src="//code.jquery.com/jquery-1.11.1.min.js"></script> | ||
343 | +<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script> | ||
344 | +<script src="//cdn.datatables.net/1.10.2/js/jquery.dataTables.min.js"></script> | ||
345 | +<script src="//cdn.datatables.net/plug-ins/725b2a2115b/integration/bootstrap/3/dataTables.bootstrap.js | ||
346 | +"></script> | ||
347 | + | ||
348 | +<script type="text/javascript"> | ||
349 | +$(document).ready(function() { | ||
350 | + $('.table').dataTable(); | ||
351 | +}); | ||
352 | +</script> | ||
353 | +{{template "scripts" .}} | ||
354 | +</body> | ||
355 | +</html> | ||
356 | +` |
vendor/github.com/astaxie/beego/app.go
0 → 100644
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package beego | ||
16 | + | ||
17 | +import ( | ||
18 | + "crypto/tls" | ||
19 | + "crypto/x509" | ||
20 | + "fmt" | ||
21 | + "io/ioutil" | ||
22 | + "net" | ||
23 | + "net/http" | ||
24 | + "net/http/fcgi" | ||
25 | + "os" | ||
26 | + "path" | ||
27 | + "strings" | ||
28 | + "time" | ||
29 | + | ||
30 | + "github.com/astaxie/beego/grace" | ||
31 | + "github.com/astaxie/beego/logs" | ||
32 | + "github.com/astaxie/beego/utils" | ||
33 | + "golang.org/x/crypto/acme/autocert" | ||
34 | +) | ||
35 | + | ||
36 | +var ( | ||
37 | + // BeeApp is an application instance | ||
38 | + BeeApp *App | ||
39 | +) | ||
40 | + | ||
41 | +func init() { | ||
42 | + // create beego application | ||
43 | + BeeApp = NewApp() | ||
44 | +} | ||
45 | + | ||
46 | +// App defines beego application with a new PatternServeMux. | ||
47 | +type App struct { | ||
48 | + Handlers *ControllerRegister | ||
49 | + Server *http.Server | ||
50 | +} | ||
51 | + | ||
52 | +// NewApp returns a new beego application. | ||
53 | +func NewApp() *App { | ||
54 | + cr := NewControllerRegister() | ||
55 | + app := &App{Handlers: cr, Server: &http.Server{}} | ||
56 | + return app | ||
57 | +} | ||
58 | + | ||
59 | +// MiddleWare function for http.Handler | ||
60 | +type MiddleWare func(http.Handler) http.Handler | ||
61 | + | ||
62 | +// Run beego application. | ||
63 | +func (app *App) Run(mws ...MiddleWare) { | ||
64 | + addr := BConfig.Listen.HTTPAddr | ||
65 | + | ||
66 | + if BConfig.Listen.HTTPPort != 0 { | ||
67 | + addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPAddr, BConfig.Listen.HTTPPort) | ||
68 | + } | ||
69 | + | ||
70 | + var ( | ||
71 | + err error | ||
72 | + l net.Listener | ||
73 | + endRunning = make(chan bool, 1) | ||
74 | + ) | ||
75 | + | ||
76 | + // run cgi server | ||
77 | + if BConfig.Listen.EnableFcgi { | ||
78 | + if BConfig.Listen.EnableStdIo { | ||
79 | + if err = fcgi.Serve(nil, app.Handlers); err == nil { // standard I/O | ||
80 | + logs.Info("Use FCGI via standard I/O") | ||
81 | + } else { | ||
82 | + logs.Critical("Cannot use FCGI via standard I/O", err) | ||
83 | + } | ||
84 | + return | ||
85 | + } | ||
86 | + if BConfig.Listen.HTTPPort == 0 { | ||
87 | + // remove the Socket file before start | ||
88 | + if utils.FileExists(addr) { | ||
89 | + os.Remove(addr) | ||
90 | + } | ||
91 | + l, err = net.Listen("unix", addr) | ||
92 | + } else { | ||
93 | + l, err = net.Listen("tcp", addr) | ||
94 | + } | ||
95 | + if err != nil { | ||
96 | + logs.Critical("Listen: ", err) | ||
97 | + } | ||
98 | + if err = fcgi.Serve(l, app.Handlers); err != nil { | ||
99 | + logs.Critical("fcgi.Serve: ", err) | ||
100 | + } | ||
101 | + return | ||
102 | + } | ||
103 | + | ||
104 | + app.Server.Handler = app.Handlers | ||
105 | + for i := len(mws) - 1; i >= 0; i-- { | ||
106 | + if mws[i] == nil { | ||
107 | + continue | ||
108 | + } | ||
109 | + app.Server.Handler = mws[i](app.Server.Handler) | ||
110 | + } | ||
111 | + app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second | ||
112 | + app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second | ||
113 | + app.Server.ErrorLog = logs.GetLogger("HTTP") | ||
114 | + | ||
115 | + // run graceful mode | ||
116 | + if BConfig.Listen.Graceful { | ||
117 | + httpsAddr := BConfig.Listen.HTTPSAddr | ||
118 | + app.Server.Addr = httpsAddr | ||
119 | + if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS { | ||
120 | + go func() { | ||
121 | + time.Sleep(1000 * time.Microsecond) | ||
122 | + if BConfig.Listen.HTTPSPort != 0 { | ||
123 | + httpsAddr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort) | ||
124 | + app.Server.Addr = httpsAddr | ||
125 | + } | ||
126 | + server := grace.NewServer(httpsAddr, app.Handlers) | ||
127 | + server.Server.ReadTimeout = app.Server.ReadTimeout | ||
128 | + server.Server.WriteTimeout = app.Server.WriteTimeout | ||
129 | + if BConfig.Listen.EnableMutualHTTPS { | ||
130 | + if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil { | ||
131 | + logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) | ||
132 | + time.Sleep(100 * time.Microsecond) | ||
133 | + endRunning <- true | ||
134 | + } | ||
135 | + } else { | ||
136 | + if BConfig.Listen.AutoTLS { | ||
137 | + m := autocert.Manager{ | ||
138 | + Prompt: autocert.AcceptTOS, | ||
139 | + HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...), | ||
140 | + Cache: autocert.DirCache(BConfig.Listen.TLSCacheDir), | ||
141 | + } | ||
142 | + app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate} | ||
143 | + BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", "" | ||
144 | + } | ||
145 | + if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { | ||
146 | + logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) | ||
147 | + time.Sleep(100 * time.Microsecond) | ||
148 | + endRunning <- true | ||
149 | + } | ||
150 | + } | ||
151 | + }() | ||
152 | + } | ||
153 | + if BConfig.Listen.EnableHTTP { | ||
154 | + go func() { | ||
155 | + server := grace.NewServer(addr, app.Handlers) | ||
156 | + server.Server.ReadTimeout = app.Server.ReadTimeout | ||
157 | + server.Server.WriteTimeout = app.Server.WriteTimeout | ||
158 | + if BConfig.Listen.ListenTCP4 { | ||
159 | + server.Network = "tcp4" | ||
160 | + } | ||
161 | + if err := server.ListenAndServe(); err != nil { | ||
162 | + logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) | ||
163 | + time.Sleep(100 * time.Microsecond) | ||
164 | + endRunning <- true | ||
165 | + } | ||
166 | + }() | ||
167 | + } | ||
168 | + <-endRunning | ||
169 | + return | ||
170 | + } | ||
171 | + | ||
172 | + // run normal mode | ||
173 | + if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS { | ||
174 | + go func() { | ||
175 | + time.Sleep(1000 * time.Microsecond) | ||
176 | + if BConfig.Listen.HTTPSPort != 0 { | ||
177 | + app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort) | ||
178 | + } else if BConfig.Listen.EnableHTTP { | ||
179 | + BeeLogger.Info("Start https server error, conflict with http. Please reset https port") | ||
180 | + return | ||
181 | + } | ||
182 | + logs.Info("https server Running on https://%s", app.Server.Addr) | ||
183 | + if BConfig.Listen.AutoTLS { | ||
184 | + m := autocert.Manager{ | ||
185 | + Prompt: autocert.AcceptTOS, | ||
186 | + HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...), | ||
187 | + Cache: autocert.DirCache(BConfig.Listen.TLSCacheDir), | ||
188 | + } | ||
189 | + app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate} | ||
190 | + BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", "" | ||
191 | + } else if BConfig.Listen.EnableMutualHTTPS { | ||
192 | + pool := x509.NewCertPool() | ||
193 | + data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile) | ||
194 | + if err != nil { | ||
195 | + BeeLogger.Info("MutualHTTPS should provide TrustCaFile") | ||
196 | + return | ||
197 | + } | ||
198 | + pool.AppendCertsFromPEM(data) | ||
199 | + app.Server.TLSConfig = &tls.Config{ | ||
200 | + ClientCAs: pool, | ||
201 | + ClientAuth: tls.RequireAndVerifyClientCert, | ||
202 | + } | ||
203 | + } | ||
204 | + if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { | ||
205 | + logs.Critical("ListenAndServeTLS: ", err) | ||
206 | + time.Sleep(100 * time.Microsecond) | ||
207 | + endRunning <- true | ||
208 | + } | ||
209 | + }() | ||
210 | + | ||
211 | + } | ||
212 | + if BConfig.Listen.EnableHTTP { | ||
213 | + go func() { | ||
214 | + app.Server.Addr = addr | ||
215 | + logs.Info("http server Running on http://%s", app.Server.Addr) | ||
216 | + if BConfig.Listen.ListenTCP4 { | ||
217 | + ln, err := net.Listen("tcp4", app.Server.Addr) | ||
218 | + if err != nil { | ||
219 | + logs.Critical("ListenAndServe: ", err) | ||
220 | + time.Sleep(100 * time.Microsecond) | ||
221 | + endRunning <- true | ||
222 | + return | ||
223 | + } | ||
224 | + if err = app.Server.Serve(ln); err != nil { | ||
225 | + logs.Critical("ListenAndServe: ", err) | ||
226 | + time.Sleep(100 * time.Microsecond) | ||
227 | + endRunning <- true | ||
228 | + return | ||
229 | + } | ||
230 | + } else { | ||
231 | + if err := app.Server.ListenAndServe(); err != nil { | ||
232 | + logs.Critical("ListenAndServe: ", err) | ||
233 | + time.Sleep(100 * time.Microsecond) | ||
234 | + endRunning <- true | ||
235 | + } | ||
236 | + } | ||
237 | + }() | ||
238 | + } | ||
239 | + <-endRunning | ||
240 | +} | ||
241 | + | ||
242 | +// Router adds a patterned controller handler to BeeApp. | ||
243 | +// it's an alias method of App.Router. | ||
244 | +// usage: | ||
245 | +// simple router | ||
246 | +// beego.Router("/admin", &admin.UserController{}) | ||
247 | +// beego.Router("/admin/index", &admin.ArticleController{}) | ||
248 | +// | ||
249 | +// regex router | ||
250 | +// | ||
251 | +// beego.Router("/api/:id([0-9]+)", &controllers.RController{}) | ||
252 | +// | ||
253 | +// custom rules | ||
254 | +// beego.Router("/api/list",&RestController{},"*:ListFood") | ||
255 | +// beego.Router("/api/create",&RestController{},"post:CreateFood") | ||
256 | +// beego.Router("/api/update",&RestController{},"put:UpdateFood") | ||
257 | +// beego.Router("/api/delete",&RestController{},"delete:DeleteFood") | ||
258 | +func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { | ||
259 | + BeeApp.Handlers.Add(rootpath, c, mappingMethods...) | ||
260 | + return BeeApp | ||
261 | +} | ||
262 | + | ||
263 | +// UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful | ||
264 | +// in web applications that inherit most routes from a base webapp via the underscore | ||
265 | +// import, and aim to overwrite only certain paths. | ||
266 | +// The method parameter can be empty or "*" for all HTTP methods, or a particular | ||
267 | +// method type (e.g. "GET" or "POST") for selective removal. | ||
268 | +// | ||
269 | +// Usage (replace "GET" with "*" for all methods): | ||
270 | +// beego.UnregisterFixedRoute("/yourpreviouspath", "GET") | ||
271 | +// beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage") | ||
272 | +func UnregisterFixedRoute(fixedRoute string, method string) *App { | ||
273 | + subPaths := splitPath(fixedRoute) | ||
274 | + if method == "" || method == "*" { | ||
275 | + for m := range HTTPMETHOD { | ||
276 | + if _, ok := BeeApp.Handlers.routers[m]; !ok { | ||
277 | + continue | ||
278 | + } | ||
279 | + if BeeApp.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") { | ||
280 | + findAndRemoveSingleTree(BeeApp.Handlers.routers[m]) | ||
281 | + continue | ||
282 | + } | ||
283 | + findAndRemoveTree(subPaths, BeeApp.Handlers.routers[m], m) | ||
284 | + } | ||
285 | + return BeeApp | ||
286 | + } | ||
287 | + // Single HTTP method | ||
288 | + um := strings.ToUpper(method) | ||
289 | + if _, ok := BeeApp.Handlers.routers[um]; ok { | ||
290 | + if BeeApp.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") { | ||
291 | + findAndRemoveSingleTree(BeeApp.Handlers.routers[um]) | ||
292 | + return BeeApp | ||
293 | + } | ||
294 | + findAndRemoveTree(subPaths, BeeApp.Handlers.routers[um], um) | ||
295 | + } | ||
296 | + return BeeApp | ||
297 | +} | ||
298 | + | ||
299 | +func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) { | ||
300 | + for i := range entryPointTree.fixrouters { | ||
301 | + if entryPointTree.fixrouters[i].prefix == paths[0] { | ||
302 | + if len(paths) == 1 { | ||
303 | + if len(entryPointTree.fixrouters[i].fixrouters) > 0 { | ||
304 | + // If the route had children subtrees, remove just the functional leaf, | ||
305 | + // to allow children to function as before | ||
306 | + if len(entryPointTree.fixrouters[i].leaves) > 0 { | ||
307 | + entryPointTree.fixrouters[i].leaves[0] = nil | ||
308 | + entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:] | ||
309 | + } | ||
310 | + } else { | ||
311 | + // Remove the *Tree from the fixrouters slice | ||
312 | + entryPointTree.fixrouters[i] = nil | ||
313 | + | ||
314 | + if i == len(entryPointTree.fixrouters)-1 { | ||
315 | + entryPointTree.fixrouters = entryPointTree.fixrouters[:i] | ||
316 | + } else { | ||
317 | + entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...) | ||
318 | + } | ||
319 | + } | ||
320 | + return | ||
321 | + } | ||
322 | + findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method) | ||
323 | + } | ||
324 | + } | ||
325 | +} | ||
326 | + | ||
327 | +func findAndRemoveSingleTree(entryPointTree *Tree) { | ||
328 | + if entryPointTree == nil { | ||
329 | + return | ||
330 | + } | ||
331 | + if len(entryPointTree.fixrouters) > 0 { | ||
332 | + // If the route had children subtrees, remove just the functional leaf, | ||
333 | + // to allow children to function as before | ||
334 | + if len(entryPointTree.leaves) > 0 { | ||
335 | + entryPointTree.leaves[0] = nil | ||
336 | + entryPointTree.leaves = entryPointTree.leaves[1:] | ||
337 | + } | ||
338 | + } | ||
339 | +} | ||
340 | + | ||
341 | +// Include will generate router file in the router/xxx.go from the controller's comments | ||
342 | +// usage: | ||
343 | +// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) | ||
344 | +// type BankAccount struct{ | ||
345 | +// beego.Controller | ||
346 | +// } | ||
347 | +// | ||
348 | +// register the function | ||
349 | +// func (b *BankAccount)Mapping(){ | ||
350 | +// b.Mapping("ShowAccount" , b.ShowAccount) | ||
351 | +// b.Mapping("ModifyAccount", b.ModifyAccount) | ||
352 | +//} | ||
353 | +// | ||
354 | +// //@router /account/:id [get] | ||
355 | +// func (b *BankAccount) ShowAccount(){ | ||
356 | +// //logic | ||
357 | +// } | ||
358 | +// | ||
359 | +// | ||
360 | +// //@router /account/:id [post] | ||
361 | +// func (b *BankAccount) ModifyAccount(){ | ||
362 | +// //logic | ||
363 | +// } | ||
364 | +// | ||
365 | +// the comments @router url methodlist | ||
366 | +// url support all the function Router's pattern | ||
367 | +// methodlist [get post head put delete options *] | ||
368 | +func Include(cList ...ControllerInterface) *App { | ||
369 | + BeeApp.Handlers.Include(cList...) | ||
370 | + return BeeApp | ||
371 | +} | ||
372 | + | ||
373 | +// RESTRouter adds a restful controller handler to BeeApp. | ||
374 | +// its' controller implements beego.ControllerInterface and | ||
375 | +// defines a param "pattern/:objectId" to visit each resource. | ||
376 | +func RESTRouter(rootpath string, c ControllerInterface) *App { | ||
377 | + Router(rootpath, c) | ||
378 | + Router(path.Join(rootpath, ":objectId"), c) | ||
379 | + return BeeApp | ||
380 | +} | ||
381 | + | ||
382 | +// AutoRouter adds defined controller handler to BeeApp. | ||
383 | +// it's same to App.AutoRouter. | ||
384 | +// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page, | ||
385 | +// visit the url /main/list to exec List function or /main/page to exec Page function. | ||
386 | +func AutoRouter(c ControllerInterface) *App { | ||
387 | + BeeApp.Handlers.AddAuto(c) | ||
388 | + return BeeApp | ||
389 | +} | ||
390 | + | ||
391 | +// AutoPrefix adds controller handler to BeeApp with prefix. | ||
392 | +// it's same to App.AutoRouterWithPrefix. | ||
393 | +// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page, | ||
394 | +// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function. | ||
395 | +func AutoPrefix(prefix string, c ControllerInterface) *App { | ||
396 | + BeeApp.Handlers.AddAutoPrefix(prefix, c) | ||
397 | + return BeeApp | ||
398 | +} | ||
399 | + | ||
400 | +// Get used to register router for Get method | ||
401 | +// usage: | ||
402 | +// beego.Get("/", func(ctx *context.Context){ | ||
403 | +// ctx.Output.Body("hello world") | ||
404 | +// }) | ||
405 | +func Get(rootpath string, f FilterFunc) *App { | ||
406 | + BeeApp.Handlers.Get(rootpath, f) | ||
407 | + return BeeApp | ||
408 | +} | ||
409 | + | ||
410 | +// Post used to register router for Post method | ||
411 | +// usage: | ||
412 | +// beego.Post("/api", func(ctx *context.Context){ | ||
413 | +// ctx.Output.Body("hello world") | ||
414 | +// }) | ||
415 | +func Post(rootpath string, f FilterFunc) *App { | ||
416 | + BeeApp.Handlers.Post(rootpath, f) | ||
417 | + return BeeApp | ||
418 | +} | ||
419 | + | ||
420 | +// Delete used to register router for Delete method | ||
421 | +// usage: | ||
422 | +// beego.Delete("/api", func(ctx *context.Context){ | ||
423 | +// ctx.Output.Body("hello world") | ||
424 | +// }) | ||
425 | +func Delete(rootpath string, f FilterFunc) *App { | ||
426 | + BeeApp.Handlers.Delete(rootpath, f) | ||
427 | + return BeeApp | ||
428 | +} | ||
429 | + | ||
430 | +// Put used to register router for Put method | ||
431 | +// usage: | ||
432 | +// beego.Put("/api", func(ctx *context.Context){ | ||
433 | +// ctx.Output.Body("hello world") | ||
434 | +// }) | ||
435 | +func Put(rootpath string, f FilterFunc) *App { | ||
436 | + BeeApp.Handlers.Put(rootpath, f) | ||
437 | + return BeeApp | ||
438 | +} | ||
439 | + | ||
440 | +// Head used to register router for Head method | ||
441 | +// usage: | ||
442 | +// beego.Head("/api", func(ctx *context.Context){ | ||
443 | +// ctx.Output.Body("hello world") | ||
444 | +// }) | ||
445 | +func Head(rootpath string, f FilterFunc) *App { | ||
446 | + BeeApp.Handlers.Head(rootpath, f) | ||
447 | + return BeeApp | ||
448 | +} | ||
449 | + | ||
450 | +// Options used to register router for Options method | ||
451 | +// usage: | ||
452 | +// beego.Options("/api", func(ctx *context.Context){ | ||
453 | +// ctx.Output.Body("hello world") | ||
454 | +// }) | ||
455 | +func Options(rootpath string, f FilterFunc) *App { | ||
456 | + BeeApp.Handlers.Options(rootpath, f) | ||
457 | + return BeeApp | ||
458 | +} | ||
459 | + | ||
460 | +// Patch used to register router for Patch method | ||
461 | +// usage: | ||
462 | +// beego.Patch("/api", func(ctx *context.Context){ | ||
463 | +// ctx.Output.Body("hello world") | ||
464 | +// }) | ||
465 | +func Patch(rootpath string, f FilterFunc) *App { | ||
466 | + BeeApp.Handlers.Patch(rootpath, f) | ||
467 | + return BeeApp | ||
468 | +} | ||
469 | + | ||
470 | +// Any used to register router for all methods | ||
471 | +// usage: | ||
472 | +// beego.Any("/api", func(ctx *context.Context){ | ||
473 | +// ctx.Output.Body("hello world") | ||
474 | +// }) | ||
475 | +func Any(rootpath string, f FilterFunc) *App { | ||
476 | + BeeApp.Handlers.Any(rootpath, f) | ||
477 | + return BeeApp | ||
478 | +} | ||
479 | + | ||
480 | +// Handler used to register a Handler router | ||
481 | +// usage: | ||
482 | +// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { | ||
483 | +// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) | ||
484 | +// })) | ||
485 | +func Handler(rootpath string, h http.Handler, options ...interface{}) *App { | ||
486 | + BeeApp.Handlers.Handler(rootpath, h, options...) | ||
487 | + return BeeApp | ||
488 | +} | ||
489 | + | ||
490 | +// InsertFilter adds a FilterFunc with pattern condition and action constant. | ||
491 | +// The pos means action constant including | ||
492 | +// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. | ||
493 | +// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) | ||
494 | +func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App { | ||
495 | + BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...) | ||
496 | + return BeeApp | ||
497 | +} |
vendor/github.com/astaxie/beego/beego.go
0 → 100644
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package beego | ||
16 | + | ||
17 | +import ( | ||
18 | + "os" | ||
19 | + "path/filepath" | ||
20 | + "strconv" | ||
21 | + "strings" | ||
22 | +) | ||
23 | + | ||
24 | +const ( | ||
25 | + // VERSION represent beego web framework version. | ||
26 | + VERSION = "1.10.0" | ||
27 | + | ||
28 | + // DEV is for develop | ||
29 | + DEV = "dev" | ||
30 | + // PROD is for production | ||
31 | + PROD = "prod" | ||
32 | +) | ||
33 | + | ||
34 | +//hook function to run | ||
35 | +type hookfunc func() error | ||
36 | + | ||
37 | +var ( | ||
38 | + hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc | ||
39 | +) | ||
40 | + | ||
41 | +// AddAPPStartHook is used to register the hookfunc | ||
42 | +// The hookfuncs will run in beego.Run() | ||
43 | +// such as initiating session , starting middleware , building template, starting admin control and so on. | ||
44 | +func AddAPPStartHook(hf ...hookfunc) { | ||
45 | + hooks = append(hooks, hf...) | ||
46 | +} | ||
47 | + | ||
48 | +// Run beego application. | ||
49 | +// beego.Run() default run on HttpPort | ||
50 | +// beego.Run("localhost") | ||
51 | +// beego.Run(":8089") | ||
52 | +// beego.Run("127.0.0.1:8089") | ||
53 | +func Run(params ...string) { | ||
54 | + | ||
55 | + initBeforeHTTPRun() | ||
56 | + | ||
57 | + if len(params) > 0 && params[0] != "" { | ||
58 | + strs := strings.Split(params[0], ":") | ||
59 | + if len(strs) > 0 && strs[0] != "" { | ||
60 | + BConfig.Listen.HTTPAddr = strs[0] | ||
61 | + } | ||
62 | + if len(strs) > 1 && strs[1] != "" { | ||
63 | + BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1]) | ||
64 | + } | ||
65 | + | ||
66 | + BConfig.Listen.Domains = params | ||
67 | + } | ||
68 | + | ||
69 | + BeeApp.Run() | ||
70 | +} | ||
71 | + | ||
72 | +// RunWithMiddleWares Run beego application with middlewares. | ||
73 | +func RunWithMiddleWares(addr string, mws ...MiddleWare) { | ||
74 | + initBeforeHTTPRun() | ||
75 | + | ||
76 | + strs := strings.Split(addr, ":") | ||
77 | + if len(strs) > 0 && strs[0] != "" { | ||
78 | + BConfig.Listen.HTTPAddr = strs[0] | ||
79 | + BConfig.Listen.Domains = []string{strs[0]} | ||
80 | + } | ||
81 | + if len(strs) > 1 && strs[1] != "" { | ||
82 | + BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1]) | ||
83 | + } | ||
84 | + | ||
85 | + BeeApp.Run(mws...) | ||
86 | +} | ||
87 | + | ||
88 | +func initBeforeHTTPRun() { | ||
89 | + //init hooks | ||
90 | + AddAPPStartHook( | ||
91 | + registerMime, | ||
92 | + registerDefaultErrorHandler, | ||
93 | + registerSession, | ||
94 | + registerTemplate, | ||
95 | + registerAdmin, | ||
96 | + registerGzip, | ||
97 | + ) | ||
98 | + | ||
99 | + for _, hk := range hooks { | ||
100 | + if err := hk(); err != nil { | ||
101 | + panic(err) | ||
102 | + } | ||
103 | + } | ||
104 | +} | ||
105 | + | ||
106 | +// TestBeegoInit is for test package init | ||
107 | +func TestBeegoInit(ap string) { | ||
108 | + path := filepath.Join(ap, "conf", "app.conf") | ||
109 | + os.Chdir(ap) | ||
110 | + InitBeegoBeforeTest(path) | ||
111 | +} | ||
112 | + | ||
113 | +// InitBeegoBeforeTest is for test package init | ||
114 | +func InitBeegoBeforeTest(appConfigPath string) { | ||
115 | + if err := LoadAppConfig(appConfigProvider, appConfigPath); err != nil { | ||
116 | + panic(err) | ||
117 | + } | ||
118 | + BConfig.RunMode = "test" | ||
119 | + initBeforeHTTPRun() | ||
120 | +} |
vendor/github.com/astaxie/beego/config.go
0 → 100644
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package beego | ||
16 | + | ||
17 | +import ( | ||
18 | + "fmt" | ||
19 | + "os" | ||
20 | + "path/filepath" | ||
21 | + "reflect" | ||
22 | + "runtime" | ||
23 | + "strings" | ||
24 | + | ||
25 | + "github.com/astaxie/beego/config" | ||
26 | + "github.com/astaxie/beego/context" | ||
27 | + "github.com/astaxie/beego/logs" | ||
28 | + "github.com/astaxie/beego/session" | ||
29 | + "github.com/astaxie/beego/utils" | ||
30 | +) | ||
31 | + | ||
32 | +// Config is the main struct for BConfig | ||
33 | +type Config struct { | ||
34 | + AppName string //Application name | ||
35 | + RunMode string //Running Mode: dev | prod | ||
36 | + RouterCaseSensitive bool | ||
37 | + ServerName string | ||
38 | + RecoverPanic bool | ||
39 | + RecoverFunc func(*context.Context) | ||
40 | + CopyRequestBody bool | ||
41 | + EnableGzip bool | ||
42 | + MaxMemory int64 | ||
43 | + EnableErrorsShow bool | ||
44 | + EnableErrorsRender bool | ||
45 | + Listen Listen | ||
46 | + WebConfig WebConfig | ||
47 | + Log LogConfig | ||
48 | +} | ||
49 | + | ||
50 | +// Listen holds for http and https related config | ||
51 | +type Listen struct { | ||
52 | + Graceful bool // Graceful means use graceful module to start the server | ||
53 | + ServerTimeOut int64 | ||
54 | + ListenTCP4 bool | ||
55 | + EnableHTTP bool | ||
56 | + HTTPAddr string | ||
57 | + HTTPPort int | ||
58 | + AutoTLS bool | ||
59 | + Domains []string | ||
60 | + TLSCacheDir string | ||
61 | + EnableHTTPS bool | ||
62 | + EnableMutualHTTPS bool | ||
63 | + HTTPSAddr string | ||
64 | + HTTPSPort int | ||
65 | + HTTPSCertFile string | ||
66 | + HTTPSKeyFile string | ||
67 | + TrustCaFile string | ||
68 | + EnableAdmin bool | ||
69 | + AdminAddr string | ||
70 | + AdminPort int | ||
71 | + EnableFcgi bool | ||
72 | + EnableStdIo bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O | ||
73 | +} | ||
74 | + | ||
75 | +// WebConfig holds web related config | ||
76 | +type WebConfig struct { | ||
77 | + AutoRender bool | ||
78 | + EnableDocs bool | ||
79 | + FlashName string | ||
80 | + FlashSeparator string | ||
81 | + DirectoryIndex bool | ||
82 | + StaticDir map[string]string | ||
83 | + StaticExtensionsToGzip []string | ||
84 | + TemplateLeft string | ||
85 | + TemplateRight string | ||
86 | + ViewsPath string | ||
87 | + EnableXSRF bool | ||
88 | + XSRFKey string | ||
89 | + XSRFExpire int | ||
90 | + Session SessionConfig | ||
91 | +} | ||
92 | + | ||
93 | +// SessionConfig holds session related config | ||
94 | +type SessionConfig struct { | ||
95 | + SessionOn bool | ||
96 | + SessionProvider string | ||
97 | + SessionName string | ||
98 | + SessionGCMaxLifetime int64 | ||
99 | + SessionProviderConfig string | ||
100 | + SessionCookieLifeTime int | ||
101 | + SessionAutoSetCookie bool | ||
102 | + SessionDomain string | ||
103 | + SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies. | ||
104 | + SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers | ||
105 | + SessionNameInHTTPHeader string | ||
106 | + SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params | ||
107 | +} | ||
108 | + | ||
109 | +// LogConfig holds Log related config | ||
110 | +type LogConfig struct { | ||
111 | + AccessLogs bool | ||
112 | + EnableStaticLogs bool //log static files requests default: false | ||
113 | + AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string | ||
114 | + FileLineNum bool | ||
115 | + Outputs map[string]string // Store Adaptor : config | ||
116 | +} | ||
117 | + | ||
118 | +var ( | ||
119 | + // BConfig is the default config for Application | ||
120 | + BConfig *Config | ||
121 | + // AppConfig is the instance of Config, store the config information from file | ||
122 | + AppConfig *beegoAppConfig | ||
123 | + // AppPath is the absolute path to the app | ||
124 | + AppPath string | ||
125 | + // GlobalSessions is the instance for the session manager | ||
126 | + GlobalSessions *session.Manager | ||
127 | + | ||
128 | + // appConfigPath is the path to the config files | ||
129 | + appConfigPath string | ||
130 | + // appConfigProvider is the provider for the config, default is ini | ||
131 | + appConfigProvider = "ini" | ||
132 | +) | ||
133 | + | ||
134 | +func init() { | ||
135 | + BConfig = newBConfig() | ||
136 | + var err error | ||
137 | + if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil { | ||
138 | + panic(err) | ||
139 | + } | ||
140 | + workPath, err := os.Getwd() | ||
141 | + if err != nil { | ||
142 | + panic(err) | ||
143 | + } | ||
144 | + var filename = "app.conf" | ||
145 | + if os.Getenv("BEEGO_RUNMODE") != "" { | ||
146 | + filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf" | ||
147 | + } | ||
148 | + appConfigPath = filepath.Join(workPath, "conf", filename) | ||
149 | + if !utils.FileExists(appConfigPath) { | ||
150 | + appConfigPath = filepath.Join(AppPath, "conf", filename) | ||
151 | + if !utils.FileExists(appConfigPath) { | ||
152 | + AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} | ||
153 | + return | ||
154 | + } | ||
155 | + } | ||
156 | + if err = parseConfig(appConfigPath); err != nil { | ||
157 | + panic(err) | ||
158 | + } | ||
159 | +} | ||
160 | + | ||
161 | +func recoverPanic(ctx *context.Context) { | ||
162 | + if err := recover(); err != nil { | ||
163 | + if err == ErrAbort { | ||
164 | + return | ||
165 | + } | ||
166 | + if !BConfig.RecoverPanic { | ||
167 | + panic(err) | ||
168 | + } | ||
169 | + if BConfig.EnableErrorsShow { | ||
170 | + if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { | ||
171 | + exception(fmt.Sprint(err), ctx) | ||
172 | + return | ||
173 | + } | ||
174 | + } | ||
175 | + var stack string | ||
176 | + logs.Critical("the request url is ", ctx.Input.URL()) | ||
177 | + logs.Critical("Handler crashed with error", err) | ||
178 | + for i := 1; ; i++ { | ||
179 | + _, file, line, ok := runtime.Caller(i) | ||
180 | + if !ok { | ||
181 | + break | ||
182 | + } | ||
183 | + logs.Critical(fmt.Sprintf("%s:%d", file, line)) | ||
184 | + stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) | ||
185 | + } | ||
186 | + if BConfig.RunMode == DEV && BConfig.EnableErrorsRender { | ||
187 | + showErr(err, ctx, stack) | ||
188 | + } | ||
189 | + if ctx.Output.Status != 0 { | ||
190 | + ctx.ResponseWriter.WriteHeader(ctx.Output.Status) | ||
191 | + } else { | ||
192 | + ctx.ResponseWriter.WriteHeader(500) | ||
193 | + } | ||
194 | + } | ||
195 | +} | ||
196 | + | ||
197 | +func newBConfig() *Config { | ||
198 | + return &Config{ | ||
199 | + AppName: "beego", | ||
200 | + RunMode: PROD, | ||
201 | + RouterCaseSensitive: true, | ||
202 | + ServerName: "beegoServer:" + VERSION, | ||
203 | + RecoverPanic: true, | ||
204 | + RecoverFunc: recoverPanic, | ||
205 | + CopyRequestBody: false, | ||
206 | + EnableGzip: false, | ||
207 | + MaxMemory: 1 << 26, //64MB | ||
208 | + EnableErrorsShow: true, | ||
209 | + EnableErrorsRender: true, | ||
210 | + Listen: Listen{ | ||
211 | + Graceful: false, | ||
212 | + ServerTimeOut: 0, | ||
213 | + ListenTCP4: false, | ||
214 | + EnableHTTP: true, | ||
215 | + AutoTLS: false, | ||
216 | + Domains: []string{}, | ||
217 | + TLSCacheDir: ".", | ||
218 | + HTTPAddr: "", | ||
219 | + HTTPPort: 8080, | ||
220 | + EnableHTTPS: false, | ||
221 | + HTTPSAddr: "", | ||
222 | + HTTPSPort: 10443, | ||
223 | + HTTPSCertFile: "", | ||
224 | + HTTPSKeyFile: "", | ||
225 | + EnableAdmin: false, | ||
226 | + AdminAddr: "", | ||
227 | + AdminPort: 8088, | ||
228 | + EnableFcgi: false, | ||
229 | + EnableStdIo: false, | ||
230 | + }, | ||
231 | + WebConfig: WebConfig{ | ||
232 | + AutoRender: true, | ||
233 | + EnableDocs: false, | ||
234 | + FlashName: "BEEGO_FLASH", | ||
235 | + FlashSeparator: "BEEGOFLASH", | ||
236 | + DirectoryIndex: false, | ||
237 | + StaticDir: map[string]string{"/static": "static"}, | ||
238 | + StaticExtensionsToGzip: []string{".css", ".js"}, | ||
239 | + TemplateLeft: "{{", | ||
240 | + TemplateRight: "}}", | ||
241 | + ViewsPath: "views", | ||
242 | + EnableXSRF: false, | ||
243 | + XSRFKey: "beegoxsrf", | ||
244 | + XSRFExpire: 0, | ||
245 | + Session: SessionConfig{ | ||
246 | + SessionOn: false, | ||
247 | + SessionProvider: "memory", | ||
248 | + SessionName: "beegosessionID", | ||
249 | + SessionGCMaxLifetime: 3600, | ||
250 | + SessionProviderConfig: "", | ||
251 | + SessionDisableHTTPOnly: false, | ||
252 | + SessionCookieLifeTime: 0, //set cookie default is the browser life | ||
253 | + SessionAutoSetCookie: true, | ||
254 | + SessionDomain: "", | ||
255 | + SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers | ||
256 | + SessionNameInHTTPHeader: "Beegosessionid", | ||
257 | + SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params | ||
258 | + }, | ||
259 | + }, | ||
260 | + Log: LogConfig{ | ||
261 | + AccessLogs: false, | ||
262 | + EnableStaticLogs: false, | ||
263 | + AccessLogsFormat: "APACHE_FORMAT", | ||
264 | + FileLineNum: true, | ||
265 | + Outputs: map[string]string{"console": ""}, | ||
266 | + }, | ||
267 | + } | ||
268 | +} | ||
269 | + | ||
270 | +// now only support ini, next will support json. | ||
271 | +func parseConfig(appConfigPath string) (err error) { | ||
272 | + AppConfig, err = newAppConfig(appConfigProvider, appConfigPath) | ||
273 | + if err != nil { | ||
274 | + return err | ||
275 | + } | ||
276 | + return assignConfig(AppConfig) | ||
277 | +} | ||
278 | + | ||
279 | +func assignConfig(ac config.Configer) error { | ||
280 | + for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { | ||
281 | + assignSingleConfig(i, ac) | ||
282 | + } | ||
283 | + // set the run mode first | ||
284 | + if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" { | ||
285 | + BConfig.RunMode = envRunMode | ||
286 | + } else if runMode := ac.String("RunMode"); runMode != "" { | ||
287 | + BConfig.RunMode = runMode | ||
288 | + } | ||
289 | + | ||
290 | + if sd := ac.String("StaticDir"); sd != "" { | ||
291 | + BConfig.WebConfig.StaticDir = map[string]string{} | ||
292 | + sds := strings.Fields(sd) | ||
293 | + for _, v := range sds { | ||
294 | + if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { | ||
295 | + BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1] | ||
296 | + } else { | ||
297 | + BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0] | ||
298 | + } | ||
299 | + } | ||
300 | + } | ||
301 | + | ||
302 | + if sgz := ac.String("StaticExtensionsToGzip"); sgz != "" { | ||
303 | + extensions := strings.Split(sgz, ",") | ||
304 | + fileExts := []string{} | ||
305 | + for _, ext := range extensions { | ||
306 | + ext = strings.TrimSpace(ext) | ||
307 | + if ext == "" { | ||
308 | + continue | ||
309 | + } | ||
310 | + if !strings.HasPrefix(ext, ".") { | ||
311 | + ext = "." + ext | ||
312 | + } | ||
313 | + fileExts = append(fileExts, ext) | ||
314 | + } | ||
315 | + if len(fileExts) > 0 { | ||
316 | + BConfig.WebConfig.StaticExtensionsToGzip = fileExts | ||
317 | + } | ||
318 | + } | ||
319 | + | ||
320 | + if lo := ac.String("LogOutputs"); lo != "" { | ||
321 | + // if lo is not nil or empty | ||
322 | + // means user has set his own LogOutputs | ||
323 | + // clear the default setting to BConfig.Log.Outputs | ||
324 | + BConfig.Log.Outputs = make(map[string]string) | ||
325 | + los := strings.Split(lo, ";") | ||
326 | + for _, v := range los { | ||
327 | + if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 { | ||
328 | + BConfig.Log.Outputs[logType2Config[0]] = logType2Config[1] | ||
329 | + } else { | ||
330 | + continue | ||
331 | + } | ||
332 | + } | ||
333 | + } | ||
334 | + | ||
335 | + //init log | ||
336 | + logs.Reset() | ||
337 | + for adaptor, config := range BConfig.Log.Outputs { | ||
338 | + err := logs.SetLogger(adaptor, config) | ||
339 | + if err != nil { | ||
340 | + fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, config, err.Error())) | ||
341 | + } | ||
342 | + } | ||
343 | + logs.SetLogFuncCall(BConfig.Log.FileLineNum) | ||
344 | + | ||
345 | + return nil | ||
346 | +} | ||
347 | + | ||
348 | +func assignSingleConfig(p interface{}, ac config.Configer) { | ||
349 | + pt := reflect.TypeOf(p) | ||
350 | + if pt.Kind() != reflect.Ptr { | ||
351 | + return | ||
352 | + } | ||
353 | + pt = pt.Elem() | ||
354 | + if pt.Kind() != reflect.Struct { | ||
355 | + return | ||
356 | + } | ||
357 | + pv := reflect.ValueOf(p).Elem() | ||
358 | + | ||
359 | + for i := 0; i < pt.NumField(); i++ { | ||
360 | + pf := pv.Field(i) | ||
361 | + if !pf.CanSet() { | ||
362 | + continue | ||
363 | + } | ||
364 | + name := pt.Field(i).Name | ||
365 | + switch pf.Kind() { | ||
366 | + case reflect.String: | ||
367 | + pf.SetString(ac.DefaultString(name, pf.String())) | ||
368 | + case reflect.Int, reflect.Int64: | ||
369 | + pf.SetInt(ac.DefaultInt64(name, pf.Int())) | ||
370 | + case reflect.Bool: | ||
371 | + pf.SetBool(ac.DefaultBool(name, pf.Bool())) | ||
372 | + case reflect.Struct: | ||
373 | + default: | ||
374 | + //do nothing here | ||
375 | + } | ||
376 | + } | ||
377 | + | ||
378 | +} | ||
379 | + | ||
380 | +// LoadAppConfig allow developer to apply a config file | ||
381 | +func LoadAppConfig(adapterName, configPath string) error { | ||
382 | + absConfigPath, err := filepath.Abs(configPath) | ||
383 | + if err != nil { | ||
384 | + return err | ||
385 | + } | ||
386 | + | ||
387 | + if !utils.FileExists(absConfigPath) { | ||
388 | + return fmt.Errorf("the target config file: %s don't exist", configPath) | ||
389 | + } | ||
390 | + | ||
391 | + appConfigPath = absConfigPath | ||
392 | + appConfigProvider = adapterName | ||
393 | + | ||
394 | + return parseConfig(appConfigPath) | ||
395 | +} | ||
396 | + | ||
397 | +type beegoAppConfig struct { | ||
398 | + innerConfig config.Configer | ||
399 | +} | ||
400 | + | ||
401 | +func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) { | ||
402 | + ac, err := config.NewConfig(appConfigProvider, appConfigPath) | ||
403 | + if err != nil { | ||
404 | + return nil, err | ||
405 | + } | ||
406 | + return &beegoAppConfig{ac}, nil | ||
407 | +} | ||
408 | + | ||
409 | +func (b *beegoAppConfig) Set(key, val string) error { | ||
410 | + if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil { | ||
411 | + return err | ||
412 | + } | ||
413 | + return b.innerConfig.Set(key, val) | ||
414 | +} | ||
415 | + | ||
416 | +func (b *beegoAppConfig) String(key string) string { | ||
417 | + if v := b.innerConfig.String(BConfig.RunMode + "::" + key); v != "" { | ||
418 | + return v | ||
419 | + } | ||
420 | + return b.innerConfig.String(key) | ||
421 | +} | ||
422 | + | ||
423 | +func (b *beegoAppConfig) Strings(key string) []string { | ||
424 | + if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 { | ||
425 | + return v | ||
426 | + } | ||
427 | + return b.innerConfig.Strings(key) | ||
428 | +} | ||
429 | + | ||
430 | +func (b *beegoAppConfig) Int(key string) (int, error) { | ||
431 | + if v, err := b.innerConfig.Int(BConfig.RunMode + "::" + key); err == nil { | ||
432 | + return v, nil | ||
433 | + } | ||
434 | + return b.innerConfig.Int(key) | ||
435 | +} | ||
436 | + | ||
437 | +func (b *beegoAppConfig) Int64(key string) (int64, error) { | ||
438 | + if v, err := b.innerConfig.Int64(BConfig.RunMode + "::" + key); err == nil { | ||
439 | + return v, nil | ||
440 | + } | ||
441 | + return b.innerConfig.Int64(key) | ||
442 | +} | ||
443 | + | ||
444 | +func (b *beegoAppConfig) Bool(key string) (bool, error) { | ||
445 | + if v, err := b.innerConfig.Bool(BConfig.RunMode + "::" + key); err == nil { | ||
446 | + return v, nil | ||
447 | + } | ||
448 | + return b.innerConfig.Bool(key) | ||
449 | +} | ||
450 | + | ||
451 | +func (b *beegoAppConfig) Float(key string) (float64, error) { | ||
452 | + if v, err := b.innerConfig.Float(BConfig.RunMode + "::" + key); err == nil { | ||
453 | + return v, nil | ||
454 | + } | ||
455 | + return b.innerConfig.Float(key) | ||
456 | +} | ||
457 | + | ||
458 | +func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string { | ||
459 | + if v := b.String(key); v != "" { | ||
460 | + return v | ||
461 | + } | ||
462 | + return defaultVal | ||
463 | +} | ||
464 | + | ||
465 | +func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string { | ||
466 | + if v := b.Strings(key); len(v) != 0 { | ||
467 | + return v | ||
468 | + } | ||
469 | + return defaultVal | ||
470 | +} | ||
471 | + | ||
472 | +func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int { | ||
473 | + if v, err := b.Int(key); err == nil { | ||
474 | + return v | ||
475 | + } | ||
476 | + return defaultVal | ||
477 | +} | ||
478 | + | ||
479 | +func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 { | ||
480 | + if v, err := b.Int64(key); err == nil { | ||
481 | + return v | ||
482 | + } | ||
483 | + return defaultVal | ||
484 | +} | ||
485 | + | ||
486 | +func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool { | ||
487 | + if v, err := b.Bool(key); err == nil { | ||
488 | + return v | ||
489 | + } | ||
490 | + return defaultVal | ||
491 | +} | ||
492 | + | ||
493 | +func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 { | ||
494 | + if v, err := b.Float(key); err == nil { | ||
495 | + return v | ||
496 | + } | ||
497 | + return defaultVal | ||
498 | +} | ||
499 | + | ||
500 | +func (b *beegoAppConfig) DIY(key string) (interface{}, error) { | ||
501 | + return b.innerConfig.DIY(key) | ||
502 | +} | ||
503 | + | ||
504 | +func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) { | ||
505 | + return b.innerConfig.GetSection(section) | ||
506 | +} | ||
507 | + | ||
508 | +func (b *beegoAppConfig) SaveConfigFile(filename string) error { | ||
509 | + return b.innerConfig.SaveConfigFile(filename) | ||
510 | +} |
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +// Package config is used to parse config. | ||
16 | +// Usage: | ||
17 | +// import "github.com/astaxie/beego/config" | ||
18 | +//Examples. | ||
19 | +// | ||
20 | +// cnf, err := config.NewConfig("ini", "config.conf") | ||
21 | +// | ||
22 | +// cnf APIS: | ||
23 | +// | ||
24 | +// cnf.Set(key, val string) error | ||
25 | +// cnf.String(key string) string | ||
26 | +// cnf.Strings(key string) []string | ||
27 | +// cnf.Int(key string) (int, error) | ||
28 | +// cnf.Int64(key string) (int64, error) | ||
29 | +// cnf.Bool(key string) (bool, error) | ||
30 | +// cnf.Float(key string) (float64, error) | ||
31 | +// cnf.DefaultString(key string, defaultVal string) string | ||
32 | +// cnf.DefaultStrings(key string, defaultVal []string) []string | ||
33 | +// cnf.DefaultInt(key string, defaultVal int) int | ||
34 | +// cnf.DefaultInt64(key string, defaultVal int64) int64 | ||
35 | +// cnf.DefaultBool(key string, defaultVal bool) bool | ||
36 | +// cnf.DefaultFloat(key string, defaultVal float64) float64 | ||
37 | +// cnf.DIY(key string) (interface{}, error) | ||
38 | +// cnf.GetSection(section string) (map[string]string, error) | ||
39 | +// cnf.SaveConfigFile(filename string) error | ||
40 | +//More docs http://beego.me/docs/module/config.md | ||
41 | +package config | ||
42 | + | ||
43 | +import ( | ||
44 | + "fmt" | ||
45 | + "os" | ||
46 | + "reflect" | ||
47 | + "time" | ||
48 | +) | ||
49 | + | ||
50 | +// Configer defines how to get and set value from configuration raw data. | ||
51 | +type Configer interface { | ||
52 | + Set(key, val string) error //support section::key type in given key when using ini type. | ||
53 | + String(key string) string //support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same. | ||
54 | + Strings(key string) []string //get string slice | ||
55 | + Int(key string) (int, error) | ||
56 | + Int64(key string) (int64, error) | ||
57 | + Bool(key string) (bool, error) | ||
58 | + Float(key string) (float64, error) | ||
59 | + DefaultString(key string, defaultVal string) string // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same. | ||
60 | + DefaultStrings(key string, defaultVal []string) []string //get string slice | ||
61 | + DefaultInt(key string, defaultVal int) int | ||
62 | + DefaultInt64(key string, defaultVal int64) int64 | ||
63 | + DefaultBool(key string, defaultVal bool) bool | ||
64 | + DefaultFloat(key string, defaultVal float64) float64 | ||
65 | + DIY(key string) (interface{}, error) | ||
66 | + GetSection(section string) (map[string]string, error) | ||
67 | + SaveConfigFile(filename string) error | ||
68 | +} | ||
69 | + | ||
70 | +// Config is the adapter interface for parsing config file to get raw data to Configer. | ||
71 | +type Config interface { | ||
72 | + Parse(key string) (Configer, error) | ||
73 | + ParseData(data []byte) (Configer, error) | ||
74 | +} | ||
75 | + | ||
76 | +var adapters = make(map[string]Config) | ||
77 | + | ||
78 | +// Register makes a config adapter available by the adapter name. | ||
79 | +// If Register is called twice with the same name or if driver is nil, | ||
80 | +// it panics. | ||
81 | +func Register(name string, adapter Config) { | ||
82 | + if adapter == nil { | ||
83 | + panic("config: Register adapter is nil") | ||
84 | + } | ||
85 | + if _, ok := adapters[name]; ok { | ||
86 | + panic("config: Register called twice for adapter " + name) | ||
87 | + } | ||
88 | + adapters[name] = adapter | ||
89 | +} | ||
90 | + | ||
91 | +// NewConfig adapterName is ini/json/xml/yaml. | ||
92 | +// filename is the config file path. | ||
93 | +func NewConfig(adapterName, filename string) (Configer, error) { | ||
94 | + adapter, ok := adapters[adapterName] | ||
95 | + if !ok { | ||
96 | + return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName) | ||
97 | + } | ||
98 | + return adapter.Parse(filename) | ||
99 | +} | ||
100 | + | ||
101 | +// NewConfigData adapterName is ini/json/xml/yaml. | ||
102 | +// data is the config data. | ||
103 | +func NewConfigData(adapterName string, data []byte) (Configer, error) { | ||
104 | + adapter, ok := adapters[adapterName] | ||
105 | + if !ok { | ||
106 | + return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName) | ||
107 | + } | ||
108 | + return adapter.ParseData(data) | ||
109 | +} | ||
110 | + | ||
111 | +// ExpandValueEnvForMap convert all string value with environment variable. | ||
112 | +func ExpandValueEnvForMap(m map[string]interface{}) map[string]interface{} { | ||
113 | + for k, v := range m { | ||
114 | + switch value := v.(type) { | ||
115 | + case string: | ||
116 | + m[k] = ExpandValueEnv(value) | ||
117 | + case map[string]interface{}: | ||
118 | + m[k] = ExpandValueEnvForMap(value) | ||
119 | + case map[string]string: | ||
120 | + for k2, v2 := range value { | ||
121 | + value[k2] = ExpandValueEnv(v2) | ||
122 | + } | ||
123 | + m[k] = value | ||
124 | + } | ||
125 | + } | ||
126 | + return m | ||
127 | +} | ||
128 | + | ||
129 | +// ExpandValueEnv returns value of convert with environment variable. | ||
130 | +// | ||
131 | +// Return environment variable if value start with "${" and end with "}". | ||
132 | +// Return default value if environment variable is empty or not exist. | ||
133 | +// | ||
134 | +// It accept value formats "${env}" , "${env||}}" , "${env||defaultValue}" , "defaultvalue". | ||
135 | +// Examples: | ||
136 | +// v1 := config.ExpandValueEnv("${GOPATH}") // return the GOPATH environment variable. | ||
137 | +// v2 := config.ExpandValueEnv("${GOAsta||/usr/local/go}") // return the default value "/usr/local/go/". | ||
138 | +// v3 := config.ExpandValueEnv("Astaxie") // return the value "Astaxie". | ||
139 | +func ExpandValueEnv(value string) (realValue string) { | ||
140 | + realValue = value | ||
141 | + | ||
142 | + vLen := len(value) | ||
143 | + // 3 = ${} | ||
144 | + if vLen < 3 { | ||
145 | + return | ||
146 | + } | ||
147 | + // Need start with "${" and end with "}", then return. | ||
148 | + if value[0] != '$' || value[1] != '{' || value[vLen-1] != '}' { | ||
149 | + return | ||
150 | + } | ||
151 | + | ||
152 | + key := "" | ||
153 | + defaultV := "" | ||
154 | + // value start with "${" | ||
155 | + for i := 2; i < vLen; i++ { | ||
156 | + if value[i] == '|' && (i+1 < vLen && value[i+1] == '|') { | ||
157 | + key = value[2:i] | ||
158 | + defaultV = value[i+2 : vLen-1] // other string is default value. | ||
159 | + break | ||
160 | + } else if value[i] == '}' { | ||
161 | + key = value[2:i] | ||
162 | + break | ||
163 | + } | ||
164 | + } | ||
165 | + | ||
166 | + realValue = os.Getenv(key) | ||
167 | + if realValue == "" { | ||
168 | + realValue = defaultV | ||
169 | + } | ||
170 | + | ||
171 | + return | ||
172 | +} | ||
173 | + | ||
174 | +// ParseBool returns the boolean value represented by the string. | ||
175 | +// | ||
176 | +// It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On, | ||
177 | +// 0, 0.0, f, F, FALSE, false, False, NO, no, No, N,n, OFF, off, Off. | ||
178 | +// Any other value returns an error. | ||
179 | +func ParseBool(val interface{}) (value bool, err error) { | ||
180 | + if val != nil { | ||
181 | + switch v := val.(type) { | ||
182 | + case bool: | ||
183 | + return v, nil | ||
184 | + case string: | ||
185 | + switch v { | ||
186 | + case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "Y", "y", "ON", "on", "On": | ||
187 | + return true, nil | ||
188 | + case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "N", "n", "OFF", "off", "Off": | ||
189 | + return false, nil | ||
190 | + } | ||
191 | + case int8, int32, int64: | ||
192 | + strV := fmt.Sprintf("%d", v) | ||
193 | + if strV == "1" { | ||
194 | + return true, nil | ||
195 | + } else if strV == "0" { | ||
196 | + return false, nil | ||
197 | + } | ||
198 | + case float64: | ||
199 | + if v == 1.0 { | ||
200 | + return true, nil | ||
201 | + } else if v == 0.0 { | ||
202 | + return false, nil | ||
203 | + } | ||
204 | + } | ||
205 | + return false, fmt.Errorf("parsing %q: invalid syntax", val) | ||
206 | + } | ||
207 | + return false, fmt.Errorf("parsing <nil>: invalid syntax") | ||
208 | +} | ||
209 | + | ||
210 | +// ToString converts values of any type to string. | ||
211 | +func ToString(x interface{}) string { | ||
212 | + switch y := x.(type) { | ||
213 | + | ||
214 | + // Handle dates with special logic | ||
215 | + // This needs to come above the fmt.Stringer | ||
216 | + // test since time.Time's have a .String() | ||
217 | + // method | ||
218 | + case time.Time: | ||
219 | + return y.Format("A Monday") | ||
220 | + | ||
221 | + // Handle type string | ||
222 | + case string: | ||
223 | + return y | ||
224 | + | ||
225 | + // Handle type with .String() method | ||
226 | + case fmt.Stringer: | ||
227 | + return y.String() | ||
228 | + | ||
229 | + // Handle type with .Error() method | ||
230 | + case error: | ||
231 | + return y.Error() | ||
232 | + | ||
233 | + } | ||
234 | + | ||
235 | + // Handle named string type | ||
236 | + if v := reflect.ValueOf(x); v.Kind() == reflect.String { | ||
237 | + return v.String() | ||
238 | + } | ||
239 | + | ||
240 | + // Fallback to fmt package for anything else like numeric types | ||
241 | + return fmt.Sprint(x) | ||
242 | +} |
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package config | ||
16 | + | ||
17 | +import ( | ||
18 | + "errors" | ||
19 | + "strconv" | ||
20 | + "strings" | ||
21 | +) | ||
22 | + | ||
23 | +type fakeConfigContainer struct { | ||
24 | + data map[string]string | ||
25 | +} | ||
26 | + | ||
27 | +func (c *fakeConfigContainer) getData(key string) string { | ||
28 | + return c.data[strings.ToLower(key)] | ||
29 | +} | ||
30 | + | ||
31 | +func (c *fakeConfigContainer) Set(key, val string) error { | ||
32 | + c.data[strings.ToLower(key)] = val | ||
33 | + return nil | ||
34 | +} | ||
35 | + | ||
36 | +func (c *fakeConfigContainer) String(key string) string { | ||
37 | + return c.getData(key) | ||
38 | +} | ||
39 | + | ||
40 | +func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string { | ||
41 | + v := c.String(key) | ||
42 | + if v == "" { | ||
43 | + return defaultval | ||
44 | + } | ||
45 | + return v | ||
46 | +} | ||
47 | + | ||
48 | +func (c *fakeConfigContainer) Strings(key string) []string { | ||
49 | + v := c.String(key) | ||
50 | + if v == "" { | ||
51 | + return nil | ||
52 | + } | ||
53 | + return strings.Split(v, ";") | ||
54 | +} | ||
55 | + | ||
56 | +func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []string { | ||
57 | + v := c.Strings(key) | ||
58 | + if v == nil { | ||
59 | + return defaultval | ||
60 | + } | ||
61 | + return v | ||
62 | +} | ||
63 | + | ||
64 | +func (c *fakeConfigContainer) Int(key string) (int, error) { | ||
65 | + return strconv.Atoi(c.getData(key)) | ||
66 | +} | ||
67 | + | ||
68 | +func (c *fakeConfigContainer) DefaultInt(key string, defaultval int) int { | ||
69 | + v, err := c.Int(key) | ||
70 | + if err != nil { | ||
71 | + return defaultval | ||
72 | + } | ||
73 | + return v | ||
74 | +} | ||
75 | + | ||
76 | +func (c *fakeConfigContainer) Int64(key string) (int64, error) { | ||
77 | + return strconv.ParseInt(c.getData(key), 10, 64) | ||
78 | +} | ||
79 | + | ||
80 | +func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 { | ||
81 | + v, err := c.Int64(key) | ||
82 | + if err != nil { | ||
83 | + return defaultval | ||
84 | + } | ||
85 | + return v | ||
86 | +} | ||
87 | + | ||
88 | +func (c *fakeConfigContainer) Bool(key string) (bool, error) { | ||
89 | + return ParseBool(c.getData(key)) | ||
90 | +} | ||
91 | + | ||
92 | +func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool { | ||
93 | + v, err := c.Bool(key) | ||
94 | + if err != nil { | ||
95 | + return defaultval | ||
96 | + } | ||
97 | + return v | ||
98 | +} | ||
99 | + | ||
100 | +func (c *fakeConfigContainer) Float(key string) (float64, error) { | ||
101 | + return strconv.ParseFloat(c.getData(key), 64) | ||
102 | +} | ||
103 | + | ||
104 | +func (c *fakeConfigContainer) DefaultFloat(key string, defaultval float64) float64 { | ||
105 | + v, err := c.Float(key) | ||
106 | + if err != nil { | ||
107 | + return defaultval | ||
108 | + } | ||
109 | + return v | ||
110 | +} | ||
111 | + | ||
112 | +func (c *fakeConfigContainer) DIY(key string) (interface{}, error) { | ||
113 | + if v, ok := c.data[strings.ToLower(key)]; ok { | ||
114 | + return v, nil | ||
115 | + } | ||
116 | + return nil, errors.New("key not find") | ||
117 | +} | ||
118 | + | ||
119 | +func (c *fakeConfigContainer) GetSection(section string) (map[string]string, error) { | ||
120 | + return nil, errors.New("not implement in the fakeConfigContainer") | ||
121 | +} | ||
122 | + | ||
123 | +func (c *fakeConfigContainer) SaveConfigFile(filename string) error { | ||
124 | + return errors.New("not implement in the fakeConfigContainer") | ||
125 | +} | ||
126 | + | ||
127 | +var _ Configer = new(fakeConfigContainer) | ||
128 | + | ||
129 | +// NewFakeConfig return a fake Configer | ||
130 | +func NewFakeConfig() Configer { | ||
131 | + return &fakeConfigContainer{ | ||
132 | + data: make(map[string]string), | ||
133 | + } | ||
134 | +} |
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package config | ||
16 | + | ||
17 | +import ( | ||
18 | + "bufio" | ||
19 | + "bytes" | ||
20 | + "errors" | ||
21 | + "io" | ||
22 | + "io/ioutil" | ||
23 | + "os" | ||
24 | + "os/user" | ||
25 | + "path/filepath" | ||
26 | + "strconv" | ||
27 | + "strings" | ||
28 | + "sync" | ||
29 | +) | ||
30 | + | ||
31 | +var ( | ||
32 | + defaultSection = "default" // default section means if some ini items not in a section, make them in default section, | ||
33 | + bNumComment = []byte{'#'} // number signal | ||
34 | + bSemComment = []byte{';'} // semicolon signal | ||
35 | + bEmpty = []byte{} | ||
36 | + bEqual = []byte{'='} // equal signal | ||
37 | + bDQuote = []byte{'"'} // quote signal | ||
38 | + sectionStart = []byte{'['} // section start signal | ||
39 | + sectionEnd = []byte{']'} // section end signal | ||
40 | + lineBreak = "\n" | ||
41 | +) | ||
42 | + | ||
43 | +// IniConfig implements Config to parse ini file. | ||
44 | +type IniConfig struct { | ||
45 | +} | ||
46 | + | ||
47 | +// Parse creates a new Config and parses the file configuration from the named file. | ||
48 | +func (ini *IniConfig) Parse(name string) (Configer, error) { | ||
49 | + return ini.parseFile(name) | ||
50 | +} | ||
51 | + | ||
52 | +func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { | ||
53 | + data, err := ioutil.ReadFile(name) | ||
54 | + if err != nil { | ||
55 | + return nil, err | ||
56 | + } | ||
57 | + | ||
58 | + return ini.parseData(filepath.Dir(name), data) | ||
59 | +} | ||
60 | + | ||
61 | +func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, error) { | ||
62 | + cfg := &IniConfigContainer{ | ||
63 | + data: make(map[string]map[string]string), | ||
64 | + sectionComment: make(map[string]string), | ||
65 | + keyComment: make(map[string]string), | ||
66 | + RWMutex: sync.RWMutex{}, | ||
67 | + } | ||
68 | + cfg.Lock() | ||
69 | + defer cfg.Unlock() | ||
70 | + | ||
71 | + var comment bytes.Buffer | ||
72 | + buf := bufio.NewReader(bytes.NewBuffer(data)) | ||
73 | + // check the BOM | ||
74 | + head, err := buf.Peek(3) | ||
75 | + if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 { | ||
76 | + for i := 1; i <= 3; i++ { | ||
77 | + buf.ReadByte() | ||
78 | + } | ||
79 | + } | ||
80 | + section := defaultSection | ||
81 | + for { | ||
82 | + line, _, err := buf.ReadLine() | ||
83 | + if err == io.EOF { | ||
84 | + break | ||
85 | + } | ||
86 | + //It might be a good idea to throw a error on all unknonw errors? | ||
87 | + if _, ok := err.(*os.PathError); ok { | ||
88 | + return nil, err | ||
89 | + } | ||
90 | + line = bytes.TrimSpace(line) | ||
91 | + if bytes.Equal(line, bEmpty) { | ||
92 | + continue | ||
93 | + } | ||
94 | + var bComment []byte | ||
95 | + switch { | ||
96 | + case bytes.HasPrefix(line, bNumComment): | ||
97 | + bComment = bNumComment | ||
98 | + case bytes.HasPrefix(line, bSemComment): | ||
99 | + bComment = bSemComment | ||
100 | + } | ||
101 | + if bComment != nil { | ||
102 | + line = bytes.TrimLeft(line, string(bComment)) | ||
103 | + // Need append to a new line if multi-line comments. | ||
104 | + if comment.Len() > 0 { | ||
105 | + comment.WriteByte('\n') | ||
106 | + } | ||
107 | + comment.Write(line) | ||
108 | + continue | ||
109 | + } | ||
110 | + | ||
111 | + if bytes.HasPrefix(line, sectionStart) && bytes.HasSuffix(line, sectionEnd) { | ||
112 | + section = strings.ToLower(string(line[1 : len(line)-1])) // section name case insensitive | ||
113 | + if comment.Len() > 0 { | ||
114 | + cfg.sectionComment[section] = comment.String() | ||
115 | + comment.Reset() | ||
116 | + } | ||
117 | + if _, ok := cfg.data[section]; !ok { | ||
118 | + cfg.data[section] = make(map[string]string) | ||
119 | + } | ||
120 | + continue | ||
121 | + } | ||
122 | + | ||
123 | + if _, ok := cfg.data[section]; !ok { | ||
124 | + cfg.data[section] = make(map[string]string) | ||
125 | + } | ||
126 | + keyValue := bytes.SplitN(line, bEqual, 2) | ||
127 | + | ||
128 | + key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive | ||
129 | + key = strings.ToLower(key) | ||
130 | + | ||
131 | + // handle include "other.conf" | ||
132 | + if len(keyValue) == 1 && strings.HasPrefix(key, "include") { | ||
133 | + | ||
134 | + includefiles := strings.Fields(key) | ||
135 | + if includefiles[0] == "include" && len(includefiles) == 2 { | ||
136 | + | ||
137 | + otherfile := strings.Trim(includefiles[1], "\"") | ||
138 | + if !filepath.IsAbs(otherfile) { | ||
139 | + otherfile = filepath.Join(dir, otherfile) | ||
140 | + } | ||
141 | + | ||
142 | + i, err := ini.parseFile(otherfile) | ||
143 | + if err != nil { | ||
144 | + return nil, err | ||
145 | + } | ||
146 | + | ||
147 | + for sec, dt := range i.data { | ||
148 | + if _, ok := cfg.data[sec]; !ok { | ||
149 | + cfg.data[sec] = make(map[string]string) | ||
150 | + } | ||
151 | + for k, v := range dt { | ||
152 | + cfg.data[sec][k] = v | ||
153 | + } | ||
154 | + } | ||
155 | + | ||
156 | + for sec, comm := range i.sectionComment { | ||
157 | + cfg.sectionComment[sec] = comm | ||
158 | + } | ||
159 | + | ||
160 | + for k, comm := range i.keyComment { | ||
161 | + cfg.keyComment[k] = comm | ||
162 | + } | ||
163 | + | ||
164 | + continue | ||
165 | + } | ||
166 | + } | ||
167 | + | ||
168 | + if len(keyValue) != 2 { | ||
169 | + return nil, errors.New("read the content error: \"" + string(line) + "\", should key = val") | ||
170 | + } | ||
171 | + val := bytes.TrimSpace(keyValue[1]) | ||
172 | + if bytes.HasPrefix(val, bDQuote) { | ||
173 | + val = bytes.Trim(val, `"`) | ||
174 | + } | ||
175 | + | ||
176 | + cfg.data[section][key] = ExpandValueEnv(string(val)) | ||
177 | + if comment.Len() > 0 { | ||
178 | + cfg.keyComment[section+"."+key] = comment.String() | ||
179 | + comment.Reset() | ||
180 | + } | ||
181 | + | ||
182 | + } | ||
183 | + return cfg, nil | ||
184 | +} | ||
185 | + | ||
186 | +// ParseData parse ini the data | ||
187 | +// When include other.conf,other.conf is either absolute directory | ||
188 | +// or under beego in default temporary directory(/tmp/beego[-username]). | ||
189 | +func (ini *IniConfig) ParseData(data []byte) (Configer, error) { | ||
190 | + dir := "beego" | ||
191 | + currentUser, err := user.Current() | ||
192 | + if err == nil { | ||
193 | + dir = "beego-" + currentUser.Username | ||
194 | + } | ||
195 | + dir = filepath.Join(os.TempDir(), dir) | ||
196 | + if err = os.MkdirAll(dir, os.ModePerm); err != nil { | ||
197 | + return nil, err | ||
198 | + } | ||
199 | + | ||
200 | + return ini.parseData(dir, data) | ||
201 | +} | ||
202 | + | ||
203 | +// IniConfigContainer A Config represents the ini configuration. | ||
204 | +// When set and get value, support key as section:name type. | ||
205 | +type IniConfigContainer struct { | ||
206 | + data map[string]map[string]string // section=> key:val | ||
207 | + sectionComment map[string]string // section : comment | ||
208 | + keyComment map[string]string // id: []{comment, key...}; id 1 is for main comment. | ||
209 | + sync.RWMutex | ||
210 | +} | ||
211 | + | ||
212 | +// Bool returns the boolean value for a given key. | ||
213 | +func (c *IniConfigContainer) Bool(key string) (bool, error) { | ||
214 | + return ParseBool(c.getdata(key)) | ||
215 | +} | ||
216 | + | ||
217 | +// DefaultBool returns the boolean value for a given key. | ||
218 | +// if err != nil return defaultval | ||
219 | +func (c *IniConfigContainer) DefaultBool(key string, defaultval bool) bool { | ||
220 | + v, err := c.Bool(key) | ||
221 | + if err != nil { | ||
222 | + return defaultval | ||
223 | + } | ||
224 | + return v | ||
225 | +} | ||
226 | + | ||
227 | +// Int returns the integer value for a given key. | ||
228 | +func (c *IniConfigContainer) Int(key string) (int, error) { | ||
229 | + return strconv.Atoi(c.getdata(key)) | ||
230 | +} | ||
231 | + | ||
232 | +// DefaultInt returns the integer value for a given key. | ||
233 | +// if err != nil return defaultval | ||
234 | +func (c *IniConfigContainer) DefaultInt(key string, defaultval int) int { | ||
235 | + v, err := c.Int(key) | ||
236 | + if err != nil { | ||
237 | + return defaultval | ||
238 | + } | ||
239 | + return v | ||
240 | +} | ||
241 | + | ||
242 | +// Int64 returns the int64 value for a given key. | ||
243 | +func (c *IniConfigContainer) Int64(key string) (int64, error) { | ||
244 | + return strconv.ParseInt(c.getdata(key), 10, 64) | ||
245 | +} | ||
246 | + | ||
247 | +// DefaultInt64 returns the int64 value for a given key. | ||
248 | +// if err != nil return defaultval | ||
249 | +func (c *IniConfigContainer) DefaultInt64(key string, defaultval int64) int64 { | ||
250 | + v, err := c.Int64(key) | ||
251 | + if err != nil { | ||
252 | + return defaultval | ||
253 | + } | ||
254 | + return v | ||
255 | +} | ||
256 | + | ||
257 | +// Float returns the float value for a given key. | ||
258 | +func (c *IniConfigContainer) Float(key string) (float64, error) { | ||
259 | + return strconv.ParseFloat(c.getdata(key), 64) | ||
260 | +} | ||
261 | + | ||
262 | +// DefaultFloat returns the float64 value for a given key. | ||
263 | +// if err != nil return defaultval | ||
264 | +func (c *IniConfigContainer) DefaultFloat(key string, defaultval float64) float64 { | ||
265 | + v, err := c.Float(key) | ||
266 | + if err != nil { | ||
267 | + return defaultval | ||
268 | + } | ||
269 | + return v | ||
270 | +} | ||
271 | + | ||
272 | +// String returns the string value for a given key. | ||
273 | +func (c *IniConfigContainer) String(key string) string { | ||
274 | + return c.getdata(key) | ||
275 | +} | ||
276 | + | ||
277 | +// DefaultString returns the string value for a given key. | ||
278 | +// if err != nil return defaultval | ||
279 | +func (c *IniConfigContainer) DefaultString(key string, defaultval string) string { | ||
280 | + v := c.String(key) | ||
281 | + if v == "" { | ||
282 | + return defaultval | ||
283 | + } | ||
284 | + return v | ||
285 | +} | ||
286 | + | ||
287 | +// Strings returns the []string value for a given key. | ||
288 | +// Return nil if config value does not exist or is empty. | ||
289 | +func (c *IniConfigContainer) Strings(key string) []string { | ||
290 | + v := c.String(key) | ||
291 | + if v == "" { | ||
292 | + return nil | ||
293 | + } | ||
294 | + return strings.Split(v, ";") | ||
295 | +} | ||
296 | + | ||
297 | +// DefaultStrings returns the []string value for a given key. | ||
298 | +// if err != nil return defaultval | ||
299 | +func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []string { | ||
300 | + v := c.Strings(key) | ||
301 | + if v == nil { | ||
302 | + return defaultval | ||
303 | + } | ||
304 | + return v | ||
305 | +} | ||
306 | + | ||
307 | +// GetSection returns map for the given section | ||
308 | +func (c *IniConfigContainer) GetSection(section string) (map[string]string, error) { | ||
309 | + if v, ok := c.data[section]; ok { | ||
310 | + return v, nil | ||
311 | + } | ||
312 | + return nil, errors.New("not exist section") | ||
313 | +} | ||
314 | + | ||
315 | +// SaveConfigFile save the config into file. | ||
316 | +// | ||
317 | +// BUG(env): The environment variable config item will be saved with real value in SaveConfigFile Function. | ||
318 | +func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { | ||
319 | + // Write configuration file by filename. | ||
320 | + f, err := os.Create(filename) | ||
321 | + if err != nil { | ||
322 | + return err | ||
323 | + } | ||
324 | + defer f.Close() | ||
325 | + | ||
326 | + // Get section or key comments. Fixed #1607 | ||
327 | + getCommentStr := func(section, key string) string { | ||
328 | + var ( | ||
329 | + comment string | ||
330 | + ok bool | ||
331 | + ) | ||
332 | + if len(key) == 0 { | ||
333 | + comment, ok = c.sectionComment[section] | ||
334 | + } else { | ||
335 | + comment, ok = c.keyComment[section+"."+key] | ||
336 | + } | ||
337 | + | ||
338 | + if ok { | ||
339 | + // Empty comment | ||
340 | + if len(comment) == 0 || len(strings.TrimSpace(comment)) == 0 { | ||
341 | + return string(bNumComment) | ||
342 | + } | ||
343 | + prefix := string(bNumComment) | ||
344 | + // Add the line head character "#" | ||
345 | + return prefix + strings.Replace(comment, lineBreak, lineBreak+prefix, -1) | ||
346 | + } | ||
347 | + return "" | ||
348 | + } | ||
349 | + | ||
350 | + buf := bytes.NewBuffer(nil) | ||
351 | + // Save default section at first place | ||
352 | + if dt, ok := c.data[defaultSection]; ok { | ||
353 | + for key, val := range dt { | ||
354 | + if key != " " { | ||
355 | + // Write key comments. | ||
356 | + if v := getCommentStr(defaultSection, key); len(v) > 0 { | ||
357 | + if _, err = buf.WriteString(v + lineBreak); err != nil { | ||
358 | + return err | ||
359 | + } | ||
360 | + } | ||
361 | + | ||
362 | + // Write key and value. | ||
363 | + if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil { | ||
364 | + return err | ||
365 | + } | ||
366 | + } | ||
367 | + } | ||
368 | + | ||
369 | + // Put a line between sections. | ||
370 | + if _, err = buf.WriteString(lineBreak); err != nil { | ||
371 | + return err | ||
372 | + } | ||
373 | + } | ||
374 | + // Save named sections | ||
375 | + for section, dt := range c.data { | ||
376 | + if section != defaultSection { | ||
377 | + // Write section comments. | ||
378 | + if v := getCommentStr(section, ""); len(v) > 0 { | ||
379 | + if _, err = buf.WriteString(v + lineBreak); err != nil { | ||
380 | + return err | ||
381 | + } | ||
382 | + } | ||
383 | + | ||
384 | + // Write section name. | ||
385 | + if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil { | ||
386 | + return err | ||
387 | + } | ||
388 | + | ||
389 | + for key, val := range dt { | ||
390 | + if key != " " { | ||
391 | + // Write key comments. | ||
392 | + if v := getCommentStr(section, key); len(v) > 0 { | ||
393 | + if _, err = buf.WriteString(v + lineBreak); err != nil { | ||
394 | + return err | ||
395 | + } | ||
396 | + } | ||
397 | + | ||
398 | + // Write key and value. | ||
399 | + if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil { | ||
400 | + return err | ||
401 | + } | ||
402 | + } | ||
403 | + } | ||
404 | + | ||
405 | + // Put a line between sections. | ||
406 | + if _, err = buf.WriteString(lineBreak); err != nil { | ||
407 | + return err | ||
408 | + } | ||
409 | + } | ||
410 | + } | ||
411 | + _, err = buf.WriteTo(f) | ||
412 | + return err | ||
413 | +} | ||
414 | + | ||
415 | +// Set writes a new value for key. | ||
416 | +// if write to one section, the key need be "section::key". | ||
417 | +// if the section is not existed, it panics. | ||
418 | +func (c *IniConfigContainer) Set(key, value string) error { | ||
419 | + c.Lock() | ||
420 | + defer c.Unlock() | ||
421 | + if len(key) == 0 { | ||
422 | + return errors.New("key is empty") | ||
423 | + } | ||
424 | + | ||
425 | + var ( | ||
426 | + section, k string | ||
427 | + sectionKey = strings.Split(strings.ToLower(key), "::") | ||
428 | + ) | ||
429 | + | ||
430 | + if len(sectionKey) >= 2 { | ||
431 | + section = sectionKey[0] | ||
432 | + k = sectionKey[1] | ||
433 | + } else { | ||
434 | + section = defaultSection | ||
435 | + k = sectionKey[0] | ||
436 | + } | ||
437 | + | ||
438 | + if _, ok := c.data[section]; !ok { | ||
439 | + c.data[section] = make(map[string]string) | ||
440 | + } | ||
441 | + c.data[section][k] = value | ||
442 | + return nil | ||
443 | +} | ||
444 | + | ||
445 | +// DIY returns the raw value by a given key. | ||
446 | +func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) { | ||
447 | + if v, ok := c.data[strings.ToLower(key)]; ok { | ||
448 | + return v, nil | ||
449 | + } | ||
450 | + return v, errors.New("key not find") | ||
451 | +} | ||
452 | + | ||
453 | +// section.key or key | ||
454 | +func (c *IniConfigContainer) getdata(key string) string { | ||
455 | + if len(key) == 0 { | ||
456 | + return "" | ||
457 | + } | ||
458 | + c.RLock() | ||
459 | + defer c.RUnlock() | ||
460 | + | ||
461 | + var ( | ||
462 | + section, k string | ||
463 | + sectionKey = strings.Split(strings.ToLower(key), "::") | ||
464 | + ) | ||
465 | + if len(sectionKey) >= 2 { | ||
466 | + section = sectionKey[0] | ||
467 | + k = sectionKey[1] | ||
468 | + } else { | ||
469 | + section = defaultSection | ||
470 | + k = sectionKey[0] | ||
471 | + } | ||
472 | + if v, ok := c.data[section]; ok { | ||
473 | + if vv, ok := v[k]; ok { | ||
474 | + return vv | ||
475 | + } | ||
476 | + } | ||
477 | + return "" | ||
478 | +} | ||
479 | + | ||
480 | +func init() { | ||
481 | + Register("ini", &IniConfig{}) | ||
482 | +} |
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package config | ||
16 | + | ||
17 | +import ( | ||
18 | + "encoding/json" | ||
19 | + "errors" | ||
20 | + "fmt" | ||
21 | + "io/ioutil" | ||
22 | + "os" | ||
23 | + "strings" | ||
24 | + "sync" | ||
25 | +) | ||
26 | + | ||
27 | +// JSONConfig is a json config parser and implements Config interface. | ||
28 | +type JSONConfig struct { | ||
29 | +} | ||
30 | + | ||
31 | +// Parse returns a ConfigContainer with parsed json config map. | ||
32 | +func (js *JSONConfig) Parse(filename string) (Configer, error) { | ||
33 | + file, err := os.Open(filename) | ||
34 | + if err != nil { | ||
35 | + return nil, err | ||
36 | + } | ||
37 | + defer file.Close() | ||
38 | + content, err := ioutil.ReadAll(file) | ||
39 | + if err != nil { | ||
40 | + return nil, err | ||
41 | + } | ||
42 | + | ||
43 | + return js.ParseData(content) | ||
44 | +} | ||
45 | + | ||
46 | +// ParseData returns a ConfigContainer with json string | ||
47 | +func (js *JSONConfig) ParseData(data []byte) (Configer, error) { | ||
48 | + x := &JSONConfigContainer{ | ||
49 | + data: make(map[string]interface{}), | ||
50 | + } | ||
51 | + err := json.Unmarshal(data, &x.data) | ||
52 | + if err != nil { | ||
53 | + var wrappingArray []interface{} | ||
54 | + err2 := json.Unmarshal(data, &wrappingArray) | ||
55 | + if err2 != nil { | ||
56 | + return nil, err | ||
57 | + } | ||
58 | + x.data["rootArray"] = wrappingArray | ||
59 | + } | ||
60 | + | ||
61 | + x.data = ExpandValueEnvForMap(x.data) | ||
62 | + | ||
63 | + return x, nil | ||
64 | +} | ||
65 | + | ||
66 | +// JSONConfigContainer A Config represents the json configuration. | ||
67 | +// Only when get value, support key as section:name type. | ||
68 | +type JSONConfigContainer struct { | ||
69 | + data map[string]interface{} | ||
70 | + sync.RWMutex | ||
71 | +} | ||
72 | + | ||
73 | +// Bool returns the boolean value for a given key. | ||
74 | +func (c *JSONConfigContainer) Bool(key string) (bool, error) { | ||
75 | + val := c.getData(key) | ||
76 | + if val != nil { | ||
77 | + return ParseBool(val) | ||
78 | + } | ||
79 | + return false, fmt.Errorf("not exist key: %q", key) | ||
80 | +} | ||
81 | + | ||
82 | +// DefaultBool return the bool value if has no error | ||
83 | +// otherwise return the defaultval | ||
84 | +func (c *JSONConfigContainer) DefaultBool(key string, defaultval bool) bool { | ||
85 | + if v, err := c.Bool(key); err == nil { | ||
86 | + return v | ||
87 | + } | ||
88 | + return defaultval | ||
89 | +} | ||
90 | + | ||
91 | +// Int returns the integer value for a given key. | ||
92 | +func (c *JSONConfigContainer) Int(key string) (int, error) { | ||
93 | + val := c.getData(key) | ||
94 | + if val != nil { | ||
95 | + if v, ok := val.(float64); ok { | ||
96 | + return int(v), nil | ||
97 | + } | ||
98 | + return 0, errors.New("not int value") | ||
99 | + } | ||
100 | + return 0, errors.New("not exist key:" + key) | ||
101 | +} | ||
102 | + | ||
103 | +// DefaultInt returns the integer value for a given key. | ||
104 | +// if err != nil return defaultval | ||
105 | +func (c *JSONConfigContainer) DefaultInt(key string, defaultval int) int { | ||
106 | + if v, err := c.Int(key); err == nil { | ||
107 | + return v | ||
108 | + } | ||
109 | + return defaultval | ||
110 | +} | ||
111 | + | ||
112 | +// Int64 returns the int64 value for a given key. | ||
113 | +func (c *JSONConfigContainer) Int64(key string) (int64, error) { | ||
114 | + val := c.getData(key) | ||
115 | + if val != nil { | ||
116 | + if v, ok := val.(float64); ok { | ||
117 | + return int64(v), nil | ||
118 | + } | ||
119 | + return 0, errors.New("not int64 value") | ||
120 | + } | ||
121 | + return 0, errors.New("not exist key:" + key) | ||
122 | +} | ||
123 | + | ||
124 | +// DefaultInt64 returns the int64 value for a given key. | ||
125 | +// if err != nil return defaultval | ||
126 | +func (c *JSONConfigContainer) DefaultInt64(key string, defaultval int64) int64 { | ||
127 | + if v, err := c.Int64(key); err == nil { | ||
128 | + return v | ||
129 | + } | ||
130 | + return defaultval | ||
131 | +} | ||
132 | + | ||
133 | +// Float returns the float value for a given key. | ||
134 | +func (c *JSONConfigContainer) Float(key string) (float64, error) { | ||
135 | + val := c.getData(key) | ||
136 | + if val != nil { | ||
137 | + if v, ok := val.(float64); ok { | ||
138 | + return v, nil | ||
139 | + } | ||
140 | + return 0.0, errors.New("not float64 value") | ||
141 | + } | ||
142 | + return 0.0, errors.New("not exist key:" + key) | ||
143 | +} | ||
144 | + | ||
145 | +// DefaultFloat returns the float64 value for a given key. | ||
146 | +// if err != nil return defaultval | ||
147 | +func (c *JSONConfigContainer) DefaultFloat(key string, defaultval float64) float64 { | ||
148 | + if v, err := c.Float(key); err == nil { | ||
149 | + return v | ||
150 | + } | ||
151 | + return defaultval | ||
152 | +} | ||
153 | + | ||
154 | +// String returns the string value for a given key. | ||
155 | +func (c *JSONConfigContainer) String(key string) string { | ||
156 | + val := c.getData(key) | ||
157 | + if val != nil { | ||
158 | + if v, ok := val.(string); ok { | ||
159 | + return v | ||
160 | + } | ||
161 | + } | ||
162 | + return "" | ||
163 | +} | ||
164 | + | ||
165 | +// DefaultString returns the string value for a given key. | ||
166 | +// if err != nil return defaultval | ||
167 | +func (c *JSONConfigContainer) DefaultString(key string, defaultval string) string { | ||
168 | + // TODO FIXME should not use "" to replace non existence | ||
169 | + if v := c.String(key); v != "" { | ||
170 | + return v | ||
171 | + } | ||
172 | + return defaultval | ||
173 | +} | ||
174 | + | ||
175 | +// Strings returns the []string value for a given key. | ||
176 | +func (c *JSONConfigContainer) Strings(key string) []string { | ||
177 | + stringVal := c.String(key) | ||
178 | + if stringVal == "" { | ||
179 | + return nil | ||
180 | + } | ||
181 | + return strings.Split(c.String(key), ";") | ||
182 | +} | ||
183 | + | ||
184 | +// DefaultStrings returns the []string value for a given key. | ||
185 | +// if err != nil return defaultval | ||
186 | +func (c *JSONConfigContainer) DefaultStrings(key string, defaultval []string) []string { | ||
187 | + if v := c.Strings(key); v != nil { | ||
188 | + return v | ||
189 | + } | ||
190 | + return defaultval | ||
191 | +} | ||
192 | + | ||
193 | +// GetSection returns map for the given section | ||
194 | +func (c *JSONConfigContainer) GetSection(section string) (map[string]string, error) { | ||
195 | + if v, ok := c.data[section]; ok { | ||
196 | + return v.(map[string]string), nil | ||
197 | + } | ||
198 | + return nil, errors.New("nonexist section " + section) | ||
199 | +} | ||
200 | + | ||
201 | +// SaveConfigFile save the config into file | ||
202 | +func (c *JSONConfigContainer) SaveConfigFile(filename string) (err error) { | ||
203 | + // Write configuration file by filename. | ||
204 | + f, err := os.Create(filename) | ||
205 | + if err != nil { | ||
206 | + return err | ||
207 | + } | ||
208 | + defer f.Close() | ||
209 | + b, err := json.MarshalIndent(c.data, "", " ") | ||
210 | + if err != nil { | ||
211 | + return err | ||
212 | + } | ||
213 | + _, err = f.Write(b) | ||
214 | + return err | ||
215 | +} | ||
216 | + | ||
217 | +// Set writes a new value for key. | ||
218 | +func (c *JSONConfigContainer) Set(key, val string) error { | ||
219 | + c.Lock() | ||
220 | + defer c.Unlock() | ||
221 | + c.data[key] = val | ||
222 | + return nil | ||
223 | +} | ||
224 | + | ||
225 | +// DIY returns the raw value by a given key. | ||
226 | +func (c *JSONConfigContainer) DIY(key string) (v interface{}, err error) { | ||
227 | + val := c.getData(key) | ||
228 | + if val != nil { | ||
229 | + return val, nil | ||
230 | + } | ||
231 | + return nil, errors.New("not exist key") | ||
232 | +} | ||
233 | + | ||
234 | +// section.key or key | ||
235 | +func (c *JSONConfigContainer) getData(key string) interface{} { | ||
236 | + if len(key) == 0 { | ||
237 | + return nil | ||
238 | + } | ||
239 | + | ||
240 | + c.RLock() | ||
241 | + defer c.RUnlock() | ||
242 | + | ||
243 | + sectionKeys := strings.Split(key, "::") | ||
244 | + if len(sectionKeys) >= 2 { | ||
245 | + curValue, ok := c.data[sectionKeys[0]] | ||
246 | + if !ok { | ||
247 | + return nil | ||
248 | + } | ||
249 | + for _, key := range sectionKeys[1:] { | ||
250 | + if v, ok := curValue.(map[string]interface{}); ok { | ||
251 | + if curValue, ok = v[key]; !ok { | ||
252 | + return nil | ||
253 | + } | ||
254 | + } | ||
255 | + } | ||
256 | + return curValue | ||
257 | + } | ||
258 | + if v, ok := c.data[key]; ok { | ||
259 | + return v | ||
260 | + } | ||
261 | + return nil | ||
262 | +} | ||
263 | + | ||
264 | +func init() { | ||
265 | + Register("json", &JSONConfig{}) | ||
266 | +} |
1 | +// Copyright 2015 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package context | ||
16 | + | ||
17 | +import ( | ||
18 | + "bytes" | ||
19 | + "compress/flate" | ||
20 | + "compress/gzip" | ||
21 | + "compress/zlib" | ||
22 | + "io" | ||
23 | + "net/http" | ||
24 | + "os" | ||
25 | + "strconv" | ||
26 | + "strings" | ||
27 | + "sync" | ||
28 | +) | ||
29 | + | ||
30 | +var ( | ||
31 | + //Default size==20B same as nginx | ||
32 | + defaultGzipMinLength = 20 | ||
33 | + //Content will only be compressed if content length is either unknown or greater than gzipMinLength. | ||
34 | + gzipMinLength = defaultGzipMinLength | ||
35 | + //The compression level used for deflate compression. (0-9). | ||
36 | + gzipCompressLevel int | ||
37 | + //List of HTTP methods to compress. If not set, only GET requests are compressed. | ||
38 | + includedMethods map[string]bool | ||
39 | + getMethodOnly bool | ||
40 | +) | ||
41 | + | ||
42 | +// InitGzip init the gzipcompress | ||
43 | +func InitGzip(minLength, compressLevel int, methods []string) { | ||
44 | + if minLength >= 0 { | ||
45 | + gzipMinLength = minLength | ||
46 | + } | ||
47 | + gzipCompressLevel = compressLevel | ||
48 | + if gzipCompressLevel < flate.NoCompression || gzipCompressLevel > flate.BestCompression { | ||
49 | + gzipCompressLevel = flate.BestSpeed | ||
50 | + } | ||
51 | + getMethodOnly = (len(methods) == 0) || (len(methods) == 1 && strings.ToUpper(methods[0]) == "GET") | ||
52 | + includedMethods = make(map[string]bool, len(methods)) | ||
53 | + for _, v := range methods { | ||
54 | + includedMethods[strings.ToUpper(v)] = true | ||
55 | + } | ||
56 | +} | ||
57 | + | ||
58 | +type resetWriter interface { | ||
59 | + io.Writer | ||
60 | + Reset(w io.Writer) | ||
61 | +} | ||
62 | + | ||
63 | +type nopResetWriter struct { | ||
64 | + io.Writer | ||
65 | +} | ||
66 | + | ||
67 | +func (n nopResetWriter) Reset(w io.Writer) { | ||
68 | + //do nothing | ||
69 | +} | ||
70 | + | ||
71 | +type acceptEncoder struct { | ||
72 | + name string | ||
73 | + levelEncode func(int) resetWriter | ||
74 | + customCompressLevelPool *sync.Pool | ||
75 | + bestCompressionPool *sync.Pool | ||
76 | +} | ||
77 | + | ||
78 | +func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter { | ||
79 | + if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil { | ||
80 | + return nopResetWriter{wr} | ||
81 | + } | ||
82 | + var rwr resetWriter | ||
83 | + switch level { | ||
84 | + case flate.BestSpeed: | ||
85 | + rwr = ac.customCompressLevelPool.Get().(resetWriter) | ||
86 | + case flate.BestCompression: | ||
87 | + rwr = ac.bestCompressionPool.Get().(resetWriter) | ||
88 | + default: | ||
89 | + rwr = ac.levelEncode(level) | ||
90 | + } | ||
91 | + rwr.Reset(wr) | ||
92 | + return rwr | ||
93 | +} | ||
94 | + | ||
95 | +func (ac acceptEncoder) put(wr resetWriter, level int) { | ||
96 | + if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil { | ||
97 | + return | ||
98 | + } | ||
99 | + wr.Reset(nil) | ||
100 | + | ||
101 | + //notice | ||
102 | + //compressionLevel==BestCompression DOES NOT MATTER | ||
103 | + //sync.Pool will not memory leak | ||
104 | + | ||
105 | + switch level { | ||
106 | + case gzipCompressLevel: | ||
107 | + ac.customCompressLevelPool.Put(wr) | ||
108 | + case flate.BestCompression: | ||
109 | + ac.bestCompressionPool.Put(wr) | ||
110 | + } | ||
111 | +} | ||
112 | + | ||
113 | +var ( | ||
114 | + noneCompressEncoder = acceptEncoder{"", nil, nil, nil} | ||
115 | + gzipCompressEncoder = acceptEncoder{ | ||
116 | + name: "gzip", | ||
117 | + levelEncode: func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr }, | ||
118 | + customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, gzipCompressLevel); return wr }}, | ||
119 | + bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr }}, | ||
120 | + } | ||
121 | + | ||
122 | + //according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed | ||
123 | + //deflate | ||
124 | + //The "zlib" format defined in RFC 1950 [31] in combination with | ||
125 | + //the "deflate" compression mechanism described in RFC 1951 [29]. | ||
126 | + deflateCompressEncoder = acceptEncoder{ | ||
127 | + name: "deflate", | ||
128 | + levelEncode: func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr }, | ||
129 | + customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, gzipCompressLevel); return wr }}, | ||
130 | + bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestCompression); return wr }}, | ||
131 | + } | ||
132 | +) | ||
133 | + | ||
134 | +var ( | ||
135 | + encoderMap = map[string]acceptEncoder{ // all the other compress methods will ignore | ||
136 | + "gzip": gzipCompressEncoder, | ||
137 | + "deflate": deflateCompressEncoder, | ||
138 | + "*": gzipCompressEncoder, // * means any compress will accept,we prefer gzip | ||
139 | + "identity": noneCompressEncoder, // identity means none-compress | ||
140 | + } | ||
141 | +) | ||
142 | + | ||
143 | +// WriteFile reads from file and writes to writer by the specific encoding(gzip/deflate) | ||
144 | +func WriteFile(encoding string, writer io.Writer, file *os.File) (bool, string, error) { | ||
145 | + return writeLevel(encoding, writer, file, flate.BestCompression) | ||
146 | +} | ||
147 | + | ||
148 | +// WriteBody reads writes content to writer by the specific encoding(gzip/deflate) | ||
149 | +func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, error) { | ||
150 | + if encoding == "" || len(content) < gzipMinLength { | ||
151 | + _, err := writer.Write(content) | ||
152 | + return false, "", err | ||
153 | + } | ||
154 | + return writeLevel(encoding, writer, bytes.NewReader(content), gzipCompressLevel) | ||
155 | +} | ||
156 | + | ||
157 | +// writeLevel reads from reader,writes to writer by specific encoding and compress level | ||
158 | +// the compress level is defined by deflate package | ||
159 | +func writeLevel(encoding string, writer io.Writer, reader io.Reader, level int) (bool, string, error) { | ||
160 | + var outputWriter resetWriter | ||
161 | + var err error | ||
162 | + var ce = noneCompressEncoder | ||
163 | + | ||
164 | + if cf, ok := encoderMap[encoding]; ok { | ||
165 | + ce = cf | ||
166 | + } | ||
167 | + encoding = ce.name | ||
168 | + outputWriter = ce.encode(writer, level) | ||
169 | + defer ce.put(outputWriter, level) | ||
170 | + | ||
171 | + _, err = io.Copy(outputWriter, reader) | ||
172 | + if err != nil { | ||
173 | + return false, "", err | ||
174 | + } | ||
175 | + | ||
176 | + switch outputWriter.(type) { | ||
177 | + case io.WriteCloser: | ||
178 | + outputWriter.(io.WriteCloser).Close() | ||
179 | + } | ||
180 | + return encoding != "", encoding, nil | ||
181 | +} | ||
182 | + | ||
183 | +// ParseEncoding will extract the right encoding for response | ||
184 | +// the Accept-Encoding's sec is here: | ||
185 | +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 | ||
186 | +func ParseEncoding(r *http.Request) string { | ||
187 | + if r == nil { | ||
188 | + return "" | ||
189 | + } | ||
190 | + if (getMethodOnly && r.Method == "GET") || includedMethods[r.Method] { | ||
191 | + return parseEncoding(r) | ||
192 | + } | ||
193 | + return "" | ||
194 | +} | ||
195 | + | ||
196 | +type q struct { | ||
197 | + name string | ||
198 | + value float64 | ||
199 | +} | ||
200 | + | ||
201 | +func parseEncoding(r *http.Request) string { | ||
202 | + acceptEncoding := r.Header.Get("Accept-Encoding") | ||
203 | + if acceptEncoding == "" { | ||
204 | + return "" | ||
205 | + } | ||
206 | + var lastQ q | ||
207 | + for _, v := range strings.Split(acceptEncoding, ",") { | ||
208 | + v = strings.TrimSpace(v) | ||
209 | + if v == "" { | ||
210 | + continue | ||
211 | + } | ||
212 | + vs := strings.Split(v, ";") | ||
213 | + var cf acceptEncoder | ||
214 | + var ok bool | ||
215 | + if cf, ok = encoderMap[vs[0]]; !ok { | ||
216 | + continue | ||
217 | + } | ||
218 | + if len(vs) == 1 { | ||
219 | + return cf.name | ||
220 | + } | ||
221 | + if len(vs) == 2 { | ||
222 | + f, _ := strconv.ParseFloat(strings.Replace(vs[1], "q=", "", -1), 64) | ||
223 | + if f == 0 { | ||
224 | + continue | ||
225 | + } | ||
226 | + if f > lastQ.value { | ||
227 | + lastQ = q{cf.name, f} | ||
228 | + } | ||
229 | + } | ||
230 | + } | ||
231 | + return lastQ.name | ||
232 | +} |
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +// Package context provide the context utils | ||
16 | +// Usage: | ||
17 | +// | ||
18 | +// import "github.com/astaxie/beego/context" | ||
19 | +// | ||
20 | +// ctx := context.Context{Request:req,ResponseWriter:rw} | ||
21 | +// | ||
22 | +// more docs http://beego.me/docs/module/context.md | ||
23 | +package context | ||
24 | + | ||
25 | +import ( | ||
26 | + "bufio" | ||
27 | + "crypto/hmac" | ||
28 | + "crypto/sha1" | ||
29 | + "encoding/base64" | ||
30 | + "errors" | ||
31 | + "fmt" | ||
32 | + "net" | ||
33 | + "net/http" | ||
34 | + "strconv" | ||
35 | + "strings" | ||
36 | + "time" | ||
37 | + | ||
38 | + "github.com/astaxie/beego/utils" | ||
39 | +) | ||
40 | + | ||
41 | +// NewContext return the Context with Input and Output | ||
42 | +func NewContext() *Context { | ||
43 | + return &Context{ | ||
44 | + Input: NewInput(), | ||
45 | + Output: NewOutput(), | ||
46 | + } | ||
47 | +} | ||
48 | + | ||
49 | +// Context Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter. | ||
50 | +// BeegoInput and BeegoOutput provides some api to operate request and response more easily. | ||
51 | +type Context struct { | ||
52 | + Input *BeegoInput | ||
53 | + Output *BeegoOutput | ||
54 | + Request *http.Request | ||
55 | + ResponseWriter *Response | ||
56 | + _xsrfToken string | ||
57 | +} | ||
58 | + | ||
59 | +// Reset init Context, BeegoInput and BeegoOutput | ||
60 | +func (ctx *Context) Reset(rw http.ResponseWriter, r *http.Request) { | ||
61 | + ctx.Request = r | ||
62 | + if ctx.ResponseWriter == nil { | ||
63 | + ctx.ResponseWriter = &Response{} | ||
64 | + } | ||
65 | + ctx.ResponseWriter.reset(rw) | ||
66 | + ctx.Input.Reset(ctx) | ||
67 | + ctx.Output.Reset(ctx) | ||
68 | + ctx._xsrfToken = "" | ||
69 | +} | ||
70 | + | ||
71 | +// Redirect does redirection to localurl with http header status code. | ||
72 | +func (ctx *Context) Redirect(status int, localurl string) { | ||
73 | + http.Redirect(ctx.ResponseWriter, ctx.Request, localurl, status) | ||
74 | +} | ||
75 | + | ||
76 | +// Abort stops this request. | ||
77 | +// if beego.ErrorMaps exists, panic body. | ||
78 | +func (ctx *Context) Abort(status int, body string) { | ||
79 | + ctx.Output.SetStatus(status) | ||
80 | + panic(body) | ||
81 | +} | ||
82 | + | ||
83 | +// WriteString Write string to response body. | ||
84 | +// it sends response body. | ||
85 | +func (ctx *Context) WriteString(content string) { | ||
86 | + ctx.ResponseWriter.Write([]byte(content)) | ||
87 | +} | ||
88 | + | ||
89 | +// GetCookie Get cookie from request by a given key. | ||
90 | +// It's alias of BeegoInput.Cookie. | ||
91 | +func (ctx *Context) GetCookie(key string) string { | ||
92 | + return ctx.Input.Cookie(key) | ||
93 | +} | ||
94 | + | ||
95 | +// SetCookie Set cookie for response. | ||
96 | +// It's alias of BeegoOutput.Cookie. | ||
97 | +func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { | ||
98 | + ctx.Output.Cookie(name, value, others...) | ||
99 | +} | ||
100 | + | ||
101 | +// GetSecureCookie Get secure cookie from request by a given key. | ||
102 | +func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) { | ||
103 | + val := ctx.Input.Cookie(key) | ||
104 | + if val == "" { | ||
105 | + return "", false | ||
106 | + } | ||
107 | + | ||
108 | + parts := strings.SplitN(val, "|", 3) | ||
109 | + | ||
110 | + if len(parts) != 3 { | ||
111 | + return "", false | ||
112 | + } | ||
113 | + | ||
114 | + vs := parts[0] | ||
115 | + timestamp := parts[1] | ||
116 | + sig := parts[2] | ||
117 | + | ||
118 | + h := hmac.New(sha1.New, []byte(Secret)) | ||
119 | + fmt.Fprintf(h, "%s%s", vs, timestamp) | ||
120 | + | ||
121 | + if fmt.Sprintf("%02x", h.Sum(nil)) != sig { | ||
122 | + return "", false | ||
123 | + } | ||
124 | + res, _ := base64.URLEncoding.DecodeString(vs) | ||
125 | + return string(res), true | ||
126 | +} | ||
127 | + | ||
128 | +// SetSecureCookie Set Secure cookie for response. | ||
129 | +func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { | ||
130 | + vs := base64.URLEncoding.EncodeToString([]byte(value)) | ||
131 | + timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) | ||
132 | + h := hmac.New(sha1.New, []byte(Secret)) | ||
133 | + fmt.Fprintf(h, "%s%s", vs, timestamp) | ||
134 | + sig := fmt.Sprintf("%02x", h.Sum(nil)) | ||
135 | + cookie := strings.Join([]string{vs, timestamp, sig}, "|") | ||
136 | + ctx.Output.Cookie(name, cookie, others...) | ||
137 | +} | ||
138 | + | ||
139 | +// XSRFToken creates a xsrf token string and returns. | ||
140 | +func (ctx *Context) XSRFToken(key string, expire int64) string { | ||
141 | + if ctx._xsrfToken == "" { | ||
142 | + token, ok := ctx.GetSecureCookie(key, "_xsrf") | ||
143 | + if !ok { | ||
144 | + token = string(utils.RandomCreateBytes(32)) | ||
145 | + ctx.SetSecureCookie(key, "_xsrf", token, expire) | ||
146 | + } | ||
147 | + ctx._xsrfToken = token | ||
148 | + } | ||
149 | + return ctx._xsrfToken | ||
150 | +} | ||
151 | + | ||
152 | +// CheckXSRFCookie checks xsrf token in this request is valid or not. | ||
153 | +// the token can provided in request header "X-Xsrftoken" and "X-CsrfToken" | ||
154 | +// or in form field value named as "_xsrf". | ||
155 | +func (ctx *Context) CheckXSRFCookie() bool { | ||
156 | + token := ctx.Input.Query("_xsrf") | ||
157 | + if token == "" { | ||
158 | + token = ctx.Request.Header.Get("X-Xsrftoken") | ||
159 | + } | ||
160 | + if token == "" { | ||
161 | + token = ctx.Request.Header.Get("X-Csrftoken") | ||
162 | + } | ||
163 | + if token == "" { | ||
164 | + ctx.Abort(403, "'_xsrf' argument missing from POST") | ||
165 | + return false | ||
166 | + } | ||
167 | + if ctx._xsrfToken != token { | ||
168 | + ctx.Abort(403, "XSRF cookie does not match POST argument") | ||
169 | + return false | ||
170 | + } | ||
171 | + return true | ||
172 | +} | ||
173 | + | ||
174 | +// RenderMethodResult renders the return value of a controller method to the output | ||
175 | +func (ctx *Context) RenderMethodResult(result interface{}) { | ||
176 | + if result != nil { | ||
177 | + renderer, ok := result.(Renderer) | ||
178 | + if !ok { | ||
179 | + err, ok := result.(error) | ||
180 | + if ok { | ||
181 | + renderer = errorRenderer(err) | ||
182 | + } else { | ||
183 | + renderer = jsonRenderer(result) | ||
184 | + } | ||
185 | + } | ||
186 | + renderer.Render(ctx) | ||
187 | + } | ||
188 | +} | ||
189 | + | ||
190 | +//Response is a wrapper for the http.ResponseWriter | ||
191 | +//started set to true if response was written to then don't execute other handler | ||
192 | +type Response struct { | ||
193 | + http.ResponseWriter | ||
194 | + Started bool | ||
195 | + Status int | ||
196 | +} | ||
197 | + | ||
198 | +func (r *Response) reset(rw http.ResponseWriter) { | ||
199 | + r.ResponseWriter = rw | ||
200 | + r.Status = 0 | ||
201 | + r.Started = false | ||
202 | +} | ||
203 | + | ||
204 | +// Write writes the data to the connection as part of an HTTP reply, | ||
205 | +// and sets `started` to true. | ||
206 | +// started means the response has sent out. | ||
207 | +func (r *Response) Write(p []byte) (int, error) { | ||
208 | + r.Started = true | ||
209 | + return r.ResponseWriter.Write(p) | ||
210 | +} | ||
211 | + | ||
212 | +// WriteHeader sends an HTTP response header with status code, | ||
213 | +// and sets `started` to true. | ||
214 | +func (r *Response) WriteHeader(code int) { | ||
215 | + if r.Status > 0 { | ||
216 | + //prevent multiple response.WriteHeader calls | ||
217 | + return | ||
218 | + } | ||
219 | + r.Status = code | ||
220 | + r.Started = true | ||
221 | + r.ResponseWriter.WriteHeader(code) | ||
222 | +} | ||
223 | + | ||
224 | +// Hijack hijacker for http | ||
225 | +func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { | ||
226 | + hj, ok := r.ResponseWriter.(http.Hijacker) | ||
227 | + if !ok { | ||
228 | + return nil, nil, errors.New("webserver doesn't support hijacking") | ||
229 | + } | ||
230 | + return hj.Hijack() | ||
231 | +} | ||
232 | + | ||
233 | +// Flush http.Flusher | ||
234 | +func (r *Response) Flush() { | ||
235 | + if f, ok := r.ResponseWriter.(http.Flusher); ok { | ||
236 | + f.Flush() | ||
237 | + } | ||
238 | +} | ||
239 | + | ||
240 | +// CloseNotify http.CloseNotifier | ||
241 | +func (r *Response) CloseNotify() <-chan bool { | ||
242 | + if cn, ok := r.ResponseWriter.(http.CloseNotifier); ok { | ||
243 | + return cn.CloseNotify() | ||
244 | + } | ||
245 | + return nil | ||
246 | +} |
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package context | ||
16 | + | ||
17 | +import ( | ||
18 | + "bytes" | ||
19 | + "compress/gzip" | ||
20 | + "errors" | ||
21 | + "io" | ||
22 | + "io/ioutil" | ||
23 | + "net" | ||
24 | + "net/http" | ||
25 | + "net/url" | ||
26 | + "reflect" | ||
27 | + "regexp" | ||
28 | + "strconv" | ||
29 | + "strings" | ||
30 | + | ||
31 | + "github.com/astaxie/beego/session" | ||
32 | +) | ||
33 | + | ||
34 | +// Regexes for checking the accept headers | ||
35 | +// TODO make sure these are correct | ||
36 | +var ( | ||
37 | + acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`) | ||
38 | + acceptsXMLRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`) | ||
39 | + acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`) | ||
40 | + acceptsYAMLRegex = regexp.MustCompile(`(application/x-yaml)(?:,|$)`) | ||
41 | + maxParam = 50 | ||
42 | +) | ||
43 | + | ||
44 | +// BeegoInput operates the http request header, data, cookie and body. | ||
45 | +// it also contains router params and current session. | ||
46 | +type BeegoInput struct { | ||
47 | + Context *Context | ||
48 | + CruSession session.Store | ||
49 | + pnames []string | ||
50 | + pvalues []string | ||
51 | + data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. | ||
52 | + RequestBody []byte | ||
53 | + RunMethod string | ||
54 | + RunController reflect.Type | ||
55 | +} | ||
56 | + | ||
57 | +// NewInput return BeegoInput generated by Context. | ||
58 | +func NewInput() *BeegoInput { | ||
59 | + return &BeegoInput{ | ||
60 | + pnames: make([]string, 0, maxParam), | ||
61 | + pvalues: make([]string, 0, maxParam), | ||
62 | + data: make(map[interface{}]interface{}), | ||
63 | + } | ||
64 | +} | ||
65 | + | ||
66 | +// Reset init the BeegoInput | ||
67 | +func (input *BeegoInput) Reset(ctx *Context) { | ||
68 | + input.Context = ctx | ||
69 | + input.CruSession = nil | ||
70 | + input.pnames = input.pnames[:0] | ||
71 | + input.pvalues = input.pvalues[:0] | ||
72 | + input.data = nil | ||
73 | + input.RequestBody = []byte{} | ||
74 | +} | ||
75 | + | ||
76 | +// Protocol returns request protocol name, such as HTTP/1.1 . | ||
77 | +func (input *BeegoInput) Protocol() string { | ||
78 | + return input.Context.Request.Proto | ||
79 | +} | ||
80 | + | ||
81 | +// URI returns full request url with query string, fragment. | ||
82 | +func (input *BeegoInput) URI() string { | ||
83 | + return input.Context.Request.RequestURI | ||
84 | +} | ||
85 | + | ||
86 | +// URL returns request url path (without query string, fragment). | ||
87 | +func (input *BeegoInput) URL() string { | ||
88 | + return input.Context.Request.URL.Path | ||
89 | +} | ||
90 | + | ||
91 | +// Site returns base site url as scheme://domain type. | ||
92 | +func (input *BeegoInput) Site() string { | ||
93 | + return input.Scheme() + "://" + input.Domain() | ||
94 | +} | ||
95 | + | ||
96 | +// Scheme returns request scheme as "http" or "https". | ||
97 | +func (input *BeegoInput) Scheme() string { | ||
98 | + if scheme := input.Header("X-Forwarded-Proto"); scheme != "" { | ||
99 | + return scheme | ||
100 | + } | ||
101 | + if input.Context.Request.URL.Scheme != "" { | ||
102 | + return input.Context.Request.URL.Scheme | ||
103 | + } | ||
104 | + if input.Context.Request.TLS == nil { | ||
105 | + return "http" | ||
106 | + } | ||
107 | + return "https" | ||
108 | +} | ||
109 | + | ||
110 | +// Domain returns host name. | ||
111 | +// Alias of Host method. | ||
112 | +func (input *BeegoInput) Domain() string { | ||
113 | + return input.Host() | ||
114 | +} | ||
115 | + | ||
116 | +// Host returns host name. | ||
117 | +// if no host info in request, return localhost. | ||
118 | +func (input *BeegoInput) Host() string { | ||
119 | + if input.Context.Request.Host != "" { | ||
120 | + if hostPart, _, err := net.SplitHostPort(input.Context.Request.Host); err == nil { | ||
121 | + return hostPart | ||
122 | + } | ||
123 | + return input.Context.Request.Host | ||
124 | + } | ||
125 | + return "localhost" | ||
126 | +} | ||
127 | + | ||
128 | +// Method returns http request method. | ||
129 | +func (input *BeegoInput) Method() string { | ||
130 | + return input.Context.Request.Method | ||
131 | +} | ||
132 | + | ||
133 | +// Is returns boolean of this request is on given method, such as Is("POST"). | ||
134 | +func (input *BeegoInput) Is(method string) bool { | ||
135 | + return input.Method() == method | ||
136 | +} | ||
137 | + | ||
138 | +// IsGet Is this a GET method request? | ||
139 | +func (input *BeegoInput) IsGet() bool { | ||
140 | + return input.Is("GET") | ||
141 | +} | ||
142 | + | ||
143 | +// IsPost Is this a POST method request? | ||
144 | +func (input *BeegoInput) IsPost() bool { | ||
145 | + return input.Is("POST") | ||
146 | +} | ||
147 | + | ||
148 | +// IsHead Is this a Head method request? | ||
149 | +func (input *BeegoInput) IsHead() bool { | ||
150 | + return input.Is("HEAD") | ||
151 | +} | ||
152 | + | ||
153 | +// IsOptions Is this a OPTIONS method request? | ||
154 | +func (input *BeegoInput) IsOptions() bool { | ||
155 | + return input.Is("OPTIONS") | ||
156 | +} | ||
157 | + | ||
158 | +// IsPut Is this a PUT method request? | ||
159 | +func (input *BeegoInput) IsPut() bool { | ||
160 | + return input.Is("PUT") | ||
161 | +} | ||
162 | + | ||
163 | +// IsDelete Is this a DELETE method request? | ||
164 | +func (input *BeegoInput) IsDelete() bool { | ||
165 | + return input.Is("DELETE") | ||
166 | +} | ||
167 | + | ||
168 | +// IsPatch Is this a PATCH method request? | ||
169 | +func (input *BeegoInput) IsPatch() bool { | ||
170 | + return input.Is("PATCH") | ||
171 | +} | ||
172 | + | ||
173 | +// IsAjax returns boolean of this request is generated by ajax. | ||
174 | +func (input *BeegoInput) IsAjax() bool { | ||
175 | + return input.Header("X-Requested-With") == "XMLHttpRequest" | ||
176 | +} | ||
177 | + | ||
178 | +// IsSecure returns boolean of this request is in https. | ||
179 | +func (input *BeegoInput) IsSecure() bool { | ||
180 | + return input.Scheme() == "https" | ||
181 | +} | ||
182 | + | ||
183 | +// IsWebsocket returns boolean of this request is in webSocket. | ||
184 | +func (input *BeegoInput) IsWebsocket() bool { | ||
185 | + return input.Header("Upgrade") == "websocket" | ||
186 | +} | ||
187 | + | ||
188 | +// IsUpload returns boolean of whether file uploads in this request or not.. | ||
189 | +func (input *BeegoInput) IsUpload() bool { | ||
190 | + return strings.Contains(input.Header("Content-Type"), "multipart/form-data") | ||
191 | +} | ||
192 | + | ||
193 | +// AcceptsHTML Checks if request accepts html response | ||
194 | +func (input *BeegoInput) AcceptsHTML() bool { | ||
195 | + return acceptsHTMLRegex.MatchString(input.Header("Accept")) | ||
196 | +} | ||
197 | + | ||
198 | +// AcceptsXML Checks if request accepts xml response | ||
199 | +func (input *BeegoInput) AcceptsXML() bool { | ||
200 | + return acceptsXMLRegex.MatchString(input.Header("Accept")) | ||
201 | +} | ||
202 | + | ||
203 | +// AcceptsJSON Checks if request accepts json response | ||
204 | +func (input *BeegoInput) AcceptsJSON() bool { | ||
205 | + return acceptsJSONRegex.MatchString(input.Header("Accept")) | ||
206 | +} | ||
207 | +// AcceptsYAML Checks if request accepts json response | ||
208 | +func (input *BeegoInput) AcceptsYAML() bool { | ||
209 | + return acceptsYAMLRegex.MatchString(input.Header("Accept")) | ||
210 | +} | ||
211 | + | ||
212 | +// IP returns request client ip. | ||
213 | +// if in proxy, return first proxy id. | ||
214 | +// if error, return RemoteAddr. | ||
215 | +func (input *BeegoInput) IP() string { | ||
216 | + ips := input.Proxy() | ||
217 | + if len(ips) > 0 && ips[0] != "" { | ||
218 | + rip, _, err := net.SplitHostPort(ips[0]) | ||
219 | + if err != nil { | ||
220 | + rip = ips[0] | ||
221 | + } | ||
222 | + return rip | ||
223 | + } | ||
224 | + if ip, _, err := net.SplitHostPort(input.Context.Request.RemoteAddr); err == nil { | ||
225 | + return ip | ||
226 | + } | ||
227 | + return input.Context.Request.RemoteAddr | ||
228 | +} | ||
229 | + | ||
230 | +// Proxy returns proxy client ips slice. | ||
231 | +func (input *BeegoInput) Proxy() []string { | ||
232 | + if ips := input.Header("X-Forwarded-For"); ips != "" { | ||
233 | + return strings.Split(ips, ",") | ||
234 | + } | ||
235 | + return []string{} | ||
236 | +} | ||
237 | + | ||
238 | +// Referer returns http referer header. | ||
239 | +func (input *BeegoInput) Referer() string { | ||
240 | + return input.Header("Referer") | ||
241 | +} | ||
242 | + | ||
243 | +// Refer returns http referer header. | ||
244 | +func (input *BeegoInput) Refer() string { | ||
245 | + return input.Referer() | ||
246 | +} | ||
247 | + | ||
248 | +// SubDomains returns sub domain string. | ||
249 | +// if aa.bb.domain.com, returns aa.bb . | ||
250 | +func (input *BeegoInput) SubDomains() string { | ||
251 | + parts := strings.Split(input.Host(), ".") | ||
252 | + if len(parts) >= 3 { | ||
253 | + return strings.Join(parts[:len(parts)-2], ".") | ||
254 | + } | ||
255 | + return "" | ||
256 | +} | ||
257 | + | ||
258 | +// Port returns request client port. | ||
259 | +// when error or empty, return 80. | ||
260 | +func (input *BeegoInput) Port() int { | ||
261 | + if _, portPart, err := net.SplitHostPort(input.Context.Request.Host); err == nil { | ||
262 | + port, _ := strconv.Atoi(portPart) | ||
263 | + return port | ||
264 | + } | ||
265 | + return 80 | ||
266 | +} | ||
267 | + | ||
268 | +// UserAgent returns request client user agent string. | ||
269 | +func (input *BeegoInput) UserAgent() string { | ||
270 | + return input.Header("User-Agent") | ||
271 | +} | ||
272 | + | ||
273 | +// ParamsLen return the length of the params | ||
274 | +func (input *BeegoInput) ParamsLen() int { | ||
275 | + return len(input.pnames) | ||
276 | +} | ||
277 | + | ||
278 | +// Param returns router param by a given key. | ||
279 | +func (input *BeegoInput) Param(key string) string { | ||
280 | + for i, v := range input.pnames { | ||
281 | + if v == key && i <= len(input.pvalues) { | ||
282 | + return input.pvalues[i] | ||
283 | + } | ||
284 | + } | ||
285 | + return "" | ||
286 | +} | ||
287 | + | ||
288 | +// Params returns the map[key]value. | ||
289 | +func (input *BeegoInput) Params() map[string]string { | ||
290 | + m := make(map[string]string) | ||
291 | + for i, v := range input.pnames { | ||
292 | + if i <= len(input.pvalues) { | ||
293 | + m[v] = input.pvalues[i] | ||
294 | + } | ||
295 | + } | ||
296 | + return m | ||
297 | +} | ||
298 | + | ||
299 | +// SetParam will set the param with key and value | ||
300 | +func (input *BeegoInput) SetParam(key, val string) { | ||
301 | + // check if already exists | ||
302 | + for i, v := range input.pnames { | ||
303 | + if v == key && i <= len(input.pvalues) { | ||
304 | + input.pvalues[i] = val | ||
305 | + return | ||
306 | + } | ||
307 | + } | ||
308 | + input.pvalues = append(input.pvalues, val) | ||
309 | + input.pnames = append(input.pnames, key) | ||
310 | +} | ||
311 | + | ||
312 | +// ResetParams clears any of the input's Params | ||
313 | +// This function is used to clear parameters so they may be reset between filter | ||
314 | +// passes. | ||
315 | +func (input *BeegoInput) ResetParams() { | ||
316 | + input.pnames = input.pnames[:0] | ||
317 | + input.pvalues = input.pvalues[:0] | ||
318 | +} | ||
319 | + | ||
320 | +// Query returns input data item string by a given string. | ||
321 | +func (input *BeegoInput) Query(key string) string { | ||
322 | + if val := input.Param(key); val != "" { | ||
323 | + return val | ||
324 | + } | ||
325 | + if input.Context.Request.Form == nil { | ||
326 | + input.Context.Request.ParseForm() | ||
327 | + } | ||
328 | + return input.Context.Request.Form.Get(key) | ||
329 | +} | ||
330 | + | ||
331 | +// Header returns request header item string by a given string. | ||
332 | +// if non-existed, return empty string. | ||
333 | +func (input *BeegoInput) Header(key string) string { | ||
334 | + return input.Context.Request.Header.Get(key) | ||
335 | +} | ||
336 | + | ||
337 | +// Cookie returns request cookie item string by a given key. | ||
338 | +// if non-existed, return empty string. | ||
339 | +func (input *BeegoInput) Cookie(key string) string { | ||
340 | + ck, err := input.Context.Request.Cookie(key) | ||
341 | + if err != nil { | ||
342 | + return "" | ||
343 | + } | ||
344 | + return ck.Value | ||
345 | +} | ||
346 | + | ||
347 | +// Session returns current session item value by a given key. | ||
348 | +// if non-existed, return nil. | ||
349 | +func (input *BeegoInput) Session(key interface{}) interface{} { | ||
350 | + return input.CruSession.Get(key) | ||
351 | +} | ||
352 | + | ||
353 | +// CopyBody returns the raw request body data as bytes. | ||
354 | +func (input *BeegoInput) CopyBody(MaxMemory int64) []byte { | ||
355 | + if input.Context.Request.Body == nil { | ||
356 | + return []byte{} | ||
357 | + } | ||
358 | + | ||
359 | + var requestbody []byte | ||
360 | + safe := &io.LimitedReader{R: input.Context.Request.Body, N: MaxMemory} | ||
361 | + if input.Header("Content-Encoding") == "gzip" { | ||
362 | + reader, err := gzip.NewReader(safe) | ||
363 | + if err != nil { | ||
364 | + return nil | ||
365 | + } | ||
366 | + requestbody, _ = ioutil.ReadAll(reader) | ||
367 | + } else { | ||
368 | + requestbody, _ = ioutil.ReadAll(safe) | ||
369 | + } | ||
370 | + | ||
371 | + input.Context.Request.Body.Close() | ||
372 | + bf := bytes.NewBuffer(requestbody) | ||
373 | + input.Context.Request.Body = http.MaxBytesReader(input.Context.ResponseWriter, ioutil.NopCloser(bf), MaxMemory) | ||
374 | + input.RequestBody = requestbody | ||
375 | + return requestbody | ||
376 | +} | ||
377 | + | ||
378 | +// Data return the implicit data in the input | ||
379 | +func (input *BeegoInput) Data() map[interface{}]interface{} { | ||
380 | + if input.data == nil { | ||
381 | + input.data = make(map[interface{}]interface{}) | ||
382 | + } | ||
383 | + return input.data | ||
384 | +} | ||
385 | + | ||
386 | +// GetData returns the stored data in this context. | ||
387 | +func (input *BeegoInput) GetData(key interface{}) interface{} { | ||
388 | + if v, ok := input.data[key]; ok { | ||
389 | + return v | ||
390 | + } | ||
391 | + return nil | ||
392 | +} | ||
393 | + | ||
394 | +// SetData stores data with given key in this context. | ||
395 | +// This data are only available in this context. | ||
396 | +func (input *BeegoInput) SetData(key, val interface{}) { | ||
397 | + if input.data == nil { | ||
398 | + input.data = make(map[interface{}]interface{}) | ||
399 | + } | ||
400 | + input.data[key] = val | ||
401 | +} | ||
402 | + | ||
403 | +// ParseFormOrMulitForm parseForm or parseMultiForm based on Content-type | ||
404 | +func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error { | ||
405 | + // Parse the body depending on the content type. | ||
406 | + if strings.Contains(input.Header("Content-Type"), "multipart/form-data") { | ||
407 | + if err := input.Context.Request.ParseMultipartForm(maxMemory); err != nil { | ||
408 | + return errors.New("Error parsing request body:" + err.Error()) | ||
409 | + } | ||
410 | + } else if err := input.Context.Request.ParseForm(); err != nil { | ||
411 | + return errors.New("Error parsing request body:" + err.Error()) | ||
412 | + } | ||
413 | + return nil | ||
414 | +} | ||
415 | + | ||
416 | +// Bind data from request.Form[key] to dest | ||
417 | +// like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie | ||
418 | +// var id int beegoInput.Bind(&id, "id") id ==123 | ||
419 | +// var isok bool beegoInput.Bind(&isok, "isok") isok ==true | ||
420 | +// var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2 | ||
421 | +// ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2] | ||
422 | +// ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array] | ||
423 | +// user struct{Name} beegoInput.Bind(&user, "user") user == {Name:"astaxie"} | ||
424 | +func (input *BeegoInput) Bind(dest interface{}, key string) error { | ||
425 | + value := reflect.ValueOf(dest) | ||
426 | + if value.Kind() != reflect.Ptr { | ||
427 | + return errors.New("beego: non-pointer passed to Bind: " + key) | ||
428 | + } | ||
429 | + value = value.Elem() | ||
430 | + if !value.CanSet() { | ||
431 | + return errors.New("beego: non-settable variable passed to Bind: " + key) | ||
432 | + } | ||
433 | + typ := value.Type() | ||
434 | + // Get real type if dest define with interface{}. | ||
435 | + // e.g var dest interface{} dest=1.0 | ||
436 | + if value.Kind() == reflect.Interface { | ||
437 | + typ = value.Elem().Type() | ||
438 | + } | ||
439 | + rv := input.bind(key, typ) | ||
440 | + if !rv.IsValid() { | ||
441 | + return errors.New("beego: reflect value is empty") | ||
442 | + } | ||
443 | + value.Set(rv) | ||
444 | + return nil | ||
445 | +} | ||
446 | + | ||
447 | +func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { | ||
448 | + if input.Context.Request.Form == nil { | ||
449 | + input.Context.Request.ParseForm() | ||
450 | + } | ||
451 | + rv := reflect.Zero(typ) | ||
452 | + switch typ.Kind() { | ||
453 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
454 | + val := input.Query(key) | ||
455 | + if len(val) == 0 { | ||
456 | + return rv | ||
457 | + } | ||
458 | + rv = input.bindInt(val, typ) | ||
459 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||
460 | + val := input.Query(key) | ||
461 | + if len(val) == 0 { | ||
462 | + return rv | ||
463 | + } | ||
464 | + rv = input.bindUint(val, typ) | ||
465 | + case reflect.Float32, reflect.Float64: | ||
466 | + val := input.Query(key) | ||
467 | + if len(val) == 0 { | ||
468 | + return rv | ||
469 | + } | ||
470 | + rv = input.bindFloat(val, typ) | ||
471 | + case reflect.String: | ||
472 | + val := input.Query(key) | ||
473 | + if len(val) == 0 { | ||
474 | + return rv | ||
475 | + } | ||
476 | + rv = input.bindString(val, typ) | ||
477 | + case reflect.Bool: | ||
478 | + val := input.Query(key) | ||
479 | + if len(val) == 0 { | ||
480 | + return rv | ||
481 | + } | ||
482 | + rv = input.bindBool(val, typ) | ||
483 | + case reflect.Slice: | ||
484 | + rv = input.bindSlice(&input.Context.Request.Form, key, typ) | ||
485 | + case reflect.Struct: | ||
486 | + rv = input.bindStruct(&input.Context.Request.Form, key, typ) | ||
487 | + case reflect.Ptr: | ||
488 | + rv = input.bindPoint(key, typ) | ||
489 | + case reflect.Map: | ||
490 | + rv = input.bindMap(&input.Context.Request.Form, key, typ) | ||
491 | + } | ||
492 | + return rv | ||
493 | +} | ||
494 | + | ||
495 | +func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value { | ||
496 | + rv := reflect.Zero(typ) | ||
497 | + switch typ.Kind() { | ||
498 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
499 | + rv = input.bindInt(val, typ) | ||
500 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||
501 | + rv = input.bindUint(val, typ) | ||
502 | + case reflect.Float32, reflect.Float64: | ||
503 | + rv = input.bindFloat(val, typ) | ||
504 | + case reflect.String: | ||
505 | + rv = input.bindString(val, typ) | ||
506 | + case reflect.Bool: | ||
507 | + rv = input.bindBool(val, typ) | ||
508 | + case reflect.Slice: | ||
509 | + rv = input.bindSlice(&url.Values{"": {val}}, "", typ) | ||
510 | + case reflect.Struct: | ||
511 | + rv = input.bindStruct(&url.Values{"": {val}}, "", typ) | ||
512 | + case reflect.Ptr: | ||
513 | + rv = input.bindPoint(val, typ) | ||
514 | + case reflect.Map: | ||
515 | + rv = input.bindMap(&url.Values{"": {val}}, "", typ) | ||
516 | + } | ||
517 | + return rv | ||
518 | +} | ||
519 | + | ||
520 | +func (input *BeegoInput) bindInt(val string, typ reflect.Type) reflect.Value { | ||
521 | + intValue, err := strconv.ParseInt(val, 10, 64) | ||
522 | + if err != nil { | ||
523 | + return reflect.Zero(typ) | ||
524 | + } | ||
525 | + pValue := reflect.New(typ) | ||
526 | + pValue.Elem().SetInt(intValue) | ||
527 | + return pValue.Elem() | ||
528 | +} | ||
529 | + | ||
530 | +func (input *BeegoInput) bindUint(val string, typ reflect.Type) reflect.Value { | ||
531 | + uintValue, err := strconv.ParseUint(val, 10, 64) | ||
532 | + if err != nil { | ||
533 | + return reflect.Zero(typ) | ||
534 | + } | ||
535 | + pValue := reflect.New(typ) | ||
536 | + pValue.Elem().SetUint(uintValue) | ||
537 | + return pValue.Elem() | ||
538 | +} | ||
539 | + | ||
540 | +func (input *BeegoInput) bindFloat(val string, typ reflect.Type) reflect.Value { | ||
541 | + floatValue, err := strconv.ParseFloat(val, 64) | ||
542 | + if err != nil { | ||
543 | + return reflect.Zero(typ) | ||
544 | + } | ||
545 | + pValue := reflect.New(typ) | ||
546 | + pValue.Elem().SetFloat(floatValue) | ||
547 | + return pValue.Elem() | ||
548 | +} | ||
549 | + | ||
550 | +func (input *BeegoInput) bindString(val string, typ reflect.Type) reflect.Value { | ||
551 | + return reflect.ValueOf(val) | ||
552 | +} | ||
553 | + | ||
554 | +func (input *BeegoInput) bindBool(val string, typ reflect.Type) reflect.Value { | ||
555 | + val = strings.TrimSpace(strings.ToLower(val)) | ||
556 | + switch val { | ||
557 | + case "true", "on", "1": | ||
558 | + return reflect.ValueOf(true) | ||
559 | + } | ||
560 | + return reflect.ValueOf(false) | ||
561 | +} | ||
562 | + | ||
563 | +type sliceValue struct { | ||
564 | + index int // Index extracted from brackets. If -1, no index was provided. | ||
565 | + value reflect.Value // the bound value for this slice element. | ||
566 | +} | ||
567 | + | ||
568 | +func (input *BeegoInput) bindSlice(params *url.Values, key string, typ reflect.Type) reflect.Value { | ||
569 | + maxIndex := -1 | ||
570 | + numNoIndex := 0 | ||
571 | + sliceValues := []sliceValue{} | ||
572 | + for reqKey, vals := range *params { | ||
573 | + if !strings.HasPrefix(reqKey, key+"[") { | ||
574 | + continue | ||
575 | + } | ||
576 | + // Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey) | ||
577 | + index := -1 | ||
578 | + leftBracket, rightBracket := len(key), strings.Index(reqKey[len(key):], "]")+len(key) | ||
579 | + if rightBracket > leftBracket+1 { | ||
580 | + index, _ = strconv.Atoi(reqKey[leftBracket+1 : rightBracket]) | ||
581 | + } | ||
582 | + subKeyIndex := rightBracket + 1 | ||
583 | + | ||
584 | + // Handle the indexed case. | ||
585 | + if index > -1 { | ||
586 | + if index > maxIndex { | ||
587 | + maxIndex = index | ||
588 | + } | ||
589 | + sliceValues = append(sliceValues, sliceValue{ | ||
590 | + index: index, | ||
591 | + value: input.bind(reqKey[:subKeyIndex], typ.Elem()), | ||
592 | + }) | ||
593 | + continue | ||
594 | + } | ||
595 | + | ||
596 | + // It's an un-indexed element. (e.g. element[]) | ||
597 | + numNoIndex += len(vals) | ||
598 | + for _, val := range vals { | ||
599 | + // Unindexed values can only be direct-bound. | ||
600 | + sliceValues = append(sliceValues, sliceValue{ | ||
601 | + index: -1, | ||
602 | + value: input.bindValue(val, typ.Elem()), | ||
603 | + }) | ||
604 | + } | ||
605 | + } | ||
606 | + resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex) | ||
607 | + for _, sv := range sliceValues { | ||
608 | + if sv.index != -1 { | ||
609 | + resultArray.Index(sv.index).Set(sv.value) | ||
610 | + } else { | ||
611 | + resultArray = reflect.Append(resultArray, sv.value) | ||
612 | + } | ||
613 | + } | ||
614 | + return resultArray | ||
615 | +} | ||
616 | + | ||
617 | +func (input *BeegoInput) bindStruct(params *url.Values, key string, typ reflect.Type) reflect.Value { | ||
618 | + result := reflect.New(typ).Elem() | ||
619 | + fieldValues := make(map[string]reflect.Value) | ||
620 | + for reqKey, val := range *params { | ||
621 | + var fieldName string | ||
622 | + if strings.HasPrefix(reqKey, key+".") { | ||
623 | + fieldName = reqKey[len(key)+1:] | ||
624 | + } else if strings.HasPrefix(reqKey, key+"[") && reqKey[len(reqKey)-1] == ']' { | ||
625 | + fieldName = reqKey[len(key)+1 : len(reqKey)-1] | ||
626 | + } else { | ||
627 | + continue | ||
628 | + } | ||
629 | + | ||
630 | + if _, ok := fieldValues[fieldName]; !ok { | ||
631 | + // Time to bind this field. Get it and make sure we can set it. | ||
632 | + fieldValue := result.FieldByName(fieldName) | ||
633 | + if !fieldValue.IsValid() { | ||
634 | + continue | ||
635 | + } | ||
636 | + if !fieldValue.CanSet() { | ||
637 | + continue | ||
638 | + } | ||
639 | + boundVal := input.bindValue(val[0], fieldValue.Type()) | ||
640 | + fieldValue.Set(boundVal) | ||
641 | + fieldValues[fieldName] = boundVal | ||
642 | + } | ||
643 | + } | ||
644 | + | ||
645 | + return result | ||
646 | +} | ||
647 | + | ||
648 | +func (input *BeegoInput) bindPoint(key string, typ reflect.Type) reflect.Value { | ||
649 | + return input.bind(key, typ.Elem()).Addr() | ||
650 | +} | ||
651 | + | ||
652 | +func (input *BeegoInput) bindMap(params *url.Values, key string, typ reflect.Type) reflect.Value { | ||
653 | + var ( | ||
654 | + result = reflect.MakeMap(typ) | ||
655 | + keyType = typ.Key() | ||
656 | + valueType = typ.Elem() | ||
657 | + ) | ||
658 | + for paramName, values := range *params { | ||
659 | + if !strings.HasPrefix(paramName, key+"[") || paramName[len(paramName)-1] != ']' { | ||
660 | + continue | ||
661 | + } | ||
662 | + | ||
663 | + key := paramName[len(key)+1 : len(paramName)-1] | ||
664 | + result.SetMapIndex(input.bindValue(key, keyType), input.bindValue(values[0], valueType)) | ||
665 | + } | ||
666 | + return result | ||
667 | +} |
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package context | ||
16 | + | ||
17 | +import ( | ||
18 | + "bytes" | ||
19 | + "encoding/json" | ||
20 | + "encoding/xml" | ||
21 | + "errors" | ||
22 | + "fmt" | ||
23 | + "html/template" | ||
24 | + "io" | ||
25 | + "mime" | ||
26 | + "net/http" | ||
27 | + "net/url" | ||
28 | + "os" | ||
29 | + "path/filepath" | ||
30 | + "strconv" | ||
31 | + "strings" | ||
32 | + "time" | ||
33 | + "gopkg.in/yaml.v2" | ||
34 | +) | ||
35 | + | ||
36 | +// BeegoOutput does work for sending response header. | ||
37 | +type BeegoOutput struct { | ||
38 | + Context *Context | ||
39 | + Status int | ||
40 | + EnableGzip bool | ||
41 | +} | ||
42 | + | ||
43 | +// NewOutput returns new BeegoOutput. | ||
44 | +// it contains nothing now. | ||
45 | +func NewOutput() *BeegoOutput { | ||
46 | + return &BeegoOutput{} | ||
47 | +} | ||
48 | + | ||
49 | +// Reset init BeegoOutput | ||
50 | +func (output *BeegoOutput) Reset(ctx *Context) { | ||
51 | + output.Context = ctx | ||
52 | + output.Status = 0 | ||
53 | +} | ||
54 | + | ||
55 | +// Header sets response header item string via given key. | ||
56 | +func (output *BeegoOutput) Header(key, val string) { | ||
57 | + output.Context.ResponseWriter.Header().Set(key, val) | ||
58 | +} | ||
59 | + | ||
60 | +// Body sets response body content. | ||
61 | +// if EnableGzip, compress content string. | ||
62 | +// it sends out response body directly. | ||
63 | +func (output *BeegoOutput) Body(content []byte) error { | ||
64 | + var encoding string | ||
65 | + var buf = &bytes.Buffer{} | ||
66 | + if output.EnableGzip { | ||
67 | + encoding = ParseEncoding(output.Context.Request) | ||
68 | + } | ||
69 | + if b, n, _ := WriteBody(encoding, buf, content); b { | ||
70 | + output.Header("Content-Encoding", n) | ||
71 | + output.Header("Content-Length", strconv.Itoa(buf.Len())) | ||
72 | + } else { | ||
73 | + output.Header("Content-Length", strconv.Itoa(len(content))) | ||
74 | + } | ||
75 | + // Write status code if it has been set manually | ||
76 | + // Set it to 0 afterwards to prevent "multiple response.WriteHeader calls" | ||
77 | + if output.Status != 0 { | ||
78 | + output.Context.ResponseWriter.WriteHeader(output.Status) | ||
79 | + output.Status = 0 | ||
80 | + } else { | ||
81 | + output.Context.ResponseWriter.Started = true | ||
82 | + } | ||
83 | + io.Copy(output.Context.ResponseWriter, buf) | ||
84 | + return nil | ||
85 | +} | ||
86 | + | ||
87 | +// Cookie sets cookie value via given key. | ||
88 | +// others are ordered as cookie's max age time, path,domain, secure and httponly. | ||
89 | +func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) { | ||
90 | + var b bytes.Buffer | ||
91 | + fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value)) | ||
92 | + | ||
93 | + //fix cookie not work in IE | ||
94 | + if len(others) > 0 { | ||
95 | + var maxAge int64 | ||
96 | + | ||
97 | + switch v := others[0].(type) { | ||
98 | + case int: | ||
99 | + maxAge = int64(v) | ||
100 | + case int32: | ||
101 | + maxAge = int64(v) | ||
102 | + case int64: | ||
103 | + maxAge = v | ||
104 | + } | ||
105 | + | ||
106 | + switch { | ||
107 | + case maxAge > 0: | ||
108 | + fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(maxAge)*time.Second).UTC().Format(time.RFC1123), maxAge) | ||
109 | + case maxAge < 0: | ||
110 | + fmt.Fprintf(&b, "; Max-Age=0") | ||
111 | + } | ||
112 | + } | ||
113 | + | ||
114 | + // the settings below | ||
115 | + // Path, Domain, Secure, HttpOnly | ||
116 | + // can use nil skip set | ||
117 | + | ||
118 | + // default "/" | ||
119 | + if len(others) > 1 { | ||
120 | + if v, ok := others[1].(string); ok && len(v) > 0 { | ||
121 | + fmt.Fprintf(&b, "; Path=%s", sanitizeValue(v)) | ||
122 | + } | ||
123 | + } else { | ||
124 | + fmt.Fprintf(&b, "; Path=%s", "/") | ||
125 | + } | ||
126 | + | ||
127 | + // default empty | ||
128 | + if len(others) > 2 { | ||
129 | + if v, ok := others[2].(string); ok && len(v) > 0 { | ||
130 | + fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(v)) | ||
131 | + } | ||
132 | + } | ||
133 | + | ||
134 | + // default empty | ||
135 | + if len(others) > 3 { | ||
136 | + var secure bool | ||
137 | + switch v := others[3].(type) { | ||
138 | + case bool: | ||
139 | + secure = v | ||
140 | + default: | ||
141 | + if others[3] != nil { | ||
142 | + secure = true | ||
143 | + } | ||
144 | + } | ||
145 | + if secure { | ||
146 | + fmt.Fprintf(&b, "; Secure") | ||
147 | + } | ||
148 | + } | ||
149 | + | ||
150 | + // default false. for session cookie default true | ||
151 | + if len(others) > 4 { | ||
152 | + if v, ok := others[4].(bool); ok && v { | ||
153 | + fmt.Fprintf(&b, "; HttpOnly") | ||
154 | + } | ||
155 | + } | ||
156 | + | ||
157 | + output.Context.ResponseWriter.Header().Add("Set-Cookie", b.String()) | ||
158 | +} | ||
159 | + | ||
160 | +var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-") | ||
161 | + | ||
162 | +func sanitizeName(n string) string { | ||
163 | + return cookieNameSanitizer.Replace(n) | ||
164 | +} | ||
165 | + | ||
166 | +var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ") | ||
167 | + | ||
168 | +func sanitizeValue(v string) string { | ||
169 | + return cookieValueSanitizer.Replace(v) | ||
170 | +} | ||
171 | + | ||
172 | +func jsonRenderer(value interface{}) Renderer { | ||
173 | + return rendererFunc(func(ctx *Context) { | ||
174 | + ctx.Output.JSON(value, false, false) | ||
175 | + }) | ||
176 | +} | ||
177 | + | ||
178 | +func errorRenderer(err error) Renderer { | ||
179 | + return rendererFunc(func(ctx *Context) { | ||
180 | + ctx.Output.SetStatus(500) | ||
181 | + ctx.Output.Body([]byte(err.Error())) | ||
182 | + }) | ||
183 | +} | ||
184 | + | ||
185 | +// JSON writes json to response body. | ||
186 | +// if encoding is true, it converts utf-8 to \u0000 type. | ||
187 | +func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, encoding bool) error { | ||
188 | + output.Header("Content-Type", "application/json; charset=utf-8") | ||
189 | + var content []byte | ||
190 | + var err error | ||
191 | + if hasIndent { | ||
192 | + content, err = json.MarshalIndent(data, "", " ") | ||
193 | + } else { | ||
194 | + content, err = json.Marshal(data) | ||
195 | + } | ||
196 | + if err != nil { | ||
197 | + http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError) | ||
198 | + return err | ||
199 | + } | ||
200 | + if encoding { | ||
201 | + content = []byte(stringsToJSON(string(content))) | ||
202 | + } | ||
203 | + return output.Body(content) | ||
204 | +} | ||
205 | + | ||
206 | + | ||
207 | +// YAML writes yaml to response body. | ||
208 | +func (output *BeegoOutput) YAML(data interface{}) error { | ||
209 | + output.Header("Content-Type", "application/application/x-yaml; charset=utf-8") | ||
210 | + var content []byte | ||
211 | + var err error | ||
212 | + content, err = yaml.Marshal(data) | ||
213 | + if err != nil { | ||
214 | + http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError) | ||
215 | + return err | ||
216 | + } | ||
217 | + return output.Body(content) | ||
218 | +} | ||
219 | + | ||
220 | +// JSONP writes jsonp to response body. | ||
221 | +func (output *BeegoOutput) JSONP(data interface{}, hasIndent bool) error { | ||
222 | + output.Header("Content-Type", "application/javascript; charset=utf-8") | ||
223 | + var content []byte | ||
224 | + var err error | ||
225 | + if hasIndent { | ||
226 | + content, err = json.MarshalIndent(data, "", " ") | ||
227 | + } else { | ||
228 | + content, err = json.Marshal(data) | ||
229 | + } | ||
230 | + if err != nil { | ||
231 | + http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError) | ||
232 | + return err | ||
233 | + } | ||
234 | + callback := output.Context.Input.Query("callback") | ||
235 | + if callback == "" { | ||
236 | + return errors.New(`"callback" parameter required`) | ||
237 | + } | ||
238 | + callback = template.JSEscapeString(callback) | ||
239 | + callbackContent := bytes.NewBufferString(" if(window." + callback + ")" + callback) | ||
240 | + callbackContent.WriteString("(") | ||
241 | + callbackContent.Write(content) | ||
242 | + callbackContent.WriteString(");\r\n") | ||
243 | + return output.Body(callbackContent.Bytes()) | ||
244 | +} | ||
245 | + | ||
246 | +// XML writes xml string to response body. | ||
247 | +func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error { | ||
248 | + output.Header("Content-Type", "application/xml; charset=utf-8") | ||
249 | + var content []byte | ||
250 | + var err error | ||
251 | + if hasIndent { | ||
252 | + content, err = xml.MarshalIndent(data, "", " ") | ||
253 | + } else { | ||
254 | + content, err = xml.Marshal(data) | ||
255 | + } | ||
256 | + if err != nil { | ||
257 | + http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError) | ||
258 | + return err | ||
259 | + } | ||
260 | + return output.Body(content) | ||
261 | +} | ||
262 | + | ||
263 | +// Download forces response for download file. | ||
264 | +// it prepares the download response header automatically. | ||
265 | +func (output *BeegoOutput) Download(file string, filename ...string) { | ||
266 | + // check get file error, file not found or other error. | ||
267 | + if _, err := os.Stat(file); err != nil { | ||
268 | + http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file) | ||
269 | + return | ||
270 | + } | ||
271 | + | ||
272 | + var fName string | ||
273 | + if len(filename) > 0 && filename[0] != "" { | ||
274 | + fName = filename[0] | ||
275 | + } else { | ||
276 | + fName = filepath.Base(file) | ||
277 | + } | ||
278 | + output.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fName)) | ||
279 | + output.Header("Content-Description", "File Transfer") | ||
280 | + output.Header("Content-Type", "application/octet-stream") | ||
281 | + output.Header("Content-Transfer-Encoding", "binary") | ||
282 | + output.Header("Expires", "0") | ||
283 | + output.Header("Cache-Control", "must-revalidate") | ||
284 | + output.Header("Pragma", "public") | ||
285 | + http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file) | ||
286 | +} | ||
287 | + | ||
288 | +// ContentType sets the content type from ext string. | ||
289 | +// MIME type is given in mime package. | ||
290 | +func (output *BeegoOutput) ContentType(ext string) { | ||
291 | + if !strings.HasPrefix(ext, ".") { | ||
292 | + ext = "." + ext | ||
293 | + } | ||
294 | + ctype := mime.TypeByExtension(ext) | ||
295 | + if ctype != "" { | ||
296 | + output.Header("Content-Type", ctype) | ||
297 | + } | ||
298 | +} | ||
299 | + | ||
300 | +// SetStatus sets response status code. | ||
301 | +// It writes response header directly. | ||
302 | +func (output *BeegoOutput) SetStatus(status int) { | ||
303 | + output.Status = status | ||
304 | +} | ||
305 | + | ||
306 | +// IsCachable returns boolean of this request is cached. | ||
307 | +// HTTP 304 means cached. | ||
308 | +func (output *BeegoOutput) IsCachable() bool { | ||
309 | + return output.Status >= 200 && output.Status < 300 || output.Status == 304 | ||
310 | +} | ||
311 | + | ||
312 | +// IsEmpty returns boolean of this request is empty. | ||
313 | +// HTTP 201,204 and 304 means empty. | ||
314 | +func (output *BeegoOutput) IsEmpty() bool { | ||
315 | + return output.Status == 201 || output.Status == 204 || output.Status == 304 | ||
316 | +} | ||
317 | + | ||
318 | +// IsOk returns boolean of this request runs well. | ||
319 | +// HTTP 200 means ok. | ||
320 | +func (output *BeegoOutput) IsOk() bool { | ||
321 | + return output.Status == 200 | ||
322 | +} | ||
323 | + | ||
324 | +// IsSuccessful returns boolean of this request runs successfully. | ||
325 | +// HTTP 2xx means ok. | ||
326 | +func (output *BeegoOutput) IsSuccessful() bool { | ||
327 | + return output.Status >= 200 && output.Status < 300 | ||
328 | +} | ||
329 | + | ||
330 | +// IsRedirect returns boolean of this request is redirection header. | ||
331 | +// HTTP 301,302,307 means redirection. | ||
332 | +func (output *BeegoOutput) IsRedirect() bool { | ||
333 | + return output.Status == 301 || output.Status == 302 || output.Status == 303 || output.Status == 307 | ||
334 | +} | ||
335 | + | ||
336 | +// IsForbidden returns boolean of this request is forbidden. | ||
337 | +// HTTP 403 means forbidden. | ||
338 | +func (output *BeegoOutput) IsForbidden() bool { | ||
339 | + return output.Status == 403 | ||
340 | +} | ||
341 | + | ||
342 | +// IsNotFound returns boolean of this request is not found. | ||
343 | +// HTTP 404 means not found. | ||
344 | +func (output *BeegoOutput) IsNotFound() bool { | ||
345 | + return output.Status == 404 | ||
346 | +} | ||
347 | + | ||
348 | +// IsClientError returns boolean of this request client sends error data. | ||
349 | +// HTTP 4xx means client error. | ||
350 | +func (output *BeegoOutput) IsClientError() bool { | ||
351 | + return output.Status >= 400 && output.Status < 500 | ||
352 | +} | ||
353 | + | ||
354 | +// IsServerError returns boolean of this server handler errors. | ||
355 | +// HTTP 5xx means server internal error. | ||
356 | +func (output *BeegoOutput) IsServerError() bool { | ||
357 | + return output.Status >= 500 && output.Status < 600 | ||
358 | +} | ||
359 | + | ||
360 | +func stringsToJSON(str string) string { | ||
361 | + var jsons bytes.Buffer | ||
362 | + for _, r := range str { | ||
363 | + rint := int(r) | ||
364 | + if rint < 128 { | ||
365 | + jsons.WriteRune(r) | ||
366 | + } else { | ||
367 | + jsons.WriteString("\\u") | ||
368 | + if rint < 0x100 { | ||
369 | + jsons.WriteString("00") | ||
370 | + } else if rint < 0x1000 { | ||
371 | + jsons.WriteString("0") | ||
372 | + } | ||
373 | + jsons.WriteString(strconv.FormatInt(int64(rint), 16)) | ||
374 | + } | ||
375 | + } | ||
376 | + return jsons.String() | ||
377 | +} | ||
378 | + | ||
379 | +// Session sets session item value with given key. | ||
380 | +func (output *BeegoOutput) Session(name interface{}, value interface{}) { | ||
381 | + output.Context.Input.CruSession.Set(name, value) | ||
382 | +} |
1 | +package param | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "reflect" | ||
6 | + | ||
7 | + beecontext "github.com/astaxie/beego/context" | ||
8 | + "github.com/astaxie/beego/logs" | ||
9 | +) | ||
10 | + | ||
11 | +// ConvertParams converts http method params to values that will be passed to the method controller as arguments | ||
12 | +func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { | ||
13 | + result = make([]reflect.Value, 0, len(methodParams)) | ||
14 | + for i := 0; i < len(methodParams); i++ { | ||
15 | + reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) | ||
16 | + result = append(result, reflectValue) | ||
17 | + } | ||
18 | + return | ||
19 | +} | ||
20 | + | ||
21 | +func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { | ||
22 | + paramValue := getParamValue(param, ctx) | ||
23 | + if paramValue == "" { | ||
24 | + if param.required { | ||
25 | + ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) | ||
26 | + } else { | ||
27 | + paramValue = param.defaultValue | ||
28 | + } | ||
29 | + } | ||
30 | + | ||
31 | + reflectValue, err := parseValue(param, paramValue, paramType) | ||
32 | + if err != nil { | ||
33 | + logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %v, Error: %s", param.name, paramType, paramValue, err)) | ||
34 | + ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %v to type %s", param.name, paramValue, paramType)) | ||
35 | + } | ||
36 | + | ||
37 | + return reflectValue | ||
38 | +} | ||
39 | + | ||
40 | +func getParamValue(param *MethodParam, ctx *beecontext.Context) string { | ||
41 | + switch param.in { | ||
42 | + case body: | ||
43 | + return string(ctx.Input.RequestBody) | ||
44 | + case header: | ||
45 | + return ctx.Input.Header(param.name) | ||
46 | + case path: | ||
47 | + return ctx.Input.Query(":" + param.name) | ||
48 | + default: | ||
49 | + return ctx.Input.Query(param.name) | ||
50 | + } | ||
51 | +} | ||
52 | + | ||
53 | +func parseValue(param *MethodParam, paramValue string, paramType reflect.Type) (result reflect.Value, err error) { | ||
54 | + if paramValue == "" { | ||
55 | + return reflect.Zero(paramType), nil | ||
56 | + } | ||
57 | + parser := getParser(param, paramType) | ||
58 | + value, err := parser.parse(paramValue, paramType) | ||
59 | + if err != nil { | ||
60 | + return result, err | ||
61 | + } | ||
62 | + | ||
63 | + return safeConvert(reflect.ValueOf(value), paramType) | ||
64 | +} | ||
65 | + | ||
66 | +func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { | ||
67 | + defer func() { | ||
68 | + if r := recover(); r != nil { | ||
69 | + var ok bool | ||
70 | + err, ok = r.(error) | ||
71 | + if !ok { | ||
72 | + err = fmt.Errorf("%v", r) | ||
73 | + } | ||
74 | + } | ||
75 | + }() | ||
76 | + result = value.Convert(t) | ||
77 | + return | ||
78 | +} |
1 | +package param | ||
2 | + | ||
3 | +import ( | ||
4 | + "fmt" | ||
5 | + "strings" | ||
6 | +) | ||
7 | + | ||
8 | +//MethodParam keeps param information to be auto passed to controller methods | ||
9 | +type MethodParam struct { | ||
10 | + name string | ||
11 | + in paramType | ||
12 | + required bool | ||
13 | + defaultValue string | ||
14 | +} | ||
15 | + | ||
16 | +type paramType byte | ||
17 | + | ||
18 | +const ( | ||
19 | + param paramType = iota | ||
20 | + path | ||
21 | + body | ||
22 | + header | ||
23 | +) | ||
24 | + | ||
25 | +//New creates a new MethodParam with name and specific options | ||
26 | +func New(name string, opts ...MethodParamOption) *MethodParam { | ||
27 | + return newParam(name, nil, opts) | ||
28 | +} | ||
29 | + | ||
30 | +func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) { | ||
31 | + param = &MethodParam{name: name} | ||
32 | + for _, option := range opts { | ||
33 | + option(param) | ||
34 | + } | ||
35 | + return | ||
36 | +} | ||
37 | + | ||
38 | +//Make creates an array of MethodParmas or an empty array | ||
39 | +func Make(list ...*MethodParam) []*MethodParam { | ||
40 | + if len(list) > 0 { | ||
41 | + return list | ||
42 | + } | ||
43 | + return nil | ||
44 | +} | ||
45 | + | ||
46 | +func (mp *MethodParam) String() string { | ||
47 | + options := []string{} | ||
48 | + result := "param.New(\"" + mp.name + "\"" | ||
49 | + if mp.required { | ||
50 | + options = append(options, "param.IsRequired") | ||
51 | + } | ||
52 | + switch mp.in { | ||
53 | + case path: | ||
54 | + options = append(options, "param.InPath") | ||
55 | + case body: | ||
56 | + options = append(options, "param.InBody") | ||
57 | + case header: | ||
58 | + options = append(options, "param.InHeader") | ||
59 | + } | ||
60 | + if mp.defaultValue != "" { | ||
61 | + options = append(options, fmt.Sprintf(`param.Default("%s")`, mp.defaultValue)) | ||
62 | + } | ||
63 | + if len(options) > 0 { | ||
64 | + result += ", " | ||
65 | + } | ||
66 | + result += strings.Join(options, ", ") | ||
67 | + result += ")" | ||
68 | + return result | ||
69 | +} |
-
请 注册 或 登录 后发表评论