Browse Source

添加依赖库

lifei6671 8 years ago
parent
commit
d38417535a
100 changed files with 19211 additions and 9 deletions
  1. 102 9
      Godeps/Godeps.json
  2. 339 0
      vendor/github.com/adamzy/cedar-go/LICENSE.md
  3. 83 0
      vendor/github.com/adamzy/cedar-go/README.md
  4. 231 0
      vendor/github.com/adamzy/cedar-go/api.go
  5. 407 0
      vendor/github.com/adamzy/cedar-go/cedar.go
  6. 12 0
      vendor/github.com/adamzy/cedar-go/doc.go
  7. 11 0
      vendor/github.com/adamzy/cedar-go/errors.go
  8. 63 0
      vendor/github.com/adamzy/cedar-go/io.go
  9. 17 0
      vendor/github.com/astaxie/beego/.github/ISSUE_TEMPLATE
  10. 73 0
      vendor/github.com/astaxie/beego/admin_test.go
  11. 59 0
      vendor/github.com/astaxie/beego/cache/README.md
  12. 103 0
      vendor/github.com/astaxie/beego/cache/cache.go
  13. 168 0
      vendor/github.com/astaxie/beego/cache/cache_test.go
  14. 100 0
      vendor/github.com/astaxie/beego/cache/conv.go
  15. 143 0
      vendor/github.com/astaxie/beego/cache/conv_test.go
  16. 255 0
      vendor/github.com/astaxie/beego/cache/file.go
  17. 191 0
      vendor/github.com/astaxie/beego/cache/memcache/memcache.go
  18. 108 0
      vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go
  19. 244 0
      vendor/github.com/astaxie/beego/cache/memory.go
  20. 240 0
      vendor/github.com/astaxie/beego/cache/redis/redis.go
  21. 106 0
      vendor/github.com/astaxie/beego/cache/redis/redis_test.go
  22. 240 0
      vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go
  23. 104 0
      vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go
  24. 55 0
      vendor/github.com/astaxie/beego/config/config_test.go
  25. 85 0
      vendor/github.com/astaxie/beego/config/env/env.go
  26. 75 0
      vendor/github.com/astaxie/beego/config/env/env_test.go
  27. 190 0
      vendor/github.com/astaxie/beego/config/ini_test.go
  28. 222 0
      vendor/github.com/astaxie/beego/config/json_test.go
  29. 228 0
      vendor/github.com/astaxie/beego/config/xml/xml.go
  30. 125 0
      vendor/github.com/astaxie/beego/config/xml/xml_test.go
  31. 297 0
      vendor/github.com/astaxie/beego/config/yaml/yaml.go
  32. 115 0
      vendor/github.com/astaxie/beego/config/yaml/yaml_test.go
  33. 138 0
      vendor/github.com/astaxie/beego/config_test.go
  34. 59 0
      vendor/github.com/astaxie/beego/context/acceptencoder_test.go
  35. 47 0
      vendor/github.com/astaxie/beego/context/context_test.go
  36. 207 0
      vendor/github.com/astaxie/beego/context/input_test.go
  37. 181 0
      vendor/github.com/astaxie/beego/controller_test.go
  38. 88 0
      vendor/github.com/astaxie/beego/error_test.go
  39. 68 0
      vendor/github.com/astaxie/beego/filter_test.go
  40. 54 0
      vendor/github.com/astaxie/beego/flash_test.go
  41. 97 0
      vendor/github.com/astaxie/beego/httplib/README.md
  42. 585 0
      vendor/github.com/astaxie/beego/httplib/httplib.go
  43. 218 0
      vendor/github.com/astaxie/beego/httplib/httplib_test.go
  44. 192 0
      vendor/github.com/astaxie/beego/logs/alils/alils.go
  45. 13 0
      vendor/github.com/astaxie/beego/logs/alils/config.go
  46. 984 0
      vendor/github.com/astaxie/beego/logs/alils/log.pb.go
  47. 39 0
      vendor/github.com/astaxie/beego/logs/alils/log_config.go
  48. 818 0
      vendor/github.com/astaxie/beego/logs/alils/log_project.go
  49. 269 0
      vendor/github.com/astaxie/beego/logs/alils/log_store.go
  50. 87 0
      vendor/github.com/astaxie/beego/logs/alils/machine_group.go
  51. 62 0
      vendor/github.com/astaxie/beego/logs/alils/request.go
  52. 112 0
      vendor/github.com/astaxie/beego/logs/alils/signature.go
  53. 294 0
      vendor/github.com/astaxie/beego/logs/color_windows_test.go
  54. 25 0
      vendor/github.com/astaxie/beego/logs/conn_test.go
  55. 51 0
      vendor/github.com/astaxie/beego/logs/console_test.go
  56. 80 0
      vendor/github.com/astaxie/beego/logs/es/es.go
  57. 280 0
      vendor/github.com/astaxie/beego/logs/file_test.go
  58. 75 0
      vendor/github.com/astaxie/beego/logs/logger_test.go
  59. 78 0
      vendor/github.com/astaxie/beego/logs/multifile_test.go
  60. 27 0
      vendor/github.com/astaxie/beego/logs/smtp_test.go
  61. 53 0
      vendor/github.com/astaxie/beego/migration/ddl.go
  62. 278 0
      vendor/github.com/astaxie/beego/migration/migration.go
  63. 171 0
      vendor/github.com/astaxie/beego/namespace_test.go
  64. 493 0
      vendor/github.com/astaxie/beego/orm/models_test.go
  65. 2320 0
      vendor/github.com/astaxie/beego/orm/orm_test.go
  66. 36 0
      vendor/github.com/astaxie/beego/orm/utils_test.go
  67. 160 0
      vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go
  68. 20 0
      vendor/github.com/astaxie/beego/plugins/apiauth/apiauth_test.go
  69. 107 0
      vendor/github.com/astaxie/beego/plugins/auth/basic.go
  70. 228 0
      vendor/github.com/astaxie/beego/plugins/cors/cors.go
  71. 253 0
      vendor/github.com/astaxie/beego/plugins/cors/cors_test.go
  72. 689 0
      vendor/github.com/astaxie/beego/router_test.go
  73. 243 0
      vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go
  74. 179 0
      vendor/github.com/astaxie/beego/session/ledis/ledis_session.go
  75. 232 0
      vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go
  76. 234 0
      vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go
  77. 248 0
      vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go
  78. 256 0
      vendor/github.com/astaxie/beego/session/redis/sess_redis.go
  79. 105 0
      vendor/github.com/astaxie/beego/session/sess_cookie_test.go
  80. 58 0
      vendor/github.com/astaxie/beego/session/sess_mem_test.go
  81. 132 0
      vendor/github.com/astaxie/beego/session/sess_test.go
  82. 192 0
      vendor/github.com/astaxie/beego/session/ssdb/sess_ssdb.go
  83. 73 0
      vendor/github.com/astaxie/beego/staticfile_test.go
  84. 171 0
      vendor/github.com/astaxie/beego/swagger/swagger.go
  85. 144 0
      vendor/github.com/astaxie/beego/template_test.go
  86. 375 0
      vendor/github.com/astaxie/beego/templatefunc_test.go
  87. 15 0
      vendor/github.com/astaxie/beego/testing/assertions.go
  88. 65 0
      vendor/github.com/astaxie/beego/testing/client.go
  89. 28 0
      vendor/github.com/astaxie/beego/toolbox/profile_test.go
  90. 40 0
      vendor/github.com/astaxie/beego/toolbox/statistics_test.go
  91. 63 0
      vendor/github.com/astaxie/beego/toolbox/task_test.go
  92. 306 0
      vendor/github.com/astaxie/beego/tree_test.go
  93. 28 0
      vendor/github.com/astaxie/beego/utils/caller_test.go
  94. 19 0
      vendor/github.com/astaxie/beego/utils/captcha/LICENSE
  95. 45 0
      vendor/github.com/astaxie/beego/utils/captcha/README.md
  96. 270 0
      vendor/github.com/astaxie/beego/utils/captcha/captcha.go
  97. 501 0
      vendor/github.com/astaxie/beego/utils/captcha/image.go
  98. 52 0
      vendor/github.com/astaxie/beego/utils/captcha/image_test.go
  99. 277 0
      vendor/github.com/astaxie/beego/utils/captcha/siprng.go
  100. 33 0
      vendor/github.com/astaxie/beego/utils/captcha/siprng_test.go

+ 102 - 9
Godeps/Godeps.json

@@ -2,60 +2,153 @@
 	"ImportPath": "github.com/lifei6671/godoc",
 	"GoVersion": "go1.8",
 	"GodepVersion": "v79",
+	"Packages": [
+		"./..."
+	],
 	"Deps": [
+		{
+			"ImportPath": "github.com/adamzy/cedar-go",
+			"Rev": "d348c21f72432c2b6d5f05f68759fde94f64b227"
+		},
 		{
 			"ImportPath": "github.com/astaxie/beego",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
 		{
 			"ImportPath": "github.com/astaxie/beego/config",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
 		{
 			"ImportPath": "github.com/astaxie/beego/context",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
 		{
 			"ImportPath": "github.com/astaxie/beego/grace",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
 		{
 			"ImportPath": "github.com/astaxie/beego/logs",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
 		{
 			"ImportPath": "github.com/astaxie/beego/orm",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
 		{
 			"ImportPath": "github.com/astaxie/beego/session",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
 		{
 			"ImportPath": "github.com/astaxie/beego/toolbox",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
 		{
 			"ImportPath": "github.com/astaxie/beego/utils",
-			"Comment": "v1.8.0",
+			"Comment": "v1.8.1",
 			"Rev": "323a1c4214101331a4b71922c23d19b7409ac71f"
 		},
+		{
+			"ImportPath": "github.com/boltdb/bolt",
+			"Comment": "v1.3.0-58-ge9cf4fa",
+			"Rev": "e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd"
+		},
+		{
+			"ImportPath": "github.com/cznic/fileutil",
+			"Rev": "90cf820aafe8f7df39416fdbb932029ff99bd1ab"
+		},
+		{
+			"ImportPath": "github.com/cznic/internal/buffer",
+			"Comment": "1.0.0-1-ge5e1c3e",
+			"Rev": "e5e1c3e9165d0a72507c2bbb0ffac1c02b8d3f7c"
+		},
+		{
+			"ImportPath": "github.com/cznic/internal/file",
+			"Comment": "1.0.0-1-ge5e1c3e",
+			"Rev": "e5e1c3e9165d0a72507c2bbb0ffac1c02b8d3f7c"
+		},
+		{
+			"ImportPath": "github.com/cznic/internal/slice",
+			"Comment": "1.0.0-1-ge5e1c3e",
+			"Rev": "e5e1c3e9165d0a72507c2bbb0ffac1c02b8d3f7c"
+		},
+		{
+			"ImportPath": "github.com/cznic/kv",
+			"Rev": "c5de474a2ccdaed5ba5ff8b5d2d213dbf48a8b5e"
+		},
+		{
+			"ImportPath": "github.com/cznic/lldb",
+			"Comment": "v1.1.0",
+			"Rev": "bea8611dd5c407f3c5eab9f9c68e887a27dc6f0e"
+		},
+		{
+			"ImportPath": "github.com/cznic/mathutil",
+			"Rev": "1447ad269d64ca91aa8d7079baa40b6fc8b965e7"
+		},
+		{
+			"ImportPath": "github.com/cznic/sortutil",
+			"Rev": "4c7342852e65c2088c981288f2c5610d10b9f7f4"
+		},
+		{
+			"ImportPath": "github.com/cznic/zappy",
+			"Rev": "2533cb5b45cc6c07421468ce262899ddc9d53fb7"
+		},
+		{
+			"ImportPath": "github.com/edsrzf/mmap-go",
+			"Rev": "0bce6a6887123b67a60366d2c9fe2dfb74289d2e"
+		},
 		{
 			"ImportPath": "github.com/go-sql-driver/mysql",
 			"Comment": "v1.2-191-g1421caf",
 			"Rev": "1421caf44f6464fd2ee8de694c7508ee13f92964"
 		},
+		{
+			"ImportPath": "github.com/huichen/murmur",
+			"Rev": "e0489551cf5116e27d7cc69d97a53cdbdd028acf"
+		},
+		{
+			"ImportPath": "github.com/huichen/sego",
+			"Rev": "d06fe1b3abe3877ab593b57e5e43daf6c4c25add"
+		},
+		{
+			"ImportPath": "github.com/huichen/wukong/core",
+			"Comment": "v0.1-94-gd014a1f",
+			"Rev": "d014a1f19dae3664677c11bd25549cfc820cd890"
+		},
+		{
+			"ImportPath": "github.com/huichen/wukong/engine",
+			"Comment": "v0.1-94-gd014a1f",
+			"Rev": "d014a1f19dae3664677c11bd25549cfc820cd890"
+		},
+		{
+			"ImportPath": "github.com/huichen/wukong/storage",
+			"Comment": "v0.1-94-gd014a1f",
+			"Rev": "d014a1f19dae3664677c11bd25549cfc820cd890"
+		},
+		{
+			"ImportPath": "github.com/huichen/wukong/types",
+			"Comment": "v0.1-94-gd014a1f",
+			"Rev": "d014a1f19dae3664677c11bd25549cfc820cd890"
+		},
+		{
+			"ImportPath": "github.com/huichen/wukong/utils",
+			"Comment": "v0.1-94-gd014a1f",
+			"Rev": "d014a1f19dae3664677c11bd25549cfc820cd890"
+		},
 		{
 			"ImportPath": "github.com/nfnt/resize",
 			"Rev": "891127d8d1b52734debe1b3c3d7e747502b6c366"
+		},
+		{
+			"ImportPath": "golang.org/x/sys/unix",
+			"Rev": "9ccfe848b9db8435a24c424abbc07a921adf1df5"
 		}
 	]
 }

+ 339 - 0
vendor/github.com/adamzy/cedar-go/LICENSE.md

@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {description}
+    Copyright (C) {year}  {fullname}
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  {signature of Ty Coon}, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 83 - 0
vendor/github.com/adamzy/cedar-go/README.md

@@ -0,0 +1,83 @@
+# cedar-go [![GoDoc](https://godoc.org/github.com/adamzy/cedar-go?status.svg)](https://godoc.org/github.com/adamzy/cedar-go)
+
+Package `cedar-go` implementes double-array trie.
+
+It is a [Golang](https://golang.org/) port of [cedar](http://www.tkl.iis.u-tokyo.ac.jp/~ynaga/cedar) which is written in C++ by Naoki Yoshinaga. `cedar-go` currently implements the `reduced` verion of cedar. 
+This package is not thread safe if there is one goroutine doing insertions or deletions. 
+
+## Install
+```
+go get github.com/adamzy/cedar-go
+```
+
+## Usage
+```go
+package main
+
+import (
+	"fmt"
+
+	"github.com/adamzy/cedar-go"
+)
+
+func main() {
+	// create a new cedar trie.
+	trie := cedar.New()
+
+	// a helper function to print the id-key-value triple given trie node id
+	printIdKeyValue := func(id int) {
+		// the key of node `id`.
+		key, _ := trie.Key(id)
+		// the value of node `id`.
+		value, _ := trie.Value(id)
+		fmt.Printf("%d\t%s:%v\n", id, key, value)
+	}
+
+	// Insert key-value pairs.
+    // The order of insertion is not important.
+	trie.Insert([]byte("How many"), 0)
+	trie.Insert([]byte("How many loved"), 1)
+	trie.Insert([]byte("How many loved your moments"), 2)
+	trie.Insert([]byte("How many loved your moments of glad grace"), 3)
+	trie.Insert([]byte("姑苏"), 4)
+	trie.Insert([]byte("姑苏城外"), 5)
+	trie.Insert([]byte("姑苏城外寒山寺"), 6)
+
+	// Get the associated value of a key directly.
+	value, _ := trie.Get([]byte("How many loved your moments of glad grace"))
+	fmt.Println(value)
+
+	// Or, jump to the node first,
+	id, _ := trie.Jump([]byte("How many loved your moments"), 0)
+	// then get the key and the value
+	printIdKeyValue(id)
+
+	fmt.Println("\nPrefixMatch\nid\tkey:value")
+	for _, id := range trie.PrefixMatch([]byte("How many loved your moments of glad grace"), 0) {
+		printIdKeyValue(id)
+	}
+
+	fmt.Println("\nPrefixPredict\nid\tkey:value")
+	for _, id := range trie.PrefixPredict([]byte("姑苏"), 0) {
+		printIdKeyValue(id)
+	}
+}
+```
+will produce
+```
+3
+281	How many loved your moments:2
+
+PrefixMatch
+id	key:value
+262	How many:0
+268	How many loved:1
+281	How many loved your moments:2
+296	How many loved your moments of glad grace:3
+
+PrefixPredict
+id	key:value
+303	姑苏:4
+309	姑苏城外:5
+318	姑苏城外寒山寺:6
+```

+ 231 - 0
vendor/github.com/adamzy/cedar-go/api.go

@@ -0,0 +1,231 @@
+package cedar
+
+// Status reports the following statistics of the cedar:
+//	keys:		number of keys that are in the cedar,
+//	nodes:		number of trie nodes (slots in the base array) has been taken,
+//	size:			the size of the base array used by the cedar,
+//	capacity:		the capicity of the base array used by the cedar.
+func (da *Cedar) Status() (keys, nodes, size, capacity int) {
+	for i := 0; i < da.Size; i++ {
+		n := da.Array[i]
+		if n.Check >= 0 {
+			nodes++
+			if n.Value >= 0 {
+				keys++
+			}
+		}
+	}
+	return keys, nodes, da.Size, da.Capacity
+}
+
+// Jump travels from a node `from` to another node `to` by following the path `path`.
+// For example, if the following keys were inserted:
+//	id	key
+//	19	abc
+//	23	ab
+//	37	abcd
+// then
+//	Jump([]byte("ab"), 0) = 23, nil		// reach "ab" from root
+//	Jump([]byte("c"), 23) = 19, nil			// reach "abc" from "ab"
+//	Jump([]byte("cd"), 23) = 37, nil		// reach "abcd" from "ab"
+func (da *Cedar) Jump(path []byte, from int) (to int, err error) {
+	for _, b := range path {
+		if da.Array[from].Value >= 0 {
+			return from, ErrNoPath
+		}
+		to = da.Array[from].base() ^ int(b)
+		if da.Array[to].Check != from {
+			return from, ErrNoPath
+		}
+		from = to
+	}
+	return to, nil
+}
+
+// Key returns the key of the node with the given `id`.
+// It will return ErrNoPath, if the node does not exist.
+func (da *Cedar) Key(id int) (key []byte, err error) {
+	for id > 0 {
+		from := da.Array[id].Check
+		if from < 0 {
+			return nil, ErrNoPath
+		}
+		if char := byte(da.Array[from].base() ^ id); char != 0 {
+			key = append(key, char)
+		}
+		id = from
+	}
+	if id != 0 || len(key) == 0 {
+		return nil, ErrInvalidKey
+	}
+	for i := 0; i < len(key)/2; i++ {
+		key[i], key[len(key)-i-1] = key[len(key)-i-1], key[i]
+	}
+	return key, nil
+}
+
+// Value returns the value of the node with the given `id`.
+// It will return ErrNoValue, if the node does not have a value.
+func (da *Cedar) Value(id int) (value int, err error) {
+	value = da.Array[id].Value
+	if value >= 0 {
+		return value, nil
+	}
+	to := da.Array[id].base()
+	if da.Array[to].Check == id && da.Array[to].Value >= 0 {
+		return da.Array[to].Value, nil
+	}
+	return 0, ErrNoValue
+}
+
+// Insert adds a key-value pair into the cedar.
+// It will return ErrInvalidValue, if value < 0 or >= ValueLimit.
+func (da *Cedar) Insert(key []byte, value int) error {
+	if value < 0 || value >= ValueLimit {
+		return ErrInvalidValue
+	}
+	p := da.get(key, 0, 0)
+	*p = value
+	return nil
+}
+
+// Update increases the value associated with the `key`.
+// The `key` will be inserted if it is not in the cedar.
+// It will return ErrInvalidValue, if the updated value < 0 or >= ValueLimit.
+func (da *Cedar) Update(key []byte, value int) error {
+	p := da.get(key, 0, 0)
+	if *p+value < 0 || *p+value >= ValueLimit {
+		return ErrInvalidValue
+	}
+	*p += value
+	return nil
+}
+
+// Delete removes a key-value pair from the cedar.
+// It will return ErrNoPath, if the key has not been added.
+func (da *Cedar) Delete(key []byte) error {
+	// if the path does not exist, or the end is not a leaf, nothing to delete
+	to, err := da.Jump(key, 0)
+	if err != nil {
+		return ErrNoPath
+	}
+
+	if da.Array[to].Value < 0 {
+		base := da.Array[to].base()
+		if da.Array[base].Check == to {
+			to = base
+		}
+	}
+
+	for {
+		from := da.Array[to].Check
+		base := da.Array[from].base()
+		label := byte(to ^ base)
+
+		// if `to` has sibling, remove `to` from the sibling list, then stop
+		if da.Ninfos[to].Sibling != 0 || da.Ninfos[from].Child != label {
+			// delete the label from the child ring first
+			da.popSibling(from, base, label)
+			// then release the current node `to` to the empty node ring
+			da.pushEnode(to)
+			break
+		}
+		// otherwise, just release the current node `to` to the empty node ring
+		da.pushEnode(to)
+		// then check its parent node
+		to = from
+	}
+	return nil
+}
+
+// Get returns the value associated with the given `key`.
+// It is equivalent to
+//		id, err1 = Jump(key)
+//		value, err2 = Value(id)
+// Thus, it may return ErrNoPath or ErrNoValue,
+func (da *Cedar) Get(key []byte) (value int, err error) {
+	to, err := da.Jump(key, 0)
+	if err != nil {
+		return 0, err
+	}
+	return da.Value(to)
+}
+
+// PrefixMatch returns a list of at most `num` nodes which match the prefix of the key.
+// If `num` is 0, it returns all matches.
+// For example, if the following keys were inserted:
+//	id	key
+//	19	abc
+//	23	ab
+//	37	abcd
+// then
+//	PrefixMatch([]byte("abc"), 1) = [ 23 ]				// match ["ab"]
+//	PrefixMatch([]byte("abcd"), 0) = [ 23, 19, 37]		// match ["ab", "abc", "abcd"]
+func (da *Cedar) PrefixMatch(key []byte, num int) (ids []int) {
+	for from, i := 0, 0; i < len(key); i++ {
+		to, err := da.Jump(key[i:i+1], from)
+		if err != nil {
+			break
+		}
+		if _, err := da.Value(to); err == nil {
+			ids = append(ids, to)
+			num--
+			if num == 0 {
+				return
+			}
+		}
+		from = to
+	}
+	return
+}
+
+// PrefixPredict returns a list of at most `num` nodes which has the key as their prefix.
+// These nodes are ordered by their keys.
+// If `num` is 0, it returns all matches.
+// For example, if the following keys were inserted:
+//	id	key
+//	19	abc
+//	23	ab
+//	37	abcd
+// then
+//	PrefixPredict([]byte("ab"), 2) = [ 23, 19 ]			// predict ["ab", "abc"]
+//	PrefixPredict([]byte("ab"), 0) = [ 23, 19, 37 ]		// predict ["ab", "abc", "abcd"]
+func (da *Cedar) PrefixPredict(key []byte, num int) (ids []int) {
+	root, err := da.Jump(key, 0)
+	if err != nil {
+		return
+	}
+	for from, err := da.begin(root); err == nil; from, err = da.next(from, root) {
+		ids = append(ids, from)
+		num--
+		if num == 0 {
+			return
+		}
+	}
+	return
+}
+
+func (da *Cedar) begin(from int) (to int, err error) {
+	for c := da.Ninfos[from].Child; c != 0; {
+		to = da.Array[from].base() ^ int(c)
+		c = da.Ninfos[to].Child
+		from = to
+	}
+	if da.Array[from].base() > 0 {
+		return da.Array[from].base(), nil
+	}
+	return from, nil
+}
+
+func (da *Cedar) next(from int, root int) (to int, err error) {
+	c := da.Ninfos[from].Sibling
+	for c == 0 && from != root && da.Array[from].Check >= 0 {
+		from = da.Array[from].Check
+		c = da.Ninfos[from].Sibling
+	}
+	if from == root {
+		return 0, ErrNoPath
+	}
+	from = da.Array[da.Array[from].Check].base() ^ int(c)
+	return da.begin(from)
+}

+ 407 - 0
vendor/github.com/adamzy/cedar-go/cedar.go

@@ -0,0 +1,407 @@
+package cedar
+
+const ValueLimit = int(^uint(0) >> 1)
+
+type node struct {
+	Value int
+	Check int
+}
+
+func (n *node) base() int { return -(n.Value + 1) }
+
+type ninfo struct {
+	Sibling, Child byte
+}
+
+type block struct {
+	Prev, Next, Num, Reject, Trial, Ehead int
+}
+
+func (b *block) init() {
+	b.Num = 256
+	b.Reject = 257
+}
+
+type Cedar struct {
+	*cedar
+}
+
+type cedar struct {
+	Array    []node
+	Ninfos   []ninfo
+	Blocks   []block
+	Reject   [257]int
+	BheadF   int
+	BheadC   int
+	BheadO   int
+	Capacity int
+	Size     int
+	Ordered  bool
+	MaxTrial int
+}
+
+func New() *Cedar {
+	da := cedar{
+		Array:    make([]node, 256),
+		Ninfos:   make([]ninfo, 256),
+		Blocks:   make([]block, 1),
+		Capacity: 256,
+		Size:     256,
+		Ordered:  true,
+		MaxTrial: 1,
+	}
+
+	da.Array[0] = node{-2, 0}
+	for i := 1; i < 256; i++ {
+		da.Array[i] = node{-(i - 1), -(i + 1)}
+	}
+	da.Array[1].Value = -255
+	da.Array[255].Check = -1
+
+	da.Blocks[0].Ehead = 1
+	da.Blocks[0].init()
+
+	for i := 0; i <= 256; i++ {
+		da.Reject[i] = i + 1
+	}
+
+	return &Cedar{&da}
+}
+
+// Get value by key, insert the key if not exist
+func (da *cedar) get(key []byte, from, pos int) *int {
+	for ; pos < len(key); pos++ {
+		if value := da.Array[from].Value; value >= 0 && value != ValueLimit {
+			to := da.follow(from, 0)
+			da.Array[to].Value = value
+		}
+		from = da.follow(from, key[pos])
+	}
+	to := from
+	if da.Array[from].Value < 0 {
+		to = da.follow(from, 0)
+	}
+	return &da.Array[to].Value
+}
+
+func (da *cedar) follow(from int, label byte) int {
+	base := da.Array[from].base()
+	to := base ^ int(label)
+	if base < 0 || da.Array[to].Check < 0 {
+		hasChild := false
+		if base >= 0 {
+			hasChild = (da.Array[base^int(da.Ninfos[from].Child)].Check == from)
+		}
+		to = da.popEnode(base, label, from)
+		da.pushSibling(from, to^int(label), label, hasChild)
+	} else if da.Array[to].Check != from {
+		to = da.resolve(from, base, label)
+	} else if da.Array[to].Check == from {
+	} else {
+		panic("cedar: internal error, should not be here")
+	}
+	return to
+}
+
+func (da *cedar) popBlock(bi int, head_in *int, last bool) {
+	if last {
+		*head_in = 0
+	} else {
+		b := &da.Blocks[bi]
+		da.Blocks[b.Prev].Next = b.Next
+		da.Blocks[b.Next].Prev = b.Prev
+		if bi == *head_in {
+			*head_in = b.Next
+		}
+	}
+}
+
+func (da *cedar) pushBlock(bi int, head_out *int, empty bool) {
+	b := &da.Blocks[bi]
+	if empty {
+		*head_out, b.Prev, b.Next = bi, bi, bi
+	} else {
+		tail_out := &da.Blocks[*head_out].Prev
+		b.Prev = *tail_out
+		b.Next = *head_out
+		*head_out, *tail_out, da.Blocks[*tail_out].Next = bi, bi, bi
+	}
+}
+
+func (da *cedar) addBlock() int {
+	if da.Size == da.Capacity {
+		da.Capacity *= 2
+
+		oldArray := da.Array
+		da.Array = make([]node, da.Capacity)
+		copy(da.Array, oldArray)
+
+		oldNinfo := da.Ninfos
+		da.Ninfos = make([]ninfo, da.Capacity)
+		copy(da.Ninfos, oldNinfo)
+
+		oldBlock := da.Blocks
+		da.Blocks = make([]block, da.Capacity>>8)
+		copy(da.Blocks, oldBlock)
+	}
+
+	da.Blocks[da.Size>>8].init()
+	da.Blocks[da.Size>>8].Ehead = da.Size
+
+	da.Array[da.Size] = node{-(da.Size + 255), -(da.Size + 1)}
+	for i := da.Size + 1; i < da.Size+255; i++ {
+		da.Array[i] = node{-(i - 1), -(i + 1)}
+	}
+	da.Array[da.Size+255] = node{-(da.Size + 254), -da.Size}
+
+	da.pushBlock(da.Size>>8, &da.BheadO, da.BheadO == 0)
+	da.Size += 256
+	return da.Size>>8 - 1
+}
+
+func (da *cedar) transferBlock(bi int, head_in, head_out *int) {
+	da.popBlock(bi, head_in, bi == da.Blocks[bi].Next)
+	da.pushBlock(bi, head_out, *head_out == 0 && da.Blocks[bi].Num != 0)
+}
+
+func (da *cedar) popEnode(base int, label byte, from int) int {
+	e := base ^ int(label)
+	if base < 0 {
+		e = da.findPlace()
+	}
+	bi := e >> 8
+	n := &da.Array[e]
+	b := &da.Blocks[bi]
+	b.Num--
+	if b.Num == 0 {
+		if bi != 0 {
+			da.transferBlock(bi, &da.BheadC, &da.BheadF)
+		}
+	} else {
+		da.Array[-n.Value].Check = n.Check
+		da.Array[-n.Check].Value = n.Value
+		if e == b.Ehead {
+			b.Ehead = -n.Check
+		}
+		if bi != 0 && b.Num == 1 && b.Trial != da.MaxTrial {
+			da.transferBlock(bi, &da.BheadO, &da.BheadC)
+		}
+	}
+	n.Value = ValueLimit
+	n.Check = from
+	if base < 0 {
+		da.Array[from].Value = -(e ^ int(label)) - 1
+	}
+	return e
+}
+
+func (da *cedar) pushEnode(e int) {
+	bi := e >> 8
+	b := &da.Blocks[bi]
+	b.Num++
+	if b.Num == 1 {
+		b.Ehead = e
+		da.Array[e] = node{-e, -e}
+		if bi != 0 {
+			da.transferBlock(bi, &da.BheadF, &da.BheadC)
+		}
+	} else {
+		prev := b.Ehead
+		next := -da.Array[prev].Check
+		da.Array[e] = node{-prev, -next}
+		da.Array[prev].Check = -e
+		da.Array[next].Value = -e
+		if b.Num == 2 || b.Trial == da.MaxTrial {
+			if bi != 0 {
+				da.transferBlock(bi, &da.BheadC, &da.BheadO)
+			}
+		}
+		b.Trial = 0
+	}
+	if b.Reject < da.Reject[b.Num] {
+		b.Reject = da.Reject[b.Num]
+	}
+	da.Ninfos[e] = ninfo{}
+}
+
+// hasChild: wherether the `from` node has children
+func (da *cedar) pushSibling(from, base int, label byte, hasChild bool) {
+	c := &da.Ninfos[from].Child
+	keepOrder := *c == 0
+	if da.Ordered {
+		keepOrder = label > *c
+	}
+	if hasChild && keepOrder {
+		c = &da.Ninfos[base^int(*c)].Sibling
+		for da.Ordered && *c != 0 && *c < label {
+			c = &da.Ninfos[base^int(*c)].Sibling
+		}
+	}
+	da.Ninfos[base^int(label)].Sibling = *c
+	*c = label
+}
+
+func (da *cedar) popSibling(from, base int, label byte) {
+	c := &da.Ninfos[from].Child
+	for *c != label {
+		c = &da.Ninfos[base^int(*c)].Sibling
+	}
+	*c = da.Ninfos[base^int(*c)].Sibling
+}
+
+func (da *cedar) consult(base_n, base_p int, c_n, c_p byte) bool {
+	c_n = da.Ninfos[base_n^int(c_n)].Sibling
+	c_p = da.Ninfos[base_p^int(c_p)].Sibling
+	for c_n != 0 && c_p != 0 {
+		c_n = da.Ninfos[base_n^int(c_n)].Sibling
+		c_p = da.Ninfos[base_p^int(c_p)].Sibling
+	}
+	return c_p != 0
+}
+
+func (da *cedar) setChild(base int, c byte, label byte, flag bool) []byte {
+	child := make([]byte, 0, 257)
+	if c == 0 {
+		child = append(child, c)
+		c = da.Ninfos[base^int(c)].Sibling
+	}
+	if da.Ordered {
+		for c != 0 && c <= label {
+			child = append(child, c)
+			c = da.Ninfos[base^int(c)].Sibling
+		}
+	}
+	if flag {
+		child = append(child, label)
+	}
+	for c != 0 {
+		child = append(child, c)
+		c = da.Ninfos[base^int(c)].Sibling
+	}
+	return child
+}
+
+func (da *cedar) findPlace() int {
+	if da.BheadC != 0 {
+		return da.Blocks[da.BheadC].Ehead
+	}
+	if da.BheadO != 0 {
+		return da.Blocks[da.BheadO].Ehead
+	}
+	return da.addBlock() << 8
+}
+
+func (da *cedar) findPlaces(child []byte) int {
+	bi := da.BheadO
+	if bi != 0 {
+		bz := da.Blocks[da.BheadO].Prev
+		nc := len(child)
+		for {
+			b := &da.Blocks[bi]
+			if b.Num >= nc && nc < b.Reject {
+				for e := b.Ehead; ; {
+					base := e ^ int(child[0])
+					for i := 0; da.Array[base^int(child[i])].Check < 0; i++ {
+						if i == len(child)-1 {
+							b.Ehead = e
+							return e
+						}
+					}
+					e = -da.Array[e].Check
+					if e == b.Ehead {
+						break
+					}
+				}
+			}
+			b.Reject = nc
+			if b.Reject < da.Reject[b.Num] {
+				da.Reject[b.Num] = b.Reject
+			}
+			bi_ := b.Next
+			b.Trial++
+			if b.Trial == da.MaxTrial {
+				da.transferBlock(bi, &da.BheadO, &da.BheadC)
+			}
+			if bi == bz {
+				break
+			}
+			bi = bi_
+		}
+	}
+	return da.addBlock() << 8
+}
+
+func (da *cedar) resolve(from_n, base_n int, label_n byte) int {
+	to_pn := base_n ^ int(label_n)
+	from_p := da.Array[to_pn].Check
+	base_p := da.Array[from_p].base()
+
+	flag := da.consult(base_n, base_p, da.Ninfos[from_n].Child, da.Ninfos[from_p].Child)
+	var children []byte
+	if flag {
+		children = da.setChild(base_n, da.Ninfos[from_n].Child, label_n, true)
+	} else {
+		children = da.setChild(base_p, da.Ninfos[from_p].Child, 255, false)
+	}
+	var base int
+	if len(children) == 1 {
+		base = da.findPlace()
+	} else {
+		base = da.findPlaces(children)
+	}
+	base ^= int(children[0])
+	var from int
+	var base_ int
+	if flag {
+		from = from_n
+		base_ = base_n
+	} else {
+		from = from_p
+		base_ = base_p
+	}
+	if flag && children[0] == label_n {
+		da.Ninfos[from].Child = label_n
+	}
+	da.Array[from].Value = -base - 1
+	for i := 0; i < len(children); i++ {
+		to := da.popEnode(base, children[i], from)
+		to_ := base_ ^ int(children[i])
+		if i == len(children)-1 {
+			da.Ninfos[to].Sibling = 0
+		} else {
+			da.Ninfos[to].Sibling = children[i+1]
+		}
+		if flag && to_ == to_pn { // new node has no child
+			continue
+		}
+		n := &da.Array[to]
+		n_ := &da.Array[to_]
+		n.Value = n_.Value
+		if n.Value < 0 && children[i] != 0 {
+			// this node has children, fix their check
+			c := da.Ninfos[to_].Child
+			da.Ninfos[to].Child = c
+			da.Array[n.base()^int(c)].Check = to
+			c = da.Ninfos[n.base()^int(c)].Sibling
+			for c != 0 {
+				da.Array[n.base()^int(c)].Check = to
+				c = da.Ninfos[n.base()^int(c)].Sibling
+			}
+		}
+		if !flag && to_ == from_n { // parent node moved
+			from_n = to
+		}
+		if !flag && to_ == to_pn {
+			da.pushSibling(from_n, to_pn^int(label_n), label_n, true)
+			da.Ninfos[to_].Child = 0
+			n_.Value = ValueLimit
+			n_.Check = from_n
+		} else {
+			da.pushEnode(to_)
+		}
+	}
+	if flag {
+		return base ^ int(label_n)
+	}
+	return to_pn
+}

+ 12 - 0
vendor/github.com/adamzy/cedar-go/doc.go

@@ -0,0 +1,12 @@
+// Package cedar-go implements double-array trie.
+//
+// It is a golang port of cedar (http://www.tkl.iis.u-tokyo.ac.jp/~ynaga/cedar) which is written in C++ by Naoki Yoshinaga.
+// Currently cedar-go implements the `reduced` verion of cedar.
+// This package is not thread safe if there is one goroutine doing
+// insertions or deletions.
+//
+// Note
+//
+// key must be `[]byte` without zero items,
+// while value must be integer in the range [0, 2<<63-2] or [0, 2<<31-2] depends on the platform.
+package cedar

+ 11 - 0
vendor/github.com/adamzy/cedar-go/errors.go

@@ -0,0 +1,11 @@
+package cedar
+
+import "errors"
+
+var (
+	ErrInvalidDataType = errors.New("cedar: invalid datatype")
+	ErrInvalidValue    = errors.New("cedar: invalid value")
+	ErrInvalidKey      = errors.New("cedar: invalid key")
+	ErrNoPath          = errors.New("cedar: no path")
+	ErrNoValue         = errors.New("cedar: no value")
+)

+ 63 - 0
vendor/github.com/adamzy/cedar-go/io.go

@@ -0,0 +1,63 @@
+package cedar
+
+import (
+	"bufio"
+	"encoding/gob"
+	"encoding/json"
+	"io"
+	"os"
+)
+
+// Save saves the cedar to an io.Writer,
+// where dataType is either "json" or "gob".
+func (da *Cedar) Save(out io.Writer, dataType string) error {
+	switch dataType {
+	case "gob", "GOB":
+		dataEecoder := gob.NewEncoder(out)
+		return dataEecoder.Encode(da.cedar)
+	case "json", "JSON":
+		dataEecoder := json.NewEncoder(out)
+		return dataEecoder.Encode(da.cedar)
+	}
+	return ErrInvalidDataType
+}
+
+// SaveToFile saves the cedar to a file,
+// where dataType is either "json" or "gob".
+func (da *Cedar) SaveToFile(fileName string, dataType string) error {
+	file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY, 0666)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+	out := bufio.NewWriter(file)
+	defer out.Flush()
+	da.Save(out, dataType)
+	return nil
+}
+
+// Load loads the cedar from an io.Writer,
+// where dataType is either "json" or "gob".
+func (da *Cedar) Load(in io.Reader, dataType string) error {
+	switch dataType {
+	case "gob", "GOB":
+		dataDecoder := gob.NewDecoder(in)
+		return dataDecoder.Decode(da.cedar)
+	case "json", "JSON":
+		dataDecoder := json.NewDecoder(in)
+		return dataDecoder.Decode(da.cedar)
+	}
+	return ErrInvalidDataType
+}
+
+// LoadFromFile loads the cedar from a file,
+// where dataType is either "json" or "gob".
+func (da *Cedar) LoadFromFile(fileName string, dataType string) error {
+	file, err := os.OpenFile(fileName, os.O_RDONLY, 0600)
+	defer file.Close()
+	if err != nil {
+		return err
+	}
+	in := bufio.NewReader(file)
+	return da.Load(in, dataType)
+}

+ 17 - 0
vendor/github.com/astaxie/beego/.github/ISSUE_TEMPLATE

@@ -0,0 +1,17 @@
+Please answer these questions before submitting your issue. Thanks!
+
+1. What version of Go and beego are you using (`bee version`)?
+
+
+2. What operating system and processor architecture are you using (`go env`)?
+
+
+3. What did you do?
+If possible, provide a recipe for reproducing the error.
+A complete runnable program is good.
+
+
+4. What did you expect to see?
+
+
+5. What did you see instead?

+ 73 - 0
vendor/github.com/astaxie/beego/admin_test.go

@@ -0,0 +1,73 @@
+package beego
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestList_01(t *testing.T) {
+	m := make(map[string]interface{})
+	list("BConfig", BConfig, m)
+	t.Log(m)
+	om := oldMap()
+	for k, v := range om {
+		if fmt.Sprint(m[k]) != fmt.Sprint(v) {
+			t.Log(k, "old-key", v, "new-key", m[k])
+			t.FailNow()
+		}
+	}
+}
+
+func oldMap() map[string]interface{} {
+	m := make(map[string]interface{})
+	m["BConfig.AppName"] = BConfig.AppName
+	m["BConfig.RunMode"] = BConfig.RunMode
+	m["BConfig.RouterCaseSensitive"] = BConfig.RouterCaseSensitive
+	m["BConfig.ServerName"] = BConfig.ServerName
+	m["BConfig.RecoverPanic"] = BConfig.RecoverPanic
+	m["BConfig.CopyRequestBody"] = BConfig.CopyRequestBody
+	m["BConfig.EnableGzip"] = BConfig.EnableGzip
+	m["BConfig.MaxMemory"] = BConfig.MaxMemory
+	m["BConfig.EnableErrorsShow"] = BConfig.EnableErrorsShow
+	m["BConfig.Listen.Graceful"] = BConfig.Listen.Graceful
+	m["BConfig.Listen.ServerTimeOut"] = BConfig.Listen.ServerTimeOut
+	m["BConfig.Listen.ListenTCP4"] = BConfig.Listen.ListenTCP4
+	m["BConfig.Listen.EnableHTTP"] = BConfig.Listen.EnableHTTP
+	m["BConfig.Listen.HTTPAddr"] = BConfig.Listen.HTTPAddr
+	m["BConfig.Listen.HTTPPort"] = BConfig.Listen.HTTPPort
+	m["BConfig.Listen.EnableHTTPS"] = BConfig.Listen.EnableHTTPS
+	m["BConfig.Listen.HTTPSAddr"] = BConfig.Listen.HTTPSAddr
+	m["BConfig.Listen.HTTPSPort"] = BConfig.Listen.HTTPSPort
+	m["BConfig.Listen.HTTPSCertFile"] = BConfig.Listen.HTTPSCertFile
+	m["BConfig.Listen.HTTPSKeyFile"] = BConfig.Listen.HTTPSKeyFile
+	m["BConfig.Listen.EnableAdmin"] = BConfig.Listen.EnableAdmin
+	m["BConfig.Listen.AdminAddr"] = BConfig.Listen.AdminAddr
+	m["BConfig.Listen.AdminPort"] = BConfig.Listen.AdminPort
+	m["BConfig.Listen.EnableFcgi"] = BConfig.Listen.EnableFcgi
+	m["BConfig.Listen.EnableStdIo"] = BConfig.Listen.EnableStdIo
+	m["BConfig.WebConfig.AutoRender"] = BConfig.WebConfig.AutoRender
+	m["BConfig.WebConfig.EnableDocs"] = BConfig.WebConfig.EnableDocs
+	m["BConfig.WebConfig.FlashName"] = BConfig.WebConfig.FlashName
+	m["BConfig.WebConfig.FlashSeparator"] = BConfig.WebConfig.FlashSeparator
+	m["BConfig.WebConfig.DirectoryIndex"] = BConfig.WebConfig.DirectoryIndex
+	m["BConfig.WebConfig.StaticDir"] = BConfig.WebConfig.StaticDir
+	m["BConfig.WebConfig.StaticExtensionsToGzip"] = BConfig.WebConfig.StaticExtensionsToGzip
+	m["BConfig.WebConfig.TemplateLeft"] = BConfig.WebConfig.TemplateLeft
+	m["BConfig.WebConfig.TemplateRight"] = BConfig.WebConfig.TemplateRight
+	m["BConfig.WebConfig.ViewsPath"] = BConfig.WebConfig.ViewsPath
+	m["BConfig.WebConfig.EnableXSRF"] = BConfig.WebConfig.EnableXSRF
+	m["BConfig.WebConfig.XSRFExpire"] = BConfig.WebConfig.XSRFExpire
+	m["BConfig.WebConfig.Session.SessionOn"] = BConfig.WebConfig.Session.SessionOn
+	m["BConfig.WebConfig.Session.SessionProvider"] = BConfig.WebConfig.Session.SessionProvider
+	m["BConfig.WebConfig.Session.SessionName"] = BConfig.WebConfig.Session.SessionName
+	m["BConfig.WebConfig.Session.SessionGCMaxLifetime"] = BConfig.WebConfig.Session.SessionGCMaxLifetime
+	m["BConfig.WebConfig.Session.SessionProviderConfig"] = BConfig.WebConfig.Session.SessionProviderConfig
+	m["BConfig.WebConfig.Session.SessionCookieLifeTime"] = BConfig.WebConfig.Session.SessionCookieLifeTime
+	m["BConfig.WebConfig.Session.SessionAutoSetCookie"] = BConfig.WebConfig.Session.SessionAutoSetCookie
+	m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain
+	m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly
+	m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs
+	m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
+	m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
+	return m
+}

+ 59 - 0
vendor/github.com/astaxie/beego/cache/README.md

@@ -0,0 +1,59 @@
+## cache
+cache is a Go cache manager. It can use many cache adapters. The repo is inspired by `database/sql` .
+
+
+## How to install?
+
+	go get github.com/astaxie/beego/cache
+
+
+## What adapters are supported?
+
+As of now this cache support memory, Memcache and Redis.
+
+
+## How to use it?
+
+First you must import it
+
+	import (
+		"github.com/astaxie/beego/cache"
+	)
+
+Then init a Cache (example with memory adapter)
+
+	bm, err := cache.NewCache("memory", `{"interval":60}`)	
+
+Use it like this:	
+	
+	bm.Put("astaxie", 1, 10 * time.Second)
+	bm.Get("astaxie")
+	bm.IsExist("astaxie")
+	bm.Delete("astaxie")
+
+
+## Memory adapter
+
+Configure memory adapter like this:
+
+	{"interval":60}
+
+interval means the gc time. The cache will check at each time interval, whether item has expired.
+
+
+## Memcache adapter
+
+Memcache adapter use the [gomemcache](http://github.com/bradfitz/gomemcache) client.
+
+Configure like this:
+
+	{"conn":"127.0.0.1:11211"}
+
+
+## Redis adapter
+
+Redis adapter use the [redigo](http://github.com/garyburd/redigo) client.
+
+Configure like this:
+
+	{"conn":":6039"}

+ 103 - 0
vendor/github.com/astaxie/beego/cache/cache.go

@@ -0,0 +1,103 @@
+// 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 cache provide a Cache interface and some implemetn engine
+// Usage:
+//
+// import(
+//   "github.com/astaxie/beego/cache"
+// )
+//
+// bm, err := cache.NewCache("memory", `{"interval":60}`)
+//
+// Use it like this:
+//
+//	bm.Put("astaxie", 1, 10 * time.Second)
+//	bm.Get("astaxie")
+//	bm.IsExist("astaxie")
+//	bm.Delete("astaxie")
+//
+//  more docs http://beego.me/docs/module/cache.md
+package cache
+
+import (
+	"fmt"
+	"time"
+)
+
+// Cache interface contains all behaviors for cache adapter.
+// usage:
+//	cache.Register("file",cache.NewFileCache) // this operation is run in init method of file.go.
+//	c,err := cache.NewCache("file","{....}")
+//	c.Put("key",value, 3600 * time.Second)
+//	v := c.Get("key")
+//
+//	c.Incr("counter")  // now is 1
+//	c.Incr("counter")  // now is 2
+//	count := c.Get("counter").(int)
+type Cache interface {
+	// get cached value by key.
+	Get(key string) interface{}
+	// GetMulti is a batch version of Get.
+	GetMulti(keys []string) []interface{}
+	// set cached value with key and expire time.
+	Put(key string, val interface{}, timeout time.Duration) error
+	// delete cached value by key.
+	Delete(key string) error
+	// increase cached int value by key, as a counter.
+	Incr(key string) error
+	// decrease cached int value by key, as a counter.
+	Decr(key string) error
+	// check if cached value exists or not.
+	IsExist(key string) bool
+	// clear all cache.
+	ClearAll() error
+	// start gc routine based on config string settings.
+	StartAndGC(config string) error
+}
+
+// Instance is a function create a new Cache Instance
+type Instance func() Cache
+
+var adapters = make(map[string]Instance)
+
+// Register makes a cache adapter available by the adapter name.
+// If Register is called twice with the same name or if driver is nil,
+// it panics.
+func Register(name string, adapter Instance) {
+	if adapter == nil {
+		panic("cache: Register adapter is nil")
+	}
+	if _, ok := adapters[name]; ok {
+		panic("cache: Register called twice for adapter " + name)
+	}
+	adapters[name] = adapter
+}
+
+// NewCache Create a new cache driver by adapter name and config string.
+// config need to be correct JSON as string: {"interval":360}.
+// it will start gc automatically.
+func NewCache(adapterName, config string) (adapter Cache, err error) {
+	instanceFunc, ok := adapters[adapterName]
+	if !ok {
+		err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
+		return
+	}
+	adapter = instanceFunc()
+	err = adapter.StartAndGC(config)
+	if err != nil {
+		adapter = nil
+	}
+	return
+}

+ 168 - 0
vendor/github.com/astaxie/beego/cache/cache_test.go

@@ -0,0 +1,168 @@
+// 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 cache
+
+import (
+	"os"
+	"testing"
+	"time"
+)
+
+func TestCache(t *testing.T) {
+	bm, err := NewCache("memory", `{"interval":20}`)
+	if err != nil {
+		t.Error("init err")
+	}
+	timeoutDuration := 10 * time.Second
+	if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if !bm.IsExist("astaxie") {
+		t.Error("check err")
+	}
+
+	if v := bm.Get("astaxie"); v.(int) != 1 {
+		t.Error("get err")
+	}
+
+	time.Sleep(30 * time.Second)
+
+	if bm.IsExist("astaxie") {
+		t.Error("check err")
+	}
+
+	if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+
+	if err = bm.Incr("astaxie"); err != nil {
+		t.Error("Incr Error", err)
+	}
+
+	if v := bm.Get("astaxie"); v.(int) != 2 {
+		t.Error("get err")
+	}
+
+	if err = bm.Decr("astaxie"); err != nil {
+		t.Error("Decr Error", err)
+	}
+
+	if v := bm.Get("astaxie"); v.(int) != 1 {
+		t.Error("get err")
+	}
+	bm.Delete("astaxie")
+	if bm.IsExist("astaxie") {
+		t.Error("delete err")
+	}
+
+	//test GetMulti
+	if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if !bm.IsExist("astaxie") {
+		t.Error("check err")
+	}
+	if v := bm.Get("astaxie"); v.(string) != "author" {
+		t.Error("get err")
+	}
+
+	if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if !bm.IsExist("astaxie1") {
+		t.Error("check err")
+	}
+
+	vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
+	if len(vv) != 2 {
+		t.Error("GetMulti ERROR")
+	}
+	if vv[0].(string) != "author" {
+		t.Error("GetMulti ERROR")
+	}
+	if vv[1].(string) != "author1" {
+		t.Error("GetMulti ERROR")
+	}
+}
+
+func TestFileCache(t *testing.T) {
+	bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)
+	if err != nil {
+		t.Error("init err")
+	}
+	timeoutDuration := 10 * time.Second
+	if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if !bm.IsExist("astaxie") {
+		t.Error("check err")
+	}
+
+	if v := bm.Get("astaxie"); v.(int) != 1 {
+		t.Error("get err")
+	}
+
+	if err = bm.Incr("astaxie"); err != nil {
+		t.Error("Incr Error", err)
+	}
+
+	if v := bm.Get("astaxie"); v.(int) != 2 {
+		t.Error("get err")
+	}
+
+	if err = bm.Decr("astaxie"); err != nil {
+		t.Error("Decr Error", err)
+	}
+
+	if v := bm.Get("astaxie"); v.(int) != 1 {
+		t.Error("get err")
+	}
+	bm.Delete("astaxie")
+	if bm.IsExist("astaxie") {
+		t.Error("delete err")
+	}
+
+	//test string
+	if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if !bm.IsExist("astaxie") {
+		t.Error("check err")
+	}
+	if v := bm.Get("astaxie"); v.(string) != "author" {
+		t.Error("get err")
+	}
+
+	//test GetMulti
+	if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if !bm.IsExist("astaxie1") {
+		t.Error("check err")
+	}
+
+	vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
+	if len(vv) != 2 {
+		t.Error("GetMulti ERROR")
+	}
+	if vv[0].(string) != "author" {
+		t.Error("GetMulti ERROR")
+	}
+	if vv[1].(string) != "author1" {
+		t.Error("GetMulti ERROR")
+	}
+
+	os.RemoveAll("cache")
+}

+ 100 - 0
vendor/github.com/astaxie/beego/cache/conv.go

@@ -0,0 +1,100 @@
+// 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 cache
+
+import (
+	"fmt"
+	"strconv"
+)
+
+// GetString convert interface to string.
+func GetString(v interface{}) string {
+	switch result := v.(type) {
+	case string:
+		return result
+	case []byte:
+		return string(result)
+	default:
+		if v != nil {
+			return fmt.Sprintf("%v", result)
+		}
+	}
+	return ""
+}
+
+// GetInt convert interface to int.
+func GetInt(v interface{}) int {
+	switch result := v.(type) {
+	case int:
+		return result
+	case int32:
+		return int(result)
+	case int64:
+		return int(result)
+	default:
+		if d := GetString(v); d != "" {
+			value, _ := strconv.Atoi(d)
+			return value
+		}
+	}
+	return 0
+}
+
+// GetInt64 convert interface to int64.
+func GetInt64(v interface{}) int64 {
+	switch result := v.(type) {
+	case int:
+		return int64(result)
+	case int32:
+		return int64(result)
+	case int64:
+		return result
+	default:
+
+		if d := GetString(v); d != "" {
+			value, _ := strconv.ParseInt(d, 10, 64)
+			return value
+		}
+	}
+	return 0
+}
+
+// GetFloat64 convert interface to float64.
+func GetFloat64(v interface{}) float64 {
+	switch result := v.(type) {
+	case float64:
+		return result
+	default:
+		if d := GetString(v); d != "" {
+			value, _ := strconv.ParseFloat(d, 64)
+			return value
+		}
+	}
+	return 0
+}
+
+// GetBool convert interface to bool.
+func GetBool(v interface{}) bool {
+	switch result := v.(type) {
+	case bool:
+		return result
+	default:
+		if d := GetString(v); d != "" {
+			value, _ := strconv.ParseBool(d)
+			return value
+		}
+	}
+	return false
+}

+ 143 - 0
vendor/github.com/astaxie/beego/cache/conv_test.go

@@ -0,0 +1,143 @@
+// 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 cache
+
+import (
+	"testing"
+)
+
+func TestGetString(t *testing.T) {
+	var t1 = "test1"
+	if "test1" != GetString(t1) {
+		t.Error("get string from string error")
+	}
+	var t2 = []byte("test2")
+	if "test2" != GetString(t2) {
+		t.Error("get string from byte array error")
+	}
+	var t3 = 1
+	if "1" != GetString(t3) {
+		t.Error("get string from int error")
+	}
+	var t4 int64 = 1
+	if "1" != GetString(t4) {
+		t.Error("get string from int64 error")
+	}
+	var t5 = 1.1
+	if "1.1" != GetString(t5) {
+		t.Error("get string from float64 error")
+	}
+
+	if "" != GetString(nil) {
+		t.Error("get string from nil error")
+	}
+}
+
+func TestGetInt(t *testing.T) {
+	var t1 = 1
+	if 1 != GetInt(t1) {
+		t.Error("get int from int error")
+	}
+	var t2 int32 = 32
+	if 32 != GetInt(t2) {
+		t.Error("get int from int32 error")
+	}
+	var t3 int64 = 64
+	if 64 != GetInt(t3) {
+		t.Error("get int from int64 error")
+	}
+	var t4 = "128"
+	if 128 != GetInt(t4) {
+		t.Error("get int from num string error")
+	}
+	if 0 != GetInt(nil) {
+		t.Error("get int from nil error")
+	}
+}
+
+func TestGetInt64(t *testing.T) {
+	var i int64 = 1
+	var t1 = 1
+	if i != GetInt64(t1) {
+		t.Error("get int64 from int error")
+	}
+	var t2 int32 = 1
+	if i != GetInt64(t2) {
+		t.Error("get int64 from int32 error")
+	}
+	var t3 int64 = 1
+	if i != GetInt64(t3) {
+		t.Error("get int64 from int64 error")
+	}
+	var t4 = "1"
+	if i != GetInt64(t4) {
+		t.Error("get int64 from num string error")
+	}
+	if 0 != GetInt64(nil) {
+		t.Error("get int64 from nil")
+	}
+}
+
+func TestGetFloat64(t *testing.T) {
+	var f = 1.11
+	var t1 float32 = 1.11
+	if f != GetFloat64(t1) {
+		t.Error("get float64 from float32 error")
+	}
+	var t2 = 1.11
+	if f != GetFloat64(t2) {
+		t.Error("get float64 from float64 error")
+	}
+	var t3 = "1.11"
+	if f != GetFloat64(t3) {
+		t.Error("get float64 from string error")
+	}
+
+	var f2 float64 = 1
+	var t4 = 1
+	if f2 != GetFloat64(t4) {
+		t.Error("get float64 from int error")
+	}
+
+	if 0 != GetFloat64(nil) {
+		t.Error("get float64 from nil error")
+	}
+}
+
+func TestGetBool(t *testing.T) {
+	var t1 = true
+	if true != GetBool(t1) {
+		t.Error("get bool from bool error")
+	}
+	var t2 = "true"
+	if true != GetBool(t2) {
+		t.Error("get bool from string error")
+	}
+	if false != GetBool(nil) {
+		t.Error("get bool from nil error")
+	}
+}
+
+func byteArrayEquals(a []byte, b []byte) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if v != b[i] {
+			return false
+		}
+	}
+	return true
+}

+ 255 - 0
vendor/github.com/astaxie/beego/cache/file.go

@@ -0,0 +1,255 @@
+// 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 cache
+
+import (
+	"bytes"
+	"crypto/md5"
+	"encoding/gob"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"strconv"
+	"time"
+)
+
+// FileCacheItem is basic unit of file cache adapter.
+// it contains data and expire time.
+type FileCacheItem struct {
+	Data       interface{}
+	Lastaccess time.Time
+	Expired    time.Time
+}
+
+// FileCache Config
+var (
+	FileCachePath           = "cache"     // cache directory
+	FileCacheFileSuffix     = ".bin"      // cache file suffix
+	FileCacheDirectoryLevel = 2           // cache file deep level if auto generated cache files.
+	FileCacheEmbedExpiry    time.Duration // cache expire time, default is no expire forever.
+)
+
+// FileCache is cache adapter for file storage.
+type FileCache struct {
+	CachePath      string
+	FileSuffix     string
+	DirectoryLevel int
+	EmbedExpiry    int
+}
+
+// NewFileCache Create new file cache with no config.
+// the level and expiry need set in method StartAndGC as config string.
+func NewFileCache() Cache {
+	//    return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix}
+	return &FileCache{}
+}
+
+// StartAndGC will start and begin gc for file cache.
+// 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)
+	if _, ok := cfg["CachePath"]; !ok {
+		cfg["CachePath"] = FileCachePath
+	}
+	if _, ok := cfg["FileSuffix"]; !ok {
+		cfg["FileSuffix"] = FileCacheFileSuffix
+	}
+	if _, ok := cfg["DirectoryLevel"]; !ok {
+		cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel)
+	}
+	if _, ok := cfg["EmbedExpiry"]; !ok {
+		cfg["EmbedExpiry"] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10)
+	}
+	fc.CachePath = cfg["CachePath"]
+	fc.FileSuffix = cfg["FileSuffix"]
+	fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"])
+	fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"])
+
+	fc.Init()
+	return nil
+}
+
+// Init will make new dir for file cache if not exist.
+func (fc *FileCache) Init() {
+	if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
+		_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
+	}
+}
+
+// get cached file name. it's md5 encoded.
+func (fc *FileCache) getCacheFileName(key string) string {
+	m := md5.New()
+	io.WriteString(m, key)
+	keyMd5 := hex.EncodeToString(m.Sum(nil))
+	cachePath := fc.CachePath
+	switch fc.DirectoryLevel {
+	case 2:
+		cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4])
+	case 1:
+		cachePath = filepath.Join(cachePath, keyMd5[0:2])
+	}
+
+	if ok, _ := exists(cachePath); !ok { // todo : error handle
+		_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle
+	}
+
+	return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix))
+}
+
+// Get value from file cache.
+// if non-exist or expired, return empty string.
+func (fc *FileCache) Get(key string) interface{} {
+	fileData, err := FileGetContents(fc.getCacheFileName(key))
+	if err != nil {
+		return ""
+	}
+	var to FileCacheItem
+	GobDecode(fileData, &to)
+	if to.Expired.Before(time.Now()) {
+		return ""
+	}
+	return to.Data
+}
+
+// GetMulti gets values from file cache.
+// if non-exist or expired, return empty string.
+func (fc *FileCache) GetMulti(keys []string) []interface{} {
+	var rc []interface{}
+	for _, key := range keys {
+		rc = append(rc, fc.Get(key))
+	}
+	return rc
+}
+
+// Put value into file cache.
+// timeout means how long to keep this file, unit of ms.
+// if timeout equals FileCacheEmbedExpiry(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 {
+		item.Expired = time.Now().Add((86400 * 365 * 10) * time.Second) // ten years
+	} else {
+		item.Expired = time.Now().Add(timeout)
+	}
+	item.Lastaccess = time.Now()
+	data, err := GobEncode(item)
+	if err != nil {
+		return err
+	}
+	return FilePutContents(fc.getCacheFileName(key), data)
+}
+
+// Delete file cache value.
+func (fc *FileCache) Delete(key string) error {
+	filename := fc.getCacheFileName(key)
+	if ok, _ := exists(filename); ok {
+		return os.Remove(filename)
+	}
+	return nil
+}
+
+// Incr will increase cached int value.
+// fc value is saving forever unless Delete.
+func (fc *FileCache) Incr(key string) error {
+	data := fc.Get(key)
+	var incr int
+	if reflect.TypeOf(data).Name() != "int" {
+		incr = 0
+	} else {
+		incr = data.(int) + 1
+	}
+	fc.Put(key, incr, FileCacheEmbedExpiry)
+	return nil
+}
+
+// Decr will decrease cached int value.
+func (fc *FileCache) Decr(key string) error {
+	data := fc.Get(key)
+	var decr int
+	if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 {
+		decr = 0
+	} else {
+		decr = data.(int) - 1
+	}
+	fc.Put(key, decr, FileCacheEmbedExpiry)
+	return nil
+}
+
+// IsExist check value is exist.
+func (fc *FileCache) IsExist(key string) bool {
+	ret, _ := exists(fc.getCacheFileName(key))
+	return ret
+}
+
+// ClearAll will clean cached files.
+// not implemented.
+func (fc *FileCache) ClearAll() error {
+	return nil
+}
+
+// check file exist.
+func exists(path string) (bool, error) {
+	_, err := os.Stat(path)
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, err
+}
+
+// FileGetContents Get bytes to file.
+// if non-exist, create this file.
+func FileGetContents(filename string) (data []byte, e error) {
+	return ioutil.ReadFile(filename)
+}
+
+// FilePutContents Put bytes to file.
+// if non-exist, create this file.
+func FilePutContents(filename string, content []byte) error {
+	return ioutil.WriteFile(filename, content, os.ModePerm)
+}
+
+// GobEncode Gob encodes file cache item.
+func GobEncode(data interface{}) ([]byte, error) {
+	buf := bytes.NewBuffer(nil)
+	enc := gob.NewEncoder(buf)
+	err := enc.Encode(data)
+	if err != nil {
+		return nil, err
+	}
+	return buf.Bytes(), err
+}
+
+// GobDecode Gob decodes file cache item.
+func GobDecode(data []byte, to *FileCacheItem) error {
+	buf := bytes.NewBuffer(data)
+	dec := gob.NewDecoder(buf)
+	return dec.Decode(&to)
+}
+
+func init() {
+	Register("file", NewFileCache)
+}

+ 191 - 0
vendor/github.com/astaxie/beego/cache/memcache/memcache.go

@@ -0,0 +1,191 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package memcache for cache provider
+//
+// depend on github.com/bradfitz/gomemcache/memcache
+//
+// go install github.com/bradfitz/gomemcache/memcache
+//
+// Usage:
+// import(
+//   _ "github.com/astaxie/beego/cache/memcache"
+//   "github.com/astaxie/beego/cache"
+// )
+//
+//  bm, err := cache.NewCache("memcache", `{"conn":"127.0.0.1:11211"}`)
+//
+//  more docs http://beego.me/docs/module/cache.md
+package memcache
+
+import (
+	"encoding/json"
+	"errors"
+	"strings"
+	"time"
+
+	"github.com/astaxie/beego/cache"
+	"github.com/bradfitz/gomemcache/memcache"
+)
+
+// Cache Memcache adapter.
+type Cache struct {
+	conn     *memcache.Client
+	conninfo []string
+}
+
+// NewMemCache create new memcache adapter.
+func NewMemCache() cache.Cache {
+	return &Cache{}
+}
+
+// Get get value from memcache.
+func (rc *Cache) Get(key string) interface{} {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	if item, err := rc.conn.Get(key); err == nil {
+		return item.Value
+	}
+	return nil
+}
+
+// GetMulti get value from memcache.
+func (rc *Cache) GetMulti(keys []string) []interface{} {
+	size := len(keys)
+	var rv []interface{}
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			for i := 0; i < size; i++ {
+				rv = append(rv, err)
+			}
+			return rv
+		}
+	}
+	mv, err := rc.conn.GetMulti(keys)
+	if err == nil {
+		for _, v := range mv {
+			rv = append(rv, v.Value)
+		}
+		return rv
+	}
+	for i := 0; i < size; i++ {
+		rv = append(rv, err)
+	}
+	return rv
+}
+
+// Put put value to memcache.
+func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	item := memcache.Item{Key: key, Expiration: int32(timeout / time.Second)}
+	if v, ok := val.([]byte); ok {
+		item.Value = v
+	} else if str, ok := val.(string); ok {
+		item.Value = []byte(str)
+	} else {
+		return errors.New("val only support string and []byte")
+	}
+	return rc.conn.Set(&item)
+}
+
+// Delete delete value in memcache.
+func (rc *Cache) Delete(key string) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	return rc.conn.Delete(key)
+}
+
+// Incr increase counter.
+func (rc *Cache) Incr(key string) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	_, err := rc.conn.Increment(key, 1)
+	return err
+}
+
+// Decr decrease counter.
+func (rc *Cache) Decr(key string) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	_, err := rc.conn.Decrement(key, 1)
+	return err
+}
+
+// IsExist check value exists in memcache.
+func (rc *Cache) IsExist(key string) bool {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return false
+		}
+	}
+	_, err := rc.conn.Get(key)
+	if err != nil {
+		return false
+	}
+	return true
+}
+
+// ClearAll clear all cached in memcache.
+func (rc *Cache) ClearAll() error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	return rc.conn.FlushAll()
+}
+
+// StartAndGC start memcache adapter.
+// config string is like {"conn":"connection info"}.
+// if connecting error, return.
+func (rc *Cache) StartAndGC(config string) error {
+	var cf map[string]string
+	json.Unmarshal([]byte(config), &cf)
+	if _, ok := cf["conn"]; !ok {
+		return errors.New("config has no conn key")
+	}
+	rc.conninfo = strings.Split(cf["conn"], ";")
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// connect to memcache and keep the connection.
+func (rc *Cache) connectInit() error {
+	rc.conn = memcache.New(rc.conninfo...)
+	return nil
+}
+
+func init() {
+	cache.Register("memcache", NewMemCache)
+}

+ 108 - 0
vendor/github.com/astaxie/beego/cache/memcache/memcache_test.go

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

+ 244 - 0
vendor/github.com/astaxie/beego/cache/memory.go

@@ -0,0 +1,244 @@
+// 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 cache
+
+import (
+	"encoding/json"
+	"errors"
+	"sync"
+	"time"
+)
+
+var (
+	// DefaultEvery means the clock time of recycling the expired cache items in memory.
+	DefaultEvery = 60 // 1 minute
+)
+
+// MemoryItem store memory cache item.
+type MemoryItem struct {
+	val         interface{}
+	createdTime time.Time
+	lifespan    time.Duration
+}
+
+func (mi *MemoryItem) isExpire() bool {
+	// 0 means forever
+	if mi.lifespan == 0 {
+		return false
+	}
+	return time.Now().Sub(mi.createdTime) > mi.lifespan
+}
+
+// MemoryCache is Memory cache adapter.
+// it contains a RW locker for safe map storage.
+type MemoryCache struct {
+	sync.RWMutex
+	dur   time.Duration
+	items map[string]*MemoryItem
+	Every int // run an expiration check Every clock time
+}
+
+// NewMemoryCache returns a new MemoryCache.
+func NewMemoryCache() Cache {
+	cache := MemoryCache{items: make(map[string]*MemoryItem)}
+	return &cache
+}
+
+// Get cache from memory.
+// if non-existed or expired, return nil.
+func (bc *MemoryCache) Get(name string) interface{} {
+	bc.RLock()
+	defer bc.RUnlock()
+	if itm, ok := bc.items[name]; ok {
+		if itm.isExpire() {
+			return nil
+		}
+		return itm.val
+	}
+	return nil
+}
+
+// GetMulti gets caches from memory.
+// if non-existed or expired, return nil.
+func (bc *MemoryCache) GetMulti(names []string) []interface{} {
+	var rc []interface{}
+	for _, name := range names {
+		rc = append(rc, bc.Get(name))
+	}
+	return rc
+}
+
+// Put cache to memory.
+// if lifespan is 0, it will be forever till restart.
+func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error {
+	bc.Lock()
+	defer bc.Unlock()
+	bc.items[name] = &MemoryItem{
+		val:         value,
+		createdTime: time.Now(),
+		lifespan:    lifespan,
+	}
+	return nil
+}
+
+// Delete cache in memory.
+func (bc *MemoryCache) Delete(name string) error {
+	bc.Lock()
+	defer bc.Unlock()
+	if _, ok := bc.items[name]; !ok {
+		return errors.New("key not exist")
+	}
+	delete(bc.items, name)
+	if _, ok := bc.items[name]; ok {
+		return errors.New("delete key error")
+	}
+	return nil
+}
+
+// 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()
+	itm, ok := bc.items[key]
+	if !ok {
+		return errors.New("key not exist")
+	}
+	switch itm.val.(type) {
+	case int:
+		itm.val = itm.val.(int) + 1
+	case int32:
+		itm.val = itm.val.(int32) + 1
+	case int64:
+		itm.val = itm.val.(int64) + 1
+	case uint:
+		itm.val = itm.val.(uint) + 1
+	case uint32:
+		itm.val = itm.val.(uint32) + 1
+	case uint64:
+		itm.val = itm.val.(uint64) + 1
+	default:
+		return errors.New("item val is not (u)int (u)int32 (u)int64")
+	}
+	return nil
+}
+
+// Decr decrease counter in memory.
+func (bc *MemoryCache) Decr(key string) error {
+	bc.RLock()
+	defer bc.RUnlock()
+	itm, ok := bc.items[key]
+	if !ok {
+		return errors.New("key not exist")
+	}
+	switch itm.val.(type) {
+	case int:
+		itm.val = itm.val.(int) - 1
+	case int64:
+		itm.val = itm.val.(int64) - 1
+	case int32:
+		itm.val = itm.val.(int32) - 1
+	case uint:
+		if itm.val.(uint) > 0 {
+			itm.val = itm.val.(uint) - 1
+		} else {
+			return errors.New("item val is less than 0")
+		}
+	case uint32:
+		if itm.val.(uint32) > 0 {
+			itm.val = itm.val.(uint32) - 1
+		} else {
+			return errors.New("item val is less than 0")
+		}
+	case uint64:
+		if itm.val.(uint64) > 0 {
+			itm.val = itm.val.(uint64) - 1
+		} else {
+			return errors.New("item val is less than 0")
+		}
+	default:
+		return errors.New("item val is not int int64 int32")
+	}
+	return nil
+}
+
+// IsExist check cache exist in memory.
+func (bc *MemoryCache) IsExist(name string) bool {
+	bc.RLock()
+	defer bc.RUnlock()
+	if v, ok := bc.items[name]; ok {
+		return !v.isExpire()
+	}
+	return false
+}
+
+// ClearAll will delete all cache in memory.
+func (bc *MemoryCache) ClearAll() error {
+	bc.Lock()
+	defer bc.Unlock()
+	bc.items = make(map[string]*MemoryItem)
+	return nil
+}
+
+// StartAndGC start memory cache. it will check expiration in every clock time.
+func (bc *MemoryCache) StartAndGC(config string) error {
+	var cf map[string]int
+	json.Unmarshal([]byte(config), &cf)
+	if _, ok := cf["interval"]; !ok {
+		cf = make(map[string]int)
+		cf["interval"] = DefaultEvery
+	}
+	dur := time.Duration(cf["interval"]) * time.Second
+	bc.Every = cf["interval"]
+	bc.dur = dur
+	go bc.vaccuum()
+	return nil
+}
+
+// check expiration.
+func (bc *MemoryCache) vaccuum() {
+	if bc.Every < 1 {
+		return
+	}
+	for {
+		<-time.After(bc.dur)
+		if bc.items == nil {
+			return
+		}
+		for name := range bc.items {
+			bc.itemExpired(name)
+		}
+	}
+}
+
+// itemExpired returns true if an item is expired.
+func (bc *MemoryCache) itemExpired(name string) bool {
+	bc.Lock()
+	defer bc.Unlock()
+
+	itm, ok := bc.items[name]
+	if !ok {
+		return true
+	}
+	if itm.isExpire() {
+		delete(bc.items, name)
+		return true
+	}
+	return false
+}
+
+func init() {
+	Register("memory", NewMemoryCache)
+}

+ 240 - 0
vendor/github.com/astaxie/beego/cache/redis/redis.go

@@ -0,0 +1,240 @@
+// 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 cache provider
+//
+// depend on github.com/garyburd/redigo/redis
+//
+// go install github.com/garyburd/redigo/redis
+//
+// Usage:
+// import(
+//   _ "github.com/astaxie/beego/cache/redis"
+//   "github.com/astaxie/beego/cache"
+// )
+//
+//  bm, err := cache.NewCache("redis", `{"conn":"127.0.0.1:11211"}`)
+//
+//  more docs http://beego.me/docs/module/cache.md
+package redis
+
+import (
+	"encoding/json"
+	"errors"
+	"strconv"
+	"time"
+
+	"github.com/garyburd/redigo/redis"
+
+	"github.com/astaxie/beego/cache"
+)
+
+var (
+	// DefaultKey the collection name of redis for cache adapter.
+	DefaultKey = "beecacheRedis"
+)
+
+// Cache is Redis cache adapter.
+type Cache struct {
+	p        *redis.Pool // redis connection pool
+	conninfo string
+	dbNum    int
+	key      string
+	password string
+}
+
+// NewRedisCache create new redis cache with default collection name.
+func NewRedisCache() cache.Cache {
+	return &Cache{key: DefaultKey}
+}
+
+// actually do the redis cmds
+func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) {
+	c := rc.p.Get()
+	defer c.Close()
+
+	return c.Do(commandName, args...)
+}
+
+// Get cache from redis.
+func (rc *Cache) Get(key string) interface{} {
+	if v, err := rc.do("GET", key); err == nil {
+		return v
+	}
+	return nil
+}
+
+// GetMulti get cache from redis.
+func (rc *Cache) GetMulti(keys []string) []interface{} {
+	size := len(keys)
+	var rv []interface{}
+	c := rc.p.Get()
+	defer c.Close()
+	var err error
+	for _, key := range keys {
+		err = c.Send("GET", key)
+		if err != nil {
+			goto ERROR
+		}
+	}
+	if err = c.Flush(); err != nil {
+		goto ERROR
+	}
+	for i := 0; i < size; i++ {
+		if v, err := c.Receive(); err == nil {
+			rv = append(rv, v.([]byte))
+		} else {
+			rv = append(rv, err)
+		}
+	}
+	return rv
+ERROR:
+	rv = rv[0:0]
+	for i := 0; i < size; i++ {
+		rv = append(rv, nil)
+	}
+
+	return rv
+}
+
+// Put put cache to redis.
+func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error {
+	var err error
+	if _, err = rc.do("SETEX", key, int64(timeout/time.Second), val); err != nil {
+		return err
+	}
+
+	if _, err = rc.do("HSET", rc.key, key, true); err != nil {
+		return err
+	}
+	return err
+}
+
+// Delete delete cache in redis.
+func (rc *Cache) Delete(key string) error {
+	var err error
+	if _, err = rc.do("DEL", key); err != nil {
+		return err
+	}
+	_, err = rc.do("HDEL", rc.key, key)
+	return err
+}
+
+// IsExist check cache's existence in redis.
+func (rc *Cache) IsExist(key string) bool {
+	v, err := redis.Bool(rc.do("EXISTS", key))
+	if err != nil {
+		return false
+	}
+	if v == false {
+		if _, err = rc.do("HDEL", rc.key, key); err != nil {
+			return false
+		}
+	}
+	return v
+}
+
+// Incr increase counter in redis.
+func (rc *Cache) Incr(key string) error {
+	_, err := redis.Bool(rc.do("INCRBY", key, 1))
+	return err
+}
+
+// Decr decrease counter in redis.
+func (rc *Cache) Decr(key string) error {
+	_, err := redis.Bool(rc.do("INCRBY", key, -1))
+	return err
+}
+
+// ClearAll clean all cache in redis. delete this redis collection.
+func (rc *Cache) ClearAll() error {
+	cachedKeys, err := redis.Strings(rc.do("HKEYS", rc.key))
+	if err != nil {
+		return err
+	}
+	for _, str := range cachedKeys {
+		if _, err = rc.do("DEL", str); err != nil {
+			return err
+		}
+	}
+	_, err = rc.do("DEL", rc.key)
+	return err
+}
+
+// StartAndGC start redis cache adapter.
+// config is like {"key":"collection key","conn":"connection info","dbNum":"0"}
+// the cache item in redis are stored forever,
+// so no gc operation.
+func (rc *Cache) StartAndGC(config string) error {
+	var cf map[string]string
+	json.Unmarshal([]byte(config), &cf)
+
+	if _, ok := cf["key"]; !ok {
+		cf["key"] = DefaultKey
+	}
+	if _, ok := cf["conn"]; !ok {
+		return errors.New("config has no conn key")
+	}
+	if _, ok := cf["dbNum"]; !ok {
+		cf["dbNum"] = "0"
+	}
+	if _, ok := cf["password"]; !ok {
+		cf["password"] = ""
+	}
+	rc.key = cf["key"]
+	rc.conninfo = cf["conn"]
+	rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
+	rc.password = cf["password"]
+
+	rc.connectInit()
+
+	c := rc.p.Get()
+	defer c.Close()
+
+	return c.Err()
+}
+
+// connect to redis.
+func (rc *Cache) connectInit() {
+	dialFunc := func() (c redis.Conn, err error) {
+		c, err = redis.Dial("tcp", rc.conninfo)
+		if err != nil {
+			return nil, err
+		}
+
+		if rc.password != "" {
+			if _, err := c.Do("AUTH", rc.password); err != nil {
+				c.Close()
+				return nil, err
+			}
+		}
+
+		_, selecterr := c.Do("SELECT", rc.dbNum)
+		if selecterr != nil {
+			c.Close()
+			return nil, selecterr
+		}
+		return
+	}
+	// initialize a new pool
+	rc.p = &redis.Pool{
+		MaxIdle:     3,
+		IdleTimeout: 180 * time.Second,
+		Dial:        dialFunc,
+	}
+}
+
+func init() {
+	cache.Register("redis", NewRedisCache)
+}

+ 106 - 0
vendor/github.com/astaxie/beego/cache/redis/redis_test.go

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

+ 240 - 0
vendor/github.com/astaxie/beego/cache/ssdb/ssdb.go

@@ -0,0 +1,240 @@
+package ssdb
+
+import (
+	"encoding/json"
+	"errors"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/ssdb/gossdb/ssdb"
+
+	"github.com/astaxie/beego/cache"
+)
+
+// Cache SSDB adapter
+type Cache struct {
+	conn     *ssdb.Client
+	conninfo []string
+}
+
+//NewSsdbCache create new ssdb adapter.
+func NewSsdbCache() cache.Cache {
+	return &Cache{}
+}
+
+// Get get value from memcache.
+func (rc *Cache) Get(key string) interface{} {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return nil
+		}
+	}
+	value, err := rc.conn.Get(key)
+	if err == nil {
+		return value
+	}
+	return nil
+}
+
+// GetMulti get value from memcache.
+func (rc *Cache) GetMulti(keys []string) []interface{} {
+	size := len(keys)
+	var values []interface{}
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			for i := 0; i < size; i++ {
+				values = append(values, err)
+			}
+			return values
+		}
+	}
+	res, err := rc.conn.Do("multi_get", keys)
+	resSize := len(res)
+	if err == nil {
+		for i := 1; i < resSize; i += 2 {
+			values = append(values, string(res[i+1]))
+		}
+		return values
+	}
+	for i := 0; i < size; i++ {
+		values = append(values, err)
+	}
+	return values
+}
+
+// DelMulti get value from memcache.
+func (rc *Cache) DelMulti(keys []string) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	_, err := rc.conn.Do("multi_del", keys)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// Put put value to memcache. only support string.
+func (rc *Cache) Put(key string, value interface{}, timeout time.Duration) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	v, ok := value.(string)
+	if !ok {
+		return errors.New("value must string")
+	}
+	var resp []string
+	var err error
+	ttl := int(timeout / time.Second)
+	if ttl < 0 {
+		resp, err = rc.conn.Do("set", key, v)
+	} else {
+		resp, err = rc.conn.Do("setx", key, v, ttl)
+	}
+	if err != nil {
+		return err
+	}
+	if len(resp) == 2 && resp[0] == "ok" {
+		return nil
+	}
+	return errors.New("bad response")
+}
+
+// Delete delete value in memcache.
+func (rc *Cache) Delete(key string) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	_, err := rc.conn.Del(key)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// Incr increase counter.
+func (rc *Cache) Incr(key string) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	_, err := rc.conn.Do("incr", key, 1)
+	return err
+}
+
+// Decr decrease counter.
+func (rc *Cache) Decr(key string) error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	_, err := rc.conn.Do("incr", key, -1)
+	return err
+}
+
+// IsExist check value exists in memcache.
+func (rc *Cache) IsExist(key string) bool {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return false
+		}
+	}
+	resp, err := rc.conn.Do("exists", key)
+	if err != nil {
+		return false
+	}
+	if len(resp) == 2 && resp[1] == "1" {
+		return true
+	}
+	return false
+
+}
+
+// ClearAll clear all cached in memcache.
+func (rc *Cache) ClearAll() error {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	keyStart, keyEnd, limit := "", "", 50
+	resp, err := rc.Scan(keyStart, keyEnd, limit)
+	for err == nil {
+		size := len(resp)
+		if size == 1 {
+			return nil
+		}
+		keys := []string{}
+		for i := 1; i < size; i += 2 {
+			keys = append(keys, string(resp[i]))
+		}
+		_, e := rc.conn.Do("multi_del", keys)
+		if e != nil {
+			return e
+		}
+		keyStart = resp[size-2]
+		resp, err = rc.Scan(keyStart, keyEnd, limit)
+	}
+	return err
+}
+
+// Scan key all cached in ssdb.
+func (rc *Cache) Scan(keyStart string, keyEnd string, limit int) ([]string, error) {
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return nil, err
+		}
+	}
+	resp, err := rc.conn.Do("scan", keyStart, keyEnd, limit)
+	if err != nil {
+		return nil, err
+	}
+	return resp, nil
+}
+
+// StartAndGC start memcache adapter.
+// config string is like {"conn":"connection info"}.
+// if connecting error, return.
+func (rc *Cache) StartAndGC(config string) error {
+	var cf map[string]string
+	json.Unmarshal([]byte(config), &cf)
+	if _, ok := cf["conn"]; !ok {
+		return errors.New("config has no conn key")
+	}
+	rc.conninfo = strings.Split(cf["conn"], ";")
+	if rc.conn == nil {
+		if err := rc.connectInit(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// connect to memcache and keep the connection.
+func (rc *Cache) connectInit() error {
+	conninfoArray := strings.Split(rc.conninfo[0], ":")
+	host := conninfoArray[0]
+	port, e := strconv.Atoi(conninfoArray[1])
+	if e != nil {
+		return e
+	}
+	var err error
+	rc.conn, err = ssdb.Connect(host, port)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func init() {
+	cache.Register("ssdb", NewSsdbCache)
+}

+ 104 - 0
vendor/github.com/astaxie/beego/cache/ssdb/ssdb_test.go

@@ -0,0 +1,104 @@
+package ssdb
+
+import (
+	"strconv"
+	"testing"
+	"time"
+
+	"github.com/astaxie/beego/cache"
+)
+
+func TestSsdbcacheCache(t *testing.T) {
+	ssdb, err := cache.NewCache("ssdb", `{"conn": "127.0.0.1:8888"}`)
+	if err != nil {
+		t.Error("init err")
+	}
+
+	// test put and exist
+	if ssdb.IsExist("ssdb") {
+		t.Error("check err")
+	}
+	timeoutDuration := 10 * time.Second
+	//timeoutDuration := -10*time.Second   if timeoutDuration is negtive,it means permanent
+	if err = ssdb.Put("ssdb", "ssdb", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if !ssdb.IsExist("ssdb") {
+		t.Error("check err")
+	}
+
+	// Get test done
+	if err = ssdb.Put("ssdb", "ssdb", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+
+	if v := ssdb.Get("ssdb"); v != "ssdb" {
+		t.Error("get Error")
+	}
+
+	//inc/dec test done
+	if err = ssdb.Put("ssdb", "2", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if err = ssdb.Incr("ssdb"); err != nil {
+		t.Error("incr Error", err)
+	}
+
+	if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 3 {
+		t.Error("get err")
+	}
+
+	if err = ssdb.Decr("ssdb"); err != nil {
+		t.Error("decr error")
+	}
+
+	// test del
+	if err = ssdb.Put("ssdb", "3", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+	if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 3 {
+		t.Error("get err")
+	}
+	if err := ssdb.Delete("ssdb"); err == nil {
+		if ssdb.IsExist("ssdb") {
+			t.Error("delete err")
+		}
+	}
+
+	//test string
+	if err = ssdb.Put("ssdb", "ssdb", -10*time.Second); err != nil {
+		t.Error("set Error", err)
+	}
+	if !ssdb.IsExist("ssdb") {
+		t.Error("check err")
+	}
+	if v := ssdb.Get("ssdb").(string); v != "ssdb" {
+		t.Error("get err")
+	}
+
+	//test GetMulti done
+	if err = ssdb.Put("ssdb1", "ssdb1", -10*time.Second); err != nil {
+		t.Error("set Error", err)
+	}
+	if !ssdb.IsExist("ssdb1") {
+		t.Error("check err")
+	}
+	vv := ssdb.GetMulti([]string{"ssdb", "ssdb1"})
+	if len(vv) != 2 {
+		t.Error("getmulti error")
+	}
+	if vv[0].(string) != "ssdb" {
+		t.Error("getmulti error")
+	}
+	if vv[1].(string) != "ssdb1" {
+		t.Error("getmulti error")
+	}
+
+	// test clear all done
+	if err = ssdb.ClearAll(); err != nil {
+		t.Error("clear all err")
+	}
+	if ssdb.IsExist("ssdb") || ssdb.IsExist("ssdb1") {
+		t.Error("check err")
+	}
+}

+ 55 - 0
vendor/github.com/astaxie/beego/config/config_test.go

@@ -0,0 +1,55 @@
+// Copyright 2016 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"os"
+	"testing"
+)
+
+func TestExpandValueEnv(t *testing.T) {
+
+	testCases := []struct {
+		item string
+		want string
+	}{
+		{"", ""},
+		{"$", "$"},
+		{"{", "{"},
+		{"{}", "{}"},
+		{"${}", ""},
+		{"${|}", ""},
+		{"${}", ""},
+		{"${{}}", ""},
+		{"${{||}}", "}"},
+		{"${pwd||}", ""},
+		{"${pwd||}", ""},
+		{"${pwd||}", ""},
+		{"${pwd||}}", "}"},
+		{"${pwd||{{||}}}", "{{||}}"},
+		{"${GOPATH}", os.Getenv("GOPATH")},
+		{"${GOPATH||}", os.Getenv("GOPATH")},
+		{"${GOPATH||root}", os.Getenv("GOPATH")},
+		{"${GOPATH_NOT||root}", "root"},
+		{"${GOPATH_NOT||||root}", "||root"},
+	}
+
+	for _, c := range testCases {
+		if got := ExpandValueEnv(c.item); got != c.want {
+			t.Errorf("expand value error, item %q want %q, got %q", c.item, c.want, got)
+		}
+	}
+
+}

+ 85 - 0
vendor/github.com/astaxie/beego/config/env/env.go

@@ -0,0 +1,85 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+// Copyright 2017 Faissal Elamraoui. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package env
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/astaxie/beego/utils"
+)
+
+var env *utils.BeeMap
+
+func init() {
+	env = utils.NewBeeMap()
+	for _, e := range os.Environ() {
+		splits := strings.Split(e, "=")
+		env.Set(splits[0], os.Getenv(splits[0]))
+	}
+}
+
+// Get returns a value by key.
+// If the key does not exist, the default value will be returned.
+func Get(key string, defVal string) string {
+	if val := env.Get(key); val != nil {
+		return val.(string)
+	}
+	return defVal
+}
+
+// MustGet returns a value by key.
+// If the key does not exist, it will return an error.
+func MustGet(key string) (string, error) {
+	if val := env.Get(key); val != nil {
+		return val.(string), nil
+	}
+	return "", fmt.Errorf("no env variable with %s", key)
+}
+
+// Set sets a value in the ENV copy.
+// This does not affect the child process environment.
+func Set(key string, value string) {
+	env.Set(key, value)
+}
+
+// MustSet sets a value in the ENV copy and the child process environment.
+// It returns an error in case the set operation failed.
+func MustSet(key string, value string) error {
+	err := os.Setenv(key, value)
+	if err != nil {
+		return err
+	}
+	env.Set(key, value)
+	return nil
+}
+
+// GetAll returns all keys/values in the current child process environment.
+func GetAll() map[string]string {
+	items := env.Items()
+	envs := make(map[string]string, env.Count())
+
+	for key, val := range items {
+		switch key := key.(type) {
+		case string:
+			switch val := val.(type) {
+			case string:
+				envs[key] = val
+			}
+		}
+	}
+	return envs
+}

+ 75 - 0
vendor/github.com/astaxie/beego/config/env/env_test.go

@@ -0,0 +1,75 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+// Copyright 2017 Faissal Elamraoui. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package env
+
+import (
+	"os"
+	"testing"
+)
+
+func TestEnvGet(t *testing.T) {
+	gopath := Get("GOPATH", "")
+	if gopath != os.Getenv("GOPATH") {
+		t.Error("expected GOPATH not empty.")
+	}
+
+	noExistVar := Get("NOEXISTVAR", "foo")
+	if noExistVar != "foo" {
+		t.Errorf("expected NOEXISTVAR to equal foo, got %s.", noExistVar)
+	}
+}
+
+func TestEnvMustGet(t *testing.T) {
+	gopath, err := MustGet("GOPATH")
+	if err != nil {
+		t.Error(err)
+	}
+
+	if gopath != os.Getenv("GOPATH") {
+		t.Errorf("expected GOPATH to be the same, got %s.", gopath)
+	}
+
+	_, err = MustGet("NOEXISTVAR")
+	if err == nil {
+		t.Error("expected error to be non-nil")
+	}
+}
+
+func TestEnvSet(t *testing.T) {
+	Set("MYVAR", "foo")
+	myVar := Get("MYVAR", "bar")
+	if myVar != "foo" {
+		t.Errorf("expected MYVAR to equal foo, got %s.", myVar)
+	}
+}
+
+func TestEnvMustSet(t *testing.T) {
+	err := MustSet("FOO", "bar")
+	if err != nil {
+		t.Error(err)
+	}
+
+	fooVar := os.Getenv("FOO")
+	if fooVar != "bar" {
+		t.Errorf("expected FOO variable to equal bar, got %s.", fooVar)
+	}
+}
+
+func TestEnvGetAll(t *testing.T) {
+	envMap := GetAll()
+	if len(envMap) == 0 {
+		t.Error("expected environment not empty.")
+	}
+}

+ 190 - 0
vendor/github.com/astaxie/beego/config/ini_test.go

@@ -0,0 +1,190 @@
+// 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 config
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+)
+
+func TestIni(t *testing.T) {
+
+	var (
+		inicontext = `
+;comment one
+#comment two
+appname = beeapi
+httpport = 8080
+mysqlport = 3600
+PI = 3.1415976
+runmode = "dev"
+autorender = false
+copyrequestbody = true
+session= on
+cookieon= off
+newreg = OFF
+needlogin = ON
+enableSession = Y
+enableCookie = N
+flag = 1
+path1 = ${GOPATH}
+path2 = ${GOPATH||/home/go}
+[demo]
+key1="asta"
+key2 = "xie"
+CaseInsensitive = true
+peers = one;two;three
+password = ${GOPATH}
+`
+
+		keyValue = map[string]interface{}{
+			"appname":               "beeapi",
+			"httpport":              8080,
+			"mysqlport":             int64(3600),
+			"pi":                    3.1415976,
+			"runmode":               "dev",
+			"autorender":            false,
+			"copyrequestbody":       true,
+			"session":               true,
+			"cookieon":              false,
+			"newreg":                false,
+			"needlogin":             true,
+			"enableSession":         true,
+			"enableCookie":          false,
+			"flag":                  true,
+			"path1":                 os.Getenv("GOPATH"),
+			"path2":                 os.Getenv("GOPATH"),
+			"demo::key1":            "asta",
+			"demo::key2":            "xie",
+			"demo::CaseInsensitive": true,
+			"demo::peers":           []string{"one", "two", "three"},
+			"demo::password":        os.Getenv("GOPATH"),
+			"null":                  "",
+			"demo2::key1":           "",
+			"error":                 "",
+			"emptystrings":          []string{},
+		}
+	)
+
+	f, err := os.Create("testini.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(inicontext)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testini.conf")
+	iniconf, err := NewConfig("ini", "testini.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	for k, v := range keyValue {
+		var err error
+		var value interface{}
+		switch v.(type) {
+		case int:
+			value, err = iniconf.Int(k)
+		case int64:
+			value, err = iniconf.Int64(k)
+		case float64:
+			value, err = iniconf.Float(k)
+		case bool:
+			value, err = iniconf.Bool(k)
+		case []string:
+			value = iniconf.Strings(k)
+		case string:
+			value = iniconf.String(k)
+		default:
+			value, err = iniconf.DIY(k)
+		}
+		if err != nil {
+			t.Fatalf("get key %q value fail,err %s", k, err)
+		} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+			t.Fatalf("get key %q value, want %v got %v .", k, v, value)
+		}
+
+	}
+	if err = iniconf.Set("name", "astaxie"); err != nil {
+		t.Fatal(err)
+	}
+	if iniconf.String("name") != "astaxie" {
+		t.Fatal("get name error")
+	}
+
+}
+
+func TestIniSave(t *testing.T) {
+
+	const (
+		inicontext = `
+app = app
+;comment one
+#comment two
+# comment three
+appname = beeapi
+httpport = 8080
+# DB Info
+# enable db
+[dbinfo]
+# db type name
+# suport mysql,sqlserver
+name = mysql
+`
+
+		saveResult = `
+app=app
+#comment one
+#comment two
+# comment three
+appname=beeapi
+httpport=8080
+
+# DB Info
+# enable db
+[dbinfo]
+# db type name
+# suport mysql,sqlserver
+name=mysql
+`
+	)
+	cfg, err := NewConfigData("ini", []byte(inicontext))
+	if err != nil {
+		t.Fatal(err)
+	}
+	name := "newIniConfig.ini"
+	if err := cfg.SaveConfigFile(name); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(name)
+
+	if data, err := ioutil.ReadFile(name); err != nil {
+		t.Fatal(err)
+	} else {
+		cfgData := string(data)
+		datas := strings.Split(saveResult, "\n")
+		for _, line := range datas {
+			if strings.Contains(cfgData, line+"\n") == false {
+				t.Fatalf("different after save ini config file. need contains %q", line)
+			}
+		}
+
+	}
+}

+ 222 - 0
vendor/github.com/astaxie/beego/config/json_test.go

@@ -0,0 +1,222 @@
+// 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 config
+
+import (
+	"fmt"
+	"os"
+	"testing"
+)
+
+func TestJsonStartsWithArray(t *testing.T) {
+
+	const jsoncontextwitharray = `[
+	{
+		"url": "user",
+		"serviceAPI": "http://www.test.com/user"
+	},
+	{
+		"url": "employee",
+		"serviceAPI": "http://www.test.com/employee"
+	}
+]`
+	f, err := os.Create("testjsonWithArray.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(jsoncontextwitharray)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testjsonWithArray.conf")
+	jsonconf, err := NewConfig("json", "testjsonWithArray.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	rootArray, err := jsonconf.DIY("rootArray")
+	if err != nil {
+		t.Error("array does not exist as element")
+	}
+	rootArrayCasted := rootArray.([]interface{})
+	if rootArrayCasted == nil {
+		t.Error("array from root is nil")
+	} else {
+		elem := rootArrayCasted[0].(map[string]interface{})
+		if elem["url"] != "user" || elem["serviceAPI"] != "http://www.test.com/user" {
+			t.Error("array[0] values are not valid")
+		}
+
+		elem2 := rootArrayCasted[1].(map[string]interface{})
+		if elem2["url"] != "employee" || elem2["serviceAPI"] != "http://www.test.com/employee" {
+			t.Error("array[1] values are not valid")
+		}
+	}
+}
+
+func TestJson(t *testing.T) {
+
+	var (
+		jsoncontext = `{
+"appname": "beeapi",
+"testnames": "foo;bar",
+"httpport": 8080,
+"mysqlport": 3600,
+"PI": 3.1415976, 
+"runmode": "dev",
+"autorender": false,
+"copyrequestbody": true,
+"session": "on",
+"cookieon": "off",
+"newreg": "OFF",
+"needlogin": "ON",
+"enableSession": "Y",
+"enableCookie": "N",
+"flag": 1,
+"path1": "${GOPATH}",
+"path2": "${GOPATH||/home/go}",
+"database": {
+        "host": "host",
+        "port": "port",
+        "database": "database",
+        "username": "username",
+        "password": "${GOPATH}",
+		"conns":{
+			"maxconnection":12,
+			"autoconnect":true,
+			"connectioninfo":"info",
+			"root": "${GOPATH}"
+		}
+    }
+}`
+		keyValue = map[string]interface{}{
+			"appname":                         "beeapi",
+			"testnames":                       []string{"foo", "bar"},
+			"httpport":                        8080,
+			"mysqlport":                       int64(3600),
+			"PI":                              3.1415976,
+			"runmode":                         "dev",
+			"autorender":                      false,
+			"copyrequestbody":                 true,
+			"session":                         true,
+			"cookieon":                        false,
+			"newreg":                          false,
+			"needlogin":                       true,
+			"enableSession":                   true,
+			"enableCookie":                    false,
+			"flag":                            true,
+			"path1":                           os.Getenv("GOPATH"),
+			"path2":                           os.Getenv("GOPATH"),
+			"database::host":                  "host",
+			"database::port":                  "port",
+			"database::database":              "database",
+			"database::password":              os.Getenv("GOPATH"),
+			"database::conns::maxconnection":  12,
+			"database::conns::autoconnect":    true,
+			"database::conns::connectioninfo": "info",
+			"database::conns::root":           os.Getenv("GOPATH"),
+			"unknown":                         "",
+		}
+	)
+
+	f, err := os.Create("testjson.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(jsoncontext)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testjson.conf")
+	jsonconf, err := NewConfig("json", "testjson.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for k, v := range keyValue {
+		var err error
+		var value interface{}
+		switch v.(type) {
+		case int:
+			value, err = jsonconf.Int(k)
+		case int64:
+			value, err = jsonconf.Int64(k)
+		case float64:
+			value, err = jsonconf.Float(k)
+		case bool:
+			value, err = jsonconf.Bool(k)
+		case []string:
+			value = jsonconf.Strings(k)
+		case string:
+			value = jsonconf.String(k)
+		default:
+			value, err = jsonconf.DIY(k)
+		}
+		if err != nil {
+			t.Fatalf("get key %q value fatal,%v err %s", k, v, err)
+		} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+			t.Fatalf("get key %q value, want %v got %v .", k, v, value)
+		}
+
+	}
+	if err = jsonconf.Set("name", "astaxie"); err != nil {
+		t.Fatal(err)
+	}
+	if jsonconf.String("name") != "astaxie" {
+		t.Fatal("get name error")
+	}
+
+	if db, err := jsonconf.DIY("database"); err != nil {
+		t.Fatal(err)
+	} else if m, ok := db.(map[string]interface{}); !ok {
+		t.Log(db)
+		t.Fatal("db not map[string]interface{}")
+	} else {
+		if m["host"].(string) != "host" {
+			t.Fatal("get host err")
+		}
+	}
+
+	if _, err := jsonconf.Int("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting an Int")
+	}
+
+	if _, err := jsonconf.Int64("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting an Int64")
+	}
+
+	if _, err := jsonconf.Float("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting a Float")
+	}
+
+	if _, err := jsonconf.DIY("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting an interface{}")
+	}
+
+	if val := jsonconf.String("unknown"); val != "" {
+		t.Error("unknown keys should return an empty string when expecting a String")
+	}
+
+	if _, err := jsonconf.Bool("unknown"); err == nil {
+		t.Error("unknown keys should return an error when expecting a Bool")
+	}
+
+	if !jsonconf.DefaultBool("unknow", true) {
+		t.Error("unknown keys with default value wrong")
+	}
+}

+ 228 - 0
vendor/github.com/astaxie/beego/config/xml/xml.go

@@ -0,0 +1,228 @@
+// 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 xml for config provider.
+//
+// depend on github.com/beego/x2j.
+//
+// go install github.com/beego/x2j.
+//
+// Usage:
+//  import(
+//    _ "github.com/astaxie/beego/config/xml"
+//      "github.com/astaxie/beego/config"
+//  )
+//
+//  cnf, err := config.NewConfig("xml", "config.xml")
+//
+//More docs http://beego.me/docs/module/config.md
+package xml
+
+import (
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/astaxie/beego/config"
+	"github.com/beego/x2j"
+)
+
+// Config is a xml config parser and implements Config interface.
+// xml configurations should be included in <config></config> tag.
+// only support key/value pair as <key>value</key> as each item.
+type Config struct{}
+
+// Parse returns a ConfigContainer with parsed xml config map.
+func (xc *Config) Parse(filename string) (config.Configer, error) {
+	context, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	return xc.ParseData(context)
+}
+
+// ParseData xml data
+func (xc *Config) ParseData(data []byte) (config.Configer, error) {
+	x := &ConfigContainer{data: make(map[string]interface{})}
+
+	d, err := x2j.DocToMap(string(data))
+	if err != nil {
+		return nil, err
+	}
+
+	x.data = config.ExpandValueEnvForMap(d["config"].(map[string]interface{}))
+
+	return x, nil
+}
+
+// ConfigContainer A Config represents the xml configuration.
+type ConfigContainer struct {
+	data map[string]interface{}
+	sync.Mutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *ConfigContainer) Bool(key string) (bool, error) {
+	if v := c.data[key]; v != nil {
+		return config.ParseBool(v)
+	}
+	return false, fmt.Errorf("not exist key: %q", key)
+}
+
+// DefaultBool return the bool value if has no error
+// otherwise return the defaultval
+func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool {
+	v, err := c.Bool(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int returns the integer value for a given key.
+func (c *ConfigContainer) Int(key string) (int, error) {
+	return strconv.Atoi(c.data[key].(string))
+}
+
+// DefaultInt returns the integer value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
+	v, err := c.Int(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int64 returns the int64 value for a given key.
+func (c *ConfigContainer) Int64(key string) (int64, error) {
+	return strconv.ParseInt(c.data[key].(string), 10, 64)
+}
+
+// DefaultInt64 returns the int64 value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+	v, err := c.Int64(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+
+}
+
+// Float returns the float value for a given key.
+func (c *ConfigContainer) Float(key string) (float64, error) {
+	return strconv.ParseFloat(c.data[key].(string), 64)
+}
+
+// DefaultFloat returns the float64 value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+	v, err := c.Float(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// String returns the string value for a given key.
+func (c *ConfigContainer) String(key string) string {
+	if v, ok := c.data[key].(string); ok {
+		return v
+	}
+	return ""
+}
+
+// DefaultString returns the string value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
+	v := c.String(key)
+	if v == "" {
+		return defaultval
+	}
+	return v
+}
+
+// Strings returns the []string value for a given key.
+func (c *ConfigContainer) Strings(key string) []string {
+	v := c.String(key)
+	if v == "" {
+		return nil
+	}
+	return strings.Split(v, ";")
+}
+
+// DefaultStrings returns the []string value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+	v := c.Strings(key)
+	if v == nil {
+		return defaultval
+	}
+	return v
+}
+
+// GetSection returns map for the given section
+func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
+	if v, ok := c.data[section].(map[string]interface{}); ok {
+		mapstr := make(map[string]string)
+		for k, val := range v {
+			mapstr[k] = config.ToString(val)
+		}
+		return mapstr, nil
+	}
+	return nil, fmt.Errorf("section '%s' not found", section)
+}
+
+// SaveConfigFile save the config into file
+func (c *ConfigContainer) SaveConfigFile(filename string) (err error) {
+	// Write configuration file by filename.
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	b, err := xml.MarshalIndent(c.data, "  ", "    ")
+	if err != nil {
+		return err
+	}
+	_, err = f.Write(b)
+	return err
+}
+
+// Set writes a new value for key.
+func (c *ConfigContainer) Set(key, val string) error {
+	c.Lock()
+	defer c.Unlock()
+	c.data[key] = val
+	return nil
+}
+
+// DIY returns the raw value by a given key.
+func (c *ConfigContainer) DIY(key string) (v interface{}, err error) {
+	if v, ok := c.data[key]; ok {
+		return v, nil
+	}
+	return nil, errors.New("not exist key")
+}
+
+func init() {
+	config.Register("xml", &Config{})
+}

+ 125 - 0
vendor/github.com/astaxie/beego/config/xml/xml_test.go

@@ -0,0 +1,125 @@
+// 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 xml
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/astaxie/beego/config"
+)
+
+func TestXML(t *testing.T) {
+
+	var (
+		//xml parse should incluce in <config></config> tags
+		xmlcontext = `<?xml version="1.0" encoding="UTF-8"?>
+<config>
+<appname>beeapi</appname>
+<httpport>8080</httpport>
+<mysqlport>3600</mysqlport>
+<PI>3.1415976</PI>
+<runmode>dev</runmode>
+<autorender>false</autorender>
+<copyrequestbody>true</copyrequestbody>
+<path1>${GOPATH}</path1>
+<path2>${GOPATH||/home/go}</path2>
+<mysection>
+<id>1</id>
+<name>MySection</name>
+</mysection>
+</config>
+`
+		keyValue = map[string]interface{}{
+			"appname":         "beeapi",
+			"httpport":        8080,
+			"mysqlport":       int64(3600),
+			"PI":              3.1415976,
+			"runmode":         "dev",
+			"autorender":      false,
+			"copyrequestbody": true,
+			"path1":           os.Getenv("GOPATH"),
+			"path2":           os.Getenv("GOPATH"),
+			"error":           "",
+			"emptystrings":    []string{},
+		}
+	)
+
+	f, err := os.Create("testxml.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(xmlcontext)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testxml.conf")
+
+	xmlconf, err := config.NewConfig("xml", "testxml.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var xmlsection map[string]string
+	xmlsection, err = xmlconf.GetSection("mysection")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(xmlsection) == 0 {
+		t.Error("section should not be empty")
+	}
+
+	for k, v := range keyValue {
+
+		var (
+			value interface{}
+			err   error
+		)
+
+		switch v.(type) {
+		case int:
+			value, err = xmlconf.Int(k)
+		case int64:
+			value, err = xmlconf.Int64(k)
+		case float64:
+			value, err = xmlconf.Float(k)
+		case bool:
+			value, err = xmlconf.Bool(k)
+		case []string:
+			value = xmlconf.Strings(k)
+		case string:
+			value = xmlconf.String(k)
+		default:
+			value, err = xmlconf.DIY(k)
+		}
+		if err != nil {
+			t.Errorf("get key %q value fatal,%v err %s", k, v, err)
+		} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+			t.Errorf("get key %q value, want %v got %v .", k, v, value)
+		}
+
+	}
+
+	if err = xmlconf.Set("name", "astaxie"); err != nil {
+		t.Fatal(err)
+	}
+	if xmlconf.String("name") != "astaxie" {
+		t.Fatal("get name error")
+	}
+}

+ 297 - 0
vendor/github.com/astaxie/beego/config/yaml/yaml.go

@@ -0,0 +1,297 @@
+// 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 yaml for config provider
+//
+// depend on github.com/beego/goyaml2
+//
+// go install github.com/beego/goyaml2
+//
+// Usage:
+//  import(
+//   _ "github.com/astaxie/beego/config/yaml"
+//     "github.com/astaxie/beego/config"
+//  )
+//
+//  cnf, err := config.NewConfig("yaml", "config.yaml")
+//
+//More docs http://beego.me/docs/module/config.md
+package yaml
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"strings"
+	"sync"
+
+	"github.com/astaxie/beego/config"
+	"github.com/beego/goyaml2"
+)
+
+// Config is a yaml config parser and implements Config interface.
+type Config struct{}
+
+// Parse returns a ConfigContainer with parsed yaml config map.
+func (yaml *Config) Parse(filename string) (y config.Configer, err error) {
+	cnf, err := ReadYmlReader(filename)
+	if err != nil {
+		return
+	}
+	y = &ConfigContainer{
+		data: cnf,
+	}
+	return
+}
+
+// ParseData parse yaml data
+func (yaml *Config) ParseData(data []byte) (config.Configer, error) {
+	cnf, err := parseYML(data)
+	if err != nil {
+		return nil, err
+	}
+
+	return &ConfigContainer{
+		data: cnf,
+	}, nil
+}
+
+// ReadYmlReader Read yaml file to map.
+// if json like, use json package, unless goyaml2 package.
+func ReadYmlReader(path string) (cnf map[string]interface{}, err error) {
+	buf, err := ioutil.ReadFile(path)
+	if err != nil {
+		return
+	}
+
+	return parseYML(buf)
+}
+
+// parseYML parse yaml formatted []byte to map.
+func parseYML(buf []byte) (cnf map[string]interface{}, err error) {
+	if len(buf) < 3 {
+		return
+	}
+
+	if string(buf[0:1]) == "{" {
+		log.Println("Look like a Json, try json umarshal")
+		err = json.Unmarshal(buf, &cnf)
+		if err == nil {
+			log.Println("It is Json Map")
+			return
+		}
+	}
+
+	data, err := goyaml2.Read(bytes.NewBuffer(buf))
+	if err != nil {
+		log.Println("Goyaml2 ERR>", string(buf), err)
+		return
+	}
+
+	if data == nil {
+		log.Println("Goyaml2 output nil? Pls report bug\n" + string(buf))
+		return
+	}
+	cnf, ok := data.(map[string]interface{})
+	if !ok {
+		log.Println("Not a Map? >> ", string(buf), data)
+		cnf = nil
+	}
+	cnf = config.ExpandValueEnvForMap(cnf)
+	return
+}
+
+// ConfigContainer A Config represents the yaml configuration.
+type ConfigContainer struct {
+	data map[string]interface{}
+	sync.Mutex
+}
+
+// Bool returns the boolean value for a given key.
+func (c *ConfigContainer) Bool(key string) (bool, error) {
+	v, err := c.getData(key)
+	if err != nil {
+		return false, err
+	}
+	return config.ParseBool(v)
+}
+
+// DefaultBool return the bool value if has no error
+// otherwise return the defaultval
+func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool {
+	v, err := c.Bool(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int returns the integer value for a given key.
+func (c *ConfigContainer) Int(key string) (int, error) {
+	if v, err := c.getData(key); err != nil {
+		return 0, err
+	} else if vv, ok := v.(int); ok {
+		return vv, nil
+	} else if vv, ok := v.(int64); ok {
+		return int(vv), nil
+	}
+	return 0, errors.New("not int value")
+}
+
+// DefaultInt returns the integer value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
+	v, err := c.Int(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Int64 returns the int64 value for a given key.
+func (c *ConfigContainer) Int64(key string) (int64, error) {
+	if v, err := c.getData(key); err != nil {
+		return 0, err
+	} else if vv, ok := v.(int64); ok {
+		return vv, nil
+	}
+	return 0, errors.New("not bool value")
+}
+
+// DefaultInt64 returns the int64 value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
+	v, err := c.Int64(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// Float returns the float value for a given key.
+func (c *ConfigContainer) Float(key string) (float64, error) {
+	if v, err := c.getData(key); err != nil {
+		return 0.0, err
+	} else if vv, ok := v.(float64); ok {
+		return vv, nil
+	} else if vv, ok := v.(int); ok {
+		return float64(vv), nil
+	} else if vv, ok := v.(int64); ok {
+		return float64(vv), nil
+	}
+	return 0.0, errors.New("not float64 value")
+}
+
+// DefaultFloat returns the float64 value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
+	v, err := c.Float(key)
+	if err != nil {
+		return defaultval
+	}
+	return v
+}
+
+// String returns the string value for a given key.
+func (c *ConfigContainer) String(key string) string {
+	if v, err := c.getData(key); err == nil {
+		if vv, ok := v.(string); ok {
+			return vv
+		}
+	}
+	return ""
+}
+
+// DefaultString returns the string value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
+	v := c.String(key)
+	if v == "" {
+		return defaultval
+	}
+	return v
+}
+
+// Strings returns the []string value for a given key.
+func (c *ConfigContainer) Strings(key string) []string {
+	v := c.String(key)
+	if v == "" {
+		return nil
+	}
+	return strings.Split(v, ";")
+}
+
+// DefaultStrings returns the []string value for a given key.
+// if err != nil return defaltval
+func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
+	v := c.Strings(key)
+	if v == nil {
+		return defaultval
+	}
+	return v
+}
+
+// GetSection returns map for the given section
+func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
+
+	if v, ok := c.data[section]; ok {
+		return v.(map[string]string), nil
+	}
+	return nil, errors.New("not exist section")
+}
+
+// SaveConfigFile save the config into file
+func (c *ConfigContainer) SaveConfigFile(filename string) (err error) {
+	// Write configuration file by filename.
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	err = goyaml2.Write(f, c.data)
+	return err
+}
+
+// Set writes a new value for key.
+func (c *ConfigContainer) Set(key, val string) error {
+	c.Lock()
+	defer c.Unlock()
+	c.data[key] = val
+	return nil
+}
+
+// DIY returns the raw value by a given key.
+func (c *ConfigContainer) DIY(key string) (v interface{}, err error) {
+	return c.getData(key)
+}
+
+func (c *ConfigContainer) getData(key string) (interface{}, error) {
+
+	if len(key) == 0 {
+		return nil, errors.New("key is empty")
+	}
+
+	if v, ok := c.data[key]; ok {
+		return v, nil
+	}
+	return nil, fmt.Errorf("not exist key %q", key)
+}
+
+func init() {
+	config.Register("yaml", &Config{})
+}

+ 115 - 0
vendor/github.com/astaxie/beego/config/yaml/yaml_test.go

@@ -0,0 +1,115 @@
+// 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 yaml
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/astaxie/beego/config"
+)
+
+func TestYaml(t *testing.T) {
+
+	var (
+		yamlcontext = `
+"appname": beeapi
+"httpport": 8080
+"mysqlport": 3600
+"PI": 3.1415976
+"runmode": dev
+"autorender": false
+"copyrequestbody": true
+"PATH": GOPATH
+"path1": ${GOPATH}
+"path2": ${GOPATH||/home/go}
+"empty": "" 
+`
+
+		keyValue = map[string]interface{}{
+			"appname":         "beeapi",
+			"httpport":        8080,
+			"mysqlport":       int64(3600),
+			"PI":              3.1415976,
+			"runmode":         "dev",
+			"autorender":      false,
+			"copyrequestbody": true,
+			"PATH":            "GOPATH",
+			"path1":           os.Getenv("GOPATH"),
+			"path2":           os.Getenv("GOPATH"),
+			"error":           "",
+			"emptystrings":    []string{},
+		}
+	)
+	f, err := os.Create("testyaml.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = f.WriteString(yamlcontext)
+	if err != nil {
+		f.Close()
+		t.Fatal(err)
+	}
+	f.Close()
+	defer os.Remove("testyaml.conf")
+	yamlconf, err := config.NewConfig("yaml", "testyaml.conf")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if yamlconf.String("appname") != "beeapi" {
+		t.Fatal("appname not equal to beeapi")
+	}
+
+	for k, v := range keyValue {
+
+		var (
+			value interface{}
+			err   error
+		)
+
+		switch v.(type) {
+		case int:
+			value, err = yamlconf.Int(k)
+		case int64:
+			value, err = yamlconf.Int64(k)
+		case float64:
+			value, err = yamlconf.Float(k)
+		case bool:
+			value, err = yamlconf.Bool(k)
+		case []string:
+			value = yamlconf.Strings(k)
+		case string:
+			value = yamlconf.String(k)
+		default:
+			value, err = yamlconf.DIY(k)
+		}
+		if err != nil {
+			t.Errorf("get key %q value fatal,%v err %s", k, v, err)
+		} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
+			t.Errorf("get key %q value, want %v got %v .", k, v, value)
+		}
+
+	}
+
+	if err = yamlconf.Set("name", "astaxie"); err != nil {
+		t.Fatal(err)
+	}
+	if yamlconf.String("name") != "astaxie" {
+		t.Fatal("get name error")
+	}
+
+}

+ 138 - 0
vendor/github.com/astaxie/beego/config_test.go

@@ -0,0 +1,138 @@
+// 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 beego
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+
+	"github.com/astaxie/beego/config"
+)
+
+func TestDefaults(t *testing.T) {
+	if BConfig.WebConfig.FlashName != "BEEGO_FLASH" {
+		t.Errorf("FlashName was not set to default.")
+	}
+
+	if BConfig.WebConfig.FlashSeparator != "BEEGOFLASH" {
+		t.Errorf("FlashName was not set to default.")
+	}
+}
+
+func TestAssignConfig_01(t *testing.T) {
+	_BConfig := &Config{}
+	_BConfig.AppName = "beego_test"
+	jcf := &config.JSONConfig{}
+	ac, _ := jcf.ParseData([]byte(`{"AppName":"beego_json"}`))
+	assignSingleConfig(_BConfig, ac)
+	if _BConfig.AppName != "beego_json" {
+		t.Log(_BConfig)
+		t.FailNow()
+	}
+}
+
+func TestAssignConfig_02(t *testing.T) {
+	_BConfig := &Config{}
+	bs, _ := json.Marshal(newBConfig())
+
+	jsonMap := map[string]interface{}{}
+	json.Unmarshal(bs, &jsonMap)
+
+	configMap := map[string]interface{}{}
+	for k, v := range jsonMap {
+		if reflect.TypeOf(v).Kind() == reflect.Map {
+			for k1, v1 := range v.(map[string]interface{}) {
+				if reflect.TypeOf(v1).Kind() == reflect.Map {
+					for k2, v2 := range v1.(map[string]interface{}) {
+						configMap[k2] = v2
+					}
+				} else {
+					configMap[k1] = v1
+				}
+			}
+		} else {
+			configMap[k] = v
+		}
+	}
+	configMap["MaxMemory"] = 1024
+	configMap["Graceful"] = true
+	configMap["XSRFExpire"] = 32
+	configMap["SessionProviderConfig"] = "file"
+	configMap["FileLineNum"] = true
+
+	jcf := &config.JSONConfig{}
+	bs, _ = json.Marshal(configMap)
+	ac, _ := jcf.ParseData([]byte(bs))
+
+	for _, i := range []interface{}{_BConfig, &_BConfig.Listen, &_BConfig.WebConfig, &_BConfig.Log, &_BConfig.WebConfig.Session} {
+		assignSingleConfig(i, ac)
+	}
+
+	if _BConfig.MaxMemory != 1024 {
+		t.Log(_BConfig.MaxMemory)
+		t.FailNow()
+	}
+
+	if !_BConfig.Listen.Graceful {
+		t.Log(_BConfig.Listen.Graceful)
+		t.FailNow()
+	}
+
+	if _BConfig.WebConfig.XSRFExpire != 32 {
+		t.Log(_BConfig.WebConfig.XSRFExpire)
+		t.FailNow()
+	}
+
+	if _BConfig.WebConfig.Session.SessionProviderConfig != "file" {
+		t.Log(_BConfig.WebConfig.Session.SessionProviderConfig)
+		t.FailNow()
+	}
+
+	if !_BConfig.Log.FileLineNum {
+		t.Log(_BConfig.Log.FileLineNum)
+		t.FailNow()
+	}
+
+}
+
+func TestAssignConfig_03(t *testing.T) {
+	jcf := &config.JSONConfig{}
+	ac, _ := jcf.ParseData([]byte(`{"AppName":"beego"}`))
+	ac.Set("AppName", "test_app")
+	ac.Set("RunMode", "online")
+	ac.Set("StaticDir", "download:down download2:down2")
+	ac.Set("StaticExtensionsToGzip", ".css,.js,.html,.jpg,.png")
+	assignConfig(ac)
+
+	t.Logf("%#v", BConfig)
+
+	if BConfig.AppName != "test_app" {
+		t.FailNow()
+	}
+
+	if BConfig.RunMode != "online" {
+		t.FailNow()
+	}
+	if BConfig.WebConfig.StaticDir["/download"] != "down" {
+		t.FailNow()
+	}
+	if BConfig.WebConfig.StaticDir["/download2"] != "down2" {
+		t.FailNow()
+	}
+	if len(BConfig.WebConfig.StaticExtensionsToGzip) != 5 {
+		t.FailNow()
+	}
+}

+ 59 - 0
vendor/github.com/astaxie/beego/context/acceptencoder_test.go

@@ -0,0 +1,59 @@
+// Copyright 2015 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+	"net/http"
+	"testing"
+)
+
+func Test_ExtractEncoding(t *testing.T) {
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip,deflate"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"deflate,gzip"}}}) != "deflate" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=.5,deflate"}}}) != "deflate" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=.5,deflate;q=0.3"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0,deflate"}}}) != "deflate" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"deflate;q=0.5,gzip;q=0.5,identity"}}}) != "" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"*"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"x,gzip,deflate"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip,x,deflate"}}}) != "gzip" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0.5,x,deflate"}}}) != "deflate" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"x"}}}) != "" {
+		t.Fail()
+	}
+	if parseEncoding(&http.Request{Header: map[string][]string{"Accept-Encoding": {"gzip;q=0.5,x;q=0.8"}}}) != "gzip" {
+		t.Fail()
+	}
+}

+ 47 - 0
vendor/github.com/astaxie/beego/context/context_test.go

@@ -0,0 +1,47 @@
+// Copyright 2016 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package context
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func TestXsrfReset_01(t *testing.T) {
+	r := &http.Request{}
+	c := NewContext()
+	c.Request = r
+	c.ResponseWriter = &Response{}
+	c.ResponseWriter.reset(httptest.NewRecorder())
+	c.Output.Reset(c)
+	c.Input.Reset(c)
+	c.XSRFToken("key", 16)
+	if c._xsrfToken == "" {
+		t.FailNow()
+	}
+	token := c._xsrfToken
+	c.Reset(&Response{ResponseWriter: httptest.NewRecorder()}, r)
+	if c._xsrfToken != "" {
+		t.FailNow()
+	}
+	c.XSRFToken("key", 16)
+	if c._xsrfToken == "" {
+		t.FailNow()
+	}
+	if token == c._xsrfToken {
+		t.FailNow()
+	}
+}

+ 207 - 0
vendor/github.com/astaxie/beego/context/input_test.go

@@ -0,0 +1,207 @@
+// 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 context
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"reflect"
+	"testing"
+)
+
+func TestBind(t *testing.T) {
+	type testItem struct {
+		field string
+		empty interface{}
+		want  interface{}
+	}
+	type Human struct {
+		ID   int
+		Nick string
+		Pwd  string
+		Ms   bool
+	}
+
+	cases := []struct {
+		request string
+		valueGp []testItem
+	}{
+		{"/?p=str", []testItem{{"p", interface{}(""), interface{}("str")}}},
+
+		{"/?p=", []testItem{{"p", "", ""}}},
+		{"/?p=str", []testItem{{"p", "", "str"}}},
+
+		{"/?p=123", []testItem{{"p", 0, 123}}},
+		{"/?p=123", []testItem{{"p", uint(0), uint(123)}}},
+
+		{"/?p=1.0", []testItem{{"p", 0.0, 1.0}}},
+		{"/?p=1", []testItem{{"p", false, true}}},
+
+		{"/?p=true", []testItem{{"p", false, true}}},
+		{"/?p=ON", []testItem{{"p", false, true}}},
+		{"/?p=on", []testItem{{"p", false, true}}},
+		{"/?p=1", []testItem{{"p", false, true}}},
+		{"/?p=2", []testItem{{"p", false, false}}},
+		{"/?p=false", []testItem{{"p", false, false}}},
+
+		{"/?p[a]=1&p[b]=2&p[c]=3", []testItem{{"p", map[string]int{}, map[string]int{"a": 1, "b": 2, "c": 3}}}},
+		{"/?p[a]=v1&p[b]=v2&p[c]=v3", []testItem{{"p", map[string]string{}, map[string]string{"a": "v1", "b": "v2", "c": "v3"}}}},
+
+		{"/?p[]=8&p[]=9&p[]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}},
+		{"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}},
+		{"/?p[0]=8&p[1]=9&p[2]=10&p[5]=14", []testItem{{"p", []int{}, []int{8, 9, 10, 0, 0, 14}}}},
+		{"/?p[0]=8.0&p[1]=9.0&p[2]=10.0", []testItem{{"p", []float64{}, []float64{8.0, 9.0, 10.0}}}},
+
+		{"/?p[]=10&p[]=9&p[]=8", []testItem{{"p", []string{}, []string{"10", "9", "8"}}}},
+		{"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []string{}, []string{"8", "9", "10"}}}},
+
+		{"/?p[0]=true&p[1]=false&p[2]=true&p[5]=1&p[6]=ON&p[7]=other", []testItem{{"p", []bool{}, []bool{true, false, true, false, false, true, true, false}}}},
+
+		{"/?human.Nick=astaxie", []testItem{{"human", Human{}, Human{Nick: "astaxie"}}}},
+		{"/?human.ID=888&human.Nick=astaxie&human.Ms=true&human[Pwd]=pass", []testItem{{"human", Human{}, Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass"}}}},
+		{"/?human[0].ID=888&human[0].Nick=astaxie&human[0].Ms=true&human[0][Pwd]=pass01&human[1].ID=999&human[1].Nick=ysqi&human[1].Ms=On&human[1].Pwd=pass02",
+			[]testItem{{"human", []Human{}, []Human{
+				Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"},
+				Human{ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"},
+			}}}},
+
+		{
+			"/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&human.Nick=astaxie",
+			[]testItem{
+				{"id", 0, 123},
+				{"isok", false, true},
+				{"ft", 0.0, 1.2},
+				{"ol", []int{}, []int{1, 2}},
+				{"ul", []string{}, []string{"str", "array"}},
+				{"human", Human{}, Human{Nick: "astaxie"}},
+			},
+		},
+	}
+	for _, c := range cases {
+		r, _ := http.NewRequest("GET", c.request, nil)
+		beegoInput := NewInput()
+		beegoInput.Context = NewContext()
+		beegoInput.Context.Reset(httptest.NewRecorder(), r)
+
+		for _, item := range c.valueGp {
+			got := item.empty
+			err := beegoInput.Bind(&got, item.field)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if !reflect.DeepEqual(got, item.want) {
+				t.Fatalf("Bind %q error,should be:\n%#v \ngot:\n%#v", item.field, item.want, got)
+			}
+		}
+
+	}
+}
+
+func TestSubDomain(t *testing.T) {
+	r, _ := http.NewRequest("GET", "http://www.example.com/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
+	beegoInput := NewInput()
+	beegoInput.Context = NewContext()
+	beegoInput.Context.Reset(httptest.NewRecorder(), r)
+
+	subdomain := beegoInput.SubDomains()
+	if subdomain != "www" {
+		t.Fatal("Subdomain parse error, got" + subdomain)
+	}
+
+	r, _ = http.NewRequest("GET", "http://localhost/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "" {
+		t.Fatal("Subdomain parse error, should be empty, got " + beegoInput.SubDomains())
+	}
+
+	r, _ = http.NewRequest("GET", "http://aa.bb.example.com/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "aa.bb" {
+		t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+	}
+
+	/* TODO Fix this
+	r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "" {
+		t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+	}
+	*/
+
+	r, _ = http.NewRequest("GET", "http://example.com/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "" {
+		t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+	}
+
+	r, _ = http.NewRequest("GET", "http://aa.bb.cc.dd.example.com/", nil)
+	beegoInput.Context.Request = r
+	if beegoInput.SubDomains() != "aa.bb.cc.dd" {
+		t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
+	}
+}
+
+func TestParams(t *testing.T) {
+	inp := NewInput()
+
+	inp.SetParam("p1", "val1_ver1")
+	inp.SetParam("p2", "val2_ver1")
+	inp.SetParam("p3", "val3_ver1")
+	if l := inp.ParamsLen(); l != 3 {
+		t.Fatalf("Input.ParamsLen wrong value: %d, expected %d", l, 3)
+	}
+
+	if val := inp.Param("p1"); val != "val1_ver1" {
+		t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver1")
+	}
+	if val := inp.Param("p3"); val != "val3_ver1" {
+		t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val3_ver1")
+	}
+	vals := inp.Params()
+	expected := map[string]string{
+		"p1": "val1_ver1",
+		"p2": "val2_ver1",
+		"p3": "val3_ver1",
+	}
+	if !reflect.DeepEqual(vals, expected) {
+		t.Fatalf("Input.Params wrong value: %s, expected %s", vals, expected)
+	}
+
+	// overwriting existing params
+	inp.SetParam("p1", "val1_ver2")
+	inp.SetParam("p2", "val2_ver2")
+	expected = map[string]string{
+		"p1": "val1_ver2",
+		"p2": "val2_ver2",
+		"p3": "val3_ver1",
+	}
+	vals = inp.Params()
+	if !reflect.DeepEqual(vals, expected) {
+		t.Fatalf("Input.Params wrong value: %s, expected %s", vals, expected)
+	}
+
+	if l := inp.ParamsLen(); l != 3 {
+		t.Fatalf("Input.ParamsLen wrong value: %d, expected %d", l, 3)
+	}
+
+	if val := inp.Param("p1"); val != "val1_ver2" {
+		t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver2")
+	}
+
+	if val := inp.Param("p2"); val != "val2_ver2" {
+		t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver2")
+	}
+
+}

+ 181 - 0
vendor/github.com/astaxie/beego/controller_test.go

@@ -0,0 +1,181 @@
+// 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 beego
+
+import (
+	"math"
+	"strconv"
+	"testing"
+
+	"github.com/astaxie/beego/context"
+	"os"
+	"path/filepath"
+)
+
+func TestGetInt(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt("age")
+	if val != 40 {
+		t.Errorf("TestGetInt expect 40,get %T,%v", val, val)
+	}
+}
+
+func TestGetInt8(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt8("age")
+	if val != 40 {
+		t.Errorf("TestGetInt8 expect 40,get %T,%v", val, val)
+	}
+	//Output: int8
+}
+
+func TestGetInt16(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt16("age")
+	if val != 40 {
+		t.Errorf("TestGetInt16 expect 40,get %T,%v", val, val)
+	}
+}
+
+func TestGetInt32(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt32("age")
+	if val != 40 {
+		t.Errorf("TestGetInt32 expect 40,get %T,%v", val, val)
+	}
+}
+
+func TestGetInt64(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", "40")
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetInt64("age")
+	if val != 40 {
+		t.Errorf("TestGeetInt64 expect 40,get %T,%v", val, val)
+	}
+}
+
+func TestGetUint8(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", strconv.FormatUint(math.MaxUint8, 10))
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetUint8("age")
+	if val != math.MaxUint8 {
+		t.Errorf("TestGetUint8 expect %v,get %T,%v", math.MaxUint8, val, val)
+	}
+}
+
+func TestGetUint16(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", strconv.FormatUint(math.MaxUint16, 10))
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetUint16("age")
+	if val != math.MaxUint16 {
+		t.Errorf("TestGetUint16 expect %v,get %T,%v", math.MaxUint16, val, val)
+	}
+}
+
+func TestGetUint32(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", strconv.FormatUint(math.MaxUint32, 10))
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetUint32("age")
+	if val != math.MaxUint32 {
+		t.Errorf("TestGetUint32 expect %v,get %T,%v", math.MaxUint32, val, val)
+	}
+}
+
+func TestGetUint64(t *testing.T) {
+	i := context.NewInput()
+	i.SetParam("age", strconv.FormatUint(math.MaxUint64, 10))
+	ctx := &context.Context{Input: i}
+	ctrlr := Controller{Ctx: ctx}
+	val, _ := ctrlr.GetUint64("age")
+	if val != math.MaxUint64 {
+		t.Errorf("TestGetUint64 expect %v,get %T,%v", uint64(math.MaxUint64), val, val)
+	}
+}
+
+func TestAdditionalViewPaths(t *testing.T) {
+	dir1 := "_beeTmp"
+	dir2 := "_beeTmp2"
+	defer os.RemoveAll(dir1)
+	defer os.RemoveAll(dir2)
+
+	dir1file := "file1.tpl"
+	dir2file := "file2.tpl"
+
+	genFile := func(dir string, name string, content string) {
+		os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777)
+		if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+			t.Fatal(err)
+		} else {
+			defer f.Close()
+			f.WriteString(content)
+			f.Close()
+		}
+
+	}
+	genFile(dir1, dir1file, `<div>{{.Content}}</div>`)
+	genFile(dir2, dir2file, `<html>{{.Content}}</html>`)
+
+	AddViewPath(dir1)
+	AddViewPath(dir2)
+
+	ctrl := Controller{
+		TplName:  "file1.tpl",
+		ViewPath: dir1,
+	}
+	ctrl.Data = map[interface{}]interface{}{
+		"Content": "value2",
+	}
+	if result, err := ctrl.RenderString(); err != nil {
+		t.Fatal(err)
+	} else {
+		if result != "<div>value2</div>" {
+			t.Fatalf("TestAdditionalViewPaths expect %s got %s", "<div>value2</div>", result)
+		}
+	}
+
+	func() {
+		ctrl.TplName = "file2.tpl"
+		defer func() {
+			if r := recover(); r == nil {
+				t.Fatal("TestAdditionalViewPaths expected error")
+			}
+		}()
+		ctrl.RenderString();
+	}()
+
+	ctrl.TplName = "file2.tpl"
+	ctrl.ViewPath = dir2
+	ctrl.RenderString();
+}

+ 88 - 0
vendor/github.com/astaxie/beego/error_test.go

@@ -0,0 +1,88 @@
+// Copyright 2016 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package beego
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"strconv"
+	"strings"
+	"testing"
+)
+
+type errorTestController struct {
+	Controller
+}
+
+const parseCodeError = "parse code error"
+
+func (ec *errorTestController) Get() {
+	errorCode, err := ec.GetInt("code")
+	if err != nil {
+		ec.Abort(parseCodeError)
+	}
+	if errorCode != 0 {
+		ec.CustomAbort(errorCode, ec.GetString("code"))
+	}
+	ec.Abort("404")
+}
+
+func TestErrorCode_01(t *testing.T) {
+	registerDefaultErrorHandler()
+	for k := range ErrorMaps {
+		r, _ := http.NewRequest("GET", "/error?code="+k, nil)
+		w := httptest.NewRecorder()
+
+		handler := NewControllerRegister()
+		handler.Add("/error", &errorTestController{})
+		handler.ServeHTTP(w, r)
+		code, _ := strconv.Atoi(k)
+		if w.Code != code {
+			t.Fail()
+		}
+		if !strings.Contains(string(w.Body.Bytes()), http.StatusText(code)) {
+			t.Fail()
+		}
+	}
+}
+
+func TestErrorCode_02(t *testing.T) {
+	registerDefaultErrorHandler()
+	r, _ := http.NewRequest("GET", "/error?code=0", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/error", &errorTestController{})
+	handler.ServeHTTP(w, r)
+	if w.Code != 404 {
+		t.Fail()
+	}
+}
+
+func TestErrorCode_03(t *testing.T) {
+	registerDefaultErrorHandler()
+	r, _ := http.NewRequest("GET", "/error?code=panic", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/error", &errorTestController{})
+	handler.ServeHTTP(w, r)
+	if w.Code != 200 {
+		t.Fail()
+	}
+	if string(w.Body.Bytes()) != parseCodeError {
+		t.Fail()
+	}
+}

+ 68 - 0
vendor/github.com/astaxie/beego/filter_test.go

@@ -0,0 +1,68 @@
+// 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 beego
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/astaxie/beego/context"
+)
+
+var FilterUser = func(ctx *context.Context) {
+	ctx.Output.Body([]byte("i am " + ctx.Input.Param(":last") + ctx.Input.Param(":first")))
+}
+
+func TestFilter(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/person/asta/Xie", nil)
+	w := httptest.NewRecorder()
+	handler := NewControllerRegister()
+	handler.InsertFilter("/person/:last/:first", BeforeRouter, FilterUser)
+	handler.Add("/person/:last/:first", &TestController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am astaXie" {
+		t.Errorf("user define func can't run")
+	}
+}
+
+var FilterAdminUser = func(ctx *context.Context) {
+	ctx.Output.Body([]byte("i am admin"))
+}
+
+// Filter pattern /admin/:all
+// all url like    /admin/    /admin/xie    will all get filter
+
+func TestPatternTwo(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/admin/", nil)
+	w := httptest.NewRecorder()
+	handler := NewControllerRegister()
+	handler.InsertFilter("/admin/?:all", BeforeRouter, FilterAdminUser)
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am admin" {
+		t.Errorf("filter /admin/ can't run")
+	}
+}
+
+func TestPatternThree(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/admin/astaxie", nil)
+	w := httptest.NewRecorder()
+	handler := NewControllerRegister()
+	handler.InsertFilter("/admin/:all", BeforeRouter, FilterAdminUser)
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am admin" {
+		t.Errorf("filter /admin/astaxie can't run")
+	}
+}

+ 54 - 0
vendor/github.com/astaxie/beego/flash_test.go

@@ -0,0 +1,54 @@
+// 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 beego
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+type TestFlashController struct {
+	Controller
+}
+
+func (t *TestFlashController) TestWriteFlash() {
+	flash := NewFlash()
+	flash.Notice("TestFlashString")
+	flash.Store(&t.Controller)
+	// we choose to serve json because we don't want to load a template html file
+	t.ServeJSON(true)
+}
+
+func TestFlashHeader(t *testing.T) {
+	// create fake GET request
+	r, _ := http.NewRequest("GET", "/", nil)
+	w := httptest.NewRecorder()
+
+	// setup the handler
+	handler := NewControllerRegister()
+	handler.Add("/", &TestFlashController{}, "get:TestWriteFlash")
+	handler.ServeHTTP(w, r)
+
+	// get the Set-Cookie value
+	sc := w.Header().Get("Set-Cookie")
+	// match for the expected header
+	res := strings.Contains(sc, "BEEGO_FLASH=%00notice%23BEEGOFLASH%23TestFlashString%00")
+	// validate the assertion
+	if res != true {
+		t.Errorf("TestFlashHeader() unable to validate flash message")
+	}
+}

+ 97 - 0
vendor/github.com/astaxie/beego/httplib/README.md

@@ -0,0 +1,97 @@
+# httplib
+httplib is an libs help you to curl remote url.
+
+# How to use?
+
+## GET
+you can use Get to crawl data.
+
+	import "github.com/astaxie/beego/httplib"
+	
+	str, err := httplib.Get("http://beego.me/").String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+	
+## POST
+POST data to remote url
+
+	req := httplib.Post("http://beego.me/")
+	req.Param("username","astaxie")
+	req.Param("password","123456")
+	str, err := req.String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+
+## Set timeout
+
+The default timeout is `60` seconds, function prototype:
+
+	SetTimeout(connectTimeout, readWriteTimeout time.Duration)
+
+Exmaple:
+
+	// GET
+	httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
+	
+	// POST
+	httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
+
+
+## Debug
+
+If you want to debug the request info, set the debug on
+
+	httplib.Get("http://beego.me/").Debug(true)
+	
+## Set HTTP Basic Auth
+
+	str, err := Get("http://beego.me/").SetBasicAuth("user", "passwd").String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+	
+## Set HTTPS
+
+If request url is https, You can set the client support TSL:
+
+	httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
+	
+More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config	
+
+## Set HTTP Version
+
+some servers need to specify the protocol version of HTTP
+
+	httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1")
+	
+## Set Cookie
+
+some http request need setcookie. So set it like this:
+
+	cookie := &http.Cookie{}
+	cookie.Name = "username"
+	cookie.Value  = "astaxie"
+	httplib.Get("http://beego.me/").SetCookie(cookie)
+
+## Upload file
+
+httplib support mutil file upload, use `req.PostFile()`
+
+	req := httplib.Post("http://beego.me/")
+	req.Param("username","astaxie")
+	req.PostFile("uploadfile1", "httplib.pdf")
+	str, err := req.String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+
+
+See godoc for further documentation and examples.
+
+* [godoc.org/github.com/astaxie/beego/httplib](https://godoc.org/github.com/astaxie/beego/httplib)

+ 585 - 0
vendor/github.com/astaxie/beego/httplib/httplib.go

@@ -0,0 +1,585 @@
+// 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 httplib is used as http.Client
+// Usage:
+//
+// import "github.com/astaxie/beego/httplib"
+//
+//	b := httplib.Post("http://beego.me/")
+//	b.Param("username","astaxie")
+//	b.Param("password","123456")
+//	b.PostFile("uploadfile1", "httplib.pdf")
+//	b.PostFile("uploadfile2", "httplib.txt")
+//	str, err := b.String()
+//	if err != nil {
+//		t.Fatal(err)
+//	}
+//	fmt.Println(str)
+//
+//  more docs http://beego.me/docs/module/httplib.md
+package httplib
+
+import (
+	"bytes"
+	"compress/gzip"
+	"crypto/tls"
+	"encoding/json"
+	"encoding/xml"
+	"io"
+	"io/ioutil"
+	"log"
+	"mime/multipart"
+	"net"
+	"net/http"
+	"net/http/cookiejar"
+	"net/http/httputil"
+	"net/url"
+	"os"
+	"strings"
+	"sync"
+	"time"
+)
+
+var defaultSetting = BeegoHTTPSettings{
+	UserAgent:        "beegoServer",
+	ConnectTimeout:   60 * time.Second,
+	ReadWriteTimeout: 60 * time.Second,
+	Gzip:             true,
+	DumpBody:         true,
+}
+
+var defaultCookieJar http.CookieJar
+var settingMutex sync.Mutex
+
+// createDefaultCookie creates a global cookiejar to store cookies.
+func createDefaultCookie() {
+	settingMutex.Lock()
+	defer settingMutex.Unlock()
+	defaultCookieJar, _ = cookiejar.New(nil)
+}
+
+// SetDefaultSetting Overwrite default settings
+func SetDefaultSetting(setting BeegoHTTPSettings) {
+	settingMutex.Lock()
+	defer settingMutex.Unlock()
+	defaultSetting = setting
+}
+
+// NewBeegoRequest return *BeegoHttpRequest with specific method
+func NewBeegoRequest(rawurl, method string) *BeegoHTTPRequest {
+	var resp http.Response
+	u, err := url.Parse(rawurl)
+	if err != nil {
+		log.Println("Httplib:", err)
+	}
+	req := http.Request{
+		URL:        u,
+		Method:     method,
+		Header:     make(http.Header),
+		Proto:      "HTTP/1.1",
+		ProtoMajor: 1,
+		ProtoMinor: 1,
+	}
+	return &BeegoHTTPRequest{
+		url:     rawurl,
+		req:     &req,
+		params:  map[string][]string{},
+		files:   map[string]string{},
+		setting: defaultSetting,
+		resp:    &resp,
+	}
+}
+
+// Get returns *BeegoHttpRequest with GET method.
+func Get(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "GET")
+}
+
+// Post returns *BeegoHttpRequest with POST method.
+func Post(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "POST")
+}
+
+// Put returns *BeegoHttpRequest with PUT method.
+func Put(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "PUT")
+}
+
+// Delete returns *BeegoHttpRequest DELETE method.
+func Delete(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "DELETE")
+}
+
+// Head returns *BeegoHttpRequest with HEAD method.
+func Head(url string) *BeegoHTTPRequest {
+	return NewBeegoRequest(url, "HEAD")
+}
+
+// BeegoHTTPSettings is the http.Client setting
+type BeegoHTTPSettings struct {
+	ShowDebug        bool
+	UserAgent        string
+	ConnectTimeout   time.Duration
+	ReadWriteTimeout time.Duration
+	TLSClientConfig  *tls.Config
+	Proxy            func(*http.Request) (*url.URL, error)
+	Transport        http.RoundTripper
+	CheckRedirect    func(req *http.Request, via []*http.Request) error
+	EnableCookie     bool
+	Gzip             bool
+	DumpBody         bool
+	Retries          int // if set to -1 means will retry forever
+}
+
+// BeegoHTTPRequest provides more useful methods for requesting one url than http.Request.
+type BeegoHTTPRequest struct {
+	url     string
+	req     *http.Request
+	params  map[string][]string
+	files   map[string]string
+	setting BeegoHTTPSettings
+	resp    *http.Response
+	body    []byte
+	dump    []byte
+}
+
+// GetRequest return the request object
+func (b *BeegoHTTPRequest) GetRequest() *http.Request {
+	return b.req
+}
+
+// Setting Change request settings
+func (b *BeegoHTTPRequest) Setting(setting BeegoHTTPSettings) *BeegoHTTPRequest {
+	b.setting = setting
+	return b
+}
+
+// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
+func (b *BeegoHTTPRequest) SetBasicAuth(username, password string) *BeegoHTTPRequest {
+	b.req.SetBasicAuth(username, password)
+	return b
+}
+
+// SetEnableCookie sets enable/disable cookiejar
+func (b *BeegoHTTPRequest) SetEnableCookie(enable bool) *BeegoHTTPRequest {
+	b.setting.EnableCookie = enable
+	return b
+}
+
+// SetUserAgent sets User-Agent header field
+func (b *BeegoHTTPRequest) SetUserAgent(useragent string) *BeegoHTTPRequest {
+	b.setting.UserAgent = useragent
+	return b
+}
+
+// Debug sets show debug or not when executing request.
+func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest {
+	b.setting.ShowDebug = isdebug
+	return b
+}
+
+// Retries sets Retries times.
+// default is 0 means no retried.
+// -1 means retried forever.
+// others means retried times.
+func (b *BeegoHTTPRequest) Retries(times int) *BeegoHTTPRequest {
+	b.setting.Retries = times
+	return b
+}
+
+// DumpBody setting whether need to Dump the Body.
+func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
+	b.setting.DumpBody = isdump
+	return b
+}
+
+// DumpRequest return the DumpRequest
+func (b *BeegoHTTPRequest) DumpRequest() []byte {
+	return b.dump
+}
+
+// SetTimeout sets connect time out and read-write time out for BeegoRequest.
+func (b *BeegoHTTPRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHTTPRequest {
+	b.setting.ConnectTimeout = connectTimeout
+	b.setting.ReadWriteTimeout = readWriteTimeout
+	return b
+}
+
+// SetTLSClientConfig sets tls connection configurations if visiting https url.
+func (b *BeegoHTTPRequest) SetTLSClientConfig(config *tls.Config) *BeegoHTTPRequest {
+	b.setting.TLSClientConfig = config
+	return b
+}
+
+// Header add header item string in request.
+func (b *BeegoHTTPRequest) Header(key, value string) *BeegoHTTPRequest {
+	b.req.Header.Set(key, value)
+	return b
+}
+
+// SetHost set the request host
+func (b *BeegoHTTPRequest) SetHost(host string) *BeegoHTTPRequest {
+	b.req.Host = host
+	return b
+}
+
+// SetProtocolVersion Set the protocol version for incoming requests.
+// Client requests always use HTTP/1.1.
+func (b *BeegoHTTPRequest) SetProtocolVersion(vers string) *BeegoHTTPRequest {
+	if len(vers) == 0 {
+		vers = "HTTP/1.1"
+	}
+
+	major, minor, ok := http.ParseHTTPVersion(vers)
+	if ok {
+		b.req.Proto = vers
+		b.req.ProtoMajor = major
+		b.req.ProtoMinor = minor
+	}
+
+	return b
+}
+
+// SetCookie add cookie into request.
+func (b *BeegoHTTPRequest) SetCookie(cookie *http.Cookie) *BeegoHTTPRequest {
+	b.req.Header.Add("Cookie", cookie.String())
+	return b
+}
+
+// SetTransport set the setting transport
+func (b *BeegoHTTPRequest) SetTransport(transport http.RoundTripper) *BeegoHTTPRequest {
+	b.setting.Transport = transport
+	return b
+}
+
+// SetProxy set the http proxy
+// example:
+//
+//	func(req *http.Request) (*url.URL, error) {
+// 		u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
+// 		return u, nil
+// 	}
+func (b *BeegoHTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHTTPRequest {
+	b.setting.Proxy = proxy
+	return b
+}
+
+// SetCheckRedirect specifies the policy for handling redirects.
+//
+// If CheckRedirect is nil, the Client uses its default policy,
+// which is to stop after 10 consecutive requests.
+func (b *BeegoHTTPRequest) SetCheckRedirect(redirect func(req *http.Request, via []*http.Request) error) *BeegoHTTPRequest {
+	b.setting.CheckRedirect = redirect
+	return b
+}
+
+// Param adds query param in to request.
+// params build query string as ?key1=value1&key2=value2...
+func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest {
+	if param, ok := b.params[key]; ok {
+		b.params[key] = append(param, value)
+	} else {
+		b.params[key] = []string{value}
+	}
+	return b
+}
+
+// PostFile add a post file to the request
+func (b *BeegoHTTPRequest) PostFile(formname, filename string) *BeegoHTTPRequest {
+	b.files[formname] = filename
+	return b
+}
+
+// Body adds request raw body.
+// it supports string and []byte.
+func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
+	switch t := data.(type) {
+	case string:
+		bf := bytes.NewBufferString(t)
+		b.req.Body = ioutil.NopCloser(bf)
+		b.req.ContentLength = int64(len(t))
+	case []byte:
+		bf := bytes.NewBuffer(t)
+		b.req.Body = ioutil.NopCloser(bf)
+		b.req.ContentLength = int64(len(t))
+	}
+	return b
+}
+
+// JSONBody adds request raw body encoding by JSON.
+func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) {
+	if b.req.Body == nil && obj != nil {
+		byts, err := json.Marshal(obj)
+		if err != nil {
+			return b, err
+		}
+		b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
+		b.req.ContentLength = int64(len(byts))
+		b.req.Header.Set("Content-Type", "application/json")
+	}
+	return b, nil
+}
+
+func (b *BeegoHTTPRequest) buildURL(paramBody string) {
+	// build GET url with query string
+	if b.req.Method == "GET" && len(paramBody) > 0 {
+		if strings.Index(b.url, "?") != -1 {
+			b.url += "&" + paramBody
+		} else {
+			b.url = b.url + "?" + paramBody
+		}
+		return
+	}
+
+	// build POST/PUT/PATCH url and body
+	if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && b.req.Body == nil {
+		// with files
+		if len(b.files) > 0 {
+			pr, pw := io.Pipe()
+			bodyWriter := multipart.NewWriter(pw)
+			go func() {
+				for formname, filename := range b.files {
+					fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
+					if err != nil {
+						log.Println("Httplib:", err)
+					}
+					fh, err := os.Open(filename)
+					if err != nil {
+						log.Println("Httplib:", err)
+					}
+					//iocopy
+					_, err = io.Copy(fileWriter, fh)
+					fh.Close()
+					if err != nil {
+						log.Println("Httplib:", err)
+					}
+				}
+				for k, v := range b.params {
+					for _, vv := range v {
+						bodyWriter.WriteField(k, vv)
+					}
+				}
+				bodyWriter.Close()
+				pw.Close()
+			}()
+			b.Header("Content-Type", bodyWriter.FormDataContentType())
+			b.req.Body = ioutil.NopCloser(pr)
+			return
+		}
+
+		// with params
+		if len(paramBody) > 0 {
+			b.Header("Content-Type", "application/x-www-form-urlencoded")
+			b.Body(paramBody)
+		}
+	}
+}
+
+func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) {
+	if b.resp.StatusCode != 0 {
+		return b.resp, nil
+	}
+	resp, err := b.DoRequest()
+	if err != nil {
+		return nil, err
+	}
+	b.resp = resp
+	return resp, nil
+}
+
+// DoRequest will do the client.Do
+func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
+	var paramBody string
+	if len(b.params) > 0 {
+		var buf bytes.Buffer
+		for k, v := range b.params {
+			for _, vv := range v {
+				buf.WriteString(url.QueryEscape(k))
+				buf.WriteByte('=')
+				buf.WriteString(url.QueryEscape(vv))
+				buf.WriteByte('&')
+			}
+		}
+		paramBody = buf.String()
+		paramBody = paramBody[0 : len(paramBody)-1]
+	}
+
+	b.buildURL(paramBody)
+	url, err := url.Parse(b.url)
+	if err != nil {
+		return nil, err
+	}
+
+	b.req.URL = url
+
+	trans := b.setting.Transport
+
+	if trans == nil {
+		// create default transport
+		trans = &http.Transport{
+			TLSClientConfig:     b.setting.TLSClientConfig,
+			Proxy:               b.setting.Proxy,
+			Dial:                TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
+			MaxIdleConnsPerHost: -1,
+		}
+	} else {
+		// if b.transport is *http.Transport then set the settings.
+		if t, ok := trans.(*http.Transport); ok {
+			if t.TLSClientConfig == nil {
+				t.TLSClientConfig = b.setting.TLSClientConfig
+			}
+			if t.Proxy == nil {
+				t.Proxy = b.setting.Proxy
+			}
+			if t.Dial == nil {
+				t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
+			}
+		}
+	}
+
+	var jar http.CookieJar
+	if b.setting.EnableCookie {
+		if defaultCookieJar == nil {
+			createDefaultCookie()
+		}
+		jar = defaultCookieJar
+	}
+
+	client := &http.Client{
+		Transport: trans,
+		Jar:       jar,
+	}
+
+	if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
+		b.req.Header.Set("User-Agent", b.setting.UserAgent)
+	}
+
+	if b.setting.CheckRedirect != nil {
+		client.CheckRedirect = b.setting.CheckRedirect
+	}
+
+	if b.setting.ShowDebug {
+		dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
+		if err != nil {
+			log.Println(err.Error())
+		}
+		b.dump = dump
+	}
+	// retries default value is 0, it will run once.
+	// retries equal to -1, it will run forever until success
+	// retries is setted, it will retries fixed times.
+	for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
+		resp, err = client.Do(b.req)
+		if err == nil {
+			break
+		}
+	}
+	return resp, err
+}
+
+// String returns the body string in response.
+// it calls Response inner.
+func (b *BeegoHTTPRequest) String() (string, error) {
+	data, err := b.Bytes()
+	if err != nil {
+		return "", err
+	}
+
+	return string(data), nil
+}
+
+// Bytes returns the body []byte in response.
+// it calls Response inner.
+func (b *BeegoHTTPRequest) Bytes() ([]byte, error) {
+	if b.body != nil {
+		return b.body, nil
+	}
+	resp, err := b.getResponse()
+	if err != nil {
+		return nil, err
+	}
+	if resp.Body == nil {
+		return nil, nil
+	}
+	defer resp.Body.Close()
+	if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
+		reader, err := gzip.NewReader(resp.Body)
+		if err != nil {
+			return nil, err
+		}
+		b.body, err = ioutil.ReadAll(reader)
+	} else {
+		b.body, err = ioutil.ReadAll(resp.Body)
+	}
+	return b.body, err
+}
+
+// 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
+	}
+	if resp.Body == nil {
+		return nil
+	}
+	defer resp.Body.Close()
+	_, err = io.Copy(f, resp.Body)
+	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 {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	return json.Unmarshal(data, v)
+}
+
+// ToXML returns the map that marshals from the body bytes as xml in response .
+// it calls Response inner.
+func (b *BeegoHTTPRequest) ToXML(v interface{}) error {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	return xml.Unmarshal(data, v)
+}
+
+// Response executes request client gets response mannually.
+func (b *BeegoHTTPRequest) Response() (*http.Response, error) {
+	return b.getResponse()
+}
+
+// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
+func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
+	return func(netw, addr string) (net.Conn, error) {
+		conn, err := net.DialTimeout(netw, addr, cTimeout)
+		if err != nil {
+			return nil, err
+		}
+		err = conn.SetDeadline(time.Now().Add(rwTimeout))
+		return conn, err
+	}
+}

+ 218 - 0
vendor/github.com/astaxie/beego/httplib/httplib_test.go

@@ -0,0 +1,218 @@
+// 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 httplib
+
+import (
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+	"time"
+)
+
+func TestResponse(t *testing.T) {
+	req := Get("http://httpbin.org/get")
+	resp, err := req.Response()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(resp)
+}
+
+func TestGet(t *testing.T) {
+	req := Get("http://httpbin.org/get")
+	b, err := req.Bytes()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(b)
+
+	s, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(s)
+
+	if string(b) != s {
+		t.Fatal("request data not match")
+	}
+}
+
+func TestSimplePost(t *testing.T) {
+	v := "smallfish"
+	req := Post("http://httpbin.org/post")
+	req.Param("username", v)
+
+	str, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in post")
+	}
+}
+
+//func TestPostFile(t *testing.T) {
+//	v := "smallfish"
+//	req := Post("http://httpbin.org/post")
+//	req.Debug(true)
+//	req.Param("username", v)
+//	req.PostFile("uploadfile", "httplib_test.go")
+
+//	str, err := req.String()
+//	if err != nil {
+//		t.Fatal(err)
+//	}
+//	t.Log(str)
+
+//	n := strings.Index(str, v)
+//	if n == -1 {
+//		t.Fatal(v + " not found in post")
+//	}
+//}
+
+func TestSimplePut(t *testing.T) {
+	str, err := Put("http://httpbin.org/put").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}
+
+func TestSimpleDelete(t *testing.T) {
+	str, err := Delete("http://httpbin.org/delete").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}
+
+func TestWithCookie(t *testing.T) {
+	v := "smallfish"
+	str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in cookie")
+	}
+}
+
+func TestWithBasicAuth(t *testing.T) {
+	str, err := Get("http://httpbin.org/basic-auth/user/passwd").SetBasicAuth("user", "passwd").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+	n := strings.Index(str, "authenticated")
+	if n == -1 {
+		t.Fatal("authenticated not found in response")
+	}
+}
+
+func TestWithUserAgent(t *testing.T) {
+	v := "beego"
+	str, err := Get("http://httpbin.org/headers").SetUserAgent(v).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in user-agent")
+	}
+}
+
+func TestWithSetting(t *testing.T) {
+	v := "beego"
+	var setting BeegoHTTPSettings
+	setting.EnableCookie = true
+	setting.UserAgent = v
+	setting.Transport = nil
+	setting.ReadWriteTimeout = 5 * time.Second
+	SetDefaultSetting(setting)
+
+	str, err := Get("http://httpbin.org/get").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in user-agent")
+	}
+}
+
+func TestToJson(t *testing.T) {
+	req := Get("http://httpbin.org/ip")
+	resp, err := req.Response()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(resp)
+
+	// httpbin will return http remote addr
+	type IP struct {
+		Origin string `json:"origin"`
+	}
+	var ip IP
+	err = req.ToJSON(&ip)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(ip.Origin)
+
+	if n := strings.Count(ip.Origin, "."); n != 3 {
+		t.Fatal("response is not valid ip")
+	}
+}
+
+func TestToFile(t *testing.T) {
+	f := "beego_testfile"
+	req := Get("http://httpbin.org/ip")
+	err := req.ToFile(f)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(f)
+	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")
+	str, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}

+ 192 - 0
vendor/github.com/astaxie/beego/logs/alils/alils.go

@@ -0,0 +1,192 @@
+package alils
+
+import (
+	"encoding/json"
+	"github.com/astaxie/beego/logs"
+	"github.com/gogo/protobuf/proto"
+	"strings"
+	"sync"
+	"time"
+)
+
+const (
+	CacheSize int    = 64
+	Delimiter string = "##"
+)
+
+type AliLSConfig struct {
+	Project   string   `json:"project"`
+	Endpoint  string   `json:"endpoint"`
+	KeyID     string   `json:"key_id"`
+	KeySecret string   `json:"key_secret"`
+	LogStore  string   `json:"log_store"`
+	Topics    []string `json:"topics"`
+	Source    string   `json:"source"`
+	Level     int      `json:"level"`
+	FlushWhen int      `json:"flush_when"`
+}
+
+// aliLSWriter implements LoggerInterface.
+// it writes messages in keep-live tcp connection.
+type aliLSWriter struct {
+	store    *LogStore
+	group    []*LogGroup
+	withMap  bool
+	groupMap map[string]*LogGroup
+	lock     *sync.Mutex
+	AliLSConfig
+}
+
+// 创建提供Logger接口的日志服务
+func NewAliLS() logs.Logger {
+	alils := new(aliLSWriter)
+	alils.Level = logs.LevelTrace
+	return alils
+}
+
+// 读取配置
+// 初始化必要的数据结构
+func (c *aliLSWriter) Init(jsonConfig string) (err error) {
+
+	json.Unmarshal([]byte(jsonConfig), c)
+
+	if c.FlushWhen > CacheSize {
+		c.FlushWhen = CacheSize
+	}
+
+	// 初始化Project
+	prj := &LogProject{
+		Name:            c.Project,
+		Endpoint:        c.Endpoint,
+		AccessKeyId:     c.KeyID,
+		AccessKeySecret: c.KeySecret,
+	}
+
+	// 获取logstore
+	c.store, err = prj.GetLogStore(c.LogStore)
+	if err != nil {
+		return err
+	}
+
+	// 创建默认Log Group
+	c.group = append(c.group, &LogGroup{
+		Topic:  proto.String(""),
+		Source: proto.String(c.Source),
+		Logs:   make([]*Log, 0, c.FlushWhen),
+	})
+
+	// 创建其它Log Group
+	c.groupMap = make(map[string]*LogGroup)
+	for _, topic := range c.Topics {
+
+		lg := &LogGroup{
+			Topic:  proto.String(topic),
+			Source: proto.String(c.Source),
+			Logs:   make([]*Log, 0, c.FlushWhen),
+		}
+
+		c.group = append(c.group, lg)
+		c.groupMap[topic] = lg
+	}
+
+	if len(c.group) == 1 {
+		c.withMap = false
+	} else {
+		c.withMap = true
+	}
+
+	c.lock = &sync.Mutex{}
+
+	return nil
+}
+
+// WriteMsg write message in connection.
+// if connection is down, try to re-connect.
+func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error) {
+
+	if level > c.Level {
+		return nil
+	}
+
+	var topic string
+	var content string
+	var lg *LogGroup
+	if c.withMap {
+
+		// 解析出Topic,并匹配LogGroup
+		strs := strings.SplitN(msg, Delimiter, 2)
+		if len(strs) == 2 {
+			pos := strings.LastIndex(strs[0], " ")
+			topic = strs[0][pos+1 : len(strs[0])]
+			content = strs[0][0:pos] + strs[1]
+			lg = c.groupMap[topic]
+		}
+
+		// 默认发到空Topic
+		if lg == nil {
+			topic = ""
+			content = msg
+			lg = c.group[0]
+		}
+	} else {
+		topic = ""
+		content = msg
+		lg = c.group[0]
+	}
+
+	// 生成日志
+	c1 := &Log_Content{
+		Key:   proto.String("msg"),
+		Value: proto.String(content),
+	}
+
+	l := &Log{
+		Time: proto.Uint32(uint32(when.Unix())), // 填写日志时间
+		Contents: []*Log_Content{
+			c1,
+		},
+	}
+
+	c.lock.Lock()
+	lg.Logs = append(lg.Logs, l)
+	c.lock.Unlock()
+
+	// 满足条件则Flush
+	if len(lg.Logs) >= c.FlushWhen {
+		c.flush(lg)
+	}
+
+	return nil
+}
+
+// Flush implementing method. empty.
+func (c *aliLSWriter) Flush() {
+
+	// flush所有group
+	for _, lg := range c.group {
+		c.flush(lg)
+	}
+}
+
+// Destroy destroy connection writer and close tcp listener.
+func (c *aliLSWriter) Destroy() {
+}
+
+func (c *aliLSWriter) flush(lg *LogGroup) {
+
+	c.lock.Lock()
+	defer c.lock.Unlock()
+
+	// 把以上的LogGroup推送到SLS服务器,
+	// SLS服务器会根据该logstore的shard个数自动进行负载均衡。
+	err := c.store.PutLogs(lg)
+	if err != nil {
+		return
+	}
+
+	lg.Logs = make([]*Log, 0, c.FlushWhen)
+}
+
+func init() {
+	logs.Register(logs.AdapterAliLS, NewAliLS)
+}

+ 13 - 0
vendor/github.com/astaxie/beego/logs/alils/config.go

@@ -0,0 +1,13 @@
+package alils
+
+const (
+	version         = "0.5.0"     // SDK version
+	signatureMethod = "hmac-sha1" // Signature method
+
+	// OffsetNewest stands for the log head offset, i.e. the offset that will be
+	// assigned to the next message that will be produced to the shard.
+	OffsetNewest = "end"
+	// OffsetOldest stands for the oldest offset available on the logstore for a
+	// shard.
+	OffsetOldest = "begin"
+)

+ 984 - 0
vendor/github.com/astaxie/beego/logs/alils/log.pb.go

@@ -0,0 +1,984 @@
+package alils
+
+import "github.com/gogo/protobuf/proto"
+import "fmt"
+import "math"
+
+// discarding unused import gogoproto "."
+
+import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
+
+import "io"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+type Log struct {
+	Time             *uint32        `protobuf:"varint,1,req,name=Time" json:"Time,omitempty"`
+	Contents         []*Log_Content `protobuf:"bytes,2,rep,name=Contents" json:"Contents,omitempty"`
+	XXX_unrecognized []byte         `json:"-"`
+}
+
+func (m *Log) Reset()         { *m = Log{} }
+func (m *Log) String() string { return proto.CompactTextString(m) }
+func (*Log) ProtoMessage()    {}
+
+func (m *Log) GetTime() uint32 {
+	if m != nil && m.Time != nil {
+		return *m.Time
+	}
+	return 0
+}
+
+func (m *Log) GetContents() []*Log_Content {
+	if m != nil {
+		return m.Contents
+	}
+	return nil
+}
+
+type Log_Content struct {
+	Key              *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"`
+	Value            *string `protobuf:"bytes,2,req,name=Value" json:"Value,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *Log_Content) Reset()         { *m = Log_Content{} }
+func (m *Log_Content) String() string { return proto.CompactTextString(m) }
+func (*Log_Content) ProtoMessage()    {}
+
+func (m *Log_Content) GetKey() string {
+	if m != nil && m.Key != nil {
+		return *m.Key
+	}
+	return ""
+}
+
+func (m *Log_Content) GetValue() string {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return ""
+}
+
+type LogGroup struct {
+	Logs             []*Log  `protobuf:"bytes,1,rep,name=Logs" json:"Logs,omitempty"`
+	Reserved         *string `protobuf:"bytes,2,opt,name=Reserved" json:"Reserved,omitempty"`
+	Topic            *string `protobuf:"bytes,3,opt,name=Topic" json:"Topic,omitempty"`
+	Source           *string `protobuf:"bytes,4,opt,name=Source" json:"Source,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *LogGroup) Reset()         { *m = LogGroup{} }
+func (m *LogGroup) String() string { return proto.CompactTextString(m) }
+func (*LogGroup) ProtoMessage()    {}
+
+func (m *LogGroup) GetLogs() []*Log {
+	if m != nil {
+		return m.Logs
+	}
+	return nil
+}
+
+func (m *LogGroup) GetReserved() string {
+	if m != nil && m.Reserved != nil {
+		return *m.Reserved
+	}
+	return ""
+}
+
+func (m *LogGroup) GetTopic() string {
+	if m != nil && m.Topic != nil {
+		return *m.Topic
+	}
+	return ""
+}
+
+func (m *LogGroup) GetSource() string {
+	if m != nil && m.Source != nil {
+		return *m.Source
+	}
+	return ""
+}
+
+type LogGroupList struct {
+	LogGroups        []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"`
+	XXX_unrecognized []byte      `json:"-"`
+}
+
+func (m *LogGroupList) Reset()         { *m = LogGroupList{} }
+func (m *LogGroupList) String() string { return proto.CompactTextString(m) }
+func (*LogGroupList) ProtoMessage()    {}
+
+func (m *LogGroupList) GetLogGroups() []*LogGroup {
+	if m != nil {
+		return m.LogGroups
+	}
+	return nil
+}
+
+func (m *Log) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Log) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.Time == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time")
+	} else {
+		data[i] = 0x8
+		i++
+		i = encodeVarintLog(data, i, uint64(*m.Time))
+	}
+	if len(m.Contents) > 0 {
+		for _, msg := range m.Contents {
+			data[i] = 0x12
+			i++
+			i = encodeVarintLog(data, i, uint64(msg.Size()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		i += copy(data[i:], m.XXX_unrecognized)
+	}
+	return i, nil
+}
+
+func (m *Log_Content) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Log_Content) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.Key == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key")
+	} else {
+		data[i] = 0xa
+		i++
+		i = encodeVarintLog(data, i, uint64(len(*m.Key)))
+		i += copy(data[i:], *m.Key)
+	}
+	if m.Value == nil {
+		return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value")
+	} else {
+		data[i] = 0x12
+		i++
+		i = encodeVarintLog(data, i, uint64(len(*m.Value)))
+		i += copy(data[i:], *m.Value)
+	}
+	if m.XXX_unrecognized != nil {
+		i += copy(data[i:], m.XXX_unrecognized)
+	}
+	return i, nil
+}
+
+func (m *LogGroup) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *LogGroup) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Logs) > 0 {
+		for _, msg := range m.Logs {
+			data[i] = 0xa
+			i++
+			i = encodeVarintLog(data, i, uint64(msg.Size()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	if m.Reserved != nil {
+		data[i] = 0x12
+		i++
+		i = encodeVarintLog(data, i, uint64(len(*m.Reserved)))
+		i += copy(data[i:], *m.Reserved)
+	}
+	if m.Topic != nil {
+		data[i] = 0x1a
+		i++
+		i = encodeVarintLog(data, i, uint64(len(*m.Topic)))
+		i += copy(data[i:], *m.Topic)
+	}
+	if m.Source != nil {
+		data[i] = 0x22
+		i++
+		i = encodeVarintLog(data, i, uint64(len(*m.Source)))
+		i += copy(data[i:], *m.Source)
+	}
+	if m.XXX_unrecognized != nil {
+		i += copy(data[i:], m.XXX_unrecognized)
+	}
+	return i, nil
+}
+
+func (m *LogGroupList) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *LogGroupList) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.LogGroups) > 0 {
+		for _, msg := range m.LogGroups {
+			data[i] = 0xa
+			i++
+			i = encodeVarintLog(data, i, uint64(msg.Size()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		i += copy(data[i:], m.XXX_unrecognized)
+	}
+	return i, nil
+}
+
+func encodeFixed64Log(data []byte, offset int, v uint64) int {
+	data[offset] = uint8(v)
+	data[offset+1] = uint8(v >> 8)
+	data[offset+2] = uint8(v >> 16)
+	data[offset+3] = uint8(v >> 24)
+	data[offset+4] = uint8(v >> 32)
+	data[offset+5] = uint8(v >> 40)
+	data[offset+6] = uint8(v >> 48)
+	data[offset+7] = uint8(v >> 56)
+	return offset + 8
+}
+func encodeFixed32Log(data []byte, offset int, v uint32) int {
+	data[offset] = uint8(v)
+	data[offset+1] = uint8(v >> 8)
+	data[offset+2] = uint8(v >> 16)
+	data[offset+3] = uint8(v >> 24)
+	return offset + 4
+}
+func encodeVarintLog(data []byte, offset int, v uint64) int {
+	for v >= 1<<7 {
+		data[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	data[offset] = uint8(v)
+	return offset + 1
+}
+func (m *Log) Size() (n int) {
+	var l int
+	_ = l
+	if m.Time != nil {
+		n += 1 + sovLog(uint64(*m.Time))
+	}
+	if len(m.Contents) > 0 {
+		for _, e := range m.Contents {
+			l = e.Size()
+			n += 1 + l + sovLog(uint64(l))
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *Log_Content) Size() (n int) {
+	var l int
+	_ = l
+	if m.Key != nil {
+		l = len(*m.Key)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.Value != nil {
+		l = len(*m.Value)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *LogGroup) Size() (n int) {
+	var l int
+	_ = l
+	if len(m.Logs) > 0 {
+		for _, e := range m.Logs {
+			l = e.Size()
+			n += 1 + l + sovLog(uint64(l))
+		}
+	}
+	if m.Reserved != nil {
+		l = len(*m.Reserved)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.Topic != nil {
+		l = len(*m.Topic)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.Source != nil {
+		l = len(*m.Source)
+		n += 1 + l + sovLog(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *LogGroupList) Size() (n int) {
+	var l int
+	_ = l
+	if len(m.LogGroups) > 0 {
+		for _, e := range m.LogGroups {
+			l = e.Size()
+			n += 1 + l + sovLog(uint64(l))
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func sovLog(x uint64) (n int) {
+	for {
+		n++
+		x >>= 7
+		if x == 0 {
+			break
+		}
+	}
+	return n
+}
+func sozLog(x uint64) (n int) {
+	return sovLog(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Log) Unmarshal(data []byte) error {
+	var hasFields [1]uint64
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Log: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Log: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType)
+			}
+			var v uint32
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (uint32(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.Time = &v
+			hasFields[0] |= uint64(0x00000001)
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Contents", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Contents = append(m.Contents, &Log_Content{})
+			if err := m.Contents[len(m.Contents)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLog(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLog
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+	if hasFields[0]&uint64(0x00000001) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time")
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Log_Content) Unmarshal(data []byte) error {
+	var hasFields [1]uint64
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Content: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Key = &s
+			iNdEx = postIndex
+			hasFields[0] |= uint64(0x00000001)
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Value = &s
+			iNdEx = postIndex
+			hasFields[0] |= uint64(0x00000002)
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLog(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLog
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+	if hasFields[0]&uint64(0x00000001) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key")
+	}
+	if hasFields[0]&uint64(0x00000002) == 0 {
+		return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value")
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *LogGroup) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: LogGroup: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: LogGroup: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Logs = append(m.Logs, &Log{})
+			if err := m.Logs[len(m.Logs)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Reserved", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Reserved = &s
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Topic", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Topic = &s
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			s := string(data[iNdEx:postIndex])
+			m.Source = &s
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLog(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLog
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *LogGroupList) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: LogGroupList: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: LogGroupList: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LogGroups", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthLog
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.LogGroups = append(m.LogGroups, &LogGroup{})
+			if err := m.LogGroups[len(m.LogGroups)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLog(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLog
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipLog(data []byte) (n int, err error) {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowLog
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if data[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+			return iNdEx, nil
+		case 1:
+			iNdEx += 8
+			return iNdEx, nil
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowLog
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			iNdEx += length
+			if length < 0 {
+				return 0, ErrInvalidLengthLog
+			}
+			return iNdEx, nil
+		case 3:
+			for {
+				var innerWire uint64
+				var start int = iNdEx
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return 0, ErrIntOverflowLog
+					}
+					if iNdEx >= l {
+						return 0, io.ErrUnexpectedEOF
+					}
+					b := data[iNdEx]
+					iNdEx++
+					innerWire |= (uint64(b) & 0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				innerWireType := int(innerWire & 0x7)
+				if innerWireType == 4 {
+					break
+				}
+				next, err := skipLog(data[start:])
+				if err != nil {
+					return 0, err
+				}
+				iNdEx = start + next
+			}
+			return iNdEx, nil
+		case 4:
+			return iNdEx, nil
+		case 5:
+			iNdEx += 4
+			return iNdEx, nil
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+	}
+	panic("unreachable")
+}
+
+var (
+	ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowLog   = fmt.Errorf("proto: integer overflow")
+)

+ 39 - 0
vendor/github.com/astaxie/beego/logs/alils/log_config.go

@@ -0,0 +1,39 @@
+package alils
+
+type InputDetail struct {
+	LogType       string   `json:"logType"`
+	LogPath       string   `json:"logPath"`
+	FilePattern   string   `json:"filePattern"`
+	LocalStorage  bool     `json:"localStorage"`
+	TimeFormat    string   `json:"timeFormat"`
+	LogBeginRegex string   `json:"logBeginRegex"`
+	Regex         string   `json:"regex"`
+	Keys          []string `json:"key"`
+	FilterKeys    []string `json:"filterKey"`
+	FilterRegex   []string `json:"filterRegex"`
+	TopicFormat   string   `json:"topicFormat"`
+}
+
+type OutputDetail struct {
+	Endpoint     string `json:"endpoint"`
+	LogStoreName string `json:"logstoreName"`
+}
+
+type LogConfig struct {
+	Name         string       `json:"configName"`
+	InputType    string       `json:"inputType"`
+	InputDetail  InputDetail  `json:"inputDetail"`
+	OutputType   string       `json:"outputType"`
+	OutputDetail OutputDetail `json:"outputDetail"`
+
+	CreateTime     uint32
+	LastModifyTime uint32
+
+	project *LogProject
+}
+
+// GetAppliedMachineGroup returns applied machine group of this config.
+func (c *LogConfig) GetAppliedMachineGroup(confName string) (groupNames []string, err error) {
+	groupNames, err = c.project.GetAppliedMachineGroups(c.Name)
+	return
+}

+ 818 - 0
vendor/github.com/astaxie/beego/logs/alils/log_project.go

@@ -0,0 +1,818 @@
+/*
+Package sls implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS).
+
+For more description about SLS, please read this article:
+http://gitlab.alibaba-inc.com/sls/doc.
+*/
+package alils
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/httputil"
+)
+
+// Error message in SLS HTTP response.
+type errorMessage struct {
+	Code    string `json:"errorCode"`
+	Message string `json:"errorMessage"`
+}
+
+type LogProject struct {
+	Name            string // Project name
+	Endpoint        string // IP or hostname of SLS endpoint
+	AccessKeyId     string
+	AccessKeySecret string
+}
+
+// NewLogProject creates a new SLS project.
+func NewLogProject(name, endpoint, accessKeyId, accessKeySecret string) (p *LogProject, err error) {
+	p = &LogProject{
+		Name:            name,
+		Endpoint:        endpoint,
+		AccessKeyId:     accessKeyId,
+		AccessKeySecret: accessKeySecret,
+	}
+	return p, nil
+}
+
+// ListLogStore returns all logstore names of project p.
+func (p *LogProject) ListLogStore() (storeNames []string, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/logstores")
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to list logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		Count     int
+		LogStores []string
+	}
+	body := &Body{}
+
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	storeNames = body.LogStores
+
+	return
+}
+
+// GetLogStore returns logstore according by logstore name.
+func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "GET", "/logstores/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	s = &LogStore{}
+	err = json.Unmarshal(buf, s)
+	if err != nil {
+		return
+	}
+	s.project = p
+	return
+}
+
+// CreateLogStore creates a new logstore in SLS,
+// where name is logstore name,
+// and ttl is time-to-live(in day) of logs,
+// and shardCnt is the number of shards.
+func (p *LogProject) CreateLogStore(name string, ttl, shardCnt int) (err error) {
+
+	type Body struct {
+		Name       string `json:"logstoreName"`
+		TTL        int    `json:"ttl"`
+		ShardCount int    `json:"shardCount"`
+	}
+
+	store := &Body{
+		Name:       name,
+		TTL:        ttl,
+		ShardCount: shardCnt,
+	}
+
+	body, err := json.Marshal(store)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "POST", "/logstores", h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to create logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// DeleteLogStore deletes a logstore according by logstore name.
+func (p *LogProject) DeleteLogStore(name string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "DELETE", "/logstores/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// UpdateLogStore updates a logstore according by logstore name,
+// obviously we can't modify the logstore name itself.
+func (p *LogProject) UpdateLogStore(name string, ttl, shardCnt int) (err error) {
+
+	type Body struct {
+		Name       string `json:"logstoreName"`
+		TTL        int    `json:"ttl"`
+		ShardCount int    `json:"shardCount"`
+	}
+
+	store := &Body{
+		Name:       name,
+		TTL:        ttl,
+		ShardCount: shardCnt,
+	}
+
+	body, err := json.Marshal(store)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "PUT", "/logstores", h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to update logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// ListMachineGroup returns machine group name list and the total number of machine groups.
+// The offset starts from 0 and the size is the max number of machine groups could be returned.
+func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	if size <= 0 {
+		size = 500
+	}
+
+	uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size)
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to list machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		MachineGroups []string
+		Count         int
+		Total         int
+	}
+	body := &Body{}
+
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	m = body.MachineGroups
+	total = body.Total
+
+	return
+}
+
+// GetMachineGroup retruns machine group according by machine group name.
+func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "GET", "/machinegroups/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get machine group:%v", name)
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	m = &MachineGroup{}
+	err = json.Unmarshal(buf, m)
+	if err != nil {
+		return
+	}
+	m.project = p
+	return
+}
+
+// CreateMachineGroup creates a new machine group in SLS.
+func (p *LogProject) CreateMachineGroup(m *MachineGroup) (err error) {
+
+	body, err := json.Marshal(m)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "POST", "/machinegroups", h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to create machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// UpdateMachineGroup updates a machine group.
+func (p *LogProject) UpdateMachineGroup(m *MachineGroup) (err error) {
+
+	body, err := json.Marshal(m)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "PUT", "/machinegroups/"+m.Name, h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to update machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// DeleteMachineGroup deletes machine group according machine group name.
+func (p *LogProject) DeleteMachineGroup(name string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "DELETE", "/machinegroups/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// ListConfig returns config names list and the total number of configs.
+// The offset starts from 0 and the size is the max number of configs could be returned.
+func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	if size <= 0 {
+		size = 100
+	}
+
+	uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size)
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		Total   int
+		Configs []string
+	}
+	body := &Body{}
+
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	cfgNames = body.Configs
+	total = body.Total
+	return
+}
+
+// GetConfig returns config according by config name.
+func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "GET", "/configs/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete config")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	c = &LogConfig{}
+	err = json.Unmarshal(buf, c)
+	if err != nil {
+		return
+	}
+	c.project = p
+	return
+}
+
+// UpdateConfig updates a config.
+func (p *LogProject) UpdateConfig(c *LogConfig) (err error) {
+
+	body, err := json.Marshal(c)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "PUT", "/configs/"+c.Name, h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to update config")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// CreateConfig creates a new config in SLS.
+func (p *LogProject) CreateConfig(c *LogConfig) (err error) {
+
+	body, err := json.Marshal(c)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)),
+		"Content-Type":      "application/json",
+		"Accept-Encoding":   "deflate", // TODO: support lz4
+	}
+
+	r, err := request(p, "POST", "/configs", h, body)
+	if err != nil {
+		return
+	}
+
+	body, err = ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to update config")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	return
+}
+
+// DeleteConfig deletes a config according by config name.
+func (p *LogProject) DeleteConfig(name string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	r, err := request(p, "DELETE", "/configs/"+name, h, nil)
+	if err != nil {
+		return
+	}
+
+	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(body, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to delete config")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// GetAppliedMachineGroups returns applied machine group names list according config name.
+func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/configs/%v/machinegroups", confName)
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get applied machine groups")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		Count         int
+		Machinegroups []string
+	}
+
+	body := &Body{}
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	groupNames = body.Machinegroups
+	return
+}
+
+// GetAppliedConfigs returns applied config names list according machine group name groupName.
+func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/machinegroups/%v/configs", groupName)
+	r, err := request(p, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to applied configs")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Cfg struct {
+		Count   int      `json:"count"`
+		Configs []string `json:"configs"`
+	}
+
+	body := &Cfg{}
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	confNames = body.Configs
+	return
+}
+
+// ApplyConfigToMachineGroup applies config to machine group.
+func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
+	r, err := request(p, "PUT", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to apply config to machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// RemoveConfigFromMachineGroup removes config from machine group.
+func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
+	r, err := request(p, "DELETE", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to remove config from machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Printf("%s\n", dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}

+ 269 - 0
vendor/github.com/astaxie/beego/logs/alils/log_store.go

@@ -0,0 +1,269 @@
+package alils
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/httputil"
+	"strconv"
+
+	lz4 "github.com/cloudflare/golz4"
+	"github.com/gogo/protobuf/proto"
+)
+
+type LogStore struct {
+	Name       string `json:"logstoreName"`
+	TTL        int
+	ShardCount int
+
+	CreateTime     uint32
+	LastModifyTime uint32
+
+	project *LogProject
+}
+
+type Shard struct {
+	ShardID int `json:"shardID"`
+}
+
+// ListShards returns shard id list of this logstore.
+func (s *LogStore) ListShards() (shardIDs []int, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/logstores/%v/shards", s.Name)
+	r, err := request(s.project, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to list logstore")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	var shards []*Shard
+	err = json.Unmarshal(buf, &shards)
+	if err != nil {
+		return
+	}
+
+	for _, v := range shards {
+		shardIDs = append(shardIDs, v.ShardID)
+	}
+	return
+}
+
+// PutLogs put logs into logstore.
+// The callers should transform user logs into LogGroup.
+func (s *LogStore) PutLogs(lg *LogGroup) (err error) {
+	body, err := proto.Marshal(lg)
+	if err != nil {
+		return
+	}
+
+	// Compresse body with lz4
+	out := make([]byte, lz4.CompressBound(body))
+	n, err := lz4.Compress(body, out)
+	if err != nil {
+		return
+	}
+
+	h := map[string]string{
+		"x-sls-compresstype": "lz4",
+		"x-sls-bodyrawsize":  fmt.Sprintf("%v", len(body)),
+		"Content-Type":       "application/x-protobuf",
+	}
+
+	uri := fmt.Sprintf("/logstores/%v", s.Name)
+	r, err := request(s.project, "POST", uri, h, out[:n])
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to put logs")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+	return
+}
+
+// GetCursor gets log cursor of one shard specified by shardId.
+// The from can be in three form: a) unix timestamp in seccond, b) "begin", c) "end".
+// For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore
+func (s *LogStore) GetCursor(shardId int, from string) (cursor string, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v",
+		s.Name, shardId, from)
+
+	r, err := request(s.project, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get cursor")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	type Body struct {
+		Cursor string
+	}
+	body := &Body{}
+
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+	cursor = body.Cursor
+	return
+}
+
+// GetLogsBytes gets logs binary data from shard specified by shardId according cursor.
+// The logGroupMaxCount is the max number of logGroup could be returned.
+// The nextCursor is the next curosr can be used to read logs at next time.
+func (s *LogStore) GetLogsBytes(shardId int, cursor string,
+	logGroupMaxCount int) (out []byte, nextCursor string, err error) {
+
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+		"Accept":            "application/x-protobuf",
+		"Accept-Encoding":   "lz4",
+	}
+
+	uri := fmt.Sprintf("/logstores/%v/shards/%v?type=logs&cursor=%v&count=%v",
+		s.Name, shardId, cursor, logGroupMaxCount)
+
+	r, err := request(s.project, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to get cursor")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	v, ok := r.Header["X-Sls-Compresstype"]
+	if !ok || len(v) == 0 {
+		err = fmt.Errorf("can't find 'x-sls-compresstype' header")
+		return
+	}
+	if v[0] != "lz4" {
+		err = fmt.Errorf("unexpected compress type:%v", v[0])
+		return
+	}
+
+	v, ok = r.Header["X-Sls-Cursor"]
+	if !ok || len(v) == 0 {
+		err = fmt.Errorf("can't find 'x-sls-cursor' header")
+		return
+	}
+	nextCursor = v[0]
+
+	v, ok = r.Header["X-Sls-Bodyrawsize"]
+	if !ok || len(v) == 0 {
+		err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header")
+		return
+	}
+	bodyRawSize, err := strconv.Atoi(v[0])
+	if err != nil {
+		return
+	}
+
+	out = make([]byte, bodyRawSize)
+	err = lz4.Uncompress(buf, out)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// LogsBytesDecode decodes logs binary data retruned by GetLogsBytes API
+func LogsBytesDecode(data []byte) (gl *LogGroupList, err error) {
+
+	gl = &LogGroupList{}
+	err = proto.Unmarshal(data, gl)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// GetLogs gets logs from shard specified by shardId according cursor.
+// The logGroupMaxCount is the max number of logGroup could be returned.
+// The nextCursor is the next curosr can be used to read logs at next time.
+func (s *LogStore) GetLogs(shardId int, cursor string,
+	logGroupMaxCount int) (gl *LogGroupList, nextCursor string, err error) {
+
+	out, nextCursor, err := s.GetLogsBytes(shardId, cursor, logGroupMaxCount)
+	if err != nil {
+		return
+	}
+
+	gl, err = LogsBytesDecode(out)
+	if err != nil {
+		return
+	}
+
+	return
+}

+ 87 - 0
vendor/github.com/astaxie/beego/logs/alils/machine_group.go

@@ -0,0 +1,87 @@
+package alils
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/httputil"
+)
+
+type MachinGroupAttribute struct {
+	ExternalName string `json:"externalName"`
+	TopicName    string `json:"groupTopic"`
+}
+
+type MachineGroup struct {
+	Name          string   `json:"groupName"`
+	Type          string   `json:"groupType"`
+	MachineIdType string   `json:"machineIdentifyType"`
+	MachineIdList []string `json:"machineList"`
+
+	Attribute MachinGroupAttribute `json:"groupAttribute"`
+
+	CreateTime     uint32
+	LastModifyTime uint32
+
+	project *LogProject
+}
+
+type Machine struct {
+	IP            string
+	UniqueId      string `json:"machine-uniqueid"`
+	UserdefinedId string `json:"userdefined-id"`
+}
+
+type MachineList struct {
+	Total    int
+	Machines []*Machine
+}
+
+// ListMachines returns machine list of this machine group.
+func (m *MachineGroup) ListMachines() (ms []*Machine, total int, err error) {
+	h := map[string]string{
+		"x-sls-bodyrawsize": "0",
+	}
+
+	uri := fmt.Sprintf("/machinegroups/%v/machines", m.Name)
+	r, err := request(m.project, "GET", uri, h, nil)
+	if err != nil {
+		return
+	}
+
+	buf, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		return
+	}
+
+	if r.StatusCode != http.StatusOK {
+		errMsg := &errorMessage{}
+		err = json.Unmarshal(buf, errMsg)
+		if err != nil {
+			err = fmt.Errorf("failed to remove config from machine group")
+			dump, _ := httputil.DumpResponse(r, true)
+			fmt.Println(dump)
+			return
+		}
+		err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
+		return
+	}
+
+	body := &MachineList{}
+	err = json.Unmarshal(buf, body)
+	if err != nil {
+		return
+	}
+
+	ms = body.Machines
+	total = body.Total
+
+	return
+}
+
+// GetAppliedConfigs returns applied configs of this machine group.
+func (m *MachineGroup) GetAppliedConfigs() (confNames []string, err error) {
+	confNames, err = m.project.GetAppliedConfigs(m.Name)
+	return
+}

+ 62 - 0
vendor/github.com/astaxie/beego/logs/alils/request.go

@@ -0,0 +1,62 @@
+package alils
+
+import (
+	"bytes"
+	"crypto/md5"
+	"fmt"
+	"net/http"
+)
+
+// request sends a request to SLS.
+func request(project *LogProject, method, uri string, headers map[string]string,
+	body []byte) (resp *http.Response, err error) {
+
+	// The caller should provide 'x-sls-bodyrawsize' header
+	if _, ok := headers["x-sls-bodyrawsize"]; !ok {
+		err = fmt.Errorf("Can't find 'x-sls-bodyrawsize' header")
+		return
+	}
+
+	// SLS public request headers
+	headers["Host"] = project.Name + "." + project.Endpoint
+	headers["Date"] = nowRFC1123()
+	headers["x-sls-apiversion"] = version
+	headers["x-sls-signaturemethod"] = signatureMethod
+	if body != nil {
+		bodyMD5 := fmt.Sprintf("%X", md5.Sum(body))
+		headers["Content-MD5"] = bodyMD5
+
+		if _, ok := headers["Content-Type"]; !ok {
+			err = fmt.Errorf("Can't find 'Content-Type' header")
+			return
+		}
+	}
+
+	// Calc Authorization
+	// Authorization = "SLS <AccessKeyId>:<Signature>"
+	digest, err := signature(project, method, uri, headers)
+	if err != nil {
+		return
+	}
+	auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyId, digest)
+	headers["Authorization"] = auth
+
+	// Initialize http request
+	reader := bytes.NewReader(body)
+	urlStr := fmt.Sprintf("http://%v.%v%v", project.Name, project.Endpoint, uri)
+	req, err := http.NewRequest(method, urlStr, reader)
+	if err != nil {
+		return
+	}
+	for k, v := range headers {
+		req.Header.Add(k, v)
+	}
+
+	// Get ready to do request
+	resp, err = http.DefaultClient.Do(req)
+	if err != nil {
+		return
+	}
+
+	return
+}

+ 112 - 0
vendor/github.com/astaxie/beego/logs/alils/signature.go

@@ -0,0 +1,112 @@
+package alils
+
+import (
+	"crypto/hmac"
+	"crypto/sha1"
+	"encoding/base64"
+	"fmt"
+	"net/url"
+	"sort"
+	"strings"
+	"time"
+)
+
+// GMT location
+var gmtLoc = time.FixedZone("GMT", 0)
+
+// NowRFC1123 returns now time in RFC1123 format with GMT timezone,
+// eg. "Mon, 02 Jan 2006 15:04:05 GMT".
+func nowRFC1123() string {
+	return time.Now().In(gmtLoc).Format(time.RFC1123)
+}
+
+// signature calculates a request's signature digest.
+func signature(project *LogProject, method, uri string,
+	headers map[string]string) (digest string, err error) {
+	var contentMD5, contentType, date, canoHeaders, canoResource string
+	var slsHeaderKeys sort.StringSlice
+
+	// SignString = VERB + "\n"
+	//              + CONTENT-MD5 + "\n"
+	//              + CONTENT-TYPE + "\n"
+	//              + DATE + "\n"
+	//              + CanonicalizedSLSHeaders + "\n"
+	//              + CanonicalizedResource
+
+	if val, ok := headers["Content-MD5"]; ok {
+		contentMD5 = val
+	}
+
+	if val, ok := headers["Content-Type"]; ok {
+		contentType = val
+	}
+
+	date, ok := headers["Date"]
+	if !ok {
+		err = fmt.Errorf("Can't find 'Date' header")
+		return
+	}
+
+	// Calc CanonicalizedSLSHeaders
+	slsHeaders := make(map[string]string, len(headers))
+	for k, v := range headers {
+		l := strings.TrimSpace(strings.ToLower(k))
+		if strings.HasPrefix(l, "x-sls-") {
+			slsHeaders[l] = strings.TrimSpace(v)
+			slsHeaderKeys = append(slsHeaderKeys, l)
+		}
+	}
+
+	sort.Sort(slsHeaderKeys)
+	for i, k := range slsHeaderKeys {
+		canoHeaders += k + ":" + slsHeaders[k]
+		if i+1 < len(slsHeaderKeys) {
+			canoHeaders += "\n"
+		}
+	}
+
+	// Calc CanonicalizedResource
+	u, err := url.Parse(uri)
+	if err != nil {
+		return
+	}
+
+	canoResource += url.QueryEscape(u.Path)
+	if u.RawQuery != "" {
+		var keys sort.StringSlice
+
+		vals := u.Query()
+		for k, _ := range vals {
+			keys = append(keys, k)
+		}
+
+		sort.Sort(keys)
+		canoResource += "?"
+		for i, k := range keys {
+			if i > 0 {
+				canoResource += "&"
+			}
+
+			for _, v := range vals[k] {
+				canoResource += k + "=" + v
+			}
+		}
+	}
+
+	signStr := method + "\n" +
+		contentMD5 + "\n" +
+		contentType + "\n" +
+		date + "\n" +
+		canoHeaders + "\n" +
+		canoResource
+
+	// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret))
+	mac := hmac.New(sha1.New, []byte(project.AccessKeySecret))
+	_, err = mac.Write([]byte(signStr))
+	if err != nil {
+		return
+	}
+	digest = base64.StdEncoding.EncodeToString(mac.Sum(nil))
+	return
+}
+

+ 294 - 0
vendor/github.com/astaxie/beego/logs/color_windows_test.go

@@ -0,0 +1,294 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build windows
+
+package logs
+
+import (
+	"bytes"
+	"fmt"
+	"syscall"
+	"testing"
+)
+
+var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo
+
+func ChangeColor(color uint16) {
+	setConsoleTextAttribute(uintptr(syscall.Stdout), color)
+}
+
+func ResetColor() {
+	ChangeColor(uint16(0x0007))
+}
+
+func TestWritePlanText(t *testing.T) {
+	inner := bytes.NewBufferString("")
+	w := NewAnsiColorWriter(inner)
+	expected := "plain text"
+	fmt.Fprintf(w, expected)
+	actual := inner.String()
+	if actual != expected {
+		t.Errorf("Get %q, want %q", actual, expected)
+	}
+}
+
+func TestWriteParseText(t *testing.T) {
+	inner := bytes.NewBufferString("")
+	w := NewAnsiColorWriter(inner)
+
+	inputTail := "\x1b[0mtail text"
+	expectedTail := "tail text"
+	fmt.Fprintf(w, inputTail)
+	actualTail := inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+
+	inputHead := "head text\x1b[0m"
+	expectedHead := "head text"
+	fmt.Fprintf(w, inputHead)
+	actualHead := inner.String()
+	inner.Reset()
+	if actualHead != expectedHead {
+		t.Errorf("Get %q, want %q", actualHead, expectedHead)
+	}
+
+	inputBothEnds := "both ends \x1b[0m text"
+	expectedBothEnds := "both ends  text"
+	fmt.Fprintf(w, inputBothEnds)
+	actualBothEnds := inner.String()
+	inner.Reset()
+	if actualBothEnds != expectedBothEnds {
+		t.Errorf("Get %q, want %q", actualBothEnds, expectedBothEnds)
+	}
+
+	inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc"
+	expectedManyEsc := "\x1b\x1b\x1b many esc"
+	fmt.Fprintf(w, inputManyEsc)
+	actualManyEsc := inner.String()
+	inner.Reset()
+	if actualManyEsc != expectedManyEsc {
+		t.Errorf("Get %q, want %q", actualManyEsc, expectedManyEsc)
+	}
+
+	expectedSplit := "split  text"
+	for _, ch := range "split \x1b[0m text" {
+		fmt.Fprintf(w, string(ch))
+	}
+	actualSplit := inner.String()
+	inner.Reset()
+	if actualSplit != expectedSplit {
+		t.Errorf("Get %q, want %q", actualSplit, expectedSplit)
+	}
+}
+
+type screenNotFoundError struct {
+	error
+}
+
+func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) {
+	inner := bytes.NewBufferString("")
+	w := NewAnsiColorWriter(inner)
+	fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText)
+
+	actualText = inner.String()
+	screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
+	if screenInfo != nil {
+		actualAttributes = screenInfo.WAttributes
+	} else {
+		err = &screenNotFoundError{}
+	}
+	return
+}
+
+type testParam struct {
+	text       string
+	attributes uint16
+	ansiColor  string
+}
+
+func TestWriteAnsiColorText(t *testing.T) {
+	screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
+	if screenInfo == nil {
+		t.Fatal("Could not get ConsoleScreenBufferInfo")
+	}
+	defer ChangeColor(screenInfo.WAttributes)
+	defaultFgColor := screenInfo.WAttributes & uint16(0x0007)
+	defaultBgColor := screenInfo.WAttributes & uint16(0x0070)
+	defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008)
+	defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080)
+
+	fgParam := []testParam{
+		{"foreground black  ", uint16(0x0000 | 0x0000), "30"},
+		{"foreground red    ", uint16(0x0004 | 0x0000), "31"},
+		{"foreground green  ", uint16(0x0002 | 0x0000), "32"},
+		{"foreground yellow ", uint16(0x0006 | 0x0000), "33"},
+		{"foreground blue   ", uint16(0x0001 | 0x0000), "34"},
+		{"foreground magenta", uint16(0x0005 | 0x0000), "35"},
+		{"foreground cyan   ", uint16(0x0003 | 0x0000), "36"},
+		{"foreground white  ", uint16(0x0007 | 0x0000), "37"},
+		{"foreground default", defaultFgColor | 0x0000, "39"},
+		{"foreground light gray   ", uint16(0x0000 | 0x0008 | 0x0000), "90"},
+		{"foreground light red    ", uint16(0x0004 | 0x0008 | 0x0000), "91"},
+		{"foreground light green  ", uint16(0x0002 | 0x0008 | 0x0000), "92"},
+		{"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"},
+		{"foreground light blue   ", uint16(0x0001 | 0x0008 | 0x0000), "94"},
+		{"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"},
+		{"foreground light cyan   ", uint16(0x0003 | 0x0008 | 0x0000), "96"},
+		{"foreground light white  ", uint16(0x0007 | 0x0008 | 0x0000), "97"},
+	}
+
+	bgParam := []testParam{
+		{"background black  ", uint16(0x0007 | 0x0000), "40"},
+		{"background red    ", uint16(0x0007 | 0x0040), "41"},
+		{"background green  ", uint16(0x0007 | 0x0020), "42"},
+		{"background yellow ", uint16(0x0007 | 0x0060), "43"},
+		{"background blue   ", uint16(0x0007 | 0x0010), "44"},
+		{"background magenta", uint16(0x0007 | 0x0050), "45"},
+		{"background cyan   ", uint16(0x0007 | 0x0030), "46"},
+		{"background white  ", uint16(0x0007 | 0x0070), "47"},
+		{"background default", uint16(0x0007) | defaultBgColor, "49"},
+		{"background light gray   ", uint16(0x0007 | 0x0000 | 0x0080), "100"},
+		{"background light red    ", uint16(0x0007 | 0x0040 | 0x0080), "101"},
+		{"background light green  ", uint16(0x0007 | 0x0020 | 0x0080), "102"},
+		{"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"},
+		{"background light blue   ", uint16(0x0007 | 0x0010 | 0x0080), "104"},
+		{"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"},
+		{"background light cyan   ", uint16(0x0007 | 0x0030 | 0x0080), "106"},
+		{"background light white  ", uint16(0x0007 | 0x0070 | 0x0080), "107"},
+	}
+
+	resetParam := []testParam{
+		{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"},
+		{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""},
+	}
+
+	boldParam := []testParam{
+		{"bold on", uint16(0x0007 | 0x0008), "1"},
+		{"bold off", uint16(0x0007), "21"},
+	}
+
+	underscoreParam := []testParam{
+		{"underscore on", uint16(0x0007 | 0x8000), "4"},
+		{"underscore off", uint16(0x0007), "24"},
+	}
+
+	blinkParam := []testParam{
+		{"blink on", uint16(0x0007 | 0x0080), "5"},
+		{"blink off", uint16(0x0007), "25"},
+	}
+
+	mixedParam := []testParam{
+		{"both black,   bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"},
+		{"both red,     bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"},
+		{"both green,   bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"},
+		{"both yellow,  bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"},
+		{"both blue,    bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"},
+		{"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"},
+		{"both cyan,    bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"},
+		{"both white,   bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"},
+		{"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"},
+	}
+
+	assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) {
+		actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor)
+		if actualText != expectedText {
+			t.Errorf("Get %q, want %q", actualText, expectedText)
+		}
+		if err != nil {
+			t.Fatal("Could not get ConsoleScreenBufferInfo")
+		}
+		if actualAttributes != expectedAttributes {
+			t.Errorf("Text: %q, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes)
+		}
+	}
+
+	for _, v := range fgParam {
+		ResetColor()
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	for _, v := range bgParam {
+		ChangeColor(uint16(0x0070 | 0x0007))
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	for _, v := range resetParam {
+		ChangeColor(uint16(0x0000 | 0x0070 | 0x0008))
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	ResetColor()
+	for _, v := range boldParam {
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	ResetColor()
+	for _, v := range underscoreParam {
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	ResetColor()
+	for _, v := range blinkParam {
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+
+	for _, v := range mixedParam {
+		ResetColor()
+		assertTextAttribute(v.text, v.attributes, v.ansiColor)
+	}
+}
+
+func TestIgnoreUnknownSequences(t *testing.T) {
+	inner := bytes.NewBufferString("")
+	w := NewModeAnsiColorWriter(inner, OutputNonColorEscSeq)
+
+	inputText := "\x1b[=decpath mode"
+	expectedTail := inputText
+	fmt.Fprintf(w, inputText)
+	actualTail := inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+
+	inputText = "\x1b[=tailing esc and bracket\x1b["
+	expectedTail = inputText
+	fmt.Fprintf(w, inputText)
+	actualTail = inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+
+	inputText = "\x1b[?tailing esc\x1b"
+	expectedTail = inputText
+	fmt.Fprintf(w, inputText)
+	actualTail = inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+
+	inputText = "\x1b[1h;3punended color code invalid\x1b3"
+	expectedTail = inputText
+	fmt.Fprintf(w, inputText)
+	actualTail = inner.String()
+	inner.Reset()
+	if actualTail != expectedTail {
+		t.Errorf("Get %q, want %q", actualTail, expectedTail)
+	}
+}

+ 25 - 0
vendor/github.com/astaxie/beego/logs/conn_test.go

@@ -0,0 +1,25 @@
+// 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 logs
+
+import (
+	"testing"
+)
+
+func TestConn(t *testing.T) {
+	log := NewLogger(1000)
+	log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
+	log.Informational("informational")
+}

+ 51 - 0
vendor/github.com/astaxie/beego/logs/console_test.go

@@ -0,0 +1,51 @@
+// 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 logs
+
+import (
+	"testing"
+)
+
+// Try each log level in decreasing order of priority.
+func testConsoleCalls(bl *BeeLogger) {
+	bl.Emergency("emergency")
+	bl.Alert("alert")
+	bl.Critical("critical")
+	bl.Error("error")
+	bl.Warning("warning")
+	bl.Notice("notice")
+	bl.Informational("informational")
+	bl.Debug("debug")
+}
+
+// Test console logging by visually comparing the lines being output with and
+// without a log level specification.
+func TestConsole(t *testing.T) {
+	log1 := NewLogger(10000)
+	log1.EnableFuncCallDepth(true)
+	log1.SetLogger("console", "")
+	testConsoleCalls(log1)
+
+	log2 := NewLogger(100)
+	log2.SetLogger("console", `{"level":3}`)
+	testConsoleCalls(log2)
+}
+
+// Test console without color
+func TestConsoleNoColor(t *testing.T) {
+	log := NewLogger(100)
+	log.SetLogger("console", `{"color":false}`)
+	testConsoleCalls(log)
+}

+ 80 - 0
vendor/github.com/astaxie/beego/logs/es/es.go

@@ -0,0 +1,80 @@
+package es
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net"
+	"net/url"
+	"time"
+
+	"github.com/astaxie/beego/logs"
+	"github.com/belogik/goes"
+)
+
+// NewES return a LoggerInterface
+func NewES() logs.Logger {
+	cw := &esLogger{
+		Level: logs.LevelDebug,
+	}
+	return cw
+}
+
+type esLogger struct {
+	*goes.Connection
+	DSN   string `json:"dsn"`
+	Level int    `json:"level"`
+}
+
+// {"dsn":"http://localhost:9200/","level":1}
+func (el *esLogger) Init(jsonconfig string) error {
+	err := json.Unmarshal([]byte(jsonconfig), el)
+	if err != nil {
+		return err
+	}
+	if el.DSN == "" {
+		return errors.New("empty dsn")
+	} else if u, err := url.Parse(el.DSN); err != nil {
+		return err
+	} else if u.Path == "" {
+		return errors.New("missing prefix")
+	} else if host, port, err := net.SplitHostPort(u.Host); err != nil {
+		return err
+	} else {
+		conn := goes.NewConnection(host, port)
+		el.Connection = conn
+	}
+	return nil
+}
+
+// WriteMsg will write the msg and level into es
+func (el *esLogger) WriteMsg(when time.Time, msg string, level int) error {
+	if level > el.Level {
+		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,
+	}
+	_, err := el.Index(d, nil)
+	return err
+}
+
+// Destroy is a empty method
+func (el *esLogger) Destroy() {
+
+}
+
+// Flush is a empty method
+func (el *esLogger) Flush() {
+
+}
+
+func init() {
+	logs.Register(logs.AdapterEs, NewES)
+}

+ 280 - 0
vendor/github.com/astaxie/beego/logs/file_test.go

@@ -0,0 +1,280 @@
+// 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 logs
+
+import (
+	"bufio"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strconv"
+	"testing"
+	"time"
+)
+
+func TestFilePerm(t *testing.T) {
+	log := NewLogger(10000)
+	// use 0666 as test perm cause the default umask is 022
+	log.SetLogger("file", `{"filename":"test.log", "perm": "0666"}`)
+	log.Debug("debug")
+	log.Informational("info")
+	log.Notice("notice")
+	log.Warning("warning")
+	log.Error("error")
+	log.Alert("alert")
+	log.Critical("critical")
+	log.Emergency("emergency")
+	file, err := os.Stat("test.log")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if file.Mode() != 0666 {
+		t.Fatal("unexpected log file permission")
+	}
+	os.Remove("test.log")
+}
+
+func TestFile1(t *testing.T) {
+	log := NewLogger(10000)
+	log.SetLogger("file", `{"filename":"test.log"}`)
+	log.Debug("debug")
+	log.Informational("info")
+	log.Notice("notice")
+	log.Warning("warning")
+	log.Error("error")
+	log.Alert("alert")
+	log.Critical("critical")
+	log.Emergency("emergency")
+	f, err := os.Open("test.log")
+	if err != nil {
+		t.Fatal(err)
+	}
+	b := bufio.NewReader(f)
+	lineNum := 0
+	for {
+		line, _, err := b.ReadLine()
+		if err != nil {
+			break
+		}
+		if len(line) > 0 {
+			lineNum++
+		}
+	}
+	var expected = LevelDebug + 1
+	if lineNum != expected {
+		t.Fatal(lineNum, "not "+strconv.Itoa(expected)+" lines")
+	}
+	os.Remove("test.log")
+}
+
+func TestFile2(t *testing.T) {
+	log := NewLogger(10000)
+	log.SetLogger("file", fmt.Sprintf(`{"filename":"test2.log","level":%d}`, LevelError))
+	log.Debug("debug")
+	log.Info("info")
+	log.Notice("notice")
+	log.Warning("warning")
+	log.Error("error")
+	log.Alert("alert")
+	log.Critical("critical")
+	log.Emergency("emergency")
+	f, err := os.Open("test2.log")
+	if err != nil {
+		t.Fatal(err)
+	}
+	b := bufio.NewReader(f)
+	lineNum := 0
+	for {
+		line, _, err := b.ReadLine()
+		if err != nil {
+			break
+		}
+		if len(line) > 0 {
+			lineNum++
+		}
+	}
+	var expected = LevelError + 1
+	if lineNum != expected {
+		t.Fatal(lineNum, "not "+strconv.Itoa(expected)+" lines")
+	}
+	os.Remove("test2.log")
+}
+
+func TestFileRotate_01(t *testing.T) {
+	log := NewLogger(10000)
+	log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`)
+	log.Debug("debug")
+	log.Info("info")
+	log.Notice("notice")
+	log.Warning("warning")
+	log.Error("error")
+	log.Alert("alert")
+	log.Critical("critical")
+	log.Emergency("emergency")
+	rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log"
+	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 TestFileRotate_02(t *testing.T) {
+	fn1 := "rotate_day.log"
+	fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log"
+	testFileRotate(t, fn1, fn2)
+}
+
+func TestFileRotate_03(t *testing.T) {
+	fn1 := "rotate_day.log"
+	fn := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log"
+	os.Create(fn)
+	fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log"
+	testFileRotate(t, fn1, fn2)
+	os.Remove(fn)
+}
+
+func TestFileRotate_04(t *testing.T) {
+	fn1 := "rotate_day.log"
+	fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log"
+	testFileDailyRotate(t, fn1, fn2)
+}
+
+func TestFileRotate_05(t *testing.T) {
+	fn1 := "rotate_day.log"
+	fn := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log"
+	os.Create(fn)
+	fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log"
+	testFileDailyRotate(t, fn1, fn2)
+	os.Remove(fn)
+}
+
+func testFileRotate(t *testing.T, fn1, fn2 string) {
+	fw := &fileLogWriter{
+		Daily:   true,
+		MaxDays: 7,
+		Rotate:  true,
+		Level:   LevelTrace,
+		Perm:    "0660",
+	}
+	fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
+	fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
+	fw.dailyOpenDate = fw.dailyOpenTime.Day()
+	fw.WriteMsg(time.Now(), "this is a msg for test", LevelDebug)
+
+	for _, file := range []string{fn1, fn2} {
+		_, err := os.Stat(file)
+		if err != nil {
+			t.FailNow()
+		}
+		os.Remove(file)
+	}
+	fw.Destroy()
+}
+
+func testFileDailyRotate(t *testing.T, fn1, fn2 string) {
+	fw := &fileLogWriter{
+		Daily:   true,
+		MaxDays: 7,
+		Rotate:  true,
+		Level:   LevelTrace,
+		Perm:    "0660",
+	}
+	fw.Init(fmt.Sprintf(`{"filename":"%v","maxdays":1}`, fn1))
+	fw.dailyOpenTime = time.Now().Add(-24 * time.Hour)
+	fw.dailyOpenDate = fw.dailyOpenTime.Day()
+	today, _ := time.ParseInLocation("2006-01-02", time.Now().Format("2006-01-02"), fw.dailyOpenTime.Location())
+	today = today.Add(-1 * time.Second)
+	fw.dailyRotate(today)
+	for _, file := range []string{fn1, fn2} {
+		_, err := os.Stat(file)
+		if err != nil {
+			t.FailNow()
+		}
+		content, err := ioutil.ReadFile(file)
+		if err != nil {
+			t.FailNow()
+		}
+		if len(content) > 0 {
+			t.FailNow()
+		}
+		os.Remove(file)
+	}
+	fw.Destroy()
+}
+
+func exists(path string) (bool, error) {
+	_, err := os.Stat(path)
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, err
+}
+
+func BenchmarkFile(b *testing.B) {
+	log := NewLogger(100000)
+	log.SetLogger("file", `{"filename":"test4.log"}`)
+	for i := 0; i < b.N; i++ {
+		log.Debug("debug")
+	}
+	os.Remove("test4.log")
+}
+
+func BenchmarkFileAsynchronous(b *testing.B) {
+	log := NewLogger(100000)
+	log.SetLogger("file", `{"filename":"test4.log"}`)
+	log.Async()
+	for i := 0; i < b.N; i++ {
+		log.Debug("debug")
+	}
+	os.Remove("test4.log")
+}
+
+func BenchmarkFileCallDepth(b *testing.B) {
+	log := NewLogger(100000)
+	log.SetLogger("file", `{"filename":"test4.log"}`)
+	log.EnableFuncCallDepth(true)
+	log.SetLogFuncCallDepth(2)
+	for i := 0; i < b.N; i++ {
+		log.Debug("debug")
+	}
+	os.Remove("test4.log")
+}
+
+func BenchmarkFileAsynchronousCallDepth(b *testing.B) {
+	log := NewLogger(100000)
+	log.SetLogger("file", `{"filename":"test4.log"}`)
+	log.EnableFuncCallDepth(true)
+	log.SetLogFuncCallDepth(2)
+	log.Async()
+	for i := 0; i < b.N; i++ {
+		log.Debug("debug")
+	}
+	os.Remove("test4.log")
+}
+
+func BenchmarkFileOnGoroutine(b *testing.B) {
+	log := NewLogger(100000)
+	log.SetLogger("file", `{"filename":"test4.log"}`)
+	for i := 0; i < b.N; i++ {
+		go log.Debug("debug")
+	}
+	os.Remove("test4.log")
+}

+ 75 - 0
vendor/github.com/astaxie/beego/logs/logger_test.go

@@ -0,0 +1,75 @@
+// Copyright 2016 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package logs
+
+import (
+	"bytes"
+	"testing"
+	"time"
+)
+
+func TestFormatHeader_0(t *testing.T) {
+	tm := time.Now()
+	if tm.Year() >= 2100 {
+		t.FailNow()
+	}
+	dur := time.Second
+	for {
+		if tm.Year() >= 2100 {
+			break
+		}
+		h, _ := formatTimeHeader(tm)
+		if tm.Format("2006/01/02 15:04:05 ") != string(h) {
+			t.Log(tm)
+			t.FailNow()
+		}
+		tm = tm.Add(dur)
+		dur *= 2
+	}
+}
+
+func TestFormatHeader_1(t *testing.T) {
+	tm := time.Now()
+	year := tm.Year()
+	dur := time.Second
+	for {
+		if tm.Year() >= year+1 {
+			break
+		}
+		h, _ := formatTimeHeader(tm)
+		if tm.Format("2006/01/02 15:04:05 ") != string(h) {
+			t.Log(tm)
+			t.FailNow()
+		}
+		tm = tm.Add(dur)
+	}
+}
+
+func TestNewAnsiColor1(t *testing.T) {
+	inner := bytes.NewBufferString("")
+	w := NewAnsiColorWriter(inner)
+	if w == inner {
+		t.Errorf("Get %#v, want %#v", w, inner)
+	}
+}
+
+func TestNewAnsiColor2(t *testing.T) {
+	inner := bytes.NewBufferString("")
+	w1 := NewAnsiColorWriter(inner)
+	w2 := NewAnsiColorWriter(w1)
+	if w1 != w2 {
+		t.Errorf("Get %#v, want %#v", w1, w2)
+	}
+}

+ 78 - 0
vendor/github.com/astaxie/beego/logs/multifile_test.go

@@ -0,0 +1,78 @@
+// 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 logs
+
+import (
+	"bufio"
+	"os"
+	"strconv"
+	"strings"
+	"testing"
+)
+
+func TestFiles_1(t *testing.T) {
+	log := NewLogger(10000)
+	log.SetLogger("multifile", `{"filename":"test.log","separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"]}`)
+	log.Debug("debug")
+	log.Informational("info")
+	log.Notice("notice")
+	log.Warning("warning")
+	log.Error("error")
+	log.Alert("alert")
+	log.Critical("critical")
+	log.Emergency("emergency")
+	fns := []string{""}
+	fns = append(fns, levelNames[0:]...)
+	name := "test"
+	suffix := ".log"
+	for _, fn := range fns {
+
+		file := name + suffix
+		if fn != "" {
+			file = name + "." + fn + suffix
+		}
+		f, err := os.Open(file)
+		if err != nil {
+			t.Fatal(err)
+		}
+		b := bufio.NewReader(f)
+		lineNum := 0
+		lastLine := ""
+		for {
+			line, _, err := b.ReadLine()
+			if err != nil {
+				break
+			}
+			if len(line) > 0 {
+				lastLine = string(line)
+				lineNum++
+			}
+		}
+		var expected = 1
+		if fn == "" {
+			expected = LevelDebug + 1
+		}
+		if lineNum != expected {
+			t.Fatal(file, "has", lineNum, "lines not "+strconv.Itoa(expected)+" lines")
+		}
+		if lineNum == 1 {
+			if !strings.Contains(lastLine, fn) {
+				t.Fatal(file + " " + lastLine + " not contains the log msg " + fn)
+			}
+		}
+		os.Remove(file)
+	}
+
+}

+ 27 - 0
vendor/github.com/astaxie/beego/logs/smtp_test.go

@@ -0,0 +1,27 @@
+// 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 logs
+
+import (
+	"testing"
+	"time"
+)
+
+func TestSmtp(t *testing.T) {
+	log := NewLogger(10000)
+	log.SetLogger("smtp", `{"username":"[email protected]","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["[email protected]"]}`)
+	log.Critical("sendmail critical")
+	time.Sleep(time.Second * 30)
+}

+ 53 - 0
vendor/github.com/astaxie/beego/migration/ddl.go

@@ -0,0 +1,53 @@
+// 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 migration
+
+// Table store the tablename and Column
+type Table struct {
+	TableName string
+	Columns   []*Column
+}
+
+// Create return the create sql
+func (t *Table) Create() string {
+	return ""
+}
+
+// Drop return the drop sql
+func (t *Table) Drop() string {
+	return ""
+}
+
+// Column define the columns name type and Default
+type Column struct {
+	Name    string
+	Type    string
+	Default interface{}
+}
+
+// Create return create sql with the provided tbname and columns
+func Create(tbname string, columns ...Column) string {
+	return ""
+}
+
+// Drop return the drop sql with the provided tbname and columns
+func Drop(tbname string, columns ...Column) string {
+	return ""
+}
+
+// TableDDL is still in think
+func TableDDL(tbname string, columns ...Column) string {
+	return ""
+}

+ 278 - 0
vendor/github.com/astaxie/beego/migration/migration.go

@@ -0,0 +1,278 @@
+// 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 migration is used for migration
+//
+// The table structure is as follow:
+//
+//	CREATE TABLE `migrations` (
+//		`id_migration` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
+//		`name` varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
+//		`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'date migrated or rolled back',
+//		`statements` longtext COMMENT 'SQL statements for this migration',
+//		`rollback_statements` longtext,
+//		`status` enum('update','rollback') DEFAULT NULL COMMENT 'update indicates it is a normal migration while rollback means this migration is rolled back',
+//		PRIMARY KEY (`id_migration`)
+//	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+package migration
+
+import (
+	"errors"
+	"sort"
+	"strings"
+	"time"
+
+	"github.com/astaxie/beego/logs"
+	"github.com/astaxie/beego/orm"
+)
+
+// const the data format for the bee generate migration datatype
+const (
+	DateFormat   = "20060102_150405"
+	DBDateFormat = "2006-01-02 15:04:05"
+)
+
+// Migrationer is an interface for all Migration struct
+type Migrationer interface {
+	Up()
+	Down()
+	Reset()
+	Exec(name, status string) error
+	GetCreated() int64
+}
+
+var (
+	migrationMap map[string]Migrationer
+)
+
+func init() {
+	migrationMap = make(map[string]Migrationer)
+}
+
+// Migration the basic type which will implement the basic type
+type Migration struct {
+	sqls    []string
+	Created string
+}
+
+// Up implement in the Inheritance struct for upgrade
+func (m *Migration) Up() {
+
+}
+
+// Down implement in the Inheritance struct for down
+func (m *Migration) Down() {
+
+}
+
+// SQL add sql want to execute
+func (m *Migration) SQL(sql string) {
+	m.sqls = append(m.sqls, sql)
+}
+
+// Reset the sqls
+func (m *Migration) Reset() {
+	m.sqls = make([]string, 0)
+}
+
+// Exec execute the sql already add in the sql
+func (m *Migration) Exec(name, status string) error {
+	o := orm.NewOrm()
+	for _, s := range m.sqls {
+		logs.Info("exec sql:", s)
+		r := o.Raw(s)
+		_, err := r.Exec()
+		if err != nil {
+			return err
+		}
+	}
+	return m.addOrUpdateRecord(name, status)
+}
+
+func (m *Migration) addOrUpdateRecord(name, status string) error {
+	o := orm.NewOrm()
+	if status == "down" {
+		status = "rollback"
+		p, err := o.Raw("update migrations set status = ?, rollback_statements = ?, created_at = ? where name = ?").Prepare()
+		if err != nil {
+			return nil
+		}
+		_, err = p.Exec(status, strings.Join(m.sqls, "; "), time.Now().Format(DBDateFormat), name)
+		return err
+	}
+	status = "update"
+	p, err := o.Raw("insert into migrations(name, created_at, statements, status) values(?,?,?,?)").Prepare()
+	if err != nil {
+		return err
+	}
+	_, err = p.Exec(name, time.Now().Format(DBDateFormat), strings.Join(m.sqls, "; "), status)
+	return err
+}
+
+// GetCreated get the unixtime from the Created
+func (m *Migration) GetCreated() int64 {
+	t, err := time.Parse(DateFormat, m.Created)
+	if err != nil {
+		return 0
+	}
+	return t.Unix()
+}
+
+// Register register the Migration in the map
+func Register(name string, m Migrationer) error {
+	if _, ok := migrationMap[name]; ok {
+		return errors.New("already exist name:" + name)
+	}
+	migrationMap[name] = m
+	return nil
+}
+
+// Upgrade upgrate the migration from lasttime
+func Upgrade(lasttime int64) error {
+	sm := sortMap(migrationMap)
+	i := 0
+	for _, v := range sm {
+		if v.created > lasttime {
+			logs.Info("start upgrade", v.name)
+			v.m.Reset()
+			v.m.Up()
+			err := v.m.Exec(v.name, "up")
+			if err != nil {
+				logs.Error("execute error:", err)
+				time.Sleep(2 * time.Second)
+				return err
+			}
+			logs.Info("end upgrade:", v.name)
+			i++
+		}
+	}
+	logs.Info("total success upgrade:", i, " migration")
+	time.Sleep(2 * time.Second)
+	return nil
+}
+
+// Rollback rollback the migration by the name
+func Rollback(name string) error {
+	if v, ok := migrationMap[name]; ok {
+		logs.Info("start rollback")
+		v.Reset()
+		v.Down()
+		err := v.Exec(name, "down")
+		if err != nil {
+			logs.Error("execute error:", err)
+			time.Sleep(2 * time.Second)
+			return err
+		}
+		logs.Info("end rollback")
+		time.Sleep(2 * time.Second)
+		return nil
+	}
+	logs.Error("not exist the migrationMap name:" + name)
+	time.Sleep(2 * time.Second)
+	return errors.New("not exist the migrationMap name:" + name)
+}
+
+// Reset reset all migration
+// run all migration's down function
+func Reset() error {
+	sm := sortMap(migrationMap)
+	i := 0
+	for j := len(sm) - 1; j >= 0; j-- {
+		v := sm[j]
+		if isRollBack(v.name) {
+			logs.Info("skip the", v.name)
+			time.Sleep(1 * time.Second)
+			continue
+		}
+		logs.Info("start reset:", v.name)
+		v.m.Reset()
+		v.m.Down()
+		err := v.m.Exec(v.name, "down")
+		if err != nil {
+			logs.Error("execute error:", err)
+			time.Sleep(2 * time.Second)
+			return err
+		}
+		i++
+		logs.Info("end reset:", v.name)
+	}
+	logs.Info("total success reset:", i, " migration")
+	time.Sleep(2 * time.Second)
+	return nil
+}
+
+// Refresh first Reset, then Upgrade
+func Refresh() error {
+	err := Reset()
+	if err != nil {
+		logs.Error("execute error:", err)
+		time.Sleep(2 * time.Second)
+		return err
+	}
+	err = Upgrade(0)
+	return err
+}
+
+type dataSlice []data
+
+type data struct {
+	created int64
+	name    string
+	m       Migrationer
+}
+
+// Len is part of sort.Interface.
+func (d dataSlice) Len() int {
+	return len(d)
+}
+
+// Swap is part of sort.Interface.
+func (d dataSlice) Swap(i, j int) {
+	d[i], d[j] = d[j], d[i]
+}
+
+// Less is part of sort.Interface. We use count as the value to sort by
+func (d dataSlice) Less(i, j int) bool {
+	return d[i].created < d[j].created
+}
+
+func sortMap(m map[string]Migrationer) dataSlice {
+	s := make(dataSlice, 0, len(m))
+	for k, v := range m {
+		d := data{}
+		d.created = v.GetCreated()
+		d.name = k
+		d.m = v
+		s = append(s, d)
+	}
+	sort.Sort(s)
+	return s
+}
+
+func isRollBack(name string) bool {
+	o := orm.NewOrm()
+	var maps []orm.Params
+	num, err := o.Raw("select * from migrations where `name` = ? order by id_migration desc", name).Values(&maps)
+	if err != nil {
+		logs.Info("get name has error", err)
+		return false
+	}
+	if num <= 0 {
+		return false
+	}
+	if maps[0]["status"] == "rollback" {
+		return true
+	}
+	return false
+}

+ 171 - 0
vendor/github.com/astaxie/beego/namespace_test.go

@@ -0,0 +1,171 @@
+// 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 beego
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"strconv"
+	"testing"
+
+	"github.com/astaxie/beego/context"
+)
+
+func TestNamespaceGet(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/v1/user", nil)
+	w := httptest.NewRecorder()
+
+	ns := NewNamespace("/v1")
+	ns.Get("/user", func(ctx *context.Context) {
+		ctx.Output.Body([]byte("v1_user"))
+	})
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Body.String() != "v1_user" {
+		t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String())
+	}
+}
+
+func TestNamespacePost(t *testing.T) {
+	r, _ := http.NewRequest("POST", "/v1/user/123", nil)
+	w := httptest.NewRecorder()
+
+	ns := NewNamespace("/v1")
+	ns.Post("/user/:id", func(ctx *context.Context) {
+		ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+	})
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Body.String() != "123" {
+		t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String())
+	}
+}
+
+func TestNamespaceNest(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/v1/admin/order", nil)
+	w := httptest.NewRecorder()
+
+	ns := NewNamespace("/v1")
+	ns.Namespace(
+		NewNamespace("/admin").
+			Get("/order", func(ctx *context.Context) {
+				ctx.Output.Body([]byte("order"))
+			}),
+	)
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Body.String() != "order" {
+		t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String())
+	}
+}
+
+func TestNamespaceNestParam(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/v1/admin/order/123", nil)
+	w := httptest.NewRecorder()
+
+	ns := NewNamespace("/v1")
+	ns.Namespace(
+		NewNamespace("/admin").
+			Get("/order/:id", func(ctx *context.Context) {
+				ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+			}),
+	)
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Body.String() != "123" {
+		t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String())
+	}
+}
+
+func TestNamespaceRouter(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/v1/api/list", nil)
+	w := httptest.NewRecorder()
+
+	ns := NewNamespace("/v1")
+	ns.Router("/api/list", &TestController{}, "*:List")
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Body.String() != "i am list" {
+		t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String())
+	}
+}
+
+func TestNamespaceAutoFunc(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/v1/test/list", nil)
+	w := httptest.NewRecorder()
+
+	ns := NewNamespace("/v1")
+	ns.AutoRouter(&TestController{})
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Body.String() != "i am list" {
+		t.Errorf("user define func can't run")
+	}
+}
+
+func TestNamespaceFilter(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/v1/user/123", nil)
+	w := httptest.NewRecorder()
+
+	ns := NewNamespace("/v1")
+	ns.Filter("before", func(ctx *context.Context) {
+		ctx.Output.Body([]byte("this is Filter"))
+	}).
+		Get("/user/:id", func(ctx *context.Context) {
+			ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+		})
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Body.String() != "this is Filter" {
+		t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String())
+	}
+}
+
+func TestNamespaceCond(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/v2/test/list", nil)
+	w := httptest.NewRecorder()
+
+	ns := NewNamespace("/v2")
+	ns.Cond(func(ctx *context.Context) bool {
+		if ctx.Input.Domain() == "beego.me" {
+			return true
+		}
+		return false
+	}).
+		AutoRouter(&TestController{})
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Code != 405 {
+		t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code))
+	}
+}
+
+func TestNamespaceInside(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/v3/shop/order/123", nil)
+	w := httptest.NewRecorder()
+	ns := NewNamespace("/v3",
+		NSAutoRouter(&TestController{}),
+		NSNamespace("/shop",
+			NSGet("/order/:id", func(ctx *context.Context) {
+				ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+			}),
+		),
+	)
+	AddNamespace(ns)
+	BeeApp.Handlers.ServeHTTP(w, r)
+	if w.Body.String() != "123" {
+		t.Errorf("TestNamespaceInside can't run, get the response is " + w.Body.String())
+	}
+}

+ 493 - 0
vendor/github.com/astaxie/beego/orm/models_test.go

@@ -0,0 +1,493 @@
+// 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 orm
+
+import (
+	"database/sql"
+	"encoding/json"
+	"fmt"
+	"os"
+	"strings"
+	"time"
+
+	_ "github.com/go-sql-driver/mysql"
+	_ "github.com/lib/pq"
+	_ "github.com/mattn/go-sqlite3"
+	// As tidb can't use go get, so disable the tidb testing now
+	// _ "github.com/pingcap/tidb"
+)
+
+// A slice string field.
+type SliceStringField []string
+
+func (e SliceStringField) Value() []string {
+	return []string(e)
+}
+
+func (e *SliceStringField) Set(d []string) {
+	*e = SliceStringField(d)
+}
+
+func (e *SliceStringField) Add(v string) {
+	*e = append(*e, v)
+}
+
+func (e *SliceStringField) String() string {
+	return strings.Join(e.Value(), ",")
+}
+
+func (e *SliceStringField) FieldType() int {
+	return TypeCharField
+}
+
+func (e *SliceStringField) SetRaw(value interface{}) error {
+	switch d := value.(type) {
+	case []string:
+		e.Set(d)
+	case string:
+		if len(d) > 0 {
+			parts := strings.Split(d, ",")
+			v := make([]string, 0, len(parts))
+			for _, p := range parts {
+				v = append(v, strings.TrimSpace(p))
+			}
+			e.Set(v)
+		}
+	default:
+		return fmt.Errorf("<SliceStringField.SetRaw> unknown value `%v`", value)
+	}
+	return nil
+}
+
+func (e *SliceStringField) RawValue() interface{} {
+	return e.String()
+}
+
+var _ Fielder = new(SliceStringField)
+
+// A json field.
+type JSONFieldTest struct {
+	Name string
+	Data string
+}
+
+func (e *JSONFieldTest) String() string {
+	data, _ := json.Marshal(e)
+	return string(data)
+}
+
+func (e *JSONFieldTest) FieldType() int {
+	return TypeTextField
+}
+
+func (e *JSONFieldTest) SetRaw(value interface{}) error {
+	switch d := value.(type) {
+	case string:
+		return json.Unmarshal([]byte(d), e)
+	default:
+		return fmt.Errorf("<JSONField.SetRaw> unknown value `%v`", value)
+	}
+}
+
+func (e *JSONFieldTest) RawValue() interface{} {
+	return e.String()
+}
+
+var _ Fielder = new(JSONFieldTest)
+
+type Data struct {
+	ID       int `orm:"column(id)"`
+	Boolean  bool
+	Char     string    `orm:"size(50)"`
+	Text     string    `orm:"type(text)"`
+	JSON     string    `orm:"type(json);default({\"name\":\"json\"})"`
+	Jsonb    string    `orm:"type(jsonb)"`
+	Time     time.Time `orm:"type(time)"`
+	Date     time.Time `orm:"type(date)"`
+	DateTime time.Time `orm:"column(datetime)"`
+	Byte     byte
+	Rune     rune
+	Int      int
+	Int8     int8
+	Int16    int16
+	Int32    int32
+	Int64    int64
+	Uint     uint
+	Uint8    uint8
+	Uint16   uint16
+	Uint32   uint32
+	Uint64   uint64
+	Float32  float32
+	Float64  float64
+	Decimal  float64 `orm:"digits(8);decimals(4)"`
+}
+
+type DataNull struct {
+	ID          int             `orm:"column(id)"`
+	Boolean     bool            `orm:"null"`
+	Char        string          `orm:"null;size(50)"`
+	Text        string          `orm:"null;type(text)"`
+	JSON        string          `orm:"type(json);null"`
+	Jsonb       string          `orm:"type(jsonb);null"`
+	Time        time.Time       `orm:"null;type(time)"`
+	Date        time.Time       `orm:"null;type(date)"`
+	DateTime    time.Time       `orm:"null;column(datetime)"`
+	Byte        byte            `orm:"null"`
+	Rune        rune            `orm:"null"`
+	Int         int             `orm:"null"`
+	Int8        int8            `orm:"null"`
+	Int16       int16           `orm:"null"`
+	Int32       int32           `orm:"null"`
+	Int64       int64           `orm:"null"`
+	Uint        uint            `orm:"null"`
+	Uint8       uint8           `orm:"null"`
+	Uint16      uint16          `orm:"null"`
+	Uint32      uint32          `orm:"null"`
+	Uint64      uint64          `orm:"null"`
+	Float32     float32         `orm:"null"`
+	Float64     float64         `orm:"null"`
+	Decimal     float64         `orm:"digits(8);decimals(4);null"`
+	NullString  sql.NullString  `orm:"null"`
+	NullBool    sql.NullBool    `orm:"null"`
+	NullFloat64 sql.NullFloat64 `orm:"null"`
+	NullInt64   sql.NullInt64   `orm:"null"`
+	BooleanPtr  *bool           `orm:"null"`
+	CharPtr     *string         `orm:"null;size(50)"`
+	TextPtr     *string         `orm:"null;type(text)"`
+	BytePtr     *byte           `orm:"null"`
+	RunePtr     *rune           `orm:"null"`
+	IntPtr      *int            `orm:"null"`
+	Int8Ptr     *int8           `orm:"null"`
+	Int16Ptr    *int16          `orm:"null"`
+	Int32Ptr    *int32          `orm:"null"`
+	Int64Ptr    *int64          `orm:"null"`
+	UintPtr     *uint           `orm:"null"`
+	Uint8Ptr    *uint8          `orm:"null"`
+	Uint16Ptr   *uint16         `orm:"null"`
+	Uint32Ptr   *uint32         `orm:"null"`
+	Uint64Ptr   *uint64         `orm:"null"`
+	Float32Ptr  *float32        `orm:"null"`
+	Float64Ptr  *float64        `orm:"null"`
+	DecimalPtr  *float64        `orm:"digits(8);decimals(4);null"`
+	TimePtr     *time.Time      `orm:"null;type(time)"`
+	DatePtr     *time.Time      `orm:"null;type(date)"`
+	DateTimePtr *time.Time      `orm:"null"`
+}
+
+type String string
+type Boolean bool
+type Byte byte
+type Rune rune
+type Int int
+type Int8 int8
+type Int16 int16
+type Int32 int32
+type Int64 int64
+type Uint uint
+type Uint8 uint8
+type Uint16 uint16
+type Uint32 uint32
+type Uint64 uint64
+type Float32 float64
+type Float64 float64
+
+type DataCustom struct {
+	ID      int `orm:"column(id)"`
+	Boolean Boolean
+	Char    string `orm:"size(50)"`
+	Text    string `orm:"type(text)"`
+	Byte    Byte
+	Rune    Rune
+	Int     Int
+	Int8    Int8
+	Int16   Int16
+	Int32   Int32
+	Int64   Int64
+	Uint    Uint
+	Uint8   Uint8
+	Uint16  Uint16
+	Uint32  Uint32
+	Uint64  Uint64
+	Float32 Float32
+	Float64 Float64
+	Decimal Float64 `orm:"digits(8);decimals(4)"`
+}
+
+// only for mysql
+type UserBig struct {
+	ID   uint64 `orm:"column(id)"`
+	Name string
+}
+
+type User struct {
+	ID           int    `orm:"column(id)"`
+	UserName     string `orm:"size(30);unique"`
+	Email        string `orm:"size(100)"`
+	Password     string `orm:"size(100)"`
+	Status       int16  `orm:"column(Status)"`
+	IsStaff      bool
+	IsActive     bool      `orm:"default(true)"`
+	Created      time.Time `orm:"auto_now_add;type(date)"`
+	Updated      time.Time `orm:"auto_now"`
+	Profile      *Profile  `orm:"null;rel(one);on_delete(set_null)"`
+	Posts        []*Post   `orm:"reverse(many)" json:"-"`
+	ShouldSkip   string    `orm:"-"`
+	Nums         int
+	Langs        SliceStringField `orm:"size(100)"`
+	Extra        JSONFieldTest    `orm:"type(text)"`
+	unexport     bool             `orm:"-"`
+	unexportBool bool
+}
+
+func (u *User) TableIndex() [][]string {
+	return [][]string{
+		{"Id", "UserName"},
+		{"Id", "Created"},
+	}
+}
+
+func (u *User) TableUnique() [][]string {
+	return [][]string{
+		{"UserName", "Email"},
+	}
+}
+
+func NewUser() *User {
+	obj := new(User)
+	return obj
+}
+
+type Profile struct {
+	ID       int `orm:"column(id)"`
+	Age      int16
+	Money    float64
+	User     *User `orm:"reverse(one)" json:"-"`
+	BestPost *Post `orm:"rel(one);null"`
+}
+
+func (u *Profile) TableName() string {
+	return "user_profile"
+}
+
+func NewProfile() *Profile {
+	obj := new(Profile)
+	return obj
+}
+
+type Post struct {
+	ID      int       `orm:"column(id)"`
+	User    *User     `orm:"rel(fk)"`
+	Title   string    `orm:"size(60)"`
+	Content string    `orm:"type(text)"`
+	Created time.Time `orm:"auto_now_add"`
+	Updated time.Time `orm:"auto_now"`
+	Tags    []*Tag    `orm:"rel(m2m);rel_through(github.com/astaxie/beego/orm.PostTags)"`
+}
+
+func (u *Post) TableIndex() [][]string {
+	return [][]string{
+		{"Id", "Created"},
+	}
+}
+
+func NewPost() *Post {
+	obj := new(Post)
+	return obj
+}
+
+type Tag struct {
+	ID       int     `orm:"column(id)"`
+	Name     string  `orm:"size(30)"`
+	BestPost *Post   `orm:"rel(one);null"`
+	Posts    []*Post `orm:"reverse(many)" json:"-"`
+}
+
+func NewTag() *Tag {
+	obj := new(Tag)
+	return obj
+}
+
+type PostTags struct {
+	ID   int   `orm:"column(id)"`
+	Post *Post `orm:"rel(fk)"`
+	Tag  *Tag  `orm:"rel(fk)"`
+}
+
+func (m *PostTags) TableName() string {
+	return "prefix_post_tags"
+}
+
+type Comment struct {
+	ID      int       `orm:"column(id)"`
+	Post    *Post     `orm:"rel(fk);column(post)"`
+	Content string    `orm:"type(text)"`
+	Parent  *Comment  `orm:"null;rel(fk)"`
+	Created time.Time `orm:"auto_now_add"`
+}
+
+func NewComment() *Comment {
+	obj := new(Comment)
+	return obj
+}
+
+type Group struct {
+	ID          int `orm:"column(gid);size(32)"`
+	Name        string
+	Permissions []*Permission `orm:"reverse(many)" json:"-"`
+}
+
+type Permission struct {
+	ID     int `orm:"column(id)"`
+	Name   string
+	Groups []*Group `orm:"rel(m2m);rel_through(github.com/astaxie/beego/orm.GroupPermissions)"`
+}
+
+type GroupPermissions struct {
+	ID         int         `orm:"column(id)"`
+	Group      *Group      `orm:"rel(fk)"`
+	Permission *Permission `orm:"rel(fk)"`
+}
+
+type ModelID struct {
+	ID int64
+}
+
+type ModelBase struct {
+	ModelID
+
+	Created time.Time `orm:"auto_now_add;type(datetime)"`
+	Updated time.Time `orm:"auto_now;type(datetime)"`
+}
+
+type InLine struct {
+	// Common Fields
+	ModelBase
+
+	// Other Fields
+	Name  string `orm:"unique"`
+	Email string
+}
+
+func NewInLine() *InLine {
+	return new(InLine)
+}
+
+type InLineOneToOne struct {
+	// Common Fields
+	ModelBase
+
+	Note   string
+	InLine *InLine `orm:"rel(fk);column(inline)"`
+}
+
+func NewInLineOneToOne() *InLineOneToOne {
+	return new(InLineOneToOne)
+}
+
+type IntegerPk struct {
+	ID    int64 `orm:"pk"`
+	Value string
+}
+
+type UintPk struct {
+	ID   uint32 `orm:"pk"`
+	Name string
+}
+
+type PtrPk struct {
+	ID       *IntegerPk `orm:"pk;rel(one)"`
+	Positive bool
+}
+
+var DBARGS = struct {
+	Driver string
+	Source string
+	Debug  string
+}{
+	os.Getenv("ORM_DRIVER"),
+	os.Getenv("ORM_SOURCE"),
+	os.Getenv("ORM_DEBUG"),
+}
+
+var (
+	IsMysql    = DBARGS.Driver == "mysql"
+	IsSqlite   = DBARGS.Driver == "sqlite3"
+	IsPostgres = DBARGS.Driver == "postgres"
+	IsTidb     = DBARGS.Driver == "tidb"
+)
+
+var (
+	dORM     Ormer
+	dDbBaser dbBaser
+)
+
+func init() {
+	Debug, _ = StrTo(DBARGS.Debug).Bool()
+
+	if DBARGS.Driver == "" || DBARGS.Source == "" {
+		fmt.Println(`need driver and source!
+
+Default DB Drivers.
+
+  driver: url
+   mysql: https://github.com/go-sql-driver/mysql
+ sqlite3: https://github.com/mattn/go-sqlite3
+postgres: https://github.com/lib/pq
+tidb: https://github.com/pingcap/tidb
+
+usage:
+
+go get -u github.com/astaxie/beego/orm
+go get -u github.com/go-sql-driver/mysql
+go get -u github.com/mattn/go-sqlite3
+go get -u github.com/lib/pq
+go get -u github.com/pingcap/tidb
+
+#### MySQL
+mysql -u root -e 'create database orm_test;'
+export ORM_DRIVER=mysql
+export ORM_SOURCE="root:@/orm_test?charset=utf8"
+go test -v github.com/astaxie/beego/orm
+
+
+#### Sqlite3
+export ORM_DRIVER=sqlite3
+export ORM_SOURCE='file:memory_test?mode=memory'
+go test -v github.com/astaxie/beego/orm
+
+
+#### PostgreSQL
+psql -c 'create database orm_test;' -U postgres
+export ORM_DRIVER=postgres
+export ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
+go test -v github.com/astaxie/beego/orm
+
+#### TiDB
+export ORM_DRIVER=tidb
+export ORM_SOURCE='memory://test/test'
+go test -v github.com/astaxie/beego/orm
+
+`)
+		os.Exit(2)
+	}
+
+	RegisterDataBase("default", DBARGS.Driver, DBARGS.Source, 20)
+
+	alias := getDbAlias("default")
+	if alias.Driver == DRMySQL {
+		alias.Engine = "INNODB"
+	}
+
+}

+ 2320 - 0
vendor/github.com/astaxie/beego/orm/orm_test.go

@@ -0,0 +1,2320 @@
+// 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 orm
+
+import (
+	"bytes"
+	"database/sql"
+	"fmt"
+	"io/ioutil"
+	"math"
+	"os"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"strings"
+	"testing"
+	"time"
+)
+
+var _ = os.PathSeparator
+
+var (
+	testDate     = formatDate + " -0700"
+	testDateTime = formatDateTime + " -0700"
+	testTime     = formatTime + " -0700"
+)
+
+type argAny []interface{}
+
+// get interface by index from interface slice
+func (a argAny) Get(i int, args ...interface{}) (r interface{}) {
+	if i >= 0 && i < len(a) {
+		r = a[i]
+	}
+	if len(args) > 0 {
+		r = args[0]
+	}
+	return
+}
+
+func ValuesCompare(is bool, a interface{}, args ...interface{}) (ok bool, err error) {
+	if len(args) == 0 {
+		return false, fmt.Errorf("miss args")
+	}
+	b := args[0]
+	arg := argAny(args)
+
+	switch v := a.(type) {
+	case reflect.Kind:
+		ok = reflect.ValueOf(b).Kind() == v
+	case time.Time:
+		if v2, vo := b.(time.Time); vo {
+			if arg.Get(1) != nil {
+				format := ToStr(arg.Get(1))
+				a = v.Format(format)
+				b = v2.Format(format)
+				ok = a == b
+			} else {
+				err = fmt.Errorf("compare datetime miss format")
+				goto wrongArg
+			}
+		}
+	default:
+		ok = ToStr(a) == ToStr(b)
+	}
+	ok = is && ok || !is && !ok
+	if !ok {
+		if is {
+			err = fmt.Errorf("expected: `%v`, get `%v`", b, a)
+		} else {
+			err = fmt.Errorf("expected: `%v`, get `%v`", b, a)
+		}
+	}
+
+wrongArg:
+	if err != nil {
+		return false, err
+	}
+
+	return true, nil
+}
+
+func AssertIs(a interface{}, args ...interface{}) error {
+	if ok, err := ValuesCompare(true, a, args...); ok == false {
+		return err
+	}
+	return nil
+}
+
+func AssertNot(a interface{}, args ...interface{}) error {
+	if ok, err := ValuesCompare(false, a, args...); ok == false {
+		return err
+	}
+	return nil
+}
+
+func getCaller(skip int) string {
+	pc, file, line, _ := runtime.Caller(skip)
+	fun := runtime.FuncForPC(pc)
+	_, fn := filepath.Split(file)
+	data, err := ioutil.ReadFile(file)
+	var codes []string
+	if err == nil {
+		lines := bytes.Split(data, []byte{'\n'})
+		n := 10
+		for i := 0; i < n; i++ {
+			o := line - n
+			if o < 0 {
+				continue
+			}
+			cur := o + i + 1
+			flag := "  "
+			if cur == line {
+				flag = ">>"
+			}
+			code := fmt.Sprintf(" %s %5d:   %s", flag, cur, strings.Replace(string(lines[o+i]), "\t", "    ", -1))
+			if code != "" {
+				codes = append(codes, code)
+			}
+		}
+	}
+	funName := fun.Name()
+	if i := strings.LastIndex(funName, "."); i > -1 {
+		funName = funName[i+1:]
+	}
+	return fmt.Sprintf("%s:%d: \n%s", fn, line, strings.Join(codes, "\n"))
+}
+
+func throwFail(t *testing.T, err error, args ...interface{}) {
+	if err != nil {
+		con := fmt.Sprintf("\t\nError: %s\n%s\n", err.Error(), getCaller(2))
+		if len(args) > 0 {
+			parts := make([]string, 0, len(args))
+			for _, arg := range args {
+				parts = append(parts, fmt.Sprintf("%v", arg))
+			}
+			con += " " + strings.Join(parts, ", ")
+		}
+		t.Error(con)
+		t.Fail()
+	}
+}
+
+func throwFailNow(t *testing.T, err error, args ...interface{}) {
+	if err != nil {
+		con := fmt.Sprintf("\t\nError: %s\n%s\n", err.Error(), getCaller(2))
+		if len(args) > 0 {
+			parts := make([]string, 0, len(args))
+			for _, arg := range args {
+				parts = append(parts, fmt.Sprintf("%v", arg))
+			}
+			con += " " + strings.Join(parts, ", ")
+		}
+		t.Error(con)
+		t.FailNow()
+	}
+}
+
+func TestGetDB(t *testing.T) {
+	if db, err := GetDB(); err != nil {
+		throwFailNow(t, err)
+	} else {
+		err = db.Ping()
+		throwFailNow(t, err)
+	}
+}
+
+func TestSyncDb(t *testing.T) {
+	RegisterModel(new(Data), new(DataNull), new(DataCustom))
+	RegisterModel(new(User))
+	RegisterModel(new(Profile))
+	RegisterModel(new(Post))
+	RegisterModel(new(Tag))
+	RegisterModel(new(Comment))
+	RegisterModel(new(UserBig))
+	RegisterModel(new(PostTags))
+	RegisterModel(new(Group))
+	RegisterModel(new(Permission))
+	RegisterModel(new(GroupPermissions))
+	RegisterModel(new(InLine))
+	RegisterModel(new(InLineOneToOne))
+	RegisterModel(new(IntegerPk))
+	RegisterModel(new(UintPk))
+	RegisterModel(new(PtrPk))
+
+	err := RunSyncdb("default", true, Debug)
+	throwFail(t, err)
+
+	modelCache.clean()
+}
+
+func TestRegisterModels(t *testing.T) {
+	RegisterModel(new(Data), new(DataNull), new(DataCustom))
+	RegisterModel(new(User))
+	RegisterModel(new(Profile))
+	RegisterModel(new(Post))
+	RegisterModel(new(Tag))
+	RegisterModel(new(Comment))
+	RegisterModel(new(UserBig))
+	RegisterModel(new(PostTags))
+	RegisterModel(new(Group))
+	RegisterModel(new(Permission))
+	RegisterModel(new(GroupPermissions))
+	RegisterModel(new(InLine))
+	RegisterModel(new(InLineOneToOne))
+	RegisterModel(new(IntegerPk))
+	RegisterModel(new(UintPk))
+	RegisterModel(new(PtrPk))
+
+	BootStrap()
+
+	dORM = NewOrm()
+	dDbBaser = getDbAlias("default").DbBaser
+}
+
+func TestModelSyntax(t *testing.T) {
+	user := &User{}
+	ind := reflect.ValueOf(user).Elem()
+	fn := getFullName(ind.Type())
+	mi, ok := modelCache.getByFullName(fn)
+	throwFail(t, AssertIs(ok, true))
+
+	mi, ok = modelCache.get("user")
+	throwFail(t, AssertIs(ok, true))
+	if ok {
+		throwFail(t, AssertIs(mi.fields.GetByName("ShouldSkip") == nil, true))
+	}
+}
+
+var DataValues = map[string]interface{}{
+	"Boolean":  true,
+	"Char":     "char",
+	"Text":     "text",
+	"JSON":     `{"name":"json"}`,
+	"Jsonb":    `{"name": "jsonb"}`,
+	"Time":     time.Now(),
+	"Date":     time.Now(),
+	"DateTime": time.Now(),
+	"Byte":     byte(1<<8 - 1),
+	"Rune":     rune(1<<31 - 1),
+	"Int":      int(1<<31 - 1),
+	"Int8":     int8(1<<7 - 1),
+	"Int16":    int16(1<<15 - 1),
+	"Int32":    int32(1<<31 - 1),
+	"Int64":    int64(1<<63 - 1),
+	"Uint":     uint(1<<32 - 1),
+	"Uint8":    uint8(1<<8 - 1),
+	"Uint16":   uint16(1<<16 - 1),
+	"Uint32":   uint32(1<<32 - 1),
+	"Uint64":   uint64(1<<63 - 1), // uint64 values with high bit set are not supported
+	"Float32":  float32(100.1234),
+	"Float64":  float64(100.1234),
+	"Decimal":  float64(100.1234),
+}
+
+func TestDataTypes(t *testing.T) {
+	d := Data{}
+	ind := reflect.Indirect(reflect.ValueOf(&d))
+
+	for name, value := range DataValues {
+		if name == "JSON" {
+			continue
+		}
+		e := ind.FieldByName(name)
+		e.Set(reflect.ValueOf(value))
+	}
+	id, err := dORM.Insert(&d)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 1))
+
+	d = Data{ID: 1}
+	err = dORM.Read(&d)
+	throwFail(t, err)
+
+	ind = reflect.Indirect(reflect.ValueOf(&d))
+
+	for name, value := range DataValues {
+		e := ind.FieldByName(name)
+		vu := e.Interface()
+		switch name {
+		case "Date":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testDate)
+		case "DateTime":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+		case "Time":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testTime)
+		}
+		throwFail(t, AssertIs(vu == value, true), value, vu)
+	}
+}
+
+func TestNullDataTypes(t *testing.T) {
+	d := DataNull{}
+
+	if IsPostgres {
+		// can removed when this fixed
+		// https://github.com/lib/pq/pull/125
+		d.DateTime = time.Now()
+	}
+
+	id, err := dORM.Insert(&d)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 1))
+
+	data := `{"ok":1,"data":{"arr":[1,2],"msg":"gopher"}}`
+	d = DataNull{ID: 1, JSON: data}
+	num, err := dORM.Update(&d)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	d = DataNull{ID: 1}
+	err = dORM.Read(&d)
+	throwFail(t, err)
+
+	throwFail(t, AssertIs(d.JSON, data))
+
+	throwFail(t, AssertIs(d.NullBool.Valid, false))
+	throwFail(t, AssertIs(d.NullString.Valid, false))
+	throwFail(t, AssertIs(d.NullInt64.Valid, false))
+	throwFail(t, AssertIs(d.NullFloat64.Valid, false))
+
+	throwFail(t, AssertIs(d.BooleanPtr, nil))
+	throwFail(t, AssertIs(d.CharPtr, nil))
+	throwFail(t, AssertIs(d.TextPtr, nil))
+	throwFail(t, AssertIs(d.BytePtr, nil))
+	throwFail(t, AssertIs(d.RunePtr, nil))
+	throwFail(t, AssertIs(d.IntPtr, nil))
+	throwFail(t, AssertIs(d.Int8Ptr, nil))
+	throwFail(t, AssertIs(d.Int16Ptr, nil))
+	throwFail(t, AssertIs(d.Int32Ptr, nil))
+	throwFail(t, AssertIs(d.Int64Ptr, nil))
+	throwFail(t, AssertIs(d.UintPtr, nil))
+	throwFail(t, AssertIs(d.Uint8Ptr, nil))
+	throwFail(t, AssertIs(d.Uint16Ptr, nil))
+	throwFail(t, AssertIs(d.Uint32Ptr, nil))
+	throwFail(t, AssertIs(d.Uint64Ptr, nil))
+	throwFail(t, AssertIs(d.Float32Ptr, nil))
+	throwFail(t, AssertIs(d.Float64Ptr, nil))
+	throwFail(t, AssertIs(d.DecimalPtr, nil))
+	throwFail(t, AssertIs(d.TimePtr, nil))
+	throwFail(t, AssertIs(d.DatePtr, nil))
+	throwFail(t, AssertIs(d.DateTimePtr, nil))
+
+	_, err = dORM.Raw(`INSERT INTO data_null (boolean) VALUES (?)`, nil).Exec()
+	throwFail(t, err)
+
+	d = DataNull{ID: 2}
+	err = dORM.Read(&d)
+	throwFail(t, err)
+
+	booleanPtr := true
+	charPtr := string("test")
+	textPtr := string("test")
+	bytePtr := byte('t')
+	runePtr := rune('t')
+	intPtr := int(42)
+	int8Ptr := int8(42)
+	int16Ptr := int16(42)
+	int32Ptr := int32(42)
+	int64Ptr := int64(42)
+	uintPtr := uint(42)
+	uint8Ptr := uint8(42)
+	uint16Ptr := uint16(42)
+	uint32Ptr := uint32(42)
+	uint64Ptr := uint64(42)
+	float32Ptr := float32(42.0)
+	float64Ptr := float64(42.0)
+	decimalPtr := float64(42.0)
+	timePtr := time.Now()
+	datePtr := time.Now()
+	dateTimePtr := time.Now()
+
+	d = DataNull{
+		DateTime:    time.Now(),
+		NullString:  sql.NullString{String: "test", Valid: true},
+		NullBool:    sql.NullBool{Bool: true, Valid: true},
+		NullInt64:   sql.NullInt64{Int64: 42, Valid: true},
+		NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
+		BooleanPtr:  &booleanPtr,
+		CharPtr:     &charPtr,
+		TextPtr:     &textPtr,
+		BytePtr:     &bytePtr,
+		RunePtr:     &runePtr,
+		IntPtr:      &intPtr,
+		Int8Ptr:     &int8Ptr,
+		Int16Ptr:    &int16Ptr,
+		Int32Ptr:    &int32Ptr,
+		Int64Ptr:    &int64Ptr,
+		UintPtr:     &uintPtr,
+		Uint8Ptr:    &uint8Ptr,
+		Uint16Ptr:   &uint16Ptr,
+		Uint32Ptr:   &uint32Ptr,
+		Uint64Ptr:   &uint64Ptr,
+		Float32Ptr:  &float32Ptr,
+		Float64Ptr:  &float64Ptr,
+		DecimalPtr:  &decimalPtr,
+		TimePtr:     &timePtr,
+		DatePtr:     &datePtr,
+		DateTimePtr: &dateTimePtr,
+	}
+
+	id, err = dORM.Insert(&d)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 3))
+
+	d = DataNull{ID: 3}
+	err = dORM.Read(&d)
+	throwFail(t, err)
+
+	throwFail(t, AssertIs(d.NullBool.Valid, true))
+	throwFail(t, AssertIs(d.NullBool.Bool, true))
+
+	throwFail(t, AssertIs(d.NullString.Valid, true))
+	throwFail(t, AssertIs(d.NullString.String, "test"))
+
+	throwFail(t, AssertIs(d.NullInt64.Valid, true))
+	throwFail(t, AssertIs(d.NullInt64.Int64, 42))
+
+	throwFail(t, AssertIs(d.NullFloat64.Valid, true))
+	throwFail(t, AssertIs(d.NullFloat64.Float64, 42.42))
+
+	throwFail(t, AssertIs(*d.BooleanPtr, booleanPtr))
+	throwFail(t, AssertIs(*d.CharPtr, charPtr))
+	throwFail(t, AssertIs(*d.TextPtr, textPtr))
+	throwFail(t, AssertIs(*d.BytePtr, bytePtr))
+	throwFail(t, AssertIs(*d.RunePtr, runePtr))
+	throwFail(t, AssertIs(*d.IntPtr, intPtr))
+	throwFail(t, AssertIs(*d.Int8Ptr, int8Ptr))
+	throwFail(t, AssertIs(*d.Int16Ptr, int16Ptr))
+	throwFail(t, AssertIs(*d.Int32Ptr, int32Ptr))
+	throwFail(t, AssertIs(*d.Int64Ptr, int64Ptr))
+	throwFail(t, AssertIs(*d.UintPtr, uintPtr))
+	throwFail(t, AssertIs(*d.Uint8Ptr, uint8Ptr))
+	throwFail(t, AssertIs(*d.Uint16Ptr, uint16Ptr))
+	throwFail(t, AssertIs(*d.Uint32Ptr, uint32Ptr))
+	throwFail(t, AssertIs(*d.Uint64Ptr, uint64Ptr))
+	throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr))
+	throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr))
+	throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr))
+	throwFail(t, AssertIs((*d.TimePtr).Format(testTime), timePtr.Format(testTime)))
+	throwFail(t, AssertIs((*d.DatePtr).Format(testDate), datePtr.Format(testDate)))
+	throwFail(t, AssertIs((*d.DateTimePtr).Format(testDateTime), dateTimePtr.Format(testDateTime)))
+}
+
+func TestDataCustomTypes(t *testing.T) {
+	d := DataCustom{}
+	ind := reflect.Indirect(reflect.ValueOf(&d))
+
+	for name, value := range DataValues {
+		e := ind.FieldByName(name)
+		if !e.IsValid() {
+			continue
+		}
+		e.Set(reflect.ValueOf(value).Convert(e.Type()))
+	}
+
+	id, err := dORM.Insert(&d)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 1))
+
+	d = DataCustom{ID: 1}
+	err = dORM.Read(&d)
+	throwFail(t, err)
+
+	ind = reflect.Indirect(reflect.ValueOf(&d))
+
+	for name, value := range DataValues {
+		e := ind.FieldByName(name)
+		if !e.IsValid() {
+			continue
+		}
+		vu := e.Interface()
+		value = reflect.ValueOf(value).Convert(e.Type()).Interface()
+		throwFail(t, AssertIs(vu == value, true), value, vu)
+	}
+}
+
+func TestCRUD(t *testing.T) {
+	profile := NewProfile()
+	profile.Age = 30
+	profile.Money = 1234.12
+	id, err := dORM.Insert(profile)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 1))
+
+	user := NewUser()
+	user.UserName = "slene"
+	user.Email = "[email protected]"
+	user.Password = "pass"
+	user.Status = 3
+	user.IsStaff = true
+	user.IsActive = true
+
+	id, err = dORM.Insert(user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 1))
+
+	u := &User{ID: user.ID}
+	err = dORM.Read(u)
+	throwFail(t, err)
+
+	throwFail(t, AssertIs(u.UserName, "slene"))
+	throwFail(t, AssertIs(u.Email, "[email protected]"))
+	throwFail(t, AssertIs(u.Password, "pass"))
+	throwFail(t, AssertIs(u.Status, 3))
+	throwFail(t, AssertIs(u.IsStaff, true))
+	throwFail(t, AssertIs(u.IsActive, true))
+	throwFail(t, AssertIs(u.Created.In(DefaultTimeLoc), user.Created.In(DefaultTimeLoc), testDate))
+	throwFail(t, AssertIs(u.Updated.In(DefaultTimeLoc), user.Updated.In(DefaultTimeLoc), testDateTime))
+
+	user.UserName = "astaxie"
+	user.Profile = profile
+	num, err := dORM.Update(user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	u = &User{ID: user.ID}
+	err = dORM.Read(u)
+	throwFailNow(t, err)
+	throwFail(t, AssertIs(u.UserName, "astaxie"))
+	throwFail(t, AssertIs(u.Profile.ID, profile.ID))
+
+	u = &User{UserName: "astaxie", Password: "pass"}
+	err = dORM.Read(u, "UserName")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(id, 1))
+
+	u.UserName = "QQ"
+	u.Password = "111"
+	num, err = dORM.Update(u, "UserName")
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	u = &User{ID: user.ID}
+	err = dORM.Read(u)
+	throwFailNow(t, err)
+	throwFail(t, AssertIs(u.UserName, "QQ"))
+	throwFail(t, AssertIs(u.Password, "pass"))
+
+	num, err = dORM.Delete(profile)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	u = &User{ID: user.ID}
+	err = dORM.Read(u)
+	throwFail(t, err)
+	throwFail(t, AssertIs(true, u.Profile == nil))
+
+	num, err = dORM.Delete(user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	u = &User{ID: 100}
+	err = dORM.Read(u)
+	throwFail(t, AssertIs(err, ErrNoRows))
+
+	ub := UserBig{}
+	ub.Name = "name"
+	id, err = dORM.Insert(&ub)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 1))
+
+	ub = UserBig{ID: 1}
+	err = dORM.Read(&ub)
+	throwFail(t, err)
+	throwFail(t, AssertIs(ub.Name, "name"))
+
+	num, err = dORM.Delete(&ub, "name")
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+}
+
+func TestInsertTestData(t *testing.T) {
+	var users []*User
+
+	profile := NewProfile()
+	profile.Age = 28
+	profile.Money = 1234.12
+
+	id, err := dORM.Insert(profile)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 2))
+
+	user := NewUser()
+	user.UserName = "slene"
+	user.Email = "[email protected]"
+	user.Password = "pass"
+	user.Status = 1
+	user.IsStaff = false
+	user.IsActive = true
+	user.Profile = profile
+
+	users = append(users, user)
+
+	id, err = dORM.Insert(user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 2))
+
+	profile = NewProfile()
+	profile.Age = 30
+	profile.Money = 4321.09
+
+	id, err = dORM.Insert(profile)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 3))
+
+	user = NewUser()
+	user.UserName = "astaxie"
+	user.Email = "[email protected]"
+	user.Password = "password"
+	user.Status = 2
+	user.IsStaff = true
+	user.IsActive = false
+	user.Profile = profile
+
+	users = append(users, user)
+
+	id, err = dORM.Insert(user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 3))
+
+	user = NewUser()
+	user.UserName = "nobody"
+	user.Email = "[email protected]"
+	user.Password = "nobody"
+	user.Status = 3
+	user.IsStaff = false
+	user.IsActive = false
+
+	users = append(users, user)
+
+	id, err = dORM.Insert(user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 4))
+
+	tags := []*Tag{
+		{Name: "golang", BestPost: &Post{ID: 2}},
+		{Name: "example"},
+		{Name: "format"},
+		{Name: "c++"},
+	}
+
+	posts := []*Post{
+		{User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand.
+This document gives tips for writing clear, idiomatic Go code. It augments the language specification, the Tour of Go, and How to Write Go Code, all of which you should read first.`},
+		{User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`},
+		{User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide.
+With Go we take an unusual approach and let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't seem right, rearrange your program (or file a bug about gofmt), don't work around it.`},
+		{User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code.
+The program—and web server—godoc processes Go source files to extract documentation about the contents of the package. Comments that appear before top-level declarations, with no intervening newlines, are extracted along with the declaration to serve as explanatory text for the item. The nature and style of these comments determines the quality of the documentation godoc produces.`},
+	}
+
+	comments := []*Comment{
+		{Post: posts[0], Content: "a comment"},
+		{Post: posts[1], Content: "yes"},
+		{Post: posts[1]},
+		{Post: posts[1]},
+		{Post: posts[2]},
+		{Post: posts[2]},
+	}
+
+	for _, tag := range tags {
+		id, err := dORM.Insert(tag)
+		throwFail(t, err)
+		throwFail(t, AssertIs(id > 0, true))
+	}
+
+	for _, post := range posts {
+		id, err := dORM.Insert(post)
+		throwFail(t, err)
+		throwFail(t, AssertIs(id > 0, true))
+
+		num := len(post.Tags)
+		if num > 0 {
+			nums, err := dORM.QueryM2M(post, "tags").Add(post.Tags)
+			throwFailNow(t, err)
+			throwFailNow(t, AssertIs(nums, num))
+		}
+	}
+
+	for _, comment := range comments {
+		id, err := dORM.Insert(comment)
+		throwFail(t, err)
+		throwFail(t, AssertIs(id > 0, true))
+	}
+
+	permissions := []*Permission{
+		{Name: "writePosts"},
+		{Name: "readComments"},
+		{Name: "readPosts"},
+	}
+
+	groups := []*Group{
+		{
+			Name:        "admins",
+			Permissions: []*Permission{permissions[0], permissions[1], permissions[2]},
+		},
+		{
+			Name:        "users",
+			Permissions: []*Permission{permissions[1], permissions[2]},
+		},
+	}
+
+	for _, permission := range permissions {
+		id, err := dORM.Insert(permission)
+		throwFail(t, err)
+		throwFail(t, AssertIs(id > 0, true))
+	}
+
+	for _, group := range groups {
+		_, err := dORM.Insert(group)
+		throwFail(t, err)
+		throwFail(t, AssertIs(id > 0, true))
+
+		num := len(group.Permissions)
+		if num > 0 {
+			nums, err := dORM.QueryM2M(group, "permissions").Add(group.Permissions)
+			throwFailNow(t, err)
+			throwFailNow(t, AssertIs(nums, num))
+		}
+	}
+
+}
+
+func TestCustomField(t *testing.T) {
+	user := User{ID: 2}
+	err := dORM.Read(&user)
+	throwFailNow(t, err)
+
+	user.Langs = append(user.Langs, "zh-CN", "en-US")
+	user.Extra.Name = "beego"
+	user.Extra.Data = "orm"
+	_, err = dORM.Update(&user, "Langs", "Extra")
+	throwFailNow(t, err)
+
+	user = User{ID: 2}
+	err = dORM.Read(&user)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(len(user.Langs), 2))
+	throwFailNow(t, AssertIs(user.Langs[0], "zh-CN"))
+	throwFailNow(t, AssertIs(user.Langs[1], "en-US"))
+
+	throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
+	throwFailNow(t, AssertIs(user.Extra.Data, "orm"))
+}
+
+func TestExpr(t *testing.T) {
+	user := &User{}
+	qs := dORM.QueryTable(user)
+	qs = dORM.QueryTable((*User)(nil))
+	qs = dORM.QueryTable("User")
+	qs = dORM.QueryTable("user")
+	num, err := qs.Filter("UserName", "slene").Filter("user_name", "slene").Filter("profile__Age", 28).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("created", time.Now()).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+
+	// num, err = qs.Filter("created", time.Now().Format(format_Date)).Count()
+	// throwFail(t, err)
+	// throwFail(t, AssertIs(num, 3))
+}
+
+func TestOperators(t *testing.T) {
+	qs := dORM.QueryTable("user")
+	num, err := qs.Filter("user_name", "slene").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name__exact", String("slene")).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name__exact", "slene").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name__iexact", "Slene").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name__contains", "e").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	var shouldNum int
+
+	if IsSqlite || IsTidb {
+		shouldNum = 2
+	} else {
+		shouldNum = 0
+	}
+
+	num, err = qs.Filter("user_name__contains", "E").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, shouldNum))
+
+	num, err = qs.Filter("user_name__icontains", "E").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Filter("user_name__icontains", "E").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Filter("status__gt", 1).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Filter("status__gte", 1).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+
+	num, err = qs.Filter("status__lt", Uint(3)).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Filter("status__lte", Int(3)).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+
+	num, err = qs.Filter("user_name__startswith", "s").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	if IsSqlite || IsTidb {
+		shouldNum = 1
+	} else {
+		shouldNum = 0
+	}
+
+	num, err = qs.Filter("user_name__startswith", "S").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, shouldNum))
+
+	num, err = qs.Filter("user_name__istartswith", "S").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name__endswith", "e").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	if IsSqlite || IsTidb {
+		shouldNum = 2
+	} else {
+		shouldNum = 0
+	}
+
+	num, err = qs.Filter("user_name__endswith", "E").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, shouldNum))
+
+	num, err = qs.Filter("user_name__iendswith", "E").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Filter("profile__isnull", true).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("status__in", 1, 2).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Filter("status__in", []int{1, 2}).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	n1, n2 := 1, 2
+	num, err = qs.Filter("status__in", []*int{&n1}, &n2).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Filter("id__between", 2, 3).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Filter("id__between", []int{2, 3}).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+}
+
+func TestSetCond(t *testing.T) {
+	cond := NewCondition()
+	cond1 := cond.And("profile__isnull", false).AndNot("status__in", 1).Or("profile__age__gt", 2000)
+
+	qs := dORM.QueryTable("user")
+	num, err := qs.SetCond(cond1).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	cond2 := cond.AndCond(cond1).OrCond(cond.And("user_name", "slene"))
+	num, err = qs.SetCond(cond2).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	cond3 := cond.AndNotCond(cond.And("status__in", 1))
+	num, err = qs.SetCond(cond3).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	cond4 := cond.And("user_name", "slene").OrNotCond(cond.And("user_name", "slene"))
+	num, err = qs.SetCond(cond4).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+}
+
+func TestLimit(t *testing.T) {
+	var posts []*Post
+	qs := dORM.QueryTable("post")
+	num, err := qs.Limit(1).All(&posts)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Limit(-1).All(&posts)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 4))
+
+	num, err = qs.Limit(-1, 2).All(&posts)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	num, err = qs.Limit(0, 2).All(&posts)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+}
+
+func TestOffset(t *testing.T) {
+	var posts []*Post
+	qs := dORM.QueryTable("post")
+	num, err := qs.Limit(1).Offset(2).All(&posts)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Offset(2).All(&posts)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+}
+
+func TestOrderBy(t *testing.T) {
+	qs := dORM.QueryTable("user")
+	num, err := qs.OrderBy("-status").Filter("user_name", "nobody").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.OrderBy("status").Filter("user_name", "slene").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.OrderBy("-profile__age").Filter("user_name", "astaxie").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+}
+
+func TestAll(t *testing.T) {
+	var users []*User
+	qs := dORM.QueryTable("user")
+	num, err := qs.OrderBy("Id").All(&users)
+	throwFail(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+
+	throwFail(t, AssertIs(users[0].UserName, "slene"))
+	throwFail(t, AssertIs(users[1].UserName, "astaxie"))
+	throwFail(t, AssertIs(users[2].UserName, "nobody"))
+
+	var users2 []User
+	qs = dORM.QueryTable("user")
+	num, err = qs.OrderBy("Id").All(&users2)
+	throwFail(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+
+	throwFailNow(t, AssertIs(users2[0].UserName, "slene"))
+	throwFailNow(t, AssertIs(users2[1].UserName, "astaxie"))
+	throwFailNow(t, AssertIs(users2[2].UserName, "nobody"))
+
+	qs = dORM.QueryTable("user")
+	num, err = qs.OrderBy("Id").RelatedSel().All(&users2, "UserName")
+	throwFail(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+	throwFailNow(t, AssertIs(len(users2), 3))
+	throwFailNow(t, AssertIs(users2[0].UserName, "slene"))
+	throwFailNow(t, AssertIs(users2[1].UserName, "astaxie"))
+	throwFailNow(t, AssertIs(users2[2].UserName, "nobody"))
+	throwFailNow(t, AssertIs(users2[0].ID, 0))
+	throwFailNow(t, AssertIs(users2[1].ID, 0))
+	throwFailNow(t, AssertIs(users2[2].ID, 0))
+	throwFailNow(t, AssertIs(users2[0].Profile == nil, false))
+	throwFailNow(t, AssertIs(users2[1].Profile == nil, false))
+	throwFailNow(t, AssertIs(users2[2].Profile == nil, true))
+
+	qs = dORM.QueryTable("user")
+	num, err = qs.Filter("user_name", "nothing").All(&users)
+	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(users3 == nil, false))
+}
+
+func TestOne(t *testing.T) {
+	var user User
+	qs := dORM.QueryTable("user")
+	err := qs.One(&user)
+	throwFail(t, err)
+
+	user = User{}
+	err = qs.OrderBy("Id").Limit(1).One(&user)
+	throwFailNow(t, err)
+	throwFail(t, AssertIs(user.UserName, "slene"))
+	throwFail(t, AssertNot(err, ErrMultiRows))
+
+	user = User{}
+	err = qs.OrderBy("-Id").Limit(100).One(&user)
+	throwFailNow(t, err)
+	throwFail(t, AssertIs(user.UserName, "nobody"))
+	throwFail(t, AssertNot(err, ErrMultiRows))
+
+	err = qs.Filter("user_name", "nothing").One(&user)
+	throwFail(t, AssertIs(err, ErrNoRows))
+
+}
+
+func TestValues(t *testing.T) {
+	var maps []Params
+	qs := dORM.QueryTable("user")
+
+	num, err := qs.OrderBy("Id").Values(&maps)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+	if num == 3 {
+		throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
+		throwFail(t, AssertIs(maps[2]["Profile"], nil))
+	}
+
+	num, err = qs.OrderBy("Id").Values(&maps, "UserName", "Profile__Age")
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+	if num == 3 {
+		throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
+		throwFail(t, AssertIs(maps[0]["Profile__Age"], 28))
+		throwFail(t, AssertIs(maps[2]["Profile__Age"], nil))
+	}
+
+	num, err = qs.Filter("UserName", "slene").Values(&maps)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+}
+
+func TestValuesList(t *testing.T) {
+	var list []ParamsList
+	qs := dORM.QueryTable("user")
+
+	num, err := qs.OrderBy("Id").ValuesList(&list)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+	if num == 3 {
+		throwFail(t, AssertIs(list[0][1], "slene"))
+		throwFail(t, AssertIs(list[2][9], nil))
+	}
+
+	num, err = qs.OrderBy("Id").ValuesList(&list, "UserName", "Profile__Age")
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+	if num == 3 {
+		throwFail(t, AssertIs(list[0][0], "slene"))
+		throwFail(t, AssertIs(list[0][1], 28))
+		throwFail(t, AssertIs(list[2][1], nil))
+	}
+}
+
+func TestValuesFlat(t *testing.T) {
+	var list ParamsList
+	qs := dORM.QueryTable("user")
+
+	num, err := qs.OrderBy("id").ValuesFlat(&list, "UserName")
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+	if num == 3 {
+		throwFail(t, AssertIs(list[0], "slene"))
+		throwFail(t, AssertIs(list[1], "astaxie"))
+		throwFail(t, AssertIs(list[2], "nobody"))
+	}
+}
+
+func TestRelatedSel(t *testing.T) {
+	if IsTidb {
+		// Skip it. TiDB does not support relation now.
+		return
+	}
+	qs := dORM.QueryTable("user")
+	num, err := qs.Filter("profile__age", 28).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("profile__age__gt", 28).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("profile__user__profile__age__gt", 28).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	var user User
+	err = qs.Filter("user_name", "slene").RelatedSel("profile").One(&user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+	throwFail(t, AssertNot(user.Profile, nil))
+	if user.Profile != nil {
+		throwFail(t, AssertIs(user.Profile.Age, 28))
+	}
+
+	err = qs.Filter("user_name", "slene").RelatedSel().One(&user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+	throwFail(t, AssertNot(user.Profile, nil))
+	if user.Profile != nil {
+		throwFail(t, AssertIs(user.Profile.Age, 28))
+	}
+
+	err = qs.Filter("user_name", "nobody").RelatedSel("profile").One(&user)
+	throwFail(t, AssertIs(num, 1))
+	throwFail(t, AssertIs(user.Profile, nil))
+
+	qs = dORM.QueryTable("user_profile")
+	num, err = qs.Filter("user__username", "slene").Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	var posts []*Post
+	qs = dORM.QueryTable("post")
+	num, err = qs.RelatedSel().All(&posts)
+	throwFail(t, err)
+	throwFailNow(t, AssertIs(num, 4))
+
+	throwFailNow(t, AssertIs(posts[0].User.UserName, "slene"))
+	throwFailNow(t, AssertIs(posts[1].User.UserName, "astaxie"))
+	throwFailNow(t, AssertIs(posts[2].User.UserName, "astaxie"))
+	throwFailNow(t, AssertIs(posts[3].User.UserName, "nobody"))
+}
+
+func TestReverseQuery(t *testing.T) {
+	var profile Profile
+	err := dORM.QueryTable("user_profile").Filter("User", 3).One(&profile)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(profile.Age, 30))
+
+	profile = Profile{}
+	err = dORM.QueryTable("user_profile").Filter("User__UserName", "astaxie").One(&profile)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(profile.Age, 30))
+
+	var user User
+	err = dORM.QueryTable("user").Filter("Posts__Title", "Examples").One(&user)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(user.UserName, "astaxie"))
+
+	user = User{}
+	err = dORM.QueryTable("user").Filter("Posts__User__UserName", "astaxie").Limit(1).One(&user)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(user.UserName, "astaxie"))
+
+	user = User{}
+	err = dORM.QueryTable("user").Filter("Posts__User__UserName", "astaxie").RelatedSel().Limit(1).One(&user)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(user.UserName, "astaxie"))
+	throwFailNow(t, AssertIs(user.Profile == nil, false))
+	throwFailNow(t, AssertIs(user.Profile.Age, 30))
+
+	var posts []*Post
+	num, err := dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").All(&posts)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+	throwFailNow(t, AssertIs(posts[0].Title, "Introduction"))
+
+	posts = []*Post{}
+	num, err = dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").Filter("User__UserName", "slene").All(&posts)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(posts[0].Title, "Introduction"))
+
+	posts = []*Post{}
+	num, err = dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").
+		Filter("User__UserName", "slene").RelatedSel().All(&posts)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(posts[0].User == nil, false))
+	throwFailNow(t, AssertIs(posts[0].User.UserName, "slene"))
+
+	var tags []*Tag
+	num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").All(&tags)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(tags[0].Name, "golang"))
+
+	tags = []*Tag{}
+	num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").
+		Filter("BestPost__User__UserName", "astaxie").All(&tags)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(tags[0].Name, "golang"))
+
+	tags = []*Tag{}
+	num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").
+		Filter("BestPost__User__UserName", "astaxie").RelatedSel().All(&tags)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(tags[0].Name, "golang"))
+	throwFailNow(t, AssertIs(tags[0].BestPost == nil, false))
+	throwFailNow(t, AssertIs(tags[0].BestPost.Title, "Examples"))
+	throwFailNow(t, AssertIs(tags[0].BestPost.User == nil, false))
+	throwFailNow(t, AssertIs(tags[0].BestPost.User.UserName, "astaxie"))
+}
+
+func TestLoadRelated(t *testing.T) {
+	// load reverse foreign key
+	user := User{ID: 3}
+
+	err := dORM.Read(&user)
+	throwFailNow(t, err)
+
+	num, err := dORM.LoadRelated(&user, "Posts")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 2))
+	throwFailNow(t, AssertIs(len(user.Posts), 2))
+	throwFailNow(t, AssertIs(user.Posts[0].User.ID, 3))
+
+	num, err = dORM.LoadRelated(&user, "Posts", true)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(len(user.Posts), 2))
+	throwFailNow(t, AssertIs(user.Posts[0].User.UserName, "astaxie"))
+
+	num, err = dORM.LoadRelated(&user, "Posts", true, 1)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(len(user.Posts), 1))
+
+	num, err = dORM.LoadRelated(&user, "Posts", true, 0, 0, "-Id")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(len(user.Posts), 2))
+	throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))
+
+	num, err = dORM.LoadRelated(&user, "Posts", true, 1, 1, "Id")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(len(user.Posts), 1))
+	throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))
+
+	// load reverse one to one
+	profile := Profile{ID: 3}
+	profile.BestPost = &Post{ID: 2}
+	num, err = dORM.Update(&profile, "BestPost")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	err = dORM.Read(&profile)
+	throwFailNow(t, err)
+
+	num, err = dORM.LoadRelated(&profile, "User")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(profile.User == nil, false))
+	throwFailNow(t, AssertIs(profile.User.UserName, "astaxie"))
+
+	num, err = dORM.LoadRelated(&profile, "User", true)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(profile.User == nil, false))
+	throwFailNow(t, AssertIs(profile.User.UserName, "astaxie"))
+	throwFailNow(t, AssertIs(profile.User.Profile.Age, profile.Age))
+
+	// load rel one to one
+	err = dORM.Read(&user)
+	throwFailNow(t, err)
+
+	num, err = dORM.LoadRelated(&user, "Profile")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(user.Profile == nil, false))
+	throwFailNow(t, AssertIs(user.Profile.Age, 30))
+
+	num, err = dORM.LoadRelated(&user, "Profile", true)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(user.Profile == nil, false))
+	throwFailNow(t, AssertIs(user.Profile.Age, 30))
+	throwFailNow(t, AssertIs(user.Profile.BestPost == nil, false))
+	throwFailNow(t, AssertIs(user.Profile.BestPost.Title, "Examples"))
+
+	post := Post{ID: 2}
+
+	// load rel foreign key
+	err = dORM.Read(&post)
+	throwFailNow(t, err)
+
+	num, err = dORM.LoadRelated(&post, "User")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(post.User == nil, false))
+	throwFailNow(t, AssertIs(post.User.UserName, "astaxie"))
+
+	num, err = dORM.LoadRelated(&post, "User", true)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(post.User == nil, false))
+	throwFailNow(t, AssertIs(post.User.UserName, "astaxie"))
+	throwFailNow(t, AssertIs(post.User.Profile == nil, false))
+	throwFailNow(t, AssertIs(post.User.Profile.Age, 30))
+
+	// load rel m2m
+	post = Post{ID: 2}
+
+	err = dORM.Read(&post)
+	throwFailNow(t, err)
+
+	num, err = dORM.LoadRelated(&post, "Tags")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 2))
+	throwFailNow(t, AssertIs(len(post.Tags), 2))
+	throwFailNow(t, AssertIs(post.Tags[0].Name, "golang"))
+
+	num, err = dORM.LoadRelated(&post, "Tags", true)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 2))
+	throwFailNow(t, AssertIs(len(post.Tags), 2))
+	throwFailNow(t, AssertIs(post.Tags[0].Name, "golang"))
+	throwFailNow(t, AssertIs(post.Tags[0].BestPost == nil, false))
+	throwFailNow(t, AssertIs(post.Tags[0].BestPost.User.UserName, "astaxie"))
+
+	// load reverse m2m
+	tag := Tag{ID: 1}
+
+	err = dORM.Read(&tag)
+	throwFailNow(t, err)
+
+	num, err = dORM.LoadRelated(&tag, "Posts")
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+	throwFailNow(t, AssertIs(tag.Posts[0].Title, "Introduction"))
+	throwFailNow(t, AssertIs(tag.Posts[0].User.ID, 2))
+	throwFailNow(t, AssertIs(tag.Posts[0].User.Profile == nil, true))
+
+	num, err = dORM.LoadRelated(&tag, "Posts", true)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+	throwFailNow(t, AssertIs(tag.Posts[0].Title, "Introduction"))
+	throwFailNow(t, AssertIs(tag.Posts[0].User.ID, 2))
+	throwFailNow(t, AssertIs(tag.Posts[0].User.UserName, "slene"))
+}
+
+func TestQueryM2M(t *testing.T) {
+	post := Post{ID: 4}
+	m2m := dORM.QueryM2M(&post, "Tags")
+
+	tag1 := []*Tag{{Name: "TestTag1"}, {Name: "TestTag2"}}
+	tag2 := &Tag{Name: "TestTag3"}
+	tag3 := []interface{}{&Tag{Name: "TestTag4"}}
+
+	tags := []interface{}{tag1[0], tag1[1], tag2, tag3[0]}
+
+	for _, tag := range tags {
+		_, err := dORM.Insert(tag)
+		throwFailNow(t, err)
+	}
+
+	num, err := m2m.Add(tag1)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 2))
+
+	num, err = m2m.Add(tag2)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	num, err = m2m.Add(tag3)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	num, err = m2m.Count()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 5))
+
+	num, err = m2m.Remove(tag3)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	num, err = m2m.Count()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 4))
+
+	exist := m2m.Exist(tag2)
+	throwFailNow(t, AssertIs(exist, true))
+
+	num, err = m2m.Remove(tag2)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	exist = m2m.Exist(tag2)
+	throwFailNow(t, AssertIs(exist, false))
+
+	num, err = m2m.Count()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+
+	num, err = m2m.Clear()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+
+	num, err = m2m.Count()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 0))
+
+	tag := Tag{Name: "test"}
+	_, err = dORM.Insert(&tag)
+	throwFailNow(t, err)
+
+	m2m = dORM.QueryM2M(&tag, "Posts")
+
+	post1 := []*Post{{Title: "TestPost1"}, {Title: "TestPost2"}}
+	post2 := &Post{Title: "TestPost3"}
+	post3 := []interface{}{&Post{Title: "TestPost4"}}
+
+	posts := []interface{}{post1[0], post1[1], post2, post3[0]}
+
+	for _, post := range posts {
+		p := post.(*Post)
+		p.User = &User{ID: 1}
+		_, err := dORM.Insert(post)
+		throwFailNow(t, err)
+	}
+
+	num, err = m2m.Add(post1)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 2))
+
+	num, err = m2m.Add(post2)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	num, err = m2m.Add(post3)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	num, err = m2m.Count()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 4))
+
+	num, err = m2m.Remove(post3)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	num, err = m2m.Count()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+
+	exist = m2m.Exist(post2)
+	throwFailNow(t, AssertIs(exist, true))
+
+	num, err = m2m.Remove(post2)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+
+	exist = m2m.Exist(post2)
+	throwFailNow(t, AssertIs(exist, false))
+
+	num, err = m2m.Count()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 2))
+
+	num, err = m2m.Clear()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 2))
+
+	num, err = m2m.Count()
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 0))
+
+	num, err = dORM.Delete(&tag)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+}
+
+func TestQueryRelate(t *testing.T) {
+	// post := &Post{Id: 2}
+
+	// qs := dORM.QueryRelate(post, "Tags")
+	// num, err := qs.Count()
+	// throwFailNow(t, err)
+	// throwFailNow(t, AssertIs(num, 2))
+
+	// var tags []*Tag
+	// num, err = qs.All(&tags)
+	// throwFailNow(t, err)
+	// throwFailNow(t, AssertIs(num, 2))
+	// throwFailNow(t, AssertIs(tags[0].Name, "golang"))
+
+	// num, err = dORM.QueryTable("Tag").Filter("Posts__Post", 2).Count()
+	// throwFailNow(t, err)
+	// throwFailNow(t, AssertIs(num, 2))
+}
+
+func TestPkManyRelated(t *testing.T) {
+	permission := &Permission{Name: "readPosts"}
+	err := dORM.Read(permission, "Name")
+	throwFailNow(t, err)
+
+	var groups []*Group
+	qs := dORM.QueryTable("Group")
+	num, err := qs.Filter("Permissions__Permission", permission.ID).All(&groups)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 2))
+}
+
+func TestPrepareInsert(t *testing.T) {
+	qs := dORM.QueryTable("user")
+	i, err := qs.PrepareInsert()
+	throwFailNow(t, err)
+
+	var user User
+	user.UserName = "testing1"
+	num, err := i.Insert(&user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num > 0, true))
+
+	user.UserName = "testing2"
+	num, err = i.Insert(&user)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num > 0, true))
+
+	num, err = qs.Filter("user_name__in", "testing1", "testing2").Delete()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 2))
+
+	err = i.Close()
+	throwFail(t, err)
+	err = i.Close()
+	throwFail(t, AssertIs(err, ErrStmtClosed))
+}
+
+func TestRawExec(t *testing.T) {
+	Q := dDbBaser.TableQuote()
+
+	query := fmt.Sprintf("UPDATE %suser%s SET %suser_name%s = ? WHERE %suser_name%s = ?", Q, Q, Q, Q, Q, Q)
+	res, err := dORM.Raw(query, "testing", "slene").Exec()
+	throwFail(t, err)
+	num, err := res.RowsAffected()
+	throwFail(t, AssertIs(num, 1), err)
+
+	res, err = dORM.Raw(query, "slene", "testing").Exec()
+	throwFail(t, err)
+	num, err = res.RowsAffected()
+	throwFail(t, AssertIs(num, 1), err)
+}
+
+func TestRawQueryRow(t *testing.T) {
+	var (
+		Boolean  bool
+		Char     string
+		Text     string
+		Time     time.Time
+		Date     time.Time
+		DateTime time.Time
+		Byte     byte
+		Rune     rune
+		Int      int
+		Int8     int
+		Int16    int16
+		Int32    int32
+		Int64    int64
+		Uint     uint
+		Uint8    uint8
+		Uint16   uint16
+		Uint32   uint32
+		Uint64   uint64
+		Float32  float32
+		Float64  float64
+		Decimal  float64
+	)
+
+	dataValues := make(map[string]interface{}, len(DataValues))
+
+	for k, v := range DataValues {
+		dataValues[strings.ToLower(k)] = v
+	}
+
+	Q := dDbBaser.TableQuote()
+
+	cols := []string{
+		"id", "boolean", "char", "text", "time", "date", "datetime", "byte", "rune", "int", "int8", "int16", "int32",
+		"int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "decimal",
+	}
+	sep := fmt.Sprintf("%s, %s", Q, Q)
+	query := fmt.Sprintf("SELECT %s%s%s FROM data WHERE id = ?", Q, strings.Join(cols, sep), Q)
+	var id int
+	values := []interface{}{
+		&id, &Boolean, &Char, &Text, &Time, &Date, &DateTime, &Byte, &Rune, &Int, &Int8, &Int16, &Int32,
+		&Int64, &Uint, &Uint8, &Uint16, &Uint32, &Uint64, &Float32, &Float64, &Decimal,
+	}
+	err := dORM.Raw(query, 1).QueryRow(values...)
+	throwFailNow(t, err)
+	for i, col := range cols {
+		vu := values[i]
+		v := reflect.ValueOf(vu).Elem().Interface()
+		switch col {
+		case "id":
+			throwFail(t, AssertIs(id, 1))
+		case "time":
+			v = v.(time.Time).In(DefaultTimeLoc)
+			value := dataValues[col].(time.Time).In(DefaultTimeLoc)
+			throwFail(t, AssertIs(v, value, testTime))
+		case "date":
+			v = v.(time.Time).In(DefaultTimeLoc)
+			value := dataValues[col].(time.Time).In(DefaultTimeLoc)
+			throwFail(t, AssertIs(v, value, testDate))
+		case "datetime":
+			v = v.(time.Time).In(DefaultTimeLoc)
+			value := dataValues[col].(time.Time).In(DefaultTimeLoc)
+			throwFail(t, AssertIs(v, value, testDateTime))
+		default:
+			throwFail(t, AssertIs(v, dataValues[col]))
+		}
+	}
+
+	var (
+		uid    int
+		status *int
+		pid    *int
+	)
+
+	cols = []string{
+		"id", "Status", "profile_id",
+	}
+	query = fmt.Sprintf("SELECT %s%s%s FROM %suser%s WHERE id = ?", Q, strings.Join(cols, sep), Q, Q, Q)
+	err = dORM.Raw(query, 4).QueryRow(&uid, &status, &pid)
+	throwFail(t, err)
+	throwFail(t, AssertIs(uid, 4))
+	throwFail(t, AssertIs(*status, 3))
+	throwFail(t, AssertIs(pid, nil))
+}
+
+func TestQueryRows(t *testing.T) {
+	Q := dDbBaser.TableQuote()
+
+	var datas []*Data
+
+	query := fmt.Sprintf("SELECT * FROM %sdata%s", Q, Q)
+	num, err := dORM.Raw(query).QueryRows(&datas)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(len(datas), 1))
+
+	ind := reflect.Indirect(reflect.ValueOf(datas[0]))
+
+	for name, value := range DataValues {
+		e := ind.FieldByName(name)
+		vu := e.Interface()
+		switch name {
+		case "Time":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testTime)
+		case "Date":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testDate)
+		case "DateTime":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+		}
+		throwFail(t, AssertIs(vu == value, true), value, vu)
+	}
+
+	var datas2 []Data
+
+	query = fmt.Sprintf("SELECT * FROM %sdata%s", Q, Q)
+	num, err = dORM.Raw(query).QueryRows(&datas2)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 1))
+	throwFailNow(t, AssertIs(len(datas2), 1))
+
+	ind = reflect.Indirect(reflect.ValueOf(datas2[0]))
+
+	for name, value := range DataValues {
+		e := ind.FieldByName(name)
+		vu := e.Interface()
+		switch name {
+		case "Time":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testTime)
+		case "Date":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testDate)
+		case "DateTime":
+			vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+			value = value.(time.Time).In(DefaultTimeLoc).Format(testDateTime)
+		}
+		throwFail(t, AssertIs(vu == value, true), value, vu)
+	}
+
+	var ids []int
+	var usernames []string
+	query = fmt.Sprintf("SELECT %sid%s, %suser_name%s FROM %suser%s ORDER BY %sid%s ASC", Q, Q, Q, Q, Q, Q, Q, Q)
+	num, err = dORM.Raw(query).QueryRows(&ids, &usernames)
+	throwFailNow(t, err)
+	throwFailNow(t, AssertIs(num, 3))
+	throwFailNow(t, AssertIs(len(ids), 3))
+	throwFailNow(t, AssertIs(ids[0], 2))
+	throwFailNow(t, AssertIs(usernames[0], "slene"))
+	throwFailNow(t, AssertIs(ids[1], 3))
+	throwFailNow(t, AssertIs(usernames[1], "astaxie"))
+	throwFailNow(t, AssertIs(ids[2], 4))
+	throwFailNow(t, AssertIs(usernames[2], "nobody"))
+}
+
+func TestRawValues(t *testing.T) {
+	Q := dDbBaser.TableQuote()
+
+	var maps []Params
+	query := fmt.Sprintf("SELECT %suser_name%s FROM %suser%s WHERE %sStatus%s = ?", Q, Q, Q, Q, Q, Q)
+	num, err := dORM.Raw(query, 1).Values(&maps)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+	if num == 1 {
+		throwFail(t, AssertIs(maps[0]["user_name"], "slene"))
+	}
+
+	var lists []ParamsList
+	num, err = dORM.Raw(query, 1).ValuesList(&lists)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+	if num == 1 {
+		throwFail(t, AssertIs(lists[0][0], "slene"))
+	}
+
+	query = fmt.Sprintf("SELECT %sprofile_id%s FROM %suser%s ORDER BY %sid%s ASC", Q, Q, Q, Q, Q, Q)
+	var list ParamsList
+	num, err = dORM.Raw(query).ValuesFlat(&list)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+	if num == 3 {
+		throwFail(t, AssertIs(list[0], "2"))
+		throwFail(t, AssertIs(list[1], "3"))
+		throwFail(t, AssertIs(list[2], nil))
+	}
+}
+
+func TestRawPrepare(t *testing.T) {
+	switch {
+	case IsMysql || IsSqlite:
+
+		pre, err := dORM.Raw("INSERT INTO tag (name) VALUES (?)").Prepare()
+		throwFail(t, err)
+		if pre != nil {
+			r, err := pre.Exec("name1")
+			throwFail(t, err)
+
+			tid, err := r.LastInsertId()
+			throwFail(t, err)
+			throwFail(t, AssertIs(tid > 0, true))
+
+			r, err = pre.Exec("name2")
+			throwFail(t, err)
+
+			id, err := r.LastInsertId()
+			throwFail(t, err)
+			throwFail(t, AssertIs(id, tid+1))
+
+			r, err = pre.Exec("name3")
+			throwFail(t, err)
+
+			id, err = r.LastInsertId()
+			throwFail(t, err)
+			throwFail(t, AssertIs(id, tid+2))
+
+			err = pre.Close()
+			throwFail(t, err)
+
+			res, err := dORM.Raw("DELETE FROM tag WHERE name IN (?, ?, ?)", []string{"name1", "name2", "name3"}).Exec()
+			throwFail(t, err)
+
+			num, err := res.RowsAffected()
+			throwFail(t, err)
+			throwFail(t, AssertIs(num, 3))
+		}
+
+	case IsPostgres:
+
+		pre, err := dORM.Raw(`INSERT INTO "tag" ("name") VALUES (?) RETURNING "id"`).Prepare()
+		throwFail(t, err)
+		if pre != nil {
+			_, err := pre.Exec("name1")
+			throwFail(t, err)
+
+			_, err = pre.Exec("name2")
+			throwFail(t, err)
+
+			_, err = pre.Exec("name3")
+			throwFail(t, err)
+
+			err = pre.Close()
+			throwFail(t, err)
+
+			res, err := dORM.Raw(`DELETE FROM "tag" WHERE "name" IN (?, ?, ?)`, []string{"name1", "name2", "name3"}).Exec()
+			throwFail(t, err)
+
+			if err == nil {
+				num, err := res.RowsAffected()
+				throwFail(t, err)
+				throwFail(t, AssertIs(num, 3))
+			}
+		}
+	}
+}
+
+func TestUpdate(t *testing.T) {
+	qs := dORM.QueryTable("user")
+	num, err := qs.Filter("user_name", "slene").Filter("is_staff", false).Update(Params{
+		"is_staff":  true,
+		"is_active": true,
+	})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	// with join
+	num, err = qs.Filter("user_name", "slene").Filter("profile__age", 28).Filter("is_staff", true).Update(Params{
+		"is_staff": false,
+	})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name", "slene").Update(Params{
+		"Nums": ColValue(ColAdd, 100),
+	})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name", "slene").Update(Params{
+		"Nums": ColValue(ColMinus, 50),
+	})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name", "slene").Update(Params{
+		"Nums": ColValue(ColMultiply, 3),
+	})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	num, err = qs.Filter("user_name", "slene").Update(Params{
+		"Nums": ColValue(ColExcept, 5),
+	})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	user := User{UserName: "slene"}
+	err = dORM.Read(&user, "UserName")
+	throwFail(t, err)
+	throwFail(t, AssertIs(user.Nums, 30))
+}
+
+func TestDelete(t *testing.T) {
+	qs := dORM.QueryTable("user_profile")
+	num, err := qs.Filter("user__user_name", "slene").Delete()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	qs = dORM.QueryTable("user")
+	num, err = qs.Filter("user_name", "slene").Filter("profile__isnull", true).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	qs = dORM.QueryTable("comment")
+	num, err = qs.Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 6))
+
+	qs = dORM.QueryTable("post")
+	num, err = qs.Filter("Id", 3).Delete()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	qs = dORM.QueryTable("comment")
+	num, err = qs.Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 4))
+
+	qs = dORM.QueryTable("comment")
+	num, err = qs.Filter("Post__User", 3).Delete()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+
+	qs = dORM.QueryTable("comment")
+	num, err = qs.Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+}
+
+func TestTransaction(t *testing.T) {
+	// this test worked when database support transaction
+
+	o := NewOrm()
+	err := o.Begin()
+	throwFail(t, err)
+
+	var names = []string{"1", "2", "3"}
+
+	var tag Tag
+	tag.Name = names[0]
+	id, err := o.Insert(&tag)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id > 0, true))
+
+	num, err := o.QueryTable("tag").Filter("name", "golang").Update(Params{"name": names[1]})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+	switch {
+	case IsMysql || IsSqlite:
+		res, err := o.Raw("INSERT INTO tag (name) VALUES (?)", names[2]).Exec()
+		throwFail(t, err)
+		if err == nil {
+			id, err = res.LastInsertId()
+			throwFail(t, err)
+			throwFail(t, AssertIs(id > 0, true))
+		}
+	}
+
+	err = o.Rollback()
+	throwFail(t, err)
+
+	num, err = o.QueryTable("tag").Filter("name__in", names).Count()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 0))
+
+	err = o.Begin()
+	throwFail(t, err)
+
+	tag.Name = "commit"
+	id, err = o.Insert(&tag)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id > 0, true))
+
+	o.Commit()
+	throwFail(t, err)
+
+	num, err = o.QueryTable("tag").Filter("name", "commit").Delete()
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+
+}
+
+func TestReadOrCreate(t *testing.T) {
+	u := &User{
+		UserName: "Kyle",
+		Email:    "[email protected]",
+		Password: "other_pass",
+		Status:   7,
+		IsStaff:  false,
+		IsActive: true,
+	}
+
+	created, pk, err := dORM.ReadOrCreate(u, "UserName")
+	throwFail(t, err)
+	throwFail(t, AssertIs(created, true))
+	throwFail(t, AssertIs(u.UserName, "Kyle"))
+	throwFail(t, AssertIs(u.Email, "[email protected]"))
+	throwFail(t, AssertIs(u.Password, "other_pass"))
+	throwFail(t, AssertIs(u.Status, 7))
+	throwFail(t, AssertIs(u.IsStaff, false))
+	throwFail(t, AssertIs(u.IsActive, true))
+	throwFail(t, AssertIs(u.Created.In(DefaultTimeLoc), u.Created.In(DefaultTimeLoc), testDate))
+	throwFail(t, AssertIs(u.Updated.In(DefaultTimeLoc), u.Updated.In(DefaultTimeLoc), testDateTime))
+
+	nu := &User{UserName: u.UserName, Email: "[email protected]"}
+	created, pk, err = dORM.ReadOrCreate(nu, "UserName")
+	throwFail(t, err)
+	throwFail(t, AssertIs(created, false))
+	throwFail(t, AssertIs(nu.ID, u.ID))
+	throwFail(t, AssertIs(pk, u.ID))
+	throwFail(t, AssertIs(nu.UserName, u.UserName))
+	throwFail(t, AssertIs(nu.Email, u.Email)) // should contain the value in the table, not the one specified above
+	throwFail(t, AssertIs(nu.Password, u.Password))
+	throwFail(t, AssertIs(nu.Status, u.Status))
+	throwFail(t, AssertIs(nu.IsStaff, u.IsStaff))
+	throwFail(t, AssertIs(nu.IsActive, u.IsActive))
+
+	dORM.Delete(u)
+}
+
+func TestInLine(t *testing.T) {
+	name := "inline"
+	email := "[email protected]"
+	inline := NewInLine()
+	inline.Name = name
+	inline.Email = email
+
+	id, err := dORM.Insert(inline)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 1))
+
+	il := NewInLine()
+	il.ID = 1
+	err = dORM.Read(il)
+	throwFail(t, err)
+
+	throwFail(t, AssertIs(il.Name, name))
+	throwFail(t, AssertIs(il.Email, email))
+	throwFail(t, AssertIs(il.Created.In(DefaultTimeLoc), inline.Created.In(DefaultTimeLoc), testDate))
+	throwFail(t, AssertIs(il.Updated.In(DefaultTimeLoc), inline.Updated.In(DefaultTimeLoc), testDateTime))
+}
+
+func TestInLineOneToOne(t *testing.T) {
+	name := "121"
+	email := "[email protected]"
+	inline := NewInLine()
+	inline.Name = name
+	inline.Email = email
+
+	id, err := dORM.Insert(inline)
+	throwFail(t, err)
+	throwFail(t, AssertIs(id, 2))
+
+	note := "one2one"
+	il121 := NewInLineOneToOne()
+	il121.Note = note
+	il121.InLine = inline
+	_, err = dORM.Insert(il121)
+	throwFail(t, err)
+	throwFail(t, AssertIs(il121.ID, 1))
+
+	il := NewInLineOneToOne()
+	err = dORM.QueryTable(il).Filter("Id", 1).RelatedSel().One(il)
+
+	throwFail(t, err)
+	throwFail(t, AssertIs(il.Note, note))
+	throwFail(t, AssertIs(il.InLine.ID, id))
+	throwFail(t, AssertIs(il.InLine.Name, name))
+	throwFail(t, AssertIs(il.InLine.Email, email))
+
+	rinline := NewInLine()
+	err = dORM.QueryTable(rinline).Filter("InLineOneToOne__Id", 1).One(rinline)
+
+	throwFail(t, err)
+	throwFail(t, AssertIs(rinline.ID, id))
+	throwFail(t, AssertIs(rinline.Name, name))
+	throwFail(t, AssertIs(rinline.Email, email))
+}
+
+func TestIntegerPk(t *testing.T) {
+	its := []IntegerPk{
+		{ID: math.MinInt64, Value: "-"},
+		{ID: 0, Value: "0"},
+		{ID: math.MaxInt64, Value: "+"},
+	}
+
+	num, err := dORM.InsertMulti(len(its), its)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, len(its)))
+
+	for _, intPk := range its {
+		out := IntegerPk{ID: intPk.ID}
+		err = dORM.Read(&out)
+		throwFail(t, err)
+		throwFail(t, AssertIs(out.Value, intPk.Value))
+	}
+
+	num, err = dORM.InsertMulti(1, []*IntegerPk{{
+		ID: 1, Value: "ok",
+	}})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+}
+
+func TestInsertAuto(t *testing.T) {
+	u := &User{
+		UserName: "autoPre",
+		Email:    "[email protected]",
+	}
+
+	id, err := dORM.Insert(u)
+	throwFail(t, err)
+
+	id += 100
+	su := &User{
+		ID:       int(id),
+		UserName: "auto",
+		Email:    "[email protected]",
+	}
+
+	nid, err := dORM.Insert(su)
+	throwFail(t, err)
+	throwFail(t, AssertIs(nid, id))
+
+	users := []User{
+		{ID: int(id + 100), UserName: "auto_100"},
+		{ID: int(id + 110), UserName: "auto_110"},
+		{ID: int(id + 120), UserName: "auto_120"},
+	}
+	num, err := dORM.InsertMulti(100, users)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 3))
+
+	u = &User{
+		UserName: "auto_121",
+	}
+
+	nid, err = dORM.Insert(u)
+	throwFail(t, err)
+	throwFail(t, AssertIs(nid, id+120+1))
+}
+
+func TestUintPk(t *testing.T) {
+	name := "go"
+	u := &UintPk{
+		ID:   8,
+		Name: name,
+	}
+
+	created, pk, err := dORM.ReadOrCreate(u, "ID")
+	throwFail(t, err)
+	throwFail(t, AssertIs(created, true))
+	throwFail(t, AssertIs(u.Name, name))
+
+	nu := &UintPk{ID: 8}
+	created, pk, err = dORM.ReadOrCreate(nu, "ID")
+	throwFail(t, err)
+	throwFail(t, AssertIs(created, false))
+	throwFail(t, AssertIs(nu.ID, u.ID))
+	throwFail(t, AssertIs(pk, u.ID))
+	throwFail(t, AssertIs(nu.Name, name))
+
+	dORM.Delete(u)
+}
+
+func TestPtrPk(t *testing.T) {
+	parent := &IntegerPk{ID: 10, Value: "10"}
+
+	id, _ := dORM.Insert(parent)
+	if !IsMysql {
+		// MySql does not support last_insert_id in this case: see #2382
+		throwFail(t, AssertIs(id, 10))
+	}
+
+	ptr := PtrPk{ID: parent, Positive: true}
+	num, err := dORM.InsertMulti(2, []PtrPk{ptr})
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+	throwFail(t, AssertIs(ptr.ID, parent))
+
+	nptr := &PtrPk{ID: parent}
+	created, pk, err := dORM.ReadOrCreate(nptr, "ID")
+	throwFail(t, err)
+	throwFail(t, AssertIs(created, false))
+	throwFail(t, AssertIs(pk, 10))
+	throwFail(t, AssertIs(nptr.ID, parent))
+	throwFail(t, AssertIs(nptr.Positive, true))
+
+	nptr = &PtrPk{Positive: true}
+	created, pk, err = dORM.ReadOrCreate(nptr, "Positive")
+	throwFail(t, err)
+	throwFail(t, AssertIs(created, false))
+	throwFail(t, AssertIs(pk, 10))
+	throwFail(t, AssertIs(nptr.ID, parent))
+
+	nptr.Positive = false
+	num, err = dORM.Update(nptr)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+	throwFail(t, AssertIs(nptr.ID, parent))
+	throwFail(t, AssertIs(nptr.Positive, false))
+
+	num, err = dORM.Delete(nptr)
+	throwFail(t, err)
+	throwFail(t, AssertIs(num, 1))
+}
+
+func TestSnake(t *testing.T) {
+	cases := map[string]string{
+		"i":           "i",
+		"I":           "i",
+		"iD":          "i_d",
+		"ID":          "i_d",
+		"NO":          "n_o",
+		"NOO":         "n_o_o",
+		"NOOooOOoo":   "n_o_ooo_o_ooo",
+		"OrderNO":     "order_n_o",
+		"tagName":     "tag_name",
+		"tag_Name":    "tag__name",
+		"tag_name":    "tag_name",
+		"_tag_name":   "_tag_name",
+		"tag_666name": "tag_666name",
+		"tag_666Name": "tag_666_name",
+	}
+	for name, want := range cases {
+		got := snakeString(name)
+		throwFail(t, AssertIs(got, want))
+	}
+}
+
+func TestIgnoreCaseTag(t *testing.T) {
+	type testTagModel struct {
+		ID     int    `orm:"pk"`
+		NOO    string `orm:"column(n)"`
+		Name01 string `orm:"NULL"`
+		Name02 string `orm:"COLUMN(Name)"`
+		Name03 string `orm:"Column(name)"`
+	}
+	modelCache.clean()
+	RegisterModel(&testTagModel{})
+	info, ok := modelCache.get("test_tag_model")
+	throwFail(t, AssertIs(ok, true))
+	throwFail(t, AssertNot(info, nil))
+	if t == nil {
+		return
+	}
+	throwFail(t, AssertIs(info.fields.GetByName("NOO").column, "n"))
+	throwFail(t, AssertIs(info.fields.GetByName("Name01").null, true))
+	throwFail(t, AssertIs(info.fields.GetByName("Name02").column, "Name"))
+	throwFail(t, AssertIs(info.fields.GetByName("Name03").column, "name"))
+}
+func TestInsertOrUpdate(t *testing.T) {
+	RegisterModel(new(User))
+	user := User{UserName: "unique_username133", Status: 1, Password: "o"}
+	user1 := User{UserName: "unique_username133", Status: 2, Password: "o"}
+	user2 := User{UserName: "unique_username133", Status: 3, Password: "oo"}
+	dORM.Insert(&user)
+	test := User{UserName: "unique_username133"}
+	fmt.Println(dORM.Driver().Name())
+	if dORM.Driver().Name() == "sqlite3" {
+		fmt.Println("sqlite3 is nonsupport")
+		return
+	}
+	//test1
+	_, err := dORM.InsertOrUpdate(&user1, "user_name")
+	if err != nil {
+		fmt.Println(err)
+		if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" {
+		} else {
+			throwFailNow(t, err)
+		}
+	} else {
+		dORM.Read(&test, "user_name")
+		throwFailNow(t, AssertIs(user1.Status, test.Status))
+	}
+	//test2
+	_, err = dORM.InsertOrUpdate(&user2, "user_name")
+	if err != nil {
+		fmt.Println(err)
+		if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" {
+		} else {
+			throwFailNow(t, err)
+		}
+	} else {
+		dORM.Read(&test, "user_name")
+		throwFailNow(t, AssertIs(user2.Status, test.Status))
+		throwFailNow(t, AssertIs(user2.Password, strings.TrimSpace(test.Password)))
+	}
+	//test3 +
+	_, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status+1")
+	if err != nil {
+		fmt.Println(err)
+		if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" {
+		} else {
+			throwFailNow(t, err)
+		}
+	} else {
+		dORM.Read(&test, "user_name")
+		throwFailNow(t, AssertIs(user2.Status+1, test.Status))
+	}
+	//test4 -
+	_, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status-1")
+	if err != nil {
+		fmt.Println(err)
+		if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" {
+		} else {
+			throwFailNow(t, err)
+		}
+	} else {
+		dORM.Read(&test, "user_name")
+		throwFailNow(t, AssertIs((user2.Status+1)-1, test.Status))
+	}
+	//test5 *
+	_, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status*3")
+	if err != nil {
+		fmt.Println(err)
+		if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" {
+		} else {
+			throwFailNow(t, err)
+		}
+	} else {
+		dORM.Read(&test, "user_name")
+		throwFailNow(t, AssertIs(((user2.Status+1)-1)*3, test.Status))
+	}
+	//test6 /
+	_, err = dORM.InsertOrUpdate(&user2, "user_name", "Status=Status/3")
+	if err != nil {
+		fmt.Println(err)
+		if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" {
+		} else {
+			throwFailNow(t, err)
+		}
+	} else {
+		dORM.Read(&test, "user_name")
+		throwFailNow(t, AssertIs((((user2.Status+1)-1)*3)/3, test.Status))
+	}
+}

+ 36 - 0
vendor/github.com/astaxie/beego/orm/utils_test.go

@@ -0,0 +1,36 @@
+// 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 orm
+
+import (
+	"testing"
+)
+
+func TestCamelString(t *testing.T) {
+	snake := []string{"pic_url", "hello_world_", "hello__World", "_HelLO_Word", "pic_url_1", "pic_url__1"}
+	camel := []string{"PicUrl", "HelloWorld", "HelloWorld", "HelLOWord", "PicUrl1", "PicUrl1"}
+
+	answer := make(map[string]string)
+	for i, v := range snake {
+		answer[v] = camel[i]
+	}
+
+	for _, v := range snake {
+		res := camelString(v)
+		if res != answer[v] {
+			t.Error("Unit Test Fail:", v, res, answer[v])
+		}
+	}
+}

+ 160 - 0
vendor/github.com/astaxie/beego/plugins/apiauth/apiauth.go

@@ -0,0 +1,160 @@
+// 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 apiauth provides handlers to enable apiauth support.
+//
+// Simple Usage:
+//	import(
+//		"github.com/astaxie/beego"
+//		"github.com/astaxie/beego/plugins/apiauth"
+//	)
+//
+//	func main(){
+//		// apiauth every request
+//		beego.InsertFilter("*", beego.BeforeRouter,apiauth.APIBaiscAuth("appid","appkey"))
+//		beego.Run()
+//	}
+//
+// Advanced Usage:
+//
+//	func getAppSecret(appid string) string {
+//		// get appsecret by appid
+//		// maybe store in configure, maybe in database
+//	}
+//
+//	beego.InsertFilter("*", beego.BeforeRouter,apiauth.APISecretAuth(getAppSecret, 360))
+//
+// Information:
+//
+// In the request user should include these params in the query
+//
+// 1. appid
+//
+//		 appid is assigned to the application
+//
+// 2. signature
+//
+//	get the signature use apiauth.Signature()
+//
+//	when you send to server remember use url.QueryEscape()
+//
+// 3. timestamp:
+//
+//       send the request time, the format is yyyy-mm-dd HH:ii:ss
+//
+package apiauth
+
+import (
+	"bytes"
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/base64"
+	"fmt"
+	"net/url"
+	"sort"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+)
+
+// 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 {
+	ft := func(aid string) string {
+		if aid == appid {
+			return appkey
+		}
+		return ""
+	}
+	return APISecretAuth(ft, 300)
+}
+
+// APISecretAuth use AppIdToAppSecret verify and
+func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc {
+	return func(ctx *context.Context) {
+		if ctx.Input.Query("appid") == "" {
+			ctx.ResponseWriter.WriteHeader(403)
+			ctx.WriteString("miss query param: appid")
+			return
+		}
+		appsecret := f(ctx.Input.Query("appid"))
+		if appsecret == "" {
+			ctx.ResponseWriter.WriteHeader(403)
+			ctx.WriteString("not exist this appid")
+			return
+		}
+		if ctx.Input.Query("signature") == "" {
+			ctx.ResponseWriter.WriteHeader(403)
+			ctx.WriteString("miss query param: signature")
+			return
+		}
+		if ctx.Input.Query("timestamp") == "" {
+			ctx.ResponseWriter.WriteHeader(403)
+			ctx.WriteString("miss query param: timestamp")
+			return
+		}
+		u, err := time.Parse("2006-01-02 15:04:05", ctx.Input.Query("timestamp"))
+		if err != nil {
+			ctx.ResponseWriter.WriteHeader(403)
+			ctx.WriteString("timestamp format is error, should 2006-01-02 15:04:05")
+			return
+		}
+		t := time.Now()
+		if t.Sub(u).Seconds() > float64(timeout) {
+			ctx.ResponseWriter.WriteHeader(403)
+			ctx.WriteString("timeout! the request time is long ago, please try again")
+			return
+		}
+		if ctx.Input.Query("signature") !=
+			Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.URL()) {
+			ctx.ResponseWriter.WriteHeader(403)
+			ctx.WriteString("auth failed")
+		}
+	}
+}
+
+// Signature used to generate signature with the appsecret/method/params/RequestURI
+func Signature(appsecret, method string, params url.Values, RequestURL string) (result string) {
+	var b bytes.Buffer
+	keys := make([]string, len(params))
+	pa := make(map[string]string)
+	for k, v := range params {
+		pa[k] = v[0]
+		keys = append(keys, k)
+	}
+
+	sort.Strings(keys)
+
+	for _, key := range keys {
+		if key == "signature" {
+			continue
+		}
+
+		val := pa[key]
+		if key != "" && val != "" {
+			b.WriteString(key)
+			b.WriteString(val)
+		}
+	}
+
+	stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, b.String(), RequestURL)
+
+	sha256 := sha256.New
+	hash := hmac.New(sha256, []byte(appsecret))
+	hash.Write([]byte(stringToSign))
+	return base64.StdEncoding.EncodeToString(hash.Sum(nil))
+}

+ 20 - 0
vendor/github.com/astaxie/beego/plugins/apiauth/apiauth_test.go

@@ -0,0 +1,20 @@
+package apiauth
+
+import (
+	"net/url"
+	"testing"
+)
+
+func TestSignature(t *testing.T) {
+	appsecret := "beego secret"
+	method := "GET"
+	RequestURL := "http://localhost/test/url"
+	params := make(url.Values)
+	params.Add("arg1", "hello")
+	params.Add("arg2", "beego")
+
+	signature := "mFdpvLh48ca4mDVEItE9++AKKQ/IVca7O/ZyyB8hR58="
+	if Signature(appsecret, method, params, RequestURL) != signature {
+		t.Error("Signature error")
+	}
+}

+ 107 - 0
vendor/github.com/astaxie/beego/plugins/auth/basic.go

@@ -0,0 +1,107 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package auth provides handlers to enable basic auth support.
+// Simple Usage:
+//	import(
+//		"github.com/astaxie/beego"
+//		"github.com/astaxie/beego/plugins/auth"
+//	)
+//
+//	func main(){
+//		// authenticate every request
+//		beego.InsertFilter("*", beego.BeforeRouter,auth.Basic("username","secretpassword"))
+//		beego.Run()
+//	}
+//
+//
+// Advanced Usage:
+//
+//	func SecretAuth(username, password string) bool {
+//		return username == "astaxie" && password == "helloBeego"
+//	}
+//	authPlugin := auth.NewBasicAuthenticator(SecretAuth, "Authorization Required")
+//	beego.InsertFilter("*", beego.BeforeRouter,authPlugin)
+package auth
+
+import (
+	"encoding/base64"
+	"net/http"
+	"strings"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+)
+
+var defaultRealm = "Authorization Required"
+
+// Basic is the http basic auth
+func Basic(username string, password string) beego.FilterFunc {
+	secrets := func(user, pass string) bool {
+		return user == username && pass == password
+	}
+	return NewBasicAuthenticator(secrets, defaultRealm)
+}
+
+// NewBasicAuthenticator return the BasicAuth
+func NewBasicAuthenticator(secrets SecretProvider, Realm string) beego.FilterFunc {
+	return func(ctx *context.Context) {
+		a := &BasicAuth{Secrets: secrets, Realm: Realm}
+		if username := a.CheckAuth(ctx.Request); username == "" {
+			a.RequireAuth(ctx.ResponseWriter, ctx.Request)
+		}
+	}
+}
+
+// SecretProvider is the SecretProvider function
+type SecretProvider func(user, pass string) bool
+
+// BasicAuth store the SecretProvider and Realm
+type BasicAuth struct {
+	Secrets SecretProvider
+	Realm   string
+}
+
+// CheckAuth Checks the username/password combination from the request. Returns
+// either an empty string (authentication failed) or the name of the
+// authenticated user.
+// Supports MD5 and SHA1 password entries
+func (a *BasicAuth) CheckAuth(r *http.Request) string {
+	s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
+	if len(s) != 2 || s[0] != "Basic" {
+		return ""
+	}
+
+	b, err := base64.StdEncoding.DecodeString(s[1])
+	if err != nil {
+		return ""
+	}
+	pair := strings.SplitN(string(b), ":", 2)
+	if len(pair) != 2 {
+		return ""
+	}
+
+	if a.Secrets(pair[0], pair[1]) {
+		return pair[0]
+	}
+	return ""
+}
+
+// RequireAuth http.Handler for BasicAuth which initiates the authentication process
+// (or requires reauthentication).
+func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("WWW-Authenticate", `Basic realm="`+a.Realm+`"`)
+	w.WriteHeader(401)
+	w.Write([]byte("401 Unauthorized\n"))
+}

+ 228 - 0
vendor/github.com/astaxie/beego/plugins/cors/cors.go

@@ -0,0 +1,228 @@
+// 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 cors provides handlers to enable CORS support.
+// Usage
+//	import (
+// 		"github.com/astaxie/beego"
+//		"github.com/astaxie/beego/plugins/cors"
+// )
+//
+//	func main() {
+//		// CORS for https://foo.* origins, allowing:
+//		// - PUT and PATCH methods
+//		// - Origin header
+//		// - Credentials share
+//		beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
+//			AllowOrigins:     []string{"https://*.foo.com"},
+//			AllowMethods:     []string{"PUT", "PATCH"},
+//			AllowHeaders:     []string{"Origin"},
+//			ExposeHeaders:    []string{"Content-Length"},
+//			AllowCredentials: true,
+//		}))
+//		beego.Run()
+//	}
+package cors
+
+import (
+	"net/http"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+)
+
+const (
+	headerAllowOrigin      = "Access-Control-Allow-Origin"
+	headerAllowCredentials = "Access-Control-Allow-Credentials"
+	headerAllowHeaders     = "Access-Control-Allow-Headers"
+	headerAllowMethods     = "Access-Control-Allow-Methods"
+	headerExposeHeaders    = "Access-Control-Expose-Headers"
+	headerMaxAge           = "Access-Control-Max-Age"
+
+	headerOrigin         = "Origin"
+	headerRequestMethod  = "Access-Control-Request-Method"
+	headerRequestHeaders = "Access-Control-Request-Headers"
+)
+
+var (
+	defaultAllowHeaders = []string{"Origin", "Accept", "Content-Type", "Authorization"}
+	// Regex patterns are generated from AllowOrigins. These are used and generated internally.
+	allowOriginPatterns = []string{}
+)
+
+// Options represents Access Control options.
+type Options struct {
+	// If set, all origins are allowed.
+	AllowAllOrigins bool
+	// A list of allowed origins. Wild cards and FQDNs are supported.
+	AllowOrigins []string
+	// If set, allows to share auth credentials such as cookies.
+	AllowCredentials bool
+	// A list of allowed HTTP methods.
+	AllowMethods []string
+	// A list of allowed HTTP headers.
+	AllowHeaders []string
+	// A list of exposed HTTP headers.
+	ExposeHeaders []string
+	// Max age of the CORS headers.
+	MaxAge time.Duration
+}
+
+// Header converts options into CORS headers.
+func (o *Options) Header(origin string) (headers map[string]string) {
+	headers = make(map[string]string)
+	// if origin is not allowed, don't extend the headers
+	// with CORS headers.
+	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
+		return
+	}
+
+	// add allow origin
+	if o.AllowAllOrigins {
+		headers[headerAllowOrigin] = "*"
+	} else {
+		headers[headerAllowOrigin] = origin
+	}
+
+	// add allow credentials
+	headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
+
+	// add allow methods
+	if len(o.AllowMethods) > 0 {
+		headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
+	}
+
+	// add allow headers
+	if len(o.AllowHeaders) > 0 {
+		headers[headerAllowHeaders] = strings.Join(o.AllowHeaders, ",")
+	}
+
+	// add exposed header
+	if len(o.ExposeHeaders) > 0 {
+		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
+	}
+	// add a max age header
+	if o.MaxAge > time.Duration(0) {
+		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
+	}
+	return
+}
+
+// PreflightHeader converts options into CORS headers for a preflight response.
+func (o *Options) PreflightHeader(origin, rMethod, rHeaders string) (headers map[string]string) {
+	headers = make(map[string]string)
+	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
+		return
+	}
+	// verify if requested method is allowed
+	for _, method := range o.AllowMethods {
+		if method == rMethod {
+			headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
+			break
+		}
+	}
+
+	// verify if requested headers are allowed
+	var allowed []string
+	for _, rHeader := range strings.Split(rHeaders, ",") {
+		rHeader = strings.TrimSpace(rHeader)
+	lookupLoop:
+		for _, allowedHeader := range o.AllowHeaders {
+			if strings.ToLower(rHeader) == strings.ToLower(allowedHeader) {
+				allowed = append(allowed, rHeader)
+				break lookupLoop
+			}
+		}
+	}
+
+	headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
+	// add allow origin
+	if o.AllowAllOrigins {
+		headers[headerAllowOrigin] = "*"
+	} else {
+		headers[headerAllowOrigin] = origin
+	}
+
+	// add allowed headers
+	if len(allowed) > 0 {
+		headers[headerAllowHeaders] = strings.Join(allowed, ",")
+	}
+
+	// add exposed headers
+	if len(o.ExposeHeaders) > 0 {
+		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
+	}
+	// add a max age header
+	if o.MaxAge > time.Duration(0) {
+		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
+	}
+	return
+}
+
+// IsOriginAllowed looks up if the origin matches one of the patterns
+// generated from Options.AllowOrigins patterns.
+func (o *Options) IsOriginAllowed(origin string) (allowed bool) {
+	for _, pattern := range allowOriginPatterns {
+		allowed, _ = regexp.MatchString(pattern, origin)
+		if allowed {
+			return
+		}
+	}
+	return
+}
+
+// Allow enables CORS for requests those match the provided options.
+func Allow(opts *Options) beego.FilterFunc {
+	// Allow default headers if nothing is specified.
+	if len(opts.AllowHeaders) == 0 {
+		opts.AllowHeaders = defaultAllowHeaders
+	}
+
+	for _, origin := range opts.AllowOrigins {
+		pattern := regexp.QuoteMeta(origin)
+		pattern = strings.Replace(pattern, "\\*", ".*", -1)
+		pattern = strings.Replace(pattern, "\\?", ".", -1)
+		allowOriginPatterns = append(allowOriginPatterns, "^"+pattern+"$")
+	}
+
+	return func(ctx *context.Context) {
+		var (
+			origin           = ctx.Input.Header(headerOrigin)
+			requestedMethod  = ctx.Input.Header(headerRequestMethod)
+			requestedHeaders = ctx.Input.Header(headerRequestHeaders)
+			// additional headers to be added
+			// to the response.
+			headers map[string]string
+		)
+
+		if ctx.Input.Method() == "OPTIONS" &&
+			(requestedMethod != "" || requestedHeaders != "") {
+			headers = opts.PreflightHeader(origin, requestedMethod, requestedHeaders)
+			for key, value := range headers {
+				ctx.Output.Header(key, value)
+			}
+			ctx.ResponseWriter.WriteHeader(http.StatusOK)
+			return
+		}
+		headers = opts.Header(origin)
+
+		for key, value := range headers {
+			ctx.Output.Header(key, value)
+		}
+	}
+}

+ 253 - 0
vendor/github.com/astaxie/beego/plugins/cors/cors_test.go

@@ -0,0 +1,253 @@
+// 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 cors
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+)
+
+// HTTPHeaderGuardRecorder is httptest.ResponseRecorder with own http.Header
+type HTTPHeaderGuardRecorder struct {
+	*httptest.ResponseRecorder
+	savedHeaderMap http.Header
+}
+
+// NewRecorder return HttpHeaderGuardRecorder
+func NewRecorder() *HTTPHeaderGuardRecorder {
+	return &HTTPHeaderGuardRecorder{httptest.NewRecorder(), nil}
+}
+
+func (gr *HTTPHeaderGuardRecorder) WriteHeader(code int) {
+	gr.ResponseRecorder.WriteHeader(code)
+	gr.savedHeaderMap = gr.ResponseRecorder.Header()
+}
+
+func (gr *HTTPHeaderGuardRecorder) Header() http.Header {
+	if gr.savedHeaderMap != nil {
+		// headers were written. clone so we don't get updates
+		clone := make(http.Header)
+		for k, v := range gr.savedHeaderMap {
+			clone[k] = v
+		}
+		return clone
+	}
+	return gr.ResponseRecorder.Header()
+}
+
+func Test_AllowAll(t *testing.T) {
+	recorder := httptest.NewRecorder()
+	handler := beego.NewControllerRegister()
+	handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+		AllowAllOrigins: true,
+	}))
+	handler.Any("/foo", func(ctx *context.Context) {
+		ctx.Output.SetStatus(500)
+	})
+	r, _ := http.NewRequest("PUT", "/foo", nil)
+	handler.ServeHTTP(recorder, r)
+
+	if recorder.HeaderMap.Get(headerAllowOrigin) != "*" {
+		t.Errorf("Allow-Origin header should be *")
+	}
+}
+
+func Test_AllowRegexMatch(t *testing.T) {
+	recorder := httptest.NewRecorder()
+	handler := beego.NewControllerRegister()
+	handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+		AllowOrigins: []string{"https://aaa.com", "https://*.foo.com"},
+	}))
+	handler.Any("/foo", func(ctx *context.Context) {
+		ctx.Output.SetStatus(500)
+	})
+	origin := "https://bar.foo.com"
+	r, _ := http.NewRequest("PUT", "/foo", nil)
+	r.Header.Add("Origin", origin)
+	handler.ServeHTTP(recorder, r)
+
+	headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
+	if headerValue != origin {
+		t.Errorf("Allow-Origin header should be %v, found %v", origin, headerValue)
+	}
+}
+
+func Test_AllowRegexNoMatch(t *testing.T) {
+	recorder := httptest.NewRecorder()
+	handler := beego.NewControllerRegister()
+	handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+		AllowOrigins: []string{"https://*.foo.com"},
+	}))
+	handler.Any("/foo", func(ctx *context.Context) {
+		ctx.Output.SetStatus(500)
+	})
+	origin := "https://ww.foo.com.evil.com"
+	r, _ := http.NewRequest("PUT", "/foo", nil)
+	r.Header.Add("Origin", origin)
+	handler.ServeHTTP(recorder, r)
+
+	headerValue := recorder.HeaderMap.Get(headerAllowOrigin)
+	if headerValue != "" {
+		t.Errorf("Allow-Origin header should not exist, found %v", headerValue)
+	}
+}
+
+func Test_OtherHeaders(t *testing.T) {
+	recorder := httptest.NewRecorder()
+	handler := beego.NewControllerRegister()
+	handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+		AllowAllOrigins:  true,
+		AllowCredentials: true,
+		AllowMethods:     []string{"PATCH", "GET"},
+		AllowHeaders:     []string{"Origin", "X-whatever"},
+		ExposeHeaders:    []string{"Content-Length", "Hello"},
+		MaxAge:           5 * time.Minute,
+	}))
+	handler.Any("/foo", func(ctx *context.Context) {
+		ctx.Output.SetStatus(500)
+	})
+	r, _ := http.NewRequest("PUT", "/foo", nil)
+	handler.ServeHTTP(recorder, r)
+
+	credentialsVal := recorder.HeaderMap.Get(headerAllowCredentials)
+	methodsVal := recorder.HeaderMap.Get(headerAllowMethods)
+	headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
+	exposedHeadersVal := recorder.HeaderMap.Get(headerExposeHeaders)
+	maxAgeVal := recorder.HeaderMap.Get(headerMaxAge)
+
+	if credentialsVal != "true" {
+		t.Errorf("Allow-Credentials is expected to be true, found %v", credentialsVal)
+	}
+
+	if methodsVal != "PATCH,GET" {
+		t.Errorf("Allow-Methods is expected to be PATCH,GET; found %v", methodsVal)
+	}
+
+	if headersVal != "Origin,X-whatever" {
+		t.Errorf("Allow-Headers is expected to be Origin,X-whatever; found %v", headersVal)
+	}
+
+	if exposedHeadersVal != "Content-Length,Hello" {
+		t.Errorf("Expose-Headers are expected to be Content-Length,Hello. Found %v", exposedHeadersVal)
+	}
+
+	if maxAgeVal != "300" {
+		t.Errorf("Max-Age is expected to be 300, found %v", maxAgeVal)
+	}
+}
+
+func Test_DefaultAllowHeaders(t *testing.T) {
+	recorder := httptest.NewRecorder()
+	handler := beego.NewControllerRegister()
+	handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+		AllowAllOrigins: true,
+	}))
+	handler.Any("/foo", func(ctx *context.Context) {
+		ctx.Output.SetStatus(500)
+	})
+
+	r, _ := http.NewRequest("PUT", "/foo", nil)
+	handler.ServeHTTP(recorder, r)
+
+	headersVal := recorder.HeaderMap.Get(headerAllowHeaders)
+	if headersVal != "Origin,Accept,Content-Type,Authorization" {
+		t.Errorf("Allow-Headers is expected to be Origin,Accept,Content-Type,Authorization; found %v", headersVal)
+	}
+}
+
+func Test_Preflight(t *testing.T) {
+	recorder := NewRecorder()
+	handler := beego.NewControllerRegister()
+	handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+		AllowAllOrigins: true,
+		AllowMethods:    []string{"PUT", "PATCH"},
+		AllowHeaders:    []string{"Origin", "X-whatever", "X-CaseSensitive"},
+	}))
+
+	handler.Any("/foo", func(ctx *context.Context) {
+		ctx.Output.SetStatus(200)
+	})
+
+	r, _ := http.NewRequest("OPTIONS", "/foo", nil)
+	r.Header.Add(headerRequestMethod, "PUT")
+	r.Header.Add(headerRequestHeaders, "X-whatever, x-casesensitive")
+	handler.ServeHTTP(recorder, r)
+
+	headers := recorder.Header()
+	methodsVal := headers.Get(headerAllowMethods)
+	headersVal := headers.Get(headerAllowHeaders)
+	originVal := headers.Get(headerAllowOrigin)
+
+	if methodsVal != "PUT,PATCH" {
+		t.Errorf("Allow-Methods is expected to be PUT,PATCH, found %v", methodsVal)
+	}
+
+	if !strings.Contains(headersVal, "X-whatever") {
+		t.Errorf("Allow-Headers is expected to contain X-whatever, found %v", headersVal)
+	}
+
+	if !strings.Contains(headersVal, "x-casesensitive") {
+		t.Errorf("Allow-Headers is expected to contain x-casesensitive, found %v", headersVal)
+	}
+
+	if originVal != "*" {
+		t.Errorf("Allow-Origin is expected to be *, found %v", originVal)
+	}
+
+	if recorder.Code != http.StatusOK {
+		t.Errorf("Status code is expected to be 200, found %d", recorder.Code)
+	}
+}
+
+func Benchmark_WithoutCORS(b *testing.B) {
+	recorder := httptest.NewRecorder()
+	handler := beego.NewControllerRegister()
+	beego.BConfig.RunMode = beego.PROD
+	handler.Any("/foo", func(ctx *context.Context) {
+		ctx.Output.SetStatus(500)
+	})
+	b.ResetTimer()
+	r, _ := http.NewRequest("PUT", "/foo", nil)
+	for i := 0; i < b.N; i++ {
+		handler.ServeHTTP(recorder, r)
+	}
+}
+
+func Benchmark_WithCORS(b *testing.B) {
+	recorder := httptest.NewRecorder()
+	handler := beego.NewControllerRegister()
+	beego.BConfig.RunMode = beego.PROD
+	handler.InsertFilter("*", beego.BeforeRouter, Allow(&Options{
+		AllowAllOrigins:  true,
+		AllowCredentials: true,
+		AllowMethods:     []string{"PATCH", "GET"},
+		AllowHeaders:     []string{"Origin", "X-whatever"},
+		MaxAge:           5 * time.Minute,
+	}))
+	handler.Any("/foo", func(ctx *context.Context) {
+		ctx.Output.SetStatus(500)
+	})
+	b.ResetTimer()
+	r, _ := http.NewRequest("PUT", "/foo", nil)
+	for i := 0; i < b.N; i++ {
+		handler.ServeHTTP(recorder, r)
+	}
+}

+ 689 - 0
vendor/github.com/astaxie/beego/router_test.go

@@ -0,0 +1,689 @@
+// 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 beego
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+
+	"github.com/astaxie/beego/context"
+	"github.com/astaxie/beego/logs"
+)
+
+type TestController struct {
+	Controller
+}
+
+func (tc *TestController) Get() {
+	tc.Data["Username"] = "astaxie"
+	tc.Ctx.Output.Body([]byte("ok"))
+}
+
+func (tc *TestController) Post() {
+	tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
+}
+
+func (tc *TestController) Param() {
+	tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
+}
+
+func (tc *TestController) List() {
+	tc.Ctx.Output.Body([]byte("i am list"))
+}
+
+func (tc *TestController) Params() {
+	tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param("0") + tc.Ctx.Input.Param("1") + tc.Ctx.Input.Param("2")))
+}
+
+func (tc *TestController) Myext() {
+	tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext")))
+}
+
+func (tc *TestController) GetURL() {
+	tc.Ctx.Output.Body([]byte(tc.URLFor(".Myext")))
+}
+
+func (tc *TestController) GetParams() {
+	tc.Ctx.WriteString(tc.Ctx.Input.Query(":last") + "+" +
+		tc.Ctx.Input.Query(":first") + "+" + tc.Ctx.Input.Query("learn"))
+}
+
+func (tc *TestController) GetManyRouter() {
+	tc.Ctx.WriteString(tc.Ctx.Input.Query(":id") + tc.Ctx.Input.Query(":page"))
+}
+
+func (tc *TestController) GetEmptyBody() {
+	var res []byte
+	tc.Ctx.Output.Body(res)
+}
+
+type ResStatus struct {
+	Code int
+	Msg  string
+}
+
+type JSONController struct {
+	Controller
+}
+
+func (jc *JSONController) Prepare() {
+	jc.Data["json"] = "prepare"
+	jc.ServeJSON(true)
+}
+
+func (jc *JSONController) Get() {
+	jc.Data["Username"] = "astaxie"
+	jc.Ctx.Output.Body([]byte("ok"))
+}
+
+func TestUrlFor(t *testing.T) {
+	handler := NewControllerRegister()
+	handler.Add("/api/list", &TestController{}, "*:List")
+	handler.Add("/person/:last/:first", &TestController{}, "*:Param")
+	if a := handler.URLFor("TestController.List"); a != "/api/list" {
+		logs.Info(a)
+		t.Errorf("TestController.List must equal to /api/list")
+	}
+	if a := handler.URLFor("TestController.Param", ":last", "xie", ":first", "asta"); a != "/person/xie/asta" {
+		t.Errorf("TestController.Param must equal to /person/xie/asta, but get " + a)
+	}
+}
+
+func TestUrlFor3(t *testing.T) {
+	handler := NewControllerRegister()
+	handler.AddAuto(&TestController{})
+	if a := handler.URLFor("TestController.Myext"); a != "/test/myext" && a != "/Test/Myext" {
+		t.Errorf("TestController.Myext must equal to /test/myext, but get " + a)
+	}
+	if a := handler.URLFor("TestController.GetURL"); a != "/test/geturl" && a != "/Test/GetURL" {
+		t.Errorf("TestController.GetURL must equal to /test/geturl, but get " + a)
+	}
+}
+
+func TestUrlFor2(t *testing.T) {
+	handler := NewControllerRegister()
+	handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, "*:List")
+	handler.Add("/v1/:username/edit", &TestController{}, "get:GetURL")
+	handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, "*:Param")
+	handler.Add("/:year:int/:month:int/:title/:entid", &TestController{})
+	if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" {
+		logs.Info(handler.URLFor("TestController.GetURL"))
+		t.Errorf("TestController.List must equal to /v1/astaxie/edit")
+	}
+
+	if handler.URLFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") !=
+		"/v1/za/cms_12_123.html" {
+		logs.Info(handler.URLFor("TestController.List"))
+		t.Errorf("TestController.List must equal to /v1/za/cms_12_123.html")
+	}
+	if handler.URLFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") !=
+		"/v1/za_cms/ttt_12_123.html" {
+		logs.Info(handler.URLFor("TestController.Param"))
+		t.Errorf("TestController.List must equal to /v1/za_cms/ttt_12_123.html")
+	}
+	if handler.URLFor("TestController.Get", ":year", "1111", ":month", "11",
+		":title", "aaaa", ":entid", "aaaa") !=
+		"/1111/11/aaaa/aaaa" {
+		logs.Info(handler.URLFor("TestController.Get"))
+		t.Errorf("TestController.Get must equal to /1111/11/aaaa/aaaa")
+	}
+}
+
+func TestUserFunc(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/api/list", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/api/list", &TestController{}, "*:List")
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am list" {
+		t.Errorf("user define func can't run")
+	}
+}
+
+func TestPostFunc(t *testing.T) {
+	r, _ := http.NewRequest("POST", "/astaxie", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/:name", &TestController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "astaxie" {
+		t.Errorf("post func should astaxie")
+	}
+}
+
+func TestAutoFunc(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/test/list", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.AddAuto(&TestController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am list" {
+		t.Errorf("user define func can't run")
+	}
+}
+
+func TestAutoFunc2(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/Test/List", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.AddAuto(&TestController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am list" {
+		t.Errorf("user define func can't run")
+	}
+}
+
+func TestAutoFuncParams(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/test/params/2009/11/12", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.AddAuto(&TestController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "20091112" {
+		t.Errorf("user define func can't run")
+	}
+}
+
+func TestAutoExtFunc(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/test/myext.json", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.AddAuto(&TestController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "json" {
+		t.Errorf("user define func can't run")
+	}
+}
+
+func TestRouteOk(t *testing.T) {
+
+	r, _ := http.NewRequest("GET", "/person/anderson/thomas?learn=kungfu", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/person/:last/:first", &TestController{}, "get:GetParams")
+	handler.ServeHTTP(w, r)
+	body := w.Body.String()
+	if body != "anderson+thomas+kungfu" {
+		t.Errorf("url param set to [%s];", body)
+	}
+}
+
+func TestManyRoute(t *testing.T) {
+
+	r, _ := http.NewRequest("GET", "/beego32-12.html", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, "get:GetManyRouter")
+	handler.ServeHTTP(w, r)
+
+	body := w.Body.String()
+
+	if body != "3212" {
+		t.Errorf("url param set to [%s];", body)
+	}
+}
+
+// Test for issue #1669
+func TestEmptyResponse(t *testing.T) {
+
+	r, _ := http.NewRequest("GET", "/beego-empty.html", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/beego-empty.html", &TestController{}, "get:GetEmptyBody")
+	handler.ServeHTTP(w, r)
+
+	if body := w.Body.String(); body != "" {
+		t.Error("want empty body")
+	}
+}
+
+func TestNotFound(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.ServeHTTP(w, r)
+
+	if w.Code != http.StatusNotFound {
+		t.Errorf("Code set to [%v]; want [%v]", w.Code, http.StatusNotFound)
+	}
+}
+
+// TestStatic tests the ability to serve static
+// content from the filesystem
+func TestStatic(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/static/js/jquery.js", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.ServeHTTP(w, r)
+
+	if w.Code != 404 {
+		t.Errorf("handler.Static failed to serve file")
+	}
+}
+
+func TestPrepare(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/json/list", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Add("/json/list", &JSONController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != `"prepare"` {
+		t.Errorf(w.Body.String() + "user define func can't run")
+	}
+}
+
+func TestAutoPrefix(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/admin/test/list", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.AddAutoPrefix("/admin", &TestController{})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "i am list" {
+		t.Errorf("TestAutoPrefix can't run")
+	}
+}
+
+func TestRouterGet(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/user", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Get("/user", func(ctx *context.Context) {
+		ctx.Output.Body([]byte("Get userlist"))
+	})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "Get userlist" {
+		t.Errorf("TestRouterGet can't run")
+	}
+}
+
+func TestRouterPost(t *testing.T) {
+	r, _ := http.NewRequest("POST", "/user/123", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Post("/user/:id", func(ctx *context.Context) {
+		ctx.Output.Body([]byte(ctx.Input.Param(":id")))
+	})
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "123" {
+		t.Errorf("TestRouterPost can't run")
+	}
+}
+
+func sayhello(w http.ResponseWriter, r *http.Request) {
+	w.Write([]byte("sayhello"))
+}
+
+func TestRouterHandler(t *testing.T) {
+	r, _ := http.NewRequest("POST", "/sayhi", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Handler("/sayhi", http.HandlerFunc(sayhello))
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "sayhello" {
+		t.Errorf("TestRouterHandler can't run")
+	}
+}
+
+func TestRouterHandlerAll(t *testing.T) {
+	r, _ := http.NewRequest("POST", "/sayhi/a/b/c", nil)
+	w := httptest.NewRecorder()
+
+	handler := NewControllerRegister()
+	handler.Handler("/sayhi", http.HandlerFunc(sayhello), true)
+	handler.ServeHTTP(w, r)
+	if w.Body.String() != "sayhello" {
+		t.Errorf("TestRouterHandler can't run")
+	}
+}
+
+//
+// Benchmarks NewApp:
+//
+
+func beegoFilterFunc(ctx *context.Context) {
+	ctx.WriteString("hello")
+}
+
+type AdminController struct {
+	Controller
+}
+
+func (a *AdminController) Get() {
+	a.Ctx.WriteString("hello")
+}
+
+func TestRouterFunc(t *testing.T) {
+	mux := NewControllerRegister()
+	mux.Get("/action", beegoFilterFunc)
+	mux.Post("/action", beegoFilterFunc)
+	rw, r := testRequest("GET", "/action")
+	mux.ServeHTTP(rw, r)
+	if rw.Body.String() != "hello" {
+		t.Errorf("TestRouterFunc can't run")
+	}
+}
+
+func BenchmarkFunc(b *testing.B) {
+	mux := NewControllerRegister()
+	mux.Get("/action", beegoFilterFunc)
+	rw, r := testRequest("GET", "/action")
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		mux.ServeHTTP(rw, r)
+	}
+}
+
+func BenchmarkController(b *testing.B) {
+	mux := NewControllerRegister()
+	mux.Add("/action", &AdminController{})
+	rw, r := testRequest("GET", "/action")
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		mux.ServeHTTP(rw, r)
+	}
+}
+
+func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request) {
+	request, _ := http.NewRequest(method, path, nil)
+	recorder := httptest.NewRecorder()
+
+	return recorder, request
+}
+
+// Expectation: A Filter with the correct configuration should be created given
+// specific parameters.
+func TestInsertFilter(t *testing.T) {
+	testName := "TestInsertFilter"
+
+	mux := NewControllerRegister()
+	mux.InsertFilter("*", BeforeRouter, func(*context.Context) {})
+	if !mux.filters[BeforeRouter][0].returnOnOutput {
+		t.Errorf(
+			"%s: passing no variadic params should set returnOnOutput to true",
+			testName)
+	}
+	if mux.filters[BeforeRouter][0].resetParams {
+		t.Errorf(
+			"%s: passing no variadic params should set resetParams to false",
+			testName)
+	}
+
+	mux = NewControllerRegister()
+	mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, false)
+	if mux.filters[BeforeRouter][0].returnOnOutput {
+		t.Errorf(
+			"%s: passing false as 1st variadic param should set returnOnOutput to false",
+			testName)
+	}
+
+	mux = NewControllerRegister()
+	mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, true, true)
+	if !mux.filters[BeforeRouter][0].resetParams {
+		t.Errorf(
+			"%s: passing true as 2nd variadic param should set resetParams to true",
+			testName)
+	}
+}
+
+// Expectation: the second variadic arg should cause the execution of the filter
+// to preserve the parameters from before its execution.
+func TestParamResetFilter(t *testing.T) {
+	testName := "TestParamResetFilter"
+	route := "/beego/*" // splat
+	path := "/beego/routes/routes"
+
+	mux := NewControllerRegister()
+
+	mux.InsertFilter("*", BeforeExec, beegoResetParams, true, true)
+
+	mux.Get(route, beegoHandleResetParams)
+
+	rw, r := testRequest("GET", path)
+	mux.ServeHTTP(rw, r)
+
+	// The two functions, `beegoResetParams` and `beegoHandleResetParams` add
+	// a response header of `Splat`.  The expectation here is that that Header
+	// value should match what the _request's_ router set, not the filter's.
+
+	headers := rw.HeaderMap
+	if len(headers["Splat"]) != 1 {
+		t.Errorf(
+			"%s: There was an error in the test. Splat param not set in Header",
+			testName)
+	}
+	if headers["Splat"][0] != "routes/routes" {
+		t.Errorf(
+			"%s: expected `:splat` param to be [routes/routes] but it was [%s]",
+			testName, headers["Splat"][0])
+	}
+}
+
+// Execution point: BeforeRouter
+// expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle
+func TestFilterBeforeRouter(t *testing.T) {
+	testName := "TestFilterBeforeRouter"
+	url := "/beforeRouter"
+
+	mux := NewControllerRegister()
+	mux.InsertFilter(url, BeforeRouter, beegoBeforeRouter1)
+
+	mux.Get(url, beegoFilterFunc)
+
+	rw, r := testRequest("GET", url)
+	mux.ServeHTTP(rw, r)
+
+	if strings.Contains(rw.Body.String(), "BeforeRouter1") == false {
+		t.Errorf(testName + " BeforeRouter did not run")
+	}
+	if strings.Contains(rw.Body.String(), "hello") == true {
+		t.Errorf(testName + " BeforeRouter did not return properly")
+	}
+}
+
+// Execution point: BeforeExec
+// expectation: only BeforeExec function is executed, match as router determines route only
+func TestFilterBeforeExec(t *testing.T) {
+	testName := "TestFilterBeforeExec"
+	url := "/beforeExec"
+
+	mux := NewControllerRegister()
+	mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
+	mux.InsertFilter(url, BeforeExec, beegoBeforeExec1)
+
+	mux.Get(url, beegoFilterFunc)
+
+	rw, r := testRequest("GET", url)
+	mux.ServeHTTP(rw, r)
+
+	if strings.Contains(rw.Body.String(), "BeforeExec1") == false {
+		t.Errorf(testName + " BeforeExec did not run")
+	}
+	if strings.Contains(rw.Body.String(), "hello") == true {
+		t.Errorf(testName + " BeforeExec did not return properly")
+	}
+	if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
+		t.Errorf(testName + " BeforeRouter ran in error")
+	}
+}
+
+// Execution point: AfterExec
+// expectation: only AfterExec function is executed, match as router handles
+func TestFilterAfterExec(t *testing.T) {
+	testName := "TestFilterAfterExec"
+	url := "/afterExec"
+
+	mux := NewControllerRegister()
+	mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
+	mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
+	mux.InsertFilter(url, AfterExec, beegoAfterExec1, false)
+
+	mux.Get(url, beegoFilterFunc)
+
+	rw, r := testRequest("GET", url)
+	mux.ServeHTTP(rw, r)
+
+	if strings.Contains(rw.Body.String(), "AfterExec1") == false {
+		t.Errorf(testName + " AfterExec did not run")
+	}
+	if strings.Contains(rw.Body.String(), "hello") == false {
+		t.Errorf(testName + " handler did not run properly")
+	}
+	if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
+		t.Errorf(testName + " BeforeRouter ran in error")
+	}
+	if strings.Contains(rw.Body.String(), "BeforeExec") == true {
+		t.Errorf(testName + " BeforeExec ran in error")
+	}
+}
+
+// Execution point: FinishRouter
+// expectation: only FinishRouter function is executed, match as router handles
+func TestFilterFinishRouter(t *testing.T) {
+	testName := "TestFilterFinishRouter"
+	url := "/finishRouter"
+
+	mux := NewControllerRegister()
+	mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
+	mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
+	mux.InsertFilter(url, AfterExec, beegoFilterNoOutput)
+	mux.InsertFilter(url, FinishRouter, beegoFinishRouter1)
+
+	mux.Get(url, beegoFilterFunc)
+
+	rw, r := testRequest("GET", url)
+	mux.ServeHTTP(rw, r)
+
+	if strings.Contains(rw.Body.String(), "FinishRouter1") == true {
+		t.Errorf(testName + " FinishRouter did not run")
+	}
+	if strings.Contains(rw.Body.String(), "hello") == false {
+		t.Errorf(testName + " handler did not run properly")
+	}
+	if strings.Contains(rw.Body.String(), "AfterExec1") == true {
+		t.Errorf(testName + " AfterExec ran in error")
+	}
+	if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
+		t.Errorf(testName + " BeforeRouter ran in error")
+	}
+	if strings.Contains(rw.Body.String(), "BeforeExec") == true {
+		t.Errorf(testName + " BeforeExec ran in error")
+	}
+}
+
+// Execution point: FinishRouter
+// expectation: only first FinishRouter function is executed, match as router handles
+func TestFilterFinishRouterMultiFirstOnly(t *testing.T) {
+	testName := "TestFilterFinishRouterMultiFirstOnly"
+	url := "/finishRouterMultiFirstOnly"
+
+	mux := NewControllerRegister()
+	mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
+	mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
+
+	mux.Get(url, beegoFilterFunc)
+
+	rw, r := testRequest("GET", url)
+	mux.ServeHTTP(rw, r)
+
+	if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
+		t.Errorf(testName + " FinishRouter1 did not run")
+	}
+	if strings.Contains(rw.Body.String(), "hello") == false {
+		t.Errorf(testName + " handler did not run properly")
+	}
+	// not expected in body
+	if strings.Contains(rw.Body.String(), "FinishRouter2") == true {
+		t.Errorf(testName + " FinishRouter2 did run")
+	}
+}
+
+// Execution point: FinishRouter
+// expectation: both FinishRouter functions execute, match as router handles
+func TestFilterFinishRouterMulti(t *testing.T) {
+	testName := "TestFilterFinishRouterMulti"
+	url := "/finishRouterMulti"
+
+	mux := NewControllerRegister()
+	mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
+	mux.InsertFilter(url, FinishRouter, beegoFinishRouter2, false)
+
+	mux.Get(url, beegoFilterFunc)
+
+	rw, r := testRequest("GET", url)
+	mux.ServeHTTP(rw, r)
+
+	if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
+		t.Errorf(testName + " FinishRouter1 did not run")
+	}
+	if strings.Contains(rw.Body.String(), "hello") == false {
+		t.Errorf(testName + " handler did not run properly")
+	}
+	if strings.Contains(rw.Body.String(), "FinishRouter2") == false {
+		t.Errorf(testName + " FinishRouter2 did not run properly")
+	}
+}
+
+func beegoFilterNoOutput(ctx *context.Context) {
+	return
+}
+func beegoBeforeRouter1(ctx *context.Context) {
+	ctx.WriteString("|BeforeRouter1")
+}
+func beegoBeforeRouter2(ctx *context.Context) {
+	ctx.WriteString("|BeforeRouter2")
+}
+func beegoBeforeExec1(ctx *context.Context) {
+	ctx.WriteString("|BeforeExec1")
+}
+func beegoBeforeExec2(ctx *context.Context) {
+	ctx.WriteString("|BeforeExec2")
+}
+func beegoAfterExec1(ctx *context.Context) {
+	ctx.WriteString("|AfterExec1")
+}
+func beegoAfterExec2(ctx *context.Context) {
+	ctx.WriteString("|AfterExec2")
+}
+func beegoFinishRouter1(ctx *context.Context) {
+	ctx.WriteString("|FinishRouter1")
+}
+func beegoFinishRouter2(ctx *context.Context) {
+	ctx.WriteString("|FinishRouter2")
+}
+func beegoResetParams(ctx *context.Context) {
+	ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat"))
+}
+
+func beegoHandleResetParams(ctx *context.Context) {
+	ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat"))
+}

+ 243 - 0
vendor/github.com/astaxie/beego/session/couchbase/sess_couchbase.go

@@ -0,0 +1,243 @@
+// 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 couchbase for session provider
+//
+// depend on github.com/couchbaselabs/go-couchbasee
+//
+// go install github.com/couchbaselabs/go-couchbase
+//
+// Usage:
+// import(
+//   _ "github.com/astaxie/beego/session/couchbase"
+//   "github.com/astaxie/beego/session"
+// )
+//
+//	func init() {
+//		globalSessions, _ = session.NewManager("couchbase", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"http://host:port/, Pool, Bucket"}``)
+//		go globalSessions.GC()
+//	}
+//
+// more docs: http://beego.me/docs/module/session.md
+package couchbase
+
+import (
+	"net/http"
+	"strings"
+	"sync"
+
+	couchbase "github.com/couchbase/go-couchbase"
+
+	"github.com/astaxie/beego/session"
+)
+
+var couchbpder = &Provider{}
+
+// SessionStore store each session
+type SessionStore struct {
+	b           *couchbase.Bucket
+	sid         string
+	lock        sync.RWMutex
+	values      map[interface{}]interface{}
+	maxlifetime int64
+}
+
+// Provider couchabse provided
+type Provider struct {
+	maxlifetime int64
+	savePath    string
+	pool        string
+	bucket      string
+	b           *couchbase.Bucket
+}
+
+// Set value to couchabse session
+func (cs *SessionStore) Set(key, value interface{}) error {
+	cs.lock.Lock()
+	defer cs.lock.Unlock()
+	cs.values[key] = value
+	return nil
+}
+
+// Get value from couchabse session
+func (cs *SessionStore) Get(key interface{}) interface{} {
+	cs.lock.RLock()
+	defer cs.lock.RUnlock()
+	if v, ok := cs.values[key]; ok {
+		return v
+	}
+	return nil
+}
+
+// Delete value in couchbase session by given key
+func (cs *SessionStore) Delete(key interface{}) error {
+	cs.lock.Lock()
+	defer cs.lock.Unlock()
+	delete(cs.values, key)
+	return nil
+}
+
+// Flush Clean all values in couchbase session
+func (cs *SessionStore) Flush() error {
+	cs.lock.Lock()
+	defer cs.lock.Unlock()
+	cs.values = make(map[interface{}]interface{})
+	return nil
+}
+
+// SessionID Get couchbase session store id
+func (cs *SessionStore) SessionID() string {
+	return cs.sid
+}
+
+// SessionRelease Write couchbase session with Gob string
+func (cs *SessionStore) SessionRelease(w http.ResponseWriter) {
+	defer cs.b.Close()
+
+	bo, err := session.EncodeGob(cs.values)
+	if err != nil {
+		return
+	}
+
+	cs.b.Set(cs.sid, int(cs.maxlifetime), bo)
+}
+
+func (cp *Provider) getBucket() *couchbase.Bucket {
+	c, err := couchbase.Connect(cp.savePath)
+	if err != nil {
+		return nil
+	}
+
+	pool, err := c.GetPool(cp.pool)
+	if err != nil {
+		return nil
+	}
+
+	bucket, err := pool.GetBucket(cp.bucket)
+	if err != nil {
+		return nil
+	}
+
+	return bucket
+}
+
+// SessionInit init couchbase session
+// savepath like couchbase server REST/JSON URL
+// e.g. http://host:port/, Pool, Bucket
+func (cp *Provider) SessionInit(maxlifetime int64, savePath string) error {
+	cp.maxlifetime = maxlifetime
+	configs := strings.Split(savePath, ",")
+	if len(configs) > 0 {
+		cp.savePath = configs[0]
+	}
+	if len(configs) > 1 {
+		cp.pool = configs[1]
+	}
+	if len(configs) > 2 {
+		cp.bucket = configs[2]
+	}
+
+	return nil
+}
+
+// SessionRead read couchbase session by sid
+func (cp *Provider) SessionRead(sid string) (session.Store, error) {
+	cp.b = cp.getBucket()
+
+	var doc []byte
+
+	err := cp.b.Get(sid, &doc)
+	var kv map[interface{}]interface{}
+	if doc == nil {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob(doc)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	cs := &SessionStore{b: cp.b, sid: sid, values: kv, maxlifetime: cp.maxlifetime}
+	return cs, nil
+}
+
+// SessionExist Check couchbase session exist.
+// it checkes sid exist or not.
+func (cp *Provider) SessionExist(sid string) bool {
+	cp.b = cp.getBucket()
+	defer cp.b.Close()
+
+	var doc []byte
+
+	if err := cp.b.Get(sid, &doc); err != nil || doc == nil {
+		return false
+	}
+	return true
+}
+
+// SessionRegenerate remove oldsid and use sid to generate new session
+func (cp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+	cp.b = cp.getBucket()
+
+	var doc []byte
+	if err := cp.b.Get(oldsid, &doc); err != nil || doc == nil {
+		cp.b.Set(sid, int(cp.maxlifetime), "")
+	} else {
+		err := cp.b.Delete(oldsid)
+		if err != nil {
+			return nil, err
+		}
+		_, _ = cp.b.Add(sid, int(cp.maxlifetime), doc)
+	}
+
+	err := cp.b.Get(sid, &doc)
+	if err != nil {
+		return nil, err
+	}
+	var kv map[interface{}]interface{}
+	if doc == nil {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob(doc)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	cs := &SessionStore{b: cp.b, sid: sid, values: kv, maxlifetime: cp.maxlifetime}
+	return cs, nil
+}
+
+// SessionDestroy Remove bucket in this couchbase
+func (cp *Provider) SessionDestroy(sid string) error {
+	cp.b = cp.getBucket()
+	defer cp.b.Close()
+
+	cp.b.Delete(sid)
+	return nil
+}
+
+// SessionGC Recycle
+func (cp *Provider) SessionGC() {
+	return
+}
+
+// SessionAll return all active session
+func (cp *Provider) SessionAll() int {
+	return 0
+}
+
+func init() {
+	session.Register("couchbase", couchbpder)
+}

+ 179 - 0
vendor/github.com/astaxie/beego/session/ledis/ledis_session.go

@@ -0,0 +1,179 @@
+// Package ledis provide session Provider
+package ledis
+
+import (
+	"net/http"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/astaxie/beego/session"
+	"github.com/siddontang/ledisdb/config"
+	"github.com/siddontang/ledisdb/ledis"
+)
+
+var ledispder = &Provider{}
+var c *ledis.DB
+
+// SessionStore ledis session store
+type SessionStore struct {
+	sid         string
+	lock        sync.RWMutex
+	values      map[interface{}]interface{}
+	maxlifetime int64
+}
+
+// Set value in ledis session
+func (ls *SessionStore) Set(key, value interface{}) error {
+	ls.lock.Lock()
+	defer ls.lock.Unlock()
+	ls.values[key] = value
+	return nil
+}
+
+// Get value in ledis session
+func (ls *SessionStore) Get(key interface{}) interface{} {
+	ls.lock.RLock()
+	defer ls.lock.RUnlock()
+	if v, ok := ls.values[key]; ok {
+		return v
+	}
+	return nil
+}
+
+// Delete value in ledis session
+func (ls *SessionStore) Delete(key interface{}) error {
+	ls.lock.Lock()
+	defer ls.lock.Unlock()
+	delete(ls.values, key)
+	return nil
+}
+
+// Flush clear all values in ledis session
+func (ls *SessionStore) Flush() error {
+	ls.lock.Lock()
+	defer ls.lock.Unlock()
+	ls.values = make(map[interface{}]interface{})
+	return nil
+}
+
+// SessionID get ledis session id
+func (ls *SessionStore) SessionID() string {
+	return ls.sid
+}
+
+// SessionRelease save session values to ledis
+func (ls *SessionStore) SessionRelease(w http.ResponseWriter) {
+	b, err := session.EncodeGob(ls.values)
+	if err != nil {
+		return
+	}
+	c.Set([]byte(ls.sid), b)
+	c.Expire([]byte(ls.sid), ls.maxlifetime)
+}
+
+// Provider ledis session provider
+type Provider struct {
+	maxlifetime int64
+	savePath    string
+	db          int
+}
+
+// SessionInit init ledis session
+// savepath like ledis server saveDataPath,pool size
+// e.g. 127.0.0.1:6379,100,astaxie
+func (lp *Provider) SessionInit(maxlifetime int64, savePath string) error {
+	var err error
+	lp.maxlifetime = maxlifetime
+	configs := strings.Split(savePath, ",")
+	if len(configs) == 1 {
+		lp.savePath = configs[0]
+	} else if len(configs) == 2 {
+		lp.savePath = configs[0]
+		lp.db, err = strconv.Atoi(configs[1])
+		if err != nil {
+			return err
+		}
+	}
+	cfg := new(config.Config)
+	cfg.DataDir = lp.savePath
+	nowLedis, err := ledis.Open(cfg)
+	c, err = nowLedis.Select(lp.db)
+	if err != nil {
+		println(err)
+		return nil
+	}
+	return nil
+}
+
+// SessionRead read ledis session by sid
+func (lp *Provider) SessionRead(sid string) (session.Store, error) {
+	kvs, err := c.Get([]byte(sid))
+	var kv map[interface{}]interface{}
+	if len(kvs) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob(kvs)
+		if err != nil {
+			return nil, err
+		}
+	}
+	ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
+	return ls, nil
+}
+
+// SessionExist check ledis session exist by sid
+func (lp *Provider) SessionExist(sid string) bool {
+	count, _ := c.Exists([]byte(sid))
+	if count == 0 {
+		return false
+	}
+	return true
+}
+
+// SessionRegenerate generate new sid for ledis session
+func (lp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+	count, _ := c.Exists([]byte(sid))
+	if count == 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([]byte(sid), []byte(""))
+		c.Expire([]byte(sid), lp.maxlifetime)
+	} else {
+		data, _ := c.Get([]byte(oldsid))
+		c.Set([]byte(sid), data)
+		c.Expire([]byte(sid), lp.maxlifetime)
+	}
+	kvs, err := c.Get([]byte(sid))
+	var kv map[interface{}]interface{}
+	if len(kvs) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob([]byte(kvs))
+		if err != nil {
+			return nil, err
+		}
+	}
+	ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
+	return ls, nil
+}
+
+// SessionDestroy delete ledis session by id
+func (lp *Provider) SessionDestroy(sid string) error {
+	c.Del([]byte(sid))
+	return nil
+}
+
+// SessionGC Impelment method, no used.
+func (lp *Provider) SessionGC() {
+	return
+}
+
+// SessionAll return all active session
+func (lp *Provider) SessionAll() int {
+	return 0
+}
+func init() {
+	session.Register("ledis", ledispder)
+}

+ 232 - 0
vendor/github.com/astaxie/beego/session/memcache/sess_memcache.go

@@ -0,0 +1,232 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package memcache for session provider
+//
+// depend on github.com/bradfitz/gomemcache/memcache
+//
+// go install github.com/bradfitz/gomemcache/memcache
+//
+// Usage:
+// import(
+//   _ "github.com/astaxie/beego/session/memcache"
+//   "github.com/astaxie/beego/session"
+// )
+//
+//	func init() {
+//		globalSessions, _ = session.NewManager("memcache", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:11211"}``)
+//		go globalSessions.GC()
+//	}
+//
+// more docs: http://beego.me/docs/module/session.md
+package memcache
+
+import (
+	"net/http"
+	"strings"
+	"sync"
+
+	"github.com/astaxie/beego/session"
+
+	"github.com/bradfitz/gomemcache/memcache"
+)
+
+var mempder = &MemProvider{}
+var client *memcache.Client
+
+// SessionStore memcache session store
+type SessionStore struct {
+	sid         string
+	lock        sync.RWMutex
+	values      map[interface{}]interface{}
+	maxlifetime int64
+}
+
+// Set value in memcache 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 memcache 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 memcache 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 memcache session
+func (rs *SessionStore) Flush() error {
+	rs.lock.Lock()
+	defer rs.lock.Unlock()
+	rs.values = make(map[interface{}]interface{})
+	return nil
+}
+
+// SessionID get memcache session id
+func (rs *SessionStore) SessionID() string {
+	return rs.sid
+}
+
+// SessionRelease save session values to memcache
+func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
+	b, err := session.EncodeGob(rs.values)
+	if err != nil {
+		return
+	}
+	item := memcache.Item{Key: rs.sid, Value: b, Expiration: int32(rs.maxlifetime)}
+	client.Set(&item)
+}
+
+// MemProvider memcache session provider
+type MemProvider struct {
+	maxlifetime int64
+	conninfo    []string
+	poolsize    int
+	password    string
+}
+
+// SessionInit init memcache session
+// savepath like
+// e.g. 127.0.0.1:9090
+func (rp *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
+	rp.maxlifetime = maxlifetime
+	rp.conninfo = strings.Split(savePath, ";")
+	client = memcache.New(rp.conninfo...)
+	return nil
+}
+
+// SessionRead read memcache session by sid
+func (rp *MemProvider) SessionRead(sid string) (session.Store, error) {
+	if client == nil {
+		if err := rp.connectInit(); err != nil {
+			return nil, err
+		}
+	}
+	item, err := client.Get(sid)
+	if err != nil && err == memcache.ErrCacheMiss {
+		rs := &SessionStore{sid: sid, values: make(map[interface{}]interface{}), maxlifetime: rp.maxlifetime}
+		return rs, nil
+	}
+	var kv map[interface{}]interface{}
+	if len(item.Value) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob(item.Value)
+		if err != nil {
+			return nil, err
+		}
+	}
+	rs := &SessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+	return rs, nil
+}
+
+// SessionExist check memcache session exist by sid
+func (rp *MemProvider) SessionExist(sid string) bool {
+	if client == nil {
+		if err := rp.connectInit(); err != nil {
+			return false
+		}
+	}
+	if item, err := client.Get(sid); err != nil || len(item.Value) == 0 {
+		return false
+	}
+	return true
+}
+
+// SessionRegenerate generate new sid for memcache session
+func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+	if client == nil {
+		if err := rp.connectInit(); err != nil {
+			return nil, err
+		}
+	}
+	var contain []byte
+	if item, err := client.Get(sid); err != nil || len(item.Value) == 0 {
+		// oldsid doesn't exists, set the new sid directly
+		// ignore error here, since if it return error
+		// the existed value will be 0
+		item.Key = sid
+		item.Value = []byte("")
+		item.Expiration = int32(rp.maxlifetime)
+		client.Set(item)
+	} else {
+		client.Delete(oldsid)
+		item.Key = sid
+		item.Expiration = int32(rp.maxlifetime)
+		client.Set(item)
+		contain = item.Value
+	}
+
+	var kv map[interface{}]interface{}
+	if len(contain) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		var err error
+		kv, err = session.DecodeGob(contain)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	rs := &SessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+	return rs, nil
+}
+
+// SessionDestroy delete memcache session by id
+func (rp *MemProvider) SessionDestroy(sid string) error {
+	if client == nil {
+		if err := rp.connectInit(); err != nil {
+			return err
+		}
+	}
+
+	err := client.Delete(sid)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (rp *MemProvider) connectInit() error {
+	client = memcache.New(rp.conninfo...)
+	return nil
+}
+
+// SessionGC Impelment method, no used.
+func (rp *MemProvider) SessionGC() {
+	return
+}
+
+// SessionAll return all activeSession
+func (rp *MemProvider) SessionAll() int {
+	return 0
+}
+
+func init() {
+	session.Register("memcache", mempder)
+}

+ 234 - 0
vendor/github.com/astaxie/beego/session/mysql/sess_mysql.go

@@ -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 mysql for session provider
+//
+// depends on github.com/go-sql-driver/mysql:
+//
+// go install github.com/go-sql-driver/mysql
+//
+// mysql session support need create table as sql:
+//	CREATE TABLE `session` (
+//	`session_key` char(64) NOT NULL,
+//	`session_data` blob,
+//	`session_expiry` int(11) unsigned NOT NULL,
+//	PRIMARY KEY (`session_key`)
+//	) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+//
+// Usage:
+// import(
+//   _ "github.com/astaxie/beego/session/mysql"
+//   "github.com/astaxie/beego/session"
+// )
+//
+//	func init() {
+//		globalSessions, _ = session.NewManager("mysql", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]"}``)
+//		go globalSessions.GC()
+//	}
+//
+// more docs: http://beego.me/docs/module/session.md
+package mysql
+
+import (
+	"database/sql"
+	"net/http"
+	"sync"
+	"time"
+
+	"github.com/astaxie/beego/session"
+	// import mysql driver
+	_ "github.com/go-sql-driver/mysql"
+)
+
+var (
+	// TableName store the session in MySQL
+	TableName = "session"
+	mysqlpder = &Provider{}
+)
+
+// SessionStore mysql session store
+type SessionStore struct {
+	c      *sql.DB
+	sid    string
+	lock   sync.RWMutex
+	values map[interface{}]interface{}
+}
+
+// Set value in mysql session.
+// it is temp value in map.
+func (st *SessionStore) Set(key, value interface{}) error {
+	st.lock.Lock()
+	defer st.lock.Unlock()
+	st.values[key] = value
+	return nil
+}
+
+// Get value from mysql session
+func (st *SessionStore) Get(key interface{}) interface{} {
+	st.lock.RLock()
+	defer st.lock.RUnlock()
+	if v, ok := st.values[key]; ok {
+		return v
+	}
+	return nil
+}
+
+// Delete value in mysql session
+func (st *SessionStore) Delete(key interface{}) error {
+	st.lock.Lock()
+	defer st.lock.Unlock()
+	delete(st.values, key)
+	return nil
+}
+
+// Flush clear all values in mysql session
+func (st *SessionStore) Flush() error {
+	st.lock.Lock()
+	defer st.lock.Unlock()
+	st.values = make(map[interface{}]interface{})
+	return nil
+}
+
+// SessionID get session id of this mysql session store
+func (st *SessionStore) SessionID() string {
+	return st.sid
+}
+
+// SessionRelease save mysql session values to database.
+// must call this method to save values to database.
+func (st *SessionStore) SessionRelease(w http.ResponseWriter) {
+	defer st.c.Close()
+	b, err := session.EncodeGob(st.values)
+	if err != nil {
+		return
+	}
+	st.c.Exec("UPDATE "+TableName+" set `session_data`=?, `session_expiry`=? where session_key=?",
+		b, time.Now().Unix(), st.sid)
+}
+
+// Provider mysql session provider
+type Provider struct {
+	maxlifetime int64
+	savePath    string
+}
+
+// connect to mysql
+func (mp *Provider) connectInit() *sql.DB {
+	db, e := sql.Open("mysql", mp.savePath)
+	if e != nil {
+		return nil
+	}
+	return db
+}
+
+// SessionInit init mysql session.
+// savepath is the connection string of mysql.
+func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error {
+	mp.maxlifetime = maxlifetime
+	mp.savePath = savePath
+	return nil
+}
+
+// SessionRead get mysql session by sid
+func (mp *Provider) SessionRead(sid string) (session.Store, error) {
+	c := mp.connectInit()
+	defer c.Close()
+	row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid)
+	var sessiondata []byte
+	err := row.Scan(&sessiondata)
+	if err == sql.ErrNoRows {
+		c.Exec("insert into "+TableName+"(`session_key`,`session_data`,`session_expiry`) values(?,?,?)",
+			sid, "", time.Now().Unix())
+	}
+	var kv map[interface{}]interface{}
+	if len(sessiondata) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob(sessiondata)
+		if err != nil {
+			return nil, err
+		}
+	}
+	rs := &SessionStore{c: c, sid: sid, values: kv}
+	return rs, nil
+}
+
+// SessionExist check mysql session exist
+func (mp *Provider) SessionExist(sid string) bool {
+	c := mp.connectInit()
+	defer c.Close()
+	row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid)
+	var sessiondata []byte
+	err := row.Scan(&sessiondata)
+	if err == sql.ErrNoRows {
+		return false
+	}
+	return true
+}
+
+// SessionRegenerate generate new sid for mysql session
+func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+	c := mp.connectInit()
+	defer c.Close()
+	row := c.QueryRow("select session_data from "+TableName+" where session_key=?", oldsid)
+	var sessiondata []byte
+	err := row.Scan(&sessiondata)
+	if err == sql.ErrNoRows {
+		c.Exec("insert into "+TableName+"(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", oldsid, "", time.Now().Unix())
+	}
+	c.Exec("update "+TableName+" set `session_key`=? where session_key=?", sid, oldsid)
+	var kv map[interface{}]interface{}
+	if len(sessiondata) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob(sessiondata)
+		if err != nil {
+			return nil, err
+		}
+	}
+	rs := &SessionStore{c: c, sid: sid, values: kv}
+	return rs, nil
+}
+
+// SessionDestroy delete mysql session by sid
+func (mp *Provider) SessionDestroy(sid string) error {
+	c := mp.connectInit()
+	c.Exec("DELETE FROM "+TableName+" where session_key=?", sid)
+	c.Close()
+	return nil
+}
+
+// SessionGC delete expired values in mysql session
+func (mp *Provider) SessionGC() {
+	c := mp.connectInit()
+	c.Exec("DELETE from "+TableName+" where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
+	c.Close()
+	return
+}
+
+// SessionAll count values in mysql session
+func (mp *Provider) SessionAll() int {
+	c := mp.connectInit()
+	defer c.Close()
+	var total int
+	err := c.QueryRow("SELECT count(*) as num from " + TableName).Scan(&total)
+	if err != nil {
+		return 0
+	}
+	return total
+}
+
+func init() {
+	session.Register("mysql", mysqlpder)
+}

+ 248 - 0
vendor/github.com/astaxie/beego/session/postgres/sess_postgresql.go

@@ -0,0 +1,248 @@
+// 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 postgres for session provider
+//
+// depends on github.com/lib/pq:
+//
+// go install github.com/lib/pq
+//
+//
+// needs this table in your database:
+//
+// CREATE TABLE session (
+// session_key	char(64) NOT NULL,
+// session_data	bytea,
+// session_expiry	timestamp NOT NULL,
+// CONSTRAINT session_key PRIMARY KEY(session_key)
+// );
+//
+// will be activated with these settings in app.conf:
+//
+// SessionOn = true
+// SessionProvider = postgresql
+// SessionSavePath = "user=a password=b dbname=c sslmode=disable"
+// SessionName = session
+//
+//
+// Usage:
+// import(
+//   _ "github.com/astaxie/beego/session/postgresql"
+//   "github.com/astaxie/beego/session"
+// )
+//
+//	func init() {
+//		globalSessions, _ = session.NewManager("postgresql", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"user=pqgotest dbname=pqgotest sslmode=verify-full"}``)
+//		go globalSessions.GC()
+//	}
+//
+// more docs: http://beego.me/docs/module/session.md
+package postgres
+
+import (
+	"database/sql"
+	"net/http"
+	"sync"
+	"time"
+
+	"github.com/astaxie/beego/session"
+	// import postgresql Driver
+	_ "github.com/lib/pq"
+)
+
+var postgresqlpder = &Provider{}
+
+// SessionStore postgresql session store
+type SessionStore struct {
+	c      *sql.DB
+	sid    string
+	lock   sync.RWMutex
+	values map[interface{}]interface{}
+}
+
+// Set value in postgresql session.
+// it is temp value in map.
+func (st *SessionStore) Set(key, value interface{}) error {
+	st.lock.Lock()
+	defer st.lock.Unlock()
+	st.values[key] = value
+	return nil
+}
+
+// Get value from postgresql session
+func (st *SessionStore) Get(key interface{}) interface{} {
+	st.lock.RLock()
+	defer st.lock.RUnlock()
+	if v, ok := st.values[key]; ok {
+		return v
+	}
+	return nil
+}
+
+// Delete value in postgresql session
+func (st *SessionStore) Delete(key interface{}) error {
+	st.lock.Lock()
+	defer st.lock.Unlock()
+	delete(st.values, key)
+	return nil
+}
+
+// Flush clear all values in postgresql session
+func (st *SessionStore) Flush() error {
+	st.lock.Lock()
+	defer st.lock.Unlock()
+	st.values = make(map[interface{}]interface{})
+	return nil
+}
+
+// SessionID get session id of this postgresql session store
+func (st *SessionStore) SessionID() string {
+	return st.sid
+}
+
+// SessionRelease save postgresql session values to database.
+// must call this method to save values to database.
+func (st *SessionStore) SessionRelease(w http.ResponseWriter) {
+	defer st.c.Close()
+	b, err := session.EncodeGob(st.values)
+	if err != nil {
+		return
+	}
+	st.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3",
+		b, time.Now().Format(time.RFC3339), st.sid)
+
+}
+
+// Provider postgresql session provider
+type Provider struct {
+	maxlifetime int64
+	savePath    string
+}
+
+// connect to postgresql
+func (mp *Provider) connectInit() *sql.DB {
+	db, e := sql.Open("postgres", mp.savePath)
+	if e != nil {
+		return nil
+	}
+	return db
+}
+
+// SessionInit init postgresql session.
+// savepath is the connection string of postgresql.
+func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error {
+	mp.maxlifetime = maxlifetime
+	mp.savePath = savePath
+	return nil
+}
+
+// SessionRead get postgresql session by sid
+func (mp *Provider) SessionRead(sid string) (session.Store, error) {
+	c := mp.connectInit()
+	row := c.QueryRow("select session_data from session where session_key=$1", sid)
+	var sessiondata []byte
+	err := row.Scan(&sessiondata)
+	if err == sql.ErrNoRows {
+		_, err = c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)",
+			sid, "", time.Now().Format(time.RFC3339))
+
+		if err != nil {
+			return nil, err
+		}
+	} else if err != nil {
+		return nil, err
+	}
+
+	var kv map[interface{}]interface{}
+	if len(sessiondata) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob(sessiondata)
+		if err != nil {
+			return nil, err
+		}
+	}
+	rs := &SessionStore{c: c, sid: sid, values: kv}
+	return rs, nil
+}
+
+// SessionExist check postgresql session exist
+func (mp *Provider) SessionExist(sid string) bool {
+	c := mp.connectInit()
+	defer c.Close()
+	row := c.QueryRow("select session_data from session where session_key=$1", sid)
+	var sessiondata []byte
+	err := row.Scan(&sessiondata)
+
+	if err == sql.ErrNoRows {
+		return false
+	}
+	return true
+}
+
+// SessionRegenerate generate new sid for postgresql session
+func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+	c := mp.connectInit()
+	row := c.QueryRow("select session_data from session where session_key=$1", oldsid)
+	var sessiondata []byte
+	err := row.Scan(&sessiondata)
+	if err == sql.ErrNoRows {
+		c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)",
+			oldsid, "", time.Now().Format(time.RFC3339))
+	}
+	c.Exec("update session set session_key=$1 where session_key=$2", sid, oldsid)
+	var kv map[interface{}]interface{}
+	if len(sessiondata) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob(sessiondata)
+		if err != nil {
+			return nil, err
+		}
+	}
+	rs := &SessionStore{c: c, sid: sid, values: kv}
+	return rs, nil
+}
+
+// SessionDestroy delete postgresql session by sid
+func (mp *Provider) SessionDestroy(sid string) error {
+	c := mp.connectInit()
+	c.Exec("DELETE FROM session where session_key=$1", sid)
+	c.Close()
+	return nil
+}
+
+// SessionGC delete expired values in postgresql session
+func (mp *Provider) SessionGC() {
+	c := mp.connectInit()
+	c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime)
+	c.Close()
+	return
+}
+
+// SessionAll count values in postgresql session
+func (mp *Provider) SessionAll() int {
+	c := mp.connectInit()
+	defer c.Close()
+	var total int
+	err := c.QueryRow("SELECT count(*) as num from session").Scan(&total)
+	if err != nil {
+		return 0
+	}
+	return total
+}
+
+func init() {
+	session.Register("postgresql", postgresqlpder)
+}

+ 256 - 0
vendor/github.com/astaxie/beego/session/redis/sess_redis.go

@@ -0,0 +1,256 @@
+// 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/garyburd/redigo/redis
+//
+// go install github.com/garyburd/redigo/redis
+//
+// Usage:
+// import(
+//   _ "github.com/astaxie/beego/session/redis"
+//   "github.com/astaxie/beego/session"
+// )
+//
+//	func init() {
+//		globalSessions, _ = session.NewManager("redis", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070"}``)
+//		go globalSessions.GC()
+//	}
+//
+// more docs: http://beego.me/docs/module/session.md
+package redis
+
+import (
+	"net/http"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/astaxie/beego/session"
+
+	"github.com/garyburd/redigo/redis"
+)
+
+var redispder = &Provider{}
+
+// MaxPoolSize redis max pool size
+var MaxPoolSize = 100
+
+// SessionStore redis session store
+type SessionStore struct {
+	p           *redis.Pool
+	sid         string
+	lock        sync.RWMutex
+	values      map[interface{}]interface{}
+	maxlifetime int64
+}
+
+// Set value in redis 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 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 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 session
+func (rs *SessionStore) Flush() error {
+	rs.lock.Lock()
+	defer rs.lock.Unlock()
+	rs.values = make(map[interface{}]interface{})
+	return nil
+}
+
+// SessionID get redis session id
+func (rs *SessionStore) SessionID() string {
+	return rs.sid
+}
+
+// SessionRelease save session values to redis
+func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
+	b, err := session.EncodeGob(rs.values)
+	if err != nil {
+		return
+	}
+	c := rs.p.Get()
+	defer c.Close()
+	c.Do("SETEX", rs.sid, rs.maxlifetime, string(b))
+}
+
+// Provider redis session provider
+type Provider struct {
+	maxlifetime int64
+	savePath    string
+	poolsize    int
+	password    string
+	dbNum       int
+	poollist    *redis.Pool
+}
+
+// SessionInit init redis session
+// savepath like redis server addr,pool size,password,dbnum
+// e.g. 127.0.0.1:6379,100,astaxie,0
+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 = MaxPoolSize
+		} else {
+			rp.poolsize = poolsize
+		}
+	} else {
+		rp.poolsize = MaxPoolSize
+	}
+	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
+	}
+	rp.poollist = redis.NewPool(func() (redis.Conn, error) {
+		c, err := redis.Dial("tcp", rp.savePath)
+		if err != nil {
+			return nil, err
+		}
+		if rp.password != "" {
+			if _, err := c.Do("AUTH", rp.password); err != nil {
+				c.Close()
+				return nil, err
+			}
+		}
+		_, err = c.Do("SELECT", rp.dbNum)
+		if err != nil {
+			c.Close()
+			return nil, err
+		}
+		return c, err
+	}, rp.poolsize)
+
+	return rp.poollist.Get().Err()
+}
+
+// SessionRead read redis session by sid
+func (rp *Provider) SessionRead(sid string) (session.Store, error) {
+	c := rp.poollist.Get()
+	defer c.Close()
+
+	kvs, err := redis.String(c.Do("GET", sid))
+	var kv map[interface{}]interface{}
+	if len(kvs) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob([]byte(kvs))
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	rs := &SessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+	return rs, nil
+}
+
+// SessionExist check redis session exist by sid
+func (rp *Provider) SessionExist(sid string) bool {
+	c := rp.poollist.Get()
+	defer c.Close()
+
+	if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 {
+		return false
+	}
+	return true
+}
+
+// SessionRegenerate generate new sid for redis session
+func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+	c := rp.poollist.Get()
+	defer c.Close()
+
+	if existed, _ := redis.Int(c.Do("EXISTS", oldsid)); 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.Do("SET", sid, "", "EX", rp.maxlifetime)
+	} else {
+		c.Do("RENAME", oldsid, sid)
+		c.Do("EXPIRE", sid, rp.maxlifetime)
+	}
+
+	kvs, err := redis.String(c.Do("GET", sid))
+	var kv map[interface{}]interface{}
+	if len(kvs) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob([]byte(kvs))
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	rs := &SessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+	return rs, nil
+}
+
+// SessionDestroy delete redis session by id
+func (rp *Provider) SessionDestroy(sid string) error {
+	c := rp.poollist.Get()
+	defer c.Close()
+
+	c.Do("DEL", sid)
+	return nil
+}
+
+// SessionGC Impelment method, no used.
+func (rp *Provider) SessionGC() {
+	return
+}
+
+// SessionAll return all activeSession
+func (rp *Provider) SessionAll() int {
+	return 0
+}
+
+func init() {
+	session.Register("redis", redispder)
+}

+ 105 - 0
vendor/github.com/astaxie/beego/session/sess_cookie_test.go

@@ -0,0 +1,105 @@
+// 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 session
+
+import (
+	"encoding/json"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+func TestCookie(t *testing.T) {
+	config := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
+	conf := new(ManagerConfig)
+	if err := json.Unmarshal([]byte(config), conf); err != nil {
+		t.Fatal("json decode error", err)
+	}
+	globalSessions, err := NewManager("cookie", conf)
+	if err != nil {
+		t.Fatal("init cookie session err", err)
+	}
+	r, _ := http.NewRequest("GET", "/", nil)
+	w := httptest.NewRecorder()
+	sess, err := globalSessions.SessionStart(w, r)
+	if err != nil {
+		t.Fatal("set error,", err)
+	}
+	err = sess.Set("username", "astaxie")
+	if err != nil {
+		t.Fatal("set error,", err)
+	}
+	if username := sess.Get("username"); username != "astaxie" {
+		t.Fatal("get username error")
+	}
+	sess.SessionRelease(w)
+	if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" {
+		t.Fatal("setcookie error")
+	} else {
+		parts := strings.Split(strings.TrimSpace(cookiestr), ";")
+		for k, v := range parts {
+			nameval := strings.Split(v, "=")
+			if k == 0 && nameval[0] != "gosessionid" {
+				t.Fatal("error")
+			}
+		}
+	}
+}
+
+func TestDestorySessionCookie(t *testing.T) {
+	config := `{"cookieName":"gosessionid","enableSetCookie":true,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
+	conf := new(ManagerConfig)
+	if err := json.Unmarshal([]byte(config), conf); err != nil {
+		t.Fatal("json decode error", err)
+	}
+	globalSessions, err := NewManager("cookie", conf)
+	if err != nil {
+		t.Fatal("init cookie session err", err)
+	}
+
+	r, _ := http.NewRequest("GET", "/", nil)
+	w := httptest.NewRecorder()
+	session, err := globalSessions.SessionStart(w, r)
+	if err != nil {
+		t.Fatal("session start err,", err)
+	}
+
+	// request again ,will get same sesssion id .
+	r1, _ := http.NewRequest("GET", "/", nil)
+	r1.Header.Set("Cookie", w.Header().Get("Set-Cookie"))
+	w = httptest.NewRecorder()
+	newSession, err := globalSessions.SessionStart(w, r1)
+	if err != nil {
+		t.Fatal("session start err,", err)
+	}
+	if newSession.SessionID() != session.SessionID() {
+		t.Fatal("get cookie session id is not the same again.")
+	}
+
+	// After destroy session , will get a new session id .
+	globalSessions.SessionDestroy(w, r1)
+	r2, _ := http.NewRequest("GET", "/", nil)
+	r2.Header.Set("Cookie", w.Header().Get("Set-Cookie"))
+
+	w = httptest.NewRecorder()
+	newSession, err = globalSessions.SessionStart(w, r2)
+	if err != nil {
+		t.Fatal("session start error")
+	}
+	if newSession.SessionID() == session.SessionID() {
+		t.Fatal("after destroy session and reqeust again ,get cookie session id is same.")
+	}
+}

+ 58 - 0
vendor/github.com/astaxie/beego/session/sess_mem_test.go

@@ -0,0 +1,58 @@
+// 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 session
+
+import (
+	"encoding/json"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+func TestMem(t *testing.T) {
+	config := `{"cookieName":"gosessionid","gclifetime":10, "enableSetCookie":true}`
+	conf := new(ManagerConfig)
+	if err := json.Unmarshal([]byte(config), conf); err != nil {
+		t.Fatal("json decode error", err)
+	}
+	globalSessions, _ := NewManager("memory", conf)
+	go globalSessions.GC()
+	r, _ := http.NewRequest("GET", "/", nil)
+	w := httptest.NewRecorder()
+	sess, err := globalSessions.SessionStart(w, r)
+	if err != nil {
+		t.Fatal("set error,", err)
+	}
+	defer sess.SessionRelease(w)
+	err = sess.Set("username", "astaxie")
+	if err != nil {
+		t.Fatal("set error,", err)
+	}
+	if username := sess.Get("username"); username != "astaxie" {
+		t.Fatal("get username error")
+	}
+	if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" {
+		t.Fatal("setcookie error")
+	} else {
+		parts := strings.Split(strings.TrimSpace(cookiestr), ";")
+		for k, v := range parts {
+			nameval := strings.Split(v, "=")
+			if k == 0 && nameval[0] != "gosessionid" {
+				t.Fatal("error")
+			}
+		}
+	}
+}

+ 132 - 0
vendor/github.com/astaxie/beego/session/sess_test.go

@@ -0,0 +1,132 @@
+// 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 session
+
+import (
+	"crypto/aes"
+	"encoding/json"
+	"testing"
+)
+
+func Test_gob(t *testing.T) {
+	a := make(map[interface{}]interface{})
+	a["username"] = "astaxie"
+	a[12] = 234
+	a["user"] = User{"asta", "xie"}
+	b, err := EncodeGob(a)
+	if err != nil {
+		t.Error(err)
+	}
+	c, err := DecodeGob(b)
+	if err != nil {
+		t.Error(err)
+	}
+	if len(c) == 0 {
+		t.Error("decodeGob empty")
+	}
+	if c["username"] != "astaxie" {
+		t.Error("decode string error")
+	}
+	if c[12] != 234 {
+		t.Error("decode int error")
+	}
+	if c["user"].(User).Username != "asta" {
+		t.Error("decode struct error")
+	}
+}
+
+type User struct {
+	Username string
+	NickName string
+}
+
+func TestGenerate(t *testing.T) {
+	str := generateRandomKey(20)
+	if len(str) != 20 {
+		t.Fatal("generate length is not equal to 20")
+	}
+}
+
+func TestCookieEncodeDecode(t *testing.T) {
+	hashKey := "testhashKey"
+	blockkey := generateRandomKey(16)
+	block, err := aes.NewCipher(blockkey)
+	if err != nil {
+		t.Fatal("NewCipher:", err)
+	}
+	securityName := string(generateRandomKey(20))
+	val := make(map[interface{}]interface{})
+	val["name"] = "astaxie"
+	val["gender"] = "male"
+	str, err := encodeCookie(block, hashKey, securityName, val)
+	if err != nil {
+		t.Fatal("encodeCookie:", err)
+	}
+	dst := make(map[interface{}]interface{})
+	dst, err = decodeCookie(block, hashKey, securityName, str, 3600)
+	if err != nil {
+		t.Fatal("decodeCookie", err)
+	}
+	if dst["name"] != "astaxie" {
+		t.Fatal("dst get map error")
+	}
+	if dst["gender"] != "male" {
+		t.Fatal("dst get map error")
+	}
+}
+
+func TestParseConfig(t *testing.T) {
+	s := `{"cookieName":"gosessionid","gclifetime":3600}`
+	cf := new(ManagerConfig)
+	cf.EnableSetCookie = true
+	err := json.Unmarshal([]byte(s), cf)
+	if err != nil {
+		t.Fatal("parse json error,", err)
+	}
+	if cf.CookieName != "gosessionid" {
+		t.Fatal("parseconfig get cookiename error")
+	}
+	if cf.Gclifetime != 3600 {
+		t.Fatal("parseconfig get gclifetime error")
+	}
+
+	cc := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
+	cf2 := new(ManagerConfig)
+	cf2.EnableSetCookie = true
+	err = json.Unmarshal([]byte(cc), cf2)
+	if err != nil {
+		t.Fatal("parse json error,", err)
+	}
+	if cf2.CookieName != "gosessionid" {
+		t.Fatal("parseconfig get cookiename error")
+	}
+	if cf2.Gclifetime != 3600 {
+		t.Fatal("parseconfig get gclifetime error")
+	}
+	if cf2.EnableSetCookie != false {
+		t.Fatal("parseconfig get enableSetCookie error")
+	}
+	cconfig := new(cookieConfig)
+	err = json.Unmarshal([]byte(cf2.ProviderConfig), cconfig)
+	if err != nil {
+		t.Fatal("parse ProviderConfig err,", err)
+	}
+	if cconfig.CookieName != "gosessionid" {
+		t.Fatal("ProviderConfig get cookieName error")
+	}
+	if cconfig.SecurityKey != "beegocookiehashkey" {
+		t.Fatal("ProviderConfig get securityKey error")
+	}
+}

+ 192 - 0
vendor/github.com/astaxie/beego/session/ssdb/sess_ssdb.go

@@ -0,0 +1,192 @@
+package ssdb
+
+import (
+	"errors"
+	"net/http"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/astaxie/beego/session"
+	"github.com/ssdb/gossdb/ssdb"
+)
+
+var ssdbProvider = &SsdbProvider{}
+
+type SsdbProvider struct {
+	client      *ssdb.Client
+	host        string
+	port        int
+	maxLifetime int64
+}
+
+func (p *SsdbProvider) connectInit() error {
+	var err error
+	if p.host == "" || p.port == 0 {
+		return errors.New("SessionInit First")
+	}
+	p.client, err = ssdb.Connect(p.host, p.port)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *SsdbProvider) SessionInit(maxLifetime int64, savePath string) error {
+	var e error = nil
+	p.maxLifetime = maxLifetime
+	address := strings.Split(savePath, ":")
+	p.host = address[0]
+	p.port, e = strconv.Atoi(address[1])
+	if e != nil {
+		return e
+	}
+	err := p.connectInit()
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *SsdbProvider) SessionRead(sid string) (session.Store, error) {
+	if p.client == nil {
+		if err := p.connectInit(); err != nil {
+			return nil, err
+		}
+	}
+	var kv map[interface{}]interface{}
+	value, err := p.client.Get(sid)
+	if err != nil {
+		return nil, err
+	}
+	if value == nil || len(value.(string)) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob([]byte(value.(string)))
+		if err != nil {
+			return nil, err
+		}
+	}
+	rs := &SessionStore{sid: sid, values: kv, maxLifetime: p.maxLifetime, client: p.client}
+	return rs, nil
+}
+
+func (p *SsdbProvider) SessionExist(sid string) bool {
+	if p.client == nil {
+		if err := p.connectInit(); err != nil {
+			panic(err)
+		}
+	}
+	value, err := p.client.Get(sid)
+	if err != nil {
+		panic(err)
+	}
+	if value == nil || len(value.(string)) == 0 {
+		return false
+	}
+	return true
+
+}
+func (p *SsdbProvider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
+	//conn.Do("setx", key, v, ttl)
+	if p.client == nil {
+		if err := p.connectInit(); err != nil {
+			return nil, err
+		}
+	}
+	value, err := p.client.Get(oldsid)
+	if err != nil {
+		return nil, err
+	}
+	var kv map[interface{}]interface{}
+	if value == nil || len(value.(string)) == 0 {
+		kv = make(map[interface{}]interface{})
+	} else {
+		kv, err = session.DecodeGob([]byte(value.(string)))
+		if err != nil {
+			return nil, err
+		}
+		_, err = p.client.Del(oldsid)
+		if err != nil {
+			return nil, err
+		}
+	}
+	_, e := p.client.Do("setx", sid, value, p.maxLifetime)
+	if e != nil {
+		return nil, e
+	}
+	rs := &SessionStore{sid: sid, values: kv, maxLifetime: p.maxLifetime, client: p.client}
+	return rs, nil
+}
+
+func (p *SsdbProvider) SessionDestroy(sid string) error {
+	if p.client == nil {
+		if err := p.connectInit(); err != nil {
+			return err
+		}
+	}
+	_, err := p.client.Del(sid)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (p *SsdbProvider) SessionGC() {
+	return
+}
+
+func (p *SsdbProvider) SessionAll() int {
+	return 0
+}
+
+type SessionStore struct {
+	sid         string
+	lock        sync.RWMutex
+	values      map[interface{}]interface{}
+	maxLifetime int64
+	client      *ssdb.Client
+}
+
+func (s *SessionStore) Set(key, value interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	s.values[key] = value
+	return nil
+}
+func (s *SessionStore) Get(key interface{}) interface{} {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	if value, ok := s.values[key]; ok {
+		return value
+	}
+	return nil
+}
+
+func (s *SessionStore) Delete(key interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	delete(s.values, key)
+	return nil
+}
+func (s *SessionStore) Flush() error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	s.values = make(map[interface{}]interface{})
+	return nil
+}
+func (s *SessionStore) SessionID() string {
+	return s.sid
+}
+
+func (s *SessionStore) SessionRelease(w http.ResponseWriter) {
+	b, err := session.EncodeGob(s.values)
+	if err != nil {
+		return
+	}
+	s.client.Do("setx", s.sid, string(b), s.maxLifetime)
+
+}
+func init() {
+	session.Register("ssdb", ssdbProvider)
+}

+ 73 - 0
vendor/github.com/astaxie/beego/staticfile_test.go

@@ -0,0 +1,73 @@
+package beego
+
+import (
+	"bytes"
+	"compress/gzip"
+	"compress/zlib"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+var currentWorkDir, _ = os.Getwd()
+var licenseFile = filepath.Join(currentWorkDir, "LICENSE")
+
+func testOpenFile(encoding string, content []byte, t *testing.T) {
+	fi, _ := os.Stat(licenseFile)
+	b, n, sch, err := openFile(licenseFile, fi, encoding)
+	if err != nil {
+		t.Log(err)
+		t.Fail()
+	}
+
+	t.Log("open static file encoding "+n, b)
+
+	assetOpenFileAndContent(sch, content, t)
+}
+func TestOpenStaticFile_1(t *testing.T) {
+	file, _ := os.Open(licenseFile)
+	content, _ := ioutil.ReadAll(file)
+	testOpenFile("", content, t)
+}
+
+func TestOpenStaticFileGzip_1(t *testing.T) {
+	file, _ := os.Open(licenseFile)
+	var zipBuf bytes.Buffer
+	fileWriter, _ := gzip.NewWriterLevel(&zipBuf, gzip.BestCompression)
+	io.Copy(fileWriter, file)
+	fileWriter.Close()
+	content, _ := ioutil.ReadAll(&zipBuf)
+
+	testOpenFile("gzip", content, t)
+}
+func TestOpenStaticFileDeflate_1(t *testing.T) {
+	file, _ := os.Open(licenseFile)
+	var zipBuf bytes.Buffer
+	fileWriter, _ := zlib.NewWriterLevel(&zipBuf, zlib.BestCompression)
+	io.Copy(fileWriter, file)
+	fileWriter.Close()
+	content, _ := ioutil.ReadAll(&zipBuf)
+
+	testOpenFile("deflate", content, t)
+}
+
+func assetOpenFileAndContent(sch *serveContentHolder, content []byte, t *testing.T) {
+	t.Log(sch.size, len(content))
+	if sch.size != int64(len(content)) {
+		t.Log("static content file size not same")
+		t.Fail()
+	}
+	bs, _ := ioutil.ReadAll(sch)
+	for i, v := range content {
+		if v != bs[i] {
+			t.Log("content not same")
+			t.Fail()
+		}
+	}
+	if len(staticFileMap) == 0 {
+		t.Log("men map is empty")
+		t.Fail()
+	}
+}

+ 171 - 0
vendor/github.com/astaxie/beego/swagger/swagger.go

@@ -0,0 +1,171 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Swagger™ is a project used to describe and document RESTful APIs.
+//
+// The Swagger specification defines a set of files required to describe such an API. These files can then be used by the Swagger-UI project to display the API and Swagger-Codegen to generate clients in various languages. Additional utilities can also take advantage of the resulting files, such as testing tools.
+// Now in version 2.0, Swagger is more enabling than ever. And it's 100% open source software.
+
+// Package swagger struct definition
+package swagger
+
+// Swagger list the resource
+type Swagger struct {
+	SwaggerVersion      string              `json:"swagger,omitempty" yaml:"swagger,omitempty"`
+	Infos               Information         `json:"info" yaml:"info"`
+	Host                string              `json:"host,omitempty" yaml:"host,omitempty"`
+	BasePath            string              `json:"basePath,omitempty" yaml:"basePath,omitempty"`
+	Schemes             []string            `json:"schemes,omitempty" yaml:"schemes,omitempty"`
+	Consumes            []string            `json:"consumes,omitempty" yaml:"consumes,omitempty"`
+	Produces            []string            `json:"produces,omitempty" yaml:"produces,omitempty"`
+	Paths               map[string]*Item    `json:"paths" yaml:"paths"`
+	Definitions         map[string]Schema   `json:"definitions,omitempty" yaml:"definitions,omitempty"`
+	SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"`
+	Security            map[string][]string `json:"security,omitempty" yaml:"security,omitempty"`
+	Tags                []Tag               `json:"tags,omitempty" yaml:"tags,omitempty"`
+	ExternalDocs        *ExternalDocs       `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
+}
+
+// Information Provides metadata about the API. The metadata can be used by the clients if needed.
+type Information struct {
+	Title          string `json:"title,omitempty" yaml:"title,omitempty"`
+	Description    string `json:"description,omitempty" yaml:"description,omitempty"`
+	Version        string `json:"version,omitempty" yaml:"version,omitempty"`
+	TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"`
+
+	Contact Contact  `json:"contact,omitempty" yaml:"contact,omitempty"`
+	License *License `json:"license,omitempty" yaml:"license,omitempty"`
+}
+
+// Contact information for the exposed API.
+type Contact struct {
+	Name  string `json:"name,omitempty" yaml:"name,omitempty"`
+	URL   string `json:"url,omitempty" yaml:"url,omitempty"`
+	EMail string `json:"email,omitempty" yaml:"email,omitempty"`
+}
+
+// License information for the exposed API.
+type License struct {
+	Name string `json:"name,omitempty" yaml:"name,omitempty"`
+	URL  string `json:"url,omitempty" yaml:"url,omitempty"`
+}
+
+// Item Describes the operations available on a single path.
+type Item struct {
+	Ref     string     `json:"$ref,omitempty" yaml:"$ref,omitempty"`
+	Get     *Operation `json:"get,omitempty" yaml:"get,omitempty"`
+	Put     *Operation `json:"put,omitempty" yaml:"put,omitempty"`
+	Post    *Operation `json:"post,omitempty" yaml:"post,omitempty"`
+	Delete  *Operation `json:"delete,omitempty" yaml:"delete,omitempty"`
+	Options *Operation `json:"options,omitempty" yaml:"options,omitempty"`
+	Head    *Operation `json:"head,omitempty" yaml:"head,omitempty"`
+	Patch   *Operation `json:"patch,omitempty" yaml:"patch,omitempty"`
+}
+
+// Operation Describes a single API operation on a path.
+type Operation struct {
+	Tags        []string            `json:"tags,omitempty" yaml:"tags,omitempty"`
+	Summary     string              `json:"summary,omitempty" yaml:"summary,omitempty"`
+	Description string              `json:"description,omitempty" yaml:"description,omitempty"`
+	OperationID string              `json:"operationId,omitempty" yaml:"operationId,omitempty"`
+	Consumes    []string            `json:"consumes,omitempty" yaml:"consumes,omitempty"`
+	Produces    []string            `json:"produces,omitempty" yaml:"produces,omitempty"`
+	Schemes     []string            `json:"schemes,omitempty" yaml:"schemes,omitempty"`
+	Parameters  []Parameter         `json:"parameters,omitempty" yaml:"parameters,omitempty"`
+	Responses   map[string]Response `json:"responses,omitempty" yaml:"responses,omitempty"`
+	Deprecated  bool                `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
+}
+
+// Parameter Describes a single operation parameter.
+type Parameter struct {
+	In          string          `json:"in,omitempty" yaml:"in,omitempty"`
+	Name        string          `json:"name,omitempty" yaml:"name,omitempty"`
+	Description string          `json:"description,omitempty" yaml:"description,omitempty"`
+	Required    bool            `json:"required,omitempty" yaml:"required,omitempty"`
+	Schema      *Schema         `json:"schema,omitempty" yaml:"schema,omitempty"`
+	Type        string          `json:"type,omitempty" yaml:"type,omitempty"`
+	Format      string          `json:"format,omitempty" yaml:"format,omitempty"`
+	Items       *ParameterItems `json:"items,omitempty" yaml:"items,omitempty"`
+	Default     interface{}     `json:"default,omitempty" yaml:"default,omitempty"`
+}
+
+// A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body".
+// http://swagger.io/specification/#itemsObject
+type ParameterItems struct {
+	Type             string            `json:"type,omitempty" yaml:"type,omitempty"`
+	Format           string            `json:"format,omitempty" yaml:"format,omitempty"`
+	Items            []*ParameterItems `json:"items,omitempty" yaml:"items,omitempty"` //Required if type is "array". Describes the type of items in the array.
+	CollectionFormat string            `json:"collectionFormat,omitempty" yaml:"collectionFormat,omitempty"`
+	Default          string            `json:"default,omitempty" yaml:"default,omitempty"`
+}
+
+// Schema Object allows the definition of input and output data types.
+type Schema struct {
+	Ref         string               `json:"$ref,omitempty" yaml:"$ref,omitempty"`
+	Title       string               `json:"title,omitempty" yaml:"title,omitempty"`
+	Format      string               `json:"format,omitempty" yaml:"format,omitempty"`
+	Description string               `json:"description,omitempty" yaml:"description,omitempty"`
+	Required    []string             `json:"required,omitempty" yaml:"required,omitempty"`
+	Type        string               `json:"type,omitempty" yaml:"type,omitempty"`
+	Items       *Schema              `json:"items,omitempty" yaml:"items,omitempty"`
+	Properties  map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
+}
+
+// Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification
+type Propertie struct {
+	Ref                  string               `json:"$ref,omitempty" yaml:"$ref,omitempty"`
+	Title                string               `json:"title,omitempty" yaml:"title,omitempty"`
+	Description          string               `json:"description,omitempty" yaml:"description,omitempty"`
+	Default              interface{}          `json:"default,omitempty" yaml:"default,omitempty"`
+	Type                 string               `json:"type,omitempty" yaml:"type,omitempty"`
+	Example              string               `json:"example,omitempty" yaml:"example,omitempty"`
+	Required             []string             `json:"required,omitempty" yaml:"required,omitempty"`
+	Format               string               `json:"format,omitempty" yaml:"format,omitempty"`
+	ReadOnly             bool                 `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`
+	Properties           map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
+	Items                *Propertie           `json:"items,omitempty" yaml:"items,omitempty"`
+	AdditionalProperties *Propertie           `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
+}
+
+// Response as they are returned from executing this operation.
+type Response struct {
+	Description string  `json:"description,omitempty" yaml:"description,omitempty"`
+	Schema      *Schema `json:"schema,omitempty" yaml:"schema,omitempty"`
+	Ref         string  `json:"$ref,omitempty" yaml:"$ref,omitempty"`
+}
+
+// Security Allows the definition of a security scheme that can be used by the operations
+type Security struct {
+	Type             string            `json:"type,omitempty" yaml:"type,omitempty"` // Valid values are "basic", "apiKey" or "oauth2".
+	Description      string            `json:"description,omitempty" yaml:"description,omitempty"`
+	Name             string            `json:"name,omitempty" yaml:"name,omitempty"`
+	In               string            `json:"in,omitempty" yaml:"in,omitempty"`     // Valid values are "query" or "header".
+	Flow             string            `json:"flow,omitempty" yaml:"flow,omitempty"` // Valid values are "implicit", "password", "application" or "accessCode".
+	AuthorizationURL string            `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
+	TokenURL         string            `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
+	Scopes           map[string]string `json:"scopes,omitempty" yaml:"scopes,omitempty"` // The available scopes for the OAuth2 security scheme.
+}
+
+// Tag Allows adding meta data to a single tag that is used by the Operation Object
+type Tag struct {
+	Name         string        `json:"name,omitempty" yaml:"name,omitempty"`
+	Description  string        `json:"description,omitempty" yaml:"description,omitempty"`
+	ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
+}
+
+// ExternalDocs include Additional external documentation
+type ExternalDocs struct {
+	Description string `json:"description,omitempty" yaml:"description,omitempty"`
+	URL         string `json:"url,omitempty" yaml:"url,omitempty"`
+}

+ 144 - 0
vendor/github.com/astaxie/beego/template_test.go

@@ -0,0 +1,144 @@
+// 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 beego
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+var header = `{{define "header"}}
+<h1>Hello, astaxie!</h1>
+{{end}}`
+
+var index = `<!DOCTYPE html>
+<html>
+  <head>
+    <title>beego welcome template</title>
+  </head>
+  <body>
+{{template "block"}}
+{{template "header"}}
+{{template "blocks/block.tpl"}}
+  </body>
+</html>
+`
+
+var block = `{{define "block"}}
+<h1>Hello, blocks!</h1>
+{{end}}`
+
+func TestTemplate(t *testing.T) {
+	dir := "_beeTmp"
+	files := []string{
+		"header.tpl",
+		"index.tpl",
+		"blocks/block.tpl",
+	}
+	if err := os.MkdirAll(dir, 0777); err != nil {
+		t.Fatal(err)
+	}
+	for k, name := range files {
+		os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777)
+		if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+			t.Fatal(err)
+		} else {
+			if k == 0 {
+				f.WriteString(header)
+			} else if k == 1 {
+				f.WriteString(index)
+			} else if k == 2 {
+				f.WriteString(block)
+			}
+
+			f.Close()
+		}
+	}
+	if err := AddViewPath(dir); err != nil {
+		t.Fatal(err)
+	}
+	beeTemplates := beeViewPathTemplates[dir]
+	if len(beeTemplates) != 3 {
+		t.Fatalf("should be 3 but got %v", len(beeTemplates))
+	}
+	if err := beeTemplates["index.tpl"].ExecuteTemplate(os.Stdout, "index.tpl", nil); err != nil {
+		t.Fatal(err)
+	}
+	for _, name := range files {
+		os.RemoveAll(filepath.Join(dir, name))
+	}
+	os.RemoveAll(dir)
+}
+
+var menu = `<div class="menu">
+<ul>
+<li>menu1</li>
+<li>menu2</li>
+<li>menu3</li>
+</ul>
+</div>
+`
+var user = `<!DOCTYPE html>
+<html>
+  <head>
+    <title>beego welcome template</title>
+  </head>
+  <body>
+{{template "../public/menu.tpl"}}
+  </body>
+</html>
+`
+
+func TestRelativeTemplate(t *testing.T) {
+	dir := "_beeTmp"
+
+	//Just add dir to known viewPaths
+	if err := AddViewPath(dir); err != nil {
+		t.Fatal(err)
+	}
+
+	files := []string{
+		"easyui/public/menu.tpl",
+		"easyui/rbac/user.tpl",
+	}
+	if err := os.MkdirAll(dir, 0777); err != nil {
+		t.Fatal(err)
+	}
+	for k, name := range files {
+		os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777)
+		if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+			t.Fatal(err)
+		} else {
+			if k == 0 {
+				f.WriteString(menu)
+			} else if k == 1 {
+				f.WriteString(user)
+			}
+			f.Close()
+		}
+	}
+	if err := BuildTemplate(dir, files[1]); err != nil {
+		t.Fatal(err)
+	}
+	beeTemplates := beeViewPathTemplates[dir]
+	if err := beeTemplates["easyui/rbac/user.tpl"].ExecuteTemplate(os.Stdout, "easyui/rbac/user.tpl", nil); err != nil {
+		t.Fatal(err)
+	}
+	for _, name := range files {
+		os.RemoveAll(filepath.Join(dir, name))
+	}
+	os.RemoveAll(dir)
+}

+ 375 - 0
vendor/github.com/astaxie/beego/templatefunc_test.go

@@ -0,0 +1,375 @@
+// 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 beego
+
+import (
+	"html/template"
+	"net/url"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestSubstr(t *testing.T) {
+	s := `012345`
+	if Substr(s, 0, 2) != "01" {
+		t.Error("should be equal")
+	}
+	if Substr(s, 0, 100) != "012345" {
+		t.Error("should be equal")
+	}
+	if Substr(s, 12, 100) != "012345" {
+		t.Error("should be equal")
+	}
+}
+
+func TestHtml2str(t *testing.T) {
+	h := `<HTML><style></style><script>x<x</script></HTML><123>  123\n
+
+
+	\n`
+	if HTML2str(h) != "123\\n\n\\n" {
+		t.Error("should be equal")
+	}
+}
+
+func TestDateFormat(t *testing.T) {
+	ts := "Mon, 01 Jul 2013 13:27:42 CST"
+	tt, _ := time.Parse(time.RFC1123, ts)
+
+	if ss := DateFormat(tt, "2006-01-02 15:04:05"); ss != "2013-07-01 13:27:42" {
+		t.Errorf("2013-07-01 13:27:42 does not equal %v", ss)
+	}
+}
+
+func TestDate(t *testing.T) {
+	ts := "Mon, 01 Jul 2013 13:27:42 CST"
+	tt, _ := time.Parse(time.RFC1123, ts)
+
+	if ss := Date(tt, "Y-m-d H:i:s"); ss != "2013-07-01 13:27:42" {
+		t.Errorf("2013-07-01 13:27:42 does not equal %v", ss)
+	}
+	if ss := Date(tt, "y-n-j h:i:s A"); ss != "13-7-1 01:27:42 PM" {
+		t.Errorf("13-7-1 01:27:42 PM does not equal %v", ss)
+	}
+	if ss := Date(tt, "D, d M Y g:i:s a"); ss != "Mon, 01 Jul 2013 1:27:42 pm" {
+		t.Errorf("Mon, 01 Jul 2013 1:27:42 pm does not equal %v", ss)
+	}
+	if ss := Date(tt, "l, d F Y G:i:s"); ss != "Monday, 01 July 2013 13:27:42" {
+		t.Errorf("Monday, 01 July 2013 13:27:42 does not equal %v", ss)
+	}
+}
+
+func TestCompareRelated(t *testing.T) {
+	if !Compare("abc", "abc") {
+		t.Error("should be equal")
+	}
+	if Compare("abc", "aBc") {
+		t.Error("should be not equal")
+	}
+	if !Compare("1", 1) {
+		t.Error("should be equal")
+	}
+	if CompareNot("abc", "abc") {
+		t.Error("should be equal")
+	}
+	if !CompareNot("abc", "aBc") {
+		t.Error("should be not equal")
+	}
+	if !NotNil("a string") {
+		t.Error("should not be nil")
+	}
+}
+
+func TestHtmlquote(t *testing.T) {
+	h := `&lt;&#39;&nbsp;&rdquo;&ldquo;&amp;&quot;&gt;`
+	s := `<' ”“&">`
+	if Htmlquote(s) != h {
+		t.Error("should be equal")
+	}
+}
+
+func TestHtmlunquote(t *testing.T) {
+	h := `&lt;&#39;&nbsp;&rdquo;&ldquo;&amp;&quot;&gt;`
+	s := `<' ”“&">`
+	if Htmlunquote(h) != s {
+		t.Error("should be equal")
+	}
+}
+
+func TestParseForm(t *testing.T) {
+	type ExtendInfo struct {
+		Hobby string `form:"hobby"`
+		Memo  string
+	}
+
+	type OtherInfo struct {
+		Organization string `form:"organization"`
+		Title        string `form:"title"`
+		ExtendInfo
+	}
+
+	type user struct {
+		ID      int         `form:"-"`
+		tag     string      `form:"tag"`
+		Name    interface{} `form:"username"`
+		Age     int         `form:"age,text"`
+		Email   string
+		Intro   string    `form:",textarea"`
+		StrBool bool      `form:"strbool"`
+		Date    time.Time `form:"date,2006-01-02"`
+		OtherInfo
+	}
+
+	u := user{}
+	form := url.Values{
+		"ID":           []string{"1"},
+		"-":            []string{"1"},
+		"tag":          []string{"no"},
+		"username":     []string{"test"},
+		"age":          []string{"40"},
+		"Email":        []string{"[email protected]"},
+		"Intro":        []string{"I am an engineer!"},
+		"strbool":      []string{"yes"},
+		"date":         []string{"2014-11-12"},
+		"organization": []string{"beego"},
+		"title":        []string{"CXO"},
+		"hobby":        []string{"Basketball"},
+		"memo":         []string{"nothing"},
+	}
+	if err := ParseForm(form, u); err == nil {
+		t.Fatal("nothing will be changed")
+	}
+	if err := ParseForm(form, &u); err != nil {
+		t.Fatal(err)
+	}
+	if u.ID != 0 {
+		t.Errorf("ID should equal 0 but got %v", u.ID)
+	}
+	if len(u.tag) != 0 {
+		t.Errorf("tag's length should equal 0 but got %v", len(u.tag))
+	}
+	if u.Name.(string) != "test" {
+		t.Errorf("Name should equal `test` but got `%v`", u.Name.(string))
+	}
+	if u.Age != 40 {
+		t.Errorf("Age should equal 40 but got %v", u.Age)
+	}
+	if u.Email != "[email protected]" {
+		t.Errorf("Email should equal `[email protected]` but got `%v`", u.Email)
+	}
+	if u.Intro != "I am an engineer!" {
+		t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro)
+	}
+	if u.StrBool != true {
+		t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool)
+	}
+	y, m, d := u.Date.Date()
+	if y != 2014 || m.String() != "November" || d != 12 {
+		t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String())
+	}
+	if u.Organization != "beego" {
+		t.Errorf("Organization should equal `beego`, but got `%v`", u.Organization)
+	}
+	if u.Title != "CXO" {
+		t.Errorf("Title should equal `CXO`, but got `%v`", u.Title)
+	}
+	if u.Hobby != "Basketball" {
+		t.Errorf("Hobby should equal `Basketball`, but got `%v`", u.Hobby)
+	}
+	if len(u.Memo) != 0 {
+		t.Errorf("Memo's length should equal 0 but got %v", len(u.Memo))
+	}
+}
+
+func TestRenderForm(t *testing.T) {
+	type user struct {
+		ID      int         `form:"-"`
+		tag     string      `form:"tag"`
+		Name    interface{} `form:"username"`
+		Age     int         `form:"age,text,年龄:"`
+		Sex     string
+		Email   []string
+		Intro   string `form:",textarea"`
+		Ignored string `form:"-"`
+	}
+
+	u := user{Name: "test", Intro: "Some Text"}
+	output := RenderForm(u)
+	if output != template.HTML("") {
+		t.Errorf("output should be empty but got %v", output)
+	}
+	output = RenderForm(&u)
+	result := template.HTML(
+		`Name: <input name="username" type="text" value="test"></br>` +
+			`年龄:<input name="age" type="text" value="0"></br>` +
+			`Sex: <input name="Sex" type="text" value=""></br>` +
+			`Intro: <textarea name="Intro">Some Text</textarea>`)
+	if output != result {
+		t.Errorf("output should equal `%v` but got `%v`", result, output)
+	}
+}
+
+func TestRenderFormField(t *testing.T) {
+	html := renderFormField("Label: ", "Name", "text", "Value", "", "", false)
+	if html != `Label: <input name="Name" type="text" value="Value">` {
+		t.Errorf("Wrong html output for input[type=text]: %v ", html)
+	}
+
+	html = renderFormField("Label: ", "Name", "textarea", "Value", "", "", false)
+	if html != `Label: <textarea name="Name">Value</textarea>` {
+		t.Errorf("Wrong html output for textarea: %v ", html)
+	}
+
+	html = renderFormField("Label: ", "Name", "textarea", "Value", "", "", true)
+	if html != `Label: <textarea name="Name" required>Value</textarea>` {
+		t.Errorf("Wrong html output for textarea: %v ", html)
+	}
+}
+
+func TestParseFormTag(t *testing.T) {
+	// create struct to contain field with different types of struct-tag `form`
+	type user struct {
+		All            int `form:"name,text,年龄:"`
+		NoName         int `form:",hidden,年龄:"`
+		OnlyLabel      int `form:",,年龄:"`
+		OnlyName       int `form:"name" id:"name" class:"form-name"`
+		Ignored        int `form:"-"`
+		Required       int `form:"name" required:"true"`
+		IgnoreRequired int `form:"name"`
+		NotRequired    int `form:"name" required:"false"`
+	}
+
+	objT := reflect.TypeOf(&user{}).Elem()
+
+	label, name, fType, id, class, ignored, required := parseFormTag(objT.Field(0))
+	if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) {
+		t.Errorf("Form Tag with name, label and type was not correctly parsed.")
+	}
+
+	label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(1))
+	if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) {
+		t.Errorf("Form Tag with label and type but without name was not correctly parsed.")
+	}
+
+	label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(2))
+	if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) {
+		t.Errorf("Form Tag containing only label was not correctly parsed.")
+	}
+
+	label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(3))
+	if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false &&
+		id == "name" && class == "form-name") {
+		t.Errorf("Form Tag containing only name was not correctly parsed.")
+	}
+
+	label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(4))
+	if ignored == false {
+		t.Errorf("Form Tag that should be ignored was not correctly parsed.")
+	}
+
+	label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(5))
+	if !(name == "name" && required == true) {
+		t.Errorf("Form Tag containing only name and required was not correctly parsed.")
+	}
+
+	label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(6))
+	if !(name == "name" && required == false) {
+		t.Errorf("Form Tag containing only name and ignore required was not correctly parsed.")
+	}
+
+	label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(7))
+	if !(name == "name" && required == false) {
+		t.Errorf("Form Tag containing only name and not required was not correctly parsed.")
+	}
+
+}
+
+func TestMapGet(t *testing.T) {
+	// test one level map
+	m1 := map[string]int64{
+		"a": 1,
+		"1": 2,
+	}
+
+	if res, err := MapGet(m1, "a"); err == nil {
+		if res.(int64) != 1 {
+			t.Errorf("Should return 1, but return %v", res)
+		}
+	} else {
+		t.Errorf("Error happens %v", err)
+	}
+
+	if res, err := MapGet(m1, "1"); err == nil {
+		if res.(int64) != 2 {
+			t.Errorf("Should return 2, but return %v", res)
+		}
+	} else {
+		t.Errorf("Error happens %v", err)
+	}
+
+	if res, err := MapGet(m1, 1); err == nil {
+		if res.(int64) != 2 {
+			t.Errorf("Should return 2, but return %v", res)
+		}
+	} else {
+		t.Errorf("Error happens %v", err)
+	}
+
+	// test 2 level map
+	m2 := map[string]interface{}{
+		"1": map[string]float64{
+			"2": 3.5,
+		},
+	}
+
+	if res, err := MapGet(m2, 1, 2); err == nil {
+		if res.(float64) != 3.5 {
+			t.Errorf("Should return 3.5, but return %v", res)
+		}
+	} else {
+		t.Errorf("Error happens %v", err)
+	}
+
+	// test 5 level map
+	m5 := map[string]interface{}{
+		"1": map[string]interface{}{
+			"2": map[string]interface{}{
+				"3": map[string]interface{}{
+					"4": map[string]interface{}{
+						"5": 1.2,
+					},
+				},
+			},
+		},
+	}
+
+	if res, err := MapGet(m5, 1, 2, 3, 4, 5); err == nil {
+		if res.(float64) != 1.2 {
+			t.Errorf("Should return 1.2, but return %v", res)
+		}
+	} else {
+		t.Errorf("Error happens %v", err)
+	}
+
+	// check whether element not exists in map
+	if res, err := MapGet(m5, 5, 4, 3, 2, 1); err == nil {
+		if res != nil {
+			t.Errorf("Should return nil, but return %v", res)
+		}
+	} else {
+		t.Errorf("Error happens %v", err)
+	}
+}

+ 15 - 0
vendor/github.com/astaxie/beego/testing/assertions.go

@@ -0,0 +1,15 @@
+// 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 testing

+ 65 - 0
vendor/github.com/astaxie/beego/testing/client.go

@@ -0,0 +1,65 @@
+// 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 testing
+
+import (
+	"github.com/astaxie/beego/config"
+	"github.com/astaxie/beego/httplib"
+)
+
+var port = ""
+var baseURL = "http://localhost:"
+
+// TestHTTPRequest beego test request client
+type TestHTTPRequest struct {
+	httplib.BeegoHTTPRequest
+}
+
+func getPort() string {
+	if port == "" {
+		config, err := config.NewConfig("ini", "../conf/app.conf")
+		if err != nil {
+			return "8080"
+		}
+		port = config.String("httpport")
+		return port
+	}
+	return port
+}
+
+// Get returns test client in GET method
+func Get(path string) *TestHTTPRequest {
+	return &TestHTTPRequest{*httplib.Get(baseURL + getPort() + path)}
+}
+
+// Post returns test client in POST method
+func Post(path string) *TestHTTPRequest {
+	return &TestHTTPRequest{*httplib.Post(baseURL + getPort() + path)}
+}
+
+// Put returns test client in PUT method
+func Put(path string) *TestHTTPRequest {
+	return &TestHTTPRequest{*httplib.Put(baseURL + getPort() + path)}
+}
+
+// Delete returns test client in DELETE method
+func Delete(path string) *TestHTTPRequest {
+	return &TestHTTPRequest{*httplib.Delete(baseURL + getPort() + path)}
+}
+
+// Head returns test client in HEAD method
+func Head(path string) *TestHTTPRequest {
+	return &TestHTTPRequest{*httplib.Head(baseURL + getPort() + path)}
+}

+ 28 - 0
vendor/github.com/astaxie/beego/toolbox/profile_test.go

@@ -0,0 +1,28 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package toolbox
+
+import (
+	"os"
+	"testing"
+)
+
+func TestProcessInput(t *testing.T) {
+	ProcessInput("lookup goroutine", os.Stdout)
+	ProcessInput("lookup heap", os.Stdout)
+	ProcessInput("lookup threadcreate", os.Stdout)
+	ProcessInput("lookup block", os.Stdout)
+	ProcessInput("gc summary", os.Stdout)
+}

+ 40 - 0
vendor/github.com/astaxie/beego/toolbox/statistics_test.go

@@ -0,0 +1,40 @@
+// 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 toolbox
+
+import (
+	"encoding/json"
+	"testing"
+	"time"
+)
+
+func TestStatics(t *testing.T) {
+	StatisticsMap.AddStatistics("POST", "/api/user", "&admin.user", time.Duration(2000))
+	StatisticsMap.AddStatistics("POST", "/api/user", "&admin.user", time.Duration(120000))
+	StatisticsMap.AddStatistics("GET", "/api/user", "&admin.user", time.Duration(13000))
+	StatisticsMap.AddStatistics("POST", "/api/admin", "&admin.user", time.Duration(14000))
+	StatisticsMap.AddStatistics("POST", "/api/user/astaxie", "&admin.user", time.Duration(12000))
+	StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(13000))
+	StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400))
+	t.Log(StatisticsMap.GetMap())
+
+	data := StatisticsMap.GetMapData()
+	b, err := json.Marshal(data)
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+
+	t.Log(string(b))
+}

+ 63 - 0
vendor/github.com/astaxie/beego/toolbox/task_test.go

@@ -0,0 +1,63 @@
+// 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 toolbox
+
+import (
+	"fmt"
+	"sync"
+	"testing"
+	"time"
+)
+
+func TestParse(t *testing.T) {
+	tk := NewTask("taska", "0/30 * * * * *", func() error { fmt.Println("hello world"); return nil })
+	err := tk.Run()
+	if err != nil {
+		t.Fatal(err)
+	}
+	AddTask("taska", tk)
+	StartTask()
+	time.Sleep(6 * time.Second)
+	StopTask()
+}
+
+func TestSpec(t *testing.T) {
+	wg := &sync.WaitGroup{}
+	wg.Add(2)
+	tk1 := NewTask("tk1", "0 12 * * * *", func() error { fmt.Println("tk1"); return nil })
+	tk2 := NewTask("tk2", "0,10,20 * * * * *", func() error { fmt.Println("tk2"); wg.Done(); return nil })
+	tk3 := NewTask("tk3", "0 10 * * * *", func() error { fmt.Println("tk3"); wg.Done(); return nil })
+
+	AddTask("tk1", tk1)
+	AddTask("tk2", tk2)
+	AddTask("tk3", tk3)
+	StartTask()
+	defer StopTask()
+
+	select {
+	case <-time.After(200 * time.Second):
+		t.FailNow()
+	case <-wait(wg):
+	}
+}
+
+func wait(wg *sync.WaitGroup) chan bool {
+	ch := make(chan bool)
+	go func() {
+		wg.Wait()
+		ch <- true
+	}()
+	return ch
+}

+ 306 - 0
vendor/github.com/astaxie/beego/tree_test.go

@@ -0,0 +1,306 @@
+// 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 beego
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/astaxie/beego/context"
+)
+
+type testinfo struct {
+	url        string
+	requesturl string
+	params     map[string]string
+}
+
+var routers []testinfo
+
+func init() {
+	routers = make([]testinfo, 0)
+	routers = append(routers, testinfo{"/topic/?:auth:int", "/topic", nil})
+	routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth": "123"}})
+	routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}})
+	routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1", ":auth": "2"}})
+	routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}})
+	routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1", ":auth": "123"}})
+	routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}})
+	routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}})
+	routers = append(routers, testinfo{"/", "/", nil})
+	routers = append(routers, testinfo{"/customer/login", "/customer/login", nil})
+	routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}})
+	routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}})
+	routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}})
+	routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}})
+	routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}})
+	routers = append(routers, testinfo{"/cc/:id/*", "/cc/2009/11/dd", map[string]string{":id": "2009", ":splat": "11/dd"}})
+	routers = append(routers, testinfo{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}})
+	routers = append(routers, testinfo{"/thumbnail/:size/uploads/*",
+		"/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg",
+		map[string]string{":size": "100x100", ":splat": "items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg"}})
+	routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}})
+	routers = append(routers, testinfo{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
+	routers = append(routers, testinfo{"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
+	routers = append(routers, testinfo{"/dl/:width:int/:height:int/*.*",
+		"/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg",
+		map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}})
+	routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}})
+	routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(a)", map[string]string{":id": "123"}})
+	routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(b)", map[string]string{":id": "123"}})
+	routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(c)", map[string]string{":id": "123"}})
+	routers = append(routers, testinfo{"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}})
+	routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}})
+	routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}})
+	routers = append(routers, testinfo{"/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}})
+	routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}})
+	routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}})
+	routers = append(routers, testinfo{"/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}})
+	routers = append(routers, testinfo{"/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}})
+	routers = append(routers, testinfo{"/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
+	routers = append(routers, testinfo{"/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
+	routers = append(routers, testinfo{"/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
+	routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members", map[string]string{":pid": "1"}})
+	routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members/2", map[string]string{":pid": "1", ":mid": "2"}})
+}
+
+func TestTreeRouters(t *testing.T) {
+	for _, r := range routers {
+		tr := NewTree()
+		tr.AddRouter(r.url, "astaxie")
+		ctx := context.NewContext()
+		obj := tr.Match(r.requesturl, ctx)
+		if obj == nil || obj.(string) != "astaxie" {
+			t.Fatal(r.url+" can't get obj, Expect ", r.requesturl)
+		}
+		if r.params != nil {
+			for k, v := range r.params {
+				if vv := ctx.Input.Param(k); vv != v {
+					t.Fatal("The Rule: " + r.url + "\nThe RequestURL:" + r.requesturl + "\nThe Key is " + k + ", The Value should be: " + v + ", but get: " + vv)
+				} else if vv == "" && v != "" {
+					t.Fatal(r.url + "    " + r.requesturl + " get param empty:" + k)
+				}
+			}
+		}
+	}
+}
+
+func TestStaticPath(t *testing.T) {
+	tr := NewTree()
+	tr.AddRouter("/topic/:id", "wildcard")
+	tr.AddRouter("/topic", "static")
+	ctx := context.NewContext()
+	obj := tr.Match("/topic", ctx)
+	if obj == nil || obj.(string) != "static" {
+		t.Fatal("/topic is  a static route")
+	}
+	obj = tr.Match("/topic/1", ctx)
+	if obj == nil || obj.(string) != "wildcard" {
+		t.Fatal("/topic/1 is a wildcard route")
+	}
+}
+
+func TestAddTree(t *testing.T) {
+	tr := NewTree()
+	tr.AddRouter("/shop/:id/account", "astaxie")
+	tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
+	t1 := NewTree()
+	t1.AddTree("/v1/zl", tr)
+	ctx := context.NewContext()
+	obj := t1.Match("/v1/zl/shop/123/account", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/v1/zl/shop/:id/account can't get obj ")
+	}
+	if ctx.Input.ParamsLen() == 0 {
+		t.Fatal("get param error")
+	}
+	if ctx.Input.Param(":id") != "123" {
+		t.Fatal("get :id param error")
+	}
+	ctx.Input.Reset(ctx)
+	obj = t1.Match("/v1/zl/shop/123/ttt_1_12.html", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
+	}
+	if ctx.Input.ParamsLen() == 0 {
+		t.Fatal("get param error")
+	}
+	if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" {
+		t.Fatal("get :sd :id :page param error")
+	}
+
+	t2 := NewTree()
+	t2.AddTree("/v1/:shopid", tr)
+	ctx.Input.Reset(ctx)
+	obj = t2.Match("/v1/zl/shop/123/account", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/v1/:shopid/shop/:id/account can't get obj ")
+	}
+	if ctx.Input.ParamsLen() == 0 {
+		t.Fatal("get param error")
+	}
+	if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":shopid") != "zl" {
+		t.Fatal("get :id :shopid param error")
+	}
+	ctx.Input.Reset(ctx)
+	obj = t2.Match("/v1/zl/shop/123/ttt_1_12.html", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
+	}
+	if ctx.Input.ParamsLen() == 0 {
+		t.Fatal("get :shopid param error")
+	}
+	if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" || ctx.Input.Param(":shopid") != "zl" {
+		t.Fatal("get :sd :id :page :shopid param error")
+	}
+}
+
+func TestAddTree2(t *testing.T) {
+	tr := NewTree()
+	tr.AddRouter("/shop/:id/account", "astaxie")
+	tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
+	t3 := NewTree()
+	t3.AddTree("/:version(v1|v2)/:prefix", tr)
+	ctx := context.NewContext()
+	obj := t3.Match("/v1/zl/shop/123/account", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ")
+	}
+	if ctx.Input.ParamsLen() == 0 {
+		t.Fatal("get param error")
+	}
+	if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":prefix") != "zl" || ctx.Input.Param(":version") != "v1" {
+		t.Fatal("get :id :prefix :version param error")
+	}
+}
+
+func TestAddTree3(t *testing.T) {
+	tr := NewTree()
+	tr.AddRouter("/create", "astaxie")
+	tr.AddRouter("/shop/:sd/account", "astaxie")
+	t3 := NewTree()
+	t3.AddTree("/table/:num", tr)
+	ctx := context.NewContext()
+	obj := t3.Match("/table/123/shop/123/account", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/table/:num/shop/:sd/account can't get obj ")
+	}
+	if ctx.Input.ParamsLen() == 0 {
+		t.Fatal("get param error")
+	}
+	if ctx.Input.Param(":num") != "123" || ctx.Input.Param(":sd") != "123" {
+		t.Fatal("get :num :sd param error")
+	}
+	ctx.Input.Reset(ctx)
+	obj = t3.Match("/table/123/create", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/table/:num/create can't get obj ")
+	}
+}
+
+func TestAddTree4(t *testing.T) {
+	tr := NewTree()
+	tr.AddRouter("/create", "astaxie")
+	tr.AddRouter("/shop/:sd/:account", "astaxie")
+	t4 := NewTree()
+	t4.AddTree("/:info:int/:num/:id", tr)
+	ctx := context.NewContext()
+	obj := t4.Match("/12/123/456/shop/123/account", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ")
+	}
+	if ctx.Input.ParamsLen() == 0 {
+		t.Fatal("get param error")
+	}
+	if ctx.Input.Param(":info") != "12" || ctx.Input.Param(":num") != "123" ||
+		ctx.Input.Param(":id") != "456" || ctx.Input.Param(":sd") != "123" ||
+		ctx.Input.Param(":account") != "account" {
+		t.Fatal("get :info :num :id :sd :account param error")
+	}
+	ctx.Input.Reset(ctx)
+	obj = t4.Match("/12/123/456/create", ctx)
+	if obj == nil || obj.(string) != "astaxie" {
+		t.Fatal("/:info:int/:num/:id/create can't get obj ")
+	}
+}
+
+// Test for issue #1595
+func TestAddTree5(t *testing.T) {
+	tr := NewTree()
+	tr.AddRouter("/v1/shop/:id", "shopdetail")
+	tr.AddRouter("/v1/shop/", "shophome")
+	ctx := context.NewContext()
+	obj := tr.Match("/v1/shop/", ctx)
+	if obj == nil || obj.(string) != "shophome" {
+		t.Fatal("url /v1/shop/ need match router /v1/shop/ ")
+	}
+}
+
+func TestSplitPath(t *testing.T) {
+	a := splitPath("")
+	if len(a) != 0 {
+		t.Fatal("/ should retrun []")
+	}
+	a = splitPath("/")
+	if len(a) != 0 {
+		t.Fatal("/ should retrun []")
+	}
+	a = splitPath("/admin")
+	if len(a) != 1 || a[0] != "admin" {
+		t.Fatal("/admin should retrun [admin]")
+	}
+	a = splitPath("/admin/")
+	if len(a) != 1 || a[0] != "admin" {
+		t.Fatal("/admin/ should retrun [admin]")
+	}
+	a = splitPath("/admin/users")
+	if len(a) != 2 || a[0] != "admin" || a[1] != "users" {
+		t.Fatal("/admin should retrun [admin users]")
+	}
+	a = splitPath("/admin/:id:int")
+	if len(a) != 2 || a[0] != "admin" || a[1] != ":id:int" {
+		t.Fatal("/admin should retrun [admin :id:int]")
+	}
+}
+
+func TestSplitSegment(t *testing.T) {
+
+	items := map[string]struct {
+		isReg  bool
+		params []string
+		regStr string
+	}{
+		"admin":                      {false, nil, ""},
+		"*":                          {true, []string{":splat"}, ""},
+		"*.*":                        {true, []string{".", ":path", ":ext"}, ""},
+		":id":                        {true, []string{":id"}, ""},
+		"?:id":                       {true, []string{":", ":id"}, ""},
+		":id:int":                    {true, []string{":id"}, "([0-9]+)"},
+		":name:string":               {true, []string{":name"}, `([\w]+)`},
+		":id([0-9]+)":                {true, []string{":id"}, `([0-9]+)`},
+		":id([0-9]+)_:name":          {true, []string{":id", ":name"}, `([0-9]+)_(.+)`},
+		":id(.+)_cms.html":           {true, []string{":id"}, `(.+)_cms.html`},
+		"cms_:id(.+)_:page(.+).html": {true, []string{":id", ":page"}, `cms_(.+)_(.+).html`},
+		`:app(a|b|c)`:                {true, []string{":app"}, `(a|b|c)`},
+		`:app\((a|b|c)\)`:            {true, []string{":app"}, `(.+)\((a|b|c)\)`},
+	}
+
+	for pattern, v := range items {
+		b, w, r := splitSegment(pattern)
+		if b != v.isReg || r != v.regStr || strings.Join(w, ",") != strings.Join(v.params, ",") {
+			t.Fatalf("%s should return %t,%s,%q, got %t,%s,%q", pattern, v.isReg, v.params, v.regStr, b, w, r)
+		}
+	}
+}

+ 28 - 0
vendor/github.com/astaxie/beego/utils/caller_test.go

@@ -0,0 +1,28 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestGetFuncName(t *testing.T) {
+	name := GetFuncName(TestGetFuncName)
+	t.Log(name)
+	if !strings.HasSuffix(name, ".TestGetFuncName") {
+		t.Error("get func name error")
+	}
+}

+ 19 - 0
vendor/github.com/astaxie/beego/utils/captcha/LICENSE

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

+ 45 - 0
vendor/github.com/astaxie/beego/utils/captcha/README.md

@@ -0,0 +1,45 @@
+# Captcha
+
+an example for use captcha
+
+```
+package controllers
+
+import (
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/cache"
+	"github.com/astaxie/beego/utils/captcha"
+)
+
+var cpt *captcha.Captcha
+
+func init() {
+	// use beego cache system store the captcha data
+	store := cache.NewMemoryCache()
+	cpt = captcha.NewWithFilter("/captcha/", store)
+}
+
+type MainController struct {
+	beego.Controller
+}
+
+func (this *MainController) Get() {
+	this.TplName = "index.tpl"
+}
+
+func (this *MainController) Post() {
+	this.TplName = "index.tpl"
+
+	this.Data["Success"] = cpt.VerifyReq(this.Ctx.Request)
+}
+```
+
+template usage
+
+```
+{{.Success}}
+<form action="/" method="post">
+	{{create_captcha}}
+	<input name="captcha" type="text">
+</form>
+```

+ 270 - 0
vendor/github.com/astaxie/beego/utils/captcha/captcha.go

@@ -0,0 +1,270 @@
+// 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 captcha implements generation and verification of image CAPTCHAs.
+// an example for use captcha
+//
+// ```
+// package controllers
+//
+// import (
+// 	"github.com/astaxie/beego"
+// 	"github.com/astaxie/beego/cache"
+// 	"github.com/astaxie/beego/utils/captcha"
+// )
+//
+// var cpt *captcha.Captcha
+//
+// func init() {
+// 	// use beego cache system store the captcha data
+// 	store := cache.NewMemoryCache()
+// 	cpt = captcha.NewWithFilter("/captcha/", store)
+// }
+//
+// type MainController struct {
+// 	beego.Controller
+// }
+//
+// func (this *MainController) Get() {
+// 	this.TplName = "index.tpl"
+// }
+//
+// func (this *MainController) Post() {
+// 	this.TplName = "index.tpl"
+//
+// 	this.Data["Success"] = cpt.VerifyReq(this.Ctx.Request)
+// }
+// ```
+//
+// template usage
+//
+// ```
+// {{.Success}}
+// <form action="/" method="post">
+// 	{{create_captcha}}
+// 	<input name="captcha" type="text">
+// </form>
+// ```
+package captcha
+
+import (
+	"fmt"
+	"html/template"
+	"net/http"
+	"path"
+	"strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/cache"
+	"github.com/astaxie/beego/context"
+	"github.com/astaxie/beego/logs"
+	"github.com/astaxie/beego/utils"
+)
+
+var (
+	defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+)
+
+const (
+	// default captcha attributes
+	challengeNums    = 6
+	expiration       = 600 * time.Second
+	fieldIDName      = "captcha_id"
+	fieldCaptchaName = "captcha"
+	cachePrefix      = "captcha_"
+	defaultURLPrefix = "/captcha/"
+)
+
+// Captcha struct
+type Captcha struct {
+	// beego cache store
+	store cache.Cache
+
+	// url prefix for captcha image
+	URLPrefix string
+
+	// specify captcha id input field name
+	FieldIDName string
+	// specify captcha result input field name
+	FieldCaptchaName string
+
+	// captcha image width and height
+	StdWidth  int
+	StdHeight int
+
+	// captcha chars nums
+	ChallengeNums int
+
+	// captcha expiration seconds
+	Expiration time.Duration
+
+	// cache key prefix
+	CachePrefix string
+}
+
+// generate key string
+func (c *Captcha) key(id string) string {
+	return c.CachePrefix + id
+}
+
+// generate rand chars with default chars
+func (c *Captcha) genRandChars() []byte {
+	return utils.RandomCreateBytes(c.ChallengeNums, defaultChars...)
+}
+
+// Handler beego filter handler for serve captcha image
+func (c *Captcha) Handler(ctx *context.Context) {
+	var chars []byte
+
+	id := path.Base(ctx.Request.RequestURI)
+	if i := strings.Index(id, "."); i != -1 {
+		id = id[:i]
+	}
+
+	key := c.key(id)
+
+	if len(ctx.Input.Query("reload")) > 0 {
+		chars = c.genRandChars()
+		if err := c.store.Put(key, chars, c.Expiration); err != nil {
+			ctx.Output.SetStatus(500)
+			ctx.WriteString("captcha reload error")
+			logs.Error("Reload Create Captcha Error:", err)
+			return
+		}
+	} else {
+		if v, ok := c.store.Get(key).([]byte); ok {
+			chars = v
+		} else {
+			ctx.Output.SetStatus(404)
+			ctx.WriteString("captcha not found")
+			return
+		}
+	}
+
+	img := NewImage(chars, c.StdWidth, c.StdHeight)
+	if _, err := img.WriteTo(ctx.ResponseWriter); err != nil {
+		logs.Error("Write Captcha Image Error:", err)
+	}
+}
+
+// CreateCaptchaHTML template func for output html
+func (c *Captcha) CreateCaptchaHTML() template.HTML {
+	value, err := c.CreateCaptcha()
+	if err != nil {
+		logs.Error("Create Captcha Error:", err)
+		return ""
+	}
+
+	// create html
+	return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`+
+		`<a class="captcha" href="javascript:">`+
+		`<img onclick="this.src=('%s%s.png?reload='+(new Date()).getTime())" class="captcha-img" src="%s%s.png">`+
+		`</a>`, c.FieldIDName, value, c.URLPrefix, value, c.URLPrefix, value))
+}
+
+// CreateCaptcha create a new captcha id
+func (c *Captcha) CreateCaptcha() (string, error) {
+	// generate captcha id
+	id := string(utils.RandomCreateBytes(15))
+
+	// get the captcha chars
+	chars := c.genRandChars()
+
+	// save to store
+	if err := c.store.Put(c.key(id), chars, c.Expiration); err != nil {
+		return "", err
+	}
+
+	return id, nil
+}
+
+// VerifyReq verify from a request
+func (c *Captcha) VerifyReq(req *http.Request) bool {
+	req.ParseForm()
+	return c.Verify(req.Form.Get(c.FieldIDName), req.Form.Get(c.FieldCaptchaName))
+}
+
+// Verify direct verify id and challenge string
+func (c *Captcha) Verify(id string, challenge string) (success bool) {
+	if len(challenge) == 0 || len(id) == 0 {
+		return
+	}
+
+	var chars []byte
+
+	key := c.key(id)
+
+	if v, ok := c.store.Get(key).([]byte); ok {
+		chars = v
+	} else {
+		return
+	}
+
+	defer func() {
+		// finally remove it
+		c.store.Delete(key)
+	}()
+
+	if len(chars) != len(challenge) {
+		return
+	}
+	// verify challenge
+	for i, c := range chars {
+		if c != challenge[i]-48 {
+			return
+		}
+	}
+
+	return true
+}
+
+// NewCaptcha create a new captcha.Captcha
+func NewCaptcha(urlPrefix string, store cache.Cache) *Captcha {
+	cpt := &Captcha{}
+	cpt.store = store
+	cpt.FieldIDName = fieldIDName
+	cpt.FieldCaptchaName = fieldCaptchaName
+	cpt.ChallengeNums = challengeNums
+	cpt.Expiration = expiration
+	cpt.CachePrefix = cachePrefix
+	cpt.StdWidth = stdWidth
+	cpt.StdHeight = stdHeight
+
+	if len(urlPrefix) == 0 {
+		urlPrefix = defaultURLPrefix
+	}
+
+	if urlPrefix[len(urlPrefix)-1] != '/' {
+		urlPrefix += "/"
+	}
+
+	cpt.URLPrefix = urlPrefix
+
+	return cpt
+}
+
+// NewWithFilter create a new captcha.Captcha and auto AddFilter for serve captacha image
+// and add a template func for output html
+func NewWithFilter(urlPrefix string, store cache.Cache) *Captcha {
+	cpt := NewCaptcha(urlPrefix, store)
+
+	// create filter for serve captcha image
+	beego.InsertFilter(cpt.URLPrefix+"*", beego.BeforeRouter, cpt.Handler)
+
+	// add to template func map
+	beego.AddFuncMap("create_captcha", cpt.CreateCaptchaHTML)
+
+	return cpt
+}

+ 501 - 0
vendor/github.com/astaxie/beego/utils/captcha/image.go

@@ -0,0 +1,501 @@
+// 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 captcha
+
+import (
+	"bytes"
+	"image"
+	"image/color"
+	"image/png"
+	"io"
+	"math"
+)
+
+const (
+	fontWidth  = 11
+	fontHeight = 18
+	blackChar  = 1
+
+	// Standard width and height of a captcha image.
+	stdWidth  = 240
+	stdHeight = 80
+	// Maximum absolute skew factor of a single digit.
+	maxSkew = 0.7
+	// Number of background circles.
+	circleCount = 20
+)
+
+var font = [][]byte{
+	{ // 0
+		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
+		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
+		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
+		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+	},
+	{ // 1
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+		0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+		0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	},
+	{ // 2
+		0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
+		0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+		0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	},
+	{ // 3
+		0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+		0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+		1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+	},
+	{ // 4
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
+		0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
+		0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
+		1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+	},
+	{ // 5
+		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+	},
+	{ // 6
+		0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0,
+		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
+		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0,
+		1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
+		1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0,
+		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
+		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
+		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+	},
+	{ // 7
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
+		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
+		0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+		0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+		0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+	},
+	{ // 8
+		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+		0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+		0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
+		0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0,
+		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
+		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+	},
+	{ // 9
+		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+		0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
+		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+		0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+		0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
+		0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
+		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+		0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+	},
+}
+
+// Image struct
+type Image struct {
+	*image.Paletted
+	numWidth  int
+	numHeight int
+	dotSize   int
+}
+
+var prng = &siprng{}
+
+// randIntn returns a pseudorandom non-negative int in range [0, n).
+func randIntn(n int) int {
+	return prng.Intn(n)
+}
+
+// randInt returns a pseudorandom int in range [from, to].
+func randInt(from, to int) int {
+	return prng.Intn(to+1-from) + from
+}
+
+// randFloat returns a pseudorandom float64 in range [from, to].
+func randFloat(from, to float64) float64 {
+	return (to-from)*prng.Float64() + from
+}
+
+func randomPalette() color.Palette {
+	p := make([]color.Color, circleCount+1)
+	// Transparent color.
+	p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00}
+	// Primary color.
+	prim := color.RGBA{
+		uint8(randIntn(129)),
+		uint8(randIntn(129)),
+		uint8(randIntn(129)),
+		0xFF,
+	}
+	p[1] = prim
+	// Circle colors.
+	for i := 2; i <= circleCount; i++ {
+		p[i] = randomBrightness(prim, 255)
+	}
+	return p
+}
+
+// NewImage returns a new captcha image of the given width and height with the
+// given digits, where each digit must be in range 0-9.
+func NewImage(digits []byte, width, height int) *Image {
+	m := new(Image)
+	m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette())
+	m.calculateSizes(width, height, len(digits))
+	// Randomly position captcha inside the image.
+	maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize
+	maxy := height - m.numHeight - m.dotSize*2
+	var border int
+	if width > height {
+		border = height / 5
+	} else {
+		border = width / 5
+	}
+	x := randInt(border, maxx-border)
+	y := randInt(border, maxy-border)
+	// Draw digits.
+	for _, n := range digits {
+		m.drawDigit(font[n], x, y)
+		x += m.numWidth + m.dotSize
+	}
+	// Draw strike-through line.
+	m.strikeThrough()
+	// Apply wave distortion.
+	m.distort(randFloat(5, 10), randFloat(100, 200))
+	// Fill image with random circles.
+	m.fillWithCircles(circleCount, m.dotSize)
+	return m
+}
+
+// encodedPNG encodes an image to PNG and returns
+// the result as a byte slice.
+func (m *Image) encodedPNG() []byte {
+	var buf bytes.Buffer
+	if err := png.Encode(&buf, m.Paletted); err != nil {
+		panic(err.Error())
+	}
+	return buf.Bytes()
+}
+
+// WriteTo writes captcha image in PNG format into the given writer.
+func (m *Image) WriteTo(w io.Writer) (int64, error) {
+	n, err := w.Write(m.encodedPNG())
+	return int64(n), err
+}
+
+func (m *Image) calculateSizes(width, height, ncount int) {
+	// Goal: fit all digits inside the image.
+	var border int
+	if width > height {
+		border = height / 4
+	} else {
+		border = width / 4
+	}
+	// Convert everything to floats for calculations.
+	w := float64(width - border*2)
+	h := float64(height - border*2)
+	// fw takes into account 1-dot spacing between digits.
+	fw := float64(fontWidth + 1)
+	fh := float64(fontHeight)
+	nc := float64(ncount)
+	// Calculate the width of a single digit taking into account only the
+	// width of the image.
+	nw := w / nc
+	// Calculate the height of a digit from this width.
+	nh := nw * fh / fw
+	// Digit too high?
+	if nh > h {
+		// Fit digits based on height.
+		nh = h
+		nw = fw / fh * nh
+	}
+	// Calculate dot size.
+	m.dotSize = int(nh / fh)
+	if m.dotSize < 1 {
+		m.dotSize = 1
+	}
+	// Save everything, making the actual width smaller by 1 dot to account
+	// for spacing between digits.
+	m.numWidth = int(nw) - m.dotSize
+	m.numHeight = int(nh)
+}
+
+func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) {
+	for x := fromX; x <= toX; x++ {
+		m.SetColorIndex(x, y, colorIdx)
+	}
+}
+
+func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) {
+	f := 1 - radius
+	dfx := 1
+	dfy := -2 * radius
+	xo := 0
+	yo := radius
+
+	m.SetColorIndex(x, y+radius, colorIdx)
+	m.SetColorIndex(x, y-radius, colorIdx)
+	m.drawHorizLine(x-radius, x+radius, y, colorIdx)
+
+	for xo < yo {
+		if f >= 0 {
+			yo--
+			dfy += 2
+			f += dfy
+		}
+		xo++
+		dfx += 2
+		f += dfx
+		m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx)
+		m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx)
+		m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx)
+		m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx)
+	}
+}
+
+func (m *Image) fillWithCircles(n, maxradius int) {
+	maxx := m.Bounds().Max.X
+	maxy := m.Bounds().Max.Y
+	for i := 0; i < n; i++ {
+		colorIdx := uint8(randInt(1, circleCount-1))
+		r := randInt(1, maxradius)
+		m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx)
+	}
+}
+
+func (m *Image) strikeThrough() {
+	maxx := m.Bounds().Max.X
+	maxy := m.Bounds().Max.Y
+	y := randInt(maxy/3, maxy-maxy/3)
+	amplitude := randFloat(5, 20)
+	period := randFloat(80, 180)
+	dx := 2.0 * math.Pi / period
+	for x := 0; x < maxx; x++ {
+		xo := amplitude * math.Cos(float64(y)*dx)
+		yo := amplitude * math.Sin(float64(x)*dx)
+		for yn := 0; yn < m.dotSize; yn++ {
+			r := randInt(0, m.dotSize)
+			m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1)
+		}
+	}
+}
+
+func (m *Image) drawDigit(digit []byte, x, y int) {
+	skf := randFloat(-maxSkew, maxSkew)
+	xs := float64(x)
+	r := m.dotSize / 2
+	y += randInt(-r, r)
+	for yo := 0; yo < fontHeight; yo++ {
+		for xo := 0; xo < fontWidth; xo++ {
+			if digit[yo*fontWidth+xo] != blackChar {
+				continue
+			}
+			m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1)
+		}
+		xs += skf
+		x = int(xs)
+	}
+}
+
+func (m *Image) distort(amplude float64, period float64) {
+	w := m.Bounds().Max.X
+	h := m.Bounds().Max.Y
+
+	oldm := m.Paletted
+	newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette)
+
+	dx := 2.0 * math.Pi / period
+	for x := 0; x < w; x++ {
+		for y := 0; y < h; y++ {
+			xo := amplude * math.Sin(float64(y)*dx)
+			yo := amplude * math.Cos(float64(x)*dx)
+			newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo)))
+		}
+	}
+	m.Paletted = newm
+}
+
+func randomBrightness(c color.RGBA, max uint8) color.RGBA {
+	minc := min3(c.R, c.G, c.B)
+	maxc := max3(c.R, c.G, c.B)
+	if maxc > max {
+		return c
+	}
+	n := randIntn(int(max-maxc)) - int(minc)
+	return color.RGBA{
+		uint8(int(c.R) + n),
+		uint8(int(c.G) + n),
+		uint8(int(c.B) + n),
+		uint8(c.A),
+	}
+}
+
+func min3(x, y, z uint8) (m uint8) {
+	m = x
+	if y < m {
+		m = y
+	}
+	if z < m {
+		m = z
+	}
+	return
+}
+
+func max3(x, y, z uint8) (m uint8) {
+	m = x
+	if y > m {
+		m = y
+	}
+	if z > m {
+		m = z
+	}
+	return
+}

+ 52 - 0
vendor/github.com/astaxie/beego/utils/captcha/image_test.go

@@ -0,0 +1,52 @@
+// 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 captcha
+
+import (
+	"testing"
+
+	"github.com/astaxie/beego/utils"
+)
+
+type byteCounter struct {
+	n int64
+}
+
+func (bc *byteCounter) Write(b []byte) (int, error) {
+	bc.n += int64(len(b))
+	return len(b), nil
+}
+
+func BenchmarkNewImage(b *testing.B) {
+	b.StopTimer()
+	d := utils.RandomCreateBytes(challengeNums, defaultChars...)
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		NewImage(d, stdWidth, stdHeight)
+	}
+}
+
+func BenchmarkImageWriteTo(b *testing.B) {
+	b.StopTimer()
+	d := utils.RandomCreateBytes(challengeNums, defaultChars...)
+	b.StartTimer()
+	counter := &byteCounter{}
+	for i := 0; i < b.N; i++ {
+		img := NewImage(d, stdWidth, stdHeight)
+		img.WriteTo(counter)
+		b.SetBytes(counter.n)
+		counter.n = 0
+	}
+}

+ 277 - 0
vendor/github.com/astaxie/beego/utils/captcha/siprng.go

@@ -0,0 +1,277 @@
+// 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 captcha
+
+import (
+	"crypto/rand"
+	"encoding/binary"
+	"io"
+	"sync"
+)
+
+// siprng is PRNG based on SipHash-2-4.
+type siprng struct {
+	mu          sync.Mutex
+	k0, k1, ctr uint64
+}
+
+// siphash implements SipHash-2-4, accepting a uint64 as a message.
+func siphash(k0, k1, m uint64) uint64 {
+	// Initialization.
+	v0 := k0 ^ 0x736f6d6570736575
+	v1 := k1 ^ 0x646f72616e646f6d
+	v2 := k0 ^ 0x6c7967656e657261
+	v3 := k1 ^ 0x7465646279746573
+	t := uint64(8) << 56
+
+	// Compression.
+	v3 ^= m
+
+	// Round 1.
+	v0 += v1
+	v1 = v1<<13 | v1>>(64-13)
+	v1 ^= v0
+	v0 = v0<<32 | v0>>(64-32)
+
+	v2 += v3
+	v3 = v3<<16 | v3>>(64-16)
+	v3 ^= v2
+
+	v0 += v3
+	v3 = v3<<21 | v3>>(64-21)
+	v3 ^= v0
+
+	v2 += v1
+	v1 = v1<<17 | v1>>(64-17)
+	v1 ^= v2
+	v2 = v2<<32 | v2>>(64-32)
+
+	// Round 2.
+	v0 += v1
+	v1 = v1<<13 | v1>>(64-13)
+	v1 ^= v0
+	v0 = v0<<32 | v0>>(64-32)
+
+	v2 += v3
+	v3 = v3<<16 | v3>>(64-16)
+	v3 ^= v2
+
+	v0 += v3
+	v3 = v3<<21 | v3>>(64-21)
+	v3 ^= v0
+
+	v2 += v1
+	v1 = v1<<17 | v1>>(64-17)
+	v1 ^= v2
+	v2 = v2<<32 | v2>>(64-32)
+
+	v0 ^= m
+
+	// Compress last block.
+	v3 ^= t
+
+	// Round 1.
+	v0 += v1
+	v1 = v1<<13 | v1>>(64-13)
+	v1 ^= v0
+	v0 = v0<<32 | v0>>(64-32)
+
+	v2 += v3
+	v3 = v3<<16 | v3>>(64-16)
+	v3 ^= v2
+
+	v0 += v3
+	v3 = v3<<21 | v3>>(64-21)
+	v3 ^= v0
+
+	v2 += v1
+	v1 = v1<<17 | v1>>(64-17)
+	v1 ^= v2
+	v2 = v2<<32 | v2>>(64-32)
+
+	// Round 2.
+	v0 += v1
+	v1 = v1<<13 | v1>>(64-13)
+	v1 ^= v0
+	v0 = v0<<32 | v0>>(64-32)
+
+	v2 += v3
+	v3 = v3<<16 | v3>>(64-16)
+	v3 ^= v2
+
+	v0 += v3
+	v3 = v3<<21 | v3>>(64-21)
+	v3 ^= v0
+
+	v2 += v1
+	v1 = v1<<17 | v1>>(64-17)
+	v1 ^= v2
+	v2 = v2<<32 | v2>>(64-32)
+
+	v0 ^= t
+
+	// Finalization.
+	v2 ^= 0xff
+
+	// Round 1.
+	v0 += v1
+	v1 = v1<<13 | v1>>(64-13)
+	v1 ^= v0
+	v0 = v0<<32 | v0>>(64-32)
+
+	v2 += v3
+	v3 = v3<<16 | v3>>(64-16)
+	v3 ^= v2
+
+	v0 += v3
+	v3 = v3<<21 | v3>>(64-21)
+	v3 ^= v0
+
+	v2 += v1
+	v1 = v1<<17 | v1>>(64-17)
+	v1 ^= v2
+	v2 = v2<<32 | v2>>(64-32)
+
+	// Round 2.
+	v0 += v1
+	v1 = v1<<13 | v1>>(64-13)
+	v1 ^= v0
+	v0 = v0<<32 | v0>>(64-32)
+
+	v2 += v3
+	v3 = v3<<16 | v3>>(64-16)
+	v3 ^= v2
+
+	v0 += v3
+	v3 = v3<<21 | v3>>(64-21)
+	v3 ^= v0
+
+	v2 += v1
+	v1 = v1<<17 | v1>>(64-17)
+	v1 ^= v2
+	v2 = v2<<32 | v2>>(64-32)
+
+	// Round 3.
+	v0 += v1
+	v1 = v1<<13 | v1>>(64-13)
+	v1 ^= v0
+	v0 = v0<<32 | v0>>(64-32)
+
+	v2 += v3
+	v3 = v3<<16 | v3>>(64-16)
+	v3 ^= v2
+
+	v0 += v3
+	v3 = v3<<21 | v3>>(64-21)
+	v3 ^= v0
+
+	v2 += v1
+	v1 = v1<<17 | v1>>(64-17)
+	v1 ^= v2
+	v2 = v2<<32 | v2>>(64-32)
+
+	// Round 4.
+	v0 += v1
+	v1 = v1<<13 | v1>>(64-13)
+	v1 ^= v0
+	v0 = v0<<32 | v0>>(64-32)
+
+	v2 += v3
+	v3 = v3<<16 | v3>>(64-16)
+	v3 ^= v2
+
+	v0 += v3
+	v3 = v3<<21 | v3>>(64-21)
+	v3 ^= v0
+
+	v2 += v1
+	v1 = v1<<17 | v1>>(64-17)
+	v1 ^= v2
+	v2 = v2<<32 | v2>>(64-32)
+
+	return v0 ^ v1 ^ v2 ^ v3
+}
+
+// rekey sets a new PRNG key, which is read from crypto/rand.
+func (p *siprng) rekey() {
+	var k [16]byte
+	if _, err := io.ReadFull(rand.Reader, k[:]); err != nil {
+		panic(err.Error())
+	}
+	p.k0 = binary.LittleEndian.Uint64(k[0:8])
+	p.k1 = binary.LittleEndian.Uint64(k[8:16])
+	p.ctr = 1
+}
+
+// Uint64 returns a new pseudorandom uint64.
+// It rekeys PRNG on the first call and every 64 MB of generated data.
+func (p *siprng) Uint64() uint64 {
+	p.mu.Lock()
+	if p.ctr == 0 || p.ctr > 8*1024*1024 {
+		p.rekey()
+	}
+	v := siphash(p.k0, p.k1, p.ctr)
+	p.ctr++
+	p.mu.Unlock()
+	return v
+}
+
+func (p *siprng) Int63() int64 {
+	return int64(p.Uint64() & 0x7fffffffffffffff)
+}
+
+func (p *siprng) Uint32() uint32 {
+	return uint32(p.Uint64())
+}
+
+func (p *siprng) Int31() int32 {
+	return int32(p.Uint32() & 0x7fffffff)
+}
+
+func (p *siprng) Intn(n int) int {
+	if n <= 0 {
+		panic("invalid argument to Intn")
+	}
+	if n <= 1<<31-1 {
+		return int(p.Int31n(int32(n)))
+	}
+	return int(p.Int63n(int64(n)))
+}
+
+func (p *siprng) Int63n(n int64) int64 {
+	if n <= 0 {
+		panic("invalid argument to Int63n")
+	}
+	max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
+	v := p.Int63()
+	for v > max {
+		v = p.Int63()
+	}
+	return v % n
+}
+
+func (p *siprng) Int31n(n int32) int32 {
+	if n <= 0 {
+		panic("invalid argument to Int31n")
+	}
+	max := int32((1 << 31) - 1 - (1<<31)%uint32(n))
+	v := p.Int31()
+	for v > max {
+		v = p.Int31()
+	}
+	return v % n
+}
+
+func (p *siprng) Float64() float64 { return float64(p.Int63()) / (1 << 63) }

+ 33 - 0
vendor/github.com/astaxie/beego/utils/captcha/siprng_test.go

@@ -0,0 +1,33 @@
+// 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 captcha
+
+import "testing"
+
+func TestSiphash(t *testing.T) {
+	good := uint64(0xe849e8bb6ffe2567)
+	cur := siphash(0, 0, 0)
+	if cur != good {
+		t.Fatalf("siphash: expected %x, got %x", good, cur)
+	}
+}
+
+func BenchmarkSiprng(b *testing.B) {
+	b.SetBytes(8)
+	p := &siprng{}
+	for i := 0; i < b.N; i++ {
+		p.Uint64()
+	}
+}

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