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

779 Commits

Author SHA1 Message Date
56d89cc55c Merge pull request #8 from flycash/fix4734-1x
do not reset id to 0
2021-08-20 19:54:42 +08:00
e048377594 do not reset id to 0 2021-08-20 19:53:51 +08:00
34334a52be Merge pull request #7 from flycash/fix4734-1x
Fix 4734: we don't need to reset the id to 0
2021-08-19 21:52:16 +08:00
7cdf96d21a Fix 4734: we don't need to reset the id to 0 2021-08-19 21:49:37 +08:00
fad897346f Merge pull request #4325 from flycash/revert1
Revert "Merge pull request #4254 from astaxie/develop-2.0"
2020-11-26 20:18:35 +08:00
650fde66aa Revert "Merge pull request #4254 from astaxie/develop-2.0"
This reverts commit e284b0ddae, reversing
changes made to 8ef8fd2606.
2020-11-26 17:48:29 +08:00
e284b0ddae Merge pull request #4254 from astaxie/develop-2.0
Prepare Release v2.0.0-beta
2020-11-07 21:20:18 +08:00
20a0de6bd0 Merge pull request #4290 from flycash/develop-2.0
fix init error of global instance
2020-11-05 23:51:52 +08:00
b4396c97bb fix init error of global instance 2020-11-05 22:00:43 +08:00
471ebba64d Merge pull request #4281 from flycash/httplibTest
Add test for httplib
2020-10-27 22:08:35 +08:00
d07a1eaa8e Add test for httplib 2020-10-27 21:58:39 +08:00
9524036aab Merge pull request #4278 from flycash/defaultCfg
Add global instance for config module
2020-10-24 22:27:12 +08:00
45260e4119 Add global instance for config module 2020-10-24 22:13:15 +08:00
02234dc503 Merge pull request #4277 from flycash/session
support using json string to init session
2020-10-22 09:53:55 +08:00
05f4e0c146 support using json string to init session 2020-10-21 22:24:53 +08:00
ae108ec826 Merge pull request #4276 from flycash/newHttpServer
Change NewHttpServer API
2020-10-21 21:59:45 +08:00
7c61eb058f Change NewHttpServer API 2020-10-21 20:54:33 +08:00
03ba495b7f Merge pull request #4275 from flycash/fix-4224
add MaxUploadFile to provide more safety uploading controll
2020-10-21 10:04:32 +08:00
d26683799a add MaxUploadFile to provide more safety uploading controll 2020-10-20 23:36:34 +08:00
f9075e8274 Merge pull request #4272 from jianzhiyao/fix-4224
fix 4224:form entity too large casue run out of memory
2020-10-20 20:53:19 +08:00
022ad862ac Merge pull request #4274 from flycash/loadCfg
Fix ini Unmarshall method
2020-10-19 21:51:37 +08:00
93bdf97068 Fix ini Unmarshall method 2020-10-19 21:04:57 +08:00
3c48719999 complete condition 2020-10-19 00:22:55 +08:00
cbb3de741d fix application/x-www-form-urlencoded request body oversize 2020-10-18 23:38:08 +08:00
c510926cb8 fix 4224:form entity too large casue run out of memory 2020-10-18 23:18:13 +08:00
140a4b90a3 Merge pull request #4266 from flycash/loadCfg
Using unmarshaler to parse config in web module
2020-10-14 22:26:12 +08:00
c07acaebbc Support unmarshaler 2020-10-14 22:20:25 +08:00
105b874477 Merge pull request #4265 from flycash/rft/configCtx
Upgrade toml version
2020-10-14 00:28:30 +08:00
3fc21ae6ec Upgrade toml version 2020-10-14 00:25:31 +08:00
ccf873fa8b Merge pull request #4264 from flycash/rft/configCtx
remove config API's context parameter
2020-10-13 22:52:46 +08:00
2572094a8d remove config API's context parameter 2020-10-13 22:33:39 +08:00
568626cd57 Merge pull request #4262 from flycash/ftr/toml
Support toml config
2020-10-12 22:55:55 +08:00
34d6a733e9 Support toml config 2020-10-11 23:26:48 +08:00
e44f16c672 Merge pull request #4257 from flycash/fix/adapter
Remove scripts directory & update readme
2020-10-09 08:48:08 +08:00
d41abdb5e4 Remove scripts directory; update readme 2020-10-08 23:18:10 +08:00
f1358cf78d Merge pull request #4255 from flycash/fix/adapter
Reorganize packages
2020-10-08 18:37:42 +08:00
14c1b76569 remove pkg directory;
remove build directory;
remove githook directory;
2020-10-08 18:29:36 +08:00
2708916f96 Merge pull request #4252 from flycash/fix/adapter
Reset func call depth & add adapter.sh
2020-10-06 18:33:45 +08:00
034cb3222e Add adapter script which is used to replace v1 package with v2 adapter package 2020-10-06 16:43:16 +08:00
66804324f2 Fix: Set func call depth as 3 2020-10-06 11:52:24 +08:00
dc65055cf6 Merge pull request #4250 from flycash/adt/logs
logs Adapter
2020-10-05 23:26:55 +08:00
8cc74652a2 Fix: adapter's controller must implement ControllerInterface 2020-10-05 23:00:20 +08:00
6aa6c55f07 logs Adapter 2020-10-05 21:55:26 +08:00
ff762b561c Merge pull request #4249 from flycash/rft/moveSession
move core/session to web/session
2020-10-05 19:45:38 +08:00
d8e8f41230 move core/session to web/session 2020-10-05 19:04:57 +08:00
9e6b8fcf34 Merge pull request #4248 from flycash/rft/renameInfra
rename infrastructure to core
2020-10-05 18:40:20 +08:00
48e98482f7 rename infrastructure to core 2020-10-05 18:14:01 +08:00
ff7a8b966b Merge pull request #4247 from flycash/adt/cache
Adapter: cache API
2020-10-05 14:46:13 +08:00
f9bef68aa9 Adapter: cache API 2020-10-05 14:35:12 +08:00
484beb8bad Merge pull request #4245 from flycash/ftr/cache-ctx
Add context to cache API
2020-10-05 14:25:09 +08:00
43560dede4 Merge pull request #4246 from jianzhiyao/frt/seperate_orm_alone
seperate orm alone & deadlock in task module
2020-10-05 14:24:51 +08:00
c435d231ab complete check 2020-10-05 10:38:14 +08:00
b838683731 add api for testing 2020-10-05 10:33:23 +08:00
70cca5e298 make code testable in task module 2020-10-05 10:13:29 +08:00
f1cca45d8d fix deadlock about changed sign 2020-10-05 01:31:27 +08:00
4dc694411f fix deadlock in task module 2020-10-05 00:16:58 +08:00
3364c609de Add context to cache API 2020-10-04 23:12:29 +08:00
c5d43e87fe seperate orm alone 2020-10-04 22:16:19 +08:00
b89d9511ab Merge pull request #4237 from flycash/rft/cache-decup
Decouple web module from cache module
2020-10-04 19:01:31 +08:00
325a0821c1 Merge pull request #4239 from flycash/rft/httplib
decouple httplib module from web module and config module
2020-09-29 22:22:27 +08:00
dd3f1ce9be decouple httplib from config 2020-09-27 00:44:02 +08:00
463e96447a decouple httplib from web module 2020-09-27 00:37:46 +08:00
03498529b9 Decouple web module from cache module 2020-09-22 22:58:58 +08:00
9a7c43c404 Merge pull request #4235 from flycash/ftr/adminCommand
decouple web module and task module
2020-09-21 22:50:08 +08:00
44127edefc design Command for governor module & decouple web module from task module 2020-09-20 14:52:29 +00:00
089006525e Merge pull request #4234 from flycash/ftr/multi-sever
Multi server support
2020-09-20 22:44:32 +08:00
2846043f2a Fix UT 2020-09-20 14:27:30 +00:00
e6a257f987 Fix BUG 2020-09-20 12:02:28 +00:00
2473e69417 Rewrite admin service by using multiple server feature 2020-09-20 15:36:53 +08:00
d455805a0a Multiple server refactor 2020-09-20 15:36:53 +08:00
bd1cfefec7 rft: Move build info to pkg 2020-09-20 15:36:53 +08:00
7effdb0e7d Merge pull request #4233 from flycash/esIndexName
Add IndexNaming interface
2020-09-20 14:22:32 +08:00
b027968c0b Merge pull request #4231 from flycash/ut/log
Add tests for log module
2020-09-20 13:50:22 +08:00
961f300c14 Fix JL tests 2020-09-20 13:15:48 +08:00
7570abd310 Merge pull request #4232 from flycash/stale-workflows
Add stale.yml
2020-09-19 23:57:40 +08:00
7c8136710c Add stale.yml 2020-09-19 23:54:33 +08:00
a3ece98cec Add IndexNaming interface so users can custom the index name when they use es as the logger 2020-09-19 23:49:52 +08:00
a31dce6216 Merge pull request #4230 from jianzhiyao/frt/movement_for_4198
movement for 4198
2020-09-19 23:24:57 +08:00
a1782cc22d Add tests for log module 2020-09-19 23:14:17 +08:00
67f64afa85 movement for 4198 2020-09-19 21:45:37 +08:00
2539fe3831 Merge pull request #4229 from wangle201210/develop-2.0
Provides a quick format method by PatternLogFormatter struct
2020-09-19 21:25:12 +08:00
05c125ec2d change to pointer receiver 2020-09-19 20:18:09 +08:00
6e638ef6c8 Provides a quick format method by PatternLogFormatter struct 2020-09-19 18:28:53 +08:00
df043f22fc Merge pull request #4225 from HITWHTigerLiu/develop-2.0
Empty field in validator.Error when label struct tag is not declared #4222
2020-09-17 21:27:18 +08:00
fbaf3380c6 Merge pull request #4203 from jianzhiyao/frt/proposal_4105
warpping for global modelCache
2020-09-17 21:11:33 +08:00
b7bc57c4d1 delete interface 2020-09-16 19:46:14 +08:00
32cdda96bd Merge pull request #4205 from AllenX2018/postgres-query-builder
PostgresQueryBuilder
2020-09-14 21:21:59 +08:00
5995b00fa2 Merge branch 'develop-2.0' into frt/proposal_4105 2020-09-14 19:16:09 +08:00
5618df8c76 Empty field in validator.Error when label struct tag is not declared #4222 2020-09-14 16:18:24 +08:00
c6c9ad46f9 PostgresQueryBuilder 2020-09-14 09:50:28 +08:00
5973ef107c Merge pull request #4221 from flycash/ftr/log_format
fix 4219
2020-09-12 00:02:51 +08:00
b575fa1ebe fix 4219 2020-09-11 23:48:45 +08:00
6bbca96c6c Merge pull request #4220 from flycash/ftr/log_format
refactor log module
2020-09-11 22:08:05 +08:00
63cd8e4e15 refactor log module 2020-09-11 21:47:22 +08:00
93736a8e66 Merge pull request #4216 from flycash/ftr/log_format
Log format support
2020-09-11 00:11:27 +08:00
654d87b210 Merge log_format 2020-09-10 23:31:49 +08:00
0048b7d158 Merge pull request #4188 from IamCathal/custom-log-formatter-third-pr
Custom logging format PR#3
2020-09-10 22:40:48 +08:00
00e44952ff optimize modelCache 2020-09-09 19:04:34 +08:00
8982f5d702 Add unit tests for custom log formatter
Also moved is Colorful check to WriteMsg function to make the interface for user's using the custom logging formatting simpler. The user does not have to check if the text is colorful now, the WriteMsg function handles it.
2020-09-09 00:23:57 +01:00
6612bc4c2a Merge pull request #4214 from flycash/rft/dbOption
Optimize orm by using BDOption rather than hints
2020-09-09 00:06:44 +08:00
f580a714d5 Optimize orm by using BDOption rather than hints 2020-09-08 21:44:14 +08:00
9ccd58bfff Merge pull request #4211 from flycash/adt/all
allow users to ignore some table when run orm commands
2020-09-07 21:52:39 +08:00
0f50b07a20 allow users to ignore some table when run orm commands 2020-09-07 21:40:44 +08:00
b86cf22fc4 Merge pull request #4210 from flycash/adt/all
Move pr 3784
2020-09-07 20:53:45 +08:00
6bf01eaeca Move pr 3784 here 2020-09-07 20:37:05 +08:00
8e015deee5 Merge pull request #4208 from flycash/adt/all
Adapter: all module
2020-09-06 19:58:46 +08:00
3acda41bc7 Fix UT 2020-09-06 10:39:20 +00:00
5b3dd7e50f Adapter: orm 2020-09-06 13:33:52 +08:00
f4a43814be Adapter: utils 2020-09-05 18:07:42 +08:00
35f1bd2119 Adapter: testing 2020-09-05 16:58:49 +08:00
f6c95ad534 Adapter: swagger module 2020-09-05 16:56:56 +08:00
f1950482c2 Adapter: plugin 2020-09-05 16:54:22 +08:00
1dae2c9eb3 Adapter: web module 2020-09-05 16:24:19 +08:00
8ef9965eef Adapter: session module 2020-09-03 23:37:07 +08:00
3530457ff9 Adapter: toolbox module 2020-09-03 21:34:57 +08:00
cbd51616f1 adapter: validation module 2020-09-02 23:23:48 +08:00
bdd8df6751 adapt migration 2020-09-02 21:01:54 +08:00
8fc4f8847c adapt grace and metric 2020-09-02 20:43:35 +08:00
3bf5cde38c adapt context 2020-09-02 20:36:53 +08:00
7a53baaf9b rename modelRegister to modelCacheHandler 2020-09-02 00:33:46 +08:00
7574b91760 add type modelRegister interface into Ormer 2020-09-02 00:26:25 +08:00
78d91062c9 Adapt new API to old API: httplib 2020-09-01 22:16:49 +08:00
23792401b5 Merge pull request #4201 from jianzhiyao/frt/proposal_4105
WIP:supports for proposal 4105
2020-09-01 22:05:15 +08:00
e54dbabf0b movement for global modelCache 2020-09-01 21:56:48 +08:00
cdc8110ea4 Merge pull request #4202 from flycash/adt/config
Using new API to adapt to old API
2020-09-01 21:50:08 +08:00
185d55eb46 adapt config 2020-09-01 21:29:26 +08:00
8e879726fe Merge pull request #4200 from flycash/ftr/config-sub
Implement Sub, Unmarshaler and OnChange methods for yaml, json, xml
2020-08-31 22:29:14 +08:00
33b052bc7a support json 2020-08-31 14:15:01 +00:00
087399c44a support xml 2020-08-31 13:57:26 +00:00
f4f200cf04 enhance yaml 2020-08-31 13:02:38 +00:00
0a58428220 Merge pull request #4199 from flycash/rft/task-api
Add ctx to Task module API
2020-08-31 19:43:53 +08:00
c0462f75bf Add ctx to Task module API 2020-08-30 16:18:59 +00:00
5cf33f2655 Merge pull request #4197 from flycash/rft/session-api
Add ctx to session API
2020-08-31 00:14:52 +08:00
670064686e Add ctx to session API 2020-08-30 15:39:07 +00:00
0019e0fc1b Merge pull request #4195 from flycash/ftr/etcd
Add contect as first parameter for all config method
2020-08-30 00:42:15 +08:00
03bec05714 Add contect as first parameter for all config method 2020-08-29 16:25:20 +00:00
e831b97eb8 Merge pull request #4194 from flycash/ftr/etcd
Support etcd
2020-08-29 22:44:55 +08:00
81b9a1382a Fix UT 2020-08-29 14:27:06 +00:00
0189e6329a Add global logging override 2020-08-28 18:47:28 +01:00
6684924e99 empty commit to restart CI again 2020-08-28 18:30:41 +01:00
e0a934af1d empty commit to restart CI 2020-08-28 18:24:57 +01:00
8178f035a0 Custom formatting opts implementation 2020-08-28 18:18:28 +01:00
2b39ff7837 New opts formatter working for console 2020-08-28 18:00:45 +01:00
c2361170b3 Support etcd 2020-08-29 00:07:33 +08:00
5b35bf6065 Merge pull request #4192 from AllenX2018/supplement-datetimePrecision-UT
supplement datetime precision UT
2020-08-26 19:52:19 +08:00
14c911e9d7 Merge pull request #4190 from flycash/ftr/time-precision
Support precision
2020-08-26 12:45:17 +08:00
9472cba6c9 Fix UT 2020-08-26 04:16:09 +00:00
b83094ac1e supplement datetime precision UT 2020-08-26 11:51:05 +08:00
1cb0ff560d Support precision 2020-08-25 13:07:21 +00:00
cceecad8c2 Merge pull request #4186 from AllenX2018/Feature-datetime-precision
Feature: implement the time precison for time.Time type
2020-08-25 21:05:08 +08:00
d24f861629 empty commit to restart CI 2020-08-24 21:00:58 +01:00
c2471b22ad Remove ineffectual assignments
Removed 3 lines due to warning from test suite saying these lines had innefectual assignments
2020-08-24 20:54:55 +01:00
c5970766a3 Add init to es.go 2020-08-24 20:41:39 +01:00
48a98ec1a5 Fix init for alils.go 2020-08-24 20:39:53 +01:00
ed1d2c7f6e Add custom logging format functionality and global formatter functionality 2020-08-24 20:22:38 +01:00
7a94996e22 Feature: implement the time precison for time.Time type 2020-08-24 20:23:54 +08:00
597c55d547 Merge pull request #4183 from flycash/rft/module-2
Reorganize package
2020-08-23 21:43:48 +08:00
026e8bc55a Merge branch 'develop-2.0' of https://github.com/astaxie/beego into rft/module-2 2020-08-23 21:25:15 +08:00
581e48679e Merge pull request #4173 from AllenX2018/fix-bug-queryRow
Fix issue 3866
2020-08-23 21:04:34 +08:00
09afe0ae8e Merge pull request #4179 from IamCathal/custom-log-formatter-secondpr
Custom log formatter secondpr
2020-08-22 21:36:50 +08:00
08e49ca323 Test empty commit 2020-08-20 19:32:42 +01:00
e1da804b2b Add format func to alils 2020-08-20 19:20:30 +01:00
705e091593 Add format call before logging 2020-08-20 19:06:51 +01:00
6bdedff457 LogFormatter Implementation 2020-08-20 19:00:35 +01:00
f3be6dd2e9 Merge pull request #4173 from AllenX2018/fix-bug-queryRow
Fix issue 3866
2020-08-20 22:25:37 +08:00
9fe353dd0b Fix issue 3886 2020-08-20 10:00:49 +08:00
9003ca3eef Merge pull request #4174 from IamCathal/custom-log-formatter-firstpr
Custom Log Formatter PR#1
2020-08-19 23:37:44 +08:00
ff5ac3adf4 Update signature of WriteMsg in es.go 2020-08-19 16:20:19 +01:00
ca4a217783 Merge branch 'develop-2.0' into custom-log-formatter-firstpr 2020-08-19 16:05:42 +01:00
e6ea307549 Merge pull request #4175 from flycash/ftr/config
Add more methods to Configer
2020-08-19 22:44:43 +08:00
2c16c7b917 Add more methods to Configer 2020-08-19 22:09:05 +08:00
77ddc3338f Fix file path logging for enableFullFilePath 2020-08-19 15:07:46 +01:00
ac3a549187 Fix test with new parameters 2020-08-19 14:21:29 +01:00
fe56de06b5 Add enableFullFilePath field to BeeLogger 2020-08-18 21:30:39 +01:00
6c002a3124 Update WriteMsg signatures for custom log formatting update 2020-08-18 21:30:11 +01:00
cead72c6df Merge pull request #4170 from flycash/rft/ormFilter
Refactor orm filter
2020-08-18 22:46:45 +08:00
7fe4eaef50 Refactor orm filter 2020-08-18 14:31:06 +00:00
63599c0032 Merge pull request #4168 from flycash/ftr/layout
Add git hooks
2020-08-18 21:08:56 +08:00
4db256c9fb Add git hooks 2020-08-18 20:56:15 +08:00
c548764c8e Merge pull request #4163 from flycash/ftr/rmFiles
Remove files
2020-08-16 23:17:05 +08:00
b4a85c8f13 Remove files 2020-08-16 23:09:19 +08:00
94f476fa39 Merge pull request #4158 from jianzhiyao/frt/fix_spk_upsert
fix:return error when calling ``InsertOrUpdate`` is successful with string primary key
2020-08-16 21:21:25 +08:00
8574e30b3a Merge pull request #4160 from AllenX2018/Improve-orm.Fielder-function
fix issue #3776
2020-08-14 22:20:07 +08:00
7442919f5a fix issue #3776 2020-08-14 17:11:52 +08:00
f6ec4efc70 Merge pull request #4156 from flycash/ftr/bean
Supporting default value by using filter
2020-08-14 15:47:50 +08:00
7b899aa9af add ErrLastInsertIdUnavailable 2020-08-14 15:09:47 +08:00
f73eee75ff Merge develop-2.0 2020-08-14 14:34:59 +08:00
739b8bab0c fix UT 2020-08-14 10:31:08 +08:00
139c393f08 add const ErrLastInsertIdUnavailable 2020-08-14 09:59:11 +08:00
bdec93986b Bean: Support autowire by tag
Orm: Support default value filter
2020-08-13 21:26:39 +08:00
7ce0fde171 fix:return error when calling `InsertOrUpdate` is successful with string primary key 2020-08-13 19:14:00 +08:00
d6a2621b3c Merge pull request #4137 from phiphi282/change_redis_provider
Add additional options to redis session prov
2020-08-12 13:38:44 +08:00
813a4df3c5 Make sure expiry time is in seconds 2020-08-11 16:21:43 +02:00
7267f5e573 Add new config option into provider struct 2020-08-11 16:09:29 +02:00
5bc8d90d7f Merge pull request #4150 from jianzhiyao/frt/fix_3830
fix:return error after inserting data when primary key is string
2020-08-11 21:22:17 +08:00
2d1c02e1c1 Merge branch 'develop-2.0' of https://github.com/astaxie/beego into frt/fix_3830
# Conflicts:
#	pkg/orm/orm_test.go
2020-08-11 17:37:24 +08:00
9ca9535c48 fix:return error after inserting data when primary key is string 2020-08-11 16:53:31 +08:00
d1d9df74c7 Merge branch 'develop-2.0' of github.com:astaxie/beego into change_redis_provider 2020-08-11 10:47:06 +02:00
d326d74c34 Merge pull request #4148 from flycash/fix/end2end
Fix prometheus and opentracing bug found in end2end tests
2020-08-11 16:33:54 +08:00
82178a487b Merge develop-2.0 and resolve conflict 2020-08-11 08:16:04 +00:00
a1b7fd3c93 Merge pull request #4147 from jianzhiyao/frt/specify_index_2
specify index
2020-08-11 16:13:13 +08:00
0813471202 Merge branch 'develop-2.0' of github.com:astaxie/beego into change_redis_provider 2020-08-11 09:52:53 +02:00
ce698aacf6 rm some methods 2020-08-11 12:06:02 +08:00
c22af4c611 Fix Tracing and prometheus bug 2020-08-11 03:23:38 +00:00
f8c0e6fec5 fix UT 2020-08-11 00:06:36 +08:00
882f1273c8 add UT for specifying indexes 2020-08-10 23:27:03 +08:00
5a1fa4e1ec specify index 2020-08-10 18:46:16 +08:00
d05460237c Merge pull request #4145 from flycash/ftr/ormTracing
Support prometheus and opentracing for ORM and httplib module
2020-08-10 13:53:23 +08:00
75107f735e Support opentracing filter 2020-08-09 14:59:41 +00:00
2e891152dd deprecated httplib and then support prometheus for httplib 2020-08-09 14:59:41 +00:00
dec98f004c Support opentracing filter for Orm 2020-08-09 14:59:41 +00:00
26b016a3a4 Merge pull request #4143 from flycash/fix/moveIni
Move init so it will be default implementation of config
2020-08-09 22:51:47 +08:00
19aae0b7e1 Merge pull request #4107 from AllenX2018/fix-comment-router-issue
add comment router path configuration
2020-08-09 19:23:05 +08:00
2e192e1ed0 Depracated config module and recommend using pkg/config 2020-08-08 13:26:30 +00:00
f9a3eae9d5 Move init so it will be default implementation of config 2020-08-08 13:17:49 +00:00
7fc1e4de96 Merge pull request #4141 from flycash/ftr/ormInterceptor
Orm filter support
2020-08-08 20:52:37 +08:00
993ccac2bd fix comment router generate issue 2020-08-08 16:17:12 +08:00
2fd65a469c Support prometheus 2020-08-07 14:14:07 +00:00
a2fa073072 Merge pull request #4139 from IamCathal/coc-grammar-fixes
More minor grammar fixes
2020-08-07 22:00:22 +08:00
08cec9178f Orm filter support 2020-08-07 13:45:24 +00:00
63b3fc4a99 Fix retry amount comment 2020-08-06 16:09:06 +01:00
1b4bb43df0 More minor grammar fixes 2020-08-06 16:07:18 +01:00
ec55edfbc4 Add additional options to redis session prov
Adding option for frequency of checking timed out connections as well as
an option to specify retries.

These changes make redis provider more stable since connection problems
are becoming fewer.

Since redigo does not have this options and since redis_sentinel and
redis_cluster are using go-redis as a client, this commit changes from
redigo to go-redis for redis session provider.

Added tests for redis session provider as well.
2020-08-06 11:14:36 +02:00
2fce8f9d1b Merge pull request #4124 from phiphi282/session_exists_return_err
Update session provider interface to return errors on SessionExist
2020-08-06 09:17:49 +08:00
15489fa76a Merge pull request #4135 from IamCathal/grammar-fixes
Minor grammar fixes
2020-08-06 09:05:30 +08:00
e7d8bab5d9 Improved definition of DefaultEvery 2020-08-05 17:56:11 +01:00
2f5683610f Minor grammar fixes 2020-08-05 17:50:05 +01:00
5c8c088684 Revert "Change interface in session README"
This reverts commit 6f5c5bd3a6.
2020-08-05 18:33:17 +02:00
009074725e Move interface change to pkg/session/README.md 2020-08-05 18:32:33 +02:00
ce50ca22d7 Merge branch 'develop-2.0' of github.com:astaxie/beego into session_exists_return_err 2020-08-05 18:30:19 +02:00
3052c64b6c Revert "Add error to SessionExist interface"
This reverts commit 28e6b3b924.
2020-08-05 18:29:47 +02:00
5f2f6e4f86 Add interface change in pkg folder 2020-08-05 18:29:22 +02:00
3382a5baa1 Merge pull request #4134 from flycash/ftr/ormInterceptor
Deprecated old web module
2020-08-05 22:22:59 +08:00
882aa9b967 Deprecated old web module 2020-08-05 21:57:20 +08:00
02972d8702 Merge pull request #4130 from flycash/ftr/prometheuseAndOpentracing
Support prometheus and opentracing by using FilterChainFunc
2020-08-05 15:46:03 +08:00
261b704d8b Fix UT 2020-08-05 07:25:34 +00:00
6c6cf91741 Support prometheus and opentracing filter 2020-08-04 23:15:42 +08:00
ae8461f95d Merge pull request #4125 from flycash/ftr/middleware
Support FilterChain
2020-08-04 23:06:18 +08:00
aa3987f816 Merge remote-tracking branch 'origin/develop' into ftr/middleware 2020-08-04 22:26:01 +08:00
3dc5ec1060 Merge pull request #4129 from astaxie/revert-4128-fix-ci-fail
Revert "fix CI fail for connection log test"
2020-08-04 21:38:50 +08:00
1961c1e441 Revert "fix CI fail for connection log test" 2020-08-04 21:37:46 +08:00
787bb60b42 Merge pull request #4128 from AllenX2018/fix-ci-fail
fix CI fail for connection log test
2020-08-04 21:14:45 +08:00
12b984861d fix CI fail for connection log test 2020-08-04 20:32:07 +08:00
79ffef90e3 support filter chain 2020-08-04 07:26:51 +00:00
310161f9d4 Merge pull request #4126 from flycash/fix/secureFlagV1
XSRF add secure and http only flag
2020-08-03 21:24:23 +08:00
a0d1c42dac XSRF add secure and http only flag 2020-08-03 21:04:33 +08:00
6f5c5bd3a6 Change interface in session README 2020-08-03 13:33:30 +02:00
28e6b3b924 Add error to SessionExist interface
Implement changed interface for all default providers as well and change
tests accordingly
2020-08-03 13:31:49 +02:00
9e1346ef4d Merge pull request #4113 from flycash/ftr/usingPkg
using pkg module
2020-07-31 13:40:39 +08:00
9f295067b7 Resolve conflict 2020-07-30 22:44:44 +08:00
71776e4bef Merge pull request #4114 from wangle201210/develop-2.0
Add the operator(>,>=,<,<=,=,!=) of orm
2020-07-30 16:51:02 +08:00
87b40ee9e7 Merge pull request #4116 from livelyRyan/patch-2
添加 BConfig.Listen.ClientAuth 字段及处理逻辑
2020-07-30 16:07:00 +08:00
7831638f37 移除多余的条件判断 2020-07-30 14:48:46 +08:00
5203804165 调整默认配置中的 ClientAuth 值,使之与原来的行为保持一致 2020-07-30 14:46:17 +08:00
d4074b5004 Merge pull request #4103 from flycash/ftr/newOrmWithDB
Enhance: NewOrmUsingDB & remove useless methods
2020-07-30 11:43:47 +08:00
0815e77f9a 修复笔误产生的拼写错误 2020-07-30 11:20:22 +08:00
c46ba86215 修复笔误产生的拼写错误 2020-07-30 11:18:14 +08:00
9d23e5a3fb 简化代码写法 2020-07-30 11:03:32 +08:00
513a4afff1 对 Listen 结构体增加 ClientAuth 字段
对 Listen 结构体增加 ClientAuth 字段,赋予默认配置对象该字段值为 tls.VerifyClientCertIfGiven,与原代码逻辑的默认值保持一致
2020-07-30 10:59:32 +08:00
15e11931fc 添加对 BConfig.Listen.ClientAuth 字段的逻辑处理。当指定了该配置时,使用配置的值来作为验证客户端的方式。如果没指定,使用默认值 tls.RequireAndVerifyClientCert 2020-07-30 10:53:30 +08:00
7d561607d8 Merge pull request #4111 from liuhaogui/develop
添加配置文件路径环境变量
2020-07-30 09:14:12 +08:00
22b8cae73b Add the operator(>,>=,<,<=,=,!=) of orm
eg:
qs.Filter("counts__>=","20")
qs.Filter("counts__!=","20")
2020-07-29 23:23:02 +08:00
aa06a10493 uing pkg module 2020-07-29 14:42:27 +00:00
9b58c2836c Merge develop-2.0 and resolve confilct 2020-07-29 22:26:29 +08:00
15f04b8da4 add env BEEGO_CONFIG_PATH 2020-07-29 21:57:16 +08:00
7312197732 Merge pull request #4098 from jianzhiyao/frt/stmt_config
make stmt cache size configurable & wrap kv
2020-07-29 20:57:07 +08:00
e87de70c6d adapt wrapping kv 2020-07-29 00:45:41 +08:00
4304b40a82 Merge branch 'frt/wrap_kv' into frt/stmt_config
# Conflicts:
#	pkg/orm/constant.go
#	pkg/orm/db_alias.go
2020-07-28 18:32:24 +08:00
e8facd28f5 wrap kv 2020-07-28 17:37:36 +08:00
54ef476600 add tag interfaces and remove log.go 2020-07-28 06:28:51 +00:00
756df9385f make stmt cache size configurable 2020-07-28 12:57:19 +08:00
21f281655d remove QueryRelated and QueryRelatedCtx 2020-07-27 21:22:40 +08:00
2e7fb81348 deprecated orm.go and add NewOrmUsingDB method 2020-07-27 21:19:34 +08:00
ebc0207909 Merge pull request #4096 from jianzhiyao/request_context
fix memory leak of request context
2020-07-27 18:04:37 +08:00
2386c9c80d delete useless if-stmt 2020-07-26 22:37:42 +08:00
b9f9fcca5f Merge pull request #4099 from jianzhiyao/fix_3869
orm.rawPrepare support FlatParams
2020-07-26 21:01:39 +08:00
16d71893cd orm.rawPrepare support FlatParams 2020-07-26 19:40:13 +08:00
cfff0f3b46 fix memory leak of request context 2020-07-25 00:00:34 +08:00
93eb7c6b83 Merge pull request #4091 from flycash/ftr/moveToPkg
Ftr: move to pkg
2020-07-23 09:36:29 +08:00
79c2157ad4 Fix UT 2020-07-22 15:46:29 +00:00
30eb889a91 Format code 2020-07-22 23:00:06 +08:00
9c51952db4 Move package 2020-07-22 22:55:59 +08:00
16b66509f6 Merge pull request #4083 from IamCathal/content-length
Add Content-length field for logging
2020-07-21 15:47:58 +08:00
d5695060c5 Merge pull request #4082 from flycash/ftr/newOrm
Refactor RegisterDatabase
2020-07-21 15:47:40 +08:00
af2143f8fb Merge pull request #4084 from astaxie/develop
Align develop-2.0
2020-07-21 09:36:54 +08:00
a66b9950e7 Add Content-length field for logging 2020-07-20 21:23:21 +01:00
44460bc457 Refactor RegisterDatabase 2020-07-20 15:52:40 +00:00
41feb3a711 Merge pull request #4078 from jianzhiyao/frt/orm_merge
refactor orm
2020-07-20 23:50:40 +08:00
b6f7d30f9f fix unit test 2020-07-20 19:10:57 +08:00
4aad313de7 do not judge tx status in txOrm 2020-07-20 17:34:58 +08:00
aefe21b63a complete error log 2020-07-20 17:25:27 +08:00
32da446eb1 refactor orm 2020-07-19 23:46:42 +08:00
d9c016ed98 Merge pull request #4076 from flycash/ftr/taskLog
Store nearest error info
2020-07-19 23:23:13 +08:00
7258ef113a Store nearest error info 2020-07-19 14:34:57 +00:00
d6779c4a90 Merge pull request #4075 from flycash/fix/stable-ut
Fix orm test when using Driver = mysql
2020-07-19 21:22:36 +08:00
192a278a2a Fix orm test when using Driver = mysql 2020-07-19 12:56:58 +00:00
7a48fbb698 Merge pull request #4066 from playHing/self-dev
Fix concurrent issue of context/input Query method
2020-07-16 13:52:11 +08:00
3e2c795410 Rlock for form query 2020-07-15 20:44:59 +08:00
55e6298f29 Fix concurrent form parsing and getting 2020-07-15 20:44:59 +08:00
b50fb44950 Add bench test on context input query 2020-07-15 20:44:59 +08:00
9d936c58bf Merge pull request #4070 from flycash/develop-2.0
Move orm to pkg/orm
2020-07-15 10:50:05 +08:00
ffe1d52120 Move orm to pkg/orm 2020-07-15 10:05:11 +08:00
1c0714405a Merge pull request #4068 from jianzhiyao/fix_3898
fix `index out of range` when sid len = 1
2020-07-15 00:03:59 +08:00
678b90385b add log 2020-07-14 09:57:13 +08:00
5940ae33c2 fix index out of range when sid len = 1
add unit test for sess_file.go
2020-07-13 19:14:53 +08:00
3db31385cf Merge pull request #4065 from Acmefocus/develop
update README.md
2020-07-11 23:16:36 +08:00
2b9aaa5b0d update README.md
Signed-off-by: Acmefocus <107723772@qq.com>
2020-07-10 09:58:06 +08:00
dced745d55 Merge branch 'develop' of https://github.com/Acmefocus/beego into develop 2020-07-09 18:15:25 +08:00
25ba78ea72 update README.md
Signed-off-by: Acmefocus <107723772@qq.com>
2020-07-09 18:14:31 +08:00
863b5bd0f4 Merge branch 'develop' of https://github.com/Acmefocus/beego into develop
Signed-off-by: Acmefocus <107723772@qq.com>
2020-07-09 17:59:07 +08:00
ba3153621a Merge branch 'develop' of https://github.com/Acmefocus/beego into develop 2020-07-09 17:47:46 +08:00
40cdc877b6 Update README.md
Signed-off-by: Acmefocus <107723772@qq.com>
2020-07-09 17:38:08 +08:00
76debb1899 Update README.md 2020-07-09 17:18:01 +08:00
35dcc3df7c Merge pull request #4058 from a631807682/fix/issues/4054
Fix response payload too large
2020-07-09 16:41:11 +08:00
c3f14a0ad6 refactor: log error when payload too large 2020-07-09 09:45:40 +08:00
8ee167bc7b Merge pull request #4051 from gmelodie/sleep-on-reconnect-3972
Add sleep on reconnect
2020-07-09 09:41:04 +08:00
2eccb23461 Add sleep on reconnect functionality 2020-07-08 17:30:34 +01:00
28d3f624a3 Merge pull request #4055 from tayoogunbiyi/develop
Allow Healthcheck endpoint return JSON for Kubernetes
2020-07-08 23:51:03 +08:00
926b80d1d8 Merge pull request #4061 from flycash/race-condition
Fix 4059
2020-07-08 23:49:54 +08:00
c08b27111c Fix 4059 2020-07-08 23:32:18 +08:00
03f78b2e4a fix: add error code support 2020-07-08 18:09:01 +08:00
946a42c021 fix: response http 413 when body size larger then MaxMemory. 2020-07-08 17:14:52 +08:00
d7b0d55357 added extra check for same response lengths 2020-07-07 17:23:52 +01:00
728bf34006 refacted cache health check from toolbox 2020-07-07 16:46:59 +01:00
e0f8c6832d added test for buildingHealthCheckResponse 2020-07-07 16:28:16 +01:00
469dc7bea9 refactored the building of healthcheck response map 2020-07-07 16:09:22 +01:00
ca0c64b69e refactored tests for health check endpoint 2020-07-07 15:21:38 +01:00
5a4a082af0 renamed functions for clarity 2020-07-07 14:54:21 +01:00
9dc660c1da Merge pull request #4056 from gmelodie/reconn-bug-3971
Fix reconnection bug in logs/conn.go
2020-07-07 20:25:44 +08:00
d8724cb122 Add error returning to writeln 2020-07-06 21:38:47 +02:00
fc56c562db Fix logger reconnection 2020-07-06 21:38:47 +02:00
8d1a9bc92e added tests for health check endpoints 2020-07-06 19:34:48 +01:00
db547a7c84 added test for execJson 2020-07-06 16:04:29 +01:00
7c575585e9 added conditional json flag when trying to view healthchecks 2020-07-06 15:27:12 +01:00
289f86247e Merge pull request #4053 from flycash/ftr/test-docker
Add docker-compose to support running unit test locally
2020-07-05 22:08:20 +08:00
ca9b21bb30 Add docker-compose to support running test 2020-07-04 13:42:20 +00:00
8ef8fd2606 Merge pull request #4036 from astaxie/develop
V1.12.2
2020-06-30 23:25:29 +08:00
d6ef33feee Merge pull request #4045 from flycash/preprare-1.12.2
Remove tidb dependency
2020-06-30 23:16:41 +08:00
2f5ed5b433 Remove tidb dependency 2020-06-30 21:04:37 +08:00
9c18dbe7dc Merge pull request #4044 from flycash/upgrade_version
upgrade version
2020-06-30 20:58:14 +08:00
01219944a4 upgrade version 2020-06-30 20:55:31 +08:00
4fac6ceb3f Merge pull request #4039 from flycash/fixbug
Fix ES bug
2020-06-30 13:18:13 +08:00
1c4085e7ea Update es.go 2020-06-30 10:12:10 +08:00
2117562113 Update es.go 2020-06-30 10:09:36 +08:00
1f9da8d75b Fix ES bug 2020-06-29 22:25:22 +08:00
9dea9f9ae7 Merge pull request #3994 from HarryWang29/develop
update support bit operation
2020-06-29 20:27:36 +08:00
0f6735e20d Merge branch 'develop' of https://github.com/astaxie/beego into develop
 Conflicts:
	go.mod
	go.sum
