nghttpx: Add Request#push in mruby scripting

Refactor Http2Upstream so that we can share code between link header
field based push and mruby push.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-09-05 22:47:07 +09:00
parent 98c959291f
commit ef1595672c
13 changed files with 301 additions and 64 deletions

View File

@ -1308,6 +1308,91 @@ const char *to_method_string(int method_token) {
return http_method_str(static_cast<http_method>(method_token));
}
int get_pure_path_component(const char **base, size_t *baselen,
const std::string &uri) {
int rv;
http_parser_url u{};
rv = http_parser_parse_url(uri.c_str(), uri.size(), 0, &u);
if (rv != 0) {
return -1;
}
if (u.field_set & (1 << UF_PATH)) {
auto &f = u.field_data[UF_PATH];
*base = uri.c_str() + f.off;
*baselen = f.len;
return 0;
}
*base = "/";
*baselen = 1;
return 0;
}
int construct_push_component(std::string &scheme, std::string &authority,
std::string &path, const char *base,
size_t baselen, const char *uri, size_t len) {
int rv;
const char *rel, *relq = nullptr;
size_t rellen, relqlen = 0;
http_parser_url u{};
rv = http_parser_parse_url(uri, len, 0, &u);
if (rv != 0) {
if (uri[0] == '/') {
return -1;
}
// treat link_url as relative URI.
auto end = std::find(uri, uri + len, '#');
auto q = std::find(uri, end, '?');
rel = uri;
rellen = q - uri;
if (q != end) {
relq = q + 1;
relqlen = end - relq;
}
} else {
if (u.field_set & (1 << UF_SCHEMA)) {
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri);
}
if (u.field_set & (1 << UF_HOST)) {
http2::copy_url_component(authority, &u, UF_HOST, uri);
if (u.field_set & (1 << UF_PORT)) {
authority += ":";
authority += util::utos(u.port);
}
}
if (u.field_set & (1 << UF_PATH)) {
auto &f = u.field_data[UF_PATH];
rel = uri + f.off;
rellen = f.len;
} else {
rel = "/";
rellen = 1;
}
if (u.field_set & (1 << UF_QUERY)) {
auto &f = u.field_data[UF_QUERY];
relq = uri + f.off;
relqlen = f.len;
}
}
path =
http2::path_join(base, baselen, nullptr, 0, rel, rellen, relq, relqlen);
return 0;
}
} // namespace http2
} // namespace nghttp2

View File

@ -352,6 +352,21 @@ std::string rewrite_clean_path(InputIt first, InputIt last) {
return path;
}
// Stores path component of |uri| in *base. Its extracted length is
// stored in *baselen. The extracted path does not include query
// component. This function returns 0 if it succeeds, or -1.
int get_pure_path_component(const char **base, size_t *baselen,
const std::string &uri);
// Deduces scheme, authority and path from given |uri| of length
// |len|, and stores them in |scheme|, |authority|, and |path|
// respectively. If |uri| is relative path, path resolution is taken
// palce using path given in |base| of length |baselen|. This
// function returns 0 if it succeeds, or -1.
int construct_push_component(std::string &scheme, std::string &authority,
std::string &path, const char *base,
size_t baselen, const char *uri, size_t len);
} // namespace http2
} // namespace nghttp2

View File

