1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-11 11:11:01 +00:00

245 Commits

Author SHA1 Message Date
f7f390dfec fix #1221 2015-06-16 14:53:38 +08:00
8f7246e17b change to version 1.5.0 2015-06-15 23:49:13 +08:00
c8f6e0f156 remove the hardcode in runtime.Caller 2015-06-15 20:53:49 +08:00
0207caab6f keep the shortname for logs info/warn/debug 2015-06-15 20:44:14 +08:00
d629c1d3d0 change the comments 2015-06-15 20:22:05 +08:00
817650aa33 keep the short name for logs 2015-06-15 20:20:37 +08:00
ba1232dfaf filter should be always the same 2015-06-14 18:35:46 +08:00
64d4f6518b fix #1213 2015-06-14 18:10:10 +08:00
9f05db8475 Merge pull request #1212 from astaxie/revert-1211-revert-1210-develop
Revert "Revert "fix multiple filters execute issue""
2015-06-14 01:14:42 +08:00
b275d7c6f5 Revert "Revert "fix multiple filters execute issue"" 2015-06-14 01:14:33 +08:00
73770fbe22 Merge pull request #1211 from astaxie/revert-1210-develop
Revert "fix multiple filters execute issue"
2015-06-14 01:13:42 +08:00
fc11169ee3 Revert "fix multiple filters execute issue" 2015-06-14 01:13:34 +08:00
b54589fa9d Merge pull request #1210 from oiooj/develop
fix multiple filters execute issue
2015-06-14 01:08:51 +08:00
2af0c569a5 The last filterFunc with returnOnOutput=ture won't be executed
ex:
	beego.InsertFilter("/*", beego.BeforeExec, FilterLoginCheck1,false)
	beego.InsertFilter("/*", beego.BeforeExec, FilterLoginCheck2)

In function  FilterLoginCheck1 , I'll write data via ResponseWriter, and w.started = true
FilterLoginCheck2 won't be executed, it should be.
2015-06-14 01:02:41 +08:00
27b7a8f743 Merge pull request #1 from astaxie/develop
Develop
2015-06-14 00:35:38 +08:00
c143a6ec19 fix #1090 add Getfiles to support mulit file upload 2015-06-13 16:20:26 +08:00
e619d83990 fix the filter router issues 2015-06-13 12:47:01 +08:00
27b452cd95 Merge branch 'develop' of https://github.com/astaxie/beego into develop 2015-06-13 11:15:44 +08:00
b776e43962 merge bat/httplib to httplib 2015-06-13 11:15:13 +08:00
cb89cd577d Merge pull request #1201 from kongjian/develop
support eq&ne for orm
2015-06-13 10:54:47 +08:00
6b777f0c5e Merge pull request #1207 from oiooj/develop
Don't overwrite the params from function  ValidRouter
2015-06-13 09:16:07 +08:00
491238ce7d Don't overwrite the params from function ValidRouter
just add new params to context.Input.Params
2015-06-13 01:04:46 +08:00
d9bb1a3592 logs support elasticsearch adapter 2015-06-13 00:25:48 +08:00
9c6775c22c log default use synchro, and support async 2015-06-13 00:25:21 +08:00
4d70b22f96 Merge pull request #1157 from ziyel/master
Let filter function get more params info from ctx.Input.Params
2015-06-11 14:38:19 +08:00
d943d16d52 gofmt 2015-06-10 21:26:04 +08:00
bbb6f31f16 support eq&ne for orm 2015-06-09 10:18:21 +08:00
364cacf659 record the critical logs in Prod 2015-06-08 22:00:28 +08:00
21586586ba Merge pull request #1198 from kongjian/develop
remove space after int()& add sort for commentsRouter file
2015-06-08 20:20:40 +08:00
e1d7bc8826 remove space after int()& add sort for commentsRouter file 2015-06-08 17:25:46 +08:00
499ee09d4b Merge pull request #1194 from zieckey/GetMulti
Add GetMulti method for Cache interface
2015-06-08 08:36:20 +08:00
970f0b460c Add GetMulti method for Cache interface 2015-06-07 21:33:01 +08:00
9280683935 Merge pull request #1193 from zieckey/auth
Execute AUTH command when the "password" is configured
2015-06-07 20:34:32 +08:00
a58c8180e8 Execute AUTH command when the "password" is configured 2015-06-07 16:26:23 +08:00
b9852df51c Merge pull request #1190 from xboston/patch-1
fix session table
2015-06-04 22:09:26 +08:00
8e71d31dbe fix session table 2015-06-04 18:40:10 +05:00
db06e954b5 fix the session memcache bug 2015-05-28 12:04:19 +08:00
3abd01799d split into small files 2015-05-27 23:46:45 +08:00
ae37689314 fix #1176 grace support windows 2015-05-27 23:22:05 +08:00
40974365e6 Merge branch 'develop' of https://github.com/astaxie/beego into develop 2015-05-25 09:10:59 +08:00
edbaa080f1 update the string 2015-05-25 09:10:38 +08:00
d56491ab3a sync beeApp.Server to graceful 2015-05-25 09:10:38 +08:00
9fd7acf663 fix #1152 2015-05-25 09:10:37 +08:00
2dca48f26e fix sesseion redis db error 2015-05-25 09:10:37 +08:00
4138fe0217 beego suppot graceful restart application 2015-05-25 09:10:37 +08:00
245762f7d9 add apk mime 2015-05-25 09:10:37 +08:00
18bdec951d fix #1143 2015-05-25 09:10:37 +08:00
9252301fa0 Fix save config ini file 2015-05-25 09:10:37 +08:00
1053b63bbc Improve documentation of filter.go. 2015-05-25 09:10:37 +08:00
3bd6caae0a set default timeout 2015-05-25 09:10:37 +08:00
d8734cf58d set default timeout 2015-05-25 09:10:37 +08:00
51161361db more fixed 2015-05-25 09:10:37 +08:00
6da0cdb9e2 no need lock here 2015-05-25 09:10:37 +08:00
9f070c622b no need defer here 2015-05-25 09:10:37 +08:00
738e22e389 zero timeout means wait until resp 2015-05-25 09:10:37 +08:00
69fc22f0df typo fixed 2015-05-25 09:10:37 +08:00
4cd7177ece typo fixed 2015-05-25 09:10:37 +08:00
8e618192c2 better code and fixed 2015-05-25 09:10:37 +08:00
3415a5b091 better go style 2015-05-25 09:10:37 +08:00
dfe055c47c remove useless comments 2015-05-25 09:10:36 +08:00
74b22649c8 remove the double isStruct/isStructPtr check 2015-05-25 09:10:36 +08:00
4255630564 add Recursively validation 2015-05-25 09:10:36 +08:00
7e3b5e5307 remove unreached code 2015-05-25 09:10:36 +08:00
0222b8d693 fixed: when RelatedSel have multi string/relation, it only get last string 2015-05-25 09:10:36 +08:00
23268b788a *feature) 增加logcalldepth接口,使得能简单再次封装log系列函数。 2015-05-25 09:10:36 +08:00
ee4fd60e4d no output the dump 2015-05-25 09:10:36 +08:00
4414659df4 fix the init struct 2015-05-25 09:10:36 +08:00
68ccd8e5a4 fix the dump has no body 2015-05-25 09:10:36 +08:00
6f802b0a05 fix the params 2015-05-25 09:10:36 +08:00
743628a946 add DumpRequest 2015-05-25 09:10:36 +08:00
d90ce15707 httplib support gzip 2015-05-25 09:10:36 +08:00
23457ed2a0 add sethost 2015-05-25 09:10:36 +08:00
d7791ba837 Update validators.go
//176 中国联通
//177 中国电信
//145 中国联通
//147 中国移动
//149 中国电信
2015-05-25 09:10:36 +08:00
676595213f fix a comment error. 2015-05-25 09:10:36 +08:00
d446b5b011 improve the defaultval 2015-05-25 09:10:36 +08:00
2ba12ad1e1 config read and set support Runmode 2015-05-25 09:10:35 +08:00
8cc57e2fc8 fix #1112 2015-05-25 09:10:35 +08:00
322b208566 Update session.go
remove = in if statement
2015-05-25 09:10:35 +08:00
fd610d6777 Update session.go
move expire in line 154 to 247, because it will cause session_cookie not writen to explorer
2015-05-25 09:10:35 +08:00
1148359570 session cookie support IE 2015-05-25 09:10:35 +08:00
55b1d6a897 Update validators.go
edit mobile regular expression ; add 184 Section No.
2015-05-25 09:10:35 +08:00
c4c9a50c42 fix #1081 2015-05-25 09:10:35 +08:00
a311d712a5 Update output.go 2015-05-25 09:10:35 +08:00
bb5351bb9f Update output.go
fix cookie not work in IE
2015-05-25 09:10:35 +08:00
26130a5df6 fix #1073 2015-05-25 09:10:35 +08:00
2c9363d29b add tests to ensure bool value require test always return true. 2015-05-25 09:10:35 +08:00
91886a4547 bugfix: if a form field type is bool, valid Required should always return true instead of return its value. 2015-05-25 09:10:35 +08:00
a7e60c93dc Set ErrorsMap to nil in Validation.Clear function 2015-05-25 09:10:35 +08:00
5b1705b2d6 Do not check log level in writerMsg() because the check is already done outside. 2015-05-25 09:10:35 +08:00
34940d00c0 Remove unnecessary optional group flag '?' since has to match one of comma or end of string 2015-05-25 09:10:35 +08:00
1a6ea693b5 Added to input.go: AcceptHtml, AcceptsXml and AcceptsJson functions which check the header agains a regex for simpler mult-content-type handling. 2015-05-25 09:10:35 +08:00
2e51c124f1 For enhancing performance, check log level before fmt.Sprintf() 2015-05-25 09:10:35 +08:00
1d8afdc9c9 gofmt -s & go_vet 2015-05-25 09:10:34 +08:00
1592e9c04d 验证码reload问题
当页面放置一段时间,验证码将从缓存中失效。当用户再来刷新验证码将出现验证码404。对于reload操作应该直接生成验证码。
2015-05-25 09:10:34 +08:00
bf6b0d3e1f add JsonBody 2015-05-25 09:10:34 +08:00
af71289c25 Merge pull request #1171 from astaxie/add-license
add missed LICENSE of captcha
2015-05-25 09:09:21 +08:00
b1efae6ff8 add missed LICENSE of captcha 2015-05-24 23:59:39 +08:00
92f3de4027 update the string 2015-05-20 11:09:30 +08:00
185089299c sync beeApp.Server to graceful 2015-05-20 11:07:23 +08:00
59c1e74e13 Merge pull request #1158 from mlgd/master
Fix save config ini file
2015-05-18 22:38:59 +08:00
9bb9855153 Let filter function get more params info from ctx.Input.Params 2015-05-18 14:42:18 +08:00
519602a553 fix #1152 2015-05-15 15:04:08 +08:00
740a526105 fix sesseion redis db error 2015-05-13 21:20:50 +08:00
98289cd8de beego suppot graceful restart application 2015-05-13 21:17:47 +08:00
56e2143630 add apk mime 2015-05-09 14:54:30 +08:00
c0cfb5277c Merge pull request #1127 from vanthanh2305/RelatedSel-multi-string/relation
RelatedSel multi string/relation
2015-05-07 23:12:29 +08:00
b29700c3c3 Merge pull request #1131 from JessonChan/develop
httplib more fixed
2015-05-07 22:59:41 +08:00
47be2fadb5 fix #1143 2015-05-05 21:36:31 +08:00
1d72629334 Fix save config ini file 2015-05-04 15:54:03 +02:00
b4880c5e1d Merge pull request #1140 from cr7pt0gr4ph7/doc-pull-request
Improve documentation of filter.go.
2015-05-04 09:47:25 +08:00
c0bb5b9237 Improve documentation of filter.go. 2015-05-03 23:21:32 +02:00
1e1068e81c Merge pull request #1132 from dafang/master
Recursively validations
2015-04-28 11:53:45 +08:00
6c3e274b6e set default timeout 2015-04-26 16:10:18 +08:00
f56bdb6284 set default timeout 2015-04-26 16:08:25 +08:00
d8fa118727 more fixed 2015-04-26 15:42:10 +08:00
0afd04ec6f no need lock here 2015-04-26 15:24:04 +08:00
973306b28d no need defer here 2015-04-26 02:19:38 +08:00
da8c3c3910 zero timeout means wait until resp 2015-04-26 02:17:46 +08:00
0c1bb6409a typo fixed 2015-04-26 02:06:19 +08:00
cddb4fdb60 typo fixed 2015-04-26 02:05:50 +08:00
e1d20aea5d better code and fixed 2015-04-26 02:04:34 +08:00
4498a02c15 better go style 2015-04-26 01:23:18 +08:00
5534258e22 remove useless comments 2015-04-24 11:17:12 +08:00
73650e1f2b remove the double isStruct/isStructPtr check 2015-04-24 11:14:49 +08:00
d0e7dd686b add Recursively validation 2015-04-24 10:58:46 +08:00
dc58ec8316 remove unreached code 2015-04-19 15:40:23 +08:00
6d72fc63ab fixed: when RelatedSel have multi string/relation, it only get last string 2015-04-15 17:41:41 +07:00
dd4afac5dc Merge pull request #1125 from wuranbo/dev_track
*feature) add BeeLogger.GetLogFuncCallDepth make simple wrapper log method aviable.
2015-04-13 12:49:51 +08:00
e229a4762f *feature) 增加logcalldepth接口,使得能简单再次封装log系列函数。 2015-04-13 12:17:44 +08:00
ecb27f34e6 no output the dump 2015-04-09 00:18:02 +08:00
d55997e520 fix the init struct 2015-04-09 00:12:41 +08:00
44c8534477 fix the dump has no body 2015-04-09 00:11:25 +08:00
44a39a6b3e fix the params 2015-04-08 23:00:08 +08:00
cc5ca458ab add DumpRequest 2015-04-08 22:58:37 +08:00
6f6b412709 httplib support gzip 2015-04-08 21:45:00 +08:00
642a69de02 add sethost 2015-04-08 20:12:10 +08:00
466f3c49c1 Merge pull request #1120 from peeped/develop
Update validators.go
2015-04-07 18:48:44 +08:00
d1c9cb2281 Update validators.go
//176 中国联通
//177 中国电信
//145 中国联通
//147 中国移动
//149 中国电信
2015-04-07 17:48:51 +08:00
9c9ffa202a Merge pull request #1119 from lionel0806/develop
fix a comment error.
2015-04-07 11:12:54 +08:00
56dfe418dd fix a comment error. 2015-04-07 10:35:18 +08:00
7caeb91f9b improve the defaultval 2015-04-05 23:23:35 +08:00
47848fa77b config read and set support Runmode 2015-04-05 23:21:13 +08:00
e0e8b98622 fix #1112 2015-04-05 23:12:29 +08:00
5c84ada389 Merge pull request #1106 from peeped/develop
Update validators.go
2015-04-04 20:46:43 +08:00
ac6203b81b Merge pull request #1114 from ElvizLai/patch-2
Patch 2
2015-04-04 13:03:44 +08:00
5e1e618d0f Update session.go
remove = in if statement
2015-04-04 00:44:22 +08:00
a5124a1d45 Update session.go
move expire in line 154 to 247, because it will cause session_cookie not writen to explorer
2015-04-03 17:41:09 +08:00
e675594e46 session cookie support IE 2015-04-02 14:02:39 +08:00
d02e32fa51 Merge pull request #1103 from ElvizLai/patch-1
Update output.go
2015-04-02 13:35:08 +08:00
8a82e25e85 Update validators.go
edit mobile regular expression ; add 184 Section No.
2015-04-02 12:07:12 +08:00
49c0f8906f fix #1081 2015-04-01 23:31:40 +08:00
9261c80509 Update output.go 2015-03-31 12:36:39 +08:00
217e24815b Update output.go
fix cookie not work in IE
2015-03-31 12:30:47 +08:00
840fd3b64f Merge pull request #1083 from supiyun/patch-1
验证码reload问题
2015-03-30 21:40:57 +08:00
eedaea2fea fix #1073 2015-03-30 20:35:57 +08:00
a002f78443 Merge pull request #1097 from pylemon/develop
form required validate for bool field bugfix
2015-03-27 23:04:07 +08:00
cdf9ff401f Merge pull request #1096 from byrnedo/develop
Set ErrorsMap to nil in Validation.Clear function
2015-03-27 14:46:45 +08:00
caa260f053 add tests to ensure bool value require test always return true. 2015-03-27 13:43:20 +08:00
5fa55ca2d3 bugfix: if a form field type is bool, valid Required should always return true instead of return its value. 2015-03-27 13:30:59 +08:00
260b5b1951 Set ErrorsMap to nil in Validation.Clear function 2015-03-26 20:23:00 +01:00
54ae4bc25b Merge pull request #1094 from toalexjin/check_log_level
For enhancing performance, check log level before fmt.Sprintf()
2015-03-26 21:51:23 +08:00
162bee1b43 Merge pull request #1095 from byrnedo/develop
Added AcceptHtml/Json/Xml function to input
2015-03-26 21:50:41 +08:00
533b00ae56 Merge branch 'master' into develop 2015-03-26 08:40:28 +01:00
d3cdebbee2 Do not check log level in writerMsg() because the check is already done outside. 2015-03-26 14:40:12 +08:00
06b5c7f644 Remove unnecessary optional group flag '?' since has to match one of comma or end of string 2015-03-25 14:54:39 +01:00
185ee872c4 Added to input.go: AcceptHtml, AcceptsXml and AcceptsJson functions which check the header agains a regex for simpler mult-content-type handling. 2015-03-25 14:47:20 +01:00
74e0af4a9a For enhancing performance, check log level before fmt.Sprintf() 2015-03-25 15:14:57 +08:00
8aa9455900 gofmt -s & go_vet 2015-03-19 22:29:01 -07:00
2d26f7df2f 验证码reload问题
当页面放置一段时间,验证码将从缓存中失效。当用户再来刷新验证码将出现验证码404。对于reload操作应该直接生成验证码。
2015-03-16 17:40:55 +08:00
3d6408cfc2 Merge pull request #1070 from fugr/patch-5
add JsonBody
2015-03-13 23:16:18 +08:00
223f57bb4c add JsonBody 2015-03-06 14:12:24 +08:00
c4aa33fb1b Merge pull request #1052 from dockercn/develop
Fix the wrong parameter bug in ledis session support cause build failure
2015-03-01 13:58:21 +08:00
20cc5b261e Reform the ledis_session.go 2015-03-01 12:59:34 +08:00
7ec2a077d9 Fix the wrong parameter bug in ledis session. 2015-03-01 12:03:03 +08:00
9f70561f21 Merge remote-tracking branch 'upstream/master' into develop 2015-03-01 11:47:02 +08:00
1c9898dee5 Merge branch 'develop' 2015-02-27 22:52:42 +08:00
2cf7c6a58a change the jQuery URL 2015-02-27 22:51:20 +08:00
2cee46ab2b change the jQuery URL 2015-02-27 22:50:25 +08:00
020bfbcc9c Merge branch 'develop' 2015-02-27 22:47:59 +08:00
3f8252bffd change the version from 1.4.2 to 1.4.3 2015-02-27 22:47:21 +08:00
6d313aa15f fix #985 2015-02-27 22:37:41 +08:00
2a4e2d4a71 delete the group route, because we already has namespace 2015-02-27 22:37:07 +08:00
f96a6285bf fix #978 2015-02-27 22:21:58 +08:00
e938876c4a fix the cycle import 2015-02-27 00:12:10 +08:00
6e9d2dc965 add more error functions 2015-02-26 23:49:24 +08:00
3aceaf8838 error support controller 2015-02-26 23:34:43 +08:00
71b9854f48 Merge pull request #1044 from fuxiaohei/develop
code style simplify
2015-02-23 22:25:34 +08:00
f59ccd3a35 Merge pull request #1043 from Hepri/develop
Added support to parse slices of ints and strings in ParseForm func
2015-02-23 21:58:32 +08:00
181a7c35fe code simplify for package middleware 2015-02-23 11:50:45 +08:00
2ed272aeb2 code simplify for package middleware 2015-02-23 11:50:13 +08:00
77c1109134 code simplify for package logs 2015-02-23 11:42:46 +08:00
29d4823866 code simplify for package httplib 2015-02-23 11:30:59 +08:00
24cf06d288 code style simplify for context package 2015-02-23 11:15:55 +08:00
0c31c2d689 Added support to parse slices of ints and strings in ParseForm func 2015-02-22 22:13:06 +05:00
f988f035e5 redis provider for session and cache support select db 2015-02-16 21:56:32 +08:00
1b4158c15b Merge pull request #1039 from astaxie/revert-1000-group_by_queryseter
Revert "Add GroupBy to QuerySeter"
2015-02-14 20:40:52 +08:00
433e8f2ce3 Revert "Add GroupBy to QuerySeter" 2015-02-14 20:40:43 +08:00
22ba7fdce4 Merge pull request #1000 from pdf/group_by_queryseter
Add GroupBy to QuerySeter
2015-02-14 20:33:06 +08:00
2a0f87e810 Merge pull request #1010 from BlackLee/master
add compare_not/not_nil methods for template
2015-02-14 20:31:18 +08:00
19db4b67f6 Merge pull request #1025 from kongjian/develop
Update task tpl
2015-02-14 20:29:28 +08:00
b1baf4503d beego task list update for task spec list and task run url error 2015-02-04 18:07:31 +08:00
d536f5b8dc Merge pull request #1021 from kmulvey/readme-typo
typos in the readme
2015-02-03 09:07:00 +08:00
1cc1d57f55 development 2015-02-02 09:33:59 -05:00
73370ade90 modular 2015-02-02 09:33:27 -05:00
0d3b7dcd07 Merge pull request #911 from supar/add-column-default-attribute
Add attribute default to the column on create or alter commands. Skip co...
2015-01-16 10:40:11 -08:00
d7fe5ef435 Merge pull request #1004 from fugr/patch-2
Transaction
2015-01-13 11:19:03 -08:00
8bd902814f Transaction
err处理写反了
2015-01-13 11:09:43 +08:00
378356a65e Merge pull request #1001 from johndeng/develop
Fixed the status code issue at error handler.
2015-01-12 09:54:26 -08:00
30871e2617 Fixed the status issue at error handler. 2015-01-10 17:35:35 +08:00
3731088b4a Add GroupBy to QuerySeter
Adds support for GroupBy to QuerySeter SELECT operations.
2015-01-10 15:26:41 +11:00
d46833c6d8 Merge pull request #997 from dockercn/master
增加session模块中的ledisdb的动态配置
2015-01-09 13:58:13 +08:00
18659e16ba add compare_not/not_nil methods for template 2015-01-05 16:38:57 +08:00
5d8187d005 Merge pull request #977 from athurg/patch-1
Fix RequestURI nil caused template parse failed
2014-12-25 11:40:26 +08:00
d961ae4cd8 Fix RequestURI nil caused template parse failed
Sometime RequestURI is not set, e.g. running after a front proxy server.

We should always follow the document's directive, to use Request.URL instead of RequestURI.

Refer: http://golang.org/pkg/net/http/#Request
2014-12-25 11:23:04 +08:00
0e1a0049d1 Merge pull request #971 from athurg/get_request_params_with_default
Support default value for controller’s params get
2014-12-19 16:03:10 +08:00
0c933643e2 improve the empty router 2014-12-19 15:33:51 +08:00
d2c5daa5ee Update comments for controller's GetXXX functions 2014-12-19 15:28:18 +08:00
d3ab157915 fix the cache test 2014-12-19 14:40:16 +08:00
75d28d49c5 Merge pull request #965 from shuoli84/develop
Fix subdomain, add test, space and comment fix
2014-12-19 13:22:36 +08:00
76bb4827d0 Merge pull request #953 from kristen1980/patch-2
Allow absolute path for filesystem cache
2014-12-18 21:24:17 +08:00
3caba06189 Merge pull request #967 from athurg/support_all_type_on_urlfor
Add all type support for UrlFor’s params
2014-12-18 21:14:44 +08:00
572508ddd8 Clean json config. Fix DefaultStrings 2014-12-17 17:02:46 +08:00
e34f8479bb Add all type support for UrlFor’s params 2014-12-17 15:52:48 +08:00
daf85f06f8 Support default value for controller’s params get 2014-12-17 15:23:11 +08:00
22671c524e Fix subdomain, add test, space and comment fix 2014-12-17 12:06:53 +08:00
ab99d5f1e2 Merge pull request #957 from athurg/patch-2
Fix paginator attributes cannot be modified bug
2014-12-11 19:59:50 +08:00
c52f634d9c Fix paginator attributes cannot be modified bug
We can only use SetPaginator to create a pagination.

After that, we always need to modify something, like the totalNum, perPageNum.

These change should be seen in the view.

So we should give the view a pointer than a object.
2014-12-11 16:42:50 +08:00
9c665afc04 improve the error tips 2014-12-08 14:57:45 +08:00
77ed151243 Allow absolute path for filesystem cache
Gives more flexibility by making it an absolute path. A relative path can easily be created by the user.
2014-12-07 10:00:35 -07:00
29d98731c6 add sess_ledis select db config 2014-11-25 14:41:51 -08:00
934dd2e8d2 Merge branch 'master' of https://github.com/astaxie/beego 2014-11-25 14:27:13 -08:00
e65d87974a Merge pull request #940 from hilyjiang/develop
make Content-Type header more human-readable
2014-11-24 23:26:15 +08:00
db04c3cbb4 make Content-Type header more human-readable 2014-11-24 23:12:09 +08:00
802aa16136 Merge pull request #935 from mnhkahn/master
beego1.4.2,beego.AppConfig.Strings与老版本代码不兼容问题
2014-11-24 21:47:12 +08:00
f2df07f630 Merge pull request #933 from rbastic/develop
Reword message about reloading packages..
2014-11-24 13:18:05 +08:00
dc89f844f3 Reword message about reloading packages.. 2014-11-23 18:22:45 +01:00
b80cdef20f Merge pull request #932 from DeanThompson/master
count log file lines
2014-11-23 23:57:27 +08:00
98dcee0643 Merge pull request #926 from xuewuhen/master
SubDomains function bugfixed
2014-11-23 22:57:40 +08:00
0ad75cb5fa Merge pull request #928 from lei-cao/develop
Return the response directly if it's a options PreflightHeader request
2014-11-21 23:22:59 +08:00
6a9d04c269 count log file lines 2014-11-21 18:12:39 +08:00
93ca11f83d Return the response directly if it's a options PreflightHeader request 2014-11-21 01:35:30 +08:00
git
d0b43ef4f5 beego.AppConfig.Strings bug 2014-11-20 16:35:04 +08:00
c9bb9d6a09 SubDomains function bugfixed 2014-11-18 22:54:48 +08:00
f96245786a fix #912 2014-11-08 15:10:47 +08:00
1a1b0c14b9 Add attribute default to the column on create or alter commands. Skip columns which are keys and date or time data type 2014-11-06 17:43:53 +03:00
82 changed files with 2559 additions and 957 deletions

View File

@ -3,7 +3,7 @@
[![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest) [![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest)
[![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego)
beego is an open-source, high-performance, modularity, full-stack web framework. beego is an open-source, high-performance, modular, full-stack web framework.
More info [beego.me](http://beego.me) More info [beego.me](http://beego.me)
@ -19,7 +19,7 @@ More info [beego.me](http://beego.me)
* Auto API documents * Auto API documents
* Annotation router * Annotation router
* Namespace * Namespace
* Powerful develop tools * Powerful development tools
* Full stack for Web & API * Full stack for Web & API
## Documentation ## Documentation

View File

@ -19,9 +19,11 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"text/template" "text/template"
"time" "time"
"github.com/astaxie/beego/grace"
"github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/toolbox"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
@ -333,7 +335,6 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl)) tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
} }
tmpl.Execute(rw, data) tmpl.Execute(rw, data)
} else {
} }
} }
@ -397,7 +398,7 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
if err != nil { if err != nil {
data["Message"] = []string{"error", fmt.Sprintf("%s", err)} data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
} }
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is %s", taskname, t.GetStatus())} data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus())}
} else { } else {
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)} data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
} }
@ -410,12 +411,14 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
var fields = []string{ var fields = []string{
fmt.Sprintf("Task Name"), fmt.Sprintf("Task Name"),
fmt.Sprintf("Task Spec"), fmt.Sprintf("Task Spec"),
fmt.Sprintf("Task Function"), fmt.Sprintf("Task Status"),
fmt.Sprintf("Last Time"),
fmt.Sprintf(""), fmt.Sprintf(""),
} }
for tname, tk := range toolbox.AdminTaskList { for tname, tk := range toolbox.AdminTaskList {
result = []string{ result = []string{
fmt.Sprintf("%s", tname), fmt.Sprintf("%s", tname),
fmt.Sprintf("%s", tk.GetSpec()),
fmt.Sprintf("%s", tk.GetStatus()), fmt.Sprintf("%s", tk.GetStatus()),
fmt.Sprintf("%s", tk.GetPrev().String()), fmt.Sprintf("%s", tk.GetPrev().String()),
} }
@ -457,8 +460,14 @@ func (admin *adminApp) Run() {
http.Handle(p, f) http.Handle(p, f)
} }
BeeLogger.Info("Admin server Running on %s", addr) BeeLogger.Info("Admin server Running on %s", addr)
err := http.ListenAndServe(addr, nil)
var err error
if Graceful {
err = grace.ListenAndServe(addr, nil)
} else {
err = http.ListenAndServe(addr, nil)
}
if err != nil { if err != nil {
BeeLogger.Critical("Admin ListenAndServe: ", err) BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
} }
} }

View File

@ -186,7 +186,7 @@ bg-warning
</td> </td>
{{end}} {{end}}
<td> <td>
<a class="btn btn-primary btn-sm" href="/task?taskname={{index $slice 1}}">Run</a> <a class="btn btn-primary btn-sm" href="/task?taskname={{index $slice 0}}">Run</a>
</td> </td>
</tr> </tr>
{{end}} {{end}}

143
app.go
View File

@ -19,7 +19,11 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/fcgi" "net/http/fcgi"
"os"
"time" "time"
"github.com/astaxie/beego/grace"
"github.com/astaxie/beego/utils"
) )
// App defines beego application with a new PatternServeMux. // App defines beego application with a new PatternServeMux.
@ -59,6 +63,10 @@ func (app *App) Run() {
} }
} else { } else {
if HttpPort == 0 { if HttpPort == 0 {
// remove the Socket file before start
if utils.FileExists(addr) {
os.Remove(addr)
}
l, err = net.Listen("unix", addr) l, err = net.Listen("unix", addr)
} else { } else {
l, err = net.Listen("tcp", addr) l, err = net.Listen("tcp", addr)
@ -69,57 +77,96 @@ func (app *App) Run() {
err = fcgi.Serve(l, app.Handlers) err = fcgi.Serve(l, app.Handlers)
} }
} else { } else {
app.Server.Addr = addr if Graceful {
app.Server.Handler = app.Handlers app.Server.Addr = addr
app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second app.Server.Handler = app.Handlers
app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second
app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second
if EnableHttpTLS {
go func() {
time.Sleep(20 * time.Microsecond)
if HttpsPort != 0 {
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
app.Server.Addr = addr
}
server := grace.NewServer(addr, app.Handlers)
server.Server = app.Server
err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
if err != nil {
BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}()
}
if EnableHttpListen {
go func() {
server := grace.NewServer(addr, app.Handlers)
server.Server = app.Server
if ListenTCP4 && HttpAddr == "" {
server.Network = "tcp4"
}
err := server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}()
}
} else {
app.Server.Addr = addr
app.Server.Handler = app.Handlers
app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second
app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second
if EnableHttpTLS { if EnableHttpTLS {
go func() { go func() {
time.Sleep(20 * time.Microsecond) time.Sleep(20 * time.Microsecond)
if HttpsPort != 0 { if HttpsPort != 0 {
app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
} }
BeeLogger.Info("https server Running on %s", app.Server.Addr) BeeLogger.Info("https server Running on %s", app.Server.Addr)
err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
if err != nil { if err != nil {
BeeLogger.Critical("ListenAndServeTLS: ", err) BeeLogger.Critical("ListenAndServeTLS: ", err)
time.Sleep(100 * time.Microsecond) time.Sleep(100 * time.Microsecond)
endRunning <- true endRunning <- true
} }
}() }()
}
if EnableHttpListen {
go func() {
app.Server.Addr = addr
BeeLogger.Info("http server Running on %s", app.Server.Addr)
if ListenTCP4 && HttpAddr == "" {
ln, err := net.Listen("tcp4", app.Server.Addr)
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
err = app.Server.Serve(ln)
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
} else {
err := app.Server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}
}()
}
} }
if EnableHttpListen {
go func() {
app.Server.Addr = addr
BeeLogger.Info("http server Running on %s", app.Server.Addr)
if ListenTCP4 && HttpAddr == "" {
ln, err := net.Listen("tcp4", app.Server.Addr)
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
err = app.Server.Serve(ln)
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
} else {
err := app.Server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}
}()
}
} }
<-endRunning <-endRunning
} }

View File

@ -33,83 +33,15 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
) )
// beego web framework version. // beego web framework version.
const VERSION = "1.4.2" const VERSION = "1.5.0"
type hookfunc func() error //hook function to run type hookfunc func() error //hook function to run
var hooks []hookfunc //hook function slice to store the hookfunc var hooks []hookfunc //hook function slice to store the hookfunc
type groupRouter struct {
pattern string
controller ControllerInterface
mappingMethods string
}
// RouterGroups which will store routers
type GroupRouters []groupRouter
// Get a new GroupRouters
func NewGroupRouters() GroupRouters {
return make(GroupRouters, 0)
}
// Add Router in the GroupRouters
// it is for plugin or module to register router
func (gr *GroupRouters) AddRouter(pattern string, c ControllerInterface, mappingMethod ...string) {
var newRG groupRouter
if len(mappingMethod) > 0 {
newRG = groupRouter{
pattern,
c,
mappingMethod[0],
}
} else {
newRG = groupRouter{
pattern,
c,
"",
}
}
*gr = append(*gr, newRG)
}
func (gr *GroupRouters) AddAuto(c ControllerInterface) {
newRG := groupRouter{
"",
c,
"",
}
*gr = append(*gr, newRG)
}
// AddGroupRouter with the prefix
// it will register the router in BeeApp
// the follow code is write in modules:
// GR:=NewGroupRouters()
// GR.AddRouter("/login",&UserController,"get:Login")
// GR.AddRouter("/logout",&UserController,"get:Logout")
// GR.AddRouter("/register",&UserController,"get:Reg")
// the follow code is write in app:
// import "github.com/beego/modules/auth"
// AddRouterGroup("/admin", auth.GR)
func AddGroupRouter(prefix string, groups GroupRouters) *App {
for _, v := range groups {
if v.pattern == "" {
BeeApp.Handlers.AddAutoPrefix(prefix, v.controller)
} else if v.mappingMethods != "" {
BeeApp.Handlers.Add(prefix+v.pattern, v.controller, v.mappingMethods)
} else {
BeeApp.Handlers.Add(prefix+v.pattern, v.controller)
}
}
return BeeApp
}
// Router adds a patterned controller handler to BeeApp. // Router adds a patterned controller handler to BeeApp.
// it's an alias method of App.Router. // it's an alias method of App.Router.
// usage: // usage:
@ -280,15 +212,6 @@ func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
return BeeApp return BeeApp
} }
// ErrorHandler registers http.HandlerFunc to each http err code string.
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
func Errorhandler(err string, h http.HandlerFunc) *App {
middleware.Errorhandler(err, h)
return BeeApp
}
// SetViewsPath sets view directory path in beego application. // SetViewsPath sets view directory path in beego application.
func SetViewsPath(path string) *App { func SetViewsPath(path string) *App {
ViewsPath = path ViewsPath = path
@ -402,9 +325,7 @@ func initBeforeHttpRun() {
} }
} }
middleware.VERSION = VERSION registerDefaultErrorHandler()
middleware.AppName = AppName
middleware.RegisterErrorHandler()
if EnableDocs { if EnableDocs {
Get("/docs", serverDocs) Get("/docs", serverDocs)
@ -415,7 +336,7 @@ func initBeforeHttpRun() {
// this function is for test package init // this function is for test package init
func TestBeegoInit(apppath string) { func TestBeegoInit(apppath string) {
AppPath = apppath AppPath = apppath
RunMode = "test" os.Setenv("BEEGO_RUNMODE", "test")
AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") AppConfigPath = filepath.Join(AppPath, "conf", "app.conf")
err := ParseConfig() err := ParseConfig()
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {

2
cache/cache.go vendored
View File

@ -47,6 +47,8 @@ import (
type Cache interface { type Cache interface {
// get cached value by key. // get cached value by key.
Get(key string) interface{} Get(key string) interface{}
// GetMulti is a batch version of Get.
GetMulti(keys []string) []interface{}
// set cached value with key and expire time. // set cached value with key and expire time.
Put(key string, val interface{}, timeout int64) error Put(key string, val interface{}, timeout int64) error
// delete cached value by key. // delete cached value by key.

55
cache/cache_test.go vendored
View File

@ -15,6 +15,7 @@
package cache package cache
import ( import (
"os"
"testing" "testing"
"time" "time"
) )
@ -64,10 +65,39 @@ func TestCache(t *testing.T) {
if bm.IsExist("astaxie") { if bm.IsExist("astaxie") {
t.Error("delete err") t.Error("delete err")
} }
//test GetMulti
if err = bm.Put("astaxie", "author", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie") {
t.Error("check err")
}
if v := bm.Get("astaxie"); v.(string) != "author" {
t.Error("get err")
}
if err = bm.Put("astaxie1", "author1", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie1") {
t.Error("check err")
}
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
if len(vv) != 2 {
t.Error("GetMulti ERROR")
}
if vv[0].(string) != "author" {
t.Error("GetMulti ERROR")
}
if vv[1].(string) != "author1" {
t.Error("GetMulti ERROR")
}
} }
func TestFileCache(t *testing.T) { func TestFileCache(t *testing.T) {
bm, err := NewCache("file", `{"CachePath":"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`) bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)
if err != nil { if err != nil {
t.Error("init err") t.Error("init err")
} }
@ -101,6 +131,7 @@ func TestFileCache(t *testing.T) {
if bm.IsExist("astaxie") { if bm.IsExist("astaxie") {
t.Error("delete err") t.Error("delete err")
} }
//test string //test string
if err = bm.Put("astaxie", "author", 10); err != nil { if err = bm.Put("astaxie", "author", 10); err != nil {
t.Error("set Error", err) t.Error("set Error", err)
@ -108,8 +139,28 @@ func TestFileCache(t *testing.T) {
if !bm.IsExist("astaxie") { if !bm.IsExist("astaxie") {
t.Error("check err") t.Error("check err")
} }
if v := bm.Get("astaxie"); v.(string) != "author" { if v := bm.Get("astaxie"); v.(string) != "author" {
t.Error("get err") t.Error("get err")
} }
//test GetMulti
if err = bm.Put("astaxie1", "author1", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie1") {
t.Error("check err")
}
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
if len(vv) != 2 {
t.Error("GetMulti ERROR")
}
if vv[0].(string) != "author" {
t.Error("GetMulti ERROR")
}
if vv[1].(string) != "author1" {
t.Error("GetMulti ERROR")
}
os.RemoveAll("cache")
} }

12
cache/file.go vendored
View File

@ -92,8 +92,6 @@ func (fc *FileCache) StartAndGC(config string) error {
// Init will make new dir for file cache if not exist. // Init will make new dir for file cache if not exist.
func (fc *FileCache) Init() { func (fc *FileCache) Init() {
app := filepath.Dir(os.Args[0])
fc.CachePath = filepath.Join(app, fc.CachePath)
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle _ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
} }
@ -134,6 +132,16 @@ func (fc *FileCache) Get(key string) interface{} {
return to.Data return to.Data
} }
// GetMulti gets values from file cache.
// if non-exist or expired, return empty string.
func (fc *FileCache) GetMulti(keys []string) []interface{} {
var rc []interface{}
for _, key := range keys {
rc = append(rc, fc.Get(key))
}
return rc
}
// Put value into file cache. // Put value into file cache.
// timeout means how long to keep this file, unit of ms. // timeout means how long to keep this file, unit of ms.
// if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever. // if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever.

View File

@ -63,6 +63,32 @@ func (rc *MemcacheCache) Get(key string) interface{} {
return nil return nil
} }
// get value from memcache.
func (rc *MemcacheCache) GetMulti(keys []string) []interface{} {
size := len(keys)
var rv []interface{}
if rc.conn == nil {
if err := rc.connectInit(); err != nil {
for i := 0; i < size; i++ {
rv = append(rv, err)
}
return rv
}
}
mv, err := rc.conn.GetMulti(keys)
if err == nil {
for _, v := range mv {
rv = append(rv, string(v.Value))
}
return rv
} else {
for i := 0; i < size; i++ {
rv = append(rv, err)
}
return rv
}
}
// put value to memcache. only support string. // put value to memcache. only support string.
func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error { func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error {
if rc.conn == nil { if rc.conn == nil {

106
cache/memcache/memcache_test.go vendored Normal file
View File

@ -0,0 +1,106 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package memcache
import (
_ "github.com/bradfitz/gomemcache/memcache"
"github.com/astaxie/beego/cache"
"strconv"
"testing"
"time"
)
func TestRedisCache(t *testing.T) {
bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`)
if err != nil {
t.Error("init err")
}
if err = bm.Put("astaxie", "1", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie") {
t.Error("check err")
}
time.Sleep(10 * time.Second)
if bm.IsExist("astaxie") {
t.Error("check err")
}
if err = bm.Put("astaxie", "1", 10); err != nil {
t.Error("set Error", err)
}
if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 {
t.Error("get err")
}
if err = bm.Incr("astaxie"); err != nil {
t.Error("Incr Error", err)
}
if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 2 {
t.Error("get err")
}
if err = bm.Decr("astaxie"); err != nil {
t.Error("Decr Error", err)
}
if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 {
t.Error("get err")
}
bm.Delete("astaxie")
if bm.IsExist("astaxie") {
t.Error("delete err")
}
//test string
if err = bm.Put("astaxie", "author", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie") {
t.Error("check err")
}
if v := bm.Get("astaxie").(string); v != "author" {
t.Error("get err")
}
//test GetMulti
if err = bm.Put("astaxie1", "author1", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie1") {
t.Error("check err")
}
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
if len(vv) != 2 {
t.Error("GetMulti ERROR")
}
if vv[0].(string) != "author" {
t.Error("GetMulti ERROR")
}
if vv[1].(string) != "author1" {
t.Error("GetMulti ERROR")
}
// test clear all
if err = bm.ClearAll(); err != nil {
t.Error("clear all err")
}
}

12
cache/memory.go vendored
View File

@ -64,6 +64,16 @@ func (bc *MemoryCache) Get(name string) interface{} {
return nil return nil
} }
// GetMulti gets caches from memory.
// if non-existed or expired, return nil.
func (bc *MemoryCache) GetMulti(names []string) []interface{} {
var rc []interface{}
for _, name := range names {
rc = append(rc, bc.Get(name))
}
return rc
}
// Put cache to memory. // Put cache to memory.
// if expired is 0, it will be cleaned by next gc operation ( default gc clock is 1 minute). // if expired is 0, it will be cleaned by next gc operation ( default gc clock is 1 minute).
func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error { func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error {
@ -202,7 +212,7 @@ func (bc *MemoryCache) vaccuum() {
if bc.items == nil { if bc.items == nil {
return return
} }
for name, _ := range bc.items { for name := range bc.items {
bc.item_expired(name) bc.item_expired(name)
} }
} }

65
cache/redis/redis.go vendored
View File

@ -32,6 +32,7 @@ package redis
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"strconv"
"time" "time"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
@ -48,7 +49,9 @@ var (
type RedisCache struct { type RedisCache struct {
p *redis.Pool // redis connection pool p *redis.Pool // redis connection pool
conninfo string conninfo string
dbNum int
key string key string
password string
} }
// create new redis cache with default collection name. // create new redis cache with default collection name.
@ -72,6 +75,39 @@ func (rc *RedisCache) Get(key string) interface{} {
return nil return nil
} }
// GetMulti get cache from redis.
func (rc *RedisCache) GetMulti(keys []string) []interface{} {
size := len(keys)
var rv []interface{}
c := rc.p.Get()
defer c.Close()
var err error
for _, key := range keys {
err = c.Send("GET", key)
if err != nil {
goto ERROR
}
}
if err = c.Flush(); err != nil {
goto ERROR
}
for i := 0; i < size; i++ {
if v, err := c.Receive(); err == nil {
rv = append(rv, v.([]byte))
} else {
rv = append(rv, err)
}
}
return rv
ERROR:
rv = rv[0:0]
for i := 0; i < size; i++ {
rv = append(rv, nil)
}
return rv
}
// put cache to redis. // put cache to redis.
func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error {
var err error var err error
@ -137,7 +173,7 @@ func (rc *RedisCache) ClearAll() error {
} }
// start redis cache adapter. // start redis cache adapter.
// config is like {"key":"collection key","conn":"connection info"} // config is like {"key":"collection key","conn":"connection info","dbNum":"0"}
// the cache item in redis are stored forever, // the cache item in redis are stored forever,
// so no gc operation. // so no gc operation.
func (rc *RedisCache) StartAndGC(config string) error { func (rc *RedisCache) StartAndGC(config string) error {
@ -147,13 +183,20 @@ func (rc *RedisCache) StartAndGC(config string) error {
if _, ok := cf["key"]; !ok { if _, ok := cf["key"]; !ok {
cf["key"] = DefaultKey cf["key"] = DefaultKey
} }
if _, ok := cf["conn"]; !ok { if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key") return errors.New("config has no conn key")
} }
if _, ok := cf["dbNum"]; !ok {
cf["dbNum"] = "0"
}
if _, ok := cf["password"]; !ok {
cf["password"] = ""
}
rc.key = cf["key"] rc.key = cf["key"]
rc.conninfo = cf["conn"] rc.conninfo = cf["conn"]
rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
rc.password = cf["password"]
rc.connectInit() rc.connectInit()
c := rc.p.Get() c := rc.p.Get()
@ -166,6 +209,22 @@ func (rc *RedisCache) StartAndGC(config string) error {
func (rc *RedisCache) connectInit() { func (rc *RedisCache) connectInit() {
dialFunc := func() (c redis.Conn, err error) { dialFunc := func() (c redis.Conn, err error) {
c, err = redis.Dial("tcp", rc.conninfo) c, err = redis.Dial("tcp", rc.conninfo)
if err != nil {
return nil, err
}
if rc.password != "" {
if _, err := c.Do("AUTH", rc.password); err != nil {
c.Close()
return nil, err
}
}
_, selecterr := c.Do("SELECT", rc.dbNum)
if selecterr != nil {
c.Close()
return nil, selecterr
}
return return
} }
// initialize a new pool // initialize a new pool

View File

@ -67,6 +67,7 @@ func TestRedisCache(t *testing.T) {
if bm.IsExist("astaxie") { if bm.IsExist("astaxie") {
t.Error("delete err") t.Error("delete err")
} }
//test string //test string
if err = bm.Put("astaxie", "author", 10); err != nil { if err = bm.Put("astaxie", "author", 10); err != nil {
t.Error("set Error", err) t.Error("set Error", err)
@ -78,6 +79,26 @@ func TestRedisCache(t *testing.T) {
if v, _ := redis.String(bm.Get("astaxie"), err); v != "author" { if v, _ := redis.String(bm.Get("astaxie"), err); v != "author" {
t.Error("get err") t.Error("get err")
} }
//test GetMulti
if err = bm.Put("astaxie1", "author1", 10); err != nil {
t.Error("set Error", err)
}
if !bm.IsExist("astaxie1") {
t.Error("check err")
}
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
if len(vv) != 2 {
t.Error("GetMulti ERROR")
}
if v, _ := redis.String(vv[0], nil); v != "author" {
t.Error("GetMulti ERROR")
}
if v, _ := redis.String(vv[1], nil); v != "author1" {
t.Error("GetMulti ERROR")
}
// test clear all // test clear all
if err = bm.ClearAll(); err != nil { if err = bm.ClearAll(); err != nil {
t.Error("clear all err") t.Error("clear all err")

View File

@ -81,6 +81,8 @@ var (
AppConfigProvider string // config provider AppConfigProvider string // config provider
EnableDocs bool // enable generate docs & server docs API Swagger EnableDocs bool // enable generate docs & server docs API Swagger
RouterCaseSensitive bool // router case sensitive default is true RouterCaseSensitive bool // router case sensitive default is true
AccessLogs bool // print access logs, default is false
Graceful bool // use graceful start the server
) )
type beegoAppConfig struct { type beegoAppConfig struct {
@ -97,6 +99,10 @@ func newAppConfig(AppConfigProvider, AppConfigPath string) (*beegoAppConfig, err
} }
func (b *beegoAppConfig) Set(key, val string) error { func (b *beegoAppConfig) Set(key, val string) error {
err := b.innerConfig.Set(RunMode+"::"+key, val)
if err == nil {
return err
}
return b.innerConfig.Set(key, val) return b.innerConfig.Set(key, val)
} }
@ -110,7 +116,7 @@ func (b *beegoAppConfig) String(key string) string {
func (b *beegoAppConfig) Strings(key string) []string { func (b *beegoAppConfig) Strings(key string) []string {
v := b.innerConfig.Strings(RunMode + "::" + key) v := b.innerConfig.Strings(RunMode + "::" + key)
if len(v) == 0 { if v[0] == "" {
return b.innerConfig.Strings(key) return b.innerConfig.Strings(key)
} }
return v return v
@ -149,27 +155,51 @@ func (b *beegoAppConfig) Float(key string) (float64, error) {
} }
func (b *beegoAppConfig) DefaultString(key string, defaultval string) string { func (b *beegoAppConfig) DefaultString(key string, defaultval string) string {
return b.innerConfig.DefaultString(key, defaultval) v := b.String(key)
if v != "" {
return v
}
return defaultval
} }
func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string { func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string {
return b.innerConfig.DefaultStrings(key, defaultval) v := b.Strings(key)
if len(v) != 0 {
return v
}
return defaultval
} }
func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int {
return b.innerConfig.DefaultInt(key, defaultval) v, err := b.Int(key)
if err == nil {
return v
}
return defaultval
} }
func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 {
return b.innerConfig.DefaultInt64(key, defaultval) v, err := b.Int64(key)
if err == nil {
return v
}
return defaultval
} }
func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool {
return b.innerConfig.DefaultBool(key, defaultval) v, err := b.Bool(key)
if err == nil {
return v
}
return defaultval
} }
func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 {
return b.innerConfig.DefaultFloat(key, defaultval) v, err := b.Float(key)
if err == nil {
return v
}
return defaultval
} }
func (b *beegoAppConfig) DIY(key string) (interface{}, error) { func (b *beegoAppConfig) DIY(key string) (interface{}, error) {
@ -480,5 +510,8 @@ func ParseConfig() (err error) {
if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil { if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil {
RouterCaseSensitive = casesensitive RouterCaseSensitive = casesensitive
} }
if graceful, err := AppConfig.Bool("Graceful"); err == nil {
Graceful = graceful
}
return nil return nil
} }

View File

@ -300,21 +300,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
defer f.Close() defer f.Close()
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
for section, dt := range c.data { // Save default section at first place
// Write section comments. if dt, ok := c.data[DEFAULT_SECTION]; ok {
if v, ok := c.sectionComment[section]; ok {
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
return err
}
}
if section != DEFAULT_SECTION {
// Write section name.
if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil {
return err
}
}
for key, val := range dt { for key, val := range dt {
if key != " " { if key != " " {
// Write key comments. // Write key comments.
@ -336,6 +323,43 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
return err return err
} }
} }
// Save named sections
for section, dt := range c.data {
if section != DEFAULT_SECTION {
// Write section comments.
if v, ok := c.sectionComment[section]; ok {
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
return err
}
}
// Write section name.
if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil {
return err
}
for key, val := range dt {
if key != " " {
// Write key comments.
if v, ok := c.keyComment[key]; ok {
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
return err
}
}
// Write key and value.
if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil {
return err
}
}
}
// Put a line between sections.
if _, err = buf.WriteString(lineBreak); err != nil {
return err
}
}
}
if _, err = buf.WriteTo(f); err != nil { if _, err = buf.WriteTo(f); err != nil {
return err return err

View File

@ -17,13 +17,10 @@ package config
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"strings" "strings"
"sync" "sync"
"time"
) )
// JsonConfig is a json config parser and implements Config interface. // JsonConfig is a json config parser and implements Config interface.
@ -41,13 +38,19 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return js.ParseData(content)
}
// ParseData returns a ConfigContainer with json string
func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) {
x := &JsonConfigContainer{ x := &JsonConfigContainer{
data: make(map[string]interface{}), data: make(map[string]interface{}),
} }
err = json.Unmarshal(content, &x.data) err := json.Unmarshal(data, &x.data)
if err != nil { if err != nil {
var wrappingArray []interface{} var wrappingArray []interface{}
err2 := json.Unmarshal(content, &wrappingArray) err2 := json.Unmarshal(data, &wrappingArray)
if err2 != nil { if err2 != nil {
return nil, err return nil, err
} }
@ -56,16 +59,6 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
return x, nil return x, nil
} }
func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) {
// Save memory data to temporary file
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
return nil, err
}
return js.Parse(tmpName)
}
// A Config represents the json configuration. // A Config represents the json configuration.
// Only when get value, support key as section:name type. // Only when get value, support key as section:name type.
type JsonConfigContainer struct { type JsonConfigContainer struct {
@ -88,11 +81,10 @@ func (c *JsonConfigContainer) Bool(key string) (bool, error) {
// DefaultBool return the bool value if has no error // DefaultBool return the bool value if has no error
// otherwise return the defaultval // otherwise return the defaultval
func (c *JsonConfigContainer) DefaultBool(key string, defaultval bool) bool { func (c *JsonConfigContainer) DefaultBool(key string, defaultval bool) bool {
if v, err := c.Bool(key); err != nil { if v, err := c.Bool(key); err == nil {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// Int returns the integer value for a given key. // Int returns the integer value for a given key.
@ -110,11 +102,10 @@ func (c *JsonConfigContainer) Int(key string) (int, error) {
// DefaultInt returns the integer value for a given key. // DefaultInt returns the integer value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultInt(key string, defaultval int) int { func (c *JsonConfigContainer) DefaultInt(key string, defaultval int) int {
if v, err := c.Int(key); err != nil { if v, err := c.Int(key); err == nil {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// Int64 returns the int64 value for a given key. // Int64 returns the int64 value for a given key.
@ -132,11 +123,10 @@ func (c *JsonConfigContainer) Int64(key string) (int64, error) {
// DefaultInt64 returns the int64 value for a given key. // DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultInt64(key string, defaultval int64) int64 { func (c *JsonConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
if v, err := c.Int64(key); err != nil { if v, err := c.Int64(key); err == nil {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// Float returns the float value for a given key. // Float returns the float value for a given key.
@ -154,11 +144,10 @@ func (c *JsonConfigContainer) Float(key string) (float64, error) {
// DefaultFloat returns the float64 value for a given key. // DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultFloat(key string, defaultval float64) float64 { func (c *JsonConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
if v, err := c.Float(key); err != nil { if v, err := c.Float(key); err == nil {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// String returns the string value for a given key. // String returns the string value for a given key.
@ -175,35 +164,37 @@ func (c *JsonConfigContainer) String(key string) string {
// DefaultString returns the string value for a given key. // DefaultString returns the string value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultString(key string, defaultval string) string { func (c *JsonConfigContainer) DefaultString(key string, defaultval string) string {
if v := c.String(key); v == "" { // TODO FIXME should not use "" to replace non existance
return defaultval if v := c.String(key); v != "" {
} else {
return v return v
} }
return defaultval
} }
// Strings returns the []string value for a given key. // Strings returns the []string value for a given key.
func (c *JsonConfigContainer) Strings(key string) []string { func (c *JsonConfigContainer) Strings(key string) []string {
stringVal := c.String(key)
if stringVal == "" {
return []string{}
}
return strings.Split(c.String(key), ";") return strings.Split(c.String(key), ";")
} }
// DefaultStrings returns the []string value for a given key. // DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultStrings(key string, defaultval []string) []string { func (c *JsonConfigContainer) DefaultStrings(key string, defaultval []string) []string {
if v := c.Strings(key); len(v) == 0 { if v := c.Strings(key); len(v) > 0 {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// GetSection returns map for the given section // GetSection returns map for the given section
func (c *JsonConfigContainer) GetSection(section string) (map[string]string, error) { func (c *JsonConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok { if v, ok := c.data[section]; ok {
return v.(map[string]string), nil return v.(map[string]string), nil
} else {
return nil, errors.New("not exist setction")
} }
return nil, errors.New("nonexist section " + section)
} }
// SaveConfigFile save the config into file // SaveConfigFile save the config into file
@ -222,7 +213,7 @@ func (c *JsonConfigContainer) SaveConfigFile(filename string) (err error) {
return err return err
} }
// WriteValue writes a new value for key. // Set writes a new value for key.
func (c *JsonConfigContainer) Set(key, val string) error { func (c *JsonConfigContainer) Set(key, val string) error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
@ -241,18 +232,20 @@ func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) {
// section.key or key // section.key or key
func (c *JsonConfigContainer) getData(key string) interface{} { func (c *JsonConfigContainer) getData(key string) interface{} {
c.RLock()
defer c.RUnlock()
if len(key) == 0 { if len(key) == 0 {
return nil return nil
} }
sectionKey := strings.Split(key, "::")
if len(sectionKey) >= 2 { c.RLock()
curValue, ok := c.data[sectionKey[0]] defer c.RUnlock()
sectionKeys := strings.Split(key, "::")
if len(sectionKeys) >= 2 {
curValue, ok := c.data[sectionKeys[0]]
if !ok { if !ok {
return nil return nil
} }
for _, key := range sectionKey[1:] { for _, key := range sectionKeys[1:] {
if v, ok := curValue.(map[string]interface{}); ok { if v, ok := curValue.(map[string]interface{}); ok {
if curValue, ok = v[key]; !ok { if curValue, ok = v[key]; !ok {
return nil return nil

View File

@ -21,6 +21,7 @@ import (
var jsoncontext = `{ var jsoncontext = `{
"appname": "beeapi", "appname": "beeapi",
"testnames": "foo;bar",
"httpport": 8080, "httpport": 8080,
"mysqlport": 3600, "mysqlport": 3600,
"PI": 3.1415976, "PI": 3.1415976,
@ -28,8 +29,8 @@ var jsoncontext = `{
"autorender": false, "autorender": false,
"copyrequestbody": true, "copyrequestbody": true,
"database": { "database": {
"host": "host", "host": "host",
"port": "port", "port": "port",
"database": "database", "database": "database",
"username": "username", "username": "username",
"password": "password", "password": "password",
@ -122,6 +123,12 @@ func TestJson(t *testing.T) {
if jsonconf.String("runmode") != "dev" { if jsonconf.String("runmode") != "dev" {
t.Fatal("runmode not equal to dev") t.Fatal("runmode not equal to dev")
} }
if v := jsonconf.Strings("unknown"); len(v) > 0 {
t.Fatal("unknown strings, the length should be 0")
}
if v := jsonconf.Strings("testnames"); len(v) != 2 {
t.Fatal("testnames length should be 2")
}
if v, err := jsonconf.Bool("autorender"); err != nil || v != false { if v, err := jsonconf.Bool("autorender"); err != nil || v != false {
t.Error(v) t.Error(v)
t.Fatal(err) t.Fatal(err)
@ -179,4 +186,8 @@ func TestJson(t *testing.T) {
if _, err := jsonconf.Bool("unknown"); err == nil { if _, err := jsonconf.Bool("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting a Bool") t.Error("unknown keys should return an error when expecting a Bool")
} }
if !jsonconf.DefaultBool("unknow", true) {
t.Error("unknown keys with default value wrong")
}
} }

View File

@ -31,7 +31,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
@ -53,24 +52,10 @@ func (ctx *Context) Redirect(status int, localurl string) {
} }
// Abort stops this request. // Abort stops this request.
// if middleware.ErrorMaps exists, panic body. // if beego.ErrorMaps exists, panic body.
// if middleware.HTTPExceptionMaps exists, panic HTTPException struct with status and body string.
func (ctx *Context) Abort(status int, body string) { func (ctx *Context) Abort(status int, body string) {
ctx.ResponseWriter.WriteHeader(status) ctx.ResponseWriter.WriteHeader(status)
// first panic from ErrorMaps, is is user defined error functions. panic(body)
if _, ok := middleware.ErrorMaps[body]; ok {
panic(body)
}
// second panic from HTTPExceptionMaps, it is system defined functions.
if e, ok := middleware.HTTPExceptionMaps[status]; ok {
if len(body) >= 1 {
e.Description = body
}
panic(e)
}
// last panic user string
ctx.ResponseWriter.Write([]byte(body))
panic("User stop run")
} }
// Write string to response body. // Write string to response body.
@ -155,8 +140,11 @@ func (ctx *Context) CheckXsrfCookie() bool {
} }
if token == "" { if token == "" {
ctx.Abort(403, "'_xsrf' argument missing from POST") ctx.Abort(403, "'_xsrf' argument missing from POST")
} else if ctx._xsrf_token != token { return false
}
if ctx._xsrf_token != token {
ctx.Abort(403, "XSRF cookie does not match POST argument") ctx.Abort(403, "XSRF cookie does not match POST argument")
return false
} }
return true return true
} }

View File

@ -21,13 +21,22 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
) )
// BeegoInput operates the http request header ,data ,cookie and body. // Regexes for checking the accept headers
// TODO make sure these are correct
var (
acceptsHtmlRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`)
acceptsXmlRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`)
acceptsJsonRegex = regexp.MustCompile(`(application/json)(?:,|$)`)
)
// BeegoInput operates the http request header, data, cookie and body.
// it also contains router params and current session. // it also contains router params and current session.
type BeegoInput struct { type BeegoInput struct {
CruSession session.SessionStore CruSession session.SessionStore
@ -72,11 +81,11 @@ func (input *BeegoInput) Site() string {
func (input *BeegoInput) Scheme() string { func (input *BeegoInput) Scheme() string {
if input.Request.URL.Scheme != "" { if input.Request.URL.Scheme != "" {
return input.Request.URL.Scheme return input.Request.URL.Scheme
} else if input.Request.TLS == nil {
return "http"
} else {
return "https"
} }
if input.Request.TLS == nil {
return "http"
}
return "https"
} }
// Domain returns host name. // Domain returns host name.
@ -153,16 +162,31 @@ func (input *BeegoInput) IsSecure() bool {
return input.Scheme() == "https" return input.Scheme() == "https"
} }
// IsSecure returns boolean of this request is in webSocket. // IsWebsocket returns boolean of this request is in webSocket.
func (input *BeegoInput) IsWebsocket() bool { func (input *BeegoInput) IsWebsocket() bool {
return input.Header("Upgrade") == "websocket" return input.Header("Upgrade") == "websocket"
} }
// IsSecure returns boolean of whether file uploads in this request or not.. // IsUpload returns boolean of whether file uploads in this request or not..
func (input *BeegoInput) IsUpload() bool { func (input *BeegoInput) IsUpload() bool {
return strings.Contains(input.Header("Content-Type"), "multipart/form-data") return strings.Contains(input.Header("Content-Type"), "multipart/form-data")
} }
// Checks if request accepts html response
func (input *BeegoInput) AcceptsHtml() bool {
return acceptsHtmlRegex.MatchString(input.Header("Accept"))
}
// Checks if request accepts xml response
func (input *BeegoInput) AcceptsXml() bool {
return acceptsXmlRegex.MatchString(input.Header("Accept"))
}
// Checks if request accepts json response
func (input *BeegoInput) AcceptsJson() bool {
return acceptsJsonRegex.MatchString(input.Header("Accept"))
}
// IP returns request client ip. // IP returns request client ip.
// if in proxy, return first proxy id. // if in proxy, return first proxy id.
// if error, return 127.0.0.1. // if error, return 127.0.0.1.
@ -189,16 +213,24 @@ func (input *BeegoInput) Proxy() []string {
return []string{} return []string{}
} }
// Referer returns http referer header.
func (input *BeegoInput) Referer() string {
return input.Header("Referer")
}
// Refer returns http referer header. // Refer returns http referer header.
func (input *BeegoInput) Refer() string { func (input *BeegoInput) Refer() string {
return input.Header("Referer") return input.Referer()
} }
// SubDomains returns sub domain string. // SubDomains returns sub domain string.
// if aa.bb.domain.com, returns aa.bb . // if aa.bb.domain.com, returns aa.bb .
func (input *BeegoInput) SubDomains() string { func (input *BeegoInput) SubDomains() string {
parts := strings.Split(input.Host(), ".") parts := strings.Split(input.Host(), ".")
return strings.Join(parts[len(parts)-2:], ".") if len(parts) >= 3 {
return strings.Join(parts[:len(parts)-2], ".")
}
return ""
} }
// Port returns request client port. // Port returns request client port.
@ -237,6 +269,7 @@ func (input *BeegoInput) Query(key string) string {
} }
// Header returns request header item string by a given string. // Header returns request header item string by a given string.
// if non-existed, return empty string.
func (input *BeegoInput) Header(key string) string { func (input *BeegoInput) Header(key string) string {
return input.Request.Header.Get(key) return input.Request.Header.Get(key)
} }
@ -252,11 +285,12 @@ func (input *BeegoInput) Cookie(key string) string {
} }
// Session returns current session item value by a given key. // Session returns current session item value by a given key.
// if non-existed, return empty string.
func (input *BeegoInput) Session(key interface{}) interface{} { func (input *BeegoInput) Session(key interface{}) interface{} {
return input.CruSession.Get(key) return input.CruSession.Get(key)
} }
// Body returns the raw request body data as bytes. // CopyBody returns the raw request body data as bytes.
func (input *BeegoInput) CopyBody() []byte { func (input *BeegoInput) CopyBody() []byte {
requestbody, _ := ioutil.ReadAll(input.Request.Body) requestbody, _ := ioutil.ReadAll(input.Request.Body)
input.Request.Body.Close() input.Request.Body.Close()
@ -296,7 +330,7 @@ func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error {
// Bind data from request.Form[key] to dest // Bind data from request.Form[key] to dest
// like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie // like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie
// var id int beegoInput.Bind(&id, "id") id ==123 // var id int beegoInput.Bind(&id, "id") id ==123
// var isok bool beegoInput.Bind(&isok, "isok") id ==true // var isok bool beegoInput.Bind(&isok, "isok") isok ==true
// var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2 // var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2
// ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2] // ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2]
// ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array] // ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array]

View File

@ -70,3 +70,45 @@ func TestParse(t *testing.T) {
} }
fmt.Println(user) fmt.Println(user)
} }
func TestSubDomain(t *testing.T) {
r, _ := http.NewRequest("GET", "http://www.example.com/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
beegoInput := NewInput(r)
subdomain := beegoInput.SubDomains()
if subdomain != "www" {
t.Fatal("Subdomain parse error, got" + subdomain)
}
r, _ = http.NewRequest("GET", "http://localhost/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, should be empty, got " + beegoInput.SubDomains())
}
r, _ = http.NewRequest("GET", "http://aa.bb.example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "aa.bb" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
/* TODO Fix this
r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
*/
r, _ = http.NewRequest("GET", "http://example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
r, _ = http.NewRequest("GET", "http://aa.bb.cc.dd.example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "aa.bb.cc.dd" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
}

View File

@ -29,6 +29,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"time"
) )
// BeegoOutput does work for sending response header. // BeegoOutput does work for sending response header.
@ -98,28 +99,30 @@ func (output *BeegoOutput) Body(content []byte) {
func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) { func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) {
var b bytes.Buffer var b bytes.Buffer
fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value)) fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value))
if len(others) > 0 {
switch v := others[0].(type) { //fix cookie not work in IE
case int: if len(others) > 0 {
if v > 0 { switch v := others[0].(type) {
fmt.Fprintf(&b, "; Max-Age=%d", v) case int:
} else if v < 0 { if v > 0 {
fmt.Fprintf(&b, "; Max-Age=0") fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v)
} } else if v < 0 {
case int64: fmt.Fprintf(&b, "; Max-Age=0")
if v > 0 { }
fmt.Fprintf(&b, "; Max-Age=%d", v) case int64:
} else if v < 0 { if v > 0 {
fmt.Fprintf(&b, "; Max-Age=0") fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v)
} } else if v < 0 {
case int32: fmt.Fprintf(&b, "; Max-Age=0")
if v > 0 { }
fmt.Fprintf(&b, "; Max-Age=%d", v) case int32:
} else if v < 0 { if v > 0 {
fmt.Fprintf(&b, "; Max-Age=0") fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v)
} } else if v < 0 {
} fmt.Fprintf(&b, "; Max-Age=0")
} }
}
}
// the settings below // the settings below
// Path, Domain, Secure, HttpOnly // Path, Domain, Secure, HttpOnly
@ -188,7 +191,7 @@ func sanitizeValue(v string) string {
// Json writes json to response body. // Json writes json to response body.
// if coding is true, it converts utf-8 to \u0000 type. // if coding is true, it converts utf-8 to \u0000 type.
func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error { func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error {
output.Header("Content-Type", "application/json;charset=UTF-8") output.Header("Content-Type", "application/json; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {
@ -209,7 +212,7 @@ func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) e
// Jsonp writes jsonp to response body. // Jsonp writes jsonp to response body.
func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error { func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/javascript;charset=UTF-8") output.Header("Content-Type", "application/javascript; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {
@ -235,7 +238,7 @@ func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
// Xml writes xml string to response body. // Xml writes xml string to response body.
func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error { func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/xml;charset=UTF-8") output.Header("Content-Type", "application/xml; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {

View File

@ -270,16 +270,22 @@ func (c *Controller) Redirect(url string, code int) {
// Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string. // Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string.
func (c *Controller) Abort(code string) { func (c *Controller) Abort(code string) {
status, err := strconv.Atoi(code) status, err := strconv.Atoi(code)
if err == nil { if err != nil {
c.Ctx.Abort(status, code) status = 200
} else {
c.Ctx.Abort(200, code)
} }
c.CustomAbort(status, code)
} }
// CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body. // CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body.
func (c *Controller) CustomAbort(status int, body string) { func (c *Controller) CustomAbort(status int, body string) {
c.Ctx.Abort(status, body) c.Ctx.ResponseWriter.WriteHeader(status)
// first panic from ErrorMaps, is is user defined error functions.
if _, ok := ErrorMaps[body]; ok {
panic(body)
}
// last panic user string
c.Ctx.ResponseWriter.Write([]byte(body))
panic(USERSTOPRUN)
} }
// StopRun makes panic of USERSTOPRUN error and go to recover function if defined. // StopRun makes panic of USERSTOPRUN error and go to recover function if defined.
@ -289,7 +295,7 @@ func (c *Controller) StopRun() {
// UrlFor does another controller handler in this request function. // UrlFor does another controller handler in this request function.
// it goes to this controller method if endpoint is not clear. // it goes to this controller method if endpoint is not clear.
func (c *Controller) UrlFor(endpoint string, values ...string) string { func (c *Controller) UrlFor(endpoint string, values ...interface{}) string {
if len(endpoint) <= 0 { if len(endpoint) <= 0 {
return "" return ""
} }
@ -363,67 +369,128 @@ func (c *Controller) ParseForm(obj interface{}) error {
return ParseForm(c.Input(), obj) return ParseForm(c.Input(), obj)
} }
// GetString returns the input value by key string. // GetString returns the input value by key string or the default value while it's present and input is blank
func (c *Controller) GetString(key string) string { func (c *Controller) GetString(key string, def ...string) string {
return c.Ctx.Input.Query(key) var defv string
if len(def) > 0 {
defv = def[0]
}
if v := c.Ctx.Input.Query(key); v != "" {
return v
} else {
return defv
}
} }
// GetStrings returns the input string slice by key string. // GetStrings returns the input string slice by key string or the default value while it's present and input is blank
// it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection. // it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection.
func (c *Controller) GetStrings(key string) []string { func (c *Controller) GetStrings(key string, def ...[]string) []string {
var defv []string
if len(def) > 0 {
defv = def[0]
}
f := c.Input() f := c.Input()
if f == nil { if f == nil {
return []string{} return defv
} }
vs := f[key] vs := f[key]
if len(vs) > 0 { if len(vs) > 0 {
return vs return vs
} else {
return defv
} }
return []string{}
} }
// GetInt returns input as an int // GetInt returns input as an int or the default value while it's present and input is blank
func (c *Controller) GetInt(key string) (int, error) { func (c *Controller) GetInt(key string, def ...int) (int, error) {
return strconv.Atoi(c.Ctx.Input.Query(key)) if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.Atoi(strv)
} else if len(def) > 0 {
return def[0], nil
} else {
return strconv.Atoi(strv)
}
} }
// GetInt8 return input as an int8 // GetInt8 return input as an int8 or the default value while it's present and input is blank
func (c *Controller) GetInt8(key string) (int8, error) { func (c *Controller) GetInt8(key string, def ...int8) (int8, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 8) if strv := c.Ctx.Input.Query(key); strv != "" {
i8 := int8(i64) i64, err := strconv.ParseInt(strv, 10, 8)
i8 := int8(i64)
return i8, err return i8, err
} else if len(def) > 0 {
return def[0], nil
} else {
i64, err := strconv.ParseInt(strv, 10, 8)
i8 := int8(i64)
return i8, err
}
} }
// GetInt16 returns input as an int16 // GetInt16 returns input as an int16 or the default value while it's present and input is blank
func (c *Controller) GetInt16(key string) (int16, error) { func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 16) if strv := c.Ctx.Input.Query(key); strv != "" {
i16 := int16(i64) i64, err := strconv.ParseInt(strv, 10, 16)
i16 := int16(i64)
return i16, err return i16, err
} else if len(def) > 0 {
return def[0], nil
} else {
i64, err := strconv.ParseInt(strv, 10, 16)
i16 := int16(i64)
return i16, err
}
} }
// GetInt32 returns input as an int32 // GetInt32 returns input as an int32 or the default value while it's present and input is blank
func (c *Controller) GetInt32(key string) (int32, error) { func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32) if strv := c.Ctx.Input.Query(key); strv != "" {
i32 := int32(i64) i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32)
i32 := int32(i64)
return i32, err return i32, err
} else if len(def) > 0 {
return def[0], nil
} else {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32)
i32 := int32(i64)
return i32, err
}
} }
// GetInt64 returns input value as int64. // GetInt64 returns input value as int64 or the default value while it's present and input is blank.
func (c *Controller) GetInt64(key string) (int64, error) { func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64) if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseInt(strv, 10, 64)
} else if len(def) > 0 {
return def[0], nil
} else {
return strconv.ParseInt(strv, 10, 64)
}
} }
// GetBool returns input value as bool. // GetBool returns input value as bool or the default value while it's present and input is blank.
func (c *Controller) GetBool(key string) (bool, error) { func (c *Controller) GetBool(key string, def ...bool) (bool, error) {
return strconv.ParseBool(c.Ctx.Input.Query(key)) if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseBool(strv)
} else if len(def) > 0 {
return def[0], nil
} else {
return strconv.ParseBool(strv)
}
} }
// GetFloat returns input value as float64. // GetFloat returns input value as float64 or the default value while it's present and input is blank.
func (c *Controller) GetFloat(key string) (float64, error) { func (c *Controller) GetFloat(key string, def ...float64) (float64, error) {
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64) if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseFloat(strv, 64)
} else if len(def) > 0 {
return def[0], nil
} else {
return strconv.ParseFloat(strv, 64)
}
} }
// GetFile returns the file data in file upload field named as key. // GetFile returns the file data in file upload field named as key.
@ -432,6 +499,41 @@ func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader,
return c.Ctx.Request.FormFile(key) return c.Ctx.Request.FormFile(key)
} }
// GetFiles return multi-upload files
// files, err:=c.Getfiles("myfiles")
// if err != nil {
// http.Error(w, err.Error(), http.StatusNoContent)
// return
// }
// for i, _ := range files {
// //for each fileheader, get a handle to the actual file
// file, err := files[i].Open()
// defer file.Close()
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// //create destination file making sure the path is writeable.
// dst, err := os.Create("upload/" + files[i].Filename)
// defer dst.Close()
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// //copy the uploaded file to the destination file
// if _, err := io.Copy(dst, file); err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// }
func (c *Controller) GetFiles(key string) ([]*multipart.FileHeader, error) {
files, ok := c.Ctx.Request.MultipartForm.File[key]
if ok {
return files, nil
}
return nil, http.ErrMissingFile
}
// SaveToFile saves uploaded file to new path. // SaveToFile saves uploaded file to new path.
// it only operates the first one of mutil-upload form file field. // it only operates the first one of mutil-upload form file field.
func (c *Controller) SaveToFile(fromfile, tofile string) error { func (c *Controller) SaveToFile(fromfile, tofile string) error {

View File

@ -12,20 +12,26 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package middleware package beego
import ( import (
"fmt" "fmt"
"html/template" "html/template"
"net/http" "net/http"
"reflect"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/utils"
) )
var ( const (
AppName string errorTypeHandler = iota
VERSION string errorTypeController
) )
var tpl = ` var tpl = `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -76,18 +82,18 @@ var tpl = `
` `
// render default application error page with error and stack string. // render default application error page with error and stack string.
func ShowErr(err interface{}, rw http.ResponseWriter, r *http.Request, Stack string) { func showErr(err interface{}, ctx *context.Context, Stack string) {
t, _ := template.New("beegoerrortemp").Parse(tpl) t, _ := template.New("beegoerrortemp").Parse(tpl)
data := make(map[string]string) data := make(map[string]string)
data["AppError"] = AppName + ":" + fmt.Sprint(err) data["AppError"] = AppName + ":" + fmt.Sprint(err)
data["RequestMethod"] = r.Method data["RequestMethod"] = ctx.Input.Method()
data["RequestURL"] = r.RequestURI data["RequestURL"] = ctx.Input.Uri()
data["RemoteAddr"] = r.RemoteAddr data["RemoteAddr"] = ctx.Input.IP()
data["Stack"] = Stack data["Stack"] = Stack
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
data["GoVersion"] = runtime.Version() data["GoVersion"] = runtime.Version()
rw.WriteHeader(500) ctx.ResponseWriter.WriteHeader(500)
t.Execute(rw, data) t.Execute(ctx.ResponseWriter, data)
} }
var errtpl = ` var errtpl = `
@ -190,15 +196,68 @@ var errtpl = `
</html> </html>
` `
type errorInfo struct {
controllerType reflect.Type
handler http.HandlerFunc
method string
errorType int
}
// map of http handlers for each error string. // map of http handlers for each error string.
var ErrorMaps map[string]http.HandlerFunc var ErrorMaps map[string]*errorInfo
func init() { func init() {
ErrorMaps = make(map[string]http.HandlerFunc) ErrorMaps = make(map[string]*errorInfo)
}
// show 401 unauthorized error.
func unauthorized(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Unauthorized"
data["Content"] = template.HTML("<br>The page you have requested can't be authorized." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 402 Payment Required
func paymentRequired(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Payment Required"
data["Content"] = template.HTML("<br>The page you have requested Payment Required." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 403 forbidden error.
func forbidden(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Forbidden"
data["Content"] = template.HTML("<br>The page you have requested is forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Your address may be blocked" +
"<br>The site may be disabled" +
"<br>You need to log in" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
} }
// show 404 notfound error. // show 404 notfound error.
func NotFound(rw http.ResponseWriter, r *http.Request) { func notFound(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Page Not Found" data["Title"] = "Page Not Found"
@ -211,45 +270,66 @@ func NotFound(rw http.ResponseWriter, r *http.Request) {
"<br>You like 404 pages" + "<br>You like 404 pages" +
"</ul>") "</ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusNotFound)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 401 unauthorized error. // show 405 Method Not Allowed
func Unauthorized(rw http.ResponseWriter, r *http.Request) { func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Unauthorized" data["Title"] = "Method Not Allowed"
data["Content"] = template.HTML("<br>The page you have requested can't be authorized." + data["Content"] = template.HTML("<br>The method you have requested Not Allowed." +
"<br>Perhaps you are here because:" + "<br>Perhaps you are here because:" +
"<br><br><ul>" + "<br><br><ul>" +
"<br>The credentials you supplied are incorrect" + "<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI" +
"<br>There are errors in the website address" + "<br>The response MUST include an Allow header containing a list of valid methods for the requested resource." +
"</ul>") "</ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusUnauthorized)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 403 forbidden error. // show 500 internal server error.
func Forbidden(rw http.ResponseWriter, r *http.Request) { func internalServerError(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Forbidden" data["Title"] = "Internal Server Error"
data["Content"] = template.HTML("<br>The page you have requested is forbidden." + data["Content"] = template.HTML("<br>The page you have requested is down right now." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" + "<br><br><ul>" +
"<br>Your address may be blocked" + "<br>Please try again later and report the error to the website administrator" +
"<br>The site may be disabled" + "<br></ul>")
"<br>You need to log in" + data["BeegoVersion"] = VERSION
"</ul>") t.Execute(rw, data)
}
// show 501 Not Implemented.
func notImplemented(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Not Implemented"
data["Content"] = template.HTML("<br>The page you have requested is Not Implemented." +
"<br><br><ul>" +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 502 Bad Gateway.
func badGateway(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Bad Gateway"
data["Content"] = template.HTML("<br>The page you have requested is down right now." +
"<br><br><ul>" +
"<br>The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request." +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusForbidden)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 503 service unavailable error. // show 503 service unavailable error.
func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) { func serviceUnavailable(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Service Unavailable" data["Title"] = "Service Unavailable"
@ -260,80 +340,151 @@ func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) {
"<br>Please try again later." + "<br>Please try again later." +
"</ul>") "</ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusServiceUnavailable)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 500 internal server error. // show 504 Gateway Timeout.
func InternalServerError(rw http.ResponseWriter, r *http.Request) { func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Internal Server Error" data["Title"] = "Gateway Timeout"
data["Content"] = template.HTML("<br>The page you have requested is down right now." + data["Content"] = template.HTML("<br>The page you have requested is unavailable." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" + "<br><br><ul>" +
"<br>Please try again later and report the error to the website administrator" + "<br><br>The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI." +
"<br></ul>") "<br>Please try again later." +
"</ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusInternalServerError)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 500 internal error with simple text string.
func SimpleServerError(rw http.ResponseWriter, r *http.Request) {
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
// add http handler for given error string.
func Errorhandler(err string, h http.HandlerFunc) {
ErrorMaps[err] = h
}
// register default error http handlers, 404,401,403,500 and 503. // register default error http handlers, 404,401,403,500 and 503.
func RegisterErrorHandler() { func registerDefaultErrorHandler() {
if _, ok := ErrorMaps["404"]; !ok { if _, ok := ErrorMaps["401"]; !ok {
ErrorMaps["404"] = NotFound Errorhandler("401", unauthorized)
} }
if _, ok := ErrorMaps["401"]; !ok { if _, ok := ErrorMaps["402"]; !ok {
ErrorMaps["401"] = Unauthorized Errorhandler("402", paymentRequired)
} }
if _, ok := ErrorMaps["403"]; !ok { if _, ok := ErrorMaps["403"]; !ok {
ErrorMaps["403"] = Forbidden Errorhandler("403", forbidden)
} }
if _, ok := ErrorMaps["503"]; !ok { if _, ok := ErrorMaps["404"]; !ok {
ErrorMaps["503"] = ServiceUnavailable Errorhandler("404", notFound)
}
if _, ok := ErrorMaps["405"]; !ok {
Errorhandler("405", methodNotAllowed)
} }
if _, ok := ErrorMaps["500"]; !ok { if _, ok := ErrorMaps["500"]; !ok {
ErrorMaps["500"] = InternalServerError Errorhandler("500", internalServerError)
} }
if _, ok := ErrorMaps["501"]; !ok {
Errorhandler("501", notImplemented)
}
if _, ok := ErrorMaps["502"]; !ok {
Errorhandler("502", badGateway)
}
if _, ok := ErrorMaps["503"]; !ok {
Errorhandler("503", serviceUnavailable)
}
if _, ok := ErrorMaps["504"]; !ok {
Errorhandler("504", gatewayTimeout)
}
}
// ErrorHandler registers http.HandlerFunc to each http err code string.
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
func Errorhandler(code string, h http.HandlerFunc) *App {
errinfo := &errorInfo{}
errinfo.errorType = errorTypeHandler
errinfo.handler = h
errinfo.method = code
ErrorMaps[code] = errinfo
return BeeApp
}
// ErrorController registers ControllerInterface to each http err code string.
// usage:
// beego.ErrorHandler(&controllers.ErrorController{})
func ErrorController(c ControllerInterface) *App {
reflectVal := reflect.ValueOf(c)
rt := reflectVal.Type()
ct := reflect.Indirect(reflectVal).Type()
for i := 0; i < rt.NumMethod(); i++ {
if !utils.InSlice(rt.Method(i).Name, exceptMethod) && strings.HasPrefix(rt.Method(i).Name, "Error") {
errinfo := &errorInfo{}
errinfo.errorType = errorTypeController
errinfo.controllerType = ct
errinfo.method = rt.Method(i).Name
errname := strings.TrimPrefix(rt.Method(i).Name, "Error")
ErrorMaps[errname] = errinfo
}
}
return BeeApp
} }
// show error string as simple text message. // show error string as simple text message.
// if error string is empty, show 500 error as default. // if error string is empty, show 500 error as default.
func Exception(errcode string, w http.ResponseWriter, r *http.Request, msg string) { func exception(errcode string, ctx *context.Context) {
code, err := strconv.Atoi(errcode)
if err != nil {
code = 503
}
if h, ok := ErrorMaps[errcode]; ok { if h, ok := ErrorMaps[errcode]; ok {
isint, err := strconv.Atoi(errcode) executeError(h, ctx, code)
if err != nil { return
isint = 500 } else if h, ok := ErrorMaps["503"]; ok {
} executeError(h, ctx, code)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(isint)
h(w, r)
return return
} else { } else {
isint, err := strconv.Atoi(errcode) ctx.ResponseWriter.WriteHeader(code)
if err != nil { ctx.WriteString(errcode)
isint = 500 }
} }
if isint == 400 {
msg = "404 page not found" func executeError(err *errorInfo, ctx *context.Context, code int) {
} if err.errorType == errorTypeHandler {
w.Header().Set("Content-Type", "text/plain; charset=utf-8") ctx.ResponseWriter.WriteHeader(code)
w.WriteHeader(isint) err.handler(ctx.ResponseWriter, ctx.Request)
fmt.Fprintln(w, msg) return
return }
if err.errorType == errorTypeController {
ctx.Output.SetStatus(code)
//Invoke the request handler
vc := reflect.New(err.controllerType)
execController, ok := vc.Interface().(ControllerInterface)
if !ok {
panic("controller is not ControllerInterface")
}
//call the controller init function
execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface())
//call prepare function
execController.Prepare()
execController.URLMapping()
in := make([]reflect.Value, 0)
method := vc.MethodByName(err.method)
method.Call(in)
//render template
if AutoRender {
if err := execController.Render(); err != nil {
panic(err)
}
}
// finish all runrouter. release resource
execController.Finish()
} }
} }

View File

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<title>Chat Example</title> <title>Chat Example</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> <script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {

View File

@ -16,11 +16,12 @@ package beego
import "github.com/astaxie/beego/context" import "github.com/astaxie/beego/context"
// FilterFunc defines filter function type. // FilterFunc defines a filter function which is invoked before the controller handler is executed.
type FilterFunc func(*context.Context) type FilterFunc func(*context.Context)
// FilterRouter defines filter operation before controller handler execution. // FilterRouter defines a filter operation which is invoked before the controller handler is executed.
// it can match patterned url and do filter function when action arrives. // It can match the URL against a pattern, and execute a filter function
// when a request with a matching URL arrives.
type FilterRouter struct { type FilterRouter struct {
filterFunc FilterFunc filterFunc FilterFunc
tree *Tree tree *Tree
@ -28,10 +29,11 @@ type FilterRouter struct {
returnOnOutput bool returnOnOutput bool
} }
// ValidRouter check current request is valid for this filter. // ValidRouter checks if the current request is matched by this filter.
// if matched, returns parsed params in this request by defined filter router pattern. // If the request is matched, the values of the URL parameters defined
func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) { // by the filter pattern are also returned.
isok, params := f.tree.Match(router) func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) {
isok, params := f.tree.Match(url)
if isok == nil { if isok == nil {
return false, nil return false, nil
} }

13
grace/conn.go Normal file
View File

@ -0,0 +1,13 @@
package grace
import "net"
type graceConn struct {
net.Conn
server *graceServer
}
func (c graceConn) Close() error {
c.server.wg.Done()
return c.Conn.Close()
}

150
grace/grace.go Normal file
View File

@ -0,0 +1,150 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Description: http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/
//
// Usage:
//
// import(
// "log"
// "net/http"
// "os"
//
// "github.com/astaxie/beego/grace"
// )
//
// func handler(w http.ResponseWriter, r *http.Request) {
// w.Write([]byte("WORLD!"))
// }
//
// func main() {
// mux := http.NewServeMux()
// mux.HandleFunc("/hello", handler)
//
// err := grace.ListenAndServe("localhost:8080", mux1)
// if err != nil {
// log.Println(err)
// }
// log.Println("Server on 8080 stopped")
// os.Exit(0)
// }
package grace
import (
"flag"
"net/http"
"os"
"strings"
"sync"
"syscall"
"time"
)
const (
PRE_SIGNAL = iota
POST_SIGNAL
STATE_INIT
STATE_RUNNING
STATE_SHUTTING_DOWN
STATE_TERMINATE
)
var (
regLock *sync.Mutex
runningServers map[string]*graceServer
runningServersOrder []string
socketPtrOffsetMap map[string]uint
runningServersForked bool
DefaultReadTimeOut time.Duration
DefaultWriteTimeOut time.Duration
DefaultMaxHeaderBytes int
DefaultTimeout time.Duration
isChild bool
socketOrder string
)
func init() {
regLock = &sync.Mutex{}
flag.BoolVar(&isChild, "graceful", false, "listen on open fd (after forking)")
flag.StringVar(&socketOrder, "socketorder", "", "previous initialization order - used when more than one listener was started")
runningServers = make(map[string]*graceServer)
runningServersOrder = []string{}
socketPtrOffsetMap = make(map[string]uint)
DefaultMaxHeaderBytes = 0
DefaultTimeout = 60 * time.Second
}
// NewServer returns a new graceServer.
func NewServer(addr string, handler http.Handler) (srv *graceServer) {
regLock.Lock()
defer regLock.Unlock()
if !flag.Parsed() {
flag.Parse()
}
if len(socketOrder) > 0 {
for i, addr := range strings.Split(socketOrder, ",") {
socketPtrOffsetMap[addr] = uint(i)
}
} else {
socketPtrOffsetMap[addr] = uint(len(runningServersOrder))
}
srv = &graceServer{
wg: sync.WaitGroup{},
sigChan: make(chan os.Signal),
isChild: isChild,
SignalHooks: map[int]map[os.Signal][]func(){
PRE_SIGNAL: map[os.Signal][]func(){
syscall.SIGHUP: []func(){},
syscall.SIGINT: []func(){},
syscall.SIGTERM: []func(){},
},
POST_SIGNAL: map[os.Signal][]func(){
syscall.SIGHUP: []func(){},
syscall.SIGINT: []func(){},
syscall.SIGTERM: []func(){},
},
},
state: STATE_INIT,
Network: "tcp",
}
srv.Server = &http.Server{}
srv.Server.Addr = addr
srv.Server.ReadTimeout = DefaultReadTimeOut
srv.Server.WriteTimeout = DefaultWriteTimeOut
srv.Server.MaxHeaderBytes = DefaultMaxHeaderBytes
srv.Server.Handler = handler
runningServersOrder = append(runningServersOrder, addr)
runningServers[addr] = srv
return
}
// refer http.ListenAndServe
func ListenAndServe(addr string, handler http.Handler) error {
server := NewServer(addr, handler)
return server.ListenAndServe()
}
// refer http.ListenAndServeTLS
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error {
server := NewServer(addr, handler)
return server.ListenAndServeTLS(certFile, keyFile)
}

62
grace/listener.go Normal file
View File

@ -0,0 +1,62 @@
package grace
import (
"net"
"os"
"syscall"
"time"
)
type graceListener struct {
net.Listener
stop chan error
stopped bool
server *graceServer
}
func newGraceListener(l net.Listener, srv *graceServer) (el *graceListener) {
el = &graceListener{
Listener: l,
stop: make(chan error),
server: srv,
}
go func() {
_ = <-el.stop
el.stopped = true
el.stop <- el.Listener.Close()
}()
return
}
func (gl *graceListener) Accept() (c net.Conn, err error) {
tc, err := gl.Listener.(*net.TCPListener).AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
c = graceConn{
Conn: tc,
server: gl.server,
}
gl.server.wg.Add(1)
return
}
func (el *graceListener) Close() error {
if el.stopped {
return syscall.EINVAL
}
el.stop <- nil
return <-el.stop
}
func (el *graceListener) File() *os.File {
// returns a dup(2) - FD_CLOEXEC flag *not* set
tl := el.Listener.(*net.TCPListener)
fl, _ := tl.File()
return fl
}

292
grace/server.go Normal file
View File

@ -0,0 +1,292 @@
package grace
import (
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
"os"
"os/exec"
"os/signal"
"strings"
"sync"
"syscall"
"time"
)
type graceServer struct {
*http.Server
GraceListener net.Listener
SignalHooks map[int]map[os.Signal][]func()
tlsInnerListener *graceListener
wg sync.WaitGroup
sigChan chan os.Signal
isChild bool
state uint8
Network string
}
// Serve accepts incoming connections on the Listener l,
// creating a new service goroutine for each.
// The service goroutines read requests and then call srv.Handler to reply to them.
func (srv *graceServer) Serve() (err error) {
srv.state = STATE_RUNNING
err = srv.Server.Serve(srv.GraceListener)
log.Println(syscall.Getpid(), "Waiting for connections to finish...")
srv.wg.Wait()
srv.state = STATE_TERMINATE
return
}
// ListenAndServe listens on the TCP network address srv.Addr and then calls Serve
// to handle requests on incoming connections. If srv.Addr is blank, ":http" is
// used.
func (srv *graceServer) ListenAndServe() (err error) {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
go srv.handleSignals()
l, err := srv.getListener(addr)
if err != nil {
log.Println(err)
return err
}
srv.GraceListener = newGraceListener(l, srv)
if srv.isChild {
process, err := os.FindProcess(os.Getppid())
if err != nil {
log.Println(err)
return err
}
err = process.Kill()
if err != nil {
return err
}
}
log.Println(os.Getpid(), srv.Addr)
return srv.Serve()
}
// ListenAndServeTLS listens on the TCP network address srv.Addr and then calls
// Serve to handle requests on incoming TLS connections.
//
// Filenames containing a certificate and matching private key for the server must
// be provided. If the certificate is signed by a certificate authority, the
// certFile should be the concatenation of the server's certificate followed by the
// CA's certificate.
//
// If srv.Addr is blank, ":https" is used.
func (srv *graceServer) ListenAndServeTLS(certFile, keyFile string) (err error) {
addr := srv.Addr
if addr == "" {
addr = ":https"
}
config := &tls.Config{}
if srv.TLSConfig != nil {
*config = *srv.TLSConfig
}
if config.NextProtos == nil {
config.NextProtos = []string{"http/1.1"}
}
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return
}
go srv.handleSignals()
l, err := srv.getListener(addr)
if err != nil {
log.Println(err)
return err
}
srv.tlsInnerListener = newGraceListener(l, srv)
srv.GraceListener = tls.NewListener(srv.tlsInnerListener, config)
if srv.isChild {
process, err := os.FindProcess(os.Getppid())
if err != nil {
log.Println(err)
return err
}
err = process.Kill()
if err != nil {
return err
}
}
log.Println(os.Getpid(), srv.Addr)
return srv.Serve()
}
// getListener either opens a new socket to listen on, or takes the acceptor socket
// it got passed when restarted.
func (srv *graceServer) getListener(laddr string) (l net.Listener, err error) {
if srv.isChild {
var ptrOffset uint = 0
if len(socketPtrOffsetMap) > 0 {
ptrOffset = socketPtrOffsetMap[laddr]
log.Println("laddr", laddr, "ptr offset", socketPtrOffsetMap[laddr])
}
f := os.NewFile(uintptr(3+ptrOffset), "")
l, err = net.FileListener(f)
if err != nil {
err = fmt.Errorf("net.FileListener error: %v", err)
return
}
} else {
l, err = net.Listen(srv.Network, laddr)
if err != nil {
err = fmt.Errorf("net.Listen error: %v", err)
return
}
}
return
}
// handleSignals listens for os Signals and calls any hooked in function that the
// user had registered with the signal.
func (srv *graceServer) handleSignals() {
var sig os.Signal
signal.Notify(
srv.sigChan,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
)
pid := syscall.Getpid()
for {
sig = <-srv.sigChan
srv.signalHooks(PRE_SIGNAL, sig)
switch sig {
case syscall.SIGHUP:
log.Println(pid, "Received SIGHUP. forking.")
err := srv.fork()
if err != nil {
log.Println("Fork err:", err)
}
case syscall.SIGINT:
log.Println(pid, "Received SIGINT.")
srv.shutdown()
case syscall.SIGTERM:
log.Println(pid, "Received SIGTERM.")
srv.shutdown()
default:
log.Printf("Received %v: nothing i care about...\n", sig)
}
srv.signalHooks(POST_SIGNAL, sig)
}
}
func (srv *graceServer) signalHooks(ppFlag int, sig os.Signal) {
if _, notSet := srv.SignalHooks[ppFlag][sig]; !notSet {
return
}
for _, f := range srv.SignalHooks[ppFlag][sig] {
f()
}
return
}
// shutdown closes the listener so that no new connections are accepted. it also
// starts a goroutine that will serverTimeout (stop all running requests) the server
// after DefaultTimeout.
func (srv *graceServer) shutdown() {
if srv.state != STATE_RUNNING {
return
}
srv.state = STATE_SHUTTING_DOWN
if DefaultTimeout >= 0 {
go srv.serverTimeout(DefaultTimeout)
}
err := srv.GraceListener.Close()
if err != nil {
log.Println(syscall.Getpid(), "Listener.Close() error:", err)
} else {
log.Println(syscall.Getpid(), srv.GraceListener.Addr(), "Listener closed.")
}
}
// serverTimeout forces the server to shutdown in a given timeout - whether it
// finished outstanding requests or not. if Read/WriteTimeout are not set or the
// max header size is very big a connection could hang
func (srv *graceServer) serverTimeout(d time.Duration) {
defer func() {
if r := recover(); r != nil {
log.Println("WaitGroup at 0", r)
}
}()
if srv.state != STATE_SHUTTING_DOWN {
return
}
time.Sleep(d)
log.Println("[STOP - Hammer Time] Forcefully shutting down parent")
for {
if srv.state == STATE_TERMINATE {
break
}
srv.wg.Done()
}
}
func (srv *graceServer) fork() (err error) {
regLock.Lock()
defer regLock.Unlock()
if runningServersForked {
return
}
runningServersForked = true
var files = make([]*os.File, len(runningServers))
var orderArgs = make([]string, len(runningServers))
for _, srvPtr := range runningServers {
switch srvPtr.GraceListener.(type) {
case *graceListener:
files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.GraceListener.(*graceListener).File()
default:
files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.tlsInnerListener.File()
}
orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr
}
log.Println(files)
path := os.Args[0]
var args []string
if len(os.Args) > 1 {
for _, arg := range os.Args[1:] {
if arg == "-graceful" {
break
}
args = append(args, arg)
}
}
args = append(args, "-graceful")
if len(runningServers) > 1 {
args = append(args, fmt.Sprintf(`-socketorder=%s`, strings.Join(orderArgs, ",")))
log.Println(args)
}
cmd := exec.Command(path, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.ExtraFiles = files
err = cmd.Start()
if err != nil {
log.Fatalf("Restart: Failed to launch, error: %v", err)
}
return
}

View File

@ -32,6 +32,7 @@ package httplib
import ( import (
"bytes" "bytes"
"compress/gzip"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
@ -50,7 +51,14 @@ import (
"time" "time"
) )
var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false} var defaultSetting = BeegoHttpSettings{
UserAgent: "beegoServer",
ConnectTimeout: 60 * time.Second,
ReadWriteTimeout: 60 * time.Second,
Gzip: true,
DumpBody: true,
}
var defaultCookieJar http.CookieJar var defaultCookieJar http.CookieJar
var settingMutex sync.Mutex var settingMutex sync.Mutex
@ -75,41 +83,53 @@ func SetDefaultSetting(setting BeegoHttpSettings) {
} }
// return *BeegoHttpRequest with specific method // return *BeegoHttpRequest with specific method
func newBeegoRequest(url, method string) *BeegoHttpRequest { func NewBeegoRequest(rawurl, method string) *BeegoHttpRequest {
var resp http.Response var resp http.Response
u, err := url.Parse(rawurl)
if err != nil {
log.Fatal(err)
}
req := http.Request{ req := http.Request{
URL: u,
Method: method, Method: method,
Header: make(http.Header), Header: make(http.Header),
Proto: "HTTP/1.1", Proto: "HTTP/1.1",
ProtoMajor: 1, ProtoMajor: 1,
ProtoMinor: 1, ProtoMinor: 1,
} }
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil} return &BeegoHttpRequest{
url: rawurl,
req: &req,
params: map[string]string{},
files: map[string]string{},
setting: defaultSetting,
resp: &resp,
}
} }
// Get returns *BeegoHttpRequest with GET method. // Get returns *BeegoHttpRequest with GET method.
func Get(url string) *BeegoHttpRequest { func Get(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "GET") return NewBeegoRequest(url, "GET")
} }
// Post returns *BeegoHttpRequest with POST method. // Post returns *BeegoHttpRequest with POST method.
func Post(url string) *BeegoHttpRequest { func Post(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "POST") return NewBeegoRequest(url, "POST")
} }
// Put returns *BeegoHttpRequest with PUT method. // Put returns *BeegoHttpRequest with PUT method.
func Put(url string) *BeegoHttpRequest { func Put(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "PUT") return NewBeegoRequest(url, "PUT")
} }
// Delete returns *BeegoHttpRequest DELETE method. // Delete returns *BeegoHttpRequest DELETE method.
func Delete(url string) *BeegoHttpRequest { func Delete(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "DELETE") return NewBeegoRequest(url, "DELETE")
} }
// Head returns *BeegoHttpRequest with HEAD method. // Head returns *BeegoHttpRequest with HEAD method.
func Head(url string) *BeegoHttpRequest { func Head(url string) *BeegoHttpRequest {
return newBeegoRequest(url, "HEAD") return NewBeegoRequest(url, "HEAD")
} }
// BeegoHttpSettings // BeegoHttpSettings
@ -122,6 +142,8 @@ type BeegoHttpSettings struct {
Proxy func(*http.Request) (*url.URL, error) Proxy func(*http.Request) (*url.URL, error)
Transport http.RoundTripper Transport http.RoundTripper
EnableCookie bool EnableCookie bool
Gzip bool
DumpBody bool
} }
// BeegoHttpRequest provides more useful methods for requesting one url than http.Request. // BeegoHttpRequest provides more useful methods for requesting one url than http.Request.
@ -133,6 +155,12 @@ type BeegoHttpRequest struct {
setting BeegoHttpSettings setting BeegoHttpSettings
resp *http.Response resp *http.Response
body []byte body []byte
dump []byte
}
// get request
func (b *BeegoHttpRequest) GetRequest() *http.Request {
return b.req
} }
// Change request settings // Change request settings
@ -165,6 +193,17 @@ func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest {
return b return b
} }
// Dump Body.
func (b *BeegoHttpRequest) DumpBody(isdump bool) *BeegoHttpRequest {
b.setting.DumpBody = isdump
return b
}
// return the DumpRequest
func (b *BeegoHttpRequest) DumpRequest() []byte {
return b.dump
}
// SetTimeout sets connect time out and read-write time out for BeegoRequest. // SetTimeout sets connect time out and read-write time out for BeegoRequest.
func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest { func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest {
b.setting.ConnectTimeout = connectTimeout b.setting.ConnectTimeout = connectTimeout
@ -184,6 +223,12 @@ func (b *BeegoHttpRequest) Header(key, value string) *BeegoHttpRequest {
return b return b
} }
// Set HOST
func (b *BeegoHttpRequest) SetHost(host string) *BeegoHttpRequest {
b.req.Host = host
return b
}
// Set the protocol version for incoming requests. // Set the protocol version for incoming requests.
// Client requests always use HTTP/1.1. // Client requests always use HTTP/1.1.
func (b *BeegoHttpRequest) SetProtocolVersion(vers string) *BeegoHttpRequest { func (b *BeegoHttpRequest) SetProtocolVersion(vers string) *BeegoHttpRequest {
@ -253,30 +298,35 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest {
return b return b
} }
func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { // JsonBody adds request raw body encoding by JSON.
if b.resp.StatusCode != 0 { func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) {
return b.resp, nil if b.req.Body == nil && obj != nil {
} buf := bytes.NewBuffer(nil)
var paramBody string enc := json.NewEncoder(buf)
if len(b.params) > 0 { if err := enc.Encode(obj); err != nil {
var buf bytes.Buffer return b, err
for k, v := range b.params {
buf.WriteString(url.QueryEscape(k))
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(v))
buf.WriteByte('&')
} }
paramBody = buf.String() b.req.Body = ioutil.NopCloser(buf)
paramBody = paramBody[0 : len(paramBody)-1] b.req.ContentLength = int64(buf.Len())
b.req.Header.Set("Content-Type", "application/json")
} }
return b, nil
}
func (b *BeegoHttpRequest) buildUrl(paramBody string) {
// build GET url with query string
if b.req.Method == "GET" && len(paramBody) > 0 { if b.req.Method == "GET" && len(paramBody) > 0 {
if strings.Index(b.url, "?") != -1 { if strings.Index(b.url, "?") != -1 {
b.url += "&" + paramBody b.url += "&" + paramBody
} else { } else {
b.url = b.url + "?" + paramBody b.url = b.url + "?" + paramBody
} }
} else if b.req.Method == "POST" && b.req.Body == nil { return
}
// build POST/PUT/PATCH url and body
if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && b.req.Body == nil {
// with files
if len(b.files) > 0 { if len(b.files) > 0 {
pr, pw := io.Pipe() pr, pw := io.Pipe()
bodyWriter := multipart.NewWriter(pw) bodyWriter := multipart.NewWriter(pw)
@ -305,12 +355,44 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
}() }()
b.Header("Content-Type", bodyWriter.FormDataContentType()) b.Header("Content-Type", bodyWriter.FormDataContentType())
b.req.Body = ioutil.NopCloser(pr) b.req.Body = ioutil.NopCloser(pr)
} else if len(paramBody) > 0 { return
}
// with params
if len(paramBody) > 0 {
b.Header("Content-Type", "application/x-www-form-urlencoded") b.Header("Content-Type", "application/x-www-form-urlencoded")
b.Body(paramBody) b.Body(paramBody)
} }
} }
}
func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
if b.resp.StatusCode != 0 {
return b.resp, nil
}
resp, err := b.SendOut()
if err != nil {
return nil, err
}
b.resp = resp
return resp, nil
}
func (b *BeegoHttpRequest) SendOut() (*http.Response, error) {
var paramBody string
if len(b.params) > 0 {
var buf bytes.Buffer
for k, v := range b.params {
buf.WriteString(url.QueryEscape(k))
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(v))
buf.WriteByte('&')
}
paramBody = buf.String()
paramBody = paramBody[0 : len(paramBody)-1]
}
b.buildUrl(paramBody)
url, err := url.Parse(b.url) url, err := url.Parse(b.url)
if err != nil { if err != nil {
return nil, err return nil, err
@ -342,14 +424,12 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
} }
} }
var jar http.CookieJar var jar http.CookieJar = nil
if b.setting.EnableCookie { if b.setting.EnableCookie {
if defaultCookieJar == nil { if defaultCookieJar == nil {
createDefaultCookie() createDefaultCookie()
} }
jar = defaultCookieJar jar = defaultCookieJar
} else {
jar = nil
} }
client := &http.Client{ client := &http.Client{
@ -362,19 +442,13 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
} }
if b.setting.ShowDebug { if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, true) dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
if err != nil { if err != nil {
println(err.Error()) log.Println(err.Error())
} }
println(string(dump)) b.dump = dump
} }
return client.Do(b.req)
resp, err := client.Do(b.req)
if err != nil {
return nil, err
}
b.resp = resp
return resp, nil
} }
// String returns the body string in response. // String returns the body string in response.
@ -402,12 +476,16 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
return nil, nil return nil, nil
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body) if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
if err != nil { reader, err := gzip.NewReader(resp.Body)
return nil, err if err != nil {
return nil, err
}
b.body, err = ioutil.ReadAll(reader)
} else {
b.body, err = ioutil.ReadAll(resp.Body)
} }
b.body = data return b.body, err
return data, nil
} }
// ToFile saves the body data in response to one file. // ToFile saves the body data in response to one file.
@ -438,8 +516,7 @@ func (b *BeegoHttpRequest) ToJson(v interface{}) error {
if err != nil { if err != nil {
return err return err
} }
err = json.Unmarshal(data, v) return json.Unmarshal(data, v)
return err
} }
// ToXml returns the map that marshals from the body bytes as xml in response . // ToXml returns the map that marshals from the body bytes as xml in response .
@ -449,8 +526,7 @@ func (b *BeegoHttpRequest) ToXml(v interface{}) error {
if err != nil { if err != nil {
return err return err
} }
err = xml.Unmarshal(data, v) return xml.Unmarshal(data, v)
return err
} }
// Response executes request client gets response mannually. // Response executes request client gets response mannually.
@ -465,7 +541,7 @@ func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, ad
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn.SetDeadline(time.Now().Add(rwTimeout)) err = conn.SetDeadline(time.Now().Add(rwTimeout))
return conn, nil return conn, err
} }
} }

