mirror of https://github.com/grafana/grafana
FEMT: Basic frontend-service implementation (#104229)
* create the most basic frontend-server module * expose prom metrics?? * add todo list * move frontend-service to its own folder in services * check error from writer.Write * reword comment, add launch configpull/103987/head^2
parent
8e6b8fad01
commit
a8aa6b74a8
@ -0,0 +1,109 @@ |
|||||||
|
package frontend |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"net" |
||||||
|
"net/http" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/grafana/dskit/services" |
||||||
|
"github.com/grafana/grafana/pkg/infra/log" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
"github.com/prometheus/client_golang/prometheus" |
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp" |
||||||
|
) |
||||||
|
|
||||||
|
type frontendService struct { |
||||||
|
*services.BasicService |
||||||
|
cfg *setting.Cfg |
||||||
|
httpServ *http.Server |
||||||
|
log log.Logger |
||||||
|
errChan chan error |
||||||
|
promGatherer prometheus.Gatherer |
||||||
|
} |
||||||
|
|
||||||
|
func ProvideFrontendService(cfg *setting.Cfg, promGatherer prometheus.Gatherer) (*frontendService, error) { |
||||||
|
s := &frontendService{ |
||||||
|
cfg: cfg, |
||||||
|
log: log.New("frontend-server"), |
||||||
|
promGatherer: promGatherer, |
||||||
|
} |
||||||
|
s.BasicService = services.NewBasicService(s.start, s.running, s.stop) |
||||||
|
return s, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s *frontendService) start(ctx context.Context) error { |
||||||
|
s.httpServ = s.newFrontendServer(ctx) |
||||||
|
s.errChan = make(chan error) |
||||||
|
go func() { |
||||||
|
s.errChan <- s.httpServ.ListenAndServe() |
||||||
|
}() |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s *frontendService) running(ctx context.Context) error { |
||||||
|
select { |
||||||
|
case <-ctx.Done(): |
||||||
|
return nil |
||||||
|
case err := <-s.errChan: |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (s *frontendService) stop(failureReason error) error { |
||||||
|
s.log.Info("stopping frontend server", "reason", failureReason) |
||||||
|
if err := s.httpServ.Shutdown(context.Background()); err != nil { |
||||||
|
s.log.Error("failed to shutdown frontend server", "error", err) |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s *frontendService) newFrontendServer(ctx context.Context) *http.Server { |
||||||
|
s.log.Info("starting frontend server", "addr", ":"+s.cfg.HTTPPort) |
||||||
|
|
||||||
|
router := http.NewServeMux() |
||||||
|
router.Handle("/metrics", promhttp.HandlerFor(s.promGatherer, promhttp.HandlerOpts{EnableOpenMetrics: true})) |
||||||
|
router.HandleFunc("/", s.handleRequest) |
||||||
|
|
||||||
|
server := &http.Server{ |
||||||
|
// 5s timeout for header reads to avoid Slowloris attacks (https://thetooth.io/blog/slowloris-attack/)
|
||||||
|
ReadHeaderTimeout: 5 * time.Second, |
||||||
|
Addr: ":" + s.cfg.HTTPPort, |
||||||
|
Handler: router, |
||||||
|
BaseContext: func(_ net.Listener) context.Context { return ctx }, |
||||||
|
} |
||||||
|
|
||||||
|
return server |
||||||
|
} |
||||||
|
|
||||||
|
func (s *frontendService) handleRequest(writer http.ResponseWriter, request *http.Request) { |
||||||
|
// This should:
|
||||||
|
// - get correct asset urls from fs or cdn
|
||||||
|
// - generate a nonce
|
||||||
|
// - render them into the index.html
|
||||||
|
// - and return it to the user!
|
||||||
|
|
||||||
|
s.log.Info("handling request", "method", request.Method, "url", request.URL.String()) |
||||||
|
htmlContent := `<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Grafana Frontend Server</title> |
||||||
|
<style> |
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
} |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>Grafana Frontend Server</h1> |
||||||
|
<p>This is a simple static HTML page served by the Grafana frontend server module.</p> |
||||||
|
</body> |
||||||
|
</html>` |
||||||
|
|
||||||
|
writer.Header().Set("Content-Type", "text/html; charset=utf-8") |
||||||
|
_, err := writer.Write([]byte(htmlContent)) |
||||||
|
if err != nil { |
||||||
|
s.log.Error("could not write to response", "err", err) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue