Skip to content

Commit

Permalink
Merge pull request #55 from Pixboost/bugfix/#32/accept-encoding
Browse files Browse the repository at this point in the history
Bugfix/#32/accept encoding
  • Loading branch information
dooman87 authored Feb 12, 2025
2 parents 87004b4 + 0d0c6d2 commit b39aa95
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 55 deletions.
42 changes: 22 additions & 20 deletions .github/workflows/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,21 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Cache Docker layers
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
-
name: Build a dev image
- name: Build a dev image
id: docker_build_dev
uses: docker/build-push-action@v5
with:
Expand All @@ -45,16 +42,24 @@ jobs:
tags: transformimgs-dev
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
-
name: Run tests
- name: Gosec
id: gosec
run: |
docker run --entrypoint=/go/src/github.com/Pixboost/transformimgs/test.sh -v $(pwd):/go/src/github.com/Pixboost/transformimgs transformimgs-dev
-
name: Code coverage
CWD=$(pwd)
cd /tmp
curl -L https://github.com/securego/gosec/releases/download/v2.22.0/gosec_2.22.0_linux_amd64.tar.gz | tar zx
cd $CWD
/tmp/gosec -exclude-dir illustration ./...
cd illustration
/tmp/gosec ./...
- name: Run tests
run: |
bash <(curl -s https://codecov.io/bash)
-
name: Build a prod image
docker run --entrypoint=/go/src/github.com/Pixboost/transformimgs/test.sh -v $(pwd):/go/src/github.com/Pixboost/transformimgs transformimgs-dev
- id: codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Build a prod image
id: docker_build_prod
uses: docker/build-push-action@v5
with:
Expand All @@ -67,17 +72,14 @@ jobs:
"BRANCH=${{ github.ref_name }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
-
name: Run the service
- name: Run the service
run: |
docker run -p 8080:8080 -d transformimgs
sleep 5
-
name: Smoketest
- name: Smoketest
run: |
curl -o /dev/null -f http://localhost:8080/img/https://pixboost.com/img/homepage/hero.jpg/resize?size=x600
-
name: Publish image
- name: Publish image
if: ${{ github.event_name == 'release' }}
uses: docker/build-push-action@v5
with:
Expand Down
9 changes: 8 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"os"
"runtime"
"time"
)

func main() {
Expand Down Expand Up @@ -46,7 +47,13 @@ func main() {
router.HandleFunc("/health", health.Health)

img.Log.Printf("Running the application on port 8080...\n")
err = http.ListenAndServe(":8080", router)
server := http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
err = server.ListenAndServe()

if err != nil {
img.Log.Errorf("Error while stopping application: %+v", err)
Expand Down
11 changes: 7 additions & 4 deletions illustration/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ func main() {
count uint
currColor *imagick.PixelWand
pixelsCount = uint(0)
totalPixelsCount = float32(mw.GetImageHeight() * mw.GetImageWidth())
tenPercent = uint(totalPixelsCount * 0.1)
fiftyPercent = uint(totalPixelsCount * 0.5)
totalPixelsCount = mw.GetImageHeight() * mw.GetImageWidth()
tenPercent = totalPixelsCount / 10
fiftyPercent = totalPixelsCount / 2
isBackground = false
lastBackgroundColor *imagick.PixelWand
colorsInBackground = uint(0)
Expand Down Expand Up @@ -108,14 +108,17 @@ func main() {
pixelsInBackground = 0
} else {
pixelsCount += count
fiftyPercent = uint((totalPixelsCount - float32(pixelsInBackground)) * 0.5)
fiftyPercent = (totalPixelsCount - pixelsInBackground) / 2
}
}
default:
pixelsCount += count
}
}

if colorIdx < 0 {
log.Fatal("colorIdx < 0")
}
colorsCntIn50Pct := uint(colorIdx) - colorsInBackground

fmt.Print(colorsCntIn50Pct < 10 || (float32(colorsCntIn50Pct)/float32(colorsCnt)) <= 0.02)
Expand Down
18 changes: 14 additions & 4 deletions img/loader/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var client = &http.Client{
},
}

func (r *Http) Load(url string, _ context.Context) (*img.Image, error) {
func (r *Http) Load(url string, ctx context.Context) (*img.Image, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
Expand All @@ -44,6 +44,14 @@ func (r *Http) Load(url string, _ context.Context) (*img.Image, error) {
}
}

if moreHeaders, ok := img.HeaderFromContext(ctx); ok {
for k, v := range *moreHeaders {
for _, headerVal := range v {
req.Header.Add(k, headerVal)
}
}
}

resp, err := client.Do(req)
if err != nil {
return nil, err
Expand All @@ -58,15 +66,17 @@ func (r *Http) Load(url string, _ context.Context) (*img.Image, error) {
}

contentType := resp.Header.Get("Content-Type")
contentEncoding := resp.Header.Get("Content-Encoding")

result, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}

return &img.Image{
Id: url,
Data: result,
MimeType: contentType,
Id: url,
Data: result,
MimeType: contentType,
ContentEncoding: contentEncoding,
}, nil
}
29 changes: 27 additions & 2 deletions img/loader/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package loader_test

