From e7b431899e66761708c06bf421bd66c911b8f624 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Thu, 1 Feb 2018 14:08:36 +0800 Subject: [PATCH] Upgrade beego from 1.6.1 to 1.9.0 --- src/Gopkg.lock | 7 +- src/Gopkg.toml | 2 +- .../astaxie/beego/.github/ISSUE_TEMPLATE | 17 + .../github.com/astaxie/beego/.gitignore | 1 + .../github.com/astaxie/beego/.gosimpleignore | 4 + .../github.com/astaxie/beego/.travis.yml | 31 +- src/vendor/github.com/astaxie/beego/README.md | 31 +- src/vendor/github.com/astaxie/beego/admin.go | 168 ++- .../github.com/astaxie/beego/admin_test.go | 73 ++ .../github.com/astaxie/beego/adminui.go | 13 +- src/vendor/github.com/astaxie/beego/app.go | 36 +- src/vendor/github.com/astaxie/beego/beego.go | 35 +- .../github.com/astaxie/beego/cache/conv.go | 2 +- .../astaxie/beego/cache/conv_test.go | 6 +- .../github.com/astaxie/beego/cache/file.go | 25 +- .../astaxie/beego/cache/memcache/memcache.go | 26 +- .../beego/cache/memcache/memcache_test.go | 12 +- .../github.com/astaxie/beego/cache/memory.go | 31 +- .../astaxie/beego/cache/redis/redis.go | 2 +- .../astaxie/beego/cache/redis/redis_test.go | 3 +- .../astaxie/beego/cache/ssdb/ssdb.go | 21 +- .../astaxie/beego/cache/ssdb/ssdb_test.go | 3 +- src/vendor/github.com/astaxie/beego/config.go | 233 ++-- .../github.com/astaxie/beego/config/config.go | 116 +- .../astaxie/beego/config/config_test.go | 55 + .../astaxie/beego/config/env/env.go | 87 ++ .../astaxie/beego/config/env/env_test.go | 75 ++ .../github.com/astaxie/beego/config/fake.go | 4 +- .../github.com/astaxie/beego/config/ini.go | 80 +- .../astaxie/beego/config/ini_test.go | 8 +- .../github.com/astaxie/beego/config/json.go | 3 + .../astaxie/beego/config/json_test.go | 12 +- .../astaxie/beego/config/xml/xml.go | 60 +- .../astaxie/beego/config/xml/xml_test.go | 93 +- .../astaxie/beego/config/yaml/yaml.go | 87 +- .../astaxie/beego/config/yaml/yaml_test.go | 83 +- .../github.com/astaxie/beego/config_test.go | 109 ++ .../astaxie/beego/context/acceptencoder.go | 103 +- .../beego/context/acceptencoder_test.go | 15 + .../astaxie/beego/context/context.go | 32 +- .../astaxie/beego/context/context_test.go | 47 + .../github.com/astaxie/beego/context/input.go | 67 +- .../astaxie/beego/context/input_test.go | 124 +- .../astaxie/beego/context/output.go | 83 +- .../astaxie/beego/context/param/conv.go | 78 ++ .../beego/context/param/methodparams.go | 69 ++ .../astaxie/beego/context/param/options.go | 37 + .../astaxie/beego/context/param/parsers.go | 149 +++ .../beego/context/param/parsers_test.go | 84 ++ .../astaxie/beego/context/renderer.go | 12 + .../astaxie/beego/context/response.go | 27 + .../github.com/astaxie/beego/controller.go | 85 +- .../astaxie/beego/controller_test.go | 104 ++ src/vendor/github.com/astaxie/beego/error.go | 248 ++-- .../github.com/astaxie/beego/error_test.go | 88 ++ src/vendor/github.com/astaxie/beego/filter.go | 1 + .../github.com/astaxie/beego/filter_test.go | 6 - .../github.com/astaxie/beego/flash_test.go | 2 +- .../github.com/astaxie/beego/grace/conn.go | 13 +- .../github.com/astaxie/beego/grace/grace.go | 16 +- .../astaxie/beego/grace/listener.go | 4 +- .../github.com/astaxie/beego/grace/server.go | 21 +- src/vendor/github.com/astaxie/beego/hooks.go | 56 +- .../astaxie/beego/httplib/README.md | 2 +- .../astaxie/beego/httplib/httplib.go | 52 +- .../astaxie/beego/httplib/httplib_test.go | 8 + src/vendor/github.com/astaxie/beego/log.go | 35 +- .../astaxie/beego/logs/alils/alils.go | 186 +++ .../astaxie/beego/logs/alils/config.go | 13 + .../astaxie/beego/logs/alils/log.pb.go | 1038 +++++++++++++++++ .../astaxie/beego/logs/alils/log_config.go | 42 + .../astaxie/beego/logs/alils/log_project.go | 819 +++++++++++++ .../astaxie/beego/logs/alils/log_store.go | 271 +++++ .../astaxie/beego/logs/alils/machine_group.go | 91 ++ .../astaxie/beego/logs/alils/request.go | 62 + .../astaxie/beego/logs/alils/signature.go | 111 ++ .../github.com/astaxie/beego/logs/color.go | 28 + .../astaxie/beego/logs/color_windows.go | 428 +++++++ .../astaxie/beego/logs/color_windows_test.go | 294 +++++ .../github.com/astaxie/beego/logs/conn.go | 2 +- .../github.com/astaxie/beego/logs/console.go | 6 +- .../github.com/astaxie/beego/logs/es/es.go | 2 +- .../github.com/astaxie/beego/logs/file.go | 113 +- .../astaxie/beego/logs/file_test.go | 131 ++- .../github.com/astaxie/beego/logs/jianliao.go | 72 ++ .../github.com/astaxie/beego/logs/log.go | 324 ++++- .../github.com/astaxie/beego/logs/logger.go | 179 ++- .../astaxie/beego/logs/logger_test.go | 75 ++ .../astaxie/beego/logs/multifile.go | 2 +- .../github.com/astaxie/beego/logs/slack.go | 60 + .../github.com/astaxie/beego/logs/smtp.go | 19 +- .../github.com/astaxie/beego/migration/ddl.go | 392 ++++++- .../github.com/astaxie/beego/migration/doc.go | 32 + .../astaxie/beego/migration/migration.go | 80 +- src/vendor/github.com/astaxie/beego/mime.go | 2 +- .../github.com/astaxie/beego/namespace.go | 15 +- .../astaxie/beego/namespace_test.go | 17 +- .../github.com/astaxie/beego/orm/cmd.go | 4 +- .../github.com/astaxie/beego/orm/cmd_utils.go | 32 +- src/vendor/github.com/astaxie/beego/orm/db.go | 319 +++-- .../github.com/astaxie/beego/orm/db_alias.go | 14 +- .../github.com/astaxie/beego/orm/db_mysql.go | 78 ++ .../github.com/astaxie/beego/orm/db_oracle.go | 40 + .../astaxie/beego/orm/db_postgres.go | 37 +- .../github.com/astaxie/beego/orm/db_sqlite.go | 2 +- .../github.com/astaxie/beego/orm/db_tables.go | 12 +- .../github.com/astaxie/beego/orm/db_utils.go | 26 +- .../github.com/astaxie/beego/orm/models.go | 45 +- .../astaxie/beego/orm/models_boot.go | 111 +- .../astaxie/beego/orm/models_fields.go | 154 ++- .../astaxie/beego/orm/models_info_f.go | 59 +- .../astaxie/beego/orm/models_info_m.go | 96 +- .../astaxie/beego/orm/models_test.go | 52 +- .../astaxie/beego/orm/models_utils.go | 91 +- .../github.com/astaxie/beego/orm/orm.go | 69 +- .../github.com/astaxie/beego/orm/orm_conds.go | 26 + .../github.com/astaxie/beego/orm/orm_log.go | 4 +- .../astaxie/beego/orm/orm_object.go | 2 +- .../astaxie/beego/orm/orm_querym2m.go | 10 +- .../astaxie/beego/orm/orm_queryset.go | 13 +- .../github.com/astaxie/beego/orm/orm_raw.go | 66 +- .../github.com/astaxie/beego/orm/orm_test.go | 410 ++++++- src/vendor/github.com/astaxie/beego/orm/qb.go | 1 + .../github.com/astaxie/beego/orm/qb_mysql.go | 6 + .../github.com/astaxie/beego/orm/qb_tidb.go | 6 + .../github.com/astaxie/beego/orm/types.go | 26 +- .../github.com/astaxie/beego/orm/utils.go | 44 +- .../beego/{docs.go => orm/utils_test.go} | 31 +- src/vendor/github.com/astaxie/beego/parser.go | 260 ++++- .../astaxie/beego/plugins/apiauth/apiauth.go | 58 +- .../beego/plugins/apiauth/apiauth_test.go | 20 + .../astaxie/beego/plugins/authz/authz.go | 86 ++ .../beego/plugins/authz/authz_model.conf | 14 + .../beego/plugins/authz/authz_policy.csv | 7 + .../astaxie/beego/plugins/authz/authz_test.go | 107 ++ src/vendor/github.com/astaxie/beego/policy.go | 97 ++ src/vendor/github.com/astaxie/beego/router.go | 517 ++++---- .../github.com/astaxie/beego/router_test.go | 136 ++- .../beego/session/couchbase/sess_couchbase.go | 14 +- .../beego/session/ledis/ledis_session.go | 49 +- .../beego/session/memcache/sess_memcache.go | 7 +- .../astaxie/beego/session/mysql/sess_mysql.go | 7 +- .../beego/session/postgres/sess_postgresql.go | 7 +- .../astaxie/beego/session/redis/sess_redis.go | 29 +- .../astaxie/beego/session/sess_cookie.go | 24 +- .../astaxie/beego/session/sess_cookie_test.go | 13 +- .../astaxie/beego/session/sess_file.go | 102 +- .../astaxie/beego/session/sess_mem_test.go | 8 +- .../astaxie/beego/session/sess_test.go | 9 +- .../astaxie/beego/session/session.go | 121 +- .../astaxie/beego/session/ssdb/sess_ssdb.go | 199 ++++ .../github.com/astaxie/beego/staticfile.go | 37 +- .../astaxie/beego/swagger/docs_spec.go | 160 --- .../astaxie/beego/swagger/swagger.go | 172 +++ .../github.com/astaxie/beego/template.go | 106 +- .../github.com/astaxie/beego/template_test.go | 124 +- .../github.com/astaxie/beego/templatefunc.go | 134 ++- .../astaxie/beego/templatefunc_test.go | 106 +- .../astaxie/beego/toolbox/statistics.go | 6 + .../github.com/astaxie/beego/toolbox/task.go | 12 +- src/vendor/github.com/astaxie/beego/tree.go | 29 +- .../github.com/astaxie/beego/tree_test.go | 2 +- .../astaxie/beego/utils/captcha/captcha.go | 7 +- .../astaxie/beego/utils/captcha/image.go | 5 +- .../github.com/astaxie/beego/utils/file.go | 2 +- .../astaxie/beego/utils/file_test.go | 6 +- .../github.com/astaxie/beego/utils/mail.go | 10 +- .../beego/utils/pagination/controller.go | 2 +- .../github.com/astaxie/beego/utils/rand.go | 24 +- .../astaxie/beego/utils/rand_test.go | 33 + .../github.com/astaxie/beego/utils/safemap.go | 13 +- .../astaxie/beego/utils/safemap_test.go | 55 +- .../github.com/astaxie/beego/utils/utils.go | 30 + .../astaxie/beego/validation/README.md | 3 + .../astaxie/beego/validation/util.go | 31 +- .../astaxie/beego/validation/util_test.go | 2 +- .../astaxie/beego/validation/validation.go | 23 +- .../beego/validation/validation_test.go | 71 +- .../astaxie/beego/validation/validators.go | 100 +- 179 files changed, 11008 insertions(+), 2302 deletions(-) create mode 100644 src/vendor/github.com/astaxie/beego/.github/ISSUE_TEMPLATE create mode 100644 src/vendor/github.com/astaxie/beego/.gosimpleignore create mode 100644 src/vendor/github.com/astaxie/beego/admin_test.go create mode 100644 src/vendor/github.com/astaxie/beego/config/config_test.go create mode 100644 src/vendor/github.com/astaxie/beego/config/env/env.go create mode 100644 src/vendor/github.com/astaxie/beego/config/env/env_test.go create mode 100644 src/vendor/github.com/astaxie/beego/context/context_test.go create mode 100644 src/vendor/github.com/astaxie/beego/context/param/conv.go create mode 100644 src/vendor/github.com/astaxie/beego/context/param/methodparams.go create mode 100644 src/vendor/github.com/astaxie/beego/context/param/options.go create mode 100644 src/vendor/github.com/astaxie/beego/context/param/parsers.go create mode 100644 src/vendor/github.com/astaxie/beego/context/param/parsers_test.go create mode 100644 src/vendor/github.com/astaxie/beego/context/renderer.go create mode 100644 src/vendor/github.com/astaxie/beego/context/response.go create mode 100644 src/vendor/github.com/astaxie/beego/error_test.go create mode 100644 src/vendor/github.com/astaxie/beego/logs/alils/alils.go create mode 100755 src/vendor/github.com/astaxie/beego/logs/alils/config.go create mode 100755 src/vendor/github.com/astaxie/beego/logs/alils/log.pb.go create mode 100755 src/vendor/github.com/astaxie/beego/logs/alils/log_config.go create mode 100755 src/vendor/github.com/astaxie/beego/logs/alils/log_project.go create mode 100755 src/vendor/github.com/astaxie/beego/logs/alils/log_store.go create mode 100755 src/vendor/github.com/astaxie/beego/logs/alils/machine_group.go create mode 100755 src/vendor/github.com/astaxie/beego/logs/alils/request.go create mode 100755 src/vendor/github.com/astaxie/beego/logs/alils/signature.go create mode 100644 src/vendor/github.com/astaxie/beego/logs/color.go create mode 100644 src/vendor/github.com/astaxie/beego/logs/color_windows.go create mode 100644 src/vendor/github.com/astaxie/beego/logs/color_windows_test.go create mode 100644 src/vendor/github.com/astaxie/beego/logs/jianliao.go create mode 100644 src/vendor/github.com/astaxie/beego/logs/logger_test.go create mode 100644 src/vendor/github.com/astaxie/beego/logs/slack.go create mode 100644 src/vendor/github.com/astaxie/beego/migration/doc.go rename src/vendor/github.com/astaxie/beego/{docs.go => orm/utils_test.go} (56%) create mode 100644 src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth_test.go create mode 100644 src/vendor/github.com/astaxie/beego/plugins/authz/authz.go create mode 100644 src/vendor/github.com/astaxie/beego/plugins/authz/authz_model.conf create mode 100644 src/vendor/github.com/astaxie/beego/plugins/authz/authz_policy.csv create mode 100644 src/vendor/github.com/astaxie/beego/plugins/authz/authz_test.go create mode 100644 src/vendor/github.com/astaxie/beego/policy.go create mode 100644 src/vendor/github.com/astaxie/beego/session/ssdb/sess_ssdb.go delete mode 100644 src/vendor/github.com/astaxie/beego/swagger/docs_spec.go create mode 100644 src/vendor/github.com/astaxie/beego/swagger/swagger.go create mode 100644 src/vendor/github.com/astaxie/beego/utils/rand_test.go create mode 100644 src/vendor/github.com/astaxie/beego/utils/utils.go diff --git a/src/Gopkg.lock b/src/Gopkg.lock index b855157b8..157549098 100644 --- a/src/Gopkg.lock +++ b/src/Gopkg.lock @@ -27,6 +27,7 @@ "cache", "config", "context", + "context/param", "grace", "logs", "orm", @@ -36,8 +37,8 @@ "utils", "validation" ] - revision = "1aeb3d90512734def678c7aa9f612fe6f659e6b5" - version = "v1.6.1" + revision = "d96289a81bf67728cff7a19b067aaecc65a62ec6" + version = "v1.9.0" [[projects]] name = "github.com/beego/i18n" @@ -247,6 +248,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "4e59ab14b46d61b63505abcec474b7130e34e212bdecd3a36b41d5f016ba6bf2" + inputs-digest = "4375660bb0d5d8993171781c253b8ba8ac6d20ee8950c357b642dc0f6f841e09" solver-name = "gps-cdcl" solver-version = 1 diff --git a/src/Gopkg.toml b/src/Gopkg.toml index 2f22c1240..66cb40c41 100644 --- a/src/Gopkg.toml +++ b/src/Gopkg.toml @@ -22,7 +22,7 @@ ignored = ["github.com/vmware/harbor/tests*"] [[constraint]] name = "github.com/astaxie/beego" - version = "1.6.1" + version = "=1.9.0" [[constraint]] name = "github.com/dghubble/sling" diff --git a/src/vendor/github.com/astaxie/beego/.github/ISSUE_TEMPLATE b/src/vendor/github.com/astaxie/beego/.github/ISSUE_TEMPLATE new file mode 100644 index 000000000..db349198d --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/.github/ISSUE_TEMPLATE @@ -0,0 +1,17 @@ +Please answer these questions before submitting your issue. Thanks! + +1. What version of Go and beego are you using (`bee version`)? + + +2. What operating system and processor architecture are you using (`go env`)? + + +3. What did you do? +If possible, provide a recipe for reproducing the error. +A complete runnable program is good. + + +4. What did you expect to see? + + +5. What did you see instead? \ No newline at end of file diff --git a/src/vendor/github.com/astaxie/beego/.gitignore b/src/vendor/github.com/astaxie/beego/.gitignore index 9806457b9..e1b652910 100644 --- a/src/vendor/github.com/astaxie/beego/.gitignore +++ b/src/vendor/github.com/astaxie/beego/.gitignore @@ -1,4 +1,5 @@ .idea +.vscode .DS_Store *.swp *.swo diff --git a/src/vendor/github.com/astaxie/beego/.gosimpleignore b/src/vendor/github.com/astaxie/beego/.gosimpleignore new file mode 100644 index 000000000..84df9b95d --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/.gosimpleignore @@ -0,0 +1,4 @@ +github.com/astaxie/beego/*/*:S1012 +github.com/astaxie/beego/*:S1012 +github.com/astaxie/beego/*/*:S1007 +github.com/astaxie/beego/*:S1007 \ No newline at end of file diff --git a/src/vendor/github.com/astaxie/beego/.travis.yml b/src/vendor/github.com/astaxie/beego/.travis.yml index 3c821dcd3..2937e6e85 100644 --- a/src/vendor/github.com/astaxie/beego/.travis.yml +++ b/src/vendor/github.com/astaxie/beego/.travis.yml @@ -1,10 +1,9 @@ language: go go: - - tip - - 1.6.0 - - 1.5.3 - - 1.4.3 + - 1.6.4 + - 1.7.5 + - 1.8.1 services: - redis-server - mysql @@ -31,21 +30,33 @@ install: - go get github.com/belogik/goes - go get github.com/siddontang/ledisdb/config - go get github.com/siddontang/ledisdb/ledis - - go get golang.org/x/tools/cmd/vet - - go get github.com/golang/lint/golint - go get github.com/ssdb/gossdb/ssdb + - go get github.com/cloudflare/golz4 + - go get github.com/gogo/protobuf/proto + - go get github.com/Knetic/govaluate + - go get github.com/casbin/casbin + - go get -u honnef.co/go/tools/cmd/gosimple + - go get -u github.com/mdempsky/unconvert + - go get -u github.com/gordonklaus/ineffassign + - go get -u github.com/golang/lint/golint before_script: + - psql --version - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi" - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi" + - sh -c "if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi" + - sh -c "if [ $(go version) == *1.[5-9]* ]; then go tool vet .; fi" - mkdir -p res/var - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d after_script: -killall -w ssdb-server - rm -rf ./res/var/* script: - - go vet -x ./... - - $HOME/gopath/bin/golint ./... - go test -v ./... -notifications: - webhooks: https://hooks.pubu.im/services/z7m9bvybl3rgtg9 + - gosimple -ignore "$(cat .gosimpleignore)" $(go list ./... | grep -v /vendor/) + - unconvert $(go list ./... | grep -v /vendor/) + - ineffassign . + - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s + - golint ./... +addons: + postgresql: "9.4" diff --git a/src/vendor/github.com/astaxie/beego/README.md b/src/vendor/github.com/astaxie/beego/README.md index 6c5895842..c08927fb1 100644 --- a/src/vendor/github.com/astaxie/beego/README.md +++ b/src/vendor/github.com/astaxie/beego/README.md @@ -1,19 +1,17 @@ -## Beego - -[![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) -[![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) +# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) beego is used for rapid development of RESTful APIs, web apps and backend services in Go. It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. -More info [beego.me](http://beego.me) +###### More info at [beego.me](http://beego.me). -##Quick Start -######Download and install +## Quick Start + +#### Download and install go get github.com/astaxie/beego -######Create file `hello.go` +#### Create file `hello.go` ```go package main @@ -23,15 +21,16 @@ func main(){ beego.Run() } ``` -######Build and run -```bash +#### Build and run + go build hello.go ./hello -``` -######Congratulations! -You just built your first beego app. -Open your browser and visit `http://localhost:8000`. -Please see [Documentation](http://beego.me/docs) for more. + +#### Go to [http://localhost:8080](http://localhost:8080) + +Congratulations! You've just built your first **beego** app. + +###### Please see [Documentation](http://beego.me/docs) for more. ## Features @@ -55,7 +54,7 @@ Please see [Documentation](http://beego.me/docs) for more. * [http://beego.me/community](http://beego.me/community) * 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) -## LICENSE +## License beego source code is licensed under the Apache Licence, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html). diff --git a/src/vendor/github.com/astaxie/beego/admin.go b/src/vendor/github.com/astaxie/beego/admin.go index 031e64215..0688dcbcc 100644 --- a/src/vendor/github.com/astaxie/beego/admin.go +++ b/src/vendor/github.com/astaxie/beego/admin.go @@ -23,7 +23,10 @@ import ( "text/template" "time" + "reflect" + "github.com/astaxie/beego/grace" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/utils" ) @@ -34,7 +37,7 @@ var beeAdminApp *adminApp // FilterMonitorFunc is default monitor filter when admin module is enable. // if this func returns, admin module records qbs for this request by condition of this function logic. // usage: -// func MyFilterMonitor(method, requestPath string, t time.Duration) bool { +// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool { // if method == "POST" { // return false // } @@ -47,7 +50,7 @@ var beeAdminApp *adminApp // return true // } // beego.FilterMonitorFunc = MyFilterMonitor. -var FilterMonitorFunc func(string, string, time.Duration) bool +var FilterMonitorFunc func(string, string, time.Duration, string, int) bool func init() { beeAdminApp = &adminApp{ @@ -59,7 +62,7 @@ func init() { beeAdminApp.Route("/healthcheck", healthcheck) beeAdminApp.Route("/task", taskStatus) beeAdminApp.Route("/listconf", listConf) - FilterMonitorFunc = func(string, string, time.Duration) bool { return true } + FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true } } // AdminIndex is the default http.Handler for admin module. @@ -90,57 +93,9 @@ func listConf(rw http.ResponseWriter, r *http.Request) { switch command { case "conf": m := make(map[string]interface{}) + list("BConfig", BConfig, m) m["AppConfigPath"] = appConfigPath m["AppConfigProvider"] = appConfigProvider - m["BConfig.AppName"] = BConfig.AppName - m["BConfig.RunMode"] = BConfig.RunMode - m["BConfig.RouterCaseSensitive"] = BConfig.RouterCaseSensitive - m["BConfig.ServerName"] = BConfig.ServerName - m["BConfig.RecoverPanic"] = BConfig.RecoverPanic - m["BConfig.CopyRequestBody"] = BConfig.CopyRequestBody - m["BConfig.EnableGzip"] = BConfig.EnableGzip - m["BConfig.MaxMemory"] = BConfig.MaxMemory - m["BConfig.EnableErrorsShow"] = BConfig.EnableErrorsShow - m["BConfig.Listen.Graceful"] = BConfig.Listen.Graceful - m["BConfig.Listen.ServerTimeOut"] = BConfig.Listen.ServerTimeOut - m["BConfig.Listen.ListenTCP4"] = BConfig.Listen.ListenTCP4 - m["BConfig.Listen.EnableHTTP"] = BConfig.Listen.EnableHTTP - m["BConfig.Listen.HTTPAddr"] = BConfig.Listen.HTTPAddr - m["BConfig.Listen.HTTPPort"] = BConfig.Listen.HTTPPort - m["BConfig.Listen.EnableHTTPS"] = BConfig.Listen.EnableHTTPS - m["BConfig.Listen.HTTPSAddr"] = BConfig.Listen.HTTPSAddr - m["BConfig.Listen.HTTPSPort"] = BConfig.Listen.HTTPSPort - m["BConfig.Listen.HTTPSCertFile"] = BConfig.Listen.HTTPSCertFile - m["BConfig.Listen.HTTPSKeyFile"] = BConfig.Listen.HTTPSKeyFile - m["BConfig.Listen.EnableAdmin"] = BConfig.Listen.EnableAdmin - m["BConfig.Listen.AdminAddr"] = BConfig.Listen.AdminAddr - m["BConfig.Listen.AdminPort"] = BConfig.Listen.AdminPort - m["BConfig.Listen.EnableFcgi"] = BConfig.Listen.EnableFcgi - m["BConfig.Listen.EnableStdIo"] = BConfig.Listen.EnableStdIo - m["BConfig.WebConfig.AutoRender"] = BConfig.WebConfig.AutoRender - m["BConfig.WebConfig.EnableDocs"] = BConfig.WebConfig.EnableDocs - m["BConfig.WebConfig.FlashName"] = BConfig.WebConfig.FlashName - m["BConfig.WebConfig.FlashSeparator"] = BConfig.WebConfig.FlashSeparator - m["BConfig.WebConfig.DirectoryIndex"] = BConfig.WebConfig.DirectoryIndex - m["BConfig.WebConfig.StaticDir"] = BConfig.WebConfig.StaticDir - m["BConfig.WebConfig.StaticExtensionsToGzip"] = BConfig.WebConfig.StaticExtensionsToGzip - m["BConfig.WebConfig.TemplateLeft"] = BConfig.WebConfig.TemplateLeft - m["BConfig.WebConfig.TemplateRight"] = BConfig.WebConfig.TemplateRight - m["BConfig.WebConfig.ViewsPath"] = BConfig.WebConfig.ViewsPath - m["BConfig.WebConfig.EnableXSRF"] = BConfig.WebConfig.EnableXSRF - m["BConfig.WebConfig.XSRFKEY"] = BConfig.WebConfig.XSRFKey - m["BConfig.WebConfig.XSRFExpire"] = BConfig.WebConfig.XSRFExpire - m["BConfig.WebConfig.Session.SessionOn"] = BConfig.WebConfig.Session.SessionOn - m["BConfig.WebConfig.Session.SessionProvider"] = BConfig.WebConfig.Session.SessionProvider - m["BConfig.WebConfig.Session.SessionName"] = BConfig.WebConfig.Session.SessionName - m["BConfig.WebConfig.Session.SessionGCMaxLifetime"] = BConfig.WebConfig.Session.SessionGCMaxLifetime - m["BConfig.WebConfig.Session.SessionProviderConfig"] = BConfig.WebConfig.Session.SessionProviderConfig - m["BConfig.WebConfig.Session.SessionCookieLifeTime"] = BConfig.WebConfig.Session.SessionCookieLifeTime - m["BConfig.WebConfig.Session.SessionAutoSetCookie"] = BConfig.WebConfig.Session.SessionAutoSetCookie - m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain - m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs - m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum - m["BConfig.Log.Outputs"] = BConfig.Log.Outputs tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) tmpl = template.Must(tmpl.Parse(configTpl)) tmpl = template.Must(tmpl.Parse(defaultScriptsTpl)) @@ -150,29 +105,12 @@ func listConf(rw http.ResponseWriter, r *http.Request) { tmpl.Execute(rw, data) case "router": - var ( - content = map[string]interface{}{ - "Fields": []string{ - "Router Pattern", - "Methods", - "Controller", - }, - } - methods = []string{} - methodsData = make(map[string]interface{}) - ) - for method, t := range BeeApp.Handlers.routers { - - resultList := new([][]string) - - printTree(resultList, t) - - methods = append(methods, method) - methodsData[method] = resultList + content := PrintTree() + content["Fields"] = []string{ + "Router Pattern", + "Methods", + "Controller", } - - content["Data"] = methodsData - content["Methods"] = methods data["Content"] = content data["Title"] = "Routers" execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl) @@ -196,14 +134,14 @@ func listConf(rw http.ResponseWriter, r *http.Request) { BeforeExec: "Before Exec", AfterExec: "After Exec", FinishRouter: "Finish Router"} { - if bf, ok := BeeApp.Handlers.filters[k]; ok { + if bf := BeeApp.Handlers.filters[k]; len(bf) > 0 { filterType = fr filterTypes = append(filterTypes, filterType) resultList := new([][]string) for _, f := range bf { var result = []string{ - fmt.Sprintf("%s", f.pattern), - fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)), + f.pattern, + utils.GetFuncName(f.filterFunc), } *resultList = append(*resultList, result) } @@ -223,6 +161,50 @@ func listConf(rw http.ResponseWriter, r *http.Request) { } } +func list(root string, p interface{}, m map[string]interface{}) { + pt := reflect.TypeOf(p) + pv := reflect.ValueOf(p) + if pt.Kind() == reflect.Ptr { + pt = pt.Elem() + pv = pv.Elem() + } + for i := 0; i < pv.NumField(); i++ { + var key string + if root == "" { + key = pt.Field(i).Name + } else { + key = root + "." + pt.Field(i).Name + } + if pv.Field(i).Kind() == reflect.Struct { + list(key, pv.Field(i).Interface(), m) + } else { + m[key] = pv.Field(i).Interface() + } + } +} + +// PrintTree prints all registered routers. +func PrintTree() map[string]interface{} { + var ( + content = map[string]interface{}{} + methods = []string{} + methodsData = make(map[string]interface{}) + ) + for method, t := range BeeApp.Handlers.routers { + + resultList := new([][]string) + + printTree(resultList, t) + + methods = append(methods, method) + methodsData[method] = resultList + } + + content["Data"] = methodsData + content["Methods"] = methods + return content +} + func printTree(resultList *[][]string, t *Tree) { for _, tr := range t.fixrouters { printTree(resultList, tr) @@ -231,12 +213,12 @@ func printTree(resultList *[][]string, t *Tree) { printTree(resultList, t.wildcard) } for _, l := range t.leaves { - if v, ok := l.runObject.(*controllerInfo); ok { + if v, ok := l.runObject.(*ControllerInfo); ok { if v.routerType == routerTypeBeego { var result = []string{ v.pattern, fmt.Sprintf("%s", v.methods), - fmt.Sprintf("%s", v.controllerType), + v.controllerType.String(), } *resultList = append(*resultList, result) } else if v.routerType == routerTypeRESTFul { @@ -299,8 +281,8 @@ func profIndex(rw http.ResponseWriter, r *http.Request) { // it's in "/healthcheck" pattern in admin module. func healthcheck(rw http.ResponseWriter, req *http.Request) { var ( + result []string data = make(map[interface{}]interface{}) - result = []string{} resultList = new([][]string) content = map[string]interface{}{ "Fields": []string{"Name", "Message", "Status"}, @@ -310,21 +292,20 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) { for name, h := range toolbox.AdminCheckList { if err := h.Check(); err != nil { result = []string{ - fmt.Sprintf("error"), - fmt.Sprintf("%s", name), - fmt.Sprintf("%s", err.Error()), + "error", + name, + err.Error(), } - } else { result = []string{ - fmt.Sprintf("success"), - fmt.Sprintf("%s", name), - fmt.Sprintf("OK"), + "success", + name, + "OK", } - } *resultList = append(*resultList, result) } + content["Data"] = resultList data["Content"] = content data["Title"] = "Health Check" @@ -353,7 +334,6 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { // List Tasks content := make(map[string]interface{}) resultList := new([][]string) - var result = []string{} var fields = []string{ "Task Name", "Task Spec", @@ -362,10 +342,10 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { "", } for tname, tk := range toolbox.AdminTaskList { - result = []string{ + result := []string{ tname, - fmt.Sprintf("%s", tk.GetSpec()), - fmt.Sprintf("%s", tk.GetStatus()), + tk.GetSpec(), + tk.GetStatus(), tk.GetPrev().String(), } *resultList = append(*resultList, result) @@ -410,7 +390,7 @@ func (admin *adminApp) Run() { for p, f := range admin.routers { http.Handle(p, f) } - BeeLogger.Info("Admin server Running on %s", addr) + logs.Info("Admin server Running on %s", addr) var err error if BConfig.Listen.Graceful { @@ -419,6 +399,6 @@ func (admin *adminApp) Run() { err = http.ListenAndServe(addr, nil) } if err != nil { - BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) + logs.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) } } diff --git a/src/vendor/github.com/astaxie/beego/admin_test.go b/src/vendor/github.com/astaxie/beego/admin_test.go new file mode 100644 index 000000000..2348792e9 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/admin_test.go @@ -0,0 +1,73 @@ +package beego + +import ( + "fmt" + "testing" +) + +func TestList_01(t *testing.T) { + m := make(map[string]interface{}) + list("BConfig", BConfig, m) + t.Log(m) + om := oldMap() + for k, v := range om { + if fmt.Sprint(m[k]) != fmt.Sprint(v) { + t.Log(k, "old-key", v, "new-key", m[k]) + t.FailNow() + } + } +} + +func oldMap() map[string]interface{} { + m := make(map[string]interface{}) + m["BConfig.AppName"] = BConfig.AppName + m["BConfig.RunMode"] = BConfig.RunMode + m["BConfig.RouterCaseSensitive"] = BConfig.RouterCaseSensitive + m["BConfig.ServerName"] = BConfig.ServerName + m["BConfig.RecoverPanic"] = BConfig.RecoverPanic + m["BConfig.CopyRequestBody"] = BConfig.CopyRequestBody + m["BConfig.EnableGzip"] = BConfig.EnableGzip + m["BConfig.MaxMemory"] = BConfig.MaxMemory + m["BConfig.EnableErrorsShow"] = BConfig.EnableErrorsShow + m["BConfig.Listen.Graceful"] = BConfig.Listen.Graceful + m["BConfig.Listen.ServerTimeOut"] = BConfig.Listen.ServerTimeOut + m["BConfig.Listen.ListenTCP4"] = BConfig.Listen.ListenTCP4 + m["BConfig.Listen.EnableHTTP"] = BConfig.Listen.EnableHTTP + m["BConfig.Listen.HTTPAddr"] = BConfig.Listen.HTTPAddr + m["BConfig.Listen.HTTPPort"] = BConfig.Listen.HTTPPort + m["BConfig.Listen.EnableHTTPS"] = BConfig.Listen.EnableHTTPS + m["BConfig.Listen.HTTPSAddr"] = BConfig.Listen.HTTPSAddr + m["BConfig.Listen.HTTPSPort"] = BConfig.Listen.HTTPSPort + m["BConfig.Listen.HTTPSCertFile"] = BConfig.Listen.HTTPSCertFile + m["BConfig.Listen.HTTPSKeyFile"] = BConfig.Listen.HTTPSKeyFile + m["BConfig.Listen.EnableAdmin"] = BConfig.Listen.EnableAdmin + m["BConfig.Listen.AdminAddr"] = BConfig.Listen.AdminAddr + m["BConfig.Listen.AdminPort"] = BConfig.Listen.AdminPort + m["BConfig.Listen.EnableFcgi"] = BConfig.Listen.EnableFcgi + m["BConfig.Listen.EnableStdIo"] = BConfig.Listen.EnableStdIo + m["BConfig.WebConfig.AutoRender"] = BConfig.WebConfig.AutoRender + m["BConfig.WebConfig.EnableDocs"] = BConfig.WebConfig.EnableDocs + m["BConfig.WebConfig.FlashName"] = BConfig.WebConfig.FlashName + m["BConfig.WebConfig.FlashSeparator"] = BConfig.WebConfig.FlashSeparator + m["BConfig.WebConfig.DirectoryIndex"] = BConfig.WebConfig.DirectoryIndex + m["BConfig.WebConfig.StaticDir"] = BConfig.WebConfig.StaticDir + m["BConfig.WebConfig.StaticExtensionsToGzip"] = BConfig.WebConfig.StaticExtensionsToGzip + m["BConfig.WebConfig.TemplateLeft"] = BConfig.WebConfig.TemplateLeft + m["BConfig.WebConfig.TemplateRight"] = BConfig.WebConfig.TemplateRight + m["BConfig.WebConfig.ViewsPath"] = BConfig.WebConfig.ViewsPath + m["BConfig.WebConfig.EnableXSRF"] = BConfig.WebConfig.EnableXSRF + m["BConfig.WebConfig.XSRFExpire"] = BConfig.WebConfig.XSRFExpire + m["BConfig.WebConfig.Session.SessionOn"] = BConfig.WebConfig.Session.SessionOn + m["BConfig.WebConfig.Session.SessionProvider"] = BConfig.WebConfig.Session.SessionProvider + m["BConfig.WebConfig.Session.SessionName"] = BConfig.WebConfig.Session.SessionName + m["BConfig.WebConfig.Session.SessionGCMaxLifetime"] = BConfig.WebConfig.Session.SessionGCMaxLifetime + m["BConfig.WebConfig.Session.SessionProviderConfig"] = BConfig.WebConfig.Session.SessionProviderConfig + m["BConfig.WebConfig.Session.SessionCookieLifeTime"] = BConfig.WebConfig.Session.SessionCookieLifeTime + m["BConfig.WebConfig.Session.SessionAutoSetCookie"] = BConfig.WebConfig.Session.SessionAutoSetCookie + m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain + m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly + m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs + m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum + m["BConfig.Log.Outputs"] = BConfig.Log.Outputs + return m +} diff --git a/src/vendor/github.com/astaxie/beego/adminui.go b/src/vendor/github.com/astaxie/beego/adminui.go index 7bb32b349..cdcdef33f 100644 --- a/src/vendor/github.com/astaxie/beego/adminui.go +++ b/src/vendor/github.com/astaxie/beego/adminui.go @@ -78,13 +78,14 @@ var qpsTpl = `{{define "content"}} {{range $i, $elem := .Content.Data}} - {{range $elem}} - - {{.}} - - {{end}} + {{index $elem 0}} + {{index $elem 1}} + {{index $elem 2}} + {{index $elem 4}} + {{index $elem 6}} + {{index $elem 8}} + {{index $elem 10}} - {{end}} diff --git a/src/vendor/github.com/astaxie/beego/app.go b/src/vendor/github.com/astaxie/beego/app.go index af54ea4b5..25ea2a04f 100644 --- a/src/vendor/github.com/astaxie/beego/app.go +++ b/src/vendor/github.com/astaxie/beego/app.go @@ -24,6 +24,7 @@ import ( "time" "github.com/astaxie/beego/grace" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/utils" ) @@ -68,9 +69,9 @@ func (app *App) Run() { if BConfig.Listen.EnableFcgi { if BConfig.Listen.EnableStdIo { if err = fcgi.Serve(nil, app.Handlers); err == nil { // standard I/O - BeeLogger.Info("Use FCGI via standard I/O") + logs.Info("Use FCGI via standard I/O") } else { - BeeLogger.Critical("Cannot use FCGI via standard I/O", err) + logs.Critical("Cannot use FCGI via standard I/O", err) } return } @@ -84,10 +85,10 @@ func (app *App) Run() { l, err = net.Listen("tcp", addr) } if err != nil { - BeeLogger.Critical("Listen: ", err) + logs.Critical("Listen: ", err) } if err = fcgi.Serve(l, app.Handlers); err != nil { - BeeLogger.Critical("fcgi.Serve: ", err) + logs.Critical("fcgi.Serve: ", err) } return } @@ -95,6 +96,7 @@ func (app *App) Run() { app.Server.Handler = app.Handlers app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second + app.Server.ErrorLog = logs.GetLogger("HTTP") // run graceful mode if BConfig.Listen.Graceful { @@ -111,7 +113,7 @@ func (app *App) Run() { server.Server.ReadTimeout = app.Server.ReadTimeout server.Server.WriteTimeout = app.Server.WriteTimeout if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { - BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) + logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) time.Sleep(100 * time.Microsecond) endRunning <- true } @@ -126,7 +128,7 @@ func (app *App) Run() { server.Network = "tcp4" } if err := server.ListenAndServe(); err != nil { - BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) + logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) time.Sleep(100 * time.Microsecond) endRunning <- true } @@ -137,16 +139,18 @@ func (app *App) Run() { } // run normal mode - app.Server.Addr = addr if BConfig.Listen.EnableHTTPS { go func() { time.Sleep(20 * time.Microsecond) if BConfig.Listen.HTTPSPort != 0 { app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort) + } else if BConfig.Listen.EnableHTTP { + BeeLogger.Info("Start https server error, confict with http.Please reset https port") + return } - BeeLogger.Info("https server Running on %s", app.Server.Addr) + logs.Info("https server Running on https://%s", app.Server.Addr) if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { - BeeLogger.Critical("ListenAndServeTLS: ", err) + logs.Critical("ListenAndServeTLS: ", err) time.Sleep(100 * time.Microsecond) endRunning <- true } @@ -155,24 +159,24 @@ func (app *App) Run() { if BConfig.Listen.EnableHTTP { go func() { app.Server.Addr = addr - BeeLogger.Info("http server Running on %s", app.Server.Addr) + logs.Info("http server Running on http://%s", app.Server.Addr) if BConfig.Listen.ListenTCP4 { ln, err := net.Listen("tcp4", app.Server.Addr) if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) + logs.Critical("ListenAndServe: ", err) time.Sleep(100 * time.Microsecond) endRunning <- true return } if err = app.Server.Serve(ln); err != nil { - BeeLogger.Critical("ListenAndServe: ", err) + logs.Critical("ListenAndServe: ", err) time.Sleep(100 * time.Microsecond) endRunning <- true return } } else { if err := app.Server.ListenAndServe(); err != nil { - BeeLogger.Critical("ListenAndServe: ", err) + logs.Critical("ListenAndServe: ", err) time.Sleep(100 * time.Microsecond) endRunning <- true } @@ -344,9 +348,9 @@ func Any(rootpath string, f FilterFunc) *App { // Handler used to register a Handler router // usage: -// beego.Handler("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) +// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { +// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) +// })) func Handler(rootpath string, h http.Handler, options ...interface{}) *App { BeeApp.Handlers.Handler(rootpath, h, options...) return BeeApp diff --git a/src/vendor/github.com/astaxie/beego/beego.go b/src/vendor/github.com/astaxie/beego/beego.go index 8f82cdcff..0512dc60a 100644 --- a/src/vendor/github.com/astaxie/beego/beego.go +++ b/src/vendor/github.com/astaxie/beego/beego.go @@ -23,7 +23,7 @@ import ( const ( // VERSION represent beego web framework version. - VERSION = "1.6.1" + VERSION = "1.9.0" // DEV is for develop DEV = "dev" @@ -40,9 +40,9 @@ var ( // AddAPPStartHook is used to register the hookfunc // The hookfuncs will run in beego.Run() -// such as sessionInit, middlerware start, buildtemplate, admin start -func AddAPPStartHook(hf hookfunc) { - hooks = append(hooks, hf) +// such as initiating session , starting middleware , building template, starting admin control and so on. +func AddAPPStartHook(hf ...hookfunc) { + hooks = append(hooks, hf...) } // Run beego application. @@ -51,6 +51,7 @@ func AddAPPStartHook(hf hookfunc) { // beego.Run(":8089") // beego.Run("127.0.0.1:8089") func Run(params ...string) { + initBeforeHTTPRun() if len(params) > 0 && params[0] != "" { @@ -68,12 +69,14 @@ func Run(params ...string) { func initBeforeHTTPRun() { //init hooks - AddAPPStartHook(registerMime) - AddAPPStartHook(registerDefaultErrorHandler) - AddAPPStartHook(registerSession) - AddAPPStartHook(registerDocs) - AddAPPStartHook(registerTemplate) - AddAPPStartHook(registerAdmin) + AddAPPStartHook( + registerMime, + registerDefaultErrorHandler, + registerSession, + registerTemplate, + registerAdmin, + registerGzip, + ) for _, hk := range hooks { if err := hk(); err != nil { @@ -84,8 +87,16 @@ func initBeforeHTTPRun() { // TestBeegoInit is for test package init func TestBeegoInit(ap string) { - os.Setenv("BEEGO_RUNMODE", "test") - appConfigPath = filepath.Join(ap, "conf", "app.conf") + path := filepath.Join(ap, "conf", "app.conf") os.Chdir(ap) + InitBeegoBeforeTest(path) +} + +// InitBeegoBeforeTest is for test package init +func InitBeegoBeforeTest(appConfigPath string) { + if err := LoadAppConfig(appConfigProvider, appConfigPath); err != nil { + panic(err) + } + BConfig.RunMode = "test" initBeforeHTTPRun() } diff --git a/src/vendor/github.com/astaxie/beego/cache/conv.go b/src/vendor/github.com/astaxie/beego/cache/conv.go index dbdff1c77..878005864 100644 --- a/src/vendor/github.com/astaxie/beego/cache/conv.go +++ b/src/vendor/github.com/astaxie/beego/cache/conv.go @@ -28,7 +28,7 @@ func GetString(v interface{}) string { return string(result) default: if v != nil { - return fmt.Sprintf("%v", result) + return fmt.Sprint(result) } } return "" diff --git a/src/vendor/github.com/astaxie/beego/cache/conv_test.go b/src/vendor/github.com/astaxie/beego/cache/conv_test.go index cf792fa65..b90e224a3 100644 --- a/src/vendor/github.com/astaxie/beego/cache/conv_test.go +++ b/src/vendor/github.com/astaxie/beego/cache/conv_test.go @@ -118,14 +118,14 @@ func TestGetFloat64(t *testing.T) { func TestGetBool(t *testing.T) { var t1 = true - if true != GetBool(t1) { + if !GetBool(t1) { t.Error("get bool from bool error") } var t2 = "true" - if true != GetBool(t2) { + if !GetBool(t2) { t.Error("get bool from string error") } - if false != GetBool(nil) { + if GetBool(nil) { t.Error("get bool from nil error") } } diff --git a/src/vendor/github.com/astaxie/beego/cache/file.go b/src/vendor/github.com/astaxie/beego/cache/file.go index 4b0309804..691ce7cd7 100644 --- a/src/vendor/github.com/astaxie/beego/cache/file.go +++ b/src/vendor/github.com/astaxie/beego/cache/file.go @@ -22,6 +22,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "os" "path/filepath" "reflect" @@ -222,33 +223,13 @@ func exists(path string) (bool, error) { // FileGetContents Get bytes to file. // if non-exist, create this file. func FileGetContents(filename string) (data []byte, e error) { - f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm) - if e != nil { - return - } - defer f.Close() - stat, e := f.Stat() - if e != nil { - return - } - data = make([]byte, stat.Size()) - result, e := f.Read(data) - if e != nil || int64(result) != stat.Size() { - return nil, e - } - return + return ioutil.ReadFile(filename) } // FilePutContents Put bytes to file. // if non-exist, create this file. func FilePutContents(filename string, content []byte) error { - fp, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm) - if err != nil { - return err - } - defer fp.Close() - _, err = fp.Write(content) - return err + return ioutil.WriteFile(filename, content, os.ModePerm) } // GobEncode Gob encodes file cache item. diff --git a/src/vendor/github.com/astaxie/beego/cache/memcache/memcache.go b/src/vendor/github.com/astaxie/beego/cache/memcache/memcache.go index 3f0fe4111..0624f5faa 100644 --- a/src/vendor/github.com/astaxie/beego/cache/memcache/memcache.go +++ b/src/vendor/github.com/astaxie/beego/cache/memcache/memcache.go @@ -33,12 +33,10 @@ import ( "encoding/json" "errors" "strings" - - "github.com/bradfitz/gomemcache/memcache" - "time" "github.com/astaxie/beego/cache" + "github.com/bradfitz/gomemcache/memcache" ) // Cache Memcache adapter. @@ -60,7 +58,7 @@ func (rc *Cache) Get(key string) interface{} { } } if item, err := rc.conn.Get(key); err == nil { - return string(item.Value) + return item.Value } return nil } @@ -80,7 +78,7 @@ func (rc *Cache) GetMulti(keys []string) []interface{} { mv, err := rc.conn.GetMulti(keys) if err == nil { for _, v := range mv { - rv = append(rv, string(v.Value)) + rv = append(rv, v.Value) } return rv } @@ -90,18 +88,21 @@ func (rc *Cache) GetMulti(keys []string) []interface{} { return rv } -// Put put value to memcache. only support string. +// Put put value to memcache. func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error { if rc.conn == nil { if err := rc.connectInit(); err != nil { return err } } - v, ok := val.(string) - if !ok { - return errors.New("val must string") + item := memcache.Item{Key: key, Expiration: int32(timeout / time.Second)} + if v, ok := val.([]byte); ok { + item.Value = v + } else if str, ok := val.(string); ok { + item.Value = []byte(str) + } else { + return errors.New("val only support string and []byte") } - item := memcache.Item{Key: key, Value: []byte(v), Expiration: int32(timeout / time.Second)} return rc.conn.Set(&item) } @@ -145,10 +146,7 @@ func (rc *Cache) IsExist(key string) bool { } } _, err := rc.conn.Get(key) - if err != nil { - return false - } - return true + return !(err != nil) } // ClearAll clear all cached in memcache. diff --git a/src/vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go b/src/vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go index 0c8c57f29..d9129b695 100644 --- a/src/vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go +++ b/src/vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go @@ -46,7 +46,7 @@ func TestMemcacheCache(t *testing.T) { t.Error("set Error", err) } - if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 { + if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 1 { t.Error("get err") } @@ -54,7 +54,7 @@ func TestMemcacheCache(t *testing.T) { t.Error("Incr Error", err) } - if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 2 { + if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 2 { t.Error("get err") } @@ -62,7 +62,7 @@ func TestMemcacheCache(t *testing.T) { t.Error("Decr Error", err) } - if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 { + if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 1 { t.Error("get err") } bm.Delete("astaxie") @@ -78,7 +78,7 @@ func TestMemcacheCache(t *testing.T) { t.Error("check err") } - if v := bm.Get("astaxie").(string); v != "author" { + if v := bm.Get("astaxie").([]byte); string(v) != "author" { t.Error("get err") } @@ -94,10 +94,10 @@ func TestMemcacheCache(t *testing.T) { if len(vv) != 2 { t.Error("GetMulti ERROR") } - if vv[0].(string) != "author" && vv[0].(string) != "author1" { + if string(vv[0].([]byte)) != "author" && string(vv[0].([]byte)) != "author1" { t.Error("GetMulti ERROR") } - if vv[1].(string) != "author1" && vv[1].(string) != "author" { + if string(vv[1].([]byte)) != "author1" && string(vv[1].([]byte)) != "author" { t.Error("GetMulti ERROR") } diff --git a/src/vendor/github.com/astaxie/beego/cache/memory.go b/src/vendor/github.com/astaxie/beego/cache/memory.go index fff2ebbb3..57e868cfe 100644 --- a/src/vendor/github.com/astaxie/beego/cache/memory.go +++ b/src/vendor/github.com/astaxie/beego/cache/memory.go @@ -217,26 +217,31 @@ func (bc *MemoryCache) vaccuum() { if bc.items == nil { return } - for name := range bc.items { - bc.itemExpired(name) + if keys := bc.expiredKeys(); len(keys) != 0 { + bc.clearItems(keys) } } } -// itemExpired returns true if an item is expired. -func (bc *MemoryCache) itemExpired(name string) bool { +// expiredKeys returns key list which are expired. +func (bc *MemoryCache) expiredKeys() (keys []string) { + bc.RLock() + defer bc.RUnlock() + for key, itm := range bc.items { + if itm.isExpire() { + keys = append(keys, key) + } + } + return +} + +// clearItems removes all the items which key in keys. +func (bc *MemoryCache) clearItems(keys []string) { bc.Lock() defer bc.Unlock() - - itm, ok := bc.items[name] - if !ok { - return true + for _, key := range keys { + delete(bc.items, key) } - if itm.isExpire() { - delete(bc.items, name) - return true - } - return false } func init() { diff --git a/src/vendor/github.com/astaxie/beego/cache/redis/redis.go b/src/vendor/github.com/astaxie/beego/cache/redis/redis.go index 781e38366..3e71fb53a 100644 --- a/src/vendor/github.com/astaxie/beego/cache/redis/redis.go +++ b/src/vendor/github.com/astaxie/beego/cache/redis/redis.go @@ -137,7 +137,7 @@ func (rc *Cache) IsExist(key string) bool { if err != nil { return false } - if v == false { + if !v { if _, err = rc.do("HDEL", rc.key, key); err != nil { return false } diff --git a/src/vendor/github.com/astaxie/beego/cache/redis/redis_test.go b/src/vendor/github.com/astaxie/beego/cache/redis/redis_test.go index 47c5acc66..6b81da4d2 100644 --- a/src/vendor/github.com/astaxie/beego/cache/redis/redis_test.go +++ b/src/vendor/github.com/astaxie/beego/cache/redis/redis_test.go @@ -18,9 +18,8 @@ import ( "testing" "time" - "github.com/garyburd/redigo/redis" - "github.com/astaxie/beego/cache" + "github.com/garyburd/redigo/redis" ) func TestRedisCache(t *testing.T) { diff --git a/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go b/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go index bfee69ce2..fa2ce04bb 100644 --- a/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go +++ b/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go @@ -53,7 +53,7 @@ func (rc *Cache) GetMulti(keys []string) []interface{} { resSize := len(res) if err == nil { for i := 1; i < resSize; i += 2 { - values = append(values, string(res[i+1])) + values = append(values, res[i+1]) } return values } @@ -71,10 +71,7 @@ func (rc *Cache) DelMulti(keys []string) error { } } _, err := rc.conn.Do("multi_del", keys) - if err != nil { - return err - } - return nil + return err } // Put put value to memcache. only support string. @@ -113,10 +110,7 @@ func (rc *Cache) Delete(key string) error { } } _, err := rc.conn.Del(key) - if err != nil { - return err - } - return nil + return err } // Incr increase counter. @@ -152,7 +146,7 @@ func (rc *Cache) IsExist(key string) bool { if err != nil { return false } - if resp[1] == "1" { + if len(resp) == 2 && resp[1] == "1" { return true } return false @@ -175,7 +169,7 @@ func (rc *Cache) ClearAll() error { } keys := []string{} for i := 1; i < size; i += 2 { - keys = append(keys, string(resp[i])) + keys = append(keys, resp[i]) } _, e := rc.conn.Do("multi_del", keys) if e != nil { @@ -229,10 +223,7 @@ func (rc *Cache) connectInit() error { } var err error rc.conn, err = ssdb.Connect(host, port) - if err != nil { - return err - } - return nil + return err } func init() { diff --git a/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go b/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go index e03ba3437..dd474960a 100644 --- a/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go +++ b/src/vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go @@ -1,10 +1,11 @@ package ssdb import ( - "github.com/astaxie/beego/cache" "strconv" "testing" "time" + + "github.com/astaxie/beego/cache" ) func TestSsdbcacheCache(t *testing.T) { diff --git a/src/vendor/github.com/astaxie/beego/config.go b/src/vendor/github.com/astaxie/beego/config.go index 2761e7cba..e6e99570f 100644 --- a/src/vendor/github.com/astaxie/beego/config.go +++ b/src/vendor/github.com/astaxie/beego/config.go @@ -18,9 +18,13 @@ import ( "fmt" "os" "path/filepath" + "reflect" + "runtime" "strings" "github.com/astaxie/beego/config" + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/session" "github.com/astaxie/beego/utils" ) @@ -32,10 +36,12 @@ type Config struct { RouterCaseSensitive bool ServerName string RecoverPanic bool + RecoverFunc func(*context.Context) CopyRequestBody bool EnableGzip bool MaxMemory int64 EnableErrorsShow bool + EnableErrorsRender bool Listen Listen WebConfig WebConfig Log LogConfig @@ -81,14 +87,18 @@ type WebConfig struct { // SessionConfig holds session related config type SessionConfig struct { - SessionOn bool - SessionProvider string - SessionName string - SessionGCMaxLifetime int64 - SessionProviderConfig string - SessionCookieLifeTime int - SessionAutoSetCookie bool - SessionDomain string + SessionOn bool + SessionProvider string + SessionName string + SessionGCMaxLifetime int64 + SessionProviderConfig string + SessionCookieLifeTime int + SessionAutoSetCookie bool + SessionDomain string + SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies. + SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers + SessionNameInHTTPHeader string + SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params } // LogConfig holds Log related config @@ -115,20 +125,72 @@ var ( ) func init() { - AppPath, _ = filepath.Abs(filepath.Dir(os.Args[0])) + BConfig = newBConfig() + var err error + if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil { + panic(err) + } + workPath, err := os.Getwd() + if err != nil { + panic(err) + } + appConfigPath = filepath.Join(workPath, "conf", "app.conf") + if !utils.FileExists(appConfigPath) { + appConfigPath = filepath.Join(AppPath, "conf", "app.conf") + if !utils.FileExists(appConfigPath) { + AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} + return + } + } + if err = parseConfig(appConfigPath); err != nil { + panic(err) + } +} - os.Chdir(AppPath) +func recoverPanic(ctx *context.Context) { + if err := recover(); err != nil { + if err == ErrAbort { + return + } + if !BConfig.RecoverPanic { + panic(err) + } + if BConfig.EnableErrorsShow { + if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { + exception(fmt.Sprint(err), ctx) + return + } + } + var stack string + logs.Critical("the request url is ", ctx.Input.URL()) + logs.Critical("Handler crashed with error", err) + for i := 1; ; i++ { + _, file, line, ok := runtime.Caller(i) + if !ok { + break + } + logs.Critical(fmt.Sprintf("%s:%d", file, line)) + stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) + } + if BConfig.RunMode == DEV && BConfig.EnableErrorsRender { + showErr(err, ctx, stack) + } + } +} - BConfig = &Config{ +func newBConfig() *Config { + return &Config{ AppName: "beego", RunMode: DEV, RouterCaseSensitive: true, ServerName: "beegoServer:" + VERSION, RecoverPanic: true, + RecoverFunc: recoverPanic, CopyRequestBody: false, EnableGzip: false, MaxMemory: 1 << 26, //64MB EnableErrorsShow: true, + EnableErrorsRender: true, Listen: Listen{ Graceful: false, ServerTimeOut: 0, @@ -162,14 +224,18 @@ func init() { XSRFKey: "beegoxsrf", XSRFExpire: 0, Session: SessionConfig{ - SessionOn: false, - SessionProvider: "memory", - SessionName: "beegosessionID", - SessionGCMaxLifetime: 3600, - SessionProviderConfig: "", - SessionCookieLifeTime: 0, //set cookie default is the browser life - SessionAutoSetCookie: true, - SessionDomain: "", + SessionOn: false, + SessionProvider: "memory", + SessionName: "beegosessionID", + SessionGCMaxLifetime: 3600, + SessionProviderConfig: "", + SessionDisableHTTPOnly: false, + SessionCookieLifeTime: 0, //set cookie default is the browser life + SessionAutoSetCookie: true, + SessionDomain: "", + SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers + SessionNameInHTTPHeader: "Beegosessionid", + SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params }, }, Log: LogConfig{ @@ -178,16 +244,6 @@ func init() { Outputs: map[string]string{"console": ""}, }, } - - appConfigPath = filepath.Join(AppPath, "conf", "app.conf") - if !utils.FileExists(appConfigPath) { - AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} - return - } - - if err := parseConfig(appConfigPath); err != nil { - panic(err) - } } // now only support ini, next will support json. @@ -196,74 +252,33 @@ func parseConfig(appConfigPath string) (err error) { if err != nil { return err } + return assignConfig(AppConfig) +} + +func assignConfig(ac config.Configer) error { + for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { + assignSingleConfig(i, ac) + } // set the run mode first if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" { BConfig.RunMode = envRunMode - } else if runMode := AppConfig.String("RunMode"); runMode != "" { + } else if runMode := ac.String("RunMode"); runMode != "" { BConfig.RunMode = runMode } - BConfig.AppName = AppConfig.DefaultString("AppName", BConfig.AppName) - BConfig.RecoverPanic = AppConfig.DefaultBool("RecoverPanic", BConfig.RecoverPanic) - BConfig.RouterCaseSensitive = AppConfig.DefaultBool("RouterCaseSensitive", BConfig.RouterCaseSensitive) - BConfig.ServerName = AppConfig.DefaultString("ServerName", BConfig.ServerName) - BConfig.EnableGzip = AppConfig.DefaultBool("EnableGzip", BConfig.EnableGzip) - BConfig.EnableErrorsShow = AppConfig.DefaultBool("EnableErrorsShow", BConfig.EnableErrorsShow) - BConfig.CopyRequestBody = AppConfig.DefaultBool("CopyRequestBody", BConfig.CopyRequestBody) - BConfig.MaxMemory = AppConfig.DefaultInt64("MaxMemory", BConfig.MaxMemory) - BConfig.Listen.Graceful = AppConfig.DefaultBool("Graceful", BConfig.Listen.Graceful) - BConfig.Listen.HTTPAddr = AppConfig.String("HTTPAddr") - BConfig.Listen.HTTPPort = AppConfig.DefaultInt("HTTPPort", BConfig.Listen.HTTPPort) - BConfig.Listen.ListenTCP4 = AppConfig.DefaultBool("ListenTCP4", BConfig.Listen.ListenTCP4) - BConfig.Listen.EnableHTTP = AppConfig.DefaultBool("EnableHTTP", BConfig.Listen.EnableHTTP) - BConfig.Listen.EnableHTTPS = AppConfig.DefaultBool("EnableHTTPS", BConfig.Listen.EnableHTTPS) - BConfig.Listen.HTTPSAddr = AppConfig.DefaultString("HTTPSAddr", BConfig.Listen.HTTPSAddr) - BConfig.Listen.HTTPSPort = AppConfig.DefaultInt("HTTPSPort", BConfig.Listen.HTTPSPort) - BConfig.Listen.HTTPSCertFile = AppConfig.DefaultString("HTTPSCertFile", BConfig.Listen.HTTPSCertFile) - BConfig.Listen.HTTPSKeyFile = AppConfig.DefaultString("HTTPSKeyFile", BConfig.Listen.HTTPSKeyFile) - BConfig.Listen.EnableAdmin = AppConfig.DefaultBool("EnableAdmin", BConfig.Listen.EnableAdmin) - BConfig.Listen.AdminAddr = AppConfig.DefaultString("AdminAddr", BConfig.Listen.AdminAddr) - BConfig.Listen.AdminPort = AppConfig.DefaultInt("AdminPort", BConfig.Listen.AdminPort) - BConfig.Listen.EnableFcgi = AppConfig.DefaultBool("EnableFcgi", BConfig.Listen.EnableFcgi) - BConfig.Listen.EnableStdIo = AppConfig.DefaultBool("EnableStdIo", BConfig.Listen.EnableStdIo) - BConfig.Listen.ServerTimeOut = AppConfig.DefaultInt64("ServerTimeOut", BConfig.Listen.ServerTimeOut) - BConfig.WebConfig.AutoRender = AppConfig.DefaultBool("AutoRender", BConfig.WebConfig.AutoRender) - BConfig.WebConfig.ViewsPath = AppConfig.DefaultString("ViewsPath", BConfig.WebConfig.ViewsPath) - BConfig.WebConfig.DirectoryIndex = AppConfig.DefaultBool("DirectoryIndex", BConfig.WebConfig.DirectoryIndex) - BConfig.WebConfig.FlashName = AppConfig.DefaultString("FlashName", BConfig.WebConfig.FlashName) - BConfig.WebConfig.FlashSeparator = AppConfig.DefaultString("FlashSeparator", BConfig.WebConfig.FlashSeparator) - BConfig.WebConfig.EnableDocs = AppConfig.DefaultBool("EnableDocs", BConfig.WebConfig.EnableDocs) - BConfig.WebConfig.XSRFKey = AppConfig.DefaultString("XSRFKEY", BConfig.WebConfig.XSRFKey) - BConfig.WebConfig.EnableXSRF = AppConfig.DefaultBool("EnableXSRF", BConfig.WebConfig.EnableXSRF) - BConfig.WebConfig.XSRFExpire = AppConfig.DefaultInt("XSRFExpire", BConfig.WebConfig.XSRFExpire) - BConfig.WebConfig.TemplateLeft = AppConfig.DefaultString("TemplateLeft", BConfig.WebConfig.TemplateLeft) - BConfig.WebConfig.TemplateRight = AppConfig.DefaultString("TemplateRight", BConfig.WebConfig.TemplateRight) - BConfig.WebConfig.Session.SessionOn = AppConfig.DefaultBool("SessionOn", BConfig.WebConfig.Session.SessionOn) - BConfig.WebConfig.Session.SessionProvider = AppConfig.DefaultString("SessionProvider", BConfig.WebConfig.Session.SessionProvider) - BConfig.WebConfig.Session.SessionName = AppConfig.DefaultString("SessionName", BConfig.WebConfig.Session.SessionName) - BConfig.WebConfig.Session.SessionProviderConfig = AppConfig.DefaultString("SessionProviderConfig", BConfig.WebConfig.Session.SessionProviderConfig) - BConfig.WebConfig.Session.SessionGCMaxLifetime = AppConfig.DefaultInt64("SessionGCMaxLifetime", BConfig.WebConfig.Session.SessionGCMaxLifetime) - BConfig.WebConfig.Session.SessionCookieLifeTime = AppConfig.DefaultInt("SessionCookieLifeTime", BConfig.WebConfig.Session.SessionCookieLifeTime) - BConfig.WebConfig.Session.SessionAutoSetCookie = AppConfig.DefaultBool("SessionAutoSetCookie", BConfig.WebConfig.Session.SessionAutoSetCookie) - BConfig.WebConfig.Session.SessionDomain = AppConfig.DefaultString("SessionDomain", BConfig.WebConfig.Session.SessionDomain) - BConfig.Log.AccessLogs = AppConfig.DefaultBool("LogAccessLogs", BConfig.Log.AccessLogs) - BConfig.Log.FileLineNum = AppConfig.DefaultBool("LogFileLineNum", BConfig.Log.FileLineNum) - - if sd := AppConfig.String("StaticDir"); sd != "" { - for k := range BConfig.WebConfig.StaticDir { - delete(BConfig.WebConfig.StaticDir, k) - } + if sd := ac.String("StaticDir"); sd != "" { + BConfig.WebConfig.StaticDir = map[string]string{} sds := strings.Fields(sd) for _, v := range sds { if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { - BConfig.WebConfig.StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1] + BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1] } else { - BConfig.WebConfig.StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0] + BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0] } } } - if sgz := AppConfig.String("StaticExtensionsToGzip"); sgz != "" { + if sgz := ac.String("StaticExtensionsToGzip"); sgz != "" { extensions := strings.Split(sgz, ",") fileExts := []string{} for _, ext := range extensions { @@ -281,7 +296,11 @@ func parseConfig(appConfigPath string) (err error) { } } - if lo := AppConfig.String("LogOutputs"); lo != "" { + if lo := ac.String("LogOutputs"); lo != "" { + // if lo is not nil or empty + // means user has set his own LogOutputs + // clear the default setting to BConfig.Log.Outputs + BConfig.Log.Outputs = make(map[string]string) los := strings.Split(lo, ";") for _, v := range los { if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 { @@ -293,18 +312,50 @@ func parseConfig(appConfigPath string) (err error) { } //init log - BeeLogger.Reset() + logs.Reset() for adaptor, config := range BConfig.Log.Outputs { - err = BeeLogger.SetLogger(adaptor, config) + err := logs.SetLogger(adaptor, config) if err != nil { - fmt.Printf("%s with the config `%s` got err:%s\n", adaptor, config, err) + fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, config, err.Error())) } } - SetLogFuncCall(BConfig.Log.FileLineNum) + logs.SetLogFuncCall(BConfig.Log.FileLineNum) return nil } +func assignSingleConfig(p interface{}, ac config.Configer) { + pt := reflect.TypeOf(p) + if pt.Kind() != reflect.Ptr { + return + } + pt = pt.Elem() + if pt.Kind() != reflect.Struct { + return + } + pv := reflect.ValueOf(p).Elem() + + for i := 0; i < pt.NumField(); i++ { + pf := pv.Field(i) + if !pf.CanSet() { + continue + } + name := pt.Field(i).Name + switch pf.Kind() { + case reflect.String: + pf.SetString(ac.DefaultString(name, pf.String())) + case reflect.Int, reflect.Int64: + pf.SetInt(ac.DefaultInt64(name, pf.Int())) + case reflect.Bool: + pf.SetBool(ac.DefaultBool(name, pf.Bool())) + case reflect.Struct: + default: + //do nothing here + } + } + +} + // LoadAppConfig allow developer to apply a config file func LoadAppConfig(adapterName, configPath string) error { absConfigPath, err := filepath.Abs(configPath) @@ -316,10 +367,6 @@ func LoadAppConfig(adapterName, configPath string) error { return fmt.Errorf("the target config file: %s don't exist", configPath) } - if absConfigPath == appConfigPath { - return nil - } - appConfigPath = absConfigPath appConfigProvider = adapterName @@ -353,7 +400,7 @@ func (b *beegoAppConfig) String(key string) string { } func (b *beegoAppConfig) Strings(key string) []string { - if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); v[0] != "" { + if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 { return v } return b.innerConfig.Strings(key) diff --git a/src/vendor/github.com/astaxie/beego/config/config.go b/src/vendor/github.com/astaxie/beego/config/config.go index c0afec058..c620504a1 100644 --- a/src/vendor/github.com/astaxie/beego/config/config.go +++ b/src/vendor/github.com/astaxie/beego/config/config.go @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package config is used to parse config +// Package config is used to parse config. // Usage: -// import( -// "github.com/astaxie/beego/config" -// ) +// import "github.com/astaxie/beego/config" +//Examples. // // cnf, err := config.NewConfig("ini", "config.conf") // @@ -38,12 +37,14 @@ // cnf.DIY(key string) (interface{}, error) // cnf.GetSection(section string) (map[string]string, error) // cnf.SaveConfigFile(filename string) error -// -// more docs http://beego.me/docs/module/config.md +//More docs http://beego.me/docs/module/config.md package config import ( "fmt" + "os" + "reflect" + "time" ) // Configer defines how to get and set value from configuration raw data. @@ -107,6 +108,69 @@ func NewConfigData(adapterName string, data []byte) (Configer, error) { return adapter.ParseData(data) } +// ExpandValueEnvForMap convert all string value with environment variable. +func ExpandValueEnvForMap(m map[string]interface{}) map[string]interface{} { + for k, v := range m { + switch value := v.(type) { + case string: + m[k] = ExpandValueEnv(value) + case map[string]interface{}: + m[k] = ExpandValueEnvForMap(value) + case map[string]string: + for k2, v2 := range value { + value[k2] = ExpandValueEnv(v2) + } + m[k] = value + } + } + return m +} + +// ExpandValueEnv returns value of convert with environment variable. +// +// Return environment variable if value start with "${" and end with "}". +// Return default value if environment variable is empty or not exist. +// +// It accept value formats "${env}" , "${env||}}" , "${env||defaultValue}" , "defaultvalue". +// Examples: +// v1 := config.ExpandValueEnv("${GOPATH}") // return the GOPATH environment variable. +// v2 := config.ExpandValueEnv("${GOAsta||/usr/local/go}") // return the default value "/usr/local/go/". +// v3 := config.ExpandValueEnv("Astaxie") // return the value "Astaxie". +func ExpandValueEnv(value string) (realValue string) { + realValue = value + + vLen := len(value) + // 3 = ${} + if vLen < 3 { + return + } + // Need start with "${" and end with "}", then return. + if value[0] != '$' || value[1] != '{' || value[vLen-1] != '}' { + return + } + + key := "" + defalutV := "" + // value start with "${" + for i := 2; i < vLen; i++ { + if value[i] == '|' && (i+1 < vLen && value[i+1] == '|') { + key = value[2:i] + defalutV = value[i+2 : vLen-1] // other string is default value. + break + } else if value[i] == '}' { + key = value[2:i] + break + } + } + + realValue = os.Getenv(key) + if realValue == "" { + realValue = defalutV + } + + return +} + // ParseBool returns the boolean value represented by the string. // // It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On, @@ -125,16 +189,16 @@ func ParseBool(val interface{}) (value bool, err error) { return false, nil } case int8, int32, int64: - strV := fmt.Sprintf("%s", v) + strV := fmt.Sprintf("%d", v) if strV == "1" { return true, nil } else if strV == "0" { return false, nil } case float64: - if v == 1 { + if v == 1.0 { return true, nil - } else if v == 0 { + } else if v == 0.0 { return false, nil } } @@ -142,3 +206,37 @@ func ParseBool(val interface{}) (value bool, err error) { } return false, fmt.Errorf("parsing : invalid syntax") } + +// ToString converts values of any type to string. +func ToString(x interface{}) string { + switch y := x.(type) { + + // Handle dates with special logic + // This needs to come above the fmt.Stringer + // test since time.Time's have a .String() + // method + case time.Time: + return y.Format("A Monday") + + // Handle type string + case string: + return y + + // Handle type with .String() method + case fmt.Stringer: + return y.String() + + // Handle type with .Error() method + case error: + return y.Error() + + } + + // Handle named string type + if v := reflect.ValueOf(x); v.Kind() == reflect.String { + return v.String() + } + + // Fallback to fmt package for anything else like numeric types + return fmt.Sprint(x) +} diff --git a/src/vendor/github.com/astaxie/beego/config/config_test.go b/src/vendor/github.com/astaxie/beego/config/config_test.go new file mode 100644 index 000000000..15d6ffa61 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/config/config_test.go @@ -0,0 +1,55 @@ +// Copyright 2016 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "os" + "testing" +) + +func TestExpandValueEnv(t *testing.T) { + + testCases := []struct { + item string + want string + }{ + {"", ""}, + {"$", "$"}, + {"{", "{"}, + {"{}", "{}"}, + {"${}", ""}, + {"${|}", ""}, + {"${}", ""}, + {"${{}}", ""}, + {"${{||}}", "}"}, + {"${pwd||}", ""}, + {"${pwd||}", ""}, + {"${pwd||}", ""}, + {"${pwd||}}", "}"}, + {"${pwd||{{||}}}", "{{||}}"}, + {"${GOPATH}", os.Getenv("GOPATH")}, + {"${GOPATH||}", os.Getenv("GOPATH")}, + {"${GOPATH||root}", os.Getenv("GOPATH")}, + {"${GOPATH_NOT||root}", "root"}, + {"${GOPATH_NOT||||root}", "||root"}, + } + + for _, c := range testCases { + if got := ExpandValueEnv(c.item); got != c.want { + t.Errorf("expand value error, item %q want %q, got %q", c.item, c.want, got) + } + } + +} diff --git a/src/vendor/github.com/astaxie/beego/config/env/env.go b/src/vendor/github.com/astaxie/beego/config/env/env.go new file mode 100644 index 000000000..34f094feb --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/config/env/env.go @@ -0,0 +1,87 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// Copyright 2017 Faissal Elamraoui. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package env is used to parse environment. +package env + +import ( + "fmt" + "os" + "strings" + + "github.com/astaxie/beego/utils" +) + +var env *utils.BeeMap + +func init() { + env = utils.NewBeeMap() + for _, e := range os.Environ() { + splits := strings.Split(e, "=") + env.Set(splits[0], os.Getenv(splits[0])) + } +} + +// Get returns a value by key. +// If the key does not exist, the default value will be returned. +func Get(key string, defVal string) string { + if val := env.Get(key); val != nil { + return val.(string) + } + return defVal +} + +// MustGet returns a value by key. +// If the key does not exist, it will return an error. +func MustGet(key string) (string, error) { + if val := env.Get(key); val != nil { + return val.(string), nil + } + return "", fmt.Errorf("no env variable with %s", key) +} + +// Set sets a value in the ENV copy. +// This does not affect the child process environment. +func Set(key string, value string) { + env.Set(key, value) +} + +// MustSet sets a value in the ENV copy and the child process environment. +// It returns an error in case the set operation failed. +func MustSet(key string, value string) error { + err := os.Setenv(key, value) + if err != nil { + return err + } + env.Set(key, value) + return nil +} + +// GetAll returns all keys/values in the current child process environment. +func GetAll() map[string]string { + items := env.Items() + envs := make(map[string]string, env.Count()) + + for key, val := range items { + switch key := key.(type) { + case string: + switch val := val.(type) { + case string: + envs[key] = val + } + } + } + return envs +} diff --git a/src/vendor/github.com/astaxie/beego/config/env/env_test.go b/src/vendor/github.com/astaxie/beego/config/env/env_test.go new file mode 100644 index 000000000..3f1d4dbab --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/config/env/env_test.go @@ -0,0 +1,75 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// Copyright 2017 Faissal Elamraoui. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package env + +import ( + "os" + "testing" +) + +func TestEnvGet(t *testing.T) { + gopath := Get("GOPATH", "") + if gopath != os.Getenv("GOPATH") { + t.Error("expected GOPATH not empty.") + } + + noExistVar := Get("NOEXISTVAR", "foo") + if noExistVar != "foo" { + t.Errorf("expected NOEXISTVAR to equal foo, got %s.", noExistVar) + } +} + +func TestEnvMustGet(t *testing.T) { + gopath, err := MustGet("GOPATH") + if err != nil { + t.Error(err) + } + + if gopath != os.Getenv("GOPATH") { + t.Errorf("expected GOPATH to be the same, got %s.", gopath) + } + + _, err = MustGet("NOEXISTVAR") + if err == nil { + t.Error("expected error to be non-nil") + } +} + +func TestEnvSet(t *testing.T) { + Set("MYVAR", "foo") + myVar := Get("MYVAR", "bar") + if myVar != "foo" { + t.Errorf("expected MYVAR to equal foo, got %s.", myVar) + } +} + +func TestEnvMustSet(t *testing.T) { + err := MustSet("FOO", "bar") + if err != nil { + t.Error(err) + } + + fooVar := os.Getenv("FOO") + if fooVar != "bar" { + t.Errorf("expected FOO variable to equal bar, got %s.", fooVar) + } +} + +func TestEnvGetAll(t *testing.T) { + envMap := GetAll() + if len(envMap) == 0 { + t.Error("expected environment not empty.") + } +} diff --git a/src/vendor/github.com/astaxie/beego/config/fake.go b/src/vendor/github.com/astaxie/beego/config/fake.go index 7e3626087..f5144598e 100644 --- a/src/vendor/github.com/astaxie/beego/config/fake.go +++ b/src/vendor/github.com/astaxie/beego/config/fake.go @@ -38,7 +38,7 @@ func (c *fakeConfigContainer) String(key string) string { } func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string { - v := c.getData(key) + v := c.String(key) if v == "" { return defaultval } @@ -46,7 +46,7 @@ func (c *fakeConfigContainer) DefaultString(key string, defaultval string) strin } func (c *fakeConfigContainer) Strings(key string) []string { - v := c.getData(key) + v := c.String(key) if v == "" { return nil } diff --git a/src/vendor/github.com/astaxie/beego/config/ini.go b/src/vendor/github.com/astaxie/beego/config/ini.go index 9c19b9b1b..a681bc1b1 100644 --- a/src/vendor/github.com/astaxie/beego/config/ini.go +++ b/src/vendor/github.com/astaxie/beego/config/ini.go @@ -18,15 +18,14 @@ import ( "bufio" "bytes" "errors" - "fmt" "io" "io/ioutil" "os" - "path" + "os/user" + "path/filepath" "strconv" "strings" "sync" - "time" ) var ( @@ -51,24 +50,26 @@ func (ini *IniConfig) Parse(name string) (Configer, error) { } func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { - file, err := os.Open(name) + data, err := ioutil.ReadFile(name) if err != nil { return nil, err } + return ini.parseData(filepath.Dir(name), data) +} + +func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, error) { cfg := &IniConfigContainer{ - file.Name(), - make(map[string]map[string]string), - make(map[string]string), - make(map[string]string), - sync.RWMutex{}, + data: make(map[string]map[string]string), + sectionComment: make(map[string]string), + keyComment: make(map[string]string), + RWMutex: sync.RWMutex{}, } cfg.Lock() defer cfg.Unlock() - defer file.Close() var comment bytes.Buffer - buf := bufio.NewReader(file) + buf := bufio.NewReader(bytes.NewBuffer(data)) // check the BOM head, err := buf.Peek(3) if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 { @@ -82,11 +83,14 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { if err == io.EOF { break } + //It might be a good idea to throw a error on all unknonw errors? + if _, ok := err.(*os.PathError); ok { + return nil, err + } + line = bytes.TrimSpace(line) if bytes.Equal(line, bEmpty) { continue } - line = bytes.TrimSpace(line) - var bComment []byte switch { case bytes.HasPrefix(line, bNumComment): @@ -126,16 +130,20 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { // handle include "other.conf" if len(keyValue) == 1 && strings.HasPrefix(key, "include") { + includefiles := strings.Fields(key) if includefiles[0] == "include" && len(includefiles) == 2 { + otherfile := strings.Trim(includefiles[1], "\"") - if !path.IsAbs(otherfile) { - otherfile = path.Join(path.Dir(name), otherfile) + if !filepath.IsAbs(otherfile) { + otherfile = filepath.Join(dir, otherfile) } + i, err := ini.parseFile(otherfile) if err != nil { return nil, err } + for sec, dt := range i.data { if _, ok := cfg.data[sec]; !ok { cfg.data[sec] = make(map[string]string) @@ -144,12 +152,15 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { cfg.data[sec][k] = v } } + for sec, comm := range i.sectionComment { cfg.sectionComment[sec] = comm } + for k, comm := range i.keyComment { cfg.keyComment[k] = comm } + continue } } @@ -162,7 +173,7 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { val = bytes.Trim(val, `"`) } - cfg.data[section][key] = string(val) + cfg.data[section][key] = ExpandValueEnv(string(val)) if comment.Len() > 0 { cfg.keyComment[section+"."+key] = comment.String() comment.Reset() @@ -173,20 +184,25 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { } // ParseData parse ini the data +// When include other.conf,other.conf is either absolute directory +// or under beego in default temporary directory(/tmp/beego[-username]). func (ini *IniConfig) ParseData(data []byte) (Configer, error) { - // Save memory data to temporary file - tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond())) - os.MkdirAll(path.Dir(tmpName), os.ModePerm) - if err := ioutil.WriteFile(tmpName, data, 0655); err != nil { + dir := "beego" + currentUser, err := user.Current() + if err == nil { + dir = "beego-" + currentUser.Username + } + dir = filepath.Join(os.TempDir(), dir) + if err = os.MkdirAll(dir, os.ModePerm); err != nil { return nil, err } - return ini.Parse(tmpName) + + return ini.parseData(dir, data) } // IniConfigContainer A Config represents the ini configuration. // When set and get value, support key as section:name type. type IniConfigContainer struct { - filename string data map[string]map[string]string // section=> key:val sectionComment map[string]string // section : comment keyComment map[string]string // id: []{comment, key...}; id 1 is for main comment. @@ -293,10 +309,12 @@ func (c *IniConfigContainer) GetSection(section string) (map[string]string, erro if v, ok := c.data[section]; ok { return v, nil } - return nil, errors.New("not exist setction") + return nil, errors.New("not exist section") } -// SaveConfigFile save the config into file +// SaveConfigFile save the config into file. +// +// BUG(env): The environment variable config item will be saved with real value in SaveConfigFile Funcation. func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { // Write configuration file by filename. f, err := os.Create(filename) @@ -307,7 +325,10 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { // Get section or key comments. Fixed #1607 getCommentStr := func(section, key string) string { - comment, ok := "", false + var ( + comment string + ok bool + ) if len(key) == 0 { comment, ok = c.sectionComment[section] } else { @@ -387,11 +408,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { } } } - - if _, err = buf.WriteTo(f); err != nil { - return err - } - return nil + _, err = buf.WriteTo(f) + return err } // Set writes a new value for key. @@ -406,7 +424,7 @@ func (c *IniConfigContainer) Set(key, value string) error { var ( section, k string - sectionKey = strings.Split(key, "::") + sectionKey = strings.Split(strings.ToLower(key), "::") ) if len(sectionKey) >= 2 { diff --git a/src/vendor/github.com/astaxie/beego/config/ini_test.go b/src/vendor/github.com/astaxie/beego/config/ini_test.go index 93fce61ff..ffcdb294a 100644 --- a/src/vendor/github.com/astaxie/beego/config/ini_test.go +++ b/src/vendor/github.com/astaxie/beego/config/ini_test.go @@ -42,11 +42,14 @@ needlogin = ON enableSession = Y enableCookie = N flag = 1 +path1 = ${GOPATH} +path2 = ${GOPATH||/home/go} [demo] key1="asta" key2 = "xie" CaseInsensitive = true peers = one;two;three +password = ${GOPATH} ` keyValue = map[string]interface{}{ @@ -64,10 +67,13 @@ peers = one;two;three "enableSession": true, "enableCookie": false, "flag": true, + "path1": os.Getenv("GOPATH"), + "path2": os.Getenv("GOPATH"), "demo::key1": "asta", "demo::key2": "xie", "demo::CaseInsensitive": true, "demo::peers": []string{"one", "two", "three"}, + "demo::password": os.Getenv("GOPATH"), "null": "", "demo2::key1": "", "error": "", @@ -175,7 +181,7 @@ name=mysql cfgData := string(data) datas := strings.Split(saveResult, "\n") for _, line := range datas { - if strings.Contains(cfgData, line+"\n") == false { + if !strings.Contains(cfgData, line+"\n") { t.Fatalf("different after save ini config file. need contains %q", line) } } diff --git a/src/vendor/github.com/astaxie/beego/config/json.go b/src/vendor/github.com/astaxie/beego/config/json.go index fce517eb8..a0d932105 100644 --- a/src/vendor/github.com/astaxie/beego/config/json.go +++ b/src/vendor/github.com/astaxie/beego/config/json.go @@ -57,6 +57,9 @@ func (js *JSONConfig) ParseData(data []byte) (Configer, error) { } x.data["rootArray"] = wrappingArray } + + x.data = ExpandValueEnvForMap(x.data) + return x, nil } diff --git a/src/vendor/github.com/astaxie/beego/config/json_test.go b/src/vendor/github.com/astaxie/beego/config/json_test.go index df663461b..24ff96446 100644 --- a/src/vendor/github.com/astaxie/beego/config/json_test.go +++ b/src/vendor/github.com/astaxie/beego/config/json_test.go @@ -86,16 +86,19 @@ func TestJson(t *testing.T) { "enableSession": "Y", "enableCookie": "N", "flag": 1, +"path1": "${GOPATH}", +"path2": "${GOPATH||/home/go}", "database": { "host": "host", "port": "port", "database": "database", "username": "username", - "password": "password", + "password": "${GOPATH}", "conns":{ "maxconnection":12, "autoconnect":true, - "connectioninfo":"info" + "connectioninfo":"info", + "root": "${GOPATH}" } } }` @@ -115,13 +118,16 @@ func TestJson(t *testing.T) { "enableSession": true, "enableCookie": false, "flag": true, + "path1": os.Getenv("GOPATH"), + "path2": os.Getenv("GOPATH"), "database::host": "host", "database::port": "port", "database::database": "database", - "database::password": "password", + "database::password": os.Getenv("GOPATH"), "database::conns::maxconnection": 12, "database::conns::autoconnect": true, "database::conns::connectioninfo": "info", + "database::conns::root": os.Getenv("GOPATH"), "unknown": "", } ) diff --git a/src/vendor/github.com/astaxie/beego/config/xml/xml.go b/src/vendor/github.com/astaxie/beego/config/xml/xml.go index b5291bf4a..b82bf4039 100644 --- a/src/vendor/github.com/astaxie/beego/config/xml/xml.go +++ b/src/vendor/github.com/astaxie/beego/config/xml/xml.go @@ -12,21 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package xml for config provider +// Package xml for config provider. // -// depend on github.com/beego/x2j +// depend on github.com/beego/x2j. // -// go install github.com/beego/x2j +// go install github.com/beego/x2j. // // Usage: -// import( -// _ "github.com/astaxie/beego/config/xml" -// "github.com/astaxie/beego/config" -// ) +// import( +// _ "github.com/astaxie/beego/config/xml" +// "github.com/astaxie/beego/config" +// ) // // cnf, err := config.NewConfig("xml", "config.xml") // -// more docs http://beego.me/docs/module/config.md +//More docs http://beego.me/docs/module/config.md package xml import ( @@ -35,11 +35,9 @@ import ( "fmt" "io/ioutil" "os" - "path" "strconv" "strings" "sync" - "time" "github.com/astaxie/beego/config" "github.com/beego/x2j" @@ -52,36 +50,26 @@ type Config struct{} // Parse returns a ConfigContainer with parsed xml config map. func (xc *Config) Parse(filename string) (config.Configer, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - - x := &ConfigContainer{data: make(map[string]interface{})} - content, err := ioutil.ReadAll(file) + context, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - d, err := x2j.DocToMap(string(content)) - if err != nil { - return nil, err - } - - x.data = d["config"].(map[string]interface{}) - return x, nil + return xc.ParseData(context) } // ParseData xml data func (xc *Config) ParseData(data []byte) (config.Configer, error) { - // Save memory data to temporary file - tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond())) - os.MkdirAll(path.Dir(tmpName), os.ModePerm) - if err := ioutil.WriteFile(tmpName, data, 0655); err != nil { + x := &ConfigContainer{data: make(map[string]interface{})} + + d, err := x2j.DocToMap(string(data)) + if err != nil { return nil, err } - return xc.Parse(tmpName) + + x.data = config.ExpandValueEnvForMap(d["config"].(map[string]interface{})) + + return x, nil } // ConfigContainer A Config represents the xml configuration. @@ -92,7 +80,7 @@ type ConfigContainer struct { // Bool returns the boolean value for a given key. func (c *ConfigContainer) Bool(key string) (bool, error) { - if v, ok := c.data[key]; ok { + if v := c.data[key]; v != nil { return config.ParseBool(v) } return false, fmt.Errorf("not exist key: %q", key) @@ -193,10 +181,14 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri // GetSection returns map for the given section func (c *ConfigContainer) GetSection(section string) (map[string]string, error) { - if v, ok := c.data[section]; ok { - return v.(map[string]string), nil + if v, ok := c.data[section].(map[string]interface{}); ok { + mapstr := make(map[string]string) + for k, val := range v { + mapstr[k] = config.ToString(val) + } + return mapstr, nil } - return nil, errors.New("not exist setction") + return nil, fmt.Errorf("section '%s' not found", section) } // SaveConfigFile save the config into file diff --git a/src/vendor/github.com/astaxie/beego/config/xml/xml_test.go b/src/vendor/github.com/astaxie/beego/config/xml/xml_test.go index 60dcba54a..346c866ee 100644 --- a/src/vendor/github.com/astaxie/beego/config/xml/xml_test.go +++ b/src/vendor/github.com/astaxie/beego/config/xml/xml_test.go @@ -15,14 +15,18 @@ package xml import ( + "fmt" "os" "testing" "github.com/astaxie/beego/config" ) -//xml parse should incluce in tags -var xmlcontext = ` +func TestXML(t *testing.T) { + + var ( + //xml parse should incluce in tags + xmlcontext = ` beeapi 8080 @@ -31,10 +35,29 @@ var xmlcontext = ` dev false true +${GOPATH} +${GOPATH||/home/go} + +1 +MySection + ` + keyValue = map[string]interface{}{ + "appname": "beeapi", + "httpport": 8080, + "mysqlport": int64(3600), + "PI": 3.1415976, + "runmode": "dev", + "autorender": false, + "copyrequestbody": true, + "path1": os.Getenv("GOPATH"), + "path2": os.Getenv("GOPATH"), + "error": "", + "emptystrings": []string{}, + } + ) -func TestXML(t *testing.T) { f, err := os.Create("testxml.conf") if err != nil { t.Fatal(err) @@ -46,43 +69,57 @@ func TestXML(t *testing.T) { } f.Close() defer os.Remove("testxml.conf") + xmlconf, err := config.NewConfig("xml", "testxml.conf") if err != nil { t.Fatal(err) } - if xmlconf.String("appname") != "beeapi" { - t.Fatal("appname not equal to beeapi") - } - if port, err := xmlconf.Int("httpport"); err != nil || port != 8080 { - t.Error(port) + + var xmlsection map[string]string + xmlsection, err = xmlconf.GetSection("mysection") + if err != nil { t.Fatal(err) } - if port, err := xmlconf.Int64("mysqlport"); err != nil || port != 3600 { - t.Error(port) - t.Fatal(err) + + if len(xmlsection) == 0 { + t.Error("section should not be empty") } - if pi, err := xmlconf.Float("PI"); err != nil || pi != 3.1415976 { - t.Error(pi) - t.Fatal(err) - } - if xmlconf.String("runmode") != "dev" { - t.Fatal("runmode not equal to dev") - } - if v, err := xmlconf.Bool("autorender"); err != nil || v != false { - t.Error(v) - t.Fatal(err) - } - if v, err := xmlconf.Bool("copyrequestbody"); err != nil || v != true { - t.Error(v) - t.Fatal(err) + + for k, v := range keyValue { + + var ( + value interface{} + err error + ) + + switch v.(type) { + case int: + value, err = xmlconf.Int(k) + case int64: + value, err = xmlconf.Int64(k) + case float64: + value, err = xmlconf.Float(k) + case bool: + value, err = xmlconf.Bool(k) + case []string: + value = xmlconf.Strings(k) + case string: + value = xmlconf.String(k) + default: + value, err = xmlconf.DIY(k) + } + if err != nil { + t.Errorf("get key %q value fatal,%v err %s", k, v, err) + } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) { + t.Errorf("get key %q value, want %v got %v .", k, v, value) + } + } + if err = xmlconf.Set("name", "astaxie"); err != nil { t.Fatal(err) } if xmlconf.String("name") != "astaxie" { t.Fatal("get name error") } - if xmlconf.Strings("emptystrings") != nil { - t.Fatal("get emtpy strings error") - } } diff --git a/src/vendor/github.com/astaxie/beego/config/yaml/yaml.go b/src/vendor/github.com/astaxie/beego/config/yaml/yaml.go index 7e1d0426e..51fe44d36 100644 --- a/src/vendor/github.com/astaxie/beego/config/yaml/yaml.go +++ b/src/vendor/github.com/astaxie/beego/config/yaml/yaml.go @@ -19,14 +19,14 @@ // go install github.com/beego/goyaml2 // // Usage: -// import( +// import( // _ "github.com/astaxie/beego/config/yaml" -// "github.com/astaxie/beego/config" -// ) +// "github.com/astaxie/beego/config" +// ) // // cnf, err := config.NewConfig("yaml", "config.yaml") // -// more docs http://beego.me/docs/module/config.md +//More docs http://beego.me/docs/module/config.md package yaml import ( @@ -37,10 +37,8 @@ import ( "io/ioutil" "log" "os" - "path" "strings" "sync" - "time" "github.com/astaxie/beego/config" "github.com/beego/goyaml2" @@ -63,26 +61,30 @@ func (yaml *Config) Parse(filename string) (y config.Configer, err error) { // ParseData parse yaml data func (yaml *Config) ParseData(data []byte) (config.Configer, error) { - // Save memory data to temporary file - tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond())) - os.MkdirAll(path.Dir(tmpName), os.ModePerm) - if err := ioutil.WriteFile(tmpName, data, 0655); err != nil { + cnf, err := parseYML(data) + if err != nil { return nil, err } - return yaml.Parse(tmpName) + + return &ConfigContainer{ + data: cnf, + }, nil } // ReadYmlReader Read yaml file to map. // if json like, use json package, unless goyaml2 package. func ReadYmlReader(path string) (cnf map[string]interface{}, err error) { - f, err := os.Open(path) + buf, err := ioutil.ReadFile(path) if err != nil { return } - defer f.Close() - buf, err := ioutil.ReadAll(f) - if err != nil || len(buf) < 3 { + return parseYML(buf) +} + +// parseYML parse yaml formatted []byte to map. +func parseYML(buf []byte) (cnf map[string]interface{}, err error) { + if len(buf) < 3 { return } @@ -110,6 +112,7 @@ func ReadYmlReader(path string) (cnf map[string]interface{}, err error) { log.Println("Not a Map? >> ", string(buf), data) cnf = nil } + cnf = config.ExpandValueEnvForMap(cnf) return } @@ -121,10 +124,11 @@ type ConfigContainer struct { // Bool returns the boolean value for a given key. func (c *ConfigContainer) Bool(key string) (bool, error) { - if v, ok := c.data[key]; ok { - return config.ParseBool(v) + v, err := c.getData(key) + if err != nil { + return false, err } - return false, fmt.Errorf("not exist key: %q", key) + return config.ParseBool(v) } // DefaultBool return the bool value if has no error @@ -139,8 +143,12 @@ func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool { // Int returns the integer value for a given key. func (c *ConfigContainer) Int(key string) (int, error) { - if v, ok := c.data[key].(int64); ok { - return int(v), nil + if v, err := c.getData(key); err != nil { + return 0, err + } else if vv, ok := v.(int); ok { + return vv, nil + } else if vv, ok := v.(int64); ok { + return int(vv), nil } return 0, errors.New("not int value") } @@ -157,8 +165,10 @@ func (c *ConfigContainer) DefaultInt(key string, defaultval int) int { // Int64 returns the int64 value for a given key. func (c *ConfigContainer) Int64(key string) (int64, error) { - if v, ok := c.data[key].(int64); ok { - return v, nil + if v, err := c.getData(key); err != nil { + return 0, err + } else if vv, ok := v.(int64); ok { + return vv, nil } return 0, errors.New("not bool value") } @@ -175,8 +185,14 @@ func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 { // Float returns the float value for a given key. func (c *ConfigContainer) Float(key string) (float64, error) { - if v, ok := c.data[key].(float64); ok { - return v, nil + if v, err := c.getData(key); err != nil { + return 0.0, err + } else if vv, ok := v.(float64); ok { + return vv, nil + } else if vv, ok := v.(int); ok { + return float64(vv), nil + } else if vv, ok := v.(int64); ok { + return float64(vv), nil } return 0.0, errors.New("not float64 value") } @@ -193,8 +209,10 @@ func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 { // String returns the string value for a given key. func (c *ConfigContainer) String(key string) string { - if v, ok := c.data[key].(string); ok { - return v + if v, err := c.getData(key); err == nil { + if vv, ok := v.(string); ok { + return vv + } } return "" } @@ -230,11 +248,11 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri // GetSection returns map for the given section func (c *ConfigContainer) GetSection(section string) (map[string]string, error) { - v, ok := c.data[section] - if ok { + + if v, ok := c.data[section]; ok { return v.(map[string]string), nil } - return nil, errors.New("not exist setction") + return nil, errors.New("not exist section") } // SaveConfigFile save the config into file @@ -259,10 +277,19 @@ func (c *ConfigContainer) Set(key, val string) error { // DIY returns the raw value by a given key. func (c *ConfigContainer) DIY(key string) (v interface{}, err error) { + return c.getData(key) +} + +func (c *ConfigContainer) getData(key string) (interface{}, error) { + + if len(key) == 0 { + return nil, errors.New("key is empty") + } + if v, ok := c.data[key]; ok { return v, nil } - return nil, errors.New("not exist key") + return nil, fmt.Errorf("not exist key %q", key) } func init() { diff --git a/src/vendor/github.com/astaxie/beego/config/yaml/yaml_test.go b/src/vendor/github.com/astaxie/beego/config/yaml/yaml_test.go index 80cbb8fe4..49cc1d1e7 100644 --- a/src/vendor/github.com/astaxie/beego/config/yaml/yaml_test.go +++ b/src/vendor/github.com/astaxie/beego/config/yaml/yaml_test.go @@ -15,13 +15,17 @@ package yaml import ( + "fmt" "os" "testing" "github.com/astaxie/beego/config" ) -var yamlcontext = ` +func TestYaml(t *testing.T) { + + var ( + yamlcontext = ` "appname": beeapi "httpport": 8080 "mysqlport": 3600 @@ -29,9 +33,27 @@ var yamlcontext = ` "runmode": dev "autorender": false "copyrequestbody": true +"PATH": GOPATH +"path1": ${GOPATH} +"path2": ${GOPATH||/home/go} +"empty": "" ` -func TestYaml(t *testing.T) { + keyValue = map[string]interface{}{ + "appname": "beeapi", + "httpport": 8080, + "mysqlport": int64(3600), + "PI": 3.1415976, + "runmode": "dev", + "autorender": false, + "copyrequestbody": true, + "PATH": "GOPATH", + "path1": os.Getenv("GOPATH"), + "path2": os.Getenv("GOPATH"), + "error": "", + "emptystrings": []string{}, + } + ) f, err := os.Create("testyaml.conf") if err != nil { t.Fatal(err) @@ -47,32 +69,42 @@ func TestYaml(t *testing.T) { if err != nil { t.Fatal(err) } + if yamlconf.String("appname") != "beeapi" { t.Fatal("appname not equal to beeapi") } - if port, err := yamlconf.Int("httpport"); err != nil || port != 8080 { - t.Error(port) - t.Fatal(err) - } - if port, err := yamlconf.Int64("mysqlport"); err != nil || port != 3600 { - t.Error(port) - t.Fatal(err) - } - if pi, err := yamlconf.Float("PI"); err != nil || pi != 3.1415976 { - t.Error(pi) - t.Fatal(err) - } - if yamlconf.String("runmode") != "dev" { - t.Fatal("runmode not equal to dev") - } - if v, err := yamlconf.Bool("autorender"); err != nil || v != false { - t.Error(v) - t.Fatal(err) - } - if v, err := yamlconf.Bool("copyrequestbody"); err != nil || v != true { - t.Error(v) - t.Fatal(err) + + for k, v := range keyValue { + + var ( + value interface{} + err error + ) + + switch v.(type) { + case int: + value, err = yamlconf.Int(k) + case int64: + value, err = yamlconf.Int64(k) + case float64: + value, err = yamlconf.Float(k) + case bool: + value, err = yamlconf.Bool(k) + case []string: + value = yamlconf.Strings(k) + case string: + value = yamlconf.String(k) + default: + value, err = yamlconf.DIY(k) + } + if err != nil { + t.Errorf("get key %q value fatal,%v err %s", k, v, err) + } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) { + t.Errorf("get key %q value, want %v got %v .", k, v, value) + } + } + if err = yamlconf.Set("name", "astaxie"); err != nil { t.Fatal(err) } @@ -80,7 +112,4 @@ func TestYaml(t *testing.T) { t.Fatal("get name error") } - if yamlconf.Strings("emptystrings") != nil { - t.Fatal("get emtpy strings error") - } } diff --git a/src/vendor/github.com/astaxie/beego/config_test.go b/src/vendor/github.com/astaxie/beego/config_test.go index cf4a781d1..c1973f7bc 100644 --- a/src/vendor/github.com/astaxie/beego/config_test.go +++ b/src/vendor/github.com/astaxie/beego/config_test.go @@ -15,7 +15,11 @@ package beego import ( + "encoding/json" + "reflect" "testing" + + "github.com/astaxie/beego/config" ) func TestDefaults(t *testing.T) { @@ -27,3 +31,108 @@ func TestDefaults(t *testing.T) { t.Errorf("FlashName was not set to default.") } } + +func TestAssignConfig_01(t *testing.T) { + _BConfig := &Config{} + _BConfig.AppName = "beego_test" + jcf := &config.JSONConfig{} + ac, _ := jcf.ParseData([]byte(`{"AppName":"beego_json"}`)) + assignSingleConfig(_BConfig, ac) + if _BConfig.AppName != "beego_json" { + t.Log(_BConfig) + t.FailNow() + } +} + +func TestAssignConfig_02(t *testing.T) { + _BConfig := &Config{} + bs, _ := json.Marshal(newBConfig()) + + jsonMap := map[string]interface{}{} + json.Unmarshal(bs, &jsonMap) + + configMap := map[string]interface{}{} + for k, v := range jsonMap { + if reflect.TypeOf(v).Kind() == reflect.Map { + for k1, v1 := range v.(map[string]interface{}) { + if reflect.TypeOf(v1).Kind() == reflect.Map { + for k2, v2 := range v1.(map[string]interface{}) { + configMap[k2] = v2 + } + } else { + configMap[k1] = v1 + } + } + } else { + configMap[k] = v + } + } + configMap["MaxMemory"] = 1024 + configMap["Graceful"] = true + configMap["XSRFExpire"] = 32 + configMap["SessionProviderConfig"] = "file" + configMap["FileLineNum"] = true + + jcf := &config.JSONConfig{} + bs, _ = json.Marshal(configMap) + ac, _ := jcf.ParseData([]byte(bs)) + + for _, i := range []interface{}{_BConfig, &_BConfig.Listen, &_BConfig.WebConfig, &_BConfig.Log, &_BConfig.WebConfig.Session} { + assignSingleConfig(i, ac) + } + + if _BConfig.MaxMemory != 1024 { + t.Log(_BConfig.MaxMemory) + t.FailNow() + } + + if !_BConfig.Listen.Graceful { + t.Log(_BConfig.Listen.Graceful) + t.FailNow() + } + + if _BConfig.WebConfig.XSRFExpire != 32 { + t.Log(_BConfig.WebConfig.XSRFExpire) + t.FailNow() + } + + if _BConfig.WebConfig.Session.SessionProviderConfig != "file" { + t.Log(_BConfig.WebConfig.Session.SessionProviderConfig) + t.FailNow() + } + + if !_BConfig.Log.FileLineNum { + t.Log(_BConfig.Log.FileLineNum) + t.FailNow() + } + +} + +func TestAssignConfig_03(t *testing.T) { + jcf := &config.JSONConfig{} + ac, _ := jcf.ParseData([]byte(`{"AppName":"beego"}`)) + ac.Set("AppName", "test_app") + ac.Set("RunMode", "online") + ac.Set("StaticDir", "download:down download2:down2") + ac.Set("StaticExtensionsToGzip", ".css,.js,.html,.jpg,.png") + assignConfig(ac) + + t.Logf("%#v", BConfig) + + if BConfig.AppName != "test_app" { + t.FailNow() + } + + if BConfig.RunMode != "online" { + t.FailNow() + } + if BConfig.WebConfig.StaticDir["/download"] != "down" { + t.FailNow() + } + if BConfig.WebConfig.StaticDir["/download2"] != "down2" { + t.FailNow() + } + if len(BConfig.WebConfig.StaticExtensionsToGzip) != 5 { + t.FailNow() + } +} diff --git a/src/vendor/github.com/astaxie/beego/context/acceptencoder.go b/src/vendor/github.com/astaxie/beego/context/acceptencoder.go index 033d9ca8c..b4e2492c0 100644 --- a/src/vendor/github.com/astaxie/beego/context/acceptencoder.go +++ b/src/vendor/github.com/astaxie/beego/context/acceptencoder.go @@ -27,6 +27,34 @@ import ( "sync" ) +var ( + //Default size==20B same as nginx + defaultGzipMinLength = 20 + //Content will only be compressed if content length is either unknown or greater than gzipMinLength. + gzipMinLength = defaultGzipMinLength + //The compression level used for deflate compression. (0-9). + gzipCompressLevel int + //List of HTTP methods to compress. If not set, only GET requests are compressed. + includedMethods map[string]bool + getMethodOnly bool +) + +// InitGzip init the gzipcompress +func InitGzip(minLength, compressLevel int, methods []string) { + if minLength >= 0 { + gzipMinLength = minLength + } + gzipCompressLevel = compressLevel + if gzipCompressLevel < flate.NoCompression || gzipCompressLevel > flate.BestCompression { + gzipCompressLevel = flate.BestSpeed + } + getMethodOnly = (len(methods) == 0) || (len(methods) == 1 && strings.ToUpper(methods[0]) == "GET") + includedMethods = make(map[string]bool, len(methods)) + for _, v := range methods { + includedMethods[strings.ToUpper(v)] = true + } +} + type resetWriter interface { io.Writer Reset(w io.Writer) @@ -41,20 +69,20 @@ func (n nopResetWriter) Reset(w io.Writer) { } type acceptEncoder struct { - name string - levelEncode func(int) resetWriter - bestSpeedPool *sync.Pool - bestCompressionPool *sync.Pool + name string + levelEncode func(int) resetWriter + customCompressLevelPool *sync.Pool + bestCompressionPool *sync.Pool } func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter { - if ac.bestSpeedPool == nil || ac.bestCompressionPool == nil { + if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil { return nopResetWriter{wr} } var rwr resetWriter switch level { case flate.BestSpeed: - rwr = ac.bestSpeedPool.Get().(resetWriter) + rwr = ac.customCompressLevelPool.Get().(resetWriter) case flate.BestCompression: rwr = ac.bestCompressionPool.Get().(resetWriter) default: @@ -65,13 +93,18 @@ func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter { } func (ac acceptEncoder) put(wr resetWriter, level int) { - if ac.bestSpeedPool == nil || ac.bestCompressionPool == nil { + if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil { return } wr.Reset(nil) + + //notice + //compressionLevel==BestCompression DOES NOT MATTER + //sync.Pool will not memory leak + switch level { - case flate.BestSpeed: - ac.bestSpeedPool.Put(wr) + case gzipCompressLevel: + ac.customCompressLevelPool.Put(wr) case flate.BestCompression: ac.bestCompressionPool.Put(wr) } @@ -79,28 +112,22 @@ func (ac acceptEncoder) put(wr resetWriter, level int) { var ( noneCompressEncoder = acceptEncoder{"", nil, nil, nil} - gzipCompressEncoder = acceptEncoder{"gzip", - func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr }, - &sync.Pool{ - New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestSpeed); return wr }, - }, - &sync.Pool{ - New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr }, - }, + gzipCompressEncoder = acceptEncoder{ + name: "gzip", + levelEncode: func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr }, + customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, gzipCompressLevel); return wr }}, + bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr }}, } //according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed //deflate //The "zlib" format defined in RFC 1950 [31] in combination with //the "deflate" compression mechanism described in RFC 1951 [29]. - deflateCompressEncoder = acceptEncoder{"deflate", - func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr }, - &sync.Pool{ - New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestSpeed); return wr }, - }, - &sync.Pool{ - New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestCompression); return wr }, - }, + deflateCompressEncoder = acceptEncoder{ + name: "deflate", + levelEncode: func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr }, + customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, gzipCompressLevel); return wr }}, + bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestCompression); return wr }}, } ) @@ -120,7 +147,11 @@ func WriteFile(encoding string, writer io.Writer, file *os.File) (bool, string, // WriteBody reads writes content to writer by the specific encoding(gzip/deflate) func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, error) { - return writeLevel(encoding, writer, bytes.NewReader(content), flate.BestSpeed) + if encoding == "" || len(content) < gzipMinLength { + _, err := writer.Write(content) + return false, "", err + } + return writeLevel(encoding, writer, bytes.NewReader(content), gzipCompressLevel) } // writeLevel reads from reader,writes to writer by specific encoding and compress level @@ -156,7 +187,10 @@ func ParseEncoding(r *http.Request) string { if r == nil { return "" } - return parseEncoding(r) + if (getMethodOnly && r.Method == "GET") || includedMethods[r.Method] { + return parseEncoding(r) + } + return "" } type q struct { @@ -176,9 +210,13 @@ func parseEncoding(r *http.Request) string { continue } vs := strings.Split(v, ";") + var cf acceptEncoder + var ok bool + if cf, ok = encoderMap[vs[0]]; !ok { + continue + } if len(vs) == 1 { - lastQ = q{vs[0], 1} - break + return cf.name } if len(vs) == 2 { f, _ := strconv.ParseFloat(strings.Replace(vs[1], "q=", "", -1), 64) @@ -186,12 +224,9 @@ func parseEncoding(r *http.Request) string { continue } if f > lastQ.value { - lastQ = q{vs[0], f} + lastQ = q{cf.name, f} } } } - if cf, ok := encoderMap[lastQ.name]; ok { - return cf.name - } - return "" + return lastQ.name } diff --git a/src/vendor/github.com/astaxie/beego/context/acceptencoder_test.go b/src/vendor/github.com/astaxie/beego/context/acceptencoder_test.go index 3afff6799..e3d61e279 100644 --- a/src/vendor/github.com/astaxie/beego/context/acceptencoder_test.go +++ b/src/vendor/github.com/astaxie/beego/context/acceptencoder_test.go @@ -41,4 +41,19 @@ func Test_ExtractEncoding(t *testing.T) { if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"*"}}}) != "gzip" { t.Fail() } + if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"x,gzip,deflate"}}}) != "gzip" { + t.Fail() + } + if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip,x,deflate"}}}) != "gzip" { + t.Fail() + } + if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0.5,x,deflate"}}}) != "deflate" { + t.Fail() + } + if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"x"}}}) != "" { + t.Fail() + } + if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0.5,x;q=0.8"}}}) != "gzip" { + t.Fail() + } } diff --git a/src/vendor/github.com/astaxie/beego/context/context.go b/src/vendor/github.com/astaxie/beego/context/context.go index 63a1313d2..8b32062c2 100644 --- a/src/vendor/github.com/astaxie/beego/context/context.go +++ b/src/vendor/github.com/astaxie/beego/context/context.go @@ -24,13 +24,11 @@ package context import ( "bufio" - "bytes" "crypto/hmac" "crypto/sha1" "encoding/base64" "errors" "fmt" - "io" "net" "net/http" "strconv" @@ -67,18 +65,18 @@ func (ctx *Context) Reset(rw http.ResponseWriter, r *http.Request) { ctx.ResponseWriter.reset(rw) ctx.Input.Reset(ctx) ctx.Output.Reset(ctx) + ctx._xsrfToken = "" } // Redirect does redirection to localurl with http header status code. -// It sends http response header directly. func (ctx *Context) Redirect(status int, localurl string) { - ctx.Output.Header("Location", localurl) - ctx.ResponseWriter.WriteHeader(status) + http.Redirect(ctx.ResponseWriter, ctx.Request, localurl, status) } // Abort stops this request. // if beego.ErrorMaps exists, panic body. func (ctx *Context) Abort(status int, body string) { + ctx.Output.SetStatus(status) panic(body) } @@ -173,6 +171,22 @@ func (ctx *Context) CheckXSRFCookie() bool { return true } +// RenderMethodResult renders the return value of a controller method to the output +func (ctx *Context) RenderMethodResult(result interface{}) { + if result != nil { + renderer, ok := result.(Renderer) + if !ok { + err, ok := result.(error) + if ok { + renderer = errorRenderer(err) + } else { + renderer = jsonRenderer(result) + } + } + renderer.Render(ctx) + } +} + //Response is a wrapper for the http.ResponseWriter //started set to true if response was written to then don't execute other handler type Response struct { @@ -195,14 +209,6 @@ func (r *Response) Write(p []byte) (int, error) { return r.ResponseWriter.Write(p) } -// Copy writes the data to the connection as part of an HTTP reply, -// and sets `started` to true. -// started means the response has sent out. -func (r *Response) Copy(buf *bytes.Buffer) (int64, error) { - r.Started = true - return io.Copy(r.ResponseWriter, buf) -} - // WriteHeader sends an HTTP response header with status code, // and sets `started` to true. func (r *Response) WriteHeader(code int) { diff --git a/src/vendor/github.com/astaxie/beego/context/context_test.go b/src/vendor/github.com/astaxie/beego/context/context_test.go new file mode 100644 index 000000000..7c0535e0a --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/context/context_test.go @@ -0,0 +1,47 @@ +// Copyright 2016 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package context + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestXsrfReset_01(t *testing.T) { + r := &http.Request{} + c := NewContext() + c.Request = r + c.ResponseWriter = &Response{} + c.ResponseWriter.reset(httptest.NewRecorder()) + c.Output.Reset(c) + c.Input.Reset(c) + c.XSRFToken("key", 16) + if c._xsrfToken == "" { + t.FailNow() + } + token := c._xsrfToken + c.Reset(&Response{ResponseWriter: httptest.NewRecorder()}, r) + if c._xsrfToken != "" { + t.FailNow() + } + c.XSRFToken("key", 16) + if c._xsrfToken == "" { + t.FailNow() + } + if token == c._xsrfToken { + t.FailNow() + } +} diff --git a/src/vendor/github.com/astaxie/beego/context/input.go b/src/vendor/github.com/astaxie/beego/context/input.go index edfdf5302..2c53c6014 100644 --- a/src/vendor/github.com/astaxie/beego/context/input.go +++ b/src/vendor/github.com/astaxie/beego/context/input.go @@ -16,9 +16,11 @@ package context import ( "bytes" + "compress/gzip" "errors" "io" "io/ioutil" + "net/http" "net/url" "reflect" "regexp" @@ -40,12 +42,14 @@ var ( // BeegoInput operates the http request header, data, cookie and body. // it also contains router params and current session. type BeegoInput struct { - Context *Context - CruSession session.Store - pnames []string - pvalues []string - data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. - RequestBody []byte + Context *Context + CruSession session.Store + pnames []string + pvalues []string + data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. + RequestBody []byte + RunMethod string + RunController reflect.Type } // NewInput return BeegoInput generated by Context. @@ -89,6 +93,9 @@ func (input *BeegoInput) Site() string { // Scheme returns request scheme as "http" or "https". func (input *BeegoInput) Scheme() string { + if scheme := input.Header("X-Forwarded-Proto"); scheme != "" { + return scheme + } if input.Context.Request.URL.Scheme != "" { return input.Context.Request.URL.Scheme } @@ -298,6 +305,14 @@ func (input *BeegoInput) SetParam(key, val string) { input.pnames = append(input.pnames, key) } +// ResetParams clears any of the input's Params +// This function is used to clear parameters so they may be reset between filter +// passes. +func (input *BeegoInput) ResetParams() { + input.pnames = input.pnames[:0] + input.pvalues = input.pvalues[:0] +} + // Query returns input data item string by a given string. func (input *BeegoInput) Query(key string) string { if val := input.Param(key); val != "" { @@ -326,18 +341,32 @@ func (input *BeegoInput) Cookie(key string) string { } // Session returns current session item value by a given key. -// if non-existed, return empty string. +// if non-existed, return nil. func (input *BeegoInput) Session(key interface{}) interface{} { return input.CruSession.Get(key) } // CopyBody returns the raw request body data as bytes. func (input *BeegoInput) CopyBody(MaxMemory int64) []byte { + if input.Context.Request.Body == nil { + return []byte{} + } + + var requestbody []byte safe := &io.LimitedReader{R: input.Context.Request.Body, N: MaxMemory} - requestbody, _ := ioutil.ReadAll(safe) + if input.Header("Content-Encoding") == "gzip" { + reader, err := gzip.NewReader(safe) + if err != nil { + return nil + } + requestbody, _ = ioutil.ReadAll(reader) + } else { + requestbody, _ = ioutil.ReadAll(safe) + } + input.Context.Request.Body.Close() bf := bytes.NewBuffer(requestbody) - input.Context.Request.Body = ioutil.NopCloser(bf) + input.Context.Request.Body = http.MaxBytesReader(input.Context.ResponseWriter, ioutil.NopCloser(bf), MaxMemory) input.RequestBody = requestbody return requestbody } @@ -397,7 +426,13 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error { if !value.CanSet() { return errors.New("beego: non-settable variable passed to Bind: " + key) } - rv := input.bind(key, value.Type()) + typ := value.Type() + // Get real type if dest define with interface{}. + // e.g var dest interface{} dest=1.0 + if value.Kind() == reflect.Interface { + typ = value.Elem().Type() + } + rv := input.bind(key, typ) if !rv.IsValid() { return errors.New("beego: reflect value is empty") } @@ -406,6 +441,9 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error { } func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { + if input.Context.Request.Form == nil { + input.Context.Request.ParseForm() + } rv := reflect.Zero(typ) switch typ.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: @@ -576,12 +614,15 @@ func (input *BeegoInput) bindStruct(params *url.Values, key string, typ reflect. result := reflect.New(typ).Elem() fieldValues := make(map[string]reflect.Value) for reqKey, val := range *params { - if !strings.HasPrefix(reqKey, key+".") { + var fieldName string + if strings.HasPrefix(reqKey, key+".") { + fieldName = reqKey[len(key)+1:] + } else if strings.HasPrefix(reqKey, key+"[") && reqKey[len(reqKey)-1] == ']' { + fieldName = reqKey[len(key)+1 : len(reqKey)-1] + } else { continue } - fieldName := reqKey[len(key)+1:] - if _, ok := fieldValues[fieldName]; !ok { // Time to bind this field. Get it and make sure we can set it. fieldValue := result.FieldByName(fieldName) diff --git a/src/vendor/github.com/astaxie/beego/context/input_test.go b/src/vendor/github.com/astaxie/beego/context/input_test.go index 24f6fd99c..db812a0f0 100644 --- a/src/vendor/github.com/astaxie/beego/context/input_test.go +++ b/src/vendor/github.com/astaxie/beego/context/input_test.go @@ -15,64 +15,98 @@ package context import ( - "fmt" "net/http" "net/http/httptest" "reflect" "testing" ) -func TestParse(t *testing.T) { - r, _ := http.NewRequest("GET", "/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil) - beegoInput := NewInput() - beegoInput.Context = NewContext() - beegoInput.Context.Reset(httptest.NewRecorder(), r) - beegoInput.ParseFormOrMulitForm(1 << 20) +func TestBind(t *testing.T) { + type testItem struct { + field string + empty interface{} + want interface{} + } + type Human struct { + ID int + Nick string + Pwd string + Ms bool + } - var id int - err := beegoInput.Bind(&id, "id") - if id != 123 || err != nil { - t.Fatal("id should has int value") - } - fmt.Println(id) + cases := []struct { + request string + valueGp []testItem + }{ + {"/?p=str", []testItem{{"p", interface{}(""), interface{}("str")}}}, - var isok bool - err = beegoInput.Bind(&isok, "isok") - if !isok || err != nil { - t.Fatal("isok should be true") - } - fmt.Println(isok) + {"/?p=", []testItem{{"p", "", ""}}}, + {"/?p=str", []testItem{{"p", "", "str"}}}, - var float float64 - err = beegoInput.Bind(&float, "ft") - if float != 1.2 || err != nil { - t.Fatal("float should be equal to 1.2") - } - fmt.Println(float) + {"/?p=123", []testItem{{"p", 0, 123}}}, + {"/?p=123", []testItem{{"p", uint(0), uint(123)}}}, - ol := make([]int, 0, 2) - err = beegoInput.Bind(&ol, "ol") - if len(ol) != 2 || err != nil || ol[0] != 1 || ol[1] != 2 { - t.Fatal("ol should has two elements") - } - fmt.Println(ol) + {"/?p=1.0", []testItem{{"p", 0.0, 1.0}}}, + {"/?p=1", []testItem{{"p", false, true}}}, - ul := make([]string, 0, 2) - err = beegoInput.Bind(&ul, "ul") - if len(ul) != 2 || err != nil || ul[0] != "str" || ul[1] != "array" { - t.Fatal("ul should has two elements") - } - fmt.Println(ul) + {"/?p=true", []testItem{{"p", false, true}}}, + {"/?p=ON", []testItem{{"p", false, true}}}, + {"/?p=on", []testItem{{"p", false, true}}}, + {"/?p=1", []testItem{{"p", false, true}}}, + {"/?p=2", []testItem{{"p", false, false}}}, + {"/?p=false", []testItem{{"p", false, false}}}, - type User struct { - Name string + {"/?p[a]=1&p[b]=2&p[c]=3", []testItem{{"p", map[string]int{}, map[string]int{"a": 1, "b": 2, "c": 3}}}}, + {"/?p[a]=v1&p[b]=v2&p[c]=v3", []testItem{{"p", map[string]string{}, map[string]string{"a": "v1", "b": "v2", "c": "v3"}}}}, + + {"/?p[]=8&p[]=9&p[]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}}, + {"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}}, + {"/?p[0]=8&p[1]=9&p[2]=10&p[5]=14", []testItem{{"p", []int{}, []int{8, 9, 10, 0, 0, 14}}}}, + {"/?p[0]=8.0&p[1]=9.0&p[2]=10.0", []testItem{{"p", []float64{}, []float64{8.0, 9.0, 10.0}}}}, + + {"/?p[]=10&p[]=9&p[]=8", []testItem{{"p", []string{}, []string{"10", "9", "8"}}}}, + {"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []string{}, []string{"8", "9", "10"}}}}, + + {"/?p[0]=true&p[1]=false&p[2]=true&p[5]=1&p[6]=ON&p[7]=other", []testItem{{"p", []bool{}, []bool{true, false, true, false, false, true, true, false}}}}, + + {"/?human.Nick=astaxie", []testItem{{"human", Human{}, Human{Nick: "astaxie"}}}}, + {"/?human.ID=888&human.Nick=astaxie&human.Ms=true&human[Pwd]=pass", []testItem{{"human", Human{}, Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass"}}}}, + {"/?human[0].ID=888&human[0].Nick=astaxie&human[0].Ms=true&human[0][Pwd]=pass01&human[1].ID=999&human[1].Nick=ysqi&human[1].Ms=On&human[1].Pwd=pass02", + []testItem{{"human", []Human{}, []Human{ + {ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"}, + {ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"}, + }}}}, + + { + "/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&human.Nick=astaxie", + []testItem{ + {"id", 0, 123}, + {"isok", false, true}, + {"ft", 0.0, 1.2}, + {"ol", []int{}, []int{1, 2}}, + {"ul", []string{}, []string{"str", "array"}}, + {"human", Human{}, Human{Nick: "astaxie"}}, + }, + }, } - user := User{} - err = beegoInput.Bind(&user, "user") - if err != nil || user.Name != "astaxie" { - t.Fatal("user should has name") + for _, c := range cases { + r, _ := http.NewRequest("GET", c.request, nil) + beegoInput := NewInput() + beegoInput.Context = NewContext() + beegoInput.Context.Reset(httptest.NewRecorder(), r) + + for _, item := range c.valueGp { + got := item.empty + err := beegoInput.Bind(&got, item.field) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(got, item.want) { + t.Fatalf("Bind %q error,should be:\n%#v \ngot:\n%#v", item.field, item.want, got) + } + } + } - fmt.Println(user) } func TestSubDomain(t *testing.T) { @@ -100,7 +134,7 @@ func TestSubDomain(t *testing.T) { /* TODO Fix this r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil) - beegoInput.Request = r + beegoInput.Context.Request = r if beegoInput.SubDomains() != "" { t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains()) } diff --git a/src/vendor/github.com/astaxie/beego/context/output.go b/src/vendor/github.com/astaxie/beego/context/output.go index 174047020..61ce8cc76 100644 --- a/src/vendor/github.com/astaxie/beego/context/output.go +++ b/src/vendor/github.com/astaxie/beego/context/output.go @@ -21,8 +21,11 @@ import ( "errors" "fmt" "html/template" + "io" "mime" "net/http" + "net/url" + "os" "path/filepath" "strconv" "strings" @@ -64,6 +67,7 @@ func (output *BeegoOutput) Body(content []byte) error { } if b, n, _ := WriteBody(encoding, buf, content); b { output.Header("Content-Encoding", n) + output.Header("Content-Length", strconv.Itoa(buf.Len())) } else { output.Header("Content-Length", strconv.Itoa(len(content))) } @@ -72,10 +76,11 @@ func (output *BeegoOutput) Body(content []byte) error { if output.Status != 0 { output.Context.ResponseWriter.WriteHeader(output.Status) output.Status = 0 + } else { + output.Context.ResponseWriter.Started = true } - - _, err := output.Context.ResponseWriter.Copy(buf) - return err + io.Copy(output.Context.ResponseWriter, buf) + return nil } // Cookie sets cookie value via given key. @@ -142,18 +147,12 @@ func (output *BeegoOutput) Cookie(name string, value string, others ...interface } // default false. for session cookie default true - httponly := false if len(others) > 4 { if v, ok := others[4].(bool); ok && v { - // HttpOnly = true - httponly = true + fmt.Fprintf(&b, "; HttpOnly") } } - if httponly { - fmt.Fprintf(&b, "; HttpOnly") - } - output.Context.ResponseWriter.Header().Add("Set-Cookie", b.String()) } @@ -169,6 +168,19 @@ func sanitizeValue(v string) string { return cookieValueSanitizer.Replace(v) } +func jsonRenderer(value interface{}) Renderer { + return rendererFunc(func(ctx *Context) { + ctx.Output.JSON(value, false, false) + }) +} + +func errorRenderer(err error) Renderer { + return rendererFunc(func(ctx *Context) { + ctx.Output.SetStatus(500) + ctx.Output.Body([]byte(err.Error())) + }) +} + // JSON writes json to response body. // if coding is true, it converts utf-8 to \u0000 type. func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, coding bool) error { @@ -208,7 +220,8 @@ func (output *BeegoOutput) JSONP(data interface{}, hasIndent bool) error { if callback == "" { return errors.New(`"callback" parameter required`) } - callbackContent := bytes.NewBufferString(" " + template.JSEscapeString(callback)) + callback = template.JSEscapeString(callback) + callbackContent := bytes.NewBufferString(" if(window." + callback + ")" + callback) callbackContent.WriteString("(") callbackContent.Write(content) callbackContent.WriteString(");\r\n") @@ -235,13 +248,21 @@ func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error { // Download forces response for download file. // it prepares the download response header automatically. func (output *BeegoOutput) Download(file string, filename ...string) { + // check get file error, file not found or other error. + if _, err := os.Stat(file); err != nil { + http.ServeFile(output.Context.ResponseWriter, output.Context.Request, file) + return + } + + var fName string + if len(filename) > 0 && filename[0] != "" { + fName = filename[0] + } else { + fName = filepath.Base(file) + } + output.Header("Content-Disposition", "attachment; filename="+url.QueryEscape(fName)) output.Header("Content-Description", "File Transfer") output.Header("Content-Type", "application/octet-stream") - if len(filename) > 0 && filename[0] != "" { - output.Header("Content-Disposition", "attachment; filename="+filename[0]) - } else { - output.Header("Content-Disposition", "attachment; filename="+filepath.Base(file)) - } output.Header("Content-Transfer-Encoding", "binary") output.Header("Expires", "0") output.Header("Cache-Control", "must-revalidate") @@ -269,70 +290,70 @@ func (output *BeegoOutput) SetStatus(status int) { // IsCachable returns boolean of this request is cached. // HTTP 304 means cached. -func (output *BeegoOutput) IsCachable(status int) bool { +func (output *BeegoOutput) IsCachable() bool { return output.Status >= 200 && output.Status < 300 || output.Status == 304 } // IsEmpty returns boolean of this request is empty. // HTTP 201,204 and 304 means empty. -func (output *BeegoOutput) IsEmpty(status int) bool { +func (output *BeegoOutput) IsEmpty() bool { return output.Status == 201 || output.Status == 204 || output.Status == 304 } // IsOk returns boolean of this request runs well. // HTTP 200 means ok. -func (output *BeegoOutput) IsOk(status int) bool { +func (output *BeegoOutput) IsOk() bool { return output.Status == 200 } // IsSuccessful returns boolean of this request runs successfully. // HTTP 2xx means ok. -func (output *BeegoOutput) IsSuccessful(status int) bool { +func (output *BeegoOutput) IsSuccessful() bool { return output.Status >= 200 && output.Status < 300 } // IsRedirect returns boolean of this request is redirection header. // HTTP 301,302,307 means redirection. -func (output *BeegoOutput) IsRedirect(status int) bool { +func (output *BeegoOutput) IsRedirect() bool { return output.Status == 301 || output.Status == 302 || output.Status == 303 || output.Status == 307 } // IsForbidden returns boolean of this request is forbidden. // HTTP 403 means forbidden. -func (output *BeegoOutput) IsForbidden(status int) bool { +func (output *BeegoOutput) IsForbidden() bool { return output.Status == 403 } // IsNotFound returns boolean of this request is not found. // HTTP 404 means forbidden. -func (output *BeegoOutput) IsNotFound(status int) bool { +func (output *BeegoOutput) IsNotFound() bool { return output.Status == 404 } // IsClientError returns boolean of this request client sends error data. // HTTP 4xx means forbidden. -func (output *BeegoOutput) IsClientError(status int) bool { +func (output *BeegoOutput) IsClientError() bool { return output.Status >= 400 && output.Status < 500 } // IsServerError returns boolean of this server handler errors. // HTTP 5xx means server internal error. -func (output *BeegoOutput) IsServerError(status int) bool { +func (output *BeegoOutput) IsServerError() bool { return output.Status >= 500 && output.Status < 600 } func stringsToJSON(str string) string { - rs := []rune(str) - jsons := "" - for _, r := range rs { + var jsons bytes.Buffer + for _, r := range str { rint := int(r) if rint < 128 { - jsons += string(r) + jsons.WriteRune(r) } else { - jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json + jsons.WriteString("\\u") + jsons.WriteString(strconv.FormatInt(int64(rint), 16)) } } - return jsons + return jsons.String() } // Session sets session item value with given key. diff --git a/src/vendor/github.com/astaxie/beego/context/param/conv.go b/src/vendor/github.com/astaxie/beego/context/param/conv.go new file mode 100644 index 000000000..c200e0088 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/context/param/conv.go @@ -0,0 +1,78 @@ +package param + +import ( + "fmt" + "reflect" + + beecontext "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" +) + +// ConvertParams converts http method params to values that will be passed to the method controller as arguments +func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { + result = make([]reflect.Value, 0, len(methodParams)) + for i := 0; i < len(methodParams); i++ { + reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) + result = append(result, reflectValue) + } + return +} + +func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { + paramValue := getParamValue(param, ctx) + if paramValue == "" { + if param.required { + ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) + } else { + paramValue = param.defaultValue + } + } + + reflectValue, err := parseValue(param, paramValue, paramType) + if err != nil { + logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %v, Error: %s", param.name, paramType, paramValue, err)) + ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %v to type %s", param.name, paramValue, paramType)) + } + + return reflectValue +} + +func getParamValue(param *MethodParam, ctx *beecontext.Context) string { + switch param.in { + case body: + return string(ctx.Input.RequestBody) + case header: + return ctx.Input.Header(param.name) + case path: + return ctx.Input.Query(":" + param.name) + default: + return ctx.Input.Query(param.name) + } +} + +func parseValue(param *MethodParam, paramValue string, paramType reflect.Type) (result reflect.Value, err error) { + if paramValue == "" { + return reflect.Zero(paramType), nil + } + parser := getParser(param, paramType) + value, err := parser.parse(paramValue, paramType) + if err != nil { + return result, err + } + + return safeConvert(reflect.ValueOf(value), paramType) +} + +func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("%v", r) + } + } + }() + result = value.Convert(t) + return +} diff --git a/src/vendor/github.com/astaxie/beego/context/param/methodparams.go b/src/vendor/github.com/astaxie/beego/context/param/methodparams.go new file mode 100644 index 000000000..cd6708a27 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/context/param/methodparams.go @@ -0,0 +1,69 @@ +package param + +import ( + "fmt" + "strings" +) + +//MethodParam keeps param information to be auto passed to controller methods +type MethodParam struct { + name string + in paramType + required bool + defaultValue string +} + +type paramType byte + +const ( + param paramType = iota + path + body + header +) + +//New creates a new MethodParam with name and specific options +func New(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, nil, opts) +} + +func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) { + param = &MethodParam{name: name} + for _, option := range opts { + option(param) + } + return +} + +//Make creates an array of MethodParmas or an empty array +func Make(list ...*MethodParam) []*MethodParam { + if len(list) > 0 { + return list + } + return nil +} + +func (mp *MethodParam) String() string { + options := []string{} + result := "param.New(\"" + mp.name + "\"" + if mp.required { + options = append(options, "param.IsRequired") + } + switch mp.in { + case path: + options = append(options, "param.InPath") + case body: + options = append(options, "param.InBody") + case header: + options = append(options, "param.InHeader") + } + if mp.defaultValue != "" { + options = append(options, fmt.Sprintf(`param.Default("%s")`, mp.defaultValue)) + } + if len(options) > 0 { + result += ", " + } + result += strings.Join(options, ", ") + result += ")" + return result +} diff --git a/src/vendor/github.com/astaxie/beego/context/param/options.go b/src/vendor/github.com/astaxie/beego/context/param/options.go new file mode 100644 index 000000000..58bdc3d03 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/context/param/options.go @@ -0,0 +1,37 @@ +package param + +import ( + "fmt" +) + +// MethodParamOption defines a func which apply options on a MethodParam +type MethodParamOption func(*MethodParam) + +// IsRequired indicates that this param is required and can not be ommited from the http request +var IsRequired MethodParamOption = func(p *MethodParam) { + p.required = true +} + +// InHeader indicates that this param is passed via an http header +var InHeader MethodParamOption = func(p *MethodParam) { + p.in = header +} + +// InPath indicates that this param is part of the URL path +var InPath MethodParamOption = func(p *MethodParam) { + p.in = path +} + +// InBody indicates that this param is passed as an http request body +var InBody MethodParamOption = func(p *MethodParam) { + p.in = body +} + +// Default provides a default value for the http param +func Default(defaultValue interface{}) MethodParamOption { + return func(p *MethodParam) { + if defaultValue != nil { + p.defaultValue = fmt.Sprint(defaultValue) + } + } +} diff --git a/src/vendor/github.com/astaxie/beego/context/param/parsers.go b/src/vendor/github.com/astaxie/beego/context/param/parsers.go new file mode 100644 index 000000000..421aecf08 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/context/param/parsers.go @@ -0,0 +1,149 @@ +package param + +import ( + "encoding/json" + "reflect" + "strconv" + "strings" + "time" +) + +type paramParser interface { + parse(value string, toType reflect.Type) (interface{}, error) +} + +func getParser(param *MethodParam, t reflect.Type) paramParser { + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return intParser{} + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { //treat []byte as string + return stringParser{} + } + if param.in == body { + return jsonParser{} + } + elemParser := getParser(param, t.Elem()) + if elemParser == (jsonParser{}) { + return elemParser + } + return sliceParser(elemParser) + case reflect.Bool: + return boolParser{} + case reflect.String: + return stringParser{} + case reflect.Float32, reflect.Float64: + return floatParser{} + case reflect.Ptr: + elemParser := getParser(param, t.Elem()) + if elemParser == (jsonParser{}) { + return elemParser + } + return ptrParser(elemParser) + default: + if t.PkgPath() == "time" && t.Name() == "Time" { + return timeParser{} + } + return jsonParser{} + } +} + +type parserFunc func(value string, toType reflect.Type) (interface{}, error) + +func (f parserFunc) parse(value string, toType reflect.Type) (interface{}, error) { + return f(value, toType) +} + +type boolParser struct { +} + +func (p boolParser) parse(value string, toType reflect.Type) (interface{}, error) { + return strconv.ParseBool(value) +} + +type stringParser struct { +} + +func (p stringParser) parse(value string, toType reflect.Type) (interface{}, error) { + return value, nil +} + +type intParser struct { +} + +func (p intParser) parse(value string, toType reflect.Type) (interface{}, error) { + return strconv.Atoi(value) +} + +type floatParser struct { +} + +func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) { + if toType.Kind() == reflect.Float32 { + res, err := strconv.ParseFloat(value, 32) + if err != nil { + return nil, err + } + return float32(res), nil + } + return strconv.ParseFloat(value, 64) +} + +type timeParser struct { +} + +func (p timeParser) parse(value string, toType reflect.Type) (result interface{}, err error) { + result, err = time.Parse(time.RFC3339, value) + if err != nil { + result, err = time.Parse("2006-01-02", value) + } + return +} + +type jsonParser struct { +} + +func (p jsonParser) parse(value string, toType reflect.Type) (interface{}, error) { + pResult := reflect.New(toType) + v := pResult.Interface() + err := json.Unmarshal([]byte(value), v) + if err != nil { + return nil, err + } + return pResult.Elem().Interface(), nil +} + +func sliceParser(elemParser paramParser) paramParser { + return parserFunc(func(value string, toType reflect.Type) (interface{}, error) { + values := strings.Split(value, ",") + result := reflect.MakeSlice(toType, 0, len(values)) + elemType := toType.Elem() + for _, v := range values { + parsedValue, err := elemParser.parse(v, elemType) + if err != nil { + return nil, err + } + result = reflect.Append(result, reflect.ValueOf(parsedValue)) + } + return result.Interface(), nil + }) +} + +func ptrParser(elemParser paramParser) paramParser { + return parserFunc(func(value string, toType reflect.Type) (interface{}, error) { + parsedValue, err := elemParser.parse(value, toType.Elem()) + if err != nil { + return nil, err + } + newValPtr := reflect.New(toType.Elem()) + newVal := reflect.Indirect(newValPtr) + convertedVal, err := safeConvert(reflect.ValueOf(parsedValue), toType.Elem()) + if err != nil { + return nil, err + } + + newVal.Set(convertedVal) + return newValPtr.Interface(), nil + }) +} diff --git a/src/vendor/github.com/astaxie/beego/context/param/parsers_test.go b/src/vendor/github.com/astaxie/beego/context/param/parsers_test.go new file mode 100644 index 000000000..b946ba08b --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/context/param/parsers_test.go @@ -0,0 +1,84 @@ +package param + +import "testing" +import "reflect" +import "time" + +type testDefinition struct { + strValue string + expectedValue interface{} + expectedParser paramParser +} + +func Test_Parsers(t *testing.T) { + + //ints + checkParser(testDefinition{"1", 1, intParser{}}, t) + checkParser(testDefinition{"-1", int64(-1), intParser{}}, t) + checkParser(testDefinition{"1", uint64(1), intParser{}}, t) + + //floats + checkParser(testDefinition{"1.0", float32(1.0), floatParser{}}, t) + checkParser(testDefinition{"-1.0", float64(-1.0), floatParser{}}, t) + + //strings + checkParser(testDefinition{"AB", "AB", stringParser{}}, t) + checkParser(testDefinition{"AB", []byte{65, 66}, stringParser{}}, t) + + //bools + checkParser(testDefinition{"true", true, boolParser{}}, t) + checkParser(testDefinition{"0", false, boolParser{}}, t) + + //timeParser + checkParser(testDefinition{"2017-05-30T13:54:53Z", time.Date(2017, 5, 30, 13, 54, 53, 0, time.UTC), timeParser{}}, t) + checkParser(testDefinition{"2017-05-30", time.Date(2017, 5, 30, 0, 0, 0, 0, time.UTC), timeParser{}}, t) + + //json + checkParser(testDefinition{`{"X": 5, "Y":"Z"}`, struct { + X int + Y string + }{5, "Z"}, jsonParser{}}, t) + + //slice in query is parsed as comma delimited + checkParser(testDefinition{`1,2`, []int{1, 2}, sliceParser(intParser{})}, t) + + //slice in body is parsed as json + checkParser(testDefinition{`["a","b"]`, []string{"a", "b"}, jsonParser{}}, t, MethodParam{in: body}) + + //pointers + var someInt = 1 + checkParser(testDefinition{`1`, &someInt, ptrParser(intParser{})}, t) + + var someStruct = struct{ X int }{5} + checkParser(testDefinition{`{"X": 5}`, &someStruct, jsonParser{}}, t) + +} + +func checkParser(def testDefinition, t *testing.T, methodParam ...MethodParam) { + toType := reflect.TypeOf(def.expectedValue) + var mp MethodParam + if len(methodParam) == 0 { + mp = MethodParam{} + } else { + mp = methodParam[0] + } + parser := getParser(&mp, toType) + + if reflect.TypeOf(parser) != reflect.TypeOf(def.expectedParser) { + t.Errorf("Invalid parser for value %v. Expected: %v, actual: %v", def.strValue, reflect.TypeOf(def.expectedParser).Name(), reflect.TypeOf(parser).Name()) + return + } + result, err := parser.parse(def.strValue, toType) + if err != nil { + t.Errorf("Parsing error for value %v. Expected result: %v, error: %v", def.strValue, def.expectedValue, err) + return + } + convResult, err := safeConvert(reflect.ValueOf(result), toType) + if err != nil { + t.Errorf("Convertion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err) + return + } + if !reflect.DeepEqual(convResult.Interface(), def.expectedValue) { + t.Errorf("Parsing error for value %v. Expected result: %v, actual: %v", def.strValue, def.expectedValue, result) + } +} diff --git a/src/vendor/github.com/astaxie/beego/context/renderer.go b/src/vendor/github.com/astaxie/beego/context/renderer.go new file mode 100644 index 000000000..36a7cb53f --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/context/renderer.go @@ -0,0 +1,12 @@ +package context + +// Renderer defines an http response renderer +type Renderer interface { + Render(ctx *Context) +} + +type rendererFunc func(ctx *Context) + +func (f rendererFunc) Render(ctx *Context) { + f(ctx) +} diff --git a/src/vendor/github.com/astaxie/beego/context/response.go b/src/vendor/github.com/astaxie/beego/context/response.go new file mode 100644 index 000000000..9c3c715a2 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/context/response.go @@ -0,0 +1,27 @@ +package context + +import ( + "strconv" + + "net/http" +) + +const ( + //BadRequest indicates http error 400 + BadRequest StatusCode = http.StatusBadRequest + + //NotFound indicates http error 404 + NotFound StatusCode = http.StatusNotFound +) + +// StatusCode sets the http response status code +type StatusCode int + +func (s StatusCode) Error() string { + return strconv.Itoa(int(s)) +} + +// Render sets the http status code +func (s StatusCode) Render(ctx *Context) { + ctx.Output.SetStatus(int(s)) +} diff --git a/src/vendor/github.com/astaxie/beego/controller.go b/src/vendor/github.com/astaxie/beego/controller.go index 9bdfc7200..c104eb2a2 100644 --- a/src/vendor/github.com/astaxie/beego/controller.go +++ b/src/vendor/github.com/astaxie/beego/controller.go @@ -28,6 +28,7 @@ import ( "strings" "github.com/astaxie/beego/context" + "github.com/astaxie/beego/context/param" "github.com/astaxie/beego/session" ) @@ -51,8 +52,16 @@ type ControllerComments struct { Router string AllowHTTPMethods []string Params []map[string]string + MethodParams []*param.MethodParam } +// ControllerCommentsSlice implements the sort interface +type ControllerCommentsSlice []ControllerComments + +func (p ControllerCommentsSlice) Len() int { return len(p) } +func (p ControllerCommentsSlice) Less(i, j int) bool { return p[i].Router < p[j].Router } +func (p ControllerCommentsSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + // Controller defines some basic http request handler operations, such as // http context, template and view, session and xsrf. type Controller struct { @@ -69,8 +78,10 @@ type Controller struct { // template data TplName string + ViewPath string Layout string LayoutSections map[string]string // the key is the section name and the value is the template name + TplPrefix string TplExt string EnableRender bool @@ -184,7 +195,11 @@ func (c *Controller) Render() error { if err != nil { return err } - c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") + + if c.Ctx.ResponseWriter.Header().Get("Content-Type") == "" { + c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") + } + return c.Ctx.Output.Body(rb) } @@ -208,7 +223,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { continue } buf.Reset() - err = executeTemplate(&buf, sectionTpl, c.Data) + err = ExecuteViewPathTemplate(&buf, sectionTpl, c.viewPath(), c.Data) if err != nil { return nil, err } @@ -217,7 +232,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { } buf.Reset() - executeTemplate(&buf, c.Layout, c.Data) + ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data) } return buf.Bytes(), err } @@ -227,6 +242,9 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) { if c.TplName == "" { c.TplName = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt } + if c.TplPrefix != "" { + c.TplName = c.TplPrefix + c.TplName + } if BConfig.RunMode == DEV { buildFiles := []string{c.TplName} if c.Layout != "" { @@ -240,9 +258,16 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) { } } } - BuildTemplate(BConfig.WebConfig.ViewsPath, buildFiles...) + BuildTemplate(c.viewPath(), buildFiles...) } - return buf, executeTemplate(&buf, c.TplName, c.Data) + return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data) +} + +func (c *Controller) viewPath() string { + if c.ViewPath == "" { + return BConfig.WebConfig.ViewsPath + } + return c.ViewPath } // Redirect sends the redirection response to url with status code. @@ -261,14 +286,13 @@ func (c *Controller) Abort(code string) { // CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body. func (c *Controller) CustomAbort(status int, body string) { - //This has to be kept if overwritten by dep tools - c.Ctx.ResponseWriter.WriteHeader(status) - // c.Ctx.Output.Status = status - // first panic from ErrorMaps, is is user defined error functions. + // first panic from ErrorMaps, it is user defined error functions. if _, ok := ErrorMaps[body]; ok { + c.Ctx.Output.Status = status panic(body) } // last panic user string + c.Ctx.ResponseWriter.WriteHeader(status) c.Ctx.ResponseWriter.Write([]byte(body)) panic(ErrAbort) } @@ -299,7 +323,7 @@ func (c *Controller) ServeJSON(encoding ...bool) { if BConfig.RunMode == PROD { hasIndent = false } - if len(encoding) > 0 && encoding[0] == true { + if len(encoding) > 0 && encoding[0] { hasEncoding = true } c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding) @@ -396,6 +420,16 @@ func (c *Controller) GetInt8(key string, def ...int8) (int8, error) { return int8(i64), err } +// GetUint8 return input as an uint8 or the default value while it's present and input is blank +func (c *Controller) GetUint8(key string, def ...uint8) (uint8, error) { + strv := c.Ctx.Input.Query(key) + if len(strv) == 0 && len(def) > 0 { + return def[0], nil + } + u64, err := strconv.ParseUint(strv, 10, 8) + return uint8(u64), err +} + // GetInt16 returns input as an int16 or the default value while it's present and input is blank func (c *Controller) GetInt16(key string, def ...int16) (int16, error) { strv := c.Ctx.Input.Query(key) @@ -406,6 +440,16 @@ func (c *Controller) GetInt16(key string, def ...int16) (int16, error) { return int16(i64), err } +// GetUint16 returns input as an uint16 or the default value while it's present and input is blank +func (c *Controller) GetUint16(key string, def ...uint16) (uint16, error) { + strv := c.Ctx.Input.Query(key) + if len(strv) == 0 && len(def) > 0 { + return def[0], nil + } + u64, err := strconv.ParseUint(strv, 10, 16) + return uint16(u64), err +} + // GetInt32 returns input as an int32 or the default value while it's present and input is blank func (c *Controller) GetInt32(key string, def ...int32) (int32, error) { strv := c.Ctx.Input.Query(key) @@ -416,6 +460,16 @@ func (c *Controller) GetInt32(key string, def ...int32) (int32, error) { return int32(i64), err } +// GetUint32 returns input as an uint32 or the default value while it's present and input is blank +func (c *Controller) GetUint32(key string, def ...uint32) (uint32, error) { + strv := c.Ctx.Input.Query(key) + if len(strv) == 0 && len(def) > 0 { + return def[0], nil + } + u64, err := strconv.ParseUint(strv, 10, 32) + return uint32(u64), err +} + // GetInt64 returns input value as int64 or the default value while it's present and input is blank. func (c *Controller) GetInt64(key string, def ...int64) (int64, error) { strv := c.Ctx.Input.Query(key) @@ -425,6 +479,15 @@ func (c *Controller) GetInt64(key string, def ...int64) (int64, error) { return strconv.ParseInt(strv, 10, 64) } +// GetUint64 returns input value as uint64 or the default value while it's present and input is blank. +func (c *Controller) GetUint64(key string, def ...uint64) (uint64, error) { + strv := c.Ctx.Input.Query(key) + if len(strv) == 0 && len(def) > 0 { + return def[0], nil + } + return strconv.ParseUint(strv, 10, 64) +} + // GetBool returns input value as bool or the default value while it's present and input is blank. func (c *Controller) GetBool(key string, def ...bool) (bool, error) { strv := c.Ctx.Input.Query(key) @@ -450,7 +513,7 @@ func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, } // GetFiles return multi-upload files -// files, err:=c.Getfiles("myfiles") +// files, err:=c.GetFiles("myfiles") // if err != nil { // http.Error(w, err.Error(), http.StatusNoContent) // return diff --git a/src/vendor/github.com/astaxie/beego/controller_test.go b/src/vendor/github.com/astaxie/beego/controller_test.go index 51d3a5b7c..1e53416d7 100644 --- a/src/vendor/github.com/astaxie/beego/controller_test.go +++ b/src/vendor/github.com/astaxie/beego/controller_test.go @@ -15,9 +15,13 @@ package beego import ( + "math" + "strconv" "testing" "github.com/astaxie/beego/context" + "os" + "path/filepath" ) func TestGetInt(t *testing.T) { @@ -75,3 +79,103 @@ func TestGetInt64(t *testing.T) { t.Errorf("TestGeetInt64 expect 40,get %T,%v", val, val) } } + +func TestGetUint8(t *testing.T) { + i := context.NewInput() + i.SetParam("age", strconv.FormatUint(math.MaxUint8, 10)) + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + val, _ := ctrlr.GetUint8("age") + if val != math.MaxUint8 { + t.Errorf("TestGetUint8 expect %v,get %T,%v", math.MaxUint8, val, val) + } +} + +func TestGetUint16(t *testing.T) { + i := context.NewInput() + i.SetParam("age", strconv.FormatUint(math.MaxUint16, 10)) + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + val, _ := ctrlr.GetUint16("age") + if val != math.MaxUint16 { + t.Errorf("TestGetUint16 expect %v,get %T,%v", math.MaxUint16, val, val) + } +} + +func TestGetUint32(t *testing.T) { + i := context.NewInput() + i.SetParam("age", strconv.FormatUint(math.MaxUint32, 10)) + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + val, _ := ctrlr.GetUint32("age") + if val != math.MaxUint32 { + t.Errorf("TestGetUint32 expect %v,get %T,%v", math.MaxUint32, val, val) + } +} + +func TestGetUint64(t *testing.T) { + i := context.NewInput() + i.SetParam("age", strconv.FormatUint(math.MaxUint64, 10)) + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + val, _ := ctrlr.GetUint64("age") + if val != math.MaxUint64 { + t.Errorf("TestGetUint64 expect %v,get %T,%v", uint64(math.MaxUint64), val, val) + } +} + +func TestAdditionalViewPaths(t *testing.T) { + dir1 := "_beeTmp" + dir2 := "_beeTmp2" + defer os.RemoveAll(dir1) + defer os.RemoveAll(dir2) + + dir1file := "file1.tpl" + dir2file := "file2.tpl" + + genFile := func(dir string, name string, content string) { + os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777) + if f, err := os.Create(filepath.Join(dir, name)); err != nil { + t.Fatal(err) + } else { + defer f.Close() + f.WriteString(content) + f.Close() + } + + } + genFile(dir1, dir1file, `
{{.Content}}
`) + genFile(dir2, dir2file, `{{.Content}}`) + + AddViewPath(dir1) + AddViewPath(dir2) + + ctrl := Controller{ + TplName: "file1.tpl", + ViewPath: dir1, + } + ctrl.Data = map[interface{}]interface{}{ + "Content": "value2", + } + if result, err := ctrl.RenderString(); err != nil { + t.Fatal(err) + } else { + if result != "
value2
" { + t.Fatalf("TestAdditionalViewPaths expect %s got %s", "
value2
", result) + } + } + + func() { + ctrl.TplName = "file2.tpl" + defer func() { + if r := recover(); r == nil { + t.Fatal("TestAdditionalViewPaths expected error") + } + }() + ctrl.RenderString() + }() + + ctrl.TplName = "file2.tpl" + ctrl.ViewPath = dir2 + ctrl.RenderString() +} diff --git a/src/vendor/github.com/astaxie/beego/error.go b/src/vendor/github.com/astaxie/beego/error.go index 4f48fab21..b913db39d 100644 --- a/src/vendor/github.com/astaxie/beego/error.go +++ b/src/vendor/github.com/astaxie/beego/error.go @@ -93,7 +93,11 @@ func showErr(err interface{}, ctx *context.Context, stack string) { "BeegoVersion": VERSION, "GoVersion": runtime.Version(), } - ctx.ResponseWriter.WriteHeader(500) + if ctx.Output.Status != 0 { + ctx.ResponseWriter.WriteHeader(ctx.Output.Status) + } else { + ctx.ResponseWriter.WriteHeader(500) + } t.Execute(ctx.ResponseWriter, data) } @@ -210,159 +214,163 @@ var ErrorMaps = make(map[string]*errorInfo, 10) // show 401 unauthorized error. func unauthorized(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(401), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The page you have requested can't be authorized." + - "
Perhaps you are here because:" + - "

") - t.Execute(rw, data) + responseError(rw, r, + 401, + "
The page you have requested can't be authorized."+ + "
Perhaps you are here because:"+ + "

", + ) } // show 402 Payment Required func paymentRequired(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(402), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The page you have requested Payment Required." + - "
Perhaps you are here because:" + - "

") - t.Execute(rw, data) + responseError(rw, r, + 402, + "
The page you have requested Payment Required."+ + "
Perhaps you are here because:"+ + "

", + ) } // show 403 forbidden error. func forbidden(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(403), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The page you have requested is forbidden." + - "
Perhaps you are here because:" + - "

") - t.Execute(rw, data) + responseError(rw, r, + 403, + "
The page you have requested is forbidden."+ + "
Perhaps you are here because:"+ + "

", + ) } -// show 404 notfound error. +// show 422 missing xsrf token +func missingxsrf(rw http.ResponseWriter, r *http.Request) { + responseError(rw, r, + 422, + "
The page you have requested is forbidden."+ + "
Perhaps you are here because:"+ + "

", + ) +} + +// show 417 invalid xsrf token +func invalidxsrf(rw http.ResponseWriter, r *http.Request) { + responseError(rw, r, + 417, + "
The page you have requested is forbidden."+ + "
Perhaps you are here because:"+ + "

", + ) +} + +// show 404 not found error. func notFound(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(404), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The page you have requested has flown the coop." + - "
Perhaps you are here because:" + - "

") - t.Execute(rw, data) + responseError(rw, r, + 404, + "
The page you have requested has flown the coop."+ + "
Perhaps you are here because:"+ + "

", + ) } // show 405 Method Not Allowed func methodNotAllowed(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(405), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The method you have requested Not Allowed." + - "
Perhaps you are here because:" + - "

") - t.Execute(rw, data) + responseError(rw, r, + 405, + "
The method you have requested Not Allowed."+ + "
Perhaps you are here because:"+ + "

", + ) } // show 500 internal server error. func internalServerError(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(500), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The page you have requested is down right now." + - "

") - t.Execute(rw, data) + responseError(rw, r, + 500, + "
The page you have requested is down right now."+ + "

", + ) } // show 501 Not Implemented. func notImplemented(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(504), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The page you have requested is Not Implemented." + - "

") - t.Execute(rw, data) + responseError(rw, r, + 501, + "
The page you have requested is Not Implemented."+ + "

", + ) } // show 502 Bad Gateway. func badGateway(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(502), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The page you have requested is down right now." + - "

") - t.Execute(rw, data) + responseError(rw, r, + 502, + "
The page you have requested is down right now."+ + "

", + ) } // show 503 service unavailable error. func serviceUnavailable(rw http.ResponseWriter, r *http.Request) { - t, _ := template.New("beegoerrortemp").Parse(errtpl) - data := map[string]interface{}{ - "Title": http.StatusText(503), - "BeegoVersion": VERSION, - } - data["Content"] = template.HTML("
The page you have requested is unavailable." + - "
Perhaps you are here because:" + - "

") - t.Execute(rw, data) + responseError(rw, r, + 503, + "
The page you have requested is unavailable."+ + "
Perhaps you are here because:"+ + "

", + ) } // show 504 Gateway Timeout. func gatewayTimeout(rw http.ResponseWriter, r *http.Request) { + responseError(rw, r, + 504, + "
The page you have requested is unavailable"+ + "
Perhaps you are here because:"+ + "

", + ) +} + +func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) { t, _ := template.New("beegoerrortemp").Parse(errtpl) data := map[string]interface{}{ - "Title": http.StatusText(504), + "Title": http.StatusText(errCode), "BeegoVersion": VERSION, + "Content": template.HTML(errContent), } - data["Content"] = template.HTML("
The page you have requested is unavailable." + - "
Perhaps you are here because:" + - "

") t.Execute(rw, data) } @@ -400,6 +408,11 @@ func ErrorController(c ControllerInterface) *App { return BeeApp } +// Exception Write HttpStatus with errCode and Exec error handler if exist. +func Exception(errCode uint64, ctx *context.Context) { + exception(strconv.FormatUint(errCode, 10), ctx) +} + // show error string as simple text message. // if error string is empty, show 503 or 500 error as default. func exception(errCode string, ctx *context.Context) { @@ -408,7 +421,10 @@ func exception(errCode string, ctx *context.Context) { if err == nil { return v } - return 503 + if ctx.Output.Status == 0 { + return 503 + } + return ctx.Output.Status } for _, ec := range []string{errCode, "503", "500"} { diff --git a/src/vendor/github.com/astaxie/beego/error_test.go b/src/vendor/github.com/astaxie/beego/error_test.go new file mode 100644 index 000000000..378aa9538 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/error_test.go @@ -0,0 +1,88 @@ +// Copyright 2016 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package beego + +import ( + "net/http" + "net/http/httptest" + "strconv" + "strings" + "testing" +) + +type errorTestController struct { + Controller +} + +const parseCodeError = "parse code error" + +func (ec *errorTestController) Get() { + errorCode, err := ec.GetInt("code") + if err != nil { + ec.Abort(parseCodeError) + } + if errorCode != 0 { + ec.CustomAbort(errorCode, ec.GetString("code")) + } + ec.Abort("404") +} + +func TestErrorCode_01(t *testing.T) { + registerDefaultErrorHandler() + for k := range ErrorMaps { + r, _ := http.NewRequest("GET", "/error?code="+k, nil) + w := httptest.NewRecorder() + + handler := NewControllerRegister() + handler.Add("/error", &errorTestController{}) + handler.ServeHTTP(w, r) + code, _ := strconv.Atoi(k) + if w.Code != code { + t.Fail() + } + if !strings.Contains(w.Body.String(), http.StatusText(code)) { + t.Fail() + } + } +} + +func TestErrorCode_02(t *testing.T) { + registerDefaultErrorHandler() + r, _ := http.NewRequest("GET", "/error?code=0", nil) + w := httptest.NewRecorder() + + handler := NewControllerRegister() + handler.Add("/error", &errorTestController{}) + handler.ServeHTTP(w, r) + if w.Code != 404 { + t.Fail() + } +} + +func TestErrorCode_03(t *testing.T) { + registerDefaultErrorHandler() + r, _ := http.NewRequest("GET", "/error?code=panic", nil) + w := httptest.NewRecorder() + + handler := NewControllerRegister() + handler.Add("/error", &errorTestController{}) + handler.ServeHTTP(w, r) + if w.Code != 200 { + t.Fail() + } + if w.Body.String() != parseCodeError { + t.Fail() + } +} diff --git a/src/vendor/github.com/astaxie/beego/filter.go b/src/vendor/github.com/astaxie/beego/filter.go index 863223f79..9cc6e9134 100644 --- a/src/vendor/github.com/astaxie/beego/filter.go +++ b/src/vendor/github.com/astaxie/beego/filter.go @@ -27,6 +27,7 @@ type FilterRouter struct { tree *Tree pattern string returnOnOutput bool + resetParams bool } // ValidRouter checks if the current request is matched by this filter. diff --git a/src/vendor/github.com/astaxie/beego/filter_test.go b/src/vendor/github.com/astaxie/beego/filter_test.go index d9928d8d7..4ca4d2b84 100644 --- a/src/vendor/github.com/astaxie/beego/filter_test.go +++ b/src/vendor/github.com/astaxie/beego/filter_test.go @@ -20,14 +20,8 @@ import ( "testing" "github.com/astaxie/beego/context" - "github.com/astaxie/beego/logs" ) -func init() { - BeeLogger = logs.NewLogger(10000) - BeeLogger.SetLogger("console", "") -} - var FilterUser = func(ctx *context.Context) { ctx.Output.Body([]byte("i am " + ctx.Input.Param(":last") + ctx.Input.Param(":first"))) } diff --git a/src/vendor/github.com/astaxie/beego/flash_test.go b/src/vendor/github.com/astaxie/beego/flash_test.go index 640d54de6..d5e9608dc 100644 --- a/src/vendor/github.com/astaxie/beego/flash_test.go +++ b/src/vendor/github.com/astaxie/beego/flash_test.go @@ -48,7 +48,7 @@ func TestFlashHeader(t *testing.T) { // match for the expected header res := strings.Contains(sc, "BEEGO_FLASH=%00notice%23BEEGOFLASH%23TestFlashString%00") // validate the assertion - if res != true { + if !res { t.Errorf("TestFlashHeader() unable to validate flash message") } } diff --git a/src/vendor/github.com/astaxie/beego/grace/conn.go b/src/vendor/github.com/astaxie/beego/grace/conn.go index 6807e1ace..e020f8507 100644 --- a/src/vendor/github.com/astaxie/beego/grace/conn.go +++ b/src/vendor/github.com/astaxie/beego/grace/conn.go @@ -3,14 +3,17 @@ package grace import ( "errors" "net" + "sync" ) type graceConn struct { net.Conn server *Server + m sync.Mutex + closed bool } -func (c graceConn) Close() (err error) { +func (c *graceConn) Close() (err error) { defer func() { if r := recover(); r != nil { switch x := r.(type) { @@ -23,6 +26,14 @@ func (c graceConn) Close() (err error) { } } }() + + c.m.Lock() + if c.closed { + c.m.Unlock() + return + } c.server.wg.Done() + c.closed = true + c.m.Unlock() return c.Conn.Close() } diff --git a/src/vendor/github.com/astaxie/beego/grace/grace.go b/src/vendor/github.com/astaxie/beego/grace/grace.go index af4e90683..6ebf8455f 100644 --- a/src/vendor/github.com/astaxie/beego/grace/grace.go +++ b/src/vendor/github.com/astaxie/beego/grace/grace.go @@ -85,23 +85,31 @@ var ( isChild bool socketOrder string - once sync.Once + + hookableSignals []os.Signal ) -func onceInit() { - regLock = &sync.Mutex{} +func init() { flag.BoolVar(&isChild, "graceful", false, "listen on open fd (after forking)") flag.StringVar(&socketOrder, "socketorder", "", "previous initialization order - used when more than one listener was started") + + regLock = &sync.Mutex{} runningServers = make(map[string]*Server) runningServersOrder = []string{} socketPtrOffsetMap = make(map[string]uint) + + hookableSignals = []os.Signal{ + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + } } // NewServer returns a new graceServer. func NewServer(addr string, handler http.Handler) (srv *Server) { - once.Do(onceInit) regLock.Lock() defer regLock.Unlock() + if !flag.Parsed() { flag.Parse() } diff --git a/src/vendor/github.com/astaxie/beego/grace/listener.go b/src/vendor/github.com/astaxie/beego/grace/listener.go index 5439d0b20..7ede63a30 100644 --- a/src/vendor/github.com/astaxie/beego/grace/listener.go +++ b/src/vendor/github.com/astaxie/beego/grace/listener.go @@ -21,7 +21,7 @@ func newGraceListener(l net.Listener, srv *Server) (el *graceListener) { server: srv, } go func() { - _ = <-el.stop + <-el.stop el.stopped = true el.stop <- el.Listener.Close() }() @@ -37,7 +37,7 @@ func (gl *graceListener) Accept() (c net.Conn, err error) { tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(3 * time.Minute) - c = graceConn{ + c = &graceConn{ Conn: tc, server: gl.server, } diff --git a/src/vendor/github.com/astaxie/beego/grace/server.go b/src/vendor/github.com/astaxie/beego/grace/server.go index 101bda56d..b82423353 100644 --- a/src/vendor/github.com/astaxie/beego/grace/server.go +++ b/src/vendor/github.com/astaxie/beego/grace/server.go @@ -162,9 +162,7 @@ func (srv *Server) handleSignals() { signal.Notify( srv.sigChan, - syscall.SIGHUP, - syscall.SIGINT, - syscall.SIGTERM, + hookableSignals..., ) pid := syscall.Getpid() @@ -198,7 +196,6 @@ func (srv *Server) signalHooks(ppFlag int, sig os.Signal) { for _, f := range srv.SignalHooks[ppFlag][sig] { f() } - return } // shutdown closes the listener so that no new connections are accepted. it also @@ -290,3 +287,19 @@ func (srv *Server) fork() (err error) { return } + +// RegisterSignalHook registers a function to be run PreSignal or PostSignal for a given signal. +func (srv *Server) RegisterSignalHook(ppFlag int, sig os.Signal, f func()) (err error) { + if ppFlag != PreSignal && ppFlag != PostSignal { + err = fmt.Errorf("Invalid ppFlag argument. Must be either grace.PreSignal or grace.PostSignal") + return + } + for _, s := range hookableSignals { + if s == sig { + srv.SignalHooks[ppFlag][sig] = append(srv.SignalHooks[ppFlag][sig], f) + return + } + } + err = fmt.Errorf("Signal '%v' is not supported", sig) + return +} diff --git a/src/vendor/github.com/astaxie/beego/hooks.go b/src/vendor/github.com/astaxie/beego/hooks.go index 59b10b32f..c5ec8e2dd 100644 --- a/src/vendor/github.com/astaxie/beego/hooks.go +++ b/src/vendor/github.com/astaxie/beego/hooks.go @@ -6,6 +6,8 @@ import ( "net/http" "path/filepath" + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/session" ) @@ -30,6 +32,8 @@ func registerDefaultErrorHandler() error { "502": badGateway, "503": serviceUnavailable, "504": gatewayTimeout, + "417": invalidxsrf, + "422": missingxsrf, } for e, h := range m { if _, ok := ErrorMaps[e]; !ok { @@ -43,23 +47,25 @@ func registerSession() error { if BConfig.WebConfig.Session.SessionOn { var err error sessionConfig := AppConfig.String("sessionConfig") + conf := new(session.ManagerConfig) if sessionConfig == "" { - conf := map[string]interface{}{ - "cookieName": BConfig.WebConfig.Session.SessionName, - "gclifetime": BConfig.WebConfig.Session.SessionGCMaxLifetime, - "providerConfig": filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig), - "secure": BConfig.Listen.EnableHTTPS, - "enableSetCookie": BConfig.WebConfig.Session.SessionAutoSetCookie, - "domain": BConfig.WebConfig.Session.SessionDomain, - "cookieLifeTime": BConfig.WebConfig.Session.SessionCookieLifeTime, - } - confBytes, err := json.Marshal(conf) - if err != nil { + conf.CookieName = BConfig.WebConfig.Session.SessionName + conf.EnableSetCookie = BConfig.WebConfig.Session.SessionAutoSetCookie + conf.Gclifetime = BConfig.WebConfig.Session.SessionGCMaxLifetime + conf.Secure = BConfig.Listen.EnableHTTPS + conf.CookieLifeTime = BConfig.WebConfig.Session.SessionCookieLifeTime + conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig) + conf.DisableHTTPOnly = BConfig.WebConfig.Session.SessionDisableHTTPOnly + conf.Domain = BConfig.WebConfig.Session.SessionDomain + conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader + conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader + conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery + } else { + if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil { return err } - sessionConfig = string(confBytes) } - if GlobalSessions, err = session.NewManager(BConfig.WebConfig.Session.SessionProvider, sessionConfig); err != nil { + if GlobalSessions, err = session.NewManager(BConfig.WebConfig.Session.SessionProvider, conf); err != nil { return err } go GlobalSessions.GC() @@ -68,26 +74,30 @@ func registerSession() error { } func registerTemplate() error { - if err := BuildTemplate(BConfig.WebConfig.ViewsPath); err != nil { + defer lockViewPaths() + if err := AddViewPath(BConfig.WebConfig.ViewsPath); err != nil { if BConfig.RunMode == DEV { - Warn(err) + logs.Warn(err) } return err } return nil } -func registerDocs() error { - if BConfig.WebConfig.EnableDocs { - Get("/docs", serverDocs) - Get("/docs/*", serverDocs) - } - return nil -} - func registerAdmin() error { if BConfig.Listen.EnableAdmin { go beeAdminApp.Run() } return nil } + +func registerGzip() error { + if BConfig.EnableGzip { + context.InitGzip( + AppConfig.DefaultInt("gzipMinLength", -1), + AppConfig.DefaultInt("gzipCompressLevel", -1), + AppConfig.DefaultStrings("includedMethods", []string{"GET"}), + ) + } + return nil +} diff --git a/src/vendor/github.com/astaxie/beego/httplib/README.md b/src/vendor/github.com/astaxie/beego/httplib/README.md index 6a72cf7cf..97df8e6b9 100644 --- a/src/vendor/github.com/astaxie/beego/httplib/README.md +++ b/src/vendor/github.com/astaxie/beego/httplib/README.md @@ -32,7 +32,7 @@ The default timeout is `60` seconds, function prototype: SetTimeout(connectTimeout, readWriteTimeout time.Duration) -Exmaple: +Example: // GET httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second) diff --git a/src/vendor/github.com/astaxie/beego/httplib/httplib.go b/src/vendor/github.com/astaxie/beego/httplib/httplib.go index 769841226..4fd572d68 100644 --- a/src/vendor/github.com/astaxie/beego/httplib/httplib.go +++ b/src/vendor/github.com/astaxie/beego/httplib/httplib.go @@ -136,9 +136,11 @@ type BeegoHTTPSettings struct { TLSClientConfig *tls.Config Proxy func(*http.Request) (*url.URL, error) Transport http.RoundTripper + CheckRedirect func(req *http.Request, via []*http.Request) error EnableCookie bool Gzip bool DumpBody bool + Retries int // if set to -1 means will retry forever } // BeegoHTTPRequest provides more useful methods for requesting one url than http.Request. @@ -188,6 +190,15 @@ func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest { return b } +// Retries sets Retries times. +// default is 0 means no retried. +// -1 means retried forever. +// others means retried times. +func (b *BeegoHTTPRequest) Retries(times int) *BeegoHTTPRequest { + b.setting.Retries = times + return b +} + // DumpBody setting whether need to Dump the Body. func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest { b.setting.DumpBody = isdump @@ -265,6 +276,15 @@ func (b *BeegoHTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) return b } +// SetCheckRedirect specifies the policy for handling redirects. +// +// If CheckRedirect is nil, the Client uses its default policy, +// which is to stop after 10 consecutive requests. +func (b *BeegoHTTPRequest) SetCheckRedirect(redirect func(req *http.Request, via []*http.Request) error) *BeegoHTTPRequest { + b.setting.CheckRedirect = redirect + return b +} + // Param adds query param in to request. // params build query string as ?key1=value1&key2=value2... func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest { @@ -315,7 +335,7 @@ func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) func (b *BeegoHTTPRequest) buildURL(paramBody string) { // build GET url with query string if b.req.Method == "GET" && len(paramBody) > 0 { - if strings.Index(b.url, "?") != -1 { + if strings.Contains(b.url, "?") { b.url += "&" + paramBody } else { b.url = b.url + "?" + paramBody @@ -324,7 +344,7 @@ func (b *BeegoHTTPRequest) buildURL(paramBody string) { } // build POST/PUT/PATCH url and body - if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && b.req.Body == nil { + if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH" || b.req.Method == "DELETE") && b.req.Body == nil { // with files if len(b.files) > 0 { pr, pw := io.Pipe() @@ -380,7 +400,7 @@ func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) { } // DoRequest will do the client.Do -func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) { +func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) { var paramBody string if len(b.params) > 0 { var buf bytes.Buffer @@ -409,9 +429,10 @@ func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) { if trans == nil { // create default transport trans = &http.Transport{ - TLSClientConfig: b.setting.TLSClientConfig, - Proxy: b.setting.Proxy, - Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout), + TLSClientConfig: b.setting.TLSClientConfig, + Proxy: b.setting.Proxy, + Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout), + MaxIdleConnsPerHost: -1, } } else { // if b.transport is *http.Transport then set the settings. @@ -445,6 +466,10 @@ func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) { b.req.Header.Set("User-Agent", b.setting.UserAgent) } + if b.setting.CheckRedirect != nil { + client.CheckRedirect = b.setting.CheckRedirect + } + if b.setting.ShowDebug { dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody) if err != nil { @@ -452,7 +477,16 @@ func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) { } b.dump = dump } - return client.Do(b.req) + // retries default value is 0, it will run once. + // retries equal to -1, it will run forever until success + // retries is setted, it will retries fixed times. + for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ { + resp, err = client.Do(b.req) + if err == nil { + break + } + } + return resp, err } // String returns the body string in response. @@ -486,9 +520,9 @@ func (b *BeegoHTTPRequest) Bytes() ([]byte, error) { return nil, err } b.body, err = ioutil.ReadAll(reader) - } else { - b.body, err = ioutil.ReadAll(resp.Body) + return b.body, err } + b.body, err = ioutil.ReadAll(resp.Body) return b.body, err } diff --git a/src/vendor/github.com/astaxie/beego/httplib/httplib_test.go b/src/vendor/github.com/astaxie/beego/httplib/httplib_test.go index 058150547..32d3e7f68 100644 --- a/src/vendor/github.com/astaxie/beego/httplib/httplib_test.go +++ b/src/vendor/github.com/astaxie/beego/httplib/httplib_test.go @@ -102,6 +102,14 @@ func TestSimpleDelete(t *testing.T) { t.Log(str) } +func TestSimpleDeleteParam(t *testing.T) { + str, err := Delete("http://httpbin.org/delete").Param("key", "val").String() + if err != nil { + t.Fatal(err) + } + t.Log(str) +} + func TestWithCookie(t *testing.T) { v := "smallfish" str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String() diff --git a/src/vendor/github.com/astaxie/beego/log.go b/src/vendor/github.com/astaxie/beego/log.go index 46ec57dd8..e9412f920 100644 --- a/src/vendor/github.com/astaxie/beego/log.go +++ b/src/vendor/github.com/astaxie/beego/log.go @@ -33,82 +33,77 @@ const ( ) // BeeLogger references the used application logger. -var BeeLogger = logs.NewLogger(100) +var BeeLogger = logs.GetBeeLogger() // SetLevel sets the global log level used by the simple logger. func SetLevel(l int) { - BeeLogger.SetLevel(l) + logs.SetLevel(l) } // SetLogFuncCall set the CallDepth, default is 3 func SetLogFuncCall(b bool) { - BeeLogger.EnableFuncCallDepth(b) - BeeLogger.SetLogFuncCallDepth(3) + logs.SetLogFuncCall(b) } // SetLogger sets a new logger. func SetLogger(adaptername string, config string) error { - err := BeeLogger.SetLogger(adaptername, config) - if err != nil { - return err - } - return nil + return logs.SetLogger(adaptername, config) } // Emergency logs a message at emergency level. func Emergency(v ...interface{}) { - BeeLogger.Emergency(generateFmtStr(len(v)), v...) + logs.Emergency(generateFmtStr(len(v)), v...) } // Alert logs a message at alert level. func Alert(v ...interface{}) { - BeeLogger.Alert(generateFmtStr(len(v)), v...) + logs.Alert(generateFmtStr(len(v)), v...) } // Critical logs a message at critical level. func Critical(v ...interface{}) { - BeeLogger.Critical(generateFmtStr(len(v)), v...) + logs.Critical(generateFmtStr(len(v)), v...) } // Error logs a message at error level. func Error(v ...interface{}) { - BeeLogger.Error(generateFmtStr(len(v)), v...) + logs.Error(generateFmtStr(len(v)), v...) } // Warning logs a message at warning level. func Warning(v ...interface{}) { - BeeLogger.Warning(generateFmtStr(len(v)), v...) + logs.Warning(generateFmtStr(len(v)), v...) } // Warn compatibility alias for Warning() func Warn(v ...interface{}) { - BeeLogger.Warn(generateFmtStr(len(v)), v...) + logs.Warn(generateFmtStr(len(v)), v...) } // Notice logs a message at notice level. func Notice(v ...interface{}) { - BeeLogger.Notice(generateFmtStr(len(v)), v...) + logs.Notice(generateFmtStr(len(v)), v...) } // Informational logs a message at info level. func Informational(v ...interface{}) { - BeeLogger.Informational(generateFmtStr(len(v)), v...) + logs.Informational(generateFmtStr(len(v)), v...) } // Info compatibility alias for Warning() func Info(v ...interface{}) { - BeeLogger.Info(generateFmtStr(len(v)), v...) + logs.Info(generateFmtStr(len(v)), v...) } // Debug logs a message at debug level. func Debug(v ...interface{}) { - BeeLogger.Debug(generateFmtStr(len(v)), v...) + logs.Debug(generateFmtStr(len(v)), v...) } // Trace logs a message at trace level. // compatibility alias for Warning() func Trace(v ...interface{}) { - BeeLogger.Trace(generateFmtStr(len(v)), v...) + logs.Trace(generateFmtStr(len(v)), v...) } func generateFmtStr(n int) string { diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/alils.go b/src/vendor/github.com/astaxie/beego/logs/alils/alils.go new file mode 100644 index 000000000..867ff4cb5 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/alils.go @@ -0,0 +1,186 @@ +package alils + +import ( + "encoding/json" + "strings" + "sync" + "time" + + "github.com/astaxie/beego/logs" + "github.com/gogo/protobuf/proto" +) + +const ( + // CacheSize set the flush size + CacheSize int = 64 + // Delimiter define the topic delimiter + Delimiter string = "##" +) + +// Config is the Config for Ali Log +type Config struct { + Project string `json:"project"` + Endpoint string `json:"endpoint"` + KeyID string `json:"key_id"` + KeySecret string `json:"key_secret"` + LogStore string `json:"log_store"` + Topics []string `json:"topics"` + Source string `json:"source"` + Level int `json:"level"` + FlushWhen int `json:"flush_when"` +} + +// aliLSWriter implements LoggerInterface. +// it writes messages in keep-live tcp connection. +type aliLSWriter struct { + store *LogStore + group []*LogGroup + withMap bool + groupMap map[string]*LogGroup + lock *sync.Mutex + Config +} + +// NewAliLS create a new Logger +func NewAliLS() logs.Logger { + alils := new(aliLSWriter) + alils.Level = logs.LevelTrace + return alils +} + +// Init parse config and init struct +func (c *aliLSWriter) Init(jsonConfig string) (err error) { + + json.Unmarshal([]byte(jsonConfig), c) + + if c.FlushWhen > CacheSize { + c.FlushWhen = CacheSize + } + + prj := &LogProject{ + Name: c.Project, + Endpoint: c.Endpoint, + AccessKeyID: c.KeyID, + AccessKeySecret: c.KeySecret, + } + + c.store, err = prj.GetLogStore(c.LogStore) + if err != nil { + return err + } + + // Create default Log Group + c.group = append(c.group, &LogGroup{ + Topic: proto.String(""), + Source: proto.String(c.Source), + Logs: make([]*Log, 0, c.FlushWhen), + }) + + // Create other Log Group + c.groupMap = make(map[string]*LogGroup) + for _, topic := range c.Topics { + + lg := &LogGroup{ + Topic: proto.String(topic), + Source: proto.String(c.Source), + Logs: make([]*Log, 0, c.FlushWhen), + } + + c.group = append(c.group, lg) + c.groupMap[topic] = lg + } + + if len(c.group) == 1 { + c.withMap = false + } else { + c.withMap = true + } + + c.lock = &sync.Mutex{} + + return nil +} + +// WriteMsg write message in connection. +// if connection is down, try to re-connect. +func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error) { + + if level > c.Level { + return nil + } + + var topic string + var content string + var lg *LogGroup + if c.withMap { + + // Topic,LogGroup + strs := strings.SplitN(msg, Delimiter, 2) + if len(strs) == 2 { + pos := strings.LastIndex(strs[0], " ") + topic = strs[0][pos+1 : len(strs[0])] + content = strs[0][0:pos] + strs[1] + lg = c.groupMap[topic] + } + + // send to empty Topic + if lg == nil { + content = msg + lg = c.group[0] + } + } else { + content = msg + lg = c.group[0] + } + + c1 := &LogContent{ + Key: proto.String("msg"), + Value: proto.String(content), + } + + l := &Log{ + Time: proto.Uint32(uint32(when.Unix())), + Contents: []*LogContent{ + c1, + }, + } + + c.lock.Lock() + lg.Logs = append(lg.Logs, l) + c.lock.Unlock() + + if len(lg.Logs) >= c.FlushWhen { + c.flush(lg) + } + + return nil +} + +// Flush implementing method. empty. +func (c *aliLSWriter) Flush() { + + // flush all group + for _, lg := range c.group { + c.flush(lg) + } +} + +// Destroy destroy connection writer and close tcp listener. +func (c *aliLSWriter) Destroy() { +} + +func (c *aliLSWriter) flush(lg *LogGroup) { + + c.lock.Lock() + defer c.lock.Unlock() + err := c.store.PutLogs(lg) + if err != nil { + return + } + + lg.Logs = make([]*Log, 0, c.FlushWhen) +} + +func init() { + logs.Register(logs.AdapterAliLS, NewAliLS) +} diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/config.go b/src/vendor/github.com/astaxie/beego/logs/alils/config.go new file mode 100755 index 000000000..e8c24448f --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/config.go @@ -0,0 +1,13 @@ +package alils + +const ( + version = "0.5.0" // SDK version + signatureMethod = "hmac-sha1" // Signature method + + // OffsetNewest stands for the log head offset, i.e. the offset that will be + // assigned to the next message that will be produced to the shard. + OffsetNewest = "end" + // OffsetOldest stands for the oldest offset available on the logstore for a + // shard. + OffsetOldest = "begin" +) diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/log.pb.go b/src/vendor/github.com/astaxie/beego/logs/alils/log.pb.go new file mode 100755 index 000000000..601b0d78d --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/log.pb.go @@ -0,0 +1,1038 @@ +package alils + +import ( + "fmt" + "io" + "math" + + "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +var ( + // ErrInvalidLengthLog invalid proto + ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling") + // ErrIntOverflowLog overflow + ErrIntOverflowLog = fmt.Errorf("proto: integer overflow") +) + +// Log define the proto Log +type Log struct { + Time *uint32 `protobuf:"varint,1,req,name=Time" json:"Time,omitempty"` + Contents []*LogContent `protobuf:"bytes,2,rep,name=Contents" json:"Contents,omitempty"` + XXXUnrecognized []byte `json:"-"` +} + +// Reset the Log +func (m *Log) Reset() { *m = Log{} } + +// String return the Compact Log +func (m *Log) String() string { return proto.CompactTextString(m) } + +// ProtoMessage not implemented +func (*Log) ProtoMessage() {} + +// GetTime return the Log's Time +func (m *Log) GetTime() uint32 { + if m != nil && m.Time != nil { + return *m.Time + } + return 0 +} + +// GetContents return the Log's Contents +func (m *Log) GetContents() []*LogContent { + if m != nil { + return m.Contents + } + return nil +} + +// LogContent define the Log content struct +type LogContent struct { + Key *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"` + Value *string `protobuf:"bytes,2,req,name=Value" json:"Value,omitempty"` + XXXUnrecognized []byte `json:"-"` +} + +// Reset LogContent +func (m *LogContent) Reset() { *m = LogContent{} } + +// String return the compact text +func (m *LogContent) String() string { return proto.CompactTextString(m) } + +// ProtoMessage not implemented +func (*LogContent) ProtoMessage() {} + +// GetKey return the Key +func (m *LogContent) GetKey() string { + if m != nil && m.Key != nil { + return *m.Key + } + return "" +} + +// GetValue return the Value +func (m *LogContent) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +// LogGroup define the logs struct +type LogGroup struct { + Logs []*Log `protobuf:"bytes,1,rep,name=Logs" json:"Logs,omitempty"` + Reserved *string `protobuf:"bytes,2,opt,name=Reserved" json:"Reserved,omitempty"` + Topic *string `protobuf:"bytes,3,opt,name=Topic" json:"Topic,omitempty"` + Source *string `protobuf:"bytes,4,opt,name=Source" json:"Source,omitempty"` + XXXUnrecognized []byte `json:"-"` +} + +// Reset LogGroup +func (m *LogGroup) Reset() { *m = LogGroup{} } + +// String return the compact text +func (m *LogGroup) String() string { return proto.CompactTextString(m) } + +// ProtoMessage not implemented +func (*LogGroup) ProtoMessage() {} + +// GetLogs return the loggroup logs +func (m *LogGroup) GetLogs() []*Log { + if m != nil { + return m.Logs + } + return nil +} + +// GetReserved return Reserved +func (m *LogGroup) GetReserved() string { + if m != nil && m.Reserved != nil { + return *m.Reserved + } + return "" +} + +// GetTopic return Topic +func (m *LogGroup) GetTopic() string { + if m != nil && m.Topic != nil { + return *m.Topic + } + return "" +} + +// GetSource return Source +func (m *LogGroup) GetSource() string { + if m != nil && m.Source != nil { + return *m.Source + } + return "" +} + +// LogGroupList define the LogGroups +type LogGroupList struct { + LogGroups []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"` + XXXUnrecognized []byte `json:"-"` +} + +// Reset LogGroupList +func (m *LogGroupList) Reset() { *m = LogGroupList{} } + +// String return compact text +func (m *LogGroupList) String() string { return proto.CompactTextString(m) } + +// ProtoMessage not implemented +func (*LogGroupList) ProtoMessage() {} + +// GetLogGroups return the LogGroups +func (m *LogGroupList) GetLogGroups() []*LogGroup { + if m != nil { + return m.LogGroups + } + return nil +} + +// Marshal the logs to byte slice +func (m *Log) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +// MarshalTo data +func (m *Log) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Time == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time") + } + data[i] = 0x8 + i++ + i = encodeVarintLog(data, i, uint64(*m.Time)) + if len(m.Contents) > 0 { + for _, msg := range m.Contents { + data[i] = 0x12 + i++ + i = encodeVarintLog(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.XXXUnrecognized != nil { + i += copy(data[i:], m.XXXUnrecognized) + } + return i, nil +} + +// Marshal LogContent +func (m *LogContent) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +// MarshalTo logcontent to data +func (m *LogContent) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Key == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key") + } + data[i] = 0xa + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Key))) + i += copy(data[i:], *m.Key) + + if m.Value == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value") + } + data[i] = 0x12 + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Value))) + i += copy(data[i:], *m.Value) + if m.XXXUnrecognized != nil { + i += copy(data[i:], m.XXXUnrecognized) + } + return i, nil +} + +// Marshal LogGroup +func (m *LogGroup) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +// MarshalTo LogGroup to data +func (m *LogGroup) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Logs) > 0 { + for _, msg := range m.Logs { + data[i] = 0xa + i++ + i = encodeVarintLog(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.Reserved != nil { + data[i] = 0x12 + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Reserved))) + i += copy(data[i:], *m.Reserved) + } + if m.Topic != nil { + data[i] = 0x1a + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Topic))) + i += copy(data[i:], *m.Topic) + } + if m.Source != nil { + data[i] = 0x22 + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Source))) + i += copy(data[i:], *m.Source) + } + if m.XXXUnrecognized != nil { + i += copy(data[i:], m.XXXUnrecognized) + } + return i, nil +} + +// Marshal LogGroupList +func (m *LogGroupList) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +// MarshalTo LogGroupList to data +func (m *LogGroupList) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.LogGroups) > 0 { + for _, msg := range m.LogGroups { + data[i] = 0xa + i++ + i = encodeVarintLog(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.XXXUnrecognized != nil { + i += copy(data[i:], m.XXXUnrecognized) + } + return i, nil +} + +func encodeFixed64Log(data []byte, offset int, v uint64) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + data[offset+4] = uint8(v >> 32) + data[offset+5] = uint8(v >> 40) + data[offset+6] = uint8(v >> 48) + data[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Log(data []byte, offset int, v uint32) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintLog(data []byte, offset int, v uint64) int { + for v >= 1<<7 { + data[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + data[offset] = uint8(v) + return offset + 1 +} + +// Size return the log's size +func (m *Log) Size() (n int) { + var l int + _ = l + if m.Time != nil { + n += 1 + sovLog(uint64(*m.Time)) + } + if len(m.Contents) > 0 { + for _, e := range m.Contents { + l = e.Size() + n += 1 + l + sovLog(uint64(l)) + } + } + if m.XXXUnrecognized != nil { + n += len(m.XXXUnrecognized) + } + return n +} + +// Size return LogContent size based on Key and Value +func (m *LogContent) Size() (n int) { + var l int + _ = l + if m.Key != nil { + l = len(*m.Key) + n += 1 + l + sovLog(uint64(l)) + } + if m.Value != nil { + l = len(*m.Value) + n += 1 + l + sovLog(uint64(l)) + } + if m.XXXUnrecognized != nil { + n += len(m.XXXUnrecognized) + } + return n +} + +// Size return LogGroup size based on Logs +func (m *LogGroup) Size() (n int) { + var l int + _ = l + if len(m.Logs) > 0 { + for _, e := range m.Logs { + l = e.Size() + n += 1 + l + sovLog(uint64(l)) + } + } + if m.Reserved != nil { + l = len(*m.Reserved) + n += 1 + l + sovLog(uint64(l)) + } + if m.Topic != nil { + l = len(*m.Topic) + n += 1 + l + sovLog(uint64(l)) + } + if m.Source != nil { + l = len(*m.Source) + n += 1 + l + sovLog(uint64(l)) + } + if m.XXXUnrecognized != nil { + n += len(m.XXXUnrecognized) + } + return n +} + +// Size return LogGroupList size +func (m *LogGroupList) Size() (n int) { + var l int + _ = l + if len(m.LogGroups) > 0 { + for _, e := range m.LogGroups { + l = e.Size() + n += 1 + l + sovLog(uint64(l)) + } + } + if m.XXXUnrecognized != nil { + n += len(m.XXXUnrecognized) + } + return n +} + +func sovLog(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozLog(x uint64) (n int) { + return sovLog((x << 1) ^ (x >> 63)) +} + +// Unmarshal data to log +func (m *Log) Unmarshal(data []byte) error { + var hasFields [1]uint64 + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Log: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Log: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + v |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Time = &v + hasFields[0] |= uint64(0x00000001) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Contents", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Contents = append(m.Contents, &LogContent{}) + if err := m.Contents[len(m.Contents)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLog(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLog + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + if hasFields[0]&uint64(0x00000001) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time") + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} + +// Unmarshal data to LogContent +func (m *LogContent) Unmarshal(data []byte) error { + var hasFields [1]uint64 + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Content: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Key = &s + iNdEx = postIndex + hasFields[0] |= uint64(0x00000001) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Value = &s + iNdEx = postIndex + hasFields[0] |= uint64(0x00000002) + default: + iNdEx = preIndex + skippy, err := skipLog(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLog + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + if hasFields[0]&uint64(0x00000001) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key") + } + if hasFields[0]&uint64(0x00000002) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value") + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} + +// Unmarshal data to LogGroup +func (m *LogGroup) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LogGroup: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogGroup: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Logs = append(m.Logs, &Log{}) + if err := m.Logs[len(m.Logs)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reserved", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Reserved = &s + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Topic", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Topic = &s + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Source = &s + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLog(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLog + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} + +// Unmarshal data to LogGroupList +func (m *LogGroupList) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LogGroupList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogGroupList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LogGroups", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LogGroups = append(m.LogGroups, &LogGroup{}) + if err := m.LogGroups[len(m.LogGroups)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLog(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLog + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} + +func skipLog(data []byte) (n int, err error) { + l := len(data) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLog + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLog + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if data[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLog + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthLog + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLog + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipLog(data[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/log_config.go b/src/vendor/github.com/astaxie/beego/logs/alils/log_config.go new file mode 100755 index 000000000..e8564efbd --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/log_config.go @@ -0,0 +1,42 @@ +package alils + +// InputDetail define log detail +type InputDetail struct { + LogType string `json:"logType"` + LogPath string `json:"logPath"` + FilePattern string `json:"filePattern"` + LocalStorage bool `json:"localStorage"` + TimeFormat string `json:"timeFormat"` + LogBeginRegex string `json:"logBeginRegex"` + Regex string `json:"regex"` + Keys []string `json:"key"` + FilterKeys []string `json:"filterKey"` + FilterRegex []string `json:"filterRegex"` + TopicFormat string `json:"topicFormat"` +} + +// OutputDetail define the output detail +type OutputDetail struct { + Endpoint string `json:"endpoint"` + LogStoreName string `json:"logstoreName"` +} + +// LogConfig define Log Config +type LogConfig struct { + Name string `json:"configName"` + InputType string `json:"inputType"` + InputDetail InputDetail `json:"inputDetail"` + OutputType string `json:"outputType"` + OutputDetail OutputDetail `json:"outputDetail"` + + CreateTime uint32 + LastModifyTime uint32 + + project *LogProject +} + +// GetAppliedMachineGroup returns applied machine group of this config. +func (c *LogConfig) GetAppliedMachineGroup(confName string) (groupNames []string, err error) { + groupNames, err = c.project.GetAppliedMachineGroups(c.Name) + return +} diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/log_project.go b/src/vendor/github.com/astaxie/beego/logs/alils/log_project.go new file mode 100755 index 000000000..59db8cbf7 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/log_project.go @@ -0,0 +1,819 @@ +/* +Package alils implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS). + +For more description about SLS, please read this article: +http://gitlab.alibaba-inc.com/sls/doc. +*/ +package alils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" +) + +// Error message in SLS HTTP response. +type errorMessage struct { + Code string `json:"errorCode"` + Message string `json:"errorMessage"` +} + +// LogProject Define the Ali Project detail +type LogProject struct { + Name string // Project name + Endpoint string // IP or hostname of SLS endpoint + AccessKeyID string + AccessKeySecret string +} + +// NewLogProject creates a new SLS project. +func NewLogProject(name, endpoint, AccessKeyID, accessKeySecret string) (p *LogProject, err error) { + p = &LogProject{ + Name: name, + Endpoint: endpoint, + AccessKeyID: AccessKeyID, + AccessKeySecret: accessKeySecret, + } + return p, nil +} + +// ListLogStore returns all logstore names of project p. +func (p *LogProject) ListLogStore() (storeNames []string, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/logstores") + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to list logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + Count int + LogStores []string + } + body := &Body{} + + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + storeNames = body.LogStores + + return +} + +// GetLogStore returns logstore according by logstore name. +func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "GET", "/logstores/"+name, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + s = &LogStore{} + err = json.Unmarshal(buf, s) + if err != nil { + return + } + s.project = p + return +} + +// CreateLogStore creates a new logstore in SLS, +// where name is logstore name, +// and ttl is time-to-live(in day) of logs, +// and shardCnt is the number of shards. +func (p *LogProject) CreateLogStore(name string, ttl, shardCnt int) (err error) { + + type Body struct { + Name string `json:"logstoreName"` + TTL int `json:"ttl"` + ShardCount int `json:"shardCount"` + } + + store := &Body{ + Name: name, + TTL: ttl, + ShardCount: shardCnt, + } + + body, err := json.Marshal(store) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "POST", "/logstores", h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to create logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// DeleteLogStore deletes a logstore according by logstore name. +func (p *LogProject) DeleteLogStore(name string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "DELETE", "/logstores/"+name, h, nil) + if err != nil { + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// UpdateLogStore updates a logstore according by logstore name, +// obviously we can't modify the logstore name itself. +func (p *LogProject) UpdateLogStore(name string, ttl, shardCnt int) (err error) { + + type Body struct { + Name string `json:"logstoreName"` + TTL int `json:"ttl"` + ShardCount int `json:"shardCount"` + } + + store := &Body{ + Name: name, + TTL: ttl, + ShardCount: shardCnt, + } + + body, err := json.Marshal(store) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "PUT", "/logstores", h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to update logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// ListMachineGroup returns machine group name list and the total number of machine groups. +// The offset starts from 0 and the size is the max number of machine groups could be returned. +func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + if size <= 0 { + size = 500 + } + + uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size) + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to list machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + MachineGroups []string + Count int + Total int + } + body := &Body{} + + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + m = body.MachineGroups + total = body.Total + + return +} + +// GetMachineGroup retruns machine group according by machine group name. +func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "GET", "/machinegroups/"+name, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get machine group:%v", name) + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + m = &MachineGroup{} + err = json.Unmarshal(buf, m) + if err != nil { + return + } + m.project = p + return +} + +// CreateMachineGroup creates a new machine group in SLS. +func (p *LogProject) CreateMachineGroup(m *MachineGroup) (err error) { + + body, err := json.Marshal(m) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "POST", "/machinegroups", h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to create machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// UpdateMachineGroup updates a machine group. +func (p *LogProject) UpdateMachineGroup(m *MachineGroup) (err error) { + + body, err := json.Marshal(m) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "PUT", "/machinegroups/"+m.Name, h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to update machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// DeleteMachineGroup deletes machine group according machine group name. +func (p *LogProject) DeleteMachineGroup(name string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "DELETE", "/machinegroups/"+name, h, nil) + if err != nil { + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// ListConfig returns config names list and the total number of configs. +// The offset starts from 0 and the size is the max number of configs could be returned. +func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + if size <= 0 { + size = 100 + } + + uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size) + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + Total int + Configs []string + } + body := &Body{} + + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + cfgNames = body.Configs + total = body.Total + return +} + +// GetConfig returns config according by config name. +func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "GET", "/configs/"+name, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete config") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + c = &LogConfig{} + err = json.Unmarshal(buf, c) + if err != nil { + return + } + c.project = p + return +} + +// UpdateConfig updates a config. +func (p *LogProject) UpdateConfig(c *LogConfig) (err error) { + + body, err := json.Marshal(c) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "PUT", "/configs/"+c.Name, h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to update config") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// CreateConfig creates a new config in SLS. +func (p *LogProject) CreateConfig(c *LogConfig) (err error) { + + body, err := json.Marshal(c) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "POST", "/configs", h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to update config") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// DeleteConfig deletes a config according by config name. +func (p *LogProject) DeleteConfig(name string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "DELETE", "/configs/"+name, h, nil) + if err != nil { + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete config") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// GetAppliedMachineGroups returns applied machine group names list according config name. +func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/configs/%v/machinegroups", confName) + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get applied machine groups") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + Count int + Machinegroups []string + } + + body := &Body{} + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + groupNames = body.Machinegroups + return +} + +// GetAppliedConfigs returns applied config names list according machine group name groupName. +func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/machinegroups/%v/configs", groupName) + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to applied configs") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Cfg struct { + Count int `json:"count"` + Configs []string `json:"configs"` + } + + body := &Cfg{} + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + confNames = body.Configs + return +} + +// ApplyConfigToMachineGroup applies config to machine group. +func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName) + r, err := request(p, "PUT", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to apply config to machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// RemoveConfigFromMachineGroup removes config from machine group. +func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName) + r, err := request(p, "DELETE", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to remove config from machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/log_store.go b/src/vendor/github.com/astaxie/beego/logs/alils/log_store.go new file mode 100755 index 000000000..fa5027364 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/log_store.go @@ -0,0 +1,271 @@ +package alils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" + "strconv" + + lz4 "github.com/cloudflare/golz4" + "github.com/gogo/protobuf/proto" +) + +// LogStore Store the logs +type LogStore struct { + Name string `json:"logstoreName"` + TTL int + ShardCount int + + CreateTime uint32 + LastModifyTime uint32 + + project *LogProject +} + +// Shard define the Log Shard +type Shard struct { + ShardID int `json:"shardID"` +} + +// ListShards returns shard id list of this logstore. +func (s *LogStore) ListShards() (shardIDs []int, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/logstores/%v/shards", s.Name) + r, err := request(s.project, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to list logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + var shards []*Shard + err = json.Unmarshal(buf, &shards) + if err != nil { + return + } + + for _, v := range shards { + shardIDs = append(shardIDs, v.ShardID) + } + return +} + +// PutLogs put logs into logstore. +// The callers should transform user logs into LogGroup. +func (s *LogStore) PutLogs(lg *LogGroup) (err error) { + body, err := proto.Marshal(lg) + if err != nil { + return + } + + // Compresse body with lz4 + out := make([]byte, lz4.CompressBound(body)) + n, err := lz4.Compress(body, out) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-compresstype": "lz4", + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/x-protobuf", + } + + uri := fmt.Sprintf("/logstores/%v", s.Name) + r, err := request(s.project, "POST", uri, h, out[:n]) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to put logs") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// GetCursor gets log cursor of one shard specified by shardID. +// The from can be in three form: a) unix timestamp in seccond, b) "begin", c) "end". +// For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore +func (s *LogStore) GetCursor(shardID int, from string) (cursor string, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v", + s.Name, shardID, from) + + r, err := request(s.project, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get cursor") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + Cursor string + } + body := &Body{} + + err = json.Unmarshal(buf, body) + if err != nil { + return + } + cursor = body.Cursor + return +} + +// GetLogsBytes gets logs binary data from shard specified by shardID according cursor. +// The logGroupMaxCount is the max number of logGroup could be returned. +// The nextCursor is the next curosr can be used to read logs at next time. +func (s *LogStore) GetLogsBytes(shardID int, cursor string, + logGroupMaxCount int) (out []byte, nextCursor string, err error) { + + h := map[string]string{ + "x-sls-bodyrawsize": "0", + "Accept": "application/x-protobuf", + "Accept-Encoding": "lz4", + } + + uri := fmt.Sprintf("/logstores/%v/shards/%v?type=logs&cursor=%v&count=%v", + s.Name, shardID, cursor, logGroupMaxCount) + + r, err := request(s.project, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get cursor") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + v, ok := r.Header["X-Sls-Compresstype"] + if !ok || len(v) == 0 { + err = fmt.Errorf("can't find 'x-sls-compresstype' header") + return + } + if v[0] != "lz4" { + err = fmt.Errorf("unexpected compress type:%v", v[0]) + return + } + + v, ok = r.Header["X-Sls-Cursor"] + if !ok || len(v) == 0 { + err = fmt.Errorf("can't find 'x-sls-cursor' header") + return + } + nextCursor = v[0] + + v, ok = r.Header["X-Sls-Bodyrawsize"] + if !ok || len(v) == 0 { + err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header") + return + } + bodyRawSize, err := strconv.Atoi(v[0]) + if err != nil { + return + } + + out = make([]byte, bodyRawSize) + err = lz4.Uncompress(buf, out) + if err != nil { + return + } + + return +} + +// LogsBytesDecode decodes logs binary data retruned by GetLogsBytes API +func LogsBytesDecode(data []byte) (gl *LogGroupList, err error) { + + gl = &LogGroupList{} + err = proto.Unmarshal(data, gl) + if err != nil { + return + } + + return +} + +// GetLogs gets logs from shard specified by shardID according cursor. +// The logGroupMaxCount is the max number of logGroup could be returned. +// The nextCursor is the next curosr can be used to read logs at next time. +func (s *LogStore) GetLogs(shardID int, cursor string, + logGroupMaxCount int) (gl *LogGroupList, nextCursor string, err error) { + + out, nextCursor, err := s.GetLogsBytes(shardID, cursor, logGroupMaxCount) + if err != nil { + return + } + + gl, err = LogsBytesDecode(out) + if err != nil { + return + } + + return +} diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/machine_group.go b/src/vendor/github.com/astaxie/beego/logs/alils/machine_group.go new file mode 100755 index 000000000..b6c69a141 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/machine_group.go @@ -0,0 +1,91 @@ +package alils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" +) + +// MachineGroupAttribute define the Attribute +type MachineGroupAttribute struct { + ExternalName string `json:"externalName"` + TopicName string `json:"groupTopic"` +} + +// MachineGroup define the machine Group +type MachineGroup struct { + Name string `json:"groupName"` + Type string `json:"groupType"` + MachineIDType string `json:"machineIdentifyType"` + MachineIDList []string `json:"machineList"` + + Attribute MachineGroupAttribute `json:"groupAttribute"` + + CreateTime uint32 + LastModifyTime uint32 + + project *LogProject +} + +// Machine define the Machine +type Machine struct { + IP string + UniqueID string `json:"machine-uniqueid"` + UserdefinedID string `json:"userdefined-id"` +} + +// MachineList define the Machine List +type MachineList struct { + Total int + Machines []*Machine +} + +// ListMachines returns machine list of this machine group. +func (m *MachineGroup) ListMachines() (ms []*Machine, total int, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/machinegroups/%v/machines", m.Name) + r, err := request(m.project, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to remove config from machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + body := &MachineList{} + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + ms = body.Machines + total = body.Total + + return +} + +// GetAppliedConfigs returns applied configs of this machine group. +func (m *MachineGroup) GetAppliedConfigs() (confNames []string, err error) { + confNames, err = m.project.GetAppliedConfigs(m.Name) + return +} diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/request.go b/src/vendor/github.com/astaxie/beego/logs/alils/request.go new file mode 100755 index 000000000..50d9c43c5 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/request.go @@ -0,0 +1,62 @@ +package alils + +import ( + "bytes" + "crypto/md5" + "fmt" + "net/http" +) + +// request sends a request to SLS. +func request(project *LogProject, method, uri string, headers map[string]string, + body []byte) (resp *http.Response, err error) { + + // The caller should provide 'x-sls-bodyrawsize' header + if _, ok := headers["x-sls-bodyrawsize"]; !ok { + err = fmt.Errorf("Can't find 'x-sls-bodyrawsize' header") + return + } + + // SLS public request headers + headers["Host"] = project.Name + "." + project.Endpoint + headers["Date"] = nowRFC1123() + headers["x-sls-apiversion"] = version + headers["x-sls-signaturemethod"] = signatureMethod + if body != nil { + bodyMD5 := fmt.Sprintf("%X", md5.Sum(body)) + headers["Content-MD5"] = bodyMD5 + + if _, ok := headers["Content-Type"]; !ok { + err = fmt.Errorf("Can't find 'Content-Type' header") + return + } + } + + // Calc Authorization + // Authorization = "SLS :" + digest, err := signature(project, method, uri, headers) + if err != nil { + return + } + auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyID, digest) + headers["Authorization"] = auth + + // Initialize http request + reader := bytes.NewReader(body) + urlStr := fmt.Sprintf("http://%v.%v%v", project.Name, project.Endpoint, uri) + req, err := http.NewRequest(method, urlStr, reader) + if err != nil { + return + } + for k, v := range headers { + req.Header.Add(k, v) + } + + // Get ready to do request + resp, err = http.DefaultClient.Do(req) + if err != nil { + return + } + + return +} diff --git a/src/vendor/github.com/astaxie/beego/logs/alils/signature.go b/src/vendor/github.com/astaxie/beego/logs/alils/signature.go new file mode 100755 index 000000000..2d6113076 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/alils/signature.go @@ -0,0 +1,111 @@ +package alils + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "fmt" + "net/url" + "sort" + "strings" + "time" +) + +// GMT location +var gmtLoc = time.FixedZone("GMT", 0) + +// NowRFC1123 returns now time in RFC1123 format with GMT timezone, +// eg. "Mon, 02 Jan 2006 15:04:05 GMT". +func nowRFC1123() string { + return time.Now().In(gmtLoc).Format(time.RFC1123) +} + +// signature calculates a request's signature digest. +func signature(project *LogProject, method, uri string, + headers map[string]string) (digest string, err error) { + var contentMD5, contentType, date, canoHeaders, canoResource string + var slsHeaderKeys sort.StringSlice + + // SignString = VERB + "\n" + // + CONTENT-MD5 + "\n" + // + CONTENT-TYPE + "\n" + // + DATE + "\n" + // + CanonicalizedSLSHeaders + "\n" + // + CanonicalizedResource + + if val, ok := headers["Content-MD5"]; ok { + contentMD5 = val + } + + if val, ok := headers["Content-Type"]; ok { + contentType = val + } + + date, ok := headers["Date"] + if !ok { + err = fmt.Errorf("Can't find 'Date' header") + return + } + + // Calc CanonicalizedSLSHeaders + slsHeaders := make(map[string]string, len(headers)) + for k, v := range headers { + l := strings.TrimSpace(strings.ToLower(k)) + if strings.HasPrefix(l, "x-sls-") { + slsHeaders[l] = strings.TrimSpace(v) + slsHeaderKeys = append(slsHeaderKeys, l) + } + } + + sort.Sort(slsHeaderKeys) + for i, k := range slsHeaderKeys { + canoHeaders += k + ":" + slsHeaders[k] + if i+1 < len(slsHeaderKeys) { + canoHeaders += "\n" + } + } + + // Calc CanonicalizedResource + u, err := url.Parse(uri) + if err != nil { + return + } + + canoResource += url.QueryEscape(u.Path) + if u.RawQuery != "" { + var keys sort.StringSlice + + vals := u.Query() + for k := range vals { + keys = append(keys, k) + } + + sort.Sort(keys) + canoResource += "?" + for i, k := range keys { + if i > 0 { + canoResource += "&" + } + + for _, v := range vals[k] { + canoResource += k + "=" + v + } + } + } + + signStr := method + "\n" + + contentMD5 + "\n" + + contentType + "\n" + + date + "\n" + + canoHeaders + "\n" + + canoResource + + // Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret)) + mac := hmac.New(sha1.New, []byte(project.AccessKeySecret)) + _, err = mac.Write([]byte(signStr)) + if err != nil { + return + } + digest = base64.StdEncoding.EncodeToString(mac.Sum(nil)) + return +} diff --git a/src/vendor/github.com/astaxie/beego/logs/color.go b/src/vendor/github.com/astaxie/beego/logs/color.go new file mode 100644 index 000000000..41d23638a --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/color.go @@ -0,0 +1,28 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !windows + +package logs + +import "io" + +type ansiColorWriter struct { + w io.Writer + mode outputMode +} + +func (cw *ansiColorWriter) Write(p []byte) (int, error) { + return cw.w.Write(p) +} diff --git a/src/vendor/github.com/astaxie/beego/logs/color_windows.go b/src/vendor/github.com/astaxie/beego/logs/color_windows.go new file mode 100644 index 000000000..4e28f1888 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/color_windows.go @@ -0,0 +1,428 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build windows + +package logs + +import ( + "bytes" + "io" + "strings" + "syscall" + "unsafe" +) + +type ( + csiState int + parseResult int +) + +const ( + outsideCsiCode csiState = iota + firstCsiCode + secondCsiCode +) + +const ( + noConsole parseResult = iota + changedColor + unknown +) + +type ansiColorWriter struct { + w io.Writer + mode outputMode + state csiState + paramStartBuf bytes.Buffer + paramBuf bytes.Buffer +} + +const ( + firstCsiChar byte = '\x1b' + secondeCsiChar byte = '[' + separatorChar byte = ';' + sgrCode byte = 'm' +) + +const ( + foregroundBlue = uint16(0x0001) + foregroundGreen = uint16(0x0002) + foregroundRed = uint16(0x0004) + foregroundIntensity = uint16(0x0008) + backgroundBlue = uint16(0x0010) + backgroundGreen = uint16(0x0020) + backgroundRed = uint16(0x0040) + backgroundIntensity = uint16(0x0080) + underscore = uint16(0x8000) + + foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity + backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity +) + +const ( + ansiReset = "0" + ansiIntensityOn = "1" + ansiIntensityOff = "21" + ansiUnderlineOn = "4" + ansiUnderlineOff = "24" + ansiBlinkOn = "5" + ansiBlinkOff = "25" + + ansiForegroundBlack = "30" + ansiForegroundRed = "31" + ansiForegroundGreen = "32" + ansiForegroundYellow = "33" + ansiForegroundBlue = "34" + ansiForegroundMagenta = "35" + ansiForegroundCyan = "36" + ansiForegroundWhite = "37" + ansiForegroundDefault = "39" + + ansiBackgroundBlack = "40" + ansiBackgroundRed = "41" + ansiBackgroundGreen = "42" + ansiBackgroundYellow = "43" + ansiBackgroundBlue = "44" + ansiBackgroundMagenta = "45" + ansiBackgroundCyan = "46" + ansiBackgroundWhite = "47" + ansiBackgroundDefault = "49" + + ansiLightForegroundGray = "90" + ansiLightForegroundRed = "91" + ansiLightForegroundGreen = "92" + ansiLightForegroundYellow = "93" + ansiLightForegroundBlue = "94" + ansiLightForegroundMagenta = "95" + ansiLightForegroundCyan = "96" + ansiLightForegroundWhite = "97" + + ansiLightBackgroundGray = "100" + ansiLightBackgroundRed = "101" + ansiLightBackgroundGreen = "102" + ansiLightBackgroundYellow = "103" + ansiLightBackgroundBlue = "104" + ansiLightBackgroundMagenta = "105" + ansiLightBackgroundCyan = "106" + ansiLightBackgroundWhite = "107" +) + +type drawType int + +const ( + foreground drawType = iota + background +) + +type winColor struct { + code uint16 + drawType drawType +} + +var colorMap = map[string]winColor{ + ansiForegroundBlack: {0, foreground}, + ansiForegroundRed: {foregroundRed, foreground}, + ansiForegroundGreen: {foregroundGreen, foreground}, + ansiForegroundYellow: {foregroundRed | foregroundGreen, foreground}, + ansiForegroundBlue: {foregroundBlue, foreground}, + ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground}, + ansiForegroundCyan: {foregroundGreen | foregroundBlue, foreground}, + ansiForegroundWhite: {foregroundRed | foregroundGreen | foregroundBlue, foreground}, + ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground}, + + ansiBackgroundBlack: {0, background}, + ansiBackgroundRed: {backgroundRed, background}, + ansiBackgroundGreen: {backgroundGreen, background}, + ansiBackgroundYellow: {backgroundRed | backgroundGreen, background}, + ansiBackgroundBlue: {backgroundBlue, background}, + ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background}, + ansiBackgroundCyan: {backgroundGreen | backgroundBlue, background}, + ansiBackgroundWhite: {backgroundRed | backgroundGreen | backgroundBlue, background}, + ansiBackgroundDefault: {0, background}, + + ansiLightForegroundGray: {foregroundIntensity, foreground}, + ansiLightForegroundRed: {foregroundIntensity | foregroundRed, foreground}, + ansiLightForegroundGreen: {foregroundIntensity | foregroundGreen, foreground}, + ansiLightForegroundYellow: {foregroundIntensity | foregroundRed | foregroundGreen, foreground}, + ansiLightForegroundBlue: {foregroundIntensity | foregroundBlue, foreground}, + ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground}, + ansiLightForegroundCyan: {foregroundIntensity | foregroundGreen | foregroundBlue, foreground}, + ansiLightForegroundWhite: {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground}, + + ansiLightBackgroundGray: {backgroundIntensity, background}, + ansiLightBackgroundRed: {backgroundIntensity | backgroundRed, background}, + ansiLightBackgroundGreen: {backgroundIntensity | backgroundGreen, background}, + ansiLightBackgroundYellow: {backgroundIntensity | backgroundRed | backgroundGreen, background}, + ansiLightBackgroundBlue: {backgroundIntensity | backgroundBlue, background}, + ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background}, + ansiLightBackgroundCyan: {backgroundIntensity | backgroundGreen | backgroundBlue, background}, + ansiLightBackgroundWhite: {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background}, +} + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + defaultAttr *textAttributes +) + +func init() { + screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout)) + if screenInfo != nil { + colorMap[ansiForegroundDefault] = winColor{ + screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue), + foreground, + } + colorMap[ansiBackgroundDefault] = winColor{ + screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue), + background, + } + defaultAttr = convertTextAttr(screenInfo.WAttributes) + } +} + +type coord struct { + X, Y int16 +} + +type smallRect struct { + Left, Top, Right, Bottom int16 +} + +type consoleScreenBufferInfo struct { + DwSize coord + DwCursorPosition coord + WAttributes uint16 + SrWindow smallRect + DwMaximumWindowSize coord +} + +func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo { + var csbi consoleScreenBufferInfo + ret, _, _ := procGetConsoleScreenBufferInfo.Call( + hConsoleOutput, + uintptr(unsafe.Pointer(&csbi))) + if ret == 0 { + return nil + } + return &csbi +} + +func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { + ret, _, _ := procSetConsoleTextAttribute.Call( + hConsoleOutput, + uintptr(wAttributes)) + return ret != 0 +} + +type textAttributes struct { + foregroundColor uint16 + backgroundColor uint16 + foregroundIntensity uint16 + backgroundIntensity uint16 + underscore uint16 + otherAttributes uint16 +} + +func convertTextAttr(winAttr uint16) *textAttributes { + fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue) + bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue) + fgIntensity := winAttr & foregroundIntensity + bgIntensity := winAttr & backgroundIntensity + underline := winAttr & underscore + otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore) + return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes} +} + +func convertWinAttr(textAttr *textAttributes) uint16 { + var winAttr uint16 + winAttr |= textAttr.foregroundColor + winAttr |= textAttr.backgroundColor + winAttr |= textAttr.foregroundIntensity + winAttr |= textAttr.backgroundIntensity + winAttr |= textAttr.underscore + winAttr |= textAttr.otherAttributes + return winAttr +} + +func changeColor(param []byte) parseResult { + screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout)) + if screenInfo == nil { + return noConsole + } + + winAttr := convertTextAttr(screenInfo.WAttributes) + strParam := string(param) + if len(strParam) <= 0 { + strParam = "0" + } + csiParam := strings.Split(strParam, string(separatorChar)) + for _, p := range csiParam { + c, ok := colorMap[p] + switch { + case !ok: + switch p { + case ansiReset: + winAttr.foregroundColor = defaultAttr.foregroundColor + winAttr.backgroundColor = defaultAttr.backgroundColor + winAttr.foregroundIntensity = defaultAttr.foregroundIntensity + winAttr.backgroundIntensity = defaultAttr.backgroundIntensity + winAttr.underscore = 0 + winAttr.otherAttributes = 0 + case ansiIntensityOn: + winAttr.foregroundIntensity = foregroundIntensity + case ansiIntensityOff: + winAttr.foregroundIntensity = 0 + case ansiUnderlineOn: + winAttr.underscore = underscore + case ansiUnderlineOff: + winAttr.underscore = 0 + case ansiBlinkOn: + winAttr.backgroundIntensity = backgroundIntensity + case ansiBlinkOff: + winAttr.backgroundIntensity = 0 + default: + // unknown code + } + case c.drawType == foreground: + winAttr.foregroundColor = c.code + case c.drawType == background: + winAttr.backgroundColor = c.code + } + } + winTextAttribute := convertWinAttr(winAttr) + setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute) + + return changedColor +} + +func parseEscapeSequence(command byte, param []byte) parseResult { + if defaultAttr == nil { + return noConsole + } + + switch command { + case sgrCode: + return changeColor(param) + default: + return unknown + } +} + +func (cw *ansiColorWriter) flushBuffer() (int, error) { + return cw.flushTo(cw.w) +} + +func (cw *ansiColorWriter) resetBuffer() (int, error) { + return cw.flushTo(nil) +} + +func (cw *ansiColorWriter) flushTo(w io.Writer) (int, error) { + var n1, n2 int + var err error + + startBytes := cw.paramStartBuf.Bytes() + cw.paramStartBuf.Reset() + if w != nil { + n1, err = cw.w.Write(startBytes) + if err != nil { + return n1, err + } + } else { + n1 = len(startBytes) + } + paramBytes := cw.paramBuf.Bytes() + cw.paramBuf.Reset() + if w != nil { + n2, err = cw.w.Write(paramBytes) + if err != nil { + return n1 + n2, err + } + } else { + n2 = len(paramBytes) + } + return n1 + n2, nil +} + +func isParameterChar(b byte) bool { + return ('0' <= b && b <= '9') || b == separatorChar +} + +func (cw *ansiColorWriter) Write(p []byte) (int, error) { + var r, nw, first, last int + if cw.mode != DiscardNonColorEscSeq { + cw.state = outsideCsiCode + cw.resetBuffer() + } + + var err error + for i, ch := range p { + switch cw.state { + case outsideCsiCode: + if ch == firstCsiChar { + cw.paramStartBuf.WriteByte(ch) + cw.state = firstCsiCode + } + case firstCsiCode: + switch ch { + case firstCsiChar: + cw.paramStartBuf.WriteByte(ch) + break + case secondeCsiChar: + cw.paramStartBuf.WriteByte(ch) + cw.state = secondCsiCode + last = i - 1 + default: + cw.resetBuffer() + cw.state = outsideCsiCode + } + case secondCsiCode: + if isParameterChar(ch) { + cw.paramBuf.WriteByte(ch) + } else { + nw, err = cw.w.Write(p[first:last]) + r += nw + if err != nil { + return r, err + } + first = i + 1 + result := parseEscapeSequence(ch, cw.paramBuf.Bytes()) + if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) { + cw.paramBuf.WriteByte(ch) + nw, err := cw.flushBuffer() + if err != nil { + return r, err + } + r += nw + } else { + n, _ := cw.resetBuffer() + // Add one more to the size of the buffer for the last ch + r += n + 1 + } + + cw.state = outsideCsiCode + } + default: + cw.state = outsideCsiCode + } + } + + if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode { + nw, err = cw.w.Write(p[first:]) + r += nw + } + + return r, err +} diff --git a/src/vendor/github.com/astaxie/beego/logs/color_windows_test.go b/src/vendor/github.com/astaxie/beego/logs/color_windows_test.go new file mode 100644 index 000000000..5074841ac --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/color_windows_test.go @@ -0,0 +1,294 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build windows + +package logs + +import ( + "bytes" + "fmt" + "syscall" + "testing" +) + +var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo + +func ChangeColor(color uint16) { + setConsoleTextAttribute(uintptr(syscall.Stdout), color) +} + +func ResetColor() { + ChangeColor(uint16(0x0007)) +} + +func TestWritePlanText(t *testing.T) { + inner := bytes.NewBufferString("") + w := NewAnsiColorWriter(inner) + expected := "plain text" + fmt.Fprintf(w, expected) + actual := inner.String() + if actual != expected { + t.Errorf("Get %q, want %q", actual, expected) + } +} + +func TestWriteParseText(t *testing.T) { + inner := bytes.NewBufferString("") + w := NewAnsiColorWriter(inner) + + inputTail := "\x1b[0mtail text" + expectedTail := "tail text" + fmt.Fprintf(w, inputTail) + actualTail := inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } + + inputHead := "head text\x1b[0m" + expectedHead := "head text" + fmt.Fprintf(w, inputHead) + actualHead := inner.String() + inner.Reset() + if actualHead != expectedHead { + t.Errorf("Get %q, want %q", actualHead, expectedHead) + } + + inputBothEnds := "both ends \x1b[0m text" + expectedBothEnds := "both ends text" + fmt.Fprintf(w, inputBothEnds) + actualBothEnds := inner.String() + inner.Reset() + if actualBothEnds != expectedBothEnds { + t.Errorf("Get %q, want %q", actualBothEnds, expectedBothEnds) + } + + inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc" + expectedManyEsc := "\x1b\x1b\x1b many esc" + fmt.Fprintf(w, inputManyEsc) + actualManyEsc := inner.String() + inner.Reset() + if actualManyEsc != expectedManyEsc { + t.Errorf("Get %q, want %q", actualManyEsc, expectedManyEsc) + } + + expectedSplit := "split text" + for _, ch := range "split \x1b[0m text" { + fmt.Fprintf(w, string(ch)) + } + actualSplit := inner.String() + inner.Reset() + if actualSplit != expectedSplit { + t.Errorf("Get %q, want %q", actualSplit, expectedSplit) + } +} + +type screenNotFoundError struct { + error +} + +func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) { + inner := bytes.NewBufferString("") + w := NewAnsiColorWriter(inner) + fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText) + + actualText = inner.String() + screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout)) + if screenInfo != nil { + actualAttributes = screenInfo.WAttributes + } else { + err = &screenNotFoundError{} + } + return +} + +type testParam struct { + text string + attributes uint16 + ansiColor string +} + +func TestWriteAnsiColorText(t *testing.T) { + screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout)) + if screenInfo == nil { + t.Fatal("Could not get ConsoleScreenBufferInfo") + } + defer ChangeColor(screenInfo.WAttributes) + defaultFgColor := screenInfo.WAttributes & uint16(0x0007) + defaultBgColor := screenInfo.WAttributes & uint16(0x0070) + defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008) + defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080) + + fgParam := []testParam{ + {"foreground black ", uint16(0x0000 | 0x0000), "30"}, + {"foreground red ", uint16(0x0004 | 0x0000), "31"}, + {"foreground green ", uint16(0x0002 | 0x0000), "32"}, + {"foreground yellow ", uint16(0x0006 | 0x0000), "33"}, + {"foreground blue ", uint16(0x0001 | 0x0000), "34"}, + {"foreground magenta", uint16(0x0005 | 0x0000), "35"}, + {"foreground cyan ", uint16(0x0003 | 0x0000), "36"}, + {"foreground white ", uint16(0x0007 | 0x0000), "37"}, + {"foreground default", defaultFgColor | 0x0000, "39"}, + {"foreground light gray ", uint16(0x0000 | 0x0008 | 0x0000), "90"}, + {"foreground light red ", uint16(0x0004 | 0x0008 | 0x0000), "91"}, + {"foreground light green ", uint16(0x0002 | 0x0008 | 0x0000), "92"}, + {"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"}, + {"foreground light blue ", uint16(0x0001 | 0x0008 | 0x0000), "94"}, + {"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"}, + {"foreground light cyan ", uint16(0x0003 | 0x0008 | 0x0000), "96"}, + {"foreground light white ", uint16(0x0007 | 0x0008 | 0x0000), "97"}, + } + + bgParam := []testParam{ + {"background black ", uint16(0x0007 | 0x0000), "40"}, + {"background red ", uint16(0x0007 | 0x0040), "41"}, + {"background green ", uint16(0x0007 | 0x0020), "42"}, + {"background yellow ", uint16(0x0007 | 0x0060), "43"}, + {"background blue ", uint16(0x0007 | 0x0010), "44"}, + {"background magenta", uint16(0x0007 | 0x0050), "45"}, + {"background cyan ", uint16(0x0007 | 0x0030), "46"}, + {"background white ", uint16(0x0007 | 0x0070), "47"}, + {"background default", uint16(0x0007) | defaultBgColor, "49"}, + {"background light gray ", uint16(0x0007 | 0x0000 | 0x0080), "100"}, + {"background light red ", uint16(0x0007 | 0x0040 | 0x0080), "101"}, + {"background light green ", uint16(0x0007 | 0x0020 | 0x0080), "102"}, + {"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"}, + {"background light blue ", uint16(0x0007 | 0x0010 | 0x0080), "104"}, + {"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"}, + {"background light cyan ", uint16(0x0007 | 0x0030 | 0x0080), "106"}, + {"background light white ", uint16(0x0007 | 0x0070 | 0x0080), "107"}, + } + + resetParam := []testParam{ + {"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"}, + {"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""}, + } + + boldParam := []testParam{ + {"bold on", uint16(0x0007 | 0x0008), "1"}, + {"bold off", uint16(0x0007), "21"}, + } + + underscoreParam := []testParam{ + {"underscore on", uint16(0x0007 | 0x8000), "4"}, + {"underscore off", uint16(0x0007), "24"}, + } + + blinkParam := []testParam{ + {"blink on", uint16(0x0007 | 0x0080), "5"}, + {"blink off", uint16(0x0007), "25"}, + } + + mixedParam := []testParam{ + {"both black, bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"}, + {"both red, bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"}, + {"both green, bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"}, + {"both yellow, bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"}, + {"both blue, bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"}, + {"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"}, + {"both cyan, bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"}, + {"both white, bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"}, + {"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"}, + } + + assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) { + actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor) + if actualText != expectedText { + t.Errorf("Get %q, want %q", actualText, expectedText) + } + if err != nil { + t.Fatal("Could not get ConsoleScreenBufferInfo") + } + if actualAttributes != expectedAttributes { + t.Errorf("Text: %q, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes) + } + } + + for _, v := range fgParam { + ResetColor() + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + for _, v := range bgParam { + ChangeColor(uint16(0x0070 | 0x0007)) + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + for _, v := range resetParam { + ChangeColor(uint16(0x0000 | 0x0070 | 0x0008)) + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + ResetColor() + for _, v := range boldParam { + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + ResetColor() + for _, v := range underscoreParam { + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + ResetColor() + for _, v := range blinkParam { + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + for _, v := range mixedParam { + ResetColor() + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } +} + +func TestIgnoreUnknownSequences(t *testing.T) { + inner := bytes.NewBufferString("") + w := NewModeAnsiColorWriter(inner, OutputNonColorEscSeq) + + inputText := "\x1b[=decpath mode" + expectedTail := inputText + fmt.Fprintf(w, inputText) + actualTail := inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } + + inputText = "\x1b[=tailing esc and bracket\x1b[" + expectedTail = inputText + fmt.Fprintf(w, inputText) + actualTail = inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } + + inputText = "\x1b[?tailing esc\x1b" + expectedTail = inputText + fmt.Fprintf(w, inputText) + actualTail = inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } + + inputText = "\x1b[1h;3punended color code invalid\x1b3" + expectedTail = inputText + fmt.Fprintf(w, inputText) + actualTail = inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } +} diff --git a/src/vendor/github.com/astaxie/beego/logs/conn.go b/src/vendor/github.com/astaxie/beego/logs/conn.go index 1db1a427c..6d5bf6bfc 100644 --- a/src/vendor/github.com/astaxie/beego/logs/conn.go +++ b/src/vendor/github.com/astaxie/beego/logs/conn.go @@ -113,5 +113,5 @@ func (c *connWriter) needToConnectOnMsg() bool { } func init() { - Register("conn", NewConn) + Register(AdapterConn, NewConn) } diff --git a/src/vendor/github.com/astaxie/beego/logs/console.go b/src/vendor/github.com/astaxie/beego/logs/console.go index 05d08a42e..e75f2a1b1 100644 --- a/src/vendor/github.com/astaxie/beego/logs/console.go +++ b/src/vendor/github.com/astaxie/beego/logs/console.go @@ -41,7 +41,7 @@ var colors = []brush{ newBrush("1;33"), // Warning yellow newBrush("1;32"), // Notice green newBrush("1;34"), // Informational blue - newBrush("1;34"), // Debug blue + newBrush("1;44"), // Debug Background blue } // consoleWriter implements LoggerInterface and writes messages to terminal. @@ -56,7 +56,7 @@ func NewConsole() Logger { cw := &consoleWriter{ lg: newLogWriter(os.Stdout), Level: LevelDebug, - Colorful: true, + Colorful: runtime.GOOS != "windows", } return cw } @@ -97,5 +97,5 @@ func (c *consoleWriter) Flush() { } func init() { - Register("console", NewConsole) + Register(AdapterConsole, NewConsole) } diff --git a/src/vendor/github.com/astaxie/beego/logs/es/es.go b/src/vendor/github.com/astaxie/beego/logs/es/es.go index 397ca2eff..22f4f650d 100644 --- a/src/vendor/github.com/astaxie/beego/logs/es/es.go +++ b/src/vendor/github.com/astaxie/beego/logs/es/es.go @@ -76,5 +76,5 @@ func (el *esLogger) Flush() { } func init() { - logs.Register("es", NewES) + logs.Register(logs.AdapterEs, NewES) } diff --git a/src/vendor/github.com/astaxie/beego/logs/file.go b/src/vendor/github.com/astaxie/beego/logs/file.go index 9d3f78a05..e8c1f37e8 100644 --- a/src/vendor/github.com/astaxie/beego/logs/file.go +++ b/src/vendor/github.com/astaxie/beego/logs/file.go @@ -22,6 +22,7 @@ import ( "io" "os" "path/filepath" + "strconv" "strings" "sync" "time" @@ -30,7 +31,7 @@ import ( // fileLogWriter implements LoggerInterface. // It writes messages by lines limit, file size limit, or time frequency. type fileLogWriter struct { - sync.Mutex // write log order by order and atomic incr maxLinesCurLines and maxSizeCurSize + sync.RWMutex // write log order by order and atomic incr maxLinesCurLines and maxSizeCurSize // The opened file Filename string `json:"filename"` fileWriter *os.File @@ -47,12 +48,15 @@ type fileLogWriter struct { Daily bool `json:"daily"` MaxDays int64 `json:"maxdays"` dailyOpenDate int + dailyOpenTime time.Time Rotate bool `json:"rotate"` Level int `json:"level"` - Perm os.FileMode `json:"perm"` + Perm string `json:"perm"` + + RotatePerm string `json:"rotateperm"` fileNameOnly, suffix string // like "project.log", project is fileNameOnly and .log is suffix } @@ -60,14 +64,12 @@ type fileLogWriter struct { // newFileWriter create a FileLogWriter returning as LoggerInterface. func newFileWriter() Logger { w := &fileLogWriter{ - Filename: "", - MaxLines: 1000000, - MaxSize: 1 << 28, //256 MB - Daily: true, - MaxDays: 7, - Rotate: true, - Level: LevelTrace, - Perm: 0660, + Daily: true, + MaxDays: 7, + Rotate: true, + RotatePerm: "0440", + Level: LevelTrace, + Perm: "0660", } return w } @@ -77,11 +79,11 @@ func newFileWriter() Logger { // { // "filename":"logs/beego.log", // "maxLines":10000, -// "maxsize":1<<30, +// "maxsize":1024, // "daily":true, // "maxDays":15, // "rotate":true, -// "perm":0600 +// "perm":"0600" // } func (w *fileLogWriter) Init(jsonConfig string) error { err := json.Unmarshal([]byte(jsonConfig), w) @@ -128,7 +130,9 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { h, d := formatTimeHeader(when) msg = string(h) + msg + "\n" if w.Rotate { + w.RLock() if w.needRotate(len(msg), d) { + w.RUnlock() w.Lock() if w.needRotate(len(msg), d) { if err := w.doRotate(when); err != nil { @@ -136,6 +140,8 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { } } w.Unlock() + } else { + w.RUnlock() } } @@ -151,7 +157,15 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { func (w *fileLogWriter) createLogFile() (*os.File, error) { // Open the log file - fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, w.Perm) + perm, err := strconv.ParseInt(w.Perm, 8, 64) + if err != nil { + return nil, err + } + fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm)) + if err == nil { + // Make sure file perm is user set perm cause of `os.OpenFile` will obey umask + os.Chmod(w.Filename, os.FileMode(perm)) + } return fd, err } @@ -159,11 +173,15 @@ func (w *fileLogWriter) initFd() error { fd := w.fileWriter fInfo, err := fd.Stat() if err != nil { - return fmt.Errorf("get stat err: %s\n", err) + return fmt.Errorf("get stat err: %s", err) } w.maxSizeCurSize = int(fInfo.Size()) - w.dailyOpenDate = time.Now().Day() + w.dailyOpenTime = time.Now() + w.dailyOpenDate = w.dailyOpenTime.Day() w.maxLinesCurLines = 0 + if w.Daily { + go w.dailyRotate(w.dailyOpenTime) + } if fInfo.Size() > 0 { count, err := w.lines() if err != nil { @@ -174,6 +192,20 @@ func (w *fileLogWriter) initFd() error { return nil } +func (w *fileLogWriter) dailyRotate(openTime time.Time) { + y, m, d := openTime.Add(24 * time.Hour).Date() + nextDay := time.Date(y, m, d, 0, 0, 0, 0, openTime.Location()) + tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100)) + <-tm.C + w.Lock() + if w.needRotate(0, time.Now().Day()) { + if err := w.doRotate(time.Now()); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) + } + } + w.Unlock() +} + func (w *fileLogWriter) lines() (int, error) { fd, err := os.Open(w.Filename) if err != nil { @@ -204,26 +236,37 @@ func (w *fileLogWriter) lines() (int, error) { // DoRotate means it need to write file in new file. // new file name like xx.2013-01-01.log (daily) or xx.001.log (by line or size) func (w *fileLogWriter) doRotate(logTime time.Time) error { - _, err := os.Lstat(w.Filename) - if err != nil { - return err - } // file exists // Find the next available number num := 1 fName := "" + rotatePerm, err := strconv.ParseInt(w.RotatePerm, 8, 64) + if err != nil { + return err + } + + _, err = os.Lstat(w.Filename) + if err != nil { + //even if the file is not exist or other ,we should RESTART the logger + goto RESTART_LOGGER + } + if w.MaxLines > 0 || w.MaxSize > 0 { for ; err == nil && num <= 999; num++ { fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, w.suffix) _, err = os.Lstat(fName) } } else { - fName = fmt.Sprintf("%s.%s%s", w.fileNameOnly, logTime.Format("2006-01-02"), w.suffix) + fName = fmt.Sprintf("%s.%s%s", w.fileNameOnly, w.dailyOpenTime.Format("2006-01-02"), w.suffix) _, err = os.Lstat(fName) + for ; err == nil && num <= 999; num++ { + fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", w.dailyOpenTime.Format("2006-01-02"), num, w.suffix) + _, err = os.Lstat(fName) + } } // return error if the last file checked still existed if err == nil { - return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename) + return fmt.Errorf("Rotate: Cannot find free log number to rename %s", w.Filename) } // close fileWriter before rename @@ -231,19 +274,25 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { // Rename the file to its new found name // even if occurs error,we MUST guarantee to restart new logger - renameErr := os.Rename(w.Filename, fName) - // re-start logger + err = os.Rename(w.Filename, fName) + if err != nil { + goto RESTART_LOGGER + } + + err = os.Chmod(fName, os.FileMode(rotatePerm)) + +RESTART_LOGGER: + startLoggerErr := w.startLogger() go w.deleteOldLog() if startLoggerErr != nil { - return fmt.Errorf("Rotate StartLogger: %s\n", startLoggerErr) + return fmt.Errorf("Rotate StartLogger: %s", startLoggerErr) } - if renameErr != nil { - return fmt.Errorf("Rotate: %s\n", renameErr) + if err != nil { + return fmt.Errorf("Rotate: %s", err) } return nil - } func (w *fileLogWriter) deleteOldLog() { @@ -255,8 +304,12 @@ func (w *fileLogWriter) deleteOldLog() { } }() - if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.MaxDays) { - if strings.HasPrefix(filepath.Base(path), w.fileNameOnly) && + if info == nil { + return + } + + if !info.IsDir() && info.ModTime().Add(24*time.Hour*time.Duration(w.MaxDays)).Before(time.Now()) { + if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) && strings.HasSuffix(filepath.Base(path), w.suffix) { os.Remove(path) } @@ -278,5 +331,5 @@ func (w *fileLogWriter) Flush() { } func init() { - Register("file", newFileWriter) + Register(AdapterFile, newFileWriter) } diff --git a/src/vendor/github.com/astaxie/beego/logs/file_test.go b/src/vendor/github.com/astaxie/beego/logs/file_test.go index 1fa6cdaa4..626521b9d 100644 --- a/src/vendor/github.com/astaxie/beego/logs/file_test.go +++ b/src/vendor/github.com/astaxie/beego/logs/file_test.go @@ -17,12 +17,35 @@ package logs import ( "bufio" "fmt" + "io/ioutil" "os" "strconv" "testing" "time" ) +func TestFilePerm(t *testing.T) { + log := NewLogger(10000) + // use 0666 as test perm cause the default umask is 022 + log.SetLogger("file", `{"filename":"test.log", "perm": "0666"}`) + log.Debug("debug") + log.Informational("info") + log.Notice("notice") + log.Warning("warning") + log.Error("error") + log.Alert("alert") + log.Critical("critical") + log.Emergency("emergency") + file, err := os.Stat("test.log") + if err != nil { + t.Fatal(err) + } + if file.Mode() != 0666 { + t.Fatal("unexpected log file permission") + } + os.Remove("test.log") +} + func TestFile1(t *testing.T) { log := NewLogger(10000) log.SetLogger("file", `{"filename":"test.log"}`) @@ -89,7 +112,7 @@ func TestFile2(t *testing.T) { os.Remove("test2.log") } -func TestFileRotate(t *testing.T) { +func TestFileRotate_01(t *testing.T) { log := NewLogger(10000) log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`) log.Debug("debug") @@ -110,6 +133,112 @@ func TestFileRotate(t *testing.T) { os.Remove("test3.log") } +func TestFileRotate_02(t *testing.T) { + fn1 := "rotate_day.log" + fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log" + testFileRotate(t, fn1, fn2) +} + +func TestFileRotate_03(t *testing.T) { + fn1 := "rotate_day.log" + fn := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log" + os.Create(fn) + fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log" + testFileRotate(t, fn1, fn2) + os.Remove(fn) +} + +func TestFileRotate_04(t *testing.T) { + fn1 := "rotate_day.log" + fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log" + testFileDailyRotate(t, fn1, fn2) +} + +func TestFileRotate_05(t *testing.T) { + fn1 := "rotate_day.log" + fn := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log" + os.Create(fn) + fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log" + testFileDailyRotate(t, fn1, fn2) + os.Remove(fn) +} +func TestFileRotate_06(t *testing.T) { //test file mode + log := NewLogger(10000) + log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`) + log.Debug("debug") + log.Info("info") + log.Notice("notice") + log.Warning("warning") + log.Error("error") + log.Alert("alert") + log.Critical("critical") + log.Emergency("emergency") + rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log" + s, _ := os.Lstat(rotateName) + if s.Mode() != 0440 { + os.Remove(rotateName) + os.Remove("test3.log") + t.Fatal("rotate file mode error") + } + os.Remove(rotateName) + os.Remove("test3.log") +} +func testFileRotate(t *testing.T, fn1, fn2 string) { + fw := &fileLogWriter{ + Daily: true, + MaxDays: 7, + Rotate: true, + Level: LevelTrace, + Perm: "0660", + RotatePerm: "0440", + } + fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1)) + fw.dailyOpenTime = time.Now().Add(-24 * time.Hour) + fw.dailyOpenDate = fw.dailyOpenTime.Day() + fw.WriteMsg(time.Now(), "this is a msg for test", LevelDebug) + + for _, file := range []string{fn1, fn2} { + _, err := os.Stat(file) + if err != nil { + t.FailNow() + } + os.Remove(file) + } + fw.Destroy() +} + +func testFileDailyRotate(t *testing.T, fn1, fn2 string) { + fw := &fileLogWriter{ + Daily: true, + MaxDays: 7, + Rotate: true, + Level: LevelTrace, + Perm: "0660", + RotatePerm: "0440", + } + fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1)) + fw.dailyOpenTime = time.Now().Add(-24 * time.Hour) + fw.dailyOpenDate = fw.dailyOpenTime.Day() + today, _ := time.ParseInLocation("2006-01-02", time.Now().Format("2006-01-02"), fw.dailyOpenTime.Location()) + today = today.Add(-1 * time.Second) + fw.dailyRotate(today) + for _, file := range []string{fn1, fn2} { + _, err := os.Stat(file) + if err != nil { + t.FailNow() + } + content, err := ioutil.ReadFile(file) + if err != nil { + t.FailNow() + } + if len(content) > 0 { + t.FailNow() + } + os.Remove(file) + } + fw.Destroy() +} + func exists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { diff --git a/src/vendor/github.com/astaxie/beego/logs/jianliao.go b/src/vendor/github.com/astaxie/beego/logs/jianliao.go new file mode 100644 index 000000000..88ba0f9af --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/jianliao.go @@ -0,0 +1,72 @@ +package logs + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" +) + +// JLWriter implements beego LoggerInterface and is used to send jiaoliao webhook +type JLWriter struct { + AuthorName string `json:"authorname"` + Title string `json:"title"` + WebhookURL string `json:"webhookurl"` + RedirectURL string `json:"redirecturl,omitempty"` + ImageURL string `json:"imageurl,omitempty"` + Level int `json:"level"` +} + +// newJLWriter create jiaoliao writer. +func newJLWriter() Logger { + return &JLWriter{Level: LevelTrace} +} + +// Init JLWriter with json config string +func (s *JLWriter) Init(jsonconfig string) error { + return json.Unmarshal([]byte(jsonconfig), s) +} + +// WriteMsg write message in smtp writer. +// it will send an email with subject and only this message. +func (s *JLWriter) WriteMsg(when time.Time, msg string, level int) error { + if level > s.Level { + return nil + } + + text := fmt.Sprintf("%s %s", when.Format("2006-01-02 15:04:05"), msg) + + form := url.Values{} + form.Add("authorName", s.AuthorName) + form.Add("title", s.Title) + form.Add("text", text) + if s.RedirectURL != "" { + form.Add("redirectUrl", s.RedirectURL) + } + if s.ImageURL != "" { + form.Add("imageUrl", s.ImageURL) + } + + resp, err := http.PostForm(s.WebhookURL, form) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode) + } + return nil +} + +// Flush implementing method. empty. +func (s *JLWriter) Flush() { +} + +// Destroy implementing method. empty. +func (s *JLWriter) Destroy() { +} + +func init() { + Register(AdapterJianLiao, newJLWriter) +} diff --git a/src/vendor/github.com/astaxie/beego/logs/log.go b/src/vendor/github.com/astaxie/beego/logs/log.go index 3b3e22081..0e97a70e3 100644 --- a/src/vendor/github.com/astaxie/beego/logs/log.go +++ b/src/vendor/github.com/astaxie/beego/logs/log.go @@ -35,10 +35,12 @@ package logs import ( "fmt" + "log" "os" "path" "runtime" "strconv" + "strings" "sync" "time" ) @@ -55,16 +57,31 @@ const ( LevelDebug ) -// Legacy loglevel constants to ensure backwards compatibility. -// -// Deprecated: will be removed in 1.5.0. +// levelLogLogger is defined to implement log.Logger +// the real log level will be LevelEmergency +const levelLoggerImpl = -1 + +// Name for adapter with beego official support +const ( + AdapterConsole = "console" + AdapterFile = "file" + AdapterMultiFile = "multifile" + AdapterMail = "smtp" + AdapterConn = "conn" + AdapterEs = "es" + AdapterJianLiao = "jianliao" + AdapterSlack = "slack" + AdapterAliLS = "alils" +) + +// Legacy log level constants to ensure backwards compatibility. const ( LevelInfo = LevelInformational LevelTrace = LevelDebug LevelWarn = LevelWarning ) -type loggerType func() Logger +type newLoggerFunc func() Logger // Logger defines the behavior of a log provider. type Logger interface { @@ -74,12 +91,13 @@ type Logger interface { Flush() } -var adapters = make(map[string]loggerType) +var adapters = make(map[string]newLoggerFunc) +var levelPrefix = [LevelDebug + 1]string{"[M] ", "[A] ", "[C] ", "[E] ", "[W] ", "[N] ", "[I] ", "[D] "} // Register makes a log provide available by the provided name. // If Register is called twice with the same name or if driver is nil, // it panics. -func Register(name string, log loggerType) { +func Register(name string, log newLoggerFunc) { if log == nil { panic("logs: Register provide is nil") } @@ -94,15 +112,19 @@ func Register(name string, log loggerType) { type BeeLogger struct { lock sync.Mutex level int + init bool enableFuncCallDepth bool loggerFuncCallDepth int asynchronous bool + msgChanLen int64 msgChan chan *logMsg signalChan chan string wg sync.WaitGroup outputs []*nameLogger } +const defaultAsyncMsgLen = 1e3 + type nameLogger struct { Logger name string @@ -119,18 +141,31 @@ var logMsgPool *sync.Pool // NewLogger returns a new BeeLogger. // channelLen means the number of messages in chan(used where asynchronous is true). // if the buffering chan is full, logger adapters write to file or other way. -func NewLogger(channelLen int64) *BeeLogger { +func NewLogger(channelLens ...int64) *BeeLogger { bl := new(BeeLogger) bl.level = LevelDebug bl.loggerFuncCallDepth = 2 - bl.msgChan = make(chan *logMsg, channelLen) + bl.msgChanLen = append(channelLens, 0)[0] + if bl.msgChanLen <= 0 { + bl.msgChanLen = defaultAsyncMsgLen + } bl.signalChan = make(chan string, 1) + bl.setLogger(AdapterConsole) return bl } // Async set the log to asynchronous and start the goroutine -func (bl *BeeLogger) Async() *BeeLogger { +func (bl *BeeLogger) Async(msgLen ...int64) *BeeLogger { + bl.lock.Lock() + defer bl.lock.Unlock() + if bl.asynchronous { + return bl + } bl.asynchronous = true + if len(msgLen) > 0 && msgLen[0] > 0 { + bl.msgChanLen = msgLen[0] + } + bl.msgChan = make(chan *logMsg, bl.msgChanLen) logMsgPool = &sync.Pool{ New: func() interface{} { return &logMsg{} @@ -143,16 +178,14 @@ func (bl *BeeLogger) Async() *BeeLogger { // SetLogger provides a given logger adapter into BeeLogger with config string. // config need to be correct JSON as string: {"interval":360}. -func (bl *BeeLogger) SetLogger(adapterName string, config string) error { - bl.lock.Lock() - defer bl.lock.Unlock() - +func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error { + config := append(configs, "{}")[0] for _, l := range bl.outputs { if l.name == adapterName { return fmt.Errorf("logs: duplicate adaptername %q (you have set this logger before)", adapterName) } } - + log, ok := adapters[adapterName] if !ok { return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adapterName) @@ -168,6 +201,18 @@ func (bl *BeeLogger) SetLogger(adapterName string, config string) error { return nil } +// SetLogger provides a given logger adapter into BeeLogger with config string. +// config need to be correct JSON as string: {"interval":360}. +func (bl *BeeLogger) SetLogger(adapterName string, configs ...string) error { + bl.lock.Lock() + defer bl.lock.Unlock() + if !bl.init { + bl.outputs = []*nameLogger{} + bl.init = true + } + return bl.setLogger(adapterName, configs...) +} + // DelLogger remove a logger adapter in BeeLogger. func (bl *BeeLogger) DelLogger(adapterName string) error { bl.lock.Lock() @@ -196,7 +241,32 @@ func (bl *BeeLogger) writeToLoggers(when time.Time, msg string, level int) { } } -func (bl *BeeLogger) writeMsg(logLevel int, msg string) error { +func (bl *BeeLogger) Write(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + // writeMsg will always add a '\n' character + if p[len(p)-1] == '\n' { + p = p[0 : len(p)-1] + } + // set levelLoggerImpl to ensure all log message will be write out + err = bl.writeMsg(levelLoggerImpl, string(p)) + if err == nil { + return len(p), err + } + return 0, err +} + +func (bl *BeeLogger) writeMsg(logLevel int, msg string, v ...interface{}) error { + if !bl.init { + bl.lock.Lock() + bl.setLogger(AdapterConsole) + bl.lock.Unlock() + } + + if len(v) > 0 { + msg = fmt.Sprintf(msg, v...) + } when := time.Now() if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) @@ -205,8 +275,17 @@ func (bl *BeeLogger) writeMsg(logLevel int, msg string) error { line = 0 } _, filename := path.Split(file) - msg = "[" + filename + ":" + strconv.FormatInt(int64(line), 10) + "]" + msg + msg = "[" + filename + ":" + strconv.Itoa(line) + "] " + msg } + + //set level info in front of filename info + if logLevel == levelLoggerImpl { + // set to emergency to ensure all log will be print out correctly + logLevel = LevelEmergency + } else { + msg = levelPrefix[logLevel] + msg + } + if bl.asynchronous { lm := logMsgPool.Get().(*logMsg) lm.level = logLevel @@ -273,8 +352,7 @@ func (bl *BeeLogger) Emergency(format string, v ...interface{}) { if LevelEmergency > bl.level { return } - msg := fmt.Sprintf("[M] "+format, v...) - bl.writeMsg(LevelEmergency, msg) + bl.writeMsg(LevelEmergency, format, v...) } // Alert Log ALERT level message. @@ -282,8 +360,7 @@ func (bl *BeeLogger) Alert(format string, v ...interface{}) { if LevelAlert > bl.level { return } - msg := fmt.Sprintf("[A] "+format, v...) - bl.writeMsg(LevelAlert, msg) + bl.writeMsg(LevelAlert, format, v...) } // Critical Log CRITICAL level message. @@ -291,8 +368,7 @@ func (bl *BeeLogger) Critical(format string, v ...interface{}) { if LevelCritical > bl.level { return } - msg := fmt.Sprintf("[C] "+format, v...) - bl.writeMsg(LevelCritical, msg) + bl.writeMsg(LevelCritical, format, v...) } // Error Log ERROR level message. @@ -300,17 +376,15 @@ func (bl *BeeLogger) Error(format string, v ...interface{}) { if LevelError > bl.level { return } - msg := fmt.Sprintf("[E] "+format, v...) - bl.writeMsg(LevelError, msg) + bl.writeMsg(LevelError, format, v...) } // Warning Log WARNING level message. func (bl *BeeLogger) Warning(format string, v ...interface{}) { - if LevelWarning > bl.level { + if LevelWarn > bl.level { return } - msg := fmt.Sprintf("[W] "+format, v...) - bl.writeMsg(LevelWarning, msg) + bl.writeMsg(LevelWarn, format, v...) } // Notice Log NOTICE level message. @@ -318,17 +392,15 @@ func (bl *BeeLogger) Notice(format string, v ...interface{}) { if LevelNotice > bl.level { return } - msg := fmt.Sprintf("[N] "+format, v...) - bl.writeMsg(LevelNotice, msg) + bl.writeMsg(LevelNotice, format, v...) } // Informational Log INFORMATIONAL level message. func (bl *BeeLogger) Informational(format string, v ...interface{}) { - if LevelInformational > bl.level { + if LevelInfo > bl.level { return } - msg := fmt.Sprintf("[I] "+format, v...) - bl.writeMsg(LevelInformational, msg) + bl.writeMsg(LevelInfo, format, v...) } // Debug Log DEBUG level message. @@ -336,28 +408,25 @@ func (bl *BeeLogger) Debug(format string, v ...interface{}) { if LevelDebug > bl.level { return } - msg := fmt.Sprintf("[D] "+format, v...) - bl.writeMsg(LevelDebug, msg) + bl.writeMsg(LevelDebug, format, v...) } // Warn Log WARN level message. // compatibility alias for Warning() func (bl *BeeLogger) Warn(format string, v ...interface{}) { - if LevelWarning > bl.level { + if LevelWarn > bl.level { return } - msg := fmt.Sprintf("[W] "+format, v...) - bl.writeMsg(LevelWarning, msg) + bl.writeMsg(LevelWarn, format, v...) } // Info Log INFO level message. // compatibility alias for Informational() func (bl *BeeLogger) Info(format string, v ...interface{}) { - if LevelInformational > bl.level { + if LevelInfo > bl.level { return } - msg := fmt.Sprintf("[I] "+format, v...) - bl.writeMsg(LevelInformational, msg) + bl.writeMsg(LevelInfo, format, v...) } // Trace Log TRACE level message. @@ -366,8 +435,7 @@ func (bl *BeeLogger) Trace(format string, v ...interface{}) { if LevelDebug > bl.level { return } - msg := fmt.Sprintf("[D] "+format, v...) - bl.writeMsg(LevelDebug, msg) + bl.writeMsg(LevelDebug, format, v...) } // Flush flush all chan data. @@ -386,6 +454,7 @@ func (bl *BeeLogger) Close() { if bl.asynchronous { bl.signalChan <- "close" bl.wg.Wait() + close(bl.msgChan) } else { bl.flush() for _, l := range bl.outputs { @@ -393,7 +462,6 @@ func (bl *BeeLogger) Close() { } bl.outputs = nil } - close(bl.msgChan) close(bl.signalChan) } @@ -407,16 +475,172 @@ func (bl *BeeLogger) Reset() { } func (bl *BeeLogger) flush() { - for { - if len(bl.msgChan) > 0 { - bm := <-bl.msgChan - bl.writeToLoggers(bm.when, bm.msg, bm.level) - logMsgPool.Put(bm) - continue + if bl.asynchronous { + for { + if len(bl.msgChan) > 0 { + bm := <-bl.msgChan + bl.writeToLoggers(bm.when, bm.msg, bm.level) + logMsgPool.Put(bm) + continue + } + break } - break } for _, l := range bl.outputs { l.Flush() } } + +// beeLogger references the used application logger. +var beeLogger = NewLogger() + +// GetBeeLogger returns the default BeeLogger +func GetBeeLogger() *BeeLogger { + return beeLogger +} + +var beeLoggerMap = struct { + sync.RWMutex + logs map[string]*log.Logger +}{ + logs: map[string]*log.Logger{}, +} + +// GetLogger returns the default BeeLogger +func GetLogger(prefixes ...string) *log.Logger { + prefix := append(prefixes, "")[0] + if prefix != "" { + prefix = fmt.Sprintf(`[%s] `, strings.ToUpper(prefix)) + } + beeLoggerMap.RLock() + l, ok := beeLoggerMap.logs[prefix] + if ok { + beeLoggerMap.RUnlock() + return l + } + beeLoggerMap.RUnlock() + beeLoggerMap.Lock() + defer beeLoggerMap.Unlock() + l, ok = beeLoggerMap.logs[prefix] + if !ok { + l = log.New(beeLogger, prefix, 0) + beeLoggerMap.logs[prefix] = l + } + return l +} + +// Reset will remove all the adapter +func Reset() { + beeLogger.Reset() +} + +// Async set the beelogger with Async mode and hold msglen messages +func Async(msgLen ...int64) *BeeLogger { + return beeLogger.Async(msgLen...) +} + +// SetLevel sets the global log level used by the simple logger. +func SetLevel(l int) { + beeLogger.SetLevel(l) +} + +// EnableFuncCallDepth enable log funcCallDepth +func EnableFuncCallDepth(b bool) { + beeLogger.enableFuncCallDepth = b +} + +// SetLogFuncCall set the CallDepth, default is 4 +func SetLogFuncCall(b bool) { + beeLogger.EnableFuncCallDepth(b) + beeLogger.SetLogFuncCallDepth(4) +} + +// SetLogFuncCallDepth set log funcCallDepth +func SetLogFuncCallDepth(d int) { + beeLogger.loggerFuncCallDepth = d +} + +// SetLogger sets a new logger. +func SetLogger(adapter string, config ...string) error { + return beeLogger.SetLogger(adapter, config...) +} + +// Emergency logs a message at emergency level. +func Emergency(f interface{}, v ...interface{}) { + beeLogger.Emergency(formatLog(f, v...)) +} + +// Alert logs a message at alert level. +func Alert(f interface{}, v ...interface{}) { + beeLogger.Alert(formatLog(f, v...)) +} + +// Critical logs a message at critical level. +func Critical(f interface{}, v ...interface{}) { + beeLogger.Critical(formatLog(f, v...)) +} + +// Error logs a message at error level. +func Error(f interface{}, v ...interface{}) { + beeLogger.Error(formatLog(f, v...)) +} + +// Warning logs a message at warning level. +func Warning(f interface{}, v ...interface{}) { + beeLogger.Warn(formatLog(f, v...)) +} + +// Warn compatibility alias for Warning() +func Warn(f interface{}, v ...interface{}) { + beeLogger.Warn(formatLog(f, v...)) +} + +// Notice logs a message at notice level. +func Notice(f interface{}, v ...interface{}) { + beeLogger.Notice(formatLog(f, v...)) +} + +// Informational logs a message at info level. +func Informational(f interface{}, v ...interface{}) { + beeLogger.Info(formatLog(f, v...)) +} + +// Info compatibility alias for Warning() +func Info(f interface{}, v ...interface{}) { + beeLogger.Info(formatLog(f, v...)) +} + +// Debug logs a message at debug level. +func Debug(f interface{}, v ...interface{}) { + beeLogger.Debug(formatLog(f, v...)) +} + +// Trace logs a message at trace level. +// compatibility alias for Warning() +func Trace(f interface{}, v ...interface{}) { + beeLogger.Trace(formatLog(f, v...)) +} + +func formatLog(f interface{}, v ...interface{}) string { + var msg string + switch f.(type) { + case string: + msg = f.(string) + if len(v) == 0 { + return msg + } + if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") { + //format string + } else { + //do not contain format char + msg += strings.Repeat(" %v", len(v)) + } + default: + msg = fmt.Sprint(f) + if len(v) == 0 { + return msg + } + msg += strings.Repeat(" %v", len(v)) + } + return fmt.Sprintf(msg, v...) +} diff --git a/src/vendor/github.com/astaxie/beego/logs/logger.go b/src/vendor/github.com/astaxie/beego/logs/logger.go index 323c41c56..b5d7255f0 100644 --- a/src/vendor/github.com/astaxie/beego/logs/logger.go +++ b/src/vendor/github.com/astaxie/beego/logs/logger.go @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. - package logs import ( + "fmt" "io" + "os" "sync" "time" ) @@ -37,44 +38,164 @@ func (lg *logWriter) println(when time.Time, msg string) { lg.Unlock() } +type outputMode int + +// DiscardNonColorEscSeq supports the divided color escape sequence. +// But non-color escape sequence is not output. +// Please use the OutputNonColorEscSeq If you want to output a non-color +// escape sequences such as ncurses. However, it does not support the divided +// color escape sequence. +const ( + _ outputMode = iota + DiscardNonColorEscSeq + OutputNonColorEscSeq +) + +// NewAnsiColorWriter creates and initializes a new ansiColorWriter +// using io.Writer w as its initial contents. +// In the console of Windows, which change the foreground and background +// colors of the text by the escape sequence. +// In the console of other systems, which writes to w all text. +func NewAnsiColorWriter(w io.Writer) io.Writer { + return NewModeAnsiColorWriter(w, DiscardNonColorEscSeq) +} + +// NewModeAnsiColorWriter create and initializes a new ansiColorWriter +// by specifying the outputMode. +func NewModeAnsiColorWriter(w io.Writer, mode outputMode) io.Writer { + if _, ok := w.(*ansiColorWriter); !ok { + return &ansiColorWriter{ + w: w, + mode: mode, + } + } + return w +} + +const ( + y1 = `0123456789` + y2 = `0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789` + y3 = `0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999` + y4 = `0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789` + mo1 = `000000000111` + mo2 = `123456789012` + d1 = `0000000001111111111222222222233` + d2 = `1234567890123456789012345678901` + h1 = `000000000011111111112222` + h2 = `012345678901234567890123` + mi1 = `000000000011111111112222222222333333333344444444445555555555` + mi2 = `012345678901234567890123456789012345678901234567890123456789` + s1 = `000000000011111111112222222222333333333344444444445555555555` + s2 = `012345678901234567890123456789012345678901234567890123456789` +) + func formatTimeHeader(when time.Time) ([]byte, int) { y, mo, d := when.Date() h, mi, s := when.Clock() - //len(2006/01/02 15:03:04)==19 + //len("2006/01/02 15:04:05 ")==20 var buf [20]byte - t := 3 - for y >= 10 { - p := y / 10 - buf[t] = byte('0' + y - p*10) - y = p - t-- - } - buf[0] = byte('0' + y) + + buf[0] = y1[y/1000%10] + buf[1] = y2[y/100] + buf[2] = y3[y-y/100*100] + buf[3] = y4[y-y/100*100] buf[4] = '/' - if mo > 9 { - buf[5] = '1' - buf[6] = byte('0' + mo - 9) - } else { - buf[5] = '0' - buf[6] = byte('0' + mo) - } + buf[5] = mo1[mo-1] + buf[6] = mo2[mo-1] buf[7] = '/' - t = d / 10 - buf[8] = byte('0' + t) - buf[9] = byte('0' + d - t*10) + buf[8] = d1[d-1] + buf[9] = d2[d-1] buf[10] = ' ' - t = h / 10 - buf[11] = byte('0' + t) - buf[12] = byte('0' + h - t*10) + buf[11] = h1[h] + buf[12] = h2[h] buf[13] = ':' - t = mi / 10 - buf[14] = byte('0' + t) - buf[15] = byte('0' + mi - t*10) + buf[14] = mi1[mi] + buf[15] = mi2[mi] buf[16] = ':' - t = s / 10 - buf[17] = byte('0' + t) - buf[18] = byte('0' + s - t*10) + buf[17] = s1[s] + buf[18] = s2[s] buf[19] = ' ' return buf[0:], d } + +var ( + green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109}) + white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109}) + yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109}) + red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109}) + blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109}) + magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109}) + cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109}) + + w32Green = string([]byte{27, 91, 52, 50, 109}) + w32White = string([]byte{27, 91, 52, 55, 109}) + w32Yellow = string([]byte{27, 91, 52, 51, 109}) + w32Red = string([]byte{27, 91, 52, 49, 109}) + w32Blue = string([]byte{27, 91, 52, 52, 109}) + w32Magenta = string([]byte{27, 91, 52, 53, 109}) + w32Cyan = string([]byte{27, 91, 52, 54, 109}) + + reset = string([]byte{27, 91, 48, 109}) +) + +// ColorByStatus return color by http code +// 2xx return Green +// 3xx return White +// 4xx return Yellow +// 5xx return Red +func ColorByStatus(cond bool, code int) string { + switch { + case code >= 200 && code < 300: + return map[bool]string{true: green, false: w32Green}[cond] + case code >= 300 && code < 400: + return map[bool]string{true: white, false: w32White}[cond] + case code >= 400 && code < 500: + return map[bool]string{true: yellow, false: w32Yellow}[cond] + default: + return map[bool]string{true: red, false: w32Red}[cond] + } +} + +// ColorByMethod return color by http code +// GET return Blue +// POST return Cyan +// PUT return Yellow +// DELETE return Red +// PATCH return Green +// HEAD return Magenta +// OPTIONS return WHITE +func ColorByMethod(cond bool, method string) string { + switch method { + case "GET": + return map[bool]string{true: blue, false: w32Blue}[cond] + case "POST": + return map[bool]string{true: cyan, false: w32Cyan}[cond] + case "PUT": + return map[bool]string{true: yellow, false: w32Yellow}[cond] + case "DELETE": + return map[bool]string{true: red, false: w32Red}[cond] + case "PATCH": + return map[bool]string{true: green, false: w32Green}[cond] + case "HEAD": + return map[bool]string{true: magenta, false: w32Magenta}[cond] + case "OPTIONS": + return map[bool]string{true: white, false: w32White}[cond] + default: + return reset + } +} + +// Guard Mutex to guarantee atomic of W32Debug(string) function +var mu sync.Mutex + +// W32Debug Helper method to output colored logs in Windows terminals +func W32Debug(msg string) { + mu.Lock() + defer mu.Unlock() + + current := time.Now() + w := NewAnsiColorWriter(os.Stdout) + + fmt.Fprintf(w, "[beego] %v %s\n", current.Format("2006/01/02 - 15:04:05"), msg) +} diff --git a/src/vendor/github.com/astaxie/beego/logs/logger_test.go b/src/vendor/github.com/astaxie/beego/logs/logger_test.go new file mode 100644 index 000000000..119b7bd31 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/logger_test.go @@ -0,0 +1,75 @@ +// Copyright 2016 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logs + +import ( + "bytes" + "testing" + "time" +) + +func TestFormatHeader_0(t *testing.T) { + tm := time.Now() + if tm.Year() >= 2100 { + t.FailNow() + } + dur := time.Second + for { + if tm.Year() >= 2100 { + break + } + h, _ := formatTimeHeader(tm) + if tm.Format("2006/01/02 15:04:05 ") != string(h) { + t.Log(tm) + t.FailNow() + } + tm = tm.Add(dur) + dur *= 2 + } +} + +func TestFormatHeader_1(t *testing.T) { + tm := time.Now() + year := tm.Year() + dur := time.Second + for { + if tm.Year() >= year+1 { + break + } + h, _ := formatTimeHeader(tm) + if tm.Format("2006/01/02 15:04:05 ") != string(h) { + t.Log(tm) + t.FailNow() + } + tm = tm.Add(dur) + } +} + +func TestNewAnsiColor1(t *testing.T) { + inner := bytes.NewBufferString("") + w := NewAnsiColorWriter(inner) + if w == inner { + t.Errorf("Get %#v, want %#v", w, inner) + } +} + +func TestNewAnsiColor2(t *testing.T) { + inner := bytes.NewBufferString("") + w1 := NewAnsiColorWriter(inner) + w2 := NewAnsiColorWriter(w1) + if w1 != w2 { + t.Errorf("Get %#v, want %#v", w1, w2) + } +} diff --git a/src/vendor/github.com/astaxie/beego/logs/multifile.go b/src/vendor/github.com/astaxie/beego/logs/multifile.go index b82ba2741..63204e176 100644 --- a/src/vendor/github.com/astaxie/beego/logs/multifile.go +++ b/src/vendor/github.com/astaxie/beego/logs/multifile.go @@ -112,5 +112,5 @@ func newFilesWriter() Logger { } func init() { - Register("multifile", newFilesWriter) + Register(AdapterMultiFile, newFilesWriter) } diff --git a/src/vendor/github.com/astaxie/beego/logs/slack.go b/src/vendor/github.com/astaxie/beego/logs/slack.go new file mode 100644 index 000000000..1cd2e5aee --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/logs/slack.go @@ -0,0 +1,60 @@ +package logs + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" +) + +// SLACKWriter implements beego LoggerInterface and is used to send jiaoliao webhook +type SLACKWriter struct { + WebhookURL string `json:"webhookurl"` + Level int `json:"level"` +} + +// newSLACKWriter create jiaoliao writer. +func newSLACKWriter() Logger { + return &SLACKWriter{Level: LevelTrace} +} + +// Init SLACKWriter with json config string +func (s *SLACKWriter) Init(jsonconfig string) error { + return json.Unmarshal([]byte(jsonconfig), s) +} + +// WriteMsg write message in smtp writer. +// it will send an email with subject and only this message. +func (s *SLACKWriter) WriteMsg(when time.Time, msg string, level int) error { + if level > s.Level { + return nil + } + + text := fmt.Sprintf("{\"text\": \"%s %s\"}", when.Format("2006-01-02 15:04:05"), msg) + + form := url.Values{} + form.Add("payload", text) + + resp, err := http.PostForm(s.WebhookURL, form) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode) + } + return nil +} + +// Flush implementing method. empty. +func (s *SLACKWriter) Flush() { +} + +// Destroy implementing method. empty. +func (s *SLACKWriter) Destroy() { +} + +func init() { + Register(AdapterSlack, newSLACKWriter) +} diff --git a/src/vendor/github.com/astaxie/beego/logs/smtp.go b/src/vendor/github.com/astaxie/beego/logs/smtp.go index 47f5a0c69..6208d7b85 100644 --- a/src/vendor/github.com/astaxie/beego/logs/smtp.go +++ b/src/vendor/github.com/astaxie/beego/logs/smtp.go @@ -52,11 +52,7 @@ func newSMTPWriter() Logger { // "level":LevelError // } func (s *SMTPWriter) Init(jsonconfig string) error { - err := json.Unmarshal([]byte(jsonconfig), s) - if err != nil { - return err - } - return nil + return json.Unmarshal([]byte(jsonconfig), s) } func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth { @@ -106,7 +102,7 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd if err != nil { return err } - _, err = w.Write([]byte(msgContent)) + _, err = w.Write(msgContent) if err != nil { return err } @@ -116,12 +112,7 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd return err } - err = client.Quit() - if err != nil { - return err - } - - return nil + return client.Quit() } // WriteMsg write message in smtp writer. @@ -147,14 +138,12 @@ func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error { // Flush implementing method. empty. func (s *SMTPWriter) Flush() { - return } // Destroy implementing method. empty. func (s *SMTPWriter) Destroy() { - return } func init() { - Register("smtp", newSMTPWriter) + Register(AdapterMail, newSMTPWriter) } diff --git a/src/vendor/github.com/astaxie/beego/migration/ddl.go b/src/vendor/github.com/astaxie/beego/migration/ddl.go index 51243337f..cea103558 100644 --- a/src/vendor/github.com/astaxie/beego/migration/ddl.go +++ b/src/vendor/github.com/astaxie/beego/migration/ddl.go @@ -14,40 +14,382 @@ package migration -// Table store the tablename and Column -type Table struct { - TableName string - Columns []*Column +import ( + "fmt" + + "github.com/astaxie/beego" +) + +// Index struct defines the structure of Index Columns +type Index struct { + Name string } -// Create return the create sql -func (t *Table) Create() string { - return "" +// Unique struct defines a single unique key combination +type Unique struct { + Definition string + Columns []*Column } -// Drop return the drop sql -func (t *Table) Drop() string { - return "" -} - -// Column define the columns name type and Default +//Column struct defines a single column of a table type Column struct { - Name string - Type string - Default interface{} + Name string + Inc string + Null string + Default string + Unsign string + DataType string + remove bool + Modify bool } -// Create return create sql with the provided tbname and columns -func Create(tbname string, columns ...Column) string { - return "" +// Foreign struct defines a single foreign relationship +type Foreign struct { + ForeignTable string + ForeignColumn string + OnDelete string + OnUpdate string + Column } -// Drop return the drop sql with the provided tbname and columns -func Drop(tbname string, columns ...Column) string { - return "" +// RenameColumn struct allows renaming of columns +type RenameColumn struct { + OldName string + OldNull string + OldDefault string + OldUnsign string + OldDataType string + NewName string + Column } -// TableDDL is still in think -func TableDDL(tbname string, columns ...Column) string { - return "" +// CreateTable creates the table on system +func (m *Migration) CreateTable(tablename, engine, charset string, p ...func()) { + m.TableName = tablename + m.Engine = engine + m.Charset = charset + m.ModifyType = "create" +} + +// AlterTable set the ModifyType to alter +func (m *Migration) AlterTable(tablename string) { + m.TableName = tablename + m.ModifyType = "alter" +} + +// NewCol creates a new standard column and attaches it to m struct +func (m *Migration) NewCol(name string) *Column { + col := &Column{Name: name} + m.AddColumns(col) + return col +} + +//PriCol creates a new primary column and attaches it to m struct +func (m *Migration) PriCol(name string) *Column { + col := &Column{Name: name} + m.AddColumns(col) + m.AddPrimary(col) + return col +} + +//UniCol creates / appends columns to specified unique key and attaches it to m struct +func (m *Migration) UniCol(uni, name string) *Column { + col := &Column{Name: name} + m.AddColumns(col) + + uniqueOriginal := &Unique{} + + for _, unique := range m.Uniques { + if unique.Definition == uni { + unique.AddColumnsToUnique(col) + uniqueOriginal = unique + } + } + if uniqueOriginal.Definition == "" { + unique := &Unique{Definition: uni} + unique.AddColumnsToUnique(col) + m.AddUnique(unique) + } + + return col +} + +//ForeignCol creates a new foreign column and returns the instance of column +func (m *Migration) ForeignCol(colname, foreigncol, foreigntable string) (foreign *Foreign) { + + foreign = &Foreign{ForeignColumn: foreigncol, ForeignTable: foreigntable} + foreign.Name = colname + m.AddForeign(foreign) + return foreign +} + +//SetOnDelete sets the on delete of foreign +func (foreign *Foreign) SetOnDelete(del string) *Foreign { + foreign.OnDelete = "ON DELETE" + del + return foreign +} + +//SetOnUpdate sets the on update of foreign +func (foreign *Foreign) SetOnUpdate(update string) *Foreign { + foreign.OnUpdate = "ON UPDATE" + update + return foreign +} + +//Remove marks the columns to be removed. +//it allows reverse m to create the column. +func (c *Column) Remove() { + c.remove = true +} + +//SetAuto enables auto_increment of column (can be used once) +func (c *Column) SetAuto(inc bool) *Column { + if inc { + c.Inc = "auto_increment" + } + return c +} + +//SetNullable sets the column to be null +func (c *Column) SetNullable(null bool) *Column { + if null { + c.Null = "" + + } else { + c.Null = "NOT NULL" + } + return c +} + +//SetDefault sets the default value, prepend with "DEFAULT " +func (c *Column) SetDefault(def string) *Column { + c.Default = "DEFAULT " + def + return c +} + +//SetUnsigned sets the column to be unsigned int +func (c *Column) SetUnsigned(unsign bool) *Column { + if unsign { + c.Unsign = "UNSIGNED" + } + return c +} + +//SetDataType sets the dataType of the column +func (c *Column) SetDataType(dataType string) *Column { + c.DataType = dataType + return c +} + +//SetOldNullable allows reverting to previous nullable on reverse ms +func (c *RenameColumn) SetOldNullable(null bool) *RenameColumn { + if null { + c.OldNull = "" + + } else { + c.OldNull = "NOT NULL" + } + return c +} + +//SetOldDefault allows reverting to previous default on reverse ms +func (c *RenameColumn) SetOldDefault(def string) *RenameColumn { + c.OldDefault = def + return c +} + +//SetOldUnsigned allows reverting to previous unsgined on reverse ms +func (c *RenameColumn) SetOldUnsigned(unsign bool) *RenameColumn { + if unsign { + c.OldUnsign = "UNSIGNED" + } + return c +} + +//SetOldDataType allows reverting to previous datatype on reverse ms +func (c *RenameColumn) SetOldDataType(dataType string) *RenameColumn { + c.OldDataType = dataType + return c +} + +//SetPrimary adds the columns to the primary key (can only be used any number of times in only one m) +func (c *Column) SetPrimary(m *Migration) *Column { + m.Primary = append(m.Primary, c) + return c +} + +//AddColumnsToUnique adds the columns to Unique Struct +func (unique *Unique) AddColumnsToUnique(columns ...*Column) *Unique { + + unique.Columns = append(unique.Columns, columns...) + + return unique +} + +//AddColumns adds columns to m struct +func (m *Migration) AddColumns(columns ...*Column) *Migration { + + m.Columns = append(m.Columns, columns...) + + return m +} + +//AddPrimary adds the column to primary in m struct +func (m *Migration) AddPrimary(primary *Column) *Migration { + m.Primary = append(m.Primary, primary) + return m +} + +//AddUnique adds the column to unique in m struct +func (m *Migration) AddUnique(unique *Unique) *Migration { + m.Uniques = append(m.Uniques, unique) + return m +} + +//AddForeign adds the column to foreign in m struct +func (m *Migration) AddForeign(foreign *Foreign) *Migration { + m.Foreigns = append(m.Foreigns, foreign) + return m +} + +//AddIndex adds the column to index in m struct +func (m *Migration) AddIndex(index *Index) *Migration { + m.Indexes = append(m.Indexes, index) + return m +} + +//RenameColumn allows renaming of columns +func (m *Migration) RenameColumn(from, to string) *RenameColumn { + rename := &RenameColumn{OldName: from, NewName: to} + m.Renames = append(m.Renames, rename) + return rename +} + +//GetSQL returns the generated sql depending on ModifyType +func (m *Migration) GetSQL() (sql string) { + sql = "" + switch m.ModifyType { + case "create": + { + sql += fmt.Sprintf("CREATE TABLE `%s` (", m.TableName) + for index, column := range m.Columns { + sql += fmt.Sprintf("\n `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + if len(m.Columns) > index+1 { + sql += "," + } + } + + if len(m.Primary) > 0 { + sql += fmt.Sprintf(",\n PRIMARY KEY( ") + } + for index, column := range m.Primary { + sql += fmt.Sprintf(" `%s`", column.Name) + if len(m.Primary) > index+1 { + sql += "," + } + + } + if len(m.Primary) > 0 { + sql += fmt.Sprintf(")") + } + + for _, unique := range m.Uniques { + sql += fmt.Sprintf(",\n UNIQUE KEY `%s`( ", unique.Definition) + for index, column := range unique.Columns { + sql += fmt.Sprintf(" `%s`", column.Name) + if len(unique.Columns) > index+1 { + sql += "," + } + } + sql += fmt.Sprintf(")") + } + for _, foreign := range m.Foreigns { + sql += fmt.Sprintf(",\n `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default) + sql += fmt.Sprintf(",\n KEY `%s_%s_foreign`(`%s`),", m.TableName, foreign.Column.Name, foreign.Column.Name) + sql += fmt.Sprintf("\n CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate) + + } + sql += fmt.Sprintf(")ENGINE=%s DEFAULT CHARSET=%s;", m.Engine, m.Charset) + break + } + case "alter": + { + sql += fmt.Sprintf("ALTER TABLE `%s` ", m.TableName) + for index, column := range m.Columns { + if !column.remove { + beego.BeeLogger.Info("col") + sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + } else { + sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name) + } + + if len(m.Columns) > index { + sql += "," + } + } + for index, column := range m.Renames { + sql += fmt.Sprintf("CHANGE COLUMN `%s` `%s` %s %s %s %s %s", column.OldName, column.NewName, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + if len(m.Renames) > index+1 { + sql += "," + } + } + + for index, foreign := range m.Foreigns { + sql += fmt.Sprintf("ADD `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default) + sql += fmt.Sprintf(",\n ADD KEY `%s_%s_foreign`(`%s`)", m.TableName, foreign.Column.Name, foreign.Column.Name) + sql += fmt.Sprintf(",\n ADD CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate) + if len(m.Foreigns) > index+1 { + sql += "," + } + } + sql += ";" + + break + } + case "reverse": + { + + sql += fmt.Sprintf("ALTER TABLE `%s`", m.TableName) + for index, column := range m.Columns { + if column.remove { + sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + } else { + sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name) + } + if len(m.Columns) > index { + sql += "," + } + } + + if len(m.Primary) > 0 { + sql += fmt.Sprintf("\n DROP PRIMARY KEY,") + } + + for index, unique := range m.Uniques { + sql += fmt.Sprintf("\n DROP KEY `%s`", unique.Definition) + if len(m.Uniques) > index { + sql += "," + } + + } + for index, column := range m.Renames { + sql += fmt.Sprintf("\n CHANGE COLUMN `%s` `%s` %s %s %s %s", column.NewName, column.OldName, column.OldDataType, column.OldUnsign, column.OldNull, column.OldDefault) + if len(m.Renames) > index { + sql += "," + } + } + + for _, foreign := range m.Foreigns { + sql += fmt.Sprintf("\n DROP KEY `%s_%s_foreign`", m.TableName, foreign.Column.Name) + sql += fmt.Sprintf(",\n DROP FOREIGN KEY `%s_%s_foreign`", m.TableName, foreign.Column.Name) + sql += fmt.Sprintf(",\n DROP COLUMN `%s`", foreign.Name) + } + sql += ";" + } + case "delete": + { + sql += fmt.Sprintf("DROP TABLE IF EXISTS `%s`;", m.TableName) + } + } + + return } diff --git a/src/vendor/github.com/astaxie/beego/migration/doc.go b/src/vendor/github.com/astaxie/beego/migration/doc.go new file mode 100644 index 000000000..0c6564d4d --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/migration/doc.go @@ -0,0 +1,32 @@ +// Package migration enables you to generate migrations back and forth. It generates both migrations. +// +// //Creates a table +// m.CreateTable("tablename","InnoDB","utf8"); +// +// //Alter a table +// m.AlterTable("tablename") +// +// Standard Column Methods +// * SetDataType +// * SetNullable +// * SetDefault +// * SetUnsigned (use only on integer types unless produces error) +// +// //Sets a primary column, multiple calls allowed, standard column methods available +// m.PriCol("id").SetAuto(true).SetNullable(false).SetDataType("INT(10)").SetUnsigned(true) +// +// //UniCol Can be used multiple times, allows standard Column methods. Use same "index" string to add to same index +// m.UniCol("index","column") +// +// //Standard Column Initialisation, can call .Remove() after NewCol("") on alter to remove +// m.NewCol("name").SetDataType("VARCHAR(255) COLLATE utf8_unicode_ci").SetNullable(false) +// m.NewCol("value").SetDataType("DOUBLE(8,2)").SetNullable(false) +// +// //Rename Columns , only use with Alter table, doesn't works with Create, prefix standard column methods with "Old" to +// //create a true reversible migration eg: SetOldDataType("DOUBLE(12,3)") +// m.RenameColumn("from","to")... +// +// //Foreign Columns, single columns are only supported, SetOnDelete & SetOnUpdate are available, call appropriately. +// //Supports standard column methods, automatic reverse. +// m.ForeignCol("local_col","foreign_col","foreign_table") +package migration diff --git a/src/vendor/github.com/astaxie/beego/migration/migration.go b/src/vendor/github.com/astaxie/beego/migration/migration.go index 1591bc50d..510537142 100644 --- a/src/vendor/github.com/astaxie/beego/migration/migration.go +++ b/src/vendor/github.com/astaxie/beego/migration/migration.go @@ -33,7 +33,7 @@ import ( "strings" "time" - "github.com/astaxie/beego" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/orm" ) @@ -52,6 +52,26 @@ type Migrationer interface { GetCreated() int64 } +//Migration defines the migrations by either SQL or DDL +type Migration struct { + sqls []string + Created string + TableName string + Engine string + Charset string + ModifyType string + Columns []*Column + Indexes []*Index + Primary []*Column + Uniques []*Unique + Foreigns []*Foreign + Renames []*RenameColumn + RemoveColumns []*Column + RemoveIndexes []*Index + RemoveUniques []*Unique + RemoveForeigns []*Foreign +} + var ( migrationMap map[string]Migrationer ) @@ -60,20 +80,34 @@ func init() { migrationMap = make(map[string]Migrationer) } -// Migration the basic type which will implement the basic type -type Migration struct { - sqls []string - Created string -} - // Up implement in the Inheritance struct for upgrade func (m *Migration) Up() { + switch m.ModifyType { + case "reverse": + m.ModifyType = "alter" + case "delete": + m.ModifyType = "create" + } + m.sqls = append(m.sqls, m.GetSQL()) } // Down implement in the Inheritance struct for down func (m *Migration) Down() { + switch m.ModifyType { + case "alter": + m.ModifyType = "reverse" + case "create": + m.ModifyType = "delete" + } + m.sqls = append(m.sqls, m.GetSQL()) +} + +//Migrate adds the SQL to the execution list +func (m *Migration) Migrate(migrationType string) { + m.ModifyType = migrationType + m.sqls = append(m.sqls, m.GetSQL()) } // SQL add sql want to execute @@ -90,7 +124,7 @@ func (m *Migration) Reset() { func (m *Migration) Exec(name, status string) error { o := orm.NewOrm() for _, s := range m.sqls { - beego.Info("exec sql:", s) + logs.Info("exec sql:", s) r := o.Raw(s) _, err := r.Exec() if err != nil { @@ -144,20 +178,20 @@ func Upgrade(lasttime int64) error { i := 0 for _, v := range sm { if v.created > lasttime { - beego.Info("start upgrade", v.name) + logs.Info("start upgrade", v.name) v.m.Reset() v.m.Up() err := v.m.Exec(v.name, "up") if err != nil { - beego.Error("execute error:", err) + logs.Error("execute error:", err) time.Sleep(2 * time.Second) return err } - beego.Info("end upgrade:", v.name) + logs.Info("end upgrade:", v.name) i++ } } - beego.Info("total success upgrade:", i, " migration") + logs.Info("total success upgrade:", i, " migration") time.Sleep(2 * time.Second) return nil } @@ -165,20 +199,20 @@ func Upgrade(lasttime int64) error { // Rollback rollback the migration by the name func Rollback(name string) error { if v, ok := migrationMap[name]; ok { - beego.Info("start rollback") + logs.Info("start rollback") v.Reset() v.Down() err := v.Exec(name, "down") if err != nil { - beego.Error("execute error:", err) + logs.Error("execute error:", err) time.Sleep(2 * time.Second) return err } - beego.Info("end rollback") + logs.Info("end rollback") time.Sleep(2 * time.Second) return nil } - beego.Error("not exist the migrationMap name:" + name) + logs.Error("not exist the migrationMap name:" + name) time.Sleep(2 * time.Second) return errors.New("not exist the migrationMap name:" + name) } @@ -191,23 +225,23 @@ func Reset() error { for j := len(sm) - 1; j >= 0; j-- { v := sm[j] if isRollBack(v.name) { - beego.Info("skip the", v.name) + logs.Info("skip the", v.name) time.Sleep(1 * time.Second) continue } - beego.Info("start reset:", v.name) + logs.Info("start reset:", v.name) v.m.Reset() v.m.Down() err := v.m.Exec(v.name, "down") if err != nil { - beego.Error("execute error:", err) + logs.Error("execute error:", err) time.Sleep(2 * time.Second) return err } i++ - beego.Info("end reset:", v.name) + logs.Info("end reset:", v.name) } - beego.Info("total success reset:", i, " migration") + logs.Info("total success reset:", i, " migration") time.Sleep(2 * time.Second) return nil } @@ -216,7 +250,7 @@ func Reset() error { func Refresh() error { err := Reset() if err != nil { - beego.Error("execute error:", err) + logs.Error("execute error:", err) time.Sleep(2 * time.Second) return err } @@ -265,7 +299,7 @@ func isRollBack(name string) bool { var maps []orm.Params num, err := o.Raw("select * from migrations where `name` = ? order by id_migration desc", name).Values(&maps) if err != nil { - beego.Info("get name has error", err) + logs.Info("get name has error", err) return false } if num <= 0 { diff --git a/src/vendor/github.com/astaxie/beego/mime.go b/src/vendor/github.com/astaxie/beego/mime.go index e85fcb2ac..ca2878ab2 100644 --- a/src/vendor/github.com/astaxie/beego/mime.go +++ b/src/vendor/github.com/astaxie/beego/mime.go @@ -339,7 +339,7 @@ var mimemaps = map[string]string{ ".pvu": "paleovu/x-pv", ".pwz": "application/vndms-powerpoint", ".py": "text/x-scriptphyton", - ".pyc": "applicaiton/x-bytecodepython", + ".pyc": "application/x-bytecodepython", ".qcp": "audio/vndqcelp", ".qd3": "x-world/x-3dmf", ".qd3d": "x-world/x-3dmf", diff --git a/src/vendor/github.com/astaxie/beego/namespace.go b/src/vendor/github.com/astaxie/beego/namespace.go index 4007d44cc..72f22a720 100644 --- a/src/vendor/github.com/astaxie/beego/namespace.go +++ b/src/vendor/github.com/astaxie/beego/namespace.go @@ -44,7 +44,7 @@ func NewNamespace(prefix string, params ...LinkNamespace) *Namespace { return ns } -// Cond set condtion function +// Cond set condition function // if cond return true can run this namespace, else can't // usage: // ns.Cond(func (ctx *context.Context) bool{ @@ -60,7 +60,7 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace { exception("405", ctx) } } - if v, ok := n.handlers.filters[BeforeRouter]; ok { + if v := n.handlers.filters[BeforeRouter]; len(v) > 0 { mr := new(FilterRouter) mr.tree = NewTree() mr.pattern = "*" @@ -267,13 +267,12 @@ func addPrefix(t *Tree, prefix string) { addPrefix(t.wildcard, prefix) } for _, l := range t.leaves { - if c, ok := l.runObject.(*controllerInfo); ok { + if c, ok := l.runObject.(*ControllerInfo); ok { if !strings.HasPrefix(c.pattern, prefix) { c.pattern = prefix + c.pattern } } } - } // NSCond is Namespace Condition @@ -284,16 +283,16 @@ func NSCond(cond namespaceCond) LinkNamespace { } // NSBefore Namespace BeforeRouter filter -func NSBefore(filiterList ...FilterFunc) LinkNamespace { +func NSBefore(filterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { - ns.Filter("before", filiterList...) + ns.Filter("before", filterList...) } } // NSAfter add Namespace FinishRouter filter -func NSAfter(filiterList ...FilterFunc) LinkNamespace { +func NSAfter(filterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { - ns.Filter("after", filiterList...) + ns.Filter("after", filterList...) } } diff --git a/src/vendor/github.com/astaxie/beego/namespace_test.go b/src/vendor/github.com/astaxie/beego/namespace_test.go index a92ae3efd..b3f20dff2 100644 --- a/src/vendor/github.com/astaxie/beego/namespace_test.go +++ b/src/vendor/github.com/astaxie/beego/namespace_test.go @@ -61,8 +61,8 @@ func TestNamespaceNest(t *testing.T) { ns.Namespace( NewNamespace("/admin"). Get("/order", func(ctx *context.Context) { - ctx.Output.Body([]byte("order")) - }), + ctx.Output.Body([]byte("order")) + }), ) AddNamespace(ns) BeeApp.Handlers.ServeHTTP(w, r) @@ -79,8 +79,8 @@ func TestNamespaceNestParam(t *testing.T) { ns.Namespace( NewNamespace("/admin"). Get("/order/:id", func(ctx *context.Context) { - ctx.Output.Body([]byte(ctx.Input.Param(":id"))) - }), + ctx.Output.Body([]byte(ctx.Input.Param(":id"))) + }), ) AddNamespace(ns) BeeApp.Handlers.ServeHTTP(w, r) @@ -124,8 +124,8 @@ func TestNamespaceFilter(t *testing.T) { ctx.Output.Body([]byte("this is Filter")) }). Get("/user/:id", func(ctx *context.Context) { - ctx.Output.Body([]byte(ctx.Input.Param(":id"))) - }) + ctx.Output.Body([]byte(ctx.Input.Param(":id"))) + }) AddNamespace(ns) BeeApp.Handlers.ServeHTTP(w, r) if w.Body.String() != "this is Filter" { @@ -139,10 +139,7 @@ func TestNamespaceCond(t *testing.T) { ns := NewNamespace("/v2") ns.Cond(func(ctx *context.Context) bool { - if ctx.Input.Domain() == "beego.me" { - return true - } - return false + return ctx.Input.Domain() == "beego.me" }). AutoRouter(&TestController{}) AddNamespace(ns) diff --git a/src/vendor/github.com/astaxie/beego/orm/cmd.go b/src/vendor/github.com/astaxie/beego/orm/cmd.go index 3638a75cf..0ff4dc40d 100644 --- a/src/vendor/github.com/astaxie/beego/orm/cmd.go +++ b/src/vendor/github.com/astaxie/beego/orm/cmd.go @@ -150,7 +150,7 @@ func (d *commandSyncDb) Run() error { } for _, fi := range mi.fields.fieldsDB { - if _, ok := columns[fi.column]; ok == false { + if _, ok := columns[fi.column]; !ok { fields = append(fields, fi) } } @@ -175,7 +175,7 @@ func (d *commandSyncDb) Run() error { } for _, idx := range indexes[mi.table] { - if d.al.DbBaser.IndexExists(db, idx.Table, idx.Name) == false { + if !d.al.DbBaser.IndexExists(db, idx.Table, idx.Name) { if !d.noInfo { fmt.Printf("create index `%s` for table `%s`\n", idx.Name, idx.Table) } diff --git a/src/vendor/github.com/astaxie/beego/orm/cmd_utils.go b/src/vendor/github.com/astaxie/beego/orm/cmd_utils.go index da0ee8ab4..de47cb023 100644 --- a/src/vendor/github.com/astaxie/beego/orm/cmd_utils.go +++ b/src/vendor/github.com/astaxie/beego/orm/cmd_utils.go @@ -52,9 +52,15 @@ checkColumn: case TypeBooleanField: col = T["bool"] case TypeCharField: - col = fmt.Sprintf(T["string"], fieldSize) + if al.Driver == DRPostgres && fi.toText { + col = T["string-text"] + } else { + col = fmt.Sprintf(T["string"], fieldSize) + } case TypeTextField: col = T["string-text"] + case TypeTimeField: + col = T["time.Time-clock"] case TypeDateField: col = T["time.Time-date"] case TypeDateTimeField: @@ -83,11 +89,23 @@ checkColumn: col = T["float64"] case TypeDecimalField: s := T["float64-decimal"] - if strings.Index(s, "%d") == -1 { + if !strings.Contains(s, "%d") { col = s } else { col = fmt.Sprintf(s, fi.digits, fi.decimals) } + case TypeJSONField: + if al.Driver != DRPostgres { + fieldType = TypeCharField + goto checkColumn + } + col = T["json"] + case TypeJsonbField: + if al.Driver != DRPostgres { + fieldType = TypeCharField + goto checkColumn + } + col = T["jsonb"] case RelForeignKey, RelOneToOne: fieldType = fi.relModelInfo.fields.pk.fieldType fieldSize = fi.relModelInfo.fields.pk.size @@ -102,7 +120,7 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string { Q := al.DbBaser.TableQuote() typ := getColumnTyp(al, fi) - if fi.null == false { + if !fi.null { typ += " " + "NOT NULL" } @@ -154,7 +172,7 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex } else { column += col - if fi.null == false { + if !fi.null { column += " " + "NOT NULL" } @@ -174,7 +192,7 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex } } - if strings.Index(column, "%COL%") != -1 { + if strings.Contains(column, "%COL%") { column = strings.Replace(column, "%COL%", fi.column, -1) } @@ -264,7 +282,7 @@ func getColumnDefault(fi *fieldInfo) string { // These defaults will be useful if there no config value orm:"default" and NOT NULL is on switch fi.fieldType { - case TypeDateField, TypeDateTimeField, TypeTextField: + case TypeTimeField, TypeDateField, TypeDateTimeField, TypeTextField: return v case TypeBitField, TypeSmallIntegerField, TypeIntegerField, @@ -276,6 +294,8 @@ func getColumnDefault(fi *fieldInfo) string { case TypeBooleanField: t = " DEFAULT %s " d = "FALSE" + case TypeJSONField, TypeJsonbField: + d = "{}" } if fi.colDefault { diff --git a/src/vendor/github.com/astaxie/beego/orm/db.go b/src/vendor/github.com/astaxie/beego/orm/db.go index 314c3535e..12f0f54d2 100644 --- a/src/vendor/github.com/astaxie/beego/orm/db.go +++ b/src/vendor/github.com/astaxie/beego/orm/db.go @@ -24,6 +24,7 @@ import ( ) const ( + formatTime = "15:04:05" formatDate = "2006-01-02" formatDateTime = "2006-01-02 15:04:05" ) @@ -47,6 +48,7 @@ var ( "lte": true, "eq": true, "nq": true, + "ne": true, "startswith": true, "endswith": true, "istartswith": true, @@ -71,12 +73,12 @@ type dbBase struct { var _ dbBaser = new(dbBase) // get struct columns values as interface slice. -func (d *dbBase) collectValues(mi *modelInfo, ind reflect.Value, cols []string, skipAuto bool, insert bool, names *[]string, tz *time.Location) (values []interface{}, err error) { - var columns []string - - if names != nil { - columns = *names +func (d *dbBase) collectValues(mi *modelInfo, ind reflect.Value, cols []string, skipAuto bool, insert bool, names *[]string, tz *time.Location) (values []interface{}, autoFields []string, err error) { + if names == nil { + ns := make([]string, 0, len(cols)) + names = &ns } + values = make([]interface{}, 0, len(cols)) for _, column := range cols { var fi *fieldInfo @@ -85,23 +87,29 @@ func (d *dbBase) collectValues(mi *modelInfo, ind reflect.Value, cols []string, } else { panic(fmt.Errorf("wrong db field/column name `%s` for model `%s`", column, mi.fullName)) } - if fi.dbcol == false || fi.auto && skipAuto { + if !fi.dbcol || fi.auto && skipAuto { continue } value, err := d.collectFieldValue(mi, fi, ind, insert, tz) if err != nil { - return nil, err + return nil, nil, err } - if names != nil { - columns = append(columns, column) + // ignore empty value auto field + if insert && fi.auto { + if fi.fieldType&IsPositiveIntegerField > 0 { + if vu, ok := value.(uint64); !ok || vu == 0 { + continue + } + } else { + if vu, ok := value.(int64); !ok || vu == 0 { + continue + } + } + autoFields = append(autoFields, fi.column) } - values = append(values, value) - } - - if names != nil { - *names = columns + *names, values = append(*names, column), append(values, value) } return @@ -134,7 +142,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val } else { value = field.Bool() } - case TypeCharField, TypeTextField: + case TypeCharField, TypeTextField, TypeJSONField, TypeJsonbField: if ns, ok := field.Interface().(sql.NullString); ok { value = nil if ns.Valid { @@ -169,7 +177,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val value = field.Float() } } - case TypeDateField, TypeDateTimeField: + case TypeTimeField, TypeDateField, TypeDateTimeField: value = field.Interface() if t, ok := value.(time.Time); ok { d.ins.TimeToDB(&t, tz) @@ -181,7 +189,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val } default: switch { - case fi.fieldType&IsPostiveIntegerField > 0: + case fi.fieldType&IsPositiveIntegerField > 0: if field.Kind() == reflect.Ptr { if field.IsNil() { value = nil @@ -216,14 +224,14 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val value = nil } } - if fi.null == false && value == nil { + if !fi.null && value == nil { return nil, fmt.Errorf("field `%s` cannot be NULL", fi.fullName) } } } } switch fi.fieldType { - case TypeDateField, TypeDateTimeField: + case TypeTimeField, TypeDateField, TypeDateTimeField: if fi.autoNow || fi.autoNowAdd && insert { if insert { if t, ok := value.(time.Time); ok && !t.IsZero() { @@ -236,10 +244,21 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val if fi.isFielder { f := field.Addr().Interface().(Fielder) f.SetRaw(tnow.In(DefaultTimeLoc)) + } else if field.Kind() == reflect.Ptr { + v := tnow.In(DefaultTimeLoc) + field.Set(reflect.ValueOf(&v)) } else { field.Set(reflect.ValueOf(tnow.In(DefaultTimeLoc))) } } + case TypeJSONField, TypeJsonbField: + if s, ok := value.(string); (ok && len(s) == 0) || value == nil { + if fi.colDefault && fi.initial.Exist() { + value = fi.initial.String() + } else { + value = nil + } + } } } return value, nil @@ -252,7 +271,7 @@ func (d *dbBase) PrepareInsert(q dbQuerier, mi *modelInfo) (stmtQuerier, string, dbcols := make([]string, 0, len(mi.fields.dbcols)) marks := make([]string, 0, len(mi.fields.dbcols)) for _, fi := range mi.fields.fieldsDB { - if fi.auto == false { + if !fi.auto { dbcols = append(dbcols, fi.column) marks = append(marks, "?") } @@ -273,7 +292,7 @@ func (d *dbBase) PrepareInsert(q dbQuerier, mi *modelInfo) (stmtQuerier, string, // insert struct with prepared statement and given struct reflect value. func (d *dbBase) InsertStmt(stmt stmtQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location) (int64, error) { - values, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, nil, tz) + values, _, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, nil, tz) if err != nil { return 0, err } @@ -292,7 +311,7 @@ func (d *dbBase) InsertStmt(stmt stmtQuerier, mi *modelInfo, ind reflect.Value, } // query sql ,read records and persist in dbBaser. -func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) error { +func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string, isForUpdate bool) error { var whereCols []string var args []interface{} @@ -300,14 +319,14 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo if len(cols) > 0 { var err error whereCols = make([]string, 0, len(cols)) - args, err = d.collectValues(mi, ind, cols, false, false, &whereCols, tz) + args, _, err = d.collectValues(mi, ind, cols, false, false, &whereCols, tz) if err != nil { return err } } else { // default use pk value as where condtion. pkColumn, pkValue, ok := getExistPk(mi, ind) - if ok == false { + if !ok { return ErrMissPK } whereCols = []string{pkColumn} @@ -323,7 +342,12 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo sep = fmt.Sprintf("%s = ? AND %s", Q, Q) wheres := strings.Join(whereCols, sep) - query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ?", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q) + forUpdate := "" + if isForUpdate { + forUpdate = "FOR UPDATE" + } + + query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ? %s", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q, forUpdate) refs := make([]interface{}, colsNum) for i := range refs { @@ -349,13 +373,21 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo // execute insert sql dbQuerier with given struct reflect.Value. func (d *dbBase) Insert(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location) (int64, error) { - names := make([]string, 0, len(mi.fields.dbcols)-1) - values, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, &names, tz) + names := make([]string, 0, len(mi.fields.dbcols)) + values, autoFields, err := d.collectValues(mi, ind, mi.fields.dbcols, false, true, &names, tz) if err != nil { return 0, err } - return d.InsertValue(q, mi, false, names, values) + id, err := d.InsertValue(q, mi, false, names, values) + if err != nil { + return 0, err + } + + if len(autoFields) > 0 { + err = d.ins.setval(q, mi, autoFields) + } + return id, err } // multi-insert sql with given slice struct reflect.Value. @@ -369,7 +401,7 @@ func (d *dbBase) InsertMulti(q dbQuerier, mi *modelInfo, sind reflect.Value, bul // typ := reflect.Indirect(mi.addrField).Type() - length := sind.Len() + length, autoFields := sind.Len(), make([]string, 0, 1) for i := 1; i <= length; i++ { @@ -381,16 +413,18 @@ func (d *dbBase) InsertMulti(q dbQuerier, mi *modelInfo, sind reflect.Value, bul // } if i == 1 { - vus, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, &names, tz) + var ( + vus []interface{} + err error + ) + vus, autoFields, err = d.collectValues(mi, ind, mi.fields.dbcols, false, true, &names, tz) if err != nil { return cnt, err } values = make([]interface{}, bulk*len(vus)) nums += copy(values, vus) - } else { - - vus, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, nil, tz) + vus, _, err := d.collectValues(mi, ind, mi.fields.dbcols, false, true, nil, tz) if err != nil { return cnt, err } @@ -412,7 +446,12 @@ func (d *dbBase) InsertMulti(q dbQuerier, mi *modelInfo, sind reflect.Value, bul } } - return cnt, nil + var err error + if len(autoFields) > 0 { + err = d.ins.setval(q, mi, autoFields) + } + + return cnt, err } // execute insert sql with given struct and given values. @@ -455,10 +494,113 @@ func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []s return id, err } +// InsertOrUpdate a row +// If your primary key or unique column conflict will update +// If no will insert +func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) { + args0 := "" + iouStr := "" + argsMap := map[string]string{} + switch a.Driver { + case DRMySQL: + iouStr = "ON DUPLICATE KEY UPDATE" + case DRPostgres: + if len(args) == 0 { + return 0, fmt.Errorf("`%s` use InsertOrUpdate must have a conflict column", a.DriverName) + } + args0 = strings.ToLower(args[0]) + iouStr = fmt.Sprintf("ON CONFLICT (%s) DO UPDATE SET", args0) + default: + return 0, fmt.Errorf("`%s` nonsupport InsertOrUpdate in beego", a.DriverName) + } + + //Get on the key-value pairs + for _, v := range args { + kv := strings.Split(v, "=") + if len(kv) == 2 { + argsMap[strings.ToLower(kv[0])] = kv[1] + } + } + + isMulti := false + names := make([]string, 0, len(mi.fields.dbcols)-1) + Q := d.ins.TableQuote() + values, _, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, &names, a.TZ) + + if err != nil { + return 0, err + } + + marks := make([]string, len(names)) + updateValues := make([]interface{}, 0) + updates := make([]string, len(names)) + var conflitValue interface{} + for i, v := range names { + marks[i] = "?" + valueStr := argsMap[strings.ToLower(v)] + if v == args0 { + conflitValue = values[i] + } + if valueStr != "" { + switch a.Driver { + case DRMySQL: + updates[i] = v + "=" + valueStr + case DRPostgres: + if conflitValue != nil { + //postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values + updates[i] = fmt.Sprintf("%s=(select %s from %s where %s = ? )", v, valueStr, mi.table, args0) + updateValues = append(updateValues, conflitValue) + } else { + return 0, fmt.Errorf("`%s` must be in front of `%s` in your struct", args0, v) + } + } + } else { + updates[i] = v + "=?" + updateValues = append(updateValues, values[i]) + } + } + + values = append(values, updateValues...) + + sep := fmt.Sprintf("%s, %s", Q, Q) + qmarks := strings.Join(marks, ", ") + qupdates := strings.Join(updates, ", ") + columns := strings.Join(names, sep) + + multi := len(values) / len(names) + + if isMulti { + qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks + } + //conflitValue maybe is a int,can`t use fmt.Sprintf + query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s) %s "+qupdates, Q, mi.table, Q, Q, columns, Q, qmarks, iouStr) + + d.ins.ReplaceMarks(&query) + + if isMulti || !d.ins.HasReturningID(mi, &query) { + res, err := q.Exec(query, values...) + if err == nil { + if isMulti { + return res.RowsAffected() + } + return res.LastInsertId() + } + return 0, err + } + + row := q.QueryRow(query, values...) + var id int64 + err = row.Scan(&id) + if err != nil && err.Error() == `pq: syntax error at or near "ON"` { + err = fmt.Errorf("postgres version must 9.5 or higher") + } + return id, err +} + // execute update sql dbQuerier with given struct reflect.Value. func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) { pkName, pkValue, ok := getExistPk(mi, ind) - if ok == false { + if !ok { return 0, ErrMissPK } @@ -472,7 +614,7 @@ func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time. setNames = make([]string, 0, len(cols)) } - setValues, err := d.collectValues(mi, ind, cols, true, false, &setNames, tz) + setValues, _, err := d.collectValues(mi, ind, cols, true, false, &setNames, tz) if err != nil { return 0, err } @@ -497,18 +639,36 @@ func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time. // execute delete sql dbQuerier with given struct reflect.Value. // delete index is pk. -func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location) (int64, error) { - pkName, pkValue, ok := getExistPk(mi, ind) - if ok == false { - return 0, ErrMissPK +func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) { + var whereCols []string + var args []interface{} + // if specify cols length > 0, then use it for where condition. + if len(cols) > 0 { + var err error + whereCols = make([]string, 0, len(cols)) + args, _, err = d.collectValues(mi, ind, cols, false, false, &whereCols, tz) + if err != nil { + return 0, err + } + } else { + // default use pk value as where condtion. + pkColumn, pkValue, ok := getExistPk(mi, ind) + if !ok { + return 0, ErrMissPK + } + whereCols = []string{pkColumn} + args = append(args, pkValue) } Q := d.ins.TableQuote() - query := fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s = ?", Q, mi.table, Q, Q, pkName, Q) + sep := fmt.Sprintf("%s = ? AND %s", Q, Q) + wheres := strings.Join(whereCols, sep) + + query := fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s = ?", Q, mi.table, Q, Q, wheres, Q) d.ins.ReplaceMarks(&query) - res, err := q.Exec(query, pkValue) + res, err := q.Exec(query, args...) if err == nil { num, err := res.RowsAffected() if err != nil { @@ -516,13 +676,13 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time. } if num > 0 { if mi.fields.pk.auto { - if mi.fields.pk.fieldType&IsPostiveIntegerField > 0 { + if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(0) } else { ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(0) } } - err := d.deleteRels(q, mi, []interface{}{pkValue}, tz) + err := d.deleteRels(q, mi, args, tz) if err != nil { return num, err } @@ -538,7 +698,7 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con columns := make([]string, 0, len(params)) values := make([]interface{}, 0, len(params)) for col, val := range params { - if fi, ok := mi.fields.GetByAny(col); ok == false || fi.dbcol == false { + if fi, ok := mi.fields.GetByAny(col); !ok || !fi.dbcol { panic(fmt.Errorf("wrong field/column name `%s`", col)) } else { columns = append(columns, fi.column) @@ -673,7 +833,11 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con if err := rs.Scan(&ref); err != nil { return 0, err } - args = append(args, reflect.ValueOf(ref).Interface()) + pkValue, err := d.convertValueFromDB(mi.fields.pk, reflect.ValueOf(ref).Interface(), tz) + if err != nil { + return 0, err + } + args = append(args, pkValue) cnt++ } @@ -768,7 +932,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi if hasRel { for _, fi := range mi.fields.fieldsDB { if fi.fieldType&IsRelField > 0 { - if maps[fi.column] == false { + if !maps[fi.column] { tCols = append(tCols, fi.column) } } @@ -826,7 +990,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi var cnt int64 for rs.Next() { - if one && cnt == 0 || one == false { + if one && cnt == 0 || !one { if err := rs.Scan(refs...); err != nil { return 0, err } @@ -906,7 +1070,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi cnt++ } - if one == false { + if !one { if cnt > 0 { ind.Set(slice) } else { @@ -927,12 +1091,17 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition tables.parseRelated(qs.related, qs.relDepth) where, args := tables.getCondSQL(cond, false, tz) + groupBy := tables.getGroupSQL(qs.groups) tables.getOrderSQL(qs.orders) join := tables.getJoinSQL() Q := d.ins.TableQuote() - query := fmt.Sprintf("SELECT COUNT(*) FROM %s%s%s T0 %s%s", Q, mi.table, Q, join, where) + query := fmt.Sprintf("SELECT COUNT(*) FROM %s%s%s T0 %s%s%s", Q, mi.table, Q, join, where, groupBy) + + if groupBy != "" { + query = fmt.Sprintf("SELECT COUNT(*) FROM (%s) AS T", query) + } d.ins.ReplaceMarks(&query) @@ -944,7 +1113,7 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition // generate sql with replacing operator string placeholders and replaced values. func (d *dbBase) GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator string, args []interface{}, tz *time.Location) (string, []interface{}) { - sql := "" + var sql string params := getFlatParams(fi, args, tz) if len(params) == 0 { @@ -1071,13 +1240,13 @@ setValue: } value = b } - case fieldType == TypeCharField || fieldType == TypeTextField: + case fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField: if str == nil { value = ToStr(val) } else { value = str.String() } - case fieldType == TypeDateField || fieldType == TypeDateTimeField: + case fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField: if str == nil { switch t := val.(type) { case time.Time: @@ -1097,15 +1266,20 @@ setValue: if len(s) >= 19 { s = s[:19] t, err = time.ParseInLocation(formatDateTime, s, tz) - } else { + } else if len(s) >= 10 { if len(s) > 10 { s = s[:10] } t, err = time.ParseInLocation(formatDate, s, tz) + } else if len(s) >= 8 { + if len(s) > 8 { + s = s[:8] + } + t, err = time.ParseInLocation(formatTime, s, tz) } t = t.In(DefaultTimeLoc) - if err != nil && s != "0000-00-00" && s != "0000-00-00 00:00:00" { + if err != nil && s != "00:00:00" && s != "0000-00-00" && s != "0000-00-00 00:00:00" { tErr = err goto end } @@ -1140,7 +1314,7 @@ setValue: tErr = err goto end } - if fieldType&IsPostiveIntegerField > 0 { + if fieldType&IsPositiveIntegerField > 0 { v, _ := str.Uint64() value = v } else { @@ -1186,7 +1360,7 @@ end: func (d *dbBase) setFieldValue(fi *fieldInfo, value interface{}, field reflect.Value) (interface{}, error) { fieldType := fi.fieldType - isNative := fi.isFielder == false + isNative := !fi.isFielder setValue: switch { @@ -1212,7 +1386,7 @@ setValue: field.SetBool(value.(bool)) } } - case fieldType == TypeCharField || fieldType == TypeTextField: + case fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField: if isNative { if ns, ok := field.Interface().(sql.NullString); ok { if value == nil { @@ -1234,12 +1408,18 @@ setValue: field.SetString(value.(string)) } } - case fieldType == TypeDateField || fieldType == TypeDateTimeField: + case fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField: if isNative { if value == nil { value = time.Time{} + } else if field.Kind() == reflect.Ptr { + if value != nil { + v := value.(time.Time) + field.Set(reflect.ValueOf(&v)) + } + } else { + field.Set(reflect.ValueOf(value)) } - field.Set(reflect.ValueOf(value)) } case fieldType == TypePositiveBitField && field.Kind() == reflect.Ptr: if value != nil { @@ -1292,7 +1472,7 @@ setValue: field.Set(reflect.ValueOf(&v)) } case fieldType&IsIntegerField > 0: - if fieldType&IsPostiveIntegerField > 0 { + if fieldType&IsPositiveIntegerField > 0 { if isNative { if value == nil { value = uint64(0) @@ -1356,7 +1536,7 @@ setValue: } } - if isNative == false { + if !isNative { fd := field.Addr().Interface().(Fielder) err := fd.SetRaw(value) if err != nil { @@ -1417,7 +1597,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond infos = make([]*fieldInfo, 0, len(exprs)) for _, ex := range exprs { index, name, fi, suc := tables.parseExprs(mi, strings.Split(ex, ExprSep)) - if suc == false { + if !suc { panic(fmt.Errorf("unknown field/column name `%s`", ex)) } cols = append(cols, fmt.Sprintf("%s.%s%s%s %s%s%s", index, Q, fi.column, Q, Q, name, Q)) @@ -1440,7 +1620,11 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond sels := strings.Join(cols, ", ") - query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s%s", sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) + sqlSelect := "SELECT" + if qs.distinct { + sqlSelect += " DISTINCT" + } + query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) d.ins.ReplaceMarks(&query) @@ -1552,7 +1736,7 @@ func (d *dbBase) TableQuote() string { return "`" } -// replace value placeholer in parametered sql string. +// replace value placeholder in parametered sql string. func (d *dbBase) ReplaceMarks(query *string) { // default use `?` as mark, do nothing } @@ -1562,6 +1746,11 @@ func (d *dbBase) HasReturningID(*modelInfo, *string) bool { return false } +// sync auto key +func (d *dbBase) setval(db dbQuerier, mi *modelInfo, autoFields []string) error { + return nil +} + // convert time from db. func (d *dbBase) TimeFromDB(t *time.Time, tz *time.Location) { *t = t.In(tz) diff --git a/src/vendor/github.com/astaxie/beego/orm/db_alias.go b/src/vendor/github.com/astaxie/beego/orm/db_alias.go index b6c833a71..c70892392 100644 --- a/src/vendor/github.com/astaxie/beego/orm/db_alias.go +++ b/src/vendor/github.com/astaxie/beego/orm/db_alias.go @@ -60,6 +60,8 @@ var ( "sqlite3": DRSqlite, "tidb": DRTiDB, "oracle": DROracle, + "oci8": DROracle, // github.com/mattn/go-oci8 + "ora": DROracle, //https://github.com/rana/ora } dbBasers = map[DriverType]dbBaser{ DRMySQL: newdbBaseMysql(), @@ -80,7 +82,7 @@ type _dbCache struct { func (ac *_dbCache) add(name string, al *alias) (added bool) { ac.mux.Lock() defer ac.mux.Unlock() - if _, ok := ac.cache[name]; ok == false { + if _, ok := ac.cache[name]; !ok { ac.cache[name] = al added = true } @@ -186,7 +188,7 @@ func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) { return nil, fmt.Errorf("register db Ping `%s`, %s", aliasName, err.Error()) } - if dataBaseCache.add(aliasName, al) == false { + if !dataBaseCache.add(aliasName, al) { return nil, fmt.Errorf("DataBase alias name `%s` already registered, cannot reuse", aliasName) } @@ -244,11 +246,11 @@ end: // RegisterDriver Register a database driver use specify driver name, this can be definition the driver is which database type. func RegisterDriver(driverName string, typ DriverType) error { - if t, ok := drivers[driverName]; ok == false { + if t, ok := drivers[driverName]; !ok { drivers[driverName] = typ } else { if t != typ { - return fmt.Errorf("driverName `%s` db driver already registered and is other type\n", driverName) + return fmt.Errorf("driverName `%s` db driver already registered and is other type", driverName) } } return nil @@ -259,7 +261,7 @@ func SetDataBaseTZ(aliasName string, tz *time.Location) error { if al, ok := dataBaseCache.get(aliasName); ok { al.TZ = tz } else { - return fmt.Errorf("DataBase alias name `%s` not registered\n", aliasName) + return fmt.Errorf("DataBase alias name `%s` not registered", aliasName) } return nil } @@ -294,5 +296,5 @@ func GetDB(aliasNames ...string) (*sql.DB, error) { if ok { return al.DB, nil } - return nil, fmt.Errorf("DataBase of alias name `%s` not found\n", name) + return nil, fmt.Errorf("DataBase of alias name `%s` not found", name) } diff --git a/src/vendor/github.com/astaxie/beego/orm/db_mysql.go b/src/vendor/github.com/astaxie/beego/orm/db_mysql.go index 10fe26571..51185563f 100644 --- a/src/vendor/github.com/astaxie/beego/orm/db_mysql.go +++ b/src/vendor/github.com/astaxie/beego/orm/db_mysql.go @@ -16,6 +16,8 @@ package orm import ( "fmt" + "reflect" + "strings" ) // mysql operators. @@ -96,6 +98,82 @@ func (d *dbBaseMysql) IndexExists(db dbQuerier, table string, name string) bool return cnt > 0 } +// InsertOrUpdate a row +// If your primary key or unique column conflict will update +// If no will insert +// Add "`" for mysql sql building +func (d *dbBaseMysql) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) { + var iouStr string + argsMap := map[string]string{} + + iouStr = "ON DUPLICATE KEY UPDATE" + + //Get on the key-value pairs + for _, v := range args { + kv := strings.Split(v, "=") + if len(kv) == 2 { + argsMap[strings.ToLower(kv[0])] = kv[1] + } + } + + isMulti := false + names := make([]string, 0, len(mi.fields.dbcols)-1) + Q := d.ins.TableQuote() + values, _, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, &names, a.TZ) + + if err != nil { + return 0, err + } + + marks := make([]string, len(names)) + updateValues := make([]interface{}, 0) + updates := make([]string, len(names)) + + for i, v := range names { + marks[i] = "?" + valueStr := argsMap[strings.ToLower(v)] + if valueStr != "" { + updates[i] = "`" + v + "`" + "=" + valueStr + } else { + updates[i] = "`" + v + "`" + "=?" + updateValues = append(updateValues, values[i]) + } + } + + values = append(values, updateValues...) + + sep := fmt.Sprintf("%s, %s", Q, Q) + qmarks := strings.Join(marks, ", ") + qupdates := strings.Join(updates, ", ") + columns := strings.Join(names, sep) + + multi := len(values) / len(names) + + if isMulti { + qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks + } + //conflitValue maybe is a int,can`t use fmt.Sprintf + query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s) %s "+qupdates, Q, mi.table, Q, Q, columns, Q, qmarks, iouStr) + + d.ins.ReplaceMarks(&query) + + if isMulti || !d.ins.HasReturningID(mi, &query) { + res, err := q.Exec(query, values...) + if err == nil { + if isMulti { + return res.RowsAffected() + } + return res.LastInsertId() + } + return 0, err + } + + row := q.QueryRow(query, values...) + var id int64 + err = row.Scan(&id) + return id, err +} + // create new mysql dbBaser. func newdbBaseMysql() dbBaser { b := new(dbBaseMysql) diff --git a/src/vendor/github.com/astaxie/beego/orm/db_oracle.go b/src/vendor/github.com/astaxie/beego/orm/db_oracle.go index deca36ad6..f5d6aaa26 100644 --- a/src/vendor/github.com/astaxie/beego/orm/db_oracle.go +++ b/src/vendor/github.com/astaxie/beego/orm/db_oracle.go @@ -94,3 +94,43 @@ func (d *dbBaseOracle) IndexExists(db dbQuerier, table string, name string) bool row.Scan(&cnt) return cnt > 0 } + +// execute insert sql with given struct and given values. +// insert the given values, not the field values in struct. +func (d *dbBaseOracle) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []string, values []interface{}) (int64, error) { + Q := d.ins.TableQuote() + + marks := make([]string, len(names)) + for i := range marks { + marks[i] = ":" + names[i] + } + + sep := fmt.Sprintf("%s, %s", Q, Q) + qmarks := strings.Join(marks, ", ") + columns := strings.Join(names, sep) + + multi := len(values) / len(names) + + if isMulti { + qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks + } + + query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s)", Q, mi.table, Q, Q, columns, Q, qmarks) + + d.ins.ReplaceMarks(&query) + + if isMulti || !d.ins.HasReturningID(mi, &query) { + res, err := q.Exec(query, values...) + if err == nil { + if isMulti { + return res.RowsAffected() + } + return res.LastInsertId() + } + return 0, err + } + row := q.QueryRow(query, values...) + var id int64 + err := row.Scan(&id) + return id, err +} diff --git a/src/vendor/github.com/astaxie/beego/orm/db_postgres.go b/src/vendor/github.com/astaxie/beego/orm/db_postgres.go index 7dbef95a2..e972c4a25 100644 --- a/src/vendor/github.com/astaxie/beego/orm/db_postgres.go +++ b/src/vendor/github.com/astaxie/beego/orm/db_postgres.go @@ -56,6 +56,8 @@ var postgresTypes = map[string]string{ "uint64": `bigint CHECK("%COL%" >= 0)`, "float64": "double precision", "float64-decimal": "numeric(%d, %d)", + "json": "json", + "jsonb": "jsonb", } // postgresql dbBaser. @@ -123,14 +125,35 @@ func (d *dbBasePostgres) ReplaceMarks(query *string) { } // make returning sql support for postgresql. -func (d *dbBasePostgres) HasReturningID(mi *modelInfo, query *string) (has bool) { - if mi.fields.pk.auto { - if query != nil { - *query = fmt.Sprintf(`%s RETURNING "%s"`, *query, mi.fields.pk.column) - } - has = true +func (d *dbBasePostgres) HasReturningID(mi *modelInfo, query *string) bool { + fi := mi.fields.pk + if fi.fieldType&IsPositiveIntegerField == 0 && fi.fieldType&IsIntegerField == 0 { + return false } - return + + if query != nil { + *query = fmt.Sprintf(`%s RETURNING "%s"`, *query, fi.column) + } + return true +} + +// sync auto key +func (d *dbBasePostgres) setval(db dbQuerier, mi *modelInfo, autoFields []string) error { + if len(autoFields) == 0 { + return nil + } + + Q := d.ins.TableQuote() + for _, name := range autoFields { + query := fmt.Sprintf("SELECT setval(pg_get_serial_sequence('%s', '%s'), (SELECT MAX(%s%s%s) FROM %s%s%s));", + mi.table, name, + Q, name, Q, + Q, mi.table, Q) + if _, err := db.Exec(query); err != nil { + return err + } + } + return nil } // show table sql for postgresql. diff --git a/src/vendor/github.com/astaxie/beego/orm/db_sqlite.go b/src/vendor/github.com/astaxie/beego/orm/db_sqlite.go index a3cb69a79..a43a5594c 100644 --- a/src/vendor/github.com/astaxie/beego/orm/db_sqlite.go +++ b/src/vendor/github.com/astaxie/beego/orm/db_sqlite.go @@ -134,7 +134,7 @@ func (d *dbBaseSqlite) IndexExists(db dbQuerier, table string, name string) bool defer rows.Close() for rows.Next() { var tmp, index sql.NullString - rows.Scan(&tmp, &index, &tmp) + rows.Scan(&tmp, &index, &tmp, &tmp, &tmp) if name == index.String { return true } diff --git a/src/vendor/github.com/astaxie/beego/orm/db_tables.go b/src/vendor/github.com/astaxie/beego/orm/db_tables.go index e4c74acee..42be5550e 100644 --- a/src/vendor/github.com/astaxie/beego/orm/db_tables.go +++ b/src/vendor/github.com/astaxie/beego/orm/db_tables.go @@ -63,7 +63,7 @@ func (t *dbTables) set(names []string, mi *modelInfo, fi *fieldInfo, inner bool) // add table info to collection. func (t *dbTables) add(names []string, mi *modelInfo, fi *fieldInfo, inner bool) (*dbTable, bool) { name := strings.Join(names, ExprSep) - if _, ok := t.tablesM[name]; ok == false { + if _, ok := t.tablesM[name]; !ok { i := len(t.tables) + 1 jt := &dbTable{i, fmt.Sprintf("T%d", i), name, names, false, inner, mi, fi, nil} t.tablesM[name] = jt @@ -261,7 +261,7 @@ loopFor: fiN, okN = mmi.fields.GetByAny(exprs[i+1]) } - if isRel && (fi.mi.isThrough == false || num != i) { + if isRel && (!fi.mi.isThrough || num != i) { if fi.null || t.skipEnd { inner = false } @@ -364,7 +364,7 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe } index, _, fi, suc := t.parseExprs(mi, exprs) - if suc == false { + if !suc { panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(p.exprs, ExprSep))) } @@ -383,7 +383,7 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe } } - if sub == false && where != "" { + if !sub && where != "" { where = "WHERE " + where } @@ -403,7 +403,7 @@ func (t *dbTables) getGroupSQL(groups []string) (groupSQL string) { exprs := strings.Split(group, ExprSep) index, _, fi, suc := t.parseExprs(t.mi, exprs) - if suc == false { + if !suc { panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) } @@ -432,7 +432,7 @@ func (t *dbTables) getOrderSQL(orders []string) (orderSQL string) { exprs := strings.Split(order, ExprSep) index, _, fi, suc := t.parseExprs(t.mi, exprs) - if suc == false { + if !suc { panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) } diff --git a/src/vendor/github.com/astaxie/beego/orm/db_utils.go b/src/vendor/github.com/astaxie/beego/orm/db_utils.go index c97caf361..7ae10ca5e 100644 --- a/src/vendor/github.com/astaxie/beego/orm/db_utils.go +++ b/src/vendor/github.com/astaxie/beego/orm/db_utils.go @@ -33,14 +33,16 @@ func getExistPk(mi *modelInfo, ind reflect.Value) (column string, value interfac fi := mi.fields.pk v := ind.FieldByIndex(fi.fieldIndex) - if fi.fieldType&IsPostiveIntegerField > 0 { + if fi.fieldType&IsPositiveIntegerField > 0 { vu := v.Uint() exist = vu > 0 value = vu } else if fi.fieldType&IsIntegerField > 0 { vu := v.Int() - exist = vu > 0 + exist = true value = vu + } else if fi.fieldType&IsRelField > 0 { + _, value, exist = getExistPk(fi.relModelInfo, reflect.Indirect(v)) } else { vu := v.String() exist = vu != "" @@ -74,24 +76,32 @@ outFor: case reflect.String: v := val.String() if fi != nil { - if fi.fieldType == TypeDateField || fi.fieldType == TypeDateTimeField { + if fi.fieldType == TypeTimeField || fi.fieldType == TypeDateField || fi.fieldType == TypeDateTimeField { var t time.Time var err error if len(v) >= 19 { s := v[:19] t, err = time.ParseInLocation(formatDateTime, s, DefaultTimeLoc) - } else { + } else if len(v) >= 10 { s := v if len(v) > 10 { s = v[:10] } t, err = time.ParseInLocation(formatDate, s, tz) + } else { + s := v + if len(s) > 8 { + s = v[:8] + } + t, err = time.ParseInLocation(formatTime, s, tz) } if err == nil { if fi.fieldType == TypeDateField { v = t.In(tz).Format(formatDate) - } else { + } else if fi.fieldType == TypeDateTimeField { v = t.In(tz).Format(formatDateTime) + } else { + v = t.In(tz).Format(formatTime) } } } @@ -137,6 +147,10 @@ outFor: if v, ok := arg.(time.Time); ok { if fi != nil && fi.fieldType == TypeDateField { arg = v.In(tz).Format(formatDate) + } else if fi != nil && fi.fieldType == TypeDateTimeField { + arg = v.In(tz).Format(formatDateTime) + } else if fi != nil && fi.fieldType == TypeTimeField { + arg = v.In(tz).Format(formatTime) } else { arg = v.In(tz).Format(formatDateTime) } @@ -144,7 +158,7 @@ outFor: typ := val.Type() name := getFullName(typ) var value interface{} - if mmi, ok := modelCache.getByFN(name); ok { + if mmi, ok := modelCache.getByFullName(name); ok { if _, vu, exist := getExistPk(mmi, val); exist { value = vu } diff --git a/src/vendor/github.com/astaxie/beego/orm/models.go b/src/vendor/github.com/astaxie/beego/orm/models.go index faf551be2..1d5a4dc26 100644 --- a/src/vendor/github.com/astaxie/beego/orm/models.go +++ b/src/vendor/github.com/astaxie/beego/orm/models.go @@ -29,39 +29,18 @@ const ( var ( modelCache = &_modelCache{ - cache: make(map[string]*modelInfo), - cacheByFN: make(map[string]*modelInfo), - } - supportTag = map[string]int{ - "-": 1, - "null": 1, - "index": 1, - "unique": 1, - "pk": 1, - "auto": 1, - "auto_now": 1, - "auto_now_add": 1, - "size": 2, - "column": 2, - "default": 2, - "rel": 2, - "reverse": 2, - "rel_table": 2, - "rel_through": 2, - "digits": 2, - "decimals": 2, - "on_delete": 2, - "type": 2, + cache: make(map[string]*modelInfo), + cacheByFullName: make(map[string]*modelInfo), } ) // model info collection type _modelCache struct { - sync.RWMutex - orders []string - cache map[string]*modelInfo - cacheByFN map[string]*modelInfo - done bool + sync.RWMutex // only used outsite for bootStrap + orders []string + cache map[string]*modelInfo + cacheByFullName map[string]*modelInfo + done bool } // get all model info @@ -88,9 +67,9 @@ func (mc *_modelCache) get(table string) (mi *modelInfo, ok bool) { return } -// get model info by field name -func (mc *_modelCache) getByFN(name string) (mi *modelInfo, ok bool) { - mi, ok = mc.cacheByFN[name] +// get model info by full name +func (mc *_modelCache) getByFullName(name string) (mi *modelInfo, ok bool) { + mi, ok = mc.cacheByFullName[name] return } @@ -98,7 +77,7 @@ func (mc *_modelCache) getByFN(name string) (mi *modelInfo, ok bool) { func (mc *_modelCache) set(table string, mi *modelInfo) *modelInfo { mii := mc.cache[table] mc.cache[table] = mi - mc.cacheByFN[mi.fullName] = mi + mc.cacheByFullName[mi.fullName] = mi if mii == nil { mc.orders = append(mc.orders, table) } @@ -109,7 +88,7 @@ func (mc *_modelCache) set(table string, mi *modelInfo) *modelInfo { func (mc *_modelCache) clean() { mc.orders = make([]string, 0) mc.cache = make(map[string]*modelInfo) - mc.cacheByFN = make(map[string]*modelInfo) + mc.cacheByFullName = make(map[string]*modelInfo) mc.done = false } diff --git a/src/vendor/github.com/astaxie/beego/orm/models_boot.go b/src/vendor/github.com/astaxie/beego/orm/models_boot.go index 3690557b8..5327f754c 100644 --- a/src/vendor/github.com/astaxie/beego/orm/models_boot.go +++ b/src/vendor/github.com/astaxie/beego/orm/models_boot.go @@ -15,7 +15,6 @@ package orm import ( - "errors" "fmt" "os" "reflect" @@ -23,24 +22,34 @@ import ( ) // register models. -// prefix means table name prefix. -func registerModel(prefix string, model interface{}) { +// PrefixOrSuffix means table name prefix or suffix. +// isPrefix whether the prefix is prefix or suffix +func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) { val := reflect.ValueOf(model) - ind := reflect.Indirect(val) - typ := ind.Type() + typ := reflect.Indirect(val).Type() if val.Kind() != reflect.Ptr { panic(fmt.Errorf(" cannot use non-ptr model struct `%s`", getFullName(typ))) } + // For this case: + // u := &User{} + // registerModel(&u) + if typ.Kind() == reflect.Ptr { + panic(fmt.Errorf(" only allow ptr model struct, it looks you use two reference to the struct `%s`", typ)) + } table := getTableName(val) - if prefix != "" { - table = prefix + table + if PrefixOrSuffix != "" { + if isPrefix { + table = PrefixOrSuffix + table + } else { + table = table + PrefixOrSuffix + } } - + // models's fullname is pkgpath + struct name name := getFullName(typ) - if _, ok := modelCache.getByFN(name); ok { + if _, ok := modelCache.getByFullName(name); ok { fmt.Printf(" model `%s` repeat register, must be unique\n", name) os.Exit(2) } @@ -50,34 +59,34 @@ func registerModel(prefix string, model interface{}) { os.Exit(2) } - info := newModelInfo(val) - if info.fields.pk == nil { + mi := newModelInfo(val) + if mi.fields.pk == nil { outFor: - for _, fi := range info.fields.fieldsDB { + for _, fi := range mi.fields.fieldsDB { if strings.ToLower(fi.name) == "id" { switch fi.addrValue.Elem().Kind() { case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64: fi.auto = true fi.pk = true - info.fields.pk = fi + mi.fields.pk = fi break outFor } } } - if info.fields.pk == nil { - fmt.Printf(" `%s` need a primary key field\n", name) + if mi.fields.pk == nil { + fmt.Printf(" `%s` needs a primary key field, default is to use 'id' if not set\n", name) os.Exit(2) } } - info.table = table - info.pkg = typ.PkgPath() - info.model = model - info.manual = true + mi.table = table + mi.pkg = typ.PkgPath() + mi.model = model + mi.manual = true - modelCache.set(table, info) + modelCache.set(table, mi) } // boostrap models @@ -85,31 +94,30 @@ func bootStrap() { if modelCache.done { return } - var ( err error models map[string]*modelInfo ) - if dataBaseCache.getDefault() == nil { err = fmt.Errorf("must have one register DataBase alias named `default`") goto end } + // set rel and reverse model + // RelManyToMany set the relTable models = modelCache.all() for _, mi := range models { for _, fi := range mi.fields.columns { if fi.rel || fi.reverse { elm := fi.addrValue.Type().Elem() - switch fi.fieldType { - case RelReverseMany, RelManyToMany: + if fi.fieldType == RelReverseMany || fi.fieldType == RelManyToMany { elm = elm.Elem() } - + // check the rel or reverse model already register name := getFullName(elm) - mii, ok := modelCache.getByFN(name) - if ok == false || mii.pkg != elm.PkgPath() { - err = fmt.Errorf("can not found rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String()) + mii, ok := modelCache.getByFullName(name) + if !ok || mii.pkg != elm.PkgPath() { + err = fmt.Errorf("can not find rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String()) goto end } fi.relModelInfo = mii @@ -117,20 +125,17 @@ func bootStrap() { switch fi.fieldType { case RelManyToMany: if fi.relThrough != "" { - msg := fmt.Sprintf("field `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough) if i := strings.LastIndex(fi.relThrough, "."); i != -1 && len(fi.relThrough) > (i+1) { pn := fi.relThrough[:i] - rmi, ok := modelCache.getByFN(fi.relThrough) - if ok == false || pn != rmi.pkg { - err = errors.New(msg + " cannot find table") + rmi, ok := modelCache.getByFullName(fi.relThrough) + if !ok || pn != rmi.pkg { + err = fmt.Errorf("field `%s` wrong rel_through value `%s` cannot find table", fi.fullName, fi.relThrough) goto end } - fi.relThroughModelInfo = rmi fi.relTable = rmi.table - } else { - err = errors.New(msg) + err = fmt.Errorf("field `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough) goto end } } else { @@ -138,7 +143,6 @@ func bootStrap() { if fi.relTable != "" { i.table = fi.relTable } - if v := modelCache.set(i.table, i); v != nil { err = fmt.Errorf("the rel table name `%s` already registered, cannot be use, please change one", fi.relTable) goto end @@ -153,6 +157,8 @@ func bootStrap() { } } + // check the rel filed while the relModelInfo also has filed point to current model + // if not exist, add a new field to the relModelInfo models = modelCache.all() for _, mi := range models { for _, fi := range mi.fields.fieldsRel { @@ -165,8 +171,7 @@ func bootStrap() { break } } - - if inModel == false { + if !inModel { rmi := fi.relModelInfo ffi := new(fieldInfo) ffi.name = mi.name @@ -180,7 +185,7 @@ func bootStrap() { } else { ffi.fieldType = RelReverseMany } - if rmi.fields.Add(ffi) == false { + if !rmi.fields.Add(ffi) { added := false for cnt := 0; cnt < 5; cnt++ { ffi.name = fmt.Sprintf("%s%d", mi.name, cnt) @@ -190,7 +195,7 @@ func bootStrap() { break } } - if added == false { + if !added { panic(fmt.Errorf("cannot generate auto reverse field info `%s` to `%s`", fi.fullName, ffi.fullName)) } } @@ -216,7 +221,6 @@ func bootStrap() { } } } - if fi.reverseFieldInfoTwo == nil { err = fmt.Errorf("can not find m2m field for m2m model `%s`, ensure your m2m model defined correct", fi.relThroughModelInfo.fullName) @@ -244,7 +248,7 @@ func bootStrap() { break mForA } } - if found == false { + if !found { err = fmt.Errorf("reverse field `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName) goto end } @@ -263,7 +267,7 @@ func bootStrap() { break mForB } } - if found == false { + if !found { mForC: for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelManyToMany] { conditions := fi.relThrough != "" && fi.relThrough == ffi.relThrough || @@ -283,7 +287,7 @@ func bootStrap() { } } } - if found == false { + if !found { err = fmt.Errorf("reverse field for `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName) goto end } @@ -300,17 +304,31 @@ end: // RegisterModel register models func RegisterModel(models ...interface{}) { + if modelCache.done { + panic(fmt.Errorf("RegisterModel must be run before BootStrap")) + } RegisterModelWithPrefix("", models...) } // RegisterModelWithPrefix register models with a prefix func RegisterModelWithPrefix(prefix string, models ...interface{}) { if modelCache.done { - panic(fmt.Errorf("RegisterModel must be run before BootStrap")) + panic(fmt.Errorf("RegisterModelWithPrefix must be run before BootStrap")) } for _, model := range models { - registerModel(prefix, model) + registerModel(prefix, model, true) + } +} + +// RegisterModelWithSuffix register models with a suffix +func RegisterModelWithSuffix(suffix string, models ...interface{}) { + if modelCache.done { + panic(fmt.Errorf("RegisterModelWithSuffix must be run before BootStrap")) + } + + for _, model := range models { + registerModel(suffix, model, false) } } @@ -320,7 +338,6 @@ func BootStrap() { if modelCache.done { return } - modelCache.Lock() defer modelCache.Unlock() bootStrap() diff --git a/src/vendor/github.com/astaxie/beego/orm/models_fields.go b/src/vendor/github.com/astaxie/beego/orm/models_fields.go index a8cf8e4f6..578206009 100644 --- a/src/vendor/github.com/astaxie/beego/orm/models_fields.go +++ b/src/vendor/github.com/astaxie/beego/orm/models_fields.go @@ -25,6 +25,7 @@ const ( TypeBooleanField = 1 << iota TypeCharField TypeTextField + TypeTimeField TypeDateField TypeDateTimeField TypeBitField @@ -37,6 +38,8 @@ const ( TypePositiveBigIntegerField TypeFloatField TypeDecimalField + TypeJSONField + TypeJsonbField RelForeignKey RelOneToOne RelManyToMany @@ -46,10 +49,10 @@ const ( // Define some logic enum const ( - IsIntegerField = ^-TypePositiveBigIntegerField >> 4 << 5 - IsPostiveIntegerField = ^-TypePositiveBigIntegerField >> 8 << 9 - IsRelField = ^-RelReverseMany >> 14 << 15 - IsFieldType = ^-RelReverseMany<<1 + 1 + IsIntegerField = ^-TypePositiveBigIntegerField >> 5 << 6 + IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 9 << 10 + IsRelField = ^-RelReverseMany >> 17 << 18 + IsFieldType = ^-RelReverseMany<<1 + 1 ) // BooleanField A true/false field. @@ -145,6 +148,65 @@ func (e *CharField) RawValue() interface{} { // verify CharField implement Fielder var _ Fielder = new(CharField) +// TimeField A time, represented in go by a time.Time instance. +// only time values like 10:00:00 +// Has a few extra, optional attr tag: +// +// auto_now: +// Automatically set the field to now every time the object is saved. Useful for “last-modified” timestamps. +// Note that the current date is always used; it’s not just a default value that you can override. +// +// auto_now_add: +// Automatically set the field to now when the object is first created. Useful for creation of timestamps. +// Note that the current date is always used; it’s not just a default value that you can override. +// +// eg: `orm:"auto_now"` or `orm:"auto_now_add"` +type TimeField time.Time + +// Value return the time.Time +func (e TimeField) Value() time.Time { + return time.Time(e) +} + +// Set set the TimeField's value +func (e *TimeField) Set(d time.Time) { + *e = TimeField(d) +} + +// String convert time to string +func (e *TimeField) String() string { + return e.Value().String() +} + +// FieldType return enum type Date +func (e *TimeField) FieldType() int { + return TypeDateField +} + +// SetRaw convert the interface to time.Time. Allow string and time.Time +func (e *TimeField) SetRaw(value interface{}) error { + switch d := value.(type) { + case time.Time: + e.Set(d) + case string: + v, err := timeParse(d, formatTime) + if err != nil { + e.Set(v) + } + return err + default: + return fmt.Errorf(" unknown value `%s`", value) + } + return nil +} + +// RawValue return time value +func (e *TimeField) RawValue() interface{} { + return e.Value() +} + +var _ Fielder = new(TimeField) + // DateField A date, represented in go by a time.Time instance. // only date values like 2006-01-02 // Has a few extra, optional attr tag: @@ -627,3 +689,87 @@ func (e *TextField) RawValue() interface{} { // verify TextField implement Fielder var _ Fielder = new(TextField) + +// JSONField postgres json field. +type JSONField string + +// Value return JSONField value +func (j JSONField) Value() string { + return string(j) +} + +// Set the JSONField value +func (j *JSONField) Set(d string) { + *j = JSONField(d) +} + +// String convert JSONField to string +func (j *JSONField) String() string { + return j.Value() +} + +// FieldType return enum type +func (j *JSONField) FieldType() int { + return TypeJSONField +} + +// SetRaw convert interface string to string +func (j *JSONField) SetRaw(value interface{}) error { + switch d := value.(type) { + case string: + j.Set(d) + default: + return fmt.Errorf(" unknown value `%s`", value) + } + return nil +} + +// RawValue return JSONField value +func (j *JSONField) RawValue() interface{} { + return j.Value() +} + +// verify JSONField implement Fielder +var _ Fielder = new(JSONField) + +// JsonbField postgres json field. +type JsonbField string + +// Value return JsonbField value +func (j JsonbField) Value() string { + return string(j) +} + +// Set the JsonbField value +func (j *JsonbField) Set(d string) { + *j = JsonbField(d) +} + +// String convert JsonbField to string +func (j *JsonbField) String() string { + return j.Value() +} + +// FieldType return enum type +func (j *JsonbField) FieldType() int { + return TypeJsonbField +} + +// SetRaw convert interface string to string +func (j *JsonbField) SetRaw(value interface{}) error { + switch d := value.(type) { + case string: + j.Set(d) + default: + return fmt.Errorf(" unknown value `%s`", value) + } + return nil +} + +// RawValue return JsonbField value +func (j *JsonbField) RawValue() interface{} { + return j.Value() +} + +// verify JsonbField implement Fielder +var _ Fielder = new(JsonbField) diff --git a/src/vendor/github.com/astaxie/beego/orm/models_info_f.go b/src/vendor/github.com/astaxie/beego/orm/models_info_f.go index 996a2f408..bbb7d71fe 100644 --- a/src/vendor/github.com/astaxie/beego/orm/models_info_f.go +++ b/src/vendor/github.com/astaxie/beego/orm/models_info_f.go @@ -47,7 +47,7 @@ func (f *fields) Add(fi *fieldInfo) (added bool) { } else { return } - if _, ok := f.fieldsByType[fi.fieldType]; ok == false { + if _, ok := f.fieldsByType[fi.fieldType]; !ok { f.fieldsByType[fi.fieldType] = make([]*fieldInfo, 0) } f.fieldsByType[fi.fieldType] = append(f.fieldsByType[fi.fieldType], fi) @@ -104,7 +104,7 @@ type fieldInfo struct { mi *modelInfo fieldIndex []int fieldType int - dbcol bool + dbcol bool // table column fk and onetoone inModel bool name string fullName string @@ -116,12 +116,13 @@ type fieldInfo struct { null bool index bool unique bool - colDefault bool - initial StrTo + colDefault bool // whether has default tag + initial StrTo // store the default value size int + toText bool autoNow bool autoNowAdd bool - rel bool + rel bool // if type equal to RelForeignKey, RelOneToOne, RelManyToMany then true reverse bool reverseField string reverseFieldInfo *fieldInfo @@ -133,7 +134,7 @@ type fieldInfo struct { relModelInfo *modelInfo digits int decimals int - isFielder bool + isFielder bool // implement Fielder interface onDelete string } @@ -142,7 +143,7 @@ func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mN var ( tag string tagValue string - initial StrTo + initial StrTo // store the default value fieldType int attrs map[string]bool tags map[string]string @@ -151,6 +152,10 @@ func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mN fi = new(fieldInfo) + // if field which CanAddr is the follow type + // A value is addressable if it is an element of a slice, + // an element of an addressable array, a field of an + // addressable struct, or the result of dereferencing a pointer. addrField = field if field.CanAddr() && field.Kind() != reflect.Ptr { addrField = field.Addr() @@ -161,7 +166,7 @@ func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mN } } - parseStructTag(sf.Tag.Get(defaultStructTagName), &attrs, &tags) + attrs, tags = parseStructTag(sf.Tag.Get(defaultStructTagName)) if _, ok := attrs["-"]; ok { return nil, errSkipField @@ -187,7 +192,7 @@ checkType: } fieldType = f.FieldType() if fieldType&IsRelField > 0 { - err = fmt.Errorf("unsupport rel type custom field") + err = fmt.Errorf("unsupport type custom field, please refer to https://github.com/astaxie/beego/blob/master/orm/models_fields.go#L24-L42") goto end } default: @@ -210,7 +215,7 @@ checkType: } break checkType default: - err = fmt.Errorf("error") + err = fmt.Errorf("rel only allow these value: fk, one, m2m") goto wrongTag } } @@ -230,7 +235,7 @@ checkType: } break checkType default: - err = fmt.Errorf("error") + err = fmt.Errorf("reverse only allow these value: one, many") goto wrongTag } } @@ -239,8 +244,15 @@ checkType: if err != nil { goto end } - if fieldType == TypeCharField && tags["type"] == "text" { - fieldType = TypeTextField + if fieldType == TypeCharField { + switch tags["type"] { + case "text": + fieldType = TypeTextField + case "json": + fieldType = TypeJSONField + case "jsonb": + fieldType = TypeJsonbField + } } if fieldType == TypeFloatField && (digits != "" || decimals != "") { fieldType = TypeDecimalField @@ -248,8 +260,14 @@ checkType: if fieldType == TypeDateTimeField && tags["type"] == "date" { fieldType = TypeDateField } + if fieldType == TypeTimeField && tags["type"] == "time" { + fieldType = TypeTimeField + } } + // check the rel and reverse type + // rel should Ptr + // reverse should slice []*struct switch fieldType { case RelForeignKey, RelOneToOne, RelReverseOne: if field.Kind() != reflect.Ptr { @@ -316,12 +334,12 @@ checkType: switch onDelete { case odCascade, odDoNothing: case odSetDefault: - if initial.Exist() == false { + if !initial.Exist() { err = errors.New("on_delete: set_default need set field a default value") goto end } case odSetNULL: - if fi.null == false { + if !fi.null { err = errors.New("on_delete: set_null need set field null") goto end } @@ -339,7 +357,7 @@ checkType: switch fieldType { case TypeBooleanField: - case TypeCharField: + case TypeCharField, TypeJSONField, TypeJsonbField: if size != "" { v, e := StrTo(size).Int32() if e != nil { @@ -349,11 +367,12 @@ checkType: } } else { fi.size = 255 + fi.toText = true } case TypeTextField: fi.index = false fi.unique = false - case TypeDateField, TypeDateTimeField: + case TypeTimeField, TypeDateField, TypeDateTimeField: if attrs["auto_now"] { fi.autoNow = true } else if attrs["auto_now_add"] { @@ -387,14 +406,12 @@ checkType: if fi.auto || fi.pk { if fi.auto { - switch addrField.Elem().Kind() { case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64: default: err = fmt.Errorf("auto primary key only support int, int32, int64, uint, uint32, uint64 but found `%s`", addrField.Elem().Kind()) goto end } - fi.pk = true } fi.null = false @@ -406,8 +423,8 @@ checkType: fi.index = false } - if fi.auto || fi.pk || fi.unique || fieldType == TypeDateField || fieldType == TypeDateTimeField { - // can not set default + // can not set default for these type + if fi.auto || fi.pk || fi.unique || fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField { initial.Clear() } diff --git a/src/vendor/github.com/astaxie/beego/orm/models_info_m.go b/src/vendor/github.com/astaxie/beego/orm/models_info_m.go index bbb82444d..4a3a37f94 100644 --- a/src/vendor/github.com/astaxie/beego/orm/models_info_m.go +++ b/src/vendor/github.com/astaxie/beego/orm/models_info_m.go @@ -29,31 +29,25 @@ type modelInfo struct { model interface{} fields *fields manual bool - addrField reflect.Value + addrField reflect.Value //store the original struct value uniques []string isThrough bool } // new model info -func newModelInfo(val reflect.Value) (info *modelInfo) { - - info = &modelInfo{} - info.fields = newFields() - +func newModelInfo(val reflect.Value) (mi *modelInfo) { + mi = &modelInfo{} + mi.fields = newFields() ind := reflect.Indirect(val) - typ := ind.Type() - - info.addrField = val - - info.name = typ.Name() - info.fullName = getFullName(typ) - - addModelFields(info, ind, "", []int{}) - + mi.addrField = val + mi.name = ind.Type().Name() + mi.fullName = getFullName(ind.Type()) + addModelFields(mi, ind, "", []int{}) return } -func addModelFields(info *modelInfo, ind reflect.Value, mName string, index []int) { +// index: FieldByIndex returns the nested field corresponding to index +func addModelFields(mi *modelInfo, ind reflect.Value, mName string, index []int) { var ( err error fi *fieldInfo @@ -63,43 +57,39 @@ func addModelFields(info *modelInfo, ind reflect.Value, mName string, index []in for i := 0; i < ind.NumField(); i++ { field := ind.Field(i) sf = ind.Type().Field(i) + // if the field is unexported skip if sf.PkgPath != "" { continue } // add anonymous struct fields if sf.Anonymous { - addModelFields(info, field, mName+"."+sf.Name, append(index, i)) + addModelFields(mi, field, mName+"."+sf.Name, append(index, i)) continue } - fi, err = newFieldInfo(info, field, sf, mName) - - if err != nil { - if err == errSkipField { - err = nil - continue - } + fi, err = newFieldInfo(mi, field, sf, mName) + if err == errSkipField { + err = nil + continue + } else if err != nil { break } - - added := info.fields.Add(fi) - if added == false { + //record current field index + fi.fieldIndex = append(index, i) + fi.mi = mi + fi.inModel = true + if !mi.fields.Add(fi) { err = fmt.Errorf("duplicate column name: %s", fi.column) break } - if fi.pk { - if info.fields.pk != nil { + if mi.fields.pk != nil { err = fmt.Errorf("one model must have one pk field only") break } else { - info.fields.pk = fi + mi.fields.pk = fi } } - - fi.fieldIndex = append(index, i) - fi.mi = info - fi.inModel = true } if err != nil { @@ -110,23 +100,23 @@ func addModelFields(info *modelInfo, ind reflect.Value, mName string, index []in // combine related model info to new model info. // prepare for relation models query. -func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) { - info = new(modelInfo) - info.fields = newFields() - info.table = m1.table + "_" + m2.table + "s" - info.name = camelString(info.table) - info.fullName = m1.pkg + "." + info.name +func newM2MModelInfo(m1, m2 *modelInfo) (mi *modelInfo) { + mi = new(modelInfo) + mi.fields = newFields() + mi.table = m1.table + "_" + m2.table + "s" + mi.name = camelString(mi.table) + mi.fullName = m1.pkg + "." + mi.name - fa := new(fieldInfo) - f1 := new(fieldInfo) - f2 := new(fieldInfo) + fa := new(fieldInfo) // pk + f1 := new(fieldInfo) // m1 table RelForeignKey + f2 := new(fieldInfo) // m2 table RelForeignKey fa.fieldType = TypeBigIntegerField fa.auto = true fa.pk = true fa.dbcol = true fa.name = "Id" fa.column = "id" - fa.fullName = info.fullName + "." + fa.name + fa.fullName = mi.fullName + "." + fa.name f1.dbcol = true f2.dbcol = true @@ -134,8 +124,8 @@ func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) { f2.fieldType = RelForeignKey f1.name = camelString(m1.table) f2.name = camelString(m2.table) - f1.fullName = info.fullName + "." + f1.name - f2.fullName = info.fullName + "." + f2.name + f1.fullName = mi.fullName + "." + f1.name + f2.fullName = mi.fullName + "." + f2.name f1.column = m1.table + "_id" f2.column = m2.table + "_id" f1.rel = true @@ -144,14 +134,14 @@ func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) { f2.relTable = m2.table f1.relModelInfo = m1 f2.relModelInfo = m2 - f1.mi = info - f2.mi = info + f1.mi = mi + f2.mi = mi - info.fields.Add(fa) - info.fields.Add(f1) - info.fields.Add(f2) - info.fields.pk = fa + mi.fields.Add(fa) + mi.fields.Add(f1) + mi.fields.Add(f2) + mi.fields.pk = fa - info.uniques = []string{f1.column, f2.column} + mi.uniques = []string{f1.column, f2.column} return } diff --git a/src/vendor/github.com/astaxie/beego/orm/models_test.go b/src/vendor/github.com/astaxie/beego/orm/models_test.go index ffb16ea02..9843a87de 100644 --- a/src/vendor/github.com/astaxie/beego/orm/models_test.go +++ b/src/vendor/github.com/astaxie/beego/orm/models_test.go @@ -78,40 +78,43 @@ func (e *SliceStringField) RawValue() interface{} { var _ Fielder = new(SliceStringField) // A json field. -type JSONField struct { +type JSONFieldTest struct { Name string Data string } -func (e *JSONField) String() string { +func (e *JSONFieldTest) String() string { data, _ := json.Marshal(e) return string(data) } -func (e *JSONField) FieldType() int { +func (e *JSONFieldTest) FieldType() int { return TypeTextField } -func (e *JSONField) SetRaw(value interface{}) error { +func (e *JSONFieldTest) SetRaw(value interface{}) error { switch d := value.(type) { case string: return json.Unmarshal([]byte(d), e) default: - return fmt.Errorf(" unknown value `%v`", value) + return fmt.Errorf(" unknown value `%v`", value) } } -func (e *JSONField) RawValue() interface{} { +func (e *JSONFieldTest) RawValue() interface{} { return e.String() } -var _ Fielder = new(JSONField) +var _ Fielder = new(JSONFieldTest) type Data struct { ID int `orm:"column(id)"` Boolean bool Char string `orm:"size(50)"` Text string `orm:"type(text)"` + JSON string `orm:"type(json);default({\"name\":\"json\"})"` + Jsonb string `orm:"type(jsonb)"` + Time time.Time `orm:"type(time)"` Date time.Time `orm:"type(date)"` DateTime time.Time `orm:"column(datetime)"` Byte byte @@ -136,6 +139,9 @@ type DataNull struct { Boolean bool `orm:"null"` Char string `orm:"null;size(50)"` Text string `orm:"null;type(text)"` + JSON string `orm:"type(json);null"` + Jsonb string `orm:"type(jsonb);null"` + Time time.Time `orm:"null;type(time)"` Date time.Time `orm:"null;type(date)"` DateTime time.Time `orm:"null;column(datetime)"` Byte byte `orm:"null"` @@ -175,6 +181,9 @@ type DataNull struct { Float32Ptr *float32 `orm:"null"` Float64Ptr *float64 `orm:"null"` DecimalPtr *float64 `orm:"digits(8);decimals(4);null"` + TimePtr *time.Time `orm:"null;type(time)"` + DatePtr *time.Time `orm:"null;type(date)"` + DateTimePtr *time.Time `orm:"null"` } type String string @@ -237,7 +246,7 @@ type User struct { ShouldSkip string `orm:"-"` Nums int Langs SliceStringField `orm:"size(100)"` - Extra JSONField `orm:"type(text)"` + Extra JSONFieldTest `orm:"type(text)"` unexport bool `orm:"-"` unexportBool bool } @@ -375,6 +384,33 @@ func NewInLine() *InLine { return new(InLine) } +type InLineOneToOne struct { + // Common Fields + ModelBase + + Note string + InLine *InLine `orm:"rel(fk);column(inline)"` +} + +func NewInLineOneToOne() *InLineOneToOne { + return new(InLineOneToOne) +} + +type IntegerPk struct { + ID int64 `orm:"pk"` + Value string +} + +type UintPk struct { + ID uint32 `orm:"pk"` + Name string +} + +type PtrPk struct { + ID *IntegerPk `orm:"pk;rel(one)"` + Positive bool +} + var DBARGS = struct { Driver string Source string diff --git a/src/vendor/github.com/astaxie/beego/orm/models_utils.go b/src/vendor/github.com/astaxie/beego/orm/models_utils.go index ec11d5169..44a0e76a1 100644 --- a/src/vendor/github.com/astaxie/beego/orm/models_utils.go +++ b/src/vendor/github.com/astaxie/beego/orm/models_utils.go @@ -22,25 +22,47 @@ import ( "time" ) +// 1 is attr +// 2 is tag +var supportTag = map[string]int{ + "-": 1, + "null": 1, + "index": 1, + "unique": 1, + "pk": 1, + "auto": 1, + "auto_now": 1, + "auto_now_add": 1, + "size": 2, + "column": 2, + "default": 2, + "rel": 2, + "reverse": 2, + "rel_table": 2, + "rel_through": 2, + "digits": 2, + "decimals": 2, + "on_delete": 2, + "type": 2, +} + // get reflect.Type name with package path. func getFullName(typ reflect.Type) string { return typ.PkgPath() + "." + typ.Name() } -// get table name. method, or field name. auto snaked. +// getTableName get struct table name. +// If the struct implement the TableName, then get the result as tablename +// else use the struct name which will apply snakeString. func getTableName(val reflect.Value) string { - ind := reflect.Indirect(val) - fun := val.MethodByName("TableName") - if fun.IsValid() { + if fun := val.MethodByName("TableName"); fun.IsValid() { vals := fun.Call([]reflect.Value{}) - if len(vals) > 0 { - val := vals[0] - if val.Kind() == reflect.String { - return val.String() - } + // has return and the first val is string + if len(vals) > 0 && vals[0].Kind() == reflect.String { + return vals[0].String() } } - return snakeString(ind.Type().Name()) + return snakeString(reflect.Indirect(val).Type().Name()) } // get table engine, mysiam or innodb. @@ -48,11 +70,8 @@ func getTableEngine(val reflect.Value) string { fun := val.MethodByName("TableEngine") if fun.IsValid() { vals := fun.Call([]reflect.Value{}) - if len(vals) > 0 { - val := vals[0] - if val.Kind() == reflect.String { - return val.String() - } + if len(vals) > 0 && vals[0].Kind() == reflect.String { + return vals[0].String() } } return "" @@ -63,12 +82,9 @@ func getTableIndex(val reflect.Value) [][]string { fun := val.MethodByName("TableIndex") if fun.IsValid() { vals := fun.Call([]reflect.Value{}) - if len(vals) > 0 { - val := vals[0] - if val.CanInterface() { - if d, ok := val.Interface().([][]string); ok { - return d - } + if len(vals) > 0 && vals[0].CanInterface() { + if d, ok := vals[0].Interface().([][]string); ok { + return d } } } @@ -80,12 +96,9 @@ func getTableUnique(val reflect.Value) [][]string { fun := val.MethodByName("TableUnique") if fun.IsValid() { vals := fun.Call([]reflect.Value{}) - if len(vals) > 0 { - val := vals[0] - if val.CanInterface() { - if d, ok := val.Interface().([][]string); ok { - return d - } + if len(vals) > 0 && vals[0].CanInterface() { + if d, ok := vals[0].Interface().([][]string); ok { + return d } } } @@ -137,6 +150,8 @@ func getFieldType(val reflect.Value) (ft int, err error) { ft = TypeBooleanField case reflect.TypeOf(new(string)): ft = TypeCharField + case reflect.TypeOf(new(time.Time)): + ft = TypeDateTimeField default: elm := reflect.Indirect(val) switch elm.Kind() { @@ -187,21 +202,25 @@ func getFieldType(val reflect.Value) (ft int, err error) { } // parse struct tag string -func parseStructTag(data string, attrs *map[string]bool, tags *map[string]string) { - attr := make(map[string]bool) - tag := make(map[string]string) +func parseStructTag(data string) (attrs map[string]bool, tags map[string]string) { + attrs = make(map[string]bool) + tags = make(map[string]string) for _, v := range strings.Split(data, defaultStructTagDelim) { + if v == "" { + continue + } v = strings.TrimSpace(v) - if supportTag[v] == 1 { - attr[v] = true + if t := strings.ToLower(v); supportTag[t] == 1 { + attrs[t] = true } else if i := strings.Index(v, "("); i > 0 && strings.Index(v, ")") == len(v)-1 { - name := v[:i] + name := t[:i] if supportTag[name] == 2 { v = v[i+1 : len(v)-1] - tag[name] = v + tags[name] = v } + } else { + DebugLog.Println("unsupport orm tag", v) } } - *attrs = attr - *tags = tag + return } diff --git a/src/vendor/github.com/astaxie/beego/orm/orm.go b/src/vendor/github.com/astaxie/beego/orm/orm.go index 0ffb6b869..fcf82590f 100644 --- a/src/vendor/github.com/astaxie/beego/orm/orm.go +++ b/src/vendor/github.com/astaxie/beego/orm/orm.go @@ -68,7 +68,7 @@ const ( // Define common vars var ( Debug = false - DebugLog = NewLog(os.Stderr) + DebugLog = NewLog(os.Stdout) DefaultRowsLimit = 1000 DefaultRelsDepth = 2 DefaultTimeLoc = time.Local @@ -104,10 +104,10 @@ func (o *orm) getMiInd(md interface{}, needPtr bool) (mi *modelInfo, ind reflect panic(fmt.Errorf(" cannot use non-ptr model struct `%s`", getFullName(typ))) } name := getFullName(typ) - if mi, ok := modelCache.getByFN(name); ok { + if mi, ok := modelCache.getByFullName(name); ok { return mi, ind } - panic(fmt.Errorf(" table: `%s` not found, maybe not RegisterModel", name)) + panic(fmt.Errorf(" table: `%s` not found, make sure it was registered with `RegisterModel()`", name)) } // get field info from model info by given field name @@ -122,25 +122,36 @@ func (o *orm) getFieldInfo(mi *modelInfo, name string) *fieldInfo { // read data to model func (o *orm) Read(md interface{}, cols ...string) error { mi, ind := o.getMiInd(md, true) - err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols) - if err != nil { - return err - } - return nil + return o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, false) +} + +// read data to model, like Read(), but use "SELECT FOR UPDATE" form +func (o *orm) ReadForUpdate(md interface{}, cols ...string) error { + mi, ind := o.getMiInd(md, true) + return o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, true) } // Try to read a row from the database, or insert one if it doesn't exist func (o *orm) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) { cols = append([]string{col1}, cols...) mi, ind := o.getMiInd(md, true) - err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols) + err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, false) if err == ErrNoRows { // Create id, err := o.Insert(md) return (err == nil), id, err } - return false, ind.FieldByIndex(mi.fields.pk.fieldIndex).Int(), err + id, vid := int64(0), ind.FieldByIndex(mi.fields.pk.fieldIndex) + if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { + id = int64(vid.Uint()) + } else if mi.fields.pk.rel { + return o.ReadOrCreate(vid.Interface(), mi.fields.pk.relModelInfo.fields.pk.name) + } else { + id = vid.Int() + } + + return false, id, err } // insert model data to database @@ -159,7 +170,7 @@ func (o *orm) Insert(md interface{}) (int64, error) { // set auto pk field func (o *orm) setPk(mi *modelInfo, ind reflect.Value, id int64) { if mi.fields.pk.auto { - if mi.fields.pk.fieldType&IsPostiveIntegerField > 0 { + if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(uint64(id)) } else { ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(id) @@ -184,7 +195,7 @@ func (o *orm) InsertMulti(bulk int, mds interface{}) (int64, error) { if bulk <= 1 { for i := 0; i < sind.Len(); i++ { - ind := sind.Index(i) + ind := reflect.Indirect(sind.Index(i)) mi, _ := o.getMiInd(ind.Interface(), false) id, err := o.alias.DbBaser.Insert(o.db, mi, ind, o.alias.TZ) if err != nil { @@ -202,21 +213,31 @@ func (o *orm) InsertMulti(bulk int, mds interface{}) (int64, error) { return cnt, nil } +// InsertOrUpdate data to database +func (o *orm) InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error) { + mi, ind := o.getMiInd(md, true) + id, err := o.alias.DbBaser.InsertOrUpdate(o.db, mi, ind, o.alias, colConflitAndArgs...) + if err != nil { + return id, err + } + + o.setPk(mi, ind, id) + + return id, nil +} + // update model to database. // cols set the columns those want to update. func (o *orm) Update(md interface{}, cols ...string) (int64, error) { mi, ind := o.getMiInd(md, true) - num, err := o.alias.DbBaser.Update(o.db, mi, ind, o.alias.TZ, cols) - if err != nil { - return num, err - } - return num, nil + return o.alias.DbBaser.Update(o.db, mi, ind, o.alias.TZ, cols) } // delete model in database -func (o *orm) Delete(md interface{}) (int64, error) { +// cols shows the delete conditions values read from. default is pk +func (o *orm) Delete(md interface{}, cols ...string) (int64, error) { mi, ind := o.getMiInd(md, true) - num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ) + num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ, cols) if err != nil { return num, err } @@ -328,7 +349,7 @@ func (o *orm) queryRelated(md interface{}, name string) (*modelInfo, *fieldInfo, fi := o.getFieldInfo(mi, name) _, _, exist := getExistPk(mi, ind) - if exist == false { + if !exist { panic(ErrMissPK) } @@ -399,7 +420,7 @@ func (o *orm) getRelQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet { // table name can be string or struct. // e.g. QueryTable("user"), QueryTable(&user{}) or QueryTable((*User)(nil)), func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) { - name := "" + var name string if table, ok := ptrStructOrTableName.(string); ok { name = snakeString(table) if mi, ok := modelCache.get(name); ok { @@ -407,7 +428,7 @@ func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) { } } else { name = getFullName(indirectType(reflect.TypeOf(ptrStructOrTableName))) - if mi, ok := modelCache.getByFN(name); ok { + if mi, ok := modelCache.getByFullName(name); ok { qs = newQuerySet(o, mi) } } @@ -456,7 +477,7 @@ func (o *orm) Begin() error { // commit transaction func (o *orm) Commit() error { - if o.isTx == false { + if !o.isTx { return ErrTxDone } err := o.db.(txEnder).Commit() @@ -471,7 +492,7 @@ func (o *orm) Commit() error { // rollback transaction func (o *orm) Rollback() error { - if o.isTx == false { + if !o.isTx { return ErrTxDone } err := o.db.(txEnder).Rollback() diff --git a/src/vendor/github.com/astaxie/beego/orm/orm_conds.go b/src/vendor/github.com/astaxie/beego/orm/orm_conds.go index e56d6fbbd..f6e389ec7 100644 --- a/src/vendor/github.com/astaxie/beego/orm/orm_conds.go +++ b/src/vendor/github.com/astaxie/beego/orm/orm_conds.go @@ -75,6 +75,19 @@ func (c *Condition) AndCond(cond *Condition) *Condition { return c } +// AndNotCond combine a AND NOT condition to current condition +func (c *Condition) AndNotCond(cond *Condition) *Condition { + c = c.clone() + if c == cond { + panic(fmt.Errorf(" cannot use self as sub cond")) + } + + if cond != nil { + c.params = append(c.params, condValue{cond: cond, isCond: true, isNot: true}) + } + return c +} + // Or add OR expression to condition func (c Condition) Or(expr string, args ...interface{}) *Condition { if expr == "" || len(args) == 0 { @@ -105,6 +118,19 @@ func (c *Condition) OrCond(cond *Condition) *Condition { return c } +// OrNotCond combine a OR NOT condition to current condition +func (c *Condition) OrNotCond(cond *Condition) *Condition { + c = c.clone() + if c == cond { + panic(fmt.Errorf(" cannot use self as sub cond")) + } + + if cond != nil { + c.params = append(c.params, condValue{cond: cond, isCond: true, isNot: true, isOr: true}) + } + return c +} + // IsEmpty check the condition arguments are empty or not. func (c *Condition) IsEmpty() bool { return len(c.params) == 0 diff --git a/src/vendor/github.com/astaxie/beego/orm/orm_log.go b/src/vendor/github.com/astaxie/beego/orm/orm_log.go index 712eb219f..26c73f9ee 100644 --- a/src/vendor/github.com/astaxie/beego/orm/orm_log.go +++ b/src/vendor/github.com/astaxie/beego/orm/orm_log.go @@ -31,7 +31,7 @@ type Log struct { // NewLog set io.Writer to create a Logger. func NewLog(out io.Writer) *Log { d := new(Log) - d.Logger = log.New(out, "[ORM]", 1e9) + d.Logger = log.New(out, "[ORM]", log.LstdFlags) return d } @@ -42,7 +42,7 @@ func debugLogQueies(alias *alias, operaton, query string, t time.Time, err error if err != nil { flag = "FAIL" } - con := fmt.Sprintf(" - %s - [Queries/%s] - [%s / %11s / %7.1fms] - [%s]", t.Format(formatDateTime), alias.Name, flag, operaton, elsp, query) + con := fmt.Sprintf(" -[Queries/%s] - [%s / %11s / %7.1fms] - [%s]", alias.Name, flag, operaton, elsp, query) cons := make([]string, 0, len(args)) for _, arg := range args { cons = append(cons, fmt.Sprintf("%v", arg)) diff --git a/src/vendor/github.com/astaxie/beego/orm/orm_object.go b/src/vendor/github.com/astaxie/beego/orm/orm_object.go index 8a5d85e28..de3181ce2 100644 --- a/src/vendor/github.com/astaxie/beego/orm/orm_object.go +++ b/src/vendor/github.com/astaxie/beego/orm/orm_object.go @@ -50,7 +50,7 @@ func (o *insertSet) Insert(md interface{}) (int64, error) { } if id > 0 { if o.mi.fields.pk.auto { - if o.mi.fields.pk.fieldType&IsPostiveIntegerField > 0 { + if o.mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { ind.FieldByIndex(o.mi.fields.pk.fieldIndex).SetUint(uint64(id)) } else { ind.FieldByIndex(o.mi.fields.pk.fieldIndex).SetInt(id) diff --git a/src/vendor/github.com/astaxie/beego/orm/orm_querym2m.go b/src/vendor/github.com/astaxie/beego/orm/orm_querym2m.go index b220bda6e..6a270a0d8 100644 --- a/src/vendor/github.com/astaxie/beego/orm/orm_querym2m.go +++ b/src/vendor/github.com/astaxie/beego/orm/orm_querym2m.go @@ -72,7 +72,7 @@ func (o *queryM2M) Add(mds ...interface{}) (int64, error) { } _, v1, exist := getExistPk(o.mi, o.ind) - if exist == false { + if !exist { panic(ErrMissPK) } @@ -87,7 +87,7 @@ func (o *queryM2M) Add(mds ...interface{}) (int64, error) { v2 = ind.Interface() } else { _, v2, exist = getExistPk(fi.relModelInfo, ind) - if exist == false { + if !exist { panic(ErrMissPK) } } @@ -104,11 +104,7 @@ func (o *queryM2M) Remove(mds ...interface{}) (int64, error) { fi := o.fi qs := o.qs.Filter(fi.reverseFieldInfo.name, o.md) - nums, err := qs.Filter(fi.reverseFieldInfoTwo.name+ExprSep+"in", mds).Delete() - if err != nil { - return nums, err - } - return nums, nil + return qs.Filter(fi.reverseFieldInfoTwo.name+ExprSep+"in", mds).Delete() } // check model is existed in relationship of origin model diff --git a/src/vendor/github.com/astaxie/beego/orm/orm_queryset.go b/src/vendor/github.com/astaxie/beego/orm/orm_queryset.go index 802a1fe08..4e33646d6 100644 --- a/src/vendor/github.com/astaxie/beego/orm/orm_queryset.go +++ b/src/vendor/github.com/astaxie/beego/orm/orm_queryset.go @@ -153,6 +153,11 @@ func (o querySet) SetCond(cond *Condition) QuerySeter { return &o } +// get condition from QuerySeter +func (o querySet) GetCond() *Condition { + return o.cond +} + // return QuerySeter execution result number func (o *querySet) Count() (int64, error) { return o.orm.alias.DbBaser.Count(o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ) @@ -192,16 +197,18 @@ func (o *querySet) All(container interface{}, cols ...string) (int64, error) { // query one row data and map to containers. // cols means the columns when querying. func (o *querySet) One(container interface{}, cols ...string) error { + o.limit = 1 num, err := o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols) if err != nil { return err } - if num > 1 { - return ErrMultiRows - } if num == 0 { return ErrNoRows } + + if num > 1 { + return ErrMultiRows + } return nil } diff --git a/src/vendor/github.com/astaxie/beego/orm/orm_raw.go b/src/vendor/github.com/astaxie/beego/orm/orm_raw.go index 5f88121cb..c8e741ea0 100644 --- a/src/vendor/github.com/astaxie/beego/orm/orm_raw.go +++ b/src/vendor/github.com/astaxie/beego/orm/orm_raw.go @@ -286,7 +286,7 @@ func (o *rawSet) QueryRow(containers ...interface{}) error { structMode = true fn := getFullName(typ) - if mi, ok := modelCache.getByFN(fn); ok { + if mi, ok := modelCache.getByFullName(fn); ok { sMi = mi } } else { @@ -342,19 +342,22 @@ func (o *rawSet) QueryRow(containers ...interface{}) error { for _, col := range columns { if fi := sMi.fields.GetByColumn(col); fi != nil { value := reflect.ValueOf(columnsMp[col]).Elem().Interface() - o.setFieldValue(ind.FieldByIndex(fi.fieldIndex), value) + field := ind.FieldByIndex(fi.fieldIndex) + if fi.fieldType&IsRelField > 0 { + mf := reflect.New(fi.relModelInfo.addrField.Elem().Type()) + field.Set(mf) + field = mf.Elem().FieldByIndex(fi.relModelInfo.fields.pk.fieldIndex) + } + o.setFieldValue(field, value) } } } else { for i := 0; i < ind.NumField(); i++ { f := ind.Field(i) fe := ind.Type().Field(i) - - var attrs map[string]bool - var tags map[string]string - parseStructTag(fe.Tag.Get("orm"), &attrs, &tags) + _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName)) var col string - if col = tags["column"]; len(col) == 0 { + if col = tags["column"]; col == "" { col = snakeString(fe.Name) } if v, ok := columnsMp[col]; ok { @@ -416,7 +419,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) { structMode = true fn := getFullName(typ) - if mi, ok := modelCache.getByFN(fn); ok { + if mi, ok := modelCache.getByFullName(fn); ok { sMi = mi } } else { @@ -480,26 +483,43 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) { for _, col := range columns { if fi := sMi.fields.GetByColumn(col); fi != nil { value := reflect.ValueOf(columnsMp[col]).Elem().Interface() - o.setFieldValue(ind.FieldByIndex(fi.fieldIndex), value) + field := ind.FieldByIndex(fi.fieldIndex) + if fi.fieldType&IsRelField > 0 { + mf := reflect.New(fi.relModelInfo.addrField.Elem().Type()) + field.Set(mf) + field = mf.Elem().FieldByIndex(fi.relModelInfo.fields.pk.fieldIndex) + } + o.setFieldValue(field, value) } } } else { - for i := 0; i < ind.NumField(); i++ { - f := ind.Field(i) - fe := ind.Type().Field(i) + // define recursive function + var recursiveSetField func(rv reflect.Value) + recursiveSetField = func(rv reflect.Value) { + for i := 0; i < rv.NumField(); i++ { + f := rv.Field(i) + fe := rv.Type().Field(i) - var attrs map[string]bool - var tags map[string]string - parseStructTag(fe.Tag.Get("orm"), &attrs, &tags) - var col string - if col = tags["column"]; len(col) == 0 { - col = snakeString(fe.Name) - } - if v, ok := columnsMp[col]; ok { - value := reflect.ValueOf(v).Elem().Interface() - o.setFieldValue(f, value) + // check if the field is a Struct + // recursive the Struct type + if fe.Type.Kind() == reflect.Struct { + recursiveSetField(f) + } + + _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName)) + var col string + if col = tags["column"]; col == "" { + col = snakeString(fe.Name) + } + if v, ok := columnsMp[col]; ok { + value := reflect.ValueOf(v).Elem().Interface() + o.setFieldValue(f, value) + } } } + + // init call the recursive function + recursiveSetField(ind) } if eTyps[0].Kind() == reflect.Ptr { @@ -665,7 +685,7 @@ func (o *rawSet) queryRowsTo(container interface{}, keyCol, valueCol string) (in ind *reflect.Value ) - typ := 0 + var typ int switch container.(type) { case *Params: typ = 1 diff --git a/src/vendor/github.com/astaxie/beego/orm/orm_test.go b/src/vendor/github.com/astaxie/beego/orm/orm_test.go index ec0f0d3ab..f1f2d85ec 100644 --- a/src/vendor/github.com/astaxie/beego/orm/orm_test.go +++ b/src/vendor/github.com/astaxie/beego/orm/orm_test.go @@ -19,6 +19,7 @@ import ( "database/sql" "fmt" "io/ioutil" + "math" "os" "path/filepath" "reflect" @@ -33,6 +34,7 @@ var _ = os.PathSeparator var ( testDate = formatDate + " -0700" testDateTime = formatDateTime + " -0700" + testTime = formatTime + " -0700" ) type argAny []interface{} @@ -91,14 +93,14 @@ wrongArg: } func AssertIs(a interface{}, args ...interface{}) error { - if ok, err := ValuesCompare(true, a, args...); ok == false { + if ok, err := ValuesCompare(true, a, args...); !ok { return err } return nil } func AssertNot(a interface{}, args ...interface{}) error { - if ok, err := ValuesCompare(false, a, args...); ok == false { + if ok, err := ValuesCompare(false, a, args...); !ok { return err } return nil @@ -133,7 +135,7 @@ func getCaller(skip int) string { if i := strings.LastIndex(funName, "."); i > -1 { funName = funName[i+1:] } - return fmt.Sprintf("%s:%d: \n%s", fn, line, strings.Join(codes, "\n")) + return fmt.Sprintf("%s:%s:%d: \n%s", fn, funName, line, strings.Join(codes, "\n")) } func throwFail(t *testing.T, err error, args ...interface{}) { @@ -188,6 +190,10 @@ func TestSyncDb(t *testing.T) { RegisterModel(new(Permission)) RegisterModel(new(GroupPermissions)) RegisterModel(new(InLine)) + RegisterModel(new(InLineOneToOne)) + RegisterModel(new(IntegerPk)) + RegisterModel(new(UintPk)) + RegisterModel(new(PtrPk)) err := RunSyncdb("default", true, Debug) throwFail(t, err) @@ -208,6 +214,10 @@ func TestRegisterModels(t *testing.T) { RegisterModel(new(Permission)) RegisterModel(new(GroupPermissions)) RegisterModel(new(InLine)) + RegisterModel(new(InLineOneToOne)) + RegisterModel(new(IntegerPk)) + RegisterModel(new(UintPk)) + RegisterModel(new(PtrPk)) BootStrap() @@ -219,7 +229,7 @@ func TestModelSyntax(t *testing.T) { user := &User{} ind := reflect.ValueOf(user).Elem() fn := getFullName(ind.Type()) - mi, ok := modelCache.getByFN(fn) + mi, ok := modelCache.getByFullName(fn) throwFail(t, AssertIs(ok, true)) mi, ok = modelCache.get("user") @@ -233,6 +243,9 @@ var DataValues = map[string]interface{}{ "Boolean": true, "Char": "char", "Text": "text", + "JSON": `{"name":"json"}`, + "Jsonb": `{"name": "jsonb"}`, + "Time": time.Now(), "Date": time.Now(), "DateTime": time.Now(), "Byte": byte(1<<8 - 1), @@ -257,10 +270,12 @@ func TestDataTypes(t *testing.T) { ind := reflect.Indirect(reflect.ValueOf(&d)) for name, value := range DataValues { + if name == "JSON" { + continue + } e := ind.FieldByName(name) e.Set(reflect.ValueOf(value)) } - id, err := dORM.Insert(&d) throwFail(t, err) throwFail(t, AssertIs(id, 1)) @@ -281,6 +296,9 @@ func TestDataTypes(t *testing.T) { case "DateTime": vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDateTime) value = value.(time.Time).In(DefaultTimeLoc).Format(testDateTime) + case "Time": + vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime) + value = value.(time.Time).In(DefaultTimeLoc).Format(testTime) } throwFail(t, AssertIs(vu == value, true), value, vu) } @@ -299,10 +317,18 @@ func TestNullDataTypes(t *testing.T) { throwFail(t, err) throwFail(t, AssertIs(id, 1)) + data := `{"ok":1,"data":{"arr":[1,2],"msg":"gopher"}}` + d = DataNull{ID: 1, JSON: data} + num, err := dORM.Update(&d) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) + d = DataNull{ID: 1} err = dORM.Read(&d) throwFail(t, err) + throwFail(t, AssertIs(d.JSON, data)) + throwFail(t, AssertIs(d.NullBool.Valid, false)) throwFail(t, AssertIs(d.NullString.Valid, false)) throwFail(t, AssertIs(d.NullInt64.Valid, false)) @@ -326,6 +352,9 @@ func TestNullDataTypes(t *testing.T) { throwFail(t, AssertIs(d.Float32Ptr, nil)) throwFail(t, AssertIs(d.Float64Ptr, nil)) throwFail(t, AssertIs(d.DecimalPtr, nil)) + throwFail(t, AssertIs(d.TimePtr, nil)) + throwFail(t, AssertIs(d.DatePtr, nil)) + throwFail(t, AssertIs(d.DateTimePtr, nil)) _, err = dORM.Raw(`INSERT INTO data_null (boolean) VALUES (?)`, nil).Exec() throwFail(t, err) @@ -352,6 +381,9 @@ func TestNullDataTypes(t *testing.T) { float32Ptr := float32(42.0) float64Ptr := float64(42.0) decimalPtr := float64(42.0) + timePtr := time.Now() + datePtr := time.Now() + dateTimePtr := time.Now() d = DataNull{ DateTime: time.Now(), @@ -377,6 +409,9 @@ func TestNullDataTypes(t *testing.T) { Float32Ptr: &float32Ptr, Float64Ptr: &float64Ptr, DecimalPtr: &decimalPtr, + TimePtr: &timePtr, + DatePtr: &datePtr, + DateTimePtr: &dateTimePtr, } id, err = dORM.Insert(&d) @@ -417,6 +452,9 @@ func TestNullDataTypes(t *testing.T) { throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr)) throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr)) throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr)) + throwFail(t, AssertIs((*d.TimePtr).Format(testTime), timePtr.Format(testTime))) + throwFail(t, AssertIs((*d.DatePtr).Format(testDate), datePtr.Format(testDate))) + throwFail(t, AssertIs((*d.DateTimePtr).Format(testDateTime), dateTimePtr.Format(testDateTime))) } func TestDataCustomTypes(t *testing.T) { @@ -541,6 +579,10 @@ func TestCRUD(t *testing.T) { err = dORM.Read(&ub) throwFail(t, err) throwFail(t, AssertIs(ub.Name, "name")) + + num, err = dORM.Delete(&ub, "name") + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) } func TestInsertTestData(t *testing.T) { @@ -869,6 +911,16 @@ func TestSetCond(t *testing.T) { num, err = qs.SetCond(cond2).Count() throwFail(t, err) throwFail(t, AssertIs(num, 2)) + + cond3 := cond.AndNotCond(cond.And("status__in", 1)) + num, err = qs.SetCond(cond3).Count() + throwFail(t, err) + throwFail(t, AssertIs(num, 2)) + + cond4 := cond.And("user_name", "slene").OrNotCond(cond.And("user_name", "slene")) + num, err = qs.SetCond(cond4).Count() + throwFail(t, err) + throwFail(t, AssertIs(num, 3)) } func TestLimit(t *testing.T) { @@ -962,6 +1014,8 @@ func TestAll(t *testing.T) { var users3 []*User qs = dORM.QueryTable("user") num, err = qs.Filter("user_name", "nothing").All(&users3) + throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 0)) throwFailNow(t, AssertIs(users3 == nil, false)) } @@ -969,12 +1023,19 @@ func TestOne(t *testing.T) { var user User qs := dORM.QueryTable("user") err := qs.One(&user) - throwFail(t, AssertIs(err, ErrMultiRows)) + throwFail(t, err) user = User{} err = qs.OrderBy("Id").Limit(1).One(&user) throwFailNow(t, err) throwFail(t, AssertIs(user.UserName, "slene")) + throwFail(t, AssertNot(err, ErrMultiRows)) + + user = User{} + err = qs.OrderBy("-Id").Limit(100).One(&user) + throwFailNow(t, err) + throwFail(t, AssertIs(user.UserName, "nobody")) + throwFail(t, AssertNot(err, ErrMultiRows)) err = qs.Filter("user_name", "nothing").One(&user) throwFail(t, AssertIs(err, ErrNoRows)) @@ -1079,6 +1140,7 @@ func TestRelatedSel(t *testing.T) { } err = qs.Filter("user_name", "nobody").RelatedSel("profile").One(&user) + throwFail(t, err) throwFail(t, AssertIs(num, 1)) throwFail(t, AssertIs(user.Profile, nil)) @@ -1187,20 +1249,24 @@ func TestLoadRelated(t *testing.T) { num, err = dORM.LoadRelated(&user, "Posts", true) throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 2)) throwFailNow(t, AssertIs(len(user.Posts), 2)) throwFailNow(t, AssertIs(user.Posts[0].User.UserName, "astaxie")) num, err = dORM.LoadRelated(&user, "Posts", true, 1) throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 1)) throwFailNow(t, AssertIs(len(user.Posts), 1)) num, err = dORM.LoadRelated(&user, "Posts", true, 0, 0, "-Id") throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 2)) throwFailNow(t, AssertIs(len(user.Posts), 2)) throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting")) num, err = dORM.LoadRelated(&user, "Posts", true, 1, 1, "Id") throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 1)) throwFailNow(t, AssertIs(len(user.Posts), 1)) throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting")) @@ -1514,6 +1580,7 @@ func TestRawQueryRow(t *testing.T) { Boolean bool Char string Text string + Time time.Time Date time.Time DateTime time.Time Byte byte @@ -1542,14 +1609,14 @@ func TestRawQueryRow(t *testing.T) { Q := dDbBaser.TableQuote() cols := []string{ - "id", "boolean", "char", "text", "date", "datetime", "byte", "rune", "int", "int8", "int16", "int32", + "id", "boolean", "char", "text", "time", "date", "datetime", "byte", "rune", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "decimal", } sep := fmt.Sprintf("%s, %s", Q, Q) query := fmt.Sprintf("SELECT %s%s%s FROM data WHERE id = ?", Q, strings.Join(cols, sep), Q) var id int values := []interface{}{ - &id, &Boolean, &Char, &Text, &Date, &DateTime, &Byte, &Rune, &Int, &Int8, &Int16, &Int32, + &id, &Boolean, &Char, &Text, &Time, &Date, &DateTime, &Byte, &Rune, &Int, &Int8, &Int16, &Int32, &Int64, &Uint, &Uint8, &Uint16, &Uint32, &Uint64, &Float32, &Float64, &Decimal, } err := dORM.Raw(query, 1).QueryRow(values...) @@ -1560,6 +1627,10 @@ func TestRawQueryRow(t *testing.T) { switch col { case "id": throwFail(t, AssertIs(id, 1)) + case "time": + v = v.(time.Time).In(DefaultTimeLoc) + value := dataValues[col].(time.Time).In(DefaultTimeLoc) + throwFail(t, AssertIs(v, value, testTime)) case "date": v = v.(time.Time).In(DefaultTimeLoc) value := dataValues[col].(time.Time).In(DefaultTimeLoc) @@ -1590,6 +1661,13 @@ func TestRawQueryRow(t *testing.T) { throwFail(t, AssertIs(pid, nil)) } +// user_profile table +type userProfile struct { + User + Age int + Money float64 +} + func TestQueryRows(t *testing.T) { Q := dDbBaser.TableQuote() @@ -1607,6 +1685,9 @@ func TestQueryRows(t *testing.T) { e := ind.FieldByName(name) vu := e.Interface() switch name { + case "Time": + vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime) + value = value.(time.Time).In(DefaultTimeLoc).Format(testTime) case "Date": vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate) value = value.(time.Time).In(DefaultTimeLoc).Format(testDate) @@ -1631,6 +1712,9 @@ func TestQueryRows(t *testing.T) { e := ind.FieldByName(name) vu := e.Interface() switch name { + case "Time": + vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime) + value = value.(time.Time).In(DefaultTimeLoc).Format(testTime) case "Date": vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate) value = value.(time.Time).In(DefaultTimeLoc).Format(testDate) @@ -1654,6 +1738,19 @@ func TestQueryRows(t *testing.T) { throwFailNow(t, AssertIs(usernames[1], "astaxie")) throwFailNow(t, AssertIs(ids[2], 4)) throwFailNow(t, AssertIs(usernames[2], "nobody")) + + //test query rows by nested struct + var l []userProfile + query = fmt.Sprintf("SELECT * FROM %suser_profile%s LEFT JOIN %suser%s ON %suser_profile%s.%sid%s = %suser%s.%sid%s", Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q) + num, err = dORM.Raw(query).QueryRows(&l) + throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 2)) + throwFailNow(t, AssertIs(len(l), 2)) + throwFailNow(t, AssertIs(l[0].UserName, "slene")) + throwFailNow(t, AssertIs(l[0].Age, 28)) + throwFailNow(t, AssertIs(l[1].UserName, "astaxie")) + throwFailNow(t, AssertIs(l[1].Age, 30)) + } func TestRawValues(t *testing.T) { @@ -1906,6 +2003,7 @@ func TestReadOrCreate(t *testing.T) { created, pk, err := dORM.ReadOrCreate(u, "UserName") throwFail(t, err) throwFail(t, AssertIs(created, true)) + throwFail(t, AssertIs(u.ID, pk)) throwFail(t, AssertIs(u.UserName, "Kyle")) throwFail(t, AssertIs(u.Email, "kylemcc@gmail.com")) throwFail(t, AssertIs(u.Password, "other_pass")) @@ -1952,3 +2050,299 @@ func TestInLine(t *testing.T) { throwFail(t, AssertIs(il.Created.In(DefaultTimeLoc), inline.Created.In(DefaultTimeLoc), testDate)) throwFail(t, AssertIs(il.Updated.In(DefaultTimeLoc), inline.Updated.In(DefaultTimeLoc), testDateTime)) } + +func TestInLineOneToOne(t *testing.T) { + name := "121" + email := "121@go.com" + inline := NewInLine() + inline.Name = name + inline.Email = email + + id, err := dORM.Insert(inline) + throwFail(t, err) + throwFail(t, AssertIs(id, 2)) + + note := "one2one" + il121 := NewInLineOneToOne() + il121.Note = note + il121.InLine = inline + _, err = dORM.Insert(il121) + throwFail(t, err) + throwFail(t, AssertIs(il121.ID, 1)) + + il := NewInLineOneToOne() + err = dORM.QueryTable(il).Filter("Id", 1).RelatedSel().One(il) + + throwFail(t, err) + throwFail(t, AssertIs(il.Note, note)) + throwFail(t, AssertIs(il.InLine.ID, id)) + throwFail(t, AssertIs(il.InLine.Name, name)) + throwFail(t, AssertIs(il.InLine.Email, email)) + + rinline := NewInLine() + err = dORM.QueryTable(rinline).Filter("InLineOneToOne__Id", 1).One(rinline) + + throwFail(t, err) + throwFail(t, AssertIs(rinline.ID, id)) + throwFail(t, AssertIs(rinline.Name, name)) + throwFail(t, AssertIs(rinline.Email, email)) +} + +func TestIntegerPk(t *testing.T) { + its := []IntegerPk{ + {ID: math.MinInt64, Value: "-"}, + {ID: 0, Value: "0"}, + {ID: math.MaxInt64, Value: "+"}, + } + + num, err := dORM.InsertMulti(len(its), its) + throwFail(t, err) + throwFail(t, AssertIs(num, len(its))) + + for _, intPk := range its { + out := IntegerPk{ID: intPk.ID} + err = dORM.Read(&out) + throwFail(t, err) + throwFail(t, AssertIs(out.Value, intPk.Value)) + } + + num, err = dORM.InsertMulti(1, []*IntegerPk{{ + ID: 1, Value: "ok", + }}) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) +} + +func TestInsertAuto(t *testing.T) { + u := &User{ + UserName: "autoPre", + Email: "autoPre@gmail.com", + } + + id, err := dORM.Insert(u) + throwFail(t, err) + + id += 100 + su := &User{ + ID: int(id), + UserName: "auto", + Email: "auto@gmail.com", + } + + nid, err := dORM.Insert(su) + throwFail(t, err) + throwFail(t, AssertIs(nid, id)) + + users := []User{ + {ID: int(id + 100), UserName: "auto_100"}, + {ID: int(id + 110), UserName: "auto_110"}, + {ID: int(id + 120), UserName: "auto_120"}, + } + num, err := dORM.InsertMulti(100, users) + throwFail(t, err) + throwFail(t, AssertIs(num, 3)) + + u = &User{ + UserName: "auto_121", + } + + nid, err = dORM.Insert(u) + throwFail(t, err) + throwFail(t, AssertIs(nid, id+120+1)) +} + +func TestUintPk(t *testing.T) { + name := "go" + u := &UintPk{ + ID: 8, + Name: name, + } + + created, _, err := dORM.ReadOrCreate(u, "ID") + throwFail(t, err) + throwFail(t, AssertIs(created, true)) + throwFail(t, AssertIs(u.Name, name)) + + nu := &UintPk{ID: 8} + created, pk, err := dORM.ReadOrCreate(nu, "ID") + throwFail(t, err) + throwFail(t, AssertIs(created, false)) + throwFail(t, AssertIs(nu.ID, u.ID)) + throwFail(t, AssertIs(pk, u.ID)) + throwFail(t, AssertIs(nu.Name, name)) + + dORM.Delete(u) +} + +func TestPtrPk(t *testing.T) { + parent := &IntegerPk{ID: 10, Value: "10"} + + id, _ := dORM.Insert(parent) + if !IsMysql { + // MySql does not support last_insert_id in this case: see #2382 + throwFail(t, AssertIs(id, 10)) + } + + ptr := PtrPk{ID: parent, Positive: true} + num, err := dORM.InsertMulti(2, []PtrPk{ptr}) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) + throwFail(t, AssertIs(ptr.ID, parent)) + + nptr := &PtrPk{ID: parent} + created, pk, err := dORM.ReadOrCreate(nptr, "ID") + throwFail(t, err) + throwFail(t, AssertIs(created, false)) + throwFail(t, AssertIs(pk, 10)) + throwFail(t, AssertIs(nptr.ID, parent)) + throwFail(t, AssertIs(nptr.Positive, true)) + + nptr = &PtrPk{Positive: true} + created, pk, err = dORM.ReadOrCreate(nptr, "Positive") + throwFail(t, err) + throwFail(t, AssertIs(created, false)) + throwFail(t, AssertIs(pk, 10)) + throwFail(t, AssertIs(nptr.ID, parent)) + + nptr.Positive = false + num, err = dORM.Update(nptr) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) + throwFail(t, AssertIs(nptr.ID, parent)) + throwFail(t, AssertIs(nptr.Positive, false)) + + num, err = dORM.Delete(nptr) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) +} + +func TestSnake(t *testing.T) { + cases := map[string]string{ + "i": "i", + "I": "i", + "iD": "i_d", + "ID": "i_d", + "NO": "n_o", + "NOO": "n_o_o", + "NOOooOOoo": "n_o_ooo_o_ooo", + "OrderNO": "order_n_o", + "tagName": "tag_name", + "tag_Name": "tag__name", + "tag_name": "tag_name", + "_tag_name": "_tag_name", + "tag_666name": "tag_666name", + "tag_666Name": "tag_666_name", + } + for name, want := range cases { + got := snakeString(name) + throwFail(t, AssertIs(got, want)) + } +} + +func TestIgnoreCaseTag(t *testing.T) { + type testTagModel struct { + ID int `orm:"pk"` + NOO string `orm:"column(n)"` + Name01 string `orm:"NULL"` + Name02 string `orm:"COLUMN(Name)"` + Name03 string `orm:"Column(name)"` + } + modelCache.clean() + RegisterModel(&testTagModel{}) + info, ok := modelCache.get("test_tag_model") + throwFail(t, AssertIs(ok, true)) + throwFail(t, AssertNot(info, nil)) + if t == nil { + return + } + throwFail(t, AssertIs(info.fields.GetByName("NOO").column, "n")) + throwFail(t, AssertIs(info.fields.GetByName("Name01").null, true)) + throwFail(t, AssertIs(info.fields.GetByName("Name02").column, "Name")) + throwFail(t, AssertIs(info.fields.GetByName("Name03").column, "name")) +} +func TestInsertOrUpdate(t *testing.T) { + RegisterModel(new(User)) + user := User{UserName: "unique_username133", Status: 1, Password: "o"} + user1 := User{UserName: "unique_username133", Status: 2, Password: "o"} + user2 := User{UserName: "unique_username133", Status: 3, Password: "oo"} + dORM.Insert(&user) + test := User{UserName: "unique_username133"} + fmt.Println(dORM.Driver().Name()) + if dORM.Driver().Name() == "sqlite3" { + fmt.Println("sqlite3 is nonsupport") + return + } + //test1 + _, err := dORM.InsertOrUpdate(&user1, "user_name") + if err != nil { + fmt.Println(err) + if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" { + } else { + throwFailNow(t, err) + } + } else { + dORM.Read(&test, "user_name") + throwFailNow(t, AssertIs(user1.Status, test.Status)) + } + //test2 + _, err = dORM.InsertOrUpdate(&user2, "user_name") + if err != nil { + fmt.Println(err) + if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" { + } else { + throwFailNow(t, err) + } + } else { + dORM.Read(&test, "user_name") + throwFailNow(t, AssertIs(user2.Status, test.Status)) + throwFailNow(t, AssertIs(user2.Password, strings.TrimSpace(test.Password))) + } + //test3 + + _, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status+1") + if err != nil { + fmt.Println(err) + if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" { + } else { + throwFailNow(t, err) + } + } else { + dORM.Read(&test, "user_name") + throwFailNow(t, AssertIs(user2.Status+1, test.Status)) + } + //test4 - + _, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status-1") + if err != nil { + fmt.Println(err) + if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" { + } else { + throwFailNow(t, err) + } + } else { + dORM.Read(&test, "user_name") + throwFailNow(t, AssertIs((user2.Status+1)-1, test.Status)) + } + //test5 * + _, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status*3") + if err != nil { + fmt.Println(err) + if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" { + } else { + throwFailNow(t, err) + } + } else { + dORM.Read(&test, "user_name") + throwFailNow(t, AssertIs(((user2.Status+1)-1)*3, test.Status)) + } + //test6 / + _, err = dORM.InsertOrUpdate(&user2, "user_name", "Status=Status/3") + if err != nil { + fmt.Println(err) + if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" { + } else { + throwFailNow(t, err) + } + } else { + dORM.Read(&test, "user_name") + throwFailNow(t, AssertIs((((user2.Status+1)-1)*3)/3, test.Status)) + } +} diff --git a/src/vendor/github.com/astaxie/beego/orm/qb.go b/src/vendor/github.com/astaxie/beego/orm/qb.go index 9f778916d..e0655a178 100644 --- a/src/vendor/github.com/astaxie/beego/orm/qb.go +++ b/src/vendor/github.com/astaxie/beego/orm/qb.go @@ -19,6 +19,7 @@ import "errors" // QueryBuilder is the Query builder interface type QueryBuilder interface { Select(fields ...string) QueryBuilder + ForUpdate() QueryBuilder From(tables ...string) QueryBuilder InnerJoin(table string) QueryBuilder LeftJoin(table string) QueryBuilder diff --git a/src/vendor/github.com/astaxie/beego/orm/qb_mysql.go b/src/vendor/github.com/astaxie/beego/orm/qb_mysql.go index 886bc50e3..23bdc9eef 100644 --- a/src/vendor/github.com/astaxie/beego/orm/qb_mysql.go +++ b/src/vendor/github.com/astaxie/beego/orm/qb_mysql.go @@ -34,6 +34,12 @@ func (qb *MySQLQueryBuilder) Select(fields ...string) QueryBuilder { return qb } +// ForUpdate add the FOR UPDATE clause +func (qb *MySQLQueryBuilder) ForUpdate() QueryBuilder { + qb.Tokens = append(qb.Tokens, "FOR UPDATE") + return qb +} + // From join the tables func (qb *MySQLQueryBuilder) From(tables ...string) QueryBuilder { qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, CommaSpace)) diff --git a/src/vendor/github.com/astaxie/beego/orm/qb_tidb.go b/src/vendor/github.com/astaxie/beego/orm/qb_tidb.go index c504049eb..87b3ae84f 100644 --- a/src/vendor/github.com/astaxie/beego/orm/qb_tidb.go +++ b/src/vendor/github.com/astaxie/beego/orm/qb_tidb.go @@ -31,6 +31,12 @@ func (qb *TiDBQueryBuilder) Select(fields ...string) QueryBuilder { return qb } +// ForUpdate add the FOR UPDATE clause +func (qb *TiDBQueryBuilder) ForUpdate() QueryBuilder { + qb.Tokens = append(qb.Tokens, "FOR UPDATE") + return qb +} + // From join the tables func (qb *TiDBQueryBuilder) From(tables ...string) QueryBuilder { qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, CommaSpace)) diff --git a/src/vendor/github.com/astaxie/beego/orm/types.go b/src/vendor/github.com/astaxie/beego/orm/types.go index 41933dd15..3e6a9e87d 100644 --- a/src/vendor/github.com/astaxie/beego/orm/types.go +++ b/src/vendor/github.com/astaxie/beego/orm/types.go @@ -45,6 +45,9 @@ type Ormer interface { // u = &User{UserName: "astaxie", Password: "pass"} // err = Ormer.Read(u, "UserName") Read(md interface{}, cols ...string) error + // Like Read(), but with "FOR UPDATE" clause, useful in transaction. + // Some databases are not support this feature. + ReadForUpdate(md interface{}, cols ...string) error // Try to read a row from the database, or insert one if it doesn't exist ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) // insert model data to database @@ -53,6 +56,11 @@ type Ormer interface { // id, err = Ormer.Insert(user) // user must a pointer and Insert will set user's pk field Insert(interface{}) (int64, error) + // mysql:InsertOrUpdate(model) or InsertOrUpdate(model,"colu=colu+value") + // if colu type is integer : can use(+-*/), string : convert(colu,"value") + // postgres: InsertOrUpdate(model,"conflictColumnName") or InsertOrUpdate(model,"conflictColumnName","colu=colu+value") + // if colu type is integer : can use(+-*/), string : colu || "value" + InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error) // insert some models to database InsertMulti(bulk int, mds interface{}) (int64, error) // update model to database. @@ -66,7 +74,7 @@ type Ormer interface { // num, err = Ormer.Update(&user, "Langs", "Extra") Update(md interface{}, cols ...string) (int64, error) // delete model in database - Delete(md interface{}) (int64, error) + Delete(md interface{}, cols ...string) (int64, error) // load related models to md model. // args are limit, offset int and order string. // @@ -137,6 +145,16 @@ type QuerySeter interface { // //sql-> WHERE T0.`profile_id` IS NOT NULL AND NOT T0.`Status` IN (?) OR T1.`age` > 2000 // num, err := qs.SetCond(cond1).Count() SetCond(*Condition) QuerySeter + // get condition from QuerySeter. + // sql's where condition + // cond := orm.NewCondition() + // cond = cond.And("profile__isnull", false).AndNot("status__in", 1) + // qs = qs.SetCond(cond) + // cond = qs.GetCond() + // cond := cond.Or("profile__age__gt", 2000) + // //sql-> WHERE T0.`profile_id` IS NOT NULL AND NOT T0.`Status` IN (?) OR T1.`age` > 2000 + // num, err := qs.SetCond(cond).Count() + GetCond() *Condition // add LIMIT value. // args[0] means offset, e.g. LIMIT num,offset. // if Limit <= 0 then Limit will be set to default limit ,eg 1000 @@ -389,13 +407,14 @@ type txEnder interface { // base database struct type dbBaser interface { - Read(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) error + Read(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string, bool) error Insert(dbQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error) + InsertOrUpdate(dbQuerier, *modelInfo, reflect.Value, *alias, ...string) (int64, error) InsertMulti(dbQuerier, *modelInfo, reflect.Value, int, *time.Location) (int64, error) InsertValue(dbQuerier, *modelInfo, bool, []string, []interface{}) (int64, error) InsertStmt(stmtQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error) Update(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) (int64, error) - Delete(dbQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error) + Delete(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) (int64, error) ReadBatch(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, *time.Location, []string) (int64, error) SupportUpdateJoin() bool UpdateBatch(dbQuerier, *querySet, *modelInfo, *Condition, Params, *time.Location) (int64, error) @@ -420,4 +439,5 @@ type dbBaser interface { ShowColumnsQuery(string) string IndexExists(dbQuerier, string, string) bool collectFieldValue(*modelInfo, *fieldInfo, reflect.Value, bool, *time.Location) (interface{}, error) + setval(dbQuerier, *modelInfo, []string) error } diff --git a/src/vendor/github.com/astaxie/beego/orm/utils.go b/src/vendor/github.com/astaxie/beego/orm/utils.go index 99437c7b0..669d47344 100644 --- a/src/vendor/github.com/astaxie/beego/orm/utils.go +++ b/src/vendor/github.com/astaxie/beego/orm/utils.go @@ -16,6 +16,7 @@ package orm import ( "fmt" + "math/big" "reflect" "strconv" "strings" @@ -87,7 +88,15 @@ func (f StrTo) Int32() (int32, error) { // Int64 string to int64 func (f StrTo) Int64() (int64, error) { v, err := strconv.ParseInt(f.String(), 10, 64) - return int64(v), err + if err != nil { + i := new(big.Int) + ni, ok := i.SetString(f.String(), 10) // octal + if !ok { + return v, err + } + return ni.Int64(), nil + } + return v, err } // Uint string to uint @@ -117,7 +126,15 @@ func (f StrTo) Uint32() (uint32, error) { // Uint64 string to uint64 func (f StrTo) Uint64() (uint64, error) { v, err := strconv.ParseUint(f.String(), 10, 64) - return uint64(v), err + if err != nil { + i := new(big.Int) + ni, ok := i.SetString(f.String(), 10) + if !ok { + return v, err + } + return ni.Uint64(), nil + } + return v, err } // String string to string @@ -181,7 +198,7 @@ func ToInt64(value interface{}) (d int64) { return } -// snake string, XxYy to xx_yy +// snake string, XxYy to xx_yy , XxYY to xx_yy func snakeString(s string) string { data := make([]byte, 0, len(s)*2) j := false @@ -202,22 +219,17 @@ func snakeString(s string) string { // camel string, xx_yy to XxYy func camelString(s string) string { data := make([]byte, 0, len(s)) - j := false - k := false - num := len(s) - 1 + flag, num := true, len(s)-1 for i := 0; i <= num; i++ { d := s[i] - if k == false && d >= 'A' && d <= 'Z' { - k = true - } - if d >= 'a' && d <= 'z' && (j || k == false) { - d = d - 32 - j = false - k = true - } - if k && d == '_' && num > i && s[i+1] >= 'a' && s[i+1] <= 'z' { - j = true + if d == '_' { + flag = true continue + } else if flag { + if d >= 'a' && d <= 'z' { + d = d - 32 + } + flag = false } data = append(data, d) } diff --git a/src/vendor/github.com/astaxie/beego/docs.go b/src/vendor/github.com/astaxie/beego/orm/utils_test.go similarity index 56% rename from src/vendor/github.com/astaxie/beego/docs.go rename to src/vendor/github.com/astaxie/beego/orm/utils_test.go index 725328760..8c7c50086 100644 --- a/src/vendor/github.com/astaxie/beego/docs.go +++ b/src/vendor/github.com/astaxie/beego/orm/utils_test.go @@ -12,28 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package orm import ( - "github.com/astaxie/beego/context" + "testing" ) -// GlobalDocAPI store the swagger api documents -var GlobalDocAPI = make(map[string]interface{}) +func TestCamelString(t *testing.T) { + snake := []string{"pic_url", "hello_world_", "hello__World", "_HelLO_Word", "pic_url_1", "pic_url__1"} + camel := []string{"PicUrl", "HelloWorld", "HelloWorld", "HelLOWord", "PicUrl1", "PicUrl1"} -func serverDocs(ctx *context.Context) { - var obj interface{} - if splat := ctx.Input.Param(":splat"); splat == "" { - obj = GlobalDocAPI["Root"] - } else { - if v, ok := GlobalDocAPI[splat]; ok { - obj = v + answer := make(map[string]string) + for i, v := range snake { + answer[v] = camel[i] + } + + for _, v := range snake { + res := camelString(v) + if res != answer[v] { + t.Error("Unit Test Fail:", v, res, answer[v]) } } - if obj != nil { - ctx.Output.Header("Access-Control-Allow-Origin", "*") - ctx.Output.JSON(obj, false, false) - return - } - ctx.Output.SetStatus(404) } diff --git a/src/vendor/github.com/astaxie/beego/parser.go b/src/vendor/github.com/astaxie/beego/parser.go index 46d023201..f787fd5cd 100644 --- a/src/vendor/github.com/astaxie/beego/parser.go +++ b/src/vendor/github.com/astaxie/beego/parser.go @@ -23,10 +23,15 @@ import ( "go/token" "io/ioutil" "os" - "path" + "path/filepath" + "regexp" "sort" + "strconv" "strings" + "unicode" + "github.com/astaxie/beego/context/param" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/utils" ) @@ -34,6 +39,7 @@ var globalRouterTemplate = `package routers import ( "github.com/astaxie/beego" + "github.com/astaxie/beego/context/param" ) func init() { @@ -48,17 +54,18 @@ var ( genInfoList map[string][]ControllerComments ) -const coomentPrefix = "commentsRouter_" +const commentPrefix = "commentsRouter_" func init() { pkgLastupdate = make(map[string]int64) } func parserPkg(pkgRealpath, pkgpath string) error { - rep := strings.NewReplacer("/", "_", ".", "_") - commentFilename = coomentPrefix + rep.Replace(pkgpath) + ".go" + rep := strings.NewReplacer("\\", "_", "/", "_", ".", "_") + commentFilename, _ = filepath.Rel(AppPath, pkgRealpath) + commentFilename = commentPrefix + rep.Replace(commentFilename) + ".go" if !compareFile(pkgRealpath) { - Info(pkgRealpath + " no changed") + logs.Info(pkgRealpath + " no changed") return nil } genInfoList = make(map[string][]ControllerComments) @@ -79,59 +86,185 @@ func parserPkg(pkgRealpath, pkgpath string) error { if specDecl.Recv != nil { exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser if ok { - parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(exp.X), pkgpath) + parserComments(specDecl, fmt.Sprint(exp.X), pkgpath) } } } } } } - genRouterCode() + genRouterCode(pkgRealpath) savetoFile(pkgRealpath) return nil } -func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error { - if comments != nil && comments.List != nil { - for _, c := range comments.List { - t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) - if strings.HasPrefix(t, "@router") { - elements := strings.TrimLeft(t, "@router ") - e1 := strings.SplitN(elements, " ", 2) - if len(e1) < 1 { - return errors.New("you should has router infomation") - } - key := pkgpath + ":" + controllerName - cc := ControllerComments{} - cc.Method = funcName - cc.Router = e1[0] - if len(e1) == 2 && e1[1] != "" { - e1 = strings.SplitN(e1[1], " ", 2) - if len(e1) >= 1 { - cc.AllowHTTPMethods = strings.Split(strings.Trim(e1[0], "[]"), ",") - } else { - cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get") - } - } else { - cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get") - } - if len(e1) == 2 && e1[1] != "" { - keyval := strings.Split(strings.Trim(e1[1], "[]"), " ") - for _, kv := range keyval { - kk := strings.Split(kv, ":") - cc.Params = append(cc.Params, map[string]string{strings.Join(kk[:len(kk)-1], ":"): kk[len(kk)-1]}) - } - } - genInfoList[key] = append(genInfoList[key], cc) - } +type parsedComment struct { + routerPath string + methods []string + params map[string]parsedParam +} + +type parsedParam struct { + name string + datatype string + location string + defValue string + required bool +} + +func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { + if f.Doc != nil { + parsedComment, err := parseComment(f.Doc.List) + if err != nil { + return err } + if parsedComment.routerPath != "" { + key := pkgpath + ":" + controllerName + cc := ControllerComments{} + cc.Method = f.Name.String() + cc.Router = parsedComment.routerPath + cc.AllowHTTPMethods = parsedComment.methods + cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment) + genInfoList[key] = append(genInfoList[key], cc) + } + } return nil } -func genRouterCode() { - os.Mkdir(path.Join(AppPath, "routers"), 0755) - Info("generate router from comments") +func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam { + result := make([]*param.MethodParam, 0, len(funcParams)) + for _, fparam := range funcParams { + for _, pName := range fparam.Names { + methodParam := buildMethodParam(fparam, pName.Name, pc) + result = append(result, methodParam) + } + } + return result +} + +func buildMethodParam(fparam *ast.Field, name string, pc *parsedComment) *param.MethodParam { + options := []param.MethodParamOption{} + if cparam, ok := pc.params[name]; ok { + //Build param from comment info + name = cparam.name + if cparam.required { + options = append(options, param.IsRequired) + } + switch cparam.location { + case "body": + options = append(options, param.InBody) + case "header": + options = append(options, param.InHeader) + case "path": + options = append(options, param.InPath) + } + if cparam.defValue != "" { + options = append(options, param.Default(cparam.defValue)) + } + } else { + if paramInPath(name, pc.routerPath) { + options = append(options, param.InPath) + } + } + return param.New(name, options...) +} + +func paramInPath(name, route string) bool { + return strings.HasSuffix(route, ":"+name) || + strings.Contains(route, ":"+name+"/") +} + +var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`) + +func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) { + pc = &parsedComment{} + for _, c := range lines { + t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) + if strings.HasPrefix(t, "@router") { + matches := routeRegex.FindStringSubmatch(t) + if len(matches) == 3 { + pc.routerPath = matches[1] + methods := matches[2] + if methods == "" { + pc.methods = []string{"get"} + //pc.hasGet = true + } else { + pc.methods = strings.Split(methods, ",") + //pc.hasGet = strings.Contains(methods, "get") + } + } else { + return nil, errors.New("Router information is missing") + } + } else if strings.HasPrefix(t, "@Param") { + pv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Param"))) + if len(pv) < 4 { + logs.Error("Invalid @Param format. Needs at least 4 parameters") + } + p := parsedParam{} + names := strings.SplitN(pv[0], "=>", 2) + p.name = names[0] + funcParamName := p.name + if len(names) > 1 { + funcParamName = names[1] + } + p.location = pv[1] + p.datatype = pv[2] + switch len(pv) { + case 5: + p.required, _ = strconv.ParseBool(pv[3]) + case 6: + p.defValue = pv[3] + p.required, _ = strconv.ParseBool(pv[4]) + } + if pc.params == nil { + pc.params = map[string]parsedParam{} + } + pc.params[funcParamName] = p + } + } + return +} + +// direct copy from bee\g_docs.go +// analisys params return []string +// @Param query form string true "The email for login" +// [query form string true "The email for login"] +func getparams(str string) []string { + var s []rune + var j int + var start bool + var r []string + var quoted int8 + for _, c := range str { + if unicode.IsSpace(c) && quoted == 0 { + if !start { + continue + } else { + start = false + j++ + r = append(r, string(s)) + s = make([]rune, 0) + continue + } + } + + start = true + if c == '"' { + quoted ^= 1 + continue + } + s = append(s, c) + } + if len(s) > 0 { + r = append(r, string(s)) + } + return r +} + +func genRouterCode(pkgRealpath string) { + os.Mkdir(getRouterDir(pkgRealpath), 0755) + logs.Info("generate router from comments") var ( globalinfo string sortKey []string @@ -142,6 +275,7 @@ func genRouterCode() { sort.Strings(sortKey) for _, k := range sortKey { cList := genInfoList[k] + sort.Sort(ControllerCommentsSlice(cList)) for _, c := range cList { allmethod := "nil" if len(c.AllowHTTPMethods) > 0 { @@ -161,18 +295,30 @@ func genRouterCode() { } params = strings.TrimRight(params, ",") + "}" } + methodParams := "param.Make(" + if len(c.MethodParams) > 0 { + lines := make([]string, 0, len(c.MethodParams)) + for _, m := range c.MethodParams { + lines = append(lines, fmt.Sprint(m)) + } + methodParams += "\n " + + strings.Join(lines, ",\n ") + + ",\n " + } + methodParams += ")" globalinfo = globalinfo + ` beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"], beego.ControllerComments{ - "` + strings.TrimSpace(c.Method) + `", - ` + "`" + c.Router + "`" + `, - ` + allmethod + `, - ` + params + `}) + Method: "` + strings.TrimSpace(c.Method) + `", + ` + "Router: `" + c.Router + "`" + `, + AllowHTTPMethods: ` + allmethod + `, + MethodParams: ` + methodParams + `, + Params: ` + params + `}) ` } } if globalinfo != "" { - f, err := os.Create(path.Join(AppPath, "routers", commentFilename)) + f, err := os.Create(filepath.Join(getRouterDir(pkgRealpath), commentFilename)) if err != nil { panic(err) } @@ -182,7 +328,7 @@ func genRouterCode() { } func compareFile(pkgRealpath string) bool { - if !utils.FileExists(path.Join(AppPath, "routers", commentFilename)) { + if !utils.FileExists(filepath.Join(getRouterDir(pkgRealpath), commentFilename)) { return true } if utils.FileExists(lastupdateFilename) { @@ -229,3 +375,19 @@ func getpathTime(pkgRealpath string) (lastupdate int64, err error) { } return lastupdate, nil } + +func getRouterDir(pkgRealpath string) string { + dir := filepath.Dir(pkgRealpath) + for { + d := filepath.Join(dir, "routers") + if utils.FileExists(d) { + return d + } + + if r, _ := filepath.Rel(dir, AppPath); r == "." { + return d + } + // Parent dir. + dir = filepath.Dir(dir) + } +} diff --git a/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go b/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go index 8af08088d..f816029c3 100644 --- a/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go +++ b/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go @@ -35,7 +35,7 @@ // // beego.InsertFilter("*", beego.BeforeRouter,apiauth.APISecretAuth(getAppSecret, 360)) // -// Infomation: +// Information: // // In the request user should include these params in the query // @@ -56,6 +56,7 @@ package apiauth import ( + "bytes" "crypto/hmac" "crypto/sha256" "encoding/base64" @@ -119,7 +120,7 @@ func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc { return } if ctx.Input.Query("signature") != - Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.URI()) { + Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.URL()) { ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("auth failed") } @@ -127,54 +128,33 @@ func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc { } // Signature used to generate signature with the appsecret/method/params/RequestURI -func Signature(appsecret, method string, params url.Values, RequestURI string) (result string) { - var query string +func Signature(appsecret, method string, params url.Values, RequestURL string) (result string) { + var b bytes.Buffer + keys := make([]string, len(params)) pa := make(map[string]string) for k, v := range params { pa[k] = v[0] + keys = append(keys, k) } - vs := mapSorter(pa) - vs.Sort() - for i := 0; i < vs.Len(); i++ { - if vs.Keys[i] == "signature" { + + sort.Strings(keys) + + for _, key := range keys { + if key == "signature" { continue } - if vs.Keys[i] != "" && vs.Vals[i] != "" { - query = fmt.Sprintf("%v%v%v", query, vs.Keys[i], vs.Vals[i]) + + val := pa[key] + if key != "" && val != "" { + b.WriteString(key) + b.WriteString(val) } } - stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, query, RequestURI) + + stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, b.String(), RequestURL) sha256 := sha256.New hash := hmac.New(sha256, []byte(appsecret)) hash.Write([]byte(stringToSign)) return base64.StdEncoding.EncodeToString(hash.Sum(nil)) } - -type valSorter struct { - Keys []string - Vals []string -} - -func mapSorter(m map[string]string) *valSorter { - vs := &valSorter{ - Keys: make([]string, 0, len(m)), - Vals: make([]string, 0, len(m)), - } - for k, v := range m { - vs.Keys = append(vs.Keys, k) - vs.Vals = append(vs.Vals, v) - } - return vs -} - -func (vs *valSorter) Sort() { - sort.Sort(vs) -} - -func (vs *valSorter) Len() int { return len(vs.Keys) } -func (vs *valSorter) Less(i, j int) bool { return vs.Keys[i] < vs.Keys[j] } -func (vs *valSorter) Swap(i, j int) { - vs.Vals[i], vs.Vals[j] = vs.Vals[j], vs.Vals[i] - vs.Keys[i], vs.Keys[j] = vs.Keys[j], vs.Keys[i] -} diff --git a/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth_test.go b/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth_test.go new file mode 100644 index 000000000..1f56cb0fa --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/plugins/apiauth/apiauth_test.go @@ -0,0 +1,20 @@ +package apiauth + +import ( + "net/url" + "testing" +) + +func TestSignature(t *testing.T) { + appsecret := "beego secret" + method := "GET" + RequestURL := "http://localhost/test/url" + params := make(url.Values) + params.Add("arg1", "hello") + params.Add("arg2", "beego") + + signature := "mFdpvLh48ca4mDVEItE9++AKKQ/IVca7O/ZyyB8hR58=" + if Signature(appsecret, method, params, RequestURL) != signature { + t.Error("Signature error") + } +} diff --git a/src/vendor/github.com/astaxie/beego/plugins/authz/authz.go b/src/vendor/github.com/astaxie/beego/plugins/authz/authz.go new file mode 100644 index 000000000..9dc0db76e --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/plugins/authz/authz.go @@ -0,0 +1,86 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package authz provides handlers to enable ACL, RBAC, ABAC authorization support. +// Simple Usage: +// import( +// "github.com/astaxie/beego" +// "github.com/astaxie/beego/plugins/authz" +// "github.com/casbin/casbin" +// ) +// +// func main(){ +// // mediate the access for every request +// beego.InsertFilter("*", beego.BeforeRouter, authz.NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv"))) +// beego.Run() +// } +// +// +// Advanced Usage: +// +// func main(){ +// e := casbin.NewEnforcer("authz_model.conf", "") +// e.AddRoleForUser("alice", "admin") +// e.AddPolicy(...) +// +// beego.InsertFilter("*", beego.BeforeRouter, authz.NewAuthorizer(e)) +// beego.Run() +// } +package authz + +import ( + "github.com/astaxie/beego" + "github.com/astaxie/beego/context" + "github.com/casbin/casbin" + "net/http" +) + +// NewAuthorizer returns the authorizer. +// Use a casbin enforcer as input +func NewAuthorizer(e *casbin.Enforcer) beego.FilterFunc { + return func(ctx *context.Context) { + a := &BasicAuthorizer{enforcer: e} + + if !a.CheckPermission(ctx.Request) { + a.RequirePermission(ctx.ResponseWriter) + } + } +} + +// BasicAuthorizer stores the casbin handler +type BasicAuthorizer struct { + enforcer *casbin.Enforcer +} + +// GetUserName gets the user name from the request. +// Currently, only HTTP basic authentication is supported +func (a *BasicAuthorizer) GetUserName(r *http.Request) string { + username, _, _ := r.BasicAuth() + return username +} + +// CheckPermission checks the user/method/path combination from the request. +// Returns true (permission granted) or false (permission forbidden) +func (a *BasicAuthorizer) CheckPermission(r *http.Request) bool { + user := a.GetUserName(r) + method := r.Method + path := r.URL.Path + return a.enforcer.Enforce(user, path, method) +} + +// RequirePermission returns the 403 Forbidden to the client +func (a *BasicAuthorizer) RequirePermission(w http.ResponseWriter) { + w.WriteHeader(403) + w.Write([]byte("403 Forbidden\n")) +} diff --git a/src/vendor/github.com/astaxie/beego/plugins/authz/authz_model.conf b/src/vendor/github.com/astaxie/beego/plugins/authz/authz_model.conf new file mode 100644 index 000000000..d1b3dbd7a --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/plugins/authz/authz_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*") \ No newline at end of file diff --git a/src/vendor/github.com/astaxie/beego/plugins/authz/authz_policy.csv b/src/vendor/github.com/astaxie/beego/plugins/authz/authz_policy.csv new file mode 100644 index 000000000..c062dd3e2 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/plugins/authz/authz_policy.csv @@ -0,0 +1,7 @@ +p, alice, /dataset1/*, GET +p, alice, /dataset1/resource1, POST +p, bob, /dataset2/resource1, * +p, bob, /dataset2/resource2, GET +p, bob, /dataset2/folder1/*, POST +p, dataset1_admin, /dataset1/*, * +g, cathy, dataset1_admin \ No newline at end of file diff --git a/src/vendor/github.com/astaxie/beego/plugins/authz/authz_test.go b/src/vendor/github.com/astaxie/beego/plugins/authz/authz_test.go new file mode 100644 index 000000000..49aed84ce --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/plugins/authz/authz_test.go @@ -0,0 +1,107 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package authz + +import ( + "github.com/astaxie/beego" + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/plugins/auth" + "github.com/casbin/casbin" + "net/http" + "net/http/httptest" + "testing" +) + +func testRequest(t *testing.T, handler *beego.ControllerRegister, user string, path string, method string, code int) { + r, _ := http.NewRequest(method, path, nil) + r.SetBasicAuth(user, "123") + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + if w.Code != code { + t.Errorf("%s, %s, %s: %d, supposed to be %d", user, path, method, w.Code, code) + } +} + +func TestBasic(t *testing.T) { + handler := beego.NewControllerRegister() + + handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("alice", "123")) + handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv"))) + + handler.Any("*", func(ctx *context.Context) { + ctx.Output.SetStatus(200) + }) + + testRequest(t, handler, "alice", "/dataset1/resource1", "GET", 200) + testRequest(t, handler, "alice", "/dataset1/resource1", "POST", 200) + testRequest(t, handler, "alice", "/dataset1/resource2", "GET", 200) + testRequest(t, handler, "alice", "/dataset1/resource2", "POST", 403) +} + +func TestPathWildcard(t *testing.T) { + handler := beego.NewControllerRegister() + + handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("bob", "123")) + handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv"))) + + handler.Any("*", func(ctx *context.Context) { + ctx.Output.SetStatus(200) + }) + + testRequest(t, handler, "bob", "/dataset2/resource1", "GET", 200) + testRequest(t, handler, "bob", "/dataset2/resource1", "POST", 200) + testRequest(t, handler, "bob", "/dataset2/resource1", "DELETE", 200) + testRequest(t, handler, "bob", "/dataset2/resource2", "GET", 200) + testRequest(t, handler, "bob", "/dataset2/resource2", "POST", 403) + testRequest(t, handler, "bob", "/dataset2/resource2", "DELETE", 403) + + testRequest(t, handler, "bob", "/dataset2/folder1/item1", "GET", 403) + testRequest(t, handler, "bob", "/dataset2/folder1/item1", "POST", 200) + testRequest(t, handler, "bob", "/dataset2/folder1/item1", "DELETE", 403) + testRequest(t, handler, "bob", "/dataset2/folder1/item2", "GET", 403) + testRequest(t, handler, "bob", "/dataset2/folder1/item2", "POST", 200) + testRequest(t, handler, "bob", "/dataset2/folder1/item2", "DELETE", 403) +} + +func TestRBAC(t *testing.T) { + handler := beego.NewControllerRegister() + + handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("cathy", "123")) + e := casbin.NewEnforcer("authz_model.conf", "authz_policy.csv") + handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(e)) + + handler.Any("*", func(ctx *context.Context) { + ctx.Output.SetStatus(200) + }) + + // cathy can access all /dataset1/* resources via all methods because it has the dataset1_admin role. + testRequest(t, handler, "cathy", "/dataset1/item", "GET", 200) + testRequest(t, handler, "cathy", "/dataset1/item", "POST", 200) + testRequest(t, handler, "cathy", "/dataset1/item", "DELETE", 200) + testRequest(t, handler, "cathy", "/dataset2/item", "GET", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "POST", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "DELETE", 403) + + // delete all roles on user cathy, so cathy cannot access any resources now. + e.DeleteRolesForUser("cathy") + + testRequest(t, handler, "cathy", "/dataset1/item", "GET", 403) + testRequest(t, handler, "cathy", "/dataset1/item", "POST", 403) + testRequest(t, handler, "cathy", "/dataset1/item", "DELETE", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "GET", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "POST", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "DELETE", 403) +} diff --git a/src/vendor/github.com/astaxie/beego/policy.go b/src/vendor/github.com/astaxie/beego/policy.go new file mode 100644 index 000000000..ab23f927a --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/policy.go @@ -0,0 +1,97 @@ +// Copyright 2016 beego authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package beego + +import ( + "strings" + + "github.com/astaxie/beego/context" +) + +// PolicyFunc defines a policy function which is invoked before the controller handler is executed. +type PolicyFunc func(*context.Context) + +// FindPolicy Find Router info for URL +func (p *ControllerRegister) FindPolicy(cont *context.Context) []PolicyFunc { + var urlPath = cont.Input.URL() + if !BConfig.RouterCaseSensitive { + urlPath = strings.ToLower(urlPath) + } + httpMethod := cont.Input.Method() + isWildcard := false + // Find policy for current method + t, ok := p.policies[httpMethod] + // If not found - find policy for whole controller + if !ok { + t, ok = p.policies["*"] + isWildcard = true + } + if ok { + runObjects := t.Match(urlPath, cont) + if r, ok := runObjects.([]PolicyFunc); ok { + return r + } else if !isWildcard { + // If no policies found and we checked not for "*" method - try to find it + t, ok = p.policies["*"] + if ok { + runObjects = t.Match(urlPath, cont) + if r, ok = runObjects.([]PolicyFunc); ok { + return r + } + } + } + } + return nil +} + +func (p *ControllerRegister) addToPolicy(method, pattern string, r ...PolicyFunc) { + method = strings.ToUpper(method) + p.enablePolicy = true + if !BConfig.RouterCaseSensitive { + pattern = strings.ToLower(pattern) + } + if t, ok := p.policies[method]; ok { + t.AddRouter(pattern, r) + } else { + t := NewTree() + t.AddRouter(pattern, r) + p.policies[method] = t + } +} + +// Policy Register new policy in beego +func Policy(pattern, method string, policy ...PolicyFunc) { + BeeApp.Handlers.addToPolicy(method, pattern, policy...) +} + +// Find policies and execute if were found +func (p *ControllerRegister) execPolicy(cont *context.Context, urlPath string) (started bool) { + if !p.enablePolicy { + return false + } + // Find Policy for method + policyList := p.FindPolicy(cont) + if len(policyList) > 0 { + // Run policies + for _, runPolicy := range policyList { + runPolicy(cont) + if cont.ResponseWriter.Started { + return true + } + } + return false + } + return false +} diff --git a/src/vendor/github.com/astaxie/beego/router.go b/src/vendor/github.com/astaxie/beego/router.go index d0bf534f2..e5a4e80de 100644 --- a/src/vendor/github.com/astaxie/beego/router.go +++ b/src/vendor/github.com/astaxie/beego/router.go @@ -17,7 +17,6 @@ package beego import ( "fmt" "net/http" - "os" "path" "path/filepath" "reflect" @@ -28,6 +27,8 @@ import ( "time" beecontext "github.com/astaxie/beego/context" + "github.com/astaxie/beego/context/param" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/utils" ) @@ -50,15 +51,22 @@ const ( var ( // HTTPMETHOD list the supported http methods. HTTPMETHOD = map[string]string{ - "GET": "GET", - "POST": "POST", - "PUT": "PUT", - "DELETE": "DELETE", - "PATCH": "PATCH", - "OPTIONS": "OPTIONS", - "HEAD": "HEAD", - "TRACE": "TRACE", - "CONNECT": "CONNECT", + "GET": "GET", + "POST": "POST", + "PUT": "PUT", + "DELETE": "DELETE", + "PATCH": "PATCH", + "OPTIONS": "OPTIONS", + "HEAD": "HEAD", + "TRACE": "TRACE", + "CONNECT": "CONNECT", + "MKCOL": "MKCOL", + "COPY": "COPY", + "MOVE": "MOVE", + "PROPFIND": "PROPFIND", + "PROPPATCH": "PROPPATCH", + "LOCK": "LOCK", + "UNLOCK": "UNLOCK", } // these beego.Controller's methods shouldn't reflect to AutoRouter exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString", @@ -101,28 +109,32 @@ func ExceptMethodAppend(action string) { exceptMethod = append(exceptMethod, action) } -type controllerInfo struct { +// ControllerInfo holds information about the controller. +type ControllerInfo struct { pattern string controllerType reflect.Type methods map[string]string handler http.Handler runFunction FilterFunc routerType int + methodParams []*param.MethodParam } // ControllerRegister containers registered router rules, controller handlers and filters. type ControllerRegister struct { routers map[string]*Tree + enablePolicy bool + policies map[string]*Tree enableFilter bool - filters map[int][]*FilterRouter + filters [FinishRouter + 1][]*FilterRouter pool sync.Pool } // NewControllerRegister returns a new ControllerRegister. func NewControllerRegister() *ControllerRegister { cr := &ControllerRegister{ - routers: make(map[string]*Tree), - filters: make(map[int][]*FilterRouter), + routers: make(map[string]*Tree), + policies: make(map[string]*Tree), } cr.pool.New = func() interface{} { return beecontext.NewContext() @@ -141,6 +153,10 @@ func NewControllerRegister() *ControllerRegister { // Add("/api",&RestController{},"get,post:ApiFunc" // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) { + p.addWithMethodParams(pattern, c, nil, mappingMethods...) +} + +func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, mappingMethods ...string) { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() methods := make(map[string]string) @@ -166,11 +182,12 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM } } - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.methods = methods route.routerType = routerTypeBeego route.controllerType = t + route.methodParams = methodParams if len(methods) == 0 { for _, m := range HTTPMETHOD { p.addToRouter(m, pattern, route) @@ -188,7 +205,7 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM } } -func (p *ControllerRegister) addToRouter(method, pattern string, r *controllerInfo) { +func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) { if !BConfig.RouterCaseSensitive { pattern = strings.ToLower(pattern) } @@ -209,13 +226,11 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { for _, c := range cList { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() - gopath := os.Getenv("GOPATH") - if gopath == "" { + wgopath := utils.GetGOPATHs() + if len(wgopath) == 0 { panic("you are in dev mode. So please set gopath") } pkgpath := "" - - wgopath := filepath.SplitList(gopath) for _, wg := range wgopath { wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath())) if utils.FileExists(wg) { @@ -237,7 +252,7 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { key := t.PkgPath() + ":" + t.Name() if comm, ok := GlobalControllerRouter[key]; ok { for _, a := range comm { - p.Add(a.Router, c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method) + p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method) } } } @@ -325,7 +340,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { if _, ok := HTTPMETHOD[method]; method != "*" && !ok { panic("not support http method: " + method) } - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.routerType = routerTypeRESTFul route.runFunction = f @@ -351,7 +366,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { // Handler add user defined Handler func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) { - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.routerType = routerTypeHandler route.handler = h @@ -386,7 +401,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) controllerName := strings.TrimSuffix(ct.Name(), "Controller") for i := 0; i < rt.NumMethod(); i++ { if !utils.InSlice(rt.Method(i).Name, exceptMethod) { - route := &controllerInfo{} + route := &ControllerInfo{} route.routerType = routerTypeBeego route.methods = map[string]string{"*": rt.Method(i).Name} route.controllerType = ct @@ -406,29 +421,39 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) } // InsertFilter Add a FilterFunc with pattern rule and action constant. -// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) +// params is for: +// 1. setting the returnOnOutput value (false allows multiple filters to execute) +// 2. determining whether or not params need to be reset. func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error { - - mr := new(FilterRouter) - mr.tree = NewTree() - mr.pattern = pattern - mr.filterFunc = filter - if !BConfig.RouterCaseSensitive { - pattern = strings.ToLower(pattern) + mr := &FilterRouter{ + tree: NewTree(), + pattern: pattern, + filterFunc: filter, + returnOnOutput: true, } - if len(params) == 0 { - mr.returnOnOutput = true - } else { + if !BConfig.RouterCaseSensitive { + mr.pattern = strings.ToLower(pattern) + } + + paramsLen := len(params) + if paramsLen > 0 { mr.returnOnOutput = params[0] } + if paramsLen > 1 { + mr.resetParams = params[1] + } mr.tree.AddRouter(pattern, true) return p.insertFilterRouter(pos, mr) } // add Filter into -func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) error { - p.filters[pos] = append(p.filters[pos], mr) +func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) { + if pos < BeforeStatic || pos > FinishRouter { + err = fmt.Errorf("can not find your filter position") + return + } p.enableFilter = true + p.filters[pos] = append(p.filters[pos], mr) return nil } @@ -437,11 +462,11 @@ func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) error func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) string { paths := strings.Split(endpoint, ".") if len(paths) <= 1 { - Warn("urlfor endpoint must like path.controller.method") + logs.Warn("urlfor endpoint must like path.controller.method") return "" } if len(values)%2 != 0 { - Warn("urlfor params must key-value pair") + logs.Warn("urlfor params must key-value pair") return "" } params := make(map[string]string) @@ -482,7 +507,7 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin } } for _, l := range t.leaves { - if c, ok := l.runObject.(*controllerInfo); ok { + if c, ok := l.runObject.(*ControllerInfo); ok { if c.routerType == routerTypeBeego && strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) { find := false @@ -577,21 +602,27 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin return false, "" } -func (p *ControllerRegister) execFilter(context *beecontext.Context, pos int, urlPath string) (started bool) { - if p.enableFilter { - if l, ok := p.filters[pos]; ok { - for _, filterR := range l { - if filterR.returnOnOutput && context.ResponseWriter.Started { - return true - } - if ok := filterR.ValidRouter(urlPath, context); ok { - filterR.filterFunc(context) - } - if filterR.returnOnOutput && context.ResponseWriter.Started { - return true +func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath string, pos int) (started bool) { + var preFilterParams map[string]string + for _, filterR := range p.filters[pos] { + if filterR.returnOnOutput && context.ResponseWriter.Started { + return true + } + if filterR.resetParams { + preFilterParams = context.Input.Params() + } + if ok := filterR.ValidRouter(urlPath, context); ok { + filterR.filterFunc(context) + if filterR.resetParams { + context.Input.ResetParams() + for k, v := range preFilterParams { + context.Input.SetParam(k, v) } } } + if filterR.returnOnOutput && context.ResponseWriter.Started { + return true + } } return false } @@ -600,16 +631,20 @@ func (p *ControllerRegister) execFilter(context *beecontext.Context, pos int, ur func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) { startTime := time.Now() var ( - runRouter reflect.Type - findRouter bool - runMethod string - routerInfo *controllerInfo + runRouter reflect.Type + findRouter bool + runMethod string + methodParams []*param.MethodParam + routerInfo *ControllerInfo + isRunnable bool ) context := p.pool.Get().(*beecontext.Context) context.Reset(rw, r) defer p.pool.Put(context) - defer p.recoverPanic(context) + if BConfig.RecoverFunc != nil { + defer BConfig.RecoverFunc(context) + } context.Output.EnableGzip = BConfig.EnableGzip @@ -617,11 +652,10 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Output.Header("Server", BConfig.ServerName) } - var urlPath string + var urlPath = r.URL.Path + if !BConfig.RouterCaseSensitive { - urlPath = strings.ToLower(r.URL.Path) - } else { - urlPath = r.URL.Path + urlPath = strings.ToLower(urlPath) } // filter wrong http method @@ -631,17 +665,18 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) } // filter for static file - if p.execFilter(context, BeforeStatic, urlPath) { + if len(p.filters[BeforeStatic]) > 0 && p.execFilter(context, urlPath, BeforeStatic) { goto Admin } serverStaticRouter(context) + if context.ResponseWriter.Started { findRouter = true goto Admin } - if r.Method != "GET" && r.Method != "HEAD" { + if r.Method != http.MethodGet && r.Method != http.MethodHead { if BConfig.CopyRequestBody && !context.Input.IsUpload() { context.Input.CopyBody(BConfig.MaxMemory) } @@ -653,9 +688,9 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) var err error context.Input.CruSession, err = GlobalSessions.SessionStart(rw, r) if err != nil { - Error(err) + logs.Error(err) exception("503", context) - return + goto Admin } defer func() { if context.Input.CruSession != nil { @@ -663,26 +698,16 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) } }() } - - if p.execFilter(context, BeforeRouter, urlPath) { + if len(p.filters[BeforeRouter]) > 0 && p.execFilter(context, urlPath, BeforeRouter) { goto Admin } - - if !findRouter { - httpMethod := r.Method - if t, ok := p.routers[httpMethod]; ok { - runObject := t.Match(urlPath, context) - if r, ok := runObject.(*controllerInfo); ok { - routerInfo = r - findRouter = true - if splat := context.Input.Param(":splat"); splat != "" { - for k, v := range strings.Split(splat, "/") { - context.Input.SetParam(strconv.Itoa(k), v) - } - } - } - } - + // User can define RunController and RunMethod in filter + if context.Input.RunController != nil && context.Input.RunMethod != "" { + findRouter = true + runMethod = context.Input.RunMethod + runRouter = context.Input.RunController + } else { + routerInfo, findRouter = p.FindRouter(context) } //if no matches to url, throw a not found exception @@ -690,122 +715,148 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) exception("404", context) goto Admin } - - if findRouter { - //execute middleware filters - if p.execFilter(context, BeforeExec, urlPath) { - goto Admin - } - isRunnable := false - if routerInfo != nil { - if routerInfo.routerType == routerTypeRESTFul { - if _, ok := routerInfo.methods[r.Method]; ok { - isRunnable = true - routerInfo.runFunction(context) - } else { - exception("405", context) - goto Admin - } - } else if routerInfo.routerType == routerTypeHandler { - isRunnable = true - routerInfo.handler.ServeHTTP(rw, r) - } else { - runRouter = routerInfo.controllerType - method := r.Method - if r.Method == "POST" && context.Input.Query("_method") == "PUT" { - method = "PUT" - } - if r.Method == "POST" && context.Input.Query("_method") == "DELETE" { - method = "DELETE" - } - if m, ok := routerInfo.methods[method]; ok { - runMethod = m - } else if m, ok = routerInfo.methods["*"]; ok { - runMethod = m - } else { - runMethod = method - } - } - } - - // also defined runRouter & runMethod from filter - if !isRunnable { - //Invoke the request handler - vc := reflect.New(runRouter) - execController, ok := vc.Interface().(ControllerInterface) - if !ok { - panic("controller is not ControllerInterface") - } - - //call the controller init function - execController.Init(context, runRouter.Name(), runMethod, vc.Interface()) - - //call prepare function - execController.Prepare() - - //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf - if BConfig.WebConfig.EnableXSRF { - execController.XSRFToken() - if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || - (r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) { - execController.CheckXSRFCookie() - } - } - - execController.URLMapping() - - if !context.ResponseWriter.Started { - //exec main logic - switch runMethod { - case "GET": - execController.Get() - case "POST": - execController.Post() - case "DELETE": - execController.Delete() - case "PUT": - execController.Put() - case "HEAD": - execController.Head() - case "PATCH": - execController.Patch() - case "OPTIONS": - execController.Options() - default: - if !execController.HandlerFunc(runMethod) { - var in []reflect.Value - method := vc.MethodByName(runMethod) - method.Call(in) - } - } - - //render template - if !context.ResponseWriter.Started && context.Output.Status == 0 { - if BConfig.WebConfig.AutoRender { - if err := execController.Render(); err != nil { - panic(err) - } - } - } - } - - // finish all runRouter. release resource - execController.Finish() - } - - //execute middleware filters - if p.execFilter(context, AfterExec, urlPath) { - goto Admin + if splat := context.Input.Param(":splat"); splat != "" { + for k, v := range strings.Split(splat, "/") { + context.Input.SetParam(strconv.Itoa(k), v) } } - p.execFilter(context, FinishRouter, urlPath) + //execute middleware filters + if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) { + goto Admin + } + + //check policies + if p.execPolicy(context, urlPath) { + goto Admin + } + + if routerInfo != nil { + //store router pattern into context + context.Input.SetData("RouterPattern", routerInfo.pattern) + if routerInfo.routerType == routerTypeRESTFul { + if _, ok := routerInfo.methods[r.Method]; ok { + isRunnable = true + routerInfo.runFunction(context) + } else { + exception("405", context) + goto Admin + } + } else if routerInfo.routerType == routerTypeHandler { + isRunnable = true + routerInfo.handler.ServeHTTP(rw, r) + } else { + runRouter = routerInfo.controllerType + methodParams = routerInfo.methodParams + method := r.Method + if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost { + method = http.MethodPut + } + if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete { + method = http.MethodDelete + } + if m, ok := routerInfo.methods[method]; ok { + runMethod = m + } else if m, ok = routerInfo.methods["*"]; ok { + runMethod = m + } else { + runMethod = method + } + } + } + + // also defined runRouter & runMethod from filter + if !isRunnable { + //Invoke the request handler + vc := reflect.New(runRouter) + execController, ok := vc.Interface().(ControllerInterface) + if !ok { + panic("controller is not ControllerInterface") + } + + //call the controller init function + execController.Init(context, runRouter.Name(), runMethod, vc.Interface()) + + //call prepare function + execController.Prepare() + + //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf + if BConfig.WebConfig.EnableXSRF { + execController.XSRFToken() + if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut || + (r.Method == http.MethodPost && (context.Input.Query("_method") == http.MethodDelete || context.Input.Query("_method") == http.MethodPut)) { + execController.CheckXSRFCookie() + } + } + + execController.URLMapping() + + if !context.ResponseWriter.Started { + //exec main logic + switch runMethod { + case http.MethodGet: + execController.Get() + case http.MethodPost: + execController.Post() + case http.MethodDelete: + execController.Delete() + case http.MethodPut: + execController.Put() + case http.MethodHead: + execController.Head() + case http.MethodPatch: + execController.Patch() + case http.MethodOptions: + execController.Options() + default: + if !execController.HandlerFunc(runMethod) { + method := vc.MethodByName(runMethod) + in := param.ConvertParams(methodParams, method.Type(), context) + out := method.Call(in) + + //For backward compatibility we only handle response if we had incoming methodParams + if methodParams != nil { + p.handleParamResponse(context, execController, out) + } + } + } + + //render template + if !context.ResponseWriter.Started && context.Output.Status == 0 { + if BConfig.WebConfig.AutoRender { + if err := execController.Render(); err != nil { + logs.Error(err) + } + } + } + } + + // finish all runRouter. release resource + execController.Finish() + } + + //execute middleware filters + if len(p.filters[AfterExec]) > 0 && p.execFilter(context, urlPath, AfterExec) { + goto Admin + } + + if len(p.filters[FinishRouter]) > 0 && p.execFilter(context, urlPath, FinishRouter) { + goto Admin + } Admin: - timeDur := time.Since(startTime) //admin module record QPS if BConfig.Listen.EnableAdmin { - if FilterMonitorFunc(r.Method, r.URL.Path, timeDur) { + timeDur := time.Since(startTime) + pattern := "" + if routerInfo != nil { + pattern = routerInfo.pattern + } + statusCode := context.ResponseWriter.Status + if statusCode == 0 { + statusCode = 200 + } + if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) { if runRouter != nil { go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur) } else { @@ -815,18 +866,36 @@ Admin: } if BConfig.RunMode == DEV || BConfig.Log.AccessLogs { + timeDur := time.Since(startTime) var devInfo string + + statusCode := context.ResponseWriter.Status + if statusCode == 0 { + statusCode = 200 + } + + iswin := (runtime.GOOS == "windows") + statusColor := logs.ColorByStatus(iswin, statusCode) + methodColor := logs.ColorByMethod(iswin, r.Method) + resetColor := logs.ColorByMethod(iswin, "") + if findRouter { if routerInfo != nil { - devInfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s | % -40s |", r.Method, r.URL.Path, timeDur.String(), "match", routerInfo.pattern) + devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode, + resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path, + routerInfo.pattern) } else { - devInfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeDur.String(), "match") + devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor, + timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path) } } else { - devInfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeDur.String(), "notmatch") + devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor, + timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path) } - if DefaultAccessLogFilter == nil || !DefaultAccessLogFilter.Filter(context) { - Debug(devInfo) + if iswin { + logs.W32Debug(devInfo) + } else { + logs.Debug(devInfo) } } @@ -836,36 +905,34 @@ Admin: } } -func (p *ControllerRegister) recoverPanic(context *beecontext.Context) { - if err := recover(); err != nil { - if err == ErrAbort { - return - } - if !BConfig.RecoverPanic { - panic(err) - } else { - if BConfig.EnableErrorsShow { - if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { - exception(fmt.Sprint(err), context) - return - } - } - var stack string - Critical("the request url is ", context.Input.URL()) - Critical("Handler crashed with error", err) - for i := 1; ; i++ { - _, file, line, ok := runtime.Caller(i) - if !ok { - break - } - Critical(fmt.Sprintf("%s:%d", file, line)) - stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) - } - if BConfig.RunMode == DEV { - showErr(err, context, stack) - } +func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, execController ControllerInterface, results []reflect.Value) { + //looping in reverse order for the case when both error and value are returned and error sets the response status code + for i := len(results) - 1; i >= 0; i-- { + result := results[i] + if result.Kind() != reflect.Interface || !result.IsNil() { + resultValue := result.Interface() + context.RenderMethodResult(resultValue) } } + if !context.ResponseWriter.Started && context.Output.Status == 0 { + context.Output.SetStatus(200) + } +} + +// FindRouter Find Router info for URL +func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *ControllerInfo, isFind bool) { + var urlPath = context.Input.URL() + if !BConfig.RouterCaseSensitive { + urlPath = strings.ToLower(urlPath) + } + httpMethod := context.Input.Method() + if t, ok := p.routers[httpMethod]; ok { + runObject := t.Match(urlPath, context) + if r, ok := runObject.(*ControllerInfo); ok { + return r, true + } + } + return } func toURL(params map[string]string) string { diff --git a/src/vendor/github.com/astaxie/beego/router_test.go b/src/vendor/github.com/astaxie/beego/router_test.go index f26f0c86b..720b4ca8c 100644 --- a/src/vendor/github.com/astaxie/beego/router_test.go +++ b/src/vendor/github.com/astaxie/beego/router_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" ) type TestController struct { @@ -94,7 +95,7 @@ func TestUrlFor(t *testing.T) { handler.Add("/api/list", &TestController{}, "*:List") handler.Add("/person/:last/:first", &TestController{}, "*:Param") if a := handler.URLFor("TestController.List"); a != "/api/list" { - Info(a) + logs.Info(a) t.Errorf("TestController.List must equal to /api/list") } if a := handler.URLFor("TestController.Param", ":last", "xie", ":first", "asta"); a != "/person/xie/asta" { @@ -120,24 +121,24 @@ func TestUrlFor2(t *testing.T) { handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, "*:Param") handler.Add("/:year:int/:month:int/:title/:entid", &TestController{}) if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" { - Info(handler.URLFor("TestController.GetURL")) + logs.Info(handler.URLFor("TestController.GetURL")) t.Errorf("TestController.List must equal to /v1/astaxie/edit") } if handler.URLFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") != "/v1/za/cms_12_123.html" { - Info(handler.URLFor("TestController.List")) + logs.Info(handler.URLFor("TestController.List")) t.Errorf("TestController.List must equal to /v1/za/cms_12_123.html") } if handler.URLFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") != "/v1/za_cms/ttt_12_123.html" { - Info(handler.URLFor("TestController.Param")) + logs.Info(handler.URLFor("TestController.Param")) t.Errorf("TestController.List must equal to /v1/za_cms/ttt_12_123.html") } if handler.URLFor("TestController.Get", ":year", "1111", ":month", "11", ":title", "aaaa", ":entid", "aaaa") != "/1111/11/aaaa/aaaa" { - Info(handler.URLFor("TestController.Get")) + logs.Info(handler.URLFor("TestController.Get")) t.Errorf("TestController.Get must equal to /1111/11/aaaa/aaaa") } } @@ -419,6 +420,74 @@ func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request return recorder, request } +// Expectation: A Filter with the correct configuration should be created given +// specific parameters. +func TestInsertFilter(t *testing.T) { + testName := "TestInsertFilter" + + mux := NewControllerRegister() + mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}) + if !mux.filters[BeforeRouter][0].returnOnOutput { + t.Errorf( + "%s: passing no variadic params should set returnOnOutput to true", + testName) + } + if mux.filters[BeforeRouter][0].resetParams { + t.Errorf( + "%s: passing no variadic params should set resetParams to false", + testName) + } + + mux = NewControllerRegister() + mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, false) + if mux.filters[BeforeRouter][0].returnOnOutput { + t.Errorf( + "%s: passing false as 1st variadic param should set returnOnOutput to false", + testName) + } + + mux = NewControllerRegister() + mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, true, true) + if !mux.filters[BeforeRouter][0].resetParams { + t.Errorf( + "%s: passing true as 2nd variadic param should set resetParams to true", + testName) + } +} + +// Expectation: the second variadic arg should cause the execution of the filter +// to preserve the parameters from before its execution. +func TestParamResetFilter(t *testing.T) { + testName := "TestParamResetFilter" + route := "/beego/*" // splat + path := "/beego/routes/routes" + + mux := NewControllerRegister() + + mux.InsertFilter("*", BeforeExec, beegoResetParams, true, true) + + mux.Get(route, beegoHandleResetParams) + + rw, r := testRequest("GET", path) + mux.ServeHTTP(rw, r) + + // The two functions, `beegoResetParams` and `beegoHandleResetParams` add + // a response header of `Splat`. The expectation here is that that Header + // value should match what the _request's_ router set, not the filter's. + + headers := rw.HeaderMap + if len(headers["Splat"]) != 1 { + t.Errorf( + "%s: There was an error in the test. Splat param not set in Header", + testName) + } + if headers["Splat"][0] != "routes/routes" { + t.Errorf( + "%s: expected `:splat` param to be [routes/routes] but it was [%s]", + testName, headers["Splat"][0]) + } +} + // Execution point: BeforeRouter // expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle func TestFilterBeforeRouter(t *testing.T) { @@ -433,10 +502,10 @@ func TestFilterBeforeRouter(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "BeforeRouter1") == false { + if !strings.Contains(rw.Body.String(), "BeforeRouter1") { t.Errorf(testName + " BeforeRouter did not run") } - if strings.Contains(rw.Body.String(), "hello") == true { + if strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " BeforeRouter did not return properly") } } @@ -456,13 +525,13 @@ func TestFilterBeforeExec(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "BeforeExec1") == false { + if !strings.Contains(rw.Body.String(), "BeforeExec1") { t.Errorf(testName + " BeforeExec did not run") } - if strings.Contains(rw.Body.String(), "hello") == true { + if strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " BeforeExec did not return properly") } - if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + if strings.Contains(rw.Body.String(), "BeforeRouter") { t.Errorf(testName + " BeforeRouter ran in error") } } @@ -483,16 +552,16 @@ func TestFilterAfterExec(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "AfterExec1") == false { + if !strings.Contains(rw.Body.String(), "AfterExec1") { t.Errorf(testName + " AfterExec did not run") } - if strings.Contains(rw.Body.String(), "hello") == false { + if !strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " handler did not run properly") } - if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + if strings.Contains(rw.Body.String(), "BeforeRouter") { t.Errorf(testName + " BeforeRouter ran in error") } - if strings.Contains(rw.Body.String(), "BeforeExec") == true { + if strings.Contains(rw.Body.String(), "BeforeExec") { t.Errorf(testName + " BeforeExec ran in error") } } @@ -514,19 +583,19 @@ func TestFilterFinishRouter(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "FinishRouter1") == true { + if strings.Contains(rw.Body.String(), "FinishRouter1") { t.Errorf(testName + " FinishRouter did not run") } - if strings.Contains(rw.Body.String(), "hello") == false { + if !strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " handler did not run properly") } - if strings.Contains(rw.Body.String(), "AfterExec1") == true { + if strings.Contains(rw.Body.String(), "AfterExec1") { t.Errorf(testName + " AfterExec ran in error") } - if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + if strings.Contains(rw.Body.String(), "BeforeRouter") { t.Errorf(testName + " BeforeRouter ran in error") } - if strings.Contains(rw.Body.String(), "BeforeExec") == true { + if strings.Contains(rw.Body.String(), "BeforeExec") { t.Errorf(testName + " BeforeExec ran in error") } } @@ -546,14 +615,14 @@ func TestFilterFinishRouterMultiFirstOnly(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "FinishRouter1") == false { + if !strings.Contains(rw.Body.String(), "FinishRouter1") { t.Errorf(testName + " FinishRouter1 did not run") } - if strings.Contains(rw.Body.String(), "hello") == false { + if !strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " handler did not run properly") } // not expected in body - if strings.Contains(rw.Body.String(), "FinishRouter2") == true { + if strings.Contains(rw.Body.String(), "FinishRouter2") { t.Errorf(testName + " FinishRouter2 did run") } } @@ -573,41 +642,56 @@ func TestFilterFinishRouterMulti(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "FinishRouter1") == false { + if !strings.Contains(rw.Body.String(), "FinishRouter1") { t.Errorf(testName + " FinishRouter1 did not run") } - if strings.Contains(rw.Body.String(), "hello") == false { + if !strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " handler did not run properly") } - if strings.Contains(rw.Body.String(), "FinishRouter2") == false { + if !strings.Contains(rw.Body.String(), "FinishRouter2") { t.Errorf(testName + " FinishRouter2 did not run properly") } } func beegoFilterNoOutput(ctx *context.Context) { - return } + func beegoBeforeRouter1(ctx *context.Context) { ctx.WriteString("|BeforeRouter1") } + func beegoBeforeRouter2(ctx *context.Context) { ctx.WriteString("|BeforeRouter2") } + func beegoBeforeExec1(ctx *context.Context) { ctx.WriteString("|BeforeExec1") } + func beegoBeforeExec2(ctx *context.Context) { ctx.WriteString("|BeforeExec2") } + func beegoAfterExec1(ctx *context.Context) { ctx.WriteString("|AfterExec1") } + func beegoAfterExec2(ctx *context.Context) { ctx.WriteString("|AfterExec2") } + func beegoFinishRouter1(ctx *context.Context) { ctx.WriteString("|FinishRouter1") } + func beegoFinishRouter2(ctx *context.Context) { ctx.WriteString("|FinishRouter2") } + +func beegoResetParams(ctx *context.Context) { + ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat")) +} + +func beegoHandleResetParams(ctx *context.Context) { + ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat")) +} diff --git a/src/vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go b/src/vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go index d5be11d0b..707d042c5 100644 --- a/src/vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go +++ b/src/vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go @@ -155,11 +155,16 @@ func (cp *Provider) SessionInit(maxlifetime int64, savePath string) error { func (cp *Provider) SessionRead(sid string) (session.Store, error) { cp.b = cp.getBucket() - var doc []byte + var ( + kv map[interface{}]interface{} + err error + doc []byte + ) - err := cp.b.Get(sid, &doc) - var kv map[interface{}]interface{} - if doc == nil { + err = cp.b.Get(sid, &doc) + if err != nil { + return nil, err + } else if doc == nil { kv = make(map[interface{}]interface{}) } else { kv, err = session.DecodeGob(doc) @@ -230,7 +235,6 @@ func (cp *Provider) SessionDestroy(sid string) error { // SessionGC Recycle func (cp *Provider) SessionGC() { - return } // SessionAll return all active session diff --git a/src/vendor/github.com/astaxie/beego/session/ledis/ledis_session.go b/src/vendor/github.com/astaxie/beego/session/ledis/ledis_session.go index 68f37b08c..77685d1e2 100644 --- a/src/vendor/github.com/astaxie/beego/session/ledis/ledis_session.go +++ b/src/vendor/github.com/astaxie/beego/session/ledis/ledis_session.go @@ -12,8 +12,10 @@ import ( "github.com/siddontang/ledisdb/ledis" ) -var ledispder = &Provider{} -var c *ledis.DB +var ( + ledispder = &Provider{} + c *ledis.DB +) // SessionStore ledis session store type SessionStore struct { @@ -97,27 +99,33 @@ func (lp *Provider) SessionInit(maxlifetime int64, savePath string) error { } cfg := new(config.Config) cfg.DataDir = lp.savePath - nowLedis, err := ledis.Open(cfg) - c, err = nowLedis.Select(lp.db) + + var ledisInstance *ledis.Ledis + ledisInstance, err = ledis.Open(cfg) if err != nil { - println(err) - return nil + return err } - return nil + c, err = ledisInstance.Select(lp.db) + return err } // SessionRead read ledis session by sid func (lp *Provider) SessionRead(sid string) (session.Store, error) { - kvs, err := c.Get([]byte(sid)) - var kv map[interface{}]interface{} + var ( + kv map[interface{}]interface{} + err error + ) + + kvs, _ := c.Get([]byte(sid)) + if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { - kv, err = session.DecodeGob(kvs) - if err != nil { + if kv, err = session.DecodeGob(kvs); err != nil { return nil, err } } + ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} return ls, nil } @@ -125,10 +133,7 @@ func (lp *Provider) SessionRead(sid string) (session.Store, error) { // SessionExist check ledis session exist by sid func (lp *Provider) SessionExist(sid string) bool { count, _ := c.Exists([]byte(sid)) - if count == 0 { - return false - } - return true + return !(count == 0) } // SessionRegenerate generate new sid for ledis session @@ -145,18 +150,7 @@ func (lp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) c.Set([]byte(sid), data) c.Expire([]byte(sid), lp.maxlifetime) } - kvs, err := c.Get([]byte(sid)) - var kv map[interface{}]interface{} - if len(kvs) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob([]byte(kvs)) - if err != nil { - return nil, err - } - } - ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} - return ls, nil + return lp.SessionRead(sid) } // SessionDestroy delete ledis session by id @@ -167,7 +161,6 @@ func (lp *Provider) SessionDestroy(sid string) error { // SessionGC Impelment method, no used. func (lp *Provider) SessionGC() { - return } // SessionAll return all active session diff --git a/src/vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go b/src/vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go index f1069bc93..755979c42 100644 --- a/src/vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go +++ b/src/vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go @@ -205,11 +205,7 @@ func (rp *MemProvider) SessionDestroy(sid string) error { } } - err := client.Delete(sid) - if err != nil { - return err - } - return nil + return client.Delete(sid) } func (rp *MemProvider) connectInit() error { @@ -219,7 +215,6 @@ func (rp *MemProvider) connectInit() error { // SessionGC Impelment method, no used. func (rp *MemProvider) SessionGC() { - return } // SessionAll return all activeSession diff --git a/src/vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go b/src/vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go index 969d26c97..4c9251e72 100644 --- a/src/vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go +++ b/src/vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go @@ -115,7 +115,6 @@ func (st *SessionStore) SessionRelease(w http.ResponseWriter) { } st.c.Exec("UPDATE "+TableName+" set `session_data`=?, `session_expiry`=? where session_key=?", b, time.Now().Unix(), st.sid) - } // Provider mysql session provider @@ -171,10 +170,7 @@ func (mp *Provider) SessionExist(sid string) bool { row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid) var sessiondata []byte err := row.Scan(&sessiondata) - if err == sql.ErrNoRows { - return false - } - return true + return !(err == sql.ErrNoRows) } // SessionRegenerate generate new sid for mysql session @@ -213,7 +209,6 @@ func (mp *Provider) SessionGC() { c := mp.connectInit() c.Exec("DELETE from "+TableName+" where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) c.Close() - return } // SessionAll count values in mysql session diff --git a/src/vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go b/src/vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go index 73f9c13a9..ffc27defb 100644 --- a/src/vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go +++ b/src/vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go @@ -184,11 +184,7 @@ func (mp *Provider) SessionExist(sid string) bool { row := c.QueryRow("select session_data from session where session_key=$1", sid) var sessiondata []byte err := row.Scan(&sessiondata) - - if err == sql.ErrNoRows { - return false - } - return true + return !(err == sql.ErrNoRows) } // SessionRegenerate generate new sid for postgresql session @@ -228,7 +224,6 @@ func (mp *Provider) SessionGC() { c := mp.connectInit() c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime) c.Close() - return } // SessionAll count values in postgresql session diff --git a/src/vendor/github.com/astaxie/beego/session/redis/sess_redis.go b/src/vendor/github.com/astaxie/beego/session/redis/sess_redis.go index c46fa7cdf..d0424515d 100644 --- a/src/vendor/github.com/astaxie/beego/session/redis/sess_redis.go +++ b/src/vendor/github.com/astaxie/beego/session/redis/sess_redis.go @@ -128,7 +128,7 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { } if len(configs) > 1 { poolsize, err := strconv.Atoi(configs[1]) - if err != nil || poolsize <= 0 { + if err != nil || poolsize < 0 { rp.poolsize = MaxPoolSize } else { rp.poolsize = poolsize @@ -155,7 +155,7 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { return nil, err } if rp.password != "" { - if _, err := c.Do("AUTH", rp.password); err != nil { + if _, err = c.Do("AUTH", rp.password); err != nil { c.Close() return nil, err } @@ -176,13 +176,16 @@ func (rp *Provider) SessionRead(sid string) (session.Store, error) { c := rp.poollist.Get() defer c.Close() - kvs, err := redis.String(c.Do("GET", sid)) var kv map[interface{}]interface{} + + kvs, err := redis.String(c.Do("GET", sid)) + if err != nil && err != redis.ErrNil { + return nil, err + } if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { - kv, err = session.DecodeGob([]byte(kvs)) - if err != nil { + if kv, err = session.DecodeGob([]byte(kvs)); err != nil { return nil, err } } @@ -216,20 +219,7 @@ func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) c.Do("RENAME", oldsid, sid) c.Do("EXPIRE", sid, rp.maxlifetime) } - - kvs, err := redis.String(c.Do("GET", sid)) - var kv map[interface{}]interface{} - if len(kvs) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob([]byte(kvs)) - if err != nil { - return nil, err - } - } - - rs := &SessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime} - return rs, nil + return rp.SessionRead(sid) } // SessionDestroy delete redis session by id @@ -243,7 +233,6 @@ func (rp *Provider) SessionDestroy(sid string) error { // SessionGC Impelment method, no used. func (rp *Provider) SessionGC() { - return } // SessionAll return all activeSession diff --git a/src/vendor/github.com/astaxie/beego/session/sess_cookie.go b/src/vendor/github.com/astaxie/beego/session/sess_cookie.go index 3fefa360f..145e53c9b 100644 --- a/src/vendor/github.com/astaxie/beego/session/sess_cookie.go +++ b/src/vendor/github.com/astaxie/beego/session/sess_cookie.go @@ -74,21 +74,16 @@ func (st *CookieSessionStore) SessionID() string { // SessionRelease Write cookie session to http response cookie func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) { - str, err := encodeCookie(cookiepder.block, - cookiepder.config.SecurityKey, - cookiepder.config.SecurityName, - st.values) - if err != nil { - return + encodedCookie, err := encodeCookie(cookiepder.block, cookiepder.config.SecurityKey, cookiepder.config.SecurityName, st.values) + if err == nil { + cookie := &http.Cookie{Name: cookiepder.config.CookieName, + Value: url.QueryEscape(encodedCookie), + Path: "/", + HttpOnly: true, + Secure: cookiepder.config.Secure, + MaxAge: cookiepder.config.Maxage} + http.SetCookie(w, cookie) } - cookie := &http.Cookie{Name: cookiepder.config.CookieName, - Value: url.QueryEscape(str), - Path: "/", - HttpOnly: true, - Secure: cookiepder.config.Secure, - MaxAge: cookiepder.config.Maxage} - http.SetCookie(w, cookie) - return } type cookieConfig struct { @@ -166,7 +161,6 @@ func (pder *CookieProvider) SessionDestroy(sid string) error { // SessionGC Implement method, no used. func (pder *CookieProvider) SessionGC() { - return } // SessionAll Implement method, return 0. diff --git a/src/vendor/github.com/astaxie/beego/session/sess_cookie_test.go b/src/vendor/github.com/astaxie/beego/session/sess_cookie_test.go index 209e501ca..b6726005f 100644 --- a/src/vendor/github.com/astaxie/beego/session/sess_cookie_test.go +++ b/src/vendor/github.com/astaxie/beego/session/sess_cookie_test.go @@ -15,6 +15,7 @@ package session import ( + "encoding/json" "net/http" "net/http/httptest" "strings" @@ -23,7 +24,11 @@ import ( func TestCookie(t *testing.T) { config := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}` - globalSessions, err := NewManager("cookie", config) + conf := new(ManagerConfig) + if err := json.Unmarshal([]byte(config), conf); err != nil { + t.Fatal("json decode error", err) + } + globalSessions, err := NewManager("cookie", conf) if err != nil { t.Fatal("init cookie session err", err) } @@ -56,7 +61,11 @@ func TestCookie(t *testing.T) { func TestDestorySessionCookie(t *testing.T) { config := `{"cookieName":"gosessionid","enableSetCookie":true,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}` - globalSessions, err := NewManager("cookie", config) + conf := new(ManagerConfig) + if err := json.Unmarshal([]byte(config), conf); err != nil { + t.Fatal("json decode error", err) + } + globalSessions, err := NewManager("cookie", conf) if err != nil { t.Fatal("init cookie session err", err) } diff --git a/src/vendor/github.com/astaxie/beego/session/sess_file.go b/src/vendor/github.com/astaxie/beego/session/sess_file.go index 9265b0304..3ca93d555 100644 --- a/src/vendor/github.com/astaxie/beego/session/sess_file.go +++ b/src/vendor/github.com/astaxie/beego/session/sess_file.go @@ -15,9 +15,7 @@ package session import ( - "errors" "fmt" - "io" "io/ioutil" "net/http" "os" @@ -82,14 +80,23 @@ func (fs *FileSessionStore) SessionID() string { func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { b, err := EncodeGob(fs.values) if err != nil { + SLogger.Println(err) return } _, err = os.Stat(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid)) var f *os.File if err == nil { f, err = os.OpenFile(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid), os.O_RDWR, 0777) + if err != nil { + SLogger.Println(err) + return + } } else if os.IsNotExist(err) { f, err = os.Create(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid)) + if err != nil { + SLogger.Println(err) + return + } } else { return } @@ -123,7 +130,7 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) { err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777) if err != nil { - println(err.Error()) + SLogger.Println(err.Error()) } _, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) var f *os.File @@ -134,6 +141,9 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) { } else { return nil, err } + + defer f.Close() + os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now()) var kv map[interface{}]interface{} b, err := ioutil.ReadAll(f) @@ -148,7 +158,7 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) { return nil, err } } - f.Close() + ss := &FileSessionStore{sid: sid, values: kv} return ss, nil } @@ -160,10 +170,7 @@ func (fp *FileProvider) SessionExist(sid string) bool { defer filepder.lock.Unlock() _, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) - if err == nil { - return true - } - return false + return err == nil } // SessionDestroy Remove all files in this save path @@ -191,7 +198,7 @@ func (fp *FileProvider) SessionAll() int { return a.visit(path, f, err) }) if err != nil { - fmt.Printf("filepath.Walk() returned %v\n", err) + SLogger.Printf("filepath.Walk() returned %v\n", err) return 0 } return a.total @@ -203,49 +210,58 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) { filepder.lock.Lock() defer filepder.lock.Unlock() - err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777) - if err != nil { - println(err.Error()) - } - err = os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777) - if err != nil { - println(err.Error()) - } - _, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) - var newf *os.File + oldPath := path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])) + oldSidFile := path.Join(oldPath, oldsid) + newPath := path.Join(fp.savePath, string(sid[0]), string(sid[1])) + newSidFile := path.Join(newPath, sid) + + // new sid file is exist + _, err := os.Stat(newSidFile) if err == nil { - return nil, errors.New("newsid exist") - } else if os.IsNotExist(err) { - newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) + return nil, fmt.Errorf("newsid %s exist", newSidFile) } - _, err = os.Stat(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid)) - var f *os.File - if err == nil { - f, err = os.OpenFile(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid), os.O_RDWR, 0777) - io.Copy(newf, f) - } else if os.IsNotExist(err) { - newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) - } else { - return nil, err - } - f.Close() - os.Remove(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]))) - os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now()) - var kv map[interface{}]interface{} - b, err := ioutil.ReadAll(newf) + err = os.MkdirAll(newPath, 0777) if err != nil { - return nil, err + SLogger.Println(err.Error()) } - if len(b) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = DecodeGob(b) + + // if old sid file exist + // 1.read and parse file content + // 2.write content to new sid file + // 3.remove old sid file, change new sid file atime and ctime + // 4.return FileSessionStore + _, err = os.Stat(oldSidFile) + if err == nil { + b, err := ioutil.ReadFile(oldSidFile) if err != nil { return nil, err } + + var kv map[interface{}]interface{} + if len(b) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = DecodeGob(b) + if err != nil { + return nil, err + } + } + + ioutil.WriteFile(newSidFile, b, 0777) + os.Remove(oldSidFile) + os.Chtimes(newSidFile, time.Now(), time.Now()) + ss := &FileSessionStore{sid: sid, values: kv} + return ss, nil } - ss := &FileSessionStore{sid: sid, values: kv} + + // if old sid file not exist, just create new sid file and return + newf, err := os.Create(newSidFile) + if err != nil { + return nil, err + } + newf.Close() + ss := &FileSessionStore{sid: sid, values: make(map[interface{}]interface{})} return ss, nil } diff --git a/src/vendor/github.com/astaxie/beego/session/sess_mem_test.go b/src/vendor/github.com/astaxie/beego/session/sess_mem_test.go index 43f5b0a90..2e8934b82 100644 --- a/src/vendor/github.com/astaxie/beego/session/sess_mem_test.go +++ b/src/vendor/github.com/astaxie/beego/session/sess_mem_test.go @@ -15,6 +15,7 @@ package session import ( + "encoding/json" "net/http" "net/http/httptest" "strings" @@ -22,7 +23,12 @@ import ( ) func TestMem(t *testing.T) { - globalSessions, _ := NewManager("memory", `{"cookieName":"gosessionid","gclifetime":10}`) + config := `{"cookieName":"gosessionid","gclifetime":10, "enableSetCookie":true}` + conf := new(ManagerConfig) + if err := json.Unmarshal([]byte(config), conf); err != nil { + t.Fatal("json decode error", err) + } + globalSessions, _ := NewManager("memory", conf) go globalSessions.GC() r, _ := http.NewRequest("GET", "/", nil) w := httptest.NewRecorder() diff --git a/src/vendor/github.com/astaxie/beego/session/sess_test.go b/src/vendor/github.com/astaxie/beego/session/sess_test.go index 5ba910f25..906abec2c 100644 --- a/src/vendor/github.com/astaxie/beego/session/sess_test.go +++ b/src/vendor/github.com/astaxie/beego/session/sess_test.go @@ -74,8 +74,7 @@ func TestCookieEncodeDecode(t *testing.T) { if err != nil { t.Fatal("encodeCookie:", err) } - dst := make(map[interface{}]interface{}) - dst, err = decodeCookie(block, hashKey, securityName, str, 3600) + dst, err := decodeCookie(block, hashKey, securityName, str, 3600) if err != nil { t.Fatal("decodeCookie", err) } @@ -89,7 +88,7 @@ func TestCookieEncodeDecode(t *testing.T) { func TestParseConfig(t *testing.T) { s := `{"cookieName":"gosessionid","gclifetime":3600}` - cf := new(managerConfig) + cf := new(ManagerConfig) cf.EnableSetCookie = true err := json.Unmarshal([]byte(s), cf) if err != nil { @@ -103,7 +102,7 @@ func TestParseConfig(t *testing.T) { } cc := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}` - cf2 := new(managerConfig) + cf2 := new(ManagerConfig) cf2.EnableSetCookie = true err = json.Unmarshal([]byte(cc), cf2) if err != nil { @@ -115,7 +114,7 @@ func TestParseConfig(t *testing.T) { if cf2.Gclifetime != 3600 { t.Fatal("parseconfig get gclifetime error") } - if cf2.EnableSetCookie != false { + if cf2.EnableSetCookie { t.Fatal("parseconfig get enableSetCookie error") } cconfig := new(cookieConfig) diff --git a/src/vendor/github.com/astaxie/beego/session/session.go b/src/vendor/github.com/astaxie/beego/session/session.go index 9fe99a174..cf647521a 100644 --- a/src/vendor/github.com/astaxie/beego/session/session.go +++ b/src/vendor/github.com/astaxie/beego/session/session.go @@ -30,10 +30,14 @@ package session import ( "crypto/rand" "encoding/hex" - "encoding/json" + "errors" "fmt" + "io" + "log" "net/http" + "net/textproto" "net/url" + "os" "time" ) @@ -61,6 +65,9 @@ type Provider interface { var provides = make(map[string]Provider) +// SLogger a helpful variable to log information about session +var SLogger = NewSessionLog(os.Stderr) + // Register makes a session provide available by the provided name. // If Register is called twice with the same name or if driver is nil, // it panics. @@ -74,22 +81,27 @@ func Register(name string, provide Provider) { provides[name] = provide } -type managerConfig struct { - CookieName string `json:"cookieName"` - EnableSetCookie bool `json:"enableSetCookie,omitempty"` - Gclifetime int64 `json:"gclifetime"` - Maxlifetime int64 `json:"maxLifetime"` - Secure bool `json:"secure"` - CookieLifeTime int `json:"cookieLifeTime"` - ProviderConfig string `json:"providerConfig"` - Domain string `json:"domain"` - SessionIDLength int64 `json:"sessionIDLength"` +// ManagerConfig define the session config +type ManagerConfig struct { + CookieName string `json:"cookieName"` + EnableSetCookie bool `json:"enableSetCookie,omitempty"` + Gclifetime int64 `json:"gclifetime"` + Maxlifetime int64 `json:"maxLifetime"` + DisableHTTPOnly bool `json:"disableHTTPOnly"` + Secure bool `json:"secure"` + CookieLifeTime int `json:"cookieLifeTime"` + ProviderConfig string `json:"providerConfig"` + Domain string `json:"domain"` + SessionIDLength int64 `json:"sessionIDLength"` + EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"` + SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"` + EnableSidInURLQuery bool `json:"EnableSidInURLQuery"` } // Manager contains Provider and its configuration. type Manager struct { provider Provider - config *managerConfig + config *ManagerConfig } // NewManager Create new Manager with provider name and json config string. @@ -104,21 +116,29 @@ type Manager struct { // 2. hashfunc default sha1 // 3. hashkey default beegosessionkey // 4. maxage default is none -func NewManager(provideName, config string) (*Manager, error) { +func NewManager(provideName string, cf *ManagerConfig) (*Manager, error) { provider, ok := provides[provideName] if !ok { return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName) } - cf := new(managerConfig) - cf.EnableSetCookie = true - err := json.Unmarshal([]byte(config), cf) - if err != nil { - return nil, err - } + if cf.Maxlifetime == 0 { cf.Maxlifetime = cf.Gclifetime } - err = provider.SessionInit(cf.Maxlifetime, cf.ProviderConfig) + + if cf.EnableSidInHTTPHeader { + if cf.SessionNameInHTTPHeader == "" { + panic(errors.New("SessionNameInHTTPHeader is empty")) + } + + strMimeHeader := textproto.CanonicalMIMEHeaderKey(cf.SessionNameInHTTPHeader) + if cf.SessionNameInHTTPHeader != strMimeHeader { + strErrMsg := "SessionNameInHTTPHeader (" + cf.SessionNameInHTTPHeader + ") has the wrong format, it should be like this : " + strMimeHeader + panic(errors.New(strErrMsg)) + } + } + + err := provider.SessionInit(cf.Maxlifetime, cf.ProviderConfig) if err != nil { return nil, err } @@ -142,13 +162,25 @@ func NewManager(provideName, config string) (*Manager, error) { // otherwise return an valid session id. func (manager *Manager) getSid(r *http.Request) (string, error) { cookie, errs := r.Cookie(manager.config.CookieName) - if errs != nil || cookie.Value == "" || cookie.MaxAge < 0 { - errs := r.ParseForm() - if errs != nil { - return "", errs + if errs != nil || cookie.Value == "" { + var sid string + if manager.config.EnableSidInURLQuery { + errs := r.ParseForm() + if errs != nil { + return "", errs + } + + sid = r.FormValue(manager.config.CookieName) + } + + // if not found in Cookie / param, then read it from request headers + if manager.config.EnableSidInHTTPHeader && sid == "" { + sids, isFound := r.Header[manager.config.SessionNameInHTTPHeader] + if isFound && len(sids) != 0 { + return sids[0], nil + } } - sid := r.FormValue(manager.config.CookieName) return sid, nil } @@ -175,11 +207,14 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se } session, err = manager.provider.SessionRead(sid) + if err != nil { + return nil, err + } cookie := &http.Cookie{ Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", - HttpOnly: true, + HttpOnly: !manager.config.DisableHTTPOnly, Secure: manager.isSecure(r), Domain: manager.config.Domain, } @@ -192,11 +227,21 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se } r.AddCookie(cookie) + if manager.config.EnableSidInHTTPHeader { + r.Header.Set(manager.config.SessionNameInHTTPHeader, sid) + w.Header().Set(manager.config.SessionNameInHTTPHeader, sid) + } + return } // SessionDestroy Destroy session by its id in http request cookie. func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { + if manager.config.EnableSidInHTTPHeader { + r.Header.Del(manager.config.SessionNameInHTTPHeader) + w.Header().Del(manager.config.SessionNameInHTTPHeader) + } + cookie, err := r.Cookie(manager.config.CookieName) if err != nil || cookie.Value == "" { return @@ -208,7 +253,7 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { expiration := time.Now() cookie = &http.Cookie{Name: manager.config.CookieName, Path: "/", - HttpOnly: true, + HttpOnly: !manager.config.DisableHTTPOnly, Expires: expiration, MaxAge: -1} @@ -242,7 +287,7 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque cookie = &http.Cookie{Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", - HttpOnly: true, + HttpOnly: !manager.config.DisableHTTPOnly, Secure: manager.isSecure(r), Domain: manager.config.Domain, } @@ -261,6 +306,12 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque http.SetCookie(w, cookie) } r.AddCookie(cookie) + + if manager.config.EnableSidInHTTPHeader { + r.Header.Set(manager.config.SessionNameInHTTPHeader, sid) + w.Header().Set(manager.config.SessionNameInHTTPHeader, sid) + } + return } @@ -278,7 +329,7 @@ func (manager *Manager) sessionID() (string, error) { b := make([]byte, manager.config.SessionIDLength) n, err := rand.Read(b) if n != len(b) || err != nil { - return "", fmt.Errorf("Could not successfully read from the system CSPRNG.") + return "", fmt.Errorf("Could not successfully read from the system CSPRNG") } return hex.EncodeToString(b), nil } @@ -296,3 +347,15 @@ func (manager *Manager) isSecure(req *http.Request) bool { } return true } + +// Log implement the log.Logger +type Log struct { + *log.Logger +} + +// NewSessionLog set io.Writer to create a Logger for session. +func NewSessionLog(out io.Writer) *Log { + sl := new(Log) + sl.Logger = log.New(out, "[SESSION]", 1e9) + return sl +} diff --git a/src/vendor/github.com/astaxie/beego/session/ssdb/sess_ssdb.go b/src/vendor/github.com/astaxie/beego/session/ssdb/sess_ssdb.go new file mode 100644 index 000000000..de0c6360c --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/session/ssdb/sess_ssdb.go @@ -0,0 +1,199 @@ +package ssdb + +import ( + "errors" + "net/http" + "strconv" + "strings" + "sync" + + "github.com/astaxie/beego/session" + "github.com/ssdb/gossdb/ssdb" +) + +var ssdbProvider = &Provider{} + +// Provider holds ssdb client and configs +type Provider struct { + client *ssdb.Client + host string + port int + maxLifetime int64 +} + +func (p *Provider) connectInit() error { + var err error + if p.host == "" || p.port == 0 { + return errors.New("SessionInit First") + } + p.client, err = ssdb.Connect(p.host, p.port) + return err +} + +// SessionInit init the ssdb with the config +func (p *Provider) SessionInit(maxLifetime int64, savePath string) error { + p.maxLifetime = maxLifetime + address := strings.Split(savePath, ":") + p.host = address[0] + + var err error + if p.port, err = strconv.Atoi(address[1]); err != nil { + return err + } + return p.connectInit() +} + +// SessionRead return a ssdb client session Store +func (p *Provider) SessionRead(sid string) (session.Store, error) { + if p.client == nil { + if err := p.connectInit(); err != nil { + return nil, err + } + } + var kv map[interface{}]interface{} + value, err := p.client.Get(sid) + if err != nil { + return nil, err + } + if value == nil || len(value.(string)) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob([]byte(value.(string))) + if err != nil { + return nil, err + } + } + rs := &SessionStore{sid: sid, values: kv, maxLifetime: p.maxLifetime, client: p.client} + return rs, nil +} + +// SessionExist judged whether sid is exist in session +func (p *Provider) SessionExist(sid string) bool { + if p.client == nil { + if err := p.connectInit(); err != nil { + panic(err) + } + } + value, err := p.client.Get(sid) + if err != nil { + panic(err) + } + if value == nil || len(value.(string)) == 0 { + return false + } + return true +} + +// SessionRegenerate regenerate session with new sid and delete oldsid +func (p *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + //conn.Do("setx", key, v, ttl) + if p.client == nil { + if err := p.connectInit(); err != nil { + return nil, err + } + } + value, err := p.client.Get(oldsid) + if err != nil { + return nil, err + } + var kv map[interface{}]interface{} + if value == nil || len(value.(string)) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob([]byte(value.(string))) + if err != nil { + return nil, err + } + _, err = p.client.Del(oldsid) + if err != nil { + return nil, err + } + } + _, e := p.client.Do("setx", sid, value, p.maxLifetime) + if e != nil { + return nil, e + } + rs := &SessionStore{sid: sid, values: kv, maxLifetime: p.maxLifetime, client: p.client} + return rs, nil +} + +// SessionDestroy destroy the sid +func (p *Provider) SessionDestroy(sid string) error { + if p.client == nil { + if err := p.connectInit(); err != nil { + return err + } + } + _, err := p.client.Del(sid) + return err +} + +// SessionGC not implemented +func (p *Provider) SessionGC() { +} + +// SessionAll not implemented +func (p *Provider) SessionAll() int { + return 0 +} + +// SessionStore holds the session information which stored in ssdb +type SessionStore struct { + sid string + lock sync.RWMutex + values map[interface{}]interface{} + maxLifetime int64 + client *ssdb.Client +} + +// Set the key and value +func (s *SessionStore) Set(key, value interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + s.values[key] = value + return nil +} + +// Get return the value by the key +func (s *SessionStore) Get(key interface{}) interface{} { + s.lock.Lock() + defer s.lock.Unlock() + if value, ok := s.values[key]; ok { + return value + } + return nil +} + +// Delete the key in session store +func (s *SessionStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + delete(s.values, key) + return nil +} + +// Flush delete all keys and values +func (s *SessionStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + s.values = make(map[interface{}]interface{}) + return nil +} + +// SessionID return the sessionID +func (s *SessionStore) SessionID() string { + return s.sid +} + +// SessionRelease Store the keyvalues into ssdb +func (s *SessionStore) SessionRelease(w http.ResponseWriter) { + b, err := session.EncodeGob(s.values) + if err != nil { + return + } + s.client.Do("setx", s.sid, string(b), s.maxLifetime) +} + +func init() { + session.Register("ssdb", ssdbProvider) +} diff --git a/src/vendor/github.com/astaxie/beego/staticfile.go b/src/vendor/github.com/astaxie/beego/staticfile.go index 0aad2c81e..bbb2a1fbf 100644 --- a/src/vendor/github.com/astaxie/beego/staticfile.go +++ b/src/vendor/github.com/astaxie/beego/staticfile.go @@ -27,6 +27,7 @@ import ( "time" "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" ) var errNotStaticRequest = errors.New("request not a static file request") @@ -48,14 +49,23 @@ func serverStaticRouter(ctx *context.Context) { if filePath == "" || fileInfo == nil { if BConfig.RunMode == DEV { - Warn("Can't find/open the file:", filePath, err) + logs.Warn("Can't find/open the file:", filePath, err) } http.NotFound(ctx.ResponseWriter, ctx.Request) return } if fileInfo.IsDir() { - //serveFile will list dir - http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath) + requestURL := ctx.Input.URL() + if requestURL[len(requestURL)-1] != '/' { + redirectURL := requestURL + "/" + if ctx.Request.URL.RawQuery != "" { + redirectURL = redirectURL + "?" + ctx.Request.URL.RawQuery + } + ctx.Redirect(302, redirectURL) + } else { + //serveFile will list dir + http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath) + } return } @@ -67,7 +77,7 @@ func serverStaticRouter(ctx *context.Context) { b, n, sch, err := openFile(filePath, fileInfo, acceptEncoding) if err != nil { if BConfig.RunMode == DEV { - Warn("Can't compress the file:", filePath, err) + logs.Warn("Can't compress the file:", filePath, err) } http.NotFound(ctx.ResponseWriter, ctx.Request) return @@ -80,8 +90,6 @@ func serverStaticRouter(ctx *context.Context) { } http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch) - return - } type serveContentHolder struct { @@ -99,14 +107,14 @@ var ( func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, error) { mapKey := acceptEncoding + ":" + filePath mapLock.RLock() - mapFile, _ := staticFileMap[mapKey] + mapFile := staticFileMap[mapKey] mapLock.RUnlock() if isOk(mapFile, fi) { return mapFile.encoding != "", mapFile.encoding, mapFile, nil } mapLock.Lock() defer mapLock.Unlock() - if mapFile, _ = staticFileMap[mapKey]; !isOk(mapFile, fi) { + if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) { file, err := os.Open(filePath) if err != nil { return false, "", nil, err @@ -157,13 +165,10 @@ func searchFile(ctx *context.Context) (string, os.FileInfo, error) { return filePath, fi, nil } } - return "", nil, errors.New(requestPath + " file not find") + return "", nil, errNotStaticRequest } for prefix, staticDir := range BConfig.WebConfig.StaticDir { - if len(prefix) == 0 { - continue - } if !strings.Contains(requestPath, prefix) { continue } @@ -189,9 +194,11 @@ func lookupFile(ctx *context.Context) (bool, string, os.FileInfo, error) { if !fi.IsDir() { return false, fp, fi, err } - ifp := filepath.Join(fp, "index.html") - if ifi, _ := os.Stat(ifp); ifi != nil && ifi.Mode().IsRegular() { - return false, ifp, ifi, err + if requestURL := ctx.Input.URL(); requestURL[len(requestURL)-1] == '/' { + ifp := filepath.Join(fp, "index.html") + if ifi, _ := os.Stat(ifp); ifi != nil && ifi.Mode().IsRegular() { + return false, ifp, ifi, err + } } return !BConfig.WebConfig.DirectoryIndex, fp, fi, err } diff --git a/src/vendor/github.com/astaxie/beego/swagger/docs_spec.go b/src/vendor/github.com/astaxie/beego/swagger/docs_spec.go deleted file mode 100644 index 680324dc0..000000000 --- a/src/vendor/github.com/astaxie/beego/swagger/docs_spec.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package swagger struct definition -package swagger - -// SwaggerVersion show the current swagger version -const SwaggerVersion = "1.2" - -// ResourceListing list the resource -type ResourceListing struct { - APIVersion string `json:"apiVersion"` - SwaggerVersion string `json:"swaggerVersion"` // e.g 1.2 - // BasePath string `json:"basePath"` obsolete in 1.1 - APIs []APIRef `json:"apis"` - Info Information `json:"info"` -} - -// APIRef description the api path and description -type APIRef struct { - Path string `json:"path"` // relative or absolute, must start with / - Description string `json:"description"` -} - -// Information show the API Information -type Information struct { - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - Contact string `json:"contact,omitempty"` - TermsOfServiceURL string `json:"termsOfServiceUrl,omitempty"` - License string `json:"license,omitempty"` - LicenseURL string `json:"licenseUrl,omitempty"` -} - -// APIDeclaration see https://github.com/wordnik/swagger-core/blob/scala_2.10-1.3-RC3/schemas/api-declaration-schema.json -type APIDeclaration struct { - APIVersion string `json:"apiVersion"` - SwaggerVersion string `json:"swaggerVersion"` - BasePath string `json:"basePath"` - ResourcePath string `json:"resourcePath"` // must start with / - Consumes []string `json:"consumes,omitempty"` - Produces []string `json:"produces,omitempty"` - APIs []API `json:"apis,omitempty"` - Models map[string]Model `json:"models,omitempty"` -} - -// API show tha API struct -type API struct { - Path string `json:"path"` // relative or absolute, must start with / - Description string `json:"description"` - Operations []Operation `json:"operations,omitempty"` -} - -// Operation desc the Operation -type Operation struct { - HTTPMethod string `json:"httpMethod"` - Nickname string `json:"nickname"` - Type string `json:"type"` // in 1.1 = DataType - // ResponseClass string `json:"responseClass"` obsolete in 1.2 - Summary string `json:"summary,omitempty"` - Notes string `json:"notes,omitempty"` - Parameters []Parameter `json:"parameters,omitempty"` - ResponseMessages []ResponseMessage `json:"responseMessages,omitempty"` // optional - Consumes []string `json:"consumes,omitempty"` - Produces []string `json:"produces,omitempty"` - Authorizations []Authorization `json:"authorizations,omitempty"` - Protocols []Protocol `json:"protocols,omitempty"` -} - -// Protocol support which Protocol -type Protocol struct { -} - -// ResponseMessage Show the -type ResponseMessage struct { - Code int `json:"code"` - Message string `json:"message"` - ResponseModel string `json:"responseModel"` -} - -// Parameter desc the request parameters -type Parameter struct { - ParamType string `json:"paramType"` // path,query,body,header,form - Name string `json:"name"` - Description string `json:"description"` - DataType string `json:"dataType"` // 1.2 needed? - Type string `json:"type"` // integer - Format string `json:"format"` // int64 - AllowMultiple bool `json:"allowMultiple"` - Required bool `json:"required"` - Minimum int `json:"minimum"` - Maximum int `json:"maximum"` -} - -// ErrorResponse desc response -type ErrorResponse struct { - Code int `json:"code"` - Reason string `json:"reason"` -} - -// Model define the data model -type Model struct { - ID string `json:"id"` - Required []string `json:"required,omitempty"` - Properties map[string]ModelProperty `json:"properties"` -} - -// ModelProperty define the properties -type ModelProperty struct { - Type string `json:"type"` - Description string `json:"description"` - Items map[string]string `json:"items,omitempty"` - Format string `json:"format"` -} - -// Authorization see https://github.com/wordnik/swagger-core/wiki/authorizations -type Authorization struct { - LocalOAuth OAuth `json:"local-oauth"` - APIKey APIKey `json:"apiKey"` -} - -// OAuth see https://github.com/wordnik/swagger-core/wiki/authorizations -type OAuth struct { - Type string `json:"type"` // e.g. oauth2 - Scopes []string `json:"scopes"` // e.g. PUBLIC - GrantTypes map[string]GrantType `json:"grantTypes"` -} - -// GrantType see https://github.com/wordnik/swagger-core/wiki/authorizations -type GrantType struct { - LoginEndpoint Endpoint `json:"loginEndpoint"` - TokenName string `json:"tokenName"` // e.g. access_code - TokenRequestEndpoint Endpoint `json:"tokenRequestEndpoint"` - TokenEndpoint Endpoint `json:"tokenEndpoint"` -} - -// Endpoint see https://github.com/wordnik/swagger-core/wiki/authorizations -type Endpoint struct { - URL string `json:"url"` - ClientIDName string `json:"clientIdName"` - ClientSecretName string `json:"clientSecretName"` - TokenName string `json:"tokenName"` -} - -// APIKey see https://github.com/wordnik/swagger-core/wiki/authorizations -type APIKey struct { - Type string `json:"type"` // e.g. apiKey - PassAs string `json:"passAs"` // e.g. header -} diff --git a/src/vendor/github.com/astaxie/beego/swagger/swagger.go b/src/vendor/github.com/astaxie/beego/swagger/swagger.go new file mode 100644 index 000000000..035d5a497 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/swagger/swagger.go @@ -0,0 +1,172 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Swagger™ is a project used to describe and document RESTful APIs. +// +// The Swagger specification defines a set of files required to describe such an API. These files can then be used by the Swagger-UI project to display the API and Swagger-Codegen to generate clients in various languages. Additional utilities can also take advantage of the resulting files, such as testing tools. +// Now in version 2.0, Swagger is more enabling than ever. And it's 100% open source software. + +// Package swagger struct definition +package swagger + +// Swagger list the resource +type Swagger struct { + SwaggerVersion string `json:"swagger,omitempty" yaml:"swagger,omitempty"` + Infos Information `json:"info" yaml:"info"` + Host string `json:"host,omitempty" yaml:"host,omitempty"` + BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"` + Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"` + Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"` + Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"` + Paths map[string]*Item `json:"paths" yaml:"paths"` + Definitions map[string]Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"` + SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"` + Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"` + Tags []Tag `json:"tags,omitempty" yaml:"tags,omitempty"` + ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` +} + +// Information Provides metadata about the API. The metadata can be used by the clients if needed. +type Information struct { + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` + TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"` + + Contact Contact `json:"contact,omitempty" yaml:"contact,omitempty"` + License *License `json:"license,omitempty" yaml:"license,omitempty"` +} + +// Contact information for the exposed API. +type Contact struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` + EMail string `json:"email,omitempty" yaml:"email,omitempty"` +} + +// License information for the exposed API. +type License struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` +} + +// Item Describes the operations available on a single path. +type Item struct { + Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Get *Operation `json:"get,omitempty" yaml:"get,omitempty"` + Put *Operation `json:"put,omitempty" yaml:"put,omitempty"` + Post *Operation `json:"post,omitempty" yaml:"post,omitempty"` + Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"` + Options *Operation `json:"options,omitempty" yaml:"options,omitempty"` + Head *Operation `json:"head,omitempty" yaml:"head,omitempty"` + Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"` +} + +// Operation Describes a single API operation on a path. +type Operation struct { + Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` + Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"` + Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"` + Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"` + Parameters []Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Responses map[string]Response `json:"responses,omitempty" yaml:"responses,omitempty"` + Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"` + Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` +} + +// Parameter Describes a single operation parameter. +type Parameter struct { + In string `json:"in,omitempty" yaml:"in,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Required bool `json:"required,omitempty" yaml:"required,omitempty"` + Schema *Schema `json:"schema,omitempty" yaml:"schema,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + Items *ParameterItems `json:"items,omitempty" yaml:"items,omitempty"` + Default interface{} `json:"default,omitempty" yaml:"default,omitempty"` +} + +// ParameterItems A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body". +// http://swagger.io/specification/#itemsObject +type ParameterItems struct { + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + Items []*ParameterItems `json:"items,omitempty" yaml:"items,omitempty"` //Required if type is "array". Describes the type of items in the array. + CollectionFormat string `json:"collectionFormat,omitempty" yaml:"collectionFormat,omitempty"` + Default string `json:"default,omitempty" yaml:"default,omitempty"` +} + +// Schema Object allows the definition of input and output data types. +type Schema struct { + Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Required []string `json:"required,omitempty" yaml:"required,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Items *Schema `json:"items,omitempty" yaml:"items,omitempty"` + Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"` +} + +// Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification +type Propertie struct { + Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Default interface{} `json:"default,omitempty" yaml:"default,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Example string `json:"example,omitempty" yaml:"example,omitempty"` + Required []string `json:"required,omitempty" yaml:"required,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` + Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"` + Items *Propertie `json:"items,omitempty" yaml:"items,omitempty"` + AdditionalProperties *Propertie `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"` +} + +// Response as they are returned from executing this operation. +type Response struct { + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Schema *Schema `json:"schema,omitempty" yaml:"schema,omitempty"` + Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` +} + +// Security Allows the definition of a security scheme that can be used by the operations +type Security struct { + Type string `json:"type,omitempty" yaml:"type,omitempty"` // Valid values are "basic", "apiKey" or "oauth2". + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + In string `json:"in,omitempty" yaml:"in,omitempty"` // Valid values are "query" or "header". + Flow string `json:"flow,omitempty" yaml:"flow,omitempty"` // Valid values are "implicit", "password", "application" or "accessCode". + AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` + TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` + Scopes map[string]string `json:"scopes,omitempty" yaml:"scopes,omitempty"` // The available scopes for the OAuth2 security scheme. +} + +// Tag Allows adding meta data to a single tag that is used by the Operation Object +type Tag struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` +} + +// ExternalDocs include Additional external documentation +type ExternalDocs struct { + Description string `json:"description,omitempty" yaml:"description,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` +} diff --git a/src/vendor/github.com/astaxie/beego/template.go b/src/vendor/github.com/astaxie/beego/template.go index e6c43f871..d4859cd70 100644 --- a/src/vendor/github.com/astaxie/beego/template.go +++ b/src/vendor/github.com/astaxie/beego/template.go @@ -26,31 +26,53 @@ import ( "strings" "sync" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/utils" ) var ( - beegoTplFuncMap = make(template.FuncMap) - // beeTemplates caching map and supported template file extensions. - beeTemplates = make(map[string]*template.Template) - templatesLock sync.RWMutex + beegoTplFuncMap = make(template.FuncMap) + beeViewPathTemplateLocked = false + // beeViewPathTemplates caching map and supported template file extensions per view + beeViewPathTemplates = make(map[string]map[string]*template.Template) + templatesLock sync.RWMutex // beeTemplateExt stores the template extension which will build beeTemplateExt = []string{"tpl", "html"} + // beeTemplatePreprocessors stores associations of extension -> preprocessor handler + beeTemplateEngines = map[string]templatePreProcessor{} ) -func executeTemplate(wr io.Writer, name string, data interface{}) error { +// ExecuteTemplate applies the template with name to the specified data object, +// writing the output to wr. +// A template will be executed safely in parallel. +func ExecuteTemplate(wr io.Writer, name string, data interface{}) error { + return ExecuteViewPathTemplate(wr, name, BConfig.WebConfig.ViewsPath, data) +} + +// ExecuteViewPathTemplate applies the template with name and from specific viewPath to the specified data object, +// writing the output to wr. +// A template will be executed safely in parallel. +func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data interface{}) error { if BConfig.RunMode == DEV { templatesLock.RLock() defer templatesLock.RUnlock() } - if t, ok := beeTemplates[name]; ok { - err := t.ExecuteTemplate(wr, name, data) - if err != nil { - Trace("template Execute err:", err) + if beeTemplates, ok := beeViewPathTemplates[viewPath]; ok { + if t, ok := beeTemplates[name]; ok { + var err error + if t.Lookup(name) != nil { + err = t.ExecuteTemplate(wr, name, data) + } else { + err = t.Execute(wr, data) + } + if err != nil { + logs.Trace("template Execute err:", err) + } + return err } - return err + panic("can't find templatefile in the path:" + viewPath + "/" + name) } - panic("can't find templatefile in the path:" + name) + panic("Unknown view path:" + viewPath) } func init() { @@ -88,6 +110,8 @@ func AddFuncMap(key string, fn interface{}) error { return nil } +type templatePreProcessor func(root, path string, funcs template.FuncMap) (*template.Template, error) + type templateFile struct { root string files map[string][]string @@ -136,6 +160,24 @@ func AddTemplateExt(ext string) { beeTemplateExt = append(beeTemplateExt, ext) } +// AddViewPath adds a new path to the supported view paths. +//Can later be used by setting a controller ViewPath to this folder +//will panic if called after beego.Run() +func AddViewPath(viewPath string) error { + if beeViewPathTemplateLocked { + if _, exist := beeViewPathTemplates[viewPath]; exist { + return nil //Ignore if viewpath already exists + } + panic("Can not add new view paths after beego.Run()") + } + beeViewPathTemplates[viewPath] = make(map[string]*template.Template) + return BuildTemplate(viewPath) +} + +func lockViewPaths() { + beeViewPathTemplateLocked = true +} + // BuildTemplate will build all template files in a directory. // it makes beego can render any template file in view directory. func BuildTemplate(dir string, files ...string) error { @@ -145,6 +187,10 @@ func BuildTemplate(dir string, files ...string) error { } return errors.New("dir open err") } + beeTemplates, ok := beeViewPathTemplates[dir] + if !ok { + panic("Unknown view path: " + dir) + } self := &templateFile{ root: dir, files: make(map[string][]string), @@ -156,13 +202,22 @@ func BuildTemplate(dir string, files ...string) error { fmt.Printf("filepath.Walk() returned %v\n", err) return err } + buildAllFiles := len(files) == 0 for _, v := range self.files { for _, file := range v { - if len(files) == 0 || utils.InSlice(file, files) { + if buildAllFiles || utils.InSlice(file, files) { templatesLock.Lock() - t, err := getTemplate(self.root, file, v...) + ext := filepath.Ext(file) + var t *template.Template + if len(ext) == 0 { + t, err = getTemplate(self.root, file, v...) + } else if fn, ok := beeTemplateEngines[ext[1:]]; ok { + t, err = fn(self.root, file, beegoTplFuncMap) + } else { + t, err = getTemplate(self.root, file, v...) + } if err != nil { - Trace("parse template err:", file, err) + logs.Error("parse template err:", file, err) } else { beeTemplates[file] = t } @@ -175,9 +230,12 @@ func BuildTemplate(dir string, files ...string) error { func getTplDeep(root, file, parent string, t *template.Template) (*template.Template, [][]string, error) { var fileAbsPath string + var rParent string if filepath.HasPrefix(file, "../") { + rParent = filepath.Join(filepath.Dir(parent), file) fileAbsPath = filepath.Join(root, filepath.Dir(parent), file) } else { + rParent = file fileAbsPath = filepath.Join(root, file) } if e := utils.FileExists(fileAbsPath); !e { @@ -202,7 +260,7 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp if !HasTemplateExt(m[1]) { continue } - t, _, err = getTplDeep(root, m[1], file, t) + _, _, err = getTplDeep(root, m[1], rParent, t) if err != nil { return nil, [][]string{}, err } @@ -240,8 +298,8 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others var subMods1 [][]string t, subMods1, err = getTplDeep(root, otherFile, "", t) if err != nil { - Trace("template parse file err:", err) - } else if subMods1 != nil && len(subMods1) > 0 { + logs.Trace("template parse file err:", err) + } else if len(subMods1) > 0 { t, err = _getTemplate(t, root, subMods1, others...) } break @@ -249,8 +307,9 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others } //second check define for _, otherFile := range others { + var data []byte fileAbsPath := filepath.Join(root, otherFile) - data, err := ioutil.ReadFile(fileAbsPath) + data, err = ioutil.ReadFile(fileAbsPath) if err != nil { continue } @@ -261,8 +320,8 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others var subMods1 [][]string t, subMods1, err = getTplDeep(root, otherFile, "", t) if err != nil { - Trace("template parse file err:", err) - } else if subMods1 != nil && len(subMods1) > 0 { + logs.Trace("template parse file err:", err) + } else if len(subMods1) > 0 { t, err = _getTemplate(t, root, subMods1, others...) } break @@ -305,3 +364,10 @@ func DelStaticPath(url string) *App { delete(BConfig.WebConfig.StaticDir, url) return BeeApp } + +// AddTemplateEngine add a new templatePreProcessor which support extension +func AddTemplateEngine(extension string, fn templatePreProcessor) *App { + AddTemplateExt(extension) + beeTemplateEngines[extension] = fn + return BeeApp +} diff --git a/src/vendor/github.com/astaxie/beego/template_test.go b/src/vendor/github.com/astaxie/beego/template_test.go index 4f13736c4..2153ef72c 100644 --- a/src/vendor/github.com/astaxie/beego/template_test.go +++ b/src/vendor/github.com/astaxie/beego/template_test.go @@ -15,6 +15,7 @@ package beego import ( + "bytes" "os" "path/filepath" "testing" @@ -67,9 +68,10 @@ func TestTemplate(t *testing.T) { f.Close() } } - if err := BuildTemplate(dir); err != nil { + if err := AddViewPath(dir); err != nil { t.Fatal(err) } + beeTemplates := beeViewPathTemplates[dir] if len(beeTemplates) != 3 { t.Fatalf("should be 3 but got %v", len(beeTemplates)) } @@ -103,6 +105,12 @@ var user = ` func TestRelativeTemplate(t *testing.T) { dir := "_beeTmp" + + //Just add dir to known viewPaths + if err := AddViewPath(dir); err != nil { + t.Fatal(err) + } + files := []string{ "easyui/public/menu.tpl", "easyui/rbac/user.tpl", @@ -126,6 +134,7 @@ func TestRelativeTemplate(t *testing.T) { if err := BuildTemplate(dir, files[1]); err != nil { t.Fatal(err) } + beeTemplates := beeViewPathTemplates[dir] if err := beeTemplates["easyui/rbac/user.tpl"].ExecuteTemplate(os.Stdout, "easyui/rbac/user.tpl", nil); err != nil { t.Fatal(err) } @@ -134,3 +143,116 @@ func TestRelativeTemplate(t *testing.T) { } os.RemoveAll(dir) } + +var add = `{{ template "layout_blog.tpl" . }} +{{ define "css" }} + +{{ end}} + + +{{ define "content" }} +

{{ .Title }}

+

This is SomeVar: {{ .SomeVar }}

+{{ end }} + +{{ define "js" }} + +{{ end}}` + +var layoutBlog = ` + + + Lin Li + + + + + {{ block "css" . }}{{ end }} + + + +
+ {{ block "content" . }}{{ end }} +
+ + + {{ block "js" . }}{{ end }} + +` + +var output = ` + + + Lin Li + + + + + + + + + + +
+ +

Hello

+

This is SomeVar: val

+ +
+ + + + + + + + + + + + +` + +func TestTemplateLayout(t *testing.T) { + dir := "_beeTmp" + files := []string{ + "add.tpl", + "layout_blog.tpl", + } + if err := os.MkdirAll(dir, 0777); err != nil { + t.Fatal(err) + } + for k, name := range files { + os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777) + if f, err := os.Create(filepath.Join(dir, name)); err != nil { + t.Fatal(err) + } else { + if k == 0 { + f.WriteString(add) + } else if k == 1 { + f.WriteString(layoutBlog) + } + f.Close() + } + } + if err := AddViewPath(dir); err != nil { + t.Fatal(err) + } + beeTemplates := beeViewPathTemplates[dir] + if len(beeTemplates) != 2 { + t.Fatalf("should be 2 but got %v", len(beeTemplates)) + } + out := bytes.NewBufferString("") + if err := beeTemplates["add.tpl"].ExecuteTemplate(out, "add.tpl", map[string]string{"Title": "Hello", "SomeVar": "val"}); err != nil { + t.Fatal(err) + } + if out.String() != output { + t.Log(out.String()) + t.Fatal("Compare failed") + } + for _, name := range files { + os.RemoveAll(filepath.Join(dir, name)) + } + os.RemoveAll(dir) +} diff --git a/src/vendor/github.com/astaxie/beego/templatefunc.go b/src/vendor/github.com/astaxie/beego/templatefunc.go index 8558733f3..a104fd241 100644 --- a/src/vendor/github.com/astaxie/beego/templatefunc.go +++ b/src/vendor/github.com/astaxie/beego/templatefunc.go @@ -26,6 +26,13 @@ import ( "time" ) +const ( + formatTime = "15:04:05" + formatDate = "2006-01-02" + formatDateTime = "2006-01-02 15:04:05" + formatDateTimeT = "2006-01-02T15:04:05" +) + // Substr returns the substr from start to length. func Substr(s string, start, length int) string { bt := []rune(s) @@ -46,26 +53,25 @@ func Substr(s string, start, length int) string { // HTML2str returns escaping text convert from html. func HTML2str(html string) string { - src := string(html) - re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") - src = re.ReplaceAllStringFunc(src, strings.ToLower) + re, _ := regexp.Compile(`\<[\S\s]+?\>`) + html = re.ReplaceAllStringFunc(html, strings.ToLower) //remove STYLE - re, _ = regexp.Compile("\\") - src = re.ReplaceAllString(src, "") + re, _ = regexp.Compile(`\`) + html = re.ReplaceAllString(html, "") //remove SCRIPT - re, _ = regexp.Compile("\\") - src = re.ReplaceAllString(src, "") + re, _ = regexp.Compile(`\`) + html = re.ReplaceAllString(html, "") - re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") - src = re.ReplaceAllString(src, "\n") + re, _ = regexp.Compile(`\<[\S\s]+?\>`) + html = re.ReplaceAllString(html, "\n") - re, _ = regexp.Compile("\\s{2,}") - src = re.ReplaceAllString(src, "\n") + re, _ = regexp.Compile(`\s{2,}`) + html = re.ReplaceAllString(html, "\n") - return strings.TrimSpace(src) + return strings.TrimSpace(html) } // DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat" @@ -193,7 +199,7 @@ func Str2html(raw string) template.HTML { } // Htmlquote returns quoted html string. -func Htmlquote(src string) string { +func Htmlquote(text string) string { //HTML编码为实体符号 /* Encodes `text` for raw use in HTML. @@ -201,8 +207,6 @@ func Htmlquote(src string) string { '<'&">' */ - text := string(src) - text = strings.Replace(text, "&", "&", -1) // Must be done first! text = strings.Replace(text, "<", "<", -1) text = strings.Replace(text, ">", ">", -1) @@ -216,7 +220,7 @@ func Htmlquote(src string) string { } // Htmlunquote returns unquoted html string. -func Htmlunquote(src string) string { +func Htmlunquote(text string) string { //实体符号解释为HTML /* Decodes `text` that's HTML quoted. @@ -227,7 +231,6 @@ func Htmlunquote(src string) string { // strings.Replace(s, old, new, n) // 在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换 - text := string(src) text = strings.Replace(text, " ", " ", -1) text = strings.Replace(text, "”", "”", -1) text = strings.Replace(text, "“", "“", -1) @@ -262,33 +265,24 @@ func URLFor(endpoint string, values ...interface{}) string { } // AssetsJs returns script tag with src string. -func AssetsJs(src string) template.HTML { - text := string(src) +func AssetsJs(text string) template.HTML { - text = "" + text = "" return template.HTML(text) } // AssetsCSS returns stylesheet link tag with src string. -func AssetsCSS(src string) template.HTML { - text := string(src) +func AssetsCSS(text string) template.HTML { - text = "" + text = "" return template.HTML(text) } // ParseForm will parse form values to struct via tag. -func ParseForm(form url.Values, obj interface{}) error { - objT := reflect.TypeOf(obj) - objV := reflect.ValueOf(obj) - if !isStructPtr(objT) { - return fmt.Errorf("%v must be a struct pointer", obj) - } - objT = objT.Elem() - objV = objV.Elem() - +// Support for anonymous struct. +func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) error { for i := 0; i < objT.NumField(); i++ { fieldV := objV.Field(i) if !fieldV.CanSet() { @@ -296,6 +290,14 @@ func ParseForm(form url.Values, obj interface{}) error { } fieldT := objT.Field(i) + if fieldT.Anonymous && fieldT.Type.Kind() == reflect.Struct { + err := parseFormToStruct(form, fieldT.Type, fieldV) + if err != nil { + return err + } + continue + } + tags := strings.Split(fieldT.Tag.Get("form"), ",") var tag string if len(tags) == 0 || len(tags[0]) == 0 { @@ -351,11 +353,32 @@ func ParseForm(form url.Values, obj interface{}) error { case reflect.Struct: switch fieldT.Type.String() { case "time.Time": - format := time.RFC3339 - if len(tags) > 1 { - format = tags[1] + var ( + t time.Time + err error + ) + if len(value) >= 25 { + value = value[:25] + t, err = time.ParseInLocation(time.RFC3339, value, time.Local) + } else if len(value) >= 19 { + if strings.Contains(value, "T") { + value = value[:19] + t, err = time.ParseInLocation(formatDateTimeT, value, time.Local) + } else { + value = value[:19] + t, err = time.ParseInLocation(formatDateTime, value, time.Local) + } + } else if len(value) >= 10 { + if len(value) > 10 { + value = value[:10] + } + t, err = time.ParseInLocation(formatDate, value, time.Local) + } else if len(value) >= 8 { + if len(value) > 8 { + value = value[:8] + } + t, err = time.ParseInLocation(formatTime, value, time.Local) } - t, err := time.ParseInLocation(format, value, time.Local) if err != nil { return err } @@ -384,6 +407,19 @@ func ParseForm(form url.Values, obj interface{}) error { return nil } +// ParseForm will parse form values to struct via tag. +func ParseForm(form url.Values, obj interface{}) error { + objT := reflect.TypeOf(obj) + objV := reflect.ValueOf(obj) + if !isStructPtr(objT) { + return fmt.Errorf("%v must be a struct pointer", obj) + } + objT = objT.Elem() + objV = objV.Elem() + + return parseFormToStruct(form, objT, objV) +} + var sliceOfInts = reflect.TypeOf([]int(nil)) var sliceOfStrings = reflect.TypeOf([]string(nil)) @@ -421,18 +457,18 @@ func RenderForm(obj interface{}) template.HTML { fieldT := objT.Field(i) - label, name, fType, id, class, ignored := parseFormTag(fieldT) + label, name, fType, id, class, ignored, required := parseFormTag(fieldT) if ignored { continue } - raw = append(raw, renderFormField(label, name, fType, fieldV.Interface(), id, class)) + raw = append(raw, renderFormField(label, name, fType, fieldV.Interface(), id, class, required)) } return template.HTML(strings.Join(raw, "
")) } // renderFormField returns a string containing HTML of a single form field. -func renderFormField(label, name, fType string, value interface{}, id string, class string) string { +func renderFormField(label, name, fType string, value interface{}, id string, class string, required bool) string { if id != "" { id = " id=\"" + id + "\"" } @@ -441,11 +477,16 @@ func renderFormField(label, name, fType string, value interface{}, id string, cl class = " class=\"" + class + "\"" } - if isValidForInput(fType) { - return fmt.Sprintf(`%v`, label, id, class, name, fType, value) + requiredString := "" + if required { + requiredString = " required" } - return fmt.Sprintf(`%v<%v%v%v name="%v">%v`, label, fType, id, class, name, value, fType) + if isValidForInput(fType) { + return fmt.Sprintf(`%v`, label, id, class, name, fType, value, requiredString) + } + + return fmt.Sprintf(`%v<%v%v%v name="%v"%v>%v`, label, fType, id, class, name, requiredString, value, fType) } // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element. @@ -461,7 +502,7 @@ func isValidForInput(fType string) bool { // parseFormTag takes the stuct-tag of a StructField and parses the `form` value. // returned are the form label, name-property, type and wether the field should be ignored. -func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool) { +func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool, required bool) { tags := strings.Split(fieldT.Tag.Get("form"), ",") label = fieldT.Name + ": " name = fieldT.Name @@ -470,6 +511,12 @@ func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id str id = fieldT.Tag.Get("id") class = fieldT.Tag.Get("class") + required = false + requiredField := fieldT.Tag.Get("required") + if requiredField != "-" && requiredField != "" { + required, _ = strconv.ParseBool(requiredField) + } + switch len(tags) { case 1: if tags[0] == "-" { @@ -496,6 +543,7 @@ func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id str label = tags[2] } } + return } diff --git a/src/vendor/github.com/astaxie/beego/templatefunc_test.go b/src/vendor/github.com/astaxie/beego/templatefunc_test.go index 98fbf7ab7..9df611253 100644 --- a/src/vendor/github.com/astaxie/beego/templatefunc_test.go +++ b/src/vendor/github.com/astaxie/beego/templatefunc_test.go @@ -110,6 +110,17 @@ func TestHtmlunquote(t *testing.T) { } func TestParseForm(t *testing.T) { + type ExtendInfo struct { + Hobby string `form:"hobby"` + Memo string + } + + type OtherInfo struct { + Organization string `form:"organization"` + Title string `form:"title"` + ExtendInfo + } + type user struct { ID int `form:"-"` tag string `form:"tag"` @@ -119,19 +130,24 @@ func TestParseForm(t *testing.T) { Intro string `form:",textarea"` StrBool bool `form:"strbool"` Date time.Time `form:"date,2006-01-02"` + OtherInfo } u := user{} form := url.Values{ - "ID": []string{"1"}, - "-": []string{"1"}, - "tag": []string{"no"}, - "username": []string{"test"}, - "age": []string{"40"}, - "Email": []string{"test@gmail.com"}, - "Intro": []string{"I am an engineer!"}, - "strbool": []string{"yes"}, - "date": []string{"2014-11-12"}, + "ID": []string{"1"}, + "-": []string{"1"}, + "tag": []string{"no"}, + "username": []string{"test"}, + "age": []string{"40"}, + "Email": []string{"test@gmail.com"}, + "Intro": []string{"I am an engineer!"}, + "strbool": []string{"yes"}, + "date": []string{"2014-11-12"}, + "organization": []string{"beego"}, + "title": []string{"CXO"}, + "hobby": []string{"Basketball"}, + "memo": []string{"nothing"}, } if err := ParseForm(form, u); err == nil { t.Fatal("nothing will be changed") @@ -157,13 +173,25 @@ func TestParseForm(t *testing.T) { if u.Intro != "I am an engineer!" { t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro) } - if u.StrBool != true { + if !u.StrBool { t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool) } y, m, d := u.Date.Date() if y != 2014 || m.String() != "November" || d != 12 { t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String()) } + if u.Organization != "beego" { + t.Errorf("Organization should equal `beego`, but got `%v`", u.Organization) + } + if u.Title != "CXO" { + t.Errorf("Title should equal `CXO`, but got `%v`", u.Title) + } + if u.Hobby != "Basketball" { + t.Errorf("Hobby should equal `Basketball`, but got `%v`", u.Hobby) + } + if len(u.Memo) != 0 { + t.Errorf("Memo's length should equal 0 but got %v", len(u.Memo)) + } } func TestRenderForm(t *testing.T) { @@ -195,54 +223,78 @@ func TestRenderForm(t *testing.T) { } func TestRenderFormField(t *testing.T) { - html := renderFormField("Label: ", "Name", "text", "Value", "", "") + html := renderFormField("Label: ", "Name", "text", "Value", "", "", false) if html != `Label: ` { t.Errorf("Wrong html output for input[type=text]: %v ", html) } - html = renderFormField("Label: ", "Name", "textarea", "Value", "", "") + html = renderFormField("Label: ", "Name", "textarea", "Value", "", "", false) if html != `Label: ` { t.Errorf("Wrong html output for textarea: %v ", html) } + + html = renderFormField("Label: ", "Name", "textarea", "Value", "", "", true) + if html != `Label: ` { + t.Errorf("Wrong html output for textarea: %v ", html) + } } func TestParseFormTag(t *testing.T) { // create struct to contain field with different types of struct-tag `form` type user struct { - All int `form:"name,text,年龄:"` - NoName int `form:",hidden,年龄:"` - OnlyLabel int `form:",,年龄:"` - OnlyName int `form:"name" id:"name" class:"form-name"` - Ignored int `form:"-"` + All int `form:"name,text,年龄:"` + NoName int `form:",hidden,年龄:"` + OnlyLabel int `form:",,年龄:"` + OnlyName int `form:"name" id:"name" class:"form-name"` + Ignored int `form:"-"` + Required int `form:"name" required:"true"` + IgnoreRequired int `form:"name"` + NotRequired int `form:"name" required:"false"` } objT := reflect.TypeOf(&user{}).Elem() - label, name, fType, id, class, ignored := parseFormTag(objT.Field(0)) - if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) { + label, name, fType, _, _, ignored, _ := parseFormTag(objT.Field(0)) + if !(name == "name" && label == "年龄:" && fType == "text" && !ignored) { t.Errorf("Form Tag with name, label and type was not correctly parsed.") } - label, name, fType, id, class, ignored = parseFormTag(objT.Field(1)) - if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) { + label, name, fType, _, _, ignored, _ = parseFormTag(objT.Field(1)) + if !(name == "NoName" && label == "年龄:" && fType == "hidden" && !ignored) { t.Errorf("Form Tag with label and type but without name was not correctly parsed.") } - label, name, fType, id, class, ignored = parseFormTag(objT.Field(2)) - if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) { + label, name, fType, _, _, ignored, _ = parseFormTag(objT.Field(2)) + if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && !ignored) { t.Errorf("Form Tag containing only label was not correctly parsed.") } - label, name, fType, id, class, ignored = parseFormTag(objT.Field(3)) - if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false && + label, name, fType, id, class, ignored, _ := parseFormTag(objT.Field(3)) + if !(name == "name" && label == "OnlyName: " && fType == "text" && !ignored && id == "name" && class == "form-name") { t.Errorf("Form Tag containing only name was not correctly parsed.") } - label, name, fType, id, class, ignored = parseFormTag(objT.Field(4)) - if ignored == false { + _, _, _, _, _, ignored, _ = parseFormTag(objT.Field(4)) + if !ignored { t.Errorf("Form Tag that should be ignored was not correctly parsed.") } + + _, name, _, _, _, _, required := parseFormTag(objT.Field(5)) + if !(name == "name" && required) { + t.Errorf("Form Tag containing only name and required was not correctly parsed.") + } + + _, name, _, _, _, _, required = parseFormTag(objT.Field(6)) + if !(name == "name" && !required) { + t.Errorf("Form Tag containing only name and ignore required was not correctly parsed.") + } + + _, name, _, _, _, _, required = parseFormTag(objT.Field(7)) + if !(name == "name" && !required) { + t.Errorf("Form Tag containing only name and not required was not correctly parsed.") + } + } func TestMapGet(t *testing.T) { diff --git a/src/vendor/github.com/astaxie/beego/toolbox/statistics.go b/src/vendor/github.com/astaxie/beego/toolbox/statistics.go index 32eb7e23a..d014544c3 100644 --- a/src/vendor/github.com/astaxie/beego/toolbox/statistics.go +++ b/src/vendor/github.com/astaxie/beego/toolbox/statistics.go @@ -99,9 +99,13 @@ func (m *URLMap) GetMap() map[string]interface{} { fmt.Sprintf("% -50s", k), fmt.Sprintf("% -10s", kk), fmt.Sprintf("% -16d", vv.RequestNum), + fmt.Sprintf("%d", vv.TotalTime), fmt.Sprintf("% -16s", toS(vv.TotalTime)), + fmt.Sprintf("%d", vv.MaxTime), fmt.Sprintf("% -16s", toS(vv.MaxTime)), + fmt.Sprintf("%d", vv.MinTime), fmt.Sprintf("% -16s", toS(vv.MinTime)), + fmt.Sprintf("%d", time.Duration(int64(vv.TotalTime)/vv.RequestNum)), fmt.Sprintf("% -16s", toS(time.Duration(int64(vv.TotalTime)/vv.RequestNum))), } resultLists = append(resultLists, result) @@ -113,6 +117,8 @@ func (m *URLMap) GetMap() map[string]interface{} { // GetMapData return all mapdata func (m *URLMap) GetMapData() []map[string]interface{} { + m.lock.Lock() + defer m.lock.Unlock() var resultLists []map[string]interface{} diff --git a/src/vendor/github.com/astaxie/beego/toolbox/task.go b/src/vendor/github.com/astaxie/beego/toolbox/task.go index 537de4288..672717cd9 100644 --- a/src/vendor/github.com/astaxie/beego/toolbox/task.go +++ b/src/vendor/github.com/astaxie/beego/toolbox/task.go @@ -389,6 +389,10 @@ func dayMatches(s *Schedule, t time.Time) bool { // StartTask start all tasks func StartTask() { + if isstart { + //If already started, no need to start another goroutine. + return + } isstart = true go run() } @@ -423,6 +427,7 @@ func run() { } continue case <-changed: + now = time.Now().Local() continue case <-stop: return @@ -432,8 +437,11 @@ func run() { // StopTask stop all tasks func StopTask() { - isstart = false - stop <- true + if isstart { + isstart = false + stop <- true + } + } // AddTask add task with name diff --git a/src/vendor/github.com/astaxie/beego/tree.go b/src/vendor/github.com/astaxie/beego/tree.go index 0601099cd..2d6c3fc3e 100644 --- a/src/vendor/github.com/astaxie/beego/tree.go +++ b/src/vendor/github.com/astaxie/beego/tree.go @@ -141,7 +141,7 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st regexpStr = "([^.]+).(.+)" params = params[1:] } else { - for _ = range params { + for range params { regexpStr = "([^/]+)/" + regexpStr } } @@ -254,7 +254,7 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, regexpStr = "/([^.]+).(.+)" params = params[1:] } else { - for _ = range params { + for range params { regexpStr = "/([^/]+)" + regexpStr } } @@ -288,10 +288,10 @@ func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{ return nil } w := make([]string, 0, 20) - return t.match(pattern, w, ctx) + return t.match(pattern[1:], pattern, w, ctx) } -func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { +func (t *Tree) match(treePattern string, pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { if len(pattern) > 0 { i := 0 for ; i < len(pattern) && pattern[i] == '/'; i++ { @@ -301,13 +301,13 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte // Handle leaf nodes: if len(pattern) == 0 { for _, l := range t.leaves { - if ok := l.match(wildcardValues, ctx); ok { + if ok := l.match(treePattern, wildcardValues, ctx); ok { return l.runObject } } if t.wildcard != nil { for _, l := range t.wildcard.leaves { - if ok := l.match(wildcardValues, ctx); ok { + if ok := l.match(treePattern, wildcardValues, ctx); ok { return l.runObject } } @@ -327,7 +327,12 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte } for _, subTree := range t.fixrouters { if subTree.prefix == seg { - runObject = subTree.match(pattern, wildcardValues, ctx) + if len(pattern) != 0 && pattern[0] == '/' { + treePattern = pattern[1:] + } else { + treePattern = pattern + } + runObject = subTree.match(treePattern, pattern, wildcardValues, ctx) if runObject != nil { break } @@ -339,7 +344,7 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte if strings.HasSuffix(seg, str) { for _, subTree := range t.fixrouters { if subTree.prefix == seg[:len(seg)-len(str)] { - runObject = subTree.match(pattern, wildcardValues, ctx) + runObject = subTree.match(treePattern, pattern, wildcardValues, ctx) if runObject != nil { ctx.Input.SetParam(":ext", str[1:]) } @@ -349,7 +354,7 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte } } if runObject == nil && t.wildcard != nil { - runObject = t.wildcard.match(pattern, append(wildcardValues, seg), ctx) + runObject = t.wildcard.match(treePattern, pattern, append(wildcardValues, seg), ctx) } if runObject == nil && len(t.leaves) > 0 { @@ -368,7 +373,7 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte wildcardValues = append(wildcardValues, pattern[start:i]) } for _, l := range t.leaves { - if ok := l.match(wildcardValues, ctx); ok { + if ok := l.match(treePattern, wildcardValues, ctx); ok { return l.runObject } } @@ -386,7 +391,7 @@ type leafInfo struct { runObject interface{} } -func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok bool) { +func (leaf *leafInfo) match(treePattern string, wildcardValues []string, ctx *context.Context) (ok bool) { //fmt.Println("Leaf:", wildcardValues, leaf.wildcards, leaf.regexps) if leaf.regexps == nil { if len(wildcardValues) == 0 && len(leaf.wildcards) == 0 { // static path @@ -394,7 +399,7 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b } // match * if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" { - ctx.Input.SetParam(":splat", path.Join(wildcardValues...)) + ctx.Input.SetParam(":splat", treePattern) return true } // match *.* or :id diff --git a/src/vendor/github.com/astaxie/beego/tree_test.go b/src/vendor/github.com/astaxie/beego/tree_test.go index 81ff7eddd..d412a3481 100644 --- a/src/vendor/github.com/astaxie/beego/tree_test.go +++ b/src/vendor/github.com/astaxie/beego/tree_test.go @@ -42,7 +42,7 @@ func init() { routers = append(routers, testinfo{"/", "/", nil}) routers = append(routers, testinfo{"/customer/login", "/customer/login", nil}) routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}}) - routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}}) + routers = append(routers, testinfo{"/*", "/http://customer/123/", map[string]string{":splat": "http://customer/123/"}}) routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}}) routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}}) routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}}) diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/captcha.go b/src/vendor/github.com/astaxie/beego/utils/captcha/captcha.go index 1a4a6edcb..42ac70d37 100644 --- a/src/vendor/github.com/astaxie/beego/utils/captcha/captcha.go +++ b/src/vendor/github.com/astaxie/beego/utils/captcha/captcha.go @@ -69,6 +69,7 @@ import ( "github.com/astaxie/beego" "github.com/astaxie/beego/cache" "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" "github.com/astaxie/beego/utils" ) @@ -139,7 +140,7 @@ func (c *Captcha) Handler(ctx *context.Context) { if err := c.store.Put(key, chars, c.Expiration); err != nil { ctx.Output.SetStatus(500) ctx.WriteString("captcha reload error") - beego.Error("Reload Create Captcha Error:", err) + logs.Error("Reload Create Captcha Error:", err) return } } else { @@ -154,7 +155,7 @@ func (c *Captcha) Handler(ctx *context.Context) { img := NewImage(chars, c.StdWidth, c.StdHeight) if _, err := img.WriteTo(ctx.ResponseWriter); err != nil { - beego.Error("Write Captcha Image Error:", err) + logs.Error("Write Captcha Image Error:", err) } } @@ -162,7 +163,7 @@ func (c *Captcha) Handler(ctx *context.Context) { func (c *Captcha) CreateCaptchaHTML() template.HTML { value, err := c.CreateCaptcha() if err != nil { - beego.Error("Create Captcha Error:", err) + logs.Error("Create Captcha Error:", err) return "" } diff --git a/src/vendor/github.com/astaxie/beego/utils/captcha/image.go b/src/vendor/github.com/astaxie/beego/utils/captcha/image.go index 1057192aa..c3c9a83a1 100644 --- a/src/vendor/github.com/astaxie/beego/utils/captcha/image.go +++ b/src/vendor/github.com/astaxie/beego/utils/captcha/image.go @@ -359,6 +359,9 @@ func (m *Image) calculateSizes(width, height, ncount int) { } // Calculate dot size. m.dotSize = int(nh / fh) + if m.dotSize < 1 { + m.dotSize = 1 + } // Save everything, making the actual width smaller by 1 dot to account // for spacing between digits. m.numWidth = int(nw) - m.dotSize @@ -471,7 +474,7 @@ func randomBrightness(c color.RGBA, max uint8) color.RGBA { uint8(int(c.R) + n), uint8(int(c.G) + n), uint8(int(c.B) + n), - uint8(c.A), + c.A, } } diff --git a/src/vendor/github.com/astaxie/beego/utils/file.go b/src/vendor/github.com/astaxie/beego/utils/file.go index db1978828..6090eb171 100644 --- a/src/vendor/github.com/astaxie/beego/utils/file.go +++ b/src/vendor/github.com/astaxie/beego/utils/file.go @@ -72,7 +72,7 @@ func GrepFile(patten string, filename string) (lines []string, err error) { lines = make([]string, 0) reader := bufio.NewReader(fd) prefix := "" - isLongLine := false + var isLongLine bool for { byteLine, isPrefix, er := reader.ReadLine() if er != nil && er != io.EOF { diff --git a/src/vendor/github.com/astaxie/beego/utils/file_test.go b/src/vendor/github.com/astaxie/beego/utils/file_test.go index 020d7e4c6..b26441577 100644 --- a/src/vendor/github.com/astaxie/beego/utils/file_test.go +++ b/src/vendor/github.com/astaxie/beego/utils/file_test.go @@ -41,7 +41,7 @@ func TestFileExists(t *testing.T) { } if FileExists(noExistedFile) { - t.Errorf("Wierd, how could this file exists: %s", noExistedFile) + t.Errorf("Weird, how could this file exists: %s", noExistedFile) } } @@ -52,9 +52,9 @@ func TestSearchFile(t *testing.T) { } t.Log(path) - path, err = SearchFile(noExistedFile, ".") + _, err = SearchFile(noExistedFile, ".") if err == nil { - t.Errorf("err shouldnot be nil, got path: %s", SelfDir()) + t.Errorf("err shouldnt be nil, got path: %s", SelfDir()) } } diff --git a/src/vendor/github.com/astaxie/beego/utils/mail.go b/src/vendor/github.com/astaxie/beego/utils/mail.go index 10555a0a3..e3fa1c909 100644 --- a/src/vendor/github.com/astaxie/beego/utils/mail.go +++ b/src/vendor/github.com/astaxie/beego/utils/mail.go @@ -232,14 +232,16 @@ func (e *Email) Send() error { return errors.New("Must specify at least one To address") } - from, err := mail.ParseAddress(e.Username) + // Use the username if no From is provided + if len(e.From) == 0 { + e.From = e.Username + } + + from, err := mail.ParseAddress(e.From) if err != nil { return err } - if len(e.From) == 0 { - e.From = e.Username - } // use mail's RFC 2047 to encode any string e.Subject = qEncode("utf-8", e.Subject) diff --git a/src/vendor/github.com/astaxie/beego/utils/pagination/controller.go b/src/vendor/github.com/astaxie/beego/utils/pagination/controller.go index 1d99cac5c..2f022d0c7 100644 --- a/src/vendor/github.com/astaxie/beego/utils/pagination/controller.go +++ b/src/vendor/github.com/astaxie/beego/utils/pagination/controller.go @@ -18,7 +18,7 @@ import ( "github.com/astaxie/beego/context" ) -// SetPaginator Instantiates a Paginator and assigns it to context.Input.Data["paginator"]. +// SetPaginator Instantiates a Paginator and assigns it to context.Input.Data("paginator"). func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) { paginator = NewPaginator(context.Request, per, nums) context.Input.SetData("paginator", &paginator) diff --git a/src/vendor/github.com/astaxie/beego/utils/rand.go b/src/vendor/github.com/astaxie/beego/utils/rand.go index 74bb41210..344d1cd53 100644 --- a/src/vendor/github.com/astaxie/beego/utils/rand.go +++ b/src/vendor/github.com/astaxie/beego/utils/rand.go @@ -20,28 +20,24 @@ import ( "time" ) +var alphaNum = []byte(`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`) + // RandomCreateBytes generate random []byte by specify chars. func RandomCreateBytes(n int, alphabets ...byte) []byte { - const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + if len(alphabets) == 0 { + alphabets = alphaNum + } var bytes = make([]byte, n) - var randby bool + var randBy bool if num, err := rand.Read(bytes); num != n || err != nil { r.Seed(time.Now().UnixNano()) - randby = true + randBy = true } for i, b := range bytes { - if len(alphabets) == 0 { - if randby { - bytes[i] = alphanum[r.Intn(len(alphanum))] - } else { - bytes[i] = alphanum[b%byte(len(alphanum))] - } + if randBy { + bytes[i] = alphabets[r.Intn(len(alphabets))] } else { - if randby { - bytes[i] = alphabets[r.Intn(len(alphabets))] - } else { - bytes[i] = alphabets[b%byte(len(alphabets))] - } + bytes[i] = alphabets[b%byte(len(alphabets))] } } return bytes diff --git a/src/vendor/github.com/astaxie/beego/utils/rand_test.go b/src/vendor/github.com/astaxie/beego/utils/rand_test.go new file mode 100644 index 000000000..6c238b5ef --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/utils/rand_test.go @@ -0,0 +1,33 @@ +// Copyright 2016 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import "testing" + +func TestRand_01(t *testing.T) { + bs0 := RandomCreateBytes(16) + bs1 := RandomCreateBytes(16) + + t.Log(string(bs0), string(bs1)) + if string(bs0) == string(bs1) { + t.FailNow() + } + + bs0 = RandomCreateBytes(4, []byte(`a`)...) + + if string(bs0) != "aaaa" { + t.FailNow() + } +} diff --git a/src/vendor/github.com/astaxie/beego/utils/safemap.go b/src/vendor/github.com/astaxie/beego/utils/safemap.go index 2e438f2c3..1793030a5 100644 --- a/src/vendor/github.com/astaxie/beego/utils/safemap.go +++ b/src/vendor/github.com/astaxie/beego/utils/safemap.go @@ -61,10 +61,8 @@ func (m *BeeMap) Set(k interface{}, v interface{}) bool { func (m *BeeMap) Check(k interface{}) bool { m.lock.RLock() defer m.lock.RUnlock() - if _, ok := m.bm[k]; !ok { - return false - } - return true + _, ok := m.bm[k] + return ok } // Delete the given key and value. @@ -84,3 +82,10 @@ func (m *BeeMap) Items() map[interface{}]interface{} { } return r } + +// Count returns the number of items within the map. +func (m *BeeMap) Count() int { + m.lock.RLock() + defer m.lock.RUnlock() + return len(m.bm) +} diff --git a/src/vendor/github.com/astaxie/beego/utils/safemap_test.go b/src/vendor/github.com/astaxie/beego/utils/safemap_test.go index fb271d180..1bfe8699e 100644 --- a/src/vendor/github.com/astaxie/beego/utils/safemap_test.go +++ b/src/vendor/github.com/astaxie/beego/utils/safemap_test.go @@ -14,25 +14,44 @@ package utils -import ( - "testing" -) +import "testing" -func Test_beemap(t *testing.T) { - bm := NewBeeMap() - if !bm.Set("astaxie", 1) { - t.Error("set Error") - } - if !bm.Check("astaxie") { - t.Error("check err") - } +var safeMap *BeeMap - if v := bm.Get("astaxie"); v.(int) != 1 { - t.Error("get err") - } - - bm.Delete("astaxie") - if bm.Check("astaxie") { - t.Error("delete err") +func TestNewBeeMap(t *testing.T) { + safeMap = NewBeeMap() + if safeMap == nil { + t.Fatal("expected to return non-nil BeeMap", "got", safeMap) + } +} + +func TestSet(t *testing.T) { + if ok := safeMap.Set("astaxie", 1); !ok { + t.Error("expected", true, "got", false) + } +} + +func TestCheck(t *testing.T) { + if exists := safeMap.Check("astaxie"); !exists { + t.Error("expected", true, "got", false) + } +} + +func TestGet(t *testing.T) { + if val := safeMap.Get("astaxie"); val.(int) != 1 { + t.Error("expected value", 1, "got", val) + } +} + +func TestDelete(t *testing.T) { + safeMap.Delete("astaxie") + if exists := safeMap.Check("astaxie"); exists { + t.Error("expected element to be deleted") + } +} + +func TestCount(t *testing.T) { + if count := safeMap.Count(); count != 0 { + t.Error("expected count to be", 0, "got", count) } } diff --git a/src/vendor/github.com/astaxie/beego/utils/utils.go b/src/vendor/github.com/astaxie/beego/utils/utils.go new file mode 100644 index 000000000..ed8857873 --- /dev/null +++ b/src/vendor/github.com/astaxie/beego/utils/utils.go @@ -0,0 +1,30 @@ +package utils + +import ( + "os" + "path/filepath" + "runtime" + "strings" +) + +// GetGOPATHs returns all paths in GOPATH variable. +func GetGOPATHs() []string { + gopath := os.Getenv("GOPATH") + if gopath == "" && strings.Compare(runtime.Version(), "go1.8") >= 0 { + gopath = defaultGOPATH() + } + return filepath.SplitList(gopath) +} + +func defaultGOPATH() string { + env := "HOME" + if runtime.GOOS == "windows" { + env = "USERPROFILE" + } else if runtime.GOOS == "plan9" { + env = "home" + } + if home := os.Getenv(env); home != "" { + return filepath.Join(home, "go") + } + return "" +} diff --git a/src/vendor/github.com/astaxie/beego/validation/README.md b/src/vendor/github.com/astaxie/beego/validation/README.md index 5c3212b0c..43373e47d 100644 --- a/src/vendor/github.com/astaxie/beego/validation/README.md +++ b/src/vendor/github.com/astaxie/beego/validation/README.md @@ -64,6 +64,9 @@ Struct Tag Use: func main() { valid := validation.Validation{} + // ignore empty field valid + // see CanSkipFuncs + // valid := validation.Validation{RequiredFirst:true} u := user{Name: "test", Age: 40} b, err := valid.Valid(u) if err != nil { diff --git a/src/vendor/github.com/astaxie/beego/validation/util.go b/src/vendor/github.com/astaxie/beego/validation/util.go index 9e7460a68..66fce2837 100644 --- a/src/vendor/github.com/astaxie/beego/validation/util.go +++ b/src/vendor/github.com/astaxie/beego/validation/util.go @@ -25,6 +25,8 @@ import ( const ( // ValidTag struct tag ValidTag = "valid" + + wordsize = 32 << (^uint(0) >> 32 & 1) ) var ( @@ -43,6 +45,8 @@ var ( "Valid": true, "NoMatch": true, } + // ErrInt64On32 show 32 bit platform not support int64 + ErrInt64On32 = fmt.Errorf("not support int64 on 32-bit platform") ) func init() { @@ -249,16 +253,39 @@ func parseParam(t reflect.Type, s string) (i interface{}, err error) { switch t.Kind() { case reflect.Int: i, err = strconv.Atoi(s) + case reflect.Int64: + if wordsize == 32 { + return nil, ErrInt64On32 + } + i, err = strconv.ParseInt(s, 10, 64) + case reflect.Int32: + var v int64 + v, err = strconv.ParseInt(s, 10, 32) + if err == nil { + i = int32(v) + } + case reflect.Int16: + var v int64 + v, err = strconv.ParseInt(s, 10, 16) + if err == nil { + i = int16(v) + } + case reflect.Int8: + var v int64 + v, err = strconv.ParseInt(s, 10, 8) + if err == nil { + i = int8(v) + } case reflect.String: i = s case reflect.Ptr: if t.Elem().String() != "regexp.Regexp" { - err = fmt.Errorf("does not support %s", t.Elem().String()) + err = fmt.Errorf("not support %s", t.Elem().String()) return } i, err = regexp.Compile(s) default: - err = fmt.Errorf("does not support %s", t.Kind().String()) + err = fmt.Errorf("not support %s", t.Kind().String()) } return } diff --git a/src/vendor/github.com/astaxie/beego/validation/util_test.go b/src/vendor/github.com/astaxie/beego/validation/util_test.go index d7e105060..e74d50edf 100644 --- a/src/vendor/github.com/astaxie/beego/validation/util_test.go +++ b/src/vendor/github.com/astaxie/beego/validation/util_test.go @@ -42,7 +42,7 @@ func TestGetValidFuncs(t *testing.T) { } f, _ = tf.FieldByName("Tag") - if vfs, err = getValidFuncs(f); err.Error() != "doesn't exsits Maxx valid function" { + if _, err = getValidFuncs(f); err.Error() != "doesn't exsits Maxx valid function" { t.Fatal(err) } diff --git a/src/vendor/github.com/astaxie/beego/validation/validation.go b/src/vendor/github.com/astaxie/beego/validation/validation.go index 2b020aa8d..ef484fb37 100644 --- a/src/vendor/github.com/astaxie/beego/validation/validation.go +++ b/src/vendor/github.com/astaxie/beego/validation/validation.go @@ -73,6 +73,10 @@ func (e *Error) String() string { return e.Message } +// Implement Error interface. +// Return e.String() +func (e *Error) Error() string { return e.String() } + // Result is returned from every validation method. // It provides an indication of success, and a pointer to the Error (if any). type Result struct { @@ -102,6 +106,11 @@ func (r *Result) Message(message string, args ...interface{}) *Result { // A Validation context manages data validation and error messages. type Validation struct { + // if this field set true, in struct tag valid + // if the struct field vale is empty + // it will skip those valid functions, see CanSkipFuncs + RequiredFirst bool + Errors []*Error ErrorsMap map[string]*Error } @@ -320,7 +329,19 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { if vfs, err = getValidFuncs(objT.Field(i)); err != nil { return } + + var hasReuired bool for _, vf := range vfs { + if vf.Name == "Required" { + hasReuired = true + } + + if !hasReuired && v.RequiredFirst && len(objV.Field(i).String()) == 0 { + if _, ok := CanSkipFuncs[vf.Name]; ok { + continue + } + } + if _, err = funcs.Call(vf.Name, mergeParam(v, objV.Field(i).Interface(), vf.Params)...); err != nil { return @@ -345,7 +366,7 @@ func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { //Step 1: validate obj itself firstly // fails if objc is not struct pass, err := v.Valid(objc) - if err != nil || false == pass { + if err != nil || !pass { return pass, err // Stop recursive validation } // Step 2: Validate struct's struct fields diff --git a/src/vendor/github.com/astaxie/beego/validation/validation_test.go b/src/vendor/github.com/astaxie/beego/validation/validation_test.go index ec65b6d02..bf6120152 100644 --- a/src/vendor/github.com/astaxie/beego/validation/validation_test.go +++ b/src/vendor/github.com/astaxie/beego/validation/validation_test.go @@ -35,6 +35,12 @@ func TestRequired(t *testing.T) { if valid.Required("", "string").Ok { t.Error("\"'\" string should be false") } + if valid.Required(" ", "string").Ok { + t.Error("\" \" string should be false") // For #2361 + } + if valid.Required("\n", "string").Ok { + t.Error("new line string should be false") // For #2361 + } if !valid.Required("astaxie", "string").Ok { t.Error("string should be true") } @@ -175,10 +181,10 @@ func TestAlphaNumeric(t *testing.T) { func TestMatch(t *testing.T) { valid := Validation{} - if valid.Match("suchuangji@gmail", regexp.MustCompile("^\\w+@\\w+\\.\\w+$"), "match").Ok { + if valid.Match("suchuangji@gmail", regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok { t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be false") } - if !valid.Match("suchuangji@gmail.com", regexp.MustCompile("^\\w+@\\w+\\.\\w+$"), "match").Ok { + if !valid.Match("suchuangji@gmail.com", regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok { t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be true") } } @@ -186,10 +192,10 @@ func TestMatch(t *testing.T) { func TestNoMatch(t *testing.T) { valid := Validation{} - if valid.NoMatch("123@gmail", regexp.MustCompile("[^\\w\\d]"), "nomatch").Ok { + if valid.NoMatch("123@gmail", regexp.MustCompile(`[^\w\d]`), "nomatch").Ok { t.Error("\"123@gmail\" not match \"[^\\w\\d]\" should be false") } - if !valid.NoMatch("123gmail", regexp.MustCompile("[^\\w\\d]"), "match").Ok { + if !valid.NoMatch("123gmail", regexp.MustCompile(`[^\w\d]`), "match").Ok { t.Error("\"123@gmail\" not match \"[^\\w\\d@]\" should be true") } } @@ -214,6 +220,12 @@ func TestEmail(t *testing.T) { if !valid.Email("suchuangji@gmail.com", "email").Ok { t.Error("\"suchuangji@gmail.com\" is a valid email address should be true") } + if valid.Email("@suchuangji@gmail.com", "email").Ok { + t.Error("\"@suchuangji@gmail.com\" is a valid email address should be false") + } + if valid.Email("suchuangji@gmail.com ok", "email").Ok { + t.Error("\"suchuangji@gmail.com ok\" is a valid email address should be false") + } } func TestIP(t *testing.T) { @@ -379,3 +391,54 @@ func TestRecursiveValid(t *testing.T) { t.Error("validation should not be passed") } } + +func TestSkipValid(t *testing.T) { + type User struct { + ID int + + Email string `valid:"Email"` + ReqEmail string `valid:"Required;Email"` + + IP string `valid:"IP"` + ReqIP string `valid:"Required;IP"` + + Mobile string `valid:"Mobile"` + ReqMobile string `valid:"Required;Mobile"` + + Tel string `valid:"Tel"` + ReqTel string `valid:"Required;Tel"` + + Phone string `valid:"Phone"` + ReqPhone string `valid:"Required;Phone"` + + ZipCode string `valid:"ZipCode"` + ReqZipCode string `valid:"Required;ZipCode"` + } + + u := User{ + ReqEmail: "a@a.com", + ReqIP: "127.0.0.1", + ReqMobile: "18888888888", + ReqTel: "02088888888", + ReqPhone: "02088888888", + ReqZipCode: "510000", + } + + valid := Validation{} + b, err := valid.Valid(u) + if err != nil { + t.Fatal(err) + } + if b { + t.Fatal("validation should not be passed") + } + + valid = Validation{RequiredFirst: true} + b, err = valid.Valid(u) + if err != nil { + t.Fatal(err) + } + if !b { + t.Fatal("validation should be passed") + } +} diff --git a/src/vendor/github.com/astaxie/beego/validation/validators.go b/src/vendor/github.com/astaxie/beego/validation/validators.go index 9b04c5cef..4dff9c0b4 100644 --- a/src/vendor/github.com/astaxie/beego/validation/validators.go +++ b/src/vendor/github.com/astaxie/beego/validation/validators.go @@ -18,10 +18,21 @@ import ( "fmt" "reflect" "regexp" + "strings" "time" "unicode/utf8" ) +// CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty +var CanSkipFuncs = map[string]struct{}{ + "Email": {}, + "IP": {}, + "Mobile": {}, + "Tel": {}, + "Phone": {}, + "ZipCode": {}, +} + // MessageTmpls store commond validate template var MessageTmpls = map[string]string{ "Required": "Can not be empty", @@ -98,7 +109,7 @@ func (r Required) IsSatisfied(obj interface{}) bool { } if str, ok := obj.(string); ok { - return len(str) > 0 + return len(strings.TrimSpace(str)) > 0 } if _, ok := obj.(bool); ok { return true @@ -145,7 +156,7 @@ func (r Required) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default error message func (r Required) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Required"]) + return MessageTmpls["Required"] } // GetKey return the r.Key @@ -165,12 +176,28 @@ type Min struct { } // IsSatisfied judge whether obj is valid +// not support int64 on 32-bit platform func (m Min) IsSatisfied(obj interface{}) bool { - num, ok := obj.(int) - if ok { - return num >= m.Min + var v int + switch obj.(type) { + case int64: + if wordsize == 32 { + return false + } + v = int(obj.(int64)) + case int: + v = obj.(int) + case int32: + v = int(obj.(int32)) + case int16: + v = int(obj.(int16)) + case int8: + v = int(obj.(int8)) + default: + return false } - return false + + return v >= m.Min } // DefaultMessage return the default min error message @@ -195,12 +222,28 @@ type Max struct { } // IsSatisfied judge whether obj is valid +// not support int64 on 32-bit platform func (m Max) IsSatisfied(obj interface{}) bool { - num, ok := obj.(int) - if ok { - return num <= m.Max + var v int + switch obj.(type) { + case int64: + if wordsize == 32 { + return false + } + v = int(obj.(int64)) + case int: + v = obj.(int) + case int32: + v = int(obj.(int32)) + case int16: + v = int(obj.(int16)) + case int8: + v = int(obj.(int8)) + default: + return false } - return false + + return v <= m.Max } // DefaultMessage return the default max error message @@ -226,6 +269,7 @@ type Range struct { } // IsSatisfied judge whether obj is valid +// not support int64 on 32-bit platform func (r Range) IsSatisfied(obj interface{}) bool { return r.Min.IsSatisfied(obj) && r.Max.IsSatisfied(obj) } @@ -364,7 +408,7 @@ func (a Alpha) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default Length error message func (a Alpha) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Alpha"]) + return MessageTmpls["Alpha"] } // GetKey return the m.Key @@ -397,7 +441,7 @@ func (n Numeric) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default Length error message func (n Numeric) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Numeric"]) + return MessageTmpls["Numeric"] } // GetKey return the n.Key @@ -430,7 +474,7 @@ func (a AlphaNumeric) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default Length error message func (a AlphaNumeric) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["AlphaNumeric"]) + return MessageTmpls["AlphaNumeric"] } // GetKey return the a.Key @@ -495,7 +539,7 @@ func (n NoMatch) GetLimitValue() interface{} { return n.Regexp.String() } -var alphaDashPattern = regexp.MustCompile("[^\\d\\w-_]") +var alphaDashPattern = regexp.MustCompile(`[^\d\w-_]`) // AlphaDash check not Alpha type AlphaDash struct { @@ -505,7 +549,7 @@ type AlphaDash struct { // DefaultMessage return the default AlphaDash error message func (a AlphaDash) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["AlphaDash"]) + return MessageTmpls["AlphaDash"] } // GetKey return the n.Key @@ -518,7 +562,7 @@ func (a AlphaDash) GetLimitValue() interface{} { return nil } -var emailPattern = regexp.MustCompile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?") +var emailPattern = regexp.MustCompile(`^[\w!#$%&'*+/=?^_` + "`" + `{|}~-]+(?:\.[\w!#$%&'*+/=?^_` + "`" + `{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[a-zA-Z0-9](?:[\w-]*[\w])?$`) // Email check struct type Email struct { @@ -528,7 +572,7 @@ type Email struct { // DefaultMessage return the default Email error message func (e Email) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Email"]) + return MessageTmpls["Email"] } // GetKey return the n.Key @@ -541,7 +585,7 @@ func (e Email) GetLimitValue() interface{} { return nil } -var ipPattern = regexp.MustCompile("^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$") +var ipPattern = regexp.MustCompile(`^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$`) // IP check struct type IP struct { @@ -551,7 +595,7 @@ type IP struct { // DefaultMessage return the default IP error message func (i IP) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["IP"]) + return MessageTmpls["IP"] } // GetKey return the i.Key @@ -564,7 +608,7 @@ func (i IP) GetLimitValue() interface{} { return nil } -var base64Pattern = regexp.MustCompile("^(?:[A-Za-z0-99+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$") +var base64Pattern = regexp.MustCompile(`^(?:[A-Za-z0-99+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$`) // Base64 check struct type Base64 struct { @@ -574,7 +618,7 @@ type Base64 struct { // DefaultMessage return the default Base64 error message func (b Base64) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Base64"]) + return MessageTmpls["Base64"] } // GetKey return the b.Key @@ -588,7 +632,7 @@ func (b Base64) GetLimitValue() interface{} { } // just for chinese mobile phone number -var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][06789]|[4][579]))\\d{8}$") +var mobilePattern = regexp.MustCompile(`^((\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][06789]|[4][579]))\d{8}$`) // Mobile check struct type Mobile struct { @@ -598,7 +642,7 @@ type Mobile struct { // DefaultMessage return the default Mobile error message func (m Mobile) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Mobile"]) + return MessageTmpls["Mobile"] } // GetKey return the m.Key @@ -612,7 +656,7 @@ func (m Mobile) GetLimitValue() interface{} { } // just for chinese telephone number -var telPattern = regexp.MustCompile("^(0\\d{2,3}(\\-)?)?\\d{7,8}$") +var telPattern = regexp.MustCompile(`^(0\d{2,3}(\-)?)?\d{7,8}$`) // Tel check telephone struct type Tel struct { @@ -622,7 +666,7 @@ type Tel struct { // DefaultMessage return the default Tel error message func (t Tel) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Tel"]) + return MessageTmpls["Tel"] } // GetKey return the t.Key @@ -649,7 +693,7 @@ func (p Phone) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default Phone error message func (p Phone) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Phone"]) + return MessageTmpls["Phone"] } // GetKey return the p.Key @@ -663,7 +707,7 @@ func (p Phone) GetLimitValue() interface{} { } // just for chinese zipcode -var zipCodePattern = regexp.MustCompile("^[1-9]\\d{5}$") +var zipCodePattern = regexp.MustCompile(`^[1-9]\d{5}$`) // ZipCode check the zip struct type ZipCode struct { @@ -673,7 +717,7 @@ type ZipCode struct { // DefaultMessage return the default Zip error message func (z ZipCode) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["ZipCode"]) + return MessageTmpls["ZipCode"] } // GetKey return the z.Key