Skip to content

Commit

Permalink
http.lua: allow setting HTTP1 reason phrase (#22847)
Browse files Browse the repository at this point in the history
Signed-off-by: Max Kuznetsov <[email protected]>
  • Loading branch information
syhpoon authored Sep 9, 2022
1 parent 2052d21 commit 667f060
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 12 deletions.
3 changes: 3 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ new_features:
change: |
added the expected :ref:`receive <envoy_v3_api_field_config.core.v3.HealthCheck.HttpHealthCheck.receive>` payload check for HTTP health check.
Added :ref:`response_buffer_size <envoy_v3_api_field_config.core.v3.HealthCheck.HttpHealthCheck.response_buffer_size>` to configure the maximum HTTP health check response buffer size.
- area: lua
change: |
added new headers method "setHttp1ReasonPhrase" for lua filter, please see :ref:`lua header wrapper <config_http_filters_lua_header_wrapper>`.
- area: lua
change: |
added stats for lua filter, please see :ref:`lua filter stats <config_http_filters_lua_stats>`.
Expand Down
11 changes: 11 additions & 0 deletions docs/root/configuration/http/http_filters/lua_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,17 @@ replace()
Replaces a header. *key* is a string that supplies the header key. *value* is a string that supplies
the header value. If the header does not exist, it is added as per the *add()* function.

setHttp1ReasonPhrase()
^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: lua
headers:setHttp1ReasonPhrase(reasonPhrase)
Sets a custom HTTP/1 response reason phrase. This call is *only valid in the response flow*.
*reasonPhrase* is a string that supplies the reason phrase value. Additionally this call only
effects HTTP/1 connections. It will have no effect if the client is HTTP/2 or HTTP/3.

.. _config_http_filters_lua_buffer_wrapper:

Buffer API
Expand Down
1 change: 1 addition & 0 deletions source/extensions/filters/http/lua/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ envoy_cc_library(
"//source/common/http:utility_lib",
"//source/extensions/filters/common/lua:lua_lib",
"//source/extensions/filters/common/lua:wrappers_lib",
"//source/extensions/http/header_formatters/preserve_case:preserve_case_formatter",
],
)

Expand Down
61 changes: 49 additions & 12 deletions source/extensions/filters/http/lua/wrappers.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include "source/extensions/filters/http/lua/wrappers.h"

#include "source/common/http/header_map_impl.h"
#include "source/common/http/header_utility.h"
#include "source/common/http/utility.h"
#include "source/extensions/filters/common/lua/wrappers.h"
#include "source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.h"

