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
|
if ENABLE_ASIO_LIB
|
||||||
|
|
||||||
noinst_PROGRAMS += asio-sv
|
noinst_PROGRAMS += asio-sv asio-sv2
|
||||||
|
|
||||||
asio_sv_SOURCES = asio-sv.cc
|
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
||||||
asio_sv_CPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
ASIOLDFLAGS = \
|
||||||
asio_sv_LDFLAGS = \
|
|
||||||
@JEMALLOC_LIBS@ \
|
@JEMALLOC_LIBS@ \
|
||||||
${BOOST_LDFLAGS} \
|
${BOOST_LDFLAGS} \
|
||||||
${BOOST_SYSTEM_LIB}
|
${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
|
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;
|
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 {
|
namespace {
|
||||||
void prepare_status_response(Stream *stream, Http2Handler *hd,
|
void prepare_status_response(Stream *stream, Http2Handler *hd,
|
||||||
const std::string& status)
|
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 = url.substr(0, query_pos);
|
||||||
}
|
}
|
||||||
url = util::percentDecode(url.begin(), url.end());
|
url = util::percentDecode(url.begin(), url.end());
|
||||||
if(!check_url(url)) {
|
if(!util::check_path(url)) {
|
||||||
prepare_status_response(stream, hd, STATUS_404);
|
prepare_status_response(stream, hd, STATUS_404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,11 @@ read_cb file_reader(const std::string& path)
|
||||||
return read_cb();
|
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);
|
auto d = defer_shared(static_cast<int>(fd), close);
|
||||||
|
|
||||||
return [fd, d](uint8_t *buf, size_t len) -> read_cb::result_type
|
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 server
|
||||||
|
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
|
|
|
@ -168,6 +168,23 @@ private:
|
||||||
// |path|. This can be passed to response::end().
|
// |path|. This can be passed to response::end().
|
||||||
read_cb file_reader(const std::string& path);
|
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 server
|
||||||
|
|
||||||
} // namespace asio_http2
|
} // 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;
|
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 util
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -464,6 +464,12 @@ std::string ascii_dump(const uint8_t *data, size_t len);
|
||||||
// it.
|
// it.
|
||||||
char* get_exec_path(int argc, char **const argv, const char *cwd);
|
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 util
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
Loading…
Reference in New Issue