2020-06-29 16:48:03 +08:00
ad68e8d866 Merge pull request #4033 from jianzhiyao/develop
make redis client idle timeout configurable
2020-06-28 23:14:28 +08:00
26beef756f Merge pull request #4038 from flycash/fixbug
Fix ES index problem
2020-06-28 23:14:07 +08:00
5c9cc805a1 make redis client idle timeout configurable 2020-06-28 22:20:46 +08:00
de51bd28d7 Fix ES index problem 2020-06-28 21:12:12 +08:00
6b6a0e8a56 Merge pull request #4032 from jianzhiyao/develop
fix strings.Repeat panic
2020-06-26 17:51:47 +08:00
8039cc8e59 Merge pull request #4017 from guhan121/fix#4000
Prohibit multiple calls SetDefaultMessage.
2020-06-26 17:49:36 +08:00
b88b7d2899 fix strings.Repeat panic 2020-06-25 23:48:07 +08:00
e725192072 fix #4000 2020-06-25 22:11:32 +08:00
469f2c226d Merge pull request #3998 from guhan121/go_modules_route_compatibility
for go modules, generate route by `GO111MODULE=on`
2020-06-25 20:30:27 +08:00
828b20163d Merge pull request #4029 from flycash/feature/buildInfo
Add build info for prometheus
2020-06-25 18:37:11 +08:00
9c5eab4834 Add build info for prometheus 2020-06-25 17:44:40 +08:00
1813d414ae Merge pull request #4027 from flycash/develop
Move many PR's change here since the original authors are responseless
2020-06-25 02:17:49 +08:00
6052524a28 Merge pull request #4028 from jianzhiyao/develop
acquire() in Lock
2020-06-23 23:58:42 +08:00
3ce68d6a30 Move many PR's change here since the original authors are responseless 2020-06-23 23:14:51 +08:00
89420eacd0 go destroy 2020-06-23 23:05:58 +08:00
6d9862b924 acquire() in Lock 2020-06-23 22:29:41 +08:00
96658121dc Merge pull request #3818 from dbt4516/develop
Get the real location of the log directory before using filepath.Walk function when remove old logs
2020-06-23 21:17:14 +08:00
43a3623f51 Merge pull request #3803 from yinggaozhen/fix-statistics
GetMapData change to read lock
2020-06-23 21:08:32 +08:00
089dfbdbee Merge pull request #4016 from huija/develop
Update redis.go
2020-06-23 20:41:42 +08:00
526a5463d6 Merge pull request #4025 from jianzhiyao/develop
Fix: Too Many Prepare Statement
2020-06-23 20:33:04 +08:00
a28d294a83 upgrade acquire method return *sql.Stmt 2020-06-23 13:46:19 +08:00
f70fd5babf add comments on Scan func 2020-06-23 12:32:26 +08:00
a764e17fbf check ci 2020-06-23 10:43:51 +08:00
f5c580a403 Merge pull request #3739 from nuczzz/develop
fix graceful bug: old process didn't exist when graceful restart
2020-06-23 00:10:53 +08:00
7210dc24b6 Merge pull request #3743 from Colstuwjx/fix/orm-end-printstack
Fix/orm end printstack
2020-06-22 23:59:16 +08:00
1060dc7df8 Merge pull request #4026 from flycash/develop
fix UT
2020-06-22 23:36:25 +08:00
3ab1e48217 fix UT 2020-06-22 23:24:15 +08:00
a5e8344a0a add stmt garbage recycle 2020-06-22 23:23:52 +08:00
e1386c448c Merge pull request #4022 from jianzhiyao/develop
stmt: fix bug of non concurrent security & add double check
2020-06-21 18:01:14 +08:00
6e4d0917b2 Merge pull request #4023 from flycash/feature/prometheus
Change prometheus patter
2020-06-21 17:39:02 +08:00
9b43a90138 Change prometheus patter 2020-06-21 17:36:13 +08:00
b7833fb297 Merge pull request #4021 from flycash/feature/prometheus
Add prometheus support
2020-06-21 17:11:00 +08:00
16b81d09a7 Add prometheus support 2020-06-21 17:10:34 +08:00
0056b92d0b Merge pull request #4007 from flycash/fix3949
Store RouterPattern before filter execute
2020-06-21 17:05:04 +08:00
decc75e025 fix bug of non concurrent security & add double check 2020-06-21 16:25:32 +08:00
f2bae3e367 add UT for Scan function 2020-06-19 22:55:40 +08:00
6eeea141d8 Merge pull request #4018 from flycash/fix/xss
Using HTMLEscapeString in adminui.go to avoid XSS attack
2020-06-19 22:21:51 +08:00
6c0db4db3d Using HTMLEscapeString in adminui.go to avoid XSS attack 2020-06-19 21:49:17 +08:00
b9fbcbd906 Merge pull request #4014 from jianzhiyao/develop
over size file do not run openFile
2020-06-19 20:19:16 +08:00
40181c1042 Update redis.go
use `scan` instead of `keys`
2020-06-19 14:21:12 +08:00
3879fd9c34 修改测试用例 2020-06-18 17:25:48 +08:00
af238ee047 Merge remote-tracking branch 'origin/develop' into go_modules_route_compatibility
# Conflicts:
#	go.mod
#	go.sum
2020-06-18 16:41:03 +08:00
86935ada01 Merge pull request #3943 from zhlicen/master
#3942 fix encoded url(with slash) router match problem
2020-06-18 16:04:16 +08:00
8160059182 big size file lead to memory leak 2020-06-16 14:56:58 +08:00
dc70e9fc47 update go test 2020-06-15 18:16:40 +08:00
bac2b31afe Merge pull request #3975 from nicowaisman/patch-2
Change permission mask
2020-06-13 23:40:13 +08:00
a134cb8d17 update go test 2020-06-11 23:45:06 +08:00
4ad699b7b8 Merge pull request #3981 from zwei0526/develop
Update parser.go
2020-06-10 16:38:15 +08:00
14fc935cb6 Merge pull request #4005 from keminar/develop
fix graceful bug
2020-06-10 16:34:32 +08:00
d9f262e277 update go test 2020-06-10 12:31:38 +08:00
131748bb3d update go.mod&go.sum 2020-06-10 11:34:58 +08:00
2c3c66ccea Merge remote-tracking branch 'origin/develop' into develop 2020-06-10 11:16:17 +08:00
71cb1379b4 update support bit operation 2020-06-10 11:15:23 +08:00
b351f02525 Merge branch 'develop' of https://github.com/astaxie/beego into develop
 Conflicts:
	go.sum
