From db1ee3aa8878ed95f178ae85f84231c026e8d4f7 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 19 Mar 2016 18:33:36 +0900 Subject: [PATCH] nghttpd: Use StringRef --- src/HttpServer.cc | 387 ++++++++++++++++++++++++++-------------------- src/HttpServer.h | 37 ++++- src/nghttpd.cc | 9 ++ src/util.cc | 22 +++ src/util.h | 12 ++ src/util_test.cc | 9 ++ 6 files changed, 300 insertions(+), 176 deletions(-) diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 74e9a9b6..a414d928 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -71,8 +71,9 @@ namespace nghttp2 { namespace { // TODO could be constexpr -constexpr char DEFAULT_HTML[] = "index.html"; -constexpr char NGHTTPD_SERVER[] = "nghttpd nghttp2/" NGHTTP2_VERSION; +constexpr auto DEFAULT_HTML = StringRef::from_lit("index.html"); +constexpr auto NGHTTPD_SERVER = + StringRef::from_lit("nghttpd nghttp2/" NGHTTP2_VERSION); } // namespace namespace { @@ -86,20 +87,6 @@ namespace { void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; } } // namespace -namespace { -template 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() : mime_types_file("/etc/mime.types"), stream_read_timeout(1_min), @@ -442,7 +429,9 @@ void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents) { } // namespace Stream::Stream(Http2Handler *handler, int32_t stream_id) - : handler(handler), + : balloc(1024, 1024), + header{}, + handler(handler), file_ent(nullptr), body_length(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); rtimer.data = this; wtimer.data = this; - - headers.reserve(10); - - http2::init_hdidx(hdidx); } Stream::~Stream() { @@ -466,6 +451,15 @@ Stream::~Stream() { 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(); ev_timer_stop(loop, &rtimer); ev_timer_stop(loop, &wtimer); @@ -909,37 +903,22 @@ int Http2Handler::verify_npn_result() { return -1; } -namespace { -std::string make_trailer_header_value(const Headers &trailer) { - 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, +int Http2Handler::submit_file_response(const StringRef &status, Stream *stream, + time_t last_modified, off_t file_length, const std::string *content_type, nghttp2_data_provider *data_prd) { - std::string content_length = util::utos(file_length); std::string last_modified_str; - auto nva = make_array(http2::make_nv_ls(":status", status), - http2::make_nv_ll("server", NGHTTPD_SERVER), + auto nva = make_array(http2::make_nv_ls_nocopy(":status", status), + http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER), http2::make_nv_ll("cache-control", "max-age=3600"), 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("", "")); size_t nvlen = 4; 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) { last_modified_str = util::http_date(last_modified); @@ -948,52 +927,50 @@ int Http2Handler::submit_file_response(const std::string &status, if (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()) { - 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, data_prd); } -int Http2Handler::submit_response(const std::string &status, int32_t stream_id, - const Headers &headers, +int Http2Handler::submit_response(const StringRef &status, int32_t stream_id, + const HeaderRefs &headers, nghttp2_data_provider *data_prd) { auto nva = std::vector(); nva.reserve(4 + headers.size()); - nva.push_back(http2::make_nv_ls(":status", status)); - nva.push_back(http2::make_nv_ll("server", NGHTTPD_SERVER)); + nva.push_back(http2::make_nv_ls_nocopy(":status", status)); + nva.push_back(http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER)); nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date())); - std::string trailer_names; if (data_prd) { - trailer_names = make_trailer_header_value(get_config()->trailer); + auto &trailer_names = get_config()->trailer_names; 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) { - 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(), data_prd); 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) { - auto nva = make_array(http2::make_nv_ls(":status", status), - http2::make_nv_ll("server", NGHTTPD_SERVER), + auto nva = make_array(http2::make_nv_ls_nocopy(":status", status), + http2::make_nv_ls_nocopy("server", NGHTTPD_SERVER), http2::make_nv_ls("date", sessions_->get_cached_date()), http2::make_nv_ll("", "")); size_t nvlen = 3; - std::string trailer_names; if (data_prd) { - trailer_names = make_trailer_header_value(get_config()->trailer); + auto &trailer_names = get_config()->trailer_names; 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, - const std::string &push_path) { - auto authority = - http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); + const StringRef &push_path) { + auto authority = stream->header.authority; - if (!authority) { - authority = - http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers); + if (authority.empty()) { + authority = stream->header.host; } - auto nva = - make_array(http2::make_nv_ll(":method", "GET"), - http2::make_nv_ls(":path", push_path), - get_config()->no_tls ? http2::make_nv_ll(":scheme", "http") - : http2::make_nv_ll(":scheme", "https"), - http2::make_nv_ls(":authority", authority->value)); + auto scheme = get_config()->no_tls ? StringRef::from_lit("http") + : StringRef::from_lit("https"); + + auto nva = make_array(http2::make_nv_ll(":method", "GET"), + http2::make_nv_ls_nocopy(":path", push_path), + http2::make_nv_ls_nocopy(":scheme", scheme), + http2::make_nv_ls_nocopy(":authority", authority)); auto promised_stream_id = nghttp2_submit_push_promise( 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(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)); 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.read_callback = file_read_callback; - Headers headers; - headers.emplace_back("content-type", "text/html; charset=UTF-8"); - hd->submit_response(status_page->status, stream->stream_id, headers, - &data_prd); + HeaderRefs headers; + headers.emplace_back(StringRef::from_lit("content-type"), + StringRef::from_lit("text/html; charset=UTF-8")); + hd->submit_response(StringRef{status_page->status}, stream->stream_id, + headers, &data_prd); } } // namespace @@ -1161,13 +1144,16 @@ void prepare_echo_response(Stream *stream, Http2Handler *hd) { data_prd.source.fd = stream->file_ent->fd; data_prd.read_callback = file_read_callback; - Headers headers; - headers.emplace_back("nghttpd-response", "echo"); + HeaderRefs headers; + headers.emplace_back(StringRef::from_lit("nghttpd-response"), + StringRef::from_lit("echo")); 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 @@ -1193,27 +1179,31 @@ bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) { namespace { void prepare_redirect_response(Stream *stream, Http2Handler *hd, - const std::string &path, int status) { - auto scheme = - http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers); - auto authority = - http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); - if (!authority) { - authority = - http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers); + const StringRef &path, int status) { + auto scheme = stream->header.scheme; + + auto authority = stream->header.authority; + if (authority.empty()) { + authority = stream->header.host; } - auto redirect_url = scheme->value; - redirect_url += "://"; - redirect_url += authority->value; - redirect_url += path; + size_t len = scheme.size() + str_size("://") + authority.size() + path.size(); + auto iov = make_byte_ref(stream->balloc, len + 1); + auto p = iov.base; + 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 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 @@ -1221,39 +1211,49 @@ namespace { void prepare_response(Stream *stream, Http2Handler *hd, bool allow_push = true) { int rv; - auto pathhdr = - http2::get_header(stream->hdidx, http2::HD__PATH, stream->headers); - if (!pathhdr) { + auto reqpath = stream->header.path; + if (reqpath.empty()) { prepare_status_response(stream, hd, 405); return; } - auto reqpath = pathhdr->value; - auto ims = - get_header(stream->hdidx, http2::HD_IF_MODIFIED_SINCE, stream->headers); + + auto ims = stream->header.ims; time_t last_mod = 0; bool last_mod_found = false; - if (ims) { + if (!ims.empty()) { 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; - if (query_pos != std::string::npos) { + + StringRef raw_path, raw_query; + 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. - if (reqpath.find("nghttpd_do_not_respond_to_req=yes", query_pos) != - std::string::npos) { + if (util::streq_l("nghttpd_do_not_respond_to_req=yes", + StringRef{query_pos, std::end(reqpath)})) { return; } - url = reqpath.substr(0, query_pos); + raw_path = StringRef{std::begin(reqpath), query_pos}; + raw_query = StringRef{query_pos, std::end(reqpath)}; } else { - url = reqpath; + raw_path = reqpath; } auto sessions = hd->get_sessions(); - url = util::percent_decode(std::begin(url), std::end(url)); - if (!util::check_path(url)) { + StringRef path; + 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) { sessions->release_fd(stream->file_ent); stream->file_ent = nullptr; @@ -1261,20 +1261,39 @@ void prepare_response(Stream *stream, Http2Handler *hd, prepare_status_response(stream, hd, 404); return; } - auto push_itr = hd->get_config()->push.find(url); - if (allow_push && push_itr != std::end(hd->get_config()->push)) { - for (auto &push_path : (*push_itr).second) { - rv = hd->submit_push_promise(stream, push_path); - if (rv != 0) { - std::cerr << "nghttp2_submit_push_promise() returned error: " - << nghttp2_strerror(rv) << std::endl; + + if (!hd->get_config()->push.empty()) { + auto push_itr = hd->get_config()->push.find(path.str()); + if (allow_push && push_itr != std::end(hd->get_config()->push)) { + for (auto &push_path : (*push_itr).second) { + rv = hd->submit_push_promise(stream, StringRef{push_path}); + if (rv != 0) { + std::cerr << "nghttp2_submit_push_promise() returned error: " + << nghttp2_strerror(rv) << std::endl; + } } } } - std::string path = hd->get_config()->htdocs + url; - if (path[path.size() - 1] == '/') { - path += DEFAULT_HTML; + std::string file_path; + { + 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) { @@ -1283,10 +1302,10 @@ void prepare_response(Stream *stream, Http2Handler *hd, return; } - auto file_ent = sessions->get_cached_fd(path); + auto file_ent = sessions->get_cached_fd(file_path); 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) { prepare_status_response(stream, hd, 404); @@ -1305,11 +1324,8 @@ void prepare_response(Stream *stream, Http2Handler *hd, if (buf.st_mode & S_IFDIR) { close(file); - if (query_pos == std::string::npos) { - reqpath += '/'; - } else { - reqpath.insert(query_pos, "/"); - } + auto reqpath = concat_string_ref(stream->balloc, raw_path, + StringRef::from_lit("/"), raw_query); prepare_redirect_response(stream, hd, reqpath, 301); @@ -1318,41 +1334,36 @@ void prepare_response(Stream *stream, Http2Handler *hd, const std::string *content_type = nullptr; - if (path[path.size() - 1] == '/') { - static const std::string TEXT_HTML = "text/html"; - content_type = &TEXT_HTML; - } else { - auto ext = path.c_str() + path.size() - 1; - for (; path.c_str() < ext && *ext != '.' && *ext != '/'; --ext) - ; - if (*ext == '.') { - ++ext; + auto ext = file_path.c_str() + file_path.size() - 1; + for (; file_path.c_str() < ext && *ext != '.' && *ext != '/'; --ext) + ; + if (*ext == '.') { + ++ext; - const auto &mime_types = hd->get_config()->mime_types; - auto content_type_itr = mime_types.find(ext); - if (content_type_itr != std::end(mime_types)) { - content_type = &(*content_type_itr).second; - } + const auto &mime_types = hd->get_config()->mime_types; + auto content_type_itr = mime_types.find(ext); + if (content_type_itr != std::end(mime_types)) { + content_type = &(*content_type_itr).second; } } file_ent = sessions->cache_fd( - path, FileEntry(path, buf.st_size, buf.st_mtime, file, content_type, - ev_now(sessions->get_loop()))); + file_path, FileEntry(file_path, buf.st_size, buf.st_mtime, file, + content_type, ev_now(sessions->get_loop()))); } stream->file_ent = file_ent; 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; } - auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD, - stream->headers)->value; + auto method = stream->header.method; 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); return; } @@ -1364,21 +1375,24 @@ void prepare_response(Stream *stream, Http2Handler *hd, data_prd.source.fd = file_ent->fd; data_prd.read_callback = file_read_callback; - hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length, - file_ent->content_type, &data_prd); + hd->submit_file_response(StringRef::from_lit("200"), stream, file_ent->mtime, + file_ent->length, file_ent->content_type, &data_prd); } } // namespace namespace { -int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { +int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame, + nghttp2_rcbuf *name, nghttp2_rcbuf *value, + uint8_t flags, void *user_data) { auto hd = static_cast(user_data); + + auto namebuf = nghttp2_rcbuf_get_buf(name); + auto valuebuf = nghttp2_rcbuf_get_buf(value); + if (hd->get_config()->verbose) { print_session_id(hd->session_id()); - verbose_on_header_callback(session, frame, name, namelen, value, valuelen, - flags, user_data); + verbose_on_header_callback(session, frame, namebuf.base, namebuf.len, + valuebuf.base, valuebuf.len, flags, user_data); } if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { @@ -1389,18 +1403,55 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, 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); 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; } } // namespace @@ -1460,15 +1511,13 @@ int hd_on_frame_recv_callback(nghttp2_session *session, if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - auto expect100 = - http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers); + auto expect100 = stream->header.expect; - 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); } - auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD, - stream->headers)->value; + auto method = stream->header.method; if (hd->get_config()->echo_upload && (method == "POST" || method == "PUT")) { 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( callbacks, on_data_chunk_recv_callback); - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); + nghttp2_session_callbacks_set_on_header_callback2(callbacks, + on_header_callback2); nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); diff --git a/src/HttpServer.h b/src/HttpServer.h index 0690307e..7e4819d9 100644 --- a/src/HttpServer.h +++ b/src/HttpServer.h @@ -46,6 +46,7 @@ #include "http2.h" #include "buffer.h" #include "template.h" +#include "allocator.h" namespace nghttp2 { @@ -53,6 +54,7 @@ struct Config { std::map> push; std::map mime_types; Headers trailer; + std::string trailer_names; std::string htdocs; std::string host; std::string private_key_file; @@ -111,8 +113,29 @@ struct FileEntry { 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 { - Headers headers; + BlockAllocator balloc; + RequestHeader header; Http2Handler *handler; FileEntry *file_ent; ev_timer rtimer; @@ -123,7 +146,6 @@ struct Stream { // headers. size_t header_buffer_size; int32_t stream_id; - http2::HeaderIndex hdidx; bool echo_upload; Stream(Http2Handler *handler, int32_t stream_id); ~Stream(); @@ -143,20 +165,21 @@ public: int connection_made(); 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, const std::string *content_type, 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); - int submit_response(const std::string &status, int32_t stream_id, - const Headers &headers, nghttp2_data_provider *data_prd); + int submit_response(const StringRef &status, int32_t stream_id, + const HeaderRefs &headers, + nghttp2_data_provider *data_prd); 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); diff --git a/src/nghttpd.cc b/src/nghttpd.cc index a2921e88..ef8eec61 100644 --- a/src/nghttpd.cc +++ b/src/nghttpd.cc @@ -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))); struct sigaction act {}; diff --git a/src/util.cc b/src/util.cc index 35fc8275..2351d011 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1363,6 +1363,28 @@ int read_mime_types(std::map &res, 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 nghttp2 diff --git a/src/util.h b/src/util.h index 2d3208fc..37f7b529 100644 --- a/src/util.h +++ b/src/util.h @@ -123,6 +123,8 @@ std::string percent_decode(InputIt first, InputIt last) { return result; } +StringRef percent_decode(BlockAllocator &balloc, const StringRef &src); + // Percent encode |target| if character is not in token or '%'. 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)); } +template +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 bool iends_with(InputIterator1 first1, InputIterator1 last1, 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()); } +template +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); template void inp_strlower(InputIt first, InputIt last) { diff --git a/src/util_test.cc b/src/util_test.cc index 8a8ee181..fac806be 100644 --- a/src/util_test.cc +++ b/src/util_test.cc @@ -157,6 +157,15 @@ void test_util_percent_decode(void) { std::string s = "%66%"; 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) {