diff --git a/.vscode/launch.json b/.vscode/launch.json index 3bf3fa0..2ca2b1d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,7 +2,20 @@ "version": "0.2.0", "configurations": [ { - "name": "Launch", + "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", @@ -14,5 +27,6 @@ "args": [], "showLog": true } + ] } \ No newline at end of file diff --git a/config.json b/config.json index 325601f..eff8a5a 100644 --- a/config.json +++ b/config.json @@ -1,4 +1,9 @@ { + "server": { + "ip": "localhost", + "port": 18081, + "tls": false + }, "websocket": { "writeTimeout": 0, "readTimeout": 0, @@ -9,8 +14,8 @@ "writeBufferSize": 4096 }, "gRpc": { - "host": "localhost", - "port": 8000, + "ip": "localhost", + "port": 50006, "tls": false, "pool": { "initialCapacity": 30, diff --git a/config.yml b/config.yml deleted file mode 100644 index ef04314..0000000 --- a/config.yml +++ /dev/null @@ -1,13 +0,0 @@ - -websocket: - writeTimeout: 0 - readTimeout: 0 - pongTimeout: 60 - pingTimeout: 10 - maxMessageSize: 1024 - readBufferSize: 4096 - writeBufferSize: 4096 -gRpc: - host: localhost - port: 8000 - tls: false diff --git a/config/config.go b/config/config.go index 5025c5d..7173c4a 100644 --- a/config/config.go +++ b/config/config.go @@ -7,31 +7,37 @@ import ( var _config *Config type Config struct { + Server ServerConfig `json:"server"` Websocket WebsocketConfig `json:"websocket"` GRpc GRpcConfig `json:"gRpc"` } +type ServerConfig struct { + Ip string `required:"true"` + Port uint16 `required:"true"` + Tls bool `default:"false"` +} + type WebsocketConfig struct { - WriteTimeout int8 `default:"0"` - ReadTimeout int8 `default:"0"` - PongTimeout int8 `default:"60"` - PingTimeout int8 `default:"10"` - MaxMessageSize int64 `default:"1024"` - ReadBufferSize int `default:"4096"` - WriteBufferSize int `default:"4096"` + WriteTimeout uint8 `default:"0"` + ReadTimeout uint8 `default:"0"` + PongTimeout uint8 `default:"60"` + PingTimeout uint8 `default:"10"` + MaxMessageSize int64 `default:"1024"` + ReadBufferSize int `default:"4096"` + WriteBufferSize int `default:"4096"` + UseBinaryMessage bool `required:"false" default:"false"` } type GRpcConfig struct { - Host string `required:"true"` - Port int16 `required:"true"` - Tls bool `default:"false"` + ServerConfig Pool PoolConfig `json:"pool"` } type PoolConfig struct { - InitialCapacity int `defalut:"1"` - MaxCapacity int `defalut:"1"` - IncreaseCapacity int `defalut:"1"` + InitialCapacity uint8 `defalut:"1"` + MaxCapacity uint8 `defalut:"1"` + IncreaseCapacity uint8 `defalut:"1"` } func InitConfig(path string) error { diff --git a/config/config_test.go b/config/config_test.go index fd8bed5..e730423 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -18,5 +18,5 @@ func TestLoadConfig(t *testing.T) { } c := GetConfig() - log.Println(c.Websocket.MaxMessageSize) + log.Println(c.Server.Ip) } diff --git a/debug b/debug new file mode 100755 index 0000000..93a19d8 Binary files /dev/null and b/debug differ diff --git a/glide.yaml b/glide.yaml index caaefec..ee871b8 100644 --- a/glide.yaml +++ b/glide.yaml @@ -19,3 +19,5 @@ import: - package: gopkg.in/yaml.v2 - package: github.com/BurntSushi/toml version: ^0.3.0 +- package: github.com/valyala/fasthttp + version: ^20160617.0.0 diff --git a/main.go b/main.go index 1c1bb02..4b9fe20 100644 --- a/main.go +++ b/main.go @@ -1,28 +1,69 @@ package main import ( + "fmt" + "log" "net/http" + "os" + "path/filepath" + "git.loafle.net/overflow/overflow_service_websocket/config" "git.loafle.net/overflow/overflow_service_websocket/protocol/jsonrpc" "git.loafle.net/overflow/overflow_service_websocket/server" ) -var ( - host = "localhost:8080" +const ( + version = "1.0.0" + website = "https://www.overflow.cloud" + banner = ` + + ██████╗ ██╗ ██╗███████╗██████╗ ███████╗██╗ ██████╗ ██╗ ██╗ +██╔═══██╗██║ ██║██╔════╝██╔══██╗██╔════╝██║ ██╔═══██╗██║ ██║ +██║ ██║██║ ██║█████╗ ██████╔╝█████╗ ██║ ██║ ██║██║ █╗ ██║ +██║ ██║╚██╗ ██╔╝██╔══╝ ██╔══██╗██╔══╝ ██║ ██║ ██║██║███╗██║ +╚██████╔╝ ╚████╔╝ ███████╗██║ ██║██║ ███████╗╚██████╔╝╚███╔███╔╝ + ╚═════╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝ + +` ) func main() { - ws := server.New(server.Options{}) + + // Initialize config + config := loadConfig() + + addr := fmt.Sprintf("%s:%d", config.Server.Ip, config.Server.Port) + useTLS := config.Server.Tls + sConfig := server.NewOptions(&config.Websocket) + // Print banner + log.Println(banner) + + // Config TLS + + ws := server.New(sConfig) ws.RegistProtocol("jsonrpc", jsonrpc.NewHandler()) ws.OnConnection(func(c server.Client) { - c.OnDisconnect(func(Client) { - + log.Println("Client have been connected") + c.OnDisconnect(func(c server.Client) { + log.Println("Client have been disconnected") }) }) - http.HandleFunc("/ws", ws.HTTPHandler()) + http.Handle("/ws", ws.HTTPHandler()) + log.Printf("Address: %s, UseTLS: %t", addr, useTLS) - // ws.OnConnection(handleWebsocketConnection) + http.ListenAndServe(addr, nil) - http.ListenAndServe(host, nil) +} + +func loadConfig() *config.Config { + os.Chdir("./") + wd, _ := os.Getwd() + path := filepath.Join(wd, "config.json") + + err := config.InitConfig(path) + if nil != err { + log.Fatalln("Config error") + } + return config.GetConfig() } diff --git a/pool/grpc/service.go b/pool/grpc/service.go index e33b58c..336970c 100644 --- a/pool/grpc/service.go +++ b/pool/grpc/service.go @@ -142,6 +142,8 @@ func (p *grpcPool) create() (interface{}, error) { return nil, err } + log.Println("Client of GRPC is created") + return c, nil } diff --git a/protocol/hadler.go b/protocol/hadler.go index 3a2f4aa..ac516b2 100644 --- a/protocol/hadler.go +++ b/protocol/hadler.go @@ -5,12 +5,12 @@ import "errors" type ErrorCode int const ( - E_PARSE ErrorCode = -32700 - E_INVALID_REQ ErrorCode = -32600 - E_NO_METHOD ErrorCode = -32601 - E_BAD_PARAMS ErrorCode = -32602 - E_INTERNAL ErrorCode = -32603 - E_SERVER ErrorCode = -32000 + E_PARSE ErrorCode = -32700 + E_INVALID_REQ ErrorCode = -32600 + E_NOT_FOUND_METHOD ErrorCode = -32601 + E_INVALID_PARAMS ErrorCode = -32602 + E_INTERNAL ErrorCode = -32603 + E_SERVER ErrorCode = -32000 ) var ErrNullResult = errors.New("result is null") diff --git a/protocol/jsonrpc/rpc.go b/protocol/jsonrpc/rpc.go index 4949d9d..68742e3 100644 --- a/protocol/jsonrpc/rpc.go +++ b/protocol/jsonrpc/rpc.go @@ -2,6 +2,7 @@ package jsonrpc import ( "encoding/json" + "fmt" "log" "strings" @@ -9,15 +10,12 @@ import ( "google.golang.org/grpc" serverGrpc "git.loafle.net/overflow/overflow_api_server/golang" + "git.loafle.net/overflow/overflow_service_websocket/config" "git.loafle.net/overflow/overflow_service_websocket/pool" grpcPool "git.loafle.net/overflow/overflow_service_websocket/pool/grpc" "git.loafle.net/overflow/overflow_service_websocket/protocol" ) -var ( - host = "localhost:8080" -) - type Request struct { Method string `json:"method"` Params []string `json:"params,omitempty"` @@ -29,12 +27,16 @@ type Response struct { // NewHandler returns a new JSON RPC handler. func NewHandler() protocol.Handler { - servicePool, err := grpcPool.New(10, 30, + c := config.GetConfig().GRpc + + addr := fmt.Sprintf("%s:%d", c.Ip, c.Port) + + servicePool, err := grpcPool.New(1, 5, func(conn *grpc.ClientConn) (interface{}, error) { return serverGrpc.NewOverflowApiServerClient(conn), nil }, func() (*grpc.ClientConn, error) { - return grpc.Dial(host, grpc.WithInsecure()) + return grpc.Dial(addr, grpc.WithInsecure()) }, ) if nil != err { @@ -81,7 +83,6 @@ func (h *handler) Handle(data []byte) ([]byte, *protocol.Error) { } res := &Response{ - ID: req.ID, Result: out.Result, } diff --git a/server/options.go b/server/options.go index 1468c09..8dd8723 100644 --- a/server/options.go +++ b/server/options.go @@ -4,6 +4,7 @@ import ( "net/http" "time" + "git.loafle.net/overflow/overflow_service_websocket/config" uuid "github.com/satori/go.uuid" ) @@ -31,22 +32,6 @@ var ( DefaultIDGenerator = func(*http.Request) string { return uuid.NewV4().String() } ) -type ( - // OptionSetter sets a configuration field to the websocket config - // used to help developers to write less and configure only what they really want and nothing else - OptionSetter interface { - Set(o *Options) - } - - // OptionSet implements the OptionSetter - OptionSet func(o *Options) -) - -// Set is the func which makes the OptionSet an OptionSetter, this is used mostly -func (os OptionSet) Set(o *Options) { - os(o) -} - // Options is configuration of the websocket server type Options struct { OnError func(res http.ResponseWriter, req *http.Request, status int, reason error) @@ -63,6 +48,24 @@ type Options struct { IDGenerator func(*http.Request) string } +func NewOptions(c *config.WebsocketConfig) *Options { + o := &Options{ + WriteTimeout: time.Duration(c.WriteTimeout) * time.Second, + ReadTimeout: time.Duration(c.ReadTimeout) * time.Second, + PongTimeout: time.Duration(c.PongTimeout) * time.Second, + PingTimeout: time.Duration(c.PingTimeout) * time.Second, + MaxMessageSize: c.MaxMessageSize, + BinaryMessage: c.UseBinaryMessage, + ReadBufferSize: c.ReadBufferSize, + WriteBufferSize: c.WriteBufferSize, + } + o.Validate() + o.PongTimeout = o.PongTimeout * time.Second + o.PingTimeout = o.PingTimeout * time.Second + o.PingPeriod = (o.PingTimeout * 9) / 10 + return o +} + // Set is the func which makes the OptionSet an OptionSetter, this is used mostly func (o *Options) Set(main *Options) { main.OnError = o.OnError @@ -79,103 +82,6 @@ func (o *Options) Set(main *Options) { main.IDGenerator = o.IDGenerator } -// OnError sets the error handler -func OnError(val func(res http.ResponseWriter, req *http.Request, status int, reason error)) OptionSet { - return func(o *Options) { - o.OnError = val - } -} - -// OnCheckOrigin sets a handler which will check if different origin(domains) are allowed to contact with -// the websocket server -func OnCheckOrigin(val func(req *http.Request) bool) OptionSet { - return func(o *Options) { - o.OnCheckOrigin = val - } -} - -// WriteTimeout time allowed to write a message to the connection. -// Default value is 15 * time.Second -func WriteTimeout(val time.Duration) OptionSet { - return func(o *Options) { - o.WriteTimeout = val - } -} - -// ReadTimeout time allowed to read a message from the connection. -// Default value is 15 * time.Second -func ReadTimeout(val time.Duration) OptionSet { - return func(o *Options) { - o.ReadTimeout = val - } -} - -// PongTimeout allowed to read the next pong message from the connection -// Default value is 60 * time.Second -func PongTimeout(val time.Duration) OptionSet { - return func(o *Options) { - o.PongTimeout = val - } -} - -// PingTimeout allowed to send the ping message to the connection -// Default value is 10 * time.Second -func PingTimeout(val time.Duration) OptionSet { - return func(o *Options) { - o.PingTimeout = val - } -} - -// PingPeriod send ping messages to the connection with this period. Must be less than PongTimeout -// Default value is (PongTimeout * 9) / 10 -func PingPeriod(val time.Duration) OptionSet { - return func(o *Options) { - o.PingPeriod = val - } -} - -// MaxMessageSize max message size allowed from connection -// Default value is 1024 -func MaxMessageSize(val int64) OptionSet { - return func(o *Options) { - o.MaxMessageSize = val - } -} - -// BinaryMessage set it to true in order to denotes binary data messages instead of utf-8 text -// compatible if you wanna use the Connection's EmitMessage to send a custom binary data to the client, -// like a native server-client communication. -// defaults to false -func BinaryMessage(val bool) OptionSet { - return func(o *Options) { - o.BinaryMessage = val - } -} - -// ReadBufferSize is the buffer size for the underline reader -func ReadBufferSize(val int) OptionSet { - return func(o *Options) { - o.ReadBufferSize = val - } -} - -// WriteBufferSize is the buffer size for the underline writer -func WriteBufferSize(val int) OptionSet { - return func(o *Options) { - o.WriteBufferSize = val - } -} - -// IDGenerator used to create (and later on, set) -// an ID for each incoming websocket connections (clients). -// The request is an argument which you can use to generate the ID (from headers for example). -// If empty then the ID is generated by func: uuid.NewV4().String() -func IDGenerator(val func(*http.Request) string) OptionSet { - return func(o *Options) { - o.IDGenerator = val - } -} - // Validate validates the configuration func (o *Options) Validate() { diff --git a/server/protocol.go b/server/protocol.go index 66b16fe..b6fead6 100644 --- a/server/protocol.go +++ b/server/protocol.go @@ -6,12 +6,14 @@ import ( "git.loafle.net/overflow/overflow_service_websocket/protocol" ) +// Request is protocol which Websocket Request type Request struct { Protocol string `json:"protocol"` ID *json.RawMessage `json:"id"` Body *json.RawMessage `json:"body"` } +// Response is protocol which Websocket Response type Response struct { Protocol string `json:"protocol"` ID *json.RawMessage `json:"id"` diff --git a/server/server.go b/server/server.go index 4f7c75e..5215afa 100644 --- a/server/server.go +++ b/server/server.go @@ -19,7 +19,6 @@ type ( // Server is the websocket server, // listens on the config's port, the critical part is the event OnConnection type Server interface { - SetOptions(...OptionSetter) GetOptions() *Options RegistProtocol(protocol string, h protocol.Handler) ProtocolHandler(protocol string) protocol.Handler @@ -41,17 +40,17 @@ type server struct { var _ Server = &server{} -var defaultServer = newServer() +var defaultServer = newServer(nil) // server implementation // New creates a websocket server and returns it -func New(setters ...OptionSetter) Server { - return newServer(setters...) +func New(o *Options) Server { + return newServer(o) } // newServer creates a websocket server and returns it -func newServer(setters ...OptionSetter) Server { +func newServer(o *Options) Server { s := &server{ clients: make(map[string]Client, 100), @@ -59,7 +58,13 @@ func newServer(setters ...OptionSetter) Server { protocols: make(map[string]protocol.Handler, 0), } - s.SetOptions(setters...) + if nil == o { + o = &Options{} + } + + o.Validate() + + s.options = o return s } @@ -79,19 +84,6 @@ func (s *server) ProtocolHandler(protocol string) protocol.Handler { return s.protocols[protocol] } -// SetOptions is function that set option values -func SetOptions(setters ...OptionSetter) { - defaultServer.SetOptions(setters...) -} - -func (s *server) SetOptions(setters ...OptionSetter) { - for _, setter := range setters { - setter.Set(s.options) - } - - s.options.Validate() -} - // GetOptions is function that get option values func GetOptions() *Options { return defaultServer.GetOptions()