diff --git a/examples/Makefile.am b/examples/Makefile.am index 7b184a7e..57cd092a 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -51,15 +51,24 @@ deflate_SOURCES = deflate.c if ENABLE_ASIO_LIB -noinst_PROGRAMS += asio-sv +noinst_PROGRAMS += asio-sv asio-sv2 -asio_sv_SOURCES = asio-sv.cc -asio_sv_CPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS} -asio_sv_LDFLAGS = \ +ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS} +ASIOLDFLAGS = \ @JEMALLOC_LIBS@ \ ${BOOST_LDFLAGS} \ ${BOOST_SYSTEM_LIB} -asio_sv_LDADD = $(top_builddir)/src/libnghttp2_asio.la +ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la + +asio_sv_SOURCES = asio-sv.cc +asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} +asio_sv_LDFLAGS = ${ASIOLDFLAGS} +asio_sv_LDADD = ${ASIOLDADD} + +asio_sv2_SOURCES = asio-sv2.cc +asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS} +asio_sv2_LDFLAGS = ${ASIOLDFLAGS} +asio_sv2_LDADD = ${ASIOLDADD} endif # ENABLE_ASIO_LIB diff --git a/examples/asio-sv2.cc b/examples/asio-sv2.cc new file mode 100644 index 00000000..645e1d59 --- /dev/null +++ b/examples/asio-sv2.cc @@ -0,0 +1,110 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include +#include +#include +#include +#include +#include + +#include + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::server; + +int main(int argc, char* argv[]) +{ + try { + // Check command line arguments. + if (argc < 4) { + std::cerr << "Usage: asio-sv2 " + << " \n"; + return 1; + } + + uint16_t port = std::stoi(argv[1]); + std::size_t num_threads = std::stoi(argv[2]); + std::string docroot = argv[3]; + + http2 server; + + server.num_threads(num_threads); + + if(argc >= 6) { + server.tls(argv[4], argv[5]); + } + + server.listen + ("*", port, + [&docroot](std::shared_ptr req, std::shared_ptr res) + { + auto path = percent_decode(req->path()); + if(!check_path(path)) { + res->write_head(404); + res->end(); + return; + } + + if(path == "/") { + path = "/index.html"; + } + + path = docroot + path; + auto fd = open(path.c_str(), O_RDONLY); + if(fd == -1) { + res->write_head(404); + res->end(); + return; + } + + auto headers = std::vector
(); + + struct stat stbuf; + if(stat(path.c_str(), &stbuf) == 0) { + headers.push_back + (header{"content-length", std::to_string(stbuf.st_size)}); + headers.push_back + (header{"last-modified", http_date(stbuf.st_mtim.tv_sec)}); + } + res->write_head(200, std::move(headers)); + res->end(file_reader_from_fd(fd)); + }); + } catch (std::exception& e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/HttpServer.cc b/src/HttpServer.cc index f6f63712..25ff748b 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -796,18 +796,6 @@ ssize_t file_read_callback return nread; } -namespace { -bool check_url(const std::string& url) -{ - // We don't like '\' in url. - return !url.empty() && url[0] == '/' && - url.find('\\') == std::string::npos && - url.find("/../") == std::string::npos && - url.find("/./") == std::string::npos && - !util::endsWith(url, "/..") && !util::endsWith(url, "/."); -} -} // namespace - namespace { void prepare_status_response(Stream *stream, Http2Handler *hd, const std::string& status) @@ -885,7 +873,7 @@ void prepare_response(Stream *stream, Http2Handler *hd, bool allow_push = true) url = url.substr(0, query_pos); } url = util::percentDecode(url.begin(), url.end()); - if(!check_url(url)) { + if(!util::check_path(url)) { prepare_status_response(stream, hd, STATUS_404); return; } diff --git a/src/asio_http2_impl.cc b/src/asio_http2_impl.cc index 43255e37..ded3b1d3 100644 --- a/src/asio_http2_impl.cc +++ b/src/asio_http2_impl.cc @@ -155,6 +155,11 @@ read_cb file_reader(const std::string& path) return read_cb(); } + return file_reader_from_fd(fd); +} + +read_cb file_reader_from_fd(int fd) +{ auto d = defer_shared(static_cast(fd), close); return [fd, d](uint8_t *buf, size_t len) -> read_cb::result_type @@ -174,6 +179,21 @@ read_cb file_reader(const std::string& path) }; } +bool check_path(const std::string& path) +{ + return util::check_path(path); +} + +std::string percent_decode(const std::string& s) +{ + return util::percentDecode(std::begin(s), std::end(s)); +} + +std::string http_date(time_t t) +{ + return util::http_date(t); +} + } // namespace server } // namespace asio_http2 diff --git a/src/includes/nghttp2/asio_http2.h b/src/includes/nghttp2/asio_http2.h index 6a7cee54..c7fb4fa1 100644 --- a/src/includes/nghttp2/asio_http2.h +++ b/src/includes/nghttp2/asio_http2.h @@ -168,6 +168,23 @@ private: // |path|. This can be passed to response::end(). read_cb file_reader(const std::string& path); +// Like file_reader(const std::string&), but it takes opened file +// descriptor. The passed descriptor will be closed when returned +// function object is destroyed. +read_cb file_reader_from_fd(int fd); + +// Validates path so that it does not contain directory traversal +// vector. Returns true if path is safe. The |path| must start with +// "/" otherwise returns false. This function should be called after +// percent-decode was performed. +bool check_path(const std::string& path); + +// Performs percent-decode against string |s|. +std::string percent_decode(const std::string& s); + +// Returns HTTP date representation of current posix time |t|. +std::string http_date(time_t t); + } // namespace server } // namespace asio_http2 diff --git a/src/util.cc b/src/util.cc index a4b3e445..14a7ed07 100644 --- a/src/util.cc +++ b/src/util.cc @@ -593,6 +593,16 @@ char* get_exec_path(int argc, char **const argv, const char *cwd) return path; } +bool check_path(const std::string& path) +{ + // We don't like '\' in path. + return !path.empty() && path[0] == '/' && + path.find('\\') == std::string::npos && + path.find("/../") == std::string::npos && + path.find("/./") == std::string::npos && + !util::endsWith(path, "/..") && !util::endsWith(path, "/."); +} + } // namespace util } // namespace nghttp2 diff --git a/src/util.h b/src/util.h index 493c2366..97fb13e3 100644 --- a/src/util.h +++ b/src/util.h @@ -464,6 +464,12 @@ std::string ascii_dump(const uint8_t *data, size_t len); // it. char* get_exec_path(int argc, char **const argv, const char *cwd); +// Validates path so that it does not contain directory traversal +// vector. Returns true if path is safe. The |path| must start with +// "/" otherwise returns false. This function should be called after +// percent-decode was performed. +bool check_path(const std::string& path); + } // namespace util } // namespace nghttp2