diff --git a/activityPub.go b/activityPub.go index 6aa462e..1df5462 100644 --- a/activityPub.go +++ b/activityPub.go @@ -126,41 +126,36 @@ func (a *goBlog) apHandleWebfinger(w http.ResponseWriter, r *http.Request) { } apIri := a.apIri(blog) // Encode - buf := bufferpool.Get() - defer bufferpool.Put(buf) - if err := json.NewEncoder(buf).Encode(map[string]any{ - "subject": a.webfingerAccts[apIri], - "aliases": []string{ - a.webfingerAccts[apIri], - apIri, - }, - "links": []map[string]string{ - { - "rel": "self", - "type": contenttype.AS, - "href": apIri, + pr, pw := io.Pipe() + go func() { + _ = pw.CloseWithError(json.NewEncoder(pw).Encode(map[string]any{ + "subject": a.webfingerAccts[apIri], + "aliases": []string{a.webfingerAccts[apIri], apIri}, + "links": []map[string]string{ + { + "rel": "self", "type": contenttype.AS, "href": apIri, + }, + { + "rel": "http://webfinger.net/rel/profile-page", + "type": "text/html", "href": apIri, + }, }, - { - "rel": "http://webfinger.net/rel/profile-page", - "type": "text/html", - "href": apIri, - }, - }, - }); err != nil { - a.serveError(w, r, "Encoding failed", http.StatusInternalServerError) - return - } + })) + }() w.Header().Set(contentType, "application/jrd+json"+contenttype.CharsetUtf8Suffix) - _ = a.min.Get().Minify(contenttype.JSON, w, buf) + _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr)) } const activityPubMentionsParameter = "activitypubmentions" func (a *goBlog) apCheckMentions(p *post) { - contentBuf := bufferpool.Get() - a.postHtmlToWriter(contentBuf, &postHtmlOptions{p: p}) - links, err := allLinksFromHTML(contentBuf, a.fullPostURL(p)) - bufferpool.Put(contentBuf) + pr, pw := io.Pipe() + go func() { + a.postHtmlToWriter(pw, &postHtmlOptions{p: p}) + _ = pw.Close() + }() + links, err := allLinksFromHTML(pr, a.fullPostURL(p)) + _ = pr.CloseWithError(err) if err != nil { log.Println("Failed to extract links from post: " + err.Error()) return diff --git a/activityStreams.go b/activityStreams.go index e281a89..467c0ff 100644 --- a/activityStreams.go +++ b/activityStreams.go @@ -171,6 +171,7 @@ func (a *goBlog) serveAPItem(w http.ResponseWriter, r *http.Request, status int, return } // Send response + w.WriteHeader(status) w.Header().Set(contentType, contenttype.ASUTF8) _ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary)) } diff --git a/check.go b/check.go index a2c6fc2..d2f2b4c 100644 --- a/check.go +++ b/check.go @@ -125,6 +125,7 @@ func (a *goBlog) allLinksToCheck(posts ...*post) ([]*stringPair, error) { _ = pw.Close() }() links, err := allLinksFromHTML(pr, a.fullPostURL(post)) + _ = pr.CloseWithError(err) if err != nil { return nil, err } diff --git a/docs/build.md b/docs/build.md index a01ba46..1be5bd8 100644 --- a/docs/build.md +++ b/docs/build.md @@ -30,7 +30,7 @@ Requirements: - Linux - git -- go >= 1.19 +- go >= 1.20 - libsqlite3 with FTS5 enabled >= 3.31 (the newer the better) Build command: diff --git a/geoTiles.go b/geoTiles.go index 1713cc2..f442a05 100644 --- a/geoTiles.go +++ b/geoTiles.go @@ -15,11 +15,12 @@ func (a *goBlog) proxyTiles() http.HandlerFunc { } return func(w http.ResponseWriter, r *http.Request) { // Create a new request to proxy to the tile server - targetUrl := tileSource - targetUrl = strings.ReplaceAll(targetUrl, "{s}", chi.URLParam(r, "s")) - targetUrl = strings.ReplaceAll(targetUrl, "{z}", chi.URLParam(r, "z")) - targetUrl = strings.ReplaceAll(targetUrl, "{x}", chi.URLParam(r, "x")) - targetUrl = strings.ReplaceAll(targetUrl, "{y}", chi.URLParam(r, "y")) + targetUrl := strings.NewReplacer( + "{s}", chi.URLParam(r, "s"), + "{z}", chi.URLParam(r, "z"), + "{x}", chi.URLParam(r, "x"), + "{y}", chi.URLParam(r, "y"), + ).Replace(tileSource) proxyRequest, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, targetUrl, nil) // Copy request headers for _, k := range []string{ @@ -29,7 +30,6 @@ func (a *goBlog) proxyTiles() http.HandlerFunc { cacheControl, "If-Modified-Since", "If-None-Match", - "User-Agent", } { proxyRequest.Header.Set(k, r.Header.Get(k)) } diff --git a/go.mod b/go.mod index ab8a765..c3da7e6 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.goblog.app/app -go 1.19 +go 1.20 require ( git.jlel.se/jlelse/go-geouri v0.0.0-20210525190615-a9c1d50f42d6 @@ -11,7 +11,7 @@ require ( github.com/alecthomas/chroma/v2 v2.4.0 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b - github.com/carlmjohnson/requests v0.23.1 + github.com/carlmjohnson/requests v0.23.2 // master github.com/cretz/bine v0.2.1-0.20221201125941-b9d31d9c7866 github.com/dchest/captcha v1.0.0 @@ -61,7 +61,7 @@ require ( github.com/traefik/yaegi v0.14.4-0.20230117132604-1679870ea3c8 github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2 github.com/xhit/go-simple-mail/v2 v2.13.0 - github.com/yuin/goldmark v1.5.3 + github.com/yuin/goldmark v1.5.4 // master github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 golang.org/x/crypto v0.5.0 @@ -71,8 +71,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 maunium.net/go/mautrix v0.13.0 nhooyr.io/websocket v1.8.7 - // main - willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e + willnorris.com/go/microformats v1.2.0 ) require ( diff --git a/go.sum b/go.sum index cbf1e3b..23bbe97 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8 github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= -github.com/carlmjohnson/requests v0.23.1 h1:d8FpOZC6We3raxa3cW1oEyK+cNz4XSW8A6wjMkMsbOM= -github.com/carlmjohnson/requests v0.23.1/go.mod h1:i/0+cvgndkM7SPtw911bcbYR4CP+/9vP/+TbcFqPz3A= +github.com/carlmjohnson/requests v0.23.2 h1:SzaY+/5v8QOvt++7HTXe1xgmIb3wc/bYf2QJmrO73sM= +github.com/carlmjohnson/requests v0.23.2/go.mod h1:09VwhOaRQYCraJcByjEuvuOGO1jxUjIx6vnAEkt2ges= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -589,8 +589,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M= -github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= +github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 h1:XZjLcLoTPNZuxppY3gwhRqo/T2XF6JMGFFdkAjX3w1w= github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -993,7 +993,7 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e h1:9dbX305ANi38nJlQP9ve2v6U1NQp9YFGP3JgrT7Rp7g= -willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e/go.mod h1:RrlwCSvib4qz+JICKiN7rON4phzQ3HAT7j6s4O2cZj4= +willnorris.com/go/microformats v1.2.0 h1:73pzJCLJM69kYE5qsLI9OOC/7sImNVOzya9EQ0+1wmM= +willnorris.com/go/microformats v1.2.0/go.mod h1:RrlwCSvib4qz+JICKiN7rON4phzQ3HAT7j6s4O2cZj4= willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0 h1:V5+O+YZHchEwu6ZmPcqT1dQ+mHgE356Q+w9SVOQ+QZg= willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0/go.mod h1:DgeruqKIsZtcDXVXNbBHa0YYEm88oAnK7PahkDtuCvw= diff --git a/healthcheck.go b/healthcheck.go index 00acfa8..af423e7 100644 --- a/healthcheck.go +++ b/healthcheck.go @@ -20,7 +20,7 @@ func (a *goBlog) healthcheck() bool { fmt.Println("healthcheck:", err.Error()) return false } - defer resp.Body.Close() + _ = resp.Body.Close() return resp.StatusCode == 200 } diff --git a/indieAuthServer.go b/indieAuthServer.go index e9eb694..875edac 100644 --- a/indieAuthServer.go +++ b/indieAuthServer.go @@ -33,7 +33,7 @@ var ( // Server Metadata // https://indieauth.spec.indieweb.org/#x4-1-1-indieauth-server-metadata -func (a *goBlog) indieAuthMetadata(w http.ResponseWriter, r *http.Request) { +func (a *goBlog) indieAuthMetadata(w http.ResponseWriter, _ *http.Request) { resp := map[string]any{ "issuer": a.getInstanceRootURL(), "authorization_endpoint": a.getFullAddress(indieAuthPath), diff --git a/mediaCompression.go b/mediaCompression.go index ee4381b..3d8066e 100644 --- a/mediaCompression.go +++ b/mediaCompression.go @@ -87,28 +87,27 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli return "", tinifyErr } // Resize and download image - imgBuffer := bufferpool.Get() - defer bufferpool.Put(imgBuffer) - err = requests. - URL(compressedLocation). - Client(hc). - Method(http.MethodPost). - BasicAuth("api", tf.key). - BodyJSON(map[string]any{ - "resize": map[string]any{ - "method": "fit", - "width": defaultCompressionWidth, - "height": defaultCompressionHeight, - }, - }). - ToBytesBuffer(imgBuffer). - Fetch(context.Background()) - if err != nil { - log.Println("Tinify error:", err.Error()) - return "", tinifyErr - } + pr, pw := io.Pipe() + go func() { + _ = pw.CloseWithError(requests. + URL(compressedLocation). + Client(hc). + Method(http.MethodPost). + BasicAuth("api", tf.key). + BodyJSON(map[string]any{ + "resize": map[string]any{ + "method": "fit", + "width": defaultCompressionWidth, + "height": defaultCompressionHeight, + }, + }). + ToWriter(pw). + Fetch(context.Background())) + }() // Upload compressed file - return uploadCompressedFile(fileExtension, imgBuffer, upload) + res, err := uploadCompressedFile(fileExtension, pr, upload) + _ = pr.CloseWithError(err) + return res, err } type cloudflare struct{} @@ -121,19 +120,18 @@ func (*cloudflare) compress(url string, upload mediaStorageSaveFunc, hc *http.Cl // Force jpeg fileExtension := "jpeg" // Compress - imgBuffer := bufferpool.Get() - defer bufferpool.Put(imgBuffer) - err := requests. - URL(fmt.Sprintf("https://www.cloudflare.com/cdn-cgi/image/f=jpeg,q=75,metadata=none,fit=scale-down,w=%d,h=%d/%s", defaultCompressionWidth, defaultCompressionHeight, url)). - Client(hc). - ToBytesBuffer(imgBuffer). - Fetch(context.Background()) - if err != nil { - log.Println("Cloudflare error:", err.Error()) - return "", errors.New("failed to compress image using cloudflare") - } + pr, pw := io.Pipe() + go func() { + _ = pw.CloseWithError(requests. + URL(fmt.Sprintf("https://www.cloudflare.com/cdn-cgi/image/f=jpeg,q=75,metadata=none,fit=scale-down,w=%d,h=%d/%s", defaultCompressionWidth, defaultCompressionHeight, url)). + Client(hc). + ToWriter(pw). + Fetch(context.Background())) + }() // Upload compressed file - return uploadCompressedFile(fileExtension, imgBuffer, upload) + res, err := uploadCompressedFile(fileExtension, pr, upload) + _ = pr.CloseWithError(err) + return res, err } type localMediaCompressor struct{} @@ -144,20 +142,13 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h if !allowed { return "", nil } - // Download image - imgBuffer := bufferpool.Get() - defer bufferpool.Put(imgBuffer) - err := requests. - URL(url). - Client(hc). - ToBytesBuffer(imgBuffer). - Fetch(context.Background()) - if err != nil { - log.Println("Local compressor error:", err.Error()) - return "", errors.New("failed to download image using local compressor") - } - // Decode image - img, err := imaging.Decode(imgBuffer, imaging.AutoOrientation(true)) + // Download and decode image + pr, pw := io.Pipe() + go func() { + _ = pw.CloseWithError(requests.URL(url).Client(hc).ToWriter(pw).Fetch(context.Background())) + }() + img, err := imaging.Decode(pr, imaging.AutoOrientation(true)) + _ = pr.CloseWithError(err) if err != nil { log.Println("Local compressor error:", err.Error()) return "", errors.New("failed to compress image using local compressor") @@ -165,20 +156,19 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h // Resize image resizedImage := imaging.Fit(img, defaultCompressionWidth, defaultCompressionHeight, imaging.Lanczos) // Encode image - resizedBuffer := bufferpool.Get() - defer bufferpool.Put(resizedBuffer) - switch fileExtension { - case "jpg", "jpeg": - err = imaging.Encode(resizedBuffer, resizedImage, imaging.JPEG, imaging.JPEGQuality(75)) - case "png": - err = imaging.Encode(resizedBuffer, resizedImage, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression)) - } - if err != nil { - log.Println("Local compressor error:", err.Error()) - return "", errors.New("failed to compress image using local compressor") - } + pr, pw = io.Pipe() + go func() { + switch fileExtension { + case "png": + _ = pw.CloseWithError(imaging.Encode(pw, resizedImage, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression))) + default: + _ = pw.CloseWithError(imaging.Encode(pw, resizedImage, imaging.JPEG, imaging.JPEGQuality(75))) + } + }() // Upload compressed file - return uploadCompressedFile(fileExtension, resizedBuffer, upload) + res, err := uploadCompressedFile(fileExtension, pr, upload) + _ = pr.CloseWithError(err) + return res, err } func uploadCompressedFile(fileExtension string, r io.Reader, upload mediaStorageSaveFunc) (string, error) { @@ -186,7 +176,10 @@ func uploadCompressedFile(fileExtension string, r io.Reader, upload mediaStorage hash := sha256.New() tempBuffer := bufferpool.Get() defer bufferpool.Put(tempBuffer) - _, _ = io.Copy(io.MultiWriter(tempBuffer, hash), r) + _, err := io.Copy(io.MultiWriter(tempBuffer, hash), r) + if err != nil { + return "", err + } // Upload buffer return upload(fmt.Sprintf("%x.%s", hash.Sum(nil), fileExtension), tempBuffer) } diff --git a/mediaStorage.go b/mediaStorage.go index 67b1245..76e0941 100644 --- a/mediaStorage.go +++ b/mediaStorage.go @@ -29,12 +29,14 @@ func (a *goBlog) mediaStorageEnabled() bool { return a.mediaStorage != nil } +var errNoMediaStorageConfigured = errors.New("no media storage configured") + type mediaStorageSaveFunc func(filename string, file io.Reader) (location string, err error) func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) { a.initMediaStorage() if a.mediaStorage == nil { - return "", errors.New("no media storage configured") + return "", errNoMediaStorageConfigured } loc, err := a.mediaStorage.save(filename, f) if err != nil { @@ -46,7 +48,7 @@ func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) { func (a *goBlog) deleteMediaFile(filename string) error { a.initMediaStorage() if a.mediaStorage == nil { - return errors.New("no media storage configured") + return errNoMediaStorageConfigured } return a.mediaStorage.delete(filepath.Base(filename)) } @@ -61,7 +63,7 @@ type mediaFile struct { func (a *goBlog) mediaFiles() ([]*mediaFile, error) { a.initMediaStorage() if a.mediaStorage == nil { - return nil, errors.New("no media storage configured") + return nil, errNoMediaStorageConfigured } return a.mediaStorage.files() } diff --git a/nodeinfo.go b/nodeinfo.go index 2985918..4006c67 100644 --- a/nodeinfo.go +++ b/nodeinfo.go @@ -8,7 +8,7 @@ import ( "go.goblog.app/app/pkgs/contenttype" ) -func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) { +func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, _ *http.Request) { result := map[string]any{ "links": []map[string]any{ { @@ -25,7 +25,7 @@ func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) { _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr)) } -func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) { +func (a *goBlog) serveNodeInfo(w http.ResponseWriter, _ *http.Request) { localPosts, _ := a.db.countPosts(&postsRequestConfig{ status: []postStatus{statusPublished}, visibility: []postVisibility{visibilityPublic}, diff --git a/profileImage.go b/profileImage.go index ee04c72..40ab1cd 100644 --- a/profileImage.go +++ b/profileImage.go @@ -19,7 +19,6 @@ import ( _ "embed" "github.com/disintegration/imaging" - "go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/contenttype" ) @@ -91,19 +90,20 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc { quality = 75 } // Read from database - var imageBytes []byte + var imageReader io.ReadCloser if a.hasProfileImage() { var err error - imageBytes, err = os.ReadFile(profileImageFile) - if err != nil || imageBytes == nil { - a.serveError(w, r, "Failed to read image file", http.StatusInternalServerError) + imageReader, err = os.Open(profileImageFile) + if err != nil { + a.serveError(w, r, "Failed to open image file", http.StatusInternalServerError) return } } else { - imageBytes = defaultLogo + imageReader = io.NopCloser(bytes.NewReader(defaultLogo)) } // Decode image - img, err := imaging.Decode(bytes.NewReader(imageBytes), imaging.AutoOrientation(true)) + img, err := imaging.Decode(imageReader, imaging.AutoOrientation(true)) + _ = imageReader.Close() if err != nil { a.serveError(w, r, "Failed to decode image", http.StatusInternalServerError) return @@ -111,16 +111,14 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc { // Resize image resizedImage := imaging.Fit(img, width, height, imaging.Lanczos) // Encode - resizedBuffer := bufferpool.Get() - defer bufferpool.Put(resizedBuffer) - err = encode(resizedBuffer, resizedImage, quality) - if err != nil { - a.serveError(w, r, "Failed to encode image", http.StatusInternalServerError) - return - } + pr, pw := io.Pipe() + go func() { + _ = pw.CloseWithError(encode(pw, resizedImage, quality)) + }() // Return w.Header().Set(contentType, mediaType) - _, _ = io.Copy(w, resizedBuffer) + _, err = io.Copy(w, pr) + _ = pr.CloseWithError(err) } } diff --git a/render.go b/render.go index 8c7af60..17c6291 100644 --- a/render.go +++ b/render.go @@ -43,7 +43,7 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st renderPipeReader, renderPipeWriter := io.Pipe() go func() { f(htmlbuilder.NewHtmlBuilder(renderPipeWriter), data) - renderPipeWriter.Close() + _ = renderPipeWriter.Close() }() // Run UI plugins pluginPipeReader, pluginPipeWriter := io.Pipe() @@ -52,7 +52,7 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st blog: data.BlogString, path: r.URL.Path, }, renderPipeReader, pluginPipeWriter) - pluginPipeWriter.Close() + _ = pluginPipeWriter.Close() }() // Return minified HTML _ = pluginPipeReader.CloseWithError(a.min.Get().Minify(contenttype.HTML, w, pluginPipeReader)) @@ -69,6 +69,7 @@ func (a *goBlog) chainUiPlugins(plugins []any, rc *pluginRenderContext, rendered _ = writer.Close() }() a.chainUiPlugins(plugins[1:], rc, reader, modified) + _ = reader.Close() } func (a *goBlog) checkRenderData(r *http.Request, data *renderData) { diff --git a/sitemap.go b/sitemap.go index 3580c22..2a9a024 100644 --- a/sitemap.go +++ b/sitemap.go @@ -173,7 +173,7 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) { a.writeSitemapXML(w, r, sm) } -func (a *goBlog) writeSitemapXML(w http.ResponseWriter, r *http.Request, sm any) { +func (a *goBlog) writeSitemapXML(w http.ResponseWriter, _ *http.Request, sm any) { pr, pw := io.Pipe() go func() { _, _ = io.WriteString(pw, xml.Header) diff --git a/tts.go b/tts.go index 2790e6b..87504d7 100644 --- a/tts.go +++ b/tts.go @@ -75,6 +75,7 @@ func (a *goBlog) createPostTTSAudio(p *post) error { _ = phw.Close() }() postHtmlText, err := htmlTextFromReader(phr) + _ = phr.CloseWithError(err) if err != nil { return err } @@ -82,14 +83,13 @@ func (a *goBlog) createPostTTSAudio(p *post) error { // Create TTS audio for each part partReaders := []io.Reader{} + partWriters := []*io.PipeWriter{} var g errgroup.Group for _, part := range parts { part := part pr, pw := io.Pipe() - defer func() { - pw.Close() - }() partReaders = append(partReaders, pr) + partWriters = append(partWriters, pw) g.Go(func() error { // Build SSML ssml := "" + html.EscapeString(part) + "" @@ -105,6 +105,9 @@ func (a *goBlog) createPostTTSAudio(p *post) error { defer bufferpool.Put(buf) hash := sha256.New() err = mp3merge.MergeMP3(io.MultiWriter(buf, hash), partReaders...) + for _, pw := range partWriters { + _ = pw.CloseWithError(err) + } if err != nil { return err } diff --git a/updateDeps.sh b/updateDeps.sh index 908e2a9..ffca43a 100755 --- a/updateDeps.sh +++ b/updateDeps.sh @@ -6,7 +6,6 @@ EXTRA=" github.com/cretz/bine@master github.com/tkrajina/gpxgo@master github.com/yuin/goldmark-emoji@master -willnorris.com/go/microformats@main github.com/traefik/yaegi@master " diff --git a/utils.go b/utils.go index 3124ed7..bfc6099 100644 --- a/utils.go +++ b/utils.go @@ -289,7 +289,9 @@ func cleanHTMLText(s string) string { // Clean HTML with UGC policy and return text pr, pw := io.Pipe() go func() { _ = pw.CloseWithError(bluemonday.UGCPolicy().SanitizeReaderToWriter(strings.NewReader(s), pw)) }() - s, _ = htmlTextFromReader(pr) + var err error + s, err = htmlTextFromReader(pr) + _ = pr.CloseWithError(err) return s }