Skip to content

Commit a76260a

Browse files
committed
Fixes #16: Added a third parameter to http-request lua cors that accepts a comma-delimited list of custom headers, which sets the Access-Control-Allow-Headers response header.
1 parent 306fd2c commit a76260a

File tree

5 files changed

+49
-23
lines changed

5 files changed

+49
-23
lines changed

README.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ Lua library for enabling CORS in HAProxy.
66

77
Cross-origin Request Sharing allows you to permit client-side code running within a different domain to call your services. This module extends HAProxy so that it can:
88

9-
* set an *Access-Control-Allow-Methods* and *Access-Control-Max-Age* header in response to CORS preflight requests.
9+
* set an *Access-Control-Allow-Methods* header in response to a preflight request
10+
* set an *Access-Control-Allow-Headers* header in response to a preflight request
11+
* set an *Access-Control-Max-Age* header in response to a preflight request
1012
* set an *Access-Control-Allow-Origin* header to whitelist a domain. Note that this header should only ever return either a single domain or an asterisk (*). Otherwise, it would have been possible to hardcode all permitted domains without the need for Lua scripting.
1113

1214
This library checks the incoming *Origin* header, which contains the calling code's domain, and tries to match it with the list of permitted domains. If there is a match, that domain is sent back in the *Access-Control-Allow-Origin* header.
@@ -31,22 +33,29 @@ global
3133
lua-load /path/to/cors.lua
3234
```
3335

34-
In your `frontend` or `listen` section, capture the client's *Origin* request header by adding `http-request lua.cors` The first parameter is a comma-delimited list of HTTP methods that can be used. The second parameter is comma-delimited list of origins that are permitted to call your service.
36+
In your `frontend` or `listen` section, capture the client's *Origin* request header by adding `http-request lua.cors` Its parameters are:
3537

36-
```
37-
http-request lua.cors "GET,PUT,POST" "example.com,localhost,localhost:8080"
38-
```
38+
* The first parameter is a comma-delimited list of HTTP methods that can be used. This is used to set the *Access-Control-Allow-Methods* header.
39+
* The second parameter is comma-delimited list of origins that are permitted to call your service. This is used to set the *Access-Control-Allow-Origin* header.
40+
* The third parameter is a comma-delimited list of custom headers that can be used. This is used to set the *Access-Control-Allow-Headers* header.
41+
42+
Each of these parameters can be set to an asterisk (*) to allow all values.
3943

40-
Within the same section, invoke the `http-response lua.cors` action to attach CORS headers to responses from backend servers.
44+
Within the same `frontend` or `listen` section, add the `http-response lua.cors` action to attach CORS headers to responses from backend servers.
4145

46+
**Example 1: Allow specific methods, origins and headers**
4247
```
48+
http-request lua.cors "GET,PUT,POST" "example.com,localhost,localhost:8080", "X-Custom-Header1,X-Custom-Header2"
49+
4350
http-response lua.cors
4451
```
4552

46-
You can also whitelist all domains by setting the second parameter to an asterisk:
53+
Example 2: Allow all methods, origins, and headers
4754

4855
```
49-
http-request lua.cors "GET,PUT,POST" "*"
56+
http-request lua.cors "*" "*", "*"
57+
58+
http-response lua.cors
5059
```
5160

5261
## Preflight Requests
@@ -57,7 +66,8 @@ For versions prior to 2.2, the module must forward the request to the backend se
5766

5867
This module returns the following CORS headers for a preflight request:
5968

60-
* `Access-Control-Allow-Method` - set to the HTTP methods you set with `http-request lua cors` in the haproxy.cfg file
69+
* `Access-Control-Allow-Methods` - set to the HTTP methods you set with `http-request lua cors` in the haproxy.cfg file
70+
* `Access-Conrol-Allow-Headers` - set to the HTTP headers you set with `http-request lua cors` in the haproxy.cfg file
6171
* `Access-Control-Max-Age` - set to 600
6272

6373
## Example

example/haproxy/cors.lua

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ end
4848
-- be intercepted and returned immediately.
4949
-- txn: The current transaction object that gives access to response properties
5050
-- allowed_methods: Comma-delimited list of allowed HTTP methods. (e.g. GET,POST,PUT,DELETE)
51-
function preflight_request_ver1(txn, allowed_methods)
51+
-- allowed_headers: Comma-delimited list of allowed headers. (e.g. X-Header1,X-Header2)
52+
function preflight_request_ver1(txn, allowed_methods, allowed_headers)
5253
core.Debug("CORS: preflight request received")
5354
txn.http:res_add_header("Access-Control-Allow-Methods", allowed_methods)
55+
txn.http:res_add_header("Access-Control-Allow-Headers", allowed_headers)
5456
txn.http:res_add_header("Access-Control-Max-Age", 600)
5557
core.Debug("CORS: attaching allowed methods to response")
5658
end
@@ -62,13 +64,15 @@ end
6264
-- origin: The value from the 'origin' request header
6365
-- allowed_methods: Comma-delimited list of allowed HTTP methods. (e.g. GET,POST,PUT,DELETE)
6466
-- allowed_origins: Comma-delimited list of allowed origins. (e.g. localhost,localhost:8080,test.com)
65-
function preflight_request_ver2(txn, origin, allowed_methods, allowed_origins)
67+
-- allowed_headers: Comma-delimited list of allowed headers. (e.g. X-Header1,X-Header2)
68+
function preflight_request_ver2(txn, origin, allowed_methods, allowed_origins, allowed_headers)
6669
core.Debug("CORS: preflight request received")
6770

6871
local reply = txn:reply()
6972
reply:set_status(204, "No Content")
7073
reply:add_header("Content-Type", "text/html")
7174
reply:add_header("Access-Control-Allow-Methods", allowed_methods)
75+
reply:add_header("Access-Control-Allow-Headers", allowed_headers)
7276
reply:add_header("Access-Control-Max-Age", 600)
7377

7478
local allowed_origin = get_allowed_origin(origin, allowed_origins)
@@ -90,7 +94,8 @@ end
9094
-- txn: The current transaction object that gives access to response properties
9195
-- allowed_methods: Comma-delimited list of allowed HTTP methods. (e.g. GET,POST,PUT,DELETE)
9296
-- allowed_origins: Comma-delimited list of allowed origins. (e.g. localhost,localhost:8080,test.com)
93-
function cors_request(txn, allowed_methods, allowed_origins)
97+
-- allowed_headers: Comma-delimited list of allowed headers. (e.g. X-Header1,X-Header2)
98+
function cors_request(txn, allowed_methods, allowed_origins, allowed_headers)
9499
local headers = txn.http:req_get_headers()
95100
local origin = headers["origin"][0]
96101

@@ -103,14 +108,15 @@ function cors_request(txn, allowed_methods, allowed_origins)
103108

104109
transaction_data["allowed_methods"] = allowed_methods
105110
transaction_data["allowed_origins"] = allowed_origins
111+
transaction_data["allowed_headers"] = allowed_headers
106112

107113
txn:set_priv(transaction_data)
108114

109115
local method = txn.sf:method()
110116
transaction_data["method"] = method
111117

112118
if method == "OPTIONS" and txn.reply ~= nil then
113-
preflight_request_ver2(txn, origin, allowed_methods, allowed_origins)
119+
preflight_request_ver2(txn, origin, allowed_methods, allowed_origins, allowed_headers)
114120
end
115121
end
116122

@@ -121,6 +127,7 @@ function cors_response(txn)
121127
local origin = transaction_data["origin"]
122128
local allowed_origins = transaction_data["allowed_origins"]
123129
local allowed_methods = transaction_data["allowed_methods"]
130+
local allowed_headers = transaction_data["allowed_headers"]
124131
local method = transaction_data["method"]
125132

126133
-- Always vary on the Origin
@@ -137,7 +144,7 @@ function cors_response(txn)
137144
core.Debug("CORS: " .. origin .. " not allowed")
138145
else
139146
if method == "OPTIONS" and txn.reply == nil then
140-
preflight_request_ver1(txn, allowed_methods)
147+
preflight_request_ver1(txn, allowed_methods, allowed_headers)
141148
end
142149

143150
core.Debug("CORS: " .. origin .. " allowed")
@@ -146,5 +153,5 @@ function cors_response(txn)
146153
end
147154

148155
-- Register the actions with HAProxy
149-
core.register_action("cors", {"http-req"}, cors_request, 2)
156+
core.register_action("cors", {"http-req"}, cors_request, 3)
150157
core.register_action("cors", {"http-res"}, cors_response, 0)

example/haproxy/haproxy.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ listen ui
1818
listen api
1919
bind :8080
2020

21+
22+
2123
# Invoke the CORS service on the request to capture the Origin header
22-
http-request lua.cors "GET,PUT,POST", "localhost"
24+
http-request lua.cors "GET,PUT,POST", "localhost", "X-Custom-Header"
2325

2426
# Invoke the CORS service on the response to add CORS headers
2527
http-response lua.cors

example/web/views/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
var btn = document.getElementById('putdata_btn');
1717
btn.addEventListener("click", function() {
18-
fetch('http://localhost:8080/putdata', { method: 'put', body: "test=test" }).then(function(response) {
18+
fetch('http://localhost:8080/putdata', { method: 'put', body: "test=test", headers: { 'X-Custom-Header': 'test' } }).then(function(response) {
1919
div.innerHTML = "PUT succeeded";
2020
});
2121
});

lib/cors.lua

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ end
4848
-- be intercepted and returned immediately.
4949
-- txn: The current transaction object that gives access to response properties
5050
-- allowed_methods: Comma-delimited list of allowed HTTP methods. (e.g. GET,POST,PUT,DELETE)
51-
function preflight_request_ver1(txn, allowed_methods)
51+
-- allowed_headers: Comma-delimited list of allowed headers. (e.g. X-Header1,X-Header2)
52+
function preflight_request_ver1(txn, allowed_methods, allowed_headers)
5253
core.Debug("CORS: preflight request received")
5354
txn.http:res_add_header("Access-Control-Allow-Methods", allowed_methods)
55+
txn.http:res_add_header("Access-Control-Allow-Headers", allowed_headers)
5456
txn.http:res_add_header("Access-Control-Max-Age", 600)
5557
core.Debug("CORS: attaching allowed methods to response")
5658
end
@@ -62,13 +64,15 @@ end
6264
-- origin: The value from the 'origin' request header
6365
-- allowed_methods: Comma-delimited list of allowed HTTP methods. (e.g. GET,POST,PUT,DELETE)
6466
-- allowed_origins: Comma-delimited list of allowed origins. (e.g. localhost,localhost:8080,test.com)
65-
function preflight_request_ver2(txn, origin, allowed_methods, allowed_origins)
67+
-- allowed_headers: Comma-delimited list of allowed headers. (e.g. X-Header1,X-Header2)
68+
function preflight_request_ver2(txn, origin, allowed_methods, allowed_origins, allowed_headers)
6669
core.Debug("CORS: preflight request received")
6770

6871
local reply = txn:reply()
6972
reply:set_status(204, "No Content")
7073
reply:add_header("Content-Type", "text/html")
7174
reply:add_header("Access-Control-Allow-Methods", allowed_methods)
75+
reply:add_header("Access-Control-Allow-Headers", allowed_headers)
7276
reply:add_header("Access-Control-Max-Age", 600)
7377

7478
local allowed_origin = get_allowed_origin(origin, allowed_origins)
@@ -90,7 +94,8 @@ end
9094
-- txn: The current transaction object that gives access to response properties
9195
-- allowed_methods: Comma-delimited list of allowed HTTP methods. (e.g. GET,POST,PUT,DELETE)
9296
-- allowed_origins: Comma-delimited list of allowed origins. (e.g. localhost,localhost:8080,test.com)
93-
function cors_request(txn, allowed_methods, allowed_origins)
97+
-- allowed_headers: Comma-delimited list of allowed headers. (e.g. X-Header1,X-Header2)
98+
function cors_request(txn, allowed_methods, allowed_origins, allowed_headers)
9499
local headers = txn.http:req_get_headers()
95100
local origin = headers["origin"][0]
96101

@@ -103,14 +108,15 @@ function cors_request(txn, allowed_methods, allowed_origins)
103108

104109
transaction_data["allowed_methods"] = allowed_methods
105110
transaction_data["allowed_origins"] = allowed_origins
111+
transaction_data["allowed_headers"] = allowed_headers
106112

107113
txn:set_priv(transaction_data)
108114

109115
local method = txn.sf:method()
110116
transaction_data["method"] = method
111117

112118
if method == "OPTIONS" and txn.reply ~= nil then
113-
preflight_request_ver2(txn, origin, allowed_methods, allowed_origins)
119+
preflight_request_ver2(txn, origin, allowed_methods, allowed_origins, allowed_headers)
114120
end
115121
end
116122

@@ -121,6 +127,7 @@ function cors_response(txn)
121127
local origin = transaction_data["origin"]
122128
local allowed_origins = transaction_data["allowed_origins"]
123129
local allowed_methods = transaction_data["allowed_methods"]
130+
local allowed_headers = transaction_data["allowed_headers"]
124131
local method = transaction_data["method"]
125132

126133
-- Always vary on the Origin
@@ -137,7 +144,7 @@ function cors_response(txn)
137144
core.Debug("CORS: " .. origin .. " not allowed")
138145
else
139146
if method == "OPTIONS" and txn.reply == nil then
140-
preflight_request_ver1(txn, allowed_methods)
147+
preflight_request_ver1(txn, allowed_methods, allowed_headers)
141148
end
142149

143150
core.Debug("CORS: " .. origin .. " allowed")
@@ -146,5 +153,5 @@ function cors_response(txn)
146153
end
147154

148155
-- Register the actions with HAProxy
149-
core.register_action("cors", {"http-req"}, cors_request, 2)
156+
core.register_action("cors", {"http-req"}, cors_request, 3)
150157
core.register_action("cors", {"http-res"}, cors_response, 0)

0 commit comments

Comments
 (0)