diff --git a/admin.go b/admin.go index b4e3068f..80615957 100644 --- a/admin.go +++ b/admin.go @@ -208,7 +208,7 @@ func printTree(resultList *[][]string, t *Tree) { printTree(resultList, t.wildcard) } for _, l := range t.leaves { - if v, ok := l.runObject.(*controllerInfo); ok { + if v, ok := l.runObject.(*ControllerInfo); ok { if v.routerType == routerTypeBeego { var result = []string{ v.pattern, @@ -276,8 +276,8 @@ func profIndex(rw http.ResponseWriter, r *http.Request) { // it's in "/healthcheck" pattern in admin module. func healthcheck(rw http.ResponseWriter, req *http.Request) { var ( + result []string data = make(map[interface{}]interface{}) - result = []string{} resultList = new([][]string) content = map[string]interface{}{ "Fields": []string{"Name", "Message", "Status"}, @@ -291,17 +291,16 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) { name, err.Error(), } - } else { result = []string{ "success", name, "OK", } - } *resultList = append(*resultList, result) } + content["Data"] = resultList data["Content"] = content data["Title"] = "Health Check" @@ -330,7 +329,6 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { // List Tasks content := make(map[string]interface{}) resultList := new([][]string) - var result = []string{} var fields = []string{ "Task Name", "Task Spec", @@ -339,7 +337,7 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { "", } for tname, tk := range toolbox.AdminTaskList { - result = []string{ + result := []string{ tname, tk.GetSpec(), tk.GetStatus(), diff --git a/config/ini.go b/config/ini.go index 7e776a9a..3572b955 100644 --- a/config/ini.go +++ b/config/ini.go @@ -421,7 +421,7 @@ func (c *IniConfigContainer) Set(key, value string) error { var ( section, k string - sectionKey = strings.Split(key, "::") + sectionKey = strings.Split(strings.ToLower(key), "::") ) if len(sectionKey) >= 2 { diff --git a/grace/server.go b/grace/server.go index cc985552..c71193ad 100644 --- a/grace/server.go +++ b/grace/server.go @@ -196,7 +196,6 @@ func (srv *Server) signalHooks(ppFlag int, sig os.Signal) { for _, f := range srv.SignalHooks[ppFlag][sig] { f() } - return } // shutdown closes the listener so that no new connections are accepted. it also diff --git a/logs/jianliao.go b/logs/jianliao.go index 16bbdc2e..88ba0f9a 100644 --- a/logs/jianliao.go +++ b/logs/jianliao.go @@ -61,12 +61,10 @@ func (s *JLWriter) WriteMsg(when time.Time, msg string, level int) error { // Flush implementing method. empty. func (s *JLWriter) Flush() { - return } // Destroy implementing method. empty. func (s *JLWriter) Destroy() { - return } func init() { diff --git a/logs/slack.go b/logs/slack.go index d8482b3e..1cd2e5ae 100644 --- a/logs/slack.go +++ b/logs/slack.go @@ -49,12 +49,10 @@ func (s *SLACKWriter) WriteMsg(when time.Time, msg string, level int) error { // Flush implementing method. empty. func (s *SLACKWriter) Flush() { - return } // Destroy implementing method. empty. func (s *SLACKWriter) Destroy() { - return } func init() { diff --git a/logs/smtp.go b/logs/smtp.go index 2ec0514d..6208d7b8 100644 --- a/logs/smtp.go +++ b/logs/smtp.go @@ -138,12 +138,10 @@ func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error { // Flush implementing method. empty. func (s *SMTPWriter) Flush() { - return } // Destroy implementing method. empty. func (s *SMTPWriter) Destroy() { - return } func init() { diff --git a/namespace.go b/namespace.go index cfde0111..72f22a72 100644 --- a/namespace.go +++ b/namespace.go @@ -267,13 +267,12 @@ func addPrefix(t *Tree, prefix string) { addPrefix(t.wildcard, prefix) } for _, l := range t.leaves { - if c, ok := l.runObject.(*controllerInfo); ok { + if c, ok := l.runObject.(*ControllerInfo); ok { if !strings.HasPrefix(c.pattern, prefix) { c.pattern = prefix + c.pattern } } } - } // NSCond is Namespace Condition @@ -284,16 +283,16 @@ func NSCond(cond namespaceCond) LinkNamespace { } // NSBefore Namespace BeforeRouter filter -func NSBefore(filiterList ...FilterFunc) LinkNamespace { +func NSBefore(filterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { - ns.Filter("before", filiterList...) + ns.Filter("before", filterList...) } } // NSAfter add Namespace FinishRouter filter -func NSAfter(filiterList ...FilterFunc) LinkNamespace { +func NSAfter(filterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { - ns.Filter("after", filiterList...) + ns.Filter("after", filterList...) } } diff --git a/orm/db_alias.go b/orm/db_alias.go index 8daa4b23..e70db9f0 100644 --- a/orm/db_alias.go +++ b/orm/db_alias.go @@ -60,6 +60,8 @@ var ( "sqlite3": DRSqlite, "tidb": DRTiDB, "oracle": DROracle, + "oci8": DROracle, // github.com/mattn/go-oci8 + "ora": DROracle, //https://github.com/rana/ora } dbBasers = map[DriverType]dbBaser{ DRMySQL: newdbBaseMysql(), diff --git a/router.go b/router.go index be69419c..57479248 100644 --- a/router.go +++ b/router.go @@ -17,7 +17,6 @@ package beego import ( "fmt" "net/http" - "os" "path" "path/filepath" "reflect" @@ -111,7 +110,8 @@ func ExceptMethodAppend(action string) { exceptMethod = append(exceptMethod, action) } -type controllerInfo struct { +// ControllerInfo holds information about the controller. +type ControllerInfo struct { pattern string controllerType reflect.Type methods map[string]string @@ -183,7 +183,7 @@ func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInt } } - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.methods = methods route.routerType = routerTypeBeego @@ -206,7 +206,7 @@ func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInt } } -func (p *ControllerRegister) addToRouter(method, pattern string, r *controllerInfo) { +func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) { if !BConfig.RouterCaseSensitive { pattern = strings.ToLower(pattern) } @@ -227,13 +227,11 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { for _, c := range cList { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() - gopath := os.Getenv("GOPATH") - if gopath == "" { + wgopath := utils.GetGOPATHs() + if len(wgopath) == 0 { panic("you are in dev mode. So please set gopath") } pkgpath := "" - - wgopath := filepath.SplitList(gopath) for _, wg := range wgopath { wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath())) if utils.FileExists(wg) { @@ -343,7 +341,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { if _, ok := HTTPMETHOD[method]; method != "*" && !ok { panic("not support http method: " + method) } - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.routerType = routerTypeRESTFul route.runFunction = f @@ -369,7 +367,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { // Handler add user defined Handler func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) { - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.routerType = routerTypeHandler route.handler = h @@ -404,7 +402,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) controllerName := strings.TrimSuffix(ct.Name(), "Controller") for i := 0; i < rt.NumMethod(); i++ { if !utils.InSlice(rt.Method(i).Name, exceptMethod) { - route := &controllerInfo{} + route := &ControllerInfo{} route.routerType = routerTypeBeego route.methods = map[string]string{"*": rt.Method(i).Name} route.controllerType = ct @@ -510,7 +508,7 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin } } for _, l := range t.leaves { - if c, ok := l.runObject.(*controllerInfo); ok { + if c, ok := l.runObject.(*ControllerInfo); ok { if c.routerType == routerTypeBeego && strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) { find := false @@ -638,7 +636,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) findRouter bool runMethod string methodParams []*param.MethodParam - routerInfo *controllerInfo + routerInfo *ControllerInfo isRunnable bool ) context := p.pool.Get().(*beecontext.Context) @@ -679,7 +677,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) goto Admin } - if r.Method != "GET" && r.Method != "HEAD" { + if r.Method != http.MethodGet && r.Method != http.MethodHead { if BConfig.CopyRequestBody && !context.Input.IsUpload() { context.Input.CopyBody(BConfig.MaxMemory) } @@ -753,11 +751,11 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) runRouter = routerInfo.controllerType methodParams = routerInfo.methodParams method := r.Method - if r.Method == "POST" && context.Input.Query("_method") == "PUT" { - method = "PUT" + if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost { + method = http.MethodPut } - if r.Method == "POST" && context.Input.Query("_method") == "DELETE" { - method = "DELETE" + if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete { + method = http.MethodDelete } if m, ok := routerInfo.methods[method]; ok { runMethod = m @@ -787,8 +785,8 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf if BConfig.WebConfig.EnableXSRF { execController.XSRFToken() - if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || - (r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) { + if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut || + (r.Method == http.MethodPost && (context.Input.Query("_method") == http.MethodDelete || context.Input.Query("_method") == http.MethodPut)) { execController.CheckXSRFCookie() } } @@ -798,19 +796,19 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) if !context.ResponseWriter.Started { //exec main logic switch runMethod { - case "GET": + case http.MethodGet: execController.Get() - case "POST": + case http.MethodPost: execController.Post() - case "DELETE": + case http.MethodDelete: execController.Delete() - case "PUT": + case http.MethodPut: execController.Put() - case "HEAD": + case http.MethodHead: execController.Head() - case "PATCH": + case http.MethodPatch: execController.Patch() - case "OPTIONS": + case http.MethodOptions: execController.Options() default: if !execController.HandlerFunc(runMethod) { @@ -913,7 +911,7 @@ func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, ex } // FindRouter Find Router info for URL -func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *controllerInfo, isFind bool) { +func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *ControllerInfo, isFind bool) { var urlPath = context.Input.URL() if !BConfig.RouterCaseSensitive { urlPath = strings.ToLower(urlPath) @@ -921,7 +919,7 @@ func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo httpMethod := context.Input.Method() if t, ok := p.routers[httpMethod]; ok { runObject := t.Match(urlPath, context) - if r, ok := runObject.(*controllerInfo); ok { + if r, ok := runObject.(*ControllerInfo); ok { return r, true } } diff --git a/router_test.go b/router_test.go index 25804d67..720b4ca8 100644 --- a/router_test.go +++ b/router_test.go @@ -654,32 +654,40 @@ func TestFilterFinishRouterMulti(t *testing.T) { } func beegoFilterNoOutput(ctx *context.Context) { - return } + func beegoBeforeRouter1(ctx *context.Context) { ctx.WriteString("|BeforeRouter1") } + func beegoBeforeRouter2(ctx *context.Context) { ctx.WriteString("|BeforeRouter2") } + func beegoBeforeExec1(ctx *context.Context) { ctx.WriteString("|BeforeExec1") } + func beegoBeforeExec2(ctx *context.Context) { ctx.WriteString("|BeforeExec2") } + func beegoAfterExec1(ctx *context.Context) { ctx.WriteString("|AfterExec1") } + func beegoAfterExec2(ctx *context.Context) { ctx.WriteString("|AfterExec2") } + func beegoFinishRouter1(ctx *context.Context) { ctx.WriteString("|FinishRouter1") } + func beegoFinishRouter2(ctx *context.Context) { ctx.WriteString("|FinishRouter2") } + func beegoResetParams(ctx *context.Context) { ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat")) } diff --git a/session/couchbase/sess_couchbase.go b/session/couchbase/sess_couchbase.go index d5be11d0..791a6cc7 100644 --- a/session/couchbase/sess_couchbase.go +++ b/session/couchbase/sess_couchbase.go @@ -155,10 +155,13 @@ func (cp *Provider) SessionInit(maxlifetime int64, savePath string) error { func (cp *Provider) SessionRead(sid string) (session.Store, error) { cp.b = cp.getBucket() - var doc []byte + var ( + kv map[interface{}]interface{} + err error + doc []byte + ) - err := cp.b.Get(sid, &doc) - var kv map[interface{}]interface{} + err = cp.b.Get(sid, &doc) if doc == nil { kv = make(map[interface{}]interface{}) } else { @@ -230,7 +233,6 @@ func (cp *Provider) SessionDestroy(sid string) error { // SessionGC Recycle func (cp *Provider) SessionGC() { - return } // SessionAll return all active session diff --git a/session/ledis/ledis_session.go b/session/ledis/ledis_session.go index 4892a8ab..18b27708 100644 --- a/session/ledis/ledis_session.go +++ b/session/ledis/ledis_session.go @@ -12,8 +12,10 @@ import ( "github.com/siddontang/ledisdb/ledis" ) -var ledispder = &Provider{} -var c *ledis.DB +var ( + ledispder = &Provider{} + c *ledis.DB +) // SessionStore ledis session store type SessionStore struct { @@ -97,27 +99,36 @@ func (lp *Provider) SessionInit(maxlifetime int64, savePath string) error { } cfg := new(config.Config) cfg.DataDir = lp.savePath - nowLedis, err := ledis.Open(cfg) - c, err = nowLedis.Select(lp.db) + + var ledisInstance *ledis.Ledis + ledisInstance, err = ledis.Open(cfg) if err != nil { - println(err) - return nil + return err } - return nil + c, err = ledisInstance.Select(lp.db) + return err } // SessionRead read ledis session by sid func (lp *Provider) SessionRead(sid string) (session.Store, error) { - kvs, err := c.Get([]byte(sid)) - var kv map[interface{}]interface{} + var ( + kv map[interface{}]interface{} + kvs []byte + err error + ) + + if kvs, err = c.Get([]byte(sid)); err != nil { + return nil, err + } + if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { - kv, err = session.DecodeGob(kvs) - if err != nil { + if kv, err = session.DecodeGob(kvs); err != nil { return nil, err } } + ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} return ls, nil } @@ -142,18 +153,7 @@ func (lp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) c.Set([]byte(sid), data) c.Expire([]byte(sid), lp.maxlifetime) } - kvs, err := c.Get([]byte(sid)) - var kv map[interface{}]interface{} - if len(kvs) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob(kvs) - if err != nil { - return nil, err - } - } - ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} - return ls, nil + return lp.SessionRead(sid) } // SessionDestroy delete ledis session by id @@ -164,7 +164,6 @@ func (lp *Provider) SessionDestroy(sid string) error { // SessionGC Impelment method, no used. func (lp *Provider) SessionGC() { - return } // SessionAll return all active session diff --git a/session/memcache/sess_memcache.go b/session/memcache/sess_memcache.go index cad02258..755979c4 100644 --- a/session/memcache/sess_memcache.go +++ b/session/memcache/sess_memcache.go @@ -215,7 +215,6 @@ func (rp *MemProvider) connectInit() error { // SessionGC Impelment method, no used. func (rp *MemProvider) SessionGC() { - return } // SessionAll return all activeSession diff --git a/session/mysql/sess_mysql.go b/session/mysql/sess_mysql.go index 7bcf959c..4c9251e7 100644 --- a/session/mysql/sess_mysql.go +++ b/session/mysql/sess_mysql.go @@ -209,7 +209,6 @@ func (mp *Provider) SessionGC() { c := mp.connectInit() c.Exec("DELETE from "+TableName+" where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) c.Close() - return } // SessionAll count values in mysql session diff --git a/session/postgres/sess_postgresql.go b/session/postgres/sess_postgresql.go index f00e0711..ffc27def 100644 --- a/session/postgres/sess_postgresql.go +++ b/session/postgres/sess_postgresql.go @@ -224,7 +224,6 @@ func (mp *Provider) SessionGC() { c := mp.connectInit() c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime) c.Close() - return } // SessionAll count values in postgresql session diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index 14bbd4c1..08efa6e1 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -155,7 +155,7 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { return nil, err } if rp.password != "" { - if _, err := c.Do("AUTH", rp.password); err != nil { + if _, err = c.Do("AUTH", rp.password); err != nil { c.Close() return nil, err } @@ -176,13 +176,20 @@ func (rp *Provider) SessionRead(sid string) (session.Store, error) { c := rp.poollist.Get() defer c.Close() - kvs, err := redis.String(c.Do("GET", sid)) - var kv map[interface{}]interface{} + var ( + kv map[interface{}]interface{} + kvs string + err error + ) + + if kvs, err = redis.String(c.Do("GET", sid)); err != nil { + return nil, err + } + if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { - kv, err = session.DecodeGob([]byte(kvs)) - if err != nil { + if kv, err = session.DecodeGob([]byte(kvs)); err != nil { return nil, err } } @@ -216,20 +223,7 @@ func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) c.Do("RENAME", oldsid, sid) c.Do("EXPIRE", sid, rp.maxlifetime) } - - kvs, err := redis.String(c.Do("GET", sid)) - var kv map[interface{}]interface{} - if len(kvs) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob([]byte(kvs)) - if err != nil { - return nil, err - } - } - - rs := &SessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime} - return rs, nil + return rp.SessionRead(sid) } // SessionDestroy delete redis session by id @@ -243,7 +237,6 @@ func (rp *Provider) SessionDestroy(sid string) error { // SessionGC Impelment method, no used. func (rp *Provider) SessionGC() { - return } // SessionAll return all activeSession diff --git a/session/sess_cookie.go b/session/sess_cookie.go index 3fefa360..145e53c9 100644 --- a/session/sess_cookie.go +++ b/session/sess_cookie.go @@ -74,21 +74,16 @@ func (st *CookieSessionStore) SessionID() string { // SessionRelease Write cookie session to http response cookie func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) { - str, err := encodeCookie(cookiepder.block, - cookiepder.config.SecurityKey, - cookiepder.config.SecurityName, - st.values) - if err != nil { - return + encodedCookie, err := encodeCookie(cookiepder.block, cookiepder.config.SecurityKey, cookiepder.config.SecurityName, st.values) + if err == nil { + cookie := &http.Cookie{Name: cookiepder.config.CookieName, + Value: url.QueryEscape(encodedCookie), + Path: "/", + HttpOnly: true, + Secure: cookiepder.config.Secure, + MaxAge: cookiepder.config.Maxage} + http.SetCookie(w, cookie) } - cookie := &http.Cookie{Name: cookiepder.config.CookieName, - Value: url.QueryEscape(str), - Path: "/", - HttpOnly: true, - Secure: cookiepder.config.Secure, - MaxAge: cookiepder.config.Maxage} - http.SetCookie(w, cookie) - return } type cookieConfig struct { @@ -166,7 +161,6 @@ func (pder *CookieProvider) SessionDestroy(sid string) error { // SessionGC Implement method, no used. func (pder *CookieProvider) SessionGC() { - return } // SessionAll Implement method, return 0. diff --git a/session/ssdb/sess_ssdb.go b/session/ssdb/sess_ssdb.go index c846e73a..03b60793 100644 --- a/session/ssdb/sess_ssdb.go +++ b/session/ssdb/sess_ssdb.go @@ -30,13 +30,13 @@ func (p *SsdbProvider) connectInit() error { } func (p *SsdbProvider) SessionInit(maxLifetime int64, savePath string) error { - var e error = nil p.maxLifetime = maxLifetime address := strings.Split(savePath, ":") p.host = address[0] - p.port, e = strconv.Atoi(address[1]) - if e != nil { - return e + + var err error + if p.port, err = strconv.Atoi(address[1]); err != nil { + return err } return p.connectInit() } @@ -78,8 +78,8 @@ func (p *SsdbProvider) SessionExist(sid string) bool { return false } return true - } + func (p *SsdbProvider) SessionRegenerate(oldsid, sid string) (session.Store, error) { //conn.Do("setx", key, v, ttl) if p.client == nil { @@ -123,7 +123,6 @@ func (p *SsdbProvider) SessionDestroy(sid string) error { } func (p *SsdbProvider) SessionGC() { - return } func (p *SsdbProvider) SessionAll() int { diff --git a/staticfile.go b/staticfile.go index 7c270947..bbb2a1fb 100644 --- a/staticfile.go +++ b/staticfile.go @@ -90,8 +90,6 @@ func serverStaticRouter(ctx *context.Context) { } http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch) - return - } type serveContentHolder struct { diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 00000000..ed885787 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,30 @@ +package utils + +import ( + "os" + "path/filepath" + "runtime" + "strings" +) + +// GetGOPATHs returns all paths in GOPATH variable. +func GetGOPATHs() []string { + gopath := os.Getenv("GOPATH") + if gopath == "" && strings.Compare(runtime.Version(), "go1.8") >= 0 { + gopath = defaultGOPATH() + } + return filepath.SplitList(gopath) +} + +func defaultGOPATH() string { + env := "HOME" + if runtime.GOOS == "windows" { + env = "USERPROFILE" + } else if runtime.GOOS == "plan9" { + env = "home" + } + if home := os.Getenv(env); home != "" { + return filepath.Join(home, "go") + } + return "" +}