BUGFIX: Content-Type, Nil Raw Body Edge Case, Links

FIXES:
* There was an edge case where a raw body for HTML would potentially
  result in a nil byte slice exception. This has been fixed.
  (I don't even know if it was possible, I just made sure it wasn't.)
* The links browser is now explicitly returned as HTML and properly
  detected as a "browser".
* Hyperlinks for links, w3m added to Usage page.
* Content-Type is now always set correctly; there were cases where it
  was improperly returning e.g. text/plain for JSON.
This commit is contained in:
brent saner 2024-12-19 02:27:53 -05:00
parent 8a0465a0f4
commit 6b75e17f48
Signed by: bts
GPG Key ID: 8C004C2F93481F6B
5 changed files with 55 additions and 6 deletions

20
README.adoc Normal file
View File

@ -0,0 +1,20 @@
= ClientInfo
r00t^2 <brent.saner@gmail.com>
Last rendered {localdatetime}
:doctype: book
:docinfo: shared
:data-uri:
:imagesdir: images
:sectlinks:
:sectnums:
:sectnumlevels: 7
:toc: preamble
:toc2: left
:idprefix:
:toclevels: 7
//:toclevels: 4
:source-highlighter: rouge
:docinfo: shared

== DOCS
**TODO!**

8
TODO
View File

@ -1,3 +1,7 @@
- suggest browser, OS alternatives (again)
- link to ipinfo.io for client IP (again)
-- or just implement myself
- implement ipinfo.io equiv. myself?
- also link to https://www.useragentstring.com/
-- or maybe https://www.whatsmyua.info/
-- see https://www.useragentstring.com/pages/api.php
- note that though lynx and elinks are "graphical", links is considered text. w3m is considered graphical.
- fix the text/plain issue for the json (et. al.) renderer

View File

@ -79,10 +79,15 @@ var (
mediaJSON: json.MarshalIndent,
mediaXML: xml.MarshalIndent,
}
// valid MIMEs.
okAcceptMime []string = []string{
mediaJSON,
mediaXML,
mediaYAML,
mediaHTML,
}
// These are actually HTML.
htmlOverride map[string]bool = map[string]bool{
"Links": true,
}
)

View File

@ -19,6 +19,7 @@ import (
sysd "github.com/coreos/go-systemd/daemon"
"github.com/davecgh/go-spew/spew"
`github.com/goccy/go-yaml`
`r00t2.io/clientinfo/version`
"r00t2.io/goutils/multierr"
)

@ -280,6 +281,8 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {

s.log.Debug("server.Server.handleDefault: Handling request:\n%s", spew.Sdump(req))

resp.Header().Set("ClientInfo-Version", version.Ver.Short())

page = &Page{
Info: &R00tInfo{
Client: nil,
@ -327,6 +330,9 @@ func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
http.Error(resp, fmt.Sprintf("ERROR: Failed to parse 'User-Agent' '%s'", ua), http.StatusInternalServerError)
return
}
if parsedUA.Name != nil && htmlOverride[*parsedUA.Name] {
parsedUA.IsDesktop = true
}
page.Info.Client = append(page.Info.Client, parsedUA)
}
}
@ -470,6 +476,7 @@ func (s *Server) handleAbout(resp http.ResponseWriter, req *http.Request) {

s.log.Debug("server.Server.handleAbout: Handling request:\n%s", spew.Sdump(req))

resp.Header().Set("ClientInfo-Version", version.Ver.Short())
resp.Header().Set("Content-Type", "text/html; charset=utf-8")

if err = tpl.ExecuteTemplate(resp, "about", renderPage); err != nil {
@ -495,7 +502,9 @@ func (s *Server) handleUsage(resp http.ResponseWriter, req *http.Request) {

s.log.Debug("server.Server.handleUsage: Handling request:\n%s", spew.Sdump(req))

resp.Header().Set("ClientInfo-Version", version.Ver.Short())
resp.Header().Set("Content-Type", "text/html; charset=utf-8")

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)
http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
@ -511,6 +520,8 @@ func (s *Server) renderJSON(page *Page, resp http.ResponseWriter) (err error) {

var b []byte

resp.Header().Set("Content-Type", "application/json")

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)
@ -561,10 +572,14 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
s.log.Err("server.Server.renderHTML: Failed to render to '%s': %v", *page.RawFmt, err)
}
}
page.Raw = new(string)
*page.Raw = string(b)
if b != nil {
page.Raw = new(string)
*page.Raw = string(b)
}
}

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)
http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
@ -591,6 +606,9 @@ func (s *Server) renderXML(page *Page, resp http.ResponseWriter) (err error) {
return
}
}

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)
return
@ -609,6 +627,8 @@ func (s *Server) renderYML(page *Page, resp http.ResponseWriter) (err error) {
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)
return

View File

@ -46,8 +46,8 @@ Accept: text/html
</pre>
</p>

Note that <a href="https://lynx.invisible-island.net/">Lynx</a> and <a href="http://elinks.or.cz/">Elinks</a> are considered "graphical"
browsers by this program as they are HTML-centric.
Note that <a href="http://links.twibright.com/">Links</a>a>, <a href="https://lynx.invisible-island.net/">Lynx</a>, <a href="http://elinks.or.cz/">Elinks</a>,
and <a href="https://w3m.sourceforge.net/">W3M</a> are considered "graphical" browsers by this program as they are HTML-centric.
</p>
<p id="usage_params_mod">
The following parameters control/modify behavior.<a href="#usage_params_mod">{{ $linkico }}</a>