src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

site.go (1583B)


      1 package site
      2 
      3 import (
      4 	"bytes"
      5 	"context"
      6 	"embed"
      7 	"fmt"
      8 	"html/template"
      9 	"net/http"
     10 	"path"
     11 	"strings"
     12 	"time"
     13 
     14 	"code.dwrz.net/src/pkg/log"
     15 	"code.dwrz.net/src/pkg/randstr"
     16 )
     17 
     18 //go:embed all:static/*
     19 var static embed.FS
     20 
     21 type Site struct {
     22 	debug bool
     23 	files embed.FS
     24 	log   *log.Logger
     25 	pages map[string]*bytes.Buffer
     26 	tmpl  *template.Template
     27 }
     28 
     29 func (s *Site) ServeHTTP(w http.ResponseWriter, r *http.Request) {
     30 	now := time.Now()
     31 
     32 	// Recover and handle panics.
     33 	defer func() {
     34 		if err := recover(); err != nil {
     35 			w.Header().Set("Connection", "close")
     36 
     37 			s.error(w, r, &Error{
     38 				Code:    http.StatusInternalServerError,
     39 				Error:   fmt.Errorf("%s", err),
     40 				Message: "Sorry, something went wrong.",
     41 			})
     42 		}
     43 	}()
     44 
     45 	// Generate an id to track the request.
     46 	// Update the request context to store the id.
     47 	id := randstr.Charset(randstr.Numeric, 8)
     48 	r = r.WithContext(context.WithValue(r.Context(), "id", id))
     49 
     50 	// Log the request.
     51 	var origin = r.RemoteAddr
     52 	if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
     53 		origin = ip
     54 	}
     55 	s.log.Debug.Printf(
     56 		"%s → %s %s %s %s",
     57 		id, origin,
     58 		r.Proto, r.Method, r.URL.RequestURI(),
     59 	)
     60 
     61 	// Pass the request on to the multiplexer.
     62 	base, _ := shiftpath(r.URL.Path)
     63 	switch base {
     64 	case "static":
     65 		s.static(w, r)
     66 	case "status":
     67 		s.status(w, r)
     68 	default:
     69 		s.page(w, r)
     70 	}
     71 
     72 	s.log.Debug.Printf(
     73 		"%s → completed in %v", id, time.Since(now),
     74 	)
     75 }
     76 
     77 func shiftpath(p string) (base, rest string) {
     78 	p = path.Clean("/" + p)
     79 
     80 	i := strings.Index(p[1:], "/") + 1
     81 	if i <= 0 {
     82 		return p[1:], "/"
     83 	}
     84 
     85 	return p[1:i], p[i:]
     86 }