v0.0.1
Initial public release
This commit is contained in:
		
							parent
							
								
									db081e2699
								
							
						
					
					
						commit
						8ec5120320
					
				@ -1,12 +1,10 @@
 | 
				
			|||||||
package server
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	`crypto/tls`
 | 
					 | 
				
			||||||
	`encoding/json`
 | 
						`encoding/json`
 | 
				
			||||||
	`encoding/xml`
 | 
						`encoding/xml`
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	`mime/multipart`
 | 
					 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	`net/http/fcgi`
 | 
						`net/http/fcgi`
 | 
				
			||||||
@ -267,246 +265,6 @@ func (s *Server) explicit404(resp http.ResponseWriter, req *http.Request) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
 | 
					func (s *Server) handleDefault(resp http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var vals url.Values
 | 
					 | 
				
			||||||
	var uaVals []string
 | 
					 | 
				
			||||||
	var doInclude bool
 | 
					 | 
				
			||||||
	var doIndent bool
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	var ok bool
 | 
					 | 
				
			||||||
	var b []byte
 | 
					 | 
				
			||||||
	var remAddrPort string
 | 
					 | 
				
			||||||
	var okMedia []string
 | 
					 | 
				
			||||||
	var nAP netip.AddrPort
 | 
					 | 
				
			||||||
	var parsedFmts []*parsedMIME
 | 
					 | 
				
			||||||
	var renderPage *Page = new(Page)
 | 
					 | 
				
			||||||
	var format string = mediaJSON
 | 
					 | 
				
			||||||
	var indent string = "  "
 | 
					 | 
				
			||||||
	var client *R00tInfo = new(R00tInfo)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	renderPage.RawIndent = "  "
 | 
					 | 
				
			||||||
	renderPage.PageType = "index"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s.log.Debug("server.Server.handleDefault: Handling request:\n%s", spew.Sdump(req))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		if req.URL != nil &&
 | 
					 | 
				
			||||||
			req.URL.Path != "" &&
 | 
					 | 
				
			||||||
			req.URL.Path != "/" &&
 | 
					 | 
				
			||||||
			req.URL.Path != "/index" &&
 | 
					 | 
				
			||||||
			req.URL.Path != "/index.html" {
 | 
					 | 
				
			||||||
			resp.WriteHeader(http.StatusNotFound)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	client.Req = req
 | 
					 | 
				
			||||||
	remAddrPort = req.RemoteAddr
 | 
					 | 
				
			||||||
	if s.isHttp && req.Header.Get(httpRealHdr) != "" {
 | 
					 | 
				
			||||||
		remAddrPort = req.Header.Get(httpRealHdr)
 | 
					 | 
				
			||||||
		req.Header.Del(httpRealHdr)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if remAddrPort != "" {
 | 
					 | 
				
			||||||
		if nAP, err = netip.ParseAddrPort(remAddrPort); err != nil {
 | 
					 | 
				
			||||||
			s.log.Err("server.Server.handleDefault: Failed to parse remote address '%s': %v", req.RemoteAddr, err)
 | 
					 | 
				
			||||||
			// Don't return an error in case we're doing weird things like direct socket clients.
 | 
					 | 
				
			||||||
			err = nil
 | 
					 | 
				
			||||||
			/*
 | 
					 | 
				
			||||||
				http.Error(resp, "ERROR: Failed to parse client address", http.StatusInternalServerError)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			*/
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		client.IP = net.ParseIP(nAP.Addr().String())
 | 
					 | 
				
			||||||
		client.Port = nAP.Port()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	client.Headers = XmlHeaders(req.Header)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uaVals = req.Header.Values("User-Agent")
 | 
					 | 
				
			||||||
	if uaVals != nil && len(uaVals) > 0 {
 | 
					 | 
				
			||||||
		client.Client = make([]*R00tClient, len(uaVals))
 | 
					 | 
				
			||||||
		for idx, ua := range uaVals {
 | 
					 | 
				
			||||||
			if client.Client[idx], err = NewClient(ua); err != nil {
 | 
					 | 
				
			||||||
				s.log.Err("server.Server.handleDefault: Failed to create client for '%s': %v", ua, err)
 | 
					 | 
				
			||||||
				http.Error(resp, fmt.Sprintf("ERROR: Failed to parse 'User-Agent' '%s'", ua), http.StatusInternalServerError)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if client.Client != nil && len(client.Client) > 0 {
 | 
					 | 
				
			||||||
		// Check the passed UAs for a browser. We then change the "default" format if so.
 | 
					 | 
				
			||||||
		for _, ua := range client.Client {
 | 
					 | 
				
			||||||
			if ua.IsMobile || ua.IsDesktop {
 | 
					 | 
				
			||||||
				format = mediaHTML
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	renderPage.Info = client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vals = req.URL.Query()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Determine the format/MIME type of the response.
 | 
					 | 
				
			||||||
	if vals.Has("mime") {
 | 
					 | 
				
			||||||
		format = req.URL.Query().Get("mime")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if parsedFmts, err = parseAccept(strings.Join(req.Header.Values("Accept"), ",")); err != nil {
 | 
					 | 
				
			||||||
			s.log.Err("server.Server.handleDefault: Failed to parse Accept header: %v", err)
 | 
					 | 
				
			||||||
			http.Error(
 | 
					 | 
				
			||||||
				resp,
 | 
					 | 
				
			||||||
				"ERROR: Invalid 'Accept' header value; see RFC 9110 § 12.5.1 and https://www.iana.org/assignments/media-types/media-types.xhtml",
 | 
					 | 
				
			||||||
				http.StatusBadRequest,
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if format, err = decideParseAccept(parsedFmts, mediaJSON); err != nil {
 | 
					 | 
				
			||||||
			if errors.Is(err, ErrUnsupportedMIME) {
 | 
					 | 
				
			||||||
				s.log.Err("server.Server.handleDefault: No supported MIME type found for '%s'.", req.RemoteAddr)
 | 
					 | 
				
			||||||
				for mt := range mediaNoIndent {
 | 
					 | 
				
			||||||
					okMedia = append(okMedia, mt)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				req.Header.Set("Accept", strings.Join(okMedia, ", "))
 | 
					 | 
				
			||||||
				http.Error(resp, "ERROR: No supported MIME type specified; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				s.log.Err("server.Server.handleDefault: Received unknown error choosing an Accept header for '%s': %v", req.RemoteAddr, err)
 | 
					 | 
				
			||||||
				http.Error(resp, "ERROR: Unknown error occurred when negotiationg MIME type.", http.StatusInternalServerError)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	s.log.Debug("server.Server.handleDefault: Using format '%s' for '%s'", format, req.RemoteAddr)
 | 
					 | 
				
			||||||
	// If it's HTML and they want an include, that needs to be validated too.
 | 
					 | 
				
			||||||
	if format == mediaHTML && vals.Has("include") {
 | 
					 | 
				
			||||||
		doInclude = true
 | 
					 | 
				
			||||||
		if parsedFmts, err = parseAccept(strings.Join(vals["include"], ", ")); err != nil {
 | 
					 | 
				
			||||||
			s.log.Err("server.Server.handleDefault: Failed to parse include parameter: %v", err)
 | 
					 | 
				
			||||||
			http.Error(
 | 
					 | 
				
			||||||
				resp,
 | 
					 | 
				
			||||||
				"ERROR: Invalid 'include' parameter value; see RFC 9110 § 12.5.1 and https://www.iana.org/assignments/media-types/media-types.xhtml",
 | 
					 | 
				
			||||||
				http.StatusBadRequest,
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if renderPage.RawFmt, err = decideParseAccept(parsedFmts, format); err != nil {
 | 
					 | 
				
			||||||
			if errors.Is(err, ErrUnsupportedMIME) {
 | 
					 | 
				
			||||||
				s.log.Err("server.Server.handleDefault: No supported MIME type found for '%#v' 'include'.", vals["include"], req.RemoteAddr)
 | 
					 | 
				
			||||||
				for mt := range mediaNoIndent {
 | 
					 | 
				
			||||||
					okMedia = append(okMedia, mt)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				req.Header.Set("Accept", strings.Join(okMedia, ", "))
 | 
					 | 
				
			||||||
				http.Error(resp, "ERROR: No supported MIME type specified for 'include'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				s.log.Err("server.Server.handleDefault: Received unknown error choosing an include format for '%s': %v", req.RemoteAddr, err)
 | 
					 | 
				
			||||||
				http.Error(resp, "ERROR: Unknown error occurred when negotiationg MIME type.", http.StatusInternalServerError)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// The indentation is set below.
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Determine indentation (if the format even supports it).
 | 
					 | 
				
			||||||
	if format == mediaHTML {
 | 
					 | 
				
			||||||
		if doInclude {
 | 
					 | 
				
			||||||
			if _, ok = mediaIndent[renderPage.RawFmt]; ok {
 | 
					 | 
				
			||||||
				doIndent = vals.Has("indent")
 | 
					 | 
				
			||||||
				if doIndent {
 | 
					 | 
				
			||||||
					if req.URL.Query().Get("indent") != "" {
 | 
					 | 
				
			||||||
						renderPage.RawIndent = req.URL.Query().Get("indent")
 | 
					 | 
				
			||||||
						renderPage.DoRawIndent = true
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else if _, ok = mediaNoIndent[renderPage.RawFmt]; !ok {
 | 
					 | 
				
			||||||
				// It's not a supported MIME.
 | 
					 | 
				
			||||||
				s.log.Err("server.Server.handleDefault: Requested MIME type '%s' for '%s' unsupported.", renderPage.RawFmt, req.RemoteAddr)
 | 
					 | 
				
			||||||
				for mt := range mediaNoIndent {
 | 
					 | 
				
			||||||
					okMedia = append(okMedia, mt)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				req.Header.Set("Accept", strings.Join(okMedia, ", "))
 | 
					 | 
				
			||||||
				http.Error(
 | 
					 | 
				
			||||||
					resp,
 | 
					 | 
				
			||||||
					fmt.Sprintf("ERROR: MIME type '%s' unsupported for 'include'; see Accept header in response for valid types.", renderPage.RawFmt),
 | 
					 | 
				
			||||||
					http.StatusNotAcceptable,
 | 
					 | 
				
			||||||
				)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				// This seems backwards, but "non-indented" formats actually need indenting enabled so their whitespace renders properly.
 | 
					 | 
				
			||||||
				renderPage.DoRawIndent = true
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if _, ok = mediaIndent[format]; ok {
 | 
					 | 
				
			||||||
			doIndent = vals.Has("indent")
 | 
					 | 
				
			||||||
			if doIndent {
 | 
					 | 
				
			||||||
				if req.URL.Query().Get("indent") != "" {
 | 
					 | 
				
			||||||
					indent = req.URL.Query().Get("indent")
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else if _, ok = mediaNoIndent[format]; !ok {
 | 
					 | 
				
			||||||
			// It's not a supported MIME.
 | 
					 | 
				
			||||||
			s.log.Err("server.Server.handleDefault: Requested MIME type '%s' for '%s' unsupported.", format, req.RemoteAddr)
 | 
					 | 
				
			||||||
			for mt := range mediaNoIndent {
 | 
					 | 
				
			||||||
				okMedia = append(okMedia, mt)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			req.Header.Set("Accept", strings.Join(okMedia, ", "))
 | 
					 | 
				
			||||||
			http.Error(resp, fmt.Sprintf("ERROR: MIME type '%s' unsupported; see Accept header in response for valid types.", format), http.StatusNotAcceptable)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Now render the response.
 | 
					 | 
				
			||||||
	if format == mediaHTML {
 | 
					 | 
				
			||||||
		// This gets special treatment since it's templated.
 | 
					 | 
				
			||||||
		resp.Header().Set("Content-Type", "text/html; charset=utf-8")
 | 
					 | 
				
			||||||
		if doInclude {
 | 
					 | 
				
			||||||
			renderPage.Raw = new(string)
 | 
					 | 
				
			||||||
			if doIndent {
 | 
					 | 
				
			||||||
				if b, err = mediaIndent[renderPage.RawFmt](client, "", renderPage.RawIndent); err != nil {
 | 
					 | 
				
			||||||
					s.log.Err("server.Server.handleDefault: Failed to render indented raw '%s' for '%s': %v", renderPage.RawFmt, req.RemoteAddr, err)
 | 
					 | 
				
			||||||
					http.Error(resp, fmt.Sprintf("ERROR: Failed to render 'include' '%s'", renderPage.RawFmt), http.StatusInternalServerError)
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				if b, err = mediaNoIndent[renderPage.RawFmt](client); err != nil {
 | 
					 | 
				
			||||||
					s.log.Err("server.Server.handleDefault: Failed to render raw '%s' for '%s': %v", renderPage.RawFmt, req.RemoteAddr, err)
 | 
					 | 
				
			||||||
					http.Error(resp, fmt.Sprintf("ERROR: Failed to render '%s'", renderPage.RawFmt), http.StatusInternalServerError)
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			*renderPage.Raw = string(b)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err = tpl.ExecuteTemplate(resp, "index", renderPage); err != nil {
 | 
					 | 
				
			||||||
			s.log.Err("server.Server.handleDefault: Failed to execute template for '%s': %v", req.RemoteAddr, err)
 | 
					 | 
				
			||||||
			http.Error(resp, "ERROR: Failed to render HTML", http.StatusInternalServerError)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		resp.Header().Set("Content-Type", format)
 | 
					 | 
				
			||||||
		if doIndent {
 | 
					 | 
				
			||||||
			// This was already filtered to valid specified MIME above.
 | 
					 | 
				
			||||||
			if b, err = mediaIndent[format](client, "", indent); err != nil {
 | 
					 | 
				
			||||||
				s.log.Err("server.Server.handleDefault: Failed to render indented '%s' for '%s': %v", format, req.RemoteAddr, err)
 | 
					 | 
				
			||||||
				http.Error(resp, fmt.Sprintf("ERROR: Failed to render '%s'", format), http.StatusInternalServerError)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			if b, err = mediaNoIndent[format](client); err != nil {
 | 
					 | 
				
			||||||
				s.log.Err("server.Server.handleDefault: Failed to render '%s' for '%s': %v", format, req.RemoteAddr, err)
 | 
					 | 
				
			||||||
				http.Error(resp, fmt.Sprintf("ERROR: Failed to render '%s'", format), http.StatusInternalServerError)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if _, err = resp.Write(b); err != nil {
 | 
					 | 
				
			||||||
			s.log.Err("server.Server.handleDefault: Failed to serve indented '%s' to '%s': %v", format, req.RemoteAddr, err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s.log.Debug("server.Server.handleDefault: Handled request:\n%s", spew.Sdump(req))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *Server) handleDefaultNew(resp http.ResponseWriter, req *http.Request) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	var page *Page
 | 
						var page *Page
 | 
				
			||||||
	var uas []string
 | 
						var uas []string
 | 
				
			||||||
@ -602,7 +360,7 @@ func (s *Server) handleDefaultNew(resp http.ResponseWriter, req *http.Request) {
 | 
				
			|||||||
		if outerFmt, err = decideParseAccept(parsedFmts, outerFmt); err != nil {
 | 
							if outerFmt, err = decideParseAccept(parsedFmts, outerFmt); err != nil {
 | 
				
			||||||
			if errors.Is(err, ErrUnsupportedMIME) {
 | 
								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'.", remAddrPort, reqdMimes)
 | 
				
			||||||
				req.Header["Accept"] = okAcceptMime
 | 
									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)
 | 
									http.Error(resp, "ERROR: No supported MIME type specified via request 'Accept'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@ -627,7 +385,7 @@ func (s *Server) handleDefaultNew(resp http.ResponseWriter, req *http.Request) {
 | 
				
			|||||||
		if outerFmt, err = decideParseAccept(parsedFmts, outerFmt); err != nil {
 | 
							if outerFmt, err = decideParseAccept(parsedFmts, outerFmt); err != nil {
 | 
				
			||||||
			if errors.Is(err, ErrUnsupportedMIME) {
 | 
								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' via '%#v'.", remAddrPort, params["mime"])
 | 
				
			||||||
				req.Header["Accept"] = okAcceptMime
 | 
									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)
 | 
									http.Error(resp, "ERROR: No supported MIME type specified via URL parameter 'mime'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@ -652,7 +410,7 @@ func (s *Server) handleDefaultNew(resp http.ResponseWriter, req *http.Request) {
 | 
				
			|||||||
		if includeFmt, err = decideParseAccept(parsedFmts, includeFmt); err != nil {
 | 
							if includeFmt, err = decideParseAccept(parsedFmts, includeFmt); err != nil {
 | 
				
			||||||
			if errors.Is(err, ErrUnsupportedMIME) {
 | 
								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' via '%#v'.", remAddrPort, params["include"])
 | 
				
			||||||
				req.Header["Accept"] = okAcceptMime
 | 
									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)
 | 
									http.Error(resp, "ERROR: No supported MIME type specified via URL parameter 'include'; see 'Accept' header in response for valid types.", http.StatusNotAcceptable)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@ -791,7 +549,7 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
 | 
				
			|||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				if b, err = mediaNoIndent[*page.RawFmt](page.Indent); err != nil {
 | 
									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': %v", *page.RawFmt, err)
 | 
				
			||||||
					http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
 | 
										http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@
 | 
				
			|||||||
					{{- if $page.Raw }}
 | 
										{{- if $page.Raw }}
 | 
				
			||||||
					<h3 id="client_raw">Raw Block ({{ $page.RawFmt }})<a href="#client_raw">{{ $linkico }}</a></h3>
 | 
										<h3 id="client_raw">Raw Block ({{ $page.RawFmt }})<a href="#client_raw">{{ $linkico }}</a></h3>
 | 
				
			||||||
					<p>
 | 
										<p>
 | 
				
			||||||
						{{- if $page.DoRawIndent }}
 | 
											{{- if $page.DoIndent }}
 | 
				
			||||||
						<pre>{{ $page.Raw }}</pre>
 | 
											<pre>{{ $page.Raw }}</pre>
 | 
				
			||||||
						{{- else }}
 | 
											{{- else }}
 | 
				
			||||||
						<code>{{ $page.Raw }}</code>
 | 
											<code>{{ $page.Raw }}</code>
 | 
				
			||||||
@ -20,36 +20,64 @@
 | 
				
			|||||||
					{{- end }}
 | 
										{{- end }}
 | 
				
			||||||
					<h3 id="client_ua">User Agent Information<a href="#client_ua">{{ $linkico }}</a></h3>
 | 
										<h3 id="client_ua">User Agent Information<a href="#client_ua">{{ $linkico }}</a></h3>
 | 
				
			||||||
					<p>This is information that your browser sends to identify itself.</p>
 | 
										<p>This is information that your browser sends to identify itself.</p>
 | 
				
			||||||
 | 
										{{- range $idx, $ua := $page.Info.Client }}
 | 
				
			||||||
					<p>
 | 
										<p>
 | 
				
			||||||
						{{- range $idx, $ua := $page.Info.Client }}
 | 
					 | 
				
			||||||
						User Agent ({{ $idx }}):
 | 
											User Agent ({{ $idx }}):
 | 
				
			||||||
 | 
											<div class="tg-wrap">
 | 
				
			||||||
 | 
												<table>
 | 
				
			||||||
 | 
													<thead>
 | 
				
			||||||
 | 
														<tr>
 | 
				
			||||||
 | 
															<th>Identifier</th>
 | 
				
			||||||
 | 
															<th>Value</th>
 | 
				
			||||||
 | 
														</tr>
 | 
				
			||||||
 | 
													</thead>
 | 
				
			||||||
 | 
												    	<tbody>
 | 
				
			||||||
 | 
														{{- $flds := $ua.ToMap }}
 | 
				
			||||||
 | 
														{{- range $fld, $str := $flds }}
 | 
				
			||||||
 | 
														<tr>
 | 
				
			||||||
 | 
															<td>{{ $fld }}</td>
 | 
				
			||||||
 | 
															<td>{{ $str }}</td>
 | 
				
			||||||
 | 
														</tr>
 | 
				
			||||||
 | 
														{{- end }}
 | 
				
			||||||
 | 
													</tbody>
 | 
				
			||||||
 | 
												</table>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
					                                                {{- /*
 | 
				
			||||||
						<ul>
 | 
											<ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							{{- $flds := $ua.ToMap }}
 | 
												{{- $flds := $ua.ToMap }}
 | 
				
			||||||
							{{- range $fld, $str := $flds }}
 | 
												{{- range $fld, $str := $flds }}
 | 
				
			||||||
							<li><b>{{ $fld }}:</b> {{ $str }}</li>
 | 
												<li><b>{{ $fld }}:</b> {{ $str }}</li>
 | 
				
			||||||
							{{- end }}
 | 
												{{- end }}
 | 
				
			||||||
						</ul>
 | 
											</ul>
 | 
				
			||||||
						{{- end }}
 | 
											*/}}
 | 
				
			||||||
					</p>
 | 
										</p>
 | 
				
			||||||
 | 
										{{- end }}
 | 
				
			||||||
					<h3 id="client_hdrs">Request Headers<a href="#client_hdrs">{{ $linkico }}</a></h3>
 | 
										<h3 id="client_hdrs">Request Headers<a href="#client_hdrs">{{ $linkico }}</a></h3>
 | 
				
			||||||
					<p>
 | 
										<p>
 | 
				
			||||||
						These are headers sent along with the request your browser sends for the page's content.
 | 
											These are headers sent along with the request your browser sends for the page's content.
 | 
				
			||||||
						Note that some headers may have multiple values.
 | 
											Note that some headers may have multiple values.
 | 
				
			||||||
					</p>
 | 
										</p>
 | 
				
			||||||
					<p>
 | 
										<p>
 | 
				
			||||||
						<table>
 | 
											<div class="tg-wrap">
 | 
				
			||||||
							<tr>
 | 
												<table>
 | 
				
			||||||
								<th>Header</th>
 | 
													<thead>
 | 
				
			||||||
								<th>Value</th>
 | 
														<tr>
 | 
				
			||||||
							</tr>
 | 
															<th>Header</th>
 | 
				
			||||||
							{{- range $hdrNm, $hdrVals := $page.Info.Req.Header }}
 | 
															<th>Value</th>
 | 
				
			||||||
							<tr>
 | 
														</tr>
 | 
				
			||||||
								{{- range $val := $hdrVals }}
 | 
													</thead>
 | 
				
			||||||
								<td>{{ $hdrNm }}</td>
 | 
												    	<tbody>
 | 
				
			||||||
								<td>{{ $val }}</td>
 | 
														{{- range $hdrNm, $hdrVals := $page.Info.Req.Header }}
 | 
				
			||||||
								{{- end }}
 | 
														<tr>
 | 
				
			||||||
							</tr>
 | 
															{{- range $val := $hdrVals }}
 | 
				
			||||||
							{{- end }}
 | 
															<td>{{ $hdrNm }}</td>
 | 
				
			||||||
						</table>
 | 
															<td>{{ $val }}</td>
 | 
				
			||||||
 | 
															{{- end }}
 | 
				
			||||||
 | 
														</tr>
 | 
				
			||||||
 | 
														{{- end }}
 | 
				
			||||||
 | 
													</tbody>
 | 
				
			||||||
 | 
												</table>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
					</p>
 | 
										</p>
 | 
				
			||||||
{{- end }}
 | 
					{{- end }}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,8 +5,9 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html lang="en">
 | 
					<html lang="en">
 | 
				
			||||||
	<head>
 | 
						<head>
 | 
				
			||||||
		<meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
							<meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
				
			||||||
		<title>{{ getTitle $page.PageType }}</title>
 | 
							<title>{{ getTitle $page.PageType }}</title>
 | 
				
			||||||
 | 
							{{- /*
 | 
				
			||||||
		<!-- Bootstrap core CSS -->
 | 
							<!-- Bootstrap core CSS -->
 | 
				
			||||||
		<!--
 | 
							<!--
 | 
				
			||||||
		<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
 | 
							<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
 | 
				
			||||||
@ -24,30 +25,51 @@
 | 
				
			|||||||
		<!--
 | 
							<!--
 | 
				
			||||||
			<link href="https://getbootstrap.com/docs/4.0/examples/offcanvas/offcanvas.css" rel="stylesheet">
 | 
								<link href="https://getbootstrap.com/docs/4.0/examples/offcanvas/offcanvas.css" rel="stylesheet">
 | 
				
			||||||
		-->
 | 
							-->
 | 
				
			||||||
 | 
							*/}}
 | 
				
			||||||
 | 
							<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
 | 
				
			||||||
 | 
							<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
 | 
				
			||||||
 | 
							<link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3/dist/lux/bootstrap.min.css" rel="stylesheet">
 | 
				
			||||||
	</head>
 | 
						</head>
 | 
				
			||||||
	<body>
 | 
						<body>
 | 
				
			||||||
		<div class="container">
 | 
							<div class="container">
 | 
				
			||||||
			<div class="header clearfix">
 | 
								<div class="header clearfix">
 | 
				
			||||||
				<nav>
 | 
									<nav class="navbar navbar-expand-lg bg-body-tertiary">
 | 
				
			||||||
					<ul class="nav nav-pills pull-right">
 | 
										<div class="container-fluid">
 | 
				
			||||||
						<li role="presentation"><a href="/">Home</a></li>
 | 
											<a class="navbar-brand" href="/">Home</a>
 | 
				
			||||||
						<li role="presentation"><a href="/about">About</a></li>
 | 
											<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
 | 
				
			||||||
						<li role="presentation"><a href="/usage">Usage</a></li>
 | 
												<span class="navbar-toggler-icon"></span>
 | 
				
			||||||
						<ul role="presentation">
 | 
											</button>
 | 
				
			||||||
							<li><a href="/?mime=application/json&indent">JSON</a></li>
 | 
											<div class="collapse navbar-collapse" id="navbarNav">
 | 
				
			||||||
							<li><a href="/?mime=application/xml&indent">XML</a></li>
 | 
												<ul class="navbar-nav ms-auto"> <!-- ms-auto replaces pull-right for alignment -->
 | 
				
			||||||
							<li><a href="/?mime=application/yaml">YAML</a></li>
 | 
													<li class="nav-item">
 | 
				
			||||||
							<li><a href="/?mime=text/html">HTML (This Page)</a></li>
 | 
														<a class="nav-link" href="/">Home</a>
 | 
				
			||||||
						</ul>
 | 
													</li>
 | 
				
			||||||
						<!--
 | 
													<li class="nav-item">
 | 
				
			||||||
							the following opens in a new tab/window/whatever.
 | 
														<a class="nav-link" href="/about">About</a>
 | 
				
			||||||
							the line after opens in the same tab/window/etc.
 | 
													</li>
 | 
				
			||||||
						-->
 | 
													<li class="nav-item">
 | 
				
			||||||
						<!--
 | 
														<a class="nav-link" href="/usage">Usage</a>
 | 
				
			||||||
						<li role="presentation"><a href="https://r00t2.io/" target="_blank">r00t^2</a></li>
 | 
													</li>
 | 
				
			||||||
						-->
 | 
													<!-- Dropdown Menu -->
 | 
				
			||||||
						<li role="presentation"><a href="https://r00t2.io/">r00t^2</a></li>
 | 
													<li class="nav-item dropdown">
 | 
				
			||||||
					</ul>
 | 
														<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">Formats/MIME types</a>
 | 
				
			||||||
 | 
														<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
 | 
				
			||||||
 | 
															<li><a class="dropdown-item" href="/?mime=application/json&indent">JSON (<code>application/json</code>)</a></li>
 | 
				
			||||||
 | 
															<li><a class="dropdown-item" href="/?mime=application/xml&indent">XML (<code>application/xml</code>)</a></li>
 | 
				
			||||||
 | 
															<li><a class="dropdown-item" href="/?mime=application/yaml">YAML (<code>application/yaml</code>)</a></li>
 | 
				
			||||||
 | 
															{{- if eq $page.PageType "index" }}
 | 
				
			||||||
 | 
															<li><a class="dropdown-item" href="/?mime=text/html">HTML (this page) (<code>text/html</code>)</a></li>
 | 
				
			||||||
 | 
															{{- else }}
 | 
				
			||||||
 | 
															<li><a class="dropdown-item" href="/?mime=text/html">HTML (<code>text/html</code>)</a></li>
 | 
				
			||||||
 | 
															{{- end }}
 | 
				
			||||||
 | 
														</ul>
 | 
				
			||||||
 | 
													</li>
 | 
				
			||||||
 | 
													<li class="nav-item">
 | 
				
			||||||
 | 
														<a class="nav-link" href="https://r00t2.io/" target="_blank">r00t^2</a>
 | 
				
			||||||
 | 
													</li>
 | 
				
			||||||
 | 
												</ul>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
				</nav>
 | 
									</nav>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
{{- end }}
 | 
					{{- end }}
 | 
				
			||||||
 | 
				
			|||||||
@ -17,10 +17,10 @@
 | 
				
			|||||||
					You can force a specific raw output by specifying the <a href="https://www.iana.org/assignments/media-types/media-types.xhtml">MIME type</a> via
 | 
										You can force a specific raw output by specifying the <a href="https://www.iana.org/assignments/media-types/media-types.xhtml">MIME type</a> via
 | 
				
			||||||
					<a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-12.5.1">the <code>Accept</code> header (RFC 9110 § 12.5.1)</a>, which may be one of:
 | 
										<a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-12.5.1">the <code>Accept</code> header (RFC 9110 § 12.5.1)</a>, which may be one of:
 | 
				
			||||||
					<ul>
 | 
										<ul>
 | 
				
			||||||
						<li><code>application/json</code> for <a href="https://www.rfc-editor.org/rfc/rfc8259.html">JSON</a></li>
 | 
											<li><code>application/json</code> for JSON (<a href="https://www.rfc-editor.org/rfc/rfc8259.html">RFC 8259</a>, <a href="https://www.iana.org/assignments/media-types/application/json">IANA registration</a>)</li>
 | 
				
			||||||
						<li><code>application/xml</code> for <a href="https://www.rfc-editor.org/rfc/rfc7303.html">XML</a></li>
 | 
											<li><code>application/xml</code> for XML (<a href="https://www.rfc-editor.org/rfc/rfc7303.html">RFC 7303</a>, <a href="https://www.iana.org/assignments/media-types/application/xml">IANA registration</a>)</li>
 | 
				
			||||||
						<li><code>application/yaml</code> for <a href="https://www.rfc-editor.org/rfc/rfc9512.html">YAML</a></li>
 | 
											<li><code>application/yaml</code> for YAML (<a href="https://www.rfc-editor.org/rfc/rfc9512.html">RFC 9512</a>, <a href="https://www.iana.org/assignments/media-types/application/yaml">IANA registration</a>)</li>
 | 
				
			||||||
						<li><code>text/html</code> for <a href="https://www.rfc-editor.org/rfc/rfc2854.html">HTML</a></li>
 | 
											<li><code>text/html</code> for HTML (<a href="https://www.rfc-editor.org/rfc/rfc2854.html">RFC 2854</a>, <a href="https://www.iana.org/assignments/media-types/text/html">IANA registration</a>)</li>
 | 
				
			||||||
					</ul>
 | 
										</ul>
 | 
				
			||||||
					For example: <code>Accept: application/json</code> will return JSON.
 | 
										For example: <code>Accept: application/json</code> will return JSON.
 | 
				
			||||||
					<br/>
 | 
										<br/>
 | 
				
			||||||
@ -36,8 +36,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
					If no selectable MIME type is provided but an <code>Accept</code> was given, an error will be returned; specifically, a
 | 
										If no selectable MIME type is provided but an <code>Accept</code> was given, an error will be returned; specifically, a
 | 
				
			||||||
					<a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.7"><code>406</code> status code (RFC 9110 § 15.5.7)</a>.
 | 
										<a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.7"><code>406</code> status code (RFC 9110 § 15.5.7)</a>.
 | 
				
			||||||
					In this case, supported MIME types will be returned in the response's <code>Accept</code> header.
 | 
										In this case, supported MIME types will be returned in the response's <code>Accept</code> header values, e.g.:
 | 
				
			||||||
					<br/>
 | 
										<p>
 | 
				
			||||||
 | 
											<pre>
 | 
				
			||||||
 | 
					Accept: application/json
 | 
				
			||||||
 | 
					Accept: application/xml
 | 
				
			||||||
 | 
					Accept: application/yaml
 | 
				
			||||||
 | 
					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"
 | 
										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.
 | 
										browsers by this program as they are HTML-centric.
 | 
				
			||||||
@ -46,7 +53,7 @@
 | 
				
			|||||||
					The following parameters control/modify behavior.<a href="#usage_params_mod">{{ $linkico }}</a>
 | 
										The following parameters control/modify behavior.<a href="#usage_params_mod">{{ $linkico }}</a>
 | 
				
			||||||
					<ul>
 | 
										<ul>
 | 
				
			||||||
						<li>
 | 
											<li>
 | 
				
			||||||
							<b>mime:</b> Specify an explicit MIME type via URL instead of the <code>Accept</code> header as specified above.
 | 
												<b><code>mime</code>:</b> Specify an explicit MIME type via URL instead of the <code>Accept</code> header as specified above.
 | 
				
			||||||
							<ul>
 | 
												<ul>
 | 
				
			||||||
								<li>This should only be used by clients in which it is impossible or particularly cumbersome to modify/specify headers.
 | 
													<li>This should only be used by clients in which it is impossible or particularly cumbersome to modify/specify headers.
 | 
				
			||||||
									<code>Accept</code> is more performant.</li>
 | 
														<code>Accept</code> is more performant.</li>
 | 
				
			||||||
@ -56,7 +63,7 @@
 | 
				
			|||||||
							</ul>
 | 
												</ul>
 | 
				
			||||||
						</li>
 | 
											</li>
 | 
				
			||||||
						<li>
 | 
											<li>
 | 
				
			||||||
							<b>include:</b> Include a <code><code></code> (or <code><pre></code>, depending on if indentation is needed/requested) block in the HTML for the specified MIME type as well.</li>
 | 
												<b><code>include</code>:</b> Include a <code><code></code> (or <code><pre></code>, depending on if indentation is needed/requested) block in the HTML for the specified MIME type as well.</li>
 | 
				
			||||||
							<ul>
 | 
												<ul>
 | 
				
			||||||
								<li>Only the first supported instance of this parameter will be used.</li>
 | 
													<li>Only the first supported instance of this parameter will be used.</li>
 | 
				
			||||||
								<li>
 | 
													<li>
 | 
				
			||||||
@ -70,7 +77,7 @@
 | 
				
			|||||||
							</ul>
 | 
												</ul>
 | 
				
			||||||
						</li>
 | 
											</li>
 | 
				
			||||||
						<li>
 | 
											<li>
 | 
				
			||||||
							<b>indent:</b> Enable/specify indentation for JSON and XML output; ignored for others.
 | 
												<b><code>indent</code>:</b> Enable/specify indentation for JSON and XML output; ignored for others.
 | 
				
			||||||
							<ul>
 | 
												<ul>
 | 
				
			||||||
								<li>The default is to not indent. (Commonly referred to as "condensed" or "compressed" JSON/XML.)</li>
 | 
													<li>The default is to not indent. (Commonly referred to as "condensed" or "compressed" JSON/XML.)</li>
 | 
				
			||||||
								<li>Only the first specified instance of this parameter will be used.</li>
 | 
													<li>Only the first specified instance of this parameter will be used.</li>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user