import (
"context"
"github.com/Pixboost/transformimgs/v8/img"
"github.com/Pixboost/transformimgs/v8/img/loader"
"github.com/dooman87/kolibri/test"
"net/http"
Expand Down Expand Up @@ -72,7 +73,7 @@ func TestHttp_LoadImgErrorResponseStatus(t *testing.T) {
)
}

func TestHttp_LoadCustomHeaders(t *testing.T) {
func TestHttp_LoadCustomGlobalHeaders(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("this-is-header") != "wow" {
w.WriteHeader(http.StatusInternalServerError)
Expand Down Expand Up @@ -100,7 +101,7 @@ func TestHttp_LoadCustomHeaders(t *testing.T) {
)
}

func FuzzHttp_LoadCustomHeaders(f *testing.F) {
func FuzzHttp_LoadCustomGlobalHeaders(f *testing.F) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "cool/stuff")
w.Write([]byte("123"))
Expand Down Expand Up @@ -137,3 +138,27 @@ func FuzzHttp_LoadCustomHeaders(f *testing.F) {
}
})
}

func TestHttp_LoadCustomContextHeaders(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("this-is-header") != "wow" {
w.WriteHeader(http.StatusInternalServerError)
} else {
w.Header().Add("Content-Type", "cool/stuff")
w.Write([]byte("123"))
}
}))
defer server.Close()

httpLoader := &loader.Http{}

image, err := httpLoader.Load(server.URL, img.NewContextWithHeaders(context.Background(), &http.Header{
"This-Is-Header": []string{"wow"},
}))

test.Error(t,
test.Nil(err, "error"),
test.Equal("cool/stuff", image.MimeType, "content type"),
test.Equal("123", string(image.Data), "resulted image"),
)
}
4 changes: 2 additions & 2 deletions img/processor/imagemagick.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func (p *ImageMagick) Optimise(config *img.TransformationConfig) (*img.Image, er

func (p *ImageMagick) execImagemagick(in *bytes.Reader, args []string, imgId string) ([]byte, error) {
var out, cmderr bytes.Buffer
cmd := exec.Command(p.convertCmd)
cmd := exec.Command(p.convertCmd) // #nosec G204 - sanitizing before assigning

cmd.Args = append(cmd.Args, args...)

Expand Down Expand Up @@ -295,7 +295,7 @@ func (p *ImageMagick) LoadImageInfo(src *img.Image) (*img.Info, error) {
var out, cmderr bytes.Buffer
imgId := src.Id
in := bytes.NewReader(src.Data)
cmd := exec.Command(p.identifyCmd)
cmd := exec.Command(p.identifyCmd) // #nosec G204 - sanitizing before assigning
cmd.Args = append(cmd.Args, "-format", "%m %Q %[opaque] %w %h", "-")

cmd.Stdin = in
Expand Down
41 changes: 33 additions & 8 deletions img/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,17 +192,23 @@ func (r *Service) AsIs(resp http.ResponseWriter, req *http.Request) {

Log.Printf("Requested image %s as is\n", imgUrl)

result, err := r.Loader.Load(imgUrl, req.Context())
var proxyHeaders = make(http.Header)
accept := req.Header.Get("Accept")
if len(accept) > 0 {
proxyHeaders.Add("Accept", accept)
}
acceptEncoding := req.Header.Get("Accept-Encoding")
if len(acceptEncoding) > 0 {
proxyHeaders.Add("Accept-Encoding", acceptEncoding)
}

result, err := r.Loader.Load(imgUrl, NewContextWithHeaders(req.Context(), &proxyHeaders))

if err != nil {
sendError(resp, err)
return
}

if len(result.MimeType) > 0 {
resp.Header().Add("Content-Type", result.MimeType)
}

r.execOp(&Command{
Config: &TransformationConfig{
Src: &Image{
Expand Down Expand Up @@ -239,11 +245,15 @@ func (r *Service) getQueue() *Queue {

// Adds Content-Length and Cache-Control headers
func addHeaders(resp http.ResponseWriter, image *Image) {
headers := resp.Header()
if len(image.MimeType) != 0 {
resp.Header().Add("Content-Type", image.MimeType)
headers.Add("Content-Type", image.MimeType)
}
if len(image.ContentEncoding) != 0 {
headers.Add("Content-Encoding", image.ContentEncoding)
}
resp.Header().Add("Content-Length", strconv.Itoa(len(image.Data)))
resp.Header().Add("Cache-Control", fmt.Sprintf("public, max-age=%d", CacheTTL))
headers.Add("Content-Length", strconv.Itoa(len(image.Data)))
headers.Add("Cache-Control", fmt.Sprintf("public, max-age=%d", CacheTTL))
}

func getQueryParam(url *url.URL, name string) (string, bool) {
Expand Down Expand Up @@ -391,3 +401,18 @@ func sendError(resp http.ResponseWriter, err error) {
}
}
}

type headersKey int

func NewContextWithHeaders(ctx context.Context, headers *http.Header) context.Context {
return context.WithValue(ctx, headersKey(0), headers)
}

func HeaderFromContext(ctx context.Context) (*http.Header, bool) {
if ctx == nil {
return nil, false
}

header, ok := ctx.Value(headersKey(0)).(*http.Header)
return header, ok
}
Loading

0 comments on commit b39aa95

Please sign in to comment.