v0.2.1
FIXED: * Better request tracking through flow ADDED: * Cross-check detection
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
sysd `github.com/coreos/go-systemd/daemon`
|
||||
`github.com/davecgh/go-spew/spew`
|
||||
`github.com/goccy/go-yaml`
|
||||
`github.com/google/uuid`
|
||||
`r00t2.io/clientinfo/version`
|
||||
`r00t2.io/goutils/multierr`
|
||||
)
|
||||
@@ -261,6 +262,7 @@ func (s *Server) Reload() (err error) {
|
||||
}
|
||||
|
||||
func (s *Server) explicit404(resp http.ResponseWriter, req *http.Request) {
|
||||
s.log.Debug("server.Server.explicit404: '%s' requested '%s %s'", req.RemoteAddr, req.Method, req.URL.String())
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
@@ -278,10 +280,12 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
var parsedFmts []*parsedMIME
|
||||
var renderer outerRenderer
|
||||
var includeFmt string
|
||||
var reqId uuid.UUID = uuid.New()
|
||||
var params url.Values = make(url.Values)
|
||||
var outerFmt string = mediaJSON
|
||||
|
||||
s.log.Debug("server.Server.handleDefault: Handling request:\n%s", spew.Sdump(req))
|
||||
s.log.Info("server.Server.handleDefault: '%s' (%s): '%s %s'", reqId.String(), req.RemoteAddr, req.Method, req.URL.String())
|
||||
s.log.Debug("server.Server.handleDefault: Handling request for '%s':\n%s", reqId.String(), spew.Sdump(req))
|
||||
origin = req.Header.Get("Origin")
|
||||
if s.corsOrigins != nil && len(s.corsOrigins) != 0 && origin != "" {
|
||||
if _, ok = s.corsOrigins[origin]; ok {
|
||||
@@ -291,6 +295,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
resp.Header().Set("ClientInfo-Version", version.Ver.Short())
|
||||
resp.Header().Set(httpCIDHdr, reqId.String())
|
||||
|
||||
page = &Page{
|
||||
Info: &R00tInfo{
|
||||
@@ -305,6 +310,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
RawFmt: nil,
|
||||
Indent: "",
|
||||
DoIndent: false,
|
||||
ReqUUID: reqId,
|
||||
}
|
||||
|
||||
// First the client info.
|
||||
@@ -316,7 +322,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
if remAddrPort != "" {
|
||||
if nAP, err = netip.ParseAddrPort(remAddrPort); err != nil {
|
||||
s.log.Warning("server.Server.handleDefault: Failed to parse remote address '%s': %v", remAddrPort, err)
|
||||
s.log.Warning("server.Server.handleDefault: Failed to parse remote address '%s' for '%s': %v", remAddrPort, reqId.String(), err)
|
||||
// Don't return an error in case we're doing weird things like direct socket clients.
|
||||
/*
|
||||
http.Error(resp, "ERROR: Failed to parse client address", http.StatusInternalServerError)
|
||||
@@ -329,13 +335,16 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
if req.URL != nil {
|
||||
params = req.URL.Query()
|
||||
if params.Has(urlParamXCheck) {
|
||||
s.log.Info("server.Server.handleDefault: Cross-stack check: '%s' => '%s'", params.Get(urlParamXCheck), reqId.String())
|
||||
}
|
||||
}
|
||||
uas = req.Header.Values("User-Agent")
|
||||
if uas != nil && len(uas) > 0 {
|
||||
page.Info.Client = make([]*R00tClient, 0, len(uas))
|
||||
for _, ua := range uas {
|
||||
if parsedUA, err = NewClient(ua); err != nil {
|
||||
s.log.Err("server.Server.handleDefault: Failed to create client for '%s': %v", ua, err)
|
||||
s.log.Err("server.Server.handleDefault: Failed to create client for UA '%s' via request '%s': %v", ua, reqId.String(), err)
|
||||
http.Error(resp, fmt.Sprintf("ERROR: Failed to parse 'User-Agent' '%s'", ua), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -363,7 +372,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
reqdMimes = req.Header.Values("Accept")
|
||||
if reqdMimes != nil && len(reqdMimes) > 0 {
|
||||
if parsedFmts, err = parseAccept(strings.Join(reqdMimes, ",")); err != nil {
|
||||
s.log.Err("server.Server.handleDefault: Failed to parse Accept header '%#v' for '%s': %v", reqdMimes, remAddrPort, err)
|
||||
s.log.Err("server.Server.handleDefault: Failed to parse Accept header '%#v' for '%s' ('%s'): %v", reqdMimes, remAddrPort, reqId.String(), err)
|
||||
resp.Header()["Accept"] = okAcceptMime
|
||||
http.Error(
|
||||
resp,
|
||||
@@ -374,12 +383,12 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
if outerFmt, err = decideParseAccept(parsedFmts, outerFmt); err != nil {
|
||||
if errors.Is(err, ErrUnsupportedMIME) {
|
||||
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' via '%#v'.", remAddrPort, reqdMimes)
|
||||
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' via '%#v' for '%s'.", remAddrPort, reqdMimes, reqId.String())
|
||||
resp.Header()["Accept"] = okAcceptMime
|
||||
http.Error(resp, "ERROR: No supported MIME type specified via request 'Accept'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
|
||||
return
|
||||
} else {
|
||||
s.log.Err("server.Server.handleDefault: Received unknown error choosing from Accept header for '%s': %v", remAddrPort, err)
|
||||
s.log.Err("server.Server.handleDefault: Received unknown error choosing from Accept header for '%s' from '%s': %v", remAddrPort, reqId.String(), err)
|
||||
http.Error(resp, "ERROR: Unknown error occurred when negotiating MIME type.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -388,7 +397,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
// `mime` URL query parameter.
|
||||
if params.Has("mime") {
|
||||
if parsedFmts, err = parseAccept(strings.Join(params["mime"], ",")); err != nil {
|
||||
s.log.Err("server.Server.handleDefault: Failed to parse 'mime' URL parameter '%#v' for '%s': %v", params["mime"], remAddrPort, err)
|
||||
s.log.Err("server.Server.handleDefault: Failed to parse 'mime' URL parameter '%#v' for '%s' (%s): %v", params["mime"], remAddrPort, reqId.String(), err)
|
||||
resp.Header()["Accept"] = okAcceptMime
|
||||
http.Error(
|
||||
resp,
|
||||
@@ -399,12 +408,12 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
if outerFmt, err = decideParseAccept(parsedFmts, outerFmt); err != nil {
|
||||
if errors.Is(err, ErrUnsupportedMIME) {
|
||||
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' via '%#v'.", remAddrPort, params["mime"])
|
||||
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' (%s) via '%#v'.", remAddrPort, reqId.String(), params["mime"])
|
||||
resp.Header()["Accept"] = okAcceptMime
|
||||
http.Error(resp, "ERROR: No supported MIME type specified via URL parameter 'mime'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
|
||||
return
|
||||
} else {
|
||||
s.log.Err("server.Server.handleDefault: Received unknown error choosing from 'mime' URL parameter for '%s': %v", remAddrPort, err)
|
||||
s.log.Err("server.Server.handleDefault: Received unknown error choosing from 'mime' URL parameter for '%s' (%s): %v", remAddrPort, reqId.String(), err)
|
||||
http.Error(resp, "ERROR: Unknown error occurred when negotiating MIME type.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -413,7 +422,7 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
// 'include' URL query parameter (only for text/html).
|
||||
if outerFmt == mediaHTML && params.Has("include") {
|
||||
if parsedFmts, err = parseAccept(strings.Join(params["include"], ",")); err != nil {
|
||||
s.log.Err("server.Server.handleDefault: Failed to parse 'include' URL parameter '%#v' for '%s': %v", params["include"], remAddrPort, err)
|
||||
s.log.Err("server.Server.handleDefault: Failed to parse 'include' URL parameter '%#v' for '%s' (%s): %v", params["include"], remAddrPort, reqId.String(), err)
|
||||
resp.Header()["Accept"] = okAcceptMime
|
||||
http.Error(
|
||||
resp,
|
||||
@@ -424,12 +433,12 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
if includeFmt, err = decideParseAccept(parsedFmts, includeFmt); err != nil {
|
||||
if errors.Is(err, ErrUnsupportedMIME) {
|
||||
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' via '%#v'.", remAddrPort, params["include"])
|
||||
s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s' (%s) via '%#v'.", remAddrPort, reqId.String(), params["include"])
|
||||
resp.Header()["Accept"] = okAcceptMime
|
||||
http.Error(resp, "ERROR: No supported MIME type specified via URL parameter 'include'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
|
||||
return
|
||||
} else {
|
||||
s.log.Err("server.Server.handleDefault: Received unknown error choosing from 'include' URL parameter for '%s': %v", remAddrPort, err)
|
||||
s.log.Err("server.Server.handleDefault: Received unknown error choosing from 'include' URL parameter for '%s' (%s): %v", remAddrPort, reqId.String(), err)
|
||||
http.Error(resp, "ERROR: Unknown error occurred when negotiating MIME type.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -459,13 +468,13 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
|
||||
case mediaYAML:
|
||||
renderer = s.renderYML
|
||||
default:
|
||||
s.log.Err("server.Server.handleDefault: Unknown output format '%s'", outerFmt)
|
||||
s.log.Err("server.Server.handleDefault: Unknown output format '%s' from '%s' (%s)", outerFmt, remAddrPort, reqId.String())
|
||||
http.Error(resp, "ERROR: Unable to determine default renderer.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderer(page, resp); err != nil {
|
||||
s.log.Err("server.Server.handleDefault: Failed to render request from '%s' as '%s': %v", remAddrPort, outerFmt, err)
|
||||
s.log.Err("server.Server.handleDefault: Failed to render request from '%s' (%s) as '%s': %v", remAddrPort, reqId.String(), outerFmt, err)
|
||||
// The renderer handles the error-handling with the client.
|
||||
return
|
||||
}
|
||||
@@ -481,20 +490,23 @@ func (s *Server) handleAbout(resp http.ResponseWriter, req *http.Request) {
|
||||
Req: req,
|
||||
},
|
||||
PageType: "about",
|
||||
ReqUUID: uuid.New(),
|
||||
}
|
||||
|
||||
s.log.Debug("server.Server.handleAbout: Handling request:\n%s", spew.Sdump(req))
|
||||
s.log.Info("server.Server.handleAbout: '%s' (%s)", renderPage.ReqUUID.String(), req.RemoteAddr)
|
||||
s.log.Debug("server.Server.handleAbout: Handling request for '%s':\n%s", renderPage.ReqUUID.String(), spew.Sdump(req))
|
||||
|
||||
resp.Header().Set("ClientInfo-Version", version.Ver.Short())
|
||||
resp.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
resp.Header().Set(httpCIDHdr, renderPage.ReqUUID.String())
|
||||
|
||||
if err = tpl.ExecuteTemplate(resp, "about", renderPage); err != nil {
|
||||
s.log.Err("server.Server.handleAbout: Failed to execute template for '%s': %v", req.RemoteAddr, err)
|
||||
s.log.Err("server.Server.handleAbout: Failed to execute template for '%s': %v", renderPage.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s.log.Debug("server.Server.handleAbout: Handled request:\n%s", spew.Sdump(req))
|
||||
s.log.Debug("server.Server.handleAbout: Handled request for '%s'", renderPage.ReqUUID.String())
|
||||
|
||||
return
|
||||
}
|
||||
@@ -507,20 +519,23 @@ func (s *Server) handleUsage(resp http.ResponseWriter, req *http.Request) {
|
||||
Req: req,
|
||||
},
|
||||
PageType: "usage",
|
||||
ReqUUID: uuid.New(),
|
||||
}
|
||||
|
||||
s.log.Debug("server.Server.handleUsage: Handling request:\n%s", spew.Sdump(req))
|
||||
s.log.Info("server.Server.handleUsage: '%s' (%s)", renderPage.ReqUUID.String(), req.RemoteAddr)
|
||||
s.log.Debug("server.Server.handleUsage: Handling request for '%s':\n%s", renderPage.ReqUUID.String(), spew.Sdump(req))
|
||||
|
||||
resp.Header().Set("ClientInfo-Version", version.Ver.Short())
|
||||
resp.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
resp.Header().Set(httpCIDHdr, renderPage.ReqUUID.String())
|
||||
|
||||
if err = tpl.ExecuteTemplate(resp, "usage", renderPage); err != nil {
|
||||
s.log.Err("server.Server.handleAbout: Failed to execute template for '%s': %v", req.RemoteAddr, err)
|
||||
s.log.Err("server.Server.handleAbout: Failed to execute template for '%s' (%s): %v", renderPage.ReqUUID.String(), req.RemoteAddr, err)
|
||||
http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s.log.Debug("server.Server.handleUsage: Handled request:\n%s", spew.Sdump(req))
|
||||
s.log.Debug("server.Server.handleUsage: Handled request for '%s'", renderPage.ReqUUID.String())
|
||||
|
||||
return
|
||||
}
|
||||
@@ -533,7 +548,7 @@ func (s *Server) renderJSON(page *Page, resp http.ResponseWriter) (err error) {
|
||||
|
||||
if page.DoIndent {
|
||||
if b, err = json.MarshalIndent(page.Info, "", page.Indent); err != nil {
|
||||
s.log.Err("server.Server.renderJSON: Failed to render to indented JSON: %v", err)
|
||||
s.log.Err("server.Server.renderJSON: Failed to render to indented JSON for '%s': %v", page.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render JSON", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -566,13 +581,13 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
|
||||
case mediaJSON, mediaXML:
|
||||
if page.DoIndent {
|
||||
if b, err = mediaIndent[*page.RawFmt](page.Info, "", page.Indent); err != nil {
|
||||
s.log.Err("server.Server.renderHTML: Failed to render to indented include '%s': %v", *page.RawFmt, err)
|
||||
s.log.Err("server.Server.renderHTML: Failed to render to indented include '%s' for '%s': %v", *page.RawFmt, page.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if b, err = mediaNoIndent[*page.RawFmt](page.Info); err != nil {
|
||||
s.log.Err("server.Server.renderHTML: Failed to render to include '%s': %v", *page.RawFmt, err)
|
||||
s.log.Err("server.Server.renderHTML: Failed to render to include '%s' for '%s': %v", *page.RawFmt, page.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -580,7 +595,9 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
|
||||
// Non-indentable
|
||||
case mediaYAML:
|
||||
if b, err = mediaNoIndent[*page.RawFmt](page.Info); err != nil {
|
||||
s.log.Err("server.Server.renderHTML: Failed to render to '%s': %v", *page.RawFmt, err)
|
||||
s.log.Err("server.Server.renderHTML: Failed to render to '%s' for '%s': %v", *page.RawFmt, page.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
if b != nil {
|
||||
@@ -592,7 +609,7 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
|
||||
resp.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
|
||||
if err = tpl.ExecuteTemplate(resp, "index", page); err != nil {
|
||||
s.log.Err("server.Server.renderHTML: Failed to render template: %v", err)
|
||||
s.log.Err("server.Server.renderHTML: Failed to render template for '%s': %v", page.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -606,13 +623,13 @@ func (s *Server) renderXML(page *Page, resp http.ResponseWriter) (err error) {
|
||||
|
||||
if page.DoIndent {
|
||||
if b, err = xml.MarshalIndent(page.Info, "", page.Indent); err != nil {
|
||||
s.log.Err("server.Server.renderXML: Failed to render to indented XML: %v", err)
|
||||
s.log.Err("server.Server.renderXML: Failed to render to indented XML for '%s': %v", page.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render XML", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if b, err = xml.Marshal(page.Info); err != nil {
|
||||
s.log.Err("server.Server.renderXML: Failed to render to XML: %v", err)
|
||||
s.log.Err("server.Server.renderXML: Failed to render to XML for '%s': %v", page.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render XML", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -621,7 +638,7 @@ func (s *Server) renderXML(page *Page, resp http.ResponseWriter) (err error) {
|
||||
resp.Header().Set("Content-Type", "application/xml; charset=utf-8")
|
||||
|
||||
if _, err = resp.Write(b); err != nil {
|
||||
s.log.Err("server.Server.renderXML: Failed to send XML: %v", err)
|
||||
s.log.Err("server.Server.renderXML: Failed to send XML to '%s': %v", page.ReqUUID.String(), err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -633,15 +650,15 @@ func (s *Server) renderYML(page *Page, resp http.ResponseWriter) (err error) {
|
||||
var b []byte
|
||||
|
||||
if b, err = yaml.Marshal(page.Info); err != nil {
|
||||
s.log.Err("server.Server.renderJSON: Failed to render to JSON: %v", err)
|
||||
http.Error(resp, "ERROR: Failed to render JSON", http.StatusInternalServerError)
|
||||
s.log.Err("server.Server.renderYML: Failed to render to YAML for '%s': %v", page.ReqUUID.String(), err)
|
||||
http.Error(resp, "ERROR: Failed to render YAML", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Header().Set("Content-Type", "application/yaml")
|
||||
|
||||
if _, err = resp.Write(b); err != nil {
|
||||
s.log.Err("server.Server.renderJSON: Failed to send JSON: %v", err)
|
||||
s.log.Err("server.Server.renderYML: Failed to send YAML to '%s': %v", page.ReqUUID.String(), err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user