Skip to content
This repository was archived by the owner on Oct 8, 2025. It is now read-only.

Commit 8730a5a

Browse files
committed
Initial public commit
0 parents  commit 8730a5a

20 files changed

Lines changed: 1493 additions & 0 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.vagrant*
2+
.env
3+
bin
4+
*.py[co]

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changelog
2+
3+
## 0.1.0 (2014-03-12)
4+
5+
- Initial release

Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FROM ubuntu
2+
MAINTAINER Rafik Salama <rafik@oysterbooks.com>
3+
4+
WORKDIR /opt/go/src/github.com/oysterbooks/halfshell
5+
ENV GOPATH /opt/go
6+
7+
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
8+
RUN apt-get update
9+
RUN apt-get upgrade -qy
10+
RUN apt-get install -qy \
11+
python-software-properties \
12+
libmagickwand-dev \
13+
git
14+
15+
RUN add-apt-repository ppa:duh/golang
16+
RUN apt-get update
17+
RUN apt-get install -y golang
18+
19+
ADD . /opt/go/src/github.com/oysterbooks/halfshell
20+
RUN cd /opt/go/src/github.com/oysterbooks/halfshell && make deps && make build
21+
22+
ENTRYPOINT ["/opt/go/src/github.com/oysterbooks/halfshell/bin/halfshell"]
23+
24+
EXPOSE 8080

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2014 Oyster
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
OK_COLOR=\033[32;01m
2+
NO_COLOR=\033[0m
3+
4+
build:
5+
@echo "$(OK_COLOR)==> Compiling binary$(NO_COLOR)"
6+
go build -o bin/halfshell
7+
8+
clean:
9+
@rm -rf bin/
10+
11+
deps:
12+
@echo "$(OK_COLOR)==> Installing dependencies$(NO_COLOR)"
13+
@go get -d -v ./...
14+
@go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs -n1 go get -d
15+
16+
format:
17+
go fmt ./...
18+
19+
.PHONY: clean format deps build

README.md

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Halfshell
2+
3+
Halfshell is a proxy server for processing images on the fly. It allows you to dynamically resize (and apply effects to) images hosted in S3 via query parameters. It supports creating “families” of images which can read from distinct S3 buckets and enable different configuration values for image processing and retrieval. See the introduction blog post here.
4+
5+
Current version: `0.1.0`
6+
7+
## Architecture
8+
9+
Halfshell was architected to be extensible from the beginning. The system is composed of a few components with their own configuration and simple interfaces.
10+
11+
### Sources
12+
13+
Sources are repositories from which an “original” image can be loaded. They return an image given a path. In the initial release, a source for downloading images from S3 is included. In the future, we plan to add sources for loading images from a filesystem or arbitrary URL.
14+
15+
### Processors
16+
17+
Processors perform all image manipulation. They accept an image and a set of options and return a modified image. Out of the box, the default processor supports resizing and blurring images. Each processor can be configured with maximum and default image dimensions and enable/disable certain features.
18+
19+
### Routes
20+
21+
Routes bind URL rules (regular expressions) with a source and a processor. Halfshell supports setting up an arbitrary number of routes, and sources and processors do not need to correspond 1-1 with routes.
22+
23+
When Halfshell receives a request, it determines the matching route, retrieves the image from its source, and processes the image using its processor.
24+
25+
This simple architecture has allowed us to serve images from multiple S3 buckets and maintain isolated configuration settings for each family of images.
26+
27+
## Usage and Configuration
28+
29+
Halfshell uses a JSON file for configuration. An example is shown below:
30+
31+
32+
{
33+
"server": {
34+
"port": 8080,
35+
"read_timeout": 5,
36+
"write_timeout": 30
37+
},
38+
"sources": {
39+
"default": {
40+
"type": "s3",
41+
"s3_access_key": "<S3_ACCESS_KEY>",
42+
"s3_secret_key": "<S3_SECRET_KEY>"
43+
},
44+
"blog-post-images": {
45+
"s3_bucket": "my-company-blog-post-images"
46+
},
47+
"profile-photos": {
48+
"s3_bucket": "my-company-profile-photos"
49+
}
50+
},
51+
"processors": {
52+
"default": {
53+
"image_compression_quality": 85,
54+
"maintain_aspect_ratio": true,
55+
"max_blur_radius_percentage": 0,
56+
"max_image_height": 0,
57+
"max_image_width": 1000
58+
59+
},
60+
"profile-photos": {
61+
"default_image_width": 120
62+
}
63+
},
64+
"routes": {
65+
"^/blog(?P<image_path>/.*)$": {
66+
"name": "blog-post-images",
67+
"source": "blog-post-images",
68+
"processor": "default"
69+
},
70+
"^/users(?P<image_path>/.*)$": {
71+
"name": "profile-photos",
72+
"source": "profile-photos",
73+
"processor": "profile-photos"
74+
}
75+
}
76+
}
77+
78+
To start the server, pass configuration file path as an argument.
79+
80+
$ ./bin/halfshell config.json
81+
82+
This will start the server on port 8080, and service requests whose path begins with /users/ or /blog/, e.g.:
83+
84+
http://localhost:8080/users/joe/default.jpg?w=100&h=100
85+
http://localhost:8080/blog/posts/announcement.jpg?w=600&h=200
86+
87+
The image_host named group in the route pattern match (e.g., `^/users(?P<image_path>/.*)$`) gets extracted as the request path for the source. In this instance, the file “joe/default.jpg” is requested from the “my-company-profile-photos” S3 bucket. The processor resizes the image to a width and height of 100. Since the maintain_aspect_ratio setting is set to true, the image will have a maximum width and height of 100, but may be smaller in one dimension in order to maintain the aspect ratio.
88+
89+
### Server
90+
91+
The `server` configuration block accepts the following settings:
92+
93+
##### port
94+
95+
The port to run the server on.
96+
97+
##### read_timeout
98+
99+
The timeout in seconds for reading the initial data from the connection.
100+
101+
##### write_timeout
102+
103+
The timeout in seconds for writing the image data backto the connection.
104+
105+
### Sources
106+
107+
The `sources` block is a mapping of source names to source configuration values.
108+
Values from a source named `default` will be inherited by all other sources.
109+
110+
##### type
111+
112+
The type of image source. Currently only `s3`.
113+
114+
##### s3_access_key
115+
116+
For the S3 source type, the access key to read from S3.
117+
118+
##### s3_secret_key
119+
120+
For the S3 source type, the secret key to read from S3.
121+
122+
##### s3_bucket
123+
124+
For the S3 source type, the bucket to request images from.
125+
126+
### Processors
127+
128+
The `processors` block is a mapping of processor names to processor configuration values.
129+
Values from a processor named `default` will be inherited by all other processors.
130+
131+
##### image_compression_quality
132+
133+
The compression quality to use for JPEG images.
134+
135+
##### maintain_aspect_ratio
136+
137+
If this is set to true, the resized images will always maintain the original
138+
aspect ratio. When set to false, the image will be stretched to fit the width
139+
and height requested.
140+
141+
##### default_image_width
142+
143+
In the absence of a width parameter in the request, use this as image width. A
144+
value of `0` sets no default.
145+
##### default_image_height
146+
147+
In the absence of a height parameter in the request, use this as image height.
148+
A value of `0` sets no default.
149+
150+
##### max_image_width
151+
152+
Set a maximum image width. A value of `0` specifies no maximum.
153+
154+
##### max_image_height
155+
156+
Set a maximum image height. A value of `0` specifies no maximum.
157+
158+
##### max_blur_radius_percentage
159+
160+
Set a maximum blur radius percentage. A value of `0` disables blurring images.
161+
For Gaussian blur, the radius used is this value * the image width. This allows
162+
you to use a blur parameter (from 0-1) which will apply the same proportion of
163+
blurring to each image size.
164+
165+
### Routes
166+
167+
The `routes` block is a mapping of route patterns to route configuration values.
168+
169+
The route pattern is a regular expression with a captured group for `image_path`.
170+
The subexpression match is the path that is requested from the image source.
171+
172+
##### name
173+
174+
The name to use for the route. This is currently used in logging and StatsD key
175+
names.
176+
177+
##### source
178+
179+
The name of the source to use for the route.
180+
181+
##### processor
182+
183+
The name of the processor to use for the route.
184+
185+
## Contributing
186+
187+
Contributions are welcome.
188+
189+
### Building
190+
191+
There's a Vagrant file set up to ease development. After you have the
192+
Vagrant box set up, cd to the /vagrant directory and run `make`.
193+
194+
### Notes
195+
196+
Run `make format` before sending any pull requests.
197+
198+
### Questions?
199+
200+
File an issue or send an email to rafik@oysterbooks.com.

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.1.0

Vagrantfile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Vagrant::Config.run do |config|
2+
config.vm.box = "ubuntu1210"
3+
config.vm.box_url = "http://goo.gl/wxdwM"
4+
5+
config.vm.forward_port 8080, 8080
6+
7+
config.vm.provision :shell, :inline => <<-SH
8+
apt-get update -q
9+
apt-get install -qy libmagickwand-dev
10+
apt-get install -qy git
11+
cd /tmp
12+
wget -q https://godeb.s3.amazonaws.com/godeb-amd64.tar.gz
13+
tar xzvf godeb-amd64.tar.gz && rm godeb-amd64.tar.gz
14+
mv godeb /usr/bin/godeb
15+
/usr/bin/godeb install
16+
echo 'export GOPATH=/go' >> /home/vagrant/.bashrc
17+
GOPATH=/go go get github.com/gographics/imagick/imagick
18+
SH
19+
20+
config.vm.share_folder "gopath", "/go", ENV["GOPATH"]
21+
end

0 commit comments

Comments
 (0)