package server import ( "context" "fmt" "html/template" "net" "net/http" "github.com/0xERR0R/blocky/resolver" "github.com/0xERR0R/blocky/api" "github.com/0xERR0R/blocky/config" "github.com/0xERR0R/blocky/docs" "github.com/0xERR0R/blocky/log" "github.com/0xERR0R/blocky/model" "github.com/0xERR0R/blocky/util" "github.com/0xERR0R/blocky/web" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/miekg/dns" ) const ( dohMessageLimit = 512 contentTypeHeader = "content-type" dnsContentType = "application/dns-message" htmlContentType = "text/html; charset=UTF-8" yamlContentType = "text/yaml" ) func (s *Server) createOpenAPIInterfaceImpl() (impl api.StrictServerInterface, err error) { bControl, err := resolver.GetFromChainWithType[api.BlockingControl](s.queryResolver) if err != nil { return nil, fmt.Errorf("no blocking API implementation found %w", err) } refresher, err := resolver.GetFromChainWithType[api.ListRefresher](s.queryResolver) if err != nil { return nil, fmt.Errorf("no refresh API implementation found %w", err) } cacheControl, err := resolver.GetFromChainWithType[api.CacheControl](s.queryResolver) if err != nil { return nil, fmt.Errorf("no cache API implementation found %w", err) } return api.NewOpenAPIInterfaceImpl(bControl, s, refresher, cacheControl), nil } func (s *Server) Query( ctx context.Context, serverHost string, clientIP net.IP, question string, qType dns.Type, ) (*model.Response, error) { msg := util.NewMsgWithQuestion(question, qType) clientID := extractClientIDFromHost(serverHost) ctx, req := newRequest(ctx, clientIP, clientID, model.RequestProtocolTCP, msg) return s.resolve(ctx, req) } func configureHTTPRouter(router chi.Router, cfg *config.Config) { configureDebugHandler(router) configureDocsHandler(router) configureStaticAssetsHandler(router) configureRootHandler(cfg, router) } func configureDocsHandler(router chi.Router) { router.Get("/docs/openapi.yaml", func(writer http.ResponseWriter, request *http.Request) { writer.Header().Set(contentTypeHeader, yamlContentType) _, err := writer.Write([]byte(docs.OpenAPI)) logAndResponseWithError(err, "can't write OpenAPI definition file: ", writer) }) } func configureStaticAssetsHandler(router chi.Router) { assets, err := web.Assets() util.FatalOnError("unable to load static asset files", err) fs := http.FileServer(http.FS(assets)) router.Handle("/static/*", http.StripPrefix("/static/", fs)) } func configureRootHandler(cfg *config.Config, router chi.Router) { router.Get("/", func(writer http.ResponseWriter, request *http.Request) { writer.Header().Set(contentTypeHeader, htmlContentType) t := template.New("index") _, _ = t.Parse(web.IndexTmpl) type HandlerLink struct { URL string Title string } type PageData struct { Links []HandlerLink Version string BuildTime string } pd := PageData{ Links: nil, Version: util.Version, BuildTime: util.BuildTime, } pd.Links = []HandlerLink{ { URL: "/docs/openapi.yaml", Title: "Rest API Documentation (OpenAPI)", }, { URL: "/static/rapidoc.html", Title: "Interactive Rest API Documentation (RapiDoc)", }, { URL: "/debug/", Title: "Go Profiler", }, } if cfg.Prometheus.Enable { pd.Links = append(pd.Links, HandlerLink{ URL: cfg.Prometheus.Path, Title: "Prometheus endpoint", }) } err := t.Execute(writer, pd) logAndResponseWithError(err, "can't write index template: ", writer) }) } func logAndResponseWithError(err error, message string, writer http.ResponseWriter) { if err != nil { log.Log().Error(message, log.EscapeInput(err.Error())) http.Error(writer, err.Error(), http.StatusInternalServerError) } } func configureDebugHandler(router chi.Router) { router.Mount("/debug", middleware.Profiler()) }