10
log.go
View File

@ -78,9 +78,9 @@ func Warning(v ...interface{}) {
BeeLogger.Warning(generateFmtStr(len(v)), v...) BeeLogger.Warning(generateFmtStr(len(v)), v...)
} }
// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0. // compatibility alias for Warning()
func Warn(v ...interface{}) { func Warn(v ...interface{}) {
Warning(v...) BeeLogger.Warn(generateFmtStr(len(v)), v...)
} }
func Notice(v ...interface{}) { func Notice(v ...interface{}) {
@ -92,9 +92,9 @@ func Informational(v ...interface{}) {
BeeLogger.Informational(generateFmtStr(len(v)), v...) BeeLogger.Informational(generateFmtStr(len(v)), v...)
} }
// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0. // compatibility alias for Warning()
func Info(v ...interface{}) { func Info(v ...interface{}) {
Informational(v...) BeeLogger.Info(generateFmtStr(len(v)), v...)
} }
// Debug logs a message at debug level. // Debug logs a message at debug level.
@ -103,7 +103,7 @@ func Debug(v ...interface{}) {
} }
// Trace logs a message at trace level. // Trace logs a message at trace level.
// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0. // compatibility alias for Warning()
func Trace(v ...interface{}) { func Trace(v ...interface{}) {
BeeLogger.Trace(generateFmtStr(len(v)), v...) BeeLogger.Trace(generateFmtStr(len(v)), v...)
} }