2020-06-10 11:05:57 +08:00
8e29300f85 fix graceful bug 2020-06-09 12:20:08 +08:00
5f31bf45d4 Fix #3949 : store RouterPattern before filter execute so that filter can use the pattern 2020-06-07 23:16:26 +08:00
3e30f37172 Merge pull request #4006 from flycash/develop
fix CI and UT
2020-06-06 21:16:08 +08:00
ce4ce74c8d Fix validation test 2020-06-06 20:50:48 +08:00
ce0e7525ad Fix ES client 2020-06-06 20:14:01 +08:00
d0b7244d57 Fix ledis 2020-06-06 19:21:57 +08:00
5100a8c396 fix CI and UT 2020-06-06 18:16:36 +08:00
b8efb3ef45 Merge pull request #3984 from jianzhiyao/develop
fix bug:static can not real hit cache & memory leak
2020-06-06 13:10:58 +08:00
ae15b54f83 Merge pull request #3992 from maxshine/fix-3991
[Fix-Issue-3991] Fix Read with SQLite not supporting SELECT FOR UPDAT…
2020-06-06 13:10:08 +08:00
0dab959c95 Merge pull request #4004 from gokangaroo/develop
avoid `panic: send on closed channel` after closing logger.
2020-06-06 13:09:36 +08:00
ee615dd08f Merge pull request #3985 from leapsea/develop
修复多线程下数据库连接异常问题
2020-06-06 12:40:50 +08:00
b879a07b3a avoid panic: send on closed channel after closing logger. 2020-06-04 14:52:54 +08:00
c265f6e49c add comment 2020-06-02 20:02:58 +08:00
690e91e1b6 static file module:make cache file size and cache file numbers configurable 2020-06-02 18:22:47 +08:00
50f71a8a21 fix bug of getting int error 2020-06-02 18:10:17 +08:00
677d010d86 [Fix-Issue-3991] Add warn for SQLite Read is invoked isForUpdate=true 2020-06-01 23:12:12 +08:00
db2a1134ce Merge pull request #3993 from skanger/develop
fix httplib PostFile method
2020-06-01 22:05:01 +08:00
c2771397be delete vendor 2020-06-01 15:06:49 +08:00
70733d9810 fix label == `` #4001 2020-06-01 15:06:33 +08:00
11740cede6 Merge pull request #3996 from bharrat/3995-fix-middleware-not-working-with-graceful
Fixes #3995 Use handlers with middleware when starting Graceful server
2020-05-31 22:23:05 +08:00
4ffe26a1d2 for go modules, generate route by GO111MODULE=on 2020-05-22 11:35:45 +08:00
075db4773b Fixes #3995 Use handlers with middleware when starting Graceful server 2020-05-18 10:00:14 -07:00
2fd9dfca7b update support bit operation 2020-05-18 19:18:35 +08:00
eea20f6ceb fix httplib PostFile method 2020-05-16 11:00:15 +08:00
8055357576 [Fix-Issue-3991] Fix Read with SQLite not supporting SELECT FOR UPDATE syntax 2020-05-15 17:36:01 +08:00
9fda81b7f3 modify static cache total size about 100m 2020-05-09 18:24:14 +08:00
4b12e053b7 fix 2020-05-09 17:58:10 +08:00
0307c8b110 set static file cahce limit:file size & file count 2020-05-09 17:57:00 +08:00
867f83de34 修复数据库连接异常问题 2020-05-09 16:31:30 +08:00
af19822293 not modify go.mod 2020-05-09 14:06:51 +08:00
2449aad105 add cache-hit test example 2020-05-09 10:44:37 +08:00
d31975a752 complete test example 2020-05-08 17:35:02 +08:00
b28d5e2716 fix bug:static canot real hit cache
opt:reduce risk of memory leak with lru cache
2020-05-08 17:10:19 +08:00
ab33d683ea Update parser.go
修复genRouterCode方法解析  router 注释(// @router) 单双引号引起的bug
2020-04-26 19:26:10 +08:00
f99cbe0fa4 Change permission mask 2020-04-22 08:42:54 -07:00
0aa82d875a Update input.go 2020-03-05 14:46:17 +00:00
8f3d1c5f42 Merge pull request #3922 from BurtonQin/bug-1-2-3-inconsistent-field-protection
cache, context, session: add lock to fix inconsistent field protection
2020-02-22 15:09:25 +08:00
2410b364af Merge pull request #3923 from timchenxiaoyu/develop
fix exist typo
2020-02-22 11:25:13 +08:00
713503e43d fix exist typo 2020-02-14 16:47:47 +08:00
cfdd1cd5be cache, context, session: add lock to fix inconsistent field protection 2020-02-10 21:49:46 +08:00
0cd80525e7 Merge branch 'develop' 2020-02-07 16:25:41 +08:00
de5650b723 version 1.12.1 2020-02-07 16:23:57 +08:00
e5e4a3bea7 Merge pull request #3868 from holtyuzhuyanbo/fix_session_destory
fix: session destory
2020-02-07 11:49:54 +08:00
90d0b43f34 Merge pull request #3888 from gavin2014/develop
[Fix] Fix create table with SQLite not supporting COMMENT syntax
2020-02-07 11:46:52 +08:00
1b7f5ba2c4 Merge pull request #3900 from aixiaoxiang/develop
httplib:fixes network request failed to create an invalid file and automatically created file directory
2020-02-07 11:10:56 +08:00
96f01079cb Merge pull request #3905 from ywk253100/200110_context
Send the request from context rather than the original one to handlers
2020-02-07 11:05:01 +08:00
9d4b5b313f Merge pull request #3911 from liuzhang/develop
验证调整,增加label, xx不能为空
2020-02-07 11:04:11 +08:00
b882009979 Merge pull request #3919 from wy65701436/develop-sha256
update hash algorithm for signing the cookie for xsrf token
2020-02-07 11:01:10 +08:00
a768bf8f00 update hash algorithm for signing the cookie for xsrf token
Due to the chosen-prefix collision in SHA-1(details at https://sha-mbles.github.io/), SHA-1 hash functions should to be deprecated and SHA-2/SHA-3 should be used instead.

Signed-off-by: wang yan <wangyan@vmware.com>
2020-02-06 17:31:24 +08:00
034599ca1d 验证调整,增加label, xx不能为空 2020-01-17 16:47:19 +08:00
5a02c556b2 Send the request from context rather than the original one to handlers
The filters may do some changes to the request, such as putting values in the request's context

Signed-off-by: Wenkai Yin <yinw@vmware.com>
2020-01-10 17:58:00 +08:00
axx
dc5c42e981 httplib:fixes network request failed to create an invalid file and automatically created file directory 2019-12-30 11:24:55 +08:00
92a4119258 Update cmd_utils.go
[Fix] Fix create table with SQLite not supporting COMMENT syntax
2019-12-11 16:50:08 +08:00
aa90c67a75 Merge pull request #3867 from ywk253100/191119_xsrf
Abort with the pre-defined status code when handling XSRF error
2019-11-29 18:43:02 +08:00
38a144c68f fix: session destory 2019-11-19 21:25:30 +08:00
793047097c Abort with the pre-defined status code when handling XSRF error
As the status codes(422 and 417) are set in the error map, abort with them directly to active the pre-defined error handlers

Signed-off-by: Wenkai Yin <yinw@vmware.com>
2019-11-19 18:55:54 +08:00
1923b8c767 Merge pull request #3841 from HKail/develop
添加16开头手机号验证
2019-10-23 22:22:52 +08:00
fb640f0075 更新16开头手机号的正则测试 2019-10-22 17:07:22 +08:00
241f10b429 添加16开头手机号验证,162电信,165移动,166/167联通。 2019-10-22 16:58:05 +08:00
b8d626bbea 添加16开头手机号验证,162电信,165移动,166/167联通。 2019-10-22 16:28:19 +08:00
2a6ceca861 Update README.md 2019-10-10 11:23:19 +08:00
10236b9f2d Merge pull request #3814 from cloudzhou/patch-2
leak opened file
2019-10-10 00:48:24 +08:00
b3ae5d4ac6 Update file.go
According to issue#4759 (https://github.com/golang/go/issues/4759) filepath.Walk function in golang cannot handle symbolic path, meanwhile symbolic path for log directory is pretty common used. In such scenario this deleteOldLog function will fail without any error log. Get the real location of the log directory before using walk function can fix this.
2019-10-09 15:53:22 +08:00
77fc8e4e38 According to issue#4759 (https://github.com/golang/go/issues/4759) filepath.Walk function in golang cannot handle symbolic path, meanwhile symbolic path for log directory is pretty common used. In such scenario this deleteOldLog function will fail without any error log. Get the real location of the log directory before using walk function can fix this. 2019-10-09 15:33:38 +08:00
5a5482c77f leak opened file
should defer file.Close()
2019-09-27 19:27:44 +08:00
11774c87a5 update version 1.13 2019-09-19 00:19:33 +08:00
6d47b4a2e0 GetMapData change to read lock 2019-09-09 17:44:10 +08:00
8395a26061 Merge pull request #3801 from DennisMao/develop
fix annotation on orm/utils #3777
2019-09-09 00:46:36 +08:00
4348356d0a fix annotation on orm/utils 2019-09-09 00:47:20 +08:00
b551949a2b Add PrintStack() while orm abnormally exit. 2019-07-31 14:08:58 +08:00
32cd76396d fix graceful bug 2019-07-27 18:54:13 +08:00
5620608418 Update README.md 2019-07-21 22:58:28 +08:00
32ee728078 Merge pull request #3586 from astaxie/develop
V1.12.0
2019-07-05 12:26:39 +08:00
de7ce2f9b0 v1.12.0 2019-07-05 11:58:41 +08:00
d6c2a9fd4b Merge pull request #3703 from jkylin/develop
email的Attach和AttachFile 的参数检查逻辑有误。len(args) < 1 && len(args) > 2 改为 l…
2019-07-05 11:35:47 +08:00
582a4fa34b Merge pull request #3699 from orgmatileg/develop
router.go: add comment func LogAccess
2019-07-05 11:34:59 +08:00
99647986de Merge pull request #3689 from GeorgeXc/addStmt
Add stmt
2019-07-05 11:34:40 +08:00
5bcde306ea Revert "update"
This reverts commit 2909ff3366.
2019-06-28 23:37:32 +08:00
2909ff3366 update 2019-06-28 23:23:01 +08:00
40078cba2c update 2019-06-28 23:13:18 +08:00
5d0c0a03d7 update 2019-06-28 22:56:32 +08:00
8cfd7f5c19 email的Attach和AttachFile 的参数检查逻辑有误。len(args) < 1 && len(args) > 2 改为 len(args) < 1 || len(args) > 2 2019-06-28 20:09:23 +08:00
62d96c2e93 router.go: add comment func LogAccess 2019-06-25 08:04:21 +07:00
e844058aed Merge pull request #3632 from Wusuluren/develop
fix concurrent map access problem on BeegoInput.data
2019-06-18 12:09:34 +08:00
1eab6bb32a Merge pull request #3669 from xfwduke/develop
fix bugs of ParseForm about time in RFC3339 format
2019-06-18 12:08:55 +08:00
c265d32c36 Merge pull request #3685 from Anderson-Lu/fix_orm_datarace
fix orm datarace
2019-06-18 12:05:54 +08:00
06692c3e27 update 2019-06-17 23:38:07 +08:00
394a73c75f fix orm datarace 2019-06-14 14:43:02 +08:00
cbcde8bd1f Merge branch 'develop' of https://github.com/Wusuluren/beego into develop 2019-06-09 22:10:22 +08:00
b17e49e6aa fix concurrent map access problem on BeegoInput.data 2019-06-09 22:09:38 +08:00
873f62edff update 2019-06-09 01:19:17 +08:00
cc0eacbe02 update 2019-06-08 23:53:42 +08:00
649c5c861d fix bugs of ParseForm about time in RFC3339 format 2019-05-31 15:52:19 +08:00
206a7ed1fc Merge pull request #3662 from npu21/develop
fix typos
2019-05-27 11:07:52 +02:00
3c046a4dbf fix two typos 2019-05-27 16:38:30 +08:00
f2be6af2ca Merge pull request #3658 from priyesh-lb/patch-1
Beego skipping some migrations
2019-05-18 09:38:09 +08:00
804b9769e0 Merge pull request #3642 from GeorgeXc/fixBatchUpdate
Fix BatchUpdate not update the auto_now field
2019-05-18 09:28:22 +08:00
585df01899 Merge pull request #3635 from edwardhey/develop
Incr和Decr应该改成排它锁,否则在并发的时候会出现非期望的结果值
2019-05-18 09:27:38 +08:00
1a529c061c Merge pull request #3633 from MarxGo/develop
fix:utils.GetGOPATHs() when go version equal or after go1.10
2019-05-18 09:27:08 +08:00
fcacfc08e3 Beego skipping some migrations
Beego skipping some migrations #3657
2019-05-17 16:19:26 +05:30
a0ca3d61d6 update 2019-05-08 23:11:57 +08:00
39bd40e512 Incr和Decr应该改成排它锁,否则在并发的时候会出现非期望的结果值 2019-05-01 13:10:21 +08:00
Guo
8748de95c7 fix:utils.GetGOPATHs() when go version equal or after go1.10 func does not return defaultGoPATH() 2019-04-30 06:43:25 +08:00
0939e8e493 fix concurrent map access problem on BeegoInput.data 2019-04-30 00:15:24 +08:00
6a33feee46 Merge pull request #1 from astaxie/develop
update
2019-04-29 23:19:27 +08:00
58b2ac702c Merge pull request #3623 from Martinho0330/develop
make routers configurable for beego multi-instance in the same repo
2019-04-29 18:19:22 +08:00
175714f69a Merge pull request #3627 from haleyly/develop
route request put amendment
2019-04-29 18:18:19 +08:00
d5b70118a3 Merge branch 'develop' of https://github.com/haleyLy/beego into dev 2019-04-28 08:51:34 +08:00
a9629f707e route request put amendment 2019-04-28 08:50:30 +08:00
6123c72752 Merge pull request #3621 from guanle/guanle-patch-1
Update templatefunc.go  for default value tag
2019-04-27 23:28:35 +08:00
56afa5c2bf Merge pull request #3625 from hsluoyz/develop
remove the 1000-row limit for ORM result set
2019-04-27 23:27:58 +08:00
0c576dac82 Merge pull request #3599 from Wusuluren/cache_file
fix bug on cache/file
2019-04-27 23:27:27 +08:00
8462372c03 Merge pull request #3598 from Wusuluren/develop
fix race problem on toolbox/task
2019-04-27 23:26:29 +08:00
20ff97d53d Merge pull request #3595 from JessonChan/log_revet
Log revet
2019-04-27 23:26:06 +08:00
8ce5b6cc52 Merge pull request #3593 from JessonChan/trace_method
Trace method
2019-04-27 23:25:44 +08:00
afb787d49d remove the 1000-row limit for ORM result set 2019-04-27 16:58:00 +08:00
0b165b78a1 make routers configurable for beego multi-instance in the same repo 2019-04-22 22:18:37 +08:00
fa97488bdc Update templatefunc.go 2019-04-21 10:27:35 +08:00
3086081ec0 v1.11.2 2019-04-06 13:50:14 +08:00
dfab44c24a fix bug on cache/file 2019-04-05 22:32:28 +08:00
1900246054 fix bug on cache/file 2019-04-05 22:17:56 +08:00
e980f92c63 fix race problem on toolbox/task 2019-04-05 20:28:24 +08:00
ce3800e3ef // Deprecated: use github.com/astaxie/beego/logs instead. 2019-04-03 14:13:38 +08:00
e3d668f450 revet to log.go
https://github.com/astaxie/beego/issues/3591
https://github.com/astaxie/beego/issues/3588
2019-04-03 14:08:42 +08:00
75b4bc5896 it's no need to override Trace method. 2019-04-03 10:19:09 +08:00
c0ecf32d17 update travis 2019-04-02 21:53:01 +08:00
3155f07ccd no need to override Trace method. 2019-03-27 13:43:27 +08:00
02bead5097 handle trace request must NOT CACHE
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control
2019-03-27 13:40:34 +08:00
e8b29c9fd1 handle trace request 2019-03-27 13:34:46 +08:00
610f27d684 Merge pull request #3530 from GeorgeXc/develop
[orm] add ignore auto_now_add field when update
2019-03-27 00:14:07 +08:00
59466f6678 Merge pull request #3554 from grahamjamesaddis/travis-ci-build
Allow forked beego project to pass travis ci builds
2019-03-26 19:24:06 +08:00
3ddd8f860e Merge pull request #3561 from JessonChan/develop
refactor color logger
2019-03-26 19:23:14 +08:00
8535ec0819 Merge pull request #3567 from bsdelf/feature/ensure-custom-error-handler
Ensure custom error handler is called
2019-03-26 19:22:54 +08:00
f294121ab7 Merge pull request #3583 from maxshine/develop
[Fix] Fix the issue that genRouterCode results in @Import annotations getting overwritten
2019-03-26 19:13:44 +08:00
0b8ebaf387 Update db.go 2019-03-26 01:00:04 +08:00
3b00cfccec [Fix] Fix the issue that genRouterCode incorrect logic results in @Import annotations getting overwritten 2019-03-24 14:41:28 +08:00
005391be81 update 2019-03-23 00:33:26 +08:00
bc8fffe347 Merge pull request #3571 from dxas90/patch-1
Fixes #3570
2019-03-14 19:16:15 +01:00
914bbfd710 Update apiauth.go fixed infinite recursive call 2019-03-14 09:14:12 -04:00
be31bd2bbd Ensure custom error handler is called 2019-03-13 16:24:04 +08:00
95ff817019 undefined: beego.BeeLogger fixed 2019-03-13 09:41:13 +08:00
ea91e7638c move log function to log package 2019-03-12 17:01:23 +08:00
4564e9810c logger_test imported and not used: "bytes" 2019-03-12 16:36:00 +08:00
44a1a8f6be println is builtin function 2019-03-12 15:51:43 +08:00
9cecb22170 NewAnsiColorWriter remove 2019-03-12 15:13:54 +08:00
7693502aaa logAdapter is more readable 2019-03-12 13:20:13 +08:00
c0fae547e9 remove ansicolor code,import ansicolor package 2019-03-12 12:14:09 +08:00
0ba77a0d87 colorful is the switch to level label 2019-03-12 12:12:59 +08:00
661dcbb6ca router logger modify 2019-03-12 12:11:25 +08:00
578440a18d add ansicolor to beego 2019-03-12 12:09:41 +08:00
93485df3d2 color should be false by default otherwise the http logger's color would be wrong 2019-03-08 14:42:06 +08:00
121fab61f1 ResetColor function 2019-03-08 12:18:45 +08:00
915eec7943 Merge branch 'develop' of github.com:astaxie/beego into develop 2019-03-08 12:16:00 +08:00
8432a1c758 better comment for color map 2019-03-08 12:10:57 +08:00
4a5e108527 logger color function refactor,easy to read and run more quickly 2019-03-08 11:50:30 +08:00
6dd5171fdf logger color function refactor,easy to read and run more quickly 2019-03-08 11:08:39 +08:00
c2b6cb5c3a Merge pull request #3560 from JessonChan/develop
TestToJson bug fixed
2019-03-08 00:18:39 +08:00
1f93040af6 Merge remote-tracking branch 'upstream/develop' into develop 2019-03-07 18:07:37 +08:00
52f8ccd06c TestToJson bug fixed 2019-03-07 17:15:30 +08:00
3b86feab8a Merge pull request #3555 from hellomrleeus/develop
spelling mistake of word "Header"
2019-03-06 09:58:54 +08:00
aba51d99a1 spelling mistake of word "Header" 2019-03-04 11:05:29 +08:00
8454e8417e Allow forked beego project to pass travis ci builds 2019-03-01 14:00:20 +00:00
422e8285b5 Merge pull request #3494 from nuczzz/develop
simplify beego grace with http.Shutdown
2019-02-26 16:31:40 +08:00
bb6ca6b100 Merge pull request #3522 from saromanov/check-input-data
SessionRead: check of the length for input sid variable
2019-02-25 23:17:57 +08:00
1483c1f545 Merge pull request #3524 from Quasilyte/quasilyte/bytesreader
config/yaml: s/bytes.NewBuffer/bytes.NewReader/
2019-02-25 23:16:39 +08:00
3d6a68de77 Merge pull request #3535 from gadelkareem/develop
Make LogAccess() function public
2019-02-25 23:11:14 +08:00
387d387080 Merge pull request #3547 from snedzad/patch-1
Register .gohtml extension
2019-02-25 11:43:37 +08:00
94ed35e781 Register .gohtml extension
Goland as of 2018.3 seems to support only '.tpl.gohtml" and ".gohtml" templates. The regular ".tpl" templates will be rendered as plain text files which is not acceptable.
2019-02-23 22:49:32 +01:00
8995b291a9 Make LogAccess public 2019-02-14 16:30:25 +01:00
1942438b22 Merge pull request #3 from astaxie/develop
develop
2019-02-14 16:23:35 +01:00
40d653e659 Merge pull request #3533 from tvanriper/patch-1
APIBaiscAuth is misspelling of APIBasicAuth
2019-02-13 02:10:25 +08:00
ddcd28e67f APIBaiscAuth is misspelling of APIBasicAuth
Corrected APIBaiscAuth to APIBasicAuth.  Maintained old name (forwarding to new one) for those still using the previous API function name.
2019-02-12 13:00:11 -05:00
65f587d5e9 fix ineffectual assignment 2019-02-12 19:05:22 +08:00
2fefd8cbbf update len 2019-02-12 18:53:34 +08:00
ba17bdd366 add ignore auto_now_add field when update 2019-02-12 18:05:29 +08:00
c998e52cc0 config/yaml: s/bytes.NewBuffer/bytes.NewReader/
When io.Reader is required out of []byte,
it's better to use bytes.NewReader than bytes.NewBuffer.

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>
2019-02-10 20:37:43 +03:00
3224369ac9 Merge pull request #3523 from Quasilyte/quasilyte/boolExprSimplify
Simplify boolean expressions
2019-02-09 16:16:31 +01:00
67666dbe0f all: simplify boolean expressions
- !(a == b) => a != b
- !(a != b) => a == b

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>
2019-02-09 17:18:59 +03:00
d7430eb921 SessionRead: check of the length for input sid variable 2019-02-04 11:03:27 +05:00
26a6b426f1 Merge pull request #3487 from duyazhe/patch-1
Update orm_log.go
2019-02-04 11:26:42 +08:00
6f35ce67f7 Merge pull request #3493 from bharat-p/3492-add-db-stats-to-ormer
Add DBStats method wrapper to provide sql.DBStats when using ormer
2019-02-04 11:24:13 +08:00
3406d58797 Merge pull request #3503 from wtospit/develop
fix: when parse post form it didnt parse fields correctly
2019-02-04 11:23:23 +08:00
7925458fc0 Merge pull request #3502 from Quasilyte/patch-1
cache: remove excessive type assertions
2019-02-02 16:43:04 +08:00
b6854aaf9f Merge pull request #3506 from DennisMao/hotfixFileCachePanic
fix panic cause by the map
2019-02-02 16:38:11 +08:00
656f595226 Merge pull request #3508 from Quasilyte/patch-2
replace unchecked Compile calls with MustCompile
2019-02-02 16:36:15 +08:00
280aaf9d3b Merge pull request #3519 from zav8/add_support_for_pointer_fields
add support for pointer fields of structs to method QueryRows()
2019-02-02 16:35:41 +08:00
7abdb05f91 little fix 2019-02-01 15:39:40 +08:00
af4464ce58 add support for pointer fields of structs to method QueryRows() 2019-02-01 15:27:10 +08:00
6ca0978777 replace unchecked Compile calls with MustCompile
For constant patterns and especially when errors are ignored,
`regexp.MustCompile` is a better choice than `regexp.Compile`.

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>
2019-01-26 14:13:53 +03:00
8506194d2c fix panic cause by the map 2019-01-25 19:08:39 +08:00
3bd7614ade refactoring code after discussion 2019-01-25 11:00:24 +07:00
bd1b421491 fix: adding test for issue due to testing is not reflect changed 2019-01-25 09:04:01 +07:00
920207f72c add testing for ParseForm when form post has a slice in body 2019-01-25 00:38:14 +07:00
12fdc04f1b fix: when parse post form it didnt parse fields which have same name but the first index is empty but another is not 2019-01-25 00:15:40 +07:00
d0c744ae6a Merge pull request #3499 from JessonChan/develop
fix download filename(chinese) bug
2019-01-24 17:27:29 +08:00
dc07fa7085 cache: remove excessive type assertions
Assign type switch variable to get properly-typed value
inside case clauses.

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>
2019-01-24 08:52:30 +03:00
1c893996c0 improve the download func code 2019-01-23 12:36:14 +08:00
a9ffc2a078 https://github.com/astaxie/beego/issues/3446
Use UTF-8 as the encoding of the "filename*" parameter, when
                           present, because at least one existing implementation only
                           implements that encoding.
2019-01-23 12:30:57 +08:00
712bbfe575 Merge pull request #3498 from JessonChan/develop
change gosimple to staticcheck
2019-01-23 11:31:37 +08:00
0145fe3486 remove SA1024 staticchek 2019-01-22 20:52:06 +08:00
2a579eb27c staticcheck checks -ST1003 remove 2019-01-22 20:41:07 +08:00
5b42afa324 modiyf staticcheck checks 2019-01-22 20:30:05 +08:00
97713849a1 delete stackcheck config file and ignore some staticcheck checks 2019-01-22 20:21:00 +08:00
abc9c38224 ignore some staticcheck checks 2019-01-22 20:04:30 +08:00
f237ff049a redis_sentinel test ignore 2019-01-22 19:45:32 +08:00
00264650b5 modify travis and redis_sentinel test 2019-01-22 19:29:53 +08:00
2956d33bab no need gosimple 2019-01-22 19:12:25 +08:00
c3eca637fb no need gosimple 2019-01-22 19:10:23 +08:00
0d54bbff02 make staticcheck happy 2019-01-22 19:09:57 +08:00
e65a9cbc00 Gosimple has been deprecated. Please use staticcheck instead. 2019-01-22 18:01:14 +08:00
3ed82c0882 Gosimple has been deprecated. Please use staticcheck instead. 2019-01-22 17:43:55 +08:00
475feb7e24 camel name style 2019-01-22 16:25:17 +08:00
30b80cba92 errors is better style 2019-01-22 16:23:10 +08:00
fe519bd2a0 update tls KeepAlive setting 2019-01-20 11:17:10 +08:00
f508f8d959 Nil check 2019-01-18 16:51:40 -08:00
e295c3c7c3 add shutdown log 2019-01-18 19:50:22 +08:00
313be996cd call cancel after shutdown 2019-01-18 19:33:45 +08:00
2ae480556d Add DBStats method wrapper to provide sql.DBStats when using ormer 2019-01-17 09:56:42 -08:00
7173fd7490 modify http graceful 2019-01-17 20:17:57 +08:00
d792536c23 Update orm_log.go
orm log支持用户自定义函数处理
2019-01-14 15:14:11 +08:00
80aabdd372 Merge pull request #3472 from sangheee/develop
fix: negative WaitGroup counter has not been resolved yet.
2019-01-08 23:54:40 +08:00
6892369cc6 Merge pull request #3464 from zhl11b/develop
手机号起始三位补全
2019-01-08 23:54:00 +08:00
5b80a56c36 Merge pull request #3468 from DennisMao/hotfix/UpdateDependencyGoes
update dependency
2019-01-08 23:52:39 +08:00
bf15535a5b fix panic: sync: negative WaitGroup counter 2019-01-03 22:44:32 +09:00
edb1c52dee fix function changes 2019-01-02 17:01:46 +08:00
6e16b8cdcf fix ci losing dependency 2019-01-02 16:46:27 +08:00
24215fb3eb update dependency 2019-01-02 15:37:41 +08:00
d02699a189 add new test case for china mobile phone 2018-12-30 20:51:21 +08:00
2034d1b101 联通171,175 电信173 2018-12-29 17:41:59 +08:00
5fe19d639f Merge pull request #3456 from japettyjohn/master
Fix bug in memcache sessions
2018-12-28 22:35:11 +08:00
1dea80d4ea Fixed error handling in memcache sessions 2018-12-27 16:20:26 -08:00
28f0008075 Merge pull request #3434 from zav8/add_sql_null_support
Add support for field of type sql.NullXxx in rawSet.setFieldValue()
2018-12-19 22:26:23 +08:00
ffe1b00baf Merge branch 'develop' of https://github.com/astaxie/beego into add_sql_null_support 2018-12-18 10:41:17 +08:00
d2c289193a add test case for QueryRow and QueryRows 2018-12-18 10:37:41 +08:00
f867583256 Merge pull request #3433 from DennisMao/FixOrmDescriptionTag
Fix Issue #3337
2018-12-18 09:20:36 +08:00
98dfb92e17 Merge pull request #3443 from zhiquan911/develop
add GetProvider
2018-12-18 09:10:17 +08:00
c8f22be675 Add redis sentinel session support (#3427)
Add Redis Sentinel session support
2018-12-17 14:47:47 +01:00
f03a7d1128 add GetProvider 2018-12-13 15:37:19 +08:00
6da4a66c20 merge switch cases 2018-12-06 16:09:39 +08:00
5e4241fc87 add support for field of type sql.NullXxx in rawSet.setFieldValue() 2018-12-06 16:07:07 +08:00
bf468c8d0c fix format 2018-12-06 10:57:32 +08:00
0d77a3f8d2 FixOrmDescrptionTag 2018-12-06 10:49:50 +08:00
1b6edafc96 Merge pull request #3412 from astaxie/develop
v1.11.1
2018-11-30 21:54:26 +08:00
8152ade1b6 Merge pull request #3419 from xpzouying/close_fs_when_is_not_nil
Close fs when is not nil
2018-11-30 16:57:49 +08:00
6350f8b904 Merge pull request #3420 from xpzouying/format_list_travis_yml
format list in .travis.yml
2018-11-30 16:57:25 +08:00
0c5398a19c update travis 2018-11-30 16:42:44 +08:00
4b656268d3 travis 2018-11-28 16:17:53 +08:00
10729a1fc5 update vendor & module 2018-11-28 16:05:15 +08:00
cdb3ef808f format list in .travis.yml 2018-11-28 08:59:27 +08:00
a5a2471f2c close fs only when fs open without error
panic when close nil filesystem
2018-11-28 08:55:20 +08:00
6282747f6d update vendor 2018-11-27 14:11:14 +08:00
d5fd5cad38 Merge pull request #3417 from xpzouying/update_code_format
better format
2018-11-27 14:01:59 +08:00
fab7c6b6d0 better format
- add comments for public function
- format import order in admin.go
- better format for NewControllerRegister in router.go
2018-11-26 23:19:05 +08:00
42ade6aa49 v1.11.1 2018-11-22 13:10:52 +08:00
55d9b69cd9 update mod 2018-11-22 13:08:39 +08:00
2a8d6f943f Merge pull request #3408 from nlimpid/develop
add different column name parse strategy
2018-11-21 17:18:39 +08:00
6b0155c4fb add different column name parse strategy 2018-11-20 22:47:56 +08:00
e22a5143bc Merge pull request #3403 from nlimpid/develop
add context for db operation
2018-11-20 15:44:25 +08:00
a17eb54515 Merge pull request #3405 from coldnight/feature-add-elapsed-in-response
Add .Elapsed in context.ResponseWriter for monitor purpose
2018-11-20 15:39:13 +08:00
d5cf1050db check qs is nil before get forContext 2018-11-19 23:42:56 +08:00
b021686521 Add .Elapsed in context.ResponseWriter for monitor purpose
With this commit we can record per requests's elapsed time,
so we can easy to monitor that by use a filter.
2018-11-19 16:38:14 +08:00
b0e2bbce2a Merge pull request #3391 from astaxie/develop
V1.11.0
2018-11-19 14:40:43 +08:00
7a50ea7e36 Merge pull request #3395 from astaxie/AddTestForSnakeString
#3192 AddTestSnakeString
2018-11-18 21:59:48 +08:00
3070cfc60b Merge pull request #3396 from astaxie/FixAnnotationOnSnakeString
Fix #3192
2018-11-18 21:59:35 +08:00
e56d1b718f add context for db operation 2018-11-18 21:54:25 +08:00
c4c3067a31 Update utils.go 2018-11-15 15:16:41 +08:00
81346fe641 Update utils_test.go 2018-11-15 14:50:38 +08:00
1a66ad56c6 Merge pull request #3317 from HSoshiant/master
Lock
2018-11-14 19:24:23 +08:00
31f2adb79d Update session.go 2018-11-14 19:24:10 +08:00
f514ae309b Update session.go 2018-11-14 19:23:10 +08:00
277d3d98e3 v1.11.0 2018-11-14 17:01:12 +08:00
2c46877b36 Merge pull request #3390 from astaxie/FixDataRaceOnCache
Fix  #3354
2018-11-14 15:58:20 +08:00
cfe54a02c5 Merge pull request #3388 from s00500/develop
Closes #2515 Autodetect timezone in NewOrmWithDB()
2018-11-13 16:48:18 +08:00
55f390d08a Merge pull request #3387 from wuyumin/master
dynamically add task #2708
2018-11-13 14:51:41 +08:00
cf31222643 Merge pull request #3386 from DennisMao/AddTransportSetting
add transport setting
2018-11-13 14:50:28 +08:00
9e036bcab5 Update memory.go 2018-11-13 14:43:23 +08:00
d02170e3cb Closes #2515 Autodetect timezone in NewOrmWithDB() 2018-11-12 10:18:05 +01:00
2d6f1af1a5 dynamically add task 2018-11-12 17:17:06 +08:00
430457609f Update httplib_test.go 2018-11-12 13:04:17 +08:00
0333e26b3e Merge pull request #3382 from lxShaDoWxl/add_custom_fs_template
Add custom fs template
2018-11-10 22:44:28 +08:00
d0d28566b9 chore(GoMod): add dependency go-bindata-assetfs in vendor dir 2018-11-10 13:41:47 +06:00
872b787e6c refactor(FileSystem): add comments function 2018-11-10 12:34:53 +06:00
01a99edf80 chore(GoMod): add dependency go-bindata-assetfs 2018-11-10 12:26:19 +06:00
6050d37d2a Merge remote-tracking branch 'me/develop' into add_custom_fs_template 2018-11-10 11:39:28 +06:00
876dce8e54 fix the routerInfo is nil 2018-11-09 18:03:26 +08:00
24885c28f2 fix the comments update 2018-11-09 17:54:20 +08:00
5ea04bdfd3 update mod 2018-11-09 12:37:28 +08:00
9fdc1eaf3a Merge pull request #3352 from SongLiangChen/develop
add sessionid prefix
2018-11-08 23:29:05 +08:00
6b5a70d246 Merge pull request #3378 from nukc/develop
orm: support filter raw sql
2018-11-08 23:28:30 +08:00
8391d26220 Merge pull request #3383 from LockGit/develop
security question, fix arbitrary file read
2018-11-08 23:21:18 +08:00
f193e313a3 refactor(FileSystem): using single-line if 2018-11-07 20:21:34 +06:00
9ac4928113 refactor(Template): a detailed description of the error 2018-11-07 20:20:10 +06:00
9865779f14 security question, fix arbitrary file read 2018-11-07 11:31:27 +08:00
aa6d0f9f0b fix(Template): correct check error 2018-11-06 21:40:43 +06:00
68b0bd98fd fix(Template): error handling when reading files 2018-11-06 20:41:05 +06:00
3447798494 fix(Template): dependencies in travis 2018-11-06 20:18:48 +06:00
ca1b96f986 feat(Template): use interface http.FileSystem 2018-11-06 20:06:21 +06:00
771fe35431 feat(Template): testing fs bindata 2018-11-05 22:58:59 +06:00
2f00ad1602 fix(Template): close open file 2018-11-05 22:51:20 +06:00
f740b71ded fix(Template): remove duplicate check open/exists 2018-11-05 21:08:03 +06:00
7aae58a543 feat(Template): create interface FileSystem for to create custom fs 2018-11-05 21:05:19 +06:00
c8da875f83 add sessionId prefix 2018-11-05 09:51:27 +08:00
501d8a97f6 add sessionId prefix 2018-11-05 09:50:19 +08:00
736e66fcda orm: support filter raw sql 2018-11-05 03:47:21 +08:00
d3ad810f16 add sessionId prefix 2018-10-29 13:35:31 +08:00
abc8b78065 add sessionId prefix 2018-10-29 12:18:06 +08:00
f64e6b72e9 Merge pull request #3362 from SmartBrave/develop
modify qbs to qps
2018-10-28 20:01:23 +08:00
4e83b4400a Merge pull request #3371 from HaraldNordgren/go_versions
Bump Go versions and use 1.n.x to get latest minor versions
2018-10-28 19:49:27 +08:00
f6f61513a1 Bump Go versions and use 1.n.x to get latest minor versions 2018-10-28 00:58:36 +02:00
8217817a0b modify qbs to qps 2018-10-24 10:38:59 +08:00
833f54d818 Merge pull request #3319 from Colstuwjx/annotated-filter
Annotated filter
2018-10-23 13:45:18 +08:00
706c086bc5 Merge pull request #3345 from dingyuanhong/develop
fix / can use dynamic directory
2018-10-23 13:43:45 +08:00
187add9b84 add sessionid prefix 2018-10-10 11:02:45 +08:00
5b8e468a13 Merge pull request #3344 from akhedrane/patch-1
duo to #3278 numRow should be 0
2018-10-05 21:16:18 +08:00
dff9c8f5fa fix / can use dynamic directory 2018-10-01 15:16:35 +08:00
21a8623002 duo to #3278 numRow should be 0 2018-09-30 10:45:18 +01:00
ea9c5822e6 Merge pull request #3292 from SongLiangChen/master
Read over 4096 length values
2018-09-30 16:03:45 +08:00
ad0d166d46 Merge pull request #3295 from GNURub/feature/outputWithformat
feature/outputwithformat
2018-09-30 16:01:07 +08:00
2c2ace9a60 Merge pull request #3313 from oiooj/pr-module
support go modules
2018-09-30 15:55:57 +08:00
8134a89e81 Merge pull request #3333 from akhedrane/patch-1
Return error when wrong filtering field
2018-09-30 15:45:23 +08:00
e342a0099f Merge pull request #3340 from GNURub/feature/update-travis
Add go 1.11 version
2018-09-30 15:44:46 +08:00
c4ed5030da Merge pull request #3339 from GNURub/hotfix/redis-uri
Support redis URI format
2018-09-30 15:43:41 +08:00
7b9c24567d Merge pull request #3335 from GNURub/hotfix/ranking-response-times
Added link to time ranking
2018-09-30 15:42:05 +08:00
6906c5ce30 Updated travis go version 2018-09-27 18:27:38 +02:00
e4605f232b Support redis URI 2018-09-26 18:05:09 +02:00
6092e737a1 Added link to ranking 2018-09-24 18:33:56 +02:00
0e4d954fa7 Return error when wrong filtering field
When end user put wrong filtering field ORM should return error instead of Panic()
so developers can handle this error.
2018-09-23 12:18:15 +02:00
1cbba4d56f Add annotated filter, support @Import, @Filter.
Signed-off-by: Colstuwjx <Colstuwjx@gmail.com>
2018-09-07 15:59:57 +08:00
kun
cf5d1f3f3c support go modules 2018-09-05 14:05:16 +08:00
1097ac3682 GetProvider 2018-08-28 15:12:28 -04:00
755cc98ef7 Fix content type 2018-08-21 12:32:16 +02:00
5c407ff2e3 Add map shortcut and ServeFormatted method in output 2018-08-20 22:55:50 +02:00
8f455ef199 Read over 4096 length values 2018-08-17 11:40:00 +08:00
7e0649d661 Merge pull request #3289 from nezorflame/patch-1
Remove panic from Redirect()
2018-08-16 09:22:22 +08:00
1a3dcb4f84 Remove panic from Redirect()
This `panic(ErrAbort)` is unnecessary in `Redirect` function and causes problems in the production code.
2018-08-12 05:11:03 +03:00
b606f1f73f Merge pull request #3283 from JessonChan/develop
typo fixed
2018-08-09 17:05:25 +08:00
8241f219fd Merge pull request #3278 from hurisheng/revert-3247-develop
Revert "send ErrNoRows if the query returns zero rows ... in method orm_query…"
2018-08-09 17:05:01 +08:00
dea45a3d6c fix TestAll() 2018-08-07 16:36:27 +08:00
34a812d45f typo fixed
#3260
2018-08-07 12:08:15 +08:00
842336834f Merge pull request #3275 from WiFeng/master
fix bug of tasks that only some but not all are executed
2018-08-07 10:07:29 +08:00
58fe012446 Merge pull request #3274 from gombaniro/improvement/make-TestSet-on-map-self-contained
breaks dependency on TestNewBeeMap Testcase
2018-08-07 10:02:29 +08:00
bf0d40bca6 Merge pull request #3280 from GNURub/feature/pusher
Add access to pusher
2018-08-07 09:58:56 +08:00
48e6658eca Add access to pusher 2018-08-03 12:33:46 +02:00
1bd3fb7a33 Revert "send ErrNoRows if the query returns zero rows ... in method orm_query…" 2018-08-03 13:35:48 +08:00
d86410a631 fix bug of tasks that only some but not all are executed 2018-08-01 11:20:22 +08:00
6b62502b99 breaks rely on TestNewBeeMap 2018-08-01 10:24:39 +08:00
053a075344 Merge pull request #3271 from astaxie/develop
add vendor gopkg.in/yaml.v2
2018-07-31 21:18:48 +08:00
9dd7d19ce7 add vendor gopkg.in/yaml.v2 2018-07-31 21:18:07 +08:00
efe0f67388 Merge pull request #3267 from astaxie/develop
beego 1.10.1
2018-07-31 20:52:47 +08:00
de66d2bdfd beego 1.10.1 2018-07-31 20:09:08 +08:00
0e4fe4d177 Merge pull request #3270 from astaxie/revert-3269-master
Revert "hible"
2018-07-31 20:07:18 +08:00
6d84db1e93 Revert "hible" 2018-07-31 20:07:03 +08:00
2486f3826a Merge pull request #3269 from 514366607/master
hible
2018-07-31 20:06:55 +08:00
d7b8aa8b52 only add golang.org vendor 2018-07-31 19:25:43 +08:00
c7c0b01ec5 hible
cache add function
// IncrBy increase counter by num.
IncrBy(key string, num int)
// DecrBy decrease counter by num.
DecrBy(key string, num int)
2018-07-31 17:19:09 +08:00
6d69047fff update go vet 2018-07-30 15:13:04 +08:00
787ab12605 Merge branch 'develop' of https://github.com/astaxie/beego into develop 2018-07-30 15:01:58 +08:00
f4112accef update go vet 2018-07-30 15:01:49 +08:00
42c394e28b Merge pull request #3263 from guomao545/master
Support return middle level value
2018-07-30 12:27:05 +08:00
5051d902fb Merge pull request #2986 from oxgo/hourlylog
add hourly log rotate
2018-07-30 12:26:37 +08:00
48acfa08be add vendor 2018-07-30 12:05:51 +08:00
39fc30b8b2 Support return middle level value
fix multilevel yaml config can't correct return middle level value bug
2018-07-27 15:33:24 +08:00
two
046cb248e0 edit test case 2018-07-26 15:08:14 +08:00
two
31c746d9d7 fix all confict 2018-07-26 14:34:25 +08:00
two
38a2f32252 fix one confict 2018-07-26 14:29:26 +08:00
d55f54a8ab Merge pull request #3149 from liaoishere/feature/support-begintx
Support DB.BeginTx in go 1.8
2018-07-23 20:25:17 +08:00
feb0e67fd7 upgrade go version from 1.9.2 to 1.9.7 in test env.
upgrade to avoid bug: https://github.com/golang/go/issues/22976

Signed-off-by: Penghui Liao <liaoishere@gmail.com>
2018-07-23 15:58:12 +08:00
5acc56648d Merge branch 'develop' into feature/support-begintx 2018-07-20 23:16:12 +08:00
b8868d6d2d remove unnecessary conversion
Signed-off-by: Penghui Liao <liaoishere@gmail.com>
2018-07-20 17:07:17 +08:00
30bbc81a2e fix test case that calls All()
Signed-off-by: Penghui Liao <liaoishere@gmail.com>
2018-07-20 16:51:36 +08:00
1a3f1d66c1 rename orm_go18.go to orm.go
Signed-off-by: Penghui Liao <liaoishere@gmail.com>
2018-07-20 16:36:06 +08:00
6bdd152d91 upgrade postgres in travis
Signed-off-by: Penghui Liao <liaoishere@gmail.com>
2018-07-20 16:36:06 +08:00
443c77b303 support DB.BeginTx of golang 1.8
Signed-off-by: Penghui Liao <liaoishere@gmail.com>
2018-07-20 16:36:06 +08:00
0dff771707 fix unquoted identifier that may be misleading in postgres
Signed-off-by: Penghui Liao <liaoishere@gmail.com>
2018-07-20 16:36:06 +08:00
1a42154c64 add file test 2017-12-20 17:54:40 +08:00
e81cca304b add file test 2017-12-20 16:19:58 +08:00
07aa97aa9a add hourly rotate file.go 2017-12-20 15:56:36 +08:00
7886e69236 Update sess_file.go 2017-04-17 12:37:54 -04:00
106 changed files with 3797 additions and 1613 deletions

View File

@ -1,4 +0,0 @@
github.com/astaxie/beego/*/*:S1012
github.com/astaxie/beego/*:S1012
github.com/astaxie/beego/*/*:S1007
github.com/astaxie/beego/*:S1007

View File

@ -1,17 +1,26 @@
language: go
go:
- "1.9.2"
- "1.10.3"
- "1.13.x"
services:
- redis-server
- mysql
- postgresql
- memcached
env:
global:
- GO_REPO_FULLNAME="github.com/astaxie/beego"
matrix:
- ORM_DRIVER=sqlite3 ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db
- ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
before_install:
# link the local repo with ${GOPATH}/src/<namespace>/<repo>
- GO_REPO_NAMESPACE=${GO_REPO_FULLNAME%/*}
# relies on GOPATH to contain only one directory...
- mkdir -p ${GOPATH}/src/${GO_REPO_NAMESPACE}
- ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH}/src/${GO_REPO_FULLNAME}
- cd ${GOPATH}/src/${GO_REPO_FULLNAME}
# get and build ssdb
- git clone git://github.com/ideawu/ssdb.git
- cd ssdb
- make
@ -27,36 +36,38 @@ install:
- go get github.com/beego/goyaml2
- go get gopkg.in/yaml.v2
- go get github.com/belogik/goes
- go get github.com/siddontang/ledisdb/config
- go get github.com/siddontang/ledisdb/ledis
- go get github.com/ledisdb/ledisdb
- go get github.com/ssdb/gossdb/ssdb
- go get github.com/cloudflare/golz4
- go get github.com/gogo/protobuf/proto
- go get github.com/Knetic/govaluate
- go get github.com/casbin/casbin
- go get -u honnef.co/go/tools/cmd/gosimple
- go get github.com/elazarl/go-bindata-assetfs
- go get github.com/OwnLocal/goes
- go get github.com/shiena/ansicolor
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u github.com/mdempsky/unconvert
- go get -u github.com/gordonklaus/ineffassign
- go get -u github.com/golang/lint/golint
- go get -u golang.org/x/lint/golint
- go get -u github.com/go-redis/redis
before_script:
- psql --version
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
- sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
- sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
- sh -c "if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi"
- sh -c "if [ $(go version) == *1.[5-9]* ]; then go tool vet .; fi"
- sh -c "go get github.com/golang/lint/golint; golint ./...;"
- sh -c "go list ./... | grep -v vendor | xargs go vet -v"
- mkdir -p res/var
- ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
after_script:
-killall -w ssdb-server
- killall -w ssdb-server
- rm -rf ./res/var/*
script:
- go test -v ./...
- gosimple -ignore "$(cat .gosimpleignore)" $(go list ./... | grep -v /vendor/)
- staticcheck -show-ignored -checks "-ST1017,-U1000,-ST1005,-S1034,-S1012,-SA4006,-SA6005,-SA1019,-SA1024"
- unconvert $(go list ./... | grep -v /vendor/)
- ineffassign .
- find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
- golint ./...
addons:
postgresql: "9.4"
postgresql: "9.6"

View File

@ -33,6 +33,8 @@ Congratulations! You've just built your first **beego** app.
###### Please see [Documentation](http://beego.me/docs) for more.
###### [beego-example](https://github.com/beego-dev/beego-example)
## Features
* RESTful support
@ -54,6 +56,7 @@ Congratulations! You've just built your first **beego** app.
* [http://beego.me/community](http://beego.me/community)
* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232)
* QQ Group Group ID:523992905
## License

View File

@ -20,10 +20,11 @@ import (
"fmt"
"net/http"
"os"
"reflect"
"text/template"
"time"
"reflect"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/astaxie/beego/grace"
"github.com/astaxie/beego/logs"
@ -35,7 +36,7 @@ import (
var beeAdminApp *adminApp
// FilterMonitorFunc is default monitor filter when admin module is enable.
// if this func returns, admin module records qbs for this request by condition of this function logic.
// if this func returns, admin module records qps for this request by condition of this function logic.
// usage:
// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool {
// if method == "POST" {
@ -56,29 +57,31 @@ func init() {
beeAdminApp = &adminApp{
routers: make(map[string]http.HandlerFunc),
}
// keep in mind that all data should be html escaped to avoid XSS attack
beeAdminApp.Route("/", adminIndex)
beeAdminApp.Route("/qps", qpsIndex)
beeAdminApp.Route("/prof", profIndex)
beeAdminApp.Route("/healthcheck", healthcheck)
beeAdminApp.Route("/task", taskStatus)
beeAdminApp.Route("/listconf", listConf)
beeAdminApp.Route("/metrics", promhttp.Handler().ServeHTTP)
FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true }
}
// AdminIndex is the default http.Handler for admin module.
// it matches url pattern "/".
func adminIndex(rw http.ResponseWriter, r *http.Request) {
func adminIndex(rw http.ResponseWriter, _ *http.Request) {
execTpl(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
}
// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter.
// it's registered with url pattern "/qbs" in admin module.
func qpsIndex(rw http.ResponseWriter, r *http.Request) {
// QpsIndex is the http.Handler for writing qps statistics map result info in http.ResponseWriter.
// it's registered with url pattern "/qps" in admin module.
func qpsIndex(rw http.ResponseWriter, _ *http.Request) {
data := make(map[interface{}]interface{})
data["Content"] = toolbox.StatisticsMap.GetMap()
// do html escape before display path, avoid xss
if content, ok := (data["Content"]).(map[string]interface{}); ok {
if content, ok := (data["Content"]).(M); ok {
if resultLists, ok := (content["Data"]).([][]string); ok {
for i := range resultLists {
if len(resultLists[i]) > 0 {
@ -104,10 +107,10 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
data := make(map[interface{}]interface{})
switch command {
case "conf":
m := make(map[string]interface{})
m := make(M)
list("BConfig", BConfig, m)
m["AppConfigPath"] = appConfigPath
m["AppConfigProvider"] = appConfigProvider
m["AppConfigPath"] = template.HTMLEscapeString(appConfigPath)
m["AppConfigProvider"] = template.HTMLEscapeString(appConfigProvider)
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(configTpl))
tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
@ -128,14 +131,14 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
case "filter":
var (
content = map[string]interface{}{
content = M{
"Fields": []string{
"Router Pattern",
"Filter Function",
},
}
filterTypes = []string{}
filterTypeData = make(map[string]interface{})
filterTypeData = make(M)
)
if BeeApp.Handlers.enableFilter {
@ -152,8 +155,9 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
resultList := new([][]string)
for _, f := range bf {
var result = []string{
f.pattern,
utils.GetFuncName(f.filterFunc),
// void xss
template.HTMLEscapeString(f.pattern),
template.HTMLEscapeString(utils.GetFuncName(f.filterFunc)),
}
*resultList = append(*resultList, result)
}
@ -173,7 +177,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
}
}
func list(root string, p interface{}, m map[string]interface{}) {
func list(root string, p interface{}, m M) {
pt := reflect.TypeOf(p)
pv := reflect.ValueOf(p)
if pt.Kind() == reflect.Ptr {
@ -196,11 +200,11 @@ func list(root string, p interface{}, m map[string]interface{}) {
}
// PrintTree prints all registered routers.
func PrintTree() map[string]interface{} {
func PrintTree() M {
var (
content = map[string]interface{}{}
content = M{}
methods = []string{}
methodsData = make(map[string]interface{})
methodsData = make(M)
)
for method, t := range BeeApp.Handlers.routers {
@ -208,8 +212,8 @@ func PrintTree() map[string]interface{} {
printTree(resultList, t)
methods = append(methods, method)
methodsData[method] = resultList
methods = append(methods, template.HTMLEscapeString(method))
methodsData[template.HTMLEscapeString(method)] = resultList
}
content["Data"] = methodsData
@ -228,21 +232,21 @@ func printTree(resultList *[][]string, t *Tree) {
if v, ok := l.runObject.(*ControllerInfo); ok {
if v.routerType == routerTypeBeego {
var result = []string{
v.pattern,
fmt.Sprintf("%s", v.methods),
v.controllerType.String(),
template.HTMLEscapeString(v.pattern),
template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)),
template.HTMLEscapeString(v.controllerType.String()),
}
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeRESTFul {
var result = []string{
v.pattern,
fmt.Sprintf("%s", v.methods),
template.HTMLEscapeString(v.pattern),
template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)),
"",
}
*resultList = append(*resultList, result)
} else if v.routerType == routerTypeHandler {
var result = []string{
v.pattern,
template.HTMLEscapeString(v.pattern),
"",
"",
}
@ -267,7 +271,7 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
result bytes.Buffer
)
toolbox.ProcessInput(command, &result)
data["Content"] = result.String()
data["Content"] = template.HTMLEscapeString(result.String())
if format == "json" && command == "gc summary" {
dataJSON, err := json.Marshal(data)
@ -281,7 +285,7 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
return
}
data["Title"] = command
data["Title"] = template.HTMLEscapeString(command)
defaultTpl := defaultScriptsTpl
if command == "gc summary" {
defaultTpl = gcAjaxTpl
@ -291,12 +295,12 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
// Healthcheck is a http.Handler calling health checking and showing the result.
// it's in "/healthcheck" pattern in admin module.
func healthcheck(rw http.ResponseWriter, req *http.Request) {
func healthcheck(rw http.ResponseWriter, _ *http.Request) {
var (
result []string
data = make(map[interface{}]interface{})
resultList = new([][]string)
content = map[string]interface{}{
content = M{
"Fields": []string{"Name", "Message", "Status"},
}
)
@ -305,13 +309,13 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) {
if err := h.Check(); err != nil {
result = []string{
"error",
name,
err.Error(),
template.HTMLEscapeString(name),
template.HTMLEscapeString(err.Error()),
}
} else {
result = []string{
"success",
name,
template.HTMLEscapeString(name),
"OK",
}
}
@ -335,16 +339,16 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
if taskname != "" {
if t, ok := toolbox.AdminTaskList[taskname]; ok {
if err := t.Run(); err != nil {
data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
data["Message"] = []string{"error", template.HTMLEscapeString(fmt.Sprintf("%s", err))}
}
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus())}
data["Message"] = []string{"success", template.HTMLEscapeString(fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus()))}
} else {
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
data["Message"] = []string{"warning", template.HTMLEscapeString(fmt.Sprintf("there's no task which named: %s", taskname))}
}
}
// List Tasks
content := make(map[string]interface{})
content := make(M)
resultList := new([][]string)
var fields = []string{
"Task Name",
@ -355,10 +359,10 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
}
for tname, tk := range toolbox.AdminTaskList {
result := []string{
tname,
tk.GetSpec(),
tk.GetStatus(),
tk.GetPrev().String(),
template.HTMLEscapeString(tname),
template.HTMLEscapeString(tk.GetSpec()),
template.HTMLEscapeString(tk.GetStatus()),
template.HTMLEscapeString(tk.GetPrev().String()),
}
*resultList = append(*resultList, result)
}

View File

@ -6,7 +6,7 @@ import (
)
func TestList_01(t *testing.T) {
m := make(map[string]interface{})
m := make(M)
list("BConfig", BConfig, m)
t.Log(m)
om := oldMap()
@ -18,8 +18,8 @@ func TestList_01(t *testing.T) {
}
}
func oldMap() map[string]interface{} {
m := make(map[string]interface{})
func oldMap() M {
m := make(M)
m["BConfig.AppName"] = BConfig.AppName
m["BConfig.RunMode"] = BConfig.RunMode
m["BConfig.RouterCaseSensitive"] = BConfig.RouterCaseSensitive
@ -52,6 +52,8 @@ func oldMap() map[string]interface{} {
m["BConfig.WebConfig.DirectoryIndex"] = BConfig.WebConfig.DirectoryIndex
m["BConfig.WebConfig.StaticDir"] = BConfig.WebConfig.StaticDir
m["BConfig.WebConfig.StaticExtensionsToGzip"] = BConfig.WebConfig.StaticExtensionsToGzip
m["BConfig.WebConfig.StaticCacheFileSize"] = BConfig.WebConfig.StaticCacheFileSize
m["BConfig.WebConfig.StaticCacheFileNum"] = BConfig.WebConfig.StaticCacheFileNum
m["BConfig.WebConfig.TemplateLeft"] = BConfig.WebConfig.TemplateLeft
m["BConfig.WebConfig.TemplateRight"] = BConfig.WebConfig.TemplateRight
m["BConfig.WebConfig.ViewsPath"] = BConfig.WebConfig.ViewsPath

15
app.go
View File

@ -123,14 +123,13 @@ func (app *App) Run(mws ...MiddleWare) {
httpsAddr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
app.Server.Addr = httpsAddr
}
server := grace.NewServer(httpsAddr, app.Handlers)
server := grace.NewServer(httpsAddr, app.Server.Handler)
server.Server.ReadTimeout = app.Server.ReadTimeout
server.Server.WriteTimeout = app.Server.WriteTimeout
if BConfig.Listen.EnableMutualHTTPS {
if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil {
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
} else {
if BConfig.Listen.AutoTLS {
@ -145,14 +144,14 @@ func (app *App) Run(mws ...MiddleWare) {
if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
time.Sleep(100 * time.Microsecond)
}
}
endRunning <- true
}
}
}()
}
if BConfig.Listen.EnableHTTP {
go func() {
server := grace.NewServer(addr, app.Handlers)
server := grace.NewServer(addr, app.Server.Handler)
server.Server.ReadTimeout = app.Server.ReadTimeout
server.Server.WriteTimeout = app.Server.WriteTimeout
if BConfig.Listen.ListenTCP4 {
@ -161,8 +160,8 @@ func (app *App) Run(mws ...MiddleWare) {
if err := server.ListenAndServe(); err != nil {
logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
endRunning <- true
}()
}
<-endRunning
@ -176,7 +175,7 @@ func (app *App) Run(mws ...MiddleWare) {
if BConfig.Listen.HTTPSPort != 0 {
app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
} else if BConfig.Listen.EnableHTTP {
BeeLogger.Info("Start https server error, conflict with http. Please reset https port")
logs.Info("Start https server error, conflict with http. Please reset https port")
return
}
logs.Info("https server Running on https://%s", app.Server.Addr)
@ -192,7 +191,7 @@ func (app *App) Run(mws ...MiddleWare) {
pool := x509.NewCertPool()
data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile)
if err != nil {
BeeLogger.Info("MutualHTTPS should provide TrustCaFile")
logs.Info("MutualHTTPS should provide TrustCaFile")
return
}
pool.AppendCertsFromPEM(data)

View File

@ -23,7 +23,7 @@ import (
const (
// VERSION represent beego web framework version.
VERSION = "1.10.0"
VERSION = "1.12.2"
// DEV is for develop
DEV = "dev"
@ -31,7 +31,10 @@ const (
PROD = "prod"
)
//hook function to run
// M is Map shortcut
type M map[string]interface{}
// Hook function to run
type hookfunc func() error
var (

View File

@ -1,4 +1,4 @@
// Copyright 2014 beego Author. All Rights Reserved.
// Copyright 2020 astaxie
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -12,17 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !windows
package beego
package logs
var (
BuildVersion string
BuildGitRevision string
BuildStatus string
BuildTag string
BuildTime string
import "io"
GoVersion string
type ansiColorWriter struct {
w io.Writer
mode outputMode
}
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
return cw.w.Write(p)
}
GitBranch string
)

25
cache/cache_test.go vendored
View File

@ -16,10 +16,33 @@ package cache
import (
"os"
"sync"
"testing"
"time"
)
func TestCacheIncr(t *testing.T) {
bm, err := NewCache("memory", `{"interval":20}`)
if err != nil {
t.Error("init err")
}
//timeoutDuration := 10 * time.Second
bm.Put("edwardhey", 0, time.Second*20)
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
bm.Incr("edwardhey")
}()
}
wg.Wait()
if bm.Get("edwardhey").(int) != 10 {
t.Error("Incr err")
}
}
func TestCache(t *testing.T) {
bm, err := NewCache("memory", `{"interval":20}`)
if err != nil {
@ -98,7 +121,7 @@ func TestCache(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 {
t.Error("init err")
}

17
cache/file.go vendored
View File

@ -62,11 +62,14 @@ func NewFileCache() Cache {
}
// StartAndGC will start and begin gc for file cache.
// the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}
// the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"}
func (fc *FileCache) StartAndGC(config string) error {
var cfg map[string]string
json.Unmarshal([]byte(config), &cfg)
cfg := make(map[string]string)
err := json.Unmarshal([]byte(config), &cfg)
if err != nil {
return err
}
if _, ok := cfg["CachePath"]; !ok {
cfg["CachePath"] = FileCachePath
}
@ -142,12 +145,12 @@ func (fc *FileCache) GetMulti(keys []string) []interface{} {
// Put value into file cache.
// 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 fc.EmbedExpiry(default is 0), cache this item forever.
func (fc *FileCache) Put(key string, val interface{}, timeout time.Duration) error {
gob.Register(val)
item := FileCacheItem{Data: val}
if timeout == FileCacheEmbedExpiry {
if timeout == time.Duration(fc.EmbedExpiry) {
item.Expired = time.Now().Add((86400 * 365 * 10) * time.Second) // ten years
} else {
item.Expired = time.Now().Add(timeout)
@ -179,7 +182,7 @@ func (fc *FileCache) Incr(key string) error {
} else {
incr = data.(int) + 1
}
fc.Put(key, incr, FileCacheEmbedExpiry)
fc.Put(key, incr, time.Duration(fc.EmbedExpiry))
return nil
}
@ -192,7 +195,7 @@ func (fc *FileCache) Decr(key string) error {
} else {
decr = data.(int) - 1
}
fc.Put(key, decr, FileCacheEmbedExpiry)
fc.Put(key, decr, time.Duration(fc.EmbedExpiry))
return nil
}

View File

@ -146,7 +146,7 @@ func (rc *Cache) IsExist(key string) bool {
}
}
_, err := rc.conn.Get(key)
return !(err != nil)
return err == nil
}
// ClearAll clear all cached in memcache.

55
cache/memory.go vendored
View File

@ -110,25 +110,25 @@ func (bc *MemoryCache) Delete(name string) error {
// Incr increase cache counter in memory.
// it supports int,int32,int64,uint,uint32,uint64.
func (bc *MemoryCache) Incr(key string) error {
bc.RLock()
defer bc.RUnlock()
bc.Lock()
defer bc.Unlock()
itm, ok := bc.items[key]
if !ok {
return errors.New("key not exist")
}
switch itm.val.(type) {
switch val := itm.val.(type) {
case int:
itm.val = itm.val.(int) + 1
itm.val = val + 1
case int32:
itm.val = itm.val.(int32) + 1
itm.val = val + 1
case int64:
itm.val = itm.val.(int64) + 1
itm.val = val + 1
case uint:
itm.val = itm.val.(uint) + 1
itm.val = val + 1
case uint32:
itm.val = itm.val.(uint32) + 1
itm.val = val + 1
case uint64:
itm.val = itm.val.(uint64) + 1
itm.val = val + 1
default:
return errors.New("item val is not (u)int (u)int32 (u)int64")
}
@ -137,34 +137,34 @@ func (bc *MemoryCache) Incr(key string) error {
// Decr decrease counter in memory.
func (bc *MemoryCache) Decr(key string) error {
bc.RLock()
defer bc.RUnlock()
bc.Lock()
defer bc.Unlock()
itm, ok := bc.items[key]
if !ok {
return errors.New("key not exist")
}
switch itm.val.(type) {
switch val := itm.val.(type) {
case int:
itm.val = itm.val.(int) - 1
itm.val = val - 1
case int64:
itm.val = itm.val.(int64) - 1
itm.val = val - 1
case int32:
itm.val = itm.val.(int32) - 1
itm.val = val - 1
case uint:
if itm.val.(uint) > 0 {
itm.val = itm.val.(uint) - 1
if val > 0 {
itm.val = val - 1
} else {
return errors.New("item val is less than 0")
}
case uint32:
if itm.val.(uint32) > 0 {
itm.val = itm.val.(uint32) - 1
if val > 0 {
itm.val = val - 1
} else {
return errors.New("item val is less than 0")
}
case uint64:
if itm.val.(uint64) > 0 {
itm.val = itm.val.(uint64) - 1
if val > 0 {
itm.val = val - 1
} else {
return errors.New("item val is less than 0")
}
@ -203,20 +203,27 @@ func (bc *MemoryCache) StartAndGC(config string) error {
dur := time.Duration(cf["interval"]) * time.Second
bc.Every = cf["interval"]
bc.dur = dur
go bc.vaccuum()
go bc.vacuum()
return nil
}
// check expiration.
func (bc *MemoryCache) vaccuum() {
if bc.Every < 1 {
func (bc *MemoryCache) vacuum() {
bc.RLock()
every := bc.Every
bc.RUnlock()
if every < 1 {
return
}
for {
<-time.After(bc.dur)
bc.RLock()
if bc.items == nil {
bc.RUnlock()
return
}
bc.RUnlock()
if keys := bc.expiredKeys(); len(keys) != 0 {
bc.clearItems(keys)
}

58
cache/redis/redis.go vendored
View File

@ -39,6 +39,7 @@ import (
"github.com/gomodule/redigo/redis"
"github.com/astaxie/beego/cache"
"strings"
)
var (
@ -54,6 +55,9 @@ type Cache struct {
key string
password string
maxIdle int
//the timeout to a value less than the redis server's timeout.
timeout time.Duration
}
// NewRedisCache create new redis cache with default collection name.
@ -136,12 +140,12 @@ func (rc *Cache) Decr(key string) error {
// ClearAll clean all cache in redis. delete this redis collection.
func (rc *Cache) ClearAll() error {
c := rc.p.Get()
defer c.Close()
cachedKeys, err := redis.Strings(c.Do("KEYS", rc.key+":*"))
cachedKeys, err := rc.Scan(rc.key + ":*")
if err != nil {
return err
}
c := rc.p.Get()
defer c.Close()
for _, str := range cachedKeys {
if _, err = c.Do("DEL", str); err != nil {
return err
@ -150,6 +154,35 @@ func (rc *Cache) ClearAll() error {
return err
}
// Scan scan all keys matching the pattern. a better choice than `keys`
func (rc *Cache) Scan(pattern string) (keys []string, err error) {
c := rc.p.Get()
defer c.Close()
var (
cursor uint64 = 0 // start
result []interface{}
list []string
)
for {
result, err = redis.Values(c.Do("SCAN", cursor, "MATCH", pattern, "COUNT", 1024))
if err != nil {
return
}
list, err = redis.Strings(result[1], nil)
if err != nil {
return
}
keys = append(keys, list...)
cursor, err = redis.Uint64(result[0], nil)
if err != nil {
return
}
if cursor == 0 { // over
return
}
}
}
// StartAndGC start redis cache adapter.
// config is like {"key":"collection key","conn":"connection info","dbNum":"0"}
// the cache item in redis are stored forever,
@ -164,6 +197,14 @@ func (rc *Cache) StartAndGC(config string) error {
if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key")
}
// Format redis://<password>@<host>:<port>
cf["conn"] = strings.Replace(cf["conn"], "redis://", "", 1)
if i := strings.Index(cf["conn"], "@"); i > -1 {
cf["password"] = cf["conn"][0:i]
cf["conn"] = cf["conn"][i+1:]
}
if _, ok := cf["dbNum"]; !ok {
cf["dbNum"] = "0"
}
@ -173,12 +214,21 @@ func (rc *Cache) StartAndGC(config string) error {
if _, ok := cf["maxIdle"]; !ok {
cf["maxIdle"] = "3"
}
if _, ok := cf["timeout"]; !ok {
cf["timeout"] = "180s"
}
rc.key = cf["key"]
rc.conninfo = cf["conn"]
rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
rc.password = cf["password"]
rc.maxIdle, _ = strconv.Atoi(cf["maxIdle"])
if v, err := time.ParseDuration(cf["timeout"]); err == nil {
rc.timeout = v
} else {
rc.timeout = 180 * time.Second
}
rc.connectInit()
c := rc.p.Get()
@ -212,7 +262,7 @@ func (rc *Cache) connectInit() {
// initialize a new pool
rc.p = &redis.Pool{
MaxIdle: rc.maxIdle,
IdleTimeout: 180 * time.Second,
IdleTimeout: rc.timeout,
Dial: dialFunc,
}
}

View File

@ -15,6 +15,7 @@
package redis
import (
"fmt"
"testing"
"time"
@ -104,3 +105,40 @@ func TestRedisCache(t *testing.T) {
t.Error("clear all err")
}
}
func TestCache_Scan(t *testing.T) {
timeoutDuration := 10 * time.Second
// init
bm, err := cache.NewCache("redis", `{"conn": "127.0.0.1:6379"}`)
if err != nil {
t.Error("init err")
}
// insert all
for i := 0; i < 10000; i++ {
if err = bm.Put(fmt.Sprintf("astaxie%d", i), fmt.Sprintf("author%d", i), timeoutDuration); err != nil {
t.Error("set Error", err)
}
}
// scan all for the first time
keys, err := bm.(*Cache).Scan(DefaultKey + ":*")
if err != nil {
t.Error("scan Error", err)
}
if len(keys) != 10000 {
t.Error("scan all err")
}
// clear all
if err = bm.ClearAll(); err != nil {
t.Error("clear all err")
}
// scan all for the second time
keys, err = bm.(*Cache).Scan(DefaultKey + ":*")
if err != nil {
t.Error("scan Error", err)
}
if len(keys) != 0 {
t.Error("scan all err")
}
}

View File

@ -81,6 +81,8 @@ type WebConfig struct {
DirectoryIndex bool
StaticDir map[string]string
StaticExtensionsToGzip []string
StaticCacheFileSize int
StaticCacheFileNum int
TemplateLeft string
TemplateRight string
ViewsPath string
@ -129,6 +131,8 @@ var (
appConfigPath string
// appConfigProvider is the provider for the config, default is ini
appConfigProvider = "ini"
// WorkPath is the absolute path to project root directory
WorkPath string
)
func init() {
@ -137,7 +141,7 @@ func init() {
if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil {
panic(err)
}
workPath, err := os.Getwd()
WorkPath, err = os.Getwd()
if err != nil {
panic(err)
}
@ -145,7 +149,7 @@ func init() {
if os.Getenv("BEEGO_RUNMODE") != "" {
filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
}
appConfigPath = filepath.Join(workPath, "conf", filename)
appConfigPath = filepath.Join(WorkPath, "conf", filename)
if !utils.FileExists(appConfigPath) {
appConfigPath = filepath.Join(AppPath, "conf", filename)
if !utils.FileExists(appConfigPath) {
@ -236,6 +240,8 @@ func newBConfig() *Config {
DirectoryIndex: false,
StaticDir: map[string]string{"/static": "static"},
StaticExtensionsToGzip: []string{".css", ".js"},
StaticCacheFileSize: 1024 * 100,
StaticCacheFileNum: 1000,
TemplateLeft: "{{",
TemplateRight: "}}",
ViewsPath: "views",
@ -317,6 +323,14 @@ func assignConfig(ac config.Configer) error {
}
}
if sfs, err := ac.Int("StaticCacheFileSize"); err == nil {
BConfig.WebConfig.StaticCacheFileSize = sfs
}
if sfn, err := ac.Int("StaticCacheFileNum"); err == nil {
BConfig.WebConfig.StaticCacheFileNum = sfn
}
if lo := ac.String("LogOutputs"); lo != "" {
// if lo is not nil or empty
// means user has set his own LogOutputs
@ -408,9 +422,9 @@ func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, err
func (b *beegoAppConfig) Set(key, val string) error {
if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil {
return err
}
return b.innerConfig.Set(key, val)
}
return nil
}
func (b *beegoAppConfig) String(key string) string {

View File

@ -78,15 +78,37 @@ func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, e
}
}
section := defaultSection
tmpBuf := bytes.NewBuffer(nil)
for {
line, _, err := buf.ReadLine()
tmpBuf.Reset()
shouldBreak := false
for {
tmp, isPrefix, err := buf.ReadLine()
if err == io.EOF {
shouldBreak = true
break
}
//It might be a good idea to throw a error on all unknonw errors?
if _, ok := err.(*os.PathError); ok {
return nil, err
}
tmpBuf.Write(tmp)
if isPrefix {
continue
}
if !isPrefix {
break
}
}
if shouldBreak {
break
}
line := tmpBuf.Bytes()
line = bytes.TrimSpace(line)
if bytes.Equal(line, bEmpty) {
continue

View File

@ -20,6 +20,7 @@ import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"sync"
)
@ -94,8 +95,10 @@ func (c *JSONConfigContainer) Int(key string) (int, error) {
if val != nil {
if v, ok := val.(float64); ok {
return int(v), nil
} else if v, ok := val.(string); ok {
return strconv.Atoi(v)
}
return 0, errors.New("not int value")
return 0, errors.New("not valid value")
}
return 0, errors.New("not exist key:" + key)
}

View File

@ -97,7 +97,7 @@ func parseYML(buf []byte) (cnf map[string]interface{}, err error) {
}
}
data, err := goyaml2.Read(bytes.NewBuffer(buf))
data, err := goyaml2.Read(bytes.NewReader(buf))
if err != nil {
log.Println("Goyaml2 ERR>", string(buf), err)
return
@ -290,12 +290,15 @@ func (c *ConfigContainer) getData(key string) (interface{}, error) {
keys := strings.Split(key, ".")
tmpData := c.data
for _, k := range keys {
for idx, k := range keys {
if v, ok := tmpData[k]; ok {
switch v.(type) {
case map[string]interface{}:
{
tmpData = v.(map[string]interface{})
if idx == len(keys) - 1 {
return tmpData, nil
}
}
default:
{

View File

@ -48,15 +48,15 @@ func TestAssignConfig_02(t *testing.T) {
_BConfig := &Config{}
bs, _ := json.Marshal(newBConfig())
jsonMap := map[string]interface{}{}
jsonMap := M{}
json.Unmarshal(bs, &jsonMap)
configMap := map[string]interface{}{}
configMap := M{}
for k, v := range jsonMap {
if reflect.TypeOf(v).Kind() == reflect.Map {
for k1, v1 := range v.(map[string]interface{}) {
for k1, v1 := range v.(M) {
if reflect.TypeOf(v1).Kind() == reflect.Map {
for k2, v2 := range v1.(map[string]interface{}) {
for k2, v2 := range v1.(M) {
configMap[k2] = v2
}
} else {
@ -115,6 +115,8 @@ func TestAssignConfig_03(t *testing.T) {
ac.Set("RunMode", "online")
ac.Set("StaticDir", "download:down download2:down2")
ac.Set("StaticExtensionsToGzip", ".css,.js,.html,.jpg,.png")
ac.Set("StaticCacheFileSize", "87456")
ac.Set("StaticCacheFileNum", "1254")
assignConfig(ac)
t.Logf("%#v", BConfig)
@ -132,6 +134,12 @@ func TestAssignConfig_03(t *testing.T) {
if BConfig.WebConfig.StaticDir["/download2"] != "down2" {
t.FailNow()
}
if BConfig.WebConfig.StaticCacheFileSize != 87456 {
t.FailNow()
}
if BConfig.WebConfig.StaticCacheFileNum != 1254 {
t.FailNow()
}
if len(BConfig.WebConfig.StaticExtensionsToGzip) != 5 {
t.FailNow()
}

View File

@ -25,7 +25,7 @@ package context
import (
"bufio"
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
@ -38,6 +38,14 @@ import (
"github.com/astaxie/beego/utils"
)
//commonly used mime-types
const (
ApplicationJSON = "application/json"
ApplicationXML = "application/xml"
ApplicationYAML = "application/x-yaml"
TextXML = "text/xml"
)
// NewContext return the Context with Input and Output
func NewContext() *Context {
return &Context{
@ -115,7 +123,7 @@ func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
timestamp := parts[1]
sig := parts[2]
h := hmac.New(sha1.New, []byte(Secret))
h := hmac.New(sha256.New, []byte(Secret))
fmt.Fprintf(h, "%s%s", vs, timestamp)
if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
@ -129,7 +137,7 @@ func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
vs := base64.URLEncoding.EncodeToString([]byte(value))
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
h := hmac.New(sha1.New, []byte(Secret))
h := hmac.New(sha256.New, []byte(Secret))
fmt.Fprintf(h, "%s%s", vs, timestamp)
sig := fmt.Sprintf("%02x", h.Sum(nil))
cookie := strings.Join([]string{vs, timestamp, sig}, "|")
@ -161,11 +169,11 @@ func (ctx *Context) CheckXSRFCookie() bool {
token = ctx.Request.Header.Get("X-Csrftoken")
}
if token == "" {
ctx.Abort(403, "'_xsrf' argument missing from POST")
ctx.Abort(422, "422")
return false
}
if ctx._xsrfToken != token {
ctx.Abort(403, "XSRF cookie does not match POST argument")
ctx.Abort(417, "417")
return false
}
return true
@ -193,6 +201,7 @@ type Response struct {
http.ResponseWriter
Started bool
Status int
Elapsed time.Duration
}
func (r *Response) reset(rw http.ResponseWriter) {
@ -244,3 +253,11 @@ func (r *Response) CloseNotify() <-chan bool {
}
return nil
}
// Pusher http.Pusher
func (r *Response) Pusher() (pusher http.Pusher) {
if pusher, ok := r.ResponseWriter.(http.Pusher); ok {
return pusher
}
return nil
}

View File

@ -27,6 +27,7 @@ import (
"regexp"
"strconv"
"strings"
"sync"
"github.com/astaxie/beego/session"
)
@ -49,6 +50,7 @@ type BeegoInput struct {
pnames []string
pvalues []string
data map[interface{}]interface{} // store some values in this context when calling context in filter or controller.
dataLock sync.RWMutex
RequestBody []byte
RunMethod string
RunController reflect.Type
@ -69,7 +71,9 @@ func (input *BeegoInput) Reset(ctx *Context) {
input.CruSession = nil
input.pnames = input.pnames[:0]
input.pvalues = input.pvalues[:0]
input.dataLock.Lock()
input.data = nil
input.dataLock.Unlock()
input.RequestBody = []byte{}
}
@ -85,7 +89,7 @@ func (input *BeegoInput) URI() string {
// URL returns request url path (without query string, fragment).
func (input *BeegoInput) URL() string {
return input.Context.Request.URL.Path
return input.Context.Request.URL.EscapedPath()
}
// Site returns base site url as scheme://domain type.
@ -204,6 +208,7 @@ func (input *BeegoInput) AcceptsXML() bool {
func (input *BeegoInput) AcceptsJSON() bool {
return acceptsJSONRegex.MatchString(input.Header("Accept"))
}
// AcceptsYAML Checks if request accepts json response
func (input *BeegoInput) AcceptsYAML() bool {
return acceptsYAMLRegex.MatchString(input.Header("Accept"))
@ -279,6 +284,11 @@ func (input *BeegoInput) ParamsLen() int {
func (input *BeegoInput) Param(key string) string {
for i, v := range input.pnames {
if v == key && i <= len(input.pvalues) {
// we cannot use url.PathEscape(input.pvalues[i])
// for example, if the value is /a/b
// after url.PathEscape(input.pvalues[i]), the value is %2Fa%2Fb
// However, the value is used in ControllerRegister.ServeHTTP
// and split by "/", so function crash...
return input.pvalues[i]
}
}
@ -377,6 +387,8 @@ func (input *BeegoInput) CopyBody(MaxMemory int64) []byte {
// Data return the implicit data in the input
func (input *BeegoInput) Data() map[interface{}]interface{} {
input.dataLock.Lock()
defer input.dataLock.Unlock()
if input.data == nil {
input.data = make(map[interface{}]interface{})
}
@ -385,6 +397,8 @@ func (input *BeegoInput) Data() map[interface{}]interface{} {
// GetData returns the stored data in this context.
func (input *BeegoInput) GetData(key interface{}) interface{} {
input.dataLock.Lock()
defer input.dataLock.Unlock()
if v, ok := input.data[key]; ok {
return v
}
@ -394,6 +408,8 @@ func (input *BeegoInput) GetData(key interface{}) interface{} {
// SetData stores data with given key in this context.
// This data are only available in this context.
func (input *BeegoInput) SetData(key, val interface{}) {
input.dataLock.Lock()
defer input.dataLock.Unlock()
if input.data == nil {
input.data = make(map[interface{}]interface{})
}

View File

@ -30,7 +30,8 @@ import (
"strconv"
"strings"
"time"
"gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v2"
)
// BeegoOutput does work for sending response header.
@ -203,10 +204,9 @@ func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, encoding bool)
return output.Body(content)
}
// YAML writes yaml to response body.
func (output *BeegoOutput) YAML(data interface{}) error {
output.Header("Content-Type", "application/application/x-yaml; charset=utf-8")
output.Header("Content-Type", "application/x-yaml; charset=utf-8")
var content []byte
var err error
content, err = yaml.Marshal(data)
@ -260,6 +260,19 @@ func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error {
return output.Body(content)
}
// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header
func (output *BeegoOutput) ServeFormatted(data interface{}, hasIndent bool, hasEncode ...bool) {
accept := output.Context.Input.Header("Accept")
switch accept {
case ApplicationYAML:
output.YAML(data)
case ApplicationXML, TextXML:
output.XML(data, hasIndent)
default:
output.JSON(data, hasIndent, len(hasEncode) > 0 && hasEncode[0])
}
}
// Download forces response for download file.
// it prepares the download response header automatically.
func (output *BeegoOutput) Download(file string, filename ...string) {
@ -275,7 +288,20 @@ func (output *BeegoOutput) Download(file string, filename ...string) {
} else {
fName = filepath.Base(file)
}
output.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fName))
//https://tools.ietf.org/html/rfc6266#section-4.3
fn := url.PathEscape(fName)
if fName == fn {
fn = "filename=" + fn
} else {
/**
The parameters "filename" and "filename*" differ only in that
"filename*" uses the encoding defined in [RFC5987], allowing the use
of characters not present in the ISO-8859-1 character set
([ISO-8859-1]).
*/
fn = "filename=" + fName + "; filename*=utf-8''" + fn
}
output.Header("Content-Disposition", "attachment; "+fn)
output.Header("Content-Description", "File Transfer")
output.Header("Content-Type", "application/octet-stream")
output.Header("Content-Transfer-Encoding", "binary")

View File

@ -17,6 +17,7 @@ package beego
import (
"bytes"
"errors"
"fmt"
"html/template"
"io"
"mime/multipart"
@ -32,25 +33,44 @@ import (
"github.com/astaxie/beego/session"
)
//commonly used mime-types
const (
applicationJSON = "application/json"
applicationXML = "application/xml"
applicationYAML = "application/x-yaml"
textXML = "text/xml"
)
var (
// ErrAbort custom error when user stop request handler manually.
ErrAbort = errors.New("User stop run")
ErrAbort = errors.New("user stop run")
// GlobalControllerRouter store comments with controller. pkgpath+controller:comments
GlobalControllerRouter = make(map[string][]ControllerComments)
)
// ControllerFilter store the filter for controller
type ControllerFilter struct {
Pattern string
Pos int
Filter FilterFunc
ReturnOnOutput bool
ResetParams bool
}
// ControllerFilterComments store the comment for controller level filter
type ControllerFilterComments struct {
Pattern string
Pos int
Filter string // NOQA
ReturnOnOutput bool
ResetParams bool
}
// ControllerImportComments store the import comment for controller needed
type ControllerImportComments struct {
ImportPath string
ImportAlias string
}
// ControllerComments store the comment for the controller method
type ControllerComments struct {
Method string
Router string
Filters []*ControllerFilter
ImportComments []*ControllerImportComments
FilterComments []*ControllerFilterComments
AllowHTTPMethods []string
Params []map[string]string
MethodParams []*param.MethodParam
@ -74,7 +94,6 @@ type Controller struct {
controllerName string
actionName string
methodMapping map[string]func() //method:routertree
gotofunc string
AppController interface{}
// template data
@ -106,6 +125,7 @@ type ControllerInterface interface {
Head()
Patch()
Options()
Trace()
Finish()
Render() error
XSRFToken() string
@ -137,37 +157,59 @@ func (c *Controller) Finish() {}
// Get adds a request function to handle GET request.
func (c *Controller) Get() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}
// Post adds a request function to handle POST request.
func (c *Controller) Post() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}
// Delete adds a request function to handle DELETE request.
func (c *Controller) Delete() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}
// Put adds a request function to handle PUT request.
func (c *Controller) Put() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}
// Head adds a request function to handle HEAD request.
func (c *Controller) Head() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}
// Patch adds a request function to handle PATCH request.
func (c *Controller) Patch() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}
// Options adds a request function to handle OPTIONS request.
func (c *Controller) Options() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}
// Trace adds a request function to handle Trace request.
// this method SHOULD NOT be overridden.
// https://tools.ietf.org/html/rfc7231#section-4.3.8
// The TRACE method requests a remote, application-level loop-back of
// the request message. The final recipient of the request SHOULD
// reflect the message received, excluding some fields described below,
// back to the client as the message body of a 200 (OK) response with a
// Content-Type of "message/http" (Section 8.3.1 of [RFC7230]).
func (c *Controller) Trace() {
ts := func(h http.Header) (hs string) {
for k, v := range h {
hs += fmt.Sprintf("\r\n%s: %s", k, v)
}
return
}
hs := fmt.Sprintf("\r\nTRACE %s %s%s\r\n", c.Ctx.Request.RequestURI, c.Ctx.Request.Proto, ts(c.Ctx.Request.Header))
c.Ctx.Output.Header("Content-Type", "message/http")
c.Ctx.Output.Header("Content-Length", fmt.Sprint(len(hs)))
c.Ctx.Output.Header("Cache-Control", "no-cache, no-store, must-revalidate")
c.Ctx.WriteString(hs)
}
// HandlerFunc call function with the name
@ -273,18 +315,17 @@ func (c *Controller) viewPath() string {
// Redirect sends the redirection response to url with status code.
func (c *Controller) Redirect(url string, code int) {
logAccess(c.Ctx, nil, code)
LogAccess(c.Ctx, nil, code)
c.Ctx.Redirect(code, url)
panic(ErrAbort)
}
// Set the data depending on the accepted
// SetData set the data depending on the accepted
func (c *Controller) SetData(data interface{}) {
accept := c.Ctx.Input.Header("Accept")
switch accept {
case applicationJSON:
c.Data["json"] = data
case applicationXML, textXML:
case context.ApplicationYAML:
c.Data["yaml"] = data
case context.ApplicationXML, context.TextXML:
c.Data["xml"] = data
default:
c.Data["json"] = data
@ -333,54 +374,35 @@ func (c *Controller) URLFor(endpoint string, values ...interface{}) string {
// ServeJSON sends a json response with encoding charset.
func (c *Controller) ServeJSON(encoding ...bool) {
var (
hasIndent = true
hasEncoding = false
hasIndent = BConfig.RunMode != PROD
hasEncoding = len(encoding) > 0 && encoding[0]
)
if BConfig.RunMode == PROD {
hasIndent = false
}
if len(encoding) > 0 && encoding[0] {
hasEncoding = true
}
c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding)
}
// ServeJSONP sends a jsonp response.
func (c *Controller) ServeJSONP() {
hasIndent := true
if BConfig.RunMode == PROD {
hasIndent = false
}
hasIndent := BConfig.RunMode != PROD
c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent)
}
// ServeXML sends xml response.
func (c *Controller) ServeXML() {
hasIndent := true
if BConfig.RunMode == PROD {
hasIndent = false
}
hasIndent := BConfig.RunMode != PROD
c.Ctx.Output.XML(c.Data["xml"], hasIndent)
}
// ServeXML sends xml response.
// ServeYAML sends yaml response.
func (c *Controller) ServeYAML() {
c.Ctx.Output.YAML(c.Data["yaml"])
}
// ServeFormatted serve Xml OR Json, depending on the value of the Accept header
func (c *Controller) ServeFormatted() {
accept := c.Ctx.Input.Header("Accept")
switch accept {
case applicationJSON:
c.ServeJSON()
case applicationXML, textXML:
c.ServeXML()
case applicationYAML:
c.ServeYAML()
default:
c.ServeJSON()
}
// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header
func (c *Controller) ServeFormatted(encoding ...bool) {
hasIndent := BConfig.RunMode != PROD
hasEncoding := len(encoding) > 0 && encoding[0]
c.Ctx.Output.ServeFormatted(c.Data, hasIndent, hasEncoding)
}
// Input returns the input data map from POST or PUT request body and query string.

View File

@ -361,7 +361,7 @@ func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := map[string]interface{}{
data := M{
"Title": http.StatusText(errCode),
"BeegoVersion": VERSION,
"Content": template.HTML(errContent),
@ -435,7 +435,7 @@ func exception(errCode string, ctx *context.Context) {
func executeError(err *errorInfo, ctx *context.Context, code int) {
//make sure to log the error in the access log
logAccess(ctx, nil, code)
LogAccess(ctx, nil, code)
if err.errorType == errorTypeHandler {
ctx.ResponseWriter.WriteHeader(code)

74
fs.go Normal file
View File

@ -0,0 +1,74 @@
package beego
import (
"net/http"
"os"
"path/filepath"
)
type FileSystem struct {
}
func (d FileSystem) Open(name string) (http.File, error) {
return os.Open(name)
}
// Walk walks the file tree rooted at root in filesystem, calling walkFn for each file or
// directory in the tree, including root. All errors that arise visiting files
// and directories are filtered by walkFn.
func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error {
f, err := fs.Open(root)
if err != nil {
return err
}
info, err := f.Stat()
if err != nil {
err = walkFn(root, nil, err)
} else {
err = walk(fs, root, info, walkFn)
}
if err == filepath.SkipDir {
return nil
}
return err
}
// walk recursively descends path, calling walkFn.
func walk(fs http.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
var err error
if !info.IsDir() {
return walkFn(path, info, nil)
}
dir, err := fs.Open(path)
if err != nil {
if err1 := walkFn(path, info, err); err1 != nil {
return err1
}
return err
}
defer dir.Close()
dirs, err := dir.Readdir(-1)
err1 := walkFn(path, info, err)
// If err != nil, walk can't walk into this directory.
// err1 != nil means walkFn want walk to skip this directory or stop walking.
// Therefore, if one of err and err1 isn't nil, walk will return.
if err != nil || err1 != nil {
// The caller's behavior is controlled by the return value, which is decided
// by walkFn. walkFn may ignore err and return nil.
// If walkFn returns SkipDir, it will be handled by the caller.
// So walk should return whatever walkFn returns.
return err1
}
for _, fileInfo := range dirs {
filename := filepath.Join(path, fileInfo.Name())
if err = walk(fs, filename, fileInfo, walkFn); err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err
}
}
}
return nil
}

40
go.mod Normal file
View File

@ -0,0 +1,40 @@
module github.com/astaxie/beego
require (
github.com/Knetic/govaluate v3.0.0+incompatible // indirect
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737
github.com/casbin/casbin v1.7.0
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808 // indirect
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a // indirect
github.com/elastic/go-elasticsearch/v6 v6.8.5
github.com/elazarl/go-bindata-assetfs v1.0.0
github.com/go-redis/redis v6.14.2+incompatible
github.com/go-sql-driver/mysql v1.5.0
github.com/gogo/protobuf v1.1.1
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
github.com/gomodule/redigo v2.0.0+incompatible
github.com/hashicorp/golang-lru v0.5.4
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6
github.com/lib/pq v1.0.0
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/prometheus/client_golang v1.7.0
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec
github.com/stretchr/testify v1.4.0
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c // indirect
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b // indirect
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c
gopkg.in/yaml.v2 v2.2.8
)
replace golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 => github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85
replace gopkg.in/yaml.v2 v2.2.1 => github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d
go 1.13

217
go.sum Normal file
View File

@ -0,0 +1,217 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd h1:jZtX5jh5IOMu0fpOTC3ayh6QGSPJ/KWOv1lgPvbRw1M=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542 h1:nYXb+3jF6Oq/j8R/y90XrKpreCxIalBWfeyeKymgOPk=
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 h1:rRISKWyXfVxvoa702s91Zl5oREZTrR3yv+tXrrX7G/g=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
github.com/casbin/casbin v1.7.0 h1:PuzlE8w0JBg/DhIqnkF1Dewf3z+qmUZMVN07PonvVUQ=
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d h1:OMrhQqj1QCyDT2sxHCDjE+k8aMdn2ngTCGG7g4wrdLo=
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808 h1:8s2l8TVUwMXl6tZMe3+hPCRJ25nQXiA3d1x622JtOqc=
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a h1:Y5XsLCEhtEI8qbD9RP3Qlv5FXdTDHxZM9UPUnMRgBp8=
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 h1:Lgdd/Qp96Qj8jqLpq2cI1I1X7BJnu06efS+XkhRoLUQ=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elastic/go-elasticsearch/v6 v6.8.5 h1:U2HtkBseC1FNBmDr0TR2tKltL6FxoY+niDAlj5M8TK8=
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0=
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d h1:xy93KVe+KrIIwWDEAfQBdIfsiHJkepbYsDr+VY3g9/o=
github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6 h1:wxyqOzKxsRJ6vVRL9sXQ64Z45wmBuQ+OTH9sLsC5rKc=
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/pingcap/tidb v2.0.11+incompatible/go.mod h1:I8C6jrPINP2rrVunTRd7C9fRRhQrtR43S1/CL5ix/yQ=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U=
github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0 h1:QIF48X1cihydXibm+4wfAc0r/qyPyuFiPFRNphdMpEE=
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d h1:NVwnfyR3rENtlz62bcrkXME3INVUa4lcdGt+opvxExs=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec h1:q6XVwXmKvCRHRqesF3cSv6lNqqHi0QWOvgDlSohg8UA=
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c h1:3eGShk3EQf5gJCYW+WzA0TEJQd37HLOmlYF7N0YJwv0=
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b h1:0Ve0/CCjiAiyKddUMUn3RwIGlq2iTW4GuVzyoKBYO/8=
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,39 +0,0 @@
package grace
import (
"errors"
"net"
"sync"
)
type graceConn struct {
net.Conn
server *Server
m sync.Mutex
closed bool
}
func (c *graceConn) Close() (err error) {
defer func() {
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("Unknown panic")
}
}
}()
c.m.Lock()
if c.closed {
c.m.Unlock()
return
}
c.server.wg.Done()
c.closed = true
c.m.Unlock()
return c.Conn.Close()
}

View File

@ -78,7 +78,7 @@ var (
DefaultReadTimeOut time.Duration
// DefaultWriteTimeOut is the HTTP Write timeout
DefaultWriteTimeOut time.Duration
// DefaultMaxHeaderBytes is the Max HTTP Herder size, default is 0, no limit
// DefaultMaxHeaderBytes is the Max HTTP Header size, default is 0, no limit
DefaultMaxHeaderBytes int
// DefaultTimeout is the shutdown server's timeout. default is 60s
DefaultTimeout = 60 * time.Second
@ -122,7 +122,6 @@ func NewServer(addr string, handler http.Handler) (srv *Server) {
}
srv = &Server{
wg: sync.WaitGroup{},
sigChan: make(chan os.Signal),
isChild: isChild,
SignalHooks: map[int]map[os.Signal][]func(){
@ -139,18 +138,19 @@ func NewServer(addr string, handler http.Handler) (srv *Server) {
},
state: StateInit,
Network: "tcp",
terminalChan: make(chan error), //no cache channel
}
srv.Server = &http.Server{
Addr: addr,
ReadTimeout: DefaultReadTimeOut,
WriteTimeout: DefaultWriteTimeOut,
MaxHeaderBytes: DefaultMaxHeaderBytes,
Handler: handler,
}
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
return srv
}
// ListenAndServe refer http.ListenAndServe

View File

@ -1,62 +0,0 @@
package grace
import (
"net"
"os"
"syscall"
"time"
)
type graceListener struct {
net.Listener
stop chan error
stopped bool
server *Server
}
func newGraceListener(l net.Listener, srv *Server) (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 (gl *graceListener) Close() error {
if gl.stopped {
return syscall.EINVAL
}
gl.stop <- nil
return <-gl.stop
}
func (gl *graceListener) File() *os.File {
// returns a dup(2) - FD_CLOEXEC flag *not* set
tl := gl.Listener.(*net.TCPListener)
fl, _ := tl.File()
return fl
}

View File

@ -1,6 +1,7 @@
package grace
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
@ -12,7 +13,6 @@ import (
"os/exec"
"os/signal"
"strings"
"sync"
"syscall"
"time"
)
@ -20,14 +20,13 @@ import (
// Server embedded http.Server
type Server struct {
*http.Server
GraceListener net.Listener
ln net.Listener
SignalHooks map[int]map[os.Signal][]func()
tlsInnerListener *graceListener
wg sync.WaitGroup
sigChan chan os.Signal
isChild bool
state uint8
Network string
terminalChan chan error
}
// Serve accepts incoming connections on the Listener l,
@ -35,10 +34,21 @@ type Server struct {
// The service goroutines read requests and then call srv.Handler to reply to them.
func (srv *Server) Serve() (err error) {
srv.state = StateRunning
err = srv.Server.Serve(srv.GraceListener)
log.Println(syscall.Getpid(), "Waiting for connections to finish...")
srv.wg.Wait()
srv.state = StateTerminate
defer func() { srv.state = StateTerminate }()
// When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS
// immediately return ErrServerClosed. Make sure the program doesn't exit
// and waits instead for Shutdown to return.
if err = srv.Server.Serve(srv.ln); err != nil && err != http.ErrServerClosed {
log.Println(syscall.Getpid(), "Server.Serve() error:", err)
return err
}
log.Println(syscall.Getpid(), srv.ln.Addr(), "Listener closed.")
// wait for Shutdown to return
if shutdownErr := <-srv.terminalChan; shutdownErr != nil {
return shutdownErr
}
return
}
@ -53,14 +63,12 @@ func (srv *Server) ListenAndServe() (err error) {
go srv.handleSignals()
l, err := srv.getListener(addr)
srv.ln, 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 {
@ -107,14 +115,12 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) {
go srv.handleSignals()
l, err := srv.getListener(addr)
ln, err := srv.getListener(addr)
if err != nil {
log.Println(err)
return err
}
srv.tlsInnerListener = newGraceListener(l, srv)
srv.GraceListener = tls.NewListener(srv.tlsInnerListener, srv.TLSConfig)
srv.ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig)
if srv.isChild {
process, err := os.FindProcess(os.Getppid())
@ -127,6 +133,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) {
return err
}
}
log.Println(os.Getpid(), srv.Addr)
return srv.Serve()
}
@ -163,14 +170,12 @@ func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string)
log.Println("Mutual HTTPS")
go srv.handleSignals()
l, err := srv.getListener(addr)
ln, err := srv.getListener(addr)
if err != nil {
log.Println(err)
return err
}
srv.tlsInnerListener = newGraceListener(l, srv)
srv.GraceListener = tls.NewListener(srv.tlsInnerListener, srv.TLSConfig)
srv.ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig)
if srv.isChild {
process, err := os.FindProcess(os.Getppid())
@ -178,11 +183,12 @@ func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string)
log.Println(err)
return err
}
err = process.Kill()
err = process.Signal(syscall.SIGTERM)
if err != nil {
return err
}
}
log.Println(os.Getpid(), srv.Addr)
return srv.Serve()
}
@ -213,6 +219,20 @@ func (srv *Server) getListener(laddr string) (l net.Listener, err error) {
return
}
type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}
// handleSignals listens for os Signals and calls any hooked in function that the
// user had registered with the signal.
func (srv *Server) handleSignals() {
@ -265,37 +285,14 @@ func (srv *Server) shutdown() {
}
srv.state = StateShuttingDown
log.Println(syscall.Getpid(), "Waiting for connections to finish...")
ctx := context.Background()
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 *Server) serverTimeout(d time.Duration) {
defer func() {
if r := recover(); r != nil {
log.Println("WaitGroup at 0", r)
}
}()
if srv.state != StateShuttingDown {
return
}
time.Sleep(d)
log.Println("[STOP - Hammer Time] Forcefully shutting down parent")
for {
if srv.state == StateTerminate {
break
}
srv.wg.Done()
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
}
srv.terminalChan <- srv.Server.Shutdown(ctx)
}
func (srv *Server) fork() (err error) {
@ -309,12 +306,8 @@ func (srv *Server) fork() (err error) {
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()
}
f, _ := srvPtr.ln.(*net.TCPListener).File()
files[socketPtrOffsetMap[srvPtr.Server.Addr]] = f
orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr
}

View File

@ -11,7 +11,7 @@ import (
"github.com/astaxie/beego/session"
)
//
// register MIME type with content type
func registerMime() error {
for k, v := range mimemaps {
mime.AddExtensionType(k, v)

View File

@ -47,9 +47,11 @@ import (
"net/http/httputil"
"net/url"
"os"
"path"
"strings"
"sync"
"time"
"gopkg.in/yaml.v2"
)
@ -405,6 +407,7 @@ func (b *BeegoHTTPRequest) buildURL(paramBody string) {
}()
b.Header("Content-Type", bodyWriter.FormDataContentType())
b.req.Body = ioutil.NopCloser(pr)
b.Header("Transfer-Encoding", "chunked")
return
}
@ -558,12 +561,6 @@ func (b *BeegoHTTPRequest) Bytes() ([]byte, error) {
// ToFile saves the body data in response to one file.
// it calls Response inner.
func (b *BeegoHTTPRequest) ToFile(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
resp, err := b.getResponse()
if err != nil {
return err
@ -572,10 +569,35 @@ func (b *BeegoHTTPRequest) ToFile(filename string) error {
return nil
}
defer resp.Body.Close()
err = pathExistAndMkdir(filename)
if err != nil {
return err
}
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}
//Check that the file directory exists, there is no automatically created
func pathExistAndMkdir(filename string) (err error) {
filename = path.Dir(filename)
_, err = os.Stat(filename)
if err == nil {
return nil
}
if os.IsNotExist(err) {
err = os.MkdirAll(filename, os.ModePerm)
if err == nil {
return nil
}
}
return err
}
// ToJSON returns the map that marshals from the body bytes as json in response .
// it calls Response inner.
func (b *BeegoHTTPRequest) ToJSON(v interface{}) error {

View File

@ -16,6 +16,8 @@ package httplib
import (
"io/ioutil"
"net"
"net/http"
"os"
"strings"
"testing"
@ -161,7 +163,16 @@ func TestWithSetting(t *testing.T) {
var setting BeegoHTTPSettings
setting.EnableCookie = true
setting.UserAgent = v
setting.Transport = nil
setting.Transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 50,
IdleConnTimeout: 90 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
setting.ReadWriteTimeout = 5 * time.Second
SetDefaultSetting(setting)
@ -195,10 +206,16 @@ func TestToJson(t *testing.T) {
t.Fatal(err)
}
t.Log(ip.Origin)
if n := strings.Count(ip.Origin, "."); n != 3 {
ips := strings.Split(ip.Origin, ",")
if len(ips) == 0 {
t.Fatal("response is not valid ip")
}
for i := range ips {
if net.ParseIP(strings.TrimSpace(ips[i])).To4() == nil {
t.Fatal("response is not valid ip")
}
}
}
func TestToFile(t *testing.T) {
@ -215,6 +232,20 @@ func TestToFile(t *testing.T) {
}
}
func TestToFileDir(t *testing.T) {
f := "./files/beego_testfile"
req := Get("http://httpbin.org/ip")
err := req.ToFile(f)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll("./files")
b, err := ioutil.ReadFile(f)
if n := strings.Index(string(b), "origin"); n == -1 {
t.Fatal(err)
}
}
func TestHeader(t *testing.T) {
req := Get("http://httpbin.org/headers")
req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")

16
log.go
View File

@ -21,6 +21,7 @@ import (
)
// Log levels to control the logging output.
// Deprecated: use github.com/astaxie/beego/logs instead.
const (
LevelEmergency = iota
LevelAlert
@ -33,75 +34,90 @@ const (
)
// BeeLogger references the used application logger.
// Deprecated: use github.com/astaxie/beego/logs instead.
var BeeLogger = logs.GetBeeLogger()
// SetLevel sets the global log level used by the simple logger.
// Deprecated: use github.com/astaxie/beego/logs instead.
func SetLevel(l int) {
logs.SetLevel(l)
}
// SetLogFuncCall set the CallDepth, default is 3
// Deprecated: use github.com/astaxie/beego/logs instead.
func SetLogFuncCall(b bool) {
logs.SetLogFuncCall(b)
}
// SetLogger sets a new logger.
// Deprecated: use github.com/astaxie/beego/logs instead.
func SetLogger(adaptername string, config string) error {
return logs.SetLogger(adaptername, config)
}
// Emergency logs a message at emergency level.
// Deprecated: use github.com/astaxie/beego/logs instead.
func Emergency(v ...interface{}) {
logs.Emergency(generateFmtStr(len(v)), v...)
}
// Alert logs a message at alert level.
// Deprecated: use github.com/astaxie/beego/logs instead.
func Alert(v ...interface{}) {
logs.Alert(generateFmtStr(len(v)), v...)
}
// Critical logs a message at critical level.
// Deprecated: use github.com/astaxie/beego/logs instead.
func Critical(v ...interface{}) {
logs.Critical(generateFmtStr(len(v)), v...)
}
// Error logs a message at error level.
// Deprecated: use github.com/astaxie/beego/logs instead.
func Error(v ...interface{}) {
logs.Error(generateFmtStr(len(v)), v...)
}
// Warning logs a message at warning level.
// Deprecated: use github.com/astaxie/beego/logs instead.
func Warning(v ...interface{}) {
logs.Warning(generateFmtStr(len(v)), v...)
}
// Warn compatibility alias for Warning()
// Deprecated: use github.com/astaxie/beego/logs instead.
func Warn(v ...interface{}) {
logs.Warn(generateFmtStr(len(v)), v...)
}
// Notice logs a message at notice level.
// Deprecated: use github.com/astaxie/beego/logs instead.
func Notice(v ...interface{}) {
logs.Notice(generateFmtStr(len(v)), v...)
}
// Informational logs a message at info level.
// Deprecated: use github.com/astaxie/beego/logs instead.
func Informational(v ...interface{}) {
logs.Informational(generateFmtStr(len(v)), v...)
}
// Info compatibility alias for Warning()
// Deprecated: use github.com/astaxie/beego/logs instead.
func Info(v ...interface{}) {
logs.Info(generateFmtStr(len(v)), v...)
}
// Debug logs a message at debug level.
// Deprecated: use github.com/astaxie/beego/logs instead.
func Debug(v ...interface{}) {
logs.Debug(generateFmtStr(len(v)), v...)
}
// Trace logs a message at trace level.
// compatibility alias for Warning()
// Deprecated: use github.com/astaxie/beego/logs instead.
func Trace(v ...interface{}) {
logs.Trace(generateFmtStr(len(v)), v...)
}

View File

@ -1,428 +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.
// +build windows
package logs
import (
"bytes"
"io"
"strings"
"syscall"
"unsafe"
)
type (
csiState int
parseResult int
)
const (
outsideCsiCode csiState = iota
firstCsiCode
secondCsiCode
)
const (
noConsole parseResult = iota
changedColor
unknown
)
type ansiColorWriter struct {
w io.Writer
mode outputMode
state csiState
paramStartBuf bytes.Buffer
paramBuf bytes.Buffer
}
const (
firstCsiChar byte = '\x1b'
secondeCsiChar byte = '['
separatorChar byte = ';'
sgrCode byte = 'm'
)
const (
foregroundBlue = uint16(0x0001)
foregroundGreen = uint16(0x0002)
foregroundRed = uint16(0x0004)
foregroundIntensity = uint16(0x0008)
backgroundBlue = uint16(0x0010)
backgroundGreen = uint16(0x0020)
backgroundRed = uint16(0x0040)
backgroundIntensity = uint16(0x0080)
underscore = uint16(0x8000)
foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
)
const (
ansiReset = "0"
ansiIntensityOn = "1"
ansiIntensityOff = "21"
ansiUnderlineOn = "4"
ansiUnderlineOff = "24"
ansiBlinkOn = "5"
ansiBlinkOff = "25"
ansiForegroundBlack = "30"
ansiForegroundRed = "31"
ansiForegroundGreen = "32"
ansiForegroundYellow = "33"
ansiForegroundBlue = "34"
ansiForegroundMagenta = "35"
ansiForegroundCyan = "36"
ansiForegroundWhite = "37"
ansiForegroundDefault = "39"
ansiBackgroundBlack = "40"
ansiBackgroundRed = "41"
ansiBackgroundGreen = "42"
ansiBackgroundYellow = "43"
ansiBackgroundBlue = "44"
ansiBackgroundMagenta = "45"
ansiBackgroundCyan = "46"
ansiBackgroundWhite = "47"
ansiBackgroundDefault = "49"
ansiLightForegroundGray = "90"
ansiLightForegroundRed = "91"
ansiLightForegroundGreen = "92"
ansiLightForegroundYellow = "93"
ansiLightForegroundBlue = "94"
ansiLightForegroundMagenta = "95"
ansiLightForegroundCyan = "96"
ansiLightForegroundWhite = "97"
ansiLightBackgroundGray = "100"
ansiLightBackgroundRed = "101"
ansiLightBackgroundGreen = "102"
ansiLightBackgroundYellow = "103"
ansiLightBackgroundBlue = "104"
ansiLightBackgroundMagenta = "105"
ansiLightBackgroundCyan = "106"
ansiLightBackgroundWhite = "107"
)
type drawType int
const (
foreground drawType = iota
background
)
type winColor struct {
code uint16
drawType drawType
}
var colorMap = map[string]winColor{
ansiForegroundBlack: {0, foreground},
ansiForegroundRed: {foregroundRed, foreground},
ansiForegroundGreen: {foregroundGreen, foreground},
ansiForegroundYellow: {foregroundRed | foregroundGreen, foreground},
ansiForegroundBlue: {foregroundBlue, foreground},
ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
ansiForegroundCyan: {foregroundGreen | foregroundBlue, foreground},
ansiForegroundWhite: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
ansiBackgroundBlack: {0, background},
ansiBackgroundRed: {backgroundRed, background},
ansiBackgroundGreen: {backgroundGreen, background},
ansiBackgroundYellow: {backgroundRed | backgroundGreen, background},
ansiBackgroundBlue: {backgroundBlue, background},
ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
ansiBackgroundCyan: {backgroundGreen | backgroundBlue, background},
ansiBackgroundWhite: {backgroundRed | backgroundGreen | backgroundBlue, background},
ansiBackgroundDefault: {0, background},
ansiLightForegroundGray: {foregroundIntensity, foreground},
ansiLightForegroundRed: {foregroundIntensity | foregroundRed, foreground},
ansiLightForegroundGreen: {foregroundIntensity | foregroundGreen, foreground},
ansiLightForegroundYellow: {foregroundIntensity | foregroundRed | foregroundGreen, foreground},
ansiLightForegroundBlue: {foregroundIntensity | foregroundBlue, foreground},
ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground},
ansiLightForegroundCyan: {foregroundIntensity | foregroundGreen | foregroundBlue, foreground},
ansiLightForegroundWhite: {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground},
ansiLightBackgroundGray: {backgroundIntensity, background},
ansiLightBackgroundRed: {backgroundIntensity | backgroundRed, background},
ansiLightBackgroundGreen: {backgroundIntensity | backgroundGreen, background},
ansiLightBackgroundYellow: {backgroundIntensity | backgroundRed | backgroundGreen, background},
ansiLightBackgroundBlue: {backgroundIntensity | backgroundBlue, background},
ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background},
ansiLightBackgroundCyan: {backgroundIntensity | backgroundGreen | backgroundBlue, background},
ansiLightBackgroundWhite: {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background},
}
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
defaultAttr *textAttributes
)
func init() {
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
if screenInfo != nil {
colorMap[ansiForegroundDefault] = winColor{
screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue),
foreground,
}
colorMap[ansiBackgroundDefault] = winColor{
screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue),
background,
}
defaultAttr = convertTextAttr(screenInfo.WAttributes)
}
}
type coord struct {
X, Y int16
}
type smallRect struct {
Left, Top, Right, Bottom int16
}
type consoleScreenBufferInfo struct {
DwSize coord
DwCursorPosition coord
WAttributes uint16
SrWindow smallRect
DwMaximumWindowSize coord
}
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
var csbi consoleScreenBufferInfo
ret, _, _ := procGetConsoleScreenBufferInfo.Call(
hConsoleOutput,
uintptr(unsafe.Pointer(&csbi)))
if ret == 0 {
return nil
}
return &csbi
}
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
ret, _, _ := procSetConsoleTextAttribute.Call(
hConsoleOutput,
uintptr(wAttributes))
return ret != 0
}
type textAttributes struct {
foregroundColor uint16
backgroundColor uint16
foregroundIntensity uint16
backgroundIntensity uint16
underscore uint16
otherAttributes uint16
}
func convertTextAttr(winAttr uint16) *textAttributes {
fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue)
bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue)
fgIntensity := winAttr & foregroundIntensity
bgIntensity := winAttr & backgroundIntensity
underline := winAttr & underscore
otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore)
return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes}
}
func convertWinAttr(textAttr *textAttributes) uint16 {
var winAttr uint16
winAttr |= textAttr.foregroundColor
winAttr |= textAttr.backgroundColor
winAttr |= textAttr.foregroundIntensity
winAttr |= textAttr.backgroundIntensity
winAttr |= textAttr.underscore
winAttr |= textAttr.otherAttributes
return winAttr
}
func changeColor(param []byte) parseResult {
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
if screenInfo == nil {
return noConsole
}
winAttr := convertTextAttr(screenInfo.WAttributes)
strParam := string(param)
if len(strParam) <= 0 {
strParam = "0"
}
csiParam := strings.Split(strParam, string(separatorChar))
for _, p := range csiParam {
c, ok := colorMap[p]
switch {
case !ok:
switch p {
case ansiReset:
winAttr.foregroundColor = defaultAttr.foregroundColor
winAttr.backgroundColor = defaultAttr.backgroundColor
winAttr.foregroundIntensity = defaultAttr.foregroundIntensity
winAttr.backgroundIntensity = defaultAttr.backgroundIntensity
winAttr.underscore = 0
winAttr.otherAttributes = 0
case ansiIntensityOn:
winAttr.foregroundIntensity = foregroundIntensity
case ansiIntensityOff:
winAttr.foregroundIntensity = 0
case ansiUnderlineOn:
winAttr.underscore = underscore
case ansiUnderlineOff:
winAttr.underscore = 0
case ansiBlinkOn:
winAttr.backgroundIntensity = backgroundIntensity
case ansiBlinkOff:
winAttr.backgroundIntensity = 0
default:
// unknown code
}
case c.drawType == foreground:
winAttr.foregroundColor = c.code
case c.drawType == background:
winAttr.backgroundColor = c.code
}
}
winTextAttribute := convertWinAttr(winAttr)
setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute)
return changedColor
}
func parseEscapeSequence(command byte, param []byte) parseResult {
if defaultAttr == nil {
return noConsole
}
switch command {
case sgrCode:
return changeColor(param)
default:
return unknown
}
}
func (cw *ansiColorWriter) flushBuffer() (int, error) {
return cw.flushTo(cw.w)
}
func (cw *ansiColorWriter) resetBuffer() (int, error) {
return cw.flushTo(nil)
}
func (cw *ansiColorWriter) flushTo(w io.Writer) (int, error) {
var n1, n2 int
var err error
startBytes := cw.paramStartBuf.Bytes()
cw.paramStartBuf.Reset()
if w != nil {
n1, err = cw.w.Write(startBytes)
if err != nil {
return n1, err
}
} else {
n1 = len(startBytes)
}
paramBytes := cw.paramBuf.Bytes()
cw.paramBuf.Reset()
if w != nil {
n2, err = cw.w.Write(paramBytes)
if err != nil {
return n1 + n2, err
}
} else {
n2 = len(paramBytes)
}
return n1 + n2, nil
}
func isParameterChar(b byte) bool {
return ('0' <= b && b <= '9') || b == separatorChar
}
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
var r, nw, first, last int
if cw.mode != DiscardNonColorEscSeq {
cw.state = outsideCsiCode
cw.resetBuffer()
}
var err error
for i, ch := range p {
switch cw.state {
case outsideCsiCode:
if ch == firstCsiChar {
cw.paramStartBuf.WriteByte(ch)
cw.state = firstCsiCode
}
case firstCsiCode:
switch ch {
case firstCsiChar:
cw.paramStartBuf.WriteByte(ch)
break
case secondeCsiChar:
cw.paramStartBuf.WriteByte(ch)
cw.state = secondCsiCode
last = i - 1
default:
cw.resetBuffer()
cw.state = outsideCsiCode
}
case secondCsiCode:
if isParameterChar(ch) {
cw.paramBuf.WriteByte(ch)
} else {
nw, err = cw.w.Write(p[first:last])
r += nw
if err != nil {
return r, err
}
first = i + 1
result := parseEscapeSequence(ch, cw.paramBuf.Bytes())
if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) {
cw.paramBuf.WriteByte(ch)
nw, err := cw.flushBuffer()
if err != nil {
return r, err
}
r += nw
} else {
n, _ := cw.resetBuffer()
// Add one more to the size of the buffer for the last ch
r += n + 1
}
cw.state = outsideCsiCode
}
default:
cw.state = outsideCsiCode
}
}
if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode {
nw, err = cw.w.Write(p[first:])
r += nw
}
return r, err
}

View File

@ -1,294 +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.
// +build windows
package logs
import (
"bytes"
"fmt"
"syscall"
"testing"
)
var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo
func ChangeColor(color uint16) {
setConsoleTextAttribute(uintptr(syscall.Stdout), color)
}
func ResetColor() {
ChangeColor(uint16(0x0007))
}
func TestWritePlanText(t *testing.T) {
inner := bytes.NewBufferString("")
w := NewAnsiColorWriter(inner)
expected := "plain text"
fmt.Fprintf(w, expected)
actual := inner.String()
if actual != expected {
t.Errorf("Get %q, want %q", actual, expected)
}
}
func TestWriteParseText(t *testing.T) {
inner := bytes.NewBufferString("")
w := NewAnsiColorWriter(inner)
inputTail := "\x1b[0mtail text"
expectedTail := "tail text"
fmt.Fprintf(w, inputTail)
actualTail := inner.String()
inner.Reset()
if actualTail != expectedTail {
t.Errorf("Get %q, want %q", actualTail, expectedTail)
}
inputHead := "head text\x1b[0m"
expectedHead := "head text"
fmt.Fprintf(w, inputHead)
actualHead := inner.String()
inner.Reset()
if actualHead != expectedHead {
t.Errorf("Get %q, want %q", actualHead, expectedHead)
}
inputBothEnds := "both ends \x1b[0m text"
expectedBothEnds := "both ends text"
fmt.Fprintf(w, inputBothEnds)
actualBothEnds := inner.String()
inner.Reset()
if actualBothEnds != expectedBothEnds {
t.Errorf("Get %q, want %q", actualBothEnds, expectedBothEnds)
}
inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc"
expectedManyEsc := "\x1b\x1b\x1b many esc"
fmt.Fprintf(w, inputManyEsc)
actualManyEsc := inner.String()
inner.Reset()
if actualManyEsc != expectedManyEsc {
t.Errorf("Get %q, want %q", actualManyEsc, expectedManyEsc)
}
expectedSplit := "split text"
for _, ch := range "split \x1b[0m text" {
fmt.Fprintf(w, string(ch))
}
actualSplit := inner.String()
inner.Reset()
if actualSplit != expectedSplit {
t.Errorf("Get %q, want %q", actualSplit, expectedSplit)
}
}
type screenNotFoundError struct {
error
}
func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) {
inner := bytes.NewBufferString("")
w := NewAnsiColorWriter(inner)
fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText)
actualText = inner.String()
screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
if screenInfo != nil {
actualAttributes = screenInfo.WAttributes
} else {
err = &screenNotFoundError{}
}
return
}
type testParam struct {
text string
attributes uint16
ansiColor string
}
func TestWriteAnsiColorText(t *testing.T) {
screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
if screenInfo == nil {
t.Fatal("Could not get ConsoleScreenBufferInfo")
}
defer ChangeColor(screenInfo.WAttributes)
defaultFgColor := screenInfo.WAttributes & uint16(0x0007)
defaultBgColor := screenInfo.WAttributes & uint16(0x0070)
defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008)
defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080)
fgParam := []testParam{
{"foreground black ", uint16(0x0000 | 0x0000), "30"},
{"foreground red ", uint16(0x0004 | 0x0000), "31"},
{"foreground green ", uint16(0x0002 | 0x0000), "32"},
{"foreground yellow ", uint16(0x0006 | 0x0000), "33"},
{"foreground blue ", uint16(0x0001 | 0x0000), "34"},
{"foreground magenta", uint16(0x0005 | 0x0000), "35"},
{"foreground cyan ", uint16(0x0003 | 0x0000), "36"},
{"foreground white ", uint16(0x0007 | 0x0000), "37"},
{"foreground default", defaultFgColor | 0x0000, "39"},
{"foreground light gray ", uint16(0x0000 | 0x0008 | 0x0000), "90"},
{"foreground light red ", uint16(0x0004 | 0x0008 | 0x0000), "91"},
{"foreground light green ", uint16(0x0002 | 0x0008 | 0x0000), "92"},
{"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"},
{"foreground light blue ", uint16(0x0001 | 0x0008 | 0x0000), "94"},
{"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"},
{"foreground light cyan ", uint16(0x0003 | 0x0008 | 0x0000), "96"},
{"foreground light white ", uint16(0x0007 | 0x0008 | 0x0000), "97"},
}
bgParam := []testParam{
{"background black ", uint16(0x0007 | 0x0000), "40"},
{"background red ", uint16(0x0007 | 0x0040), "41"},
{"background green ", uint16(0x0007 | 0x0020), "42"},
{"background yellow ", uint16(0x0007 | 0x0060), "43"},
{"background blue ", uint16(0x0007 | 0x0010), "44"},
{"background magenta", uint16(0x0007 | 0x0050), "45"},
{"background cyan ", uint16(0x0007 | 0x0030), "46"},
{"background white ", uint16(0x0007 | 0x0070), "47"},
{"background default", uint16(0x0007) | defaultBgColor, "49"},
{"background light gray ", uint16(0x0007 | 0x0000 | 0x0080), "100"},
{"background light red ", uint16(0x0007 | 0x0040 | 0x0080), "101"},
{"background light green ", uint16(0x0007 | 0x0020 | 0x0080), "102"},
{"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"},
{"background light blue ", uint16(0x0007 | 0x0010 | 0x0080), "104"},
{"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"},
{"background light cyan ", uint16(0x0007 | 0x0030 | 0x0080), "106"},
{"background light white ", uint16(0x0007 | 0x0070 | 0x0080), "107"},
}
resetParam := []testParam{
{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"},
{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""},
}
boldParam := []testParam{
{"bold on", uint16(0x0007 | 0x0008), "1"},
{"bold off", uint16(0x0007), "21"},
}
underscoreParam := []testParam{
{"underscore on", uint16(0x0007 | 0x8000), "4"},
{"underscore off", uint16(0x0007), "24"},
}
blinkParam := []testParam{
{"blink on", uint16(0x0007 | 0x0080), "5"},
{"blink off", uint16(0x0007), "25"},
}
mixedParam := []testParam{
{"both black, bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"},
{"both red, bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"},
{"both green, bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"},
{"both yellow, bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"},
{"both blue, bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"},
{"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"},
{"both cyan, bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"},
{"both white, bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"},
{"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"},
}
assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) {
actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor)
if actualText != expectedText {
t.Errorf("Get %q, want %q", actualText, expectedText)
}
if err != nil {
t.Fatal("Could not get ConsoleScreenBufferInfo")
}
if actualAttributes != expectedAttributes {
t.Errorf("Text: %q, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes)
}
}
for _, v := range fgParam {
ResetColor()
assertTextAttribute(v.text, v.attributes, v.ansiColor)
}
for _, v := range bgParam {
ChangeColor(uint16(0x0070 | 0x0007))
assertTextAttribute(v.text, v.attributes, v.ansiColor)
}
for _, v := range resetParam {
ChangeColor(uint16(0x0000 | 0x0070 | 0x0008))
assertTextAttribute(v.text, v.attributes, v.ansiColor)
}
ResetColor()
for _, v := range boldParam {
assertTextAttribute(v.text, v.attributes, v.ansiColor)
}
ResetColor()
for _, v := range underscoreParam {
assertTextAttribute(v.text, v.attributes, v.ansiColor)
}
ResetColor()
for _, v := range blinkParam {
assertTextAttribute(v.text, v.attributes, v.ansiColor)
}
for _, v := range mixedParam {
ResetColor()
assertTextAttribute(v.text, v.attributes, v.ansiColor)
}
}
func TestIgnoreUnknownSequences(t *testing.T) {
inner := bytes.NewBufferString("")
w := NewModeAnsiColorWriter(inner, OutputNonColorEscSeq)
inputText := "\x1b[=decpath mode"
expectedTail := inputText
fmt.Fprintf(w, inputText)
actualTail := inner.String()
inner.Reset()
if actualTail != expectedTail {
t.Errorf("Get %q, want %q", actualTail, expectedTail)
}
inputText = "\x1b[=tailing esc and bracket\x1b["
expectedTail = inputText
fmt.Fprintf(w, inputText)
actualTail = inner.String()
inner.Reset()
if actualTail != expectedTail {
t.Errorf("Get %q, want %q", actualTail, expectedTail)
}
inputText = "\x1b[?tailing esc\x1b"
expectedTail = inputText
fmt.Fprintf(w, inputText)
actualTail = inner.String()
inner.Reset()
if actualTail != expectedTail {
t.Errorf("Get %q, want %q", actualTail, expectedTail)
}
inputText = "\x1b[1h;3punended color code invalid\x1b3"
expectedTail = inputText
fmt.Fprintf(w, inputText)
actualTail = inner.String()
inner.Reset()
if actualTail != expectedTail {
t.Errorf("Get %q, want %q", actualTail, expectedTail)
}
}

