mirror of
https://github.com/willnorris/imageproxy.git
synced 2026-04-25 04:46:24 +02:00
docs: reformat plugin-design.md for semantic line breaks
I'm hoping to pick this idea back up soon.
This commit is contained in:
parent
7f3639886b
commit
e4f7eada71
1 changed files with 72 additions and 104 deletions
|
|
@ -4,38 +4,28 @@
|
||||||
|
|
||||||
## Objective
|
## Objective
|
||||||
|
|
||||||
Rearchitect imageproxy to use a plugin-based system for most features like
|
Re-architect imageproxy to use a plugin-based system for most features like transformations, security, and caching.
|
||||||
transformations, security, and caching. This should reduce build times and
|
This should reduce build times and binary sizes in the common case,
|
||||||
binary sizes in the common case, and provide a mechanism for users to easily
|
and provide a mechanism for users to easily add custom features that would not be added to core for various reasons.
|
||||||
add custom features that would not be added to core for various reasons.
|
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
|
|
||||||
I created imageproxy to [scratch a personal itch](https://wjn.me/b/J_), I
|
I created imageproxy to [scratch a personal itch](https://wjn.me/b/J_), I needed a simple way to dynamically resize images for my personal website.
|
||||||
needed a simple way to dynamically resize images for my personal website. I
|
I published it as an open source projects because that's what I do, and I'm happy to see others finding it useful for their needs as well.
|
||||||
published it as an open source projects because that's what I do, and I'm happy
|
|
||||||
to see others finding it useful for their needs as well.
|
|
||||||
|
|
||||||
But inevitably, with more users came requests for additional features because
|
But inevitably, with more users came requests for additional features because people have different use cases and requirements.
|
||||||
people have different use cases and requirements. Some of these requests were
|
Some of these requests were relatively minor, and I was happy to add them.
|
||||||
relatively minor, and I was happy to add them. But one of the more common
|
But one of the more common requests was to support different caching backends.
|
||||||
requests was to support different caching backends. Personally, I still use the
|
Personally, I still use the on-disk cache, but many people wanted to use redis or a cloud provider like AWS, Azure, or GCP.
|
||||||
on-disk cache, but many people wanted to use redis or a cloud provider like
|
For a long time I was resistant to adding support for these, mainly out of concern for inflating build times and binary sizes.
|
||||||
AWS, Azure, or GCP. For a long time I was resistant to adding support for
|
I did eventually relent, and [#49] tracked adding support for the most common backends.
|
||||||
these, mainly out of concern for inflating build times and binary sizes. I did
|
|
||||||
eventually relent, and
|
|
||||||
[#49](https://github.com/willnorris/imageproxy/issues/49) tracked adding
|
|
||||||
support for the most common backends.
|
|
||||||
|
|
||||||
Unfortunately my concerns proved true, and build times are _significantly_
|
Unfortunately my concerns proved true, and build times are _significantly_ slower (TODO: add concrete numbers) now because of all the additional cloud SDKs that get compiled in.
|
||||||
slower (TODO: add concrete numbers) now because of all the additional cloud
|
I don't personally care too much about binary size, since I'm not running in a constrained environment, but these build times are really wearing on me.
|
||||||
SDKs that get compiled in. I don't personally care too much about binary size,
|
Additionally, there are a number of outstanding pull requests for relatively obscure features that I don't really want to have to support in the main project.
|
||||||
since I'm not running in a constrained environment, but these build times are
|
And quite honestly, there are a number of obscure features that did get merged in over the years that I kinda wish I could rip back out.
|
||||||
really wearing on me. Additionally, there are a number of outstanding pull
|
|
||||||
requests for relatively obscure features that I don't really want to have to
|
[#49]: https://github.com/willnorris/imageproxy/issues/49
|
||||||
support in the main project. And quite honestly, there are a number of obscure
|
|
||||||
features that did get merged in over the years that I kinda wish I could rip
|
|
||||||
back out.
|
|
||||||
|
|
||||||
### Plugin support in Go
|
### Plugin support in Go
|
||||||
|
|
||||||
|
|
@ -44,43 +34,32 @@ TODO: talk about options like
|
||||||
- RPC (<https://github.com/hashicorp/go-plugin>)
|
- RPC (<https://github.com/hashicorp/go-plugin>)
|
||||||
- pkg/plugin (<https://golang.org/pkg/plugin/>)
|
- pkg/plugin (<https://golang.org/pkg/plugin/>)
|
||||||
- embedded interpreter (<https://github.com/robertkrimen/otto>)
|
- embedded interpreter (<https://github.com/robertkrimen/otto>)
|
||||||
- custom binaries (<https://github.com/mholt/caddy>,
|
- custom binaries (<https://github.com/mholt/caddy>, <https://caddy.community/t/59>)
|
||||||
<https://caddy.community/t/59>)
|
|
||||||
|
|
||||||
Spoiler: I'm planning on following the Caddy approach and using custom
|
Spoiler: I'm planning on following the Caddy approach and using custom binaries.
|
||||||
binaries.
|
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
I plan to model imageproxy after Caddy, moving all key functionality into
|
I plan to model imageproxy after Caddy, moving all key functionality into separate plugins that register themselves with the server,
|
||||||
separate plugins that register themselves with the server, and which all
|
and which all compile to a single statically-linked binary.
|
||||||
compile to a single statically-linked binary. The core project will provide a
|
The core project will provide a great number of plugins to cover all of the existing functionality.
|
||||||
great number of plugins to cover all of the existing functionality. I also
|
I also expect I'll be much more open to adding plugins for features I may not care as much about personally.
|
||||||
expect I'll be much more open to adding plugins for features I may not care as
|
Of course, users can also write their own plugins and link them in without needing to contribute them to core if they don't want to.
|
||||||
much about personally. Of course, users can also write their own plugins and
|
|
||||||
link them in without needing to contribute them to core if they don't want to.
|
|
||||||
|
|
||||||
I anticipate providing two or three build configurations in core:
|
I anticipate providing two or three build configurations in core:
|
||||||
|
|
||||||
- **full** - include all the plugins that are part of core (except where they
|
- **full** - include all the plugins that are part of core (except where they may conflict)
|
||||||
may conflict)
|
- **minimal** - some set of minimal features that only includes basic caching options, limited transformation options, etc
|
||||||
- **minimal** - some set of minimal features that only includes basic caching
|
- **my personal config** - I'll also definitely have a build that I use personally on my site.
|
||||||
options, limited transformation options, etc
|
I may decide to just make that the "minimal" build and perhaps call it something different, rather than have a third configuration.
|
||||||
- **my personal config** - I'll also definitely have a build that I use
|
|
||||||
personally on my site. I may decide to just make that the "minimal" build
|
|
||||||
and perhaps call it something different, rather than have a third
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
Custom configurations beyond what is provided by core can be done by creating a
|
Custom configurations beyond what is provided by core can be done by creating a minimal main package that imports the plugins you care about
|
||||||
minimal main package that imports the plugins you care about and calling some
|
and calling some kind of bootstrap method (similar to [what Caddy now does](https://caddy.community/t/59)).
|
||||||
kind of bootstrap method (similar to [what Caddy now
|
|
||||||
does](https://caddy.community/t/59)).
|
|
||||||
|
|
||||||
### Types of plugins
|
### Types of plugins
|
||||||
|
|
||||||
(Initially in no particular order, just capturing thoughts. Lots to do here in
|
(Initially in no particular order, just capturing thoughts.
|
||||||
thinking through the use cases and what kind of plugin API we really need to
|
Lots to do here in thinking through the use cases and what kind of plugin API we really need to provide.)
|
||||||
provide.)
|
|
||||||
|
|
||||||
See also issues and PRs with [label:plugins][].
|
See also issues and PRs with [label:plugins][].
|
||||||
|
|
||||||
|
|
@ -88,70 +67,61 @@ See also issues and PRs with [label:plugins][].
|
||||||
|
|
||||||
#### Caching backend
|
#### Caching backend
|
||||||
|
|
||||||
This is one of the most common feature requests, and is also one of the worst
|
This is one of the most common feature requests, and is also one of the worst offender for inflating build times
|
||||||
offender for inflating build times and binary sizes because of the size of the
|
and binary sizes because of the size of the dependencies that are typically required.
|
||||||
dependencies that are typically required. The minimal imageproxy build would
|
The minimal imageproxy build would probably only include the in-memory and on-disk caches.
|
||||||
probably only include the in-memory and on-disk caches. Anything that talked to
|
Anything that talked to an external store (redis, cloud providers, etc) would be pulled out.
|
||||||
an external store (redis, cloud providers, etc) would be pulled out.
|
|
||||||
|
|
||||||
#### Transformation engine
|
#### Transformation engine
|
||||||
|
|
||||||
Today, imageproxy only performs transformations which can be done with pure Go
|
Today, imageproxy only performs transformations which can be done with pure Go libraries.
|
||||||
libraries. There have been a number of requests (or at least questions) to use
|
There have been a number of requests (or at least questions) to use something like [vips] or [imagemagick], which are both C libraries.
|
||||||
something like [vips](https://github.com/DAddYE/vips) or
|
They provide more options, and (likely) better performance, at the cost of complexity and loss of portability in using cgo.
|
||||||
[imagemagick](https://github.com/gographics/imagick), which are both C
|
These would likely replace the entire transformation engine in imageproxy,
|
||||||
libraries. They provide more options, and (likely) better performance, at the
|
so I don't know how they would interact with other plugins that merely extend the main engine (they probably wouldn't be able to interact at all).
|
||||||
cost of complexity and loss of portability in using cgo. These would likely
|
|
||||||
replace the entire transformation engine in imageproxy, so I don't know how
|
[vips]: https://github.com/DAddYE/vips
|
||||||
they would interact with other plugins that merely extend the main engine (they
|
[imagemagick]: https://github.com/gographics/imagick
|
||||||
probably wouldn't be able to interact at all).
|
|
||||||
|
|
||||||
#### Transformation options
|
#### Transformation options
|
||||||
|
|
||||||
Today, imageproxy performs minimal transformations, mostly around resizing,
|
Today, imageproxy performs minimal transformations, mostly around resizing, cropping, and rotation.
|
||||||
cropping, and rotation. It doesn't support any kind of filters, brightness or
|
It doesn't support any kind of filters, brightness or contrast adjustment, etc.
|
||||||
contrast adjustment, etc. There are go libraries for them, they're just outside
|
There are go libraries for them, they're just outside the scope of what I originally intended imageproxy for.
|
||||||
the scope of what I originally intended imageproxy for. But I'd be happy to
|
But I'd be happy to have plugins that do that kind of thing.
|
||||||
have plugins that do that kind of thing. These plugins would need to be able to
|
These plugins would need to be able to hook into the option parsing engine so that they could register their URL options.
|
||||||
hook into the option parsing engine so that they could register their URL
|
|
||||||
options.
|
|
||||||
|
|
||||||
#### Image format support
|
#### Image format support
|
||||||
|
|
||||||
There have been a number of requests for imge format support that require cgo
|
There have been a number of requests for image format support that require cgo libraries:
|
||||||
libraries:
|
|
||||||
|
|
||||||
- **webp encoding** - needs cgo
|
- **webp encoding** - needs cgo [#114](https://github.com/willnorris/imageproxy/issues/114)
|
||||||
[#114](https://github.com/willnorris/imageproxy/issues/114)
|
- **progressive jpegs** - probably needs cgo? [#77](https://github.com/willnorris/imageproxy/issues/77)
|
||||||
- **progressive jpegs** - probably needs cgo?
|
- **gif to mp4** - maybe doable in pure go, but probably belongs in a plugin [#136](https://github.com/willnorris/imageproxy/issues/136)
|
||||||
[#77](https://github.com/willnorris/imageproxy/issues/77)
|
- **HEIF** - formate used by newer iPhones ([HEIF](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format))
|
||||||
- **gif to mp4** - maybe doable in pure go, but probably belongs in a plugin
|
|
||||||
[#136](https://github.com/willnorris/imageproxy/issues/136)
|
|
||||||
- **HEIF** - formate used by newer iPhones
|
|
||||||
([HEIF](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format))
|
|
||||||
|
|
||||||
#### Option parsing
|
#### Option parsing
|
||||||
|
|
||||||
Today, options are specified as the first component in the URL path, but
|
Today, options are specified as the first component in the URL path, but [#66] proposes optionally moving that to a query parameter (for a good reason, actually).
|
||||||
[#66](https://github.com/willnorris/imageproxy/pull/66) proposes optionally
|
Maybe putting that in core is okay?
|
||||||
moving that to a query parameter (for a good reason, actually). Maybe putting
|
Maybe it belongs in a plugin, in which case we'd need to expose an API for replacing the option parsing code entirely.
|
||||||
that in core is okay? Maybe it belongs in a plugin, in which case we'd need to
|
|
||||||
expose an API for replacing the option parsing code entirely.
|
[#66]: https://github.com/willnorris/imageproxy/pull/66
|
||||||
|
|
||||||
#### Security options
|
#### Security options
|
||||||
|
|
||||||
Some people want to add a host blacklist
|
Some people want to add a host blacklist [#85], refusal to process non-image files [#53] [#119].
|
||||||
[#85](https://github.com/willnorris/imageproxy/pull/85), refusal to process
|
I don't think there is an issue for it,
|
||||||
non-image files [#53](https://github.com/willnorris/imageproxy/issues/53)
|
but an early fork of the project added request signing that was compatible with nginx's [secure link module](https://nginx.org/en/docs/http/ngx_http_secure_link_module.html).
|
||||||
[#119](https://github.com/willnorris/imageproxy/pull/119). I don't think there
|
|
||||||
is an issue for it, but an early fork of the project added request signing that
|
[#85]: https://github.com/willnorris/imageproxy/pull/85
|
||||||
was compatible with nginx's [secure link
|
[#53]: https://github.com/willnorris/imageproxy/issues/53
|
||||||
module](https://nginx.org/en/docs/http/ngx_http_secure_link_module.html).
|
[#119]: https://github.com/willnorris/imageproxy/pull/119
|
||||||
|
|
||||||
### Registering Plugins
|
### Registering Plugins
|
||||||
|
|
||||||
Plugins are loaded simply by importing their package. They should have an
|
Plugins are loaded simply by importing their package.
|
||||||
`init` func that calls `imageproxy.RegisterPlugin`:
|
They should have an `init` func that calls `imageproxy.RegisterPlugin`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Plugin struct {
|
type Plugin struct {
|
||||||
|
|
@ -160,9 +130,8 @@ type Plugin struct {
|
||||||
func RegisterPlugin(name string, plugin Plugin)
|
func RegisterPlugin(name string, plugin Plugin)
|
||||||
```
|
```
|
||||||
|
|
||||||
Plugins hook into various extension points of imageproxy by implementing
|
Plugins hook into various extension points of imageproxy by implementing appropriate interfaces.
|
||||||
appropriate interfaces. A single plugin can hook into multiple parts of
|
A single plugin can hook into multiple parts of imageproxy by implementing multiple interfaces.
|
||||||
imageproxy by implementing multiple interfaces.
|
|
||||||
|
|
||||||
For example, two possible interfaces for security related plugins:
|
For example, two possible interfaces for security related plugins:
|
||||||
|
|
||||||
|
|
@ -196,6 +165,5 @@ type ImageTransformer interface {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Plugins are additionally responsible for registering any additional command
|
Plugins are additionally responsible for registering any additional command line flags they wish to expose to the user,
|
||||||
line flags they wish to expose to the user, as well as storing any global state
|
as well as storing any global state that would previously have been stored on the Proxy struct.
|
||||||
that would previously have been stored on the Proxy struct.
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue