Skip to content

Commit

Permalink
Merge pull request #60 from sourcelair/redirects
Browse files Browse the repository at this point in the history
Implement "redirect" routes
  • Loading branch information
parisk authored Feb 16, 2019
2 parents 053874d + be9cb42 commit a10b135
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 26 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ curl -H "Content-Type: application/json" \
http://ceryx-api-host/api/routes/publicly.accessible.domain
```

### HTTPS redirects
### Enforce HTTPS

You can enforce redirection from HTTP to HTTPS for any host you would like.

Expand All @@ -117,6 +117,17 @@ curl -H "Content-Type: application/json" \

The above functionality works in `PUT` update requests as well.

### Redirect to target, instead of proxying

Instead of proxying the request to the targetm you can prompt the client to redirect the request there itself.

```
curl -H "Content-Type: application/json" \
-X POST \
-d '{"source":"sourcelair.com","target":"https://www.sourcelair.com", "settings": {"mode": "redirect"}}' \
http://ceryx-api-host/api/routes
```

## Ceryx web UI

The [Ceryx Web community project](https://github.com/parisk/ceryx-web) provides a sweet web UI
Expand Down
6 changes: 4 additions & 2 deletions api/ceryx/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def encode_settings(settings):
Encode and sanitize settings in order to be written to Redis.
"""
encoded_settings = {
'enforce_https': str(int(settings.get('enforce_https', False)))
'enforce_https': str(int(settings.get('enforce_https', False))),
'mode': settings.get('mode', 'proxy'),
}

return encoded_settings
Expand All @@ -32,7 +33,8 @@ def decode_settings(settings):
_str(k): _str(v) for k, v in settings.items()
}
decoded = {
'enforce_https': bool(int(_settings.get('enforce_https', '0')))
'enforce_https': bool(int(_settings.get('enforce_https', '0'))),
'mode': _settings.get('mode', 'proxy'),
}

return decoded
Expand Down
33 changes: 17 additions & 16 deletions api/ceryx/types.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
from apistar import types, validators


SETTINGS_VALIDATOR = validators.Object(
properties={
'enforce_https': validators.Boolean(default=False),
'mode': validators.String(
default='proxy',
enum=['proxy', 'redirect'],
),
},
default={
'enforce_https': False,
'mode': 'proxy',
},
)


class RouteWithoutSource(types.Type):
target = validators.String()
settings = validators.Object(
properties={
'enforce_https': validators.Boolean(default=False),
},
default={
'enforce_https': False,
},
)
settings = SETTINGS_VALIDATOR


class Route(types.Type):
source = validators.String()
target = validators.String()
settings = validators.Object(
properties={
'enforce_https': validators.Boolean(default=False),
},
default={
'enforce_https': False,
},
)
settings = SETTINGS_VALIDATOR
63 changes: 63 additions & 0 deletions api/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def test_create_route(self):
'target': 'localhost:11235',
'settings': {
'enforce_https': False,
'mode': 'proxy',
}
}

Expand All @@ -59,20 +60,23 @@ def test_enforce_https(self):
'target': 'localhost:11235',
'settings': {
'enforce_https': True,
'mode': 'proxy',
},
}
route_enforce_https_false = {
'source': 'test-enforce-https-false.dev',
'target': 'localhost:11235',
'settings': {
'enforce_https': False,
'mode': 'proxy',
},
}
expected_response_without_enforce_https = {
'source': 'test-no-enforce-https.dev',
'target': 'localhost:11235',
'settings': {
'enforce_https': False,
'mode': 'proxy',
},
}

Expand Down Expand Up @@ -109,6 +113,65 @@ def test_enforce_https(self):
response.json(), route_enforce_https_false,
)

def test_mode(self):
"""
Assert that creating a route with or without the `mode` setting returns
the expected results.
"""
route_without_mode = {
'source': 'www.my-website.dev',
'target': 'localhost:11235',
}
route_mode_proxy = {
'source': 'www.my-website.dev',
'target': 'localhost:11235',
'settings': {
'enforce_https': False,
'mode': 'proxy',
},
}
route_mode_redirect = {
'source': 'my-website.dev',
'target': 'www.my-website.dev',
'settings': {
'enforce_https': False,
'mode': 'redirect',
},
}

response = self.client.post('/api/routes', json=route_without_mode)
self.assertEqual(response.status_code, 201)
self.assertDictEqual(
response.json(), route_mode_proxy,
)

response = self.client.get('/api/routes/www.my-website.dev')
self.assertDictEqual(
response.json(), route_mode_proxy,
)

response = self.client.post('/api/routes', json=route_mode_proxy)
self.assertEqual(response.status_code, 201)
self.assertDictEqual(
response.json(), route_mode_proxy,
)

response = self.client.get('/api/routes/www.my-website.dev')
self.assertDictEqual(
response.json(), route_mode_proxy,
)

response = self.client.post('/api/routes', json=route_mode_redirect)
self.assertEqual(response.status_code, 201)
self.assertDictEqual(
response.json(), route_mode_redirect,
)

response = self.client.get('/api/routes/my-website.dev')
self.assertDictEqual(
response.json(), route_mode_redirect,
)

def test_delete_route(self):
"""
Assert that deleting a route, will actually delete it.
Expand Down
19 changes: 14 additions & 5 deletions ceryx/nginx/lualib/router.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ local host = ngx.var.host
local is_not_https = (ngx.var.scheme ~= "https")
local cache = ngx.shared.ceryx

function route(source, target)
function route(source, target, mode)
if mode == "redirect" then
ngx.log(ngx.INFO, "Redirecting request for " .. source .. " to " .. target .. ".")
return ngx.redirect(target, ngx.HTTP_MOVED_PERMANENTLY)
end

ngx.var.container_url = target
ngx.log(ngx.INFO, "Routing request for " .. source .. " to " .. target .. ".")
ngx.log(ngx.INFO, "Proxying request for " .. source .. " to " .. target .. ".")
end

local prefix = os.getenv("CERYX_REDIS_PREFIX")
Expand Down Expand Up @@ -41,8 +46,9 @@ if redis_password then
end
ngx.log(ngx.DEBUG, "Authenticated with Redis.")

local settings_key = prefix .. ":settings:" .. host

if is_not_https then
local settings_key = prefix .. ":settings:" .. host
local enforce_https, flags = cache:get(host .. ":enforce_https")

if enforce_https == nil then
Expand All @@ -56,11 +62,14 @@ if is_not_https then
end
end

-- Get routing mode
local mode, mode_flags = red:hget(settings_key, "mode")

-- Check if key exists in local cache
res, flags = cache:get(host)
if res then
ngx.log(ngx.DEBUG, "Cache hit for " .. host .. ".")
route(host, res)
route(host, res, mode)
else
ngx.log(ngx.DEBUG, "Cache miss for " .. host .. ".")

Expand All @@ -85,6 +94,6 @@ else
end

-- Save found key to local cache for 5 seconds
route(host, res)
route(host, res, mode)
cache:set(host, res, 5)
ngx.log(ngx.DEBUG, "Saving route from " .. host .. " to " .. res .. " in local cache for 5 seconds.")
16 changes: 14 additions & 2 deletions ceryx/tests/routes.bats
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,26 @@
[ $ceryx_status_code -eq $upstream_status_code ]
}

@test "301 response and appropriate 'Location' header when enforce_https=true" {
@test "301 response when enforce_https=true" {
curl -s -o /dev/null \
-X POST \
-H 'Content-Type: application/json' \
-d '{"source": "enforced-https-route", "target": "somewhere", "settings":{"enforce_https":true}}' \
-d '{"source": "enforced-https-route", "target": "somewhere", "settings":{"enforce_https": true}}' \
http://api:5555/api/routes/

status_code=$(curl -s -o /dev/null -I -w "%{http_code}" -H "Host: enforced-https-route" http://ceryx/)

[ $status_code -eq 301 ]
}

@test "301 response when mode=redirect" {
curl -s -o /dev/null \
-X POST \
-H 'Content-Type: application/json' \
-d '{"source": "redirected-route", "target": "redirection-target", "settings":{"mode": "redirect"}}' \
http://api:5555/api/routes/

status_code=$(curl -s -o /dev/null -I -w "%{http_code}" -H "Host: redirected-route" http://ceryx/)

[ $status_code -eq 301 ]
}

0 comments on commit a10b135

Please sign in to comment.