Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented WebP Convert #4

Merged
merged 16 commits into from
Oct 12, 2023
Merged
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go_version: [1.18, 1.19]
go_version: [ 1.18, 1.19 ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Dependencies
run: |
sudo apt update
sudo apt install -y webp
- name: Setup Go
uses: actions/setup-go@v3
with:
Expand Down
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ WORKDIR /go/src/oyaki
COPY . /go/src/oyaki

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

FROM gcr.io/distroless/static-debian11

COPY --from=build /go/bin/oyaki /
COPY --from=build /go/bin/cwebp /bin/

EXPOSE 8080

Expand Down
Empty file added go.sum
Empty file.
35 changes: 28 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"os"
"path/filepath"
"runtime/debug"
"strconv"
"syscall"
Expand Down Expand Up @@ -77,12 +78,22 @@ func proxy(w http.ResponseWriter, r *http.Request) {
if len(xff) > 1 {
req.Header.Set("X-Forwarded-For", xff)
}

orgRes, err := client.Do(req)
if err != nil {
http.Error(w, "Get origin failed", http.StatusBadGateway)
log.Printf("Get origin failed. %v\n", err)
return
var orgRes *http.Response
pathExt := filepath.Ext(req.URL.Path)
if pathExt == ".webp" {
orgRes, err = doWebp(req)
if err != nil {
http.Error(w, "Get origin failed", http.StatusBadGateway)
log.Printf("Get origin failed. %v\n", err)
return
}
} else {
orgRes, err = client.Do(req)
if err != nil {
http.Error(w, "Get origin failed", http.StatusBadGateway)
log.Printf("Get origin failed. %v\n", err)
return
}
}
defer orgRes.Body.Close()

Expand Down Expand Up @@ -130,8 +141,18 @@ func proxy(w http.ResponseWriter, r *http.Request) {
return
}
defer buf.Reset()
if pathExt == ".webp" {
buf, err = convWebp(buf, []string{})
if err != nil {
http.Error(w, "image convert failed", http.StatusInternalServerError)
log.Printf("Read origin body failed. %v\n", err)
return

w.Header().Set("Content-Type", "image/jpeg")
}
w.Header().Set("Content-Type", "image/webp")
} else {
w.Header().Set("Content-Type", "image/jpeg")
}
w.Header().Set("Content-Length", strconv.Itoa(buf.Len()))

if _, err := io.Copy(w, buf); err != nil {
Expand Down
60 changes: 60 additions & 0 deletions webp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"os/exec"
)

func doWebp(req *http.Request) (*http.Response, error) {
var orgRes *http.Response
orgURL := req.URL
newPath := orgURL.Path[:len(orgURL.Path)-len(".webp")]
newOrgURL, err := url.Parse(fmt.Sprintf("%s://%s%s?%s", orgURL.Scheme, orgURL.Host, newPath, orgURL.RawQuery))
if err != nil {
log.Println(err)
return nil, err
}
newReq, err := http.NewRequest("GET", newOrgURL.String(), nil)
newReq.Header = req.Header
if err != nil {
log.Println(err)
return nil, err
}
orgRes, err = client.Do(newReq)
if err != nil {
log.Println(err)
return nil, err
}
if orgRes.StatusCode != 200 {
log.Println(orgRes.Status)
return nil, err
}
return orgRes, nil
}

func convWebp(src io.Reader, params []string) (*bytes.Buffer, error) {
f, err := os.CreateTemp("/tmp", "")
if err != nil {
return nil, err
}
defer f.Close()
defer os.Remove(f.Name())

_, err = io.Copy(f, src)
if err != nil {
return nil, err
}
params = append(params, "-quiet", "-mt", "-jpeg_like", f.Name(), "-o", "-")
out, err := exec.Command("cwebp", params...).Output()
if err != nil {
log.Println(err)
return nil, err
}
return bytes.NewBuffer(out), nil
}
56 changes: 56 additions & 0 deletions webp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"io"
"net/http"
"net/http/httptest"
"testing"
)

func TestProxyWebP(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(proxy))

origin := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./testdata/oyaki.jpg")
}))

orgSrvURL = origin.URL
url := ts.URL + "/oyaki.jpg.webp"

req, _ := http.NewRequest("GET", url, nil)
resp, err := doWebp(req)
if err != nil {
t.Fatal(err)
} else {
io.ReadAll(resp.Body)
resp.Body.Close()
}
// match with origin file info
if resp.Header.Get("Content-Type") != "image/jpeg" {
t.Error("wrong header Content-Type")
t.Error(resp.Header)
}
}

func TestConvJPG2WebP(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(proxy))

origin := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./testdata/oyaki.jpg")
}))

orgSrvURL = origin.URL
url := ts.URL + "/oyaki.jpg.webp"

req, _ := http.NewRequest("GET", url, nil)
resp, err := doWebp(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
_, err = convWebp(resp.Body, []string{})
if err != nil {
t.Fatal(err)
}

}