作者 唐旭辉

新增

要显示太多修改。

为保证性能只显示 27 of 27+ 个文件。

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