v0.0.1
Initial public release
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
`crypto/tls`
|
||||
`encoding/json`
|
||||
`encoding/xml`
|
||||
"errors"
|
||||
"fmt"
|
||||
`mime/multipart`
|
||||
"net"
|
||||
"net/http"
|
||||
`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) {
|
||||
|
||||
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 page *Page
|
||||
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 errors.Is(err, ErrUnsupportedMIME) {
|
||||
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)
|
||||
return
|
||||
} else {
|
||||
@@ -627,7 +385,7 @@ func (s *Server) handleDefaultNew(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"])
|
||||
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)
|
||||
return
|
||||
} else {
|
||||
@@ -652,7 +410,7 @@ func (s *Server) handleDefaultNew(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"])
|
||||
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)
|
||||
return
|
||||
} else {
|
||||
@@ -791,7 +549,7 @@ func (s *Server) renderHTML(page *Page, resp http.ResponseWriter) (err error) {
|
||||
return
|
||||
}
|
||||
} 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)
|
||||
http.Error(resp, "ERROR: Failed to render include format", http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user