View File

@ -63,7 +63,7 @@ func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error {
defer c.innerWriter.Close()
}
c.lg.println(when, msg)
c.lg.writeln(when, msg)
return nil
}

View File

@ -17,8 +17,10 @@ package logs
import (
"encoding/json"
"os"
"runtime"
"strings"
"time"
"github.com/shiena/ansicolor"
)
// brush is a color join function
@ -54,9 +56,9 @@ type consoleWriter struct {
// NewConsole create ConsoleWriter returning as LoggerInterface.
func NewConsole() Logger {
cw := &consoleWriter{
lg: newLogWriter(os.Stdout),
lg: newLogWriter(ansicolor.NewAnsiColorWriter(os.Stdout)),
Level: LevelDebug,
Colorful: runtime.GOOS != "windows",
Colorful: true,
}
return cw
}
@ -67,11 +69,7 @@ func (c *consoleWriter) Init(jsonConfig string) error {
if len(jsonConfig) == 0 {
return nil
}
err := json.Unmarshal([]byte(jsonConfig), c)
if runtime.GOOS == "windows" {
c.Colorful = false
}
return err
return json.Unmarshal([]byte(jsonConfig), c)
}
// WriteMsg write message in console.
@ -80,9 +78,9 @@ func (c *consoleWriter) WriteMsg(when time.Time, msg string, level int) error {
return nil
}
if c.Colorful {
msg = colors[level](msg)
msg = strings.Replace(msg, levelPrefix[level], colors[level](levelPrefix[level]), 1)
}
c.lg.println(when, msg)
c.lg.writeln(when, msg)
return nil
}

View File

@ -16,6 +16,7 @@ package logs
import (
"testing"
"time"
)
// Try each log level in decreasing order of priority.
@ -49,3 +50,15 @@ func TestConsoleNoColor(t *testing.T) {
log.SetLogger("console", `{"color":false}`)
testConsoleCalls(log)
}
// Test console async
func TestConsoleAsync(t *testing.T) {
log := NewLogger(100)
log.SetLogger("console")
log.Async()
//log.Close()
testConsoleCalls(log)
for len(log.msgChan) != 0 {
time.Sleep(1 * time.Millisecond)
}
}

View File

@ -1,15 +1,18 @@
package es
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
"strings"
"time"
"github.com/elastic/go-elasticsearch/v6"
"github.com/elastic/go-elasticsearch/v6/esapi"
"github.com/astaxie/beego/logs"
"github.com/belogik/goes"
)
// NewES return a LoggerInterface
@ -20,8 +23,14 @@ func NewES() logs.Logger {
return cw
}
// esLogger will log msg into ES
// before you using this implementation,
// please import this package
// usually means that you can import this package in your main package
// for example, anonymous:
// import _ "github.com/astaxie/beego/logs/es"
type esLogger struct {
*goes.Connection
*elasticsearch.Client
DSN string `json:"dsn"`
Level int `json:"level"`
}
@ -38,11 +47,14 @@ func (el *esLogger) Init(jsonconfig string) error {
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
conn, err := elasticsearch.NewClient(elasticsearch.Config{
Addresses: []string{el.DSN},
})
if err != nil {
return err
}
el.Client = conn
}
return nil
}
@ -53,21 +65,26 @@ func (el *esLogger) WriteMsg(when time.Time, msg string, level int) error {
return nil
}
vals := make(map[string]interface{})
vals["@timestamp"] = when.Format(time.RFC3339)
vals["@msg"] = msg
d := goes.Document{
Index: fmt.Sprintf("%04d.%02d.%02d", when.Year(), when.Month(), when.Day()),
Type: "logs",
Fields: vals,
idx := LogDocument{
Timestamp: when.Format(time.RFC3339),
Msg: msg,
}
_, err := el.Index(d, nil)
body, err := json.Marshal(idx)
if err != nil {
return err
}
req := esapi.IndexRequest{
Index: fmt.Sprintf("%04d.%02d.%02d", when.Year(), when.Month(), when.Day()),
DocumentType: "logs",
Body: strings.NewReader(string(body)),
}
_, err = req.Do(context.Background(), el.Client)
return err
}
// Destroy is a empty method
func (el *esLogger) Destroy() {
}
// Flush is a empty method
@ -75,6 +92,11 @@ func (el *esLogger) Flush() {
}
type LogDocument struct {
Timestamp string `json:"timestamp"`
Msg string `json:"msg"`
}
func init() {
logs.Register(logs.AdapterEs, NewES)
}

View File

