src: Add utility APIs to asio_http2.h; add asio-sv2 example to serve files
This commit is contained in:
parent
99ca15cae0
commit
fd07f5e142
|
@ -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
|
||||
|
||||
|
|
|
@ -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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
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 <port> <threads> <doc-root> "
|
||||
<< "<private-key-file> <cert-file>\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<request> req, std::shared_ptr<response> 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<header>();
|
||||
|
||||
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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<int>(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
|
||||
|
|
|
@ -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
|
||||
|
|
10
src/util.cc
10
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue