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:
parent
98c959291f
commit
ef1595672c
85
src/http2.cc
85
src/http2.cc
|
@ -1308,6 +1308,91 @@ const char *to_method_string(int method_token) {
|
||||||
return http_method_str(static_cast<http_method>(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 http2
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
15
src/http2.h
15
src/http2.h
|
@ -352,6 +352,21 @@ std::string rewrite_clean_path(InputIt first, InputIt last) {
|
||||||
return path;
|
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 http2
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -880,4 +880,104 @@ void test_http2_rewrite_clean_path(void) {
|
||||||
CU_ASSERT(src == http2::rewrite_clean_path(std::begin(src), std::end(src)));
|
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
|
} // namespace shrpx
|
||||||
|
|
|
@ -47,6 +47,8 @@ void test_http2_parse_link_header(void);
|
||||||
void test_http2_path_join(void);
|
void test_http2_path_join(void);
|
||||||
void test_http2_normalize_path(void);
|
void test_http2_normalize_path(void);
|
||||||
void test_http2_rewrite_clean_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
|
} // namespace shrpx
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,10 @@ int main(int argc, char *argv[]) {
|
||||||
shrpx::test_http2_normalize_path) ||
|
shrpx::test_http2_normalize_path) ||
|
||||||
!CU_add_test(pSuite, "http2_rewrite_clean_path",
|
!CU_add_test(pSuite, "http2_rewrite_clean_path",
|
||||||
shrpx::test_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",
|
!CU_add_test(pSuite, "downstream_index_request_headers",
|
||||||
shrpx::test_downstream_index_request_headers) ||
|
shrpx::test_downstream_index_request_headers) ||
|
||||||
!CU_add_test(pSuite, "downstream_index_response_headers",
|
!CU_add_test(pSuite, "downstream_index_response_headers",
|
||||||
|
|
|
@ -1582,81 +1582,32 @@ int Http2Upstream::on_downstream_reset(bool no_retry) {
|
||||||
|
|
||||||
int Http2Upstream::prepare_push_promise(Downstream *downstream) {
|
int Http2Upstream::prepare_push_promise(Downstream *downstream) {
|
||||||
int rv;
|
int rv;
|
||||||
http_parser_url u{};
|
const char *base;
|
||||||
rv = http_parser_parse_url(downstream->get_request_path().c_str(),
|
size_t baselen;
|
||||||
downstream->get_request_path().size(), 0, &u);
|
|
||||||
|
rv = http2::get_pure_path_component(&base, &baselen,
|
||||||
|
downstream->get_request_path());
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return 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()) {
|
for (auto &kv : downstream->get_response_headers()) {
|
||||||
if (kv.token != http2::HD_LINK) {
|
if (kv.token != http2::HD_LINK) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (auto &link :
|
for (auto &link :
|
||||||
http2::parse_link_header(kv.value.c_str(), kv.value.size())) {
|
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;
|
auto uri = link.uri.first;
|
||||||
size_t rellen;
|
auto len = link.uri.second - link.uri.first;
|
||||||
const char *relq = nullptr;
|
|
||||||
size_t relqlen = 0;
|
|
||||||
|
|
||||||
std::string authority, scheme;
|
std::string scheme, authority, path;
|
||||||
http_parser_url v{};
|
|
||||||
rv = http_parser_parse_url(link_url, link_urllen, 0, &v);
|
rv = http2::construct_push_component(scheme, authority, path, base,
|
||||||
|
baselen, uri, len);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
assert(link_urllen);
|
|
||||||
if (link_url[0] == '/') {
|
|
||||||
continue;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scheme.empty()) {
|
if (scheme.empty()) {
|
||||||
scheme = downstream->get_request_http2_scheme();
|
scheme = downstream->get_request_http2_scheme();
|
||||||
|
@ -1666,8 +1617,6 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
|
||||||
authority = downstream->get_request_http2_authority();
|
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);
|
rv = submit_push_promise(scheme, authority, path, downstream);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1736,4 +1685,50 @@ int Http2Upstream::submit_push_promise(const std::string &scheme,
|
||||||
return 0;
|
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
|
} // namespace shrpx
|
||||||
|
|
|
@ -80,6 +80,8 @@ public:
|
||||||
virtual int on_downstream_reset(bool no_retry);
|
virtual int on_downstream_reset(bool no_retry);
|
||||||
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
size_t bodylen);
|
size_t bodylen);
|
||||||
|
virtual int initiate_push(Downstream *downstream, const char *uri,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
bool get_flow_control() const;
|
bool get_flow_control() const;
|
||||||
// Perform HTTP/2 upgrade from |upstream|. On success, this object
|
// Perform HTTP/2 upgrade from |upstream|. On success, this object
|
||||||
|
|
|
@ -1160,4 +1160,9 @@ fail:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int HttpsUpstream::initiate_push(Downstream *downstream, const char *uri,
|
||||||
|
size_t len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -76,6 +76,8 @@ public:
|
||||||
virtual int on_downstream_reset(bool no_retry);
|
virtual int on_downstream_reset(bool no_retry);
|
||||||
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
size_t bodylen);
|
size_t bodylen);
|
||||||
|
virtual int initiate_push(Downstream *downstream, const char *uri,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
void reset_current_header_length();
|
void reset_current_header_length();
|
||||||
void log_response_headers(const std::string &hdrs) const;
|
void log_response_headers(const std::string &hdrs) const;
|
||||||
|
|
|
@ -268,6 +268,22 @@ mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // 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) {
|
void init_request_class(mrb_state *mrb, RClass *module) {
|
||||||
auto request_class =
|
auto request_class =
|
||||||
mrb_define_class_under(mrb, module, "Request", mrb->object_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_ARGS_REQ(2));
|
||||||
mrb_define_method(mrb, request_class, "clear_headers", request_clear_headers,
|
mrb_define_method(mrb, request_class, "clear_headers", request_clear_headers,
|
||||||
MRB_ARGS_NONE());
|
MRB_ARGS_NONE());
|
||||||
|
mrb_define_method(mrb, request_class, "push", request_push, MRB_ARGS_REQ(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mruby
|
} // namespace mruby
|
||||||
|
|
|
@ -1203,4 +1203,9 @@ int SpdyUpstream::on_downstream_reset(bool no_retry) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SpdyUpstream::initiate_push(Downstream *downstream, const char *uri,
|
||||||
|
size_t len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -76,6 +76,8 @@ public:
|
||||||
|
|
||||||
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
size_t bodylen);
|
size_t bodylen);
|
||||||
|
virtual int initiate_push(Downstream *downstream, const char *uri,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
bool get_flow_control() const;
|
bool get_flow_control() const;
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,9 @@ public:
|
||||||
size_t consumed) = 0;
|
size_t consumed) = 0;
|
||||||
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
size_t bodylen) = 0;
|
size_t bodylen) = 0;
|
||||||
|
|
||||||
|
virtual int initiate_push(Downstream *downstream, const char *uri,
|
||||||
|
size_t len) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
Loading…
Reference in New Issue