server initial commit
This commit is contained in:
322
shared/mongo.go
Normal file
322
shared/mongo.go
Normal file
@ -0,0 +1,322 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type MongoClient struct {
|
||||
db *mongo.Database
|
||||
c *mongo.Client
|
||||
}
|
||||
|
||||
type ConnectionInfo struct {
|
||||
Url string
|
||||
Database string
|
||||
}
|
||||
|
||||
var Devflag = flag.Bool("dev", false, "")
|
||||
|
||||
type CollectionName string
|
||||
|
||||
const (
|
||||
CollectionCoupon = CollectionName("coupon")
|
||||
CollectionCouponUse = CollectionName("coupon_use")
|
||||
CollectionAccount = CollectionName("account")
|
||||
CollectionAuth = CollectionName("auth")
|
||||
CollectionMatch = CollectionName("match")
|
||||
)
|
||||
|
||||
func mongourl() string {
|
||||
v := os.Getenv("MONGO_URL")
|
||||
if len(v) > 0 {
|
||||
return v
|
||||
}
|
||||
return "mongodb://redis-dev.actionsquare.corp:27017/?replicaSet=repl01"
|
||||
}
|
||||
|
||||
func NewMongoConnectionInfo() *ConnectionInfo {
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
dbname := "anvil"
|
||||
if *Devflag {
|
||||
dbname, _ = os.Hostname()
|
||||
}
|
||||
|
||||
return &ConnectionInfo{
|
||||
Url: mongourl(),
|
||||
Database: dbname,
|
||||
}
|
||||
}
|
||||
func (ci *ConnectionInfo) SetURL(url string) *ConnectionInfo {
|
||||
ci.Url = url
|
||||
return ci
|
||||
}
|
||||
|
||||
func (ci *ConnectionInfo) SetDatabase(dbname string) *ConnectionInfo {
|
||||
ci.Database = dbname
|
||||
return ci
|
||||
}
|
||||
|
||||
func NewMongoClient(ci *ConnectionInfo) (MongoClient, error) {
|
||||
if len(ci.Url) == 0 {
|
||||
return MongoClient{}, errors.New("mongo connection string is empty")
|
||||
}
|
||||
|
||||
client, err := mongo.NewClient(options.Client().ApplyURI(ci.Url))
|
||||
if err != nil {
|
||||
return MongoClient{}, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err = client.Connect(ctx)
|
||||
if err != nil {
|
||||
return MongoClient{}, err
|
||||
}
|
||||
err = client.Ping(ctx, nil)
|
||||
if err != nil {
|
||||
return MongoClient{}, err
|
||||
}
|
||||
|
||||
anvildb := client.Database(ci.Database, nil)
|
||||
makeExpiredIndex := func(collname CollectionName, expireSeconds int32) error {
|
||||
matchcoll := anvildb.Collection(string(collname))
|
||||
indices, err := matchcoll.Indexes().List(ctx, options.ListIndexes().SetMaxTime(time.Second))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allindices := make([]interface{}, 0)
|
||||
err = indices.All(context.Background(), &allindices)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tsfound := false
|
||||
var tsname string
|
||||
var exp int32
|
||||
for _, index := range allindices {
|
||||
d := index.(bson.D)
|
||||
key := d.Map()["key"].(bson.D)
|
||||
for _, kd := range key {
|
||||
if kd.Key == "_ts" {
|
||||
tsfound = true
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := d.Map()["name"]; ok {
|
||||
tsname = v.(string)
|
||||
}
|
||||
if v, ok := d.Map()["expireAfterSeconds"]; ok {
|
||||
exp = v.(int32)
|
||||
}
|
||||
}
|
||||
|
||||
if tsfound {
|
||||
if exp == expireSeconds {
|
||||
return nil
|
||||
}
|
||||
_, err = matchcoll.Indexes().DropOne(ctx, tsname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
mod := mongo.IndexModel{
|
||||
Keys: primitive.M{"_ts": 1},
|
||||
Options: options.Index().SetExpireAfterSeconds(expireSeconds),
|
||||
}
|
||||
|
||||
_, err = matchcoll.Indexes().CreateOne(ctx, mod)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = makeExpiredIndex(CollectionMatch, 30); err != nil {
|
||||
return MongoClient{}, err
|
||||
}
|
||||
if err = makeExpiredIndex(CollectionAuth, 300); err != nil {
|
||||
return MongoClient{}, err
|
||||
}
|
||||
|
||||
return MongoClient{c: client, db: anvildb}, nil
|
||||
}
|
||||
|
||||
func (mc MongoClient) Close() {
|
||||
if mc.c != nil {
|
||||
mc.c.Disconnect(context.Background())
|
||||
}
|
||||
}
|
||||
|
||||
func (mc MongoClient) Watch(coll CollectionName, pipeline mongo.Pipeline) (*mongo.ChangeStream, error) {
|
||||
return mc.Collection(coll).Watch(context.Background(), pipeline, options.ChangeStream().SetFullDocument(options.UpdateLookup).SetMaxAwaitTime(0))
|
||||
}
|
||||
|
||||
func (mc MongoClient) Collection(collname CollectionName) *mongo.Collection {
|
||||
return mc.db.Collection(string(collname))
|
||||
}
|
||||
|
||||
func (mc MongoClient) All(coll CollectionName, opts ...*options.FindOptions) ([]bson.M, error) {
|
||||
cursor, err := mc.Collection(coll).Find(context.Background(), bson.D{}, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var all []bson.M
|
||||
err = cursor.All(context.Background(), &all)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
func (mc MongoClient) FindOneAndDelete(coll CollectionName, filter bson.M) (bson.M, error) {
|
||||
result := mc.Collection(coll).FindOneAndDelete(context.Background(), filter)
|
||||
err := result.Err()
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tmp := make(map[string]interface{})
|
||||
err = result.Decode(&tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bson.M(tmp), nil
|
||||
}
|
||||
|
||||
func (mc MongoClient) Delete(coll CollectionName, filter bson.M) (bool, error) {
|
||||
r, err := mc.Collection(coll).DeleteOne(context.Background(), filter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return r.DeletedCount > 0, nil
|
||||
}
|
||||
|
||||
func (mc MongoClient) UnsetField(coll CollectionName, filter bson.M, doc bson.M) error {
|
||||
_, err := mc.Collection(coll).UpdateOne(context.Background(), filter, bson.M{
|
||||
"$unset": doc,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (mc MongoClient) DeleteMany(coll CollectionName, filters bson.M, opts ...*options.DeleteOptions) (int, error) {
|
||||
result, err := mc.Collection(coll).DeleteMany(context.Background(), filters, opts...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(result.DeletedCount), nil
|
||||
}
|
||||
|
||||
func (mc MongoClient) InsertMany(coll CollectionName, documents []interface{}, opts ...*options.InsertManyOptions) (int, error) {
|
||||
result, err := mc.Collection(coll).InsertMany(context.Background(), documents, opts...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(result.InsertedIDs), nil
|
||||
}
|
||||
|
||||
func (mc MongoClient) UpdateMany(coll CollectionName, filter bson.M, doc bson.M, opts ...*options.UpdateOptions) (count int, err error) {
|
||||
result, e := mc.Collection(coll).UpdateMany(context.Background(), filter, doc, opts...)
|
||||
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
|
||||
err = nil
|
||||
count = int(result.UpsertedCount + result.ModifiedCount)
|
||||
return
|
||||
}
|
||||
|
||||
func (mc MongoClient) Update(coll CollectionName, filter bson.M, doc bson.M, opts ...*options.UpdateOptions) (worked bool, newid interface{}, err error) {
|
||||
result, e := mc.Collection(coll).UpdateOne(context.Background(), filter, doc, opts...)
|
||||
|
||||
if e != nil {
|
||||
return false, "", e
|
||||
}
|
||||
|
||||
err = nil
|
||||
worked = result.UpsertedCount > 0 || result.ModifiedCount > 0
|
||||
newid = result.UpsertedID
|
||||
return
|
||||
}
|
||||
|
||||
func (mc MongoClient) UpsertOne(coll CollectionName, filter bson.M, doc bson.M) (worked bool, newid interface{}, err error) {
|
||||
return mc.Update(coll, filter, bson.M{
|
||||
"$set": doc,
|
||||
}, options.Update().SetUpsert(true))
|
||||
}
|
||||
|
||||
func (mc MongoClient) FindOne(coll CollectionName, filter bson.M, opts ...*options.FindOneOptions) (doc bson.M, err error) {
|
||||
result := mc.Collection(coll).FindOne(context.Background(), filter, opts...)
|
||||
tmp := make(map[string]interface{})
|
||||
err = result.Decode(&tmp)
|
||||
if err == nil {
|
||||
doc = bson.M(tmp)
|
||||
} else if err == mongo.ErrNoDocuments {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (mc MongoClient) FindOneAndUpdate(coll CollectionName, filter bson.M, doc bson.M, opts ...*options.FindOneAndUpdateOptions) (olddoc bson.M, err error) {
|
||||
result := mc.Collection(coll).FindOneAndUpdate(context.Background(), filter, doc, opts...)
|
||||
tmp := make(map[string]interface{})
|
||||
err = result.Decode(&tmp)
|
||||
if err == nil {
|
||||
olddoc = bson.M(tmp)
|
||||
} else if err == mongo.ErrNoDocuments {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (mc MongoClient) Exists(coll CollectionName, filter bson.M) (bool, error) {
|
||||
cnt, err := mc.Collection(coll).CountDocuments(context.Background(), filter, options.Count().SetLimit(1))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return cnt > 0, nil
|
||||
}
|
||||
|
||||
func (mc MongoClient) FindAll(coll CollectionName, filter bson.M, opts ...*options.FindOptions) ([]bson.M, error) {
|
||||
cursor, err := mc.Collection(coll).Find(context.Background(), filter, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output := make([]interface{}, 0)
|
||||
err = cursor.All(context.Background(), &output)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
docs := make([]bson.M, 0, len(output))
|
||||
for _, doc := range output {
|
||||
one := make(bson.M)
|
||||
for _, kv := range doc.(bson.D) {
|
||||
one[kv.Key] = kv.Value
|
||||
}
|
||||
docs = append(docs, one)
|
||||
}
|
||||
return docs, nil
|
||||
}
|
||||
381
shared/server.go
Normal file
381
shared/server.go
Normal file
@ -0,0 +1,381 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// HTTPStatusReloginRequired : http status를 이걸 받으면 클라이언트는 로그아웃하고 로그인 화면으로 돌아가야 한다.
|
||||
HTTPStatusReloginRequired = 599
|
||||
// HTTPStatusReloginRequiredDupID :
|
||||
HTTPStatusReloginRequiredDupID = 598
|
||||
// HTTPStatusPlayerBlocked :
|
||||
HTTPStatusPlayerBlocked = 597
|
||||
)
|
||||
|
||||
type ShutdownFlag int32
|
||||
|
||||
const (
|
||||
ShutdownFlagRunning = ShutdownFlag(0)
|
||||
ShutdownFlagTerminating = ShutdownFlag(1)
|
||||
ShutdownFlagRestarting = ShutdownFlag(2)
|
||||
ShutdownFlagIdle = ShutdownFlag(3)
|
||||
)
|
||||
|
||||
type functionCallContext struct {
|
||||
Method string
|
||||
Args []interface{}
|
||||
}
|
||||
|
||||
// RPCReturnType : RPC 호출 가능한 함수가 유일하게 리턴할 수 있는 타입
|
||||
type RPCReturnType interface {
|
||||
Serialize(io.Writer) error
|
||||
Error() error
|
||||
}
|
||||
|
||||
type rpcReturnTypeImpl struct {
|
||||
value reflect.Value
|
||||
err error
|
||||
serialized []byte
|
||||
}
|
||||
|
||||
// Bytes : RPCReturnType.Serialize 구현
|
||||
func (r rpcReturnTypeImpl) Serialize(w io.Writer) (err error) {
|
||||
err = r.err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(r.serialized) > 0 {
|
||||
w.Write(r.serialized)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !r.value.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch r.value.Kind() {
|
||||
case reflect.String:
|
||||
_, err = w.Write([]byte(r.value.String()))
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
_, err = w.Write([]byte(fmt.Sprintf("%d", r.value.Int())))
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
_, err = w.Write([]byte(fmt.Sprintf("%f", r.value.Float())))
|
||||
|
||||
case reflect.Slice:
|
||||
switch r.value.Type().Elem().Kind() {
|
||||
case reflect.Uint8:
|
||||
_, err = w.Write(r.value.Bytes())
|
||||
|
||||
default:
|
||||
var conv []interface{}
|
||||
for i := 0; i < r.value.Len(); i++ {
|
||||
conv = append(conv, r.value.Index(i).Interface())
|
||||
}
|
||||
if len(conv) == 0 {
|
||||
_, err = w.Write([]byte("[]"))
|
||||
} else {
|
||||
err = json.NewEncoder(w).Encode(conv)
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Interface, reflect.Struct, reflect.Map:
|
||||
err = json.NewEncoder(w).Encode(r.value.Interface())
|
||||
|
||||
case reflect.Ptr:
|
||||
if !r.value.IsNil() {
|
||||
err = json.NewEncoder(w).Encode(r.value.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Error : RPCReturnType.Error 구현
|
||||
func (r rpcReturnTypeImpl) Error() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// MakeRPCReturn :
|
||||
func MakeRPCReturn(value interface{}, err error) RPCReturnType {
|
||||
return rpcReturnTypeImpl{
|
||||
value: reflect.ValueOf(value),
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// MakeRPCReturnSerialized : 이미 시리얼라이즈 한 Return
|
||||
func MakeRPCReturnSerialized(serialized []byte) RPCReturnType {
|
||||
return rpcReturnTypeImpl{
|
||||
serialized: serialized,
|
||||
}
|
||||
}
|
||||
|
||||
// MakeRPCError :
|
||||
func MakeRPCError(args ...interface{}) RPCReturnType {
|
||||
return rpcReturnTypeImpl{
|
||||
value: reflect.ValueOf(nil),
|
||||
err: errors.New(fmt.Sprint(args...)),
|
||||
}
|
||||
}
|
||||
|
||||
// MakeRPCErrorf :
|
||||
func MakeRPCErrorf(format string, v ...interface{}) RPCReturnType {
|
||||
return rpcReturnTypeImpl{
|
||||
value: reflect.ValueOf(nil),
|
||||
err: fmt.Errorf(format, v...),
|
||||
}
|
||||
}
|
||||
|
||||
// Server :
|
||||
type Server struct {
|
||||
httpserver *http.Server
|
||||
shutdownFlag ShutdownFlag
|
||||
preShutdown func()
|
||||
stopfunc func()
|
||||
}
|
||||
|
||||
var prefixptr = flag.String("prefix", "", "'")
|
||||
var portptr = flag.Int("port", 80, "")
|
||||
var tls = flag.String("tls", "", "")
|
||||
|
||||
func welcomeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("welcome"))
|
||||
// welcome은 accesslog 안 남김
|
||||
// if atomic.LoadInt32(&serveMux.Shutdowning) == 0 {
|
||||
// w.Write([]byte("welcome"))
|
||||
// } else {
|
||||
// w.WriteHeader(http.StatusServiceUnavailable)
|
||||
// }
|
||||
io.Copy(ioutil.Discard, r.Body)
|
||||
r.Body.Close()
|
||||
}
|
||||
|
||||
func NewDependentServer(preShutdown func()) *Server {
|
||||
server := &Server{
|
||||
httpserver: nil,
|
||||
shutdownFlag: ShutdownFlagIdle,
|
||||
preShutdown: preShutdown,
|
||||
}
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
go func() {
|
||||
<-c
|
||||
signal.Stop(c)
|
||||
server.Shutdown(ShutdownFlagTerminating)
|
||||
}()
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
// NewHTTPServer :
|
||||
func NewHTTPServer(serveMux *http.ServeMux, preShutdown func()) *Server {
|
||||
server := NewDependentServer(preShutdown)
|
||||
// 시작시 자동으로 enable됨
|
||||
if len(*tls) > 0 && *portptr == 80 {
|
||||
*portptr = 443
|
||||
}
|
||||
addr := fmt.Sprintf(":%d", *portptr)
|
||||
serveMux.HandleFunc("/welcome", welcomeHandler)
|
||||
|
||||
server.httpserver = &http.Server{Addr: addr, Handler: serveMux}
|
||||
server.httpserver.SetKeepAlivesEnabled(true)
|
||||
return server
|
||||
}
|
||||
|
||||
func (server *Server) ShutdownFlag() ShutdownFlag {
|
||||
return ShutdownFlag(atomic.LoadInt32((*int32)(&server.shutdownFlag)))
|
||||
}
|
||||
|
||||
// Shutdown :
|
||||
func (server *Server) Shutdown(flag ShutdownFlag) {
|
||||
atomic.StoreInt32((*int32)(&server.shutdownFlag), int32(flag))
|
||||
|
||||
if server.preShutdown != nil {
|
||||
server.preShutdown()
|
||||
}
|
||||
|
||||
if server.httpserver != nil {
|
||||
server.httpserver.Shutdown(context.Background())
|
||||
server.httpserver = nil
|
||||
} else if server.stopfunc != nil {
|
||||
server.stopfunc()
|
||||
server.stopfunc = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Start :
|
||||
func (server *Server) Start() {
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
atomic.StoreInt32((*int32)(&server.shutdownFlag), int32(ShutdownFlagRunning))
|
||||
|
||||
if server.httpserver != nil {
|
||||
var err error
|
||||
if len(*tls) > 0 {
|
||||
crtfile := *tls + ".crt"
|
||||
keyfile := *tls + ".key"
|
||||
err = server.httpserver.ListenAndServeTLS(crtfile, keyfile)
|
||||
} else {
|
||||
err = server.httpserver.ListenAndServe()
|
||||
}
|
||||
if err != http.ErrServerClosed {
|
||||
fmt.Println(err)
|
||||
}
|
||||
} else {
|
||||
ctx, stopfunc := context.WithCancel(context.Background())
|
||||
server.stopfunc = stopfunc
|
||||
<-ctx.Done()
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertInterface :
|
||||
func ConvertInterface(from interface{}, toType reflect.Type) reflect.Value {
|
||||
if toType.Kind() == reflect.String {
|
||||
return reflect.ValueOf(from.(string))
|
||||
}
|
||||
|
||||
fromrv := reflect.ValueOf(from)
|
||||
fromtype := reflect.TypeOf(from)
|
||||
|
||||
if toType.Kind() == reflect.Struct {
|
||||
if frommap, ok := from.(map[string]interface{}); ok {
|
||||
out := reflect.New(toType)
|
||||
for k, v := range frommap {
|
||||
if fieldval := out.Elem().FieldByName(k); fieldval.IsValid() {
|
||||
fieldval.Set(ConvertInterface(v, fieldval.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
if fromtype.Kind() == reflect.Slice {
|
||||
if fromtype.Elem() == toType.Elem() {
|
||||
return fromrv
|
||||
}
|
||||
|
||||
convslice := reflect.MakeSlice(toType, 0, fromrv.Len())
|
||||
for i := 0; i < fromrv.Len(); i++ {
|
||||
convslice = reflect.Append(convslice, ConvertInterface(fromrv.Index(i).Interface(), toType.Elem()))
|
||||
}
|
||||
|
||||
return convslice
|
||||
}
|
||||
|
||||
if toType.Kind() == reflect.Bool {
|
||||
val, _ := strconv.ParseBool(from.(string))
|
||||
return reflect.ValueOf(val)
|
||||
}
|
||||
|
||||
return fromrv.Convert(toType)
|
||||
}
|
||||
|
||||
// ErrUnmarshalRequestFailed :
|
||||
var ErrUnmarshalRequestFailed = errors.New("Unmarshal failed at rpc handler")
|
||||
|
||||
// CallMethodInternal :
|
||||
func CallMethodInternal(receiver interface{}, context functionCallContext) (interface{}, error) {
|
||||
methodTokens := strings.Split(context.Method, ".")
|
||||
context.Method = methodTokens[len(methodTokens)-1]
|
||||
for i := 0; i < len(methodTokens)-1; i++ {
|
||||
token := methodTokens[i]
|
||||
if strings.HasSuffix(token, "()") {
|
||||
token = token[:len(token)-2]
|
||||
}
|
||||
|
||||
fn := reflect.ValueOf(receiver).MethodByName(token)
|
||||
// 이 fn은 인자를 받지 않고 하나만 리턴하는 함수여야 한다.
|
||||
fnType := fn.Type()
|
||||
if fnType.NumOut() != 1 || fnType.NumIn() != 0 {
|
||||
return nil, errors.New(fmt.Sprint("method tokens are not correct :", token))
|
||||
}
|
||||
|
||||
returnVals := fn.Call(nil)
|
||||
if returnVals[0].IsNil() {
|
||||
return nil, errors.New(fmt.Sprint("method token returns nil :", token))
|
||||
}
|
||||
|
||||
receiver = returnVals[0].Interface()
|
||||
}
|
||||
|
||||
fn := reflect.ValueOf(receiver).MethodByName(context.Method)
|
||||
if fn.IsValid() {
|
||||
fnType := fn.Type()
|
||||
|
||||
if fnType.NumOut() != 1 {
|
||||
return nil, errors.New(fmt.Sprint("method should return only RPCReturnType :", context.Method))
|
||||
}
|
||||
|
||||
if len(context.Args) != fnType.NumIn() {
|
||||
return nil, errors.New(fmt.Sprint("argument is not matching :", context.Method))
|
||||
}
|
||||
|
||||
var argv []reflect.Value
|
||||
for i := 0; i < len(context.Args); i++ {
|
||||
argv = append(argv, ConvertInterface(context.Args[i], fnType.In(i)))
|
||||
}
|
||||
returnVals := fn.Call(argv)
|
||||
|
||||
return returnVals[0].Interface(), nil
|
||||
}
|
||||
|
||||
return nil, errors.New(fmt.Sprint("method is missing :", receiver, context.Method))
|
||||
}
|
||||
|
||||
// CallMethod :
|
||||
func CallMethod(receiver interface{}, context []byte) (retval RPCReturnType, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r != nil && err == nil {
|
||||
debug.PrintStack()
|
||||
err = errors.New(fmt.Sprintf("%v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
var meta functionCallContext
|
||||
if err := json.Unmarshal(context, &meta); err != nil {
|
||||
fmt.Println(string(context))
|
||||
return nil, ErrUnmarshalRequestFailed
|
||||
} else {
|
||||
fmt.Printf("%+v\n", meta)
|
||||
}
|
||||
|
||||
var v interface{}
|
||||
v, err = CallMethodInternal(receiver, meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// v는 RPCReturnType이어야 한다.
|
||||
if cast, ok := v.(RPCReturnType); ok {
|
||||
if cast.Error() != nil {
|
||||
return nil, cast.Error()
|
||||
}
|
||||
return cast, nil
|
||||
}
|
||||
|
||||
return nil, errors.New(fmt.Sprint("method should return only RPCReturnType", meta))
|
||||
}
|
||||
Reference in New Issue
Block a user