diff --git a/src/context/web/web.go b/src/context/web/web.go index c9758c90..4bdfe1b0 100644 --- a/src/context/web/web.go +++ b/src/context/web/web.go @@ -2,51 +2,60 @@ package web // {{{ // }}} import ( // {{{ "context" + "html/template" - "log" "net/http" + + "bufio" + "log" "os" "path" + "strings" ) // }}} -type WEB struct { - Run bool - Master bool - - *http.ServeMux - *http.Server - - M *ctx.Message - *ctx.Context -} - type MUX interface { Handle(string, http.Handler) HandleFunc(string, func(http.ResponseWriter, *http.Request)) - Trans(*ctx.Message, string, ...string) + Trans(*ctx.Message, string, func(*ctx.Message, *ctx.Context, string, ...string) string) } -func (web *WEB) Trans(m *ctx.Message, key string, arg ...string) { // {{{ +type WEB struct { + *http.ServeMux + *http.Server + + *ctx.Message + *ctx.Context +} + +func (web *WEB) Trans(m *ctx.Message, key string, hand func(*ctx.Message, *ctx.Context, string, ...string) string) { // {{{ web.HandleFunc(key, func(w http.ResponseWriter, r *http.Request) { - m.Set("detail", key) + msg := m.Spawn(m.Target) + msg.Set("detail", key) for k, v := range r.Form { - m.Add("option", k) - m.Meta[k] = v + msg.Add("option", k) + msg.Meta[k] = v } for _, v := range r.Cookies() { - m.Add("option", v.Name, v.Value) + msg.Add("option", v.Name, v.Value) } - m.Cmd(arg...) + msg.Log("cmd", nil, "%s %v", key, msg.Meta["option"]) + msg.Put("option", "request", r) + msg.Put("option", "response", w) + + ret := hand(msg, msg.Target, key) + if ret != "" { + msg.Echo(ret) + } header := w.Header() - for _, k := range m.Meta["append"] { - ce := &http.Cookie{Name: k, Value: m.Get(k)} + for _, k := range msg.Meta["append"] { + ce := &http.Cookie{Name: k, Value: msg.Get(k)} header.Add("Set-Cookie", ce.String()) } - for _, v := range m.Meta["result"] { + for _, v := range msg.Meta["result"] { w.Write([]byte(v)) } }) @@ -54,21 +63,19 @@ func (web *WEB) Trans(m *ctx.Message, key string, arg ...string) { // {{{ // }}} func (web *WEB) ServeHTTP(w http.ResponseWriter, r *http.Request) { // {{{ - if web.Master { + if web.Message != nil { log.Println() - web.M.Log("info", "%s: %v %s %s", web.Name, r.RemoteAddr, r.Method, r.URL) + web.Log("cmd", nil, "%v %s %s", r.RemoteAddr, r.Method, r.URL) defer log.Println() - if web.M.Conf("logheaders") == "yes" { + if web.Cap("logheaders") == "yes" { for k, v := range r.Header { log.Println(k+":", v[0]) } log.Println() } - } - r.ParseForm() - if web.Master { + r.ParseForm() if len(r.PostForm) > 0 { for k, v := range r.PostForm { log.Printf("%s: %s", k, v[0]) @@ -79,8 +86,8 @@ func (web *WEB) ServeHTTP(w http.ResponseWriter, r *http.Request) { // {{{ web.ServeMux.ServeHTTP(w, r) - if web.Master { - if web.M.Conf("logheaders") == "yes" { + if web.Message != nil { + if web.Cap("logheaders") == "yes" { for k, v := range w.Header() { log.Println(k+":", v[0]) } @@ -90,79 +97,10 @@ func (web *WEB) ServeHTTP(w http.ResponseWriter, r *http.Request) { // {{{ // }}} -func (web *WEB) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{ - web.Configs["logheaders"] = &ctx.Config{Name: "logheaders", Value: "yes", Help: "日志输出请求头"} - web.Configs["directory"] = &ctx.Config{Name: "directory", Value: "usr", Help: "服务目录"} - web.Configs["protocol"] = &ctx.Config{Name: "protocol", Value: "http", Help: "服务协议"} - web.Configs["address"] = &ctx.Config{Name: "address", Value: ":9393", Help: "监听地址"} - web.Configs["route"] = &ctx.Config{Name: "route", Value: "/" + web.Name + "/", Help: "请求路径"} - - web.ServeMux = http.NewServeMux() - mux := m.Target.Server.(MUX) - for k, _ := range web.Commands { - if k[0] == '/' { - mux.Trans(m, k) - } - } - - return web -} - -// }}} -func (web *WEB) Start(m *ctx.Message, arg ...string) bool { // {{{ - web.M = m - - if !web.Run { - web.Run = true - - if s, ok := m.Target.Context.Server.(MUX); ok { - if h, ok := m.Target.Server.(http.Handler); ok { - m.Log("info", "%s: route %s -> %s", web.Context.Context.Name, m.Conf("route"), web.Name) - s.Handle(m.Conf("route"), http.StripPrefix(path.Dir(m.Conf("route")), h)) - } - } - - if m.Conf("directory") != "" { - m.Log("info", "%s: directory [%s]", web.Name, m.Conf("directory")) - web.Handle("/", http.FileServer(http.Dir(m.Conf("directory")))) - } - - m.Set("detail", "slaver") - m.Travel(web.Context, func(m *ctx.Message) bool { - if m.Target != web.Context { - m.Target.Start(m) - } - return true - }) - } - if len(arg) > 0 && arg[0] == "slaver" { - return true - } - - if m.Conf("address") == "" { - return true - } - - web.Server = &http.Server{Addr: m.Conf("address"), Handler: web} - - m.Log("info", "%s: protocol [%s]", web.Name, m.Conf("protocol")) - m.Log("info", "%s: address [%s]", web.Name, m.Conf("address")) - web.Master = true - - if m.Conf("protocol") == "https" { - m.Log("info", "%s: cert [%s]", web.Name, m.Conf("cert")) - m.Log("info", "%s: key [%s]", web.Name, m.Conf("key")) - web.Server.ListenAndServeTLS(m.Conf("cert"), m.Conf("key")) - } else { - web.Server.ListenAndServe() - } - - return true -} - -// }}} func (web *WEB) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { // {{{ - c.Caches = map[string]*ctx.Cache{} + c.Caches = map[string]*ctx.Cache{ + "directory": &ctx.Cache{Name: "directory", Value: "usr", Help: "服务目录"}, + } c.Configs = map[string]*ctx.Config{} s := new(WEB) @@ -171,51 +109,165 @@ func (web *WEB) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server } // }}} -func (web *WEB) Close(m *ctx.Message, arg ...string) bool { // {{{ +func (web *WEB) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{ + if len(arg) > 0 { + m.Cap("directory", arg[0]) + } + + web.Caches["route"] = &ctx.Cache{Name: "route", Value: "/" + web.Context.Name + "/", Help: "请求路径"} + web.Caches["register"] = &ctx.Cache{Name: "已初始化(yes/no)", Value: "no", Help: "模块是否已注册"} + web.Caches["master"] = &ctx.Cache{Name: "master(yes/no)", Value: "no", Help: "日志输出请求头"} + m.Cap("stream", m.Cap("route")+" -> "+m.Cap("directory")) + + web.ServeMux = http.NewServeMux() + if mux, ok := m.Target.Server.(MUX); ok { + for k, x := range web.Commands { + if k[0] == '/' { + mux.Trans(m, k, x.Hand) + } + } + } + + return web +} + +// }}} +func (web *WEB) Start(m *ctx.Message, arg ...string) bool { // {{{ + if len(arg) > 0 { + m.Cap("directory", arg[0]) + } + + web.Message = m + m.Cap("master", "yes") + + m.Travel(m.Target, func(m *ctx.Message) bool { + if h, ok := m.Target.Server.(http.Handler); ok && m.Cap("register") == "no" { + m.Cap("register", "yes") + + p, i := m.Target, 0 + m.BackTrace(func(m *ctx.Message) bool { + p = m.Target + if i++; i == 2 { + return false + } + return true + }) + + if s, ok := p.Server.(MUX); ok { + m.Log("info", p, "route %s -> %s", m.Cap("route"), m.Target.Name) + s.Handle(m.Cap("route"), http.StripPrefix(path.Dir(m.Cap("route")), h)) + } + + if s, ok := m.Target.Server.(MUX); ok && m.Cap("directory") != "" { + m.Log("info", nil, "dir / -> [%s]", m.Cap("directory")) + s.Handle("/", http.FileServer(http.Dir(m.Cap("directory")))) + } + } + return true + }) + + web.Caches["address"] = &ctx.Cache{Name: "address", Value: ":9393", Help: "监听地址"} + web.Caches["protocol"] = &ctx.Cache{Name: "protocol", Value: "http", Help: "服务协议"} + if len(arg) > 1 { + m.Cap("address", arg[1]) + } + if len(arg) > 2 { + m.Cap("protocol", arg[2]) + } + + m.Cap("stream", m.Cap("address")) + m.Log("info", nil, "address [%s]", m.Cap("address")) + m.Log("info", nil, "protocol [%s]", m.Cap("protocol")) + web.Server = &http.Server{Addr: m.Cap("address"), Handler: web} + + web.Caches["logheaders"] = &ctx.Cache{Name: "日志输出报文头(yes/no)", Value: "yes", Help: "日志输出请求头"} + + if m.Cap("protocol") == "https" { + m.Log("info", nil, "key [%s]", m.Cap("key")) + m.Log("info", nil, "cert [%s]", m.Cap("cert")) + web.Server.ListenAndServeTLS(m.Cap("cert"), m.Cap("key")) + } else { + web.Server.ListenAndServe() + } + return true } +// }}} +func (web *WEB) Close(m *ctx.Message, arg ...string) bool { // {{{ + return false +} + // }}} var Index = &ctx.Context{Name: "web", Help: "网页服务", - Caches: map[string]*ctx.Cache{}, + Caches: map[string]*ctx.Cache{ + "directory": &ctx.Cache{Name: "directory", Value: "usr", Help: "服务目录"}, + }, Configs: map[string]*ctx.Config{}, Commands: map[string]*ctx.Command{ - "listen": &ctx.Command{Name: "listen [address [protocol [directory]]]", Help: "开启网页服务", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { - if len(arg) > 0 { // {{{ - m.Conf("address", arg[0]) - } - if len(arg) > 1 { - m.Conf("protocol", arg[1]) - } - if len(arg) > 2 { - m.Conf("directory", arg[2]) - } - go m.Target.Start(m) + "listen": &ctx.Command{Name: "listen [directory [address [protocol]]]", Help: "开启网页服务", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { + m.Meta["detail"] = arg // {{{ + m.Target.Start(m) return "" // }}} }}, - "content": &ctx.Command{Name: "content route template", Help: "添加响应", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { + "route": &ctx.Command{Name: "route [directory|template|script] route string", Help: "添加响应", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { mux, ok := m.Target.Server.(MUX) // {{{ - if !ok { - return "" + m.Assert(ok, "模块类型错误") + m.Assert(len(arg) == 3, "缺少参数") + + switch arg[0] { + case "directory": + mux.Handle(arg[1], http.FileServer(http.Dir(arg[2]))) + case "template": + mux.Trans(m, arg[1], func(m *ctx.Message, c *ctx.Context, key string, a ...string) string { // {{{ + w := m.Data["response"].(http.ResponseWriter) + + if _, e := os.Stat(arg[2]); e == nil { + template.Must(template.ParseGlob(arg[2])).Execute(w, m) + } else { + template.Must(template.New("temp").Parse(arg[2])).Execute(w, m) + } + + return "" + }) + // }}} + case "script": + cli := m.Find("cli", true) // {{{ + lex := m.Find("lex", true) + mux.Trans(m, arg[1], func(m *ctx.Message, c *ctx.Context, key string, a ...string) string { + f, e := os.Open(arg[2]) + line, bio := "", bufio.NewReader(f) + + if e != nil { + line = arg[2] + } + + for { + if line = strings.TrimSpace(line); line != "" { + lex.Cmd("split", line, "void") + cli.Wait = make(chan bool) + cli.Cmd(lex.Meta["result"]...) + m.Meta["result"] = cli.Meta["result"] + } + + if line, e = bio.ReadString('\n'); e != nil { + break + } + } + + return "" + }) + // }}} } - mux.HandleFunc(arg[0], func(w http.ResponseWriter, r *http.Request) { - if _, e := os.Stat(arg[1]); e == nil { - template.Must(template.ParseGlob(arg[1])).Execute(w, m.Target) - } else { - template.Must(template.New("temp").Parse(arg[1])).Execute(w, m.Target) - } - }) return "" // }}} }}, "/hi": &ctx.Command{Name: "/hi", Help: "添加响应", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { - m.Echo("hello\n") m.Add("append", "hi", "hello") - m.Add("append", "hi", "hello") - return "hello\n" + return "hello" }}, }, }