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
|
// value.c_str(). If |no_index| is true, nghttp2_nv flags member has
|
||||||
// NGHTTP2_NV_FLAG_NO_INDEX flag set.
|
// NGHTTP2_NV_FLAG_NO_INDEX flag set.
|
||||||
nghttp2_nv make_nv(const std::string& name, const std::string& value,
|
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|.
|
// Create nghttp2_nv from string literal |name| and |value|.
|
||||||
template<size_t N, size_t M>
|
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) ||
|
shrpx::test_downstream_rewrite_norm_location_response_header) ||
|
||||||
!CU_add_test(pSuite, "config_parse_config_str_list",
|
!CU_add_test(pSuite, "config_parse_config_str_list",
|
||||||
shrpx::test_shrpx_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_streq", shrpx::test_util_streq) ||
|
||||||
!CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
|
!CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) ||
|
||||||
!CU_add_test(pSuite, "util_inp_strlower",
|
!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
|
field or HTTP/2 ALTSVC frame. This option can be
|
||||||
used multiple times to specify multiple
|
used multiple times to specify multiple
|
||||||
alternative services. Example: --altsvc=h2,443
|
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>
|
--frontend-http2-dump-request-header=<PATH>
|
||||||
Dumps request headers received by HTTP/2 frontend
|
Dumps request headers received by HTTP/2 frontend
|
||||||
to the file denoted in <PATH>. The output is
|
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-rate", required_argument, &flag, 52},
|
||||||
{"worker-write-burst", required_argument, &flag, 53},
|
{"worker-write-burst", required_argument, &flag, 53},
|
||||||
{"altsvc", required_argument, &flag, 54},
|
{"altsvc", required_argument, &flag, 54},
|
||||||
|
{"add-response-header", required_argument, &flag, 55},
|
||||||
{nullptr, 0, nullptr, 0 }
|
{nullptr, 0, nullptr, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1130,6 +1138,10 @@ int main(int argc, char **argv)
|
||||||
// --altsvc
|
// --altsvc
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, optarg);
|
cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, optarg);
|
||||||
break;
|
break;
|
||||||
|
case 55:
|
||||||
|
// --add-response-header
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug";
|
||||||
const char SHRPX_OPT_PADDING[] = "padding";
|
const char SHRPX_OPT_PADDING[] = "padding";
|
||||||
const char SHRPX_OPT_ALTSVC[] = "altsvc";
|
const char SHRPX_OPT_ALTSVC[] = "altsvc";
|
||||||
|
const char SHRPX_OPT_ADD_RESPONSE_HEADER[] = "add-response-header";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Config *config = nullptr;
|
Config *config = nullptr;
|
||||||
|
@ -251,6 +252,22 @@ std::unique_ptr<char*[]> parse_config_str_list(size_t *outlen, const char *s)
|
||||||
return list;
|
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)
|
int parse_config(const char *opt, const char *optarg)
|
||||||
{
|
{
|
||||||
char host[NI_MAXHOST];
|
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));
|
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")) {
|
} else if(util::strieq(opt, "conf")) {
|
||||||
LOG(WARNING) << "conf is ignored";
|
LOG(WARNING) << "conf is ignored";
|
||||||
} else {
|
} 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_FRONTEND_FRAME_DEBUG[];
|
||||||
extern const char SHRPX_OPT_PADDING[];
|
extern const char SHRPX_OPT_PADDING[];
|
||||||
extern const char SHRPX_OPT_ALTSVC[];
|
extern const char SHRPX_OPT_ALTSVC[];
|
||||||
|
extern const char SHRPX_OPT_ADD_RESPONSE_HEADER[];
|
||||||
|
|
||||||
union sockaddr_union {
|
union sockaddr_union {
|
||||||
sockaddr sa;
|
sockaddr sa;
|
||||||
|
@ -151,6 +152,7 @@ struct Config {
|
||||||
// The list of (private key file, certificate file) pair
|
// The list of (private key file, certificate file) pair
|
||||||
std::vector<std::pair<std::string, std::string>> subcerts;
|
std::vector<std::pair<std::string, std::string>> subcerts;
|
||||||
std::vector<AltSvc> altsvcs;
|
std::vector<AltSvc> altsvcs;
|
||||||
|
std::vector<std::pair<std::string, std::string>> add_response_headers;
|
||||||
sockaddr_union downstream_addr;
|
sockaddr_union downstream_addr;
|
||||||
// binary form of http proxy host and port
|
// binary form of http proxy host and port
|
||||||
sockaddr_union downstream_http_proxy_addr;
|
sockaddr_union downstream_http_proxy_addr;
|
||||||
|
@ -285,6 +287,12 @@ std::string read_passwd_from_file(const char *filename);
|
||||||
// responsibility to deallocate its memory.
|
// responsibility to deallocate its memory.
|
||||||
std::unique_ptr<char*[]> parse_config_str_list(size_t *outlen, const char *s);
|
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
|
// Copies NULL-terminated string |val| to |*destp|. If |*destp| is not
|
||||||
// NULL, it is freed before copying.
|
// NULL, it is freed before copying.
|
||||||
void set_config_str(char **destp, const char *val);
|
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]));
|
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
|
} // namespace shrpx
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
void test_shrpx_config_parse_config_str_list(void);
|
void test_shrpx_config_parse_config_str_list(void);
|
||||||
|
void test_shrpx_config_parse_header(void);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
|
|
|
@ -1036,7 +1036,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
||||||
size_t nheader = downstream->get_response_headers().size();
|
size_t nheader = downstream->get_response_headers().size();
|
||||||
auto nva = std::vector<nghttp2_nv>();
|
auto nva = std::vector<nghttp2_nv>();
|
||||||
// 2 means :status and possible via header field.
|
// 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;
|
std::string via_value;
|
||||||
auto response_status = util::utos(downstream->get_response_http_status());
|
auto response_status = util::utos(downstream->get_response_http_status());
|
||||||
nva.push_back(http2::make_nv_ls(":status", response_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());
|
(downstream->get_response_major(), downstream->get_response_minor());
|
||||||
nva.push_back(http2::make_nv_ls("via", via_value));
|
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)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
for(auto& nv : nva) {
|
for(auto& nv : nva) {
|
||||||
|
|
|
@ -715,6 +715,14 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
||||||
(downstream->get_response_major(), downstream->get_response_minor());
|
(downstream->get_response_major(), downstream->get_response_minor());
|
||||||
hdrs += "\r\n";
|
hdrs += "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(auto& p : get_config()->add_response_headers) {
|
||||||
|
hdrs += p.first;
|
||||||
|
hdrs += ": ";
|
||||||
|
hdrs += p.second;
|
||||||
|
hdrs += "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
hdrs += "\r\n";
|
hdrs += "\r\n";
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
const char *hdrp;
|
const char *hdrp;
|
||||||
|
|
|
@ -854,7 +854,9 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream)
|
||||||
}
|
}
|
||||||
size_t nheader = downstream->get_response_headers().size();
|
size_t nheader = downstream->get_response_headers().size();
|
||||||
// 6 means :status, :version and possible via header field.
|
// 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;
|
size_t hdidx = 0;
|
||||||
std::string via_value;
|
std::string via_value;
|
||||||
std::string status_string = http2::get_status_string
|
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";
|
||||||
nv[hdidx++] = via_value.c_str();
|
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;
|
nv[hdidx++] = 0;
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
Loading…
Reference in New Issue