Small things

This commit is contained in:
Jan-Lukas Else 2021-02-24 13:16:33 +01:00
parent e74afac829
commit 243d695bf4
10 changed files with 112 additions and 69 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"context"
"crypto/x509"
"encoding/json"
"encoding/pem"
@ -12,12 +13,14 @@ import (
var asContext = []string{"https://www.w3.org/ns/activitystreams"}
const asRequestKey requestContextKey = "asRequest"
func manipulateAsPath(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if ap := appConfig.ActivityPub; ap != nil && ap.Enabled {
if lowerAccept := strings.ToLower(r.Header.Get("Accept")); (strings.Contains(lowerAccept, contentTypeAS) || strings.Contains(lowerAccept, "application/ld+json")) && !strings.Contains(lowerAccept, contentTypeHTML) {
// Is ActivityStream, add ".as" to differentiate cache and also trigger as function
r.URL.Path += ".as"
next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), asRequestKey, true)))
return
}
}
next.ServeHTTP(rw, r)

View File

@ -96,36 +96,40 @@ func checkIsLogin(next http.Handler) http.Handler {
}
func checkLogin(w http.ResponseWriter, r *http.Request) bool {
if r.Method == http.MethodPost &&
r.Header.Get(contentType) == contentTypeWWWForm &&
r.FormValue("loginaction") == "login" {
// Do original request
loginbody, _ := base64.StdEncoding.DecodeString(r.FormValue("loginbody"))
req, _ := http.NewRequest(r.FormValue("loginmethod"), r.RequestURI, bytes.NewReader(loginbody))
// Copy original headers
loginheaders, _ := base64.StdEncoding.DecodeString(r.FormValue("loginheaders"))
var headers http.Header
_ = json.Unmarshal(loginheaders, &headers)
for k, v := range headers {
req.Header[k] = v
}
// Check credential
if checkCredentials(r.FormValue("username"), r.FormValue("password")) {
tokenCookie, err := createTokenCookie(r.FormValue("username"))
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return true
}
// Add cookie to original request
req.AddCookie(tokenCookie)
// Send cookie
http.SetCookie(w, tokenCookie)
}
// Serve original request
d.ServeHTTP(w, req)
return true
if r.Method != http.MethodPost {
return false
}
return false
if r.Header.Get(contentType) != contentTypeWWWForm {
return false
}
if r.FormValue("loginaction") != "login" {
return false
}
// Do original request
loginbody, _ := base64.StdEncoding.DecodeString(r.FormValue("loginbody"))
req, _ := http.NewRequest(r.FormValue("loginmethod"), r.RequestURI, bytes.NewReader(loginbody))
// Copy original headers
loginheaders, _ := base64.StdEncoding.DecodeString(r.FormValue("loginheaders"))
var headers http.Header
_ = json.Unmarshal(loginheaders, &headers)
for k, v := range headers {
req.Header[k] = v
}
// Check credential
if checkCredentials(r.FormValue("username"), r.FormValue("password")) {
tokenCookie, err := createTokenCookie(r.FormValue("username"))
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return true
}
// Add cookie to original request
req.AddCookie(tokenCookie)
// Send cookie
http.SetCookie(w, tokenCookie)
}
// Serve original request
d.ServeHTTP(w, req)
return true
}
type authClaims struct {

View File

@ -7,7 +7,9 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"time"
"github.com/araddon/dateparse"
@ -81,7 +83,23 @@ func cacheMiddleware(next http.Handler) http.Handler {
}
func cacheKey(r *http.Request) string {
return r.URL.String()
def := cacheURLString(r.URL)
// Special cases
if asRequest, ok := r.Context().Value(asRequestKey).(bool); ok && asRequest {
return "as-" + def
}
// Default
return def
}
func cacheURLString(u *url.URL) string {
var buf strings.Builder
_, _ = buf.WriteString(u.EscapedPath())
if u.RawQuery != "" {
_ = buf.WriteByte('?')
_, _ = buf.WriteString(u.RawQuery)
}
return buf.String()
}
func setCacheHeaders(w http.ResponseWriter, cache *cacheItem) {

View File

@ -25,7 +25,6 @@ type config struct {
type configServer struct {
Logging bool `mapstructure:"logging"`
LogFile string `mapstructure:"logFile"`
Debug bool `mapstructure:"Debug"`
Port int `mapstructure:"port"`
PublicAddress string `mapstructure:"publicAddress"`
ShortPublicAddress string `mapstructure:"shortPublicAddress"`
@ -204,7 +203,6 @@ func initConfig() error {
// Defaults
viper.SetDefault("server.logging", false)
viper.SetDefault("server.logFile", "data/access.log")
viper.SetDefault("server.debug", false)
viper.SetDefault("server.port", 8080)
viper.SetDefault("server.publicAddress", "http://localhost:8080")
viper.SetDefault("server.publicHttps", false)

8
go.mod
View File

@ -10,7 +10,7 @@ require (
github.com/caddyserver/certmagic v0.12.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-chi/chi v1.5.2
github.com/go-chi/chi v1.5.3
github.com/go-fed/httpsig v1.1.0
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/gofrs/flock v0.8.0 // indirect
@ -33,7 +33,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.6
github.com/mholt/acmez v0.1.3 // indirect
github.com/microcosm-cc/bluemonday v1.0.4
github.com/miekg/dns v1.1.38 // indirect
github.com/miekg/dns v1.1.39 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect
@ -53,9 +53,9 @@ require (
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
golang.org/x/mod v0.4.1 // indirect
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d // indirect
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 // indirect
golang.org/x/sys v0.0.0-20210223212115-eede4237b368 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/text v0.3.5 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect

16
go.sum
View File

@ -66,8 +66,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v1.5.2 h1:YcLIBANL4OTaAOcTdp//sskGa0yGACQMCtbnr7YEn0Q=
github.com/go-chi/chi v1.5.2/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
github.com/go-chi/chi v1.5.3 h1:+DVDS9/D3MTbEu3WrrH3oz9oP6PlSPSNj8LLw3X17yU=
github.com/go-chi/chi v1.5.3/go.mod h1:Q8xfe6s3fjZyMr8ZTv5jL+vxhVaFyCq2s+RvSfzTD0E=
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@ -205,8 +205,8 @@ github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDE
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.38 h1:MtIY+fmHUVVgv1AXzmKMWcwdCYxTRPG1EDjpqF4RCEw=
github.com/miekg/dns v1.1.38/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.39 h1:6dRfDGnHiXOMmTZkwWANy7bBXXlKls5Qu+pn+Ue0TLo=
github.com/miekg/dns v1.1.39/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
@ -371,8 +371,8 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM=
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -405,8 +405,8 @@ golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 h1:SgQ6LNaYJU0JIuEHv9+s6EbhSCwYeAf5Yvj6lpYlqAE=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210223212115-eede4237b368 h1:fDE3p0qf2V1co1vfj3/o87Ps8Hq6QTGNxJ5Xe7xSp80=
golang.org/x/sys v0.0.0-20210223212115-eede4237b368/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=

View File

@ -100,11 +100,6 @@ func buildHandler() (http.Handler, error) {
r.Use(checkIsCaptcha)
r.Use(checkLoggedIn)
// Profiler
if appConfig.Server.Debug {
r.Mount("/debug", middleware.Profiler())
}
// Micropub
r.Route(micropubPath, func(mpRouter chi.Router) {
mpRouter.Use(checkIndieAuth)

31
main.go
View File

@ -1,13 +1,31 @@
package main
import (
"flag"
"log"
"os"
"os/signal"
"runtime/pprof"
"syscall"
)
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
func main() {
// Init CPU profiling
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
// Initialize config
log.Println("Initialize configuration...")
err := initConfig()
@ -90,6 +108,19 @@ func main() {
<-quit
log.Println("Stopping...")
// Write memory profile
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
defer f.Close()
// runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
}
// Close DB
err = closeDb()
if err != nil {

View File

@ -41,10 +41,6 @@ const (
)
func servePost(w http.ResponseWriter, r *http.Request) {
as := strings.HasSuffix(r.URL.Path, ".as")
if as {
r.URL.Path = strings.TrimSuffix(r.URL.Path, ".as")
}
p, err := getPost(r.URL.Path)
if err == errPostNotFound {
serve404(w, r)
@ -53,7 +49,7 @@ func servePost(w http.ResponseWriter, r *http.Request) {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
if as {
if asRequest, ok := r.Context().Value(asRequestKey).(bool); ok && asRequest {
p.serveActivityStreams(w)
return
}
@ -109,8 +105,7 @@ func (p *postPaginationAdapter) Slice(offset, length int, data interface{}) erro
func serveHome(blog string, path string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
as := strings.HasSuffix(r.URL.Path, ".as")
if as {
if asRequest, ok := r.Context().Value(asRequestKey).(bool); ok && asRequest {
appConfig.Blogs[blog].serveActivityStreams(blog, w, r)
return
}

View File

@ -16,15 +16,15 @@ import (
type requestContextKey string
func urlize(str string) string {
newStr := ""
for _, c := range strings.Split(strings.ToLower(str), "") {
if c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" {
newStr += c
} else if c == " " {
newStr += "-"
var sb strings.Builder
for _, c := range strings.ToLower(str) {
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' {
_, _ = sb.WriteRune(c)
} else if c == ' ' {
_, _ = sb.WriteRune('-')
}
}
return newStr
return sb.String()
}
func sortedStrings(s []string) []string {
@ -105,11 +105,10 @@ func resolveURLReferences(base string, refs ...string) ([]string, error) {
}
func unescapedPath(p string) string {
u, err := url.PathUnescape(p)
if err != nil {
return p
if u, err := url.PathUnescape(p); err == nil {
return u
}
return u
return p
}
func slashIfEmpty(s string) string {