More optimizations

This commit is contained in:
Jan-Lukas Else 2022-02-12 00:48:59 +01:00
parent 6e767e3612
commit d8caf1e6f5
11 changed files with 95 additions and 93 deletions

View File

@ -6,7 +6,6 @@ import (
"encoding/gob"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
@ -120,8 +119,7 @@ func (a *goBlog) apSendSigned(blogIri, to string, activity []byte) error {
}
defer resp.Body.Close()
if !apRequestIsSuccess(resp.StatusCode) {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("signed request failed with status %d: %s", resp.StatusCode, string(body))
return fmt.Errorf("signed request failed with status %d", resp.StatusCode)
}
return nil
}

View File

@ -55,7 +55,7 @@ func (a *goBlog) captchaMiddleware(next http.Handler) http.Handler {
}
// Encode original request
h, _ := json.Marshal(r.Header)
b, _ := io.ReadAll(io.LimitReader(r.Body, 20*1000*1000)) // Only allow 20 MB
b, _ := io.ReadAll(io.LimitReader(r.Body, 20000000)) // Only allow 20 MB
_ = r.Body.Close()
if len(b) == 0 {
// Maybe it's a form

View File

@ -36,7 +36,7 @@ func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) {
defer cancel()
for {
// Retrieve content
mt, message, err := c.Read(ctx)
mt, message, err := c.Reader(ctx)
if err != nil {
break
}
@ -48,21 +48,24 @@ func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) {
if err != nil {
break
}
a.createMarkdownPreview(w, blog, string(message))
err = w.Close()
if err != nil {
a.createMarkdownPreview(w, blog, message)
if err = w.Close(); err != nil {
break
}
}
}
func (a *goBlog) createMarkdownPreview(w io.Writer, blog string, markdown string) {
func (a *goBlog) createMarkdownPreview(w io.Writer, blog string, markdown io.Reader) {
md, err := io.ReadAll(markdown)
if err != nil {
_, _ = io.WriteString(w, err.Error())
return
}
p := &post{
Blog: blog,
Content: markdown,
Content: string(md),
}
err := a.computeExtraPostParameters(p)
if err != nil {
if err = a.computeExtraPostParameters(p); err != nil {
_, _ = io.WriteString(w, err.Error())
return
}

View File

@ -15,7 +15,6 @@ import (
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/util"
"go.goblog.app/app/pkgs/bufferpool"
)
func (a *goBlog) initMarkdown() {
@ -76,14 +75,6 @@ func (a *goBlog) initMarkdown() {
)
}
func (a *goBlog) renderMarkdown(source string, absoluteLinks bool) (rendered []byte, err error) {
buffer := bufferpool.Get()
err = a.renderMarkdownToWriter(buffer, source, absoluteLinks)
rendered = buffer.Bytes()
bufferpool.Put(buffer)
return
}
func (a *goBlog) renderMarkdownToWriter(w io.Writer, source string, absoluteLinks bool) (err error) {
if absoluteLinks {
err = a.absoluteMd.Convert([]byte(source), w)
@ -97,24 +88,34 @@ func (a *goBlog) renderText(s string) string {
if s == "" {
return ""
}
h, err := a.renderMarkdown(s, false)
pipeReader, pipeWriter := io.Pipe()
var err error
go func() {
err = a.renderMarkdownToWriter(pipeWriter, s, false)
_ = pipeWriter.Close()
}()
text := htmlTextFromReader(pipeReader)
if err != nil {
return ""
}
return htmlText(string(h))
return text
}
func (a *goBlog) renderMdTitle(s string) string {
if s == "" {
return ""
}
buffer := bufferpool.Get()
defer bufferpool.Put(buffer)
err := a.titleMd.Convert([]byte(s), buffer)
pipeReader, pipeWriter := io.Pipe()
var err error
go func() {
err = a.titleMd.Convert([]byte(s), pipeWriter)
_ = pipeWriter.Close()
}()
text := htmlTextFromReader(pipeReader)
if err != nil {
return ""
}
return htmlText(buffer.String())
return text
}
// Extensions etc...
@ -177,27 +178,24 @@ func (c *customRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
return ast.WalkContinue, nil
}
n := node.(*ast.Image)
// Make URL absolute if it's relative
destination := util.URLEscape(n.Destination, true)
if c.absoluteLinks && c.publicAddress != "" && bytes.HasPrefix(destination, []byte("/")) {
destination = util.EscapeHTML(append([]byte(c.publicAddress), destination...))
} else {
destination = util.EscapeHTML(destination)
dest := string(n.Destination)
// Make destination absolute if it's relative
if c.absoluteLinks && c.publicAddress != "" {
resolved, err := resolveURLReferences(c.publicAddress, dest)
if err != nil {
return ast.WalkStop, err
}
if len(resolved) > 0 {
dest = resolved[0]
}
}
_, _ = w.WriteString("<a href=\"")
_, _ = w.Write(destination)
_, _ = w.WriteString("\">")
_, _ = w.WriteString("<img src=\"")
_, _ = w.Write(destination)
_, _ = w.WriteString("\" alt=\"")
_, _ = w.Write(util.EscapeHTML(n.Text(source)))
_ = w.WriteByte('"')
_, _ = w.WriteString(" loading=\"lazy\"")
if n.Title != nil {
_, _ = w.WriteString(" title=\"")
_, _ = w.Write(n.Title)
_ = w.WriteByte('"')
hb := newHtmlBuilder(w)
hb.writeElementOpen("a", "href", dest)
imgEls := []interface{}{"src", dest, "alt", string(n.Text(source)), "loading", "lazy"}
if len(n.Title) > 0 {
imgEls = append(imgEls, "title", string(n.Title))
}
_, _ = w.WriteString("></a>")
hb.writeElementOpen("img", imgEls...)
hb.writeElementClose("a")
return ast.WalkSkipChildren, nil
}

View File

@ -6,8 +6,17 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.goblog.app/app/pkgs/bufferpool"
)
func (a *goBlog) renderMarkdown(source string, absoluteLinks bool) (rendered []byte, err error) {
buffer := bufferpool.Get()
err = a.renderMarkdownToWriter(buffer, source, absoluteLinks)
rendered = buffer.Bytes()
bufferpool.Put(buffer)
return
}
func Test_markdown(t *testing.T) {
t.Run("Basic Markdown tests", func(t *testing.T) {
app := &goBlog{

View File

@ -15,7 +15,7 @@ import (
func Test_compress(t *testing.T) {
fakeFileContent := "Test"
hash := sha256.New()
io.WriteString(hash, fakeFileContent)
_, _ = io.WriteString(hash, fakeFileContent)
fakeSha256 := fmt.Sprintf("%x", hash.Sum(nil))
var uf mediaStorageSaveFunc = func(filename string, f io.Reader) (location string, err error) {

View File

@ -113,8 +113,8 @@ func (a *goBlog) serveMicropubPost(w http.ResponseWriter, r *http.Request) {
a.micropubCreatePostFromForm(w, r)
case contenttype.JSON:
parsedMfItem := &microformatItem{}
b, _ := io.ReadAll(io.LimitReader(r.Body, 10000000)) // 10 MB
if err := json.Unmarshal(b, parsedMfItem); err != nil {
err := json.NewDecoder(io.LimitReader(r.Body, 10000000)).Decode(parsedMfItem)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
return
}

View File

@ -93,13 +93,14 @@ func (a *goBlog) postSummary(p *post) (summary string) {
if summary != "" {
return
}
if splitted := strings.Split(p.Content, summaryDivider); len(splitted) > 1 {
rendered, _ := a.renderMarkdown(splitted[0], false)
summary = htmlText(string(rendered))
} else {
rendered, _ := a.renderMarkdown(splitted[0], false)
summary = strings.Split(htmlText(string(rendered)), "\n\n")[0]
splitted := strings.Split(p.Content, summaryDivider)
hasDivider := len(splitted) > 1
markdown := splitted[0]
summary = a.renderText(markdown)
if !hasDivider {
summary = strings.Split(summary, "\n\n")[0]
}
summary = strings.TrimSpace(strings.ReplaceAll(summary, "\n\n", " "))
return
}

View File

@ -1,9 +1,9 @@
package main
import (
"io"
"net/http"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
)
@ -39,13 +39,14 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
// Write status code
w.WriteHeader(statusCode)
// Render
buf := bufferpool.Get()
mw := a.min.Writer(contenttype.HTML, buf)
hb := newHtmlBuilder(mw)
f(hb, data)
_ = mw.Close()
_, _ = buf.WriteTo(w)
bufferpool.Put(buf)
pipeReader, pipeWriter := io.Pipe()
go func() {
mw := a.min.Writer(contenttype.HTML, pipeWriter)
f(newHtmlBuilder(mw), data)
_ = mw.Close()
_ = pipeWriter.Close()
}()
_, _ = io.Copy(w, pipeReader)
}
func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {

View File

@ -1,7 +1,6 @@
package main
import (
"bytes"
"crypto/sha256"
"fmt"
"io"
@ -34,15 +33,11 @@ func (a *goBlog) initTemplateAssets() error {
return err
}
// Compile asset and close file
compiled, err := a.compileAsset(path, file)
err = a.compileAsset(strings.TrimPrefix(path, assetsFolder+"/"), file)
_ = file.Close()
if err != nil {
return err
}
// Add to map
if compiled != "" {
a.assetFileNames[strings.TrimPrefix(path, assetsFolder+"/")] = compiled
}
}
return nil
}); err != nil {
@ -55,7 +50,7 @@ func (a *goBlog) initTemplateAssets() error {
return nil
}
func (a *goBlog) compileAsset(name string, read io.Reader) (string, error) {
func (a *goBlog) compileAsset(name string, read io.Reader) error {
ext := path.Ext(name)
switch ext {
case ".js":
@ -69,16 +64,18 @@ func (a *goBlog) compileAsset(name string, read io.Reader) (string, error) {
hash := sha256.New()
body, err := io.ReadAll(io.TeeReader(read, hash))
if err != nil {
return "", err
return err
}
// File name
compiledFileName := fmt.Sprintf("%x%s", hash.Sum(nil), ext)
// Create struct
// Save file
a.assetFiles[compiledFileName] = &assetFile{
contentType: mime.TypeByExtension(ext),
body: body,
}
return compiledFileName, err
// Save mapping of original file name to compiled file name
a.assetFileNames[name] = compiledFileName
return err
}
// Function for templates
@ -113,22 +110,15 @@ func (a *goBlog) initChromaCSS() error {
return nil
}
// Initialize the style
chromaStyleBuilder := chromaGoBlogStyle.Builder()
chromaStyle, err := chromaStyleBuilder.Build()
chromaStyle, err := chromaGoBlogStyle.Builder().Build()
if err != nil {
return err
}
// Write the CSS to a buffer
var cssBuffer bytes.Buffer
if err = chromahtml.New(chromahtml.ClassPrefix("c-")).WriteCSS(&cssBuffer, chromaStyle); err != nil {
return err
}
// Compile asset
compiled, err := a.compileAsset(chromaPath, &cssBuffer)
if err != nil {
return err
}
// Add to map
a.assetFileNames[chromaPath] = compiled
return nil
// Generate and minify CSS
pipeReader, pipeWriter := io.Pipe()
go func() {
_ = chromahtml.New(chromahtml.ClassPrefix("c-")).WriteCSS(pipeWriter, chromaStyle)
_ = pipeWriter.Close()
}()
return a.compileAsset(chromaPath, pipeReader)
}

View File

@ -221,16 +221,18 @@ func mBytesString(size int64) string {
}
func htmlText(s string) string {
return htmlTextFromReader(strings.NewReader(s))
}
func htmlTextFromReader(r io.Reader) string {
// Build policy to only allow a subset of HTML tags
textPolicy := bluemonday.StrictPolicy()
textPolicy.AllowElements("h1", "h2", "h3", "h4", "h5", "h6") // Headers
textPolicy.AllowElements("p") // Paragraphs
textPolicy.AllowElements("ol", "ul", "li") // Lists
textPolicy.AllowElements("blockquote") // Blockquotes
// Filter HTML tags
htmlBuf := textPolicy.SanitizeReader(strings.NewReader(s))
// Read HTML into document
doc, _ := goquery.NewDocumentFromReader(htmlBuf)
// Read filtered HTML into document
doc, _ := goquery.NewDocumentFromReader(textPolicy.SanitizeReader(r))
var text strings.Builder
if bodyChild := doc.Find("body").Children(); bodyChild.Length() > 0 {
// Input was real HTML, so build the text from the body