View File

@ -43,11 +43,7 @@ func NewConn() LoggerInterface {
// init connection writer with json config. // init connection writer with json config.
// json config only need key "level". // json config only need key "level".
func (c *ConnWriter) Init(jsonconfig string) error { func (c *ConnWriter) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), c) return json.Unmarshal([]byte(jsonconfig), c)
if err != nil {
return err
}
return nil
} }
// write message in connection. // write message in connection.
@ -77,10 +73,9 @@ func (c *ConnWriter) Flush() {
// destroy connection writer and close tcp listener. // destroy connection writer and close tcp listener.
func (c *ConnWriter) Destroy() { func (c *ConnWriter) Destroy() {
if c.innerWriter == nil { if c.innerWriter != nil {
return c.innerWriter.Close()
} }
c.innerWriter.Close()
} }
func (c *ConnWriter) connect() error { func (c *ConnWriter) connect() error {

View File

@ -50,9 +50,10 @@ type ConsoleWriter struct {
// create ConsoleWriter returning as LoggerInterface. // create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface { func NewConsole() LoggerInterface {
cw := new(ConsoleWriter) cw := &ConsoleWriter{
cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime) lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
cw.Level = LevelDebug Level: LevelDebug,
}
return cw return cw
} }
@ -62,11 +63,7 @@ func (c *ConsoleWriter) Init(jsonconfig string) error {
if len(jsonconfig) == 0 { if len(jsonconfig) == 0 {
return nil return nil
} }
err := json.Unmarshal([]byte(jsonconfig), c) return json.Unmarshal([]byte(jsonconfig), c)
if err != nil {
return err
}
return nil
} }
// write message in console. // write message in console.
@ -76,9 +73,10 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
} }
if goos := runtime.GOOS; goos == "windows" { if goos := runtime.GOOS; goos == "windows" {
c.lg.Println(msg) c.lg.Println(msg)
} else { return nil
c.lg.Println(colors[level](msg))
} }
c.lg.Println(colors[level](msg))
return nil return nil
} }

76
logs/es/es.go Normal file
View File

@ -0,0 +1,76 @@
package es
import (
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
"time"
"github.com/astaxie/beego/logs"
"github.com/belogik/goes"
)
func NewES() logs.LoggerInterface {
cw := &esLogger{
Level: logs.LevelDebug,
}
return cw
}
type esLogger struct {
*goes.Connection
DSN string `json:"dsn"`
Level int `json:"level"`
}
// {"dsn":"http://localhost:9200/","level":1}
func (el *esLogger) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), el)
if err != nil {
return err
}
if el.DSN == "" {
return errors.New("empty dsn")
} else if u, err := url.Parse(el.DSN); err != nil {
return err
} else if u.Path == "" {
return errors.New("missing prefix")
} else if host, port, err := net.SplitHostPort(u.Host); err != nil {
return err
} else {
conn := goes.NewConnection(host, port)
el.Connection = conn
}
return nil
}
func (el *esLogger) WriteMsg(msg string, level int) error {
if level > el.Level {
return nil
}
t := time.Now()
vals := make(map[string]interface{})
vals["@timestamp"] = t.Format(time.RFC3339)
vals["@msg"] = msg
d := goes.Document{
Index: fmt.Sprintf("%04d.%02d.%02d", t.Year(), t.Month(), t.Day()),
Type: "logs",
Fields: vals,
}
_, err := el.Index(d, nil)
return err
}
func (el *esLogger) Destroy() {
}
func (el *esLogger) Flush() {
}
func init() {
logs.Register("es", NewES)
}

View File

