aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2023-10-30 20:16:57 -0700
committerMike Crute <mike@crute.us>2023-10-30 20:16:57 -0700
commitdc21aa0eb842ee0bf6e730aed48b7299b987f6a7 (patch)
tree91ae2faf36627b0054247f5d55674604f8631d8d
parentbeb31aa8f2a19f50f52bdb55ad8c42f5f3e487db (diff)
downloadgolib-dc21aa0eb842ee0bf6e730aed48b7299b987f6a7.tar.bz2
golib-dc21aa0eb842ee0bf6e730aed48b7299b987f6a7.tar.xz
golib-dc21aa0eb842ee0bf6e730aed48b7299b987f6a7.zip
image: updates to webp clientv0.8.2
- Support a logger - Support readiness checks for the server - Support converting raw image bytes instead of tiff
-rw-r--r--image/webp/client.go82
1 files changed, 74 insertions, 8 deletions
diff --git a/image/webp/client.go b/image/webp/client.go
index 6b08b72..aabf240 100644
--- a/image/webp/client.go
+++ b/image/webp/client.go
@@ -13,7 +13,9 @@ import (
13 "path" 13 "path"
14 "strconv" 14 "strconv"
15 "syscall" 15 "syscall"
16 "time"
16 17
18 "code.crute.us/mcrute/golib/log"
17 "golang.org/x/image/tiff" 19 "golang.org/x/image/tiff"
18) 20)
19 21
@@ -22,8 +24,9 @@ import (
22// bytestream. This struct is only threadsafe after Start has been 24// bytestream. This struct is only threadsafe after Start has been
23// called. 25// called.
24type WebPConvertClient struct { 26type WebPConvertClient struct {
25 p *exec.Cmd 27 p *exec.Cmd
26 c *http.Client 28 c *http.Client
29 Logger log.LeveledLogger
27} 30}
28 31
29// Start starts the server, cancelling the passed context will stop 32// Start starts the server, cancelling the passed context will stop
@@ -31,12 +34,17 @@ type WebPConvertClient struct {
31// process and not just context.Background. This function will return an 34// process and not just context.Background. This function will return an
32// error if setup fails but does not block. 35// error if setup fails but does not block.
33func (c *WebPConvertClient) Start(ctx context.Context) error { 36func (c *WebPConvertClient) Start(ctx context.Context) error {
37 if c.Logger == nil {
38 c.Logger = &log.NoopLeveledLogger{}
39 }
40
34 sockDir, err := os.MkdirTemp("", "webp-*") 41 sockDir, err := os.MkdirTemp("", "webp-*")
35 if err != nil { 42 if err != nil {
36 return err 43 return err
37 } 44 }
38 45
39 sockPath := path.Join(sockDir, "webp.sock") 46 sockPath := path.Join(sockDir, "webp.sock")
47 c.Logger.Debugf("Webp conversion server socket: %s", sockPath)
40 48
41 c.c = &http.Client{ 49 c.c = &http.Client{
42 Transport: &http.Transport{ 50 Transport: &http.Transport{
@@ -53,6 +61,7 @@ func (c *WebPConvertClient) Start(ctx context.Context) error {
53 } 61 }
54 62
55 c.p.Cancel = func() error { 63 c.p.Cancel = func() error {
64 c.Logger.Debugf("Shutting down webp conversion server")
56 if err := c.p.Process.Signal(syscall.SIGTERM); err != nil { 65 if err := c.p.Process.Signal(syscall.SIGTERM); err != nil {
57 return err 66 return err
58 } 67 }
@@ -65,6 +74,11 @@ func (c *WebPConvertClient) Start(ctx context.Context) error {
65 os.RemoveAll(sockDir) 74 os.RemoveAll(sockDir)
66 }() 75 }()
67 76
77 if err := c.awaitServerReadiness(ctx, 10); err != nil {
78 c.p.Cancel()
79 return err
80 }
81
68 select { 82 select {
69 case err = <-ec: 83 case err = <-ec:
70 return err 84 return err
@@ -73,6 +87,47 @@ func (c *WebPConvertClient) Start(ctx context.Context) error {
73 } 87 }
74} 88}
75 89
90// awaitServerReadiness waits for the server to become ready by pinging
91// its endpoint. This should happen rather quickly, but without this
92// check it can be a race to see if the server starts before the first
93// call is made in some programs.
94func (c *WebPConvertClient) awaitServerReadiness(ctx context.Context, maxTries int) error {
95 for {
96 c.Logger.Debugf("Waiting for webp conversion server to be ready")
97
98 maxTries--
99 if maxTries == 0 {
100 break
101 }
102
103 if err := c.ping(ctx); err == nil {
104 return nil
105 }
106
107 time.Sleep(time.Second)
108 }
109 return fmt.Errorf("Webp server failed to become ready")
110}
111
112func (c *WebPConvertClient) ping(ctx context.Context) error {
113 req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://unix/ping", nil)
114 if err != nil {
115 return err
116 }
117
118 res, err := c.c.Do(req)
119 if err != nil {
120 return err
121 }
122 defer res.Body.Close()
123
124 if res.StatusCode != http.StatusOK {
125 return fmt.Errorf("server not ready")
126 }
127
128 return nil
129}
130
76// Convert encodes an image as TIFF and sends it to the conversion 131// Convert encodes an image as TIFF and sends it to the conversion
77// server. It returns either an error or the webp encoded bytes from the 132// server. It returns either an error or the webp encoded bytes from the
78// input image. 133// input image.
@@ -86,7 +141,18 @@ func (c *WebPConvertClient) Convert(ctx context.Context, img image.Image, qualit
86 return nil, err 141 return nil, err
87 } 142 }
88 143
89 req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://unix/", bb) 144 return c.ConvertBytes(ctx, bb.Bytes(), quality, lossless, exact)
145}
146
147// ConvertBytes takes an image in any format and sends it to the
148// conversion server. It returns either an error or the webp encoded
149// bytes from the input image.
150func (c *WebPConvertClient) ConvertBytes(ctx context.Context, img []byte, quality int, lossless, exact bool) ([]byte, error) {
151 if c.c == nil {
152 return nil, fmt.Errorf("WebPConvertClient has not been started")
153 }
154
155 req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://unix/", bytes.NewReader(img))
90 if err != nil { 156 if err != nil {
91 return nil, err 157 return nil, err
92 } 158 }
@@ -100,7 +166,7 @@ func (c *WebPConvertClient) Convert(ctx context.Context, img image.Image, qualit
100 } 166 }
101 defer res.Body.Close() 167 defer res.Body.Close()
102 168
103 if res.StatusCode != 200 { 169 if res.StatusCode != http.StatusOK {
104 e, err := io.ReadAll(res.Body) 170 e, err := io.ReadAll(res.Body)
105 if err != nil { 171 if err != nil {
106 return nil, err 172 return nil, err
@@ -108,10 +174,10 @@ func (c *WebPConvertClient) Convert(ctx context.Context, img image.Image, qualit
108 return nil, fmt.Errorf("WebPConvertClient server error: %s", e) 174 return nil, fmt.Errorf("WebPConvertClient server error: %s", e)
109 } 175 }
110 176
111 bb.Reset() 177 webp, err := io.ReadAll(res.Body)
112 if _, err := io.Copy(bb, res.Body); err != nil { 178 if err != nil {
113 return nil, err 179 return nil, fmt.Errorf("WebPConvertClient error reading response: %w", err)
114 } 180 }
115 181
116 return bb.Bytes(), nil 182 return webp, nil
117} 183}