Commit 3b346f2f authored by Terence Lee's avatar Terence Lee

Merge pull request #6 from hone/ngx_mruby

use ngx_mruby to implement custom headers
parents abde0ba6 d700cdb0
......@@ -91,6 +91,25 @@ For single page web applications like Ember, it's common to back the application
}
```
#### Custom Headers
Using the headers key, you can set custom response headers. It uses the same operators for pathing as [Custom Routes](#custom-routes).
```json
{
"headers": {
"/": {
"Cache-Control": "no-store, no-cache"
},
"/assets/**": {
"Cache-Control": "public, max-age=512000"
},
"/assets/webfonts/*": {
"Access-Control-Allow-Origin": "*"
}
}
}
```
## Testing
For testing we use Docker to replicate Heroku locally. You'll need to have [it setup locally](https://docs.docker.com/installation/). We're also using rspec for testing with Ruby. You'll need to have those setup and install those deps:
......
......@@ -12,7 +12,7 @@ fetch_nginx_tarball() {
local version="1.9.1"
local tarball_file="nginx-$version.tgz"
local stack="cedar-14"
local nginx_tarball_url="https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/nginx/$stack/nginx-$version.tgz"
local nginx_tarball_url="https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/nginx/$stack/nginx-$version-ngx_mruby.tgz"
local dest_path="$cache_dir/$stack/$tarball_file"
if [ -f "$dest_path" ]; then
......
# ghetto require, since mruby doesn't have require
eval(File.read('/app/bin/config/lib/nginx_config_util.rb'))
USER_CONFIG = "/app/static.json"
config = JSON.parse(File.read(USER_CONFIG))
req = Nginx::Request.new
uri = req.var.uri
if config["headers"]
config["headers"].each do |route, header_hash|
if Regexp.compile("^#{NginxConfigUtil.to_regex(route)}$") =~ uri
header_hash.each do |key, value|
# value must be a string
req.headers_out[key] = value.to_s
end
end
end
end
......@@ -38,12 +38,17 @@ http {
<% if clean_urls %>
location / {
mruby_post_read_handler /app/bin/config/lib/ngx_mruby/headers.rb cache;
try_files $uri $uri/ $uri.html;
}
location ~ \.html$ {
try_files $uri =404;
}
<% else %>
location / {
mruby_post_read_handler /app/bin/config/lib/ngx_mruby/headers.rb cache;
}
<% end %>
<% redirects.each do |path, hash| %>
......
{
"headers": {
"/": {
"Cache-Control": "no-cache"
}
}
}
{
"clean_urls": true,
"headers": {
"/foo": {
"X-Header": "present"
}
}
}
{
"headers": {
"/overlap": {
"X-Header": "present"
}
},
"redirects": {
"/overlap": {
"url": "/",
"status": 302
}
}
}
{
"routes": {
"/*": "index.html"
},
"headers": {
"/*": {
"X-Header": "present"
}
}
}
{
"headers": {
"/cache/*": {
"Cache-Control": "max-age=38400"
}
}
}
......@@ -132,4 +132,132 @@ STATIC_JSON
end
end
end
describe "custom headers" do
let(:name) { "custom_headers" }
it "should return the respected headers only for the path specified" do
response = app.get("/")
expect(response["cache-control"]).to eq("no-cache")
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("index")
response = app.get("/foo.html")
expect(response["cache-control"]).to eq(nil)
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("foo")
end
describe "wildcard paths" do
let(:name) { "custom_headers_wildcard" }
it "should add the headers" do
response = app.get("/cache/")
expect(response["Cache-Control"]).to eq("max-age=38400")
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("cached index")
response = app.get("/")
expect(response["Cache-Control"]).to eq(nil)
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("index")
end
end
describe "redirect" do
let(:name) { "custom_headers_redirect" }
it "should add the headers" do
response = app.get("/overlap")
expect(response["X-Header"]).to eq("present")
expect(response.code).to eq("302")
end
end
describe "clean_urls" do
let(:name) { "custom_headers_clean_urls" }
it "should add the headers" do
response = app.get("/foo")
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("foo")
expect(response["X-Header"]).to eq("present")
response = app.get("/bar")
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("bar")
expect(response["X-Header"]).to be_nil
end
it "should not add headers for .html urls" do
response = app.get("/foo.html")
expect(response.code).to eq("200")
expect(response["X-Header"]).to be_nil
end
end
describe "routes" do
let(:name) { "custom_headers_routes" }
it "should add headers" do
response = app.get("/active")
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("index")
expect(response["X-Header"]).to eq("present")
response = app.get("/foo/foo.html")
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("foo")
expect(response["X-Header"]).to be_nil
end
end
describe "proxies" do
include PathHelper
let(:name) { "proxies" }
let(:static_json_path) { fixtures_path("proxies/static.json") }
let(:setup_static_json) do
Proc.new do |path|
File.open(static_json_path, "w") do |file|
file.puts <<STATIC_JSON
{
"proxies": {
"/api/": {
"origin": "http://#{AppRunner::HOST_IP}:#{AppRunner::HOST_PORT}#{path}"
}
},
"headers": {
"/api/bar/": {
"X-Header": "present"
}
}
}
STATIC_JSON
end
end
end
before do
setup_static_json.call("/foo/")
end
after do
FileUtils.rm(static_json_path)
end
it "should proxy requests" do
response = app.get("/api/bar/")
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("api")
expect(response["X-Header"]).to eq("present")
response = app.get("/api/baz/")
expect(response.code).to eq("200")
expect(response.body.chomp).to eq("baz")
expect(response["X-Header"]).to be_nil
end
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment