diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..69dba81 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "go.inferGopath": false, + "commentTranslate.targetLanguage": "zh-CN", + "commentTranslate.source": "intellsmi.deepl-translate-deepl", + "go.toolsEnvVars": { + "GOPROXY": "https://goproxy.io" + } +} \ No newline at end of file diff --git a/action.go b/action.go index 51d4f9b..1868ba1 100644 --- a/action.go +++ b/action.go @@ -7,17 +7,39 @@ import ( "reflect" "runtime/debug" "sync" + "time" "unsafe" ) -var actionPools = make(map[string]*sync.Pool, 32) +type action struct { + subsidiary string + metaData map[string]string + timeout int32 + instancesPool sync.Pool + newHandle func() interface{} +} + +func (act action) MetaData() map[string]string { + return act.metaData +} +func (act action) Timeout() int32 { + return act.timeout +} +func (act action) Instance() *ProcessorWrap { + return act.newHandle().(*ProcessorWrap) +} + +var ActionDefaultTimeout int32 = 500 +var actions = make(map[string]*action, 32) type Processor interface { Run(ctx *Context) (string, error) } -func GetActionPools() map[string]*sync.Pool { - return actionPools +func SetActionTimeout(name string, timeout int32) { + if v, ok := actions[name]; ok { + v.timeout = timeout + } } func NewProcessorWrap(handle interface{ Processor }, run func(p Processor, ctx *Context) (string, error), next map[string]*ProcessorWrap) *ProcessorWrap { @@ -30,8 +52,8 @@ type ProcessorWrap struct { next map[string]*ProcessorWrap } -func RegisterAction(name string, new func() interface{}) { - actionPools[name] = &sync.Pool{New: new} +func RegisterAction(name string, metaData map[string]string, timeout int32, new func() interface{}) { + actions[name] = &action{metaData: metaData, timeout: timeout, instancesPool: sync.Pool{New: new}} } func RunProcessor(s unsafe.Pointer, n uintptr, p Processor, ctx *Context) (string, error) { @@ -49,43 +71,64 @@ func RunProcessor(s unsafe.Pointer, n uintptr, p Processor, ctx *Context) (strin return p.Run(ctx) } -func RunAction(ctx *Context) (err error) { - var flag string +// CallAction 消化其他错误,只返回onFinish错误 +func CallAction(gctx context.Context, s *Session, request *Request) (err error) { + var act *action + var timeout int32 = ActionDefaultTimeout + hook := s.Server.Hook() + + if act = actions[request.ActionName]; act != nil && act.timeout > 0 { + timeout = act.timeout + } + ctx := NewPlayContext(gctx, s, request, time.Duration(timeout)*time.Millisecond) + defer func() { if panicInfo := recover(); panicInfo != nil { - err = fmt.Errorf("panic: %v\n%v", panicInfo, string(debug.Stack())) + ctx.err = fmt.Errorf("panic: %v\n%v", panicInfo, string(debug.Stack())) } + ctx.gcfunc() + err = hook.OnFinish(ctx) }() - pool, ok := actionPools[ctx.ActionInfo.Name] - if !ok { - return errors.New("can not find action:" + ctx.ActionInfo.Name) + + if ctx.err = hook.OnRequest(ctx); ctx.Err() == nil { + run(act, ctx) } - ihandler := pool.Get() - if ihandler == nil { - return errors.New("can not get action handle from pool:" + ctx.ActionInfo.Name) + if ctx.err = hook.OnResponse(ctx); ctx.Err() == nil { + if request.Respond { + ctx.err = s.Write(&ctx.Response) + } + } + return +} + +func run(act *action, ctx *Context) { + var flag string + defer func() { + if panicInfo := recover(); panicInfo != nil { + ctx.err = fmt.Errorf("panic: %v\n%v", panicInfo, string(debug.Stack())) + } + }() + if act == nil { + ctx.err = errors.New("can not find action:" + ctx.ActionInfo.Name) + return } - defer pool.Put(ihandler) - // set context - if ctx.ActionInfo.Timeout > 0 { - var cancel context.CancelFunc - ctx.ctx, cancel = context.WithTimeout(ctx.ctx, ctx.ActionInfo.Timeout) - defer cancel() + ihandler := act.instancesPool.Get() + if ihandler == nil { + ctx.err = errors.New("can not get action handle from pool:" + ctx.ActionInfo.Name) + return + } else { + defer act.instancesPool.Put(ihandler) } currentHandler := ihandler.(*ProcessorWrap) for ok := true; ok; currentHandler, ok = currentHandler.next[flag] { - flag, err = currentHandler.run(currentHandler.p, ctx) - if ctx.ctx.Err() != nil { - if err != nil { - return err - } - return ctx.ctx.Err() - } - if err != nil { - return err + flag, ctx.err = currentHandler.run(currentHandler.p, ctx) + if ctx.Err() != nil { + return } + if procOutputType, ok := reflect.TypeOf(currentHandler.p).Elem().FieldByName("Output"); ok { procOutputVal := reflect.ValueOf(currentHandler.p).Elem().FieldByName("Output") for i := 0; i < procOutputType.Type.NumField(); i++ { @@ -99,5 +142,4 @@ func RunAction(ctx *Context) (err error) { } } } - return } diff --git a/binder/bytes_binder.go b/binder/bytes_binder.go deleted file mode 100644 index 07a555a..0000000 --- a/binder/bytes_binder.go +++ /dev/null @@ -1,89 +0,0 @@ -package binder - -import ( - "errors" - "reflect" - "strings" -) - -type BytesBinder struct { - data []byte - exData map[string]interface{} -} - -func NewBytesBinder(data []byte) *BytesBinder { - return &BytesBinder{data: data, exData: make(map[string]interface{})} -} - -func (j *BytesBinder) Bind(v reflect.Value) error { - if v.CanSet() { - return j._bind(v, "", "") - } - return nil -} - -func (j *BytesBinder) Get(key string) (val interface{}, err error) { - if key == "" { - return j.data, nil - } else { - var ok bool - if val, ok = j.exData[key]; ok { - return val, nil - } - } - return nil, errors.New("can not find key|" + key) -} - -func (j *BytesBinder) Set(key string, val interface{}) { - j.exData[key] = val - return -} - -func (j *BytesBinder) _bind(v reflect.Value, required string, preKey string) (err error) { - var tField reflect.StructField - var vField reflect.Value - var fieldCount = v.Type().NumField() - var customKey string - var bind string // required, optional - var fullKey string - for i := 0; i < fieldCount; i++ { - if vField, tField = v.Field(i), v.Type().Field(i); !vField.CanInterface() { - continue - } - - bind = required - if tField.Tag.Get("bind") != "" { - bind = tField.Tag.Get("bind") - } - - if customKey = tField.Tag.Get("key"); customKey == "" { - customKey = tField.Name - } - customKeys := strings.Split(customKey, ",") - for _, v := range customKeys { - if preKey != "" { - fullKey = preKey + "." + v - } else { - fullKey = v - } - if ex, ok := j.exData[fullKey]; ok { - if tField.Type.String() != reflect.TypeOf(ex).String() { - return errors.New("input custom " + v + " type need " + tField.Type.String() + " but " + reflect.TypeOf(ex).String() + " given") - } - vField.Set(reflect.ValueOf(ex)) - //continue - goto NEXT - } - } - if vField.Type().String() == "[]uint8" && len(j.data) > 0 { - vField.Set(reflect.ValueOf(j.data)) - } else { - if bind == "required" { - return errors.New("input <" + fullKey + "> field is mismatch") - } - } - - NEXT: - } - return -} diff --git a/binder/json_binder.go b/binder/json_binder.go deleted file mode 100644 index 6c61acf..0000000 --- a/binder/json_binder.go +++ /dev/null @@ -1,193 +0,0 @@ -package binder - -import ( - "errors" - "github.com/tidwall/gjson" - "reflect" - "strconv" - "strings" -) - -type JsonBinder struct { - json gjson.Result - exData map[string]interface{} -} - -func NewJsonBinder(data []byte) *JsonBinder { - parser := &JsonBinder{json: gjson.GetBytes(data, "@this"), exData: make(map[string]interface{})} - return parser -} - -func (j *JsonBinder) Bind(v reflect.Value) error { - if v.CanSet() { - return j.bindGJson(v, j.json, "", "") - } - return nil -} - -func (j *JsonBinder) Get(key string) (val interface{}, err error) { - if result := j.json.Get(key); result.Exists() { - val = result.Value() - } else if key == "" { - val = j.json.Value() - } else { - err = errors.New("can not find key|" + key) - } - - return -} - -func (j *JsonBinder) Set(key string, val interface{}) { - j.exData[key] = val -} - -func (j *JsonBinder) bindGJson(v reflect.Value, source gjson.Result, required string, preKey string) (err error) { - var tField reflect.StructField - var vField reflect.Value - var item gjson.Result - var fieldCount = v.Type().NumField() - var customKey string - var bind string // required, optional - var fullKey string - - for i := 0; i < fieldCount; i++ { - if vField, tField = v.Field(i), v.Type().Field(i); !vField.CanInterface() { - continue - } - - bind = required - if tField.Tag.Get("bind") != "" { - bind = tField.Tag.Get("bind") - } - - if customKey = tField.Tag.Get("key"); customKey == "" { - customKey = tField.Name - } - customKeys := strings.Split(customKey, ",") - - for _, v := range customKeys { - if preKey != "" { - fullKey = preKey + "." + v - } else { - fullKey = v - } - if ex, ok := j.exData[v]; ok { - if tField.Type.String() != reflect.TypeOf(ex).String() { - return errors.New("input custom " + v + " type need " + tField.Type.String() + " but " + reflect.TypeOf(ex).String() + " given") - } - vField.Set(reflect.ValueOf(ex)) - //continue - goto NEXT - } - - item = source.Get(v) - item.Exists() - if item.Exists() { - break - } - } - if !item.Exists() || item.Type == gjson.Null { - if defaultValue := tField.Tag.Get("default"); defaultValue != "" { - if err = setVal(vField, tField, defaultValue, &item); err != nil { - return errors.New("input <" + customKey + "> " + err.Error()) - } - } else if bind == "required" { - return errors.New("input <" + fullKey + "> field is mismatch") - } - continue - } - - if tField.Type.Kind() == reflect.Struct && tField.Type.String() != "time.Time" { - if err = j.bindGJson(vField, item, bind, fullKey); err != nil { - return - } - continue - } - if tField.Type.Kind() == reflect.Slice && vField.Type().Elem().Kind() == reflect.Struct && vField.Type().Elem().String() != "time.Time" { - var count int - item.ForEach(func(key, value gjson.Result) bool { - count++ - v := reflect.Indirect(reflect.New(vField.Type().Elem())) - if err = j.bindGJson(v, value, bind, fullKey); err != nil { - return false - } - vField.Set(reflect.Append(vField, v)) - return true - }) - if err != nil { - return err - } - if count == 0 && bind == "required" { - return errors.New("input <" + fullKey + "> field is mismatch") - } - continue - } - - if tField.Type.Kind() == reflect.Slice { - var count int - var elems = vField - if item.ForEach(func(key, value gjson.Result) bool { - count++ - //var elem reflect.Value - //if elem, err = appendElem(vField, tField, value.String()); err != nil { - // return false - //} - if elems, err = setSliceValueWithGJson(vField.Type().String(), elems, &value); err != nil { - return false - } - vField.Set(elems) - - return true - }); err != nil { - return errors.New("input <" + fullKey + "> " + err.Error()) - } - if count == 0 && bind == "required" { - return errors.New("input <" + fullKey + "> field is mismatch") - } - } else { - if err = setVal(vField, tField, item.String(), &item); err != nil { - return errors.New("input <" + fullKey + "> " + err.Error()) - } - } - NEXT: - } - return -} - -func setSliceValueWithGJson(fieldType string, elems reflect.Value, value *gjson.Result) (reflect.Value, error) { - if fieldType == "[]interface {}" { - elems = reflect.Append(elems, reflect.ValueOf(value.Value())) - return elems, nil - } - if fieldType != "[]string" && value.Type.String() != "Number" { - if _, err := strconv.ParseFloat(value.Str, 64); err != nil { - return elems, errors.New("data type need number") - } - } - switch fieldType { - case "[]string": - elems = reflect.Append(elems, reflect.ValueOf(value.String())) - case "[]int8": - elems = reflect.Append(elems, reflect.ValueOf(int8(value.Int()))) - case "[]int32": - elems = reflect.Append(elems, reflect.ValueOf(int32(value.Int()))) - case "[]int64": - elems = reflect.Append(elems, reflect.ValueOf(value.Int())) - case "[]int": - elems = reflect.Append(elems, reflect.ValueOf(int(value.Int()))) - case "[]uint8": - elems = reflect.Append(elems, reflect.ValueOf(uint8(value.Uint()))) - case "[]uint32": - elems = reflect.Append(elems, reflect.ValueOf(uint32(value.Uint()))) - case "[]uint64": - elems = reflect.Append(elems, reflect.ValueOf(value.Uint())) - case "[]uint": - elems = reflect.Append(elems, reflect.ValueOf(uint(value.Uint()))) - case "[]float32": - elems = reflect.Append(elems, reflect.ValueOf(float32(value.Float()))) - case "[]float64": - elems = reflect.Append(elems, reflect.ValueOf(value.Float())) - } - - return elems, nil -} diff --git a/binder/protobuf_binder.go b/binder/protobuf_binder.go deleted file mode 100644 index ef781e7..0000000 --- a/binder/protobuf_binder.go +++ /dev/null @@ -1,253 +0,0 @@ -package binder - -import ( - "errors" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/dynamicpb" - "reflect" - "regexp" - "strconv" - "strings" -) - -type ProtobufBinder struct { - message protoreflect.Message - exData map[string]interface{} -} - -func NewProtobufBinder(desc protoreflect.Message) *ProtobufBinder { - parser := &ProtobufBinder{message: desc, exData: make(map[string]interface{})} - return parser -} - -func (j *ProtobufBinder) Bind(v reflect.Value) error { - if v.CanSet() { - return j.bindProtobuf(v, j.message, "", "") - } - return nil -} - -func (j *ProtobufBinder) Get(key string) (val interface{}, err error) { - if key == "" { - return nil, errors.New("unsuport empty key") - } - if item := j.message.Type().Descriptor().Fields().ByName(protoreflect.Name(key)); item == nil { - return nil, errors.New("can not find key|" + key) - } else { - return j.message.Get(item).Interface(), nil - } -} - -func (j *ProtobufBinder) Set(key string, val interface{}) { - j.exData[key] = val -} - -func (j *ProtobufBinder) bindProtobuf(v reflect.Value, source protoreflect.Message, required string, preKey string) (err error) { - var tField reflect.StructField - var vField reflect.Value - var item protoreflect.FieldDescriptor - var fieldCount = v.Type().NumField() - var customKey string - var bind string // required, optional - var fullKey string - var messageFields = source.Type().Descriptor().Fields() - - for i := 0; i < fieldCount; i++ { - if vField, tField = v.Field(i), v.Type().Field(i); !vField.CanInterface() { - continue - } - - bind = required - if tField.Tag.Get("bind") != "" { - bind = tField.Tag.Get("bind") - } - - if customKey = tField.Tag.Get("key"); customKey == "" { - customKey = tField.Name - } - customKeys := strings.Split(customKey, ",") - - for _, v := range customKeys { - if preKey != "" { - fullKey = preKey + "." + v - } else { - fullKey = v - } - if ex, ok := j.exData[v]; ok { - if tField.Type.String() != reflect.TypeOf(ex).String() { - return errors.New("input custom " + v + " type need " + tField.Type.String() + " but " + reflect.TypeOf(ex).String() + " given") - } - vField.Set(reflect.ValueOf(ex)) - //continue - goto NEXT - } - - item = messageFields.ByName(protoreflect.Name(v)) - if item != nil && source.Has(item) { - break - } - } - if item == nil { - if defaultValue := tField.Tag.Get("default"); defaultValue != "" { - if err = setProtobufVal(vField, tField, defaultValue, nil); err != nil { - return errors.New("input <" + customKey + "> " + err.Error()) - } - } else if bind == "required" { - return errors.New("input <" + fullKey + "> field is mismatch 1") - } - continue - } - if !source.Has(item) { - if defaultValue := tField.Tag.Get("default"); defaultValue != "" { - if err = setProtobufVal(vField, tField, defaultValue, nil); err != nil { - return errors.New("input <" + customKey + "> " + err.Error()) - } - continue - } - } - if tField.Type.Kind() == reflect.Struct && tField.Type.String() != "time.Time" { - if err = j.bindProtobuf(vField, dynamicpb.NewMessage(item.Message()), bind, fullKey); err != nil { - return - } - continue - } - if tField.Type.Kind() == reflect.Slice && vField.Type().Elem().Kind() == reflect.Struct && vField.Type().Elem().String() != "time.Time" { - var count int - iList := source.Get(item).List() - for i := 0; i < iList.Len(); i++ { - v := reflect.Indirect(reflect.New(vField.Type().Elem())) - if err = j.bindProtobuf(v, iList.Get(i).Message(), bind, fullKey); err != nil { - return errors.New("input <" + fullKey + "> type list is mismatch") - } - vField.Set(reflect.Append(vField, v)) - } - if err != nil { - return err - } - if count == 0 && bind == "required" { - return errors.New("input <" + fullKey + "> field is mismatch 2") - } - continue - } - - if tField.Type.Kind() == reflect.Slice { - var count int - var elems = vField - iList := source.Get(item).List() - for i := 0; i < iList.Len(); i++ { - count++ - v := iList.Get(i) - if elems, err = setSliceValueWithProtobuf(vField.Type().String(), elems, &v); err != nil { - return errors.New("input <" + fullKey + "> " + err.Error()) - } - vField.Set(elems) - } - if count == 0 && bind == "required" { - return errors.New("input <" + fullKey + "> field is mismatch 3") - } - } else { - v := source.Get(item) - if err = setProtobufVal(vField, tField, v.String(), &v); err != nil { - return errors.New("input <" + fullKey + "> " + err.Error()) - } - } - NEXT: - } - return -} - -func setSliceValueWithProtobuf(fieldType string, elems reflect.Value, value *protoreflect.Value) (reflect.Value, error) { - if fieldType == "[]interface {}" { - elems = reflect.Append(elems, reflect.ValueOf(value.Interface())) - return elems, nil - } - //if fieldType != "[]string" && value.Type.String() != "Number" { - // if _, err := strconv.ParseFloat(value.Str, 64); err != nil { - // return elems, errors.New("data type need number") - // } - //} - switch fieldType { - case "[]string": - elems = reflect.Append(elems, reflect.ValueOf(value.String())) - case "[]int8": - elems = reflect.Append(elems, reflect.ValueOf(int8(value.Int()))) - case "[]int32": - elems = reflect.Append(elems, reflect.ValueOf(int32(value.Int()))) - case "[]int64": - elems = reflect.Append(elems, reflect.ValueOf(value.Int())) - case "[]int": - elems = reflect.Append(elems, reflect.ValueOf(int(value.Int()))) - case "[]uint8": - elems = reflect.Append(elems, reflect.ValueOf(uint8(value.Uint()))) - case "[]uint32": - elems = reflect.Append(elems, reflect.ValueOf(uint32(value.Uint()))) - case "[]uint64": - elems = reflect.Append(elems, reflect.ValueOf(value.Uint())) - case "[]uint": - elems = reflect.Append(elems, reflect.ValueOf(uint(value.Uint()))) - case "[]float32": - elems = reflect.Append(elems, reflect.ValueOf(float32(value.Float()))) - case "[]float64": - elems = reflect.Append(elems, reflect.ValueOf(value.Float())) - } - - return elems, nil -} - -func setProtobufVal(vField reflect.Value, tField reflect.StructField, str string, gValue *protoreflect.Value) error { - if regexPattern := tField.Tag.Get("regex"); regexPattern != "" { - if match, _ := regexp.MatchString(regexPattern, str); match == false { - return errors.New("value is mismatch") - } - } - - if val, err := parseProtobuf(tField, str, gValue); err != nil { - return err - } else { - vField.Set(reflect.ValueOf(val)) - } - return nil -} - -func parseProtobuf(tField reflect.StructField, str string, gValue *protoreflect.Value) (interface{}, error) { - switch strings.Trim(tField.Type.String(), "[]") { - case "interface {}": - if gValue == nil { - return str, nil - } - return gValue.Interface(), nil - case "string": - return str, nil - case "time.Time": - return parseTime(tField, str) - case "bool": - return strconv.ParseBool(str) - case "byte": - return parseByte(str, 10) - case "int": - return strconv.Atoi(str) - case "int8": - return parseInt8(str, 10) - case "int16": - return parseInt16(str, 10) - case "int32": - return parseInt32(str, 10) - case "int64": - return parseInt64(str, 10) - case "uint": - return parseUint(str, 10) - case "uint8": - return parseUint8(str, 10) - case "uint16": - return parseUint16(str, 10) - case "uint32": - return parseUint32(str, 10) - case "uint64": - return parseUint64(str, 10) - case "float32": - return parseFloat32(str) - case "float64": - return strconv.ParseFloat(str, 64) - } - return nil, errors.New("not supported type " + tField.Type.String()) -} diff --git a/binder/urlvalue_binder.go b/binder/urlvalue_binder.go deleted file mode 100644 index 7092050..0000000 --- a/binder/urlvalue_binder.go +++ /dev/null @@ -1,143 +0,0 @@ -package binder - -import ( - "errors" - "net/url" - "reflect" - "strings" -) - -type UrlValueBinder struct { - values url.Values - exData map[string]interface{} - keys []string -} - -func NewUrlValueBinder(values url.Values) *UrlValueBinder { - parser := &UrlValueBinder{values: values, exData: make(map[string]interface{})} - for k, _ := range values { - parser.keys = append(parser.keys, k) - } - return parser -} - -func (parser *UrlValueBinder) Set(key string, val interface{}) { - parser.exData[key] = val -} - -func (parser *UrlValueBinder) Get(key string) (interface{}, error) { - return parser.values.Get(key), nil -} - -func (parser *UrlValueBinder) Bind(v reflect.Value) error { - if v.CanSet() { - return parser.bindValues(v, "", "") - } - return nil -} - -func (parser *UrlValueBinder) bindValues(v reflect.Value, prefix string, required string) (err error) { - var tField reflect.StructField - var vField reflect.Value - var item []string - var fieldCount = v.Type().NumField() - var customKey string - var bind string // required, optional - var customKeys []string - - for i := 0; i < fieldCount; i++ { - if vField, tField = v.Field(i), v.Type().Field(i); !vField.CanInterface() { - continue - } - - bind = required - if tField.Tag.Get("bind") != "" { - bind = tField.Tag.Get("bind") - } - if customKey = tField.Tag.Get("key"); customKey == "" { - customKey = tField.Name - } - - customKeys = strings.Split(customKey, ",") - for _, key := range customKeys { - key = strings.Trim(key, " ") - if prefix != "" { - key = prefix + "[" + key + "]" - } - for _, v := range parser.keys { - if strings.HasPrefix(v, key) { - customKey = key - break - } - } - } - - if ex, ok := parser.exData[customKey]; ok { - if tField.Type.String() != reflect.TypeOf(ex).String() { - return errors.New("input custom " + customKey + " type need " + tField.Type.String() + " but " + reflect.TypeOf(ex).String() + " given") - } - vField.Set(reflect.ValueOf(ex)) - continue - } - - if tField.Type.Kind() == reflect.Struct && tField.Type.String() != "time.Time" { - if err = parser.bindValues(vField, customKey, bind); err != nil { - return - } - continue - } - if tField.Type.Kind() == reflect.Slice && vField.Type().Elem().Kind() == reflect.Struct && vField.Type().Elem().String() != "time.Time" { - var keyList = make(map[string]struct{}, 8) - for k, _ := range parser.values { - if strings.HasPrefix(k, customKey) { - if preKey, err := parseSliceKey(k, customKey); err != nil { - return err - } else { - keyList[preKey] = struct{}{} - } - } - } - - for k, _ := range keyList { - v := reflect.Indirect(reflect.New(vField.Type().Elem())) - if err = parser.bindValues(v, k, bind); err != nil { - return - } - vField.Set(reflect.Append(vField, v)) - } - continue - } - - if tField.Type.Kind() == reflect.Slice { - customKey += "[]" - } - - item = parser.values[customKey] - if len(item) == 0 { - if defaultValue := tField.Tag.Get("default"); defaultValue != "" { - if err = setVal(vField, tField, defaultValue, nil); err != nil { - return errors.New("input <" + customKey + "> " + err.Error()) - } - } else if bind == "required" { - return errors.New("input <" + customKey + "> field is mismatch") - } - continue - } - - if tField.Type.Kind() == reflect.Slice { - for _, v := range item { - if elem, err := appendElem(vField, tField, v, nil); err != nil { - return errors.New("input <" + customKey + "> " + err.Error()) - } else { - vField.Set(elem) - } - } - } else { - if err = setVal(vField, tField, item[0], nil); err != nil { - return errors.New("input <" + customKey + "> " + err.Error()) - } - } - } - - return -} diff --git a/caller.go b/caller.go new file mode 100644 index 0000000..93c344b --- /dev/null +++ b/caller.go @@ -0,0 +1,161 @@ +package play + +import ( + "fmt" + "log" + "reflect" +) + +type Node struct { + Name string + Type interface{} + Kind reflect.Kind + Key string + Note string + Default string + Bind string + Child []Node +} + +func BuildCaller() { + for _, act := range actions { + currentHandler := act.Instance() + GetInput(currentHandler.p) + for _, v := range currentHandler.next { + GetInput(v.p) + } + } +} + +func ParsePrint(p *Node) { + if len(p.Child) > 0 { + for _, v := range p.Child { + if v.Name == "Input" || v.Name == "Output" { + fmt.Println(v.Name, ": ", v) + } + ParsePrint(&v) + } + } +} + +func GetInput(p interface{}) (node *Node) { + var v reflect.Value + t := reflect.TypeOf(p) + if t.Kind() == reflect.Ptr { + v = reflect.ValueOf(p).Elem() + } else { + v = reflect.ValueOf(p) + } + + inputStruct := v.FieldByName("Input") + inputStruct.Type() + fieldNum := inputStruct.NumField() + + for i := 0; i < fieldNum; i++ { + field := inputStruct.Field(i) + switch field.Kind() { + case reflect.Struct: + fmt.Println("find a struct") + parseStruct(field) + case reflect.Slice: + fmt.Println(field.Kind().String()) + default: + fmt.Println(field.Kind().String()) + } + } + return +} + +func parseStruct(v reflect.Value) { + fieldNum := v.NumField() + for i := 0; i < fieldNum; i++ { + field := v.Field(i) + fmt.Println(field.Type().String()) + switch field.Kind() { + case reflect.Struct: + fmt.Println("find a struct") + parseStruct(field) + default: + fmt.Println(field.Kind().String()) + } + } +} + +//获取结构体中字段的名称 +func GetFieldName2(p interface{}) *Node { + var tmp Node + t := reflect.TypeOf(p) + + tmp.Name = t.Name() + tmp.Type = t.String() + tmp.Kind = t.Kind() + switch tmp.Kind { + case reflect.Ptr: + el := reflect.New(t.Elem()).Elem() + ttmp := GetFieldName2(el.Interface()) + if ttmp != nil { + tmp.Child = append(tmp.Child, *ttmp) + } + case reflect.Map: + el := reflect.New(t.Elem()).Elem() + ttmp := GetFieldName2(el.Interface()) + if ttmp != nil { + tmp.Child = append(tmp.Child, *ttmp) + } + case reflect.Slice: + el := reflect.New(t.Elem()).Elem() + ttmp := GetFieldName2(el.Interface()) + if ttmp != nil { + tmp.Child = append(tmp.Child, *ttmp) + } + case reflect.Struct: + fieldNum := t.NumField() + fmt.Println("fieldNum", fieldNum) + for i := 0; i < fieldNum; i++ { + var tttmp = Node{} + tttmp.Name = t.Field(i).Name + tttmp.Type = t.Field(i).Type.String() + tttmp.Kind = t.Field(i).Type.Kind() + tttmp.Note = t.Field(i).Tag.Get("note") + tttmp.Key = t.Field(i).Tag.Get("key") + //tttmp.Json = t.Field(i).Tag.Get("json") + //tttmp.Xml = t.Field(i).Tag.Get("xml") + tttmp.Bind = t.Field(i).Tag.Get("bind") + tttmp.Default = t.Field(i).Tag.Get("default") + k := t.Field(i).Type.Kind() + switch k { + case reflect.Map: + el := reflect.New(t.Field(i).Type.Elem()).Elem() + ttmp := GetFieldName2(el.Interface()) + if ttmp != nil { + tttmp.Child = append(tttmp.Child, *ttmp) + } + case reflect.Ptr: + el := reflect.New(t.Field(i).Type.Elem()).Elem() + ttmp := GetFieldName2(el.Interface()) + if ttmp != nil { + tttmp.Child = append(tttmp.Child, *ttmp) + } + case reflect.Struct: + ttmp := GetFieldName2(reflect.ValueOf(p).Field(i).Interface()) + if ttmp != nil { + tttmp.Child = append(tttmp.Child, *ttmp) + } + case reflect.Slice: + el := reflect.New(t.Field(i).Type.Elem()).Elem() + ttmp := GetFieldName2(el.Interface()) + if ttmp != nil { + tttmp.Child = append(tttmp.Child, *ttmp) + } + default: + //log.Println("Check type error") + } + tmp.Child = append(tmp.Child, tttmp) + } + default: + log.Println("Check type error", tmp.Name) + return nil + } + + return &tmp +} diff --git a/binder/binder.go b/codec/binders/binder.go similarity index 69% rename from binder/binder.go rename to codec/binders/binder.go index d8eeca8..8045001 100644 --- a/binder/binder.go +++ b/codec/binders/binder.go @@ -1,19 +1,25 @@ -package binder +package binders import ( "errors" - "github.com/tidwall/gjson" "reflect" "regexp" "strconv" "strings" "time" + + "github.com/tidwall/gjson" ) var ( TimeZone = "Local" ) +type Binder interface { + Bind(v reflect.Value, s reflect.StructField) error + Get(key string) interface{} +} + func parseSliceKey(k string, c string) (string, error) { var kl, cl = len(k), len(c) if kl <= cl { @@ -48,14 +54,40 @@ func appendElem(vField reflect.Value, tField reflect.StructField, str string, gV return vField, nil } -func setVal(vField reflect.Value, tField reflect.StructField, str string, gValue *gjson.Result) error { +func setValWithGjson(vField reflect.Value, tField reflect.StructField, gValue gjson.Result) error { + var val interface{} + var err error + + if err = checkRegex(tField, gValue.String()); err != nil { + return err + } + + if tStr := strings.Trim(tField.Type.String(), "[]"); tStr == "interface {}" { + val = gValue.Value() + } else { + if val, err = parseInterface(tStr, gValue.String(), tField); err != nil { + return err + } + } + vField.Set(reflect.ValueOf(val)) + return nil +} + +func checkRegex(tField reflect.StructField, str string) error { if regexPattern := tField.Tag.Get("regex"); regexPattern != "" { if match, _ := regexp.MatchString(regexPattern, str); match == false { return errors.New("value is mismatch") } } + return nil +} - if val, err := parse(tField, str, gValue); err != nil { +func setValWithString(vField reflect.Value, tField reflect.StructField, str string) error { + if err := checkRegex(tField, str); err != nil { + return err + } + + if val, err := parseInterface(strings.Trim(tField.Type.String(), "[]"), str, tField); err != nil { return err } else { vField.Set(reflect.ValueOf(val)) @@ -63,6 +95,46 @@ func setVal(vField reflect.Value, tField reflect.StructField, str string, gValue return nil } +func parseInterface(t string, str string, tField reflect.StructField) (interface{}, error) { + switch t { + case "interface {}": + return str, nil + case "string": + return str, nil + case "time.Time": + return parseTime(tField, str) + case "bool": + return strconv.ParseBool(str) + case "byte": + return parseByte(str, 10) + case "int": + return strconv.Atoi(str) + case "int8": + return parseInt8(str, 10) + case "int16": + return parseInt16(str, 10) + case "int32": + return parseInt32(str, 10) + case "int64": + return parseInt64(str, 10) + case "uint": + return parseUint(str, 10) + case "uint8": + return parseUint8(str, 10) + case "uint16": + return parseUint16(str, 10) + case "uint32": + return parseUint32(str, 10) + case "uint64": + return parseUint64(str, 10) + case "float32": + return parseFloat32(str) + case "float64": + return strconv.ParseFloat(str, 64) + } + return nil, errors.New("not supported type " + tField.Type.String()) +} + func parse(tField reflect.StructField, str string, gValue *gjson.Result) (interface{}, error) { switch strings.Trim(tField.Type.String(), "[]") { case "interface {}": diff --git a/codec/binders/bytes_binder.go b/codec/binders/bytes_binder.go new file mode 100644 index 0000000..4c30421 --- /dev/null +++ b/codec/binders/bytes_binder.go @@ -0,0 +1,34 @@ +package binders + +import ( + "errors" + "reflect" +) + +type bytesDecoder struct { + data []byte +} + +func NewBytesDecoder(data []byte) Binder { + return &bytesDecoder{data: data} +} + +func (decoder *bytesDecoder) Get(key string) interface{} { + return decoder.data +} + +func (decoder *bytesDecoder) Bind(v reflect.Value, s reflect.StructField) error { + bind := s.Tag.Get("bind") + if (v.Type().String() == "[]int8" || v.Type().String() == "[]byte") && len(decoder.data) > 0 { + v.Set(reflect.ValueOf(decoder.data)) + } else { + if bind == "required" { + var key string + if key = s.Tag.Get("key"); key == "" { + key = s.Name + } + return errors.New("input <" + key + "> field is mismatch") + } + } + return nil +} diff --git a/codec/binders/json_binder.go b/codec/binders/json_binder.go new file mode 100644 index 0000000..814a48f --- /dev/null +++ b/codec/binders/json_binder.go @@ -0,0 +1,154 @@ +package binders + +import ( + "errors" + "reflect" + "strconv" + "strings" + + "github.com/tidwall/gjson" +) + +type jsonBinder struct { + root gjson.Result +} + +func NewJsonDecoder(data []byte) Binder { + return &jsonBinder{root: gjson.GetBytes(data, "@this")} +} + +func (b *jsonBinder) Get(key string) (val interface{}) { + if key == "" { + val = b.root.Value() + } else if result := b.root.Get(key); result.Exists() { + val = result.Value() + } + return +} + +func (b *jsonBinder) Bind(v reflect.Value, s reflect.StructField) error { + return bindValue(v, s, b.root, "") +} + +func bindValue(v reflect.Value, s reflect.StructField, source gjson.Result, preKey string) (err error) { + var keys, bind, fullKey string + var item gjson.Result + + bind, keys = s.Tag.Get("bind"), s.Tag.Get("key") + if keys == "" { + keys = s.Name + } + if !v.CanInterface() { + return + } + + for _, v := range strings.Split(keys, ",") { + key := strings.TrimSpace(v) + if fullKey == "" { + if preKey != "" { + fullKey = preKey + "." + key + } else { + fullKey = key + } + } + if item = source.Get(key); item.Exists() { + break + } + } + + if !item.Exists() || item.Type == gjson.Null { + if defaultValue := s.Tag.Get("default"); defaultValue != "" { + if err = setValWithString(v, s, defaultValue); err != nil { + return errors.New("input field <" + fullKey + "> " + err.Error()) + } + } else if bind == "required" { + return errors.New("input field <" + fullKey + "> is required") + } + return nil + } + + switch s.Type.Kind() { + case reflect.Struct: + if s.Type.String() == "time.Time" { + return setValWithGjson(v, s, item) + } else { + return bindStruct(v, item, fullKey) + } + case reflect.Slice: + return bindSlice(v, s, item, fullKey) + default: + return setValWithGjson(v, s, item) + } +} + +func bindStruct(v reflect.Value, source gjson.Result, preKey string) (err error) { + count := v.Type().NumField() + for i := 0; i < count; i++ { + if err = bindValue(v.Field(i), v.Type().Field(i), source, preKey); err != nil { + return + } + } + + return +} + +func bindSlice(v reflect.Value, s reflect.StructField, source gjson.Result, preKey string) (err error) { + if s.Type.Kind() == reflect.Struct { + source.ForEach(func(key, value gjson.Result) bool { + v := reflect.Indirect(reflect.New(v.Type().Elem())) + if err = bindStruct(v, value, preKey); err != nil { + return false + } + v.Set(reflect.Append(v, v)) + return true + }) + } else { + var elems = v + source.ForEach(func(key, value gjson.Result) bool { + if elems, err = setSliceValueWithGJson(v.Type().String(), elems, &value); err != nil { + return false + } + v.Set(elems) + return true + }) + } + return +} + +func setSliceValueWithGJson(fieldType string, elems reflect.Value, value *gjson.Result) (reflect.Value, error) { + if fieldType == "[]interface {}" { + elems = reflect.Append(elems, reflect.ValueOf(value.Value())) + return elems, nil + } + if fieldType != "[]string" && value.Type.String() != "Number" { + if _, err := strconv.ParseFloat(value.Str, 64); err != nil { + return elems, errors.New("data type need number") + } + } + switch fieldType { + case "[]string": + elems = reflect.Append(elems, reflect.ValueOf(value.String())) + case "[]int8": + elems = reflect.Append(elems, reflect.ValueOf(int8(value.Int()))) + case "[]int32": + elems = reflect.Append(elems, reflect.ValueOf(int32(value.Int()))) + case "[]int64": + elems = reflect.Append(elems, reflect.ValueOf(value.Int())) + case "[]int": + elems = reflect.Append(elems, reflect.ValueOf(int(value.Int()))) + case "[]uint8": + elems = reflect.Append(elems, reflect.ValueOf(uint8(value.Uint()))) + case "[]uint32": + elems = reflect.Append(elems, reflect.ValueOf(uint32(value.Uint()))) + case "[]uint64": + elems = reflect.Append(elems, reflect.ValueOf(value.Uint())) + case "[]uint": + elems = reflect.Append(elems, reflect.ValueOf(uint(value.Uint()))) + case "[]float32": + elems = reflect.Append(elems, reflect.ValueOf(float32(value.Float()))) + case "[]float64": + elems = reflect.Append(elems, reflect.ValueOf(value.Float())) + } + + return elems, nil +} diff --git a/codec/binders/urlvalue_binder.go b/codec/binders/urlvalue_binder.go new file mode 100644 index 0000000..6a4ff32 --- /dev/null +++ b/codec/binders/urlvalue_binder.go @@ -0,0 +1,119 @@ +package binders + +import ( + "errors" + "net/url" + "reflect" + "strings" +) + +type urlValueBinder struct { + values url.Values + keys []string +} + +func NewUrlValueBinder(values url.Values) Binder { + binder := &urlValueBinder{values: values} + for k, _ := range values { + binder.keys = append(binder.keys, k) + } + return binder +} + +func (b urlValueBinder) Get(key string) interface{} { + return nil +} + +func (b urlValueBinder) Bind(v reflect.Value, s reflect.StructField) error { + return nil +} + +func (b urlValueBinder) bindValue(v reflect.Value, s reflect.StructField, preKey string) (err error) { + var keys, bind, fullKey string + bind, keys = s.Tag.Get("bind"), s.Tag.Get("key") + if keys == "" { + keys = s.Name + } + + if !v.CanInterface() { + return + } + + for _, v := range strings.Split(keys, ",") { + foo := strings.TrimSpace(v) + if preKey != "" { + foo = preKey + "[" + foo + "]" + } + for _, ikey := range b.keys { + if ikey == foo { + fullKey = ikey + break + } + } + } + if fullKey == "" { + if defaultValue := s.Tag.Get("default"); defaultValue != "" { + if err = setValWithString(v, s, defaultValue); err != nil { + return errors.New("input field <" + fullKey + "> " + err.Error()) + } + } else if bind == "required" { + return errors.New("input field <" + fullKey + "> is required") + } + return nil + } + + switch s.Type.Kind() { + case reflect.Struct: + if s.Type.String() == "time.Time" { + return setValWithString(v, s, b.values.Get(fullKey)) + } else { + return b.bindStructWithUrlValue(v, fullKey) + } + case reflect.Slice: + return b.bindSlice(v, s, fullKey) + default: + return setValWithString(v, s, b.values.Get(fullKey)) + } +} + +func (b urlValueBinder) bindStructWithUrlValue(v reflect.Value, preKey string) (err error) { + count := v.Type().NumField() + for i := 0; i < count; i++ { + if err = b.bindValue(v.Field(i), v.Type().Field(i), preKey); err != nil { + return err + } + } + return +} + +func (b urlValueBinder) bindSlice(v reflect.Value, s reflect.StructField, preKey string) (err error) { + if s.Type.Kind() == reflect.Struct { + var keyList = make(map[string]struct{}, 8) + for k, _ := range b.values { + if strings.HasPrefix(k, preKey) { + if preKey, err := parseSliceKey(k, preKey); err != nil { + return err + } else { + keyList[preKey] = struct{}{} + } + } + } + + for k := range keyList { + v := reflect.Indirect(reflect.New(v.Type().Elem())) + if err = b.bindValue(v, s, k); err != nil { + return err + } + v.Set(reflect.Append(v, v)) + } + } else { + for _, val := range b.values[preKey] { + if elem, err := appendElem(v, s, val, nil); err != nil { + return errors.New("input <" + preKey + "> " + err.Error()) + } else { + v.Set(elem) + } + } + } + return +} diff --git a/codec/renders/json_render.go b/codec/renders/json_render.go new file mode 100644 index 0000000..a3c5342 --- /dev/null +++ b/codec/renders/json_render.go @@ -0,0 +1,20 @@ +package renders + +import "github.com/leochen2038/play/library/golang/json" + +var jRender = &jsonRender{} + +type jsonRender struct { +} + +func GetJsonRender() Render { + return jRender +} + +func (r jsonRender) Name() string { + return "json" +} + +func (r jsonRender) Render(data map[string]interface{}) ([]byte, error) { + return json.MarshalEscape(data, false, false) +} diff --git a/codec/renders/proto3_render.go b/codec/renders/proto3_render.go new file mode 100644 index 0000000..7a604ae --- /dev/null +++ b/codec/renders/proto3_render.go @@ -0,0 +1,128 @@ +package renders + +import ( + "errors" + "fmt" + "reflect" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/dynamicpb" +) + +type protto3Render struct { + descriptor protoreflect.MessageDescriptor +} + +func GetProtto3Render(descriptor protoreflect.MessageDescriptor) Render { + return &protto3Render{descriptor: descriptor} +} + +func (r protto3Render) Name() string { + return "proto3" +} + +func (r protto3Render) Render(data map[string]interface{}) ([]byte, error) { + if message, err := _toProtobuf(data, r.descriptor); err != nil { + return nil, err + } else { + return proto.Marshal(message) + } +} + +func _toProtobufRef(data reflect.Value, descriptor protoreflect.MessageDescriptor) (proto.Message, error) { + message := dynamicpb.NewMessage(descriptor) + structFieldNum := data.Type().NumField() + + for i := 0; i < structFieldNum; i++ { + customKey := data.Type().Field(i).Tag.Get("key") + val := data.Field(i) + if item := descriptor.Fields().ByName(protoreflect.Name(customKey)); item != nil { + if item.IsList() { + if val.Type().Kind() != reflect.Slice { + return nil, errors.New("assigning " + customKey + " invalid type " + reflect.TypeOf(val).Kind().String() + " need slice") + } + lst := message.NewField(item).List() + for i := 0; i < val.Len(); i++ { + if pbVal, err := _convertProtobufVal(item, val.Index(i).Interface()); err != nil { + return nil, err + } else { + lst.Append(pbVal) + } + } + message.Set(item, protoreflect.ValueOf(lst)) + } else { + if item.Kind().String() == "message" { + if sub, err := _toProtobufRef(val, item.Message()); err != nil { + return nil, err + } else { + message.Set(item, protoreflect.ValueOfMessage(sub.ProtoReflect())) + } + } else { + message.Set(item, protoreflect.ValueOf(val.Interface())) + } + } + } + } + + return message, nil +} + +func _toProtobuf(data map[string]interface{}, descriptor protoreflect.MessageDescriptor) (proto.Message, error) { + message := dynamicpb.NewMessage(descriptor) + for i := 0; i < descriptor.Fields().Len(); i++ { + field := descriptor.Fields().Get(i) + var key = string(descriptor.Fields().Get(i).Name()) + if val, ok := data[key]; ok { + if field.IsList() { + if reflect.TypeOf(val).Kind() != reflect.Slice { + return nil, errors.New("assigning " + key + " invalid type string need slice") + } + lst := message.NewField(field).List() + vRef := reflect.ValueOf(val) + for i := 0; i < vRef.Len(); i++ { + if pbVal, err := _convertProtobufVal(field, vRef.Index(i).Interface()); err != nil { + return nil, err + } else { + lst.Append(pbVal) + } + } + message.Set(field, protoreflect.ValueOfList(lst)) + } else { + if pbVal, err := _convertProtobufVal(field, val); err != nil { + return nil, err + } else { + message.Set(field, pbVal) + } + } + } + } + return message, nil +} + +func _convertProtobufVal(field protoreflect.FieldDescriptor, val interface{}) (pbVal protoreflect.Value, err error) { + defer func() { + if panicInfo := recover(); panicInfo != nil { + err = fmt.Errorf("%v", panicInfo) + } + }() + tRef := reflect.TypeOf(val) + if tRef.Kind() == reflect.Struct { + var sub proto.Message + if sub, err = _toProtobufRef(reflect.ValueOf(val), field.Message()); err != nil { + return + } + return protoreflect.ValueOfMessage(sub.ProtoReflect()), nil + } + switch v := val.(type) { + case map[string]interface{}: + var sub proto.Message + if sub, err = _toProtobuf(v, field.Message()); err != nil { + return + } else { + return protoreflect.ValueOfMessage(sub.ProtoReflect()), nil + } + default: + return protoreflect.ValueOf(val), nil + } +} diff --git a/codec/renders/render.go b/codec/renders/render.go new file mode 100644 index 0000000..75ef17a --- /dev/null +++ b/codec/renders/render.go @@ -0,0 +1,6 @@ +package renders + +type Render interface { + Name() string + Render(data map[string]interface{}) ([]byte, error) +} diff --git a/context.go b/context.go index 3fbfdeb..389ab60 100644 --- a/context.go +++ b/context.go @@ -14,8 +14,7 @@ import ( ) var ( - intranetIp net.IP = nil - defaultActionTimeout = 500 * time.Millisecond + intranetIp net.IP = nil ) type ActionInfo struct { @@ -38,6 +37,7 @@ type TraceContext struct { } type Context struct { + context.Context ServerName string values sync.Map ActionInfo ActionInfo @@ -45,17 +45,19 @@ type Context struct { Response Response Session *Session Trace *TraceContext - Err error - ctx context.Context + err error + gctx context.Context + gcfunc context.CancelFunc } -func NewContextWithRequest(s *Session, request *Request) *Context { +func NewPlayContext(parent context.Context, s *Session, request *Request, timeout time.Duration) *Context { + gctx, gcfunc := context.WithTimeout(parent, timeout) var action = ActionInfo{ Caller: request.Caller, Name: request.ActionName, Respond: request.Respond, RequestTime: time.Now(), - Timeout: defaultActionTimeout} + Timeout: timeout} var trace = TraceContext{ TagId: request.TagId, TraceId: request.TraceId, @@ -63,9 +65,7 @@ func NewContextWithRequest(s *Session, request *Request) *Context { StartTime: time.Now(), ServerName: request.ActionName} var response = Response{ - Output: &KvOutput{}, TagId: request.TagId, - Render: request.Render, SpanId: request.SpanId, TraceId: request.TraceId, Template: strings.ReplaceAll(request.ActionName, ".", "/")} @@ -76,23 +76,31 @@ func NewContextWithRequest(s *Session, request *Request) *Context { Response: response, Trace: &trace, Session: s, - ctx: context.Background(), + gctx: gctx, + gcfunc: gcfunc, } } -func (ctx *Context) Value(key string) (interface{}, bool) { - return ctx.values.Load(key) +func (c *Context) Done() <-chan struct{} { + return c.gctx.Done() } -func (ctx *Context) SetValue(key string, val interface{}) { - ctx.values.Store(key, val) +func (c *Context) Deadline() (deadline time.Time, ok bool) { + return c.gctx.Deadline() } -func (ctx *Context) Context() context.Context { - return ctx.ctx +func (c *Context) Err() error { + if c.err != nil { + return c.err + } + return c.gctx.Err() +} + +func (c *Context) Value(key interface{}) interface{} { + return c.gctx.Value(key) } -// 根据ip,按时间生成28位Id +// Generate28Id 根据ip,按时间生成28位Id func Generate28Id(prefix string, suffix string, ipv4 net.IP) string { var x uint16 var timeNow = time.Now() diff --git a/go.mod b/go.mod index 5855ee4..dffb121 100644 --- a/go.mod +++ b/go.mod @@ -10,5 +10,6 @@ require ( github.com/tidwall/gjson v1.8.1 go.etcd.io/etcd/client/v3 v3.5.0 go.mongodb.org/mongo-driver v1.7.1 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c google.golang.org/protobuf v1.27.1 ) diff --git a/go.sum b/go.sum index 1cb1f45..c9a35b1 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,10 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= @@ -326,13 +328,13 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/goplay/reconst/action/action_manager.go b/goplay/reconst/action/action_manager.go index f4cb0a3..6155110 100644 --- a/goplay/reconst/action/action_manager.go +++ b/goplay/reconst/action/action_manager.go @@ -3,6 +3,7 @@ package action import ( "bytes" "errors" + "fmt" "io/ioutil" "os" "path/filepath" @@ -11,6 +12,7 @@ import ( type action struct { name string + metaData map[string]string handlerList *processorHandler } @@ -58,8 +60,17 @@ func initActions(path string) error { // 根据token列表,构建出action结构 func buildActions(tokens []string) error { var curp *processorHandler = nil + var curActionMetaData = make(map[string]string) for i := 0; i < len(tokens); i++ { v := tokens[i] + if v == "@" { + if items := strings.SplitN(tokens[i+1], ":", 2); len(items) != 2 { + return errors.New("error metadata string:" + tokens[i+1]) + } else { + k, v := strings.TrimSpace(items[0]), strings.TrimSpace(items[1]) + curActionMetaData[k] = v + } + } if v == "{" && curp == nil && i != 0 { i += 1 v = tokens[i] @@ -71,8 +82,9 @@ func buildActions(tokens []string) error { } for _, iv := range strings.Split(tokens[i-2], ",") { - action := action{name: iv, handlerList: curp} + action := action{name: iv, handlerList: curp, metaData: curActionMetaData} actions[iv] = action + curActionMetaData = make(map[string]string) } continue } @@ -126,7 +138,6 @@ func buildActions(tokens []string) error { func parseTokenFrom(reader *bytes.Reader, filename string) ([]string, error) { token := make([]byte, 0, 32) tokens := make([]string, 0, 128) - for { c, err := reader.ReadByte() if err != nil { @@ -138,11 +149,35 @@ func parseTokenFrom(reader *bytes.Reader, filename string) ([]string, error) { } continue } - if c == '#' { + if c == '/' || c == '#' { + if c == '/' { + if c, err := reader.ReadByte(); err != nil { + break + } else if c != '/' { + return nil, errors.New("miss '/' at:" + filename) + } + } + var actionMeta = make([]byte, 0) + var findMeta bool for ; c != '\n'; c, err = reader.ReadByte() { if err != nil { break } + if c == '@' { + findMeta = true + if len(actionMeta) > 0 { + tokens = append(tokens, strings.TrimSpace(string(actionMeta))) + actionMeta = make([]byte, 0) + } + tokens = append(tokens, "@") + continue + } + if findMeta == true { + actionMeta = append(actionMeta, c) + } + } + if len(actionMeta) > 0 { + tokens = append(tokens, strings.TrimSpace(string(actionMeta))) } continue } @@ -156,7 +191,7 @@ func parseTokenFrom(reader *bytes.Reader, filename string) ([]string, error) { } if c == '{' || c == '(' { if len(token) == 0 { - return nil, errors.New("miss action name or processor define befer '{' or '(' by parse:" + filename) + return nil, errors.New("miss action name or processor define before '{' or '(' by parse:" + filename) } tokens = append(tokens, string(token)) tokens = append(tokens, string(c)) @@ -175,6 +210,6 @@ func parseTokenFrom(reader *bytes.Reader, filename string) ([]string, error) { token = append(token, c) } } - + fmt.Println("token:", tokens) return tokens, nil } diff --git a/goplay/reconst/action/reconst_action.go b/goplay/reconst/action/reconst_action.go index 6da91b6..7a61e6c 100644 --- a/goplay/reconst/action/reconst_action.go +++ b/goplay/reconst/action/reconst_action.go @@ -16,18 +16,22 @@ var packages = map[string]string{} var crontab = map[string]struct{}{} func ReconstAction() (err error) { + var emptyAction = true actions, err := getActions(env.ProjectPath + "/assets/action") - + if err != nil { + return err + } registerCode = "func init() {\n" registerCode += genRegisterCronCode(env.ProjectPath + "/crontab") for _, action := range actions { + emptyAction = false registerCode += "\tplay.RegisterAction(\"" + action.name + "\", " + "func()interface{}{return " genNextProcessorCode(action.handlerList, &action) registerCode = registerCode[:len(registerCode)-1] + "})\n" } registerCode += "}" - if err = updateRegister(env.ProjectPath, env.FrameworkName); err != nil { + if err = updateRegister(env.ProjectPath, env.FrameworkName, emptyAction); err != nil { return } @@ -101,14 +105,14 @@ func genNextProcessorCode(proc *processorHandler, act *action) { registerCode += "," } -func updateRegister(project, frameworkName string) (err error) { +func updateRegister(project, frameworkName string, emptyAction bool) (err error) { var module string if module, err = parseModuleName(project); err != nil { return } src := "package main\n\n" - if len(crontab) > 0 || len(packages) > 0 { + if len(crontab) > 0 || len(packages) > 0 || !emptyAction { src += "import (\n\t\"" + frameworkName + "\"\n" } for k, _ := range crontab { @@ -120,7 +124,7 @@ func updateRegister(project, frameworkName string) (err error) { if len(packages) > 0 { src += "\"unsafe\"\n" } - if len(crontab) > 0 || len(packages) > 0 { + if len(crontab) > 0 || len(packages) > 0 || !emptyAction { src += ")\n\n" } diff --git a/goplay/reconst/meta/query_generator.go b/goplay/reconst/meta/query_generator.go index 657893b..22beb30 100644 --- a/goplay/reconst/meta/query_generator.go +++ b/goplay/reconst/meta/query_generator.go @@ -4,7 +4,6 @@ import ( "encoding/xml" "errors" "fmt" - "github.com/leochen2038/play/goplay/reconst/env" "io/ioutil" "os" "os/exec" @@ -12,6 +11,8 @@ import ( "runtime" "strings" "unicode" + + "github.com/leochen2038/play/goplay/reconst/env" ) type Meta struct { @@ -245,6 +246,24 @@ func (q *query%s)%s%s%s(s []%s) *query%s { } } } + for k, v := range map[string]string{"Asc": "asc", "Desc": "desc"} { + // generate key + src += fmt.Sprintf(` +func (q *query%s)OrderBy%s%s() *query%s { + q.QueryInfo.Order = append(q.QueryInfo.Order, [2]string{"%s", "%s"}) + return q +} +`, funcName, formatUcfirstName(meta.Key.Name), k, funcName, meta.Key.Name, v) + // generate fields + for _, vb := range meta.Fields.List { + src += fmt.Sprintf(` +func (q *query%s)OrderBy%s%s() *query%s { + q.QueryInfo.Order = append(q.QueryInfo.Order, [2]string{"%s", "%s"}) + return q +} +`, funcName, ucfirst(vb.Name), k, funcName, vb.Name, v) + } + } src += fmt.Sprintf(` func (q *query%s)OrderBy(key, val string) *query%s { diff --git a/input.go b/input.go new file mode 100644 index 0000000..55f08e5 --- /dev/null +++ b/input.go @@ -0,0 +1,65 @@ +package play + +import ( + "errors" + "reflect" + "strings" + "sync" + + "github.com/leochen2038/play/codec/binder" +) + +type Input struct { + binder binder.Binder + exValues sync.Map +} + +func NewInput(binder binder.Binder) Input { + return Input{binder: binder} +} + +func (input *Input) SetValue(key string, val interface{}) { + input.exValues.Store(key, val) +} + +func (input *Input) Value(key string) interface{} { + if exValue, ok := input.exValues.Load(key); ok { + return exValue + } else { + return input.binder.Get(key) + } +} + +func (input *Input) Bind(v reflect.Value) (err error) { + if v.CanSet() { + var tField reflect.StructField + var vField reflect.Value + var fieldCount = v.Type().NumField() + + for i := 0; i < fieldCount; i++ { + if vField, tField = v.Field(i), v.Type().Field(i); !vField.CanInterface() { + continue + } + + key := tField.Tag.Get("key") + if key == "" { + key = tField.Name + } + for _, key := range strings.Split(key, ",") { + if exValue, ok := input.exValues.Load(key); ok { + if tField.Type.String() != reflect.TypeOf(exValue).String() { + return errors.New("input custom " + key + " type need " + tField.Type.String() + " but " + reflect.TypeOf(exValue).String() + " given") + } + vField.Set(reflect.ValueOf(exValue)) + goto NEXT + } + } + + if err = input.binder.Bind(vField, tField); err != nil { + return err + } + NEXT: + } + } + return +} diff --git a/library/agent/deplod.go b/library/agent/deplod.go new file mode 100644 index 0000000..2a11b63 --- /dev/null +++ b/library/agent/deplod.go @@ -0,0 +1,9 @@ +package agent + + + +//func CallDeplod(request AgentDeplodReq) (response error) { +// call("agent.deplod.xxxx") +//} + +// \ No newline at end of file diff --git a/library/golang/json/encode.go b/library/golang/json/encode.go index 1794730..31c0a8e 100644 --- a/library/golang/json/encode.go +++ b/library/golang/json/encode.go @@ -1241,6 +1241,9 @@ func typeFields(t reflect.Type) structFields { continue } tag := sf.Tag.Get("key") + if tag == "" { + tag = sf.Tag.Get("json") + } if tag == "-" { continue } diff --git a/output.go b/output.go index 3b766db..5c4d592 100644 --- a/output.go +++ b/output.go @@ -1,28 +1,15 @@ package play import ( - "errors" - "fmt" - "github.com/leochen2038/play/library/golang/json" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/dynamicpb" - "reflect" + "github.com/leochen2038/play/codec/render" ) -type Output interface { - Get(key string) interface{} - Set(key string, val interface{}) - ToJsonRaw() ([]byte, error) - ToProtobuf(descriptor protoreflect.MessageDescriptor) ([]byte, error) - All() interface{} +type Output struct { + render render.Render + data map[string]interface{} } -type KvOutput struct { - data map[string]interface{} -} - -func (o *KvOutput) Get(key string) interface{} { +func (o *Output) Get(key string) interface{} { if key != "" { val, _ := o.data[key] return val @@ -30,17 +17,30 @@ func (o *KvOutput) Get(key string) interface{} { return o.data } -func (o *KvOutput) All() interface{} { +func (o *Output) All() interface{} { return o.data } -func (o *KvOutput) Set(key string, val interface{}) { +func (o *Output) Set(key string, val interface{}) { if o.data == nil { o.data = make(map[string]interface{}, 10) } o.data[key] = val } +func (o Output) Render() ([]byte, error) { + return o.render.Render(o.data) +} + +func (o Output) RenderName() string { + return o.render.Name() +} + +func (o *Output) SetRender(render render.Render) { + o.render = render +} + +/* func (o *KvOutput) ToJsonRaw() (data []byte, err error) { return json.MarshalEscape(o.data, false, false) } @@ -149,3 +149,4 @@ func _convertProtobufVal(field protoreflect.FieldDescriptor, val interface{}) (p return protoreflect.ValueOf(val), nil } } +*/ diff --git a/server.go b/server.go index 4448a30..7a0090e 100644 --- a/server.go +++ b/server.go @@ -1,20 +1,23 @@ package play import ( - "github.com/gorilla/websocket" "net" "net/http" "reflect" "sync" + + "github.com/gorilla/websocket" + "github.com/leochen2038/play/codec/binder" + "github.com/leochen2038/play/codec/render" ) type IServerHook interface { OnConnect(sess *Session, err error) OnClose(sess *Session, err error) - OnRequest(ctx *Context) - OnResponse(ctx *Context) - OnFinish(ctx *Context) + OnRequest(ctx *Context) error + OnResponse(ctx *Context) error + OnFinish(ctx *Context) error } type IServer interface { @@ -35,7 +38,7 @@ type Binder interface { type ITransport interface { Receive(c *Conn) (*Request, error) - Response(c *Conn, res *Response) error + Send(c *Conn, res *Response) error } type InstanceInfo struct { @@ -77,23 +80,23 @@ type Conn struct { } type Request struct { - Version byte - Render string - Caller string - TagId int - TraceId string - SpanId []byte - Respond bool - ActionName string - InputBinder Binder + Version byte + Render string + Caller string + TagId int + TraceId string + SpanId []byte + Respond bool + ActionName string + InputBinder binder.Binder + OutputRender render.Render } type Response struct { - ErrorCode int - TagId int - Render string - TraceId string - SpanId []byte - Template string - Output Output + ErrorCode int + TagId int + TraceId string + SpanId []byte + Template string + OutputRender Output } diff --git a/server/boot_server.go b/server/boot_server.go index db8f6ba..ca3042f 100644 --- a/server/boot_server.go +++ b/server/boot_server.go @@ -1,19 +1,21 @@ package server import ( + "context" "errors" "fmt" - "github.com/leochen2038/play" "log" "net" "os" "os/exec" "os/signal" - "runtime/debug" "strconv" "strings" "sync" "syscall" + + "github.com/leochen2038/play" + "golang.org/x/sync/errgroup" ) type runningInstance struct { @@ -46,38 +48,45 @@ type filer interface { File() (*os.File, error) } -func Wait() { - instanceWaitGroup.Wait() -} - -func Boot(i play.IServer) error { - var err error - var listener net.Listener - var gracefulSocket = getGracefulSocket(i.Info().Name) +func Boot(is ...play.IServer) error { + var egr errgroup.Group + for _, i := range is { + var i = i + var err error + var listener net.Listener + var gracefulSocket = getGracefulSocket(i.Info().Name) + egr.Go(func() error { + if gracefulSocket > 0 { + if listener, err = net.FileListener(os.NewFile(gracefulSocket, "")); err != nil { + return err + } + if err = shouldKillParent(); err != nil { + log.Println("server failed to close parent:", err) + os.Exit(1) + } + } else if listener, err = net.Listen("tcp", i.Info().Address); err != nil { + return err + } + if _, ok := instances.Load(i.Info().Name); ok { + _ = listener.Close() + return errors.New("server name " + i.Info().Name + " is running") + } - if gracefulSocket > 0 { - if listener, err = net.FileListener(os.NewFile(gracefulSocket, "")); err != nil { - return err - } - if err = shouldKillParent(); err != nil { - log.Println("[http server] failed to close parent:", err) - os.Exit(1) - } - } else if listener, err = net.Listen("tcp", i.Info().Address); err != nil { - return err + instanceWaitGroup.Add(1) + instances.Store(i.Info().Name, runningInstance{listener: listener, server: i}) + go func() { + defer instanceWaitGroup.Done() + _ = i.Run(listener) + }() + return nil + }) } - if _, ok := instances.Load(i.Info().Name); ok { - _ = listener.Close() - return errors.New("server name " + i.Info().Name + " is running") + if err := egr.Wait(); err != nil { + ShutdownAll() + return err } - instanceWaitGroup.Add(1) - instances.Store(i.Info().Name, runningInstance{listener: listener, server: i}) - go func() { - defer instanceWaitGroup.Done() - _ = i.Run(listener) - }() - + instanceWaitGroup.Wait() return nil } @@ -97,34 +106,14 @@ func Shutdown(name string) { } } -func doRequest(s *play.Session, request *play.Request) (err error) { +// 返回callAction里的onFinish错误 +func doRequest(gctx context.Context, s *play.Session, request *play.Request) (err error) { s.Server.Ctrl().AddTask() - defer func() { s.Server.Ctrl().DoneTask() - if panicInfo := recover(); panicInfo != nil { - err = fmt.Errorf("panic: %v\n%v", panicInfo, string(debug.Stack())) - } }() - ctx := play.NewContextWithRequest(s, request) - - hook := s.Server.Hook() - if hook.OnRequest(ctx); ctx.Err != nil { - goto RESPONSE - } - - ctx.Err = play.RunAction(ctx) - -RESPONSE: - hook.OnResponse(ctx) - if request.Respond { - if err = s.Write(&ctx.Response); err != nil { - return - } - } - hook.OnFinish(ctx) - return + return play.CallAction(gctx, s, request) } func reload() (int, error) { diff --git a/server/http_instance.go b/server/http_instance.go index 27a3562..26360f8 100644 --- a/server/http_instance.go +++ b/server/http_instance.go @@ -87,14 +87,17 @@ func (i *httpInstance) ServeHTTP(w http.ResponseWriter, r *http.Request) { defer func() { if panicInfo := recover(); panicInfo != nil { - err = fmt.Errorf("panic: %v\n%v", panicInfo, string(debug.Stack())) + fmt.Printf("panic: %v\n%v", panicInfo, string(debug.Stack())) } + }() + defer func() { i.hook.OnClose(sess, err) }() - i.hook.OnConnect(sess, nil) - request, err = i.transport.Receive(sess.Conn) - err = doRequest(sess, request) + if request, err = i.transport.Receive(sess.Conn); err != nil { + return + } + err = doRequest(r.Context(), sess, request) } func (i *httpInstance) SetWSInstance(ws *wsInstance) { diff --git a/server/sse_instance.go b/server/sse_instance.go index 4541f53..1817ad8 100644 --- a/server/sse_instance.go +++ b/server/sse_instance.go @@ -1,6 +1,7 @@ package server import ( + "context" "crypto/rand" "crypto/tls" "errors" @@ -70,13 +71,16 @@ func (i *sseInstance) accept(s *play.Session) { var err error var w = s.Conn.Http.ResponseWriter - i.hook.OnConnect(s, nil) defer func() { if panicInfo := recover(); panicInfo != nil { - err = fmt.Errorf("panic: %v\n%v", panicInfo, string(debug.Stack())) + fmt.Printf("panic: %v\n%v", panicInfo, string(debug.Stack())) } + }() + + defer func() { i.hook.OnClose(s, err) }() + i.hook.OnConnect(s, nil) if _, ok := w.(http.Flusher); !ok { http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) @@ -96,7 +100,7 @@ func (i *sseInstance) accept(s *play.Session) { return } - if err = doRequest(s, request); err != nil { + if err = doRequest(context.Background(), s, request); err != nil { return } diff --git a/server/tcp_instance.go b/server/tcp_instance.go index 02c5498..db4f29c 100644 --- a/server/tcp_instance.go +++ b/server/tcp_instance.go @@ -4,9 +4,10 @@ import ( "context" "errors" "fmt" - "github.com/leochen2038/play" "net" "runtime/debug" + + "github.com/leochen2038/play" ) type TcpInstance struct { @@ -30,9 +31,12 @@ func (i *TcpInstance) accept(s *play.Session) { var err error defer func() { if panicInfo := recover(); panicInfo != nil { - err = fmt.Errorf("panic: %v\n%v", panicInfo, string(debug.Stack())) + fmt.Printf("panic: %v\n%v", panicInfo, string(debug.Stack())) } _ = s.Conn.Tcp.Conn.Close() + }() + + defer func() { i.hook.OnClose(s, err) }() @@ -64,7 +68,7 @@ func (i *TcpInstance) onReady(s *play.Session) (err error) { continue } else { s.Conn.Tcp.Version = request.Version - if err = doRequest(s, request); err != nil { + if err = doRequest(context.Background(), s, request); err != nil { return err } } @@ -96,6 +100,11 @@ func (i *TcpInstance) Run(listener net.Listener) error { continue } else { go func() { + defer func() { + if panicInfo := recover(); panicInfo != nil { + // call system log ? + } + }() s := play.NewSession(context.Background(), new(play.Conn), i) s.Conn.Tcp.Conn = conn i.accept(s) diff --git a/server/ws_instance.go b/server/ws_instance.go index 5565c78..c1f25a8 100644 --- a/server/ws_instance.go +++ b/server/ws_instance.go @@ -1,15 +1,17 @@ package server import ( + "context" "crypto/rand" "crypto/tls" "errors" "fmt" - "github.com/gorilla/websocket" - "github.com/leochen2038/play" "net" "net/http" "runtime/debug" + + "github.com/gorilla/websocket" + "github.com/leochen2038/play" ) var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { @@ -61,16 +63,19 @@ func (i *wsInstance) accept(s *play.Session) { var err error var request *play.Request - i.hook.OnConnect(s, nil) defer func() { if panicInfo := recover(); panicInfo != nil { - err = fmt.Errorf("panic: %v\n%v", panicInfo, string(debug.Stack())) + fmt.Printf("panic: %v\n%v", panicInfo, string(debug.Stack())) } + }() + + defer func() { i.hook.OnClose(s, err) }() + i.hook.OnConnect(s, nil) if request, err = i.transport.Receive(s.Conn); request != nil { - if err = doRequest(s, request); err != nil { + if err = doRequest(context.Background(), s, request); err != nil { return } } @@ -93,9 +98,10 @@ func (i *wsInstance) onReady(sess *play.Session) error { sess.Conn.Websocket.Message = message sess.Conn.Websocket.MessageType = messageType - request, err := i.transport.Receive(sess.Conn) - if request != nil { - if err := doRequest(sess, request); err != nil { + if request, err := i.transport.Receive(sess.Conn); err != nil { + return err + } else { + if err := doRequest(context.Background(), sess, request); err != nil { return err } } diff --git a/session.go b/session.go index 4a55f5c..d2e3892 100644 --- a/session.go +++ b/session.go @@ -26,7 +26,7 @@ func NewSession(cxt context.Context, c *Conn, server IServer) *Session { func (s *Session) Write(res *Response) (err error) { if res != nil { - if err = s.Server.Transport().Response(s.Conn, res); err != nil { + if err = s.Server.Transport().Send(s.Conn, res); err != nil { s.ctxCancel() } } diff --git a/transport/http.go b/transport/http.go index 9c1ab3a..b4f74a8 100644 --- a/transport/http.go +++ b/transport/http.go @@ -3,11 +3,12 @@ package transport import ( "bytes" "errors" - "github.com/leochen2038/play" - "github.com/leochen2038/play/binder" "io/ioutil" "net/http" "strings" + + "github.com/leochen2038/play" + "github.com/leochen2038/play/codec/binder" ) type HttpTransport struct { @@ -32,7 +33,8 @@ func (p *HttpTransport) Receive(c *play.Conn) (*play.Request, error) { return request, nil } -func (p *HttpTransport) Response(c *play.Conn, res *play.Response) (err error) { +func (p *HttpTransport) Send(c *play.Conn, res *play.Response) (err error) { + switch res.Render { case "json": err = HttpSendJson(c.Http.ResponseWriter, res.Output) diff --git a/transport/sse.go b/transport/sse.go index 19a35fc..84bbf58 100644 --- a/transport/sse.go +++ b/transport/sse.go @@ -25,7 +25,7 @@ func (p *SseTransport) Receive(c *play.Conn) (*play.Request, error) { return &request, nil } -func (p *SseTransport) Response(c *play.Conn, res *play.Response) error { +func (p *SseTransport) Send(c *play.Conn, res *play.Response) error { var err error var data []byte var w = c.Http.ResponseWriter diff --git a/transport/tcp_play.go b/transport/tcp_play.go index 271f0b5..e8f0467 100644 --- a/transport/tcp_play.go +++ b/transport/tcp_play.go @@ -92,7 +92,7 @@ func (p *TcpPlayTransport) Receive(c *play.Conn) (*play.Request, error) { return &request, nil } -func (p *TcpPlayTransport) Response(c *play.Conn, res *play.Response) (err error) { +func (p *TcpPlayTransport) Send(c *play.Conn, res *play.Response) (err error) { var message []byte var buffer []byte diff --git a/transport/ws_json.go b/transport/ws_json.go index 42aba7c..e396197 100644 --- a/transport/ws_json.go +++ b/transport/ws_json.go @@ -28,7 +28,7 @@ func (m *WsJsonTransport) Receive(c *play.Conn) (*play.Request, error) { return &request, nil } -func (m *WsJsonTransport) Response(c *play.Conn, res *play.Response) error { +func (m *WsJsonTransport) Send(c *play.Conn, res *play.Response) error { var err error var data []byte var messageType = c.Websocket.MessageType