nghttpx: Add altsvc related options
To advertise alternative serive, at least --altsvc-port and --altsvc-protocol-id must be specified.
This commit is contained in:
parent
1d38df0a31
commit
db6c41a219
|
@ -125,6 +125,8 @@ const char* strframetype(uint8_t type)
|
||||||
return "GOAWAY";
|
return "GOAWAY";
|
||||||
case NGHTTP2_WINDOW_UPDATE:
|
case NGHTTP2_WINDOW_UPDATE:
|
||||||
return "WINDOW_UPDATE";
|
return "WINDOW_UPDATE";
|
||||||
|
case NGHTTP2_ALTSVC:
|
||||||
|
return "ALTSVC";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
@ -458,6 +460,25 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
|
||||||
fprintf(outfile, "(window_size_increment=%d)\n",
|
fprintf(outfile, "(window_size_increment=%d)\n",
|
||||||
frame->window_update.window_size_increment);
|
frame->window_update.window_size_increment);
|
||||||
break;
|
break;
|
||||||
|
case NGHTTP2_ALTSVC:
|
||||||
|
print_frame_attr_indent();
|
||||||
|
fprintf(outfile, "(max-age=%u, port=%u, protocol_id=",
|
||||||
|
frame->altsvc.max_age, frame->altsvc.port);
|
||||||
|
if(frame->altsvc.protocol_id_len) {
|
||||||
|
fwrite(frame->altsvc.protocol_id, frame->altsvc.protocol_id_len, 1,
|
||||||
|
outfile);
|
||||||
|
}
|
||||||
|
fprintf(outfile, ", host=");
|
||||||
|
if(frame->altsvc.host_len) {
|
||||||
|
fwrite(frame->altsvc.host, frame->altsvc.host_len, 1, outfile);
|
||||||
|
}
|
||||||
|
fprintf(outfile, ", origin=");
|
||||||
|
if(frame->altsvc.origin_len) {
|
||||||
|
fwrite(frame->altsvc.origin, frame->altsvc.origin_len, 1, outfile);
|
||||||
|
}
|
||||||
|
fprintf(outfile, ")\n");
|
||||||
|
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(outfile, "\n");
|
fprintf(outfile, "\n");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -112,7 +112,9 @@ int main(int argc, char* argv[])
|
||||||
!CU_add_test(pSuite, "util_inp_strlower",
|
!CU_add_test(pSuite, "util_inp_strlower",
|
||||||
shrpx::test_util_inp_strlower) ||
|
shrpx::test_util_inp_strlower) ||
|
||||||
!CU_add_test(pSuite, "util_to_base64",
|
!CU_add_test(pSuite, "util_to_base64",
|
||||||
shrpx::test_util_to_base64)) {
|
shrpx::test_util_to_base64) ||
|
||||||
|
!CU_add_test(pSuite, "util_percent_encode_token",
|
||||||
|
shrpx::test_util_percent_encode_token)) {
|
||||||
CU_cleanup_registry();
|
CU_cleanup_registry();
|
||||||
return CU_get_error();
|
return CU_get_error();
|
||||||
}
|
}
|
||||||
|
|
41
src/shrpx.cc
41
src/shrpx.cc
|
@ -443,6 +443,14 @@ void fill_default_config()
|
||||||
mod_config()->http2_no_cookie_crumbling = false;
|
mod_config()->http2_no_cookie_crumbling = false;
|
||||||
mod_config()->upstream_frame_debug = false;
|
mod_config()->upstream_frame_debug = false;
|
||||||
mod_config()->padding = 0;
|
mod_config()->padding = 0;
|
||||||
|
|
||||||
|
mod_config()->altsvc_port = 0;
|
||||||
|
mod_config()->altsvc_protocol_id = nullptr;
|
||||||
|
mod_config()->altsvc_protocol_id_len = 0;
|
||||||
|
mod_config()->altsvc_host = nullptr;
|
||||||
|
mod_config()->altsvc_host_len = 0;
|
||||||
|
mod_config()->altsvc_origin = nullptr;
|
||||||
|
mod_config()->altsvc_origin_len = 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -749,6 +757,19 @@ Misc:
|
||||||
downstream request.
|
downstream request.
|
||||||
--no-via Don't append to Via header field. If Via header
|
--no-via Don't append to Via header field. If Via header
|
||||||
field is received, it is left unaltered.
|
field is received, it is left unaltered.
|
||||||
|
--altsvc-port=<PORT>
|
||||||
|
Port number of alternative service advertised in
|
||||||
|
alt-svc header field or HTTP/2 ALTSVC frame.
|
||||||
|
--altsvc-protocol-id=<PROTOID>
|
||||||
|
ALPN protocol identifier of alternative service
|
||||||
|
advertised in alt-svc header field or HTTP/2
|
||||||
|
ALTSVC frame.
|
||||||
|
--altsvc-host=<HOST>
|
||||||
|
Host name that alternative service is available
|
||||||
|
upon, which is advertised in HTTP/2 ALTSVC frame.
|
||||||
|
--altsvc-origin=<ORIGIN>
|
||||||
|
Origin that alternative service is applicable to,
|
||||||
|
which is advertised in HTTP/2 ALTSVC frame.
|
||||||
--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
|
||||||
|
@ -855,6 +876,10 @@ int main(int argc, char **argv)
|
||||||
{"worker-read-burst", required_argument, &flag, 51},
|
{"worker-read-burst", required_argument, &flag, 51},
|
||||||
{"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-port", required_argument, &flag, 54},
|
||||||
|
{"altsvc-protocol-id", required_argument, &flag, 55},
|
||||||
|
{"altsvc-host", required_argument, &flag, 56},
|
||||||
|
{"altsvc-origin", required_argument, &flag, 57},
|
||||||
{nullptr, 0, nullptr, 0 }
|
{nullptr, 0, nullptr, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1111,6 +1136,22 @@ int main(int argc, char **argv)
|
||||||
// --worker-write-burst
|
// --worker-write-burst
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, optarg);
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, optarg);
|
||||||
break;
|
break;
|
||||||
|
case 54:
|
||||||
|
// --altsvc-port
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC_PORT, optarg);
|
||||||
|
break;
|
||||||
|
case 55:
|
||||||
|
// --altsvc-protocol-id
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC_PROTOCOL_ID, optarg);
|
||||||
|
break;
|
||||||
|
case 56:
|
||||||
|
// --altsvc-host
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC_HOST, optarg);
|
||||||
|
break;
|
||||||
|
case 57:
|
||||||
|
// --altsvc-origin
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC_ORIGIN, optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,6 +121,10 @@ const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[] =
|
||||||
const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling";
|
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_PORT[] = "altsvc-port";
|
||||||
|
const char SHRPX_OPT_ALTSVC_PROTOCOL_ID[] = "altsvc-protocol-id";
|
||||||
|
const char SHRPX_OPT_ALTSVC_HOST[] = "altsvc-host";
|
||||||
|
const char SHRPX_OPT_ALTSVC_ORIGIN[] = "altsvc-origin";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Config *config = nullptr;
|
Config *config = nullptr;
|
||||||
|
@ -498,6 +502,32 @@ int parse_config(const char *opt, const char *optarg)
|
||||||
mod_config()->upstream_frame_debug = util::strieq(optarg, "yes");
|
mod_config()->upstream_frame_debug = util::strieq(optarg, "yes");
|
||||||
} else if(util::strieq(opt, SHRPX_OPT_PADDING)) {
|
} else if(util::strieq(opt, SHRPX_OPT_PADDING)) {
|
||||||
mod_config()->padding = strtoul(optarg, nullptr, 10);
|
mod_config()->padding = strtoul(optarg, nullptr, 10);
|
||||||
|
} else if(util::strieq(opt, SHRPX_OPT_ALTSVC_PORT)) {
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
auto port = strtoul(optarg, nullptr, 10);
|
||||||
|
|
||||||
|
if(errno == 0 &&
|
||||||
|
1 <= port && port <= std::numeric_limits<uint16_t>::max()) {
|
||||||
|
|
||||||
|
mod_config()->altsvc_port = port;
|
||||||
|
} else {
|
||||||
|
LOG(ERROR) << "altsvc-port is invalid: " << optarg;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if(util::strieq(opt, SHRPX_OPT_ALTSVC_PROTOCOL_ID)) {
|
||||||
|
set_config_str(&mod_config()->altsvc_protocol_id, optarg);
|
||||||
|
|
||||||
|
mod_config()->altsvc_protocol_id_len =
|
||||||
|
strlen(get_config()->altsvc_protocol_id);
|
||||||
|
} else if(util::strieq(opt, SHRPX_OPT_ALTSVC_HOST)) {
|
||||||
|
set_config_str(&mod_config()->altsvc_host, optarg);
|
||||||
|
|
||||||
|
mod_config()->altsvc_host_len = strlen(get_config()->altsvc_host);
|
||||||
|
} else if(util::strieq(opt, SHRPX_OPT_ALTSVC_ORIGIN)) {
|
||||||
|
set_config_str(&mod_config()->altsvc_origin, optarg);
|
||||||
|
|
||||||
|
mod_config()->altsvc_origin_len = strlen(get_config()->altsvc_origin);
|
||||||
} else if(util::strieq(opt, "conf")) {
|
} else if(util::strieq(opt, "conf")) {
|
||||||
LOG(WARNING) << "conf is ignored";
|
LOG(WARNING) << "conf is ignored";
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -108,6 +108,10 @@ extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[];
|
||||||
extern const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[];
|
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_PORT[];
|
||||||
|
extern const char SHRPX_OPT_ALTSVC_PROTOCOL_ID[];
|
||||||
|
extern const char SHRPX_OPT_ALTSVC_HOST[];
|
||||||
|
extern const char SHRPX_OPT_ALTSVC_ORIGIN[];
|
||||||
|
|
||||||
union sockaddr_union {
|
union sockaddr_union {
|
||||||
sockaddr sa;
|
sockaddr sa;
|
||||||
|
@ -170,6 +174,9 @@ struct Config {
|
||||||
char *client_cert_file;
|
char *client_cert_file;
|
||||||
FILE *http2_upstream_dump_request_header;
|
FILE *http2_upstream_dump_request_header;
|
||||||
FILE *http2_upstream_dump_response_header;
|
FILE *http2_upstream_dump_response_header;
|
||||||
|
char *altsvc_protocol_id;
|
||||||
|
char *altsvc_host;
|
||||||
|
char *altsvc_origin;
|
||||||
size_t downstream_addrlen;
|
size_t downstream_addrlen;
|
||||||
size_t num_worker;
|
size_t num_worker;
|
||||||
size_t http2_max_concurrent_streams;
|
size_t http2_max_concurrent_streams;
|
||||||
|
@ -192,6 +199,9 @@ struct Config {
|
||||||
// The number of elements in tls_proto_list
|
// The number of elements in tls_proto_list
|
||||||
size_t tls_proto_list_len;
|
size_t tls_proto_list_len;
|
||||||
size_t padding;
|
size_t padding;
|
||||||
|
size_t altsvc_protocol_id_len;
|
||||||
|
size_t altsvc_host_len;
|
||||||
|
size_t altsvc_origin_len;
|
||||||
// downstream protocol; this will be determined by given options.
|
// downstream protocol; this will be determined by given options.
|
||||||
shrpx_proto downstream_proto;
|
shrpx_proto downstream_proto;
|
||||||
int syslog_facility;
|
int syslog_facility;
|
||||||
|
@ -202,6 +212,7 @@ struct Config {
|
||||||
uint16_t downstream_port;
|
uint16_t downstream_port;
|
||||||
// port in http proxy URI
|
// port in http proxy URI
|
||||||
uint16_t downstream_http_proxy_port;
|
uint16_t downstream_http_proxy_port;
|
||||||
|
uint16_t altsvc_port;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool daemon;
|
bool daemon;
|
||||||
bool verify_client;
|
bool verify_client;
|
||||||
|
|
|
@ -532,13 +532,40 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
|
||||||
rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE,
|
||||||
entry,
|
entry,
|
||||||
sizeof(entry)/sizeof(nghttp2_settings_entry));
|
sizeof(entry)/sizeof(nghttp2_settings_entry));
|
||||||
assert(rv == 0);
|
if(rv != 0) {
|
||||||
|
ULOG(ERROR, this) << "nghttp2_submit_settings() returned error: "
|
||||||
|
<< nghttp2_strerror(rv);
|
||||||
|
}
|
||||||
|
|
||||||
if(get_config()->http2_upstream_connection_window_bits > 16) {
|
if(get_config()->http2_upstream_connection_window_bits > 16) {
|
||||||
int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits)
|
int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits)
|
||||||
- 1 - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
- 1 - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||||
rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta);
|
rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta);
|
||||||
assert(rv == 0);
|
|
||||||
|
if(rv != 0) {
|
||||||
|
ULOG(ERROR, this) << "nghttp2_submit_window_update() returned error: "
|
||||||
|
<< nghttp2_strerror(rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(get_config()->altsvc_port != 0 && get_config()->altsvc_protocol_id) {
|
||||||
|
// Set max_age to 24hrs, which is default for alt-svc header
|
||||||
|
// field.
|
||||||
|
rv = nghttp2_submit_altsvc
|
||||||
|
(session_, NGHTTP2_FLAG_NONE, 0,
|
||||||
|
86400,
|
||||||
|
get_config()->altsvc_port,
|
||||||
|
reinterpret_cast<const uint8_t*>(get_config()->altsvc_protocol_id),
|
||||||
|
get_config()->altsvc_protocol_id_len,
|
||||||
|
reinterpret_cast<const uint8_t*>(get_config()->altsvc_host),
|
||||||
|
get_config()->altsvc_host_len,
|
||||||
|
reinterpret_cast<const uint8_t*>(get_config()->altsvc_origin),
|
||||||
|
get_config()->altsvc_origin_len);
|
||||||
|
|
||||||
|
if(rv != 0) {
|
||||||
|
ULOG(ERROR, this) << "nghttp2_submit_altsvc() returned error: "
|
||||||
|
<< nghttp2_strerror(rv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -678,6 +678,18 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
||||||
} else {
|
} else {
|
||||||
hdrs += "Connection: close\r\n";
|
hdrs += "Connection: close\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(downstream->get_norm_response_header("alt-svc") == end_headers) {
|
||||||
|
// We won't change or alter alt-svc from backend at the moment.
|
||||||
|
if(get_config()->altsvc_port != 0 && get_config()->altsvc_protocol_id) {
|
||||||
|
hdrs += "Alt-Svc: ";
|
||||||
|
hdrs += util::percent_encode_token(get_config()->altsvc_protocol_id);
|
||||||
|
hdrs += "=";
|
||||||
|
hdrs += util::utos(get_config()->altsvc_port);
|
||||||
|
hdrs += "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto via = downstream->get_norm_response_header("via");
|
auto via = downstream->get_norm_response_header("via");
|
||||||
if(get_config()->no_via) {
|
if(get_config()->no_via) {
|
||||||
if(via != end_headers) {
|
if(via != end_headers) {
|
||||||
|
|
28
src/util.cc
28
src/util.cc
|
@ -86,6 +86,34 @@ std::string percentEncode(const std::string& target)
|
||||||
target.size());
|
target.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool in_token(char c)
|
||||||
|
{
|
||||||
|
static const char extra[] = {
|
||||||
|
'!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~'
|
||||||
|
};
|
||||||
|
|
||||||
|
return isAlpha(c) || isDigit(c) ||
|
||||||
|
std::find(&extra[0], &extra[sizeof(extra)], c) != &extra[sizeof(extra)];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string percent_encode_token(const std::string& target)
|
||||||
|
{
|
||||||
|
auto len = target.size();
|
||||||
|
std::string dest;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < len; ++i) {
|
||||||
|
char c = target[i];
|
||||||
|
if(c != '%' && in_token(c)) {
|
||||||
|
dest += c;
|
||||||
|
} else {
|
||||||
|
char temp[4];
|
||||||
|
snprintf(temp, sizeof(temp), "%%%02X", c);
|
||||||
|
dest += temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
std::string percentDecode
|
std::string percentDecode
|
||||||
(std::string::const_iterator first, std::string::const_iterator last)
|
(std::string::const_iterator first, std::string::const_iterator last)
|
||||||
{
|
{
|
||||||
|
|
|
@ -205,6 +205,9 @@ bool isHexDigit(const char c);
|
||||||
|
|
||||||
bool inRFC3986UnreservedChars(const char c);
|
bool inRFC3986UnreservedChars(const char c);
|
||||||
|
|
||||||
|
// Returns true if |c| is in token (HTTP-p1, Section 3.2.6)
|
||||||
|
bool in_token(char c);
|
||||||
|
|
||||||
std::string percentEncode(const unsigned char* target, size_t len);
|
std::string percentEncode(const unsigned char* target, size_t len);
|
||||||
|
|
||||||
std::string percentEncode(const std::string& target);
|
std::string percentEncode(const std::string& target);
|
||||||
|
@ -212,6 +215,9 @@ std::string percentEncode(const std::string& target);
|
||||||
std::string percentDecode
|
std::string percentDecode
|
||||||
(std::string::const_iterator first, std::string::const_iterator last);
|
(std::string::const_iterator first, std::string::const_iterator last);
|
||||||
|
|
||||||
|
// Percent encode |target| if character is not in token or '%'.
|
||||||
|
std::string percent_encode_token(const std::string& target);
|
||||||
|
|
||||||
std::string format_hex(const unsigned char *s, size_t len);
|
std::string format_hex(const unsigned char *s, size_t len);
|
||||||
|
|
||||||
std::string http_date(time_t t);
|
std::string http_date(time_t t);
|
||||||
|
|
|
@ -93,4 +93,12 @@ void test_util_to_base64(void)
|
||||||
CU_ASSERT("AAA++B/B" == x);
|
CU_ASSERT("AAA++B/B" == x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_util_percent_encode_token(void)
|
||||||
|
{
|
||||||
|
CU_ASSERT("h2" == util::percent_encode_token("h2"));
|
||||||
|
CU_ASSERT("h3~" == util::percent_encode_token("h3~"));
|
||||||
|
CU_ASSERT("100%25" == util::percent_encode_token("100%"));
|
||||||
|
CU_ASSERT("http%202" == util::percent_encode_token("http 2"));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -31,6 +31,7 @@ void test_util_streq(void);
|
||||||
void test_util_strieq(void);
|
void test_util_strieq(void);
|
||||||
void test_util_inp_strlower(void);
|
void test_util_inp_strlower(void);
|
||||||
void test_util_to_base64(void);
|
void test_util_to_base64(void);
|
||||||
|
void test_util_percent_encode_token(void);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue