commit 02b77bb617983c146ff119a228f9f8ae6332e6ed Author: crusader Date: Thu Nov 9 18:14:57 2017 +0900 ing diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3733e36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Go template +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ +.idea/ +*.iml + +vendor/ +glide.lock +.DS_Store +dist/ +debug diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2ca2b1d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${workspaceRoot}/main.go", + "env": {}, + "args": [], + "showLog": true + }, + { + "name": "File Debug", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${fileDirname}", + "env": {}, + "args": [], + "showLog": true + } + + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..20af2f6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +// Place your settings in this file to overwrite default and user settings. +{ +} \ No newline at end of file diff --git a/conf/conf.go b/conf/conf.go new file mode 100644 index 0000000..0a56bb9 --- /dev/null +++ b/conf/conf.go @@ -0,0 +1,16 @@ +package conf + +import ( + oogc "git.loafle.net/overflow/overflow_gateway_config" +) + +var Config Conf + +type Conf struct { + Server oogc.Server `json:"server" yaml:"server" toml:"server"` + Auth oogc.Auth `json:"auth" yaml:"auth" toml:"auth"` + Websocket oogc.Websocket `json:"websocket" yaml:"websocket" toml:"websocket"` + GRPC oogc.GRPC `json:"gRPC" yaml:"gRPC" toml:"gRPC"` + Redis oogc.Redis `json:"redis" yaml:"redis" toml:"redis"` + Handlers map[string]oogc.Handler `json:"handlers" yaml:"handlers" toml:"handlers"` +} diff --git a/config.json b/config.json new file mode 100644 index 0000000..f81b548 --- /dev/null +++ b/config.json @@ -0,0 +1,63 @@ +{ + "server": { + "addr": ":19090", + "tls": false + }, + "auth": { + "signingKey": "tWB0lUXiCwX4U3qsJZcZ10mKvEH793RHkTJDbDuZVshQTk4uNB6ck59UQ96lhsRi4XNUiEnlIbP8XYQMPabeNtERX3iyHeDcwocgUVAor1nkAajYeq1gNyJszGpMhEOT" + }, + "websocket": { + "handshakeTimeout": 0, + "readBufferSize": 8192, + "writeBufferSize": 8192, + "enableCompression": false + }, + "gRPC": { + "addr": "127.0.0.1:50006", + "tls": false, + "pool": { + "maxIdle": 1, + "maxCapacity": 3, + "idleTimeout": 240, + "wait": false + } + }, + "redis": { + "network": "tcp", + "addr": "192.168.1.50:6379", + "tls": false, + "pool": { + "maxIdle": 1, + "maxCapacity": 3, + "idleTimeout": 240, + "wait": false + } + }, + "handlers": { + "web": { + "entry": "/web", + "socket": { + "maxMessageSize": 8192, + "writeTimeout": 0, + "readTimeout": 0, + "pongTimeout": 60, + "pingTimeout": 10, + "pingPeriod": 10, + "binaryMessage": false + } + }, + "file": { + "entry": "/file", + "socket": { + "maxMessageSize": 8192, + "writeTimeout": 0, + "readTimeout": 0, + "pongTimeout": 60, + "pingTimeout": 10, + "pingPeriod": 10, + "binaryMessage": false + } + } + + } +} diff --git a/external/external.go b/external/external.go new file mode 100644 index 0000000..1851a02 --- /dev/null +++ b/external/external.go @@ -0,0 +1,16 @@ +package external + +import ( + "git.loafle.net/overflow/overflow_gateway_app/external/grpc" + "git.loafle.net/overflow/overflow_gateway_app/external/redis" +) + +func ExternalInit() { + grpc.ExternalInit() + redis.ExternalInit() +} + +func ExternalDestroy() { + grpc.ExternalDestroy() + redis.ExternalDestroy() +} diff --git a/external/grpc/client.go b/external/grpc/client.go new file mode 100644 index 0000000..d6f6b78 --- /dev/null +++ b/external/grpc/client.go @@ -0,0 +1,35 @@ +package grpc + +import ( + "context" + "fmt" + "strings" + + ooas "git.loafle.net/overflow/overflow_api_server/golang" +) + +func Exec(ctx context.Context, method string, params []string) (string, error) { + if nil == grpcPool { + return "", fmt.Errorf("App: GRPC Pool is not initialized") + } + + var client interface{} + var err error + if client, err = grpcPool.Get(); nil != err { + return "", err + } + defer grpcPool.Put(client) + + sm := strings.Split(method, ".") + si := &ooas.ServerInput{ + Target: sm[0], + Method: sm[1], + Params: params, + } + var so *ooas.ServerOutput + if so, err = client.(ooas.OverflowApiServerClient).Exec(ctx, si); nil != err { + return "", err + } + + return so.Result, nil +} diff --git a/external/grpc/pool.go b/external/grpc/pool.go new file mode 100644 index 0000000..d39caf9 --- /dev/null +++ b/external/grpc/pool.go @@ -0,0 +1,49 @@ +package grpc + +import ( + "fmt" + + cgp "git.loafle.net/commons_go/grpc_pool" + "git.loafle.net/commons_go/logging" + ooas "git.loafle.net/overflow/overflow_api_server/golang" + "git.loafle.net/overflow/overflow_gateway_app/conf" + "google.golang.org/grpc" +) + +var grpcPool cgp.Pool + +func ExternalInit() { + ph := &grpcPoolHandlers{} + ph.MaxCapacity = conf.Config.GRPC.Pool.MaxCapacity + ph.MaxIdle = conf.Config.GRPC.Pool.MaxIdle + ph.IdleTimeout = conf.Config.GRPC.Pool.IdleTimeout + ph.Wait = conf.Config.GRPC.Pool.Wait + + grpcPool = cgp.New(ph) + + if err := grpcPool.Start(); nil != err { + logging.Logger().Panic(fmt.Sprintf("App: %v", err)) + return + } +} + +func ExternalDestroy() { + if nil != grpcPool { + grpcPool.Stop() + } +} + +type grpcPoolHandlers struct { + cgp.PoolHandlers +} + +func (h *grpcPoolHandlers) Dial() (*grpc.ClientConn, interface{}, error) { + var err error + conn, err := grpc.Dial(conf.Config.GRPC.Addr, grpc.WithInsecure()) + if nil != err { + return nil, nil, err + } + c := ooas.NewOverflowApiServerClient(conn) + + return conn, c, nil +} diff --git a/external/redis/pool.go b/external/redis/pool.go new file mode 100644 index 0000000..cc3cf48 --- /dev/null +++ b/external/redis/pool.go @@ -0,0 +1,42 @@ +package redis + +import ( + "fmt" + + "git.loafle.net/overflow/overflow_gateway_app/conf" + + "git.loafle.net/commons_go/logging" + crp "git.loafle.net/commons_go/redis_pool" + "github.com/garyburd/redigo/redis" +) + +var RedisPool crp.Pool + +func ExternalInit() { + ph := &redisPoolHandlers{} + ph.MaxCapacity = conf.Config.Redis.Pool.MaxCapacity + ph.MaxIdle = conf.Config.Redis.Pool.MaxIdle + ph.IdleTimeout = conf.Config.Redis.Pool.IdleTimeout + ph.Wait = conf.Config.Redis.Pool.Wait + + RedisPool = crp.New(ph) + + if err := RedisPool.Start(); nil != err { + logging.Logger().Panic(fmt.Sprintf("App: %v", err)) + return + } +} + +func ExternalDestroy() { + if nil != RedisPool { + RedisPool.Stop() + } +} + +type redisPoolHandlers struct { + crp.PoolHandlers +} + +func (ph *redisPoolHandlers) Dial() (redis.Conn, error) { + return redis.Dial(conf.Config.Redis.Network, conf.Config.Redis.Addr) +} diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..316f253 --- /dev/null +++ b/glide.yaml @@ -0,0 +1,18 @@ +package: git.loafle.net/overflow/overflow_gateway_app +import: +- package: git.loafle.net/commons_go/websocket_fasthttp +- package: git.loafle.net/commons_go/rpc +- package: git.loafle.net/commons_go/config +- package: git.loafle.net/overflow/overflow_gateway_config +- package: git.loafle.net/commons_go/logging +- package: git.loafle.net/commons_go/grpc_pool +- package: git.loafle.net/overflow/overflow_api_server + subpackages: + - golang +- package: git.loafle.net/commons_go/redis_pool +- package: github.com/garyburd/redigo + version: v1.3.0 + subpackages: + - redis +- package: google.golang.org/grpc + version: v1.7.2 diff --git a/main.go b/main.go new file mode 100644 index 0000000..591610c --- /dev/null +++ b/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "os/signal" + "syscall" + + "git.loafle.net/commons_go/config" + "git.loafle.net/commons_go/logging" + "git.loafle.net/overflow/overflow_gateway_app/conf" + "git.loafle.net/overflow/overflow_gateway_app/external" + "git.loafle.net/overflow/overflow_gateway_app/server" +) + +func init() { + configPath := flag.String("config", "./", "The path of config file") + + flag.Parse() + + config.SetConfigPath(*configPath) + if err := config.Load(&conf.Config, "config.json"); nil != err { + panic(err) + } +} + +func main() { + defer logging.Logger().Sync() + + external.ExternalInit() + + s := server.New() + + stop := make(chan os.Signal) + signal.Notify(stop, syscall.SIGINT) + + go func() { + if err := s.Start(); nil != err { + log.Printf("Server: Start error %v", err) + return + } + }() + + select { + case signal := <-stop: + fmt.Printf("Got signal: %v\n", signal) + } + + external.ExternalDestroy() + + s.Stop() + +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..8980ef2 --- /dev/null +++ b/server/server.go @@ -0,0 +1,17 @@ +package server + +import ( + cwf "git.loafle.net/commons_go/websocket_fasthttp" + "git.loafle.net/overflow/overflow_gateway_app/servlet/web" +) + +func New() cwf.Server { + sh := newServerHandler() + wsh := web.NewSocketHandler() + + sh.RegisterSocketHandler("/web", wsh) + + s := cwf.New(sh) + + return s +} diff --git a/server/server_handlers.go b/server/server_handlers.go new file mode 100644 index 0000000..caeaa4f --- /dev/null +++ b/server/server_handlers.go @@ -0,0 +1,45 @@ +package server + +import ( + "net" + + cwf "git.loafle.net/commons_go/websocket_fasthttp" + "github.com/valyala/fasthttp" +) + +func newServerHandler() cwf.ServerHandler { + sh := &ServerHandlers{} + + return sh +} + +type ServerHandlers struct { + cwf.ServerHandlers +} + +func (sh *ServerHandlers) Listen() (net.Listener, error) { + return net.Listen("tcp4", ":19090") +} + +func (sh *ServerHandlers) CheckOrigin(ctx *fasthttp.RequestCtx) bool { + if origin := string(ctx.Request.Header.Peek("Origin")); origin != "" { + ctx.Response.Header.Set("Access-Control-Allow-Origin", origin) + if string(ctx.Method()) == "OPTIONS" && string(ctx.Request.Header.Peek("Access-Control-Request-Method")) != "" { + ctx.Response.Header.Set("Access-Control-Allow-Headers", "Content-Type, Accept") + ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE") + } + } + return true +} + +func (sh *ServerHandlers) OnError(ctx *fasthttp.RequestCtx, status int, reason error) { + // no op +} + +func (sh *ServerHandlers) OnStart() { + // no op +} + +func (sh *ServerHandlers) OnStop() { + // no op +} diff --git a/servlet/web/rpc_server_handlers.go b/servlet/web/rpc_server_handlers.go new file mode 100644 index 0000000..c681ac8 --- /dev/null +++ b/servlet/web/rpc_server_handlers.go @@ -0,0 +1,45 @@ +package web + +import ( + "context" + + "git.loafle.net/commons_go/rpc/gateway" + "git.loafle.net/commons_go/rpc/protocol/json" + cwf "git.loafle.net/commons_go/websocket_fasthttp" + "git.loafle.net/overflow/overflow_gateway_app/external/grpc" + "google.golang.org/grpc/metadata" +) + +func newRCPServerHandler() gateway.ServerHandler { + sh := &RPCServerHandlers{} + sh.RegisterCodec(json.NewServerCodec(), "json") + return sh +} + +type RPCServerHandlers struct { + gateway.ServerHandlers +} + +func (rpcSH *RPCServerHandlers) Invoke(ctx context.Context, method string, params []string) (result interface{}, err error) { + + soc := ctx.Value("socket").(*cwf.Socket) + + md := metadata.Pairs("email", soc.ID().(string)) + grpcCTX := metadata.NewOutgoingContext(context.Background(), md) + + r, err := grpc.Exec(grpcCTX, method, params) + if err != nil { + return nil, err + } + + return r, nil +} + +func (rpcSH *RPCServerHandlers) Validate() { + rpcSH.ServerHandlers.Validate() +} + +type Result struct { + Name string `json:"name,omitempty"` + Count int `json:"count,omitempty"` +} diff --git a/servlet/web/socket_handlers.go b/servlet/web/socket_handlers.go new file mode 100644 index 0000000..b192046 --- /dev/null +++ b/servlet/web/socket_handlers.go @@ -0,0 +1,84 @@ +package web + +import ( + "context" + "log" + + "git.loafle.net/commons_go/rpc/gateway" + "git.loafle.net/commons_go/server" + cwf "git.loafle.net/commons_go/websocket_fasthttp" + "github.com/valyala/fasthttp" +) + +func NewSocketHandler() cwf.SocketHandler { + sh := &SocketHandlers{} + sh.rpcServerHandler = newRCPServerHandler() + + return sh +} + +type SocketHandlers struct { + cwf.SocketHandlers + + rpcServerHandler gateway.ServerHandler +} + +func (sh *SocketHandlers) Handshake(ctx *fasthttp.RequestCtx) (interface{}, *fasthttp.ResponseHeader) { + return "overflow@loafle.com", nil +} + +func (sh *SocketHandlers) Handle(soc *cwf.Socket, stopChan <-chan struct{}, doneChan chan<- struct{}) { + contentType := "json" + codec, err := sh.rpcServerHandler.GetCodec(contentType) + if nil != err { + log.Printf("RPC Handle: %v", err) + doneChan <- struct{}{} + return + } + + var socConn *cwf.SocketConn + baseCtx := context.Background() + // conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + + for { + if socConn, err = soc.WaitRequest(); nil != err { + doneChan <- struct{}{} + return + } + + // // "git.loafle.net/commons_go/websocket_fasthttp/websocket" + // switch socConn.MessageType { + // case websocket.TextMessage: + // case websocket.BinaryMessage: + // } + ctx := context.WithValue(baseCtx, "socket", soc) + + if err = gateway.Handle(ctx, sh.rpcServerHandler, codec, socConn, socConn); nil != err { + if server.IsClientDisconnect(err) { + doneChan <- struct{}{} + return + } + log.Printf("RPC: %v", err) + } + + if err = socConn.Close(); nil != err { + doneChan <- struct{}{} + return + } + + select { + case <-stopChan: + return + default: + } + } + +} + +func (sh *SocketHandlers) Validate() { + sh.SocketHandlers.Validate() + + if nil == sh.rpcServerHandler { + panic("RPCServerHandler must be specified.") + } +}