nghttpx: Add --add-response-header option
This commit is contained in:
parent
293b717b04
commit
a8a2236da9
|
@ -154,7 +154,7 @@ Headers concat_norm_headers(Headers headers);
|
|||
// value.c_str(). If |no_index| is true, nghttp2_nv flags member has
|
||||
// NGHTTP2_NV_FLAG_NO_INDEX flag set.
|
||||
nghttp2_nv make_nv(const std::string& name, const std::string& value,
|
||||
bool no_index);
|
||||
bool no_index = false);
|
||||
|
||||
// Create nghttp2_nv from string literal |name| and |value|.
|
||||
template<size_t N, size_t M>
|
||||
|
|
|
@ -107,6 +107,8 @@ int main(int argc, char* argv[])
|
|||
shrpx::test_downstream_rewrite_norm_location_response_header) ||
|
||||
!CU_add_test(pSuite, "config_parse_config_str_list",
|
||||
shrpx::test_shrpx_config_parse_config_str_list) ||
|
||||
!CU_add_test(pSuite, "config_parse_header",
|
||||
shrpx::test_shrpx_config_parse_header) ||
|
||||
!CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||
|
||||
!CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
|
||||
!CU_add_test(pSuite, "util_inp_strlower",
|
||||
|
|
12
src/shrpx.cc
12
src/shrpx.cc
|
@ -763,6 +763,13 @@ Misc:
|
|||
field or HTTP/2 ALTSVC frame. This option can be
|
||||
used multiple times to specify multiple
|
||||
alternative services. Example: --altsvc=h2,443
|
||||
--add-response-header=<HEADER>
|
||||
Specify additional header field to add to
|
||||
response header set. This option just appends
|
||||
header field and won't replace anything already
|
||||
set. This option can be used several times to
|
||||
specify multiple header fields.
|
||||
Example: --add-response-header="foo: bar"
|
||||
--frontend-http2-dump-request-header=<PATH>
|
||||
Dumps request headers received by HTTP/2 frontend
|
||||
to the file denoted in <PATH>. The output is
|
||||
|
@ -870,6 +877,7 @@ int main(int argc, char **argv)
|
|||
{"worker-write-rate", required_argument, &flag, 52},
|
||||
{"worker-write-burst", required_argument, &flag, 53},
|
||||
{"altsvc", required_argument, &flag, 54},
|
||||
{"add-response-header", required_argument, &flag, 55},
|
||||
{nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
|
@ -1130,6 +1138,10 @@ int main(int argc, char **argv)
|
|||
// --altsvc
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, optarg);
|
||||
break;
|
||||
case 55:
|
||||
// --add-response-header
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@ const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling";
|
|||
const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug";
|
||||
const char SHRPX_OPT_PADDING[] = "padding";
|
||||
const char SHRPX_OPT_ALTSVC[] = "altsvc";
|
||||
const char SHRPX_OPT_ADD_RESPONSE_HEADER[] = "add-response-header";
|
||||
|
||||
namespace {
|
||||
Config *config = nullptr;
|
||||
|
@ -251,6 +252,22 @@ std::unique_ptr<char*[]> parse_config_str_list(size_t *outlen, const char *s)
|
|||
return list;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> parse_header(const char *optarg)
|
||||
{
|
||||
// We skip possible ":" at the start of optarg.
|
||||
const auto *colon = strchr(optarg + 1, ':');
|
||||
|
||||
// name = ":" is not allowed
|
||||
if(colon == nullptr || (optarg[0] == ':' && colon == optarg + 1)) {
|
||||
return {"", ""};
|
||||
}
|
||||
|
||||
auto value = colon + 1;
|
||||
for(; *value == '\t' || *value == ' '; ++value);
|
||||
|
||||
return {std::string(optarg, colon), std::string(value, strlen(value))};
|
||||
}
|
||||
|
||||
int parse_config(const char *opt, const char *optarg)
|
||||
{
|
||||
char host[NI_MAXHOST];
|
||||
|
@ -543,6 +560,14 @@ int parse_config(const char *opt, const char *optarg)
|
|||
|
||||
mod_config()->altsvcs.push_back(std::move(altsvc));
|
||||
|
||||
} else if(util::strieq(opt, SHRPX_OPT_ADD_RESPONSE_HEADER)) {
|
||||
auto p = parse_header(optarg);
|
||||
if(p.first.empty()) {
|
||||
LOG(ERROR) << "add-response-header: header field name is empty: "
|
||||
<< optarg;
|
||||
return -1;
|
||||
}
|
||||
mod_config()->add_response_headers.push_back(std::move(p));
|
||||
} else if(util::strieq(opt, "conf")) {
|
||||
LOG(WARNING) << "conf is ignored";
|
||||
} else {
|
||||
|
|
|
@ -112,6 +112,7 @@ extern const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[];
|
|||
extern const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[];
|
||||
extern const char SHRPX_OPT_PADDING[];
|
||||
extern const char SHRPX_OPT_ALTSVC[];
|
||||
extern const char SHRPX_OPT_ADD_RESPONSE_HEADER[];
|
||||
|
||||
union sockaddr_union {
|
||||
sockaddr sa;
|
||||
|
@ -151,6 +152,7 @@ struct Config {
|
|||
// The list of (private key file, certificate file) pair
|
||||
std::vector<std::pair<std::string, std::string>> subcerts;
|
||||
std::vector<AltSvc> altsvcs;
|
||||
std::vector<std::pair<std::string, std::string>> add_response_headers;
|
||||
sockaddr_union downstream_addr;
|
||||
// binary form of http proxy host and port
|
||||
sockaddr_union downstream_http_proxy_addr;
|
||||
|
@ -285,6 +287,12 @@ std::string read_passwd_from_file(const char *filename);
|
|||
// responsibility to deallocate its memory.
|
||||
std::unique_ptr<char*[]> parse_config_str_list(size_t *outlen, const char *s);
|
||||
|
||||
// Parses header field in |optarg|. We expect header field is formed
|
||||
// like "NAME: VALUE". We require that NAME is non empty string. ":"
|
||||
// is allowed at the start of the NAME, but NAME == ":" is not
|
||||
// allowed. This function returns pair of NAME and VALUE.
|
||||
std::pair<std::string, std::string> parse_header(const char *optarg);
|
||||
|
||||
// Copies NULL-terminated string |val| to |*destp|. If |*destp| is not
|
||||
// NULL, it is freed before copying.
|
||||
void set_config_str(char **destp, const char *val);
|
||||
|
|
|
@ -60,4 +60,30 @@ void test_shrpx_config_parse_config_str_list(void)
|
|||
CU_ASSERT(0 == strcmp("charlie", res[2]));
|
||||
}
|
||||
|
||||
void test_shrpx_config_parse_header(void)
|
||||
{
|
||||
auto p = parse_header("a: b");
|
||||
CU_ASSERT("a" == p.first);
|
||||
CU_ASSERT("b" == p.second);
|
||||
|
||||
p = parse_header("a: b");
|
||||
CU_ASSERT("a" == p.first);
|
||||
CU_ASSERT("b" == p.second);
|
||||
|
||||
p = parse_header(":a: b");
|
||||
CU_ASSERT(":a" == p.first);
|
||||
CU_ASSERT("b" == p.second);
|
||||
|
||||
p = parse_header("a: :b");
|
||||
CU_ASSERT("a" == p.first);
|
||||
CU_ASSERT(":b" == p.second);
|
||||
|
||||
p = parse_header(": b");
|
||||
CU_ASSERT(p.first.empty());
|
||||
|
||||
p = parse_header("alpha: bravo charlie");
|
||||
CU_ASSERT("alpha" == p.first);
|
||||
CU_ASSERT("bravo charlie" == p.second);
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
namespace shrpx {
|
||||
|
||||
void test_shrpx_config_parse_config_str_list(void);
|
||||
void test_shrpx_config_parse_header(void);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
|
|
|
@ -1036,7 +1036,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
|||
size_t nheader = downstream->get_response_headers().size();
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
// 2 means :status and possible via header field.
|
||||
nva.reserve(nheader + 2);
|
||||
nva.reserve(nheader + 2 + get_config()->add_response_headers.size());
|
||||
std::string via_value;
|
||||
auto response_status = util::utos(downstream->get_response_http_status());
|
||||
nva.push_back(http2::make_nv_ls(":status", response_status));
|
||||
|
@ -1056,6 +1056,11 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
|||
(downstream->get_response_major(), downstream->get_response_minor());
|
||||
nva.push_back(http2::make_nv_ls("via", via_value));
|
||||
}
|
||||
|
||||
for(auto& p : get_config()->add_response_headers) {
|
||||
nva.push_back(http2::make_nv(p.first, p.second));
|
||||
}
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
std::stringstream ss;
|
||||
for(auto& nv : nva) {
|
||||
|
|
|
@ -715,6 +715,14 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
|||
(downstream->get_response_major(), downstream->get_response_minor());
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
|
||||
for(auto& p : get_config()->add_response_headers) {
|
||||
hdrs += p.first;
|
||||
hdrs += ": ";
|
||||
hdrs += p.second;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
|
||||
hdrs += "\r\n";
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
const char *hdrp;
|
||||
|
|
|
@ -854,7 +854,9 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream)
|
|||
}
|
||||
size_t nheader = downstream->get_response_headers().size();
|
||||
// 6 means :status, :version and possible via header field.
|
||||
auto nv = util::make_unique<const char*[]>(nheader * 2 + 6 + 1);
|
||||
auto nv = util::make_unique<const char*[]>
|
||||
(nheader * 2 + 6 + get_config()->add_response_headers.size() * 2 + 1);
|
||||
|
||||
size_t hdidx = 0;
|
||||
std::string via_value;
|
||||
std::string status_string = http2::get_status_string
|
||||
|
@ -887,6 +889,12 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream)
|
|||
nv[hdidx++] = "via";
|
||||
nv[hdidx++] = via_value.c_str();
|
||||
}
|
||||
|
||||
for(auto& p : get_config()->add_response_headers) {
|
||||
nv[hdidx++] = p.first.c_str();
|
||||
nv[hdidx++] = p.second.c_str();
|
||||
}
|
||||
|
||||
nv[hdidx++] = 0;
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
std::stringstream ss;
|
||||
|
|
Loading…
Reference in New Issue