@ -15,10 +15,11 @@
package logs package logs
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -122,11 +123,7 @@ func (w *FileLogWriter) startLogger() error {
return err return err
} }
w.mw.SetFd(fd) w.mw.SetFd(fd)
err = w.initFd() return w.initFd()
if err != nil {
return err
}
return nil
} }
func (w *FileLogWriter) docheck(size int) { func (w *FileLogWriter) docheck(size int) {
@ -169,18 +166,44 @@ func (w *FileLogWriter) initFd() error {
} }
w.maxsize_cursize = int(finfo.Size()) w.maxsize_cursize = int(finfo.Size())
w.daily_opendate = time.Now().Day() w.daily_opendate = time.Now().Day()
w.maxlines_curlines = 0
if finfo.Size() > 0 { if finfo.Size() > 0 {
content, err := ioutil.ReadFile(w.Filename) count, err := w.lines()
if err != nil { if err != nil {
return err return err
} }
w.maxlines_curlines = len(strings.Split(string(content), "\n")) w.maxlines_curlines = count
} else {
w.maxlines_curlines = 0
} }
return nil return nil
} }
func (w *FileLogWriter) lines() (int, error) {
fd, err := os.Open(w.Filename)
if err != nil {
return 0, err
}
defer fd.Close()
buf := make([]byte, 32768) // 32k
count := 0
lineSep := []byte{'\n'}
for {
c, err := fd.Read(buf)
if err != nil && err != io.EOF {
return count, err
}
count += bytes.Count(buf[:c], lineSep)
if err == io.EOF {
break
}
}
return count, nil
}
// DoRotate means it need to write file in new file. // DoRotate means it need to write file in new file.
// new file name like xx.log.2013-01-01.2 // new file name like xx.log.2013-01-01.2
func (w *FileLogWriter) DoRotate() error { func (w *FileLogWriter) DoRotate() error {

View File

@ -92,6 +92,7 @@ type BeeLogger struct {
level int level int
enableFuncCallDepth bool enableFuncCallDepth bool
loggerFuncCallDepth int loggerFuncCallDepth int
asynchronous bool
msg chan *logMsg msg chan *logMsg
outputs map[string]LoggerInterface outputs map[string]LoggerInterface
} }
@ -110,7 +111,11 @@ func NewLogger(channellen int64) *BeeLogger {
bl.loggerFuncCallDepth = 2 bl.loggerFuncCallDepth = 2
bl.msg = make(chan *logMsg, channellen) bl.msg = make(chan *logMsg, channellen)
bl.outputs = make(map[string]LoggerInterface) bl.outputs = make(map[string]LoggerInterface)
//bl.SetLogger("console", "") // default output to console return bl
}
func (bl *BeeLogger) Async() *BeeLogger {
bl.asynchronous = true
go bl.startLogger() go bl.startLogger()
return bl return bl
} }
@ -148,26 +153,30 @@ func (bl *BeeLogger) DelLogger(adaptername string) error {
} }
func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
if loglevel > bl.level {
return nil
}
lm := new(logMsg) lm := new(logMsg)
lm.level = loglevel lm.level = loglevel
if bl.enableFuncCallDepth { if bl.enableFuncCallDepth {
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) { if !ok {
_, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1) file = "???"
} line = 0
if ok {
_, filename := path.Split(file)
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
} else {
lm.msg = msg
} }
_, filename := path.Split(file)
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
} else { } else {
lm.msg = msg lm.msg = msg
} }
bl.msg <- lm if bl.asynchronous {
bl.msg <- lm
} else {
for name, l := range bl.outputs {
err := l.WriteMsg(lm.msg, lm.level)
if err != nil {
fmt.Println("unable to WriteMsg to adapter:", name, err)
return err
}
}
}
return nil return nil
} }
@ -184,6 +193,11 @@ func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
bl.loggerFuncCallDepth = d bl.loggerFuncCallDepth = d
} }
// get log funcCallDepth for wrapper
func (bl *BeeLogger) GetLogFuncCallDepth() int {
return bl.loggerFuncCallDepth
}
// enable log funcCallDepth // enable log funcCallDepth
func (bl *BeeLogger) EnableFuncCallDepth(b bool) { func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
bl.enableFuncCallDepth = b bl.enableFuncCallDepth = b
@ -207,71 +221,104 @@ func (bl *BeeLogger) startLogger() {
// Log EMERGENCY level message. // Log EMERGENCY level message.
func (bl *BeeLogger) Emergency(format string, v ...interface{}) { func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
if LevelEmergency > bl.level {
return
}
msg := fmt.Sprintf("[M] "+format, v...) msg := fmt.Sprintf("[M] "+format, v...)
bl.writerMsg(LevelEmergency, msg) bl.writerMsg(LevelEmergency, msg)
} }
// Log ALERT level message. // Log ALERT level message.
func (bl *BeeLogger) Alert(format string, v ...interface{}) { func (bl *BeeLogger) Alert(format string, v ...interface{}) {
if LevelAlert > bl.level {
return
}
msg := fmt.Sprintf("[A] "+format, v...) msg := fmt.Sprintf("[A] "+format, v...)
bl.writerMsg(LevelAlert, msg) bl.writerMsg(LevelAlert, msg)
} }
// Log CRITICAL level message. // Log CRITICAL level message.
func (bl *BeeLogger) Critical(format string, v ...interface{}) { func (bl *BeeLogger) Critical(format string, v ...interface{}) {
if LevelCritical > bl.level {
return
}
msg := fmt.Sprintf("[C] "+format, v...) msg := fmt.Sprintf("[C] "+format, v...)
bl.writerMsg(LevelCritical, msg) bl.writerMsg(LevelCritical, msg)
} }
// Log ERROR level message. // Log ERROR level message.
func (bl *BeeLogger) Error(format string, v ...interface{}) { func (bl *BeeLogger) Error(format string, v ...interface{}) {
if LevelError > bl.level {
return
}
msg := fmt.Sprintf("[E] "+format, v...) msg := fmt.Sprintf("[E] "+format, v...)
bl.writerMsg(LevelError, msg) bl.writerMsg(LevelError, msg)
} }
// Log WARNING level message. // Log WARNING level message.
func (bl *BeeLogger) Warning(format string, v ...interface{}) { func (bl *BeeLogger) Warning(format string, v ...interface{}) {
if LevelWarning > bl.level {
return
}
msg := fmt.Sprintf("[W] "+format, v...) msg := fmt.Sprintf("[W] "+format, v...)
bl.writerMsg(LevelWarning, msg) bl.writerMsg(LevelWarning, msg)
} }
// Log NOTICE level message. // Log NOTICE level message.
func (bl *BeeLogger) Notice(format string, v ...interface{}) { func (bl *BeeLogger) Notice(format string, v ...interface{}) {
if LevelNotice > bl.level {
return
}
msg := fmt.Sprintf("[N] "+format, v...) msg := fmt.Sprintf("[N] "+format, v...)
bl.writerMsg(LevelNotice, msg) bl.writerMsg(LevelNotice, msg)
} }
// Log INFORMATIONAL level message. // Log INFORMATIONAL level message.
func (bl *BeeLogger) Informational(format string, v ...interface{}) { func (bl *BeeLogger) Informational(format string, v ...interface{}) {
if LevelInformational > bl.level {
return
}
msg := fmt.Sprintf("[I] "+format, v...) msg := fmt.Sprintf("[I] "+format, v...)
bl.writerMsg(LevelInformational, msg) bl.writerMsg(LevelInformational, msg)
} }
// Log DEBUG level message. // Log DEBUG level message.
func (bl *BeeLogger) Debug(format string, v ...interface{}) { func (bl *BeeLogger) Debug(format string, v ...interface{}) {
if LevelDebug > bl.level {
return
}
msg := fmt.Sprintf("[D] "+format, v...) msg := fmt.Sprintf("[D] "+format, v...)
bl.writerMsg(LevelDebug, msg) bl.writerMsg(LevelDebug, msg)
} }
// Log WARN level message. // Log WARN level message.
// // compatibility alias for Warning()
// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0.
func (bl *BeeLogger) Warn(format string, v ...interface{}) { func (bl *BeeLogger) Warn(format string, v ...interface{}) {
bl.Warning(format, v...) if LevelWarning > bl.level {
return
}
msg := fmt.Sprintf("[W] "+format, v...)
bl.writerMsg(LevelWarning, msg)
} }
// Log INFO level message. // Log INFO level message.
// // compatibility alias for Informational()
// Deprecated: compatibility alias for Informational(), Will be removed in 1.5.0.
func (bl *BeeLogger) Info(format string, v ...interface{}) { func (bl *BeeLogger) Info(format string, v ...interface{}) {
bl.Informational(format, v...) if LevelInformational > bl.level {
return
}
msg := fmt.Sprintf("[I] "+format, v...)
bl.writerMsg(LevelInformational, msg)
} }
// Log TRACE level message. // Log TRACE level message.
// // compatibility alias for Debug()
// Deprecated: compatibility alias for Debug(), Will be removed in 1.5.0.
func (bl *BeeLogger) Trace(format string, v ...interface{}) { func (bl *BeeLogger) Trace(format string, v ...interface{}) {
bl.Debug(format, v...) if LevelDebug > bl.level {
return
}
msg := fmt.Sprintf("[D] "+format, v...)
bl.writerMsg(LevelDebug, msg)
} }
// flush all chan data. // flush all chan data.
@ -292,9 +339,9 @@ func (bl *BeeLogger) Close() {
fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err) fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
} }
} }
} else { continue
break
} }
break
} }
for _, l := range bl.outputs { for _, l := range bl.outputs {
l.Flush() l.Flush()

View File

@ -25,7 +25,8 @@ import (
) )
const ( const (
subjectPhrase = "Diagnostic message from server" // no usage
// subjectPhrase = "Diagnostic message from server"
) )
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server. // smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
@ -146,9 +147,7 @@ func (s *SmtpWriter) WriteMsg(msg string, level int) error {
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress + mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg) ">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
err := s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg) return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
return err
} }
// implementing method. empty. // implementing method. empty.

View File

@ -1,49 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package middleware
import "fmt"
// http exceptions
type HTTPException struct {
StatusCode int // http status code 4xx, 5xx
Description string
}
// return http exception error string, e.g. "400 Bad Request".
func (e *HTTPException) Error() string {
return fmt.Sprintf("%d %s", e.StatusCode, e.Description)
}
// map of http exceptions for each http status code int.
// defined 400,401,403,404,405,500,502,503 and 504 default.
var HTTPExceptionMaps map[int]HTTPException
func init() {
HTTPExceptionMaps = make(map[int]HTTPException)
// Normal 4XX HTTP Status
HTTPExceptionMaps[400] = HTTPException{400, "Bad Request"}
HTTPExceptionMaps[401] = HTTPException{401, "Unauthorized"}
HTTPExceptionMaps[403] = HTTPException{403, "Forbidden"}
HTTPExceptionMaps[404] = HTTPException{404, "Not Found"}
HTTPExceptionMaps[405] = HTTPException{405, "Method Not Allowed"}
// Normal 5XX HTTP Status
HTTPExceptionMaps[500] = HTTPException{500, "Internal Server Error"}
HTTPExceptionMaps[502] = HTTPException{502, "Bad Gateway"}
HTTPExceptionMaps[503] = HTTPException{503, "Service Unavailable"}
HTTPExceptionMaps[504] = HTTPException{504, "Gateway Timeout"}
}

View File

