New release, minor features, bimg upgrade and several fixes (#311)
* feat: add fly.io * feat(readme): fly image width * feat(readme): add html image + badge * fix(docs): try space non-URL encoding * Update README.md * Update README.md * Update README.md * feat(docs): add fly.io info and links * feat(#305, #309, #308, #305. #284) * fix(ci): golinter * fix(Dockerfile): use root folder for go test / ci linter * fix(ci): allow to fail go 1.14 build, still not sure why * fix(ci): bad yaml format * fix(ci): use travis-compatible regex * fix(ci): lets skip go1.14 for now * fix(mod): update dependencies
This commit is contained in:
parent
b2a142adb8
commit
384bb00b15
33
.travis.yml
33
.travis.yml
|
@ -3,21 +3,20 @@ language: go
|
|||
services:
|
||||
- docker
|
||||
|
||||
dist: trusty
|
||||
dist: focal
|
||||
|
||||
go:
|
||||
- 1.12.x
|
||||
- 1.11.x
|
||||
- "1.12"
|
||||
- "1.13"
|
||||
# - "1.14"
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOLANG_VERSION="${TRAVIS_GO_VERSION}"
|
||||
- IMAGINARY_VERSION="${TRAVIS_TAG:-dev}"
|
||||
matrix:
|
||||
- LIBVIPS=8.7.3
|
||||
- LIBVIPS=8.7.4
|
||||
- LIBVIPS=8.8.0
|
||||
- LIBVIPS=8.8.1
|
||||
- LIBVIPS=8.8.4
|
||||
- LIBVIPS=8.9.2
|
||||
|
||||
before_install:
|
||||
- docker pull h2non/imaginary:latest || true
|
||||
|
@ -28,14 +27,14 @@ install:
|
|||
script:
|
||||
- docker build --pull --cache-from h2non/imaginary:latest --build-arg GOLANG_VERSION="${GOLANG_VERSION%.x}" --build-arg LIBVIPS_VERSION="${LIBVIPS}" --build-arg IMAGINARY_VERSION="${IMAGINARY_VERSION#v}" --tag h2non/imaginary:${IMAGINARY_VERSION} .
|
||||
|
||||
# before_deploy:
|
||||
# - docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASS"
|
||||
before_deploy:
|
||||
- docker login -u "$DOCKER_LOGIN" -p "$DOCKER_PWD"
|
||||
|
||||
# deploy:
|
||||
# provider: script
|
||||
# script:
|
||||
# - docker tag h2non/imaginary:${IMAGINARY_VERSION} latest
|
||||
# - docker push h2non/imaginary:${IMAGINARY_VERSION}
|
||||
# - docker push h2non/imaginary:latest
|
||||
# on:
|
||||
# condition: "${TRAVIS_TAG} =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$"
|
||||
deploy:
|
||||
provider: script
|
||||
script:
|
||||
- docker tag h2non/imaginary:${IMAGINARY_VERSION} h2non/imaginary:latest
|
||||
- docker push h2non/imaginary:${IMAGINARY_VERSION}
|
||||
- docker push h2non/imaginary:latest
|
||||
on:
|
||||
condition: "${TRAVIS_TAG} =~ ^v([0-9]+).([0-9]+).([0-9]+)$ AND env(GOLANG_VERSION) = 1.13"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
ARG GOLANG_VERSION=1.13.7
|
||||
ARG GOLANG_VERSION=1.14
|
||||
FROM golang:${GOLANG_VERSION} as builder
|
||||
|
||||
ARG IMAGINARY_VERSION=dev
|
||||
ARG LIBVIPS_VERSION=8.9.1
|
||||
ARG LIBVIPS_VERSION=8.9.2
|
||||
ARG GOLANGCILINT_VERSION=1.23.3
|
||||
|
||||
# Installs libvips + required libraries
|
||||
|
@ -50,8 +50,8 @@ RUN go mod download
|
|||
COPY . .
|
||||
|
||||
# Run quality control
|
||||
RUN go test -test.v -test.race -test.covermode=atomic ./...
|
||||
RUN golangci-lint run ./...
|
||||
RUN go test -test.v -test.race -test.covermode=atomic .
|
||||
RUN golangci-lint run .
|
||||
|
||||
# Compile imaginary
|
||||
RUN go build -a \
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# imaginary [](https://travis-ci.org/h2non/imaginary) [](https://hub.docker.com/r/h2non/imaginary/) [](https://hub.docker.com/r/h2non/imaginary/) [](https://goreportcard.com/report/h2non/imaginary) [](https://fly.io/launch/github/h2non/imaginary)
|
||||
# imaginary [](https://travis-ci.org/h2non/imaginary) [](https://hub.docker.com/r/h2non/imaginary/) [](https://hub.docker.com/r/h2non/imaginary/) [](https://goreportcard.com/report/h2non/imaginary) [](https://fly.io/launch/github/h2non/imaginary)
|
||||
|
||||
**[Fast](#benchmarks) HTTP [microservice](http://microservices.io/patterns/microservices.html)** written in Go **for high-level image processing** backed by [bimg](https://github.com/h2non/bimg) and [libvips](https://github.com/jcupitt/libvips). `imaginary` can be used as private or public HTTP service for massive image processing with first-class support for [Docker](#docker) & [Fly.io](#flyio).
|
||||
It's almost dependency-free and only uses [`net/http`](http://golang.org/pkg/net/http/) native package without additional abstractions for better [performance](#performance).
|
||||
|
@ -83,7 +83,7 @@ go get -u github.com/h2non/imaginary
|
|||
|
||||
Also, be sure you have the latest version of `bimg`:
|
||||
```bash
|
||||
go get -u gopkg.in/h2non/bimg.v1
|
||||
go get -u github.com/h2non/bimg
|
||||
```
|
||||
|
||||
### libvips
|
||||
|
@ -553,7 +553,7 @@ Image measures are always in pixels, unless otherwise indicated.
|
|||
- **url** `string` - Fetch the image from a remote HTTP server. In order to use this you must pass the `-enable-url-source` flag.
|
||||
- **colorspace** `string` - Use a custom color space for the output image. Allowed values are: `srgb` or `bw` (black&white)
|
||||
- **field** `string` - Custom image form field name if using `multipart/form`. Defaults to: `file`
|
||||
- **extend** `string` - Extend represents the image extend mode used when the edges of an image are extended. Allowed values are: `black`, `copy`, `mirror`, `white` and `background`. If `background` value is specified, you can define the desired extend RGB color via `background` param, such as `?extend=background&background=250,20,10`. For more info, see [libvips docs](http://www.vips.ecs.soton.ac.uk/supported/8.4/doc/html/libvips/libvips-conversion.html#VIPS-EXTEND-BACKGROUND:CAPS).
|
||||
- **extend** `string` - Extend represents the image extend mode used when the edges of an image are extended. Defaults to `copy`. Allowed values are: `black`, `copy`, `mirror`, `white`, `lastpixel` and `background`. If `background` value is specified, you can define the desired extend RGB color via `background` param, such as `?extend=background&background=250,20,10`. For more info, see [libvips docs](http://www.vips.ecs.soton.ac.uk/supported/8.4/doc/html/libvips/libvips-conversion.html#VIPS-EXTEND-BACKGROUND:CAPS).
|
||||
- **background** `string` - Background RGB decimal base color to use when flattening transparent PNGs. Example: `255,200,150`
|
||||
- **sigma** `float` - Size of the gaussian mask to use when blurring an image. Example: `15.0`
|
||||
- **minampl** `float` - Minimum amplitude of the gaussian filter to use when blurring an image. Default: Example: `0.5`
|
||||
|
|
|
@ -44,7 +44,11 @@ func imageController(o ServerOptions, operation Operation) func(http.ResponseWri
|
|||
|
||||
buf, err := imageSource.GetImage(req)
|
||||
if err != nil {
|
||||
ErrorReply(req, w, NewError(err.Error(), BadRequest), o)
|
||||
if xerr, ok := err.(Error); ok {
|
||||
ErrorReply(req, w, xerr, o)
|
||||
} else {
|
||||
ErrorReply(req, w, NewError(err.Error(), http.StatusBadRequest), o)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -100,7 +104,7 @@ func imageHandler(w http.ResponseWriter, r *http.Request, buf []byte, operation
|
|||
|
||||
opts, err := buildParamsFromQuery(r.URL.Query())
|
||||
if err != nil {
|
||||
ErrorReply(r, w, NewError("Error while processing parameters, "+err.Error(), BadRequest), o)
|
||||
ErrorReply(r, w, NewError("Error while processing parameters, "+err.Error(), http.StatusBadRequest), o)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -115,7 +119,7 @@ func imageHandler(w http.ResponseWriter, r *http.Request, buf []byte, operation
|
|||
|
||||
image, err := operation.Run(buf, opts)
|
||||
if err != nil {
|
||||
ErrorReply(r, w, NewError("Error while processing the image: "+err.Error(), BadRequest), o)
|
||||
ErrorReply(r, w, NewError("Error while processing the image: "+err.Error(), http.StatusBadRequest), o)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
73
error.go
73
error.go
|
@ -9,38 +9,26 @@ import (
|
|||
"github.com/h2non/bimg"
|
||||
)
|
||||
|
||||
const (
|
||||
_ uint8 = iota
|
||||
BadRequest
|
||||
NotAllowed
|
||||
Unsupported
|
||||
Unauthorized
|
||||
InternalError
|
||||
NotFound
|
||||
NotImplemented
|
||||
Forbidden
|
||||
NotAcceptable
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = NewError("not found", NotFound)
|
||||
ErrInvalidAPIKey = NewError("invalid or missing API key", Unauthorized)
|
||||
ErrMethodNotAllowed = NewError("method not allowed", NotAllowed)
|
||||
ErrUnsupportedMedia = NewError("unsupported media type", Unsupported)
|
||||
ErrOutputFormat = NewError("unsupported output image format", BadRequest)
|
||||
ErrEmptyBody = NewError("empty image", BadRequest)
|
||||
ErrMissingParamFile = NewError("missing required param: file", BadRequest)
|
||||
ErrInvalidFilePath = NewError("invalid file path", BadRequest)
|
||||
ErrInvalidImageURL = NewError("invalid image URL", BadRequest)
|
||||
ErrMissingImageSource = NewError("cannot process the image due to missing or invalid params", BadRequest)
|
||||
ErrNotImplemented = NewError("not implemented endpoint", NotImplemented)
|
||||
ErrInvalidURLSignature = NewError("invalid URL signature", BadRequest)
|
||||
ErrURLSignatureMismatch = NewError("URL signature mismatch", Forbidden)
|
||||
ErrNotFound = NewError("Not found", http.StatusNotFound)
|
||||
ErrInvalidAPIKey = NewError("Invalid or missing API key", http.StatusUnauthorized)
|
||||
ErrMethodNotAllowed = NewError("HTTP method not allowed. Try with a POST or GET method (-enable-url-source flag must be defined)", http.StatusMethodNotAllowed)
|
||||
ErrGetMethodNotAllowed = NewError("GET method not allowed. Make sure remote URL source is enabled by using the flag: -enable-url-source", http.StatusMethodNotAllowed)
|
||||
ErrUnsupportedMedia = NewError("Unsupported media type", http.StatusNotAcceptable)
|
||||
ErrOutputFormat = NewError("Unsupported output image format", http.StatusBadRequest)
|
||||
ErrEmptyBody = NewError("Empty or unreadable image", http.StatusBadRequest)
|
||||
ErrMissingParamFile = NewError("Missing required param: file", http.StatusBadRequest)
|
||||
ErrInvalidFilePath = NewError("Invalid file path", http.StatusBadRequest)
|
||||
ErrInvalidImageURL = NewError("Unvalid image URL", http.StatusBadRequest)
|
||||
ErrMissingImageSource = NewError("Cannot process the image due to missing or invalid params", http.StatusBadRequest)
|
||||
ErrNotImplemented = NewError("Not implemented endpoint", http.StatusNotImplemented)
|
||||
ErrInvalidURLSignature = NewError("Invalid URL signature", http.StatusBadRequest)
|
||||
ErrURLSignatureMismatch = NewError("URL signature mismatch", http.StatusForbidden)
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
Code uint8 `json:"code"`
|
||||
Code int `json:"status"`
|
||||
}
|
||||
|
||||
func (e Error) JSON() []byte {
|
||||
|
@ -53,34 +41,21 @@ func (e Error) Error() string {
|
|||
}
|
||||
|
||||
func (e Error) HTTPCode() int {
|
||||
var codes = map[uint8]int{
|
||||
BadRequest: http.StatusBadRequest,
|
||||
NotAllowed: http.StatusMethodNotAllowed,
|
||||
Unsupported: http.StatusUnsupportedMediaType,
|
||||
InternalError: http.StatusInternalServerError,
|
||||
Unauthorized: http.StatusUnauthorized,
|
||||
NotFound: http.StatusNotFound,
|
||||
NotImplemented: http.StatusNotImplemented,
|
||||
Forbidden: http.StatusForbidden,
|
||||
NotAcceptable: http.StatusNotAcceptable,
|
||||
if e.Code >= 400 && e.Code <= 511 {
|
||||
return e.Code
|
||||
}
|
||||
|
||||
if v, ok := codes[e.Code]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return http.StatusServiceUnavailable
|
||||
}
|
||||
|
||||
func NewError(err string, code uint8) Error {
|
||||
func NewError(err string, code int) Error {
|
||||
err = strings.Replace(err, "\n", "", -1)
|
||||
return Error{err, code}
|
||||
return Error{Message: err, Code: code}
|
||||
}
|
||||
|
||||
func sendErrorResponse(w http.ResponseWriter, httpStatusCode int, imaginaryErrorCode uint8, err error) {
|
||||
func sendErrorResponse(w http.ResponseWriter, httpStatusCode int, err error) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(httpStatusCode)
|
||||
_, _ = w.Write([]byte(fmt.Sprintf("{\"error\":\"%s\", \"code\": %d}", err.Error(), imaginaryErrorCode)))
|
||||
_, _ = w.Write([]byte(fmt.Sprintf("{\"error\":\"%s\", \"status\": %d}", err.Error(), httpStatusCode)))
|
||||
}
|
||||
|
||||
func replyWithPlaceholder(req *http.Request, w http.ResponseWriter, errCaller Error, o ServerOptions) error {
|
||||
|
@ -94,20 +69,20 @@ func replyWithPlaceholder(req *http.Request, w http.ResponseWriter, errCaller Er
|
|||
|
||||
bimgOptions.Width, err = parseInt(req.URL.Query().Get("width"))
|
||||
if err != nil {
|
||||
sendErrorResponse(w, http.StatusBadRequest, BadRequest, err)
|
||||
sendErrorResponse(w, http.StatusBadRequest, err)
|
||||
return err
|
||||
}
|
||||
|
||||
bimgOptions.Height, err = parseInt(req.URL.Query().Get("height"))
|
||||
if err != nil {
|
||||
sendErrorResponse(w, http.StatusBadRequest, BadRequest, err)
|
||||
sendErrorResponse(w, http.StatusBadRequest, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Resize placeholder to expected output
|
||||
buf, err := bimg.Resize(o.PlaceholderImage, bimgOptions)
|
||||
if err != nil {
|
||||
sendErrorResponse(w, http.StatusBadRequest, BadRequest, err)
|
||||
sendErrorResponse(w, http.StatusBadRequest, err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -2,23 +2,23 @@ package main
|
|||
|
||||
import "testing"
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
err := NewError("oops!\n\n", 1)
|
||||
func TestDefaultError(t *testing.T) {
|
||||
err := NewError("oops!\n\n", 503)
|
||||
|
||||
if err.Error() != "oops!" {
|
||||
t.Fatal("Invalid error message")
|
||||
}
|
||||
if err.Code != 1 {
|
||||
if err.Code != 503 {
|
||||
t.Fatal("Invalid error code")
|
||||
}
|
||||
|
||||
code := err.HTTPCode()
|
||||
if code != 400 {
|
||||
if code != 503 {
|
||||
t.Fatalf("Invalid HTTP error status: %d", code)
|
||||
}
|
||||
|
||||
json := string(err.JSON())
|
||||
if json != "{\"message\":\"oops!\",\"code\":1}" {
|
||||
if json != "{\"message\":\"oops!\",\"status\":503}" {
|
||||
t.Fatalf("Invalid JSON output: %s", json)
|
||||
}
|
||||
}
|
||||
|
|
4
go.mod
4
go.mod
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/garyburd/redigo v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad // indirect
|
||||
github.com/rs/cors v0.0.0-20170727213201-7af7a1e09ba3
|
||||
github.com/h2non/bimg v1.0.20-0.20200405220655-daafbf6d972d
|
||||
github.com/h2non/filetype v1.0.12
|
||||
github.com/h2non/bimg v1.1.0
|
||||
github.com/h2non/filetype v1.1.0
|
||||
gopkg.in/throttled/throttled.v2 v2.0.3
|
||||
)
|
||||
|
|
8
go.sum
8
go.sum
|
@ -1,12 +1,10 @@
|
|||
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
|
||||
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/h2non/bimg v1.1.0/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
|
||||
github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA=
|
||||
github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad h1:eMxs9EL0PvIGS9TTtxg4R+JxuPGav82J8rA+GFnY7po=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/rs/cors v0.0.0-20170727213201-7af7a1e09ba3 h1:86ukAHRTa2CXdBnWJHcjjPPGTyLGEF488OFRsbBAuFs=
|
||||
github.com/rs/cors v0.0.0-20170727213201-7af7a1e09ba3/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/h2non/bimg v1.0.20-0.20200405220655-daafbf6d972d h1:AhGp7Xiew8DAYGJ1MojrE11chxHVOv4NvE/y5i2IvBI=
|
||||
github.com/h2non/bimg v1.0.20-0.20200405220655-daafbf6d972d/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
|
||||
github.com/h2non/filetype v1.0.12 h1:yHCsIe0y2cvbDARtJhGBTD2ecvqMSTvlIcph9En/Zao=
|
||||
github.com/h2non/filetype v1.0.12/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
gopkg.in/throttled/throttled.v2 v2.0.3 h1:PGm7nfjjexecEyI2knw1akeLcrjzqxuYSU9a04R8rfU=
|
||||
gopkg.in/throttled/throttled.v2 v2.0.3/go.mod h1:L4cTNZO77XKEXtn8HNFRCMNGZPtRRKAhyuJBSvK/T90=
|
||||
|
|
44
image.go
44
image.go
|
@ -65,7 +65,7 @@ func Info(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
meta, err := bimg.Metadata(buf)
|
||||
if err != nil {
|
||||
return image, NewError("Cannot retrieve image metadata: %s"+err.Error(), BadRequest)
|
||||
return image, NewError("Cannot retrieve image metadata: %s"+err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
info := ImageInfo{
|
||||
|
@ -87,7 +87,7 @@ func Info(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Resize(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Width == 0 && o.Height == 0 {
|
||||
return Image{}, NewError("Missing required param: height or width", BadRequest)
|
||||
return Image{}, NewError("Missing required param: height or width", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
@ -102,7 +102,7 @@ func Resize(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Fit(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Width == 0 || o.Height == 0 {
|
||||
return Image{}, NewError("Missing required params: height, width", BadRequest)
|
||||
return Image{}, NewError("Missing required params: height, width", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
metadata, err := bimg.Metadata(buf)
|
||||
|
@ -113,7 +113,7 @@ func Fit(buf []byte, o ImageOptions) (Image, error) {
|
|||
dims := metadata.Size
|
||||
|
||||
if dims.Width == 0 || dims.Height == 0 {
|
||||
return Image{}, NewError("Width or height of requested image is zero", NotAcceptable)
|
||||
return Image{}, NewError("Width or height of requested image is zero", http.StatusNotAcceptable)
|
||||
}
|
||||
|
||||
// metadata.Orientation
|
||||
|
@ -165,7 +165,7 @@ func calculateDestinationFitDimension(imageWidth, imageHeight, fitWidth, fitHeig
|
|||
|
||||
func Enlarge(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Width == 0 || o.Height == 0 {
|
||||
return Image{}, NewError("Missing required params: height, width", BadRequest)
|
||||
return Image{}, NewError("Missing required params: height, width", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
@ -179,7 +179,7 @@ func Enlarge(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Extract(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.AreaWidth == 0 || o.AreaHeight == 0 {
|
||||
return Image{}, NewError("Missing required params: areawidth or areaheight", BadRequest)
|
||||
return Image{}, NewError("Missing required params: areawidth or areaheight", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
@ -193,7 +193,7 @@ func Extract(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Crop(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Width == 0 && o.Height == 0 {
|
||||
return Image{}, NewError("Missing required param: height or width", BadRequest)
|
||||
return Image{}, NewError("Missing required param: height or width", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
@ -203,7 +203,7 @@ func Crop(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func SmartCrop(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Width == 0 && o.Height == 0 {
|
||||
return Image{}, NewError("Missing required param: height or width", BadRequest)
|
||||
return Image{}, NewError("Missing required param: height or width", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
@ -214,7 +214,7 @@ func SmartCrop(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Rotate(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Rotate == 0 {
|
||||
return Image{}, NewError("Missing required param: rotate", BadRequest)
|
||||
return Image{}, NewError("Missing required param: rotate", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
@ -235,7 +235,7 @@ func Flop(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Thumbnail(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Width == 0 && o.Height == 0 {
|
||||
return Image{}, NewError("Missing required params: width or height", BadRequest)
|
||||
return Image{}, NewError("Missing required params: width or height", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return Process(buf, BimgOptions(o))
|
||||
|
@ -243,14 +243,14 @@ func Thumbnail(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Zoom(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Factor == 0 {
|
||||
return Image{}, NewError("Missing required param: factor", BadRequest)
|
||||
return Image{}, NewError("Missing required param: factor", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
||||
if o.Top > 0 || o.Left > 0 {
|
||||
if o.AreaWidth == 0 && o.AreaHeight == 0 {
|
||||
return Image{}, NewError("Missing required params: areawidth, areaheight", BadRequest)
|
||||
return Image{}, NewError("Missing required params: areawidth, areaheight", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts.Top = o.Top
|
||||
|
@ -269,10 +269,10 @@ func Zoom(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Convert(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Type == "" {
|
||||
return Image{}, NewError("Missing required param: type", BadRequest)
|
||||
return Image{}, NewError("Missing required param: type", http.StatusBadRequest)
|
||||
}
|
||||
if ImageType(o.Type) == bimg.UNKNOWN {
|
||||
return Image{}, NewError("Invalid image type: "+o.Type, BadRequest)
|
||||
return Image{}, NewError("Invalid image type: "+o.Type, http.StatusBadRequest)
|
||||
}
|
||||
opts := BimgOptions(o)
|
||||
|
||||
|
@ -281,7 +281,7 @@ func Convert(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Watermark(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Text == "" {
|
||||
return Image{}, NewError("Missing required param: text", BadRequest)
|
||||
return Image{}, NewError("Missing required param: text", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
@ -302,11 +302,11 @@ func Watermark(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func WatermarkImage(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Image == "" {
|
||||
return Image{}, NewError("Missing required param: image", BadRequest)
|
||||
return Image{}, NewError("Missing required param: image", http.StatusBadRequest)
|
||||
}
|
||||
response, err := http.Get(o.Image)
|
||||
if err != nil {
|
||||
return Image{}, NewError(fmt.Sprintf("Unable to retrieve watermark image. %s", o.Image), BadRequest)
|
||||
return Image{}, NewError(fmt.Sprintf("Unable to retrieve watermark image. %s", o.Image), http.StatusBadRequest)
|
||||
}
|
||||
defer func() {
|
||||
_ = response.Body.Close()
|
||||
|
@ -322,7 +322,7 @@ func WatermarkImage(buf []byte, o ImageOptions) (Image, error) {
|
|||
errMessage = fmt.Sprintf("%s. %s", errMessage, err.Error())
|
||||
}
|
||||
|
||||
return Image{}, NewError(errMessage, BadRequest)
|
||||
return Image{}, NewError(errMessage, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
opts := BimgOptions(o)
|
||||
|
@ -336,7 +336,7 @@ func WatermarkImage(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func GaussianBlur(buf []byte, o ImageOptions) (Image, error) {
|
||||
if o.Sigma == 0 && o.MinAmpl == 0 {
|
||||
return Image{}, NewError("Missing required param: sigma or minampl", BadRequest)
|
||||
return Image{}, NewError("Missing required param: sigma or minampl", http.StatusBadRequest)
|
||||
}
|
||||
opts := BimgOptions(o)
|
||||
return Process(buf, opts)
|
||||
|
@ -344,10 +344,10 @@ func GaussianBlur(buf []byte, o ImageOptions) (Image, error) {
|
|||
|
||||
func Pipeline(buf []byte, o ImageOptions) (Image, error) {
|
||||
if len(o.Operations) == 0 {
|
||||
return Image{}, NewError("Missing or invalid pipeline operations JSON", BadRequest)
|
||||
return Image{}, NewError("Missing or invalid pipeline operations JSON", http.StatusBadRequest)
|
||||
}
|
||||
if len(o.Operations) > 10 {
|
||||
return Image{}, NewError("Maximum allowed pipeline operations exceeded", BadRequest)
|
||||
return Image{}, NewError("Maximum allowed pipeline operations exceeded", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Validate and built operations
|
||||
|
@ -355,7 +355,7 @@ func Pipeline(buf []byte, o ImageOptions) (Image, error) {
|
|||
// Validate supported operation name
|
||||
var exists bool
|
||||
if operation.Operation, exists = OperationsMap[operation.Name]; !exists {
|
||||
return Image{}, NewError(fmt.Sprintf("Unsupported operation name: %s", operation.Name), BadRequest)
|
||||
return Image{}, NewError(fmt.Sprintf("Unsupported operation name: %s", operation.Name), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Parse and construct operation options
|
||||
|
|
|
@ -114,7 +114,7 @@ type URLSignature struct {
|
|||
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
_, _ = fmt.Fprint(os.Stderr, fmt.Sprintf(usage, Version, runtime.NumCPU()))
|
||||
_, _ = fmt.Fprint(os.Stderr, usage, Version, runtime.NumCPU())
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/cors"
|
||||
"github.com/h2non/bimg"
|
||||
"github.com/rs/cors"
|
||||
"gopkg.in/throttled/throttled.v2"
|
||||
"gopkg.in/throttled/throttled.v2/store/memstore"
|
||||
)
|
||||
|
@ -105,7 +105,7 @@ func validateImage(next http.Handler, o ServerOptions) http.Handler {
|
|||
}
|
||||
|
||||
if r.Method == http.MethodGet && o.Mount == "" && !o.EnableURLSource {
|
||||
ErrorReply(r, w, ErrMethodNotAllowed, o)
|
||||
ErrorReply(r, w, ErrGetMethodNotAllowed, o)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
16
params.go
16
params.go
|
@ -344,9 +344,11 @@ func coerceInterlace(io *ImageOptions, param interface{}) (err error) {
|
|||
}
|
||||
|
||||
func buildParamsFromOperation(op PipelineOperation) (ImageOptions, error) {
|
||||
|
||||
var options ImageOptions
|
||||
|
||||
// Apply defaults
|
||||
options.Extend = bimg.ExtendCopy
|
||||
|
||||
for key, value := range op.Params {
|
||||
fn, ok := paramTypeCoercions[key]
|
||||
if !ok {
|
||||
|
@ -366,6 +368,9 @@ func buildParamsFromOperation(op PipelineOperation) (ImageOptions, error) {
|
|||
func buildParamsFromQuery(query url.Values) (ImageOptions, error) {
|
||||
var options ImageOptions
|
||||
|
||||
// Apply defaults
|
||||
options.Extend = bimg.ExtendCopy
|
||||
|
||||
// Extract only known parameters
|
||||
for key := range query {
|
||||
fn, ok := paramTypeCoercions[key]
|
||||
|
@ -448,8 +453,8 @@ func parseExtendMode(val string) bimg.Extend {
|
|||
if val == "white" {
|
||||
return bimg.ExtendWhite
|
||||
}
|
||||
if val == "copy" {
|
||||
return bimg.ExtendCopy
|
||||
if val == "black" {
|
||||
return bimg.ExtendBlack
|
||||
}
|
||||
if val == "mirror" {
|
||||
return bimg.ExtendMirror
|
||||
|
@ -457,7 +462,10 @@ func parseExtendMode(val string) bimg.Extend {
|
|||
if val == "background" {
|
||||
return bimg.ExtendBackground
|
||||
}
|
||||
return bimg.ExtendBlack
|
||||
if val == "lastpixel" {
|
||||
return bimg.ExtendLast
|
||||
}
|
||||
return bimg.ExtendCopy
|
||||
}
|
||||
|
||||
func parseGravity(val string) bimg.Gravity {
|
||||
|
|
|
@ -141,10 +141,11 @@ func TestParseExtend(t *testing.T) {
|
|||
{"black", bimg.ExtendBlack},
|
||||
{"copy", bimg.ExtendCopy},
|
||||
{"mirror", bimg.ExtendMirror},
|
||||
{"lastpixel", bimg.ExtendLast},
|
||||
{"background", bimg.ExtendBackground},
|
||||
{" BACKGROUND ", bimg.ExtendBackground},
|
||||
{"invalid", bimg.ExtendBlack},
|
||||
{"", bimg.ExtendBlack},
|
||||
{"invalid", bimg.ExtendCopy},
|
||||
{"", bimg.ExtendCopy},
|
||||
}
|
||||
|
||||
for _, extend := range cases {
|
||||
|
|
|
@ -41,11 +41,11 @@ func (s *HTTPImageSource) fetchImage(url *url.URL, ireq *http.Request) ([]byte,
|
|||
req := newHTTPRequest(s, ireq, http.MethodHead, url)
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching image http headers: %v", err)
|
||||
return nil, fmt.Errorf("error fetching remote http image headers: %v", err)
|
||||
}
|
||||
_ = res.Body.Close()
|
||||
if res.StatusCode < 200 && res.StatusCode > 206 {
|
||||
return nil, fmt.Errorf("error fetching image http headers: (status=%d) (url=%s)", res.StatusCode, req.URL.String())
|
||||
return nil, NewError(fmt.Sprintf("error fetching remote http image headers: (status=%d) (url=%s)", res.StatusCode, req.URL.String()), res.StatusCode)
|
||||
}
|
||||
|
||||
contentLength, _ := strconv.Atoi(res.Header.Get("Content-Length"))
|
||||
|
@ -58,11 +58,11 @@ func (s *HTTPImageSource) fetchImage(url *url.URL, ireq *http.Request) ([]byte,
|
|||
req := newHTTPRequest(s, ireq, http.MethodGet, url)
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error downloading image: %v", err)
|
||||
return nil, fmt.Errorf("error fetching remote http image: %v", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("error downloading image: (status=%d) (url=%s)", res.StatusCode, req.URL.String())
|
||||
return nil, NewError(fmt.Sprintf("error fetching remote http image: (status=%d) (url=%s)", res.StatusCode, req.URL.String()), res.StatusCode)
|
||||
}
|
||||
|
||||
// Read the body
|
||||
|
|
Loading…
Reference in New Issue