@ -880,4 +880,104 @@ void test_http2_rewrite_clean_path(void) {
CU_ASSERT(src == http2::rewrite_clean_path(std::begin(src), std::end(src)));
}
void test_http2_get_pure_path_component(void) {
const char *base;
size_t len;
std::string path;
path = "/";
CU_ASSERT(0 == http2::get_pure_path_component(&base, &len, path));
CU_ASSERT(util::streq_l("/", base, len));
path = "/foo";
CU_ASSERT(0 == http2::get_pure_path_component(&base, &len, path));
CU_ASSERT(util::streq_l("/foo", base, len));
path = "https://example.org/bar";
CU_ASSERT(0 == http2::get_pure_path_component(&base, &len, path));
CU_ASSERT(util::streq_l("/bar", base, len));
path = "https://example.org/alpha?q=a";
CU_ASSERT(0 == http2::get_pure_path_component(&base, &len, path));
CU_ASSERT(util::streq_l("/alpha", base, len));
path = "https://example.org/bravo?q=a#fragment";
CU_ASSERT(0 == http2::get_pure_path_component(&base, &len, path));
CU_ASSERT(util::streq_l("/bravo", base, len));
path = "\x01\x02";
CU_ASSERT(-1 == http2::get_pure_path_component(&base, &len, path));
}
void test_http2_construct_push_component(void) {
const char *base;
size_t baselen;
std::string uri;
std::string scheme, authority, path;
base = "/b/";
baselen = 3;
uri = "https://example.org/foo";
CU_ASSERT(0 == http2::construct_push_component(scheme, authority, path, base,
baselen, uri.c_str(),
uri.size()));
CU_ASSERT("https" == scheme);
CU_ASSERT("example.org" == authority);
CU_ASSERT("/foo" == path);
scheme.clear();
authority.clear();
path.clear();
uri = "/foo/bar?q=a";
CU_ASSERT(0 == http2::construct_push_component(scheme, authority, path, base,
baselen, uri.c_str(),
uri.size()));
CU_ASSERT("" == scheme);
CU_ASSERT("" == authority);
CU_ASSERT("/foo/bar?q=a" == path);
scheme.clear();
authority.clear();
path.clear();
uri = "foo/../bar?q=a";
CU_ASSERT(0 == http2::construct_push_component(scheme, authority, path, base,
baselen, uri.c_str(),
uri.size()));
CU_ASSERT("" == scheme);
CU_ASSERT("" == authority);
CU_ASSERT("/b/bar?q=a" == path);
scheme.clear();
authority.clear();
path.clear();
uri = "";
CU_ASSERT(0 == http2::construct_push_component(scheme, authority, path, base,
baselen, uri.c_str(),
uri.size()));
CU_ASSERT("" == scheme);
CU_ASSERT("" == authority);
CU_ASSERT("/" == path);
scheme.clear();
authority.clear();
path.clear();
uri = "?q=a";
CU_ASSERT(0 == http2::construct_push_component(scheme, authority, path, base,
baselen, uri.c_str(),
uri.size()));
CU_ASSERT("" == scheme);
CU_ASSERT("" == authority);
CU_ASSERT("/b/?q=a" == path);
}
} // namespace shrpx

View File

@ -47,6 +47,8 @@ void test_http2_parse_link_header(void);
void test_http2_path_join(void);
void test_http2_normalize_path(void);
void test_http2_rewrite_clean_path(void);
void test_http2_get_pure_path_component(void);
void test_http2_construct_push_component(void);
} // namespace shrpx

View File