@ -34,7 +34,6 @@ type Translation struct {
} }
func NewLocale(filepath string, defaultlocal string) *Translation { func NewLocale(filepath string, defaultlocal string) *Translation {
i18n := make(map[string]map[string]string)
file, err := os.Open(filepath) file, err := os.Open(filepath)
if err != nil { if err != nil {
panic("open " + filepath + " err :" + err.Error()) panic("open " + filepath + " err :" + err.Error())
@ -43,8 +42,9 @@ func NewLocale(filepath string, defaultlocal string) *Translation {
if err != nil { if err != nil {
panic("read " + filepath + " err :" + err.Error()) panic("read " + filepath + " err :" + err.Error())
} }
err = json.Unmarshal(data, &i18n)
if err != nil { i18n := make(map[string]map[string]string)
if err = json.Unmarshal(data, &i18n); err != nil {
panic("json.Unmarshal " + filepath + " err :" + err.Error()) panic("json.Unmarshal " + filepath + " err :" + err.Error())
} }
return &Translation{ return &Translation{

View File

@ -40,6 +40,7 @@ var mimemaps map[string]string = map[string]string{
".ani": "application/x-navi-animation", ".ani": "application/x-navi-animation",
".aos": "application/x-nokia-9000-communicator-add-on-software", ".aos": "application/x-nokia-9000-communicator-add-on-software",
".aps": "application/mime", ".aps": "application/mime",
".apk": "application/vnd.android.package-archive",
".arc": "application/x-arc-compressed", ".arc": "application/x-arc-compressed",
".arj": "application/arj", ".arj": "application/arj",
".art": "image/x-jg", ".art": "image/x-jg",

View File

@ -19,7 +19,6 @@ import (
"strings" "strings"
beecontext "github.com/astaxie/beego/context" beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
) )
type namespaceCond func(*beecontext.Context) bool type namespaceCond func(*beecontext.Context) bool
@ -57,7 +56,7 @@ func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
func (n *Namespace) Cond(cond namespaceCond) *Namespace { func (n *Namespace) Cond(cond namespaceCond) *Namespace {
fn := func(ctx *beecontext.Context) { fn := func(ctx *beecontext.Context) {
if !cond(ctx) { if !cond(ctx) {
middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed") exception("405", ctx)
} }
} }
if v, ok := n.handlers.filters[BeforeRouter]; ok { if v, ok := n.handlers.filters[BeforeRouter]; ok {

View File

@ -117,7 +117,7 @@ o.Begin()
... ...
user := User{Name: "slene"} user := User{Name: "slene"}
id, err := o.Insert(&user) id, err := o.Insert(&user)
if err != nil { if err == nil {
o.Commit() o.Commit()
} else { } else {
o.Rollback() o.Rollback()

View File

@ -104,7 +104,11 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string {
typ += " " + "NOT NULL" typ += " " + "NOT NULL"
} }
return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s", Q, fi.mi.table, Q, Q, fi.column, Q, typ) return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s %s",
Q, fi.mi.table, Q,
Q, fi.column, Q,
typ, getColumnDefault(fi),
)
} }
// create database creation string. // create database creation string.
@ -155,6 +159,9 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
//if fi.initial.String() != "" { //if fi.initial.String() != "" {
// column += " DEFAULT " + fi.initial.String() // column += " DEFAULT " + fi.initial.String()
//} //}
// Append attribute DEFAULT
column += getColumnDefault(fi)
if fi.unique { if fi.unique {
column += " " + "UNIQUE" column += " " + "UNIQUE"
@ -239,3 +246,44 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
return return
} }
// Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands
func getColumnDefault(fi *fieldInfo) string {
var (
v, t, d string
)
// Skip default attribute if field is in relations
if fi.rel || fi.reverse {
return v
}
t = " DEFAULT '%s' "
// These defaults will be useful if there no config value orm:"default" and NOT NULL is on
switch fi.fieldType {
case TypeDateField, TypeDateTimeField:
return v;
case TypeBooleanField, TypeBitField, TypeSmallIntegerField, TypeIntegerField,
TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField,
TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField,
TypeDecimalField:
d = "0"
}
if fi.colDefault {
if !fi.initial.Exist() {
v = fmt.Sprintf(t, "")
} else {
v = fmt.Sprintf(t, fi.initial.String())
}
} else {
if !fi.null {
v = fmt.Sprintf(t, d)
}
}
return v
}

View File

@ -44,6 +44,8 @@ var (
"gte": true, "gte": true,
"lt": true, "lt": true,
"lte": true, "lte": true,
"eq": true,
"nq": true,
"startswith": true, "startswith": true,
"endswith": true, "endswith": true,
"istartswith": true, "istartswith": true,
@ -324,7 +326,7 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo
query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ?", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q) query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ?", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q)
refs := make([]interface{}, colsNum) refs := make([]interface{}, colsNum)
for i, _ := range refs { for i := range refs {
var ref interface{} var ref interface{}
refs[i] = &ref refs[i] = &ref
} }
@ -423,7 +425,7 @@ func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []s
Q := d.ins.TableQuote() Q := d.ins.TableQuote()
marks := make([]string, len(names)) marks := make([]string, len(names))
for i, _ := range marks { for i := range marks {
marks[i] = "?" marks[i] = "?"
} }
@ -693,7 +695,7 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
} }
marks := make([]string, len(args)) marks := make([]string, len(args))
for i, _ := range marks { for i := range marks {
marks[i] = "?" marks[i] = "?"
} }
sql := fmt.Sprintf("IN (%s)", strings.Join(marks, ", ")) sql := fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))
@ -824,7 +826,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
} }
refs := make([]interface{}, colsNum) refs := make([]interface{}, colsNum)
for i, _ := range refs { for i := range refs {
var ref interface{} var ref interface{}
refs[i] = &ref refs[i] = &ref
} }
@ -964,7 +966,7 @@ func (d *dbBase) GenerateOperatorSql(mi *modelInfo, fi *fieldInfo, operator stri
switch operator { switch operator {
case "in": case "in":
marks := make([]string, len(params)) marks := make([]string, len(params))
for i, _ := range marks { for i := range marks {
marks[i] = "?" marks[i] = "?"
} }
sql = fmt.Sprintf("IN (%s)", strings.Join(marks, ", ")) sql = fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))
@ -1460,7 +1462,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond
} }
refs := make([]interface{}, len(cols)) refs := make([]interface{}, len(cols))
for i, _ := range refs { for i := range refs {
var ref interface{} var ref interface{}
refs[i] = &ref refs[i] = &ref
} }

View File

@ -30,6 +30,8 @@ var mysqlOperators = map[string]string{
"gte": ">= ?", "gte": ">= ?",
"lt": "< ?", "lt": "< ?",
"lte": "<= ?", "lte": "<= ?",
"eq": "= ?",
"ne": "!= ?",
"startswith": "LIKE BINARY ?", "startswith": "LIKE BINARY ?",
"endswith": "LIKE BINARY ?", "endswith": "LIKE BINARY ?",
"istartswith": "LIKE ?", "istartswith": "LIKE ?",

View File

@ -29,6 +29,8 @@ var postgresOperators = map[string]string{
"gte": ">= ?", "gte": ">= ?",
"lt": "< ?", "lt": "< ?",
"lte": "<= ?", "lte": "<= ?",
"eq": "= ?",
"ne": "!= ?",
"startswith": "LIKE ?", "startswith": "LIKE ?",
"endswith": "LIKE ?", "endswith": "LIKE ?",
"istartswith": "LIKE UPPER(?)", "istartswith": "LIKE UPPER(?)",

View File

@ -29,6 +29,8 @@ var sqliteOperators = map[string]string{
"gte": ">= ?", "gte": ">= ?",
"lt": "< ?", "lt": "< ?",
"lte": "<= ?", "lte": "<= ?",
"eq": "= ?",
"ne": "!= ?",
"startswith": "LIKE ? ESCAPE '\\'", "startswith": "LIKE ? ESCAPE '\\'",
"endswith": "LIKE ? ESCAPE '\\'", "endswith": "LIKE ? ESCAPE '\\'",
"istartswith": "LIKE ? ESCAPE '\\'", "istartswith": "LIKE ? ESCAPE '\\'",

View File

@ -116,6 +116,7 @@ type fieldInfo struct {
null bool null bool
index bool index bool
unique bool unique bool
colDefault bool
initial StrTo initial StrTo
size int size int
auto_now bool auto_now bool
@ -280,6 +281,11 @@ checkType:
fi.pk = attrs["pk"] fi.pk = attrs["pk"]
fi.unique = attrs["unique"] fi.unique = attrs["unique"]
// Mark object property if there is attribute "default" in the orm configuration
if _, ok := tags["default"]; ok {
fi.colDefault = true
}
switch fieldType { switch fieldType {
case RelManyToMany, RelReverseMany, RelReverseOne: case RelManyToMany, RelReverseMany, RelReverseOne:
fi.null = false fi.null = false

View File

@ -242,14 +242,14 @@ type User struct {
func (u *User) TableIndex() [][]string { func (u *User) TableIndex() [][]string {
return [][]string{ return [][]string{
[]string{"Id", "UserName"}, {"Id", "UserName"},
[]string{"Id", "Created"}, {"Id", "Created"},
} }
} }
func (u *User) TableUnique() [][]string { func (u *User) TableUnique() [][]string {
return [][]string{ return [][]string{
[]string{"UserName", "Email"}, {"UserName", "Email"},
} }
} }
@ -287,7 +287,7 @@ type Post struct {
func (u *Post) TableIndex() [][]string { func (u *Post) TableIndex() [][]string {
return [][]string{ return [][]string{
[]string{"Id", "Created"}, {"Id", "Created"},
} }
} }

View File

@ -489,10 +489,6 @@ func (o *orm) Driver() Driver {
return driver(o.alias.Name) return driver(o.alias.Name)
} }
func (o *orm) GetDB() dbQuerier {
panic(ErrNotImplement)
}
// create new orm // create new orm
func NewOrm() Ormer { func NewOrm() Ormer {
BootStrap() // execute only once BootStrap() // execute only once

View File

@ -115,23 +115,21 @@ func (o querySet) OrderBy(exprs ...string) QuerySeter {
// set relation model to query together. // set relation model to query together.
// it will query relation models and assign to parent model. // it will query relation models and assign to parent model.
func (o querySet) RelatedSel(params ...interface{}) QuerySeter { func (o querySet) RelatedSel(params ...interface{}) QuerySeter {
var related []string if len(params) == 0 {
if len(params) == 0 { o.relDepth = DefaultRelsDepth
o.relDepth = DefaultRelsDepth } else {
} else { for _, p := range params {
for _, p := range params { switch val := p.(type) {
switch val := p.(type) { case string:
case string: o.related = append(o.related, val)
related = append(o.related, val) case int:
case int: o.relDepth = val
o.relDepth = val default:
default: panic(fmt.Errorf("<QuerySeter.RelatedSel> wrong param kind: %v", val))
panic(fmt.Errorf("<QuerySeter.RelatedSel> wrong param kind: %v", val)) }
} }
} }
} return &o
o.related = related
return &o
} }
// set condition to QuerySeter. // set condition to QuerySeter.

View File

@ -585,7 +585,7 @@ func (o *rawSet) readValues(container interface{}, needCols []string) (int64, er
cols = columns cols = columns
refs = make([]interface{}, len(cols)) refs = make([]interface{}, len(cols))
for i, _ := range refs { for i := range refs {
var ref sql.NullString var ref sql.NullString
refs[i] = &ref refs[i] = &ref
@ -711,7 +711,7 @@ func (o *rawSet) queryRowsTo(container interface{}, keyCol, valueCol string) (in
} else { } else {
cols = columns cols = columns
refs = make([]interface{}, len(cols)) refs = make([]interface{}, len(cols))
for i, _ := range refs { for i := range refs {
if keyCol == cols[i] { if keyCol == cols[i] {
keyIndex = i keyIndex = i
} }

View File

@ -586,29 +586,29 @@ func TestInsertTestData(t *testing.T) {
throwFail(t, AssertIs(id, 4)) throwFail(t, AssertIs(id, 4))
tags := []*Tag{ tags := []*Tag{
&Tag{Name: "golang", BestPost: &Post{Id: 2}}, {Name: "golang", BestPost: &Post{Id: 2}},
&Tag{Name: "example"}, {Name: "example"},
&Tag{Name: "format"}, {Name: "format"},
&Tag{Name: "c++"}, {Name: "c++"},
} }
posts := []*Post{ posts := []*Post{
&Post{User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand. {User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand.
This document gives tips for writing clear, idiomatic Go code. It augments the language specification, the Tour of Go, and How to Write Go Code, all of which you should read first.`}, This document gives tips for writing clear, idiomatic Go code. It augments the language specification, the Tour of Go, and How to Write Go Code, all of which you should read first.`},
&Post{User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`}, {User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`},
&Post{User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide. {User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide.
With Go we take an unusual approach and let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't seem right, rearrange your program (or file a bug about gofmt), don't work around it.`}, With Go we take an unusual approach and let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't seem right, rearrange your program (or file a bug about gofmt), don't work around it.`},
&Post{User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code. {User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code.
The program—and web server—godoc processes Go source files to extract documentation about the contents of the package. Comments that appear before top-level declarations, with no intervening newlines, are extracted along with the declaration to serve as explanatory text for the item. The nature and style of these comments determines the quality of the documentation godoc produces.`}, The program—and web server—godoc processes Go source files to extract documentation about the contents of the package. Comments that appear before top-level declarations, with no intervening newlines, are extracted along with the declaration to serve as explanatory text for the item. The nature and style of these comments determines the quality of the documentation godoc produces.`},
} }
comments := []*Comment{ comments := []*Comment{
&Comment{Post: posts[0], Content: "a comment"}, {Post: posts[0], Content: "a comment"},
&Comment{Post: posts[1], Content: "yes"}, {Post: posts[1], Content: "yes"},
&Comment{Post: posts[1]}, {Post: posts[1]},
&Comment{Post: posts[1]}, {Post: posts[1]},
&Comment{Post: posts[2]}, {Post: posts[2]},
&Comment{Post: posts[2]}, {Post: posts[2]},
} }
for _, tag := range tags { for _, tag := range tags {
@ -1248,7 +1248,7 @@ func TestQueryM2M(t *testing.T) {
post := Post{Id: 4} post := Post{Id: 4}
m2m := dORM.QueryM2M(&post, "Tags") m2m := dORM.QueryM2M(&post, "Tags")
tag1 := []*Tag{&Tag{Name: "TestTag1"}, &Tag{Name: "TestTag2"}} tag1 := []*Tag{{Name: "TestTag1"}, {Name: "TestTag2"}}
tag2 := &Tag{Name: "TestTag3"} tag2 := &Tag{Name: "TestTag3"}
tag3 := []interface{}{&Tag{Name: "TestTag4"}} tag3 := []interface{}{&Tag{Name: "TestTag4"}}
@ -1311,7 +1311,7 @@ func TestQueryM2M(t *testing.T) {
m2m = dORM.QueryM2M(&tag, "Posts") m2m = dORM.QueryM2M(&tag, "Posts")
post1 := []*Post{&Post{Title: "TestPost1"}, &Post{Title: "TestPost2"}} post1 := []*Post{{Title: "TestPost1"}, {Title: "TestPost2"}}
post2 := &Post{Title: "TestPost3"} post2 := &Post{Title: "TestPost3"}
post3 := []interface{}{&Post{Title: "TestPost4"}} post3 := []interface{}{&Post{Title: "TestPost4"}}

View File

@ -51,7 +51,6 @@ type Ormer interface {
Rollback() error Rollback() error
Raw(string, ...interface{}) RawSeter Raw(string, ...interface{}) RawSeter
Driver() Driver Driver() Driver
GetDB() dbQuerier
} }
// insert prepared statement // insert prepared statement

View File

@ -195,7 +195,7 @@ func snakeString(s string) string {
} }
data = append(data, d) data = append(data, d)
} }
return strings.ToLower(string(data[:len(data)])) return strings.ToLower(string(data[:]))
} }
// camel string, xx_yy to XxYy // camel string, xx_yy to XxYy
@ -220,7 +220,7 @@ func camelString(s string) string {
} }
data = append(data, d) data = append(data, d)
} }
return string(data[:len(data)]) return string(data[:])
} }
type argString []string type argString []string

View File

@ -24,6 +24,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"sort"
"strings" "strings"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
@ -36,7 +37,7 @@ import (
) )
func init() { func init() {
{{.globalinfo}} {{.globalinfo}}
} }
` `
@ -57,7 +58,7 @@ func parserPkg(pkgRealpath, pkgpath string) error {
rep := strings.NewReplacer("/", "_", ".", "_") rep := strings.NewReplacer("/", "_", ".", "_")
commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go" commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go"
if !compareFile(pkgRealpath) { if !compareFile(pkgRealpath) {
Info(pkgRealpath + " don't has updated") Info(pkgRealpath + " has not changed, not reloading")
return nil return nil
} }
genInfoList = make(map[string][]ControllerComments) genInfoList = make(map[string][]ControllerComments)
@ -129,7 +130,13 @@ func genRouterCode() {
os.Mkdir(path.Join(workPath, "routers"), 0755) os.Mkdir(path.Join(workPath, "routers"), 0755)
Info("generate router from comments") Info("generate router from comments")
var globalinfo string var globalinfo string
for k, cList := range genInfoList { sortKey := make([]string, 0)
for k, _ := range genInfoList {
sortKey = append(sortKey, k)
}
sort.Strings(sortKey)
for _, k := range sortKey {
cList := genInfoList[k]
for _, c := range cList { for _, c := range cList {
allmethod := "nil" allmethod := "nil"
if len(c.AllowHTTPMethods) > 0 { if len(c.AllowHTTPMethods) > 0 {

View File

@ -217,6 +217,7 @@ func Allow(opts *Options) beego.FilterFunc {
ctx.Output.Header(key, value) ctx.Output.Header(key, value)
} }
ctx.Output.SetStatus(http.StatusOK) ctx.Output.SetStatus(http.StatusOK)
ctx.WriteString("")
return return
} }
headers = opts.Header(origin) headers = opts.Header(origin)

118
router.go
View File

@ -30,7 +30,6 @@ import (
"time" "time"
beecontext "github.com/astaxie/beego/context" beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/toolbox"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
@ -89,7 +88,7 @@ func (l *logFilter) Filter(ctx *beecontext.Context) bool {
if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
return true return true
} }
for prefix, _ := range StaticDir { for prefix := range StaticDir {
if strings.HasPrefix(requestPath, prefix) { if strings.HasPrefix(requestPath, prefix) {
return true return true
} }
@ -153,7 +152,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
if val := reflectVal.MethodByName(colon[1]); val.IsValid() { if val := reflectVal.MethodByName(colon[1]); val.IsValid() {
methods[strings.ToUpper(m)] = colon[1] methods[strings.ToUpper(m)] = colon[1]
} else { } else {
panic(colon[1] + " method doesn't exist in the controller " + t.Name()) panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name())
} }
} else { } else {
panic(v + " is an invalid method mapping. Method doesn't exist " + m) panic(v + " is an invalid method mapping. Method doesn't exist " + m)
@ -172,7 +171,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
p.addToRouter(m, pattern, route) p.addToRouter(m, pattern, route)
} }
} else { } else {
for k, _ := range methods { for k := range methods {
if k == "*" { if k == "*" {
for _, m := range HTTPMETHOD { for _, m := range HTTPMETHOD {
p.addToRouter(m, pattern, route) p.addToRouter(m, pattern, route)
@ -333,7 +332,7 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) {
methods[strings.ToUpper(method)] = strings.ToUpper(method) methods[strings.ToUpper(method)] = strings.ToUpper(method)
} }
route.methods = methods route.methods = methods
for k, _ := range methods { for k := range methods {
if k == "*" { if k == "*" {
for _, m := range HTTPMETHOD { for _, m := range HTTPMETHOD {
p.addToRouter(m, pattern, route) p.addToRouter(m, pattern, route)
@ -429,7 +428,7 @@ func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error
// UrlFor does another controller handler in this request function. // UrlFor does another controller handler in this request function.
// it can access any controller method. // it can access any controller method.
func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { func (p *ControllerRegistor) UrlFor(endpoint string, values ...interface{}) string {
paths := strings.Split(endpoint, ".") paths := strings.Split(endpoint, ".")
if len(paths) <= 1 { if len(paths) <= 1 {
Warn("urlfor endpoint must like path.controller.method") Warn("urlfor endpoint must like path.controller.method")
@ -444,9 +443,9 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
key := "" key := ""
for k, v := range values { for k, v := range values {
if k%2 == 0 { if k%2 == 0 {
key = v key = fmt.Sprint(v)
} else { } else {
params[key] = v params[key] = fmt.Sprint(v)
} }
} }
} }
@ -577,7 +576,6 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin
// Implement http.Handler interface. // Implement http.Handler interface.
func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
defer p.recoverPanic(rw, r)
starttime := time.Now() starttime := time.Now()
var runrouter reflect.Type var runrouter reflect.Type
var findrouter bool var findrouter bool
@ -600,6 +598,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
context.Output.Context = context context.Output.Context = context
context.Output.EnableGzip = EnableGzip context.Output.EnableGzip = EnableGzip
defer p.recoverPanic(context)
var urlPath string var urlPath string
if !RouterCaseSensitive { if !RouterCaseSensitive {
urlPath = strings.ToLower(r.URL.Path) urlPath = strings.ToLower(r.URL.Path)
@ -611,17 +611,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if p.enableFilter { if p.enableFilter {
if l, ok := p.filters[pos]; ok { if l, ok := p.filters[pos]; ok {
for _, filterR := range l { for _, filterR := range l {
if ok, p := filterR.ValidRouter(urlPath); ok { if filterR.returnOnOutput && w.started {
context.Input.Params = p return true
filterR.filterFunc(context) }
if filterR.returnOnOutput && w.started { if ok, params := filterR.ValidRouter(urlPath); ok {
return true for k, v := range params {
context.Input.Params[k] = v
} }
filterR.filterFunc(context)
}
if filterR.returnOnOutput && w.started {
return true
} }
} }
} }
} }
return false return false
} }
@ -648,7 +652,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
context.Input.CruSession, err = GlobalSessions.SessionStart(w, r) context.Input.CruSession, err = GlobalSessions.SessionStart(w, r)
if err != nil { if err != nil {
Error(err) Error(err)
middleware.Exception("503", rw, r, "") exception("503", context)
return return
} }
defer func() { defer func() {
@ -703,7 +707,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
//if no matches to url, throw a not found exception //if no matches to url, throw a not found exception
if !findrouter { if !findrouter {
middleware.Exception("404", rw, r, "") exception("404", context)
goto Admin goto Admin
} }
@ -719,7 +723,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
isRunable = true isRunable = true
routerInfo.runfunction(context) routerInfo.runfunction(context)
} else { } else {
middleware.Exception("405", rw, r, "Method Not Allowed") exception("405", context)
goto Admin goto Admin
} }
} else if routerInfo.routerType == routerTypeHandler { } else if routerInfo.routerType == routerTypeHandler {
@ -830,7 +834,7 @@ Admin:
} }
} }
if RunMode == "dev" { if RunMode == "dev" || AccessLogs {
var devinfo string var devinfo string
if findrouter { if findrouter {
if routerInfo != nil { if routerInfo != nil {
@ -852,68 +856,34 @@ Admin:
} }
} }
func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Request) { func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) {
if err := recover(); err != nil { if err := recover(); err != nil {
if err == USERSTOPRUN { if err == USERSTOPRUN {
return return
} }
if he, ok := err.(middleware.HTTPException); ok { if !RecoverPanic {
rw.WriteHeader(he.StatusCode) panic(err)
rw.Write([]byte(he.Description))
// catch intented errors, only for HTTP 4XX and 5XX
} else { } else {
if RunMode == "dev" { if ErrorsShow {
if !RecoverPanic { if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
panic(err) exception(fmt.Sprint(err), context)
} else { return
if ErrorsShow {
if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
handler(rw, r)
return
}
}
var stack string
Critical("the request url is ", r.URL.Path)
Critical("Handler crashed with error", err)
for i := 1; ; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
Critical(fmt.Sprintf("%s:%d", file, line))
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
}
middleware.ShowErr(err, rw, r, stack)
}
} else {
if !RecoverPanic {
panic(err)
} else {
// in production model show all infomation
if ErrorsShow {
if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
handler(rw, r)
return
} else if handler, ok := middleware.ErrorMaps["503"]; ok {
handler(rw, r)
return
} else {
rw.Write([]byte(fmt.Sprint(err)))
}
} else {
Critical("the request url is ", r.URL.Path)
Critical("Handler crashed with error", err)
for i := 1; ; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
Critical(fmt.Sprintf("%s:%d", file, line))
}
}
} }
} }
var stack string
Critical("the request url is ", context.Input.Url())
Critical("Handler crashed with error", err)
for i := 1; ; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
Critical(fmt.Sprintf("%s:%d", file, line))
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
}
if RunMode == "dev" {
showErr(err, context, stack)
}
} }
} }
} }

View File

@ -444,7 +444,7 @@ func TestFilterAfterExec(t *testing.T) {
mux := NewControllerRegister() mux := NewControllerRegister()
mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput) mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput) mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
mux.InsertFilter(url, AfterExec, beegoAfterExec1) mux.InsertFilter(url, AfterExec, beegoAfterExec1, false)
mux.Get(url, beegoFilterFunc) mux.Get(url, beegoFilterFunc)
@ -506,7 +506,7 @@ func TestFilterFinishRouterMultiFirstOnly(t *testing.T) {
url := "/finishRouterMultiFirstOnly" url := "/finishRouterMultiFirstOnly"
mux := NewControllerRegister() mux := NewControllerRegister()
mux.InsertFilter(url, FinishRouter, beegoFinishRouter1) mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
mux.InsertFilter(url, FinishRouter, beegoFinishRouter2) mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
mux.Get(url, beegoFilterFunc) mux.Get(url, beegoFilterFunc)
@ -534,7 +534,7 @@ func TestFilterFinishRouterMulti(t *testing.T) {
mux := NewControllerRegister() mux := NewControllerRegister()
mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false) mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
mux.InsertFilter(url, FinishRouter, beegoFinishRouter2) mux.InsertFilter(url, FinishRouter, beegoFinishRouter2, false)
mux.Get(url, beegoFilterFunc) mux.Get(url, beegoFilterFunc)

View File

@ -2,6 +2,8 @@ package session
import ( import (
"net/http" "net/http"
"strconv"
"strings"
"sync" "sync"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
@ -74,19 +76,29 @@ func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) {
type LedisProvider struct { type LedisProvider struct {
maxlifetime int64 maxlifetime int64
savePath string savePath string
db int
} }
// init ledis session // init ledis session
// savepath like ledis server saveDataPath,pool size // savepath like ledis server saveDataPath,pool size
// e.g. 127.0.0.1:6379,100,astaxie // e.g. 127.0.0.1:6379,100,astaxie
func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error { func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error {
var err error
lp.maxlifetime = maxlifetime lp.maxlifetime = maxlifetime
lp.savePath = savePath configs := strings.Split(savePath, ",")
if len(configs) == 1 {
lp.savePath = configs[0]
} else if len(configs) == 2 {
lp.savePath = configs[0]
lp.db, err = strconv.Atoi(configs[1])
if err != nil {
return err
}
}
cfg := new(config.Config) cfg := new(config.Config)
cfg.DataDir = lp.savePath cfg.DataDir = lp.savePath
var err error
nowLedis, err := ledis.Open(cfg) nowLedis, err := ledis.Open(cfg)
c, err = nowLedis.Select(0) c, err = nowLedis.Select(lp.db)
if err != nil { if err != nil {
println(err) println(err)
return nil return nil

View File

@ -129,8 +129,9 @@ func (rp *MemProvider) SessionRead(sid string) (session.SessionStore, error) {
} }
} }
item, err := client.Get(sid) item, err := client.Get(sid)
if err != nil { if err != nil && err == memcache.ErrCacheMiss {
return nil, err rs := &MemcacheSessionStore{sid: sid, values: make(map[interface{}]interface{}), maxlifetime: rp.maxlifetime}
return rs, nil
} }
var kv map[interface{}]interface{} var kv map[interface{}]interface{}
if len(item.Value) == 0 { if len(item.Value) == 0 {
@ -141,7 +142,6 @@ func (rp *MemProvider) SessionRead(sid string) (session.SessionStore, error) {
return nil, err return nil, err
} }
} }
rs := &MemcacheSessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime} rs := &MemcacheSessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
return rs, nil return rs, nil
} }
@ -179,7 +179,6 @@ func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.SessionSto
} else { } else {
client.Delete(oldsid) client.Delete(oldsid)
item.Key = sid item.Key = sid
item.Value = item.Value
item.Expiration = int32(rp.maxlifetime) item.Expiration = int32(rp.maxlifetime)
client.Set(item) client.Set(item)
contain = item.Value contain = item.Value

View File

@ -21,7 +21,7 @@
// mysql session support need create table as sql: // mysql session support need create table as sql:
// CREATE TABLE `session` ( // CREATE TABLE `session` (
// `session_key` char(64) NOT NULL, // `session_key` char(64) NOT NULL,
// session_data` blob, // `session_data` blob,
// `session_expiry` int(11) unsigned NOT NULL, // `session_expiry` int(11) unsigned NOT NULL,
// PRIMARY KEY (`session_key`) // PRIMARY KEY (`session_key`)
// ) ENGINE=MyISAM DEFAULT CHARSET=utf8; // ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

View File

@ -118,12 +118,13 @@ type RedisProvider struct {
savePath string savePath string
poolsize int poolsize int
password string password string
dbNum int
poollist *redis.Pool poollist *redis.Pool
} }
// init redis session // init redis session
// savepath like redis server addr,pool size,password // savepath like redis server addr,pool size,password,dbnum
// e.g. 127.0.0.1:6379,100,astaxie // e.g. 127.0.0.1:6379,100,astaxie,0
func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
rp.maxlifetime = maxlifetime rp.maxlifetime = maxlifetime
configs := strings.Split(savePath, ",") configs := strings.Split(savePath, ",")
@ -143,6 +144,16 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
if len(configs) > 2 { if len(configs) > 2 {
rp.password = configs[2] rp.password = configs[2]
} }
if len(configs) > 3 {
dbnum, err := strconv.Atoi(configs[3])
if err != nil || dbnum < 0 {
rp.dbNum = 0
} else {
rp.dbNum = dbnum
}
} else {
rp.dbNum = 0
}
rp.poollist = redis.NewPool(func() (redis.Conn, error) { rp.poollist = redis.NewPool(func() (redis.Conn, error) {
c, err := redis.Dial("tcp", rp.savePath) c, err := redis.Dial("tcp", rp.savePath)
if err != nil { if err != nil {
@ -154,6 +165,11 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
return nil, err return nil, err
} }
} }
_, err = c.Do("SELECT", rp.dbNum)
if err != nil {
c.Close()
return nil, err
}
return c, err return c, err
}, rp.poolsize) }, rp.poolsize)

View File

@ -1,199 +1,199 @@
// Copyright 2014 beego Author. All Rights Reserved. // Copyright 2014 beego Author. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package session package session
import ( import (
"container/list" "container/list"
"net/http" "net/http"
"sync" "sync"
"time" "time"
) )
var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)} var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)}
// memory session store. // memory session store.
// it saved sessions in a map in memory. // it saved sessions in a map in memory.
type MemSessionStore struct { type MemSessionStore struct {
sid string //session id sid string //session id
timeAccessed time.Time //last access time timeAccessed time.Time //last access time
value map[interface{}]interface{} //session store value map[interface{}]interface{} //session store
lock sync.RWMutex lock sync.RWMutex
} }
// set value to memory session // set value to memory session
func (st *MemSessionStore) Set(key, value interface{}) error { func (st *MemSessionStore) Set(key, value interface{}) error {
st.lock.Lock() st.lock.Lock()
defer st.lock.Unlock() defer st.lock.Unlock()
st.value[key] = value st.value[key] = value
return nil return nil
} }
// get value from memory session by key // get value from memory session by key
func (st *MemSessionStore) Get(key interface{}) interface{} { func (st *MemSessionStore) Get(key interface{}) interface{} {
st.lock.RLock() st.lock.RLock()
defer st.lock.RUnlock() defer st.lock.RUnlock()
if v, ok := st.value[key]; ok { if v, ok := st.value[key]; ok {
return v return v
} else { } else {
return nil return nil
} }
} }
// delete in memory session by key // delete in memory session by key
func (st *MemSessionStore) Delete(key interface{}) error { func (st *MemSessionStore) Delete(key interface{}) error {
st.lock.Lock() st.lock.Lock()
defer st.lock.Unlock() defer st.lock.Unlock()
delete(st.value, key) delete(st.value, key)
return nil return nil
} }
// clear all values in memory session // clear all values in memory session
func (st *MemSessionStore) Flush() error { func (st *MemSessionStore) Flush() error {
st.lock.Lock() st.lock.Lock()
defer st.lock.Unlock() defer st.lock.Unlock()
st.value = make(map[interface{}]interface{}) st.value = make(map[interface{}]interface{})
return nil return nil
} }
// get this id of memory session store // get this id of memory session store
func (st *MemSessionStore) SessionID() string { func (st *MemSessionStore) SessionID() string {
return st.sid return st.sid
} }
// Implement method, no used. // Implement method, no used.
func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) {
} }
type MemProvider struct { type MemProvider struct {
lock sync.RWMutex // locker lock sync.RWMutex // locker
sessions map[string]*list.Element // map in memory sessions map[string]*list.Element // map in memory
list *list.List // for gc list *list.List // for gc
maxlifetime int64 maxlifetime int64
savePath string savePath string
} }
// init memory session // init memory session
func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
pder.maxlifetime = maxlifetime pder.maxlifetime = maxlifetime
pder.savePath = savePath pder.savePath = savePath
return nil return nil
} }
// get memory session store by sid // get memory session store by sid
func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) {
pder.lock.RLock() pder.lock.RLock()
if element, ok := pder.sessions[sid]; ok { if element, ok := pder.sessions[sid]; ok {
go pder.SessionUpdate(sid) go pder.SessionUpdate(sid)
pder.lock.RUnlock() pder.lock.RUnlock()
return element.Value.(*MemSessionStore), nil return element.Value.(*MemSessionStore), nil
} else { } else {
pder.lock.RUnlock() pder.lock.RUnlock()
pder.lock.Lock() pder.lock.Lock()
newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})}
element := pder.list.PushBack(newsess) element := pder.list.PushBack(newsess)
pder.sessions[sid] = element pder.sessions[sid] = element
pder.lock.Unlock() pder.lock.Unlock()
return newsess, nil return newsess, nil
} }
} }
// check session store exist in memory session by sid // check session store exist in memory session by sid
func (pder *MemProvider) SessionExist(sid string) bool { func (pder *MemProvider) SessionExist(sid string) bool {
pder.lock.RLock() pder.lock.RLock()
defer pder.lock.RUnlock() defer pder.lock.RUnlock()
if _, ok := pder.sessions[sid]; ok { if _, ok := pder.sessions[sid]; ok {
return true return true
} else { } else {
return false return false
} }
} }
// generate new sid for session store in memory session // generate new sid for session store in memory session
func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
pder.lock.RLock() pder.lock.RLock()
if element, ok := pder.sessions[oldsid]; ok { if element, ok := pder.sessions[oldsid]; ok {
go pder.SessionUpdate(oldsid) go pder.SessionUpdate(oldsid)
pder.lock.RUnlock() pder.lock.RUnlock()
pder.lock.Lock() pder.lock.Lock()
element.Value.(*MemSessionStore).sid = sid element.Value.(*MemSessionStore).sid = sid
pder.sessions[sid] = element pder.sessions[sid] = element
delete(pder.sessions, oldsid) delete(pder.sessions, oldsid)
pder.lock.Unlock() pder.lock.Unlock()
return element.Value.(*MemSessionStore), nil return element.Value.(*MemSessionStore), nil
} else { } else {
pder.lock.RUnlock() pder.lock.RUnlock()
pder.lock.Lock() pder.lock.Lock()
newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})}
element := pder.list.PushBack(newsess) element := pder.list.PushBack(newsess)
pder.sessions[sid] = element pder.sessions[sid] = element
pder.lock.Unlock() pder.lock.Unlock()
return newsess, nil return newsess, nil
} }
} }
// delete session store in memory session by id // delete session store in memory session by id
func (pder *MemProvider) SessionDestroy(sid string) error { func (pder *MemProvider) SessionDestroy(sid string) error {
pder.lock.Lock() pder.lock.Lock()
defer pder.lock.Unlock() defer pder.lock.Unlock()
if element, ok := pder.sessions[sid]; ok { if element, ok := pder.sessions[sid]; ok {
delete(pder.sessions, sid) delete(pder.sessions, sid)
pder.list.Remove(element) pder.list.Remove(element)
return nil return nil
} }
return nil return nil
} }
// clean expired session stores in memory session // clean expired session stores in memory session
func (pder *MemProvider) SessionGC() { func (pder *MemProvider) SessionGC() {
pder.lock.RLock() pder.lock.RLock()
for { for {
element := pder.list.Back() element := pder.list.Back()
if element == nil { if element == nil {
break break
} }
if (element.Value.(*MemSessionStore).timeAccessed.Unix() + pder.maxlifetime) < time.Now().Unix() { if (element.Value.(*MemSessionStore).timeAccessed.Unix() + pder.maxlifetime) < time.Now().Unix() {
pder.lock.RUnlock() pder.lock.RUnlock()
pder.lock.Lock() pder.lock.Lock()
pder.list.Remove(element) pder.list.Remove(element)
delete(pder.sessions, element.Value.(*MemSessionStore).sid) delete(pder.sessions, element.Value.(*MemSessionStore).sid)
pder.lock.Unlock() pder.lock.Unlock()
pder.lock.RLock() pder.lock.RLock()
} else { } else {
break break
} }
} }
pder.lock.RUnlock() pder.lock.RUnlock()
} }
// get count number of memory session // get count number of memory session
func (pder *MemProvider) SessionAll() int { func (pder *MemProvider) SessionAll() int {
return pder.list.Len() return pder.list.Len()
} }
// expand time of session store by id in memory session // expand time of session store by id in memory session
func (pder *MemProvider) SessionUpdate(sid string) error { func (pder *MemProvider) SessionUpdate(sid string) error {
pder.lock.Lock() pder.lock.Lock()
defer pder.lock.Unlock() defer pder.lock.Unlock()
if element, ok := pder.sessions[sid]; ok { if element, ok := pder.sessions[sid]; ok {
element.Value.(*MemSessionStore).timeAccessed = time.Now() element.Value.(*MemSessionStore).timeAccessed = time.Now()
pder.list.MoveToFront(element) pder.list.MoveToFront(element)
return nil return nil
} }
return nil return nil
} }
func init() { func init() {
Register("memory", mempder) Register("memory", mempder)
} }

