添加注册验证码接口

master
18650180552 2019-01-31 16:57:22 +08:00
parent 48c5a9c552
commit b133067a85
404 changed files with 38277 additions and 139934 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
glide.yaml
api_user.exe

View File

@ -17,11 +17,17 @@ type ConfAPI struct {
RunMode string `yaml:"runmode"` // 服务运行模式
MaxConn int `yaml:"max_conn"`
Logs LogConfig `yaml:"logs"` // 日志
Redis RedisConfig `yaml:"redis"`
Redis1 EntityRedis `yaml:"redis1"`
Mysql MysqlConfig `yaml:"mysql"` // 认证配置
init bool
}
type EntityRedis struct {
Addr string `yaml:"addr"`
Pwd string `yaml:"password"`
DB int `yaml:"db"`
PoolSize int `yaml:"poolsize"`
}
type RedisConfig struct {
Addr string `yaml:"addr"`
Pwd string `yaml:"password"`
@ -73,6 +79,13 @@ func GetMysqlConfig() *MysqlConfig{
return nil
}
}
func GetRedis1() *EntityRedis {
if gConf.init{
return &gConf.Redis1
}else {
return nil
}
}
func GetLogConfig() *LogConfig {
if gConf.init{
return &gConf.Logs

48
config/redisConf.go Normal file
View File

@ -0,0 +1,48 @@
package config
import (
"fmt"
"github.com/pkg/errors"
"gopkg.in/redis.v4"
"user/logs"
)
var (
gRedis1 *redis.Client
)
func initRedis(addr string, pwd string, db int, coon int) (*redis.Client, error) {
red := redis.NewClient(&redis.Options{
Addr: addr,
Password: pwd,
DB: db,
PoolSize: coon,
})
_, err := red.Ping().Result()
if nil != err {
red.Close()
return nil, errors.New(fmt.Sprintf("fail to ping redis,addr :%s , pwd :%s ,DB :%d", addr, pwd, db))
}
return red, nil
}
func InitRedis() error {
redisConf := GetRedis1()
if nil == redisConf{
return errors.New("Error Config Redis")
}else {
var e error
gRedis1,e = initRedis(GetRedis1().Addr,GetRedis1().Pwd,GetRedis1().DB,GetRedis1().PoolSize)
if nil != e{
logs.Error(e.Error())
return e
}
}
return nil
}
func RedisOne() *redis.Client {
return gRedis1
}

View File

@ -520,13 +520,7 @@ func SendEmailCode(c *gin.Context) {
verify := CreateVerify(6)
session := sessions.Get(c)
session.Set(req.EmailAdress,verify)
/*
var Options *sessions.Options
Options = &sessions.Options{
MaxAge: 60,
}
session.Options(*Options)*/
session.Save()
sendcontent := make( map[string] interface{},1)
sendcontent["subject"] = "邮箱验证码,请注意查收"

View File

@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"io/ioutil"
"log"
@ -12,6 +13,9 @@ import (
"regexp"
"strings"
"text/template"
"time"
"user/controller"
"user/config"
)
type RespJson struct {
@ -49,7 +53,7 @@ func GetTemplateFile(template string) ([]byte, error) {
//静态文件存放地
const PATH_STATIC = "G://GoPath//Email//static//"
type ReqSendEmail struct {
type ReqSendEmailTpl struct {
From string `json:"from"`
To string `json:"to"`
Tittle string `json:"tittle"`
@ -57,8 +61,38 @@ type ReqSendEmail struct {
Template string `json:"template"`
Generate bool `json:"generate"`
}
type ReqSendEmail struct {
Id int `json:"id"`
From string `json:"from"`
To string `json:"to"`
Content string `json:"content"`
}
func SendToMail(title, user, password, host, to, content string) error {
var content_type string
hp := strings.Split(host, ":")
auth := smtp.PlainAuth("", user, password, hp[0])
func SendToMail(title, user, password, host, to, tplname string, content interface{}, mailtype string, ifgenerate bool) error {
msg := []byte("To: " + to + "\r\nFrom: " + user + "\r\nSubject: " + title + "\r\n" +
content_type + "\r\n\r\n" + content + "\r\n")
send_to := strings.Split(to, ";")
//检测是否是邮件地址
for k, _ := range send_to {
match, _ := regexp.MatchString("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?", send_to[k])
if !match {
return errors.New("Format Error")
}
}
err := smtp.SendMail(host, auth, user, send_to, msg)
if err != nil {
return err
}
return err
}
func SendToMailTpl(title, user, password, host, to, tplname string, content interface{}, mailtype string, ifgenerate bool) error {
var content_type string
var paseresult bytes.Buffer
writer := bufio.NewWriter(&paseresult)
@ -168,7 +202,41 @@ func OnSendEmailSendCloud(c *gin.Context){
//content :网页模板的参数 key-value结构
//temp_data 模板内具体要替换的变量名字 Key-value结构
//generate 是否生成静态html
func OnSendEmail(c *gin.Context) {
func OnSendEmailTpl(c *gin.Context) {
var req ReqSendEmailTpl
var resp RespJson
defer func() {
c.JSON(200, resp)
}()
e := c.Bind(&req)
if e != nil {
log.Println(e.Error())
resp.Msg = "ParaErr"
return
}
user := "c7458969@163.com"
password := "caiyu123"
host := "smtp.163.com:25"
//抄送给自己
//e = SendToMail(user,password,host,req.From,req.Template,req.Content,"html")
//发送
e = SendToMailTpl(req.Tittle, user, password, host, req.To, req.Template, req.TempData, "html", req.Generate)
if nil != e {
log.Println(e.Error())
resp.Msg = "Error"
return
}
resp.Msg = "OK"
}
//from: 发送人邮箱
//to:接收邮件,可以是"290198252@qq.com;29019822@qq.com;2901982@qq.com" 邮箱之间用分号隔开
//template:模板名字
//content :网页模板的参数 key-value结构
//temp_data 模板内具体要替换的变量名字 Key-value结构
//generate 是否生成静态html
func OnSendEmailCode(c *gin.Context) {
var req ReqSendEmail
var resp RespJson
defer func() {
@ -187,7 +255,11 @@ func OnSendEmail(c *gin.Context) {
//抄送给自己
//e = SendToMail(user,password,host,req.From,req.Template,req.Content,"html")
//发送
e = SendToMail(req.Tittle, user, password, host, req.To, req.Template, req.TempData, "html", req.Generate)
verCode := controller.CreateVerify(6)
content := "您的验证码是" + verCode
userKey := fmt.Sprintf("user_%d_verify",req.Id)
config.RedisOne().Set(userKey,verCode,time.Hour * 24)
e = SendToMail("后台管理系统验证码", user, password, host, req.To, content)
if nil != e {
log.Println(e.Error())
resp.Msg = "Error"

View File

@ -27,10 +27,14 @@ func main() {
}
logs.Init(config.GetLogConfig().Dir,config.GetLogConfig().File,config.GetLogConfig().Level,config.GetLogConfig().SaveFile)
db.Init()
e = config.InitRedis()
if nil != e{
logs.Error(e.Error())
return
}
r := gin.Default()
store := sessions.NewCookieStore([]byte("secret123"))
r.Use(sessions.Middleware("my_session", store))
{
/** 添加或修改用户 **/
r.POST("/api/user", controller.SetUser)
@ -46,6 +50,8 @@ func main() {
r.POST("/api/register", controller.Register)
/** 用户退出登陆 **/
r.GET("/api/logout", controller.Logout)
r.POST("/api/email_code",controller.SendEmailCode)
}
r.Run(":" + strconv.Itoa(config.GetPort()))
}

View File

@ -6,9 +6,9 @@ logs:
file: "user.log"
level: 1
savefile: false
redis:
addr: 118.24.238.198
password: 6379
redis1:
addr: 118.24.238.198:16379
password:
db: 1
mysql:
addr: 118.24.238.198

202
vendor/cloud.google.com/go/LICENSE generated vendored
View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -1,4 +1,4 @@
// Copyright 2016 Google LLC
// Copyright 2016 Google Inc. 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.

0
vendor/git.jiaxianghudong.com/go/db/README.md generated vendored Normal file
View File

475
vendor/git.jiaxianghudong.com/go/db/db.go generated vendored Normal file
View File

@ -0,0 +1,475 @@
// 数据库工具包
package db
import (
"database/sql"
"errors"
"fmt"
"reflect"
"strconv"
"sync"
"time"
)
// 数据容器抽象对象定义
type Database struct {
Type string // 用来给SqlBuilder进行一些特殊的判断 (空值或mysql 皆表示这是一个MySQL实例)
DB *sql.DB
}
// SQL异步执行队列定义
type queueList struct {
list []*QueueItem //队列列表
sleeping chan bool
loop chan bool
lock sync.RWMutex
quit chan bool
quited bool
}
// SQL异步执行队列子元素定义
type QueueItem struct {
DB *Database //数据库对象
Query string //SQL语句字符串
Params []interface{} //参数列表
}
// 缓存数据对象定义
type cache struct {
data map[string]map[string]interface{}
}
func (this *cache) Init() {
this.data["default"] = make(map[string]interface{})
}
// 设置缓存
func (this *cache) Set(key string, value interface{}, args ...string) {
var group string
if len(args) > 0 {
group = args[0]
if _, exist := this.data[group]; !exist {
this.data[group] = make(map[string]interface{})
}
} else {
group = "default"
}
this.data[group][key] = value
}
// 获取缓存数据
func (this *cache) Get(key string, args ...string) interface{} {
var group string
if len(args) > 0 {
group = args[0]
} else {
group = "default"
}
if g, exist := this.data[group]; exist {
if v, ok := g[key]; ok {
return v
}
}
return nil
}
// 删除缓存数据
func (this *cache) Del(key string, args ...string) {
var group string
if len(args) > 0 {
group = args[0]
} else {
group = "default"
}
if g, exist := this.data[group]; exist {
if _, ok := g[key]; ok {
delete(this.data[group], key)
}
}
}
var (
lastError error
Cache *cache
queue *queueList
Obj *Database
)
func init() {
Cache = &cache{data: make(map[string]map[string]interface{})}
Cache.Init()
queue = &queueList{}
go queue.Start()
}
// 关闭数据库连接
func (this *Database) Close() {
this.DB.Close()
}
// 获取最后发生的错误字符串
func LastErr() string {
if lastError != nil {
return lastError.Error()
}
return ""
}
// 执行语句
func (this *Database) Exec(query string, args ...interface{}) (sql.Result, error) {
return this.DB.Exec(query, args...)
}
// 查询单条记录
func (this *Database) Query(query string, args ...interface{}) (*sql.Rows, error) {
return this.DB.Query(query, args...)
}
// 查询单条记录
func (this *Database) QueryRow(query string, args ...interface{}) *sql.Row {
return this.DB.QueryRow(query, args...)
}
// Query2 查询实体集合
// obj 为接收数据的实体指针
func (this *Database) Query2(sql string, obj interface{}, args ...interface{}) error {
var tagMap map[string]int
var tp, tps reflect.Type
var n, i int
var err error
var ret reflect.Value
// 检测val参数是否为我们所想要的参数
tp = reflect.TypeOf(obj)
if reflect.Ptr != tp.Kind() {
return errors.New("is not pointer")
}
if reflect.Slice != tp.Elem().Kind() {
return errors.New("is not slice pointer")
}
tp = tp.Elem()
tps = tp.Elem()
if reflect.Struct != tps.Kind() {
return errors.New("is not struct slice pointer")
}
tagMap = make(map[string]int)
n = tps.NumField()
for i = 0; i < n; i++ {
tag := tps.Field(i).Tag.Get("sql")
if len(tag) > 0 {
tagMap[tag] = i + 1
}
}
// 执行查询
ret, err = this.queryAndReflect(sql, tagMap, tp, args...)
if nil != err {
return err
}
// 返回结果
reflect.ValueOf(obj).Elem().Set(ret)
return nil
}
// queryAndReflect 查询并将结果反射成实体集合
func (this *Database) queryAndReflect(sql string,
tagMap map[string]int,
tpSlice reflect.Type, args ...interface{}) (reflect.Value, error) {
var ret reflect.Value
// 执行sql语句
rows, err := this.DB.Query(sql, args...)
if nil != err {
return reflect.Value{}, err
}
defer rows.Close()
// 开始枚举结果
cols, err := rows.Columns()
if nil != err {
return reflect.Value{}, err
}
ret = reflect.MakeSlice(tpSlice, 0, 50)
// 构建接收队列
scan := make([]interface{}, len(cols))
row := make([]interface{}, len(cols))
for r := range row {
scan[r] = &row[r]
}
for rows.Next() {
feild := reflect.New(tpSlice.Elem()).Elem()
// 取得结果
err = rows.Scan(scan...)
// 开始遍历结果
for i := 0; i < len(cols); i++ {
n := tagMap[cols[i]] - 1
if n < 0 {
continue
}
switch feild.Type().Field(n).Type.Kind() {
case reflect.Bool:
if nil != row[i] {
feild.Field(n).SetBool("false" != string(row[i].([]byte)))
} else {
feild.Field(n).SetBool(false)
}
case reflect.String:
if nil != row[i] {
feild.Field(n).SetString(string(row[i].([]byte)))
} else {
feild.Field(n).SetString("")
}
case reflect.Float32:
fallthrough
case reflect.Float64:
if nil != row[i] {
v, e := strconv.ParseFloat(string(row[i].([]byte)), 0)
if nil == e {
feild.Field(n).SetFloat(v)
}
} else {
feild.Field(n).SetFloat(0)
}
case reflect.Int8:
fallthrough
case reflect.Int16:
fallthrough
case reflect.Int32:
fallthrough
case reflect.Int64:
fallthrough
case reflect.Int:
if nil != row[i] {
byRow, ok := row[i].([]byte)
if ok {
v, e := strconv.ParseInt(string(byRow), 10, 64)
if nil == e {
feild.Field(n).SetInt(v)
}
} else {
v, e := strconv.ParseInt(fmt.Sprint(row[i]), 10, 64)
if nil == e {
feild.Field(n).SetInt(v)
}
}
} else {
feild.Field(n).SetInt(0)
}
}
}
ret = reflect.Append(ret, feild)
}
return ret, nil
}
// 执行UPDATE语句并返回受影响的行数
// 返回0表示没有出错, 但没有被更新的行
// 返回-1表示出错
func (this *Database) Update(query string, args ...interface{}) (int64, error) {
ret, err := this.Exec(query, args...)
if err != nil {
return -1, err
}
aff, err := ret.RowsAffected()
if err != nil {
return -1, err
}
return aff, nil
}
// 执行DELETE语句并返回受影响的行数
// 返回0表示没有出错, 但没有被删除的行
// 返回-1表示出错
func (this *Database) Delete(query string, args ...interface{}) (int64, error) {
return this.Update(query, args...)
}
// 执行INSERT语句并返回最后生成的自增ID
// 返回0表示没有出错, 但没生成自增ID
// 返回-1表示出错
func (this *Database) Insert(query string, args ...interface{}) (int64, error) {
ret, err := this.Exec(query, args...)
if err != nil {
return -1, err
}
last, err := ret.LastInsertId()
if err != nil {
return -1, err
}
return last, nil
}
type OneRow map[string]string
type Results []OneRow
// 判断字段是否存在
func (row OneRow) Exist(field string) bool {
if _, ok := row[field]; ok {
return true
}
return false
}
// 获取指定字段的值
func (row OneRow) Get(field string) string {
if v, ok := row[field]; ok {
return v
}
return ""
}
// 获取指定字段的整数值, 注意, 如果该字段不存在则会返回0
func (row OneRow) GetInt(field string) int {
if v, ok := row[field]; ok {
return Atoi(v)
}
return 0
}
// 获取指定字段的整数值, 注意, 如果该字段不存在则会返回0
func (row OneRow) GetInt64(field string) int64 {
if v, ok := row[field]; ok {
return Atoi64(v)
}
return 0
}
// 设置值
func (row OneRow) Set(key, val string) {
row[key] = val
}
// 查询不定字段的结果集
func (this *Database) Select(query string, args ...interface{}) (Results, error) {
rows, err := this.DB.Query(query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
cols, err := rows.Columns()
if err != nil {
return nil, err
}
colNum := len(cols)
rawValues := make([][]byte, colNum)
scans := make([]interface{}, len(cols)) //query.Scan的参数因为每次查询出来的列是不定长的所以传入长度固定当次查询的长度
// 将每行数据填充到[][]byte里
for i := range rawValues {
scans[i] = &rawValues[i]
}
results := make(Results, 0)
for rows.Next() {
err = rows.Scan(scans...)
if err != nil {
return nil, err
}
row := make(map[string]string)
for k, raw := range rawValues {
key := cols[k]
/*if raw == nil {
row[key] = "\\N"
} else {*/
row[key] = string(raw)
//}
}
results = append(results, row)
}
return results, nil
}
// 查询一行不定字段的结果
func (this *Database) SelectOne(query string, args ...interface{}) (OneRow, error) {
ret, err := this.Select(query, args...)
if err != nil {
return nil, err
}
if len(ret) > 0 {
return ret[0], nil
}
return make(OneRow), nil
}
// 队列入栈
func (this *queueList) Push(item *QueueItem) {
this.lock.Lock()
this.list = append(this.list, item)
this.lock.Unlock()
}
// 队列出栈
func (this *queueList) Pop() chan *QueueItem {
item := make(chan *QueueItem)
go func() {
defer close(item)
for {
switch {
case len(this.list) == 0:
timeout := time.After(time.Second * 2)
select {
case <-this.quit:
this.quited = true
return
case <-timeout:
//log.Println("SQL Queue polling")
}
default:
this.lock.Lock()
i := this.list[0]
this.list = this.list[1:]
this.lock.Unlock()
select {
case item <- i:
return
case <-this.quit:
this.quited = true
return
}
}
}
}()
return item
}
// 执行开始执行
func (this *queueList) Start() {
for {
if this.quited {
return
}
c := this.Pop()
item := <-c
item.DB.Exec(item.Query, item.Params...)
}
}
// 停止队列
func (this *queueList) Stop() {
this.quit <- true
}
// 向Sql队列中插入一条执行语句
func (this *Database) Queue(query string, args ...interface{}) {
item := &QueueItem{
DB: this,
Query: query,
Params: args,
}
queue.Push(item)
}

76
vendor/git.jiaxianghudong.com/go/db/mssql.go generated vendored Normal file
View File

@ -0,0 +1,76 @@
package db
import (
"database/sql"
_ "github.com/denisenkom/go-mssqldb"
)
// ProcExec 执行存储过程, 返回受影响的行数
func (this *Database) ExecProc(procname string, params ...interface{}) (int64, error) {
result, err := this.Exec("EXEC " + procname + " " + this.GetProcPlaceholder(len(params)), params...)
if err != nil {
return 0, err
}
affected, err := result.RowsAffected()
if err != nil {
return 0, err
}
lastinsertid, err := result.LastInsertId()
if err != nil {
return affected, nil
}
return lastinsertid, nil
}
// GetExecProcErr 执行存储过程, 返回是否在执行过程中出现错误
func (this *Database) GetExecProcErr(procname string, params ...interface{}) error {
_, err := this.ExecProc(procname, params...)
if err != nil {
return err
}
return nil
}
// ProcQuery 通过存储过程查询记录
func (this *Database) ProcQuery(procname string, params ...interface{}) (rows *sql.Rows, err error) {
rows, err = this.Query("EXEC " + procname + " " + this.GetProcPlaceholder(len(params)), params...)
return
}
// ProcQueryRow 通过存储过程查询单条记录
func (this *Database) ProcQueryRow(procname string, params ...interface{}) *sql.Row {
return this.QueryRow("EXEC " + procname + " " + this.GetProcPlaceholder(len(params)), params...)
}
// ProcStatus 调用存储过程并获取最终的执行状态码和提示信息
func (this *Database) ProcStatus(procname string, params ...interface{}) (int, string) {
var status int
var msg string
err := this.QueryRow("EXEC " + procname + " " + this.GetProcPlaceholder(len(params)), params...).Scan(&status, &msg)
if err != nil {
return -99, err.Error()
}
return status, msg
}
// ProcSelect 通过存储过程查询结果集
func (this *Database) ProcSelect(procname string, params ...interface{}) (Results, error) {
return this.Select("EXEC " + procname + " " + this.GetProcPlaceholder(len(params)), params...)
}
// ProcSelectOne 通过存储查询一行不定字段的结果
func (this *Database) ProcSelectOne(procname string, params ...interface{}) (OneRow, error) {
return this.SelectOne("EXEC " + procname + " " + this.GetProcPlaceholder(len(params)), params...)
}
// GetProcPlaceholder 按照指定数量生成调用存储过程时所用的参数占位符
func (this *Database) GetProcPlaceholder(count int) (placeholder string) {
placeholder = ""
for i := 0; i < count; i++ {
if i > 0 {
placeholder += ","
}
placeholder += "?"
}
return
}

542
vendor/git.jiaxianghudong.com/go/db/sql_builder.go generated vendored Normal file
View File

@ -0,0 +1,542 @@
package db
import (
"database/sql"
"errors"
"strconv"
"strings"
"log"
"fmt"
"reflect"
"math/big"
"git.jiaxianghudong.com/go/utils"
)
const (
_ = iota
TYPE_INSERT
TYPE_DELETE
TYPE_UPDATE
TYPE_SELECT
TYPE_INSERTUPDATE
)
var (
WrapSymbol = "`"
DBType = "mysql"
)
// SQL语句构造结构
type SB struct {
db *Database
t int
field, table, where, group, order, limit string
values SBValues
values2 SBValues
ignore bool
fullsql bool
debug bool
unsafe bool //是否进行安全检查, 专门针对无限定的UPDATE和DELETE进行二次验证
args []interface{}
}
// Exec返回结果
type SBResult struct {
Success bool //语句是否执行成功
Code int //错误代码
Msg string //错误提示信息
LastID int64 //最后产生的ID
Affected int64 //受影响的行数
Sql string //最后执行的SQL
}
// 值对象
type SBValues map[string]interface{}
// 增量值
type IncVal struct {
Val int64
BaseField string // 为空表示对当前字段累加
}
// 向值对象中加入值
func (v SBValues) Add(key string, val interface{}) {
v[key] = val
}
// 删除值对象中的某个值
func (v SBValues) Del(key string) {
delete(v, key)
}
// 判断指定键是否存在
func (v SBValues) IsExist(key string) bool {
if _, exist := v[key]; exist {
return true
}
return false
}
// 获取键的整形值
func (v SBValues) Get(key string) interface{} {
if val, exist := v[key]; exist {
return val
}
return nil
}
// 获取键的字符串值
func (v SBValues) GetString(key string) string {
if val, exist := v[key]; exist {
if trueVal, ok := val.(string); ok {
return trueVal
}
}
return ""
}
// 获取键的整形值
func (v SBValues) GetInt(key string) int {
if val, exist := v[key]; exist {
if trueVal, ok := val.(int); ok {
return trueVal
}
}
return 0
}
// 获取键的无符号整形值
func (v SBValues) GetUint(key string) uint {
if val, exist := v[key]; exist {
if trueVal, ok := val.(uint); ok {
return trueVal
}
}
return 0
}
// 获取键的64位整形值
func (v SBValues) GetInt64(key string) int64 {
if val, exist := v[key]; exist {
if trueVal, ok := val.(int64); ok {
return trueVal
}
}
return 0
}
// 返回绑定完参数的完整的SQL语句
func FullSql(str string, args ...interface{}) (string, error) {
if !strings.Contains(str, "?") {
return str, nil
}
sons := strings.Split(str, "?")
var ret string
var argIndex int
var maxArgIndex = len(args)
for _, son := range sons {
ret += son
if argIndex < maxArgIndex {
switch v := args[argIndex].(type) {
case int:
ret += strconv.Itoa(v)
case int8:
ret += strconv.Itoa(int(v))
case int16:
ret += strconv.Itoa(int(v))
case int32:
ret += utils.I64toA(int64(v))
case int64:
ret += utils.I64toA(v)
case uint:
ret += utils.UitoA(v)
case uint8:
ret += utils.UitoA(uint(v))
case uint16:
ret += utils.UitoA(uint(v))
case uint32:
ret += utils.Ui32toA(v)
case uint64:
ret += utils.Ui64toA(v)
case float32:
ret += utils.F32toA(v)
case float64:
ret += utils.F64toA(v)
case *big.Int:
ret += v.String()
case bool:
if v {
ret += "true"
} else {
ret += "false"
}
case string:
ret += "'" + strings.Replace(strings.Replace(v, "'", "", -1), `\`, `\\`, -1) + "'"
case nil:
ret += "NULL"
default:
return "", errors.New(fmt.Sprintf("invalid sql argument type: %v => %v (sql: %s)", reflect.TypeOf(v).String(), v, str))
}
argIndex++
}
}
return ret, nil
}
// 构建SQL语句
// param: returnFullSql 是否返回完整的sql语句(即:绑定参数之后的语句)
func (q *SB) ToSql(returnFullSql ...bool) (str string, err error) {
q.args = make([]interface{}, 0)
switch q.t {
case TYPE_INSERT:
if q.table == "" {
err = errors.New("table cannot be empty.")
return
}
if len(q.values) == 0 {
err = errors.New("values cannot be empty.")
return
}
if q.ignore {
str = "INSERT IGNORE INTO " + q.table
} else {
str = "INSERT INTO " + q.table
}
var fields, placeholder string
for k, v := range q.values {
fields += "," + WrapSymbol + k + WrapSymbol
placeholder += ",?"
q.args = append(q.args, v)
}
str += " (" + utils.Substr(fields, 1) + ") VALUES (" + utils.Substr(placeholder, 1) + ")"
case TYPE_DELETE:
if q.table != "" {
if q.where == "" && !q.unsafe {
err = errors.New("deleting all data is not safe.")
return
}
str = "DELETE " + q.table
if q.table != "" {
str += " FROM " + q.table
}
if q.where != "" {
str += " WHERE " + q.where
}
}
case TYPE_UPDATE:
if q.table != "" {
if q.where == "" && !q.unsafe {
err = errors.New("updating all data is not safe.")
return
}
str = "UPDATE " + q.table
str += " SET " + utils.Substr(q.buildUpdateParams(q.values), 1)
if q.where != "" {
str += " WHERE " + q.where
}
}
case TYPE_INSERTUPDATE:
if q.table != "" {
str = "INSERT INTO " + q.table
var fields, placeholder string
for k, v := range q.values {
fields += "," + WrapSymbol + k + WrapSymbol
placeholder += ",?"
q.args = append(q.args, v)
}
str += " (" + utils.Substr(fields, 1) + ") VALUES (" + utils.Substr(placeholder, 1) + ") ON DUPLICATE KEY UPDATE "
placeholder = q.buildUpdateParams(q.values2)
str += utils.Substr(placeholder, 1)
}
case TYPE_SELECT:
str = "SELECT " + q.field
if q.table != "" {
str += " FROM " + q.table
}
if q.where != "" {
str += " WHERE " + q.where
}
if q.group != "" {
str += " GROUP BY " + q.group
}
if q.order != "" {
str += " ORDER BY " + q.order
}
if q.limit != "" && (q.db.Type == "" || q.db.Type == "mysql") {
str += " LIMIT " + q.limit
}
}
if len(returnFullSql) == 1 && returnFullSql[0] {
str, err = FullSql(str, q.args...)
}
return
}
// 构造Update更新参数
func (q *SB) buildUpdateParams(vals SBValues) string {
var placeholder string
for k, v := range vals {
if iv, ok := v.(IncVal); ok {
placeholder += "," + WrapSymbol + k + WrapSymbol + "=" + utils.Ternary(iv.BaseField == "", k, iv.BaseField).(string)
if iv.Val >= 0 {
placeholder += "+" + utils.I64toA(iv.Val)
} else {
placeholder += utils.I64toA(iv.Val)
}
} else {
placeholder += "," + WrapSymbol + k + WrapSymbol + "=?"
q.args = append(q.args, v)
}
}
return placeholder
}
// 设置数据库对象
func (q *SB) DB(db *Database) *SB {
q.db = db
return q
}
// 设置FROM字句
func (q *SB) From(str string) *SB {
q.table = str
return q
}
// 设置表名
func (q *SB) Table(str string) *SB {
return q.From(str)
}
// 设置WHERE字句
func (q *SB) Where(str string) *SB {
q.where = str
return q
}
// 设置GROUP字句
func (q *SB) Group(str string) *SB {
q.group = str
return q
}
// 设置GROUP字句
func (q *SB) Order(str string) *SB {
q.order = str
return q
}
// 设置LIMIT字句
func (q *SB) Limit(count int, offset ...int) *SB {
if len(offset) > 0 {
q.limit = utils.Itoa(offset[0]) + "," + utils.Itoa(count)
} else {
q.limit = "0," + utils.Itoa(count)
}
return q
}
// 设置安全检查开关
func (q *SB) Unsafe(unsefe ...bool) *SB {
if len(unsefe) == 1 && !unsefe[0] {
q.unsafe = false
} else {
q.unsafe = true
}
return q
}
// 是否Debug
func (q *SB) Debug(debug ...bool) *SB {
if len(debug) == 1 && !debug[0] {
q.debug = false
} else {
q.debug = true
}
return q
}
// 设置值
func (q *SB) Value(m SBValues) *SB {
q.values = m
return q
}
// 设置值2
func (q *SB) Value2(m SBValues) *SB {
q.values2 = m
return q
}
// 添加值
func (q *SB) AddValue(key string, val interface{}) *SB {
q.values.Add(key, val)
return q
}
// 添加值2
func (q *SB) AddValue2(key string, val interface{}) *SB {
q.values2.Add(key, val)
return q
}
// 获取一个值对象
func NewValues() SBValues {
return SBValues{}
}
// 构建INSERT语句
func Insert(ignore ...bool) *SB {
var i bool
if len(ignore) == 1 && ignore[0] {
i = true
}
return &SB{t: TYPE_INSERT, db: Obj, ignore: i, values: SBValues{}, args: make([]interface{}, 0)}
}
// 构建DELETE语句
func Delete() *SB {
return &SB{t: TYPE_DELETE, db: Obj}
}
// 构建UPDATE语句
func Update() *SB {
return &SB{t: TYPE_UPDATE, db: Obj, values: SBValues{}, args: make([]interface{}, 0)}
}
// 构建InsertUpdate语句, 仅针对MySQL有效, 内部使用ON DUPLICATE KEY UPDATE方式实现
func InsertUpdate() *SB {
return &SB{t: TYPE_INSERTUPDATE, db: Obj, values: SBValues{}, values2: SBValues{}, args: make([]interface{}, 0)}
}
// 构建SELECT语句
func Select(str ...string) *SB {
fields := "*"
if len(str) == 1 {
fields = str[0]
}
return &SB{t: TYPE_SELECT, db: Obj, field: fields}
}
// 获取构造SQL后的参数
func (q *SB) GetArgs() []interface{} {
return q.args
}
//
func (q *SB) FullSql(yes ...bool) *SB {
if len(yes) == 1 {
q.fullsql = yes[0]
} else {
q.fullsql = true
}
return q
}
// 执行INSERT、DELETE、UPDATE语句
func (q *SB) Exec(args ...interface{}) *SBResult {
var err error
sbRet := &SBResult{}
sbRet.Sql, err = q.ToSql()
if err != nil {
sbRet.Msg = err.Error()
} else {
if q.debug {
log.Println("\n\tSQL prepare statement:\n\t", sbRet.Sql, "\n\tMap args:\n\t", q.args, "\n\tParams:\n\t", args)
}
var ret sql.Result
var err error
if q.fullsql {
var sqlStr string
sqlStr, err = FullSql(sbRet.Sql, append(q.args, args...)...)
if err == nil {
ret, err = q.db.Exec(sqlStr)
}
} else {
ret, err = q.db.Exec(sbRet.Sql, append(q.args, args...)...)
}
if err != nil {
sbRet.Msg = err.Error()
} else {
sbRet.Success = true
switch q.t {
case TYPE_INSERT:
if DBType == "mysql" {
last, err := ret.LastInsertId()
if (err == nil) {
sbRet.LastID = last;
}
}
case TYPE_DELETE:
fallthrough
case TYPE_UPDATE:
fallthrough
case TYPE_INSERTUPDATE:
aff, err := ret.RowsAffected()
if (err == nil) {
sbRet.Affected = aff
}
}
}
}
return sbRet
}
// 查询记录集
func (q *SB) Query(args ...interface{}) (Results, error) {
s, e := q.ToSql()
if e != nil {
return nil, e
}
if q.debug {
log.Println("\n\tSQL prepare statement:\n\t", s, "\n\tParams:\n\t", args)
}
return q.db.Select(s, args...)
}
// 查询单行数据
func (q *SB) QueryOne(args ...interface{}) (OneRow, error) {
q.Limit(1, 0)
s, e := q.ToSql()
if e != nil {
return nil, e
}
if q.debug {
log.Println("\n\tSQL prepare statement:\n\t", s, "\n\tParams:\n\t", args)
}
return q.db.SelectOne(s, args...)
}
// 查询记录集
func (q *SB) QueryAllRow(args ...interface{}) (*sql.Rows, error) {
s, e := q.ToSql()
if e != nil {
return nil, e
}
if q.debug {
log.Println("\n\tSQL prepare statement:\n\t", s, "\n\tParams:\n\t", args)
}
return q.db.Query(s, args...)
}
// 查询单行数据
func (q *SB) QueryRow(args ...interface{}) *sql.Row {
s, e := q.ToSql()
if e != nil {
return nil
}
if q.debug {
log.Println("\n\tSQL prepare statement:\n\t", s, "\n\tParams:\n\t", args)
}
return q.db.QueryRow(s, args...)
}

66
vendor/git.jiaxianghudong.com/go/db/utils.go generated vendored Normal file
View File

@ -0,0 +1,66 @@
package db
import (
"strings"
"strconv"
"database/sql"
)
// 根据传入的字段列表生成相符数量的占位符
func GetPlaceholderByFields(fileds string) string {
fileds = strings.Replace(fileds, " ", "", -1)
fileds = strings.Trim(fileds, ",")
count := len(strings.Split(fileds, ","))
ret := make([]string, count)
for i := 0; i < count; i++ {
ret[i] = "?"
}
return strings.Join(ret, ",")
}
// Atoi 转换成整型
func Atoi(s string, d ...int) int {
i, err := strconv.Atoi(s)
if err != nil {
if len(d) > 0 {
return d[0]
} else {
return 0
}
}
return i
}
// Atoi64 转换成整型int64
func Atoi64(s string, d ...int64) int64 {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
if len(d) > 0 {
return d[0]
} else {
return 0
}
}
return i
}
// 返回一个带有Null值的数据库字符串
func NewNullString(s string) sql.NullString {
if len(s) == 0 {
return sql.NullString{}
}
return sql.NullString{
String: s,
Valid: true,
}
}
// 返回一个带有Null值的数据库整形
func NewNullInt64(s int64, isNull bool) sql.NullInt64 {
return sql.NullInt64{
Int64: s,
Valid: !isNull,
}
}

View File

@ -15,21 +15,33 @@ const (
)
var log *mylog
var LogMap map[string]mylog
var logDir string
/*
*
*/
func init() {
log = newMylog()
LogMap=make(map[string]mylog)
}
func Init(dir string, file string, level int, savefile bool) {
log.setDir(dir)
logDir=dir
log.setDir(dir+ "/"+fmt.Sprintf("%04d%02d%02d",time.Now().Year(), time.Now().Month(), time.Now().Day()))
log.setFile(file)
log.setLevel(level)
log.setSavefile(savefile)
}
func InitExtLogs(dir string, file string, level int, savefile bool) {
log := newMylog()
log.setDir(dir+"/"+ fmt.Sprintf("%04d%02d%02d",time.Now().Year(), time.Now().Month(), time.Now().Day()))
log.setFile(file)
log.setLevel(level)
log.setSavefile(savefile)
LogMap[file]=*log
}
func Error(err ...interface{}) {
log.write(LOG_ERROR, fmt.Sprint(err...))
}
@ -37,9 +49,7 @@ func Error(err ...interface{}) {
func Waring(war ...interface{}) {
log.write(LOG_WARING, fmt.Sprint(war...))
}
func SetLevel(level int) {
log.setLevel(level)
}
func Info(info ...interface{}) {
log.write(LOG_INFO, fmt.Sprint(info...))
}
@ -48,6 +58,23 @@ func Debug(deb ...interface{}) {
log.write(LOG_DEBUG, fmt.Sprint(deb...))
}
func (log *mylog)Error(err ...interface{}) {
log.write(LOG_ERROR, fmt.Sprint(err...))
}
func (log *mylog)Waring(war ...interface{}) {
log.write(LOG_WARING, fmt.Sprint(war...))
}
func (log *mylog)Info(info ...interface{}) {
log.write(LOG_INFO, fmt.Sprint(info...))
}
func (log *mylog)Debug(deb ...interface{}) {
fmt.Println(*log)
log.write(LOG_DEBUG, fmt.Sprint(deb...))
}
/*
*
*/
@ -128,15 +155,15 @@ func (l *mylog) write(level int, str string) {
func (l *mylog) run() {
for {
str := <-l.log
t:=time.Now()
// 判断文件夹是否存在
_, err := os.Stat(l.dir)
_, err := os.Stat(l.dir+"/"+fmt.Sprintf("%04d%02d%02d",t.Year(), t.Month(), t.Day()))
if nil != err {
l.dir=logDir+"/"+fmt.Sprintf("%04d%02d%02d",t.Year(), t.Month(), t.Day())
os.MkdirAll(l.dir, os.ModePerm)
}
// 获取时间
t := time.Now()
path := fmt.Sprintf("%s/%s-%04d-%02d-%02d.log", l.dir, l.file,
t.Year(), t.Month(), t.Day())
fp, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModePerm)

View File

@ -1,20 +1,11 @@
package utils
import (
"bytes"
"encoding/binary"
// "errors"
// "fmt"
// "reflect"
// "unsafe"
)
// Put8bit 加入8字节到[]byte中
// 加入8字节
func Put8bit(buf []byte, n byte) []byte {
return append(buf, n)
}
// Put16bit 加入16字节
// 加入16字节
func Put16bit(buf []byte, n uint16) []byte {
var by [2]byte
@ -24,7 +15,7 @@ func Put16bit(buf []byte, n uint16) []byte {
return append(buf, by[:]...)
}
// Put32bit 加入32字节
// 加入32字节
func Put32bit(buf []byte, n uint32) []byte {
var by [4]byte
@ -36,7 +27,7 @@ func Put32bit(buf []byte, n uint32) []byte {
return append(buf, by[:]...)
}
// Put64bit 加入64字节
// 加入64字节
func Put64bit(buf []byte, n uint64) []byte {
var by [8]byte
@ -52,12 +43,12 @@ func Put64bit(buf []byte, n uint64) []byte {
return append(buf, by[:]...)
}
// Get8bit 获取8bit
// 获取8bit
func Get8bit(buf []byte, start int) byte {
return buf[start]
}
// Get16bit 获取16bit
// 获取16bit
func Get16bit(buf []byte, start int) uint16 {
var ret uint16
@ -67,7 +58,7 @@ func Get16bit(buf []byte, start int) uint16 {
return ret
}
// Get32bit 获取32big
// 获取32big
func Get32bit(buf []byte, start int) uint32 {
var ret uint32
@ -79,7 +70,7 @@ func Get32bit(buf []byte, start int) uint32 {
return ret
}
// Get64bit 获取64bit
// 获取64bit
func Get64bit(buf []byte, start int) uint64 {
var ret uint64
@ -94,114 +85,3 @@ func Get64bit(buf []byte, start int) uint64 {
return ret
}
// BytesToInt 字节数组转int
func BytesToInt(b []byte) int {
buf := bytes.NewBuffer(b)
var x int
binary.Read(buf, binary.BigEndian, &x)
return int(x)
}
// IntToBytes int转字节数组
func IntToBytes(n int) []byte {
buf := bytes.NewBuffer([]byte{})
binary.Write(buf, binary.BigEndian, n)
return buf.Bytes()
}
// BytesToInt16 字节数组转int16
func BytesToInt16(b []byte) int16 {
buf := bytes.NewBuffer(b)
var x int16
binary.Read(buf, binary.BigEndian, &x)
return int16(x)
}
// Int16ToBytes int16转字节数组
func Int16ToBytes(n int16) []byte {
x := int16(n)
buf := bytes.NewBuffer([]byte{})
binary.Write(buf, binary.BigEndian, x)
return buf.Bytes()
}
// BytesToInt32 字节数组转int32
func BytesToInt32(b []byte) int32 {
buf := bytes.NewBuffer(b)
var x int32
binary.Read(buf, binary.BigEndian, &x)
return int32(x)
}
// Int32ToBytes int32转字节数组
func Int32ToBytes(n int32) []byte {
x := int32(n)
buf := bytes.NewBuffer([]byte{})
binary.Write(buf, binary.BigEndian, x)
return buf.Bytes()
}
// 字节数组转int64
func BytesToInt64(b []byte) int64 {
buf := bytes.NewBuffer(b)
var x int64
binary.Read(buf, binary.BigEndian, &x)
return int64(x)
}
// Int64ToBytes int64转字节数组
func Int64ToBytes(n int64) []byte {
buf := bytes.NewBuffer([]byte{})
binary.Write(buf, binary.BigEndian, n)
return buf.Bytes()
}
// BytesToUInt64 字节数组转uint64
func BytesToUInt64(b []byte) uint64 {
buf := bytes.NewBuffer(b)
var x uint64
binary.Read(buf, binary.BigEndian, &x)
return uint64(x)
}
// UInt64ToBytes uint64转字节数组
func UInt64ToBytes(n uint64) []byte {
buf := bytes.NewBuffer([]byte{})
binary.Write(buf, binary.BigEndian, n)
return buf.Bytes()
}
// UInt32ToBytes uint32转字节数组
func UInt32ToBytes(n uint32) []byte {
buf := bytes.NewBuffer([]byte{})
binary.Write(buf, binary.BigEndian, n)
return buf.Bytes()
}
// BytesToBool 字节数组转bool
func BytesToBool(b []byte) bool {
buf := bytes.NewBuffer(b)
var x bool
binary.Read(buf, binary.BigEndian, &x)
return x
}
// BoolToBytes bool转字节数组
func BoolToBytes(x bool) []byte {
buf := bytes.NewBuffer([]byte{})
binary.Write(buf, binary.BigEndian, x)
return buf.Bytes()
}

View File

@ -52,9 +52,6 @@ func Authcode(text string, params ...interface{}) string {
if l > 1 {
key = params[1].(string)
if key=="" {
key = "DH-Framework"
}
}
if l > 2 {
@ -172,18 +169,18 @@ func AuthcodeUrl(text string, params ...interface{}) string {
}
}
// JsonEncodeWithError 编码JSON
func JsonEncodeWithError(m interface{}) (string, error) {
// JsonEncode 编码JSON
func JsonEncode(m interface{}) string {
b, err := json.Marshal(m)
if err != nil {
return "", err
log.Printf("Json Encode[%#v] Error:%s", m, err.Error())
return ""
}
return string(b), nil
return string(b)
}
// JsonDecodeWithError 解码JSON
func JsonDecodeWithError(str string, v ...interface{}) (interface{}, error) {
// JsonDecode 解码JSON
func JsonDecode(str string, v ...interface{}) (interface{}, error) {
var m interface{}
if len(v) > 0 {
m = v[0]
@ -199,26 +196,6 @@ func JsonDecodeWithError(str string, v ...interface{}) (interface{}, error) {
return m, nil
}
// JsonEncode 编码JSON
func JsonEncode(m interface{}) string {
s, err := JsonEncodeWithError(m)
if err != nil {
log.Printf("Json Encode[%#v] Error:%s", m, err.Error())
return ""
}
return s
}
// JsonDecode 解码JSON
func JsonDecode(str string, v ...interface{}) interface{} {
i, err := JsonDecodeWithError(str, v...)
if err != nil {
log.Printf("Json Decode[%s] Error:%s", str, err.Error())
return nil
}
return i
}
func Crc32(text string) string {
h := crc32.NewIEEE()
io.WriteString(h, text)

View File

@ -6,7 +6,6 @@ import (
"os"
"os/exec"
"path/filepath"
"fmt"
)
const (
@ -17,7 +16,6 @@ const (
// ReadTextFile 读取文件
func ReadTextFile(path string) string {
fmt.Println("path:",path)
fp, err := os.Open(path)
if nil != err {
return ""

View File

@ -4,10 +4,8 @@ import (
"bytes"
"fmt"
"io/ioutil"
"net/url"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
@ -310,33 +308,6 @@ func Long2ip(ip uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
}
// SignStrBuild
func MapToRawStr(data map[string]string) string {
str := ""
if len(data) > 0 {
keys := make([]string, 0)
for k, _ := range data {
if k != "sign" && k != "encode" && k != "v" {
keys = append(keys, k)
}
}
sort.Strings(keys)
//
for _, k := range keys {
if data[k] != "" {
if str == "" {
str = fmt.Sprintf("%s=%s", k, url.QueryEscape(strings.Replace(data[k], " ", "+", -1)))
} else {
str = fmt.Sprintf("%s&%s=%s", str, k, url.QueryEscape(strings.Replace(data[k], " ", "+", -1)))
}
}
}
}
return str
}
// GbkToUtf8 GBK转UTF-8
func GbkToUtf8(s []byte) ([]byte, error) {
reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder())

View File

@ -27,6 +27,7 @@ func Get(apiUrl string, parm map[string]string, header map[string]string, isHttp
}
apiUrl = fmt.Sprintf("%s%s", apiUrl, p)
}
client := &http.Client{}
if isHttps {
@ -34,10 +35,8 @@ func Get(apiUrl string, parm map[string]string, header map[string]string, isHttp
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
reqest, err := http.NewRequest("GET", apiUrl, nil)
if err != nil {
return nil, err
}
reqest, _ := http.NewRequest("GET", apiUrl, nil)
for k, v := range header {
reqest.Header.Set(k, v)
}
@ -143,43 +142,3 @@ func GetRemoteIP(r *http.Request) string {
return strings.Split(addr, ":")[0]
}
// ParseQuery 参数解析兼容部分字段有encode部分没encode的情况
func ParseQuery(query string) (m url.Values, err error) {
m = make(url.Values)
for query != "" {
key := query
if i := strings.IndexAny(key, "&"); i >= 0 {
key, query = key[:i], key[i+1:]
} else {
query = ""
}
if key == "" {
continue
}
value := ""
if i := strings.Index(key, "="); i >= 0 {
key, value = key[:i], key[i+1:]
}
key, err1 := url.QueryUnescape(key)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
value1, err1 := url.QueryUnescape(value)
if err1 != nil {
if !strings.HasPrefix(err1.Error(), "invalid URL escape") {
if err == nil {
err = err1
}
continue
}
m[key] = append(m[key], value)
} else {
m[key] = append(m[key], value1)
}
}
return m, err
}

View File

@ -4,7 +4,6 @@ import (
"strconv"
"strings"
"net/http"
"fmt"
)
// buildLuaResponse构造Lua响应结果
@ -53,11 +52,6 @@ func BuildLuaResponse(m interface{}) string {
vStr = "{" + Substr(vStr, 1) + "}"
case string:
vStr = `"` + strings.Replace(v, `"`, `\"`, -1) + `"`
case map[string]interface{}:
for ks, vs := range v {
vStr += `,` + ks + `=` + `"`+strings.Replace(fmt.Sprint(vs), `"`, `\"`, -1)+`"`
}
vStr = "{" + Substr(vStr, 1) + "}"
default:
if vv, ok := v.(map[interface{}]interface{}); ok {
vStr = BuildLuaResponse(vv)

View File

@ -12,28 +12,12 @@ const (
regEmail = `^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$`
regPhone = `^((\d3)|(\d{3}\-))?13[0-9]\d{8}|14[0-9]\d{8}|15[0-9]\d{8}|17[0-9]\d{8}|18[0-9]\d{8}`
regUrl = `^((https?|ftp|news|http):\/\/)?([a-z]([a-z0-9\-]*[\.。])+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel)|(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&]*)?)?(#[a-z][a-z0-9_]*)?$`
regGuid = `[a-zA-Z0-9-_]{1,40}`
regGuid = `[a-zA-Z0-9-_]{0,40}`
regDescription = `^{0,64}$`
regOutTypeDescription = `^{0,20}$`
regMac = `^{0,40}$`
regTradeNo = `^[a-zA-Z0-9_-]{1,40}$`
regAttach = `^{0,127}$`
// LT
regID = `^[0-9]{0,11}$`
regTitle = `^[\s\S]{0,40}$`
regIntro = `^[\s\S]{0,120}$`
regHash = `^[\S]{0,160}$`
reqAuthorName = `^{0,60}$`
reqAtType = `^[1-7]{1}$`
reqActionListAtType = `^[1,3]{1}$`
regContent = `^[\s\S]{0,255}$`
reqSrcType = `^[1,2,3]{1}$`
reqCommentActionAtType = `^[1,2,6]{1}$`
reqUserActionAtType = `^[1,2,3,4]{1}$`
reqKeyword = `^.{0,40}$`
regReason = `^[\s\S]{0,140}$`
regUserSuggestTitle = `^[\s\S]{0,120}$`
)
func CheckString(data string, pat string) bool {
@ -103,73 +87,3 @@ func CheckTradeNo(tradeNo string) bool {
func CheckAttach(attach string) bool {
return CheckString(attach, regAttach)
}
// 检测资源id
func CheckID(anchorId string) bool {
return CheckString(anchorId, regID)
}
// 检测资源title
func CheckTitle(title string) bool {
return CheckString(title, regTitle)
}
// 检测资源intro
func CheckIntro(intro string) bool {
return CheckString(intro, regIntro)
}
// 检测资源Hash
func CheckHash(hash string) bool {
return CheckString(hash, regHash)
}
// 检测资源AuthorName
func CheckAuthorName(authorName string) bool {
return CheckString(authorName, reqAuthorName)
}
// 检测资源atType
func CheckAtType(atType string) bool {
return CheckString(atType, reqAtType)
}
// 检测资源atType
func CheckActionListAtType(atType string) bool {
return CheckString(atType, reqActionListAtType)
}
// 检测资源atType
func CheckContent(content string) bool {
return CheckString(content, regContent)
}
// 检测资源atType
func CheckSrcType(srcType string) bool {
return CheckString(srcType, reqSrcType)
}
// 检测资源atType
func CheckCommentActionAtType(atType string) bool {
return CheckString(atType, reqCommentActionAtType)
}
// 检测资源atType
func CheckUserActionAtType(atType string) bool {
return CheckString(atType, reqUserActionAtType)
}
// 检测资源keyword
func CheckKeyword(keyword string) bool {
return CheckString(keyword, reqKeyword)
}
// 检测资源Reason
func CheckReason(reason string) bool {
return CheckString(reason, regReason)
}
// 检测资源SuggestTitle
func CheckSuggestTitle(title string) bool {
return CheckString(title, regUserSuggestTitle)
}

View File

@ -4,7 +4,6 @@ import (
"fmt"
"log"
"math/rand"
"net"
"net/url"
"reflect"
"strconv"
@ -356,22 +355,10 @@ func LenSyncMap(m *sync.Map) int {
return length
}
// Ternary 模拟三元操作符
// 模拟三元操作符
func Ternary(b bool, trueVal, falseVal interface{}) interface{} {
if b {
return trueVal
}
return falseVal
}
// GetMacAddr 获取本地mac地址
func GetLocalMacAddr() ([]byte, error) {
// 获取本机的MAC地址
interfaces, err := net.Interfaces()
if err != nil || len(interfaces) == 0 {
return nil, err
}
return interfaces[0].HardwareAddr, nil
}

View File

@ -29,19 +29,24 @@ var driverInstanceNoProcess = &Driver{processQueryText: false}
func init() {
sql.Register("mssql", driverInstance)
sql.Register("sqlserver", driverInstanceNoProcess)
createDialer = func(p *connectParams) Dialer {
return netDialer{&net.Dialer{KeepAlive: p.keepAlive}}
createDialer = func(p *connectParams) dialer {
return tcpDialer{&net.Dialer{KeepAlive: p.keepAlive}}
}
}
var createDialer func(p *connectParams) Dialer
// Abstract the dialer for testing and for non-TCP based connections.
type dialer interface {
Dial(ctx context.Context, addr string) (net.Conn, error)
}
type netDialer struct {
var createDialer func(p *connectParams) dialer
type tcpDialer struct {
nd *net.Dialer
}
func (d netDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
return d.nd.DialContext(ctx, network, addr)
func (d tcpDialer) Dial(ctx context.Context, addr string) (net.Conn, error) {
return d.nd.DialContext(ctx, "tcp", addr)
}
type Driver struct {
@ -120,21 +125,6 @@ type Connector struct {
// SessionInitSQL is optional. The session will be reset even if
// SessionInitSQL is empty.
SessionInitSQL string
// Dialer sets a custom dialer for all network operations.
// If Dialer is not set, normal net dialers are used.
Dialer Dialer
}
type Dialer interface {
DialContext(ctx context.Context, network string, addr string) (net.Conn, error)
}
func (c *Connector) getDialer(p *connectParams) Dialer {
if c != nil && c.Dialer != nil {
return c.Dialer
}
return createDialer(p)
}
type Conn struct {
@ -320,12 +310,12 @@ func (d *Driver) open(ctx context.Context, dsn string) (*Conn, error) {
if err != nil {
return nil, err
}
return d.connect(ctx, nil, params)
return d.connect(ctx, params)
}
// connect to the server, using the provided context for dialing only.
func (d *Driver) connect(ctx context.Context, c *Connector, params connectParams) (*Conn, error) {
sess, err := connect(ctx, c, d.log, params)
func (d *Driver) connect(ctx context.Context, params connectParams) (*Conn, error) {
sess, err := connect(ctx, d.log, params)
if err != nil {
// main server failed, try fail-over partner
if params.failOverPartner == "" {
@ -337,7 +327,7 @@ func (d *Driver) connect(ctx context.Context, c *Connector, params connectParams
params.port = params.failOverPort
}
sess, err = connect(ctx, c, d.log, params)
sess, err = connect(ctx, d.log, params)
if err != nil {
// fail-over partner also failed, now fail
return nil, err
@ -345,7 +335,6 @@ func (d *Driver) connect(ctx context.Context, c *Connector, params connectParams
}
conn := &Conn{
connector: c,
sess: sess,
transactionCtx: context.Background(),
processQueryText: d.processQueryText,

View File

@ -34,7 +34,10 @@ func (c *Conn) ResetSession(ctx context.Context) error {
// Connect to the server and return a TDS connection.
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
conn, err := c.driver.connect(ctx, c, c.params)
conn, err := c.driver.connect(ctx, c.params)
if conn != nil {
conn.connector = c
}
if err == nil {
err = conn.ResetSession(ctx)
}

View File

@ -50,11 +50,12 @@ func parseInstances(msg []byte) map[string]map[string]string {
return results
}
func getInstances(ctx context.Context, d Dialer, address string) (map[string]map[string]string, error) {
func getInstances(ctx context.Context, address string) (map[string]map[string]string, error) {
maxTime := 5 * time.Second
ctx, cancel := context.WithTimeout(ctx, maxTime)
defer cancel()
conn, err := d.DialContext(ctx, "udp", address+":1434")
dialer := &net.Dialer{
Timeout: maxTime,
}
conn, err := dialer.DialContext(ctx, "udp", address+":1434")
if err != nil {
return nil, err
}
@ -1111,7 +1112,7 @@ type auth interface {
// SQL Server AlwaysOn Availability Group Listeners are bound by DNS to a
// list of IP addresses. So if there is more than one, try them all and
// use the first one that allows a connection.
func dialConnection(ctx context.Context, c *Connector, p connectParams) (conn net.Conn, err error) {
func dialConnection(ctx context.Context, p connectParams) (conn net.Conn, err error) {
var ips []net.IP
ips, err = net.LookupIP(p.host)
if err != nil {
@ -1122,9 +1123,9 @@ func dialConnection(ctx context.Context, c *Connector, p connectParams) (conn ne
ips = []net.IP{ip}
}
if len(ips) == 1 {
d := c.getDialer(&p)
d := createDialer(&p)
addr := net.JoinHostPort(ips[0].String(), strconv.Itoa(int(p.port)))
conn, err = d.DialContext(ctx, "tcp", addr)
conn, err = d.Dial(ctx, addr)
} else {
//Try Dials in parallel to avoid waiting for timeouts.
@ -1133,9 +1134,9 @@ func dialConnection(ctx context.Context, c *Connector, p connectParams) (conn ne
portStr := strconv.Itoa(int(p.port))
for _, ip := range ips {
go func(ip net.IP) {
d := c.getDialer(&p)
d := createDialer(&p)
addr := net.JoinHostPort(ip.String(), portStr)
conn, err := d.DialContext(ctx, "tcp", addr)
conn, err := d.Dial(ctx, addr)
if err == nil {
connChan <- conn
} else {
@ -1173,7 +1174,7 @@ func dialConnection(ctx context.Context, c *Connector, p connectParams) (conn ne
return conn, err
}
func connect(ctx context.Context, c *Connector, log optionalLogger, p connectParams) (res *tdsSession, err error) {
func connect(ctx context.Context, log optionalLogger, p connectParams) (res *tdsSession, err error) {
dialCtx := ctx
if p.dial_timeout > 0 {
var cancel func()
@ -1183,8 +1184,7 @@ func connect(ctx context.Context, c *Connector, log optionalLogger, p connectPar
// if instance is specified use instance resolution service
if p.instance != "" {
p.instance = strings.ToUpper(p.instance)
d := c.getDialer(&p)
instances, err := getInstances(dialCtx, d, p.host)
instances, err := getInstances(dialCtx, p.host)
if err != nil {
f := "Unable to get instances from Sql Server Browser on host %v: %v"
return nil, fmt.Errorf(f, p.host, err.Error())
@ -1202,7 +1202,7 @@ func connect(ctx context.Context, c *Connector, log optionalLogger, p connectPar
}
initiate_connection:
conn, err := dialConnection(dialCtx, c, p)
conn, err := dialConnection(dialCtx, p)
if err != nil {
return nil, err
}

View File

@ -116,23 +116,25 @@ func resizeHorizontal(img image.Image, width int, filter ResampleFilter) *image.
for y := range ys {
src.scan(0, y, src.w, y+1, scanLine)
j0 := y * dst.Stride
for x := 0; x < width; x++ {
for x := range weights {
var r, g, b, a float64
for _, w := range weights[x] {
i := w.index * 4
aw := float64(scanLine[i+3]) * w.weight
r += float64(scanLine[i+0]) * aw
g += float64(scanLine[i+1]) * aw
b += float64(scanLine[i+2]) * aw
s := scanLine[i : i+4 : i+4]
aw := float64(s[3]) * w.weight
r += float64(s[0]) * aw
g += float64(s[1]) * aw
b += float64(s[2]) * aw
a += aw
}
if a != 0 {
aInv := 1 / a
j := j0 + x*4
dst.Pix[j+0] = clamp(r * aInv)
dst.Pix[j+1] = clamp(g * aInv)
dst.Pix[j+2] = clamp(b * aInv)
dst.Pix[j+3] = clamp(a)
d := dst.Pix[j : j+4 : j+4]
d[0] = clamp(r * aInv)
d[1] = clamp(g * aInv)
d[2] = clamp(b * aInv)
d[3] = clamp(a)
}
}
}
@ -148,23 +150,25 @@ func resizeVertical(img image.Image, height int, filter ResampleFilter) *image.N
scanLine := make([]uint8, src.h*4)
for x := range xs {
src.scan(x, 0, x+1, src.h, scanLine)
for y := 0; y < height; y++ {
for y := range weights {
var r, g, b, a float64
for _, w := range weights[y] {
i := w.index * 4
aw := float64(scanLine[i+3]) * w.weight
r += float64(scanLine[i+0]) * aw
g += float64(scanLine[i+1]) * aw
b += float64(scanLine[i+2]) * aw
s := scanLine[i : i+4 : i+4]
aw := float64(s[3]) * w.weight
r += float64(s[0]) * aw
g += float64(s[1]) * aw
b += float64(s[2]) * aw
a += aw
}
if a != 0 {
aInv := 1 / a
j := y*dst.Stride + x*4
dst.Pix[j+0] = clamp(r * aInv)
dst.Pix[j+1] = clamp(g * aInv)
dst.Pix[j+2] = clamp(b * aInv)
dst.Pix[j+3] = clamp(a)
d := dst.Pix[j : j+4 : j+4]
d[0] = clamp(r * aInv)
d[1] = clamp(g * aInv)
d[2] = clamp(b * aInv)
d[3] = clamp(a)
}
}
}

View File

@ -33,10 +33,23 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
size := (x2 - x1) * 4
j := 0
i := y1*img.Stride + x1*4
for y := y1; y < y2; y++ {
copy(dst[j:j+size], img.Pix[i:i+size])
j += size
i += img.Stride
if size == 4 {
for y := y1; y < y2; y++ {
d := dst[j : j+4 : j+4]
s := img.Pix[i : i+4 : i+4]
d[0] = s[0]
d[1] = s[1]
d[2] = s[2]
d[3] = s[3]
j += size
i += img.Stride
}
} else {
for y := y1; y < y2; y++ {
copy(dst[j:j+size], img.Pix[i:i+size])
j += size
i += img.Stride
}
}
case *image.NRGBA64:
@ -44,10 +57,12 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
for y := y1; y < y2; y++ {
i := y*img.Stride + x1*8
for x := x1; x < x2; x++ {
dst[j+0] = img.Pix[i+0]
dst[j+1] = img.Pix[i+2]
dst[j+2] = img.Pix[i+4]
dst[j+3] = img.Pix[i+6]
s := img.Pix[i : i+8 : i+8]
d := dst[j : j+4 : j+4]
d[0] = s[0]
d[1] = s[2]
d[2] = s[4]
d[3] = s[6]
j += 4
i += 8
}
@ -58,26 +73,31 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
for y := y1; y < y2; y++ {
i := y*img.Stride + x1*4
for x := x1; x < x2; x++ {
d := dst[j : j+4 : j+4]
a := img.Pix[i+3]
switch a {
case 0:
dst[j+0] = 0
dst[j+1] = 0
dst[j+2] = 0
d[0] = 0
d[1] = 0
d[2] = 0
d[3] = a
case 0xff:
dst[j+0] = img.Pix[i+0]
dst[j+1] = img.Pix[i+1]
dst[j+2] = img.Pix[i+2]
s := img.Pix[i : i+4 : i+4]
d[0] = s[0]
d[1] = s[1]
d[2] = s[2]
d[3] = a
default:
r16 := uint16(img.Pix[i+0])
g16 := uint16(img.Pix[i+1])
b16 := uint16(img.Pix[i+2])
s := img.Pix[i : i+4 : i+4]
r16 := uint16(s[0])
g16 := uint16(s[1])
b16 := uint16(s[2])
a16 := uint16(a)
dst[j+0] = uint8(r16 * 0xff / a16)
dst[j+1] = uint8(g16 * 0xff / a16)
dst[j+2] = uint8(b16 * 0xff / a16)
d[0] = uint8(r16 * 0xff / a16)
d[1] = uint8(g16 * 0xff / a16)
d[2] = uint8(b16 * 0xff / a16)
d[3] = a
}
dst[j+3] = a
j += 4
i += 4
}
@ -88,26 +108,28 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
for y := y1; y < y2; y++ {
i := y*img.Stride + x1*8
for x := x1; x < x2; x++ {
a := img.Pix[i+6]
s := img.Pix[i : i+8 : i+8]
d := dst[j : j+4 : j+4]
a := s[6]
switch a {
case 0:
dst[j+0] = 0
dst[j+1] = 0
dst[j+2] = 0
d[0] = 0
d[1] = 0
d[2] = 0
case 0xff:
dst[j+0] = img.Pix[i+0]
dst[j+1] = img.Pix[i+2]
dst[j+2] = img.Pix[i+4]
d[0] = s[0]
d[1] = s[2]
d[2] = s[4]
default:
r32 := uint32(img.Pix[i+0])<<8 | uint32(img.Pix[i+1])
g32 := uint32(img.Pix[i+2])<<8 | uint32(img.Pix[i+3])
b32 := uint32(img.Pix[i+4])<<8 | uint32(img.Pix[i+5])
a32 := uint32(img.Pix[i+6])<<8 | uint32(img.Pix[i+7])
dst[j+0] = uint8((r32 * 0xffff / a32) >> 8)
dst[j+1] = uint8((g32 * 0xffff / a32) >> 8)
dst[j+2] = uint8((b32 * 0xffff / a32) >> 8)
r32 := uint32(s[0])<<8 | uint32(s[1])
g32 := uint32(s[2])<<8 | uint32(s[3])
b32 := uint32(s[4])<<8 | uint32(s[5])
a32 := uint32(s[6])<<8 | uint32(s[7])
d[0] = uint8((r32 * 0xffff / a32) >> 8)
d[1] = uint8((g32 * 0xffff / a32) >> 8)
d[2] = uint8((b32 * 0xffff / a32) >> 8)
}
dst[j+3] = a
d[3] = a
j += 4
i += 8
}
@ -119,10 +141,11 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
i := y*img.Stride + x1
for x := x1; x < x2; x++ {
c := img.Pix[i]
dst[j+0] = c
dst[j+1] = c
dst[j+2] = c
dst[j+3] = 0xff
d := dst[j : j+4 : j+4]
d[0] = c
d[1] = c
d[2] = c
d[3] = 0xff
j += 4
i++
}
@ -134,10 +157,11 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
i := y*img.Stride + x1*2
for x := x1; x < x2; x++ {
c := img.Pix[i]
dst[j+0] = c
dst[j+1] = c
dst[j+2] = c
dst[j+3] = 0xff
d := dst[j : j+4 : j+4]
d[0] = c
d[1] = c
d[2] = c
d[3] = 0xff
j += 4
i += 2
}
@ -149,52 +173,61 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
x2 += img.Rect.Min.X
y1 += img.Rect.Min.Y
y2 += img.Rect.Min.Y
hy := img.Rect.Min.Y / 2
hx := img.Rect.Min.X / 2
for y := y1; y < y2; y++ {
iy := (y-img.Rect.Min.Y)*img.YStride + (x1 - img.Rect.Min.X)
var yBase int
switch img.SubsampleRatio {
case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio422:
yBase = (y - img.Rect.Min.Y) * img.CStride
case image.YCbCrSubsampleRatio420, image.YCbCrSubsampleRatio440:
yBase = (y/2 - hy) * img.CStride
}
for x := x1; x < x2; x++ {
var ic int
switch img.SubsampleRatio {
case image.YCbCrSubsampleRatio444:
ic = (y-img.Rect.Min.Y)*img.CStride + (x - img.Rect.Min.X)
case image.YCbCrSubsampleRatio422:
ic = (y-img.Rect.Min.Y)*img.CStride + (x/2 - img.Rect.Min.X/2)
case image.YCbCrSubsampleRatio420:
ic = (y/2-img.Rect.Min.Y/2)*img.CStride + (x/2 - img.Rect.Min.X/2)
case image.YCbCrSubsampleRatio440:
ic = (y/2-img.Rect.Min.Y/2)*img.CStride + (x - img.Rect.Min.X)
case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio440:
ic = yBase + (x - img.Rect.Min.X)
case image.YCbCrSubsampleRatio422, image.YCbCrSubsampleRatio420:
ic = yBase + (x/2 - hx)
default:
ic = img.COffset(x, y)
}
yy := int(img.Y[iy])
cb := int(img.Cb[ic]) - 128
cr := int(img.Cr[ic]) - 128
yy1 := int32(img.Y[iy]) * 0x10101
cb1 := int32(img.Cb[ic]) - 128
cr1 := int32(img.Cr[ic]) - 128
r := (yy<<16 + 91881*cr + 1<<15) >> 16
if r > 0xff {
r = 0xff
} else if r < 0 {
r = 0
r := yy1 + 91881*cr1
if uint32(r)&0xff000000 == 0 {
r >>= 16
} else {
r = ^(r >> 31)
}
g := (yy<<16 - 22554*cb - 46802*cr + 1<<15) >> 16
if g > 0xff {
g = 0xff
} else if g < 0 {
g = 0
g := yy1 - 22554*cb1 - 46802*cr1
if uint32(g)&0xff000000 == 0 {
g >>= 16
} else {
g = ^(g >> 31)
}
b := (yy<<16 + 116130*cb + 1<<15) >> 16
if b > 0xff {
b = 0xff
} else if b < 0 {
b = 0
b := yy1 + 116130*cb1
if uint32(b)&0xff000000 == 0 {
b >>= 16
} else {
b = ^(b >> 31)
}
dst[j+0] = uint8(r)
dst[j+1] = uint8(g)
dst[j+2] = uint8(b)
dst[j+3] = 0xff
d := dst[j : j+4 : j+4]
d[0] = uint8(r)
d[1] = uint8(g)
d[2] = uint8(b)
d[3] = 0xff
iy++
j += 4
@ -207,10 +240,11 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
i := y*img.Stride + x1
for x := x1; x < x2; x++ {
c := s.palette[img.Pix[i]]
dst[j+0] = c.R
dst[j+1] = c.G
dst[j+2] = c.B
dst[j+3] = c.A
d := dst[j : j+4 : j+4]
d[0] = c.R
d[1] = c.G
d[2] = c.B
d[3] = c.A
j += 4
i++
}
@ -226,22 +260,23 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
for y := y1; y < y2; y++ {
for x := x1; x < x2; x++ {
r16, g16, b16, a16 := s.image.At(x, y).RGBA()
d := dst[j : j+4 : j+4]
switch a16 {
case 0xffff:
dst[j+0] = uint8(r16 >> 8)
dst[j+1] = uint8(g16 >> 8)
dst[j+2] = uint8(b16 >> 8)
dst[j+3] = 0xff
d[0] = uint8(r16 >> 8)
d[1] = uint8(g16 >> 8)
d[2] = uint8(b16 >> 8)
d[3] = 0xff
case 0:
dst[j+0] = 0
dst[j+1] = 0
dst[j+2] = 0
dst[j+3] = 0
d[0] = 0
d[1] = 0
d[2] = 0
d[3] = 0
default:
dst[j+0] = uint8(((r16 * 0xffff) / a16) >> 8)
dst[j+1] = uint8(((g16 * 0xffff) / a16) >> 8)
dst[j+2] = uint8(((b16 * 0xffff) / a16) >> 8)
dst[j+3] = uint8(a16 >> 8)
d[0] = uint8(((r16 * 0xffff) / a16) >> 8)
d[1] = uint8(((g16 * 0xffff) / a16) >> 8)
d[2] = uint8(((b16 * 0xffff) / a16) >> 8)
d[3] = uint8(a16 >> 8)
}
j += 4
}

View File

@ -1,7 +1,9 @@
GO ?= go
GOFMT ?= gofmt "-s"
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
VETPACKAGES ?= $(shell go list ./... | grep -v /vendor/ | grep -v /examples/)
PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/ | grep -v /examples/)
GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*")
TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples)
all: install
@ -10,7 +12,19 @@ install: deps
.PHONY: test
test:
sh coverage.sh
echo "mode: count" > coverage.out
for d in $(TESTFOLDER); do \
$(GO) test -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \
cat tmp.out; \
if grep -q "^--- FAIL" tmp.out; then \
rm tmp.out; \
exit 1;\
fi; \
if [ -f profile.out ]; then \
cat profile.out | grep -v "mode:" >> coverage.out; \
rm profile.out; \
fi; \
done
.PHONY: fmt
fmt:
@ -18,7 +32,6 @@ fmt:
.PHONY: fmt-check
fmt-check:
# get all go files and run go fmt on them
@diff=$$($(GOFMT) -d $(GOFILES)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
@ -27,14 +40,14 @@ fmt-check:
fi;
vet:
go vet $(VETPACKAGES)
$(GO) vet $(VETPACKAGES)
deps:
@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/kardianos/govendor; \
$(GO) get -u github.com/kardianos/govendor; \
fi
@hash embedmd > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/campoy/embedmd; \
$(GO) get -u github.com/campoy/embedmd; \
fi
embedmd:
@ -43,20 +56,26 @@ embedmd:
.PHONY: lint
lint:
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/golang/lint/golint; \
$(GO) get -u golang.org/x/lint/golint; \
fi
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
.PHONY: misspell-check
misspell-check:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/client9/misspell/cmd/misspell; \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -error $(GOFILES)
.PHONY: misspell
misspell:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/client9/misspell/cmd/misspell; \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -w $(GOFILES)
.PHONY: tools
tools:
go install golang.org/x/lint/golint; \
go install github.com/client9/misspell/cmd/misspell; \
go install github.com/campoy/embedmd;

View File

@ -9,6 +9,7 @@
[![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
[![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin)
[![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases)
Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
@ -38,9 +39,10 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
- [Custom Validators](#custom-validators)
- [Only Bind Query String](#only-bind-query-string)
- [Bind Query String or Post Data](#bind-query-string-or-post-data)
- [Bind Uri](#bind-uri)
- [Bind HTML checkboxes](#bind-html-checkboxes)
- [Multipart/Urlencoded binding](#multiparturlencoded-binding)
- [XML, JSON and YAML rendering](#xml-json-and-yaml-rendering)
- [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering)
- [JSONP rendering](#jsonp)
- [Serving static files](#serving-static-files)
- [Serving data from reader](#serving-data-from-reader)
@ -58,8 +60,10 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
- [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct)
- [Try to bind body into different structs](#try-to-bind-body-into-different-structs)
- [http2 server push](#http2-server-push)
- [Define format for the log of routes](#define-format-for-the-log-of-routes)
- [Set and get a cookie](#set-and-get-a-cookie)
- [Testing](#testing)
- [Users](#users--)
- [Users](#users)
## Installation
@ -100,7 +104,7 @@ $ mkdir -p $GOPATH/src/github.com/myusername/project && cd "$_"
```sh
$ govendor init
$ govendor fetch github.com/gin-gonic/gin@v1.2
$ govendor fetch github.com/gin-gonic/gin@v1.3
```
4. Copy a starting template inside your project
@ -198,7 +202,7 @@ BenchmarkVulcan_GithubAll | 5000 | 394253 | 19894
## Build with [jsoniter](https://github.com/json-iterator/go)
Gin use `encoding/json` as default json package but you can change to [jsoniter](https://github.com/json-iterator/go) by build from other tags.
Gin uses `encoding/json` as default json package but you can change to [jsoniter](https://github.com/json-iterator/go) by build from other tags.
```sh
$ go build -tags=jsoniter .
@ -526,7 +530,7 @@ func main() {
### Model binding and validation
To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz).
Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](http://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags).
@ -534,10 +538,10 @@ Note that you need to set the corresponding binding tag on all fields you want t
Also, Gin provides two sets of methods for binding:
- **Type** - Must bind
- **Methods** - `Bind`, `BindJSON`, `BindQuery`
- **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`
- **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method.
- **Type** - Should bind
- **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindQuery`
- **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`
- **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`.
@ -547,8 +551,8 @@ You can also specify that specific fields are required. If a field is decorated
```go
// Binding from JSON
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
func main() {
@ -557,30 +561,55 @@ func main() {
// Example for binding JSON ({"user": "manu", "password": "123"})
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err == nil {
if json.User == "manu" && json.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
} else {
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if json.User != "manu" || json.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// Example for binding XML (
// <?xml version="1.0" encoding="UTF-8"?>
// <root>
// <user>user</user>
// <password>123</user>
// </root>)
router.POST("/loginXML", func(c *gin.Context) {
var xml Login
if err := c.ShouldBindXML(&xml); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if xml.User != "manu" || xml.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// Example for binding a HTML form (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
var form Login
// This will infer what binder to use depending on the content-type header.
if err := c.ShouldBind(&form); err == nil {
if form.User == "manu" && form.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
} else {
if err := c.ShouldBind(&form); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if form.User != "manu" || form.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// Listen and serve on 0.0.0.0:8080
@ -632,6 +661,7 @@ import (
"gopkg.in/go-playground/validator.v8"
)
// Booking contains binded and validated data.
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
@ -679,7 +709,7 @@ $ curl "localhost:8085/bookable?check_in=2018-03-08&check_out=2018-03-09"
{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}
```
[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registed this way.
[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registered this way.
See the [struct-lvl-validation example](examples/struct-lvl-validations) to learn more.
### Only Bind Query String
@ -725,9 +755,12 @@ See the [detail information](https://github.com/gin-gonic/gin/issues/742#issueco
```go
package main
import "log"
import "github.com/gin-gonic/gin"
import "time"
import (
"log"
"time"
"github.com/gin-gonic/gin"
)
type Person struct {
Name string `form:"name"`
@ -761,6 +794,40 @@ Test it with:
$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15"
```
### Bind Uri
See the [detail information](https://github.com/gin-gonic/gin/issues/846).
```go
package main
import "github.com/gin-gonic/gin"
type Person struct {
ID string `uri:"id" binding:"required,uuid"`
Name string `uri:"name" binding:"required"`
}
func main() {
route := gin.Default()
route.GET("/:name/:id", func(c *gin.Context) {
var person Person
if err := c.ShouldBindUri(&person); err != nil {
c.JSON(400, gin.H{"msg": err})
return
}
c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
})
route.Run(":8088")
}
```
Test it with:
```sh
$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3
$ curl -v localhost:8088/thinkerou/not-uuid
```
### Bind HTML checkboxes
See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
@ -792,12 +859,12 @@ form.html
<form action="/" method="POST">
<p>Check some colors</p>
<label for="red">Red</label>
<input type="checkbox" name="colors[]" value="red" id="red" />
<input type="checkbox" name="colors[]" value="red" id="red">
<label for="green">Green</label>
<input type="checkbox" name="colors[]" value="green" id="green" />
<input type="checkbox" name="colors[]" value="green" id="green">
<label for="blue">Blue</label>
<input type="checkbox" name="colors[]" value="blue" id="blue" />
<input type="submit" />
<input type="checkbox" name="colors[]" value="blue" id="blue">
<input type="submit">
</form>
```
@ -846,7 +913,7 @@ Test it with:
$ curl -v --form user=user --form password=password http://localhost:8080/login
```
### XML, JSON and YAML rendering
### XML, JSON, YAML and ProtoBuf rendering
```go
func main() {
@ -880,6 +947,19 @@ func main() {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
label := "test"
// The specific definition of protobuf is written in the testdata/protoexample file.
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
// Note that data becomes binary data in the response
// Will output protoexample.Test protobuf serialized data
c.ProtoBuf(http.StatusOK, data)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
@ -953,6 +1033,34 @@ func main() {
}
```
#### PureJSON
Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead.
This feature is unavailable in Go 1.6 and lower.
```go
func main() {
r := gin.Default()
// Serves unicode entities
r.GET("/json", func(c *gin.Context) {
c.JSON(200, gin.H{
"html": "<b>Hello, world!</b>",
})
})
// Serves literal characters
r.GET("/purejson", func(c *gin.Context) {
c.PureJSON(200, gin.H{
"html": "<b>Hello, world!</b>",
})
})
// listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
```
### Serving static files
```go
@ -1087,7 +1195,7 @@ You may use custom delims
```go
r := gin.Default()
r.Delims("{[{", "}]}")
r.LoadHTMLGlob("/path/to/templates"))
r.LoadHTMLGlob("/path/to/templates")
```
#### Custom Template Funcs
@ -1649,11 +1757,11 @@ type StructX struct {
}
type StructY struct {
Y StructX `form:"name_y"` // HERE hava form
Y StructX `form:"name_y"` // HERE have form
}
type StructZ struct {
Z *StructZ `form:"name_z"` // HERE hava form
Z *StructZ `form:"name_z"` // HERE have form
}
```
@ -1766,6 +1874,78 @@ func main() {
}
```
### Define format for the log of routes
The default log of routes is:
```
[GIN-debug] POST /foo --> main.main.func1 (3 handlers)
[GIN-debug] GET /bar --> main.main.func2 (3 handlers)
[GIN-debug] GET /status --> main.main.func3 (3 handlers)
```
If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with `gin.DebugPrintRouteFunc`.
In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs.
```go
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
}
r.POST("/foo", func(c *gin.Context) {
c.JSON(http.StatusOK, "foo")
})
r.GET("/bar", func(c *gin.Context) {
c.JSON(http.StatusOK, "bar")
})
r.GET("/status", func(c *gin.Context) {
c.JSON(http.StatusOK, "ok")
})
// Listen and Server in http://0.0.0.0:8080
r.Run()
}
```
### Set and get a cookie
```go
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/cookie", func(c *gin.Context) {
cookie, err := c.Cookie("gin_cookie")
if err != nil {
cookie = "NotSet"
c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
}
fmt.Printf("Cookie value: %s \n", cookie)
})
router.Run()
}
```
## Testing
The `net/http/httptest` package is preferable way for HTTP testing.
@ -1816,5 +1996,9 @@ func TestPingRoute(t *testing.T) {
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go
* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go.
* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go.
* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform.
* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow.
* [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares.
* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go.

View File

@ -18,6 +18,7 @@ const (
MIMEPROTOBUF = "application/x-protobuf"
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
MIMEYAML = "application/x-yaml"
)
// Binding describes the interface which needs to be implemented for binding the
@ -35,9 +36,16 @@ type BindingBody interface {
BindBody([]byte, interface{}) error
}
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
// but it read the Params.
type BindingUri interface {
Name() string
BindUri(map[string][]string, interface{}) error
}
// StructValidator is the minimal interface which needs to be implemented in
// order for it to be used as the validator engine for ensuring the correctness
// of the reqest. Gin provides a default implementation for this using
// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v8.18.2.
type StructValidator interface {
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
@ -68,6 +76,8 @@ var (
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
MsgPack = msgpackBinding{}
YAML = yamlBinding{}
Uri = uriBinding{}
)
// Default returns the appropriate Binding instance based on the HTTP method
@ -86,6 +96,8 @@ func Default(method, contentType string) Binding {
return ProtoBuf
case MIMEMSGPACK, MIMEMSGPACK2:
return MsgPack
case MIMEYAML:
return YAML
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
return Form
}

View File

@ -12,7 +12,15 @@ import (
"time"
)
func mapUri(ptr interface{}, m map[string][]string) error {
return mapFormByTag(ptr, m, "uri")
}
func mapForm(ptr interface{}, form map[string][]string) error {
return mapFormByTag(ptr, form, "form")
}
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
typ := reflect.TypeOf(ptr).Elem()
val := reflect.ValueOf(ptr).Elem()
for i := 0; i < typ.NumField(); i++ {
@ -23,7 +31,7 @@ func mapForm(ptr interface{}, form map[string][]string) error {
}
structFieldKind := structField.Kind()
inputFieldName := typeField.Tag.Get("form")
inputFieldName := typeField.Tag.Get(tag)
inputFieldNameList := strings.Split(inputFieldName, ",")
inputFieldName = inputFieldNameList[0]
var defaultValue string
@ -74,16 +82,16 @@ func mapForm(ptr interface{}, form map[string][]string) error {
}
}
val.Field(i).Set(slice)
} else {
if _, isTime := structField.Interface().(time.Time); isTime {
if err := setTimeField(inputValue[0], typeField, structField); err != nil {
return err
}
continue
}
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
continue
}
if _, isTime := structField.Interface().(time.Time); isTime {
if err := setTimeField(inputValue[0], typeField, structField); err != nil {
return err
}
continue
}
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
return err
}
}
return nil
@ -178,7 +186,7 @@ func setFloatField(val string, bitSize int, field reflect.Value) error {
func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
timeFormat := structField.Tag.Get("time_format")
if timeFormat == "" {
return errors.New("Blank time format")
timeFormat = time.RFC3339
}
if val == "" {

View File

@ -6,10 +6,11 @@ package binding
import (
"bytes"
"fmt"
"io"
"net/http"
"github.com/gin-gonic/gin/json"
"github.com/gin-gonic/gin/internal/json"
)
// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
@ -24,6 +25,9 @@ func (jsonBinding) Name() string {
}
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
if req == nil || req.Body == nil {
return fmt.Errorf("invalid request")
}
return decodeJSON(req.Body, obj)
}

View File

@ -29,7 +29,7 @@ func (protobufBinding) BindBody(body []byte, obj interface{}) error {
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
return err
}
// Here it's same to return validate(obj), but util now we cann't add
// Here it's same to return validate(obj), but util now we can't add
// `binding:""` to the struct which automatically generate by gen-proto
return nil
// return validate(obj)

View File

@ -31,6 +31,7 @@ const (
MIMEPlain = binding.MIMEPlain
MIMEPOSTForm = binding.MIMEPOSTForm
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
MIMEYAML = binding.MIMEYAML
BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
)
@ -414,7 +415,6 @@ func (c *Context) PostFormArray(key string) []string {
// a boolean value whether at least one value exists for the given key.
func (c *Context) GetPostFormArray(key string) ([]string, bool) {
req := c.Request
req.ParseForm()
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
if values := req.PostForm[key]; len(values) > 0 {
return values, true
@ -437,7 +437,6 @@ func (c *Context) PostFormMap(key string) map[string]string {
// whether at least one value exists for the given key.
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
req := c.Request
req.ParseForm()
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
dicts, exist := c.get(req.PostForm, key)
@ -465,6 +464,11 @@ func (c *Context) get(m map[string][]string, key string) (map[string]string, boo
// FormFile returns the first file for the provided form key.
func (c *Context) FormFile(name string) (*multipart.FileHeader, error) {
if c.Request.MultipartForm == nil {
if err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
return nil, err
}
}
_, fh, err := c.Request.FormFile(name)
return fh, err
}
@ -511,13 +515,23 @@ func (c *Context) BindJSON(obj interface{}) error {
return c.MustBindWith(obj, binding.JSON)
}
// BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML).
func (c *Context) BindXML(obj interface{}) error {
return c.MustBindWith(obj, binding.XML)
}
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
func (c *Context) BindQuery(obj interface{}) error {
return c.MustBindWith(obj, binding.Query)
}
// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
func (c *Context) BindYAML(obj interface{}) error {
return c.MustBindWith(obj, binding.YAML)
}
// MustBindWith binds the passed struct pointer using the specified binding engine.
// It will abort the request with HTTP 400 if any error ocurrs.
// It will abort the request with HTTP 400 if any error occurs.
// See the binding package.
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
if err = c.ShouldBindWith(obj, b); err != nil {
@ -545,11 +559,30 @@ func (c *Context) ShouldBindJSON(obj interface{}) error {
return c.ShouldBindWith(obj, binding.JSON)
}
// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
func (c *Context) ShouldBindXML(obj interface{}) error {
return c.ShouldBindWith(obj, binding.XML)
}
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
func (c *Context) ShouldBindQuery(obj interface{}) error {
return c.ShouldBindWith(obj, binding.Query)
}
// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
func (c *Context) ShouldBindYAML(obj interface{}) error {
return c.ShouldBindWith(obj, binding.YAML)
}
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
func (c *Context) ShouldBindUri(obj interface{}) error {
m := make(map[string][]string)
for _, v := range c.Params {
m[v.Key] = []string{v.Value}
}
return binding.Uri.BindUri(m, obj)
}
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
// See the binding package.
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
@ -561,9 +594,7 @@ func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
//
// NOTE: This method reads the body before binding. So you should use
// ShouldBindWith for better performance if you need to call only once.
func (c *Context) ShouldBindBodyWith(
obj interface{}, bb binding.BindingBody,
) (err error) {
func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) {
var body []byte
if cb, ok := c.Get(BodyBytesKey); ok {
if cbb, ok := cb.([]byte); ok {
@ -655,9 +686,9 @@ func (c *Context) Status(code int) {
func (c *Context) Header(key, value string) {
if value == "" {
c.Writer.Header().Del(key)
} else {
c.Writer.Header().Set(key, value)
return
}
c.Writer.Header().Set(key, value)
}
// GetHeader returns value from request headers.
@ -701,6 +732,7 @@ func (c *Context) Cookie(name string) (string, error) {
return val, nil
}
// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {
c.Status(code)
@ -745,9 +777,9 @@ func (c *Context) JSONP(code int, obj interface{}) {
callback := c.DefaultQuery("callback", "")
if callback == "" {
c.Render(code, render.JSON{Data: obj})
} else {
c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
return
}
c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
}
// JSON serializes the given struct as JSON into the response body.
@ -773,6 +805,11 @@ func (c *Context) YAML(code int, obj interface{}) {
c.Render(code, render.YAML{Data: obj})
}
// ProtoBuf serializes the given struct as ProtoBuf into the response body.
func (c *Context) ProtoBuf(code int, obj interface{}) {
c.Render(code, render.ProtoBuf{Data: obj})
}
// String writes the given string into the response body.
func (c *Context) String(code int, format string, values ...interface{}) {
c.Render(code, render.String{Format: format, Data: values})
@ -818,6 +855,7 @@ func (c *Context) SSEvent(name string, message interface{}) {
})
}
// Stream sends a streaming response.
func (c *Context) Stream(step func(w io.Writer) bool) {
w := c.Writer
clientGone := w.CloseNotify()
@ -839,6 +877,7 @@ func (c *Context) Stream(step func(w io.Writer) bool) {
/******** CONTENT NEGOTIATION *******/
/************************************/
// Negotiate contains all negotiations data.
type Negotiate struct {
Offered []string
HTMLName string
@ -848,6 +887,7 @@ type Negotiate struct {
Data interface{}
}
// Negotiate calls different Render according acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
switch c.NegotiateFormat(config.Offered...) {
case binding.MIMEJSON:
@ -867,6 +907,7 @@ func (c *Context) Negotiate(code int, config Negotiate) {
}
}
// NegotiateFormat returns an acceptable Accept format.
func (c *Context) NegotiateFormat(offered ...string) string {
assert1(len(offered) > 0, "you must provide at least one offer")
@ -886,6 +927,7 @@ func (c *Context) NegotiateFormat(offered ...string) string {
return ""
}
// SetAccepted sets Accept header data.
func (c *Context) SetAccepted(formats ...string) {
c.Accepted = formats
}

View File

@ -1,13 +0,0 @@
#!/usr/bin/env bash
set -e
echo "mode: count" > coverage.out
for d in $(go list ./... | grep -E 'gin$|binding$|render$' | grep -v 'examples'); do
go test -v -covermode=count -coverprofile=profile.out $d
if [ -f profile.out ]; then
cat profile.out | grep -v "mode:" >> coverage.out
rm profile.out
fi
done

View File

@ -6,13 +6,15 @@ package gin
import (
"bytes"
"fmt"
"html/template"
"log"
"os"
"runtime"
"strconv"
"strings"
)
func init() {
log.SetFlags(0)
}
const ginSupportMinGoVer = 6
// IsDebugging returns true if the framework is running in debug mode.
// Use SetMode(gin.ReleaseMode) to disable debug mode.
@ -20,11 +22,18 @@ func IsDebugging() bool {
return ginMode == debugCode
}
// DebugPrintRouteFunc indicates debug log output format.
var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int)
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
if IsDebugging() {
nuHandlers := len(handlers)
handlerName := nameOfFunction(handlers.Last())
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
if DebugPrintRouteFunc == nil {
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
} else {
DebugPrintRouteFunc(httpMethod, absolutePath, handlerName, nuHandlers)
}
}
}
@ -42,14 +51,28 @@ func debugPrintLoadTemplate(tmpl *template.Template) {
func debugPrint(format string, values ...interface{}) {
if IsDebugging() {
log.Printf("[GIN-debug] "+format, values...)
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
fmt.Fprintf(os.Stderr, "[GIN-debug] "+format, values...)
}
}
func getMinVer(v string) (uint64, error) {
first := strings.IndexByte(v, '.')
last := strings.LastIndexByte(v, '.')
if first == last {
return strconv.ParseUint(v[first+1:], 10, 64)
}
return strconv.ParseUint(v[first+1:last], 10, 64)
}
func debugPrintWARNINGDefault() {
debugPrint(`[WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
debugPrint(`[WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
`)
}
debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
`)

View File

@ -9,21 +9,28 @@ import (
"fmt"
"reflect"
"github.com/gin-gonic/gin/json"
"github.com/gin-gonic/gin/internal/json"
)
// ErrorType is an unsigned 64-bit error code as defined in the gin spec.
type ErrorType uint64
const (
ErrorTypeBind ErrorType = 1 << 63 // used when c.Bind() fails
ErrorTypeRender ErrorType = 1 << 62 // used when c.Render() fails
// ErrorTypeBind is used when Context.Bind() fails.
ErrorTypeBind ErrorType = 1 << 63
// ErrorTypeRender is used when Context.Render() fails.
ErrorTypeRender ErrorType = 1 << 62
// ErrorTypePrivate indicates a private error.
ErrorTypePrivate ErrorType = 1 << 0
ErrorTypePublic ErrorType = 1 << 1
// ErrorTypePublic indicates a public error.
ErrorTypePublic ErrorType = 1 << 1
// ErrorTypeAny indicates any other error.
ErrorTypeAny ErrorType = 1<<64 - 1
ErrorTypeNu = 2
// ErrorTypeNu indicates any other error.
ErrorTypeNu = 2
)
// Error represents a error's specification.
type Error struct {
Err error
Type ErrorType
@ -34,16 +41,19 @@ type errorMsgs []*Error
var _ error = &Error{}
// SetType sets the error's type.
func (msg *Error) SetType(flags ErrorType) *Error {
msg.Type = flags
return msg
}
// SetMeta sets the error's meta data.
func (msg *Error) SetMeta(data interface{}) *Error {
msg.Meta = data
return msg
}
// JSON creates a properly formated JSON
func (msg *Error) JSON() interface{} {
json := H{}
if msg.Meta != nil {
@ -70,11 +80,12 @@ func (msg *Error) MarshalJSON() ([]byte, error) {
return json.Marshal(msg.JSON())
}
// Error implements the error interface
// Error implements the error interface.
func (msg Error) Error() string {
return msg.Err.Error()
}
// IsType judges one error.
func (msg *Error) IsType(flags ErrorType) bool {
return (msg.Type & flags) > 0
}
@ -138,6 +149,7 @@ func (a errorMsgs) JSON() interface{} {
}
}
// MarshalJSON implements the json.Marshaller interface.
func (a errorMsgs) MarshalJSON() ([]byte, error) {
return json.Marshal(a.JSON())
}

View File

@ -5,6 +5,7 @@
package gin
import (
"fmt"
"html/template"
"net"
"net/http"
@ -14,11 +15,7 @@ import (
"github.com/gin-gonic/gin/render"
)
const (
// Version is Framework's version.
Version = "v1.3.0"
defaultMultipartMemory = 32 << 20 // 32 MB
)
const defaultMultipartMemory = 32 << 20 // 32 MB
var (
default404Body = []byte("404 page not found")
@ -26,7 +23,10 @@ var (
defaultAppEngine bool
)
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
// Last returns the last handler in the chain. ie. the last handler is the main own.
@ -37,12 +37,15 @@ func (c HandlersChain) Last() HandlerFunc {
return nil
}
// RouteInfo represents a request route's specification which contains method and path and its handler.
type RouteInfo struct {
Method string
Path string
Handler string
Method string
Path string
Handler string
HandlerFunc HandlerFunc
}
// RoutesInfo defines a RouteInfo array.
type RoutesInfo []RouteInfo
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
@ -155,6 +158,7 @@ func (engine *Engine) allocateContext() *Context {
return &Context{engine: engine}
}
// Delims sets template left and right delims and returns a Engine instance.
func (engine *Engine) Delims(left, right string) *Engine {
engine.delims = render.Delims{Left: left, Right: right}
return engine
@ -264,10 +268,12 @@ func (engine *Engine) Routes() (routes RoutesInfo) {
func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
path += root.path
if len(root.handlers) > 0 {
handlerFunc := root.handlers.Last()
routes = append(routes, RouteInfo{
Method: method,
Path: path,
Handler: nameOfFunction(root.handlers.Last()),
Method: method,
Path: path,
Handler: nameOfFunction(handlerFunc),
HandlerFunc: handlerFunc,
})
}
for _, child := range root.children {
@ -316,6 +322,23 @@ func (engine *Engine) RunUnix(file string) (err error) {
return
}
// RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
// through the specified file descriptor.
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) RunFd(fd int) (err error) {
debugPrint("Listening and serving HTTP on fd@%d", fd)
defer func() { debugPrintError(err) }()
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
listener, err := net.FileListener(f)
if err != nil {
return
}
defer listener.Close()
err = http.Serve(listener, engine)
return
}
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
@ -334,7 +357,6 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func (engine *Engine) HandleContext(c *Context) {
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
func (engine *Engine) handleHTTPRequest(c *Context) {

View File

@ -1,15 +0,0 @@
// Copyright 2017 Bo-Yi Wu. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// +build !jsoniter
package json
import "encoding/json"
var (
Marshal = json.Marshal
MarshalIndent = json.MarshalIndent
NewDecoder = json.NewDecoder
)

View File

@ -1,16 +0,0 @@
// Copyright 2017 Bo-Yi Wu. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// +build jsoniter
package json
import "github.com/json-iterator/go"
var (
json = jsoniter.ConfigCompatibleWithStandardLibrary
Marshal = json.Marshal
MarshalIndent = json.MarshalIndent
NewDecoder = json.NewDecoder
)

View File

@ -17,7 +17,7 @@ import (
var (
green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
yellow = string([]byte{27, 91, 57, 48, 59, 52, 51, 109})
red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
@ -53,7 +53,7 @@ func Logger() HandlerFunc {
return LoggerWithWriter(DefaultWriter)
}
// LoggerWithWriter instance a Logger middleware with the specified writter buffer.
// LoggerWithWriter instance a Logger middleware with the specified writer buffer.
// Example: os.Stdout, a file opened in write mode, a socket...
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
isTerm := true

View File

@ -11,12 +11,16 @@ import (
"github.com/gin-gonic/gin/binding"
)
// ENV_GIN_MODE indicates environment name for gin mode.
const ENV_GIN_MODE = "GIN_MODE"
const (
DebugMode = "debug"
// DebugMode indicates gin mode is debug.
DebugMode = "debug"
// ReleaseMode indicates gin mode is release.
ReleaseMode = "release"
TestMode = "test"
// TestMode indicates gin mode is test.
TestMode = "test"
)
const (
debugCode = iota
@ -24,7 +28,7 @@ const (
testCode
)
// DefaultWriter is the default io.Writer used the Gin for debug output and
// DefaultWriter is the default io.Writer used by Gin for debug output and
// middleware output like Logger() or Recovery().
// Note that both Logger and Recovery provides custom ways to configure their
// output io.Writer.
@ -32,6 +36,8 @@ const (
// import "github.com/mattn/go-colorable"
// gin.DefaultWriter = colorable.NewColorableStdout()
var DefaultWriter io.Writer = os.Stdout
// DefaultErrorWriter is the default io.Writer used by Gin to debug errors
var DefaultErrorWriter io.Writer = os.Stderr
var ginMode = debugCode
@ -42,6 +48,7 @@ func init() {
SetMode(mode)
}
// SetMode sets gin mode according to input string.
func SetMode(value string) {
switch value {
case DebugMode, "":
@ -59,14 +66,18 @@ func SetMode(value string) {
modeName = value
}
// DisableBindValidation closes the default validator.
func DisableBindValidation() {
binding.Validator = nil
}
// EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumberto to
// call the UseNumber method on the JSON Decoder instance.
func EnableJsonDecoderUseNumber() {
binding.EnableDecoderUseNumber = true
}
// Mode returns currently gin mode.
func Mode() string {
return modeName
}

View File

@ -10,9 +10,12 @@ import (
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httputil"
"os"
"runtime"
"strings"
"time"
)
@ -37,12 +40,37 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc {
return func(c *Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
brokenPipe = true
}
}
}
if logger != nil {
stack := stack(3)
httprequest, _ := httputil.DumpRequest(c.Request, false)
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset)
if brokenPipe {
logger.Printf("%s\n%s%s", err, string(httprequest), reset)
} else if IsDebugging() {
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s",
timeFormat(time.Now()), string(httprequest), err, stack, reset)
} else {
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s",
timeFormat(time.Now()), err, stack, reset)
}
}
// If the connection is dead, we can't write a status to it.
if brokenPipe {
c.Error(err.(error))
c.Abort()
} else {
c.AbortWithStatus(http.StatusInternalServerError)
}
c.AbortWithStatus(http.StatusInternalServerError)
}
}()
c.Next()

View File

@ -6,6 +6,7 @@ package render
import "net/http"
// Data contains ContentType and bytes data.
type Data struct {
ContentType string
Data []byte
@ -18,6 +19,7 @@ func (r Data) Render(w http.ResponseWriter) (err error) {
return
}
// WriteContentType (Data) writes custom ContentType.
func (r Data) WriteContentType(w http.ResponseWriter) {
writeContentType(w, []string{r.ContentType})
}

View File

@ -9,20 +9,27 @@ import (
"net/http"
)
// Delims represents a set of Left and Right delimiters for HTML template rendering.
type Delims struct {
Left string
// Left delimiter, defaults to {{.
Left string
// Right delimiter, defaults to }}.
Right string
}
// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
type HTMLRender interface {
// Instance returns an HTML instance.
Instance(string, interface{}) Render
}
// HTMLProduction contains template reference and its delims.
type HTMLProduction struct {
Template *template.Template
Delims Delims
}
// HTMLDebug contains template delims and pattern and function with file list.
type HTMLDebug struct {
Files []string
Glob string
@ -30,6 +37,7 @@ type HTMLDebug struct {
FuncMap template.FuncMap
}
// HTML contains template reference and its name with given interface object.
type HTML struct {
Template *template.Template
Name string
@ -38,6 +46,7 @@ type HTML struct {
var htmlContentType = []string{"text/html; charset=utf-8"}
// Instance (HTMLProduction) returns an HTML instance which it realizes Render interface.
func (r HTMLProduction) Instance(name string, data interface{}) Render {
return HTML{
Template: r.Template,
@ -46,6 +55,7 @@ func (r HTMLProduction) Instance(name string, data interface{}) Render {
}
}
// Instance (HTMLDebug) returns an HTML instance which it realizes Render interface.
func (r HTMLDebug) Instance(name string, data interface{}) Render {
return HTML{
Template: r.loadTemplate(),
@ -66,6 +76,7 @@ func (r HTMLDebug) loadTemplate() *template.Template {
panic("the HTML debug render was created without files or glob pattern")
}
// Render (HTML) executes template and writes its result with custom ContentType for response.
func (r HTML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@ -75,6 +86,7 @@ func (r HTML) Render(w http.ResponseWriter) error {
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
}
// WriteContentType (HTML) writes HTML ContentType.
func (r HTML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, htmlContentType)
}

View File

@ -10,37 +10,44 @@ import (
"html/template"
"net/http"
"github.com/gin-gonic/gin/json"
"github.com/gin-gonic/gin/internal/json"
)
// JSON contains the given interface object.
type JSON struct {
Data interface{}
}
// IndentedJSON contains the given interface object.
type IndentedJSON struct {
Data interface{}
}
// SecureJSON contains the given interface object and its prefix.
type SecureJSON struct {
Prefix string
Data interface{}
}
// JsonpJSON contains the given interface object its callback.
type JsonpJSON struct {
Callback string
Data interface{}
}
// AsciiJSON contains the given interface object.
type AsciiJSON struct {
Data interface{}
}
// SecureJSONPrefix is a string which represents SecureJSON prefix.
type SecureJSONPrefix string
var jsonContentType = []string{"application/json; charset=utf-8"}
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
var jsonAsciiContentType = []string{"application/json"}
// Render (JSON) writes data with custom ContentType.
func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil {
panic(err)
@ -48,10 +55,12 @@ func (r JSON) Render(w http.ResponseWriter) (err error) {
return
}
// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, jsonContentType)
jsonBytes, err := json.Marshal(obj)
@ -62,6 +71,7 @@ func WriteJSON(w http.ResponseWriter, obj interface{}) error {
return nil
}
// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
func (r IndentedJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
@ -72,10 +82,12 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (IndentedJSON) writes JSON ContentType.
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
func (r SecureJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
jsonBytes, err := json.Marshal(r.Data)
@ -90,10 +102,12 @@ func (r SecureJSON) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (SecureJSON) writes JSON ContentType.
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
@ -115,10 +129,12 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
return nil
}
// WriteContentType (JsonpJSON) writes Javascript ContentType.
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonpContentType)
}
// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
@ -128,10 +144,8 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
var buffer bytes.Buffer
for _, r := range string(ret) {
cvt := ""
if r < 128 {
cvt = string(r)
} else {
cvt := string(r)
if r >= 128 {
cvt = fmt.Sprintf("\\u%04x", int64(r))
}
buffer.WriteString(cvt)
@ -141,6 +155,7 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
return nil
}
// WriteContentType (AsciiJSON) writes JSON ContentType.
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonAsciiContentType)
}

View File

@ -10,22 +10,26 @@ import (
"github.com/ugorji/go/codec"
)
// MsgPack contains the given interface object.
type MsgPack struct {
Data interface{}
}
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
// WriteContentType (MsgPack) writes MsgPack ContentType.
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
writeContentType(w, msgpackContentType)
}
// Render (MsgPack) encodes the given interface object and writes data with custom ContentType.
func (r MsgPack) Render(w http.ResponseWriter) error {
return WriteMsgPack(w, r.Data)
}
// WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, msgpackContentType)
var h codec.Handle = new(codec.MsgpackHandle)
return codec.NewEncoder(w, h).Encode(obj)
var mh codec.MsgpackHandle
return codec.NewEncoder(w, &mh).Encode(obj)
}

View File

@ -1,3 +1,7 @@
// Copyright 2018 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package render
import (
@ -6,6 +10,7 @@ import (
"strconv"
)
// Reader contains the IO reader and its length, and custom ContentType and other headers.
type Reader struct {
ContentType string
ContentLength int64
@ -22,10 +27,12 @@ func (r Reader) Render(w http.ResponseWriter) (err error) {
return
}
// WriteContentType (Reader) writes custom ContentType.
func (r Reader) WriteContentType(w http.ResponseWriter) {
writeContentType(w, []string{r.ContentType})
}
// writeHeaders writes custom Header.
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
header := w.Header()
for k, v := range headers {

View File

@ -9,12 +9,14 @@ import (
"net/http"
)
// Redirect contains the http request reference and redirects status code and location.
type Redirect struct {
Code int
Request *http.Request
Location string
}
// Render (Redirect) redirects the http request to new location and writes redirect response.
func (r Redirect) Render(w http.ResponseWriter) error {
// todo(thinkerou): go1.6 not support StatusPermanentRedirect(308)
// when we upgrade go version we can use http.StatusPermanentRedirect
@ -25,4 +27,5 @@ func (r Redirect) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (Redirect) don't write any ContentType.
func (r Redirect) WriteContentType(http.ResponseWriter) {}

View File

@ -6,8 +6,11 @@ package render
import "net/http"
// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
type Render interface {
// Render writes data with custom ContentType.
Render(http.ResponseWriter) error
// WriteContentType writes custom ContentType.
WriteContentType(w http.ResponseWriter)
}
@ -27,6 +30,7 @@ var (
_ Render = MsgPack{}
_ Render = Reader{}
_ Render = AsciiJSON{}
_ Render = ProtoBuf{}
)
func writeContentType(w http.ResponseWriter, value []string) {

View File

@ -10,6 +10,7 @@ import (
"net/http"
)
// String contains the given interface object slice and its format.
type String struct {
Format string
Data []interface{}
@ -17,20 +18,23 @@ type String struct {
var plainContentType = []string{"text/plain; charset=utf-8"}
// Render (String) writes data with custom ContentType.
func (r String) Render(w http.ResponseWriter) error {
WriteString(w, r.Format, r.Data)
return nil
}
// WriteContentType (String) writes Plain ContentType.
func (r String) WriteContentType(w http.ResponseWriter) {
writeContentType(w, plainContentType)
}
// WriteString writes data according to its format and write custom ContentType.
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
writeContentType(w, plainContentType)
if len(data) > 0 {
fmt.Fprintf(w, format, data...)
} else {
io.WriteString(w, format)
return
}
io.WriteString(w, format)
}

View File

@ -9,17 +9,20 @@ import (
"net/http"
)
// XML contains the given interface object.
type XML struct {
Data interface{}
}
var xmlContentType = []string{"application/xml; charset=utf-8"}
// Render (XML) encodes the given interface object and writes data with custom ContentType.
func (r XML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
return xml.NewEncoder(w).Encode(r.Data)
}
// WriteContentType (XML) writes XML ContentType for response.
func (r XML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, xmlContentType)
}

View File

@ -10,12 +10,14 @@ import (
"gopkg.in/yaml.v2"
)
// YAML contains the given interface object.
type YAML struct {
Data interface{}
}
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
// Render (YAML) marshals the given interface object and writes data with custom ContentType.
func (r YAML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@ -28,6 +30,7 @@ func (r YAML) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (YAML) writes YAML ContentType for response.
func (r YAML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, yamlContentType)
}

View File

@ -11,11 +11,13 @@ import (
"strings"
)
// IRouter defines all router handle interface includes single and group router.
type IRouter interface {
IRoutes
Group(string, ...HandlerFunc) *RouterGroup
}
// IRoutes defines all router handle interface.
type IRoutes interface {
Use(...HandlerFunc) IRoutes
@ -34,8 +36,8 @@ type IRoutes interface {
StaticFS(string, http.FileSystem) IRoutes
}
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
// and an array of handlers (middleware).
// RouterGroup is used internally to configure router, a RouterGroup is associated with
// a prefix and an array of handlers (middleware).
type RouterGroup struct {
Handlers HandlersChain
basePath string
@ -51,8 +53,8 @@ func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
return group.returnObj()
}
// Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
// For example, all the routes that use a common middlware for authorization could be grouped.
// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
// For example, all the routes that use a common middleware for authorization could be grouped.
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
return &RouterGroup{
Handlers: group.combineHandlers(handlers),
@ -61,6 +63,8 @@ func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *R
}
}
// BasePath returns the base path of router group.
// For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
func (group *RouterGroup) BasePath() string {
return group.basePath
}
@ -181,11 +185,22 @@ func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRou
func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
absolutePath := group.calculateAbsolutePath(relativePath)
fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
_, nolisting := fs.(*onlyfilesFS)
return func(c *Context) {
if nolisting {
if _, nolisting := fs.(*onlyfilesFS); nolisting {
c.Writer.WriteHeader(http.StatusNotFound)
}
file := c.Param("filepath")
// Check if file exists and/or if we have permission to access it
if _, err := fs.Open(file); err != nil {
c.Writer.WriteHeader(http.StatusNotFound)
c.handlers = group.engine.allNoRoute
// Reset index
c.index = -1
return
}
fileServer.ServeHTTP(c.Writer, c.Request)
}
}

View File

@ -193,9 +193,16 @@ func (n *node) addRoute(path string, handlers HandlersChain) {
}
}
panic("path segment '" + path +
pathSeg := path
if n.nType != catchAll {
pathSeg = strings.SplitN(path, "/", 2)[0]
}
prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
panic("'" + pathSeg +
"' in new path '" + fullPath +
"' conflicts with existing wildcard '" + n.path +
"' in path '" + fullPath + "'")
"' in existing prefix '" + prefix +
"'")
}
c := path[0]

View File

@ -14,8 +14,10 @@ import (
"strings"
)
// BindKey indicates a default bind key.
const BindKey = "_gin-gonic/gin/bindkey"
// Bind is a helper function for given interface object and returns a Gin middleware.
func Bind(val interface{}) HandlerFunc {
value := reflect.ValueOf(val)
if value.Kind() == reflect.Ptr {
@ -33,16 +35,14 @@ func Bind(val interface{}) HandlerFunc {
}
}
// WrapF is a helper function for wrapping http.HandlerFunc
// Returns a Gin middleware
// WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware.
func WrapF(f http.HandlerFunc) HandlerFunc {
return func(c *Context) {
f(c.Writer, c.Request)
}
}
// WrapH is a helper function for wrapping http.Handler
// Returns a Gin middleware
// WrapH is a helper function for wrapping http.Handler and returns a Gin middleware.
func WrapH(h http.Handler) HandlerFunc {
return func(c *Context) {
h.ServeHTTP(c.Writer, c.Request)

View File

@ -1 +0,0 @@
box: wercker/default

View File

@ -35,6 +35,7 @@ Hanno Braun <mail at hannobraun.com>
Henri Yandell <flamefew at gmail.com>
Hirotaka Yamamoto <ymmt2005 at gmail.com>
ICHINOSE Shogo <shogo82148 at gmail.com>
Ilia Cimpoes <ichimpoesh at gmail.com>
INADA Naoki <songofacandy at gmail.com>
Jacek Szwec <szwec.jacek at gmail.com>
James Harr <james.harr at gmail.com>
@ -72,6 +73,9 @@ Shuode Li <elemount at qq.com>
Soroush Pour <me at soroushjp.com>
Stan Putrya <root.vagner at gmail.com>
Stanley Gunawan <gunawan.stanley at gmail.com>
Steven Hartland <steven.hartland at multiplay.co.uk>
Thomas Wodarek <wodarekwebpage at gmail.com>
Tom Jenkinson <tom at tjenkinson.me>
Xiangyu Hu <xiangyu.hu at outlook.com>
Xiaobing Jiang <s7v7nislands at gmail.com>
Xiuming Chen <cc at cxm.cc>
@ -87,3 +91,4 @@ Keybase Inc.
Percona LLC
Pivotal Inc.
Stripe Inc.
Multiplay Ltd.

View File

@ -1,14 +1,3 @@
## Version 1.4.1 (2018-11-14)
Bugfixes:
- Fix TIME format for binary columns (#818)
- Fix handling of empty auth plugin names (#835)
- Fix caching_sha2_password with empty password (#826)
- Fix canceled context broke mysqlConn (#862)
- Fix OldAuthSwitchRequest support (#870)
- Fix Auth Response packet for cleartext password (#887)
## Version 1.4 (2018-06-03)
Changes:

View File

@ -40,7 +40,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
* Optional placeholder interpolation
## Requirements
* Go 1.7 or higher. We aim to support the 3 latest versions of Go.
* Go 1.8 or higher. We aim to support the 3 latest versions of Go.
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
---------------------------------------

View File

@ -360,13 +360,15 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
pubKey := mc.cfg.pubKey
if pubKey == nil {
// request public key from server
data := mc.buf.takeSmallBuffer(4 + 1)
data, err := mc.buf.takeSmallBuffer(4 + 1)
if err != nil {
return err
}
data[4] = cachingSha2PasswordRequestPublicKey
mc.writePacket(data)
// parse public key
data, err := mc.readPacket()
if err != nil {
if data, err = mc.readPacket(); err != nil {
return err
}

View File

@ -22,17 +22,17 @@ const defaultBufSize = 4096
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
// Also highly optimized for this particular use case.
type buffer struct {
buf []byte
buf []byte // buf is a byte buffer who's length and capacity are equal.
nc net.Conn
idx int
length int
timeout time.Duration
}
// newBuffer allocates and returns a new buffer.
func newBuffer(nc net.Conn) buffer {
var b [defaultBufSize]byte
return buffer{
buf: b[:],
buf: make([]byte, defaultBufSize),
nc: nc,
}
}
@ -105,43 +105,56 @@ func (b *buffer) readNext(need int) ([]byte, error) {
return b.buf[offset:b.idx], nil
}
// returns a buffer with the requested size.
// takeBuffer returns a buffer with the requested size.
// If possible, a slice from the existing buffer is returned.
// Otherwise a bigger buffer is made.
// Only one buffer (total) can be used at a time.
func (b *buffer) takeBuffer(length int) []byte {
func (b *buffer) takeBuffer(length int) ([]byte, error) {
if b.length > 0 {
return nil
return nil, ErrBusyBuffer
}
// test (cheap) general case first
if length <= defaultBufSize || length <= cap(b.buf) {
return b.buf[:length]
if length <= cap(b.buf) {
return b.buf[:length], nil
}
if length < maxPacketSize {
b.buf = make([]byte, length)
return b.buf
return b.buf, nil
}
return make([]byte, length)
// buffer is larger than we want to store.
return make([]byte, length), nil
}
// shortcut which can be used if the requested buffer is guaranteed to be
// smaller than defaultBufSize
// takeSmallBuffer is shortcut which can be used if length is
// known to be smaller than defaultBufSize.
// Only one buffer (total) can be used at a time.
func (b *buffer) takeSmallBuffer(length int) []byte {
func (b *buffer) takeSmallBuffer(length int) ([]byte, error) {
if b.length > 0 {
return nil
return nil, ErrBusyBuffer
}
return b.buf[:length]
return b.buf[:length], nil
}
// takeCompleteBuffer returns the complete existing buffer.
// This can be used if the necessary buffer size is unknown.
// cap and len of the returned buffer will be equal.
// Only one buffer (total) can be used at a time.
func (b *buffer) takeCompleteBuffer() []byte {
func (b *buffer) takeCompleteBuffer() ([]byte, error) {
if b.length > 0 {
return nil
return nil, ErrBusyBuffer
}
return b.buf
return b.buf, nil
}
// store stores buf, an updated buffer, if its suitable to do so.
func (b *buffer) store(buf []byte) error {
if b.length > 0 {
return ErrBusyBuffer
} else if cap(buf) <= maxPacketSize && cap(buf) > cap(b.buf) {
b.buf = buf[:cap(buf)]
}
return nil
}

View File

@ -9,6 +9,8 @@
package mysql
import (
"context"
"database/sql"
"database/sql/driver"
"io"
"net"
@ -17,16 +19,6 @@ import (
"time"
)
// a copy of context.Context for Go 1.7 and earlier
type mysqlContext interface {
Done() <-chan struct{}
Err() error
// defined in context.Context, but not used in this driver:
// Deadline() (deadline time.Time, ok bool)
// Value(key interface{}) interface{}
}
type mysqlConn struct {
buf buffer
netConn net.Conn
@ -43,7 +35,7 @@ type mysqlConn struct {
// for context support (Go 1.8+)
watching bool
watcher chan<- mysqlContext
watcher chan<- context.Context
closech chan struct{}
finished chan<- struct{}
canceled atomicError // set non-nil if conn is canceled
@ -190,10 +182,10 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
return "", driver.ErrSkip
}
buf := mc.buf.takeCompleteBuffer()
if buf == nil {
buf, err := mc.buf.takeCompleteBuffer()
if err != nil {
// can not take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
errLog.Print(err)
return "", ErrInvalidConn
}
buf = buf[:0]
@ -459,3 +451,193 @@ func (mc *mysqlConn) finish() {
case <-mc.closech:
}
}
// Ping implements driver.Pinger interface
func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
if mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return driver.ErrBadConn
}
if err = mc.watchCancel(ctx); err != nil {
return
}
defer mc.finish()
if err = mc.writeCommandPacket(comPing); err != nil {
return mc.markBadConn(err)
}
return mc.readResultOK()
}
// BeginTx implements driver.ConnBeginTx interface
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if err := mc.watchCancel(ctx); err != nil {
return nil, err
}
defer mc.finish()
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
level, err := mapIsolationLevel(opts.Isolation)
if err != nil {
return nil, err
}
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level)
if err != nil {
return nil, err
}
}
return mc.begin(opts.ReadOnly)
}
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
dargs, err := namedValueToValue(args)
if err != nil {
return nil, err
}
if err := mc.watchCancel(ctx); err != nil {
return nil, err
}
rows, err := mc.query(query, dargs)
if err != nil {
mc.finish()
return nil, err
}
rows.finish = mc.finish
return rows, err
}
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
dargs, err := namedValueToValue(args)
if err != nil {
return nil, err
}
if err := mc.watchCancel(ctx); err != nil {
return nil, err
}
defer mc.finish()
return mc.Exec(query, dargs)
}
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
if err := mc.watchCancel(ctx); err != nil {
return nil, err
}
stmt, err := mc.Prepare(query)
mc.finish()
if err != nil {
return nil, err
}
select {
default:
case <-ctx.Done():
stmt.Close()
return nil, ctx.Err()
}
return stmt, nil
}
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
dargs, err := namedValueToValue(args)
if err != nil {
return nil, err
}
if err := stmt.mc.watchCancel(ctx); err != nil {
return nil, err
}
rows, err := stmt.query(dargs)
if err != nil {
stmt.mc.finish()
return nil, err
}
rows.finish = stmt.mc.finish
return rows, err
}
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
dargs, err := namedValueToValue(args)
if err != nil {
return nil, err
}
if err := stmt.mc.watchCancel(ctx); err != nil {
return nil, err
}
defer stmt.mc.finish()
return stmt.Exec(dargs)
}
func (mc *mysqlConn) watchCancel(ctx context.Context) error {
if mc.watching {
// Reach here if canceled,
// so the connection is already invalid
mc.cleanup()
return nil
}
// When ctx is already cancelled, don't watch it.
if err := ctx.Err(); err != nil {
return err
}
// When ctx is not cancellable, don't watch it.
if ctx.Done() == nil {
return nil
}
// When watcher is not alive, can't watch it.
if mc.watcher == nil {
return nil
}
mc.watching = true
mc.watcher <- ctx
return nil
}
func (mc *mysqlConn) startWatcher() {
watcher := make(chan context.Context, 1)
mc.watcher = watcher
finished := make(chan struct{})
mc.finished = finished
go func() {
for {
var ctx context.Context
select {
case ctx = <-watcher:
case <-mc.closech:
return
}
select {
case <-ctx.Done():
mc.cancel(ctx.Err())
case <-finished:
case <-mc.closech:
return
}
}
}()
}
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
nv.Value, err = converter{}.ConvertValue(nv.Value)
return
}
// ResetSession implements driver.SessionResetter.
// (From Go 1.10)
func (mc *mysqlConn) ResetSession(ctx context.Context) error {
if mc.closed.IsSet() {
return driver.ErrBadConn
}
return nil
}

View File

@ -1,207 +0,0 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
// +build go1.8
package mysql
import (
"context"
"database/sql"
"database/sql/driver"
)
// Ping implements driver.Pinger interface
func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
if mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return driver.ErrBadConn
}
if err = mc.watchCancel(ctx); err != nil {
return
}
defer mc.finish()
if err = mc.writeCommandPacket(comPing); err != nil {
return
}
return mc.readResultOK()
}
// BeginTx implements driver.ConnBeginTx interface
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if err := mc.watchCancel(ctx); err != nil {
return nil, err
}
defer mc.finish()
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
level, err := mapIsolationLevel(opts.Isolation)
if err != nil {
return nil, err
}
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level)
if err != nil {
return nil, err
}
}
return mc.begin(opts.ReadOnly)
}
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
dargs, err := namedValueToValue(args)
if err != nil {
return nil, err
}
if err := mc.watchCancel(ctx); err != nil {
return nil, err
}
rows, err := mc.query(query, dargs)
if err != nil {
mc.finish()
return nil, err
}
rows.finish = mc.finish
return rows, err
}
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
dargs, err := namedValueToValue(args)
if err != nil {
return nil, err
}
if err := mc.watchCancel(ctx); err != nil {
return nil, err
}
defer mc.finish()
return mc.Exec(query, dargs)
}
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
if err := mc.watchCancel(ctx); err != nil {
return nil, err
}
stmt, err := mc.Prepare(query)
mc.finish()
if err != nil {
return nil, err
}
select {
default:
case <-ctx.Done():
stmt.Close()
return nil, ctx.Err()
}
return stmt, nil
}
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
dargs, err := namedValueToValue(args)
if err != nil {
return nil, err
}
if err := stmt.mc.watchCancel(ctx); err != nil {
return nil, err
}
rows, err := stmt.query(dargs)
if err != nil {
stmt.mc.finish()
return nil, err
}
rows.finish = stmt.mc.finish
return rows, err
}
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
dargs, err := namedValueToValue(args)
if err != nil {
return nil, err
}
if err := stmt.mc.watchCancel(ctx); err != nil {
return nil, err
}
defer stmt.mc.finish()
return stmt.Exec(dargs)
}
func (mc *mysqlConn) watchCancel(ctx context.Context) error {
if mc.watching {
// Reach here if canceled,
// so the connection is already invalid
mc.cleanup()
return nil
}
// When ctx is already cancelled, don't watch it.
if err := ctx.Err(); err != nil {
return err
}
// When ctx is not cancellable, don't watch it.
if ctx.Done() == nil {
return nil
}
// When watcher is not alive, can't watch it.
if mc.watcher == nil {
return nil
}
mc.watching = true
mc.watcher <- ctx
return nil
}
func (mc *mysqlConn) startWatcher() {
watcher := make(chan mysqlContext, 1)
mc.watcher = watcher
finished := make(chan struct{})
mc.finished = finished
go func() {
for {
var ctx mysqlContext
select {
case ctx = <-watcher:
case <-mc.closech:
return
}
select {
case <-ctx.Done():
mc.cancel(ctx.Err())
case <-finished:
case <-mc.closech:
return
}
}
}()
}
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
nv.Value, err = converter{}.ConvertValue(nv.Value)
return
}
// ResetSession implements driver.SessionResetter.
// (From Go 1.10)
func (mc *mysqlConn) ResetSession(ctx context.Context) error {
if mc.closed.IsSet() {
return driver.ErrBadConn
}
return nil
}

View File

@ -23,11 +23,6 @@ import (
"sync"
)
// watcher interface is used for context support (From Go 1.8)
type watcher interface {
startWatcher()
}
// MySQLDriver is exported to make the driver directly accessible.
// In general the driver is used via the database/sql package.
type MySQLDriver struct{}
@ -55,7 +50,7 @@ func RegisterDial(net string, dial DialFunc) {
// Open new Connection.
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
// the DSN string is formated
// the DSN string is formatted
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
var err error
@ -82,6 +77,10 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr)
}
if err != nil {
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
errLog.Print("net.Error from Dial()': ", nerr.Error())
return nil, driver.ErrBadConn
}
return nil, err
}
@ -96,9 +95,7 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
}
// Call startWatcher for context support (From Go 1.8)
if s, ok := interface{}(mc).(watcher); ok {
s.startWatcher()
}
mc.startWatcher()
mc.buf = newBuffer(mc.netConn)

View File

@ -51,7 +51,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
mc.sequence++
// packets with length 0 terminate a previous packet which is a
// multiple of (2^24)1 bytes long
// multiple of (2^24)-1 bytes long
if pktLen == 0 {
// there was no previous packet
if prevData == nil {
@ -286,10 +286,10 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
}
// Calculate packet length and get buffer with that size
data := mc.buf.takeSmallBuffer(pktLen + 4)
if data == nil {
data, err := mc.buf.takeSmallBuffer(pktLen + 4)
if err != nil {
// cannot take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
errLog.Print(err)
return errBadConnNoWrite
}
@ -367,10 +367,10 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error {
pktLen := 4 + len(authData)
data := mc.buf.takeSmallBuffer(pktLen)
if data == nil {
data, err := mc.buf.takeSmallBuffer(pktLen)
if err != nil {
// cannot take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
errLog.Print(err)
return errBadConnNoWrite
}
@ -387,10 +387,10 @@ func (mc *mysqlConn) writeCommandPacket(command byte) error {
// Reset Packet Sequence
mc.sequence = 0
data := mc.buf.takeSmallBuffer(4 + 1)
if data == nil {
data, err := mc.buf.takeSmallBuffer(4 + 1)
if err != nil {
// cannot take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
errLog.Print(err)
return errBadConnNoWrite
}
@ -406,10 +406,10 @@ func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error {
mc.sequence = 0
pktLen := 1 + len(arg)
data := mc.buf.takeBuffer(pktLen + 4)
if data == nil {
data, err := mc.buf.takeBuffer(pktLen + 4)
if err != nil {
// cannot take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
errLog.Print(err)
return errBadConnNoWrite
}
@ -427,10 +427,10 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
// Reset Packet Sequence
mc.sequence = 0
data := mc.buf.takeSmallBuffer(4 + 1 + 4)
if data == nil {
data, err := mc.buf.takeSmallBuffer(4 + 1 + 4)
if err != nil {
// cannot take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
errLog.Print(err)
return errBadConnNoWrite
}
@ -883,7 +883,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
const minPktLen = 4 + 1 + 4 + 1 + 4
mc := stmt.mc
// Determine threshould dynamically to avoid packet size shortage.
// Determine threshold dynamically to avoid packet size shortage.
longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1)
if longDataSize < 64 {
longDataSize = 64
@ -893,15 +893,17 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
mc.sequence = 0
var data []byte
var err error
if len(args) == 0 {
data = mc.buf.takeBuffer(minPktLen)
data, err = mc.buf.takeBuffer(minPktLen)
} else {
data = mc.buf.takeCompleteBuffer()
data, err = mc.buf.takeCompleteBuffer()
// In this case the len(data) == cap(data) which is used to optimise the flow below.
}
if data == nil {
if err != nil {
// cannot take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
errLog.Print(err)
return errBadConnNoWrite
}
@ -927,7 +929,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
pos := minPktLen
var nullMask []byte
if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= len(data) {
if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= cap(data) {
// buffer has to be extended but we don't know by how much so
// we depend on append after all data with known sizes fit.
// We stop at that because we deal with a lot of columns here
@ -936,10 +938,11 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
copy(tmp[:pos], data[:pos])
data = tmp
nullMask = data[pos : pos+maskLen]
// No need to clean nullMask as make ensures that.
pos += maskLen
} else {
nullMask = data[pos : pos+maskLen]
for i := 0; i < maskLen; i++ {
for i := range nullMask {
nullMask[i] = 0
}
pos += maskLen
@ -1076,7 +1079,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
// In that case we must build the data packet with the new values buffer
if valuesCap != cap(paramValues) {
data = append(data[:pos], paramValues...)
mc.buf.buf = data
if err = mc.buf.store(data); err != nil {
errLog.Print(err)
return errBadConnNoWrite
}
}
pos += len(paramValues)

View File

@ -10,8 +10,10 @@ package mysql
import (
"crypto/tls"
"database/sql"
"database/sql/driver"
"encoding/binary"
"errors"
"fmt"
"io"
"strconv"
@ -80,7 +82,7 @@ func DeregisterTLSConfig(key string) {
func getTLSConfigClone(key string) (config *tls.Config) {
tlsConfigLock.RLock()
if v, ok := tlsConfigRegistry[key]; ok {
config = cloneTLSConfig(v)
config = v.Clone()
}
tlsConfigLock.RUnlock()
return
@ -724,3 +726,30 @@ func (ae *atomicError) Value() error {
}
return nil
}
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
dargs := make([]driver.Value, len(named))
for n, param := range named {
if len(param.Name) > 0 {
// TODO: support the use of Named Parameters #561
return nil, errors.New("mysql: driver does not support the use of Named Parameters")
}
dargs[n] = param.Value
}
return dargs, nil
}
func mapIsolationLevel(level driver.IsolationLevel) (string, error) {
switch sql.IsolationLevel(level) {
case sql.LevelRepeatableRead:
return "REPEATABLE READ", nil
case sql.LevelReadCommitted:
return "READ COMMITTED", nil
case sql.LevelReadUncommitted:
return "READ UNCOMMITTED", nil
case sql.LevelSerializable:
return "SERIALIZABLE", nil
default:
return "", fmt.Errorf("mysql: unsupported isolation level: %v", level)
}
}

View File

@ -1,40 +0,0 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
// +build go1.7
// +build !go1.8
package mysql
import "crypto/tls"
func cloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
Renegotiation: c.Renegotiation,
}
}

View File

@ -1,50 +0,0 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
// +build go1.8
package mysql
import (
"crypto/tls"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
)
func cloneTLSConfig(c *tls.Config) *tls.Config {
return c.Clone()
}
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
dargs := make([]driver.Value, len(named))
for n, param := range named {
if len(param.Name) > 0 {
// TODO: support the use of Named Parameters #561
return nil, errors.New("mysql: driver does not support the use of Named Parameters")
}
dargs[n] = param.Value
}
return dargs, nil
}
func mapIsolationLevel(level driver.IsolationLevel) (string, error) {
switch sql.IsolationLevel(level) {
case sql.LevelRepeatableRead:
return "REPEATABLE READ", nil
case sql.LevelReadCommitted:
return "READ COMMITTED", nil
case sql.LevelReadUncommitted:
return "READ UNCOMMITTED", nil
case sql.LevelSerializable:
return "SERIALIZABLE", nil
default:
return "", fmt.Errorf("mysql: unsupported isolation level: %v", level)
}
}

View File

@ -186,7 +186,6 @@ func (p *Buffer) DecodeVarint() (x uint64, err error) {
if b&0x80 == 0 {
goto done
}
// x -= 0x80 << 63 // Always zero.
return 0, errOverflow

View File

@ -246,7 +246,8 @@ func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
return false
}
m1, m2 := e1.value, e2.value
m1 := extensionAsLegacyType(e1.value)
m2 := extensionAsLegacyType(e2.value)
if m1 == nil && m2 == nil {
// Both have only encoded form.

View File

@ -185,9 +185,25 @@ type Extension struct {
// extension will have only enc set. When such an extension is
// accessed using GetExtension (or GetExtensions) desc and value
// will be set.
desc *ExtensionDesc
desc *ExtensionDesc
// value is a concrete value for the extension field. Let the type of
// desc.ExtensionType be the "API type" and the type of Extension.value
// be the "storage type". The API type and storage type are the same except:
// * For scalars (except []byte), the API type uses *T,
// while the storage type uses T.
// * For repeated fields, the API type uses []T, while the storage type
// uses *[]T.
//
// The reason for the divergence is so that the storage type more naturally
// matches what is expected of when retrieving the values through the
// protobuf reflection APIs.
//
// The value may only be populated if desc is also populated.
value interface{}
enc []byte
// enc is the raw bytes for the extension field.
enc []byte
}
// SetRawExtension is for testing only.
@ -334,7 +350,7 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
// descriptors with the same field number.
return nil, errors.New("proto: descriptor conflict")
}
return e.value, nil
return extensionAsLegacyType(e.value), nil
}
if extension.ExtensionType == nil {
@ -349,11 +365,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
// Remember the decoded version and drop the encoded version.
// That way it is safe to mutate what we return.
e.value = v
e.value = extensionAsStorageType(v)
e.desc = extension
e.enc = nil
emap[extension.Field] = e
return e.value, nil
return extensionAsLegacyType(e.value), nil
}
// defaultExtensionValue returns the default value for extension.
@ -488,7 +504,7 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error
}
typ := reflect.TypeOf(extension.ExtensionType)
if typ != reflect.TypeOf(value) {
return errors.New("proto: bad extension value type")
return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType)
}
// nil extension values need to be caught early, because the
// encoder can't distinguish an ErrNil due to a nil extension
@ -500,7 +516,7 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error
}
extmap := epb.extensionsWrite()
extmap[extension.Field] = Extension{desc: extension, value: value}
extmap[extension.Field] = Extension{desc: extension, value: extensionAsStorageType(value)}
return nil
}
@ -541,3 +557,51 @@ func RegisterExtension(desc *ExtensionDesc) {
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
return extensionMaps[reflect.TypeOf(pb).Elem()]
}
// extensionAsLegacyType converts an value in the storage type as the API type.
// See Extension.value.
func extensionAsLegacyType(v interface{}) interface{} {
switch rv := reflect.ValueOf(v); rv.Kind() {
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
// Represent primitive types as a pointer to the value.
rv2 := reflect.New(rv.Type())
rv2.Elem().Set(rv)
v = rv2.Interface()
case reflect.Ptr:
// Represent slice types as the value itself.
switch rv.Type().Elem().Kind() {
case reflect.Slice:
if rv.IsNil() {
v = reflect.Zero(rv.Type().Elem()).Interface()
} else {
v = rv.Elem().Interface()
}
}
}
return v
}
// extensionAsStorageType converts an value in the API type as the storage type.
// See Extension.value.
func extensionAsStorageType(v interface{}) interface{} {
switch rv := reflect.ValueOf(v); rv.Kind() {
case reflect.Ptr:
// Represent slice types as the value itself.
switch rv.Type().Elem().Kind() {
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
if rv.IsNil() {
v = reflect.Zero(rv.Type().Elem()).Interface()
} else {
v = rv.Elem().Interface()
}
}
case reflect.Slice:
// Represent slice types as a pointer to the value.
if rv.Type().Elem().Kind() != reflect.Uint8 {
rv2 := reflect.New(rv.Type())
rv2.Elem().Set(rv)
v = rv2.Interface()
}
}
return v
}

View File

@ -341,26 +341,6 @@ type Message interface {
ProtoMessage()
}
// Stats records allocation details about the protocol buffer encoders
// and decoders. Useful for tuning the library itself.
type Stats struct {
Emalloc uint64 // mallocs in encode
Dmalloc uint64 // mallocs in decode
Encode uint64 // number of encodes
Decode uint64 // number of decodes
Chit uint64 // number of cache hits
Cmiss uint64 // number of cache misses
Size uint64 // number of sizes
}
// Set to true to enable stats collection.
const collectStats = false
var stats Stats
// GetStats returns a copy of the global Stats structure.
func GetStats() Stats { return stats }
// A Buffer is a buffer manager for marshaling and unmarshaling
// protocol buffers. It may be reused between invocations to
// reduce memory usage. It is not necessary to use a Buffer;
@ -960,13 +940,19 @@ func isProto3Zero(v reflect.Value) bool {
return false
}
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package.
const ProtoPackageIsVersion2 = true
const (
// ProtoPackageIsVersion3 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package.
ProtoPackageIsVersion3 = true
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package.
const ProtoPackageIsVersion1 = true
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package.
ProtoPackageIsVersion2 = true
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package.
ProtoPackageIsVersion1 = true
)
// InternalMessageInfo is a type used internally by generated .pb.go files.
// This type is not intended to be used by non-generated code.

View File

@ -36,13 +36,7 @@ package proto
*/
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"reflect"
"sort"
"sync"
)
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
@ -145,46 +139,9 @@ func skipVarint(buf []byte) []byte {
return buf[i+1:]
}
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
func MarshalMessageSet(exts interface{}) ([]byte, error) {
return marshalMessageSet(exts, false)
}
// marshaMessageSet implements above function, with the opt to turn on / off deterministic during Marshal.
func marshalMessageSet(exts interface{}, deterministic bool) ([]byte, error) {
switch exts := exts.(type) {
case *XXX_InternalExtensions:
var u marshalInfo
siz := u.sizeMessageSet(exts)
b := make([]byte, 0, siz)
return u.appendMessageSet(b, exts, deterministic)
case map[int32]Extension:
// This is an old-style extension map.
// Wrap it in a new-style XXX_InternalExtensions.
ie := XXX_InternalExtensions{
p: &struct {
mu sync.Mutex
extensionMap map[int32]Extension
}{
extensionMap: exts,
},
}
var u marshalInfo
siz := u.sizeMessageSet(&ie)
b := make([]byte, 0, siz)
return u.appendMessageSet(b, &ie, deterministic)
default:
return nil, errors.New("proto: not an extension map")
}
}
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
// unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
func UnmarshalMessageSet(buf []byte, exts interface{}) error {
func unmarshalMessageSet(buf []byte, exts interface{}) error {
var m map[int32]Extension
switch exts := exts.(type) {
case *XXX_InternalExtensions:
@ -222,93 +179,3 @@ func UnmarshalMessageSet(buf []byte, exts interface{}) error {
}
return nil
}
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
var m map[int32]Extension
switch exts := exts.(type) {
case *XXX_InternalExtensions:
var mu sync.Locker
m, mu = exts.extensionsRead()
if m != nil {
// Keep the extensions map locked until we're done marshaling to prevent
// races between marshaling and unmarshaling the lazily-{en,de}coded
// values.
mu.Lock()
defer mu.Unlock()
}
case map[int32]Extension:
m = exts
default:
return nil, errors.New("proto: not an extension map")
}
var b bytes.Buffer
b.WriteByte('{')
// Process the map in key order for deterministic output.
ids := make([]int32, 0, len(m))
for id := range m {
ids = append(ids, id)
}
sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
for i, id := range ids {
ext := m[id]
msd, ok := messageSetMap[id]
if !ok {
// Unknown type; we can't render it, so skip it.
continue
}
if i > 0 && b.Len() > 1 {
b.WriteByte(',')
}
fmt.Fprintf(&b, `"[%s]":`, msd.name)
x := ext.value
if x == nil {
x = reflect.New(msd.t.Elem()).Interface()
if err := Unmarshal(ext.enc, x.(Message)); err != nil {
return nil, err
}
}
d, err := json.Marshal(x)
if err != nil {
return nil, err
}
b.Write(d)
}
b.WriteByte('}')
return b.Bytes(), nil
}
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error {
// Common-case fast path.
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
return nil
}
// This is fairly tricky, and it's not clear that it is needed.
return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
}
// A global registry of types that can be used in a MessageSet.
var messageSetMap = make(map[int32]messageSetDesc)
type messageSetDesc struct {
t reflect.Type // pointer to struct
name string
}
// RegisterMessageSetType is called from the generated code.
func RegisterMessageSetType(m Message, fieldNum int32, name string) {
messageSetMap[fieldNum] = messageSetDesc{
t: reflect.TypeOf(m),
name: name,
}
}

View File

@ -79,10 +79,13 @@ func toPointer(i *Message) pointer {
// toAddrPointer converts an interface to a pointer that points to
// the interface data.
func toAddrPointer(i *interface{}, isptr bool) pointer {
func toAddrPointer(i *interface{}, isptr, deref bool) pointer {
v := reflect.ValueOf(*i)
u := reflect.New(v.Type())
u.Elem().Set(v)
if deref {
u = u.Elem()
}
return pointer{v: u}
}

View File

@ -85,16 +85,21 @@ func toPointer(i *Message) pointer {
// toAddrPointer converts an interface to a pointer that points to
// the interface data.
func toAddrPointer(i *interface{}, isptr bool) pointer {
func toAddrPointer(i *interface{}, isptr, deref bool) (p pointer) {
// Super-tricky - read or get the address of data word of interface value.
if isptr {
// The interface is of pointer type, thus it is a direct interface.
// The data word is the pointer data itself. We take its address.
return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
p = pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
} else {
// The interface is not of pointer type. The data word is the pointer
// to the data.
p = pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
}
// The interface is not of pointer type. The data word is the pointer
// to the data.
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
if deref {
p.p = *(*unsafe.Pointer)(p.p)
}
return p
}
// valToPointer converts v to a pointer. v must be of pointer type.

View File

@ -334,9 +334,6 @@ func GetProperties(t reflect.Type) *StructProperties {
sprop, ok := propertiesMap[t]
propertiesMu.RUnlock()
if ok {
if collectStats {
stats.Chit++
}
return sprop
}
@ -346,17 +343,20 @@ func GetProperties(t reflect.Type) *StructProperties {
return sprop
}
type (
oneofFuncsIface interface {
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
}
oneofWrappersIface interface {
XXX_OneofWrappers() []interface{}
}
)
// getPropertiesLocked requires that propertiesMu is held.
func getPropertiesLocked(t reflect.Type) *StructProperties {
if prop, ok := propertiesMap[t]; ok {
if collectStats {
stats.Chit++
}
return prop
}
if collectStats {
stats.Cmiss++
}
prop := new(StructProperties)
// in case of recursive protos, fill this in now.
@ -391,13 +391,14 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
// Re-order prop.order.
sort.Sort(prop)
type oneofMessage interface {
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
var oots []interface{}
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
case oneofFuncsIface:
_, _, _, oots = m.XXX_OneofFuncs()
case oneofWrappersIface:
oots = m.XXX_OneofWrappers()
}
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
var oots []interface{}
_, _, _, oots = om.XXX_OneofFuncs()
if len(oots) > 0 {
// Interpret oneof metadata.
prop.OneofTypes = make(map[string]*OneofProperties)
for _, oot := range oots {

View File

@ -87,6 +87,7 @@ type marshalElemInfo struct {
sizer sizer
marshaler marshaler
isptr bool // elem is pointer typed, thus interface of this type is a direct interface (extension only)
deref bool // dereference the pointer before operating on it; implies isptr
}
var (
@ -320,8 +321,11 @@ func (u *marshalInfo) computeMarshalInfo() {
// get oneof implementers
var oneofImplementers []interface{}
if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
case oneofFuncsIface:
_, _, _, oneofImplementers = m.XXX_OneofFuncs()
case oneofWrappersIface:
oneofImplementers = m.XXX_OneofWrappers()
}
n := t.NumField()
@ -407,13 +411,22 @@ func (u *marshalInfo) getExtElemInfo(desc *ExtensionDesc) *marshalElemInfo {
panic("tag is not an integer")
}
wt := wiretype(tags[0])
if t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct {
t = t.Elem()
}
sizer, marshaler := typeMarshaler(t, tags, false, false)
var deref bool
if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 {
t = reflect.PtrTo(t)
deref = true
}
e = &marshalElemInfo{
wiretag: uint64(tag)<<3 | wt,
tagsize: SizeVarint(uint64(tag) << 3),
sizer: sizer,
marshaler: marshaler,
isptr: t.Kind() == reflect.Ptr,
deref: deref,
}
// update cache
@ -448,7 +461,7 @@ func (fi *marshalFieldInfo) computeMarshalFieldInfo(f *reflect.StructField) {
func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) {
fi.field = toField(f)
fi.wiretag = 1<<31 - 1 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire.
fi.wiretag = math.MaxInt32 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire.
fi.isPointer = true
fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f)
fi.oneofElems = make(map[reflect.Type]*marshalElemInfo)
@ -476,10 +489,6 @@ func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofI
}
}
type oneofMessage interface {
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
}
// wiretype returns the wire encoding of the type.
func wiretype(encoding string) uint64 {
switch encoding {
@ -2310,8 +2319,8 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
for _, k := range m.MapKeys() {
ki := k.Interface()
vi := m.MapIndex(k).Interface()
kaddr := toAddrPointer(&ki, false) // pointer to key
vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value
kaddr := toAddrPointer(&ki, false, false) // pointer to key
vaddr := toAddrPointer(&vi, valIsPtr, false) // pointer to value
siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
n += siz + SizeVarint(uint64(siz)) + tagsize
}
@ -2329,8 +2338,8 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
for _, k := range keys {
ki := k.Interface()
vi := m.MapIndex(k).Interface()
kaddr := toAddrPointer(&ki, false) // pointer to key
vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value
kaddr := toAddrPointer(&ki, false, false) // pointer to key
vaddr := toAddrPointer(&vi, valIsPtr, false) // pointer to value
b = appendVarint(b, tag)
siz := keySizer(kaddr, 1) + valCachedSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
b = appendVarint(b, uint64(siz))
@ -2399,7 +2408,7 @@ func (u *marshalInfo) sizeExtensions(ext *XXX_InternalExtensions) int {
// the last time this function was called.
ei := u.getExtElemInfo(e.desc)
v := e.value
p := toAddrPointer(&v, ei.isptr)
p := toAddrPointer(&v, ei.isptr, ei.deref)
n += ei.sizer(p, ei.tagsize)
}
mu.Unlock()
@ -2434,7 +2443,7 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
ei := u.getExtElemInfo(e.desc)
v := e.value
p := toAddrPointer(&v, ei.isptr)
p := toAddrPointer(&v, ei.isptr, ei.deref)
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
if !nerr.Merge(err) {
return b, err
@ -2465,7 +2474,7 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
ei := u.getExtElemInfo(e.desc)
v := e.value
p := toAddrPointer(&v, ei.isptr)
p := toAddrPointer(&v, ei.isptr, ei.deref)
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
if !nerr.Merge(err) {
return b, err
@ -2510,7 +2519,7 @@ func (u *marshalInfo) sizeMessageSet(ext *XXX_InternalExtensions) int {
ei := u.getExtElemInfo(e.desc)
v := e.value
p := toAddrPointer(&v, ei.isptr)
p := toAddrPointer(&v, ei.isptr, ei.deref)
n += ei.sizer(p, 1) // message, tag = 3 (size=1)
}
mu.Unlock()
@ -2553,7 +2562,7 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
ei := u.getExtElemInfo(e.desc)
v := e.value
p := toAddrPointer(&v, ei.isptr)
p := toAddrPointer(&v, ei.isptr, ei.deref)
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
if !nerr.Merge(err) {
return b, err
@ -2591,7 +2600,7 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
ei := u.getExtElemInfo(e.desc)
v := e.value
p := toAddrPointer(&v, ei.isptr)
p := toAddrPointer(&v, ei.isptr, ei.deref)
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
b = append(b, 1<<3|WireEndGroup)
if !nerr.Merge(err) {
@ -2621,7 +2630,7 @@ func (u *marshalInfo) sizeV1Extensions(m map[int32]Extension) int {
ei := u.getExtElemInfo(e.desc)
v := e.value
p := toAddrPointer(&v, ei.isptr)
p := toAddrPointer(&v, ei.isptr, ei.deref)
n += ei.sizer(p, ei.tagsize)
}
return n
@ -2656,7 +2665,7 @@ func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, determ
ei := u.getExtElemInfo(e.desc)
v := e.value
p := toAddrPointer(&v, ei.isptr)
p := toAddrPointer(&v, ei.isptr, ei.deref)
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
if !nerr.Merge(err) {
return b, err

View File

@ -136,7 +136,7 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
u.computeUnmarshalInfo()
}
if u.isMessageSet {
return UnmarshalMessageSet(b, m.offset(u.extensions).toExtensions())
return unmarshalMessageSet(b, m.offset(u.extensions).toExtensions())
}
var reqMask uint64 // bitmask of required fields we've seen.
var errLater error
@ -362,46 +362,48 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
}
// Find any types associated with oneof fields.
// TODO: XXX_OneofFuncs returns more info than we need. Get rid of some of it?
fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("XXX_OneofFuncs")
if fn.IsValid() {
res := fn.Call(nil)[3] // last return value from XXX_OneofFuncs: []interface{}
for i := res.Len() - 1; i >= 0; i-- {
v := res.Index(i) // interface{}
tptr := reflect.ValueOf(v.Interface()).Type() // *Msg_X
typ := tptr.Elem() // Msg_X
var oneofImplementers []interface{}
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
case oneofFuncsIface:
_, _, _, oneofImplementers = m.XXX_OneofFuncs()
case oneofWrappersIface:
oneofImplementers = m.XXX_OneofWrappers()
}
for _, v := range oneofImplementers {
tptr := reflect.TypeOf(v) // *Msg_X
typ := tptr.Elem() // Msg_X
f := typ.Field(0) // oneof implementers have one field
baseUnmarshal := fieldUnmarshaler(&f)
tags := strings.Split(f.Tag.Get("protobuf"), ",")
fieldNum, err := strconv.Atoi(tags[1])
if err != nil {
panic("protobuf tag field not an integer: " + tags[1])
}
var name string
for _, tag := range tags {
if strings.HasPrefix(tag, "name=") {
name = strings.TrimPrefix(tag, "name=")
break
}
}
// Find the oneof field that this struct implements.
// Might take O(n^2) to process all of the oneofs, but who cares.
for _, of := range oneofFields {
if tptr.Implements(of.ityp) {
// We have found the corresponding interface for this struct.
// That lets us know where this struct should be stored
// when we encounter it during unmarshaling.
unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal)
u.setTag(fieldNum, of.field, unmarshal, 0, name)
}
f := typ.Field(0) // oneof implementers have one field
baseUnmarshal := fieldUnmarshaler(&f)
tags := strings.Split(f.Tag.Get("protobuf"), ",")
fieldNum, err := strconv.Atoi(tags[1])
if err != nil {
panic("protobuf tag field not an integer: " + tags[1])
}
var name string
for _, tag := range tags {
if strings.HasPrefix(tag, "name=") {
name = strings.TrimPrefix(tag, "name=")
break
}
}
// Find the oneof field that this struct implements.
// Might take O(n^2) to process all of the oneofs, but who cares.
for _, of := range oneofFields {
if tptr.Implements(of.ityp) {
// We have found the corresponding interface for this struct.
// That lets us know where this struct should be stored
// when we encounter it during unmarshaling.
unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal)
u.setTag(fieldNum, of.field, unmarshal, 0, name)
}
}
}
// Get extension ranges, if any.
fn = reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray")
fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray")
if fn.IsValid() {
if !u.extensions.IsValid() && !u.oldExtensions.IsValid() {
panic("a message with extensions, but no extensions field in " + t.Name())
@ -1948,7 +1950,7 @@ func encodeVarint(b []byte, x uint64) []byte {
// If there is an error, it returns 0,0.
func decodeVarint(b []byte) (uint64, int) {
var x, y uint64
if len(b) <= 0 {
if len(b) == 0 {
goto bad
}
x = uint64(b[0])

View File

@ -31,7 +31,7 @@ Let's start with an example that shows the sessions API in a nutshell:
// environmental variable, or flag (or both), and don't accidentally commit it
// alongside your code. Ensure your key is sufficiently random - i.e. use Go's
// crypto/rand or securecookie.GenerateRandomKey(32) and persist the result.
var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY"))
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Get a session. We're ignoring the error resulted from decoding an
@ -51,6 +51,18 @@ secret key used to authenticate the session. Inside the handler, we call
some session values in session.Values, which is a `map[interface{}]interface{}`.
And finally we call `session.Save()` to save the session in the response.
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
with
[`context.ClearHandler`](https://www.gorillatoolkit.org/pkg/context#ClearHandler)
or else you will leak memory! An easy way to do this is to wrap the top-level
mux when calling http.ListenAndServe:
```go
http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
```
The ClearHandler function is provided by the gorilla/context package.
More examples are available [on the Gorilla
website](https://www.gorillatoolkit.org/pkg/sessions).

View File

@ -59,6 +59,14 @@ session.Save(r, w), and either display an error message or otherwise handle it.
Save must be called before writing to the response, otherwise the session
cookie will not be sent to the client.
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
with context.ClearHandler as or else you will leak memory! An easy way to do this
is to wrap the top-level mux when calling http.ListenAndServe:
http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
The ClearHandler function is provided by the gorilla/context package.
That's all you need to know for the basic usage. Let's take a look at other
options, starting with flash messages.

View File

@ -5,11 +5,12 @@
package sessions
import (
"context"
"encoding/gob"
"fmt"
"net/http"
"time"
"github.com/gorilla/context"
)
// Default flashes key.
@ -107,8 +108,7 @@ const registryKey contextKey = 0
// GetRegistry returns a registry instance for the current request.
func GetRegistry(r *http.Request) *Registry {
var ctx = r.Context()
registry := ctx.Value(registryKey)
registry := context.Get(r, registryKey)
if registry != nil {
return registry.(*Registry)
}
@ -116,7 +116,7 @@ func GetRegistry(r *http.Request) *Registry {
request: r,
sessions: make(map[string]sessionInfo),
}
*r = *r.WithContext(context.WithValue(ctx, registryKey, newRegistry))
context.Set(r, registryKey, newRegistry)
return newRegistry
}

View File

@ -312,6 +312,10 @@ func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
any := *(*Any)(ptr)
if any == nil {
stream.WriteNil()
return
}
any.WriteTo(stream)
}

View File

@ -77,14 +77,12 @@ func (iter *Iterator) ReadFloat32() (ret float32) {
}
func (iter *Iterator) readPositiveFloat32() (ret float32) {
value := uint64(0)
c := byte(' ')
i := iter.head
// first char
if i == iter.tail {
return iter.readFloat32SlowPath()
}
c = iter.buf[i]
c := iter.buf[i]
i++
ind := floatDigits[c]
switch ind {
@ -107,7 +105,7 @@ func (iter *Iterator) readPositiveFloat32() (ret float32) {
return
}
}
value = uint64(ind)
value := uint64(ind)
// chars before dot
non_decimal_loop:
for ; i < iter.tail; i++ {
@ -145,9 +143,7 @@ non_decimal_loop:
}
// too many decimal places
return iter.readFloat32SlowPath()
case invalidCharForNumber:
fallthrough
case dotInNumber:
case invalidCharForNumber, dotInNumber:
return iter.readFloat32SlowPath()
}
decimalPlaces++
@ -218,14 +214,12 @@ func (iter *Iterator) ReadFloat64() (ret float64) {
}
func (iter *Iterator) readPositiveFloat64() (ret float64) {
value := uint64(0)
c := byte(' ')
i := iter.head
// first char
if i == iter.tail {
return iter.readFloat64SlowPath()
}
c = iter.buf[i]
c := iter.buf[i]
i++
ind := floatDigits[c]
switch ind {
@ -248,7 +242,7 @@ func (iter *Iterator) readPositiveFloat64() (ret float64) {
return
}
}
value = uint64(ind)
value := uint64(ind)
// chars before dot
non_decimal_loop:
for ; i < iter.tail; i++ {
@ -286,9 +280,7 @@ non_decimal_loop:
}
// too many decimal places
return iter.readFloat64SlowPath()
case invalidCharForNumber:
fallthrough
case dotInNumber:
case invalidCharForNumber, dotInNumber:
return iter.readFloat64SlowPath()
}
decimalPlaces++

View File

@ -2,12 +2,22 @@
package jsoniter
import "fmt"
import (
"fmt"
"io"
)
func (iter *Iterator) skipNumber() {
if !iter.trySkipNumber() {
iter.unreadByte()
iter.ReadFloat32()
if iter.Error != nil && iter.Error != io.EOF {
return
}
iter.ReadFloat64()
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = nil
iter.ReadBigFloat()
}
}
}

View File

@ -64,14 +64,26 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
return &numericMapKeyDecoder{decoderOfType(ctx, typ)}
default:
ptrType := reflect2.PtrTo(typ)
if ptrType.Implements(textMarshalerType) {
if ptrType.Implements(unmarshalerType) {
return &referenceDecoder{
&unmarshalerDecoder{
valType: ptrType,
},
}
}
if typ.Implements(unmarshalerType) {
return &unmarshalerDecoder{
valType: typ,
}
}
if ptrType.Implements(textUnmarshalerType) {
return &referenceDecoder{
&textUnmarshalerDecoder{
valType: ptrType,
},
}
}
if typ.Implements(textMarshalerType) {
if typ.Implements(textUnmarshalerType) {
return &textUnmarshalerDecoder{
valType: typ,
}

View File

@ -93,8 +93,7 @@ func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteNil()
return
}
marshaler := obj.(json.Marshaler)
bytes, err := marshaler.MarshalJSON()
bytes, err := json.Marshal(obj)
if err != nil {
stream.Error = err
} else {

View File

@ -1,15 +0,0 @@
// +build appengine
package isatty
// IsTerminal returns true if the file descriptor is terminal which
// is always false on on appengine classic which is a sandboxed PaaS.
func IsTerminal(fd uintptr) bool {
return false
}
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

View File

@ -16,3 +16,9 @@ func IsTerminal(fd uintptr) bool {
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

View File

@ -16,3 +16,9 @@ func IsTerminal(fd uintptr) bool {
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

View File

@ -17,3 +17,9 @@ func IsTerminal(fd uintptr) bool {
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

View File

@ -1,9 +1,14 @@
// +build !windows
// +build !appengine
// +build appengine js
package isatty
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
// IsTerminal returns true if the file descriptor is terminal which
// is always false on js and appengine classic which is a sandboxed PaaS.
func IsTerminal(fd uintptr) bool {
return false
}
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false

View File

@ -14,3 +14,9 @@ func IsTerminal(fd uintptr) bool {
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
return err == nil
}
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

23
vendor/github.com/pkg/errors/LICENSE generated vendored Normal file
View File

@ -0,0 +1,23 @@
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

52
vendor/github.com/pkg/errors/README.md generated vendored Normal file
View File

@ -0,0 +1,52 @@
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
Package errors provides simple error handling primitives.
`go get github.com/pkg/errors`
The traditional error handling idiom in Go is roughly akin to
```go
if err != nil {
return err
}
```
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
## Adding context to an error
The errors.Wrap function returns a new error that adds context to the original error. For example
```go
_, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrap(err, "read failed")
}
```
## Retrieving the cause of an error
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
```go
type causer interface {
Cause() error
}
```
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
```go
switch err := errors.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}
```
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
## Contributing
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
Before proposing a change, please discuss your change by raising an issue.
## License
BSD-2-Clause

32
vendor/github.com/pkg/errors/appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,32 @@
version: build-{build}.{branch}
clone_folder: C:\gopath\src\github.com\pkg\errors
shallow_clone: true # for startup speed
environment:
GOPATH: C:\gopath
platform:
- x64
# http://www.appveyor.com/docs/installed-software
install:
# some helpful output for debugging builds
- go version
- go env
# pre-installed MinGW at C:\MinGW is 32bit only
# but MSYS2 at C:\msys64 has mingw64
- set PATH=C:\msys64\mingw64\bin;%PATH%
- gcc --version
- g++ --version
build_script:
- go install -v ./...
test_script:
- set PATH=C:\gopath\bin;%PATH%
- go test -v ./...
#artifacts:
# - path: '%GOPATH%\bin\*.exe'
deploy: off

282
vendor/github.com/pkg/errors/errors.go generated vendored Normal file
View File

@ -0,0 +1,282 @@
// Package errors provides simple error handling primitives.
//
// The traditional error handling idiom in Go is roughly akin to
//
// if err != nil {
// return err
// }
//
// which when applied recursively up the call stack results in error reports
// without context or debugging information. The errors package allows
// programmers to add context to the failure path in their code in a way
// that does not destroy the original value of the error.
//
// Adding context to an error
//
// The errors.Wrap function returns a new error that adds context to the
// original error by recording a stack trace at the point Wrap is called,
// together with the supplied message. For example
//
// _, err := ioutil.ReadAll(r)
// if err != nil {
// return errors.Wrap(err, "read failed")
// }
//
// If additional control is required, the errors.WithStack and
// errors.WithMessage functions destructure errors.Wrap into its component
// operations: annotating an error with a stack trace and with a message,
// respectively.
//
// Retrieving the cause of an error
//
// Using errors.Wrap constructs a stack of errors, adding context to the
// preceding error. Depending on the nature of the error it may be necessary
// to reverse the operation of errors.Wrap to retrieve the original error
// for inspection. Any error value which implements this interface
//
// type causer interface {
// Cause() error
// }
//
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
// the topmost error that does not implement causer, which is assumed to be
// the original cause. For example:
//
// switch err := errors.Cause(err).(type) {
// case *MyError:
// // handle specifically
// default:
// // unknown error
// }
//
// Although the causer interface is not exported by this package, it is
// considered a part of its stable public interface.
//
// Formatted printing of errors
//
// All error values returned from this package implement fmt.Formatter and can
// be formatted by the fmt package. The following verbs are supported:
//
// %s print the error. If the error has a Cause it will be
// printed recursively.
// %v see %s
// %+v extended format. Each Frame of the error's StackTrace will
// be printed in detail.
//
// Retrieving the stack trace of an error or wrapper
//
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
// invoked. This information can be retrieved with the following interface:
//
// type stackTracer interface {
// StackTrace() errors.StackTrace
// }
//
// The returned errors.StackTrace type is defined as
//
// type StackTrace []Frame
//
// The Frame type represents a call site in the stack trace. Frame supports
// the fmt.Formatter interface that can be used for printing information about
// the stack trace of this error. For example:
//
// if err, ok := err.(stackTracer); ok {
// for _, f := range err.StackTrace() {
// fmt.Printf("%+s:%d", f)
// }
// }
//
// Although the stackTracer interface is not exported by this package, it is
// considered a part of its stable public interface.
//
// See the documentation for Frame.Format for more details.
package errors
import (
"fmt"
"io"
)
// New returns an error with the supplied message.
// New also records the stack trace at the point it was called.
func New(message string) error {
return &fundamental{
msg: message,
stack: callers(),
}
}
// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
// Errorf also records the stack trace at the point it was called.
func Errorf(format string, args ...interface{}) error {
return &fundamental{
msg: fmt.Sprintf(format, args...),
stack: callers(),
}
}
// fundamental is an error that has a message and a stack, but no caller.
type fundamental struct {
msg string
*stack
}
func (f *fundamental) Error() string { return f.msg }
func (f *fundamental) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
io.WriteString(s, f.msg)
f.stack.Format(s, verb)
return
}
fallthrough
case 's':
io.WriteString(s, f.msg)
case 'q':
fmt.Fprintf(s, "%q", f.msg)
}
}
// WithStack annotates err with a stack trace at the point WithStack was called.
// If err is nil, WithStack returns nil.
func WithStack(err error) error {
if err == nil {
return nil
}
return &withStack{
err,
callers(),
}
}
type withStack struct {
error
*stack
}
func (w *withStack) Cause() error { return w.error }
func (w *withStack) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v", w.Cause())
w.stack.Format(s, verb)
return
}
fallthrough
case 's':
io.WriteString(s, w.Error())
case 'q':
fmt.Fprintf(s, "%q", w.Error())
}
}
// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
func Wrap(err error, message string) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: message,
}
return &withStack{
err,
callers(),
}
}
// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
// If err is nil, Wrapf returns nil.
func Wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
return &withStack{
err,
callers(),
}
}
// WithMessage annotates err with a new message.
// If err is nil, WithMessage returns nil.
func WithMessage(err error, message string) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: message,
}
}
// WithMessagef annotates err with the format specifier.
// If err is nil, WithMessagef returns nil.
func WithMessagef(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
}
type withMessage struct {
cause error
msg string
}
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause }
func (w *withMessage) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v\n", w.Cause())
io.WriteString(s, w.msg)
return
}
fallthrough
case 's', 'q':
io.WriteString(s, w.Error())
}
}
// Cause returns the underlying cause of the error, if possible.
// An error value has a cause if it implements the following
// interface:
//
// type causer interface {
// Cause() error
// }
//
// If the error does not implement Cause, the original error will
// be returned. If the error is nil, nil will be returned without further
// investigation.
func Cause(err error) error {
type causer interface {
Cause() error
}
for err != nil {
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
return err
}

147
vendor/github.com/pkg/errors/stack.go generated vendored Normal file
View File

@ -0,0 +1,147 @@
package errors
import (
"fmt"
"io"
"path"
"runtime"
"strings"
)
// Frame represents a program counter inside a stack frame.
type Frame uintptr
// pc returns the program counter for this frame;
// multiple frames may have the same PC value.
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
// file returns the full path to the file that contains the
// function for this Frame's pc.
func (f Frame) file() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
file, _ := fn.FileLine(f.pc())
return file
}
// line returns the line number of source code of the
// function for this Frame's pc.
func (f Frame) line() int {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return 0
}
_, line := fn.FileLine(f.pc())
return line
}
// Format formats the frame according to the fmt.Formatter interface.
//
// %s source file
// %d source line
// %n function name
// %v equivalent to %s:%d
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+s function name and path of source file relative to the compile time
// GOPATH separated by \n\t (<funcname>\n\t<path>)
// %+v equivalent to %+s:%d
func (f Frame) Format(s fmt.State, verb rune) {
switch verb {
case 's':
switch {
case s.Flag('+'):
pc := f.pc()
fn := runtime.FuncForPC(pc)
if fn == nil {
io.WriteString(s, "unknown")
} else {
file, _ := fn.FileLine(pc)
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
}
default:
io.WriteString(s, path.Base(f.file()))
}
case 'd':
fmt.Fprintf(s, "%d", f.line())
case 'n':
name := runtime.FuncForPC(f.pc()).Name()
io.WriteString(s, funcname(name))
case 'v':
f.Format(s, 's')
io.WriteString(s, ":")
f.Format(s, 'd')
}
}
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame
// Format formats the stack of Frames according to the fmt.Formatter interface.
//
// %s lists source files for each Frame in the stack
// %v lists the source file and line number for each Frame in the stack
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+v Prints filename, function, and line number for each Frame in the stack.
func (st StackTrace) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case s.Flag('+'):
for _, f := range st {
fmt.Fprintf(s, "\n%+v", f)
}
case s.Flag('#'):
fmt.Fprintf(s, "%#v", []Frame(st))
default:
fmt.Fprintf(s, "%v", []Frame(st))
}
case 's':
fmt.Fprintf(s, "%s", []Frame(st))
}
}
// stack represents a stack of program counters.
type stack []uintptr
func (s *stack) Format(st fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case st.Flag('+'):
for _, pc := range *s {
f := Frame(pc)
fmt.Fprintf(st, "\n%+v", f)
}
}
}
}
func (s *stack) StackTrace() StackTrace {
f := make([]Frame, len(*s))
for i := 0; i < len(f); i++ {
f[i] = Frame((*s)[i])
}
return f
}
func callers() *stack {
const depth = 32
var pcs [depth]uintptr
n := runtime.Callers(3, pcs[:])
var st stack = pcs[0:n]
return &st
}
// funcname removes the path prefix component of a function's name reported by func.Name().
func funcname(name string) string {
i := strings.LastIndex(name, "/")
name = name[i+1:]
i = strings.Index(name, ".")
return name[i+1:]
}

View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2012-2015 Ugorji Nwoke.
All rights reserved.
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.

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