namespace Envoy {
namespace Extensions {
Expand All @@ -11,10 +13,11 @@ namespace Lua {

HeaderMapIterator::HeaderMapIterator(HeaderMapWrapper& parent) : parent_(parent) {
entries_.reserve(parent_.headers_.size());
parent_.headers_.iterate([this](const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate {
entries_.push_back(&header);
return Http::HeaderMap::Iterate::Continue;
});
parent_.headers_.iterate(
[this](const Envoy::Http::HeaderEntry& header) -> Envoy::Http::HeaderMap::Iterate {
entries_.push_back(&header);
return Envoy::Http::HeaderMap::Iterate::Continue;
});
}

int HeaderMapIterator::luaPairsIterator(lua_State* state) {
Expand All @@ -36,14 +39,15 @@ int HeaderMapWrapper::luaAdd(lua_State* state) {

const char* key = luaL_checkstring(state, 2);
const char* value = luaL_checkstring(state, 3);
headers_.addCopy(Http::LowerCaseString(key), value);
headers_.addCopy(Envoy::Http::LowerCaseString(key), value);
return 0;
}

int HeaderMapWrapper::luaGet(lua_State* state) {
absl::string_view key = Filters::Common::Lua::getStringViewFromLuaString(state, 2);
const Http::HeaderUtility::GetAllOfHeaderAsStringResult value =
Http::HeaderUtility::getAllOfHeaderAsString(headers_, Http::LowerCaseString(key));
const Envoy::Http::HeaderUtility::GetAllOfHeaderAsStringResult value =
Envoy::Http::HeaderUtility::getAllOfHeaderAsString(headers_,
Envoy::Http::LowerCaseString(key));
if (value.result().has_value()) {
lua_pushlstring(state, value.result().value().data(), value.result().value().size());
return 1;
Expand All @@ -55,7 +59,8 @@ int HeaderMapWrapper::luaGet(lua_State* state) {
int HeaderMapWrapper::luaGetAtIndex(lua_State* state) {
absl::string_view key = Filters::Common::Lua::getStringViewFromLuaString(state, 2);
const int index = luaL_checknumber(state, 3);
const Http::HeaderMap::GetResult header_value = headers_.get(Http::LowerCaseString(key));
const Envoy::Http::HeaderMap::GetResult header_value =
headers_.get(Envoy::Http::LowerCaseString(key));
if (index >= 0 && header_value.size() > static_cast<uint64_t>(index)) {
absl::string_view value = header_value[index]->value().getStringView();
lua_pushlstring(state, value.data(), value.size());
Expand All @@ -66,7 +71,8 @@ int HeaderMapWrapper::luaGetAtIndex(lua_State* state) {

int HeaderMapWrapper::luaGetNumValues(lua_State* state) {
absl::string_view key = Filters::Common::Lua::getStringViewFromLuaString(state, 2);
const Http::HeaderMap::GetResult header_value = headers_.get(Http::LowerCaseString(key));
const Envoy::Http::HeaderMap::GetResult header_value =
headers_.get(Envoy::Http::LowerCaseString(key));
lua_pushnumber(state, header_value.size());
return 1;
}
Expand All @@ -93,7 +99,7 @@ int HeaderMapWrapper::luaReplace(lua_State* state) {

const char* key = luaL_checkstring(state, 2);
const char* value = luaL_checkstring(state, 3);
const Http::LowerCaseString lower_key(key);
const Envoy::Http::LowerCaseString lower_key(key);

headers_.setCopy(lower_key, value);

Expand All @@ -104,7 +110,7 @@ int HeaderMapWrapper::luaRemove(lua_State* state) {
checkModifiable(state);

const char* key = luaL_checkstring(state, 2);
headers_.remove(Http::LowerCaseString(key));
headers_.remove(Envoy::Http::LowerCaseString(key));
return 0;
}

Expand All @@ -118,8 +124,39 @@ void HeaderMapWrapper::checkModifiable(lua_State* state) {
}
}

int HeaderMapWrapper::luaSetHttp1ReasonPhrase(lua_State* state) {
checkModifiable(state);

size_t input_size = 0;
const char* phrase = luaL_checklstring(state, 2, &input_size);

Envoy::Http::StatefulHeaderKeyFormatterOptRef formatter(headers_.formatter());

if (!formatter.has_value()) {
using envoy::extensions::http::header_formatters::preserve_case::v3::
PreserveCaseFormatterConfig;
using Envoy::Http::ResponseHeaderMapImpl;
using Envoy::Http::StatefulHeaderKeyFormatter;
using Http::HeaderFormatters::PreserveCase::PreserveCaseHeaderFormatter;

// Casting here to make sure the call is in the right (response) context
ResponseHeaderMapImpl* map = dynamic_cast<ResponseHeaderMapImpl*>(&headers_);
if (map) {
std::unique_ptr<StatefulHeaderKeyFormatter> fmt =
std::make_unique<PreserveCaseHeaderFormatter>(true, PreserveCaseFormatterConfig::DEFAULT);
fmt->setReasonPhrase(absl::string_view(phrase, input_size));
map->setFormatter(std::move(fmt));
}
} else {
formatter->setReasonPhrase(absl::string_view(phrase, input_size));
}

return 0;
}

int StreamInfoWrapper::luaProtocol(lua_State* state) {
const std::string& protocol = Http::Utility::getProtocolString(stream_info_.protocol().value());
const std::string& protocol =
Envoy::Http::Utility::getProtocolString(stream_info_.protocol().value());
lua_pushlstring(state, protocol.data(), protocol.size());
return 1;
}
Expand Down
8 changes: 8 additions & 0 deletions source/extensions/filters/http/lua/wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class HeaderMapWrapper : public Filters::Common::Lua::BaseLuaObject<HeaderMapWra
{"getNumValues", static_luaGetNumValues},
{"remove", static_luaRemove},
{"replace", static_luaReplace},
{"setHttp1ReasonPhrase", static_luaSetHttp1ReasonPhrase},
{"__pairs", static_luaPairs}};
}

Expand Down Expand Up @@ -105,6 +106,13 @@ class HeaderMapWrapper : public Filters::Common::Lua::BaseLuaObject<HeaderMapWra
*/
DECLARE_LUA_FUNCTION(HeaderMapWrapper, luaReplace);

/**
* Set a HTTP1 reason phrase
* @param 1 (string): reason phrase
* @return nothing.
*/
DECLARE_LUA_FUNCTION(HeaderMapWrapper, luaSetHttp1ReasonPhrase);

void checkModifiable(lua_State* state);

// Envoy::Lua::BaseLuaObject
Expand Down
21 changes: 21 additions & 0 deletions test/extensions/filters/http/lua/lua_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1235,5 +1235,26 @@ name: lua
testRewriteResponse(FILTER_AND_CODE);
}

// Test whether setting the HTTP1 reason phrase
TEST_P(LuaIntegrationTest, Http1ReasonPhrase) {
const std::string FILTER_AND_CODE =
R"EOF(
name: lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
default_source_code:
inline_string: |
function envoy_on_response(response_handle)
response_handle:headers():setHttp1ReasonPhrase("Slow Down")
end
)EOF";

initializeFilter(FILTER_AND_CODE);

std::string response;
sendRawHttpAndWaitForResponse(lookupPort("http"), "GET / HTTP/1.1\r\n\r\n", &response, true);
EXPECT_TRUE(response.find("HTTP/1.1 400 Slow Down\r\n") == 0);
}

} // namespace
} // namespace Envoy
20 changes: 20 additions & 0 deletions test/extensions/filters/http/lua/wrappers_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,26 @@ TEST_F(LuaHeaderMapWrapperTest, IteratorAcrossYield) {
"[string \"...\"]:5: object used outside of proper scope");
}

// Verify setting the HTTP1 reason phrase
TEST_F(LuaHeaderMapWrapperTest, SetHttp1ReasonPhrase) {
const std::string SCRIPT{R"EOF(
function callMe(object)
object:setHttp1ReasonPhrase("Slow Down")
end
)EOF"};

InSequence s;
setup(SCRIPT);

auto headers = Http::ResponseHeaderMapImpl::create();
HeaderMapWrapper::create(coroutine_->luaState(), *headers, []() { return true; });
start("callMe");

Http::StatefulHeaderKeyFormatterOptRef formatter(headers->formatter());
EXPECT_EQ(true, formatter.has_value());
EXPECT_EQ("Slow Down", formatter->getReasonPhrase());
}

class LuaStreamInfoWrapperTest
: public Filters::Common::Lua::LuaWrappersTestBase<StreamInfoWrapper> {
public:
Expand Down

0 comments on commit 667f060

Please sign in to comment.