package web import ( "github.com/gorilla/websocket" "github.com/shylinux/icebergs" "github.com/shylinux/toolkits" "bytes" "encoding/json" "math/rand" "net" "net/http" "net/url" "path" "strings" "text/template" "time" ) const ( MSG_MAPS = 1 ) type WEB struct { *http.Client *http.Server *http.ServeMux m *ice.Message send map[string]*ice.Message } func Cookie(msg *ice.Message, sessid string) string { w := msg.Optionv("response").(http.ResponseWriter) expire := time.Now().Add(kit.Duration(msg.Conf("aaa.sess", "meta.expire"))) msg.Log("cookie", "expire %v sessid %s", kit.Format(expire), sessid) http.SetCookie(w, &http.Cookie{Name: "sessid", Value: sessid, Path: "/", Expires: expire}) return sessid } func (web *WEB) Login(msg *ice.Message, w http.ResponseWriter, r *http.Request) bool { if msg.Options("sessid") { sub := msg.Cmd("aaa.sess", "check", msg.Option("sessid")) msg.Log("info", "role: %s user: %s", msg.Option("userrole", sub.Append("userrole")), msg.Option("username", sub.Append("username"))) } msg.Runs("_login", msg.Option("url"), kit.Simple(msg.Optionv("cmds"))...) return true } func (web *WEB) HandleWSS(m *ice.Message, safe bool, c *websocket.Conn) bool { 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") // 本地执行 if safe { msg = msg.Cmd() if msg.Detail() == "exit" { return true } } else { msg.Echo("no right") } 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"))) } } } return false } func (web *WEB) HandleCGI(m *ice.Message, which string) *template.Template { cgi := template.FuncMap{ "result": func(msg *ice.Message) string { return msg.Result() }, } tmpl := template.New("render") for k, v := range m.Target().Commands { m.Log("info", "%v, %v", k, v.Name) if strings.HasPrefix(k, "/") || strings.HasPrefix(k, "_") { continue } func(k string, v *ice.Command) { cgi[k] = func(arg ...interface{}) (res interface{}) { m.TryCatch(m.Spawn(), true, func(msg *ice.Message) { msg.Option("render", "table") v.Hand(msg, m.Target(), k, kit.Simple(arg)...) buffer := bytes.NewBuffer([]byte{}) m.Assert(tmpl.ExecuteTemplate(buffer, msg.Option("render"), msg)) res = string(buffer.Bytes()) }) return } }(k, v) } tmpl = tmpl.Funcs(cgi) // tmpl = template.Must(tmpl.ParseGlob(path.Join(m.Conf("serve", "template.path"), "/*.tmpl"))) // tmpl = template.Must(tmpl.ParseGlob(path.Join(m.Conf("serve", "template.path"), m.Target().Name, "/*.tmpl"))) tmpl = template.Must(tmpl.ParseFiles(which)) m.Confm("serve", "template.list", func(index int, value string) { tmpl = template.Must(tmpl.Parse(value)) }) for i, v := range tmpl.Templates() { m.Log("info", "%v, %v", i, v.Name()) } return tmpl } 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) { defer func() { msg.Log("cost", msg.Format("cost")) }() msg.Optionv("request", r) msg.Optionv("response", w) msg.Option("remote_ip", r.Header.Get("remote_ip")) 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("url", 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) } } } } if web.Login(msg, w, r) { msg.Log("cmd", "%s %s", msg.Target().Name, key) cmd.Hand(msg, msg.Target(), msg.Option("url"), kit.Simple(msg.Optionv("cmds"))...) w.Write([]byte(msg.Formats("meta"))) } }) }) } 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 } w.ServeMux = http.NewServeMux() msg := m.Spawns(s) // 级联路由 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)) } // 静态路由 m.Confm("web.serve", "static", func(key string, value string) { msg.Log("route", "%s <- %s <- %s", s.Name, key, value) w.Handle(key, http.StripPrefix(key, http.FileServer(http.Dir(value)))) }) // 命令路由 m.Travel(func(p *ice.Context, sub *ice.Context, k string, x *ice.Command) { if s == sub && 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) m.Cap("stream", port) web.m = m web.Server = &http.Server{Addr: port, Handler: web} m.Log("serve", "node %v", m.Conf("cli.runtime", "node")) 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": "127.0.0.1:9020"}, }}, "serve": {Name: "服务端", Value: map[string]interface{}{ "static": map[string]interface{}{"/": "usr/volcanos/", "/static/volcanos/": "usr/volcanos/", }, "template": map[string]interface{}{ "path": "usr/template", "list": []interface{}{ `{{define "raw"}}{{.|result}}{{end}}`, `{{define "title"}}{{.|result}}{{end}}`, `{{define "chapter"}}{{.|result}}{{end}}`, `{{define "section"}}{{.|result}}{{end}}`, `{{define "block"}}