Skip to content
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

lua: Allow to set header entry as table to httpCall and respond APIs #7851

Merged
merged 4 commits into from
Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions docs/root/configuration/http_filters/lua_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ more details on the supported API.
{
[":method"] = "POST",
[":path"] = "/",
[":authority"] = "lua_cluster"
[":authority"] = "lua_cluster",
["set-cookie"] = { "lang=lua; Path=/", "type=binding; Path=/" }
},
"hello world",
5000)
Expand Down Expand Up @@ -236,9 +237,9 @@ httpCall()

Makes an HTTP call to an upstream host. Envoy will yield the script until the call completes or
has an error. *cluster* is a string which maps to a configured cluster manager cluster. *headers*
is a table of key/value pairs to send. Note that the *:method*, *:path*, and *:authority* headers
must be set. *body* is an optional string of body data to send. *timeout* is an integer that
specifies the call timeout in milliseconds.
is a table of key/value pairs to send (the value can be a string or table of strings). Note that
the *:method*, *:path*, and *:authority* headers must be set. *body* is an optional string of body
data to send. *timeout* is an integer that specifies the call timeout in milliseconds.

Returns *headers* which is a table of response headers. Returns *body* which is the string response
body. May be nil if there is no body.
Expand All @@ -264,8 +265,9 @@ passed to subsequent filters. Meaning, the following Lua code is invalid:
end
end

*headers* is a table of key/value pairs to send. Note that the *:status* header
must be set. *body* is a string and supplies the optional response body. May be nil.
*headers* is a table of key/value pairs to send (the value can be a string or table of strings).
Note that the *:status* header must be set. *body* is a string and supplies the optional response
body. May be nil.

metadata()
^^^^^^^^^^
Expand Down Expand Up @@ -316,10 +318,10 @@ importPublicKey()
^^^^^^^^^^^^^^^^^

.. code-block:: lua

pubkey = handle:importPublicKey(keyder, keyderLength)

Returns public key which is used by :ref:`verifySignature <verify_signature>` to verify digital signature.
Returns public key which is used by :ref:`verifySignature <verify_signature>` to verify digital signature.

.. _verify_signature:

Expand All @@ -330,13 +332,13 @@ verifySignature()

ok, error = verifySignature(hashFunction, pubkey, signature, signatureLength, data, dataLength)

Verify signature using provided parameters. *hashFunction* is the variable for hash function which be used
for verifying signature. *SHA1*, *SHA224*, *SHA256*, *SHA384* and *SHA512* are supported.
*pubkey* is the public key. *signature* is the signature to be verified. *signatureLength* is
Verify signature using provided parameters. *hashFunction* is the variable for hash function which be used
for verifying signature. *SHA1*, *SHA224*, *SHA256*, *SHA384* and *SHA512* are supported.
*pubkey* is the public key. *signature* is the signature to be verified. *signatureLength* is
the length of the signature. *data* is the content which will be hashed. *dataLength* is the length of data.

The function returns a pair. If the first element is *true*, the second element will be empty
which means signature is verified; otherwise, the second element will store the error message.
which means signature is verified; otherwise, the second element will store the error message.

.. _config_http_filters_lua_header_wrapper:

