// GetRenderer returns the current raw renderer from the context. func GetRenderer(c context.Context) renderer.Renderer { if val, ok := c.GetGlobal(context.BaseCtxKey("renderer")); ok { return val.(renderer.Renderer) } return renderer.NewRenderer("template", "base.tmpl") }
func (con Component) Handler(c context.Context) http.Handler { mw, i18nFound := con.dispatcher.Middleware("I18N") logger := webfw.GetLogger(c) rnd := renderer.NewRenderer(con.dispatcher.Config.Renderer.Dir, "raw.tmpl") rnd.Delims("{%", "%}") if i18nFound { if i18n, ok := mw.(middleware.I18N); ok { rnd.Funcs(i18n.TemplateFuncMap()) } } else { logger.Infoln("I18N middleware not found") } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { params := webfw.GetParams(c, r) err := rnd.Render(w, renderer.RenderData{"apiPattern": con.apiPattern}, c.GetAll(r), "components/"+params["name"]+".tmpl") if err != nil { webfw.GetLogger(c).Print(err) } }) }
func RegisterControllers(dispatcher *webfw.Dispatcher, apiPattern string) { dispatcher.Handle(NewApp()) dispatcher.Handle(NewComponent(dispatcher.Config.Renderer.Dir, apiPattern)) dispatcher.Renderer = renderer.NewRenderer(dispatcher.Config.Renderer.Dir, dispatcher.Config.Renderer.Base) dispatcher.Renderer.Delims("{%", "%}") middleware.InitializeDefault(dispatcher) }
func RegisterControllers(config readeef.Config, dispatcher *webfw.Dispatcher, apiPattern string) { dispatcher.Renderer = renderer.NewRenderer(dispatcher.Config.Renderer.Dir, dispatcher.Config.Renderer.Base) dispatcher.Renderer.Delims("{%", "%}") dispatcher.Context.SetGlobal(context.BaseCtxKey("readeefConfig"), config) middleware.InitializeDefault(dispatcher) dispatcher.Handle(NewApp()) dispatcher.Handle(NewComponent(dispatcher, apiPattern)) }
func NewGoose(templateDir string) (content.Extractor, error) { rawTmpl := "raw.tmpl" f, err := fs.DefaultFS.OpenRoot(templateDir, rawTmpl) if err != nil { return nil, fmt.Errorf("Goose extractor requires %s template in %s: %v\n", rawTmpl, templateDir, err) } f.Close() renderer := renderer.NewRenderer(templateDir, rawTmpl) renderer.Delims("{%", "%}") return Goose{renderer: renderer}, nil }
func RegisterControllers(config readeef.Config, dispatcher *webfw.Dispatcher, apiPattern string) { dispatcher.Renderer = renderer.NewRenderer(dispatcher.Config.Renderer.Dir, dispatcher.Config.Renderer.Base) hasProxy := false for _, p := range config.FeedParser.Processors { if p == "proxy-http" { hasProxy = true break } } if !hasProxy { for _, p := range config.Content.ArticleProcessors { if p == "proxy-http" { hasProxy = true break } } } mw := make([]string, 0, len(dispatcher.Config.Dispatcher.Middleware)) for _, m := range dispatcher.Config.Dispatcher.Middleware { switch m { case "Session": if hasProxy { mw = append(mw, m) } default: mw = append(mw, m) } } dispatcher.Config.Dispatcher.Middleware = mw dispatcher.Renderer.Delims("{%", "%}") dispatcher.Context.SetGlobal(readeef.CtxKey("config"), config) dispatcher.Context.SetGlobal(context.BaseCtxKey("readeefConfig"), config) middleware.InitializeDefault(dispatcher) dispatcher.Handle(NewApp()) dispatcher.Handle(NewComponent(dispatcher, apiPattern)) if hasProxy { dispatcher.Handle(NewProxy()) } }
func (con Component) Handler(c context.Context) http.HandlerFunc { rnd := renderer.NewRenderer(con.dir, "raw.tmpl") rnd.Delims("{%", "%}") return func(w http.ResponseWriter, r *http.Request) { params := webfw.GetParams(c, r) err := rnd.Render(w, renderer.RenderData{"apiPattern": con.apiPattern}, c.GetAll(r), "components/"+params["name"]+".tmpl") if err != nil { webfw.GetLogger(c).Print(err) } } }
func (con Component) Handler(c context.Context) http.Handler { i18nmw, i18nFound := con.dispatcher.Middleware("I18N") urlmw, urlFound := con.dispatcher.Middleware("Url") logger := webfw.GetLogger(c) cfg := readeef.GetConfig(c) rnd := renderer.NewRenderer(con.dispatcher.Config.Renderer.Dir, "raw.tmpl") rnd.Delims("{%", "%}") if cfg.Logger.Level == "debug" { rnd.SkipCache(true) } if i18nFound { if i18n, ok := i18nmw.(middleware.I18N); ok { rnd.Funcs(i18n.TemplateFuncMap()) } } else { logger.Infoln("I18N middleware not found") } if urlFound { if url, ok := urlmw.(middleware.Url); ok { rnd.Funcs(url.TemplateFuncMap(c)) } } else { logger.Infoln("Url middleware not found") } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { params := webfw.GetParams(c, r) if r.Method != "HEAD" { err := rnd.Render(w, renderer.RenderData{"apiPattern": con.apiPattern, "config": cfg}, c.GetAll(r), "components/"+params["name"]+".tmpl") if err != nil { webfw.GetLogger(c).Print(err) } } }) }
func RegisterControllers(config readeef.Config, dispatcher *webfw.Dispatcher, logger webfw.Logger) error { repo, err := repo.New(config.DB.Driver, config.DB.Connect, logger) if err != nil { return err } capabilities := capabilities{ I18N: len(dispatcher.Config.I18n.Languages) > 1, Popularity: len(config.Popularity.Providers) > 0, } var ap []content.ArticleProcessor for _, p := range config.Content.ArticleProcessors { switch p { case "relative-url": ap = append(ap, contentProcessor.NewRelativeUrl(logger)) case "proxy-http": template := config.Content.ProxyHTTPURLTemplate if template != "" { p, err := contentProcessor.NewProxyHTTP(logger, template) if err != nil { return fmt.Errorf("Error initializing Proxy HTTP article processor: %v", err) } ap = append(ap, p) capabilities.ProxyHTTP = true } case "insert-thumbnail-target": ap = append(ap, contentProcessor.NewInsertThumbnailTarget(logger)) } } repo.ArticleProcessors(ap) if err := initAdminUser(repo, []byte(config.Auth.Secret)); err != nil { return err } mw := make([]string, 0, len(dispatcher.Config.Dispatcher.Middleware)) for _, m := range dispatcher.Config.Dispatcher.Middleware { switch m { case "I18N", "Static", "Url", "Sitemap": case "Session": if capabilities.ProxyHTTP { mw = append(mw, m) } default: mw = append(mw, m) } } dispatcher.Config.Dispatcher.Middleware = mw dispatcher.Context.SetGlobal(readeef.CtxKey("config"), config) dispatcher.Context.SetGlobal(context.BaseCtxKey("readeefConfig"), config) dispatcher.Context.SetGlobal(readeef.CtxKey("repo"), repo) fm := readeef.NewFeedManager(repo, config, logger) var processors []parser.Processor for _, p := range config.FeedParser.Processors { switch p { case "relative-url": processors = append(processors, processor.NewRelativeUrl(logger)) case "proxy-http": template := config.FeedParser.ProxyHTTPURLTemplate if template != "" { p, err := processor.NewProxyHTTP(logger, template) if err != nil { return fmt.Errorf("Error initializing Proxy HTTP processor: %v", err) } processors = append(processors, p) capabilities.ProxyHTTP = true } case "cleanup": processors = append(processors, processor.NewCleanup(logger)) case "top-image-marker": processors = append(processors, processor.NewTopImageMarker(logger)) } } fm.ParserProcessors(processors) var sp content.SearchProvider switch config.Content.SearchProvider { case "elastic": if sp, err = search.NewElastic(config.Content.ElasticURL, config.Content.SearchBatchSize, logger); err != nil { logger.Printf("Error initializing Elastic search: %v\n", err) } case "bleve": fallthrough default: if sp, err = search.NewBleve(config.Content.BlevePath, config.Content.SearchBatchSize, logger); err != nil { logger.Printf("Error initializing Bleve search: %v\n", err) } } if sp != nil { if sp.IsNewIndex() { go func() { sp.IndexAllFeeds(repo) }() } } var ce content.Extractor switch config.Content.Extractor { case "readability": if ce, err = extractor.NewReadability(config.Content.ReadabilityKey); err != nil { return fmt.Errorf("Error initializing Readability extractor: %v\n", err) } case "goose": fallthrough default: if ce, err = extractor.NewGoose(dispatcher.Config.Renderer.Dir); err != nil { return fmt.Errorf("Error initializing Goose extractor: %v\n", err) } } if ce != nil { capabilities.Extractor = true } var t content.Thumbnailer switch config.Content.Thumbnailer { case "extract": if t, err = thumbnailer.NewExtract(ce, logger); err != nil { return fmt.Errorf("Error initializing Extract thumbnailer: %v\n", err) } case "description": fallthrough default: t = thumbnailer.NewDescription(logger) } monitors := []content.FeedMonitor{monitor.NewUnread(repo, logger)} for _, m := range config.FeedManager.Monitors { switch m { case "index": if sp != nil { monitors = append(monitors, monitor.NewIndex(sp, logger)) capabilities.Search = true } case "thumbnailer": if t != nil { monitors = append(monitors, monitor.NewThumbnailer(t, logger)) } } } webSocket := NewWebSocket(fm, sp, ce, capabilities) dispatcher.Handle(webSocket) monitors = append(monitors, webSocket) if config.Hubbub.CallbackURL != "" { hubbub := readeef.NewHubbub(repo, config, logger, dispatcher.Pattern, fm.RemoveFeedChannel()) if err := hubbub.InitSubscriptions(); err != nil { return fmt.Errorf("Error initializing hubbub subscriptions: %v", err) } hubbub.FeedMonitors(monitors) fm.Hubbub(hubbub) } fm.FeedMonitors(monitors) fm.Start() nonce := readeef.NewNonce() controllers := []webfw.Controller{ NewAuth(capabilities), NewFeed(fm, sp), NewArticle(config, ce), NewUser(), NewUserSettings(), NewNonce(nonce), } if fm.Hubbub() != nil { controllers = append(controllers, NewHubbubController(fm.Hubbub(), config.Hubbub.RelativePath, fm.AddFeedChannel(), fm.RemoveFeedChannel())) } for _, e := range config.API.Emulators { switch e { case "tt-rss": controllers = append(controllers, NewTtRss(fm, sp)) case "fever": controllers = append(controllers, NewFever()) } } for _, c := range controllers { dispatcher.Handle(c) } middleware.InitializeDefault(dispatcher) dispatcher.RegisterMiddleware(readeef.Auth{Pattern: dispatcher.Pattern, Nonce: nonce, IgnoreURLPrefix: config.Auth.IgnoreURLPrefix}) dispatcher.Renderer = renderer.NewRenderer(dispatcher.Config.Renderer.Dir, dispatcher.Config.Renderer.Base) dispatcher.Renderer.Delims("{%", "%}") go func() { for { select { case <-time.After(5 * time.Minute): nonce.Clean(45 * time.Second) } } }() return nil }
// Initialize creates all configured middleware handlers, producing a chain of // functions to be called on each request. Initializes and registers all // handled controllers. This function is called automatically by the Server // object, when its ListenAndServe method is called. It should be called // directly before calling http.Handle() using the dispatcher when the Server // object is not used. func (d *Dispatcher) Initialize() { if d.Renderer == nil { d.Renderer = renderer.NewRenderer(d.Config.Renderer.Dir, d.Config.Renderer.Base) } d.Context.SetGlobal(context.BaseCtxKey("renderer"), d.Renderer) d.Context.SetGlobal(context.BaseCtxKey("logger"), d.Logger) d.Context.SetGlobal(context.BaseCtxKey("dispatcher"), d) d.Context.SetGlobal(context.BaseCtxKey("config"), d.Config) var mw []Middleware order := []string{} middlewareInserted := make(map[string]bool) for _, m := range d.Config.Dispatcher.Middleware { if custom, ok := d.middleware[m]; ok { d.Logger.Debugf("Queueing middleware %s to the chain.\n", m) mw = append(mw, custom) order = append(order, m) middlewareInserted[m] = true } } reverseOrder := d.middlewareOrder[:] sort.Sort(sort.Reverse(sort.StringSlice(reverseOrder))) for _, name := range reverseOrder { if !middlewareInserted[name] { if custom, ok := d.middleware[name]; ok { d.Logger.Debugf("Queueing middleware %s to the chain.\n", name) mw = append([]Middleware{custom}, mw...) order = append([]string{name}, order...) } } } handler := d.handlerFunc() for _, m := range mw { handler = m.Handler(handler, d.Context) } d.handler = handler d.middlewareOrder = order for i := range d.Controllers { var routes []Route switch c := d.Controllers[i].(type) { case PatternController: r := Route{ c.Pattern(), c.Method(), c.Handler(d.Context), c.Name(), c, } routes = append(routes, r) case MultiPatternController: for _, tuple := range c.Patterns() { r := Route{ tuple.Pattern, tuple.Method, c.Handler(d.Context), "", c, } routes = append(routes, r) } default: panic(fmt.Sprintf("Controllers of type '%T' are not supported\n", c)) } for _, r := range routes { d.Logger.Debugf("Adding route to %s with method %d and controller %T to %s.\n", r.Pattern, r.Method, r.Controller, d.Pattern) if err := d.trie.AddRoute(r); err != nil { panic(fmt.Sprintf("Error adding route for %s to the dispatcher: %v\n", r.Pattern, err)) } } } }
func RegisterControllers(config readeef.Config, dispatcher *webfw.Dispatcher, logger webfw.Logger) error { repo, err := repo.New(config.DB.Driver, config.DB.Connect, logger) if err != nil { return err } if err := initAdminUser(repo, []byte(config.Auth.Secret)); err != nil { return err } dispatcher.Context.SetGlobal(readeef.CtxKey("config"), config) dispatcher.Context.SetGlobal(context.BaseCtxKey("readeefConfig"), config) dispatcher.Context.SetGlobal(readeef.CtxKey("repo"), repo) um := &readeef.UpdateFeedReceiverManager{} fm := readeef.NewFeedManager(repo, config, logger, um) if config.Hubbub.CallbackURL != "" { hubbub := readeef.NewHubbub(repo, config, logger, dispatcher.Pattern, fm.RemoveFeedChannel(), fm.AddFeedChannel(), um) if err := hubbub.InitSubscriptions(); err != nil { return fmt.Errorf("Error initializing hubbub subscriptions: %v", err) } fm.SetHubbub(hubbub) dispatcher.Handle(readeef.NewHubbubController(hubbub)) } var si readeef.SearchIndex if config.SearchIndex.BlevePath != "" { var err error si, err = readeef.NewSearchIndex(repo, config, logger) if err != nil { return fmt.Errorf("Error initializing bleve search: %v", err) } if si.IsNewIndex() { go func() { si.IndexAllArticles() }() } fm.SetSearchIndex(si) } fm.Start() nonce := readeef.NewNonce() var patternController webfw.PatternController var multiPatternController webfw.MultiPatternController patternController = NewAuth() dispatcher.Handle(patternController) multiPatternController = NewFeed(fm) dispatcher.Handle(multiPatternController) multiPatternController = NewArticle(config) dispatcher.Handle(multiPatternController) if config.SearchIndex.BlevePath != "" { patternController = NewSearch(si) dispatcher.Handle(patternController) } multiPatternController = NewUser() dispatcher.Handle(multiPatternController) patternController = NewUserSettings() dispatcher.Handle(patternController) patternController = NewNonce(nonce) dispatcher.Handle(patternController) if config.API.Fever { patternController = NewFever(fm) dispatcher.Handle(patternController) } webSocket := NewWebSocket(fm, si) dispatcher.Handle(webSocket) um.AddUpdateReceiver(webSocket) middleware.InitializeDefault(dispatcher) dispatcher.RegisterMiddleware(readeef.Auth{Pattern: dispatcher.Pattern, Nonce: nonce, IgnoreURLPrefix: config.Auth.IgnoreURLPrefix}) dispatcher.Renderer = renderer.NewRenderer(dispatcher.Config.Renderer.Dir, dispatcher.Config.Renderer.Base) dispatcher.Renderer.Delims("{%", "%}") go func() { for { select { case <-time.After(5 * time.Minute): nonce.Clean(45 * time.Second) } } }() return nil }
func RegisterControllers(config readeef.Config, dispatcher *webfw.Dispatcher, logger *log.Logger) error { db := readeef.NewDB(config.DB.Driver, config.DB.Connect) if err := db.Connect(); err != nil { return errors.New(fmt.Sprintf("Error connecting to database: %v", err)) } updateFeed := make(chan readeef.Feed) fm := readeef.NewFeedManager(db, config, logger, updateFeed) if config.Hubbub.CallbackURL != "" { hubbub := readeef.NewHubbub(db, config, logger, dispatcher.Pattern, fm.RemoveFeedChannel(), fm.AddFeedChannel(), updateFeed) if err := hubbub.InitSubscriptions(); err != nil { return errors.New(fmt.Sprintf("Error initializing hubbub subscriptions: %v", err)) } fm.SetHubbub(hubbub) dispatcher.Handle(readeef.NewHubbubController(hubbub)) } fm.Start() nonce := readeef.NewNonce() var controller webfw.Controller controller = NewAuth() dispatcher.Handle(controller) controller = NewFeed(fm) dispatcher.Handle(controller) controller = NewArticle() dispatcher.Handle(controller) controller = NewUser() dispatcher.Handle(controller) controller = NewUserSettings() dispatcher.Handle(controller) controller = NewFeedUpdateNotificator(updateFeed) dispatcher.Handle(controller) controller = NewNonce(nonce) dispatcher.Handle(controller) middleware.InitializeDefault(dispatcher) dispatcher.RegisterMiddleware(readeef.Auth{DB: db, Pattern: dispatcher.Pattern, Nonce: nonce, IgnoreURLPrefix: config.Auth.IgnoreURLPrefix}) dispatcher.Context.SetGlobal(readeef.CtxKey("config"), config) dispatcher.Context.SetGlobal(readeef.CtxKey("db"), db) dispatcher.Renderer = renderer.NewRenderer(dispatcher.Config.Renderer.Dir, dispatcher.Config.Renderer.Base) dispatcher.Renderer.Delims("{%", "%}") go func() { for { select { case <-time.After(5 * time.Minute): nonce.Clean(45 * time.Second) } } }() return nil }
func TestI18NHandler(t *testing.T) { c := context.NewContext() ren := renderer.NewRenderer("testdata", "test.tmpl") c.SetGlobal(context.BaseCtxKey("renderer"), ren) mw := I18N{ Languages: []string{"en", "bg"}, Pattern: "/", Dir: "testdata", IgnoreURLPrefix: []string{"/css", "/js"}, } h := mw.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := ren.Render(w, nil, c.GetAll(r), "test_i18n.tmpl"); err != nil { t.Fatal(err) } }), c) r, _ := http.NewRequest("GET", "http://localhost:8080", nil) rec := httptest.NewRecorder() h.ServeHTTP(rec, r) if rec.Code != http.StatusFound { t.Fatalf("Expected code %d, got %d\n", http.StatusFound, rec.Code) } r, _ = http.NewRequest("GET", "http://localhost:8080/bg/", nil) r.RequestURI = "/bg/" rec = httptest.NewRecorder() h.ServeHTTP(rec, r) if rec.Code != http.StatusOK { t.Fatalf("Expected code %d, got %d\n", http.StatusOK, rec.Code) } expected := "test data bg" if !strings.Contains(rec.Body.String(), expected) { t.Fatalf("Expected body '%s' to contain '%s'\n", rec.Body.String(), expected) } r, _ = http.NewRequest("GET", "http://localhost:8080/en/", nil) r.RequestURI = "/en/" rec = httptest.NewRecorder() s := context.NewSession([]byte(""), nil, os.TempDir()) s.SetName("test1") c.Set(r, context.BaseCtxKey("session"), s) h.ServeHTTP(rec, r) if rec.Code != http.StatusOK { t.Fatalf("Expected code %d, got %d\n", http.StatusOK, rec.Code) } expected = "test data en" if !strings.Contains(rec.Body.String(), expected) { t.Fatalf("Expected body '%s' to contain '%s'\n", rec.Body.String(), expected) } if s, ok := s.Get("language"); ok { if s.(string) != "en" { t.Fatalf("Expected session.language to be '%s', got '%s'\n", "en", s.(string)) } } else { t.Fatalf("Expected the session to have a language key\n") } r, _ = http.NewRequest("GET", "http://localhost:8080/en", nil) r.RequestURI = "/en" rec = httptest.NewRecorder() h.ServeHTTP(rec, r) if rec.Code != http.StatusFound { t.Fatalf("Expected code %d, got %d\n", http.StatusFound, rec.Code) } expected = "/en/" if rec.Header().Get("Location") != expected { t.Fatalf("Expected a redirect to '%s', got '%s'\n", expected, rec.Header().Get("Location")) } r, _ = http.NewRequest("GET", "http://localhost:8080/foo/bar/baz", nil) r.RequestURI = "/foo/bar/baz" rec = httptest.NewRecorder() h.ServeHTTP(rec, r) if rec.Code != http.StatusFound { t.Fatalf("Expected code %d, got %d\n", http.StatusFound, rec.Code) } expected = "/en/foo/bar/baz" if rec.Header().Get("Location") != expected { t.Fatalf("Expected a redirect to '%s', got '%s'\n", expected, rec.Header().Get("Location")) } r, _ = http.NewRequest("GET", "http://localhost:8080/foo/bar/baz?alpha=beta&gamma=delta", nil) r.RequestURI = "/foo/bar/baz?alpha=beta&gamma=delta" rec = httptest.NewRecorder() h.ServeHTTP(rec, r) if rec.Code != http.StatusFound { t.Fatalf("Expected code %d, got %d\n", http.StatusFound, rec.Code) } expected = "/en/foo/bar/baz?alpha=beta&gamma=delta" if rec.Header().Get("Location") != expected { t.Fatalf("Expected a redirect to '%s', got '%s'\n", expected, rec.Header().Get("Location")) } r, _ = http.NewRequest("GET", "http://localhost:8080/foo/bar/baz?alpha=beta&gamma=delta#test", nil) r.RequestURI = "/foo/bar/baz?alpha=beta&gamma=delta#test" rec = httptest.NewRecorder() h.ServeHTTP(rec, r) if rec.Code != http.StatusFound { t.Fatalf("Expected code %d, got %d\n", http.StatusFound, rec.Code) } expected = "/en/foo/bar/baz?alpha=beta&gamma=delta#test" if rec.Header().Get("Location") != expected { t.Fatalf("Expected a redirect to '%s', got '%s'\n", expected, rec.Header().Get("Location")) } r, _ = http.NewRequest("GET", "http://localhost:8080/css/foo", nil) r.RequestURI = "/css/foo" rec = httptest.NewRecorder() h = mw.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { }), c) h.ServeHTTP(rec, r) if rec.Code != http.StatusOK { t.Fatalf("Expected code %d, got %d\n", http.StatusOK, rec.Code) } r, _ = http.NewRequest("GET", "http://localhost:8080/js/foo", nil) r.RequestURI = "/js/foo" rec = httptest.NewRecorder() h.ServeHTTP(rec, r) if rec.Code != http.StatusOK { t.Fatalf("Expected code %d, got %d\n", http.StatusOK, rec.Code) } }