diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..e1351bba --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +all: + cd demo && go build main.go diff --git a/core.go b/core.go new file mode 100644 index 00000000..a7a337ae --- /dev/null +++ b/core.go @@ -0,0 +1,97 @@ +package ice + +import ( + "os" + "time" +) + +type Frame struct { +} + +func (f *Frame) Spawn(m *Message, c *Context, arg ...string) Server { + return &Frame{} +} +func (f *Frame) Begin(m *Message, arg ...string) Server { + list := map[*Context]*Message{m.target: m} + m.Travel(func(p *Context, s *Context) { + if msg, ok := list[p]; ok && msg != nil { + sub := msg.Spawns(s) + s.Begin(sub, arg...) + list[s] = sub + } + }) + return f +} +func (f *Frame) Start(m *Message, arg ...string) bool { + // 加载配置 + m.Travel(func(p *Context, s *Context) { + if cmd, ok := s.Commands["_init"]; ok { + msg := m.Spawns(s) + msg.Log("_init", s.Name) + cmd.Hand(msg, s, "_init", arg...) + } + }) + + // 启动服务 + Index.begin.Cmd(arg) + + m.Travel(func(p *Context, s *Context) { + if cmd, ok := s.Commands["_exit"]; ok { + msg := m.Spawns(s) + msg.Log("_exit", "some") + cmd.Hand(msg, s, "_exit", arg...) + } + }) + // 保存配置 + return true +} +func (f *Frame) Close(m *Message, arg ...string) bool { + list := map[*Context]*Message{m.target: m} + m.Travel(func(p *Context, s *Context) { + if msg, ok := list[p]; ok && msg != nil { + sub := msg.Spawns(s) + s.Close(sub, arg...) + list[s] = sub + } + }) + return true +} + +var Index = &Context{Name: "ice", Help: "冰山模块", + Caches: map[string]*Cache{}, + Configs: map[string]*Config{}, + Commands: map[string]*Command{ + "_init": {Name: "_init", Help: "hello", Hand: func(m *Message, c *Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + "hi": {Name: "hi", Help: "hello", Hand: func(m *Message, c *Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +var Pulse = &Message{ + time: time.Now(), code: 0, + meta: map[string][]string{}, + data: map[string]interface{}{}, + + messages: []*Message{}, message: nil, root: nil, + source: Index, target: Index, Hand: true, +} + +func init() { + Index.root = Index + Pulse.root = Pulse + Index.server = &Frame{} +} + +func Run(arg ...string) string { + if len(arg) == 0 { + arg = os.Args[1:] + } + + if Index.Begin(Pulse.Spawns(), arg...).Start(Index.begin.Spawns(), arg...) { + Index.Close(Index.start.Spawns(), arg...) + } + return Pulse.Result() +} diff --git a/core/aaa/aaa.go b/core/aaa/aaa.go new file mode 100644 index 00000000..3035b444 --- /dev/null +++ b/core/aaa/aaa.go @@ -0,0 +1,17 @@ +package aaa + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "aaa", Help: "认证模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/cli/cli.go b/core/cli/cli.go new file mode 100644 index 00000000..147822e6 --- /dev/null +++ b/core/cli/cli.go @@ -0,0 +1,68 @@ +package cli + +import ( + "bytes" + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + "os" + "os/exec" + "os/user" + "path" + "runtime" +) + +var Index = &ice.Context{Name: "cli", Help: "命令模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{ + "runtime": {Name: "runtime", Value: map[string]interface{}{ + "host": map[string]interface{}{}, + "boot": map[string]interface{}{}, + "node": map[string]interface{}{}, + "user": map[string]interface{}{}, + "work": map[string]interface{}{}, + }}, + }, + Commands: map[string]*ice.Command{ + "_init": {Name: "_init", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Conf("runtime", "host.GOARCH", runtime.GOARCH) + m.Conf("runtime", "host.GOOS", runtime.GOOS) + m.Conf("runtime", "host.pid", os.Getpid()) + + if name, e := os.Hostname(); e == nil { + m.Conf("runtime", "boot.hostname", kit.Select(name, os.Getenv("HOSTNAME"))) + } + if user, e := user.Current(); e == nil { + m.Conf("runtime", "boot.username", path.Base(kit.Select(user.Name, os.Getenv("USER")))) + } + if name, e := os.Getwd(); e == nil { + m.Conf("runtime", "boot.pathname", path.Base(kit.Select(name, os.Getenv("PWD")))) + } + + m.Conf("runtime", "node.type", "worker") + m.Log("info", "runtime %v", kit.Formats(m.Confv("runtime"))) + }}, + "runtime": {Name: "runtime", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "system": {Name: "system", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + out := bytes.NewBuffer(make([]byte, 0, 1024)) + err := bytes.NewBuffer(make([]byte, 0, 1024)) + + sys := exec.Command(arg[0], arg[1:]...) + sys.Stdout = out + sys.Stderr = err + + if e := sys.Run(); e != nil { + m.Echo("error: ").Echo(kit.Select(e.Error(), err.String())) + return + } + m.Echo(out.String()) + }}, + "timer": {Name: "timer", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/ctx/ctx.go b/core/ctx/ctx.go new file mode 100644 index 00000000..3a7aa51e --- /dev/null +++ b/core/ctx/ctx.go @@ -0,0 +1,17 @@ +package ctx + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "ctx", Help: "元始模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/gdb/gdb.go b/core/gdb/gdb.go new file mode 100644 index 00000000..91bcc34b --- /dev/null +++ b/core/gdb/gdb.go @@ -0,0 +1,17 @@ +package gdb + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "gdb", Help: "调试模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/lex/lex.go b/core/lex/lex.go new file mode 100644 index 00000000..0f486400 --- /dev/null +++ b/core/lex/lex.go @@ -0,0 +1,17 @@ +package lex + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "lex", Help: "词法模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/log/log.go b/core/log/log.go new file mode 100644 index 00000000..4c0ccaa8 --- /dev/null +++ b/core/log/log.go @@ -0,0 +1,17 @@ +package log + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "log", Help: "日志模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/mdb/mdb.go b/core/mdb/mdb.go new file mode 100644 index 00000000..4dbddade --- /dev/null +++ b/core/mdb/mdb.go @@ -0,0 +1,17 @@ +package mdb + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "mdb", Help: "数据模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/nfs/nfs.go b/core/nfs/nfs.go new file mode 100644 index 00000000..3b0e9159 --- /dev/null +++ b/core/nfs/nfs.go @@ -0,0 +1,17 @@ +package nfs + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "nfs", Help: "文件模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/ssh/ssh.go b/core/ssh/ssh.go new file mode 100644 index 00000000..407ab5cf --- /dev/null +++ b/core/ssh/ssh.go @@ -0,0 +1,17 @@ +package ssh + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "ssh", Help: "远程模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/tcp/tcp.go b/core/tcp/tcp.go new file mode 100644 index 00000000..dbcb5349 --- /dev/null +++ b/core/tcp/tcp.go @@ -0,0 +1,19 @@ +package tcp + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "tcp", Help: "网络模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { + ice.Index.Register(Index, nil) +} diff --git a/core/web/web.go b/core/web/web.go new file mode 100644 index 00000000..bb92bc55 --- /dev/null +++ b/core/web/web.go @@ -0,0 +1,300 @@ +package web + +import ( + "encoding/json" + "github.com/gorilla/websocket" + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + "net" + "net/http" + "net/url" + "path" + "strings" +) + +const ( + MSG_MAPS = 1 +) + +type WEB struct { + *http.Client + *http.Server + *http.ServeMux + m *ice.Message + send map[string]*ice.Message +} + +func (web *WEB) HandleWSS(m *ice.Message, safe bool, c *websocket.Conn) { + for { + if t, b, e := c.ReadMessage(); e != nil { + m.Log("warn", "space recv %d msg %v", t, e) + break + } else { + switch t { + case MSG_MAPS: + socket, msg := c, m.Spawn(b) + source := kit.Simple(msg.Optionv("_source")) + target := kit.Simple(msg.Optionv("_target")) + msg.Log("space", "recv %v %v->%v %v", t, source, target, msg.Formats("meta")) + + if len(target) > 0 { + if s, ok := msg.Confv("web.space", "hash."+target[0]+".socket").(*websocket.Conn); ok { + msg.Log("space", "route") + // 转发报文 + socket, source, target = s, append(source, target[0]), target[1:] + } else if call, ok := web.send[msg.Option("_target")]; len(target) == 1 && ok { + msg.Log("space", "done") + // 接收响应 + delete(web.send, msg.Option("_target")) + call.Back(msg) + break + } else if msg.Option("_handle") == "true" { + msg.Log("space", "miss") + // 丢弃报文 + break + } else { + // 失败报文 + msg.Log("space", "error") + msg.Echo("error") + source, target = []string{source[len(source)-1]}, kit.Revert(source)[1:] + } + } else { + msg.Log("space", "run") + // 本地执行 + msg = msg.Cmd() + msg.Optionv("_handle", "true") + kit.Revert(source) + source, target = []string{source[0]}, source[1:] + } + + // 发送报文 + msg.Optionv("_source", source) + msg.Optionv("_target", target) + msg.Log("space", "send %v %v->%v %v", t, source, target, msg.Formats("meta")) + socket.WriteMessage(t, []byte(msg.Format("meta"))) + } + } + } +} +func (web *WEB) HandleCmd(m *ice.Message, key string, cmd *ice.Command) { + web.HandleFunc(key, func(w http.ResponseWriter, r *http.Request) { + m.TryCatch(m.Spawns(), true, func(msg *ice.Message) { + msg.Optionv("request", r) + msg.Optionv("response", w) + msg.Option("agent", r.Header.Get("User-Agent")) + msg.Option("referer", r.Header.Get("Referer")) + msg.Option("accept", r.Header.Get("Accept")) + msg.Option("method", r.Method) + msg.Option("path", r.URL.Path) + msg.Option("sessid", "") + + // 请求环境 + for _, v := range r.Cookies() { + if v.Value != "" { + msg.Option(v.Name, v.Value) + } + } + + // 请求参数 + r.ParseMultipartForm(4096) + if r.ParseForm(); len(r.PostForm) > 0 { + for k, v := range r.PostForm { + msg.Log("info", "%s: %v", k, v) + } + msg.Log("info", "") + } + for k, v := range r.Form { + for _, v := range v { + msg.Add("option", k, v) + } + } + + // 请求数据 + switch r.Header.Get("Content-Type") { + case "application/json": + var data interface{} + if e := json.NewDecoder(r.Body).Decode(&data); e != nil { + msg.Log("warn", "%v", e) + } + msg.Optionv("content_data", data) + msg.Log("info", "%v", kit.Formats(data)) + + switch d := data.(type) { + case map[string]interface{}: + for k, v := range d { + for _, v := range kit.Simple(v) { + msg.Add("option", k, v) + } + } + } + } + + msg.Log("cmd", "%s %s", msg.Target().Name, key) + cmd.Hand(msg, msg.Target(), msg.Option("path")) + msg.Set("option") + if msg.Optionv("append") == nil { + msg.Result() + } + w.Write([]byte(msg.Formats("meta"))) + msg.Log("cost", msg.Format("cost")) + }) + }) +} +func (web *WEB) ServeHTTP(w http.ResponseWriter, r *http.Request) { + m := web.m + + index := r.Header.Get("index.module") == "" + if index { + if ip := r.Header.Get("X-Forwarded-For"); ip != "" { + r.Header.Set("remote_ip", ip) + } else if ip := r.Header.Get("X-Real-Ip"); ip != "" { + r.Header.Set("remote_ip", ip) + } else if strings.HasPrefix(r.RemoteAddr, "[") { + r.Header.Set("remote_ip", strings.Split(r.RemoteAddr, "]")[0][1:]) + } else { + r.Header.Set("remote_ip", strings.Split(r.RemoteAddr, ":")[0]) + } + m.Log("info", "").Log("info", "%v %s %s", r.Header.Get("remote_ip"), r.Method, r.URL) + r.Header.Set("index.module", "some") + r.Header.Set("index.url", r.URL.String()) + r.Header.Set("index.path", r.URL.Path) + } + + web.ServeMux.ServeHTTP(w, r) +} + +func (web *WEB) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server { + return &WEB{} +} +func (web *WEB) Begin(m *ice.Message, arg ...string) ice.Server { + web.send = map[string]*ice.Message{} + return web +} +func (web *WEB) Start(m *ice.Message, arg ...string) bool { + m.Travel(func(p *ice.Context, s *ice.Context) { + if w, ok := s.Server().(*WEB); ok { + if w.ServeMux != nil { + return + } + + msg := m.Spawns(s) + w.ServeMux = http.NewServeMux() + + route := "/" + s.Name + "/" + if n, ok := p.Server().(*WEB); ok && n.ServeMux != nil { + msg.Log("route", "%s <- %s", p.Name, route) + n.Handle(route, http.StripPrefix(path.Dir(route), w)) + } + + for k, x := range s.Commands { + if k[0] == '/' { + msg.Log("route", "%s <- %s", s.Name, k) + w.HandleCmd(msg, k, x) + } + } + } + }) + + port := kit.Select(m.Conf("spide", "self.port"), arg, 0) + web.m = m + web.Server = &http.Server{Addr: port, Handler: web} + m.Log("serve", "listen %s", port) + m.Log("serve", "listen %s", web.Server.ListenAndServe()) + return true +} +func (web *WEB) Close(m *ice.Message, arg ...string) bool { + return true +} + +var Index = &ice.Context{Name: "web", Help: "网页模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{ + "spide": {Name: "客户端", Value: map[string]interface{}{ + "self": map[string]interface{}{"port": ":9020"}, + }}, + "serve": {Name: "服务端", Value: map[string]interface{}{}}, + "space": {Name: "空间端", Value: map[string]interface{}{ + "meta": map[string]interface{}{"buffer": 4096}, + "hash": map[string]interface{}{}, + "list": map[string]interface{}{}, + }}, + }, + Commands: map[string]*ice.Command{ + "_init": {Name: "_init", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + "serve": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Conf("cli.runtime", "node.type", "server") + m.Run(arg...) + }}, + "/space": &ice.Command{Name: "/space", Help: "", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + r := m.Optionv("request").(*http.Request) + w := m.Optionv("response").(http.ResponseWriter) + if s, e := websocket.Upgrade(w, r, nil, m.Confi("web.space", "meta.buffer"), m.Confi("web.space", "meta.buffer")); m.Assert(e) { + h := m.Option("name") + + meta := map[string]interface{}{ + "create_time": m.Time(), + "socket": s, + "type": m.Option("node"), + "name": m.Option("name"), + } + m.Confv("space", []string{"hash", h}, meta) + m.Log("space", "conn %v %v", h, kit.Formats(m.Confv("space"))) + + web := m.Target().Server().(*WEB) + m.Gos(m, func(m *ice.Message) { + web.HandleWSS(m, false, s) + }) + } + }}, + "space": &ice.Command{Name: "space", Help: "", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + web := m.Target().Server().(*WEB) + switch arg[0] { + case "connect": + node, name := m.Conf("cli.runtime", "node.type"), m.Conf("cli.runtime", "boot.hostname") + if node == "worker" { + name = m.Conf("cli.runtime", "boot.pathname") + } + host := kit.Select(m.Conf("web.spide", "self.port"), arg, 1) + p := "ws://" + host + kit.Select("/space", arg, 2) + "?node=" + node + "&name=" + name + + if s, e := net.Dial("tcp", host); m.Assert(e) { + if u, e := url.Parse(p); m.Assert(e) { + if s, _, e := websocket.NewClient(s, u, nil, m.Confi("web.space", "meta.buffer"), m.Confi("web.space", "meta.buffer")); m.Assert(e) { + + id := m.Option("_source", []string{kit.Format(c.ID()), "some"}) + web.send[id] = m + s.WriteMessage(MSG_MAPS, []byte(m.Format("meta"))) + web.HandleWSS(m, true, s) + } + } + } + default: + if arg[0] == "" { + m.Cmdy(arg[1:]) + break + } + target := strings.Split(arg[0], ".") + if socket, ok := m.Confv("space", "hash."+target[0]+".socket").(*websocket.Conn); !ok { + m.Echo("error").Echo("not found") + } else { + id := kit.Format(c.ID()) + m.Optionv("_source", []string{id, target[0]}) + m.Optionv("_target", target[1:]) + + web := m.Target().Server().(*WEB) + web.send[id] = m + m.Add("detail", arg[1:]...) + socket.WriteMessage(MSG_MAPS, []byte(m.Format("meta"))) + m.Call(true, func(msg *ice.Message) *ice.Message { + m.Copy(msg) + return nil + }) + } + } + }}, + }, +} + +func init() { ice.Index.Register(Index, &WEB{}) } diff --git a/core/yac/yac.go b/core/yac/yac.go new file mode 100644 index 00000000..17b431ae --- /dev/null +++ b/core/yac/yac.go @@ -0,0 +1,17 @@ +package yac + +import ( + "github.com/shylinux/icebergs" +) + +var Index = &ice.Context{Name: "yac", Help: "语法模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Echo("hello %s world", c.Name) + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/demo/Makefile b/demo/Makefile new file mode 100644 index 00000000..c86d9c6f --- /dev/null +++ b/demo/Makefile @@ -0,0 +1,2 @@ +all: + go build main.go diff --git a/demo/go.mod b/demo/go.mod new file mode 100644 index 00000000..61a32feb --- /dev/null +++ b/demo/go.mod @@ -0,0 +1,13 @@ +module github.com/shylinux/icebergs/demo + +go 1.13 + +require ( + github.com/shylinux/icebergs v0.0.0-20191212145348-fe6226481eaa + github.com/shylinux/toolkits v0.0.0-20191212145555-d32eaba90a9e +) + +replace ( + github.com/shylinux/icebergs => ../ + github.com/shylinux/toolkits => ../../toolkits +) diff --git a/demo/go.sum b/demo/go.sum new file mode 100644 index 00000000..39df5266 --- /dev/null +++ b/demo/go.sum @@ -0,0 +1,10 @@ +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/shylinux/icebergs v0.0.0-20191209060320-9f0b85bae35a h1:0RgyhBEPMnRwECFoDPYsz/FgB9yVA9/nJ+tJacwSAig= +github.com/shylinux/icebergs v0.0.0-20191209060320-9f0b85bae35a/go.mod h1:FowoSa6syu4hP1QWT9UNoQICuIwfP2PGQ/ao1fSeoOc= +github.com/shylinux/icebergs v0.0.0-20191212145348-fe6226481eaa h1:DcLg8BUz5a0ON2UICK7b61LPFJ2+HVC7E8vV5ItYABo= +github.com/shylinux/icebergs v0.0.0-20191212145348-fe6226481eaa/go.mod h1:FowoSa6syu4hP1QWT9UNoQICuIwfP2PGQ/ao1fSeoOc= +github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477 h1:xwu6cGDBy/ZCrVklmCqSgx9O7Hr1v+hkzsIh1yc0hxg= +github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477/go.mod h1:e1dV0lMyoKz4Luib6XyMNpfpn5Sn7POnq7XTT4wfN7k= +github.com/shylinux/toolkits v0.0.0-20191212145555-d32eaba90a9e h1:eFZMsw0LuDeeVgwYwNJISebbMUZCVHEdQRaYBNWvC9s= +github.com/shylinux/toolkits v0.0.0-20191212145555-d32eaba90a9e/go.mod h1:e1dV0lMyoKz4Luib6XyMNpfpn5Sn7POnq7XTT4wfN7k= diff --git a/demo/main.go b/demo/main.go new file mode 100644 index 00000000..0335d4be --- /dev/null +++ b/demo/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/shylinux/icebergs" + _ "github.com/shylinux/icebergs/misc/chat" +) + +func main() { + println(ice.Run()) +} diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..cec98e45 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/shylinux/icebergs + +go 1.13 + +require ( + github.com/gorilla/websocket v1.4.1 + github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..66bd88cb --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477 h1:xwu6cGDBy/ZCrVklmCqSgx9O7Hr1v+hkzsIh1yc0hxg= +github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477/go.mod h1:e1dV0lMyoKz4Luib6XyMNpfpn5Sn7POnq7XTT4wfN7k= diff --git a/misc/chat/chat.go b/misc/chat/chat.go new file mode 100644 index 00000000..a1a505ff --- /dev/null +++ b/misc/chat/chat.go @@ -0,0 +1,65 @@ +package chat + +import ( + "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/core/web" + _ "github.com/shylinux/icebergs/misc" + "github.com/shylinux/toolkits" +) + +var Index = &ice.Context{Name: "chat", Help: "聊天模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{ + "group": {Name: "group", Value: map[string]interface{}{ + "meta": map[string]interface{}{}, + "list": map[string]interface{}{}, + "hash": map[string]interface{}{}, + }}, + }, + Commands: map[string]*ice.Command{ + "_init": {Name: "_init", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "/ocean": {Name: "/ocean", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "/river": {Name: "/river", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) == 0 { + arg = kit.Simple(m.Optionv("cmds")) + } + + if len(arg) == 0 { + m.Confm("group", "hash", func(key string, value map[string]interface{}) { + m.Push("key", key) + m.Push("create_time", value["create_time"]) + m.Push("name", value["name"]) + }) + return + } + + switch arg[0] { + case "create": + // h := kit.Hashs("uniq") + h := kit.ShortKey(m.Confm("group", "hash"), 6) + m.Conf("group", "hash."+h, map[string]interface{}{ + "create_time": m.Time(), + "create_name": arg[1], + }) + m.Log("info", "river create %v %v", h, kit.Formats(m.Confv("group", "hash."+h))) + m.Echo(h) + } + }}, + "/action": {Name: "/action", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if cmds, ok := m.Optionv("cmds").([]string); ok { + m.Cmdy("web.space", cmds) + return + } + }}, + "/storm": {Name: "/storm", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "/steam": {Name: "/steam", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "_exit": {Name: "_init", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + }, +} + +func init() { web.Index.Register(Index, &web.WEB{}) } diff --git a/misc/shy.go b/misc/shy.go new file mode 100644 index 00000000..b77ba317 --- /dev/null +++ b/misc/shy.go @@ -0,0 +1,16 @@ +package shy + +import ( + _ "github.com/shylinux/icebergs/core/aaa" + _ "github.com/shylinux/icebergs/core/cli" + _ "github.com/shylinux/icebergs/core/ctx" + _ "github.com/shylinux/icebergs/core/gdb" + _ "github.com/shylinux/icebergs/core/lex" + _ "github.com/shylinux/icebergs/core/log" + _ "github.com/shylinux/icebergs/core/mdb" + _ "github.com/shylinux/icebergs/core/nfs" + _ "github.com/shylinux/icebergs/core/ssh" + _ "github.com/shylinux/icebergs/core/tcp" + _ "github.com/shylinux/icebergs/core/web" + _ "github.com/shylinux/icebergs/core/yac" +) diff --git a/type.go b/type.go index d850d9da..ef45c148 100644 --- a/type.go +++ b/type.go @@ -3,8 +3,12 @@ package ice import ( "github.com/shylinux/toolkits" + "encoding/json" + "errors" "fmt" + "io" "os" + "runtime" "strings" "time" ) @@ -36,9 +40,12 @@ type Context struct { contexts map[string]*Context context *Context root *Context + begin *Message + start *Message exit chan bool server Server + id int } type Server interface { Spawn(m *Message, c *Context, arg ...string) Server @@ -47,6 +54,46 @@ type Server interface { Close(m *Message, arg ...string) bool } +func (c *Context) ID() int { + c.id++ + return c.id +} +func (c *Context) Server() Server { + return c.server +} +func (c *Context) Register(s *Context, x Server) *Context { + Pulse.Log("register", "%s <- %s", c.Name, s.Name) + if c.contexts == nil { + c.contexts = map[string]*Context{} + } + c.contexts[s.Name] = s + s.root = c.root + s.context = c + s.server = x + return s +} + +func (c *Context) Begin(m *Message, arg ...string) *Context { + c.begin = m + m.Log("begin", "%s", c.Name) + if c.server != nil { + c.server.Begin(m, arg...) + } + return c +} +func (c *Context) Start(m *Message, arg ...string) bool { + c.start = m + m.Log("start", "%s", c.Name) + return c.server.Start(m, arg...) +} +func (c *Context) Close(m *Message, arg ...string) bool { + m.Log("close", "%s", c.Name) + if c.server != nil { + return c.server.Close(m, arg...) + } + return true +} + type Message struct { time time.Time code int @@ -61,8 +108,54 @@ type Message struct { source *Context target *Context Hand bool + cb func(*Message) *Message } +func (m *Message) Time() string { + return m.time.Format("2006-01-02 15:04:05") +} +func (m *Message) Target() *Context { + return m.target +} +func (m *Message) Format(key interface{}) string { + switch key := key.(type) { + case string: + switch key { + case "cost": + return time.Now().Sub(m.time).String() + case "meta": + return kit.Format(m.meta) + case "stack": + pc := make([]uintptr, 100) + pc = pc[:runtime.Callers(5, pc)] + frames := runtime.CallersFrames(pc) + + meta := []string{} + for { + frame, more := frames.Next() + file := strings.Split(frame.File, "/") + name := strings.Split(frame.Function, "/") + meta = append(meta, fmt.Sprintf("\n%s:%d\t%s", file[len(file)-1], frame.Line, name[len(name)-1])) + if !more { + break + } + } + return strings.Join(meta, "") + } + case []byte: + json.Unmarshal(key, &m.meta) + } + return m.time.Format("2006-01-02 15:04:05") +} +func (m *Message) Formats(key string) string { + switch key { + case "meta": + return kit.Formats(m.meta) + default: + return m.Format(key) + } + return m.time.Format("2006-01-02 15:04:05") +} func (m *Message) Spawn(arg ...interface{}) *Message { msg := &Message{ time: time.Now(), @@ -82,14 +175,277 @@ func (m *Message) Spawn(arg ...interface{}) *Message { switch val := arg[0].(type) { case *Context: msg.target = val + case []byte: + json.Unmarshal(val, &msg.meta) } } return msg } -func (m *Message) Log(level string, str string, arg ...interface{}) { - fmt.Fprintf(os.Stderr, "%s %s %s\n", time.Now().Format("2006-01-02 15:04:05"), level, fmt.Sprintf(str, arg...)) +func (m *Message) Spawns(arg ...interface{}) *Message { + msg := m.Spawn(arg...) + msg.code = Index.ID() + m.messages = append(m.messages, msg) + return msg } -func (m *Message) Confv(arg ...interface{}) interface{} { + +func (m *Message) Add(key string, arg ...string) *Message { + switch key { + case "detail", "result": + m.meta[key] = append(m.meta[key], arg...) + + case "option", "append": + if len(arg) > 0 { + if kit.IndexOf(m.meta[key], arg[0]) == -1 { + m.meta[key] = append(m.meta[key], arg[0]) + } + m.meta[arg[0]] = append(m.meta[arg[0]], arg[1:]...) + } + } + return m +} +func (m *Message) Set(key string, arg ...string) *Message { + switch key { + case "detail", "result": + delete(m.meta, key) + case "option", "append": + if len(arg) > 0 { + delete(m.meta, arg[0]) + } else { + for _, k := range m.meta[key] { + delete(m.meta, k) + } + delete(m.meta, key) + return m + } + } + return m.Add(key, arg...) +} +func (m *Message) Copy(msg *Message) *Message { + for _, k := range msg.meta["append"] { + if kit.IndexOf(m.meta["append"], k) == -1 { + m.meta["append"] = append(m.meta["append"], k) + } + for _, v := range msg.meta[k] { + m.meta[k] = append(m.meta[k], v) + } + } + for _, v := range msg.meta["result"] { + m.meta["result"] = append(m.meta["result"], v) + } + return m +} +func (m *Message) Push(key string, value interface{}) *Message { + return m.Add("append", key, kit.Format(value)) +} +func (m *Message) Echo(str string, arg ...interface{}) *Message { + m.meta["result"] = append(m.meta["result"], fmt.Sprintf(str, arg...)) + return m +} +func (m *Message) Option(key string, arg ...interface{}) string { + return kit.Select("", kit.Simple(m.Optionv(key, arg...)), 0) +} +func (m *Message) Optionv(key string, arg ...interface{}) interface{} { + if len(arg) > 0 { + if kit.IndexOf(m.meta["option"], key) == -1 { + m.meta["option"] = append(m.meta["option"], key) + } + + switch arg := arg[0].(type) { + case string: + m.meta[key] = []string{arg} + case []string: + m.meta[key] = arg + default: + m.data[key] = arg + } + } + + for msg := m; msg != nil; msg = msg.message { + if list, ok := m.meta[key]; ok { + return list + } + if list, ok := m.data[key]; ok { + return list + } + } + return nil +} +func (m *Message) Resultv(arg ...interface{}) []string { + return m.meta["result"] +} +func (m *Message) Result(arg ...interface{}) string { + return strings.Join(m.Resultv(), "") +} + +func (m *Message) Log(level string, str string, arg ...interface{}) *Message { + fmt.Fprintf(os.Stderr, "%s %d %s->%s %s %s\n", time.Now().Format("2006-01-02 15:04:05"), m.code, m.source.Name, m.target.Name, level, fmt.Sprintf(str, arg...)) + return m +} +func (m *Message) Assert(arg interface{}) bool { + switch arg := arg.(type) { + case nil: + return true + case bool: + if arg == true { + return true + } + } + + panic(errors.New(fmt.Sprintf("error %v", arg))) +} +func (m *Message) TryCatch(msg *Message, safe bool, hand ...func(msg *Message)) *Message { + defer func() { + switch e := recover(); e { + case io.EOF: + case nil: + default: + m.Log("bench", "chain: %s", msg.Format("chain")) + m.Log("bench", "catch: %s", e) + m.Log("bench", "stack: %s", msg.Format("stack")) + + if m.Log("error", "catch: %s", e); len(hand) > 1 { + m.TryCatch(msg, safe, hand[1:]...) + } else if !safe { + m.Assert(e) + } + } + }() + + if len(hand) > 0 { + hand[0](msg) + } + return m +} +func (m *Message) Travel(cb func(p *Context, s *Context)) *Message { + list := []*Context{m.target} + for i := 0; i < len(list); i++ { + cb(list[i].context, list[i]) + for _, v := range list[i].contexts { + list = append(list, v) + } + } + return m +} +func (m *Message) Search(key interface{}, cb func(p *Context, s *Context, key string)) *Message { + switch key := key.(type) { + case string: + if strings.Contains(key, ":") { + + } else if strings.Contains(key, ".") { + list := strings.Split(key, ".") + + p := m.target.root + for _, v := range list[:len(list)-1] { + if s, ok := p.contexts[v]; ok { + p = s + } else { + p = nil + break + } + } + if p != nil { + cb(p.context, p, list[len(list)-1]) + } + } else { + cb(m.target.context, m.target, key) + } + } + return m +} +func (m *Message) Gos(msg *Message, cb func(*Message)) *Message { + go func() { msg.TryCatch(msg, true, func(msg *Message) { cb(msg) }) }() + return m +} +func (m *Message) Run(arg ...string) *Message { + m.target.server.Start(m, arg...) + return m +} +func (m *Message) Call(sync bool, cb func(*Message) *Message) *Message { + if sync { + wait := make(chan bool) + m.cb = func(sub *Message) *Message { + wait <- true + return cb(sub) + } + <-wait + } + return m +} +func (m *Message) Back(sub *Message) *Message { + if m.cb != nil { + m.cb(sub) + } + return m +} + +func (m *Message) Cmdy(arg ...interface{}) *Message { + msg := m.Cmd(arg...) + m.Copy(msg) + return m +} +func (m *Message) Cmd(arg ...interface{}) *Message { + list := kit.Simple(arg...) + if len(list) == 0 { + list = m.meta["detail"] + } + if len(list) == 0 { + return m + } + + msg := m + m.Search(list[0], func(p *Context, s *Context, key string) { + for c := s; c != nil; c = c.context { + if cmd, ok := c.Commands[key]; ok { + msg = m.Spawns(s).Log("cmd", "%s.%s %v", c.Name, key, list[1:]) + msg.TryCatch(msg, true, func(msg *Message) { + cmd.Hand(msg, c, key, list[1:]...) + }) + break + } + } + }) + return msg +} +func (m *Message) Confv(arg ...interface{}) (val interface{}) { + m.Search(arg[0], func(p *Context, s *Context, key string) { + for c := s; c != nil; c = c.context { + if conf, ok := c.Configs[key]; ok { + if len(arg) > 0 { + val = kit.Value(conf.Value, arg[1:]...) + } else { + val = conf.Value + } + } + } + }) + return +} +func (m *Message) Confm(key string, chain interface{}, cbs ...interface{}) map[string]interface{} { + val := m.Confv(key, chain) + if len(cbs) > 0 { + switch val := val.(type) { + case map[string]interface{}: + switch cb := cbs[0].(type) { + case func(string, map[string]interface{}): + for k, v := range val { + cb(k, v.(map[string]interface{})) + } + } + } + } + value, _ := val.(map[string]interface{}) + return value +} +func (m *Message) Confs(arg ...interface{}) bool { + return kit.Format(m.Confv(arg...)) != "" +} +func (m *Message) Confi(arg ...interface{}) int { + return kit.Int(m.Confv(arg...)) +} +func (m *Message) Conf(arg ...interface{}) string { + return kit.Format(m.Confv(arg...)) +} +func (m *Message) Capv(arg ...interface{}) interface{} { key := "" switch val := arg[0].(type) { case string: @@ -98,69 +454,13 @@ func (m *Message) Confv(arg ...interface{}) interface{} { for _, s := range []*Context{m.target} { for c := s; c != nil; c = c.context { - if conf, ok := c.Configs[key]; ok { - m.Log("conf", "%s.%s", c.Name, key) - return kit.Value(conf.Value, key) + if caps, ok := c.Caches[key]; ok { + return kit.Value(caps.Value, arg[0]) } } } return nil } -func (m *Message) Conf(arg ...interface{}) string { - return kit.Format(m.Confv(arg...)) -} -func (m *Message) Cmd(arg ...interface{}) *Message { - list := kit.Trans(arg...) - if len(list) == 0 { - return m - } - for _, s := range []*Context{m.target} { - for c := s; c != nil; c = c.context { - if cmd, ok := c.Commands[list[0]]; ok { - m.Log("cmd", "%s.%s", c.Name, list[0]) - cmd.Hand(m, c, list[0], list[1:]...) - } - } - } - return m -} -func (m *Message) Echo(str string, arg ...interface{}) *Message { - m.meta["result"] = append(m.meta["result"], fmt.Sprintf(str, arg...)) - return m -} -func (m *Message) Result(arg ...interface{}) string { - return strings.Join(m.meta["result"], "") -} - -var Pulse = &Message{ - time: time.Now(), code: 0, - meta: map[string][]string{}, - data: map[string]interface{}{}, - - messages: []*Message{}, message: nil, root: nil, - source: Index, target: Index, Hand: true, -} -var Index = &Context{Name: "root", Help: "元始模块", - Caches: map[string]*Cache{}, - Configs: map[string]*Config{}, - Commands: map[string]*Command{ - "hi": {Name: "hi", Help: "hello", Hand: func(m *Message, c *Context, cmd string, arg ...string) { - m.Echo("hello world") - }}, - }, -} - -func (c *Context) Register(s *Context, x Server) *Context { - if c.contexts == nil { - c.contexts = map[string]*Context{} - } - c.contexts[s.Name] = s - s.root = c.root - s.context = c - s.server = x - return s -} -func init() { - Index.root = Index - Pulse.root = Pulse +func (m *Message) Cap(arg ...interface{}) string { + return kit.Format(m.Capv(arg...)) }