Expand Down
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Version history
* http: added the ability to reject HTTP/1.1 requests with invalid HTTP header values, using the runtime feature `envoy.reloadable_features.strict_header_validation`.
* http: added the ability to :ref:`merge adjacent slashes<envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.merge_slashes>` in the path.
* listeners: added :ref:`HTTP inspector listener filter <config_listener_filters_http_inspector>`.
* lua: extended `httpCall()` and `respond()` APIs to accept headers with entry values that can be a string or table of strings.
* router: added :ref:`rq_retry_skipped_request_not_complete <config_http_filters_router_stats>` counter stat to router stats.
* router check tool: add coverage reporting & enforcement.
* tls: added verification of IP address SAN fields in certificates against configured SANs in the
Expand Down
16 changes: 13 additions & 3 deletions source/extensions/filters/http/lua/lua_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,19 @@ Http::HeaderMapPtr StreamHandleWrapper::buildHeadersFromTable(lua_State* state,
while (lua_next(state, table_index) != 0) {
// Uses 'key' (at index -2) and 'value' (at index -1).
const char* key = luaL_checkstring(state, -2);
const char* value = luaL_checkstring(state, -1);
headers->addCopy(Http::LowerCaseString(key), value);

// Check if the current value is a table, we iterate through the table and add each element of
// it as a header entry value for the current key.
if (lua_istable(state, -1)) {
lua_pushnil(state);
while (lua_next(state, -2) != 0) {
const char* value = luaL_checkstring(state, -1);
headers->addCopy(Http::LowerCaseString(key), value);
lua_pop(state, 1);
}
} else {
const char* value = luaL_checkstring(state, -1);
headers->addCopy(Http::LowerCaseString(key), value);
}
// Removes 'value'; keeps 'key' for next iteration. This is the input for lua_next() so that
// it can push the next key/value pair onto the stack.
lua_pop(state, 1);
Expand Down
22 changes: 15 additions & 7 deletions test/extensions/filters/http/lua/lua_filter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,8 @@ TEST_F(LuaHttpFilterTest, HttpCall) {
{
[":method"] = "POST",
[":path"] = "/",
[":authority"] = "foo"
[":authority"] = "foo",
["set-cookie"] = { "flavor=chocolate; Path=/", "variant=chewy; Path=/" }
},
"hello world",
5000)
Expand All @@ -770,6 +771,8 @@ TEST_F(LuaHttpFilterTest, HttpCall) {
EXPECT_EQ((Http::TestHeaderMapImpl{{":path", "/"},
{":method", "POST"},
{":authority", "foo"},
{"set-cookie", "flavor=chocolate; Path=/"},
{"set-cookie", "variant=chewy; Path=/"},
{"content-length", "11"}}),
message->headers());
callbacks = &cb;
Expand Down Expand Up @@ -959,7 +962,10 @@ TEST_F(LuaHttpFilterTest, HttpCallImmediateResponse) {
nil,
5000)
request_handle:respond(
{[":status"] = "403"},
{
[":status"] = "403",
["set-cookie"] = { "flavor=chocolate; Path=/", "variant=chewy; Path=/" }
},
nil)
end
)EOF"};
Expand Down Expand Up @@ -988,7 +994,9 @@ TEST_F(LuaHttpFilterTest, HttpCallImmediateResponse) {

Http::MessagePtr response_message(new Http::ResponseMessageImpl(
Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{":status", "200"}}}));
Http::TestHeaderMapImpl expected_headers{{":status", "403"}};
Http::TestHeaderMapImpl expected_headers{{":status", "403"},
{"set-cookie", "flavor=chocolate; Path=/"},
{"set-cookie", "variant=chewy; Path=/"}};
EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true));
callbacks->onSuccess(std::move(response_message));
}
Expand Down Expand Up @@ -1668,28 +1676,28 @@ TEST_F(LuaHttpFilterTest, SignatureVerify) {

rawsig = signature:fromhex()

ok, error = request_handle:verifySignature(hashFunc, pubkey, rawsig, string.len(rawsig), data, string.len(data))
ok, error = request_handle:verifySignature(hashFunc, pubkey, rawsig, string.len(rawsig), data, string.len(data))
if ok then
request_handle:logTrace("signature is valid")
else
request_handle:logTrace(error)
end

ok, error = request_handle:verifySignature("unknown", pubkey, rawsig, string.len(rawsig), data, string.len(data))
ok, error = request_handle:verifySignature("unknown", pubkey, rawsig, string.len(rawsig), data, string.len(data))
if ok then
request_handle:logTrace("signature is valid")
else
request_handle:logTrace(error)
end

ok, error = request_handle:verifySignature(hashFunc, pubkey, "0000", 4, data, string.len(data))
ok, error = request_handle:verifySignature(hashFunc, pubkey, "0000", 4, data, string.len(data))
if ok then
request_handle:logTrace("signature is valid")
else
request_handle:logTrace(error)
end

ok, error = request_handle:verifySignature(hashFunc, pubkey, rawsig, string.len(rawsig), "xxxx", 4)
ok, error = request_handle:verifySignature(hashFunc, pubkey, rawsig, string.len(rawsig), "xxxx", 4)
if ok then
request_handle:logTrace("signature is valid")
else
Expand Down