diff options
author | Brian Picciano <mediocregopher@gmail.com> | 2022-05-20 11:17:31 -0600 |
---|---|---|
committer | Brian Picciano <mediocregopher@gmail.com> | 2022-05-20 11:17:31 -0600 |
commit | 09acb111a2b22f5794541fac175b024dd0f9100e (patch) | |
tree | 11d4578a42ad4aea968b42a2689f64c799f9176e /srv/src/api/api.go | |
parent | f69ed83de73bbfc4b7af0931de6ced8cf12dea61 (diff) |
Rename api package to http
Diffstat (limited to 'srv/src/api/api.go')
-rw-r--r-- | srv/src/api/api.go | 249 |
1 files changed, 0 insertions, 249 deletions
diff --git a/srv/src/api/api.go b/srv/src/api/api.go deleted file mode 100644 index 191a7bf..0000000 --- a/srv/src/api/api.go +++ /dev/null @@ -1,249 +0,0 @@ -// Package api implements the HTTP-based api for the mediocre-blog. -package api - -import ( - "context" - "errors" - "fmt" - "html/template" - "net" - "net/http" - "net/http/httputil" - "net/url" - "os" - - "github.com/mediocregopher/blog.mediocregopher.com/srv/api/apiutil" - "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg" - "github.com/mediocregopher/blog.mediocregopher.com/srv/chat" - "github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist" - "github.com/mediocregopher/blog.mediocregopher.com/srv/post" - "github.com/mediocregopher/blog.mediocregopher.com/srv/pow" - "github.com/mediocregopher/mediocre-go-lib/v2/mctx" - "github.com/mediocregopher/mediocre-go-lib/v2/mlog" -) - -// Params are used to instantiate a new API instance. All fields are required -// unless otherwise noted. -type Params struct { - Logger *mlog.Logger - PowManager pow.Manager - - // PathPrefix, if given, will be prefixed to all url paths which are - // rendered by the API's templating system. - PathPrefix string - - PostStore post.Store - PostAssetStore post.AssetStore - - MailingList mailinglist.MailingList - - GlobalRoom chat.Room - UserIDCalculator *chat.UserIDCalculator - - // ListenProto and ListenAddr are passed into net.Listen to create the - // API's listener. Both "tcp" and "unix" protocols are explicitly - // supported. - ListenProto, ListenAddr string - - // StaticDir and StaticProxy are mutually exclusive. - // - // If StaticDir is set then that directory on the filesystem will be used to - // serve the static site. - // - // Otherwise if StaticProxy is set all requests for the static site will be - // reverse-proxied there. - StaticDir string - StaticProxy *url.URL - - // AuthUsers keys are usernames which are allowed to edit server-side data, - // and the values are the password hash which accompanies those users. The - // password hash must have been produced by NewPasswordHash. - AuthUsers map[string]string -} - -// SetupCfg implement the cfg.Cfger interface. -func (p *Params) SetupCfg(cfg *cfg.Cfg) { - - cfg.StringVar(&p.ListenProto, "listen-proto", "tcp", "Protocol to listen for HTTP requests with") - cfg.StringVar(&p.ListenAddr, "listen-addr", ":4000", "Address/path to listen for HTTP requests on") - - cfg.StringVar(&p.StaticDir, "static-dir", "", "Directory from which static files are served (mutually exclusive with -static-proxy-url)") - staticProxyURLStr := cfg.String("static-proxy-url", "", "HTTP address from which static files are served (mutually exclusive with -static-dir)") - - cfg.OnInit(func(ctx context.Context) error { - if *staticProxyURLStr != "" { - var err error - if p.StaticProxy, err = url.Parse(*staticProxyURLStr); err != nil { - return fmt.Errorf("parsing -static-proxy-url: %w", err) - } - - } else if p.StaticDir == "" { - return errors.New("-static-dir or -static-proxy-url is required") - } - - return nil - }) -} - -// Annotate implements mctx.Annotator interface. -func (p *Params) Annotate(a mctx.Annotations) { - a["listenProto"] = p.ListenProto - a["listenAddr"] = p.ListenAddr - - if p.StaticProxy != nil { - a["staticProxy"] = p.StaticProxy.String() - return - } - - a["staticDir"] = p.StaticDir -} - -// API will listen on the port configured for it, and serve HTTP requests for -// the mediocre-blog. -type API interface { - Shutdown(ctx context.Context) error -} - -type api struct { - params Params - srv *http.Server - - redirectTpl *template.Template -} - -// New initializes and returns a new API instance, including setting up all -// listening ports. -func New(params Params) (API, error) { - - l, err := net.Listen(params.ListenProto, params.ListenAddr) - if err != nil { - return nil, fmt.Errorf("creating listen socket: %w", err) - } - - if params.ListenProto == "unix" { - if err := os.Chmod(params.ListenAddr, 0777); err != nil { - return nil, fmt.Errorf("chmod-ing unix socket: %w", err) - } - } - - a := &api{ - params: params, - } - - a.redirectTpl = a.mustParseTpl("redirect.html") - - a.srv = &http.Server{Handler: a.handler()} - - go func() { - - err := a.srv.Serve(l) - if err != nil && !errors.Is(err, http.ErrServerClosed) { - ctx := mctx.Annotate(context.Background(), a.params) - params.Logger.Fatal(ctx, "serving http server", err) - } - }() - - return a, nil -} - -func (a *api) Shutdown(ctx context.Context) error { - if err := a.srv.Shutdown(ctx); err != nil { - return err - } - - return nil -} - -func (a *api) handler() http.Handler { - - var staticHandler http.Handler - if a.params.StaticDir != "" { - staticHandler = http.FileServer(http.Dir(a.params.StaticDir)) - } else { - staticHandler = httputil.NewSingleHostReverseProxy(a.params.StaticProxy) - } - - // sugar - - requirePow := func(h http.Handler) http.Handler { - return a.requirePowMiddleware(h) - } - - formMiddleware := func(h http.Handler) http.Handler { - h = checkCSRFMiddleware(h) - h = disallowGetMiddleware(h) - h = logReqMiddleware(h) - h = addResponseHeaders(map[string]string{ - "Cache-Control": "no-store, max-age=0", - "Pragma": "no-cache", - "Expires": "0", - }, h) - return h - } - - auther := NewAuther(a.params.AuthUsers) - - mux := http.NewServeMux() - - mux.Handle("/", staticHandler) - - { - apiMux := http.NewServeMux() - apiMux.Handle("/pow/challenge", a.newPowChallengeHandler()) - apiMux.Handle("/pow/check", - requirePow( - http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {}), - ), - ) - - apiMux.Handle("/mailinglist/subscribe", requirePow(a.mailingListSubscribeHandler())) - apiMux.Handle("/mailinglist/finalize", a.mailingListFinalizeHandler()) - apiMux.Handle("/mailinglist/unsubscribe", a.mailingListUnsubscribeHandler()) - - apiMux.Handle("/chat/global/", http.StripPrefix("/chat/global", newChatHandler( - a.params.GlobalRoom, - a.params.UserIDCalculator, - a.requirePowMiddleware, - ))) - - mux.Handle("/api/", http.StripPrefix("/api", formMiddleware(apiMux))) - } - - { - v2Mux := http.NewServeMux() - v2Mux.Handle("/follow.html", a.renderDumbTplHandler("follow.html")) - v2Mux.Handle("/posts/", http.StripPrefix("/posts", - apiutil.MethodMux(map[string]http.Handler{ - "GET": a.renderPostHandler(), - "EDIT": a.editPostHandler(), - "POST": authMiddleware(auther, - formMiddleware(a.postPostHandler()), - ), - "DELETE": authMiddleware(auther, - formMiddleware(a.deletePostHandler()), - ), - "PREVIEW": formMiddleware(a.previewPostHandler()), - }), - )) - v2Mux.Handle("/assets/", http.StripPrefix("/assets", - apiutil.MethodMux(map[string]http.Handler{ - "GET": a.getPostAssetHandler(), - "POST": authMiddleware(auther, - formMiddleware(a.postPostAssetHandler()), - ), - "DELETE": authMiddleware(auther, - formMiddleware(a.deletePostAssetHandler()), - ), - }), - )) - v2Mux.Handle("/", a.renderIndexHandler()) - - mux.Handle("/v2/", http.StripPrefix("/v2", v2Mux)) - } - - var globalHandler http.Handler = mux - globalHandler = setCSRFMiddleware(globalHandler) - globalHandler = setLoggerMiddleware(a.params.Logger, globalHandler) - - return globalHandler -} |