正在显示
21 个修改的文件
包含
0 行增加
和
4895 行删除
| 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
已删除
100644 → 0
| 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. |
| 1 | -# Beego [](https://travis-ci.org/astaxie/beego) [](http://godoc.org/github.com/astaxie/beego) [](http://golangfoundation.org) [](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
已删除
100644 → 0
| 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 | -} |
| 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
已删除
100644 → 0
| 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
已删除
100644 → 0
| 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 | -} |
| 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 | -} |
-
请 注册 或 登录 后发表评论