-
Notifications
You must be signed in to change notification settings - Fork 170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Policy: HTTP proxy #1080
Policy: HTTP proxy #1080
Changes from all commits
1737370
1a1d15a
0a44919
45f0d02
aff0477
4515570
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# HTTP proxy policy | ||
|
||
This policy allows users to define a HTTP proxy where the traffic will be send | ||
over the defined proxy, the example traffic flow is the following: | ||
|
||
``` | ||
,-. | ||
`-' | ||
/|\ | ||
| ,-------. ,---------. ,----------. | ||
/ \ |Apicast| |HTTPPROXY| |APIBackend| | ||
User `---+---' `----+----' `----------' | ||
| GET /resource | | | | ||
| --------------->| | | | ||
| | | | | ||
| | Get /resource | | | ||
| |------------------>| | | ||
| | | | | ||
| | | Get /resource/ | | ||
| | | - - - - - - - - - >| | ||
| | | | | ||
| | | response | | ||
| | |<- - - - - - - - - -| | ||
| | | | | ||
| | response | | | ||
| |<------------------| | | ||
| | | | | ||
| | | | | ||
| <---------------| | | | ||
User ,---+---. ,----+----. ,----------. | ||
,-. |Apicast| |HTTPPROXY| |APIBackend| | ||
`-' `-------' `---------' `----------' | ||
/|\ | ||
| | ||
/ \ | ||
``` | ||
|
||
All APIcast traffic to 3scale backend will not use the proxy, due to this only | ||
applies for the service and the communication between Apicast and API backend. | ||
|
||
If you want to use all traffic through a Proxy, an HTTP_PROXY env var need to be | ||
used. | ||
|
||
## Configuration | ||
|
||
``` | ||
"policy_chain": [ | ||
{ | ||
"name": "apicast.policy.apicast" | ||
}, | ||
{ | ||
"name": "apicast.policy.http_proxy", | ||
"configuration": { | ||
"all_proxy": "http://192.168.15.103:8888/", | ||
"https_proxy": "https://192.168.15.103:8888/", | ||
"http_proxy": "https://192.168.15.103:8888/" | ||
} | ||
} | ||
] | ||
``` | ||
|
||
- If http_proxy or https_proxy is not defined the all_proxy will be taken. | ||
|
||
## Caveats | ||
|
||
- This policy will disable all load-balancing policies and traffic will be | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Asking just in case. Does the order in the chain matter? |
||
always send to the proxy. | ||
- In case of HTTP_PROXY, HTTPS_PROXY or ALL_PROXY parameters are defined, this | ||
policy will overwrite those values. | ||
- Proxy connection does not support authentication. | ||
|
||
|
||
## Example Use case | ||
|
||
This policy was designed to be able to apply more fined grained policies and | ||
transformation using Apache Camel. | ||
|
||
An example project can be found | ||
[here](https://github.com/zregvart/camel-netty-proxy). This project is an HTTP | ||
Proxy that transforms to uppercase all the response body given by the API | ||
backend. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"$schema": "http://apicast.io/policy-v1/schema#manifest#", | ||
"name": "Proxy service", | ||
"summary": "Adds an HTTP proxy to the service.", | ||
"description": [ | ||
"With this policy all the traffic for this service will be routed accross ", | ||
"the defined proxy" | ||
], | ||
"version": "builtin", | ||
"configuration": { | ||
"type": "object", | ||
"properties": { | ||
"all_proxy": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understood correctly, all_proxy is only used when the others are not set. I wonder if we can prevent invalid configs (setting both all_proxy, and the other 2) by changing the schema. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I took care in the code, also added a unittest.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
"description": "Defines a HTTP proxy to be used for connecting to services if a protocol-specific proxy is not specified. Authentication is not supported.", | ||
"type": "string" | ||
}, | ||
"https_proxy": { | ||
"description": "Defines a HTTPS proxy to be used for connecting to HTTPS services. Authentication is not supported", | ||
"type": "string" | ||
}, | ||
"http_proxy": { | ||
"description": "Defines a HTTP proxy to be used for connecting to HTTP services. Authentication is not supported", | ||
"type": "string" | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
return require("proxy") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
local policy = require('apicast.policy') | ||
local _M = policy.new('http_proxy', 'builtin') | ||
|
||
local resty_url = require 'resty.url' | ||
local ipairs = ipairs | ||
|
||
local new = _M.new | ||
|
||
local proxies = {"http", "https"} | ||
|
||
function _M.new(config) | ||
local self = new(config) | ||
self.proxies = {} | ||
|
||
if config.all_proxy then | ||
local err | ||
self.all_proxy, err = resty_url.parse(config.all_proxy) | ||
if err then | ||
ngx.log(ngx.WARN, "All proxy '", config.all_proxy, "' is not correctly defined, err:", err) | ||
end | ||
end | ||
|
||
for _, proto in ipairs(proxies) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
local val, err = resty_url.parse(config[string.format("%s_proxy", proto)]) | ||
if err then | ||
ngx.log(ngx.WARN, proto, " proxy is not correctly defined, err: ", err) | ||
end | ||
self.proxies[proto] = val or self.all_proxy | ||
end | ||
return self | ||
end | ||
|
||
local function find_proxy(self, scheme) | ||
return self.proxies[scheme] | ||
end | ||
|
||
function _M:export() | ||
return { | ||
get_http_proxy = function(uri) | ||
if not uri.scheme then | ||
return nil | ||
end | ||
return find_proxy(self, uri.scheme) | ||
end | ||
} | ||
end | ||
|
||
return _M |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
local proxy_policy = require('apicast.policy.http_proxy') | ||
local resty_url = require 'resty.url' | ||
|
||
describe('HTTP proxy policy', function() | ||
local all_proxy_val = "http://all.com" | ||
local http_proxy_val = "http://plain.com" | ||
local https_proxy_val = "http://secure.com" | ||
|
||
local http_uri = {scheme="http"} | ||
local https_uri = {scheme="https"} | ||
|
||
it("http[s] proxies are defined if all_proxy is in there", function() | ||
local proxy = proxy_policy.new({ | ||
all_proxy = all_proxy_val | ||
}) | ||
local callback = proxy:export() | ||
|
||
assert.same(callback.get_http_proxy(http_uri), resty_url.parse(all_proxy_val)) | ||
assert.same(callback.get_http_proxy(https_uri), resty_url.parse(all_proxy_val)) | ||
end) | ||
|
||
it("all_proxy does not overwrite http/https proxies", function() | ||
local proxy = proxy_policy.new({ | ||
all_proxy = all_proxy_val, | ||
http_proxy = http_proxy_val, | ||
https_proxy = https_proxy_val | ||
}) | ||
local callback = proxy:export() | ||
|
||
assert.same(callback.get_http_proxy(http_uri), resty_url.parse(http_proxy_val)) | ||
assert.same(callback.get_http_proxy(https_uri), resty_url.parse(https_proxy_val)) | ||
end) | ||
|
||
it("empty config return all nil", function() | ||
local proxy = proxy_policy.new({}) | ||
local callback = proxy:export() | ||
|
||
assert.is_nil(callback.get_http_proxy(https_uri)) | ||
assert.is_nil(callback.get_http_proxy(http_uri)) | ||
end) | ||
|
||
describe("get_http_proxy callback", function() | ||
local callback = proxy_policy.new({ | ||
all_proxy = all_proxy_val | ||
}):export() | ||
|
||
it("Valid protocol", function() | ||
|
||
local result = callback.get_http_proxy( | ||
resty_url.parse("http://google.com")) | ||
assert.same(result, resty_url.parse(all_proxy_val)) | ||
end) | ||
|
||
it("invalid protocol", function() | ||
local result = callback:get_http_proxy( | ||
{}, {scheme="invalid"}) | ||
assert.is_nil(result) | ||
end) | ||
|
||
end) | ||
end) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's important to clarify that this only applies to the API backend and not the 3scale backend (apisonator).