View File

@ -143,14 +143,17 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
return nil, errs return nil, errs
} }
session, err = manager.provider.SessionRead(sid) session, err = manager.provider.SessionRead(sid)
cookie = &http.Cookie{Name: manager.config.CookieName, cookie = &http.Cookie{
Name: manager.config.CookieName,
Value: url.QueryEscape(sid), Value: url.QueryEscape(sid),
Path: "/", Path: "/",
HttpOnly: true, HttpOnly: true,
Secure: manager.config.Secure, Secure: manager.isSecure(r),
Domain: manager.config.Domain} Domain: manager.config.Domain,
if manager.config.CookieLifeTime >= 0 { }
if manager.config.CookieLifeTime > 0 {
cookie.MaxAge = manager.config.CookieLifeTime cookie.MaxAge = manager.config.CookieLifeTime
cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second)
} }
if manager.config.EnableSetCookie { if manager.config.EnableSetCookie {
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
@ -169,14 +172,17 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
return nil, err return nil, err
} }
session, err = manager.provider.SessionRead(sid) session, err = manager.provider.SessionRead(sid)
cookie = &http.Cookie{Name: manager.config.CookieName, cookie = &http.Cookie{
Name: manager.config.CookieName,
Value: url.QueryEscape(sid), Value: url.QueryEscape(sid),
Path: "/", Path: "/",
HttpOnly: true, HttpOnly: true,
Secure: manager.config.Secure, Secure: manager.isSecure(r),
Domain: manager.config.Domain} Domain: manager.config.Domain,
if manager.config.CookieLifeTime >= 0 { }
if manager.config.CookieLifeTime > 0 {
cookie.MaxAge = manager.config.CookieLifeTime cookie.MaxAge = manager.config.CookieLifeTime
cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second)
} }
if manager.config.EnableSetCookie { if manager.config.EnableSetCookie {
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
@ -231,7 +237,7 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque
Value: url.QueryEscape(sid), Value: url.QueryEscape(sid),
Path: "/", Path: "/",
HttpOnly: true, HttpOnly: true,
Secure: manager.config.Secure, Secure: manager.isSecure(r),
Domain: manager.config.Domain, Domain: manager.config.Domain,
} }
} else { } else {
@ -241,8 +247,9 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque
cookie.HttpOnly = true cookie.HttpOnly = true
cookie.Path = "/" cookie.Path = "/"
} }
if manager.config.CookieLifeTime >= 0 { if manager.config.CookieLifeTime > 0 {
cookie.MaxAge = manager.config.CookieLifeTime cookie.MaxAge = manager.config.CookieLifeTime
cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second)
} }
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
r.AddCookie(cookie) r.AddCookie(cookie)
@ -267,3 +274,17 @@ func (manager *Manager) sessionId(r *http.Request) (string, error) {
} }
return hex.EncodeToString(b), nil return hex.EncodeToString(b), nil
} }
// Set cookie with https.
func (manager *Manager) isSecure(req *http.Request) bool {
if !manager.config.Secure {
return false
}
if req.URL.Scheme != "" {
return req.URL.Scheme == "https"
}
if req.TLS == nil {
return false
}
return true
}

View File

@ -22,7 +22,6 @@ import (
"strings" "strings"
"github.com/astaxie/beego/context" "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
@ -59,7 +58,7 @@ func serverStaticRouter(ctx *context.Context) {
finfo, err := os.Stat(file) finfo, err := os.Stat(file)
if err != nil { if err != nil {
if RunMode == "dev" { if RunMode == "dev" {
Warn(err) Warn("Can't find the file:", file, err)
} }
http.NotFound(ctx.ResponseWriter, ctx.Request) http.NotFound(ctx.ResponseWriter, ctx.Request)
return return
@ -67,7 +66,7 @@ func serverStaticRouter(ctx *context.Context) {
//if the request is dir and DirectoryIndex is false then //if the request is dir and DirectoryIndex is false then
if finfo.IsDir() { if finfo.IsDir() {
if !DirectoryIndex { if !DirectoryIndex {
middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") exception("403", ctx)
return return
} else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { } else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' {
http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302) http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302)

View File

@ -42,6 +42,9 @@ func init() {
beegoTplFuncMap["dateformat"] = DateFormat beegoTplFuncMap["dateformat"] = DateFormat
beegoTplFuncMap["date"] = Date beegoTplFuncMap["date"] = Date
beegoTplFuncMap["compare"] = Compare beegoTplFuncMap["compare"] = Compare
beegoTplFuncMap["compare_not"] = CompareNot
beegoTplFuncMap["not_nil"] = NotNil
beegoTplFuncMap["not_null"] = NotNil
beegoTplFuncMap["substr"] = Substr beegoTplFuncMap["substr"] = Substr
beegoTplFuncMap["html2str"] = Html2str beegoTplFuncMap["html2str"] = Html2str
beegoTplFuncMap["str2html"] = Str2html beegoTplFuncMap["str2html"] = Str2html

View File

@ -139,6 +139,14 @@ func Compare(a, b interface{}) (equal bool) {
return return
} }
func CompareNot(a, b interface{}) (equal bool) {
return ! Compare(a, b)
}
func NotNil(a interface{}) (is_nil bool) {
return CompareNot(a, nil)
}
func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) { func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) {
switch returnType { switch returnType {
case "String": case "String":
@ -246,7 +254,7 @@ func Htmlunquote(src string) string {
// /user/John%20Doe // /user/John%20Doe
// //
// more detail http://beego.me/docs/mvc/controller/urlbuilding.md // more detail http://beego.me/docs/mvc/controller/urlbuilding.md
func UrlFor(endpoint string, values ...string) string { func UrlFor(endpoint string, values ...interface{}) string {
return BeeApp.Handlers.UrlFor(endpoint, values...) return BeeApp.Handlers.UrlFor(endpoint, values...)
} }
@ -350,11 +358,32 @@ func ParseForm(form url.Values, obj interface{}) error {
} }
fieldV.Set(reflect.ValueOf(t)) fieldV.Set(reflect.ValueOf(t))
} }
case reflect.Slice:
if fieldT.Type == sliceOfInts {
formVals := form[tag]
fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals)))
for i := 0; i < len(formVals); i++ {
val, err := strconv.Atoi(formVals[i])
if err != nil {
return err
}
fieldV.Index(i).SetInt(int64(val))
}
} else if fieldT.Type == sliceOfStrings {
formVals := form[tag]
fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals)))
for i := 0; i < len(formVals); i++ {
fieldV.Index(i).SetString(formVals[i])
}
}
} }
} }
return nil return nil
} }
var sliceOfInts = reflect.TypeOf([]int(nil))
var sliceOfStrings = reflect.TypeOf([]string(nil))
var unKind = map[reflect.Kind]bool{ var unKind = map[reflect.Kind]bool{
reflect.Uintptr: true, reflect.Uintptr: true,
reflect.Complex64: true, reflect.Complex64: true,

View File

@ -72,7 +72,7 @@ func TestDate(t *testing.T) {
} }
} }
func TestCompare(t *testing.T) { func TestCompareRelated(t *testing.T) {
if !Compare("abc", "abc") { if !Compare("abc", "abc") {
t.Error("should be equal") t.Error("should be equal")
} }
@ -82,6 +82,15 @@ func TestCompare(t *testing.T) {
if !Compare("1", 1) { if !Compare("1", 1) {
t.Error("should be equal") t.Error("should be equal")
} }
if CompareNot("abc", "abc") {
t.Error("should be equal")
}
if !CompareNot("abc", "aBc") {
t.Error("should be not equal")
}
if !NotNil("a string") {
t.Error("should not be nil")
}
} }
func TestHtmlquote(t *testing.T) { func TestHtmlquote(t *testing.T) {

View File

@ -84,6 +84,7 @@ type TaskFunc func() error
// task interface // task interface
type Tasker interface { type Tasker interface {
GetSpec() string
GetStatus() string GetStatus() string
Run() error Run() error
SetNext(time.Time) SetNext(time.Time)
@ -102,6 +103,7 @@ type taskerr struct {
type Task struct { type Task struct {
Taskname string Taskname string
Spec *Schedule Spec *Schedule
SpecStr string
DoFunc TaskFunc DoFunc TaskFunc
Prev time.Time Prev time.Time
Next time.Time Next time.Time
@ -116,16 +118,22 @@ func NewTask(tname string, spec string, f TaskFunc) *Task {
Taskname: tname, Taskname: tname,
DoFunc: f, DoFunc: f,
ErrLimit: 100, ErrLimit: 100,
SpecStr: spec,
} }
task.SetCron(spec) task.SetCron(spec)
return task return task
} }
//get spec string
func (s *Task) GetSpec() string {
return s.SpecStr
}
// get current task status // get current task status
func (tk *Task) GetStatus() string { func (tk *Task) GetStatus() string {
var str string var str string
for _, v := range tk.Errlist { for _, v := range tk.Errlist {
str += v.t.String() + ":" + v.errinfo + "\n" str += v.t.String() + ":" + v.errinfo + "<br>"
} }
return str return str
} }

45
tree.go
View File

@ -70,18 +70,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st
} else { } else {
regexpStr = "/" + regexpStr regexpStr = "/" + regexpStr
} }
} else { } else if reg != "" {
for _, w := range wildcards { if seg == "*.*" {
if w == "." || w == ":" { regexpStr = "([^.]+).(.+)"
continue } else {
for _, w := range params {
if w == "." || w == ":" {
continue
}
regexpStr = "([^/]+)/" + regexpStr
} }
regexpStr = "([^/]+)/" + regexpStr
} }
} }
reg = strings.Trim(reg+regexpStr, "/") reg = strings.Trim(reg+"/"+regexpStr, "/")
filterTreeWithPrefix(tree, append(wildcards, params...), reg) filterTreeWithPrefix(tree, append(wildcards, params...), reg)
t.wildcard = tree t.wildcard = tree
} else { } else {
reg = strings.Trim(reg+"/"+regexpStr, "/")
filterTreeWithPrefix(tree, append(wildcards, params...), reg) filterTreeWithPrefix(tree, append(wildcards, params...), reg)
t.fixrouters[seg] = tree t.fixrouters[seg] = tree
} }
@ -104,23 +109,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st
rr = rr + "([^/]+)/" rr = rr + "([^/]+)/"
} }
} }
regexpStr = rr + regexpStr + "/" regexpStr = rr + regexpStr
} else { } else {
regexpStr = "/" + regexpStr + "/" regexpStr = "/" + regexpStr
} }
} else { } else if reg != "" {
for _, w := range wildcards { if seg == "*.*" {
if w == "." || w == ":" { regexpStr = "([^.]+).(.+)"
continue } else {
} for _, w := range params {
if w == ":splat" { if w == "." || w == ":" {
regexpStr = "(.+)/" + regexpStr continue
} else { }
regexpStr = "([^/]+)/" + regexpStr regexpStr = "([^/]+)/" + regexpStr
} }
} }
} }
reg = reg + regexpStr reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/")
t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg) t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg)
} else { } else {
subTree := NewTree() subTree := NewTree()
@ -140,7 +145,7 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) {
if reg != "" { if reg != "" {
if l.regexps != nil { if l.regexps != nil {
l.wildcards = append(wildcards, l.wildcards...) l.wildcards = append(wildcards, l.wildcards...)
l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$") l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$")
} else { } else {
for _, v := range l.wildcards { for _, v := range l.wildcards {
if v == ":" || v == "." { if v == ":" || v == "." {
@ -248,7 +253,6 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string,
regexpStr = "/([^/]+)" + regexpStr regexpStr = "/([^/]+)" + regexpStr
} }
} }
} }
t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr) t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr)
} else { } else {
@ -422,6 +426,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
// "/admin/" -> ["admin"] // "/admin/" -> ["admin"]
// "/admin/users" -> ["admin", "users"] // "/admin/users" -> ["admin", "users"]
func splitPath(key string) []string { func splitPath(key string) []string {
if key == "" {
return []string{}
}
elements := strings.Split(key, "/") elements := strings.Split(key, "/")
if elements[0] == "" { if elements[0] == "" {
elements = elements[1:] elements = elements[1:]

View File

@ -148,8 +148,56 @@ func TestAddTree2(t *testing.T) {
} }
} }
func TestAddTree3(t *testing.T) {
tr := NewTree()
tr.AddRouter("/create", "astaxie")
tr.AddRouter("/shop/:sd/account", "astaxie")
t3 := NewTree()
t3.AddTree("/table/:num", tr)
obj, param := t3.Match("/table/123/shop/123/account")
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/table/:num/shop/:sd/account can't get obj ")
}
if param == nil {
t.Fatal("get param error")
}
if param[":num"] != "123" || param[":sd"] != "123" {
t.Fatal("get :num :sd param error")
}
obj, param = t3.Match("/table/123/create")
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/table/:num/create can't get obj ")
}
}
func TestAddTree4(t *testing.T) {
tr := NewTree()
tr.AddRouter("/create", "astaxie")
tr.AddRouter("/shop/:sd/:account", "astaxie")
t4 := NewTree()
t4.AddTree("/:info:int/:num/:id", tr)
obj, param := t4.Match("/12/123/456/shop/123/account")
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ")
}
if param == nil {
t.Fatal("get param error")
}
if param[":info"] != "12" || param[":num"] != "123" || param[":id"] != "456" || param[":sd"] != "123" || param[":account"] != "account" {
t.Fatal("get :info :num :id :sd :account param error")
}
obj, param = t4.Match("/12/123/456/create")
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/:info:int/:num/:id/create can't get obj ")
}
}
func TestSplitPath(t *testing.T) { func TestSplitPath(t *testing.T) {
a := splitPath("/") a := splitPath("")
if len(a) != 0 {
t.Fatal("/ should retrun []")
}
a = splitPath("/")
if len(a) != 0 { if len(a) != 0 {
t.Fatal("/ should retrun []") t.Fatal("/ should retrun []")
} }

19
utils/captcha/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2011-2014 Dmitry Chestnykh <dmitry@codingrobots.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -132,15 +132,6 @@ func (c *Captcha) Handler(ctx *context.Context) {
key := c.key(id) key := c.key(id)
if v, ok := c.store.Get(key).([]byte); ok {
chars = v
} else {
ctx.Output.SetStatus(404)
ctx.WriteString("captcha not found")
return
}
// reload captcha
if len(ctx.Input.Query("reload")) > 0 { if len(ctx.Input.Query("reload")) > 0 {
chars = c.genRandChars() chars = c.genRandChars()
if err := c.store.Put(key, chars, c.Expiration); err != nil { if err := c.store.Put(key, chars, c.Expiration); err != nil {
@ -149,6 +140,14 @@ func (c *Captcha) Handler(ctx *context.Context) {
beego.Error("Reload Create Captcha Error:", err) beego.Error("Reload Create Captcha Error:", err)
return return
} }
} else {
if v, ok := c.store.Get(key).([]byte); ok {
chars = v
} else {
ctx.Output.SetStatus(404)
ctx.WriteString("captcha not found")
return
}
} }
img := NewImage(chars, c.StdWidth, c.StdHeight) img := NewImage(chars, c.StdWidth, c.StdHeight)

View File

@ -292,7 +292,7 @@ func qpEscape(dest []byte, c byte) {
const nums = "0123456789ABCDEF" const nums = "0123456789ABCDEF"
dest[0] = '=' dest[0] = '='
dest[1] = nums[(c&0xf0)>>4] dest[1] = nums[(c&0xf0)>>4]
dest[2] = nums[(c&0xf)] dest[2] = nums[(c & 0xf)]
} }
// headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer // headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer

View File

@ -21,6 +21,6 @@ import (
// Instantiates a Paginator and assigns it to context.Input.Data["paginator"]. // Instantiates a Paginator and assigns it to context.Input.Data["paginator"].
func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) { func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) {
paginator = NewPaginator(context.Request, per, nums) paginator = NewPaginator(context.Request, per, nums)
context.Input.Data["paginator"] = paginator context.Input.Data["paginator"] = &paginator
return return
} }

View File

@ -92,18 +92,18 @@ func (p *Paginator) Pages() []int {
case page >= pageNums-4 && pageNums > 9: case page >= pageNums-4 && pageNums > 9:
start := pageNums - 9 + 1 start := pageNums - 9 + 1
pages = make([]int, 9) pages = make([]int, 9)
for i, _ := range pages { for i := range pages {
pages[i] = start + i pages[i] = start + i
} }
case page >= 5 && pageNums > 9: case page >= 5 && pageNums > 9:
start := page - 5 + 1 start := page - 5 + 1
pages = make([]int, int(math.Min(9, float64(page+4+1)))) pages = make([]int, int(math.Min(9, float64(page+4+1))))
for i, _ := range pages { for i := range pages {
pages[i] = start + i pages[i] = start + i
} }
default: default:
pages = make([]int, int(math.Min(9, float64(pageNums)))) pages = make([]int, int(math.Min(9, float64(pageNums))))
for i, _ := range pages { for i := range pages {
pages[i] = i + 1 pages[i] = i + 1
} }
} }
@ -114,7 +114,7 @@ func (p *Paginator) Pages() []int {
// Returns URL for a given page index. // Returns URL for a given page index.
func (p *Paginator) PageLink(page int) string { func (p *Paginator) PageLink(page int) string {
link, _ := url.ParseRequestURI(p.Request.RequestURI) link, _ := url.ParseRequestURI(p.Request.URL.String())
values := link.Query() values := link.Query()
if page == 1 { if page == 1 {
values.Del("p") values.Del("p")

View File

@ -51,7 +51,7 @@ func SliceRandList(min, max int) []int {
t0 := time.Now() t0 := time.Now()
rand.Seed(int64(t0.Nanosecond())) rand.Seed(int64(t0.Nanosecond()))
list := rand.Perm(length) list := rand.Perm(length)
for index, _ := range list { for index := range list {
list[index] += min list[index] += min
} }
return list return list

View File

@ -137,7 +137,7 @@ func getRegFuncs(tag, key string) (vfs []ValidFunc, str string, err error) {
if err != nil { if err != nil {
return return
} }
vfs = []ValidFunc{ValidFunc{"Match", []interface{}{reg, key + ".Match"}}} vfs = []ValidFunc{{"Match", []interface{}{reg, key + ".Match"}}}
str = strings.TrimSpace(tag[:index]) + strings.TrimSpace(tag[end+len("/)"):]) str = strings.TrimSpace(tag[:index]) + strings.TrimSpace(tag[end+len("/)"):])
return return
} }

View File

@ -107,6 +107,7 @@ type Validation struct {
// Clean all ValidationError. // Clean all ValidationError.
func (v *Validation) Clear() { func (v *Validation) Clear() {
v.Errors = []*ValidationError{} v.Errors = []*ValidationError{}
v.ErrorsMap = nil
} }
// Has ValidationError nor not. // Has ValidationError nor not.
@ -333,3 +334,41 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) {
return !v.HasErrors(), nil return !v.HasErrors(), nil
} }
// Recursively validate a struct.
// Step1: Validate by v.Valid
// Step2: If pass on step1, then reflect obj's fields
// Step3: Do the Recursively validation to all struct or struct pointer fields
func (v *Validation) RecursiveValid(objc interface{}) (bool, error) {
//Step 1: validate obj itself firstly
// fails if objc is not struct
pass, err := v.Valid(objc)
if err != nil || false == pass {
return pass, err // Stop recursive validation
} else { //pass
// Step 2: Validate struct's struct fields
objT := reflect.TypeOf(objc)
objV := reflect.ValueOf(objc)
if isStructPtr(objT) {
objT = objT.Elem()
objV = objV.Elem()
}
for i := 0; i < objT.NumField(); i++ {
t := objT.Field(i).Type
// Recursive applies to struct or pointer to structs fields
if isStruct(t) || isStructPtr(t) {
// Step 3: do the recursive validation
// Only valid the Public field recursively
if objV.Field(i).CanInterface() {
pass, err = v.RecursiveValid(objV.Field(i).Interface())
}
}
}
return pass, err
}
}

View File

@ -26,6 +26,12 @@ func TestRequired(t *testing.T) {
if valid.Required(nil, "nil").Ok { if valid.Required(nil, "nil").Ok {
t.Error("nil object should be false") t.Error("nil object should be false")
} }
if !valid.Required(true, "bool").Ok {
t.Error("Bool value should always return true")
}
if !valid.Required(false, "bool").Ok {
t.Error("Bool value should always return true")
}
if valid.Required("", "string").Ok { if valid.Required("", "string").Ok {
t.Error("\"'\" string should be false") t.Error("\"'\" string should be false")
} }
@ -343,3 +349,33 @@ func TestValid(t *testing.T) {
t.Errorf("Message key should be `Name.Match` but got %s", valid.Errors[0].Key) t.Errorf("Message key should be `Name.Match` but got %s", valid.Errors[0].Key)
} }
} }
func TestRecursiveValid(t *testing.T) {
type User struct {
Id int
Name string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"`
Age int `valid:"Required;Range(1, 140)"`
}
type AnonymouseUser struct {
Id2 int
Name2 string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"`
Age2 int `valid:"Required;Range(1, 140)"`
}
type Account struct {
Password string `valid:"Required"`
U User
AnonymouseUser
}
valid := Validation{}
u := Account{Password: "abc123_", U: User{}}
b, err := valid.RecursiveValid(u)
if err != nil {
t.Fatal(err)
}
if b {
t.Error("validation should not be passed")
}
}

View File

@ -64,8 +64,8 @@ func (r Required) IsSatisfied(obj interface{}) bool {
if str, ok := obj.(string); ok { if str, ok := obj.(string); ok {
return len(str) > 0 return len(str) > 0
} }
if b, ok := obj.(bool); ok { if _, ok := obj.(bool); ok {
return b return true
} }
if i, ok := obj.(int); ok { if i, ok := obj.(int); ok {
return i != 0 return i != 0
@ -457,7 +457,7 @@ func (b Base64) GetLimitValue() interface{} {
} }
// just for chinese mobile phone number // just for chinese mobile phone number
var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|(47)|[8][012356789]))\\d{8}$") var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][67]|[4][579]))\\d{8}$")
type Mobile struct { type Mobile struct {
Match Match