145 lines
3.8 KiB
Go
145 lines
3.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.gay/gitgay/pages/cache"
|
|
"git.gay/gitgay/pages/errors"
|
|
"git.gay/gitgay/pages/forgejo"
|
|
"git.gay/gitgay/pages/pagerouter"
|
|
"git.gay/gitgay/pages/render"
|
|
"git.gay/gitgay/pages/utils"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
func serveFile(w http.ResponseWriter, r *http.Request, file *forgejo.TreeFile) {
|
|
reader, err := file.Reader()
|
|
if err != nil {
|
|
ErrorResponse(w, r, err)
|
|
return
|
|
}
|
|
defer reader.Close()
|
|
w.Header().Set("ETag", fmt.Sprintf("W/\"%s\"", file.SHA))
|
|
if file.Mime == "text/markdown; charset=utf-8" {
|
|
reader, err = render.Markdown(reader)
|
|
if err != nil {
|
|
ErrorResponse(w, r, err)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
} else {
|
|
w.Header().Set("Content-Type", file.Mime)
|
|
}
|
|
w.Header().Set("Cache-Control", "public, max-age=600")
|
|
if r.Header.Get("If-None-Match") == fmt.Sprintf("W/\"%s\"", file.SHA) {
|
|
w.WriteHeader(304)
|
|
return
|
|
}
|
|
_, err = io.Copy(w, reader)
|
|
if err != nil {
|
|
ErrorResponse(w, r, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func handleRedirect(w http.ResponseWriter, r *http.Request, page *pagerouter.PageRouter, pathname string) (ok bool) {
|
|
if r.URL.RawQuery != "" {
|
|
pathname += "?" + r.URL.RawQuery
|
|
}
|
|
redirect, ok := page.Redirects.Apply(fmt.Sprintf("/%s", pathname))
|
|
if !ok {
|
|
return
|
|
}
|
|
if (redirect.StatusCode != 200 && redirect.StatusCode != 400) || strings.HasPrefix(redirect.To, "https://") || strings.HasPrefix(redirect.To, "http://") {
|
|
w.Header().Add("Location", redirect.To)
|
|
w.WriteHeader(301)
|
|
return
|
|
}
|
|
file, _, err := page.GetFile(redirect.To)
|
|
if err != nil {
|
|
ErrorResponse(w, r, err)
|
|
return
|
|
}
|
|
serveFile(w, r, file)
|
|
ok = true
|
|
return
|
|
}
|
|
|
|
func Handler() http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Server", "https://git.gay/gitgay/pages-server")
|
|
begin := time.Now()
|
|
hardRefresh := r.Header.Get("Cache-Control") == "no-cache"
|
|
path := utils.CleanPath(r.URL.Path)
|
|
host := r.Host
|
|
options := []cache.CachedCallOptions{
|
|
{
|
|
HardRefresh: hardRefresh,
|
|
},
|
|
}
|
|
info, err := pagerouter.GetTarget(host, path, options...)
|
|
if err != nil {
|
|
return
|
|
}
|
|
path = strings.TrimSuffix(strings.TrimPrefix(path+"/", info.BasePath), "/")
|
|
page, err := pagerouter.GetPage(info)
|
|
if err != nil {
|
|
ErrorResponse(w, r, err)
|
|
return
|
|
}
|
|
if !page.Settings.AllowInsecure {
|
|
if r.TLS == nil {
|
|
newURL := &url.URL{Scheme: "https", Host: host, Path: path, RawQuery: r.URL.RawQuery}
|
|
w.Header().Add("Location", newURL.String())
|
|
w.WriteHeader(http.StatusMovedPermanently)
|
|
return
|
|
}
|
|
}
|
|
log.Debug().Str("host", host).Str("path", path).Dur("duration", time.Since(begin)).Msg("Got page")
|
|
matchedDomain := false
|
|
if len(page.Domains) > 0 {
|
|
for _, domains := range page.Domains {
|
|
if domains == host {
|
|
matchedDomain = true
|
|
break
|
|
}
|
|
}
|
|
preferredDomain := page.Domains[0]
|
|
// TODO: check that preferredDomain not just points at pages.gay
|
|
// but this specific repository on this specific user (not branch-specific though)
|
|
if !matchedDomain && utils.PointingAtUs(preferredDomain) {
|
|
newURL := &url.URL{Scheme: r.URL.Scheme, Host: preferredDomain, Path: path}
|
|
w.Header().Add("Location", newURL.String())
|
|
w.WriteHeader(301)
|
|
return
|
|
}
|
|
}
|
|
file, isIndex, err := page.GetFile(path)
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case *errors.ErrorNotFound:
|
|
if page.Redirects != nil {
|
|
ok := handleRedirect(w, r, page, path)
|
|
if ok {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
ErrorResponse(w, r, err)
|
|
return
|
|
}
|
|
if isIndex && !strings.HasSuffix(r.URL.Path, "/") {
|
|
w.Header().Add("Location", fmt.Sprintf("%s/", r.URL.Path))
|
|
w.WriteHeader(301)
|
|
return
|
|
}
|
|
serveFile(w, r, file)
|
|
})
|
|
}
|