@ -54,6 +54,12 @@ type fileLogWriter struct {
dailyOpenDate int
dailyOpenTime time.Time
// Rotate hourly
Hourly bool `json:"hourly"`
MaxHours int64 `json:"maxhours"`
hourlyOpenDate int
hourlyOpenTime time.Time
Rotate bool `json:"rotate"`
Level int `json:"level"`
@ -70,6 +76,8 @@ func newFileWriter() Logger {
w := &fileLogWriter{
Daily: true,
MaxDays: 7,
Hourly: false,
MaxHours: 168,
Rotate: true,
RotatePerm: "0440",
Level: LevelTrace,
@ -122,10 +130,16 @@ func (w *fileLogWriter) startLogger() error {
return w.initFd()
}
func (w *fileLogWriter) needRotate(size int, day int) bool {
func (w *fileLogWriter) needRotateDaily(size int, day int) bool {
return (w.MaxLines > 0 && w.maxLinesCurLines >= w.MaxLines) ||
(w.MaxSize > 0 && w.maxSizeCurSize >= w.MaxSize) ||
(w.Daily && day != w.dailyOpenDate)
}
func (w *fileLogWriter) needRotateHourly(size int, hour int) bool {
return (w.MaxLines > 0 && w.maxLinesCurLines >= w.MaxLines) ||
(w.MaxSize > 0 && w.maxSizeCurSize >= w.MaxSize) ||
(w.Hourly && hour != w.hourlyOpenDate)
}
@ -134,14 +148,23 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error {
if level > w.Level {
return nil
}
h, d := formatTimeHeader(when)
msg = string(h) + msg + "\n"
hd, d, h := formatTimeHeader(when)
msg = string(hd) + msg + "\n"
if w.Rotate {
w.RLock()
if w.needRotate(len(msg), d) {
if w.needRotateHourly(len(msg), h) {
w.RUnlock()
w.Lock()
if w.needRotate(len(msg), d) {
if w.needRotateHourly(len(msg), h) {
if err := w.doRotate(when); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
}
}
w.Unlock()
} else if w.needRotateDaily(len(msg), d) {
w.RUnlock()
w.Lock()
if w.needRotateDaily(len(msg), d) {
if err := w.doRotate(when); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
}
@ -189,8 +212,12 @@ func (w *fileLogWriter) initFd() error {
w.maxSizeCurSize = int(fInfo.Size())
w.dailyOpenTime = time.Now()
w.dailyOpenDate = w.dailyOpenTime.Day()
w.hourlyOpenTime = time.Now()
w.hourlyOpenDate = w.hourlyOpenTime.Hour()
w.maxLinesCurLines = 0
if w.Daily {
if w.Hourly {
go w.hourlyRotate(w.hourlyOpenTime)
} else if w.Daily {
go w.dailyRotate(w.dailyOpenTime)
}
if fInfo.Size() > 0 && w.MaxLines > 0 {
@ -209,7 +236,22 @@ func (w *fileLogWriter) dailyRotate(openTime time.Time) {
tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100))
<-tm.C
w.Lock()
if w.needRotate(0, time.Now().Day()) {
if w.needRotateDaily(0, time.Now().Day()) {
if err := w.doRotate(time.Now()); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
}
}
w.Unlock()
}
func (w *fileLogWriter) hourlyRotate(openTime time.Time) {
y, m, d := openTime.Add(1 * time.Hour).Date()
h, _, _ := openTime.Add(1 * time.Hour).Clock()
nextHour := time.Date(y, m, d, h, 0, 0, 0, openTime.Location())
tm := time.NewTimer(time.Duration(nextHour.UnixNano() - openTime.UnixNano() + 100))
<-tm.C
w.Lock()
if w.needRotateHourly(0, time.Now().Hour()) {
if err := w.doRotate(time.Now()); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
}
@ -251,6 +293,8 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
// Find the next available number
num := w.MaxFilesCurFiles + 1
fName := ""
format := ""
var openTime time.Time
rotatePerm, err := strconv.ParseInt(w.RotatePerm, 8, 64)
if err != nil {
return err
@ -262,17 +306,26 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
goto RESTART_LOGGER
}
if w.Hourly {
format = "2006010215"
openTime = w.hourlyOpenTime
} else if w.Daily {
format = "2006-01-02"
openTime = w.dailyOpenTime
}
// only when one of them be setted, then the file would be splited
if w.MaxLines > 0 || w.MaxSize > 0 {
for ; err == nil && num <= w.MaxFiles; num++ {
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, w.suffix)
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format(format), num, w.suffix)
_, err = os.Lstat(fName)
}
} else {
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", w.dailyOpenTime.Format("2006-01-02"), num, w.suffix)
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", openTime.Format(format), num, w.suffix)
_, err = os.Lstat(fName)
w.MaxFilesCurFiles = num
}
// return error if the last file checked still existed
if err == nil {
return fmt.Errorf("Rotate: Cannot find free log number to rename %s", w.Filename)
@ -306,6 +359,10 @@ RESTART_LOGGER:
func (w *fileLogWriter) deleteOldLog() {
dir := filepath.Dir(w.Filename)
absolutePath, err := filepath.EvalSymlinks(w.Filename)
if err == nil {
dir = filepath.Dir(absolutePath)
}
filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
defer func() {
if r := recover(); r != nil {
@ -316,12 +373,20 @@ func (w *fileLogWriter) deleteOldLog() {
if info == nil {
return
}
if w.Hourly {
if !info.IsDir() && info.ModTime().Add(1 * time.Hour * time.Duration(w.MaxHours)).Before(time.Now()) {
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
strings.HasSuffix(filepath.Base(path), w.suffix) {
os.Remove(path)
}
}
} else if w.Daily {
if !info.IsDir() && info.ModTime().Add(24 * time.Hour * time.Duration(w.MaxDays)).Before(time.Now()) {
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
strings.HasSuffix(filepath.Base(path), w.suffix) {
os.Remove(path)
}
}
}
return
})

View File

@ -112,7 +112,7 @@ func TestFile2(t *testing.T) {
os.Remove("test2.log")
}
func TestFileRotate_01(t *testing.T) {
func TestFileDailyRotate_01(t *testing.T) {
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`)
log.Debug("debug")
@ -133,28 +133,28 @@ func TestFileRotate_01(t *testing.T) {
os.Remove("test3.log")
}
func TestFileRotate_02(t *testing.T) {
func TestFileDailyRotate_02(t *testing.T) {
fn1 := "rotate_day.log"
fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log"
testFileRotate(t, fn1, fn2)
testFileRotate(t, fn1, fn2, true, false)
}
func TestFileRotate_03(t *testing.T) {
func TestFileDailyRotate_03(t *testing.T) {
fn1 := "rotate_day.log"
fn := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log"
os.Create(fn)
fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log"
testFileRotate(t, fn1, fn2)
testFileRotate(t, fn1, fn2, true, false)
os.Remove(fn)
}
func TestFileRotate_04(t *testing.T) {
func TestFileDailyRotate_04(t *testing.T) {
fn1 := "rotate_day.log"
fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log"
testFileDailyRotate(t, fn1, fn2)
}
func TestFileRotate_05(t *testing.T) {
func TestFileDailyRotate_05(t *testing.T) {
fn1 := "rotate_day.log"
fn := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log"
os.Create(fn)
@ -162,7 +162,7 @@ func TestFileRotate_05(t *testing.T) {
testFileDailyRotate(t, fn1, fn2)
os.Remove(fn)
}
func TestFileRotate_06(t *testing.T) { //test file mode
func TestFileDailyRotate_06(t *testing.T) { //test file mode
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`)
log.Debug("debug")
@ -183,18 +183,104 @@ func TestFileRotate_06(t *testing.T) { //test file mode
os.Remove(rotateName)
os.Remove("test3.log")
}
func testFileRotate(t *testing.T, fn1, fn2 string) {
func TestFileHourlyRotate_01(t *testing.T) {
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test3.log","hourly":true,"maxlines":4}`)
log.Debug("debug")
log.Info("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error")
log.Alert("alert")
log.Critical("critical")
log.Emergency("emergency")
rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006010215"), 1) + ".log"
b, err := exists(rotateName)
if !b || err != nil {
os.Remove("test3.log")
t.Fatal("rotate not generated")
}
os.Remove(rotateName)
os.Remove("test3.log")
}
func TestFileHourlyRotate_02(t *testing.T) {
fn1 := "rotate_hour.log"
fn2 := "rotate_hour." + time.Now().Add(-1*time.Hour).Format("2006010215") + ".001.log"
testFileRotate(t, fn1, fn2, false, true)
}
func TestFileHourlyRotate_03(t *testing.T) {
fn1 := "rotate_hour.log"
fn := "rotate_hour." + time.Now().Add(-1*time.Hour).Format("2006010215") + ".log"
os.Create(fn)
fn2 := "rotate_hour." + time.Now().Add(-1*time.Hour).Format("2006010215") + ".001.log"
testFileRotate(t, fn1, fn2, false, true)
os.Remove(fn)
}
func TestFileHourlyRotate_04(t *testing.T) {
fn1 := "rotate_hour.log"
fn2 := "rotate_hour." + time.Now().Add(-1*time.Hour).Format("2006010215") + ".001.log"
testFileHourlyRotate(t, fn1, fn2)
}
func TestFileHourlyRotate_05(t *testing.T) {
fn1 := "rotate_hour.log"
fn := "rotate_hour." + time.Now().Add(-1*time.Hour).Format("2006010215") + ".log"
os.Create(fn)
fn2 := "rotate_hour." + time.Now().Add(-1*time.Hour).Format("2006010215") + ".001.log"
testFileHourlyRotate(t, fn1, fn2)
os.Remove(fn)
}
func TestFileHourlyRotate_06(t *testing.T) { //test file mode
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test3.log", "hourly":true, "maxlines":4}`)
log.Debug("debug")
log.Info("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error")
log.Alert("alert")
log.Critical("critical")
log.Emergency("emergency")
rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006010215"), 1) + ".log"
s, _ := os.Lstat(rotateName)
if s.Mode() != 0440 {
os.Remove(rotateName)
os.Remove("test3.log")
t.Fatal("rotate file mode error")
}
os.Remove(rotateName)
os.Remove("test3.log")
}
func testFileRotate(t *testing.T, fn1, fn2 string, daily, hourly bool) {
fw := &fileLogWriter{
Daily: true,
Daily: daily,
MaxDays: 7,
Hourly: hourly,
MaxHours: 168,
Rotate: true,
Level: LevelTrace,
Perm: "0660",
RotatePerm: "0440",
}
if daily {
fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
fw.dailyOpenDate = fw.dailyOpenTime.Day()
}
if hourly {
fw.Init(fmt.Sprintf(`{"filename":"%v","maxhours":1}`, fn1))
fw.hourlyOpenTime = time.Now().Add(-1 * time.Hour)
fw.hourlyOpenDate = fw.hourlyOpenTime.Day()
}
fw.WriteMsg(time.Now(), "this is a msg for test", LevelDebug)
for _, file := range []string{fn1, fn2} {
@ -240,6 +326,37 @@ func testFileDailyRotate(t *testing.T, fn1, fn2 string) {
fw.Destroy()
}
func testFileHourlyRotate(t *testing.T, fn1, fn2 string) {
fw := &fileLogWriter{
Hourly: true,
MaxHours: 168,
Rotate: true,
Level: LevelTrace,
Perm: "0660",
RotatePerm: "0440",
}
fw.Init(fmt.Sprintf(`{"filename":"%v","maxhours":1}`, fn1))
fw.hourlyOpenTime = time.Now().Add(-1 * time.Hour)
fw.hourlyOpenDate = fw.hourlyOpenTime.Hour()
hour, _ := time.ParseInLocation("2006010215", time.Now().Format("2006010215"), fw.hourlyOpenTime.Location())
hour = hour.Add(-1 * time.Second)
fw.hourlyRotate(hour)
for _, file := range []string{fn1, fn2} {
_, err := os.Stat(file)
if err != nil {
t.FailNow()
}
content, err := ioutil.ReadFile(file)
if err != nil {
t.FailNow()
}
if len(content) > 0 {
t.FailNow()
}
os.Remove(file)
}
fw.Destroy()
}
func exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {

View File

@ -92,7 +92,7 @@ type Logger interface {
}
var adapters = make(map[string]newLoggerFunc)
var levelPrefix = [LevelDebug + 1]string{"[M] ", "[A] ", "[C] ", "[E] ", "[W] ", "[N] ", "[I] ", "[D] "}
var levelPrefix = [LevelDebug + 1]string{"[M]", "[A]", "[C]", "[E]", "[W]", "[N]", "[I]", "[D]"}
// Register makes a log provide available by the provided name.
// If Register is called twice with the same name or if driver is nil,
@ -187,12 +187,12 @@ func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error {
}
}
log, ok := adapters[adapterName]
logAdapter, ok := adapters[adapterName]
if !ok {
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adapterName)
}
lg := log()
lg := logAdapter()
err := lg.Init(config)
if err != nil {
fmt.Fprintln(os.Stderr, "logs.BeeLogger.SetLogger: "+err.Error())
@ -248,7 +248,7 @@ func (bl *BeeLogger) Write(p []byte) (n int, err error) {
}
// writeMsg will always add a '\n' character
if p[len(p)-1] == '\n' {
p = p[0: len(p)-1]
p = p[0 : len(p)-1]
}
// set levelLoggerImpl to ensure all log message will be write out
err = bl.writeMsg(levelLoggerImpl, string(p))
@ -287,7 +287,7 @@ func (bl *BeeLogger) writeMsg(logLevel int, msg string, v ...interface{}) error
// set to emergency to ensure all log will be print out correctly
logLevel = LevelEmergency
} else {
msg = levelPrefix[logLevel] + msg
msg = levelPrefix[logLevel] + " " + msg
}
if bl.asynchronous {
@ -295,7 +295,11 @@ func (bl *BeeLogger) writeMsg(logLevel int, msg string, v ...interface{}) error
lm.level = logLevel
lm.msg = msg
lm.when = when
if bl.outputs != nil {
bl.msgChan <- lm
} else {
logMsgPool.Put(lm)
}
} else {
bl.writeToLoggers(when, msg, logLevel)
}

View File

@ -15,9 +15,8 @@
package logs
import (
"fmt"
"io"
"os"
"runtime"
"sync"
"time"
)
@ -31,47 +30,13 @@ func newLogWriter(wr io.Writer) *logWriter {
return &logWriter{writer: wr}
}
func (lg *logWriter) println(when time.Time, msg string) {
func (lg *logWriter) writeln(when time.Time, msg string) {
lg.Lock()
h, _ := formatTimeHeader(when)
h, _, _ := formatTimeHeader(when)
lg.writer.Write(append(append(h, msg...), '\n'))
lg.Unlock()
}
type outputMode int
// DiscardNonColorEscSeq supports the divided color escape sequence.
// But non-color escape sequence is not output.
// Please use the OutputNonColorEscSeq If you want to output a non-color
// escape sequences such as ncurses. However, it does not support the divided
// color escape sequence.
const (
_ outputMode = iota
DiscardNonColorEscSeq
OutputNonColorEscSeq
)
// NewAnsiColorWriter creates and initializes a new ansiColorWriter
// using io.Writer w as its initial contents.
// In the console of Windows, which change the foreground and background
// colors of the text by the escape sequence.
// In the console of other systems, which writes to w all text.
func NewAnsiColorWriter(w io.Writer) io.Writer {
return NewModeAnsiColorWriter(w, DiscardNonColorEscSeq)
}
// NewModeAnsiColorWriter create and initializes a new ansiColorWriter
// by specifying the outputMode.
func NewModeAnsiColorWriter(w io.Writer, mode outputMode) io.Writer {
if _, ok := w.(*ansiColorWriter); !ok {
return &ansiColorWriter{
w: w,
mode: mode,
}
}
return w
}
const (
y1 = `0123456789`
y2 = `0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789`
@ -90,7 +55,7 @@ const (
ns1 = `0123456789`
)
func formatTimeHeader(when time.Time) ([]byte, int) {
func formatTimeHeader(when time.Time) ([]byte, int, int) {
y, mo, d := when.Date()
h, mi, s := when.Clock()
ns := when.Nanosecond() / 1000000
@ -123,7 +88,7 @@ func formatTimeHeader(when time.Time) ([]byte, int) {
buf[23] = ' '
return buf[0:], d
return buf[0:], d, h
}
var (
@ -146,63 +111,65 @@ var (
reset = string([]byte{27, 91, 48, 109})
)
var once sync.Once
var colorMap map[string]string
func initColor() {
if runtime.GOOS == "windows" {
green = w32Green
white = w32White
yellow = w32Yellow
red = w32Red
blue = w32Blue
magenta = w32Magenta
cyan = w32Cyan
}
colorMap = map[string]string{
//by color
"green": green,
"white": white,
"yellow": yellow,
"red": red,
//by method
"GET": blue,
"POST": cyan,
"PUT": yellow,
"DELETE": red,
"PATCH": green,
"HEAD": magenta,
"OPTIONS": white,
}
}
// ColorByStatus return color by http code
// 2xx return Green
// 3xx return White
// 4xx return Yellow
// 5xx return Red
func ColorByStatus(cond bool, code int) string {
func ColorByStatus(code int) string {
once.Do(initColor)
switch {
case code >= 200 && code < 300:
return map[bool]string{true: green, false: w32Green}[cond]
return colorMap["green"]
case code >= 300 && code < 400:
return map[bool]string{true: white, false: w32White}[cond]
return colorMap["white"]
case code >= 400 && code < 500:
return map[bool]string{true: yellow, false: w32Yellow}[cond]
return colorMap["yellow"]
default:
return map[bool]string{true: red, false: w32Red}[cond]
return colorMap["red"]
}
}
// ColorByMethod return color by http code
// GET return Blue
// POST return Cyan
// PUT return Yellow
// DELETE return Red
// PATCH return Green
// HEAD return Magenta
// OPTIONS return WHITE
func ColorByMethod(cond bool, method string) string {
switch method {
case "GET":
return map[bool]string{true: blue, false: w32Blue}[cond]
case "POST":
return map[bool]string{true: cyan, false: w32Cyan}[cond]
case "PUT":
return map[bool]string{true: yellow, false: w32Yellow}[cond]
case "DELETE":
return map[bool]string{true: red, false: w32Red}[cond]
case "PATCH":
return map[bool]string{true: green, false: w32Green}[cond]
case "HEAD":
return map[bool]string{true: magenta, false: w32Magenta}[cond]
case "OPTIONS":
return map[bool]string{true: white, false: w32White}[cond]
default:
return reset
func ColorByMethod(method string) string {
once.Do(initColor)
if c := colorMap[method]; c != "" {
return c
}
return reset
}
// Guard Mutex to guarantee atomic of W32Debug(string) function
var mu sync.Mutex
// W32Debug Helper method to output colored logs in Windows terminals
func W32Debug(msg string) {
mu.Lock()
defer mu.Unlock()
current := time.Now()
w := NewAnsiColorWriter(os.Stdout)
fmt.Fprintf(w, "[beego] %v %s\n", current.Format("2006/01/02 - 15:04:05"), msg)
// ResetColor return reset color
func ResetColor() string {
return reset
}

View File

@ -15,7 +15,6 @@
package logs
import (
"bytes"
"testing"
"time"
)
@ -30,7 +29,7 @@ func TestFormatHeader_0(t *testing.T) {
if tm.Year() >= 2100 {
break
}
h, _ := formatTimeHeader(tm)
h, _, _ := formatTimeHeader(tm)
if tm.Format("2006/01/02 15:04:05.000 ") != string(h) {
t.Log(tm)
t.FailNow()
@ -48,7 +47,7 @@ func TestFormatHeader_1(t *testing.T) {
if tm.Year() >= year+1 {
break
}
h, _ := formatTimeHeader(tm)
h, _, _ := formatTimeHeader(tm)
if tm.Format("2006/01/02 15:04:05.000 ") != string(h) {
t.Log(tm)
t.FailNow()
@ -56,20 +55,3 @@ func TestFormatHeader_1(t *testing.T) {
tm = tm.Add(dur)
}
}
func TestNewAnsiColor1(t *testing.T) {
inner := bytes.NewBufferString("")
w := NewAnsiColorWriter(inner)
if w == inner {
t.Errorf("Get %#v, want %#v", w, inner)
}
}
func TestNewAnsiColor2(t *testing.T) {
inner := bytes.NewBufferString("")
w1 := NewAnsiColorWriter(inner)
w2 := NewAnsiColorWriter(w1)
if w1 != w2 {
t.Errorf("Get %#v, want %#v", w1, w2)
}
}

99
metric/prometheus.go Normal file
View File

@ -0,0 +1,99 @@
// Copyright 2020 astaxie
//
// 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 metric
import (
"net/http"
"reflect"
"strconv"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
)
func PrometheusMiddleWare(next http.Handler) http.Handler {
summaryVec := prometheus.NewSummaryVec(prometheus.SummaryOpts{
Name: "beego",
Subsystem: "http_request",
ConstLabels: map[string]string{
"server": beego.BConfig.ServerName,
"env": beego.BConfig.RunMode,
"appname": beego.BConfig.AppName,
},
Help: "The statics info for http request",
}, []string{"pattern", "method", "status", "duration"})
prometheus.MustRegister(summaryVec)
registerBuildInfo()
return http.HandlerFunc(func(writer http.ResponseWriter, q *http.Request) {
start := time.Now()
next.ServeHTTP(writer, q)
end := time.Now()
go report(end.Sub(start), writer, q, summaryVec)
})
}
func registerBuildInfo() {
buildInfo := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "beego",
Subsystem: "build_info",
Help: "The building information",
ConstLabels: map[string]string{
"appname": beego.BConfig.AppName,
"build_version": beego.BuildVersion,
"build_revision": beego.BuildGitRevision,
"build_status": beego.BuildStatus,
"build_tag": beego.BuildTag,
"build_time": strings.Replace(beego.BuildTime, "--", " ", 1),
"go_version": beego.GoVersion,
"git_branch": beego.GitBranch,
"start_time": time.Now().Format("2006-01-02 15:04:05"),
},
}, []string{})
prometheus.MustRegister(buildInfo)
buildInfo.WithLabelValues().Set(1)
}
func report(dur time.Duration, writer http.ResponseWriter, q *http.Request, vec *prometheus.SummaryVec) {
ctrl := beego.BeeApp.Handlers
ctx := ctrl.GetContext()
ctx.Reset(writer, q)
defer ctrl.GiveBackContext(ctx)
// We cannot read the status code from q.Response.StatusCode
// since the http server does not set q.Response. So q.Response is nil
// Thus, we use reflection to read the status from writer whose concrete type is http.response
responseVal := reflect.ValueOf(writer).Elem()
field := responseVal.FieldByName("status")
status := -1
if field.IsValid() && field.Kind() == reflect.Int {
status = int(field.Int())
}
ptn := "UNKNOWN"
if rt, found := ctrl.FindRouter(ctx); found {
ptn = rt.GetPattern()
} else {
logs.Warn("we can not find the router info for this request, so request will be recorded as UNKNOWN: " + q.URL.String())
}
ms := dur / time.Millisecond
vec.WithLabelValues(ptn, q.Method, strconv.Itoa(status), strconv.Itoa(int(ms))).Observe(float64(ms))
}

42
metric/prometheus_test.go Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2020 astaxie
//
// 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 metric
import (
"net/http"
"net/url"
"testing"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/astaxie/beego/context"
)
func TestPrometheusMiddleWare(t *testing.T) {
middleware := PrometheusMiddleWare(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
writer := &context.Response{}
request := &http.Request{
URL: &url.URL{
Host: "localhost",
RawPath: "/a/b/c",
},
Method: "POST",
}
vec := prometheus.NewSummaryVec(prometheus.SummaryOpts{}, []string{"pattern", "method", "status", "duration"})
report(time.Second, writer, request, vec)
middleware.ServeHTTP(writer, request)
}

View File

@ -17,7 +17,7 @@ package migration
import (
"fmt"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
)
// Index struct defines the structure of Index Columns
@ -316,7 +316,7 @@ func (m *Migration) GetSQL() (sql string) {
sql += fmt.Sprintf("ALTER TABLE `%s` ", m.TableName)
for index, column := range m.Columns {
if !column.remove {
beego.BeeLogger.Info("col")
logs.Info("col")
sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
} else {
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)

View File

@ -176,8 +176,9 @@ func Register(name string, m Migrationer) error {
func Upgrade(lasttime int64) error {
sm := sortMap(migrationMap)
i := 0
migs, _ := getAllMigrations()
for _, v := range sm {
if v.created > lasttime {
if _, ok := migs[v.name]; !ok {
logs.Info("start upgrade", v.name)
v.m.Reset()
v.m.Up()
@ -310,3 +311,20 @@ func isRollBack(name string) bool {
}
return false
}
func getAllMigrations() (map[string]string, error) {
o := orm.NewOrm()
var maps []orm.Params
migs := make(map[string]string)
num, err := o.Raw("select * from migrations order by id_migration desc").Values(&maps)
if err != nil {
logs.Info("get name has error", err)
return migs, err
}
if num > 0 {
for _, v := range maps {
name := v["name"].(string)
migs[name] = v["status"].(string)
}
}
return migs, nil
}

View File

@ -207,11 +207,11 @@ func (n *Namespace) Include(cList ...ControllerInterface) *Namespace {
func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
for _, ni := range ns {
for k, v := range ni.handlers.routers {
if t, ok := n.handlers.routers[k]; ok {
if _, ok := n.handlers.routers[k]; ok {
addPrefix(v, ni.prefix)
n.handlers.routers[k].AddTree(ni.prefix, v)
} else {
t = NewTree()
t := NewTree()
t.AddTree(ni.prefix, v)
addPrefix(t, ni.prefix)
n.handlers.routers[k] = t
@ -236,11 +236,11 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
func AddNamespace(nl ...*Namespace) {
for _, n := range nl {
for k, v := range n.handlers.routers {
if t, ok := BeeApp.Handlers.routers[k]; ok {
if _, ok := BeeApp.Handlers.routers[k]; ok {
addPrefix(v, n.prefix)
BeeApp.Handlers.routers[k].AddTree(n.prefix, v)
} else {
t = NewTree()
t := NewTree()
t.AddTree(n.prefix, v)
addPrefix(t, n.prefix)
BeeApp.Handlers.routers[k] = t

View File

@ -198,7 +198,7 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
column = strings.Replace(column, "%COL%", fi.column, -1)
}
if fi.description != "" {
if fi.description != "" && al.Driver!=DRSqlite {
column += " " + fmt.Sprintf("COMMENT '%s'",fi.description)
}

View File

@ -470,7 +470,7 @@ func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []s
multi := len(values) / len(names)
if isMulti {
if isMulti && multi > 1 {
qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks
}
@ -536,6 +536,8 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a
updates := make([]string, len(names))
var conflitValue interface{}
for i, v := range names {
// identifier in database may not be case-sensitive, so quote it
v = fmt.Sprintf("%s%s%s", Q, v, Q)
marks[i] = "?"
valueStr := argsMap[strings.ToLower(v)]
if v == args0 {
@ -619,6 +621,31 @@ func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
return 0, err
}
var findAutoNowAdd, findAutoNow bool
var index int
for i, col := range setNames {
if mi.fields.GetByColumn(col).autoNowAdd {
index = i
findAutoNowAdd = true
}
if mi.fields.GetByColumn(col).autoNow {
findAutoNow = true
}
}
if findAutoNowAdd {
setNames = append(setNames[0:index], setNames[index+1:]...)
setValues = append(setValues[0:index], setValues[index+1:]...)
}
if !findAutoNow {
for col, info := range mi.fields.columns {
if info.autoNow {
setNames = append(setNames, col)
setValues = append(setValues, time.Now())
}
}
}
setValues = append(setValues, pkValue)
Q := d.ins.TableQuote()
@ -675,13 +702,6 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
return 0, err
}
if num > 0 {
if mi.fields.pk.auto {
if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 {
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(0)
} else {
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(0)
}
}
err := d.deleteRels(q, mi, args, tz)
if err != nil {
return num, err
@ -743,6 +763,16 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
cols = append(cols, col+" = "+col+" * ?")
case ColExcept:
cols = append(cols, col+" = "+col+" / ?")
case ColBitAnd:
cols = append(cols, col+" = "+col+" & ?")
case ColBitRShift:
cols = append(cols, col+" = "+col+" >> ?")
case ColBitLShift:
cols = append(cols, col+" = "+col+" << ?")
case ColBitXOR:
cols = append(cols, col+" = "+col+" ^ ?")
case ColBitOr:
cols = append(cols, col+" = "+col+" | ?")
}
values[i] = c.value
} else {
@ -760,7 +790,13 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
}
d.ins.ReplaceMarks(&query)
res, err := q.Exec(query, values...)
var err error
var res sql.Result
if qs != nil && qs.forContext {
res, err = q.ExecContext(qs.ctx, query, values...)
} else {
res, err = q.Exec(query, values...)
}
if err == nil {
return res.RowsAffected()
}
@ -849,11 +885,16 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con
for i := range marks {
marks[i] = "?"
}
sql := fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))
query = fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s %s", Q, mi.table, Q, Q, mi.fields.pk.column, Q, sql)
sqlIn := fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))
query = fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s %s", Q, mi.table, Q, Q, mi.fields.pk.column, Q, sqlIn)
d.ins.ReplaceMarks(&query)
res, err := q.Exec(query, args...)
var res sql.Result
if qs != nil && qs.forContext {
res, err = q.ExecContext(qs.ctx, query, args...)
} else {
res, err = q.Exec(query, args...)
}
if err == nil {
num, err := res.RowsAffected()
if err != nil {
@ -926,7 +967,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
maps[fi.column] = true
}
} else {
panic(fmt.Errorf("wrong field/column name `%s`", col))
return 0, fmt.Errorf("wrong field/column name `%s`", col)
}
}
if hasRel {
@ -976,11 +1017,18 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
d.ins.ReplaceMarks(&query)
var rs *sql.Rows
r, err := q.Query(query, args...)
var err error
if qs != nil && qs.forContext {
rs, err = q.QueryContext(qs.ctx, query, args...)
if err != nil {
return 0, err
}
rs = r
} else {
rs, err = q.Query(query, args...)
if err != nil {
return 0, err
}
}
refs := make([]interface{}, colsNum)
for i := range refs {
@ -1109,8 +1157,12 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition
d.ins.ReplaceMarks(&query)
row := q.QueryRow(query, args...)
var row *sql.Row
if qs != nil && qs.forContext {
row = q.QueryRowContext(qs.ctx, query, args...)
} else {
row = q.QueryRow(query, args...)
}
err = row.Scan(&cnt)
return
}

View File

@ -15,8 +15,10 @@
package orm
import (
"context"
"database/sql"
"fmt"
lru "github.com/hashicorp/golang-lru"
"reflect"
"sync"
"time"
@ -103,6 +105,121 @@ func (ac *_dbCache) getDefault() (al *alias) {
return
}
type DB struct {
*sync.RWMutex
DB *sql.DB
stmtDecorators *lru.Cache
}
func (d *DB) Begin() (*sql.Tx, error) {
return d.DB.Begin()
}
func (d *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) {
return d.DB.BeginTx(ctx, opts)
}
//su must call release to release *sql.Stmt after using
func (d *DB) getStmtDecorator(query string) (*stmtDecorator, error) {
d.RLock()
c, ok := d.stmtDecorators.Get(query)
if ok {
c.(*stmtDecorator).acquire()
d.RUnlock()
return c.(*stmtDecorator), nil
}
d.RUnlock()
d.Lock()
c, ok = d.stmtDecorators.Get(query)
if ok {
c.(*stmtDecorator).acquire()
d.Unlock()
return c.(*stmtDecorator), nil
}
stmt, err := d.Prepare(query)
if err != nil {
d.Unlock()
return nil, err
}
sd := newStmtDecorator(stmt)
sd.acquire()
d.stmtDecorators.Add(query, sd)
d.Unlock()
return sd, nil
}
func (d *DB) Prepare(query string) (*sql.Stmt, error) {
return d.DB.Prepare(query)
}
func (d *DB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
return d.DB.PrepareContext(ctx, query)
}
func (d *DB) Exec(query string, args ...interface{}) (sql.Result, error) {
sd, err := d.getStmtDecorator(query)
if err != nil {
return nil, err
}
stmt := sd.getStmt()
defer sd.release()
return stmt.Exec(args...)
}
func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
sd, err := d.getStmtDecorator(query)
if err != nil {
return nil, err
}
stmt := sd.getStmt()
defer sd.release()
return stmt.ExecContext(ctx, args...)
}
func (d *DB) Query(query string, args ...interface{}) (*sql.Rows, error) {
sd, err := d.getStmtDecorator(query)
if err != nil {
return nil, err
}
stmt := sd.getStmt()
defer sd.release()
return stmt.Query(args...)
}
func (d *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
sd, err := d.getStmtDecorator(query)
if err != nil {
return nil, err
}
stmt := sd.getStmt()
defer sd.release()
return stmt.QueryContext(ctx, args...)
}
func (d *DB) QueryRow(query string, args ...interface{}) *sql.Row {
sd, err := d.getStmtDecorator(query)
if err != nil {
panic(err)
}
stmt := sd.getStmt()
defer sd.release()
return stmt.QueryRow(args...)
}
func (d *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
sd, err := d.getStmtDecorator(query)
if err != nil {
panic(err)
}
stmt := sd.getStmt()
defer sd.release()
return stmt.QueryRowContext(ctx, args)
}
type alias struct {
Name string
Driver DriverType
@ -110,7 +227,7 @@ type alias struct {
DataSource string
MaxIdleConns int
MaxOpenConns int
DB *sql.DB
DB *DB
DbBaser dbBaser
TZ *time.Location
Engine string
@ -176,7 +293,11 @@ func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) {
al := new(alias)
al.Name = aliasName
al.DriverName = driverName
al.DB = db
al.DB = &DB{
RWMutex: new(sync.RWMutex),
DB: db,
stmtDecorators: newStmtDecoratorLruWithEvict(),
}
if dr, ok := drivers[driverName]; ok {
al.DbBaser = dbBasers[dr]
@ -272,13 +393,14 @@ func SetDataBaseTZ(aliasName string, tz *time.Location) error {
func SetMaxIdleConns(aliasName string, maxIdleConns int) {
al := getDbAlias(aliasName)
al.MaxIdleConns = maxIdleConns
al.DB.SetMaxIdleConns(maxIdleConns)
al.DB.DB.SetMaxIdleConns(maxIdleConns)
}
// SetMaxOpenConns Change the max open conns for *sql.DB, use specify database alias name
func SetMaxOpenConns(aliasName string, maxOpenConns int) {
al := getDbAlias(aliasName)
al.MaxOpenConns = maxOpenConns
al.DB.DB.SetMaxOpenConns(maxOpenConns)
// for tip go 1.2
if fun := reflect.ValueOf(al.DB).MethodByName("SetMaxOpenConns"); fun.IsValid() {
fun.Call([]reflect.Value{reflect.ValueOf(maxOpenConns)})
@ -296,7 +418,48 @@ func GetDB(aliasNames ...string) (*sql.DB, error) {
}
al, ok := dataBaseCache.get(name)
if ok {
return al.DB, nil
return al.DB.DB, nil
}
return nil, fmt.Errorf("DataBase of alias name `%s` not found", name)
}
type stmtDecorator struct {
wg sync.WaitGroup
lastUse int64
stmt *sql.Stmt
}
func (s *stmtDecorator) getStmt() *sql.Stmt {
return s.stmt
}
func (s *stmtDecorator) acquire() {
s.wg.Add(1)
s.lastUse = time.Now().Unix()
}
func (s *stmtDecorator) release() {
s.wg.Done()
}
//garbage recycle for stmt
func (s *stmtDecorator) destroy() {
go func() {
s.wg.Wait()
_ = s.stmt.Close()
}()
}
func newStmtDecorator(sqlStmt *sql.Stmt) *stmtDecorator {
return &stmtDecorator{
stmt: sqlStmt,
lastUse: time.Now().Unix(),
}
}
func newStmtDecoratorLruWithEvict() *lru.Cache {
cache, _ := lru.NewWithEvict(1000, func(key interface{}, value interface{}) {
value.(*stmtDecorator).destroy()
})
return cache
}

View File

@ -17,6 +17,8 @@ package orm
import (
"database/sql"
"fmt"
"reflect"
"time"
)
// sqlite operators.
@ -66,6 +68,14 @@ type dbBaseSqlite struct {
var _ dbBaser = new(dbBaseSqlite)
// override base db read for update behavior as SQlite does not support syntax
func (d *dbBaseSqlite) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string, isForUpdate bool) error {
if isForUpdate {
DebugLog.Println("[WARN] SQLite does not support SELECT FOR UPDATE query, isForUpdate param is ignored and always as false to do the work")
}
return d.dbBase.Read(q, mi, ind, tz, cols, false)
}
// get sqlite operator.
func (d *dbBaseSqlite) OperatorSQL(operator string) string {
return sqliteOperators[operator]

View File

@ -372,7 +372,13 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe
operator = "exact"
}
operSQL, args := t.base.GenerateOperatorSQL(mi, fi, operator, p.args, tz)
var operSQL string
var args []interface{}
if p.isRaw {
operSQL = p.sql
} else {
operSQL, args = t.base.GenerateOperatorSQL(mi, fi, operator, p.args, tz)
}
leftCol := fmt.Sprintf("%s.%s%s%s", index, Q, fi.column, Q)
t.base.GenerateOperatorLeftCol(fi, operator, &leftCol)

View File

@ -18,6 +18,7 @@ import (
"fmt"
"os"
"reflect"
"runtime/debug"
"strings"
)
@ -298,6 +299,7 @@ func bootStrap() {
end:
if err != nil {
fmt.Println(err)
debug.PrintStack()
os.Exit(2)
}
}
@ -335,11 +337,11 @@ func RegisterModelWithSuffix(suffix string, models ...interface{}) {
// BootStrap bootstrap models.
// make all model parsed and can not add more models
func BootStrap() {
modelCache.Lock()
defer modelCache.Unlock()
if modelCache.done {
return
}
modelCache.Lock()
defer modelCache.Unlock()
bootStrap()
modelCache.done = true
}

View File

@ -301,7 +301,7 @@ checkType:
fi.sf = sf
fi.fullName = mi.fullName + mName + "." + sf.Name
fi.description = sf.Tag.Get("description")
fi.description = tags["description"]
fi.null = attrs["null"]
fi.index = attrs["index"]
fi.auto = attrs["auto"]

View File

@ -44,6 +44,7 @@ var supportTag = map[string]int{
"decimals": 2,
"on_delete": 2,
"type": 2,
"description": 2,
}
// get reflect.Type name with package path.
@ -65,7 +66,7 @@ func getTableName(val reflect.Value) string {
return snakeString(reflect.Indirect(val).Type().Name())
}
// get table engine, mysiam or innodb.
// get table engine, myisam or innodb.
func getTableEngine(val reflect.Value) string {
fun := val.MethodByName("TableEngine")
if fun.IsValid() {
@ -109,7 +110,7 @@ func getTableUnique(val reflect.Value) [][]string {
func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string {
column := col
if col == "" {
column = snakeString(sf.Name)
column = nameStrategyMap[nameStrategy](sf.Name)
}
switch ft {
case RelForeignKey, RelOneToOne:

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// +build go1.8
// Package orm provide ORM for MySQL/PostgreSQL/sqlite
// Simple Usage
//
@ -52,11 +54,13 @@
package orm
import (
"context"
"database/sql"
"errors"
"fmt"
"os"
"reflect"
"sync"
"time"
)
@ -69,7 +73,7 @@ const (
var (
Debug = false
DebugLog = NewLog(os.Stdout)
DefaultRowsLimit = 1000
DefaultRowsLimit = -1
DefaultRelsDepth = 2
DefaultTimeLoc = time.Local
ErrTxHasBegan = errors.New("<Ormer.Begin> transaction already begin")
@ -238,13 +242,7 @@ func (o *orm) Update(md interface{}, cols ...string) (int64, error) {
func (o *orm) Delete(md interface{}, cols ...string) (int64, error) {
mi, ind := o.getMiInd(md, true)
num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ, cols)
if err != nil {
return num, err
}
if num > 0 {
o.setPk(mi, ind, 0)
}
return num, nil
}
// create a models to models queryer
@ -422,7 +420,7 @@ func (o *orm) getRelQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet {
func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) {
var name string
if table, ok := ptrStructOrTableName.(string); ok {
name = snakeString(table)
name = nameStrategyMap[defaultNameStrategy](table)
if mi, ok := modelCache.get(name); ok {
qs = newQuerySet(o, mi)
}
@ -458,11 +456,15 @@ func (o *orm) Using(name string) error {
// begin transaction
func (o *orm) Begin() error {
return o.BeginTx(context.Background(), nil)
}
func (o *orm) BeginTx(ctx context.Context, opts *sql.TxOptions) error {
if o.isTx {
return ErrTxHasBegan
}
var tx *sql.Tx
tx, err := o.db.(txer).Begin()
tx, err := o.db.(txer).BeginTx(ctx, opts)
if err != nil {
return err
}
@ -515,6 +517,15 @@ func (o *orm) Driver() Driver {
return driver(o.alias.Name)
}
// return sql.DBStats for current database
func (o *orm) DBStats() *sql.DBStats {
if o.alias != nil && o.alias.DB != nil {
stats := o.alias.DB.DB.Stats()
return &stats
}
return nil
}
// NewOrm create new orm
func NewOrm() Ormer {
BootStrap() // execute only once
@ -541,6 +552,13 @@ func NewOrmWithDB(driverName, aliasName string, db *sql.DB) (Ormer, error) {
al.Name = aliasName
al.DriverName = driverName
al.DB = &DB{
RWMutex: new(sync.RWMutex),
DB: db,
stmtDecorators: newStmtDecoratorLruWithEvict(),
}
detectTZ(al)
o := new(orm)
o.alias = al

View File

@ -31,6 +31,8 @@ type condValue struct {
isOr bool
isNot bool
isCond bool
isRaw bool
sql string
}
// Condition struct.
@ -45,6 +47,15 @@ func NewCondition() *Condition {
return c
}
// Raw add raw sql to condition
func (c Condition) Raw(expr string, sql string) *Condition {
if len(sql) == 0 {
panic(fmt.Errorf("<Condition.Raw> sql cannot empty"))
}
c.params = append(c.params, condValue{exprs: strings.Split(expr, ExprSep), sql: sql, isRaw: true})
return &c
}
// And add expression to condition
func (c Condition) And(expr string, args ...interface{}) *Condition {
if expr == "" || len(args) == 0 {

View File

@ -15,6 +15,7 @@
package orm
import (
"context"
"database/sql"
"fmt"
"io"
@ -28,6 +29,9 @@ type Log struct {
*log.Logger
}
//costomer log func
var LogFunc func(query map[string]interface{})
// NewLog set io.Writer to create a Logger.
func NewLog(out io.Writer) *Log {
d := new(Log)
@ -36,12 +40,15 @@ func NewLog(out io.Writer) *Log {
}
func debugLogQueies(alias *alias, operaton, query string, t time.Time, err error, args ...interface{}) {
var logMap = make(map[string]interface{})
sub := time.Now().Sub(t) / 1e5
elsp := float64(int(sub)) / 10.0
logMap["cost_time"] = elsp
flag := " OK"
if err != nil {
flag = "FAIL"
}
logMap["flag"] = flag
con := fmt.Sprintf(" -[Queries/%s] - [%s / %11s / %7.1fms] - [%s]", alias.Name, flag, operaton, elsp, query)
cons := make([]string, 0, len(args))
for _, arg := range args {
@ -53,6 +60,10 @@ func debugLogQueies(alias *alias, operaton, query string, t time.Time, err error
if err != nil {
con += " - " + err.Error()
}
logMap["sql"] = fmt.Sprintf("%s-`%s`", query, strings.Join(cons, "`, `"))
if LogFunc != nil{
LogFunc(logMap)
}
DebugLog.Println(con)
}
@ -122,6 +133,13 @@ func (d *dbQueryLog) Prepare(query string) (*sql.Stmt, error) {
return stmt, err
}
func (d *dbQueryLog) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
a := time.Now()
stmt, err := d.db.PrepareContext(ctx, query)
debugLogQueies(d.alias, "db.Prepare", query, a, err)
return stmt, err
}
func (d *dbQueryLog) Exec(query string, args ...interface{}) (sql.Result, error) {
a := time.Now()
res, err := d.db.Exec(query, args...)
@ -129,6 +147,13 @@ func (d *dbQueryLog) Exec(query string, args ...interface{}) (sql.Result, error)
return res, err
}
func (d *dbQueryLog) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
a := time.Now()
res, err := d.db.ExecContext(ctx, query, args...)
debugLogQueies(d.alias, "db.Exec", query, a, err, args...)
return res, err
}
func (d *dbQueryLog) Query(query string, args ...interface{}) (*sql.Rows, error) {
a := time.Now()
res, err := d.db.Query(query, args...)
@ -136,6 +161,13 @@ func (d *dbQueryLog) Query(query string, args ...interface{}) (*sql.Rows, error)
return res, err
}
func (d *dbQueryLog) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
a := time.Now()
res, err := d.db.QueryContext(ctx, query, args...)
debugLogQueies(d.alias, "db.Query", query, a, err, args...)
return res, err
}
func (d *dbQueryLog) QueryRow(query string, args ...interface{}) *sql.Row {
a := time.Now()
res := d.db.QueryRow(query, args...)
@ -143,6 +175,13 @@ func (d *dbQueryLog) QueryRow(query string, args ...interface{}) *sql.Row {
return res
}
func (d *dbQueryLog) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
a := time.Now()
res := d.db.QueryRowContext(ctx, query, args...)
debugLogQueies(d.alias, "db.QueryRow", query, a, nil, args...)
return res
}
func (d *dbQueryLog) Begin() (*sql.Tx, error) {
a := time.Now()
tx, err := d.db.(txer).Begin()
@ -150,6 +189,13 @@ func (d *dbQueryLog) Begin() (*sql.Tx, error) {
return tx, err
}
func (d *dbQueryLog) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) {
a := time.Now()
tx, err := d.db.(txer).BeginTx(ctx, opts)
debugLogQueies(d.alias, "db.BeginTx", "START TRANSACTION", a, err)
return tx, err
}
func (d *dbQueryLog) Commit() error {
a := time.Now()
err := d.db.(txEnder).Commit()

View File

@ -15,6 +15,7 @@
package orm
import (
"context"
"fmt"
)
@ -31,6 +32,11 @@ const (
ColMinus
ColMultiply
ColExcept
ColBitAnd
ColBitRShift
ColBitLShift
ColBitXOR
ColBitOr
)
// ColValue do the field raw changes. e.g Nums = Nums + 10. usage:
@ -39,7 +45,8 @@ const (
// }
func ColValue(opt operator, value interface{}) interface{} {
switch opt {
case ColAdd, ColMinus, ColMultiply, ColExcept:
case ColAdd, ColMinus, ColMultiply, ColExcept, ColBitAnd, ColBitRShift,
ColBitLShift, ColBitXOR, ColBitOr:
default:
panic(fmt.Errorf("orm.ColValue wrong operator"))
}
@ -66,6 +73,8 @@ type querySet struct {
distinct bool
forupdate bool
orm *orm
ctx context.Context
forContext bool
}
var _ QuerySeter = new(querySet)
@ -79,6 +88,15 @@ func (o querySet) Filter(expr string, args ...interface{}) QuerySeter {
return &o
}
// add raw sql to querySeter.
func (o querySet) FilterRaw(expr string, sql string) QuerySeter {
if o.cond == nil {
o.cond = NewCondition()
}
o.cond = o.cond.Raw(expr, sql)
return &o
}
// add NOT condition to querySeter.
func (o querySet) Exclude(expr string, args ...interface{}) QuerySeter {
if o.cond == nil {
@ -198,11 +216,7 @@ func (o *querySet) PrepareInsert() (Inserter, error) {
// query all data and map to containers.
// cols means the columns when querying.
func (o *querySet) All(container interface{}, cols ...string) (int64, error) {
num, err := o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
if num == 0 {
return 0, ErrNoRows
}
return num, err
return o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
}
// query one row data and map to containers.
@ -270,6 +284,13 @@ func (o *querySet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string)
panic(ErrNotImplement)
}
// set context to QuerySeter.
func (o querySet) WithContext(ctx context.Context) QuerySeter {
o.ctx = ctx
o.forContext = true
return &o
}
// create new QuerySeter.
func newQuerySet(orm *orm, mi *modelInfo) QuerySeter {
o := new(querySet)

View File

@ -150,8 +150,10 @@ func (o *rawSet) setFieldValue(ind reflect.Value, value interface{}) {
case reflect.Struct:
if value == nil {
ind.Set(reflect.Zero(ind.Type()))
} else if _, ok := ind.Interface().(time.Time); ok {
return
}
switch ind.Interface().(type) {
case time.Time:
var str string
switch d := value.(type) {
case time.Time:
@ -178,7 +180,25 @@ func (o *rawSet) setFieldValue(ind reflect.Value, value interface{}) {
}
}
}
case sql.NullString, sql.NullInt64, sql.NullFloat64, sql.NullBool:
indi := reflect.New(ind.Type()).Interface()
sc, ok := indi.(sql.Scanner)
if !ok {
return
}
err := sc.Scan(value)
if err == nil {
ind.Set(reflect.Indirect(reflect.ValueOf(sc)))
}
}
case reflect.Ptr:
if value == nil {
ind.Set(reflect.Zero(ind.Type()))
break
}
ind.Set(reflect.New(ind.Type().Elem()))
o.setFieldValue(reflect.Indirect(ind), value)
}
}
@ -358,7 +378,7 @@ func (o *rawSet) QueryRow(containers ...interface{}) error {
_, tags := parseStructTag(fe.Tag.Get(defaultStructTagName))
var col string
if col = tags["column"]; col == "" {
col = snakeString(fe.Name)
col = nameStrategyMap[nameStrategy](fe.Name)
}
if v, ok := columnsMp[col]; ok {
value := reflect.ValueOf(v).Elem().Interface()
@ -509,7 +529,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) {
_, tags := parseStructTag(fe.Tag.Get(defaultStructTagName))
var col string
if col = tags["column"]; col == "" {
col = snakeString(fe.Name)
col = nameStrategyMap[nameStrategy](fe.Name)
}
if v, ok := columnsMp[col]; ok {
value := reflect.ValueOf(v).Elem().Interface()

View File

@ -12,10 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// +build go1.8
package orm
import (
"bytes"
"context"
"database/sql"
"fmt"
"io/ioutil"
@ -452,9 +455,18 @@ func TestNullDataTypes(t *testing.T) {
throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr))
throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr))
throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr))
throwFail(t, AssertIs((*d.TimePtr).Format(testTime), timePtr.Format(testTime)))
throwFail(t, AssertIs((*d.DatePtr).Format(testDate), datePtr.Format(testDate)))
throwFail(t, AssertIs((*d.DateTimePtr).Format(testDateTime), dateTimePtr.Format(testDateTime)))
throwFail(t, AssertIs((*d.TimePtr).UTC().Format(testTime), timePtr.UTC().Format(testTime)))
throwFail(t, AssertIs((*d.DatePtr).UTC().Format(testDate), datePtr.UTC().Format(testDate)))
throwFail(t, AssertIs((*d.DateTimePtr).UTC().Format(testDateTime), dateTimePtr.UTC().Format(testDateTime)))
// test support for pointer fields using RawSeter.QueryRows()
var dnList []*DataNull
Q := dDbBaser.TableQuote()
num, err = dORM.Raw(fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q), 3).QueryRows(&dnList)
throwFailNow(t, err)
throwFailNow(t, AssertIs(num, 1))
equal := reflect.DeepEqual(*dnList[0], d)
throwFailNow(t, AssertIs(equal, true))
}
func TestDataCustomTypes(t *testing.T) {
@ -896,6 +908,18 @@ func TestOperators(t *testing.T) {
num, err = qs.Filter("id__between", []int{2, 3}).Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 2))
num, err = qs.FilterRaw("user_name", "= 'slene'").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 1))
num, err = qs.FilterRaw("status", "IN (1, 2)").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 2))
num, err = qs.FilterRaw("profile_id", "IN (SELECT id FROM user_profile WHERE age=30)").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 1))
}
func TestSetCond(t *testing.T) {
@ -921,6 +945,11 @@ func TestSetCond(t *testing.T) {
num, err = qs.SetCond(cond4).Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 3))
cond5 := cond.Raw("user_name", "= 'slene'").OrNotCond(cond.And("user_name", "slene"))
num, err = qs.SetCond(cond5).Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 3))
}
func TestLimit(t *testing.T) {
@ -1008,13 +1037,13 @@ func TestAll(t *testing.T) {
qs = dORM.QueryTable("user")
num, err = qs.Filter("user_name", "nothing").All(&users)
throwFailNow(t, AssertIs(err, ErrNoRows))
throwFailNow(t, err)
throwFailNow(t, AssertIs(num, 0))
var users3 []*User
qs = dORM.QueryTable("user")
num, err = qs.Filter("user_name", "nothing").All(&users3)
throwFailNow(t, AssertIs(err, ErrNoRows))
throwFailNow(t, err)
throwFailNow(t, AssertIs(num, 0))
throwFailNow(t, AssertIs(users3 == nil, false))
}
@ -1659,6 +1688,31 @@ func TestRawQueryRow(t *testing.T) {
throwFail(t, AssertIs(uid, 4))
throwFail(t, AssertIs(*status, 3))
throwFail(t, AssertIs(pid, nil))
// test for sql.Null* fields
nData := &DataNull{
NullString: sql.NullString{String: "test sql.null", Valid: true},
NullBool: sql.NullBool{Bool: true, Valid: true},
NullInt64: sql.NullInt64{Int64: 42, Valid: true},
NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
}
newId, err := dORM.Insert(nData)
throwFailNow(t, err)
var nd *DataNull
query = fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q)
err = dORM.Raw(query, newId).QueryRow(&nd)
throwFailNow(t, err)
throwFailNow(t, AssertNot(nd, nil))
throwFail(t, AssertIs(nd.NullBool.Valid, true))
throwFail(t, AssertIs(nd.NullBool.Bool, true))
throwFail(t, AssertIs(nd.NullString.Valid, true))
throwFail(t, AssertIs(nd.NullString.String, "test sql.null"))
throwFail(t, AssertIs(nd.NullInt64.Valid, true))
throwFail(t, AssertIs(nd.NullInt64.Int64, 42))
throwFail(t, AssertIs(nd.NullFloat64.Valid, true))
throwFail(t, AssertIs(nd.NullFloat64.Float64, 42.42))
}
// user_profile table
@ -1751,6 +1805,32 @@ func TestQueryRows(t *testing.T) {
throwFailNow(t, AssertIs(l[1].UserName, "astaxie"))
throwFailNow(t, AssertIs(l[1].Age, 30))
// test for sql.Null* fields
nData := &DataNull{
NullString: sql.NullString{String: "test sql.null", Valid: true},
NullBool: sql.NullBool{Bool: true, Valid: true},
NullInt64: sql.NullInt64{Int64: 42, Valid: true},
NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
}
newId, err := dORM.Insert(nData)
throwFailNow(t, err)
var nDataList []*DataNull
query = fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q)
num, err = dORM.Raw(query, newId).QueryRows(&nDataList)
throwFailNow(t, err)
throwFailNow(t, AssertIs(num, 1))
nd := nDataList[0]
throwFailNow(t, AssertNot(nd, nil))
throwFail(t, AssertIs(nd.NullBool.Valid, true))
throwFail(t, AssertIs(nd.NullBool.Bool, true))
throwFail(t, AssertIs(nd.NullString.Valid, true))
throwFail(t, AssertIs(nd.NullString.String, "test sql.null"))
throwFail(t, AssertIs(nd.NullInt64.Valid, true))
throwFail(t, AssertIs(nd.NullInt64.Int64, 42))
throwFail(t, AssertIs(nd.NullFloat64.Valid, true))
throwFail(t, AssertIs(nd.NullFloat64.Float64, 42.42))
}
func TestRawValues(t *testing.T) {
@ -1990,6 +2070,66 @@ func TestTransaction(t *testing.T) {
}
func TestTransactionIsolationLevel(t *testing.T) {
// this test worked when database support transaction isolation level
if IsSqlite {
return
}
o1 := NewOrm()
o2 := NewOrm()
// start two transaction with isolation level repeatable read
err := o1.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
throwFail(t, err)
err = o2.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
throwFail(t, err)
// o1 insert tag
var tag Tag
tag.Name = "test-transaction"
id, err := o1.Insert(&tag)
throwFail(t, err)
throwFail(t, AssertIs(id > 0, true))
// o2 query tag table, no result
num, err := o2.QueryTable("tag").Filter("name", "test-transaction").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 0))
// o1 commit
o1.Commit()
// o2 query tag table, still no result
num, err = o2.QueryTable("tag").Filter("name", "test-transaction").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 0))
// o2 commit and query tag table, get the result
o2.Commit()
num, err = o2.QueryTable("tag").Filter("name", "test-transaction").Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 1))
num, err = o1.QueryTable("tag").Filter("name", "test-transaction").Delete()
throwFail(t, err)
throwFail(t, AssertIs(num, 1))
}
func TestBeginTxWithContextCanceled(t *testing.T) {
o := NewOrm()
ctx, cancel := context.WithCancel(context.Background())
o.BeginTx(ctx, nil)
id, err := o.Insert(&Tag{Name: "test-context"})
throwFail(t, err)
throwFail(t, AssertIs(id > 0, true))
// cancel the context before commit to make it error
cancel()
err = o.Commit()
throwFail(t, AssertIs(err, context.Canceled))
}
func TestReadOrCreate(t *testing.T) {
u := &User{
UserName: "Kyle",
@ -2260,6 +2400,7 @@ func TestIgnoreCaseTag(t *testing.T) {
throwFail(t, AssertIs(info.fields.GetByName("Name02").column, "Name"))
throwFail(t, AssertIs(info.fields.GetByName("Name03").column, "name"))
}
func TestInsertOrUpdate(t *testing.T) {
RegisterModel(new(User))
user := User{UserName: "unique_username133", Status: 1, Password: "o"}
@ -2297,6 +2438,11 @@ func TestInsertOrUpdate(t *testing.T) {
throwFailNow(t, AssertIs(user2.Status, test.Status))
throwFailNow(t, AssertIs(user2.Password, strings.TrimSpace(test.Password)))
}
//postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values
if IsPostgres {
return
}
//test3 +
_, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status+1")
if err != nil {

View File

@ -15,6 +15,7 @@
package orm
import (
"context"
"database/sql"
"reflect"
"time"
@ -54,7 +55,7 @@ type Ormer interface {
// for example:
// user := new(User)
// id, err = Ormer.Insert(user)
// user must a pointer and Insert will set user's pk field
// user must be a pointer and Insert will set user's pk field
Insert(interface{}) (int64, error)
// mysql:InsertOrUpdate(model) or InsertOrUpdate(model,"colu=colu+value")
// if colu type is integer : can use(+-*/), string : convert(colu,"value")
@ -106,6 +107,17 @@ type Ormer interface {
// ...
// err = o.Rollback()
Begin() error
// begin transaction with provided context and option
// the provided context is used until the transaction is committed or rolled back.
// if the context is canceled, the transaction will be rolled back.
// the provided TxOptions is optional and may be nil if defaults should be used.
// if a non-default isolation level is used that the driver doesn't support, an error will be returned.
// for example:
// o := NewOrm()
// err := o.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
// ...
// err = o.Rollback()
BeginTx(ctx context.Context, opts *sql.TxOptions) error
// commit transaction
Commit() error
// rollback transaction
@ -116,6 +128,7 @@ type Ormer interface {
// // update user testing's name to slene
Raw(query string, args ...interface{}) RawSeter
Driver() Driver
DBStats() *sql.DBStats
}
// Inserter insert prepared statement
@ -135,6 +148,11 @@ type QuerySeter interface {
// // time compare
// qs.Filter("created", time.Now())
Filter(string, ...interface{}) QuerySeter
// add raw sql to querySeter.
// for example:
// qs.FilterRaw("user_id IN (SELECT id FROM profile WHERE age>=18)")
// //sql-> WHERE user_id IN (SELECT id FROM profile WHERE age>=18)
FilterRaw(string, string) QuerySeter
// add NOT condition to querySeter.
// have the same usage as Filter
Exclude(string, ...interface{}) QuerySeter
@ -378,16 +396,23 @@ type RawSeter interface {
type stmtQuerier interface {
Close() error
Exec(args ...interface{}) (sql.Result, error)
//ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error)
Query(args ...interface{}) (*sql.Rows, error)
//QueryContext(args ...interface{}) (*sql.Rows, error)
QueryRow(args ...interface{}) *sql.Row
//QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row
}
// db querier
type dbQuerier interface {
Prepare(query string) (*sql.Stmt, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
Exec(query string, args ...interface{}) (sql.Result, error)
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
Query(query string, args ...interface{}) (*sql.Rows, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
QueryRow(query string, args ...interface{}) *sql.Row
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
}
// type DB interface {
@ -401,6 +426,7 @@ type dbQuerier interface {
// transaction beginner
type txer interface {
Begin() (*sql.Tx, error)
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
}
// transaction ending

View File

@ -23,6 +23,18 @@ import (
"time"
)
type fn func(string) string
var (
nameStrategyMap = map[string]fn{
defaultNameStrategy: snakeString,
SnakeAcronymNameStrategy: snakeStringWithAcronym,
}
defaultNameStrategy = "snakeString"
SnakeAcronymNameStrategy = "snakeStringWithAcronym"
nameStrategy = defaultNameStrategy
)
// StrTo is the target string
type StrTo string
@ -117,7 +129,7 @@ func (f StrTo) Uint16() (uint16, error) {
return uint16(v), err
}
// Uint32 string to uint31
// Uint32 string to uint32
func (f StrTo) Uint32() (uint32, error) {
v, err := strconv.ParseUint(f.String(), 10, 32)
return uint32(v), err
@ -198,7 +210,28 @@ func ToInt64(value interface{}) (d int64) {
return
}
// snake string, XxYy to xx_yy , XxYY to xx_yy
func snakeStringWithAcronym(s string) string {
data := make([]byte, 0, len(s)*2)
num := len(s)
for i := 0; i < num; i++ {
d := s[i]
before := false
after := false
if i > 0 {
before = s[i-1] >= 'a' && s[i-1] <= 'z'
}
if i+1 < num {
after = s[i+1] >= 'a' && s[i+1] <= 'z'
}
if i > 0 && d >= 'A' && d <= 'Z' && (before || after) {
data = append(data, '_')
}
data = append(data, d)
}
return strings.ToLower(string(data[:]))
}
// snake string, XxYy to xx_yy , XxYY to xx_y_y
func snakeString(s string) string {
data := make([]byte, 0, len(s)*2)
j := false
@ -216,6 +249,14 @@ func snakeString(s string) string {
return strings.ToLower(string(data[:]))
}
// SetNameStrategy set different name strategy
func SetNameStrategy(s string) {
if SnakeAcronymNameStrategy != s {
nameStrategy = defaultNameStrategy
}
nameStrategy = s
}
// camel string, xx_yy to XxYy
func camelString(s string) string {
data := make([]byte, 0, len(s))

View File

@ -34,3 +34,37 @@ func TestCamelString(t *testing.T) {
}
}
}
func TestSnakeString(t *testing.T) {
camel := []string{"PicUrl", "HelloWorld", "HelloWorld", "HelLOWord", "PicUrl1", "XyXX"}
snake := []string{"pic_url", "hello_world", "hello_world", "hel_l_o_word", "pic_url1", "xy_x_x"}
answer := make(map[string]string)
for i, v := range camel {
answer[v] = snake[i]
}
for _, v := range camel {
res := snakeString(v)
if res != answer[v] {
t.Error("Unit Test Fail:", v, res, answer[v])
}
}
}
func TestSnakeStringWithAcronym(t *testing.T) {
camel := []string{"ID", "PicURL", "HelloWorld", "HelloWorld", "HelLOWord", "PicUrl1", "XyXX"}
snake := []string{"id", "pic_url", "hello_world", "hello_world", "hel_lo_word", "pic_url1", "xy_xx"}
answer := make(map[string]string)
for i, v := range camel {
answer[v] = snake[i]
}
for _, v := range camel {
res := snakeStringWithAcronym(v)
if res != answer[v] {
t.Error("Unit Test Fail:", v, res, answer[v])
}
}
}

198
parser.go
View File

@ -35,11 +35,11 @@ import (
"github.com/astaxie/beego/utils"
)
var globalRouterTemplate = `package routers
var globalRouterTemplate = `package {{.routersDir}}
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/context/param"
"github.com/astaxie/beego/context/param"{{.globalimport}}
)
func init() {
@ -52,6 +52,22 @@ var (
commentFilename string
pkgLastupdate map[string]int64
genInfoList map[string][]ControllerComments
routerHooks = map[string]int{
"beego.BeforeStatic": BeforeStatic,
"beego.BeforeRouter": BeforeRouter,
"beego.BeforeExec": BeforeExec,
"beego.AfterExec": AfterExec,
"beego.FinishRouter": FinishRouter,
}
routerHooksMapping = map[int]string{
BeforeStatic: "beego.BeforeStatic",
BeforeRouter: "beego.BeforeRouter",
BeforeExec: "beego.BeforeExec",
AfterExec: "beego.AfterExec",
FinishRouter: "beego.FinishRouter",
}
)
const commentPrefix = "commentsRouter_"
@ -102,6 +118,20 @@ type parsedComment struct {
routerPath string
methods []string
params map[string]parsedParam
filters []parsedFilter
imports []parsedImport
}
type parsedImport struct {
importPath string
importAlias string
}
type parsedFilter struct {
pattern string
pos int
filter string
params []bool
}
type parsedParam struct {
@ -126,6 +156,8 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
cc.Router = parsedComment.routerPath
cc.AllowHTTPMethods = parsedComment.methods
cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
cc.FilterComments = buildFilters(parsedComment.filters)
cc.ImportComments = buildImports(parsedComment.imports)
genInfoList[key] = append(genInfoList[key], cc)
}
}
@ -133,6 +165,48 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
return nil
}
func buildImports(pis []parsedImport) []*ControllerImportComments {
var importComments []*ControllerImportComments
for _, pi := range pis {
importComments = append(importComments, &ControllerImportComments{
ImportPath: pi.importPath,
ImportAlias: pi.importAlias,
})
}
return importComments
}
func buildFilters(pfs []parsedFilter) []*ControllerFilterComments {
var filterComments []*ControllerFilterComments
for _, pf := range pfs {
var (
returnOnOutput bool
resetParams bool
)
if len(pf.params) >= 1 {
returnOnOutput = pf.params[0]
}
if len(pf.params) >= 2 {
resetParams = pf.params[1]
}
filterComments = append(filterComments, &ControllerFilterComments{
Filter: pf.filter,
Pattern: pf.pattern,
Pos: pf.pos,
ReturnOnOutput: returnOnOutput,
ResetParams: resetParams,
})
}
return filterComments
}
func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam {
result := make([]*param.MethodParam, 0, len(funcParams))
for _, fparam := range funcParams {
@ -181,6 +255,8 @@ var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`)
func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) {
pcs = []*parsedComment{}
params := map[string]parsedParam{}
filters := []parsedFilter{}
imports := []parsedImport{}
for _, c := range lines {
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
@ -209,9 +285,69 @@ func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) {
}
}
for _, c := range lines {
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
if strings.HasPrefix(t, "@Import") {
iv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Import")))
if len(iv) == 0 || len(iv) > 2 {
logs.Error("Invalid @Import format. Only accepts 1 or 2 parameters")
continue
}
p := parsedImport{}
p.importPath = iv[0]
if len(iv) == 2 {
p.importAlias = iv[1]
}
imports = append(imports, p)
}
}
filterLoop:
for _, c := range lines {
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
if strings.HasPrefix(t, "@Filter") {
fv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Filter")))
if len(fv) < 3 {
logs.Error("Invalid @Filter format. Needs at least 3 parameters")
continue filterLoop
}
p := parsedFilter{}
p.pattern = fv[0]
posName := fv[1]
if pos, exists := routerHooks[posName]; exists {
p.pos = pos
} else {
logs.Error("Invalid @Filter pos: ", posName)
continue filterLoop
}
p.filter = fv[2]
fvParams := fv[3:]
for _, fvParam := range fvParams {
switch fvParam {
case "true":
p.params = append(p.params, true)
case "false":
p.params = append(p.params, false)
default:
logs.Error("Invalid @Filter param: ", fvParam)
continue filterLoop
}
}
filters = append(filters, p)
}
}
for _, c := range lines {
var pc = &parsedComment{}
pc.params = params
pc.filters = filters
pc.imports = imports
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
if strings.HasPrefix(t, "@router") {
@ -277,6 +413,7 @@ func genRouterCode(pkgRealpath string) {
logs.Info("generate router from comments")
var (
globalinfo string
globalimport string
sortKey []string
)
for k := range genInfoList {
@ -295,6 +432,7 @@ func genRouterCode(pkgRealpath string) {
}
allmethod = strings.TrimRight(allmethod, ",") + "}"
}
params := "nil"
if len(c.Params) > 0 {
params = "[]map[string]string{"
@ -305,6 +443,7 @@ func genRouterCode(pkgRealpath string) {
}
params = strings.TrimRight(params, ",") + "}"
}
methodParams := "param.Make("
if len(c.MethodParams) > 0 {
lines := make([]string, 0, len(c.MethodParams))
@ -316,24 +455,72 @@ func genRouterCode(pkgRealpath string) {
",\n "
}
methodParams += ")"
imports := ""
if len(c.ImportComments) > 0 {
for _, i := range c.ImportComments {
var s string
if i.ImportAlias != "" {
s = fmt.Sprintf(`
%s "%s"`, i.ImportAlias, i.ImportPath)
} else {
s = fmt.Sprintf(`
"%s"`, i.ImportPath)
}
if !strings.Contains(globalimport, s) {
imports += s
}
}
}
filters := ""
if len(c.FilterComments) > 0 {
for _, f := range c.FilterComments {
filters += fmt.Sprintf(` &beego.ControllerFilter{
Pattern: "%s",
Pos: %s,
Filter: %s,
ReturnOnOutput: %v,
ResetParams: %v,
},`, f.Pattern, routerHooksMapping[f.Pos], f.Filter, f.ReturnOnOutput, f.ResetParams)
}
}
if filters == "" {
filters = "nil"
} else {
filters = fmt.Sprintf(`[]*beego.ControllerFilter{
%s
}`, filters)
}
globalimport += imports
globalinfo = globalinfo + `
beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
beego.ControllerComments{
Method: "` + strings.TrimSpace(c.Method) + `",
` + "Router: `" + c.Router + "`" + `,
` + `Router: "` + c.Router + `"` + `,
AllowHTTPMethods: ` + allmethod + `,
MethodParams: ` + methodParams + `,
Filters: ` + filters + `,
Params: ` + params + `})
`
}
}
if globalinfo != "" {
f, err := os.Create(filepath.Join(getRouterDir(pkgRealpath), commentFilename))
if err != nil {
panic(err)
}
defer f.Close()
f.WriteString(strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1))
routersDir := AppConfig.DefaultString("routersdir", "routers")
content := strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1)
content = strings.Replace(content, "{{.routersDir}}", routersDir, -1)
content = strings.Replace(content, "{{.globalimport}}", globalimport, -1)
f.WriteString(content)
}
}
@ -389,7 +576,8 @@ func getpathTime(pkgRealpath string) (lastupdate int64, err error) {
func getRouterDir(pkgRealpath string) string {
dir := filepath.Dir(pkgRealpath)
for {
d := filepath.Join(dir, "routers")
routersDir := AppConfig.DefaultString("routersdir", "routers")
d := filepath.Join(dir, routersDir)
if utils.FileExists(d) {
return d
}

View File

@ -72,8 +72,8 @@ import (
// AppIDToAppSecret is used to get appsecret throw appid
type AppIDToAppSecret func(string) string
// APIBaiscAuth use the basic appid/appkey as the AppIdToAppSecret
func APIBaiscAuth(appid, appkey string) beego.FilterFunc {
// APIBasicAuth use the basic appid/appkey as the AppIdToAppSecret
func APIBasicAuth(appid, appkey string) beego.FilterFunc {
ft := func(aid string) string {
if aid == appid {
return appkey
@ -83,6 +83,11 @@ func APIBaiscAuth(appid, appkey string) beego.FilterFunc {
return APISecretAuth(ft, 300)
}
// APIBaiscAuth calls APIBasicAuth for previous callers
func APIBaiscAuth(appid, appkey string) beego.FilterFunc {
return APIBasicAuth(appid, appkey)
}
// APISecretAuth use AppIdToAppSecret verify and
func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc {
return func(ctx *context.Context) {

191
router.go
View File

@ -15,12 +15,13 @@
package beego
import (
"errors"
"fmt"
"net/http"
"os"
"path"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
@ -121,6 +122,10 @@ type ControllerInfo struct {
methodParams []*param.MethodParam
}
func (c *ControllerInfo) GetPattern() string {
return c.pattern
}
// ControllerRegister containers registered router rules, controller handlers and filters.
type ControllerRegister struct {
routers map[string]*Tree
@ -133,14 +138,15 @@ type ControllerRegister struct {
// NewControllerRegister returns a new ControllerRegister.
func NewControllerRegister() *ControllerRegister {
cr := &ControllerRegister{
return &ControllerRegister{
routers: make(map[string]*Tree),
policies: make(map[string]*Tree),
}
cr.pool.New = func() interface{} {
pool: sync.Pool{
New: func() interface{} {
return beecontext.NewContext()
},
},
}
return cr
}
// Add controller handler and pattern rules to ControllerRegister.
@ -248,10 +254,23 @@ func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerIn
func (p *ControllerRegister) Include(cList ...ControllerInterface) {
if BConfig.RunMode == DEV {
skip := make(map[string]bool, 10)
wgopath := utils.GetGOPATHs()
go111module := os.Getenv(`GO111MODULE`)
for _, c := range cList {
reflectVal := reflect.ValueOf(c)
t := reflect.Indirect(reflectVal).Type()
wgopath := utils.GetGOPATHs()
// for go modules
if go111module == `on` {
pkgpath := filepath.Join(WorkPath, "..", t.PkgPath())
if utils.FileExists(pkgpath) {
if pkgpath != "" {
if _, ok := skip[pkgpath]; !ok {
skip[pkgpath] = true
parserPkg(pkgpath, t.PkgPath())
}
}
}
} else {
if len(wgopath) == 0 {
panic("you are in dev mode. So please set gopath")
}
@ -271,18 +290,38 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) {
}
}
}
}
for _, c := range cList {
reflectVal := reflect.ValueOf(c)
t := reflect.Indirect(reflectVal).Type()
key := t.PkgPath() + ":" + t.Name()
if comm, ok := GlobalControllerRouter[key]; ok {
for _, a := range comm {
for _, f := range a.Filters {
p.InsertFilter(f.Pattern, f.Pos, f.Filter, f.ReturnOnOutput, f.ResetParams)
}
p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
}
}
}
}
// GetContext returns a context from pool, so usually you should remember to call Reset function to clean the context
// And don't forget to give back context to pool
// example:
// ctx := p.GetContext()
// ctx.Reset(w, q)
// defer p.GiveBackContext(ctx)
func (p *ControllerRegister) GetContext() *beecontext.Context {
return p.pool.Get().(*beecontext.Context)
}
// GiveBackContext put the ctx into pool so that it could be reuse
func (p *ControllerRegister) GiveBackContext(ctx *beecontext.Context) {
p.pool.Put(ctx)
}
// Get add get method
// usage:
// Get("/", func(ctx *context.Context){
@ -474,8 +513,7 @@ func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter Filter
// add Filter into
func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) {
if pos < BeforeStatic || pos > FinishRouter {
err = fmt.Errorf("can not find your filter position")
return
return errors.New("can not find your filter position")
}
p.enableFilter = true
p.filters[pos] = append(p.filters[pos], mr)
@ -505,10 +543,10 @@ func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) stri
}
}
}
controllName := strings.Join(paths[:len(paths)-1], "/")
controllerName := strings.Join(paths[:len(paths)-1], "/")
methodName := paths[len(paths)-1]
for m, t := range p.routers {
ok, url := p.geturl(t, "/", controllName, methodName, params, m)
ok, url := p.getURL(t, "/", controllerName, methodName, params, m)
if ok {
return url
}
@ -516,17 +554,17 @@ func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) stri
return ""
}
func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
func (p *ControllerRegister) getURL(t *Tree, url, controllerName, methodName string, params map[string]string, httpMethod string) (bool, string) {
for _, subtree := range t.fixrouters {
u := path.Join(url, subtree.prefix)
ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
ok, u := p.getURL(subtree, u, controllerName, methodName, params, httpMethod)
if ok {
return ok, u
}
}
if t.wildcard != nil {
u := path.Join(url, urlPlaceholder)
ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
ok, u := p.getURL(t.wildcard, u, controllerName, methodName, params, httpMethod)
if ok {
return ok, u
}
@ -534,7 +572,7 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin
for _, l := range t.leaves {
if c, ok := l.runObject.(*ControllerInfo); ok {
if c.routerType == routerTypeBeego &&
strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) {
strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllerName) {
find := false
if HTTPMETHOD[strings.ToUpper(methodName)] {
if len(c.methods) == 0 {
@ -573,18 +611,18 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin
}
}
}
canskip := false
canSkip := false
for _, v := range l.wildcards {
if v == ":" {
canskip = true
canSkip = true
continue
}
if u, ok := params[v]; ok {
delete(params, v)
url = strings.Replace(url, urlPlaceholder, u, 1)
} else {
if canskip {
canskip = false
if canSkip {
canSkip = false
continue
}
return false, ""
@ -593,27 +631,27 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin
return true, url + toURL(params)
}
var i int
var startreg bool
regurl := ""
var startReg bool
regURL := ""
for _, v := range strings.Trim(l.regexps.String(), "^$") {
if v == '(' {
startreg = true
startReg = true
continue
} else if v == ')' {
startreg = false
startReg = false
if v, ok := params[l.wildcards[i]]; ok {
delete(params, l.wildcards[i])
regurl = regurl + v
regURL = regURL + v
i++
} else {
break
}
} else if !startreg {
regurl = string(append([]rune(regurl), v))
} else if !startReg {
regURL = string(append([]rune(regURL), v))
}
}
if l.regexps.MatchString(regurl) {
ps := strings.Split(regurl, "/")
if l.regexps.MatchString(regURL) {
ps := strings.Split(regURL, "/")
for _, p := range ps {
url = strings.Replace(url, urlPlaceholder, p, 1)
}
@ -663,10 +701,11 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
routerInfo *ControllerInfo
isRunnable bool
)
context := p.pool.Get().(*beecontext.Context)
context := p.GetContext()
context.Reset(rw, r)
defer p.pool.Put(context)
defer p.GiveBackContext(context)
if BConfig.RecoverFunc != nil {
defer BConfig.RecoverFunc(context)
}
@ -685,7 +724,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
// filter wrong http method
if !HTTPMETHOD[r.Method] {
http.Error(rw, "Method Not Allowed", 405)
exception("405", context)
goto Admin
}
@ -735,7 +774,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
routerInfo, findRouter = p.FindRouter(context)
}
//if no matches to url, throw a not found exception
// if no matches to url, throw a not found exception
if !findRouter {
exception("404", context)
goto Admin
@ -746,19 +785,22 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
}
//execute middleware filters
if routerInfo != nil {
// store router pattern into context
context.Input.SetData("RouterPattern", routerInfo.pattern)
}
// execute middleware filters
if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) {
goto Admin
}
//check policies
// check policies
if p.execPolicy(context, urlPath) {
goto Admin
}
if routerInfo != nil {
//store router pattern into context
context.Input.SetData("RouterPattern", routerInfo.pattern)
if routerInfo.routerType == routerTypeRESTFul {
if _, ok := routerInfo.methods[r.Method]; ok {
isRunnable = true
@ -769,12 +811,12 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
} else if routerInfo.routerType == routerTypeHandler {
isRunnable = true
routerInfo.handler.ServeHTTP(rw, r)
routerInfo.handler.ServeHTTP(context.ResponseWriter, context.Request)
} else {
runRouter = routerInfo.controllerType
methodParams = routerInfo.methodParams
method := r.Method
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost {
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPut {
method = http.MethodPut
}
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete {
@ -792,9 +834,9 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
// also defined runRouter & runMethod from filter
if !isRunnable {
//Invoke the request handler
// Invoke the request handler
var execController ControllerInterface
if routerInfo.initialize != nil {
if routerInfo != nil && routerInfo.initialize != nil {
execController = routerInfo.initialize()
} else {
vc := reflect.New(runRouter)
@ -805,13 +847,13 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
}
//call the controller init function
// call the controller init function
execController.Init(context, runRouter.Name(), runMethod, execController)
//call prepare function
// call prepare function
execController.Prepare()
//if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf
// if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf
if BConfig.WebConfig.EnableXSRF {
execController.XSRFToken()
if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut ||
@ -823,7 +865,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
execController.URLMapping()
if !context.ResponseWriter.Started {
//exec main logic
// exec main logic
switch runMethod {
case http.MethodGet:
execController.Get()
@ -839,6 +881,8 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
execController.Patch()
case http.MethodOptions:
execController.Options()
case http.MethodTrace:
execController.Trace()
default:
if !execController.HandlerFunc(runMethod) {
vc := reflect.ValueOf(execController)
@ -846,14 +890,14 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
in := param.ConvertParams(methodParams, method.Type(), context)
out := method.Call(in)
//For backward compatibility we only handle response if we had incoming methodParams
// For backward compatibility we only handle response if we had incoming methodParams
if methodParams != nil {
p.handleParamResponse(context, execController, out)
}
}
}
//render template
// render template
if !context.ResponseWriter.Started && context.Output.Status == 0 {
if BConfig.WebConfig.AutoRender {
if err := execController.Render(); err != nil {
@ -867,7 +911,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
execController.Finish()
}
//execute middleware filters
// execute middleware filters
if len(p.filters[AfterExec]) > 0 && p.execFilter(context, urlPath, AfterExec) {
goto Admin
}
@ -877,57 +921,47 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
Admin:
//admin module record QPS
// admin module record QPS
statusCode := context.ResponseWriter.Status
if statusCode == 0 {
statusCode = 200
}
logAccess(context, &startTime, statusCode)
LogAccess(context, &startTime, statusCode)
if BConfig.Listen.EnableAdmin {
timeDur := time.Since(startTime)
context.ResponseWriter.Elapsed = timeDur
if BConfig.Listen.EnableAdmin {
pattern := ""
if routerInfo != nil {
pattern = routerInfo.pattern
}
if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) {
routerName := ""
if runRouter != nil {
go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur)
} else {
go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, "", timeDur)
routerName = runRouter.Name()
}
go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, routerName, timeDur)
}
}
if BConfig.RunMode == DEV && !BConfig.Log.AccessLogs {
var devInfo string
timeDur := time.Since(startTime)
iswin := (runtime.GOOS == "windows")
statusColor := logs.ColorByStatus(iswin, statusCode)
methodColor := logs.ColorByMethod(iswin, r.Method)
resetColor := logs.ColorByMethod(iswin, "")
if findRouter {
match := map[bool]string{true: "match", false: "nomatch"}
devInfo := fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s",
context.Input.IP(),
logs.ColorByStatus(statusCode), statusCode, logs.ResetColor(),
timeDur.String(),
match[findRouter],
logs.ColorByMethod(r.Method), r.Method, logs.ResetColor(),
r.URL.Path)
if routerInfo != nil {
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode,
resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
routerInfo.pattern)
} else {
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
devInfo += fmt.Sprintf(" r:%s", routerInfo.pattern)
}
} else {
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
}
if iswin {
logs.W32Debug(devInfo)
} else {
logs.Debug(devInfo)
}
}
// Call WriteHeader if status code has been set changed
if context.Output.Status != 0 {
context.ResponseWriter.WriteHeader(context.Output.Status)
@ -935,7 +969,7 @@ Admin:
}
func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, execController ControllerInterface, results []reflect.Value) {
//looping in reverse order for the case when both error and value are returned and error sets the response status code
// looping in reverse order for the case when both error and value are returned and error sets the response status code
for i := len(results) - 1; i >= 0; i-- {
result := results[i]
if result.Kind() != reflect.Interface || !result.IsNil() {
@ -975,12 +1009,13 @@ func toURL(params map[string]string) string {
return strings.TrimRight(u, "&")
}
func logAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) {
//Skip logging if AccessLogs config is false
// LogAccess logging info HTTP Access
func LogAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) {
// Skip logging if AccessLogs config is false
if !BConfig.Log.AccessLogs {
return
}
//Skip logging static requests unless EnableStaticLogs config is true
// Skip logging static requests unless EnableStaticLogs config is true
if !BConfig.Log.EnableStaticLogs && DefaultAccessLogFilter.Filter(ctx) {
return
}
@ -1005,7 +1040,7 @@ func logAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) {
HTTPReferrer: r.Header.Get("Referer"),
HTTPUserAgent: r.Header.Get("User-Agent"),
RemoteUser: r.Header.Get("Remote-User"),
BodyBytesSent: 0, //@todo this one is missing!
BodyBytesSent: 0, // @todo this one is missing!
}
logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
}

View File

@ -71,10 +71,6 @@ func (tc *TestController) GetEmptyBody() {
tc.Ctx.Output.Body(res)
}
type ResStatus struct {
Code int
Msg string
}
type JSONController struct {
Controller
@ -475,7 +471,7 @@ func TestParamResetFilter(t *testing.T) {
// a response header of `Splat`. The expectation here is that that Header
// value should match what the _request's_ router set, not the filter's.
headers := rw.HeaderMap
headers := rw.Result().Header
if len(headers["Splat"]) != 1 {
t.Errorf(
"%s: There was an error in the test. Splat param not set in Header",
@ -660,25 +656,16 @@ func beegoBeforeRouter1(ctx *context.Context) {
ctx.WriteString("|BeforeRouter1")
}
func beegoBeforeRouter2(ctx *context.Context) {
ctx.WriteString("|BeforeRouter2")
}
func beegoBeforeExec1(ctx *context.Context) {
ctx.WriteString("|BeforeExec1")
}
func beegoBeforeExec2(ctx *context.Context) {
ctx.WriteString("|BeforeExec2")
}
func beegoAfterExec1(ctx *context.Context) {
ctx.WriteString("|AfterExec1")
}
func beegoAfterExec2(ctx *context.Context) {
ctx.WriteString("|AfterExec2")
}
func beegoFinishRouter1(ctx *context.Context) {
ctx.WriteString("|FinishRouter1")

112
scripts/gobuild.sh Executable file
View File

@ -0,0 +1,112 @@
#!/bin/bash
# WARNING: DO NOT EDIT, THIS FILE IS PROBABLY A COPY
#
# The original version of this file is located in the https://github.com/istio/common-files repo.
# If you're looking at this file in a different repo and want to make a change, please go to the
# common-files repo, make the change there and check it in. Then come back to this repo and run
# "make update-common".
# Copyright Istio Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script builds and version stamps the output
# adatp to beego
VERBOSE=${VERBOSE:-"0"}
V=""
if [[ "${VERBOSE}" == "1" ]];then
V="-x"
set -x
fi
SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
OUT=${1:?"output path"}
shift
set -e
BUILD_GOOS=${GOOS:-linux}
BUILD_GOARCH=${GOARCH:-amd64}
GOBINARY=${GOBINARY:-go}
GOPKG="$GOPATH/pkg"
BUILDINFO=${BUILDINFO:-""}
STATIC=${STATIC:-1}
LDFLAGS=${LDFLAGS:--extldflags -static}
GOBUILDFLAGS=${GOBUILDFLAGS:-""}
# Split GOBUILDFLAGS by spaces into an array called GOBUILDFLAGS_ARRAY.
IFS=' ' read -r -a GOBUILDFLAGS_ARRAY <<< "$GOBUILDFLAGS"
GCFLAGS=${GCFLAGS:-}
export CGO_ENABLED=0
if [[ "${STATIC}" != "1" ]];then
LDFLAGS=""
fi
# gather buildinfo if not already provided
# For a release build BUILDINFO should be produced
# at the beginning of the build and used throughout
if [[ -z ${BUILDINFO} ]];then
BUILDINFO=$(mktemp)
"${SCRIPTPATH}/report_build_info.sh" > "${BUILDINFO}"
fi
# BUILD LD_EXTRAFLAGS
LD_EXTRAFLAGS=""
while read -r line; do
LD_EXTRAFLAGS="${LD_EXTRAFLAGS} -X ${line}"
done < "${BUILDINFO}"
# verify go version before build
# NB. this was copied verbatim from Kubernetes hack
minimum_go_version=go1.13 # supported patterns: go1.x, go1.x.x (x should be a number)
IFS=" " read -ra go_version <<< "$(${GOBINARY} version)"
if [[ "${minimum_go_version}" != $(echo -e "${minimum_go_version}\n${go_version[2]}" | sort -s -t. -k 1,1 -k 2,2n -k 3,3n | head -n1) && "${go_version[2]}" != "devel" ]]; then
echo "Warning: Detected that you are using an older version of the Go compiler. Beego requires ${minimum_go_version} or greater."
fi
CURRENT_BRANCH=$(git branch | grep '*')
CURRENT_BRANCH=${CURRENT_BRANCH:2}
BUILD_TIME=$(date +%Y-%m-%d--%T)
LD_EXTRAFLAGS="${LD_EXTRAFLAGS} -X github.com/astaxie/beego.GoVersion=${go_version[2]:2}"
LD_EXTRAFLAGS="${LD_EXTRAFLAGS} -X github.com/astaxie/beego.GitBranch=${CURRENT_BRANCH}"
LD_EXTRAFLAGS="${LD_EXTRAFLAGS} -X github.com/astaxie/beego.BuildTime=$BUILD_TIME"
OPTIMIZATION_FLAGS="-trimpath"
if [ "${DEBUG}" == "1" ]; then
OPTIMIZATION_FLAGS=""
fi
echo "BUILD_GOARCH: $BUILD_GOARCH"
echo "GOPKG: $GOPKG"
echo "LD_EXTRAFLAGS: $LD_EXTRAFLAGS"
echo "GO_VERSION: ${go_version[2]}"
echo "BRANCH: $CURRENT_BRANCH"
echo "BUILD_TIME: $BUILD_TIME"
time GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} ${GOBINARY} build \
${V} "${GOBUILDFLAGS_ARRAY[@]}" ${GCFLAGS:+-gcflags "${GCFLAGS}"} \
-o "${OUT}" \
${OPTIMIZATION_FLAGS} \
-pkgdir="${GOPKG}/${BUILD_GOOS}_${BUILD_GOARCH}" \
-ldflags "${LDFLAGS} ${LD_EXTRAFLAGS}" "${@}"

52
scripts/report_build_info.sh Executable file
View File

@ -0,0 +1,52 @@
#!/bin/bash
# WARNING: DO NOT EDIT, THIS FILE IS PROBABLY A COPY
#
# The original version of this file is located in the https://github.com/istio/common-files repo.
# If you're looking at this file in a different repo and want to make a change, please go to the
# common-files repo, make the change there and check it in. Then come back to this repo and run
# "make update-common".
# Copyright Istio Authors
#
# 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.
# adapt to beego
if BUILD_GIT_REVISION=$(git rev-parse HEAD 2> /dev/null); then
if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then
BUILD_GIT_REVISION=${BUILD_GIT_REVISION}"-dirty"
fi
else
BUILD_GIT_REVISION=unknown
fi
# Check for local changes
if git diff-index --quiet HEAD --; then
tree_status="Clean"
else
tree_status="Modified"
fi
# security wanted VERSION='unknown'
VERSION="${BUILD_GIT_REVISION}"
if [[ -n ${BEEGO_VERSION} ]]; then
VERSION="${BEEGO_VERSION}"
fi
GIT_DESCRIBE_TAG=$(git describe --tags)
echo "github.com/astaxie/beego.BuildVersion=${VERSION}"
echo "github.com/astaxie/beego.BuildGitRevision=${BUILD_GIT_REVISION}"
echo "github.com/astaxie/beego.BuildStatus=${tree_status}"
echo "github.com/astaxie/beego.BuildTag=${GIT_DESCRIBE_TAG}"

View File

@ -7,9 +7,10 @@ import (
"strings"
"sync"
"github.com/ledisdb/ledisdb/config"
"github.com/ledisdb/ledisdb/ledis"
"github.com/astaxie/beego/session"
"github.com/siddontang/ledisdb/config"
"github.com/siddontang/ledisdb/ledis"
)
var (
@ -133,7 +134,7 @@ func (lp *Provider) SessionRead(sid string) (session.Store, error) {
// SessionExist check ledis session exist by sid
func (lp *Provider) SessionExist(sid string) bool {
count, _ := c.Exists([]byte(sid))
return !(count == 0)
return count != 0
}
// SessionRegenerate generate new sid for ledis session

View File

@ -128,10 +128,13 @@ func (rp *MemProvider) SessionRead(sid string) (session.Store, error) {
}
}
item, err := client.Get(sid)
if err != nil && err == memcache.ErrCacheMiss {
if err != nil {
if err == memcache.ErrCacheMiss {
rs := &SessionStore{sid: sid, values: make(map[interface{}]interface{}), maxlifetime: rp.maxlifetime}
return rs, nil
}
return nil, err
}
var kv map[interface{}]interface{}
if len(item.Value) == 0 {
kv = make(map[interface{}]interface{})

View File

@ -170,7 +170,7 @@ func (mp *Provider) SessionExist(sid string) bool {
row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid)
var sessiondata []byte
err := row.Scan(&sessiondata)
return !(err == sql.ErrNoRows)
return err != sql.ErrNoRows
}
// SessionRegenerate generate new sid for mysql session

View File

@ -184,7 +184,7 @@ func (mp *Provider) SessionExist(sid string) bool {
row := c.QueryRow("select session_data from session where session_key=$1", sid)
var sessiondata []byte
err := row.Scan(&sessiondata)
return !(err == sql.ErrNoRows)
return err != sql.ErrNoRows
}
// SessionRegenerate generate new sid for postgresql session

View File

@ -0,0 +1,234 @@
// 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 redis for session provider
//
// depend on github.com/go-redis/redis
//
// go install github.com/go-redis/redis
//
// Usage:
// import(
// _ "github.com/astaxie/beego/session/redis_sentinel"
// "github.com/astaxie/beego/session"
// )
//
// func init() {
// globalSessions, _ = session.NewManager("redis_sentinel", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:26379;127.0.0.2:26379"}``)
// go globalSessions.GC()
// }
//
// more detail about params: please check the notes on the function SessionInit in this package
package redis_sentinel
import (
"github.com/astaxie/beego/session"
"github.com/go-redis/redis"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
var redispder = &Provider{}
// DefaultPoolSize redis_sentinel default pool size
var DefaultPoolSize = 100
// SessionStore redis_sentinel session store
type SessionStore struct {
p *redis.Client
sid string
lock sync.RWMutex
values map[interface{}]interface{}
maxlifetime int64
}
// Set value in redis_sentinel session
func (rs *SessionStore) Set(key, value interface{}) error {
rs.lock.Lock()
defer rs.lock.Unlock()
rs.values[key] = value
return nil
}
// Get value in redis_sentinel session
func (rs *SessionStore) Get(key interface{}) interface{} {
rs.lock.RLock()
defer rs.lock.RUnlock()
if v, ok := rs.values[key]; ok {
return v
}
return nil
}
// Delete value in redis_sentinel session
func (rs *SessionStore) Delete(key interface{}) error {
rs.lock.Lock()
defer rs.lock.Unlock()
delete(rs.values, key)
return nil
}
// Flush clear all values in redis_sentinel session
func (rs *SessionStore) Flush() error {
rs.lock.Lock()
defer rs.lock.Unlock()
rs.values = make(map[interface{}]interface{})
return nil
}
// SessionID get redis_sentinel session id
func (rs *SessionStore) SessionID() string {
return rs.sid
}
// SessionRelease save session values to redis_sentinel
func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
b, err := session.EncodeGob(rs.values)
if err != nil {
return
}
c := rs.p
c.Set(rs.sid, string(b), time.Duration(rs.maxlifetime)*time.Second)
}
// Provider redis_sentinel session provider
type Provider struct {
maxlifetime int64
savePath string
poolsize int
password string
dbNum int
poollist *redis.Client
masterName string
}
// SessionInit init redis_sentinel session
// savepath like redis sentinel addr,pool size,password,dbnum,masterName
// e.g. 127.0.0.1:26379;127.0.0.2:26379,100,1qaz2wsx,0,mymaster
func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
rp.maxlifetime = maxlifetime
configs := strings.Split(savePath, ",")
if len(configs) > 0 {
rp.savePath = configs[0]
}
if len(configs) > 1 {
poolsize, err := strconv.Atoi(configs[1])
if err != nil || poolsize < 0 {
rp.poolsize = DefaultPoolSize
} else {
rp.poolsize = poolsize
}
} else {
rp.poolsize = DefaultPoolSize
}
if len(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
}
if len(configs) > 4 {
if configs[4] != "" {
rp.masterName = configs[4]
} else {
rp.masterName = "mymaster"
}
} else {
rp.masterName = "mymaster"
}
rp.poollist = redis.NewFailoverClient(&redis.FailoverOptions{
SentinelAddrs: strings.Split(rp.savePath, ";"),
Password: rp.password,
PoolSize: rp.poolsize,
DB: rp.dbNum,
MasterName: rp.masterName,
})
return rp.poollist.Ping().Err()
}
// SessionRead read redis_sentinel session by sid
func (rp *Provider) SessionRead(sid string) (session.Store, error) {
var kv map[interface{}]interface{}
kvs, err := rp.poollist.Get(sid).Result()
if err != nil && err != redis.Nil {
return nil, err
}
if len(kvs) == 0 {
kv = make(map[interface{}]interface{})
} else {
if kv, err = session.DecodeGob([]byte(kvs)); err != nil {
return nil, err
}
}
rs := &SessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
return rs, nil
}
// SessionExist check redis_sentinel session exist by sid
func (rp *Provider) SessionExist(sid string) bool {
c := rp.poollist
if existed, err := c.Exists(sid).Result(); err != nil || existed == 0 {
return false
}
return true
}
// SessionRegenerate generate new sid for redis_sentinel session
func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
c := rp.poollist
if existed, err := c.Exists(oldsid).Result(); err != nil || existed == 0 {
// oldsid doesn't exists, set the new sid directly
// ignore error here, since if it return error
// the existed value will be 0
c.Set(sid, "", time.Duration(rp.maxlifetime)*time.Second)
} else {
c.Rename(oldsid, sid)
c.Expire(sid, time.Duration(rp.maxlifetime)*time.Second)
}
return rp.SessionRead(sid)
}
// SessionDestroy delete redis session by id
func (rp *Provider) SessionDestroy(sid string) error {
c := rp.poollist
c.Del(sid)
return nil
}
// SessionGC Impelment method, no used.
func (rp *Provider) SessionGC() {
}
// SessionAll return all activeSession
func (rp *Provider) SessionAll() int {
return 0
}
func init() {
session.Register("redis_sentinel", redispder)
}

View File

@ -0,0 +1,90 @@
package redis_sentinel
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/astaxie/beego/session"
)
func TestRedisSentinel(t *testing.T) {
sessionConfig := &session.ManagerConfig{
CookieName: "gosessionid",
EnableSetCookie: true,
Gclifetime: 3600,
Maxlifetime: 3600,
Secure: false,
CookieLifeTime: 3600,
ProviderConfig: "127.0.0.1:6379,100,,0,master",
}
globalSessions, e := session.NewManager("redis_sentinel", sessionConfig)
if e != nil {
t.Log(e)
return
}
//todo test if e==nil
go globalSessions.GC()
r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
sess, err := globalSessions.SessionStart(w, r)
if err != nil {
t.Fatal("session start failed:", err)
}
defer sess.SessionRelease(w)
// SET AND GET
err = sess.Set("username", "astaxie")
if err != nil {
t.Fatal("set username failed:", err)
}
username := sess.Get("username")
if username != "astaxie" {
t.Fatal("get username failed")
}
// DELETE
err = sess.Delete("username")
if err != nil {
t.Fatal("delete username failed:", err)
}
username = sess.Get("username")
if username != nil {
t.Fatal("delete username failed")
}
// FLUSH
err = sess.Set("username", "astaxie")
if err != nil {
t.Fatal("set failed:", err)
}
err = sess.Set("password", "1qaz2wsx")
if err != nil {
t.Fatal("set failed:", err)
}
username = sess.Get("username")
if username != "astaxie" {
t.Fatal("get username failed")
}
password := sess.Get("password")
if password != "1qaz2wsx" {
t.Fatal("get password failed")
}
err = sess.Flush()
if err != nil {
t.Fatal("flush failed:", err)
}
username = sess.Get("username")
if username != nil {
t.Fatal("flush failed")
}
password = sess.Get("password")
if password != nil {
t.Fatal("flush failed")
}
sess.SessionRelease(w)
}

View File

@ -74,7 +74,9 @@ func (st *CookieSessionStore) SessionID() string {
// SessionRelease Write cookie session to http response cookie
func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) {
st.lock.Lock()
encodedCookie, err := encodeCookie(cookiepder.block, cookiepder.config.SecurityKey, cookiepder.config.SecurityName, st.values)
st.lock.Unlock()
if err == nil {
cookie := &http.Cookie{Name: cookiepder.config.CookieName,
Value: url.QueryEscape(encodedCookie),

View File

@ -19,8 +19,10 @@ import (
"io/ioutil"
"net/http"
"os"
"errors"
"path"
"path/filepath"
"strings"
"sync"
"time"
)
@ -127,10 +129,17 @@ func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error {
// if file is not exist, create it.
// the file path is generated from sid string.
func (fp *FileProvider) SessionRead(sid string) (Store, error) {
invalidChars := "./"
if strings.ContainsAny(sid, invalidChars) {
return nil, errors.New("the sid shouldn't have following characters: " + invalidChars)
}
if len(sid) < 2 {
return nil, errors.New("length of the sid is less than 2")
}
filepder.lock.Lock()
defer filepder.lock.Unlock()
err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777)
err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0755)
if err != nil {
SLogger.Println(err.Error())
}
@ -223,7 +232,7 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) {
return nil, fmt.Errorf("newsid %s exist", newSidFile)
}
err = os.MkdirAll(newPath, 0777)
err = os.MkdirAll(newPath, 0755)
if err != nil {
SLogger.Println(err.Error())
}

View File

@ -19,7 +19,7 @@ import (
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"encoding/gob"
@ -129,7 +129,7 @@ func encodeCookie(block cipher.Block, hashKey, name string, value map[interface{
b = encode(b)
// 3. Create MAC for "name|date|value". Extra pipe to be used later.
b = []byte(fmt.Sprintf("%s|%d|%s|", name, time.Now().UTC().Unix(), b))
h := hmac.New(sha1.New, []byte(hashKey))
h := hmac.New(sha256.New, []byte(hashKey))
h.Write(b)
sig := h.Sum(nil)
// Append mac, remove name.
@ -153,7 +153,7 @@ func decodeCookie(block cipher.Block, hashKey, name, value string, gcmaxlifetime
}
b = append([]byte(name+"|"), b[:len(b)-len(parts[2])]...)
h := hmac.New(sha1.New, []byte(hashKey))
h := hmac.New(sha256.New, []byte(hashKey))
h.Write(b)
sig := h.Sum(nil)
if len(sig) != len(parts[2]) || subtle.ConstantTimeCompare(sig, parts[2]) != 1 {

View File

@ -81,6 +81,15 @@ func Register(name string, provide Provider) {
provides[name] = provide
}
//GetProvider
func GetProvider(name string) (Provider, error) {
provider, ok := provides[name]
if !ok {
return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", name)
}
return provider, nil
}
// ManagerConfig define the session config
type ManagerConfig struct {
CookieName string `json:"cookieName"`
@ -96,6 +105,7 @@ type ManagerConfig struct {
EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"`
SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"`
EnableSidInURLQuery bool `json:"EnableSidInURLQuery"`
SessionIDPrefix string `json:"sessionIDPrefix"`
}
// Manager contains Provider and its configuration.
@ -153,6 +163,11 @@ func NewManager(provideName string, cf *ManagerConfig) (*Manager, error) {
}, nil
}
// GetProvider return current manager's provider
func (manager *Manager) GetProvider() Provider {
return manager.provider
}
// getSid retrieves session identifier from HTTP Request.
// First try to retrieve id by reading from cookie, session cookie name is configurable,
// if not exist, then retrieve id from querying parameters.
@ -255,7 +270,8 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
Path: "/",
HttpOnly: !manager.config.DisableHTTPOnly,
Expires: expiration,
MaxAge: -1}
MaxAge: -1,
Domain: manager.config.Domain}
http.SetCookie(w, cookie)
}
@ -331,7 +347,7 @@ func (manager *Manager) sessionID() (string, error) {
if n != len(b) || err != nil {
return "", fmt.Errorf("Could not successfully read from the system CSPRNG")
}
return hex.EncodeToString(b), nil
return manager.config.SessionIDPrefix + hex.EncodeToString(b), nil
}
// Set cookie with https.

View File

@ -28,6 +28,7 @@ import (
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/logs"
"github.com/hashicorp/golang-lru"
)
var errNotStaticRequest = errors.New("request not a static file request")
@ -67,6 +68,10 @@ func serverStaticRouter(ctx *context.Context) {
http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath)
}
return
} else if fileInfo.Size() > int64(BConfig.WebConfig.StaticCacheFileSize) {
//over size file serve with http module
http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath)
return
}
var enableCompress = BConfig.EnableGzip && isStaticCompress(filePath)
@ -96,6 +101,7 @@ type serveContentHolder struct {
data []byte
modTime time.Time
size int64
originSize int64 //original file size:to judge file changed
encoding string
}
@ -104,22 +110,36 @@ type serveContentReader struct {
}
var (
staticFileMap = make(map[string]*serveContentHolder)
mapLock sync.RWMutex
staticFileLruCache *lru.Cache
lruLock sync.RWMutex
)
func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, *serveContentReader, error) {
if staticFileLruCache == nil {
//avoid lru cache error
if BConfig.WebConfig.StaticCacheFileNum >= 1 {
staticFileLruCache, _ = lru.New(BConfig.WebConfig.StaticCacheFileNum)
} else {
staticFileLruCache, _ = lru.New(1)
}
}
mapKey := acceptEncoding + ":" + filePath
mapLock.RLock()
mapFile := staticFileMap[mapKey]
mapLock.RUnlock()
lruLock.RLock()
var mapFile *serveContentHolder
if cacheItem, ok := staticFileLruCache.Get(mapKey); ok {
mapFile = cacheItem.(*serveContentHolder)
}
lruLock.RUnlock()
if isOk(mapFile, fi) {
reader := &serveContentReader{Reader: bytes.NewReader(mapFile.data)}
return mapFile.encoding != "", mapFile.encoding, mapFile, reader, nil
}
mapLock.Lock()
defer mapLock.Unlock()
if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) {
lruLock.Lock()
defer lruLock.Unlock()
if cacheItem, ok := staticFileLruCache.Get(mapKey); ok {
mapFile = cacheItem.(*serveContentHolder)
}
if !isOk(mapFile, fi) {
file, err := os.Open(filePath)
if err != nil {
return false, "", nil, nil, err
@ -130,8 +150,10 @@ func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, str
if err != nil {
return false, "", nil, nil, err
}
mapFile = &serveContentHolder{data: bufferWriter.Bytes(), modTime: fi.ModTime(), size: int64(bufferWriter.Len()), encoding: n}
staticFileMap[mapKey] = mapFile
mapFile = &serveContentHolder{data: bufferWriter.Bytes(), modTime: fi.ModTime(), size: int64(bufferWriter.Len()), originSize: fi.Size(), encoding: n}
if isOk(mapFile, fi) {
staticFileLruCache.Add(mapKey, mapFile)
}
}
reader := &serveContentReader{Reader: bytes.NewReader(mapFile.data)}
@ -141,8 +163,10 @@ func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, str
func isOk(s *serveContentHolder, fi os.FileInfo) bool {
if s == nil {
return false
} else if s.size > int64(BConfig.WebConfig.StaticCacheFileSize) {
return false
}
return s.modTime == fi.ModTime() && s.size == fi.Size()
return s.modTime == fi.ModTime() && s.originSize == fi.Size()
}
// isStaticCompress detect static files
@ -178,7 +202,7 @@ func searchFile(ctx *context.Context) (string, os.FileInfo, error) {
if !strings.Contains(requestPath, prefix) {
continue
}
if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
if prefix != "/" && len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
continue
}
filePath := path.Join(staticDir, requestPath[len(prefix):])

View File

@ -4,6 +4,7 @@ import (
"bytes"
"compress/gzip"
"compress/zlib"
"fmt"
"io"
"io/ioutil"
"os"
@ -53,6 +54,31 @@ func TestOpenStaticFileDeflate_1(t *testing.T) {
testOpenFile("deflate", content, t)
}
func TestStaticCacheWork(t *testing.T) {
encodings := []string{"", "gzip", "deflate"}
fi, _ := os.Stat(licenseFile)
for _, encoding := range encodings {
_, _, first, _, err := openFile(licenseFile, fi, encoding)
if err != nil {
t.Error(err)
continue
}
_, _, second, _, err := openFile(licenseFile, fi, encoding)
if err != nil {
t.Error(err)
continue
}
address1 := fmt.Sprintf("%p", first)
address2 := fmt.Sprintf("%p", second)
if address1 != address2 {
t.Errorf("encoding '%v' can not hit cache", encoding)
}
}
}
func assetOpenFileAndContent(sch *serveContentHolder, reader *serveContentReader, content []byte, t *testing.T) {
t.Log(sch.size, len(content))
if sch.size != int64(len(content)) {
@ -66,7 +92,7 @@ func assetOpenFileAndContent(sch *serveContentHolder, reader *serveContentReader
t.Fail()
}
}
if len(staticFileMap) == 0 {
if staticFileLruCache.Len() == 0 {
t.Log("men map is empty")
t.Fail()
}

View File

@ -20,6 +20,7 @@ import (
"html/template"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"regexp"
@ -37,9 +38,10 @@ var (
beeViewPathTemplates = make(map[string]map[string]*template.Template)
templatesLock sync.RWMutex
// beeTemplateExt stores the template extension which will build
beeTemplateExt = []string{"tpl", "html"}
beeTemplateExt = []string{"tpl", "html", "gohtml"}
// beeTemplatePreprocessors stores associations of extension -> preprocessor handler
beeTemplateEngines = map[string]templatePreProcessor{}
beeTemplateFS = defaultFSFunc
)
// ExecuteTemplate applies the template with name to the specified data object,
@ -181,12 +183,17 @@ func lockViewPaths() {
// BuildTemplate will build all template files in a directory.
// it makes beego can render any template file in view directory.
func BuildTemplate(dir string, files ...string) error {
if _, err := os.Stat(dir); err != nil {
var err error
fs := beeTemplateFS()
f, err := fs.Open(dir)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return errors.New("dir open err")
}
defer f.Close()
beeTemplates, ok := beeViewPathTemplates[dir]
if !ok {
panic("Unknown view path: " + dir)
@ -195,11 +202,11 @@ func BuildTemplate(dir string, files ...string) error {
root: dir,
files: make(map[string][]string),
}
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
err = Walk(fs, dir, func(path string, f os.FileInfo, err error) error {
return self.visit(path, f, err)
})
if err != nil {
fmt.Printf("filepath.Walk() returned %v\n", err)
fmt.Printf("Walk() returned %v\n", err)
return err
}
buildAllFiles := len(files) == 0
@ -210,11 +217,11 @@ func BuildTemplate(dir string, files ...string) error {
ext := filepath.Ext(file)
var t *template.Template
if len(ext) == 0 {
t, err = getTemplate(self.root, file, v...)
t, err = getTemplate(self.root, fs, file, v...)
} else if fn, ok := beeTemplateEngines[ext[1:]]; ok {
t, err = fn(self.root, file, beegoTplFuncMap)
} else {
t, err = getTemplate(self.root, file, v...)
t, err = getTemplate(self.root, fs, file, v...)
}
if err != nil {
logs.Error("parse template err:", file, err)
@ -229,20 +236,23 @@ func BuildTemplate(dir string, files ...string) error {
return nil
}
func getTplDeep(root, file, parent string, t *template.Template) (*template.Template, [][]string, error) {
func getTplDeep(root string, fs http.FileSystem, file string, parent string, t *template.Template) (*template.Template, [][]string, error) {
var fileAbsPath string
var rParent string
if filepath.HasPrefix(file, "../") {
var err error
if strings.HasPrefix(file, "../") {
rParent = filepath.Join(filepath.Dir(parent), file)
fileAbsPath = filepath.Join(root, filepath.Dir(parent), file)
} else {
rParent = file
fileAbsPath = filepath.Join(root, file)
}
if e := utils.FileExists(fileAbsPath); !e {
f, err := fs.Open(fileAbsPath)
if err != nil {
panic("can't find template file:" + file)
}
data, err := ioutil.ReadFile(fileAbsPath)
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, [][]string{}, err
}
@ -261,7 +271,7 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp
if !HasTemplateExt(m[1]) {
continue
}
_, _, err = getTplDeep(root, m[1], rParent, t)
_, _, err = getTplDeep(root, fs, m[1], rParent, t)
if err != nil {
return nil, [][]string{}, err
}
@ -270,14 +280,14 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp
return t, allSub, nil
}
func getTemplate(root, file string, others ...string) (t *template.Template, err error) {
func getTemplate(root string, fs http.FileSystem, file string, others ...string) (t *template.Template, err error) {
t = template.New(file).Delims(BConfig.WebConfig.TemplateLeft, BConfig.WebConfig.TemplateRight).Funcs(beegoTplFuncMap)
var subMods [][]string
t, subMods, err = getTplDeep(root, file, "", t)
t, subMods, err = getTplDeep(root, fs, file, "", t)
if err != nil {
return nil, err
}
t, err = _getTemplate(t, root, subMods, others...)
t, err = _getTemplate(t, root, fs, subMods, others...)
if err != nil {
return nil, err
@ -285,7 +295,7 @@ func getTemplate(root, file string, others ...string) (t *template.Template, err
return
}
func _getTemplate(t0 *template.Template, root string, subMods [][]string, others ...string) (t *template.Template, err error) {
func _getTemplate(t0 *template.Template, root string, fs http.FileSystem, subMods [][]string, others ...string) (t *template.Template, err error) {
t = t0
for _, m := range subMods {
if len(m) == 2 {
@ -297,11 +307,11 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others
for _, otherFile := range others {
if otherFile == m[1] {
var subMods1 [][]string
t, subMods1, err = getTplDeep(root, otherFile, "", t)
t, subMods1, err = getTplDeep(root, fs, otherFile, "", t)
if err != nil {
logs.Trace("template parse file err:", err)
} else if len(subMods1) > 0 {
t, err = _getTemplate(t, root, subMods1, others...)
t, err = _getTemplate(t, root, fs, subMods1, others...)
}
break
}
@ -310,8 +320,16 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others
for _, otherFile := range others {
var data []byte
fileAbsPath := filepath.Join(root, otherFile)
data, err = ioutil.ReadFile(fileAbsPath)
f, err := fs.Open(fileAbsPath)
if err != nil {
f.Close()
logs.Trace("template file parse error, not success open file:", err)
continue
}
data, err = ioutil.ReadAll(f)
f.Close()
if err != nil {
logs.Trace("template file parse error, not success read file:", err)
continue
}
reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*define[ ]+\"([^\"]+)\"")
@ -319,11 +337,14 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others
for _, sub := range allSub {
if len(sub) == 2 && sub[1] == m[1] {
var subMods1 [][]string
t, subMods1, err = getTplDeep(root, otherFile, "", t)
t, subMods1, err = getTplDeep(root, fs, otherFile, "", t)
if err != nil {
logs.Trace("template parse file err:", err)
} else if len(subMods1) > 0 {
t, err = _getTemplate(t, root, subMods1, others...)
t, err = _getTemplate(t, root, fs, subMods1, others...)
if err != nil {
logs.Trace("template parse file err:", err)
}
}
break
}
@ -335,6 +356,17 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others
return
}
type templateFSFunc func() http.FileSystem
func defaultFSFunc() http.FileSystem {
return FileSystem{}
}
// SetTemplateFSFunc set default filesystem function
func SetTemplateFSFunc(fnt templateFSFunc) {
beeTemplateFS = fnt
}
// SetViewsPath sets view directory path in beego application.
func SetViewsPath(path string) *App {
BConfig.WebConfig.ViewsPath = path

View File

@ -16,6 +16,9 @@ package beego
import (
"bytes"
"github.com/astaxie/beego/testdata"
"github.com/elazarl/go-bindata-assetfs"
"net/http"
"os"
"path/filepath"
"testing"
@ -256,3 +259,58 @@ func TestTemplateLayout(t *testing.T) {
}
os.RemoveAll(dir)
}
type TestingFileSystem struct {
assetfs *assetfs.AssetFS
}
func (d TestingFileSystem) Open(name string) (http.File, error) {
return d.assetfs.Open(name)
}
var outputBinData = `<!DOCTYPE html>
<html>
<head>
<title>beego welcome template</title>
</head>
<body>
<h1>Hello, blocks!</h1>
<h1>Hello, astaxie!</h1>
<h2>Hello</h2>
<p> This is SomeVar: val</p>
</body>
</html>
`
func TestFsBinData(t *testing.T) {
SetTemplateFSFunc(func() http.FileSystem {
return TestingFileSystem{&assetfs.AssetFS{Asset: testdata.Asset, AssetDir: testdata.AssetDir, AssetInfo: testdata.AssetInfo}}
})
dir := "views"
if err := AddViewPath("views"); err != nil {
t.Fatal(err)
}
beeTemplates := beeViewPathTemplates[dir]
if len(beeTemplates) != 3 {
t.Fatalf("should be 3 but got %v", len(beeTemplates))
}
if err := beeTemplates["index.tpl"].ExecuteTemplate(os.Stdout, "index.tpl", map[string]string{"Title": "Hello", "SomeVar": "val"}); err != nil {
t.Fatal(err)
}
out := bytes.NewBufferString("")
if err := beeTemplates["index.tpl"].ExecuteTemplate(out, "index.tpl", map[string]string{"Title": "Hello", "SomeVar": "val"}); err != nil {
t.Fatal(err)
}
if out.String() != outputBinData {
t.Log(out.String())
t.Fatal("Compare failed")
}
}

View File

@ -55,21 +55,21 @@ func Substr(s string, start, length int) string {
// HTML2str returns escaping text convert from html.
func HTML2str(html string) string {
re, _ := regexp.Compile(`\<[\S\s]+?\>`)
re := regexp.MustCompile(`\<[\S\s]+?\>`)
html = re.ReplaceAllStringFunc(html, strings.ToLower)
//remove STYLE
re, _ = regexp.Compile(`\<style[\S\s]+?\</style\>`)
re = regexp.MustCompile(`\<style[\S\s]+?\</style\>`)
html = re.ReplaceAllString(html, "")
//remove SCRIPT
re, _ = regexp.Compile(`\<script[\S\s]+?\</script\>`)
re = regexp.MustCompile(`\<script[\S\s]+?\</script\>`)
html = re.ReplaceAllString(html, "")
re, _ = regexp.Compile(`\<[\S\s]+?\>`)
re = regexp.MustCompile(`\<[\S\s]+?\>`)
html = re.ReplaceAllString(html, "\n")
re, _ = regexp.Compile(`\s{2,}`)
re = regexp.MustCompile(`\s{2,}`)
html = re.ReplaceAllString(html, "\n")
return strings.TrimSpace(html)
@ -172,7 +172,7 @@ func GetConfig(returnType, key string, defaultVal interface{}) (value interface{
case "DIY":
value, err = AppConfig.DIY(key)
default:
err = errors.New("Config keys must be of type String, Bool, Int, Int64, Float, or DIY")
err = errors.New("config keys must be of type String, Bool, Int, Int64, Float, or DIY")
}
if err != nil {
@ -297,10 +297,22 @@ func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) e
tag = tags[0]
}
value := form.Get(tag)
if len(value) == 0 {
formValues := form[tag]
var value string
if len(formValues) == 0 {
defaultValue := fieldT.Tag.Get("default")
if defaultValue != "" {
value = defaultValue
} else {
continue
}
}
if len(formValues) == 1 {
value = formValues[0]
if value == "" {
continue
}
}
switch fieldT.Type.Kind() {
case reflect.Bool:
@ -349,6 +361,8 @@ func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) e
if len(value) >= 25 {
value = value[:25]
t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
} else if strings.HasSuffix(strings.ToUpper(value), "Z") {
t, err = time.ParseInLocation(time.RFC3339, value, time.Local)
} else if len(value) >= 19 {
if strings.Contains(value, "T") {
value = value[:19]
@ -692,7 +706,7 @@ func ge(arg1, arg2 interface{}) (bool, error) {
// MapGet getting value from map by keys
// usage:
// Data["m"] = map[string]interface{} {
// Data["m"] = M{
// "a": 1,
// "1": map[string]float64{
// "c": 4,

View File

@ -111,7 +111,7 @@ func TestHtmlunquote(t *testing.T) {
func TestParseForm(t *testing.T) {
type ExtendInfo struct {
Hobby string `form:"hobby"`
Hobby []string `form:"hobby"`
Memo string
}
@ -146,7 +146,7 @@ func TestParseForm(t *testing.T) {
"date": []string{"2014-11-12"},
"organization": []string{"beego"},
"title": []string{"CXO"},
"hobby": []string{"Basketball"},
"hobby": []string{"", "Basketball", "Football"},
"memo": []string{"nothing"},
}
if err := ParseForm(form, u); err == nil {
@ -186,8 +186,14 @@ func TestParseForm(t *testing.T) {
if u.Title != "CXO" {
t.Errorf("Title should equal `CXO`, but got `%v`", u.Title)
}
if u.Hobby != "Basketball" {
t.Errorf("Hobby should equal `Basketball`, but got `%v`", u.Hobby)
if u.Hobby[0] != "" {
t.Errorf("Hobby should equal ``, but got `%v`", u.Hobby[0])
}
if u.Hobby[1] != "Basketball" {
t.Errorf("Hobby should equal `Basketball`, but got `%v`", u.Hobby[1])
}
if u.Hobby[2] != "Football" {
t.Errorf("Hobby should equal `Football`, but got `%v`", u.Hobby[2])
}
if len(u.Memo) != 0 {
t.Errorf("Memo's length should equal 0 but got %v", len(u.Memo))
@ -197,7 +203,6 @@ func TestParseForm(t *testing.T) {
func TestRenderForm(t *testing.T) {
type user struct {
ID int `form:"-"`
tag string `form:"tag"`
Name interface{} `form:"username"`
Age int `form:"age,text,年龄:"`
Sex string
@ -329,7 +334,7 @@ func TestMapGet(t *testing.T) {
}
// test 2 level map
m2 := map[string]interface{}{
m2 := M{
"1": map[string]float64{
"2": 3.5,
},
@ -344,11 +349,11 @@ func TestMapGet(t *testing.T) {
}
// test 5 level map
m5 := map[string]interface{}{
"1": map[string]interface{}{
"2": map[string]interface{}{
"3": map[string]interface{}{
"4": map[string]interface{}{
m5 := M{
"1": M{
"2": M{
"3": M{
"4": M{
"5": 1.2,
},
},

2
testdata/Makefile vendored Normal file
View File

@ -0,0 +1,2 @@
build_view:
$(GOPATH)/bin/go-bindata-assetfs -pkg testdata views/...

296
testdata/bindata.go vendored Normal file
View File

@ -0,0 +1,296 @@
// Code generated by go-bindata.
// sources:
// views/blocks/block.tpl
// views/header.tpl
// views/index.tpl
// DO NOT EDIT!
package testdata
import (
"bytes"
"compress/gzip"
"fmt"
"github.com/elazarl/go-bindata-assetfs"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}
return buf.Bytes(), nil
}
type asset struct {
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _viewsBlocksBlockTpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xae\x4e\x49\x4d\xcb\xcc\x4b\x55\x50\x4a\xca\xc9\x4f\xce\x56\xaa\xad\xe5\xb2\xc9\x30\xb4\xf3\x48\xcd\xc9\xc9\xd7\x51\x00\x8b\x15\x2b\xda\xe8\x67\x18\xda\x71\x55\x57\xa7\xe6\xa5\xd4\xd6\x02\x02\x00\x00\xff\xff\xfd\xa1\x7a\xf6\x32\x00\x00\x00")
func viewsBlocksBlockTplBytes() ([]byte, error) {
return bindataRead(
_viewsBlocksBlockTpl,
"views/blocks/block.tpl",
)
}
func viewsBlocksBlockTpl() (*asset, error) {
bytes, err := viewsBlocksBlockTplBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "views/blocks/block.tpl", size: 50, mode: os.FileMode(436), modTime: time.Unix(1541431067, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _viewsHeaderTpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xae\x4e\x49\x4d\xcb\xcc\x4b\x55\x50\xca\x48\x4d\x4c\x49\x2d\x52\xaa\xad\xe5\xb2\xc9\x30\xb4\xf3\x48\xcd\xc9\xc9\xd7\x51\x48\x2c\x2e\x49\xac\xc8\x4c\x55\xb4\xd1\xcf\x30\xb4\xe3\xaa\xae\x4e\xcd\x4b\xa9\xad\x05\x04\x00\x00\xff\xff\xe4\x12\x47\x01\x34\x00\x00\x00")
func viewsHeaderTplBytes() ([]byte, error) {
return bindataRead(
_viewsHeaderTpl,
"views/header.tpl",
)
}
func viewsHeaderTpl() (*asset, error) {
bytes, err := viewsHeaderTplBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "views/header.tpl", size: 52, mode: os.FileMode(436), modTime: time.Unix(1541431067, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _viewsIndexTpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x8f\xbd\x8a\xc3\x30\x10\x84\x6b\xeb\x29\xe6\xfc\x00\x16\xb8\x3c\x16\x35\x77\xa9\x13\x88\x09\xa4\xf4\xcf\x12\x99\x48\x48\xd8\x82\x10\x84\xde\x3d\xc8\x8a\x8b\x90\x6a\xa4\xd9\x6f\xd8\x59\xfa\xf9\x3f\xfe\x75\xd7\xd3\x01\x3a\x58\xa3\x04\x15\x01\x48\x73\x3f\xe5\x07\x40\x61\x0e\x86\xd5\xc0\x7c\x73\x78\xb0\x19\x9d\x65\x04\xb6\xde\xf4\x81\x49\x96\x69\x8e\xc8\x3d\x43\x83\x9b\x9e\x4a\x88\x2a\xc6\x9d\x43\x3d\x18\x37\xde\xeb\x94\x3e\xdd\x1c\xe1\xe5\xcb\xde\xe0\x55\x6e\xd2\x04\x6f\x32\x20\x2a\xd2\xad\x8a\x11\x4d\x97\x57\x22\x25\x92\xba\x55\xa2\x22\xaf\xd0\xe9\x79\xc5\xbc\xe2\xec\x2c\x5f\xfa\xe5\x17\x99\x7b\x7f\x36\xd2\x97\x8a\xa5\x19\xc9\x72\xe7\x2b\x00\x00\xff\xff\xb2\x39\xca\x9f\xff\x00\x00\x00")
func viewsIndexTplBytes() ([]byte, error) {
return bindataRead(
_viewsIndexTpl,
"views/index.tpl",
)
}
func viewsIndexTpl() (*asset, error) {
bytes, err := viewsIndexTplBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "views/index.tpl", size: 255, mode: os.FileMode(436), modTime: time.Unix(1541434906, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"views/blocks/block.tpl": viewsBlocksBlockTpl,
"views/header.tpl": viewsHeaderTpl,
"views/index.tpl": viewsIndexTpl,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"views": &bintree{nil, map[string]*bintree{
"blocks": &bintree{nil, map[string]*bintree{
"block.tpl": &bintree{viewsBlocksBlockTpl, map[string]*bintree{}},
}},
"header.tpl": &bintree{viewsHeaderTpl, map[string]*bintree{}},
"index.tpl": &bintree{viewsIndexTpl, map[string]*bintree{}},
}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}
func assetFS() *assetfs.AssetFS {
assetInfo := func(path string) (os.FileInfo, error) {
return os.Stat(path)
}
for k := range _bintree.Children {
return &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: assetInfo, Prefix: k}
}
panic("unreachable")
}

3
testdata/views/blocks/block.tpl vendored Normal file
View File

@ -0,0 +1,3 @@
{{define "block"}}
<h1>Hello, blocks!</h1>
{{end}}

3
testdata/views/header.tpl vendored Normal file
View File

@ -0,0 +1,3 @@
{{define "header"}}
<h1>Hello, astaxie!</h1>
{{end}}

15
testdata/views/index.tpl vendored Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>beego welcome template</title>
</head>
<body>
{{template "block"}}
{{template "header"}}
{{template "blocks/block.tpl"}}
<h2>{{ .Title }}</h2>
<p> This is SomeVar: {{ .SomeVar }}</p>
</body>
</html>

View File

@ -117,8 +117,8 @@ func (m *URLMap) GetMap() map[string]interface{} {
// GetMapData return all mapdata
func (m *URLMap) GetMapData() []map[string]interface{} {
m.lock.Lock()
defer m.lock.Unlock()
m.lock.RLock()
defer m.lock.RUnlock()
var resultLists []map[string]interface{}

View File

@ -20,6 +20,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"time"
)
@ -32,6 +33,7 @@ type bounds struct {
// The bounds for each field.
var (
AdminTaskList map[string]Tasker
taskLock sync.RWMutex
stop chan bool
changed chan bool
isstart bool
@ -389,6 +391,8 @@ func dayMatches(s *Schedule, t time.Time) bool {
// StartTask start all tasks
func StartTask() {
taskLock.Lock()
defer taskLock.Unlock()
if isstart {
//If already started no need to start another goroutine.
return
@ -404,7 +408,10 @@ func run() {
}
for {
// we only use RLock here because NewMapSorter copy the reference, do not change any thing
taskLock.RLock()
sortList := NewMapSorter(AdminTaskList)
taskLock.RUnlock()
sortList.Sort()
var effective time.Time
if len(AdminTaskList) == 0 || sortList.Vals[0].GetNext().IsZero() {
@ -428,6 +435,11 @@ func run() {
continue
case <-changed:
now = time.Now().Local()
taskLock.Lock()
for _, t := range AdminTaskList {
t.SetNext(now)
}
taskLock.Unlock()
continue
case <-stop:
return
@ -437,6 +449,8 @@ func run() {
// StopTask stop all tasks
func StopTask() {
taskLock.Lock()
defer taskLock.Unlock()
if isstart {
isstart = false
stop <- true
@ -446,6 +460,9 @@ func StopTask() {
// AddTask add task with name
func AddTask(taskname string, t Tasker) {
taskLock.Lock()
defer taskLock.Unlock()
t.SetNext(time.Now().Local())
AdminTaskList[taskname] = t
if isstart {
changed <- true
@ -454,6 +471,8 @@ func AddTask(taskname string, t Tasker) {
// DeleteTask delete task with name
func DeleteTask(taskname string) {
taskLock.Lock()
defer taskLock.Unlock()
delete(AdminTaskList, taskname)
if isstart {
changed <- true

View File

@ -162,7 +162,7 @@ func (e *Email) Bytes() ([]byte, error) {
// AttachFile Add attach file to the send mail
func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
if len(args) < 1 && len(args) > 2 {
if len(args) < 1 || len(args) > 2 { // change && to ||
err = errors.New("Must specify a file name and number of parameters can not exceed at least two")
return
}
@ -175,6 +175,7 @@ func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
if err != nil {
return
}
defer f.Close()
ct := mime.TypeByExtension(filepath.Ext(filename))
basename := path.Base(filename)
return e.Attach(f, basename, ct, id)
@ -183,7 +184,7 @@ func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
// Attach is used to attach content from an io.Reader to the email.
// Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type.
func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) {
if len(args) < 1 && len(args) > 2 {
if len(args) < 1 || len(args) > 2 { // change && to ||
err = errors.New("Must specify the file type and number of parameters can not exceed at least two")
return
}

View File

@ -26,6 +26,7 @@ func TestNewBeeMap(t *testing.T) {
}
func TestSet(t *testing.T) {
safeMap = NewBeeMap()
if ok := safeMap.Set("astaxie", 1); !ok {
t.Error("expected", true, "got", false)
}

View File

@ -3,19 +3,78 @@ package utils
import (
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
)
// GetGOPATHs returns all paths in GOPATH variable.
func GetGOPATHs() []string {
gopath := os.Getenv("GOPATH")
if gopath == "" && strings.Compare(runtime.Version(), "go1.8") >= 0 {
if gopath == "" && compareGoVersion(runtime.Version(), "go1.8") >= 0 {
gopath = defaultGOPATH()
}
return filepath.SplitList(gopath)
}
func compareGoVersion(a, b string) int {
reg := regexp.MustCompile("^\\d*")
a = strings.TrimPrefix(a, "go")
b = strings.TrimPrefix(b, "go")
versionsA := strings.Split(a, ".")
versionsB := strings.Split(b, ".")
for i := 0; i < len(versionsA) && i < len(versionsB); i++ {
versionA := versionsA[i]
versionB := versionsB[i]
vA, err := strconv.Atoi(versionA)
if err != nil {
str := reg.FindString(versionA)
if str != "" {
vA, _ = strconv.Atoi(str)
} else {
vA = -1
}
}
vB, err := strconv.Atoi(versionB)
if err != nil {
str := reg.FindString(versionB)
if str != "" {
vB, _ = strconv.Atoi(str)
} else {
vB = -1
}
}
if vA > vB {
// vA = 12, vB = 8
return 1
} else if vA < vB {
// vA = 6, vB = 8
return -1
} else if vA == -1 {
// vA = rc1, vB = rc3
return strings.Compare(versionA, versionB)
}
// vA = vB = 8
continue
}
if len(versionsA) > len(versionsB) {
return 1
} else if len(versionsA) == len(versionsB) {
return 0
}
return -1
}
func defaultGOPATH() string {
env := "HOME"
if runtime.GOOS == "windows" {

Some files were not shown because too many files have changed in this diff Show More