threatfeed: Pre-parse and cache html templates

This change pre-parses all html templates from the `templates` directory and stores the results globally. With this change, http handlers no longer need to re-parse templates on every request.
This commit is contained in:
Ryan Smith
2025-04-07 16:57:09 -07:00
parent 540b0b940c
commit a1dfb7f648
3 changed files with 16 additions and 24 deletions

View File

@@ -3,7 +3,6 @@ package threatfeed
import (
"cmp"
"encoding/json"
"html/template"
"io"
"net/http"
"os"
@@ -14,8 +13,7 @@ import (
// handleLogsMain serves a static page listing honeypot logs available for
// viewing.
func handleLogsMain(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFS(templates, "templates/logs.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(w, "logs.html", "logs")
_ = parsedTemplates.ExecuteTemplate(w, "logs.html", "logs")
}
// handleLogs directs the request to the appropriate log parser based on the
@@ -65,8 +63,7 @@ func handleLogs(w http.ResponseWriter, r *http.Request) {
// log files.
func displayLogErrorPage(w http.ResponseWriter, err error) {
w.WriteHeader(http.StatusInternalServerError)
tmpl := template.Must(template.ParseFS(templates, "templates/logs-error.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(w, "logs-error.html", map[string]any{"Error": err, "NavData": "logs"})
_ = parsedTemplates.ExecuteTemplate(w, "logs-error.html", map[string]any{"Error": err, "NavData": "logs"})
}
// handleLogSSH serves the SSH honeypot logs as a web page. It opens the
@@ -107,8 +104,7 @@ func handleLogSSH(w http.ResponseWriter) {
}
slices.Reverse(data)
tmpl := template.Must(template.ParseFS(templates, "templates/logs-ssh.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(w, "logs-ssh.html", map[string]any{"Data": data, "NavData": "logs"})
_ = parsedTemplates.ExecuteTemplate(w, "logs-ssh.html", map[string]any{"Data": data, "NavData": "logs"})
}
// handleLogHTTP serves the HTTP honeypot logs as a web page. It opens the
@@ -149,8 +145,7 @@ func handleLogHTTP(w http.ResponseWriter) {
}
slices.Reverse(data)
tmpl := template.Must(template.ParseFS(templates, "templates/logs-http.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(w, "logs-http.html", map[string]any{"Data": data, "NavData": "logs"})
_ = parsedTemplates.ExecuteTemplate(w, "logs-http.html", map[string]any{"Data": data, "NavData": "logs"})
}
// displayStats handles the processing and rendering of statistics for a given
@@ -178,8 +173,7 @@ func displayStats(w http.ResponseWriter, field fieldCounter) {
)
})
tmpl := template.Must(template.ParseFS(templates, "templates/logs-stats.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(
_ = parsedTemplates.ExecuteTemplate(
w,
"logs-stats.html",
map[string]any{

View File

@@ -2,7 +2,6 @@ package threatfeed
import (
"fmt"
"html/template"
"net"
"net/http"
"sync"
@@ -29,8 +28,7 @@ var (
// handleLiveIndex serves a web page that displays honeypot log data in
// real-time through a WebSocket connection.
func handleLiveIndex(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFS(templates, "templates/live.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(w, "live.html", "live")
_ = parsedTemplates.ExecuteTemplate(w, "live.html", "live")
}
// broadcastLogsToClients receives honeypot log data through a byte channel

View File

@@ -22,6 +22,11 @@ import (
//go:embed templates
var templates embed.FS
// parsedTemplates pre-parses and caches all HTML templates when the threat
// feed server starts. This eliminates the need for HTTP handlers to re-parse
// templates on each request.
var parsedTemplates = template.Must(template.ParseFS(templates, "templates/*.html"))
// handlePlain handles HTTP requests to serve the threat feed in plain text. It
// returns a list of IP addresses that interacted with the honeypot servers.
func handlePlain(w http.ResponseWriter, r *http.Request) {
@@ -281,15 +286,13 @@ func handleTAXIIObjects(w http.ResponseWriter, r *http.Request) {
// delivers a static HTML document with information on accessing the threat
// feed.
func handleHome(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFS(templates, "templates/home.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(w, "home.html", "home")
_ = parsedTemplates.ExecuteTemplate(w, "home.html", "home")
}
// handleDocs serves a static page with documentation for accessing the threat
// feed.
func handleDocs(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFS(templates, "templates/docs.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(w, "docs.html", "docs")
_ = parsedTemplates.ExecuteTemplate(w, "docs.html", "docs")
}
// handleCSS serves a CSS stylesheet for styling HTML templates.
@@ -311,8 +314,7 @@ func handleConfig(w http.ResponseWriter, r *http.Request) {
NavData string
}
d := templateData{C: cfg, Version: config.Version, NavData: "config"}
tmpl := template.Must(template.ParseFS(templates, "templates/config.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(w, "config.html", d)
_ = parsedTemplates.ExecuteTemplate(w, "config.html", d)
}
// handleHTML returns the threat feed as a web page for viewing in a browser.
@@ -347,8 +349,7 @@ func handleHTML(w http.ResponseWriter, r *http.Request) {
m = "observations"
}
tmpl := template.Must(template.ParseFS(templates, "templates/webfeed.html", "templates/nav.html"))
_ = tmpl.ExecuteTemplate(
_ = parsedTemplates.ExecuteTemplate(
w,
"webfeed.html",
map[string]any{"Data": prepareFeed(opt), "SortDirection": d, "SortMethod": m, "NavData": "webfeed"},
@@ -459,6 +460,5 @@ func parseParams(r *http.Request) (feedOptions, error) {
// response when a request is made to an undefined path.
func handleNotFound(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
tmpl := template.Must(template.ParseFS(templates, "templates/404.html"))
_ = tmpl.Execute(w, nil)
_ = parsedTemplates.ExecuteTemplate(w, "404.html", nil)
}