package hotime import ( "bytes" "database/sql" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "net/url" "os" "path/filepath" "strconv" "log" "strings" ) type Application struct { MethodRouter Router contextBase Port string //端口号 TLSPort string //ssl访问端口号 connectListener []func(this *Context) bool //所有的访问监听,true按原计划继续使用,false表示有监听器处理 connectDbFunc func(err ...*Error) *sql.DB configPath string Config Map Db HoTimeDB Server *http.Server CacheIns sessionLong CacheIns sessionShort CacheIns http.Handler } func (this *Application) ServeHTTP(w http.ResponseWriter, req *http.Request) { this.handler(w, req) } //启动实例 func (this *Application) Run(router Router) { this.Router = router //重新设置MethodRouter//直达路由 this.MethodRouter = MethodRouter{} modeRouterStrict := true if this.Config.Get("modeRouterStrict").(bool) == false { modeRouterStrict = false } if router != nil { for pk, pv := range router { if !modeRouterStrict { pk = strings.ToLower(pk) } if pv != nil { for ck, cv := range pv { if !modeRouterStrict { ck = strings.ToLower(ck) } if cv != nil { for mk, mv := range cv { if !modeRouterStrict { mk = strings.ToLower(mk) } this.MethodRouter["/"+pk+"/"+ck+"/"+mk] = mv } } } } } } //this.Port = port this.Port = this.Config.GetString("port") this.TLSPort = this.Config.GetString("tlsPort") if this.connectDbFunc != nil && (this.Db.DB == nil || this.Db.DB.Ping() != nil) { this.Db.SetConnect(this.connectDbFunc) } if this.CacheIns == nil { this.CacheIns = CacheIns(&CacheMemory{Map: Map{}, Time: this.Config.GetInt64("cacheShortTime")}) } //异常处理 defer func() { if err := recover(); err != nil { this.SetError(errors.New(fmt.Sprint(err)), LOG_FMT) //log.Println(err) this.Run(router) } }() this.Server = &http.Server{} if !IsRun { IsRun = true } ch := make(chan int) if ObjToCeilInt(this.Port)!=0{ go func() { App[this.Port] = this this.Server.Handler = this //启动服务 this.Server.Addr = ":" + this.Port err:=this.Server.ListenAndServe() log.Println(err) ch <-1 }() }else if ObjToCeilInt(this.TLSPort)!=0{ go func() { App[this.TLSPort] = this this.Server.Handler = this //启动服务 this.Server.Addr = ":" + this.TLSPort err:=this.Server.ListenAndServeTLS(this.Config.GetString("tlsCert"),this.Config.GetString("tlsKey")) log.Println(err) ch <-2 }() }else{ log.Println("没有端口启用") return } value := <- ch log.Println("启动服务失败 : ", value) } //启动实例 func (this *Application) SetConnectDB(connect func(err ...*Error) *sql.DB) { //this.Db.DBCached=false //if this.Config.GetCeilInt("dbCached")!=0{ // this.Db.DBCached=true //} this.connectDbFunc = connect this.Db.SetConnect(this.connectDbFunc) this.Db.DBCached=false if this.Config.GetCeilInt("dbCached")!=0{ this.Db.DBCached=true } } //设置配置文件路径全路径或者相对路径 func (this *Application) SetSession(short CacheIns, Long CacheIns) { this.sessionLong = Long this.sessionShort = short } //默认配置缓存和session实现 func (this *Application) SetDefault(connect func(err ...*Error) *sql.DB) { this.SetConfig() this.connectDbFunc = connect if this.connectDbFunc != nil { this.Db.SetConnect(this.connectDbFunc) } //防止手动设置缓存误伤 if this.CacheIns == nil { this.SetCache(CacheIns(&CacheMemory{})) } //防止手动设置session误伤 if this.sessionShort == nil && this.sessionLong == nil { if this.connectDbFunc == nil { this.SetSession(CacheIns(&CacheMemory{}), nil) } else { this.SetSession(CacheIns(&CacheMemory{}), CacheIns(&CacheDb{Db: &this.Db, Time: this.Config.GetInt64("cacheLongTime")})) } } } //设置配置文件路径全路径或者相对路径 func (this *Application) SetCache(cache CacheIns) { this.CacheIns = cache } //设置配置文件路径全路径或者相对路径 func (this *Application) SetConfig(configPath ...string) { if len(configPath) != 0 { this.configPath = configPath[0] } if this.configPath == "" { this.configPath = "config/config.json" } //加载配置文件 btes, err := ioutil.ReadFile(this.configPath) this.Config = DeepCopyMap(Config).(Map) if err == nil { cmap := Map{} //文件是否损坏 cmap.JsonToMap(string(btes), &this.Error) for k, v := range cmap { this.Config.Put(k, v) //程序配置 Config.Put(k, v) //系统配置 } }else { log.Println("配置文件不存在,或者配置出错,使用缺省默认配置") } //文件如果损坏则不写入配置防止配置文件数据丢失 if this.Error.GetError() == nil { var out bytes.Buffer err = json.Indent(&out, []byte(this.Config.ToJsonString()), "", "\t") //判断配置文件是否序列有变化有则修改配置,五则不变 //fmt.Println(len(btes)) if len(btes)!=0&&out.String()==string(btes){ return } err = ioutil.WriteFile(this.configPath, out.Bytes(), os.ModeAppend) if err != nil { os.MkdirAll(filepath.Dir(this.configPath), os.ModeDir) os.Create(this.configPath) err = ioutil.WriteFile(this.configPath, out.Bytes(), os.ModeAppend) if err != nil { this.Error.SetError(err) } } } } //连接判断,返回true继续传输至控制层,false则停止传输 func (this *Application) SetConnectListener(lis func(this *Context) bool) { this.connectListener = append(this.connectListener, lis) } //网络错误 //func (this *Application) session(w http.ResponseWriter, req *http.Request) { // //} //序列化链接 func (this *Application) urlSer(url string) (string, []string) { q := strings.Index(url, "?") if q == -1 { q = len(url) } o := Substr(url, 0, q) r := strings.SplitN(o, "/", -1) var s = make([]string, 0) for i := 0; i < len(r); i++ { if !strings.EqualFold("", r[i]) { s = append(s, r[i]) } } return o, s } //访问 func (this *Application) handler(w http.ResponseWriter, req *http.Request) { _, s := this.urlSer(req.RequestURI) //获取cookie // 如果cookie存在直接将sessionId赋值为cookie.Value // 如果cookie不存在就查找传入的参数中是否有token // 如果token不存在就生成随机的sessionId // 如果token存在就判断token是否在Session中有保存 // 如果有取出token并复制给cookie // 没有保存就生成随机的session cookie, err := req.Cookie(this.Config.GetString("sessionName")) sessionId := Md5(strconv.Itoa(Rand(10))) token := req.FormValue("token") //isFirst:=false if err != nil || (len(token) == 32 && cookie.Value != token) { if len(token) == 32 { sessionId = token } //else{ // isFirst=true; //} http.SetCookie(w, &http.Cookie{Name: this.Config.GetString("sessionName"), Value: sessionId, Path: "/"}) } else { sessionId = cookie.Value } unescapeUrl, err := url.QueryUnescape(req.RequestURI) if err != nil { unescapeUrl = req.RequestURI } //访问实例 context := Context{SessionIns: SessionIns{SessionId: sessionId, LongCache: this.sessionLong, ShortCache: this.sessionShort, }, CacheIns: this.CacheIns, Resp: w, Req: req, Application: this, RouterString: s, Config: this.Config, Db: &this.Db, HandlerStr: unescapeUrl} //header默认设置 header := w.Header() header.Set("Content-Type", "text/html; charset=utf-8") //url去掉参数并序列化 context.HandlerStr, context.RouterString = this.urlSer(context.HandlerStr) //访问拦截true继续false暂停 connectListenerLen := len(this.connectListener) if connectListenerLen != 0 { for i := 0; i < connectListenerLen; i++ { if !this.connectListener[i](&context) { context.View() return } } } //接口服务 //if len(s) == 3 { // //如果满足规则则路由到对应控制器去 // if this.Router[s[0]] != nil && this.Router[s[0]][s[1]] != nil && this.Router[s[0]][s[1]][s[2]] != nil { // //控制层 // this.Router[s[0]][s[1]][s[2]](&context) // //header.Set("Content-Type", "text/html; charset=utf-8") // context.View() // return // } // //} //验证接口严格模式 modeRouterStrict := this.Config.Get("modeRouterStrict").(bool) tempHandlerStr := context.HandlerStr if !modeRouterStrict { tempHandlerStr = strings.ToLower(tempHandlerStr) } //执行接口 if this.MethodRouter[tempHandlerStr] != nil { this.MethodRouter[tempHandlerStr](&context) context.View() return } //url赋值 path := this.Config.GetString("tpt") + tempHandlerStr //判断是否为默认 if path[len(path)-1] == '/' { defFile := this.Config.GetSlice("defFile") for i := 0; i < len(defFile); i++ { temp := path + defFile.GetString(i) _, err := os.Stat(temp) if err == nil { path = temp break } } if path[len(path)-1] == '/' { w.WriteHeader(404) return } } if strings.Contains(path, "/.") { w.WriteHeader(404) return } //设置header delete(header, "Content-Type") if this.Config.GetInt("debug") != 1 { header.Set("Cache-Control", "public") } if strings.Index(path, ".m3u8") != -1 { header.Add("Content-Type", "audio/mpegurl") } //w.Write(data) http.ServeFile(w, req, path) }