nghttpd: Use StringRef

This commit is contained in:
Tatsuhiro Tsujikawa 2016-03-19 18:33:36 +09:00
parent a1e0bd134e
commit db1ee3aa88
6 changed files with 300 additions and 176 deletions

View File

@ -71,8 +71,9 @@ namespace nghttp2 {
namespace { namespace {
// TODO could be constexpr // TODO could be constexpr
constexpr char DEFAULT_HTML[] = "index.html"; constexpr auto DEFAULT_HTML = StringRef::from_lit("index.html");
constexpr char NGHTTPD_SERVER[] = "nghttpd nghttp2/" NGHTTP2_VERSION; constexpr auto NGHTTPD_SERVER =
StringRef::from_lit("nghttpd nghttp2/" NGHTTP2_VERSION);
} // namespace } // namespace
namespace { namespace {
@ -86,20 +87,6 @@ namespace {
void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; } void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; }
} // namespace } // namespace
namespace {
template <typename Array> void append_nv(Stream *stream, const Array &nva) {
for (size_t i = 0; i < nva.size(); ++i) {
auto &nv = nva[i];
auto token = http2::lookup_token(nv.name, nv.namelen);
if (token != -1) {
http2::index_header(stream->hdidx, token, i);
}
http2::add_header(stream->headers, nv.name, nv.namelen, nv.value,
nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
}
}
} // namespace
Config::Config() Config::Config()
: mime_types_file("/etc/mime.types"), : mime_types_file("/etc/mime.types"),
stream_read_timeout(1_min), stream_read_timeout(1_min),
@ -442,7 +429,9 @@ void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents) {
} // namespace } // namespace
Stream::Stream(Http2Handler *handler, int32_t stream_id) Stream::Stream(Http2Handler *handler, int32_t stream_id)
: handler(handler), : balloc(1024, 1024),
header{},
handler(handler),
file_ent(nullptr), file_ent(nullptr),
body_length(0), body_length(0),
body_offset(0), body_offset(0),
@ -454,10 +443,6 @@ Stream::Stream(Http2Handler *handler, int32_t stream_id)
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout); ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
rtimer.data = this; rtimer.data = this;
wtimer.data = this; wtimer.data = this;
headers.reserve(10);
http2::init_hdidx(hdidx);
} }
Stream::~Stream() { Stream::~Stream() {
@ -466,6 +451,15 @@ Stream::~Stream() {
sessions->release_fd(file_ent); sessions->release_fd(file_ent);
} }
auto &rcbuf = header.rcbuf;
nghttp2_rcbuf_decref(rcbuf.method);
nghttp2_rcbuf_decref(rcbuf.scheme);
nghttp2_rcbuf_decref(rcbuf.authority);
nghttp2_rcbuf_decref(rcbuf.host);
nghttp2_rcbuf_decref(rcbuf.path);
nghttp2_rcbuf_decref(rcbuf.ims);
nghttp2_rcbuf_decref(rcbuf.expect);
auto loop = handler->get_loop(); auto loop = handler->get_loop();
ev_timer_stop(loop, &rtimer); ev_timer_stop(loop, &rtimer);
ev_timer_stop(loop, &wtimer); ev_timer_stop(loop, &wtimer);
@ -909,37 +903,22 @@ int Http2Handler::verify_npn_result() {
return -1; return -1;
} }
namespace { int Http2Handler::submit_file_response(const StringRef &status, Stream *stream,
std::string make_trailer_header_value(const Headers &trailer) { time_t last_modified, off_t file_length,
if (trailer.empty()) {
return "";
}
auto trailer_names = trailer[0].name;
for (size_t i = 1; i < trailer.size(); ++i) {
trailer_names += ", ";
trailer_names += trailer[i].name;
}
return trailer_names;
}
} // namespace
int Http2Handler::submit_file_response(const std::string &status,
Stream *stream, time_t last_modified,
off_t file_length,
const std::string *content_type, const std::string *content_type,
nghttp2_data_provider *data_prd) { nghttp2_data_provider *data_prd) {
std::string content_length = util::utos(file_length);
std::string last_modified_str; std::string last_modified_str;
auto nva = make_array(http2::make_nv_ls(":status", status), auto nva = make_array(http2::make_nv_ls_nocopy(":status", status),
http2::make_nv_ll("server", NGHTTPD_SERVER), http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER),
http2::make_nv_ll("cache-control", "max-age=3600"), http2::make_nv_ll("cache-control", "max-age=3600"),
http2::make_nv_ls("date", sessions_->get_cached_date()), http2::make_nv_ls("date", sessions_->get_cached_date()),
http2::make_nv_ll("", ""), http2::make_nv_ll("", ""), http2::make_nv_ll("", ""), http2::make_nv_ll("", ""),
http2::make_nv_ll("", ""), http2::make_nv_ll("", "")); http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
size_t nvlen = 4; size_t nvlen = 4;
if (!get_config()->no_content_length) { if (!get_config()->no_content_length) {
nva[nvlen++] = http2::make_nv_ls("content-length", content_length); nva[nvlen++] = http2::make_nv_ls_nocopy(
"content-length",
util::make_string_ref_uint(stream->balloc, file_length));
} }
if (last_modified != 0) { if (last_modified != 0) {
last_modified_str = util::http_date(last_modified); last_modified_str = util::http_date(last_modified);
@ -948,52 +927,50 @@ int Http2Handler::submit_file_response(const std::string &status,
if (content_type) { if (content_type) {
nva[nvlen++] = http2::make_nv_ls("content-type", *content_type); nva[nvlen++] = http2::make_nv_ls("content-type", *content_type);
} }
auto trailer_names = make_trailer_header_value(get_config()->trailer); auto &trailer_names = get_config()->trailer_names;
if (!trailer_names.empty()) { if (!trailer_names.empty()) {
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names); nva[nvlen++] = http2::make_nv_ls_nocopy("trailer", trailer_names);
} }
return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen, return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
data_prd); data_prd);
} }
int Http2Handler::submit_response(const std::string &status, int32_t stream_id, int Http2Handler::submit_response(const StringRef &status, int32_t stream_id,
const Headers &headers, const HeaderRefs &headers,
nghttp2_data_provider *data_prd) { nghttp2_data_provider *data_prd) {
auto nva = std::vector<nghttp2_nv>(); auto nva = std::vector<nghttp2_nv>();
nva.reserve(4 + headers.size()); nva.reserve(4 + headers.size());
nva.push_back(http2::make_nv_ls(":status", status)); nva.push_back(http2::make_nv_ls_nocopy(":status", status));
nva.push_back(http2::make_nv_ll("server", NGHTTPD_SERVER)); nva.push_back(http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER));
nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date())); nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date()));
std::string trailer_names;
if (data_prd) { if (data_prd) {
trailer_names = make_trailer_header_value(get_config()->trailer); auto &trailer_names = get_config()->trailer_names;
if (!trailer_names.empty()) { if (!trailer_names.empty()) {
nva.push_back(http2::make_nv_ls("trailer", trailer_names)); nva.push_back(http2::make_nv_ls_nocopy("trailer", trailer_names));
} }
} }
for (auto &nv : headers) { for (auto &nv : headers) {
nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index)); nva.push_back(http2::make_nv_nocopy(nv.name, nv.value, nv.no_index));
} }
int r = nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(), int r = nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
data_prd); data_prd);
return r; return r;
} }
int Http2Handler::submit_response(const std::string &status, int32_t stream_id, int Http2Handler::submit_response(const StringRef &status, int32_t stream_id,
nghttp2_data_provider *data_prd) { nghttp2_data_provider *data_prd) {
auto nva = make_array(http2::make_nv_ls(":status", status), auto nva = make_array(http2::make_nv_ls_nocopy(":status", status),
http2::make_nv_ll("server", NGHTTPD_SERVER), http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER),
http2::make_nv_ls("date", sessions_->get_cached_date()), http2::make_nv_ls("date", sessions_->get_cached_date()),
http2::make_nv_ll("", "")); http2::make_nv_ll("", ""));
size_t nvlen = 3; size_t nvlen = 3;
std::string trailer_names;
if (data_prd) { if (data_prd) {
trailer_names = make_trailer_header_value(get_config()->trailer); auto &trailer_names = get_config()->trailer_names;
if (!trailer_names.empty()) { if (!trailer_names.empty()) {
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names); nva[nvlen++] = http2::make_nv_ls_nocopy("trailer", trailer_names);
} }
} }
@ -1009,21 +986,20 @@ int Http2Handler::submit_non_final_response(const std::string &status,
} }
int Http2Handler::submit_push_promise(Stream *stream, int Http2Handler::submit_push_promise(Stream *stream,
const std::string &push_path) { const StringRef &push_path) {
auto authority = auto authority = stream->header.authority;
http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers);
if (!authority) { if (authority.empty()) {
authority = authority = stream->header.host;
http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers);
} }
auto nva = auto scheme = get_config()->no_tls ? StringRef::from_lit("http")
make_array(http2::make_nv_ll(":method", "GET"), : StringRef::from_lit("https");
http2::make_nv_ls(":path", push_path),
get_config()->no_tls ? http2::make_nv_ll(":scheme", "http") auto nva = make_array(http2::make_nv_ll(":method", "GET"),
: http2::make_nv_ll(":scheme", "https"), http2::make_nv_ls_nocopy(":path", push_path),
http2::make_nv_ls(":authority", authority->value)); http2::make_nv_ls_nocopy(":scheme", scheme),
http2::make_nv_ls_nocopy(":authority", authority));
auto promised_stream_id = nghttp2_submit_push_promise( auto promised_stream_id = nghttp2_submit_push_promise(
session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(), session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(),
@ -1035,7 +1011,13 @@ int Http2Handler::submit_push_promise(Stream *stream,
auto promised_stream = make_unique<Stream>(this, promised_stream_id); auto promised_stream = make_unique<Stream>(this, promised_stream_id);
append_nv(promised_stream.get(), nva); auto &promised_header = promised_stream->header;
promised_header.method = StringRef::from_lit("GET");
promised_header.path = push_path;
promised_header.scheme = scheme;
promised_header.authority =
make_string_ref(promised_stream->balloc, authority);
add_stream(promised_stream_id, std::move(promised_stream)); add_stream(promised_stream_id, std::move(promised_stream));
return 0; return 0;
@ -1138,10 +1120,11 @@ void prepare_status_response(Stream *stream, Http2Handler *hd, int status) {
data_prd.source.fd = file_ent->fd; data_prd.source.fd = file_ent->fd;
data_prd.read_callback = file_read_callback; data_prd.read_callback = file_read_callback;
Headers headers; HeaderRefs headers;
headers.emplace_back("content-type", "text/html; charset=UTF-8"); headers.emplace_back(StringRef::from_lit("content-type"),
hd->submit_response(status_page->status, stream->stream_id, headers, StringRef::from_lit("text/html; charset=UTF-8"));
&data_prd); hd->submit_response(StringRef{status_page->status}, stream->stream_id,
headers, &data_prd);
} }
} // namespace } // namespace
@ -1161,13 +1144,16 @@ void prepare_echo_response(Stream *stream, Http2Handler *hd) {
data_prd.source.fd = stream->file_ent->fd; data_prd.source.fd = stream->file_ent->fd;
data_prd.read_callback = file_read_callback; data_prd.read_callback = file_read_callback;
Headers headers; HeaderRefs headers;
headers.emplace_back("nghttpd-response", "echo"); headers.emplace_back(StringRef::from_lit("nghttpd-response"),
StringRef::from_lit("echo"));
if (!hd->get_config()->no_content_length) { if (!hd->get_config()->no_content_length) {
headers.emplace_back("content-length", util::utos(length)); headers.emplace_back(StringRef::from_lit("content-length"),
util::make_string_ref_uint(stream->balloc, length));
} }
hd->submit_response("200", stream->stream_id, headers, &data_prd); hd->submit_response(StringRef::from_lit("200"), stream->stream_id, headers,
&data_prd);
} }
} // namespace } // namespace
@ -1193,27 +1179,31 @@ bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
namespace { namespace {
void prepare_redirect_response(Stream *stream, Http2Handler *hd, void prepare_redirect_response(Stream *stream, Http2Handler *hd,
const std::string &path, int status) { const StringRef &path, int status) {
auto scheme = auto scheme = stream->header.scheme;
http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers);
auto authority = auto authority = stream->header.authority;
http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); if (authority.empty()) {
if (!authority) { authority = stream->header.host;
authority =
http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers);
} }
auto redirect_url = scheme->value; size_t len = scheme.size() + str_size("://") + authority.size() + path.size();
redirect_url += "://"; auto iov = make_byte_ref(stream->balloc, len + 1);
redirect_url += authority->value; auto p = iov.base;
redirect_url += path; p = std::copy(std::begin(scheme), std::end(scheme), p);
p = util::copy_lit(p, "://");
p = std::copy(std::begin(authority), std::end(authority), p);
p = std::copy(std::begin(path), std::end(path), p);
*p = '\0';
auto headers = Headers{{"location", redirect_url}}; auto headers =
HeaderRefs{{StringRef::from_lit("location"), StringRef{iov.base, p}}};
auto sessions = hd->get_sessions(); auto sessions = hd->get_sessions();
auto status_page = sessions->get_server()->get_status_page(status); auto status_page = sessions->get_server()->get_status_page(status);
hd->submit_response(status_page->status, stream->stream_id, headers, nullptr); hd->submit_response(StringRef{status_page->status}, stream->stream_id,
headers, nullptr);
} }
} // namespace } // namespace
@ -1221,39 +1211,49 @@ namespace {
void prepare_response(Stream *stream, Http2Handler *hd, void prepare_response(Stream *stream, Http2Handler *hd,
bool allow_push = true) { bool allow_push = true) {
int rv; int rv;
auto pathhdr = auto reqpath = stream->header.path;
http2::get_header(stream->hdidx, http2::HD__PATH, stream->headers); if (reqpath.empty()) {
if (!pathhdr) {
prepare_status_response(stream, hd, 405); prepare_status_response(stream, hd, 405);
return; return;
} }
auto reqpath = pathhdr->value;
auto ims = auto ims = stream->header.ims;
get_header(stream->hdidx, http2::HD_IF_MODIFIED_SINCE, stream->headers);
time_t last_mod = 0; time_t last_mod = 0;
bool last_mod_found = false; bool last_mod_found = false;
if (ims) { if (!ims.empty()) {
last_mod_found = true; last_mod_found = true;
last_mod = util::parse_http_date(StringRef{ims->value}); last_mod = util::parse_http_date(ims);
} }
auto query_pos = reqpath.find("?");
std::string url; StringRef raw_path, raw_query;
if (query_pos != std::string::npos) { auto query_pos = std::find(std::begin(reqpath), std::end(reqpath), '?');
if (query_pos != std::end(reqpath)) {
// Do not response to this request to allow clients to test timeouts. // Do not response to this request to allow clients to test timeouts.
if (reqpath.find("nghttpd_do_not_respond_to_req=yes", query_pos) != if (util::streq_l("nghttpd_do_not_respond_to_req=yes",
std::string::npos) { StringRef{query_pos, std::end(reqpath)})) {
return; return;
} }
url = reqpath.substr(0, query_pos); raw_path = StringRef{std::begin(reqpath), query_pos};
raw_query = StringRef{query_pos, std::end(reqpath)};
} else { } else {
url = reqpath; raw_path = reqpath;
} }
auto sessions = hd->get_sessions(); auto sessions = hd->get_sessions();
url = util::percent_decode(std::begin(url), std::end(url)); StringRef path;
if (!util::check_path(url)) { if (std::find(std::begin(raw_path), std::end(raw_path), '%') ==
std::end(raw_path)) {
path = raw_path;
} else {
path = util::percent_decode(stream->balloc, raw_path);
}
path = http2::path_join(stream->balloc, StringRef{}, StringRef{}, path,
StringRef{});
if (std::find(std::begin(path), std::end(path), '\\') != std::end(path)) {
if (stream->file_ent) { if (stream->file_ent) {
sessions->release_fd(stream->file_ent); sessions->release_fd(stream->file_ent);
stream->file_ent = nullptr; stream->file_ent = nullptr;
@ -1261,20 +1261,39 @@ void prepare_response(Stream *stream, Http2Handler *hd,
prepare_status_response(stream, hd, 404); prepare_status_response(stream, hd, 404);
return; return;
} }
auto push_itr = hd->get_config()->push.find(url);
if (allow_push && push_itr != std::end(hd->get_config()->push)) { if (!hd->get_config()->push.empty()) {
for (auto &push_path : (*push_itr).second) { auto push_itr = hd->get_config()->push.find(path.str());
rv = hd->submit_push_promise(stream, push_path); if (allow_push && push_itr != std::end(hd->get_config()->push)) {
if (rv != 0) { for (auto &push_path : (*push_itr).second) {
std::cerr << "nghttp2_submit_push_promise() returned error: " rv = hd->submit_push_promise(stream, StringRef{push_path});
<< nghttp2_strerror(rv) << std::endl; if (rv != 0) {
std::cerr << "nghttp2_submit_push_promise() returned error: "
<< nghttp2_strerror(rv) << std::endl;
}
} }
} }
} }
std::string path = hd->get_config()->htdocs + url; std::string file_path;
if (path[path.size() - 1] == '/') { {
path += DEFAULT_HTML; auto len = hd->get_config()->htdocs.size() + path.size();
auto trailing_slash = path[path.size() - 1] == '/';
if (trailing_slash) {
len += DEFAULT_HTML.size();
}
file_path.resize(len);
auto p = &file_path[0];
auto &htdocs = hd->get_config()->htdocs;
p = std::copy(std::begin(htdocs), std::end(htdocs), p);
p = std::copy(std::begin(path), std::end(path), p);
if (trailing_slash) {
p = std::copy(std::begin(DEFAULT_HTML), std::end(DEFAULT_HTML), p);
}
} }
if (stream->echo_upload) { if (stream->echo_upload) {
@ -1283,10 +1302,10 @@ void prepare_response(Stream *stream, Http2Handler *hd,
return; return;
} }
auto file_ent = sessions->get_cached_fd(path); auto file_ent = sessions->get_cached_fd(file_path);
if (file_ent == nullptr) { if (file_ent == nullptr) {
int file = open(path.c_str(), O_RDONLY | O_BINARY); int file = open(file_path.c_str(), O_RDONLY | O_BINARY);
if (file == -1) { if (file == -1) {
prepare_status_response(stream, hd, 404); prepare_status_response(stream, hd, 404);
@ -1305,11 +1324,8 @@ void prepare_response(Stream *stream, Http2Handler *hd,
if (buf.st_mode & S_IFDIR) { if (buf.st_mode & S_IFDIR) {
close(file); close(file);
if (query_pos == std::string::npos) { auto reqpath = concat_string_ref(stream->balloc, raw_path,
reqpath += '/'; StringRef::from_lit("/"), raw_query);
} else {
reqpath.insert(query_pos, "/");
}
prepare_redirect_response(stream, hd, reqpath, 301); prepare_redirect_response(stream, hd, reqpath, 301);
@ -1318,41 +1334,36 @@ void prepare_response(Stream *stream, Http2Handler *hd,
const std::string *content_type = nullptr; const std::string *content_type = nullptr;
if (path[path.size() - 1] == '/') { auto ext = file_path.c_str() + file_path.size() - 1;
static const std::string TEXT_HTML = "text/html"; for (; file_path.c_str() < ext && *ext != '.' && *ext != '/'; --ext)
content_type = &TEXT_HTML; ;
} else { if (*ext == '.') {
auto ext = path.c_str() + path.size() - 1; ++ext;
for (; path.c_str() < ext && *ext != '.' && *ext != '/'; --ext)
;
if (*ext == '.') {
++ext;
const auto &mime_types = hd->get_config()->mime_types; const auto &mime_types = hd->get_config()->mime_types;
auto content_type_itr = mime_types.find(ext); auto content_type_itr = mime_types.find(ext);
if (content_type_itr != std::end(mime_types)) { if (content_type_itr != std::end(mime_types)) {
content_type = &(*content_type_itr).second; content_type = &(*content_type_itr).second;
}
} }
} }
file_ent = sessions->cache_fd( file_ent = sessions->cache_fd(
path, FileEntry(path, buf.st_size, buf.st_mtime, file, content_type, file_path, FileEntry(file_path, buf.st_size, buf.st_mtime, file,
ev_now(sessions->get_loop()))); content_type, ev_now(sessions->get_loop())));
} }
stream->file_ent = file_ent; stream->file_ent = file_ent;
if (last_mod_found && file_ent->mtime <= last_mod) { if (last_mod_found && file_ent->mtime <= last_mod) {
hd->submit_response("304", stream->stream_id, nullptr); hd->submit_response(StringRef::from_lit("304"), stream->stream_id, nullptr);
return; return;
} }
auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD, auto method = stream->header.method;
stream->headers)->value;
if (method == "HEAD") { if (method == "HEAD") {
hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length, hd->submit_file_response(StringRef::from_lit("200"), stream,
file_ent->mtime, file_ent->length,
file_ent->content_type, nullptr); file_ent->content_type, nullptr);
return; return;
} }
@ -1364,21 +1375,24 @@ void prepare_response(Stream *stream, Http2Handler *hd,
data_prd.source.fd = file_ent->fd; data_prd.source.fd = file_ent->fd;
data_prd.read_callback = file_read_callback; data_prd.read_callback = file_read_callback;
hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length, hd->submit_file_response(StringRef::from_lit("200"), stream, file_ent->mtime,
file_ent->content_type, &data_prd); file_ent->length, file_ent->content_type, &data_prd);
} }
} // namespace } // namespace
namespace { namespace {
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen, nghttp2_rcbuf *name, nghttp2_rcbuf *value,
const uint8_t *value, size_t valuelen, uint8_t flags, uint8_t flags, void *user_data) {
void *user_data) {
auto hd = static_cast<Http2Handler *>(user_data); auto hd = static_cast<Http2Handler *>(user_data);
auto namebuf = nghttp2_rcbuf_get_buf(name);
auto valuebuf = nghttp2_rcbuf_get_buf(value);
if (hd->get_config()->verbose) { if (hd->get_config()->verbose) {
print_session_id(hd->session_id()); print_session_id(hd->session_id());
verbose_on_header_callback(session, frame, name, namelen, value, valuelen, verbose_on_header_callback(session, frame, namebuf.base, namebuf.len,
flags, user_data); valuebuf.base, valuebuf.len, flags, user_data);
} }
if (frame->hd.type != NGHTTP2_HEADERS || if (frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) { frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
@ -1389,18 +1403,55 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0; return 0;
} }
if (stream->header_buffer_size + namelen + valuelen > 64_k) { if (stream->header_buffer_size + namebuf.len + valuebuf.len > 64_k) {
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
return 0; return 0;
} }
stream->header_buffer_size += namelen + valuelen; stream->header_buffer_size += namebuf.len + valuebuf.len;
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(namebuf.base, namebuf.len);
auto &header = stream->header;
switch (token) {
case http2::HD__METHOD:
header.method = StringRef{valuebuf.base, valuebuf.len};
header.rcbuf.method = value;
nghttp2_rcbuf_incref(value);
break;
case http2::HD__SCHEME:
header.scheme = StringRef{valuebuf.base, valuebuf.len};
header.rcbuf.scheme = value;
nghttp2_rcbuf_incref(value);
break;
case http2::HD__AUTHORITY:
header.authority = StringRef{valuebuf.base, valuebuf.len};
header.rcbuf.authority = value;
nghttp2_rcbuf_incref(value);
break;
case http2::HD_HOST:
header.host = StringRef{valuebuf.base, valuebuf.len};
header.rcbuf.host = value;
nghttp2_rcbuf_incref(value);
break;
case http2::HD__PATH:
header.path = StringRef{valuebuf.base, valuebuf.len};
header.rcbuf.path = value;
nghttp2_rcbuf_incref(value);
break;
case http2::HD_IF_MODIFIED_SINCE:
header.ims = StringRef{valuebuf.base, valuebuf.len};
header.rcbuf.ims = value;
nghttp2_rcbuf_incref(value);
break;
case http2::HD_EXPECT:
header.expect = StringRef{valuebuf.base, valuebuf.len};
header.rcbuf.expect = value;
nghttp2_rcbuf_incref(value);
break;
}
http2::index_header(stream->hdidx, token, stream->headers.size());
http2::add_header(stream->headers, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0; return 0;
} }
} // namespace } // namespace
@ -1460,15 +1511,13 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
auto expect100 = auto expect100 = stream->header.expect;
http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers);
if (expect100 && util::strieq_l("100-continue", expect100->value)) { if (util::strieq_l("100-continue", expect100)) {
hd->submit_non_final_response("100", frame->hd.stream_id); hd->submit_non_final_response("100", frame->hd.stream_id);
} }
auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD, auto method = stream->header.method;
stream->headers)->value;
if (hd->get_config()->echo_upload && if (hd->get_config()->echo_upload &&
(method == "POST" || method == "PUT")) { (method == "POST" || method == "PUT")) {
if (!prepare_upload_temp_store(stream, hd)) { if (!prepare_upload_temp_store(stream, hd)) {
@ -1697,8 +1746,8 @@ void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) {
nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback); callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks, nghttp2_session_callbacks_set_on_header_callback2(callbacks,
on_header_callback); on_header_callback2);
nghttp2_session_callbacks_set_on_begin_headers_callback( nghttp2_session_callbacks_set_on_begin_headers_callback(
callbacks, on_begin_headers_callback); callbacks, on_begin_headers_callback);

View File

@ -46,6 +46,7 @@
#include "http2.h" #include "http2.h"
#include "buffer.h" #include "buffer.h"
#include "template.h" #include "template.h"
#include "allocator.h"
namespace nghttp2 { namespace nghttp2 {
@ -53,6 +54,7 @@ struct Config {
std::map<std::string, std::vector<std::string>> push; std::map<std::string, std::vector<std::string>> push;
std::map<std::string, std::string> mime_types; std::map<std::string, std::string> mime_types;
Headers trailer; Headers trailer;
std::string trailer_names;
std::string htdocs; std::string htdocs;
std::string host; std::string host;
std::string private_key_file; std::string private_key_file;
@ -111,8 +113,29 @@ struct FileEntry {
bool stale; bool stale;
}; };
struct RequestHeader {
StringRef method;
StringRef scheme;
StringRef authority;
StringRef host;
StringRef path;
StringRef ims;
StringRef expect;
struct {
nghttp2_rcbuf *method;
nghttp2_rcbuf *scheme;
nghttp2_rcbuf *authority;
nghttp2_rcbuf *host;
nghttp2_rcbuf *path;
nghttp2_rcbuf *ims;
nghttp2_rcbuf *expect;
} rcbuf;
};
struct Stream { struct Stream {
Headers headers; BlockAllocator balloc;
RequestHeader header;
Http2Handler *handler; Http2Handler *handler;
FileEntry *file_ent; FileEntry *file_ent;
ev_timer rtimer; ev_timer rtimer;
@ -123,7 +146,6 @@ struct Stream {
// headers. // headers.
size_t header_buffer_size; size_t header_buffer_size;
int32_t stream_id; int32_t stream_id;
http2::HeaderIndex hdidx;
bool echo_upload; bool echo_upload;
Stream(Http2Handler *handler, int32_t stream_id); Stream(Http2Handler *handler, int32_t stream_id);
~Stream(); ~Stream();
@ -143,20 +165,21 @@ public:
int connection_made(); int connection_made();
int verify_npn_result(); int verify_npn_result();
int submit_file_response(const std::string &status, Stream *stream, int submit_file_response(const StringRef &status, Stream *stream,
time_t last_modified, off_t file_length, time_t last_modified, off_t file_length,
const std::string *content_type, const std::string *content_type,
nghttp2_data_provider *data_prd); nghttp2_data_provider *data_prd);
int submit_response(const std::string &status, int32_t stream_id, int submit_response(const StringRef &status, int32_t stream_id,
nghttp2_data_provider *data_prd); nghttp2_data_provider *data_prd);
int submit_response(const std::string &status, int32_t stream_id, int submit_response(const StringRef &status, int32_t stream_id,
const Headers &headers, nghttp2_data_provider *data_prd); const HeaderRefs &headers,
nghttp2_data_provider *data_prd);
int submit_non_final_response(const std::string &status, int32_t stream_id); int submit_non_final_response(const std::string &status, int32_t stream_id);
int submit_push_promise(Stream *stream, const std::string &push_path); int submit_push_promise(Stream *stream, const StringRef &push_path);
int submit_rst_stream(Stream *stream, uint32_t error_code); int submit_rst_stream(Stream *stream, uint32_t error_code);

View File

@ -420,6 +420,15 @@ int main(int argc, char **argv) {
} }
} }
auto &trailer_names = config.trailer_names;
for (auto &h : config.trailer) {
trailer_names += h.name;
trailer_names += ", ";
}
if (trailer_names.size() >= 2) {
trailer_names.resize(trailer_names.size() - 2);
}
set_color_output(color || isatty(fileno(stdout))); set_color_output(color || isatty(fileno(stdout)));
struct sigaction act {}; struct sigaction act {};

View File

@ -1363,6 +1363,28 @@ int read_mime_types(std::map<std::string, std::string> &res,
return 0; return 0;
} }
StringRef percent_decode(BlockAllocator &balloc, const StringRef &src) {
auto iov = make_byte_ref(balloc, src.size() * 3 + 1);
auto p = iov.base;
for (auto first = std::begin(src); first != std::end(src); ++first) {
if (*first != '%') {
*p++ = *first;
continue;
}
if (first + 1 != std::end(src) && first + 2 != std::end(src) &&
is_hex_digit(*(first + 1)) && is_hex_digit(*(first + 2))) {
*p++ = (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2));
first += 2;
continue;
}
*p++ = *first;
}
*p = '\0';
return StringRef{iov.base, p};
}
} // namespace util } // namespace util
} // namespace nghttp2 } // namespace nghttp2

View File

@ -123,6 +123,8 @@ std::string percent_decode(InputIt first, InputIt last) {
return result; return result;
} }
StringRef percent_decode(BlockAllocator &balloc, const StringRef &src);
// Percent encode |target| if character is not in token or '%'. // Percent encode |target| if character is not in token or '%'.
std::string percent_encode_token(const std::string &target); std::string percent_encode_token(const std::string &target);
@ -244,6 +246,11 @@ inline bool ends_with(const std::string &a, const std::string &b) {
return ends_with(std::begin(a), std::end(a), std::begin(b), std::end(b)); return ends_with(std::begin(a), std::end(a), std::begin(b), std::end(b));
} }
template <typename CharT, size_t N>
bool ends_with_l(const StringRef &a, const CharT(&b)[N]) {
return ends_with(std::begin(a), std::end(a), b, b + N - 1);
}
template <typename InputIterator1, typename InputIterator2> template <typename InputIterator1, typename InputIterator2>
bool iends_with(InputIterator1 first1, InputIterator1 last1, bool iends_with(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2) { InputIterator2 first2, InputIterator2 last2) {
@ -356,6 +363,11 @@ bool streq_l(const CharT(&a)[N], const std::string &b) {
return streq(a, N - 1, std::begin(b), b.size()); return streq(a, N - 1, std::begin(b), b.size());
} }
template <typename CharT, size_t N>
bool streq_l(const CharT(&a)[N], const StringRef &b) {
return streq(a, N - 1, std::begin(b), b.size());
}
bool strifind(const char *a, const char *b); bool strifind(const char *a, const char *b);
template <typename InputIt> void inp_strlower(InputIt first, InputIt last) { template <typename InputIt> void inp_strlower(InputIt first, InputIt last) {

View File

@ -157,6 +157,15 @@ void test_util_percent_decode(void) {
std::string s = "%66%"; std::string s = "%66%";
CU_ASSERT("f%" == util::percent_decode(std::begin(s), std::end(s))); CU_ASSERT("f%" == util::percent_decode(std::begin(s), std::end(s)));
} }
BlockAllocator balloc(1024, 1024);
CU_ASSERT("foobar" == util::percent_decode(
balloc, StringRef::from_lit("%66%6F%6f%62%61%72")));
CU_ASSERT("f%6" ==
util::percent_decode(balloc, StringRef::from_lit("%66%6")));
CU_ASSERT("f%" == util::percent_decode(balloc, StringRef::from_lit("%66%")));
} }
void test_util_quote_string(void) { void test_util_quote_string(void) {