honk/backend.go

161 lines
3.6 KiB
Go
Raw Normal View History

//
// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package main
import (
"bytes"
2023-05-01 21:32:36 +02:00
"errors"
"net"
2023-05-01 21:32:36 +02:00
"net/http"
"net/rpc"
"os"
"os/exec"
2023-05-01 21:32:36 +02:00
"strings"
"humungus.tedunangst.com/r/webs/gate"
"humungus.tedunangst.com/r/webs/image"
)
type Shrinker struct {
}
type ShrinkerArgs struct {
Buf []byte
Params image.Params
}
type ShrinkerResult struct {
Image *image.Image
}
var shrinkgate = gate.NewLimiter(4)
func (s *Shrinker) Shrink(args *ShrinkerArgs, res *ShrinkerResult) error {
shrinkgate.Start()
defer shrinkgate.Finish()
img, err := image.Vacuum(bytes.NewReader(args.Buf), args.Params)
if err != nil {
return err
}
res.Image = img
return nil
}
func backendSockname() string {
return dataDir + "/backend.sock"
}
2023-05-01 21:32:36 +02:00
func isSVG(data []byte) bool {
ct := http.DetectContentType(data)
if strings.HasPrefix(ct, "text/xml") {
return strings.Index(string(data), "<!DOCTYPE svg PUBLIC") != -1
}
if strings.HasPrefix(ct, "text/plain") {
return bytes.HasPrefix(data, []byte("<svg "))
}
return ct == "image/svg+xml"
}
func imageFromSVG(data []byte) (*image.Image, error) {
if len(data) > 100000 {
return nil, errors.New("my svg is too big")
}
svg := &image.Image{
Data: data,
Format: "svg+xml",
}
return svg, nil
}
func shrinkit(data []byte) (*image.Image, error) {
2023-05-01 21:32:36 +02:00
if isSVG(data) {
return imageFromSVG(data)
}
cl, err := rpc.Dial("unix", backendSockname())
if err != nil {
return nil, err
}
defer cl.Close()
var res ShrinkerResult
err = cl.Call("Shrinker.Shrink", &ShrinkerArgs{
Buf: data,
Params: image.Params{LimitSize: 4200 * 4200, MaxWidth: 2048, MaxHeight: 2048},
}, &res)
if err != nil {
return nil, err
}
return res.Image, nil
}
var backendhooks []func()
2022-02-23 21:24:58 +01:00
func orphancheck() {
var b [1]byte
os.Stdin.Read(b[:])
dlog.Printf("backend shutting down")
2022-02-24 23:00:00 +01:00
os.Exit(0)
2022-02-23 21:24:58 +01:00
}
func backendServer() {
2022-02-06 06:42:13 +01:00
dlog.Printf("backend server running")
2022-02-23 21:24:58 +01:00
go orphancheck()
shrinker := new(Shrinker)
srv := rpc.NewServer()
err := srv.Register(shrinker)
if err != nil {
2022-02-06 06:42:13 +01:00
elog.Panicf("unable to register shrinker: %s", err)
}
sockname := backendSockname()
err = os.Remove(sockname)
if err != nil && !os.IsNotExist(err) {
2022-02-06 06:42:13 +01:00
elog.Panicf("unable to unlink socket: %s", err)
}
lis, err := net.Listen("unix", sockname)
if err != nil {
2022-02-06 06:42:13 +01:00
elog.Panicf("unable to register shrinker: %s", err)
}
2021-03-15 19:11:31 +01:00
err = setLimits()
if err != nil {
2022-02-06 06:42:13 +01:00
elog.Printf("error setting backend limits: %s", err)
2021-03-15 19:11:31 +01:00
}
for _, h := range backendhooks {
2019-11-12 22:27:12 +01:00
h()
}
srv.Accept(lis)
}
2020-04-28 03:14:05 +02:00
func runBackendServer() {
2022-02-24 23:00:00 +01:00
r, w, err := os.Pipe()
2022-02-23 21:24:58 +01:00
if err != nil {
elog.Panicf("can't pipe: %s", err)
}
2022-02-09 22:30:58 +01:00
proc := exec.Command(os.Args[0], reexecArgs("backend")...)
proc.Stdout = os.Stdout
proc.Stderr = os.Stderr
2022-02-23 21:24:58 +01:00
proc.Stdin = r
err = proc.Start()
if err != nil {
2022-02-06 06:42:13 +01:00
elog.Panicf("can't exec backend: %s", err)
}
go func() {
proc.Wait()
elog.Printf("lost the backend: %s", err)
w.Close()
}()
}