This commit is contained in:
crusader 2017-07-14 20:18:07 +09:00
parent 97f3e920f1
commit 0d428d20c0
14 changed files with 141 additions and 183 deletions

16
.vscode/launch.json vendored
View File

@ -2,7 +2,20 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "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", "type": "go",
"request": "launch", "request": "launch",
"mode": "debug", "mode": "debug",
@ -14,5 +27,6 @@
"args": [], "args": [],
"showLog": true "showLog": true
} }
] ]
} }

View File

@ -1,4 +1,9 @@
{ {
"server": {
"ip": "localhost",
"port": 18081,
"tls": false
},
"websocket": { "websocket": {
"writeTimeout": 0, "writeTimeout": 0,
"readTimeout": 0, "readTimeout": 0,
@ -9,8 +14,8 @@
"writeBufferSize": 4096 "writeBufferSize": 4096
}, },
"gRpc": { "gRpc": {
"host": "localhost", "ip": "localhost",
"port": 8000, "port": 50006,
"tls": false, "tls": false,
"pool": { "pool": {
"initialCapacity": 30, "initialCapacity": 30,

View File

@ -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

View File

@ -7,31 +7,37 @@ import (
var _config *Config var _config *Config
type Config struct { type Config struct {
Server ServerConfig `json:"server"`
Websocket WebsocketConfig `json:"websocket"` Websocket WebsocketConfig `json:"websocket"`
GRpc GRpcConfig `json:"gRpc"` GRpc GRpcConfig `json:"gRpc"`
} }
type ServerConfig struct {
Ip string `required:"true"`
Port uint16 `required:"true"`
Tls bool `default:"false"`
}
type WebsocketConfig struct { type WebsocketConfig struct {
WriteTimeout int8 `default:"0"` WriteTimeout uint8 `default:"0"`
ReadTimeout int8 `default:"0"` ReadTimeout uint8 `default:"0"`
PongTimeout int8 `default:"60"` PongTimeout uint8 `default:"60"`
PingTimeout int8 `default:"10"` PingTimeout uint8 `default:"10"`
MaxMessageSize int64 `default:"1024"` MaxMessageSize int64 `default:"1024"`
ReadBufferSize int `default:"4096"` ReadBufferSize int `default:"4096"`
WriteBufferSize int `default:"4096"` WriteBufferSize int `default:"4096"`
UseBinaryMessage bool `required:"false" default:"false"`
} }
type GRpcConfig struct { type GRpcConfig struct {
Host string `required:"true"` ServerConfig
Port int16 `required:"true"`
Tls bool `default:"false"`
Pool PoolConfig `json:"pool"` Pool PoolConfig `json:"pool"`
} }
type PoolConfig struct { type PoolConfig struct {
InitialCapacity int `defalut:"1"` InitialCapacity uint8 `defalut:"1"`
MaxCapacity int `defalut:"1"` MaxCapacity uint8 `defalut:"1"`
IncreaseCapacity int `defalut:"1"` IncreaseCapacity uint8 `defalut:"1"`
} }
func InitConfig(path string) error { func InitConfig(path string) error {

View File

@ -18,5 +18,5 @@ func TestLoadConfig(t *testing.T) {
} }
c := GetConfig() c := GetConfig()
log.Println(c.Websocket.MaxMessageSize) log.Println(c.Server.Ip)
} }

BIN
debug Executable file

Binary file not shown.

View File

@ -19,3 +19,5 @@ import:
- package: gopkg.in/yaml.v2 - package: gopkg.in/yaml.v2
- package: github.com/BurntSushi/toml - package: github.com/BurntSushi/toml
version: ^0.3.0 version: ^0.3.0
- package: github.com/valyala/fasthttp
version: ^20160617.0.0

57
main.go
View File

@ -1,28 +1,69 @@
package main package main
import ( import (
"fmt"
"log"
"net/http" "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/protocol/jsonrpc"
"git.loafle.net/overflow/overflow_service_websocket/server" "git.loafle.net/overflow/overflow_service_websocket/server"
) )
var ( const (
host = "localhost:8080" version = "1.0.0"
website = "https://www.overflow.cloud"
banner = `
`
) )
func main() { 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.RegistProtocol("jsonrpc", jsonrpc.NewHandler())
ws.OnConnection(func(c server.Client) { 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()
} }

View File

@ -142,6 +142,8 @@ func (p *grpcPool) create() (interface{}, error) {
return nil, err return nil, err
} }
log.Println("Client of GRPC is created")
return c, nil return c, nil
} }

View File

@ -7,8 +7,8 @@ type ErrorCode int
const ( const (
E_PARSE ErrorCode = -32700 E_PARSE ErrorCode = -32700
E_INVALID_REQ ErrorCode = -32600 E_INVALID_REQ ErrorCode = -32600
E_NO_METHOD ErrorCode = -32601 E_NOT_FOUND_METHOD ErrorCode = -32601
E_BAD_PARAMS ErrorCode = -32602 E_INVALID_PARAMS ErrorCode = -32602
E_INTERNAL ErrorCode = -32603 E_INTERNAL ErrorCode = -32603
E_SERVER ErrorCode = -32000 E_SERVER ErrorCode = -32000
) )

View File

@ -2,6 +2,7 @@ package jsonrpc
import ( import (
"encoding/json" "encoding/json"
"fmt"
"log" "log"
"strings" "strings"
@ -9,15 +10,12 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
serverGrpc "git.loafle.net/overflow/overflow_api_server/golang" 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" "git.loafle.net/overflow/overflow_service_websocket/pool"
grpcPool "git.loafle.net/overflow/overflow_service_websocket/pool/grpc" grpcPool "git.loafle.net/overflow/overflow_service_websocket/pool/grpc"
"git.loafle.net/overflow/overflow_service_websocket/protocol" "git.loafle.net/overflow/overflow_service_websocket/protocol"
) )
var (
host = "localhost:8080"
)
type Request struct { type Request struct {
Method string `json:"method"` Method string `json:"method"`
Params []string `json:"params,omitempty"` Params []string `json:"params,omitempty"`
@ -29,12 +27,16 @@ type Response struct {
// NewHandler returns a new JSON RPC handler. // NewHandler returns a new JSON RPC handler.
func NewHandler() protocol.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) { func(conn *grpc.ClientConn) (interface{}, error) {
return serverGrpc.NewOverflowApiServerClient(conn), nil return serverGrpc.NewOverflowApiServerClient(conn), nil
}, },
func() (*grpc.ClientConn, error) { func() (*grpc.ClientConn, error) {
return grpc.Dial(host, grpc.WithInsecure()) return grpc.Dial(addr, grpc.WithInsecure())
}, },
) )
if nil != err { if nil != err {
@ -81,7 +83,6 @@ func (h *handler) Handle(data []byte) ([]byte, *protocol.Error) {
} }
res := &Response{ res := &Response{
ID: req.ID,
Result: out.Result, Result: out.Result,
} }

View File

@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"time" "time"
"git.loafle.net/overflow/overflow_service_websocket/config"
uuid "github.com/satori/go.uuid" uuid "github.com/satori/go.uuid"
) )
@ -31,22 +32,6 @@ var (
DefaultIDGenerator = func(*http.Request) string { return uuid.NewV4().String() } 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 // Options is configuration of the websocket server
type Options struct { type Options struct {
OnError func(res http.ResponseWriter, req *http.Request, status int, reason error) OnError func(res http.ResponseWriter, req *http.Request, status int, reason error)
@ -63,6 +48,24 @@ type Options struct {
IDGenerator func(*http.Request) string 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 // Set is the func which makes the OptionSet an OptionSetter, this is used mostly
func (o *Options) Set(main *Options) { func (o *Options) Set(main *Options) {
main.OnError = o.OnError main.OnError = o.OnError
@ -79,103 +82,6 @@ func (o *Options) Set(main *Options) {
main.IDGenerator = o.IDGenerator 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 // Validate validates the configuration
func (o *Options) Validate() { func (o *Options) Validate() {

View File

@ -6,12 +6,14 @@ import (
"git.loafle.net/overflow/overflow_service_websocket/protocol" "git.loafle.net/overflow/overflow_service_websocket/protocol"
) )
// Request is protocol which Websocket Request
type Request struct { type Request struct {
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
ID *json.RawMessage `json:"id"` ID *json.RawMessage `json:"id"`
Body *json.RawMessage `json:"body"` Body *json.RawMessage `json:"body"`
} }
// Response is protocol which Websocket Response
type Response struct { type Response struct {
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
ID *json.RawMessage `json:"id"` ID *json.RawMessage `json:"id"`

View File

@ -19,7 +19,6 @@ type (
// Server is the websocket server, // Server is the websocket server,
// listens on the config's port, the critical part is the event OnConnection // listens on the config's port, the critical part is the event OnConnection
type Server interface { type Server interface {
SetOptions(...OptionSetter)
GetOptions() *Options GetOptions() *Options
RegistProtocol(protocol string, h protocol.Handler) RegistProtocol(protocol string, h protocol.Handler)
ProtocolHandler(protocol string) protocol.Handler ProtocolHandler(protocol string) protocol.Handler
@ -41,17 +40,17 @@ type server struct {
var _ Server = &server{} var _ Server = &server{}
var defaultServer = newServer() var defaultServer = newServer(nil)
// server implementation // server implementation
// New creates a websocket server and returns it // New creates a websocket server and returns it
func New(setters ...OptionSetter) Server { func New(o *Options) Server {
return newServer(setters...) return newServer(o)
} }
// newServer creates a websocket server and returns it // newServer creates a websocket server and returns it
func newServer(setters ...OptionSetter) Server { func newServer(o *Options) Server {
s := &server{ s := &server{
clients: make(map[string]Client, 100), clients: make(map[string]Client, 100),
@ -59,7 +58,13 @@ func newServer(setters ...OptionSetter) Server {
protocols: make(map[string]protocol.Handler, 0), protocols: make(map[string]protocol.Handler, 0),
} }
s.SetOptions(setters...) if nil == o {
o = &Options{}
}
o.Validate()
s.options = o
return s return s
} }
@ -79,19 +84,6 @@ func (s *server) ProtocolHandler(protocol string) protocol.Handler {
return s.protocols[protocol] 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 // GetOptions is function that get option values
func GetOptions() *Options { func GetOptions() *Options {
return defaultServer.GetOptions() return defaultServer.GetOptions()