Skip to content

Commit 5cf2008

Browse files
Merge pull request #4 from takutakahashi/webp
Implemented WebP Convert
2 parents 3d95a50 + 83e15ce commit 5cf2008

File tree

6 files changed

+154
-8
lines changed

6 files changed

+154
-8
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
go_version: [1.18, 1.19]
14+
go_version: [ 1.18, 1.19 ]
1515
steps:
1616
- name: Checkout
1717
uses: actions/checkout@v3
18+
- name: Install Dependencies
19+
run: |
20+
sudo apt update
21+
sudo apt install -y webp
1822
- name: Setup Go
1923
uses: actions/setup-go@v3
2024
with:

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ WORKDIR /go/src/oyaki
66
COPY . /go/src/oyaki
77

88
RUN CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${OYAKI_VERSION}" -o /go/bin/oyaki
9+
RUN apt update && apt install -y curl \
10+
&& curl https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.1-linux-x86-64.tar.gz --output libwebp.tar.gz \
11+
&& tar vzxf libwebp.tar.gz \
12+
&& mv libwebp-1.3.1-linux-x86-64/bin/cwebp /go/bin/
913

1014
FROM gcr.io/distroless/static-debian11
1115

1216
COPY --from=build /go/bin/oyaki /
17+
COPY --from=build /go/bin/cwebp /bin/
1318

1419
EXPOSE 8080
1520

go.sum

Whitespace-only changes.

main.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"net/url"
1111
"os"
12+
"path/filepath"
1213
"runtime/debug"
1314
"strconv"
1415
"syscall"
@@ -77,12 +78,22 @@ func proxy(w http.ResponseWriter, r *http.Request) {
7778
if len(xff) > 1 {
7879
req.Header.Set("X-Forwarded-For", xff)
7980
}
80-
81-
orgRes, err := client.Do(req)
82-
if err != nil {
83-
http.Error(w, "Get origin failed", http.StatusBadGateway)
84-
log.Printf("Get origin failed. %v\n", err)
85-
return
81+
var orgRes *http.Response
82+
pathExt := filepath.Ext(req.URL.Path)
83+
if pathExt == ".webp" {
84+
orgRes, err = doWebp(req)
85+
if err != nil {
86+
http.Error(w, "Get origin failed", http.StatusBadGateway)
87+
log.Printf("Get origin failed. %v\n", err)
88+
return
89+
}
90+
} else {
91+
orgRes, err = client.Do(req)
92+
if err != nil {
93+
http.Error(w, "Get origin failed", http.StatusBadGateway)
94+
log.Printf("Get origin failed. %v\n", err)
95+
return
96+
}
8697
}
8798
defer orgRes.Body.Close()
8899

@@ -130,8 +141,18 @@ func proxy(w http.ResponseWriter, r *http.Request) {
130141
return
131142
}
132143
defer buf.Reset()
144+
if pathExt == ".webp" {
145+
buf, err = convWebp(buf, []string{})
146+
if err != nil {
147+
http.Error(w, "image convert failed", http.StatusInternalServerError)
148+
log.Printf("Read origin body failed. %v\n", err)
149+
return
133150

134-
w.Header().Set("Content-Type", "image/jpeg")
151+
}
152+
w.Header().Set("Content-Type", "image/webp")
153+
} else {
154+
w.Header().Set("Content-Type", "image/jpeg")
155+
}
135156
w.Header().Set("Content-Length", strconv.Itoa(buf.Len()))
136157

137158
if _, err := io.Copy(w, buf); err != nil {

webp.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"log"
8+
"net/http"
9+
"net/url"
10+
"os"
11+
"os/exec"
12+
)
13+
14+
func doWebp(req *http.Request) (*http.Response, error) {
15+
var orgRes *http.Response
16+
orgURL := req.URL
17+
newPath := orgURL.Path[:len(orgURL.Path)-len(".webp")]
18+
newOrgURL, err := url.Parse(fmt.Sprintf("%s://%s%s?%s", orgURL.Scheme, orgURL.Host, newPath, orgURL.RawQuery))
19+
if err != nil {
20+
log.Println(err)
21+
return nil, err
22+
}
23+
newReq, err := http.NewRequest("GET", newOrgURL.String(), nil)
24+
newReq.Header = req.Header
25+
if err != nil {
26+
log.Println(err)
27+
return nil, err
28+
}
29+
orgRes, err = client.Do(newReq)
30+
if err != nil {
31+
log.Println(err)
32+
return nil, err
33+
}
34+
if orgRes.StatusCode != 200 {
35+
log.Println(orgRes.Status)
36+
return nil, err
37+
}
38+
return orgRes, nil
39+
}
40+
41+
func convWebp(src io.Reader, params []string) (*bytes.Buffer, error) {
42+
f, err := os.CreateTemp("/tmp", "")
43+
if err != nil {
44+
return nil, err
45+
}
46+
defer f.Close()
47+
defer os.Remove(f.Name())
48+
49+
_, err = io.Copy(f, src)
50+
if err != nil {
51+
return nil, err
52+
}
53+
params = append(params, "-quiet", "-mt", "-jpeg_like", f.Name(), "-o", "-")
54+
out, err := exec.Command("cwebp", params...).Output()
55+
if err != nil {
56+
log.Println(err)
57+
return nil, err
58+
}
59+
return bytes.NewBuffer(out), nil
60+
}

webp_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import (
4+
"io"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
)
9+
10+
func TestProxyWebP(t *testing.T) {
11+
ts := httptest.NewServer(http.HandlerFunc(proxy))
12+
13+
origin := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
14+
http.ServeFile(w, r, "./testdata/oyaki.jpg")
15+
}))
16+
17+
orgSrvURL = origin.URL
18+
url := ts.URL + "/oyaki.jpg.webp"
19+
20+
req, _ := http.NewRequest("GET", url, nil)
21+
resp, err := doWebp(req)
22+
if err != nil {
23+
t.Fatal(err)
24+
} else {
25+
io.ReadAll(resp.Body)
26+
resp.Body.Close()
27+
}
28+
// match with origin file info
29+
if resp.Header.Get("Content-Type") != "image/jpeg" {
30+
t.Error("wrong header Content-Type")
31+
t.Error(resp.Header)
32+
}
33+
}
34+
35+
func TestConvJPG2WebP(t *testing.T) {
36+
ts := httptest.NewServer(http.HandlerFunc(proxy))
37+
38+
origin := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
39+
http.ServeFile(w, r, "./testdata/oyaki.jpg")
40+
}))
41+
42+
orgSrvURL = origin.URL
43+
url := ts.URL + "/oyaki.jpg.webp"
44+
45+
req, _ := http.NewRequest("GET", url, nil)
46+
resp, err := doWebp(req)
47+
if err != nil {
48+
t.Fatal(err)
49+
}
50+
defer resp.Body.Close()
51+
_, err = convWebp(resp.Body, []string{})
52+
if err != nil {
53+
t.Fatal(err)
54+
}
55+
56+
}

0 commit comments

Comments
 (0)