@ -100,6 +100,10 @@ int main(int argc, char *argv[]) {
shrpx::test_http2_normalize_path) ||
!CU_add_test(pSuite, "http2_rewrite_clean_path",
shrpx::test_http2_rewrite_clean_path) ||
!CU_add_test(pSuite, "http2_get_pure_path_component",
shrpx::test_http2_get_pure_path_component) ||
!CU_add_test(pSuite, "http2_construct_push_component",
shrpx::test_http2_construct_push_component) ||
!CU_add_test(pSuite, "downstream_index_request_headers",
shrpx::test_downstream_index_request_headers) ||
!CU_add_test(pSuite, "downstream_index_response_headers",

View File

@ -1582,80 +1582,31 @@ int Http2Upstream::on_downstream_reset(bool no_retry) {
int Http2Upstream::prepare_push_promise(Downstream *downstream) {
int rv;
http_parser_url u{};
rv = http_parser_parse_url(downstream->get_request_path().c_str(),
downstream->get_request_path().size(), 0, &u);
const char *base;
size_t baselen;
rv = http2::get_pure_path_component(&base, &baselen,
downstream->get_request_path());
if (rv != 0) {
return 0;
}
const char *base;
size_t baselen;
if (u.field_set & (1 << UF_PATH)) {
auto &f = u.field_data[UF_PATH];
base = downstream->get_request_path().c_str() + f.off;
baselen = f.len;
} else {
base = "/";
baselen = 1;
}
for (auto &kv : downstream->get_response_headers()) {
if (kv.token != http2::HD_LINK) {
continue;
}
for (auto &link :
http2::parse_link_header(kv.value.c_str(), kv.value.size())) {
auto link_url = link.uri.first;
auto link_urllen = link.uri.second - link.uri.first;
const char *rel;
size_t rellen;
const char *relq = nullptr;
size_t relqlen = 0;
auto uri = link.uri.first;
auto len = link.uri.second - link.uri.first;
std::string authority, scheme;
http_parser_url v{};
rv = http_parser_parse_url(link_url, link_urllen, 0, &v);
std::string scheme, authority, path;
rv = http2::construct_push_component(scheme, authority, path, base,
baselen, uri, len);
if (rv != 0) {
assert(link_urllen);
if (link_url[0] == '/') {
continue;
}
// treat link_url as relative URI.
auto end = std::find(link_url, link_url + link_urllen, '#');
auto q = std::find(link_url, end, '?');
rel = link_url;
rellen = q - link_url;
if (q != end) {
relq = q + 1;
relqlen = end - relq;
}
} else {
if (v.field_set & (1 << UF_SCHEMA)) {
http2::copy_url_component(scheme, &v, UF_SCHEMA, link_url);
}
if (v.field_set & (1 << UF_HOST)) {
http2::copy_url_component(authority, &v, UF_HOST, link_url);
if (v.field_set & (1 << UF_PORT)) {
authority += ":";
authority += util::utos(v.port);
}
}
if (v.field_set & (1 << UF_PATH)) {
auto &f = v.field_data[UF_PATH];
rel = link_url + f.off;
rellen = f.len;
} else {
rel = "/";
rellen = 1;
}
if (v.field_set & (1 << UF_QUERY)) {
auto &f = v.field_data[UF_QUERY];
relq = link_url + f.off;
relqlen = f.len;
}
continue;
}
if (scheme.empty()) {
@ -1666,8 +1617,6 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
authority = downstream->get_request_http2_authority();
}
auto path = http2::path_join(base, baselen, nullptr, 0, rel, rellen, relq,
relqlen);
rv = submit_push_promise(scheme, authority, path, downstream);
if (rv != 0) {
return -1;
@ -1736,4 +1685,50 @@ int Http2Upstream::submit_push_promise(const std::string &scheme,
return 0;
}
int Http2Upstream::initiate_push(Downstream *downstream, const char *uri,
size_t len) {
int rv;
if (len == 0 ||
nghttp2_session_get_remote_settings(session_,
NGHTTP2_SETTINGS_ENABLE_PUSH) == 0 ||
get_config()->http2_proxy || get_config()->client_proxy ||
(downstream->get_stream_id() % 2) == 0) {
return 0;
}
const char *base;
size_t baselen;
rv = http2::get_pure_path_component(&base, &baselen,
downstream->get_request_path());
if (rv != 0) {
return -1;
}
std::string scheme, authority, path;
rv = http2::construct_push_component(scheme, authority, path, base, baselen,
uri, len);
if (rv != 0) {
return -1;
}
if (scheme.empty()) {
scheme = downstream->get_request_http2_scheme();
}
if (authority.empty()) {
authority = downstream->get_request_http2_authority();
}
rv = submit_push_promise(scheme, authority, path, downstream);
if (rv != 0) {
return -1;
}
return 0;
}
} // namespace shrpx

View File

@ -80,6 +80,8 @@ public:
virtual int on_downstream_reset(bool no_retry);
virtual int send_reply(Downstream *downstream, const uint8_t *body,
size_t bodylen);
virtual int initiate_push(Downstream *downstream, const char *uri,
size_t len);
bool get_flow_control() const;
// Perform HTTP/2 upgrade from |upstream|. On success, this object

View File

@ -1160,4 +1160,9 @@ fail:
return 0;
}
int HttpsUpstream::initiate_push(Downstream *downstream, const char *uri,
size_t len) {
return 0;
}
} // namespace shrpx

View File

@ -76,6 +76,8 @@ public:
virtual int on_downstream_reset(bool no_retry);
virtual int send_reply(Downstream *downstream, const uint8_t *body,
size_t bodylen);
virtual int initiate_push(Downstream *downstream, const char *uri,
size_t len);
void reset_current_header_length();
void log_response_headers(const std::string &hdrs) const;

View File

@ -268,6 +268,22 @@ mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) {
}
} // namespace
namespace {
mrb_value request_push(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto upstream = downstream->get_upstream();
const char *uri;
mrb_int len;
mrb_get_args(mrb, "s", &uri, &len);
upstream->initiate_push(downstream, uri, len);
return mrb_nil_value();
}
} // namespace
void init_request_class(mrb_state *mrb, RClass *module) {
auto request_class =
mrb_define_class_under(mrb, module, "Request", mrb->object_class);
@ -302,6 +318,7 @@ void init_request_class(mrb_state *mrb, RClass *module) {
MRB_ARGS_REQ(2));
mrb_define_method(mrb, request_class, "clear_headers", request_clear_headers,
MRB_ARGS_NONE());
mrb_define_method(mrb, request_class, "push", request_push, MRB_ARGS_REQ(1));
}
} // namespace mruby

View File

@ -1203,4 +1203,9 @@ int SpdyUpstream::on_downstream_reset(bool no_retry) {
return 0;
}
int SpdyUpstream::initiate_push(Downstream *downstream, const char *uri,
size_t len) {
return 0;
}
} // namespace shrpx

View File

@ -76,6 +76,8 @@ public:
virtual int send_reply(Downstream *downstream, const uint8_t *body,
size_t bodylen);
virtual int initiate_push(Downstream *downstream, const char *uri,
size_t len);
bool get_flow_control() const;

View File

@ -64,6 +64,9 @@ public:
size_t consumed) = 0;
virtual int send_reply(Downstream *downstream, const uint8_t *body,
size_t bodylen) = 0;
virtual int initiate_push(Downstream *downstream, const char *uri,
size_t len) = 0;
};
} // namespace shrpx