asio: Add serve_mux class to route incoming requet by path
serve_mux is direct port of ServeMux from go
This commit is contained in:
parent
8accf3898a
commit
e4ce595ebb
|
@ -62,10 +62,16 @@ int main(int argc, char *argv[]) {
|
||||||
server.tls(argv[3], argv[4]);
|
server.tls(argv[3], argv[4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.listen("*", port, [](const request &req, const response &res) {
|
server.handle("/", [](const request &req, const response &res) {
|
||||||
res.write_head(200, {{"foo", {"bar"}}});
|
res.write_head(200, {{"foo", {"bar"}}});
|
||||||
res.end("hello, world");
|
res.end("hello, world");
|
||||||
});
|
});
|
||||||
|
server.handle("/secret/", [](const request &req, const response &res) {
|
||||||
|
res.write_head(200);
|
||||||
|
res.end("under construction!");
|
||||||
|
});
|
||||||
|
server.listen("*", port);
|
||||||
|
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
std::cerr << "exception: " << e.what() << "\n";
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,7 @@ int main(int argc, char *argv[]) {
|
||||||
server.tls(argv[4], argv[5]);
|
server.tls(argv[4], argv[5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.listen("*", port,
|
server.handle("/", [&docroot](const request &req, const response &res) {
|
||||||
[&docroot](const request &req, const response &res) {
|
|
||||||
auto path = percent_decode(req.uri().path);
|
auto path = percent_decode(req.uri().path);
|
||||||
if (!check_path(path)) {
|
if (!check_path(path)) {
|
||||||
res.write_head(404);
|
res.write_head(404);
|
||||||
|
@ -99,6 +98,9 @@ int main(int argc, char *argv[]) {
|
||||||
res.write_head(200, std::move(header));
|
res.write_head(200, std::move(header));
|
||||||
res.end(file_reader_from_fd(fd));
|
res.end(file_reader_from_fd(fd));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.listen("*", port);
|
||||||
|
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
std::cerr << "exception: " << e.what() << "\n";
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,6 +181,7 @@ libnghttp2_asio_la_SOURCES = \
|
||||||
util.cc util.h http2.cc http2.h \
|
util.cc util.h http2.cc http2.h \
|
||||||
ssl.cc ssl.h \
|
ssl.cc ssl.h \
|
||||||
asio_common.cc asio_common.h \
|
asio_common.cc asio_common.h \
|
||||||
|
asio_server_serve_mux.cc asio_server_serve_mux.h \
|
||||||
asio_client_session.cc \
|
asio_client_session.cc \
|
||||||
asio_client_session_impl.cc asio_client_session_impl.h \
|
asio_client_session_impl.cc asio_client_session_impl.h \
|
||||||
asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \
|
asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include <nghttp2/asio_http2_server.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
#include "asio_http2_handler.h"
|
#include "asio_http2_handler.h"
|
||||||
|
#include "asio_server_serve_mux.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
|
@ -62,14 +63,14 @@ class connection : public std::enable_shared_from_this<connection<socket_type>>,
|
||||||
public:
|
public:
|
||||||
/// Construct a connection with the given io_service.
|
/// Construct a connection with the given io_service.
|
||||||
template <typename... SocketArgs>
|
template <typename... SocketArgs>
|
||||||
explicit connection(request_cb cb, SocketArgs &&... args)
|
explicit connection(serve_mux &mux, SocketArgs &&... args)
|
||||||
: socket_(std::forward<SocketArgs>(args)...), request_cb_(std::move(cb)),
|
: socket_(std::forward<SocketArgs>(args)...), mux_(mux), writing_(false) {
|
||||||
writing_(false) {}
|
}
|
||||||
|
|
||||||
/// Start the first asynchronous operation for the connection.
|
/// Start the first asynchronous operation for the connection.
|
||||||
void start() {
|
void start() {
|
||||||
handler_ = std::make_shared<http2_handler>(
|
handler_ = std::make_shared<http2_handler>(socket_.get_io_service(),
|
||||||
socket_.get_io_service(), [this]() { do_write(); }, request_cb_);
|
[this]() { do_write(); }, mux_);
|
||||||
if (handler_->start() != 0) {
|
if (handler_->start() != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +148,7 @@ public:
|
||||||
private:
|
private:
|
||||||
socket_type socket_;
|
socket_type socket_;
|
||||||
|
|
||||||
request_cb request_cb_;
|
serve_mux &mux_;
|
||||||
|
|
||||||
std::shared_ptr<http2_handler> handler_;
|
std::shared_ptr<http2_handler> handler_;
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "asio_common.h"
|
#include "asio_common.h"
|
||||||
|
#include "asio_server_serve_mux.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "template.h"
|
#include "template.h"
|
||||||
|
@ -412,8 +413,8 @@ int on_frame_not_send_callback(nghttp2_session *session,
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
http2_handler::http2_handler(boost::asio::io_service &io_service,
|
http2_handler::http2_handler(boost::asio::io_service &io_service,
|
||||||
connection_write writefun, request_cb cb)
|
connection_write writefun, serve_mux &mux)
|
||||||
: writefun_(writefun), request_cb_(std::move(cb)), io_service_(io_service),
|
: writefun_(writefun), mux_(mux), io_service_(io_service),
|
||||||
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
|
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
|
||||||
|
|
||||||
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
|
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
|
||||||
|
@ -486,7 +487,8 @@ http2_stream *http2_handler::find_stream(int32_t stream_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void http2_handler::call_on_request(http2_stream &stream) {
|
void http2_handler::call_on_request(http2_stream &stream) {
|
||||||
request_cb_(stream.request(), stream.response());
|
auto cb = mux_.handler(stream.request().impl());
|
||||||
|
cb(stream.request(), stream.response());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http2_handler::should_stop() const {
|
bool http2_handler::should_stop() const {
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace server {
|
||||||
|
|
||||||
class http2_handler;
|
class http2_handler;
|
||||||
class http2_stream;
|
class http2_stream;
|
||||||
|
class serve_mux;
|
||||||
|
|
||||||
class request_impl {
|
class request_impl {
|
||||||
public:
|
public:
|
||||||
|
@ -141,7 +142,7 @@ typedef std::function<void(void)> connection_write;
|
||||||
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
||||||
public:
|
public:
|
||||||
http2_handler(boost::asio::io_service &io_service, connection_write writefun,
|
http2_handler(boost::asio::io_service &io_service, connection_write writefun,
|
||||||
request_cb cb);
|
serve_mux &mux);
|
||||||
|
|
||||||
~http2_handler();
|
~http2_handler();
|
||||||
|
|
||||||
|
@ -232,7 +233,7 @@ public:
|
||||||
private:
|
private:
|
||||||
std::map<int32_t, std::shared_ptr<http2_stream>> streams_;
|
std::map<int32_t, std::shared_ptr<http2_stream>> streams_;
|
||||||
connection_write writefun_;
|
connection_write writefun_;
|
||||||
request_cb request_cb_;
|
serve_mux &mux_;
|
||||||
boost::asio::io_service &io_service_;
|
boost::asio::io_service &io_service_;
|
||||||
nghttp2_session *session_;
|
nghttp2_session *session_;
|
||||||
const uint8_t *buf_;
|
const uint8_t *buf_;
|
||||||
|
|
|
@ -41,8 +41,8 @@ http2::http2() : impl_(make_unique<http2_impl>()) {}
|
||||||
|
|
||||||
http2::~http2() {}
|
http2::~http2() {}
|
||||||
|
|
||||||
void http2::listen(const std::string &address, uint16_t port, request_cb cb) {
|
void http2::listen(const std::string &address, uint16_t port) {
|
||||||
impl_->listen(address, port, std::move(cb));
|
impl_->listen(address, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
||||||
|
@ -53,6 +53,10 @@ void http2::tls(std::string private_key_file, std::string certificate_file) {
|
||||||
|
|
||||||
void http2::backlog(int backlog) { impl_->backlog(backlog); }
|
void http2::backlog(int backlog) { impl_->backlog(backlog); }
|
||||||
|
|
||||||
|
bool http2::handle(std::string pattern, request_cb cb) {
|
||||||
|
return impl_->handle(std::move(pattern), std::move(cb));
|
||||||
|
}
|
||||||
|
|
||||||
http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {}
|
http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -62,8 +66,7 @@ std::vector<unsigned char> &get_alpn_token() {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void http2_impl::listen(const std::string &address, uint16_t port,
|
void http2_impl::listen(const std::string &address, uint16_t port) {
|
||||||
request_cb cb) {
|
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx;
|
std::unique_ptr<boost::asio::ssl::context> ssl_ctx;
|
||||||
|
|
||||||
if (!private_key_file_.empty() && !certificate_file_.empty()) {
|
if (!private_key_file_.empty() && !certificate_file_.empty()) {
|
||||||
|
@ -105,8 +108,7 @@ void http2_impl::listen(const std::string &address, uint16_t port,
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
server(address, port, num_threads_, std::move(cb), std::move(ssl_ctx),
|
server(address, port, num_threads_, mux_, std::move(ssl_ctx), backlog_).run();
|
||||||
backlog_).run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
||||||
|
@ -119,6 +121,10 @@ void http2_impl::tls(std::string private_key_file,
|
||||||
|
|
||||||
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
|
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
|
||||||
|
|
||||||
|
bool http2_impl::handle(std::string pattern, request_cb cb) {
|
||||||
|
return mux_.handle(std::move(pattern), std::move(cb));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
||||||
template <typename F, typename... T>
|
template <typename F, typename... T>
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
|
|
||||||
#include <nghttp2/asio_http2_server.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
#include "asio_server_serve_mux.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
|
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
|
@ -40,10 +42,11 @@ class server;
|
||||||
class http2_impl {
|
class http2_impl {
|
||||||
public:
|
public:
|
||||||
http2_impl();
|
http2_impl();
|
||||||
void listen(const std::string &address, uint16_t port, request_cb cb);
|
void listen(const std::string &address, uint16_t port);
|
||||||
void num_threads(size_t num_threads);
|
void num_threads(size_t num_threads);
|
||||||
void tls(std::string private_key_file, std::string certificate_file);
|
void tls(std::string private_key_file, std::string certificate_file);
|
||||||
void backlog(int backlog);
|
void backlog(int backlog);
|
||||||
|
bool handle(std::string pattern, request_cb cb);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string private_key_file_;
|
std::string private_key_file_;
|
||||||
|
@ -51,6 +54,7 @@ private:
|
||||||
std::unique_ptr<server> server_;
|
std::unique_ptr<server> server_;
|
||||||
std::size_t num_threads_;
|
std::size_t num_threads_;
|
||||||
int backlog_;
|
int backlog_;
|
||||||
|
serve_mux mux_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
|
@ -43,13 +43,13 @@ namespace asio_http2 {
|
||||||
namespace server {
|
namespace server {
|
||||||
|
|
||||||
server::server(const std::string &address, uint16_t port,
|
server::server(const std::string &address, uint16_t port,
|
||||||
std::size_t io_service_pool_size, request_cb cb,
|
std::size_t io_service_pool_size, serve_mux &mux,
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx, int backlog)
|
std::unique_ptr<boost::asio::ssl::context> ssl_ctx, int backlog)
|
||||||
: io_service_pool_(io_service_pool_size),
|
: io_service_pool_(io_service_pool_size),
|
||||||
signals_(io_service_pool_.get_io_service()),
|
signals_(io_service_pool_.get_io_service()),
|
||||||
tick_timer_(io_service_pool_.get_io_service(),
|
tick_timer_(io_service_pool_.get_io_service(),
|
||||||
boost::posix_time::seconds(1)),
|
boost::posix_time::seconds(1)),
|
||||||
ssl_ctx_(std::move(ssl_ctx)), request_cb_(std::move(cb)) {
|
ssl_ctx_(std::move(ssl_ctx)), mux_(mux) {
|
||||||
// Register to handle the signals that indicate when the server should exit.
|
// Register to handle the signals that indicate when the server should exit.
|
||||||
// It is safe to register for the same signal multiple times in a program,
|
// It is safe to register for the same signal multiple times in a program,
|
||||||
// provided all registration for the specified signal is made through Asio.
|
// provided all registration for the specified signal is made through Asio.
|
||||||
|
@ -114,7 +114,7 @@ typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
||||||
void server::start_accept(boost::asio::ip::tcp::acceptor &acceptor) {
|
void server::start_accept(boost::asio::ip::tcp::acceptor &acceptor) {
|
||||||
if (ssl_ctx_) {
|
if (ssl_ctx_) {
|
||||||
auto new_connection = std::make_shared<connection<ssl_socket>>(
|
auto new_connection = std::make_shared<connection<ssl_socket>>(
|
||||||
request_cb_, io_service_pool_.get_io_service(), *ssl_ctx_);
|
mux_, io_service_pool_.get_io_service(), *ssl_ctx_);
|
||||||
|
|
||||||
acceptor.async_accept(
|
acceptor.async_accept(
|
||||||
new_connection->socket().lowest_layer(),
|
new_connection->socket().lowest_layer(),
|
||||||
|
@ -136,7 +136,7 @@ void server::start_accept(boost::asio::ip::tcp::acceptor &acceptor) {
|
||||||
} else {
|
} else {
|
||||||
auto new_connection =
|
auto new_connection =
|
||||||
std::make_shared<connection<boost::asio::ip::tcp::socket>>(
|
std::make_shared<connection<boost::asio::ip::tcp::socket>>(
|
||||||
request_cb_, io_service_pool_.get_io_service());
|
mux_, io_service_pool_.get_io_service());
|
||||||
|
|
||||||
acceptor.async_accept(
|
acceptor.async_accept(
|
||||||
new_connection->socket(),
|
new_connection->socket(),
|
||||||
|
|
|
@ -56,13 +56,15 @@ namespace asio_http2 {
|
||||||
|
|
||||||
namespace server {
|
namespace server {
|
||||||
|
|
||||||
|
class serve_mux;
|
||||||
|
|
||||||
/// The top-level class of the HTTP server.
|
/// The top-level class of the HTTP server.
|
||||||
class server : private boost::noncopyable {
|
class server : private boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
/// Construct the server to listen on the specified TCP address and port, and
|
/// Construct the server to listen on the specified TCP address and port, and
|
||||||
/// serve up files from the given directory.
|
/// serve up files from the given directory.
|
||||||
explicit server(const std::string &address, uint16_t port,
|
explicit server(const std::string &address, uint16_t port,
|
||||||
std::size_t io_service_pool_size, request_cb cb,
|
std::size_t io_service_pool_size, serve_mux &mux_,
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx,
|
std::unique_ptr<boost::asio::ssl::context> ssl_ctx,
|
||||||
int backlog = -1);
|
int backlog = -1);
|
||||||
|
|
||||||
|
@ -88,7 +90,7 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
|
std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
|
||||||
|
|
||||||
request_cb request_cb_;
|
serve_mux &mux_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 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.
|
||||||
|
*/
|
||||||
|
#include "asio_server_serve_mux.h"
|
||||||
|
|
||||||
|
#include "asio_http2_handler.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "http2.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
|
||||||
|
namespace asio_http2 {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string create_html(int status_code) {
|
||||||
|
std::string res;
|
||||||
|
res.reserve(512);
|
||||||
|
auto status = ::nghttp2::http2::get_status_string(status_code);
|
||||||
|
res += "<!DOCTYPE html><html lang=en><title>";
|
||||||
|
res += status;
|
||||||
|
res += "</title><body><h1>";
|
||||||
|
res += status;
|
||||||
|
res += "</h1></body></html>";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
request_cb redirect_handler(int status_code, std::string path) {
|
||||||
|
return [status_code, path](const request &req, const response &res) {
|
||||||
|
auto &uref = req.impl().uri();
|
||||||
|
auto newloc = uref.scheme;
|
||||||
|
newloc += "://";
|
||||||
|
newloc += uref.host;
|
||||||
|
newloc += path;
|
||||||
|
if (!uref.raw_query.empty()) {
|
||||||
|
newloc += "?";
|
||||||
|
newloc += uref.raw_query;
|
||||||
|
}
|
||||||
|
auto html = create_html(status_code);
|
||||||
|
header_map h;
|
||||||
|
h.emplace("location", header_value{std::move(newloc)});
|
||||||
|
h.emplace("content-length", header_value{util::utos(html.size())});
|
||||||
|
h.emplace("content-type", header_value{"text/html; charset=utf-8"});
|
||||||
|
|
||||||
|
res.write_head(status_code, std::move(h));
|
||||||
|
res.end(std::move(html));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
request_cb status_handler(int status_code) {
|
||||||
|
return [status_code](const request &req, const response &res) {
|
||||||
|
auto html = create_html(status_code);
|
||||||
|
header_map h;
|
||||||
|
h.emplace("content-length", header_value{util::utos(html.size())});
|
||||||
|
h.emplace("content-type", header_value{"text/html; charset=utf-8"});
|
||||||
|
|
||||||
|
res.write_head(status_code, std::move(h));
|
||||||
|
res.end(std::move(html));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool serve_mux::handle(std::string pattern, request_cb cb) {
|
||||||
|
if (pattern.empty() || !cb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = mux_.find(pattern);
|
||||||
|
if (it != std::end(mux_) && (*it).second.user_defined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if pattern ends with '/' (e.g., /foo/), add implicit permanent
|
||||||
|
// redirect for '/foo'.
|
||||||
|
if (pattern.size() >= 2 && pattern.back() == '/') {
|
||||||
|
auto redirect_pattern = pattern.substr(0, pattern.size() - 1);
|
||||||
|
auto it = mux_.find(redirect_pattern);
|
||||||
|
if (it == std::end(mux_) || !(*it).second.user_defined) {
|
||||||
|
std::string path;
|
||||||
|
if (pattern[0] == '/') {
|
||||||
|
path = pattern;
|
||||||
|
} else {
|
||||||
|
// skip host part
|
||||||
|
path = pattern.substr(pattern.find('/'));
|
||||||
|
}
|
||||||
|
if (it == std::end(mux_)) {
|
||||||
|
mux_.emplace(std::move(redirect_pattern),
|
||||||
|
handler_entry{false,
|
||||||
|
redirect_handler(301, std::move(path)),
|
||||||
|
pattern});
|
||||||
|
} else {
|
||||||
|
(*it).second = handler_entry{
|
||||||
|
false, redirect_handler(301, std::move(path)), pattern};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mux_.emplace(pattern, handler_entry{true, std::move(cb), pattern});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
request_cb serve_mux::handler(request_impl &req) const {
|
||||||
|
auto &path = req.uri().path;
|
||||||
|
if (req.method() != "CONNECT") {
|
||||||
|
auto clean_path = ::nghttp2::http2::path_join(
|
||||||
|
nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0);
|
||||||
|
if (clean_path != path) {
|
||||||
|
return redirect_handler(301, std::move(clean_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto &host = req.uri().host;
|
||||||
|
|
||||||
|
auto cb = match(host + path);
|
||||||
|
if (cb) {
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
cb = match(path);
|
||||||
|
if (cb) {
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
return status_handler(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
request_cb serve_mux::match(const std::string &path) const {
|
||||||
|
const handler_entry *ent = nullptr;
|
||||||
|
size_t best = 0;
|
||||||
|
for (auto &kv : mux_) {
|
||||||
|
auto &pattern = kv.first;
|
||||||
|
if (!util::startsWith(path, pattern)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (path.size() == pattern.size() || pattern.back() == '/' ||
|
||||||
|
path[pattern.size()] == '/') {
|
||||||
|
if (!ent || best < pattern.size()) {
|
||||||
|
best = pattern.size();
|
||||||
|
ent = &kv.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ent) {
|
||||||
|
return ent->cb;
|
||||||
|
}
|
||||||
|
return request_cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
|
||||||
|
} // namespace asio_http2
|
||||||
|
|
||||||
|
} // namespace nghttp2
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 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.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_SERVER_SERVE_MUX_H
|
||||||
|
#define ASIO_SERVER_SERVE_MUX_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
|
||||||
|
namespace asio_http2 {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class request_impl;
|
||||||
|
|
||||||
|
// port from go's ServeMux
|
||||||
|
|
||||||
|
struct handler_entry {
|
||||||
|
bool user_defined;
|
||||||
|
request_cb cb;
|
||||||
|
std::string pattern;
|
||||||
|
};
|
||||||
|
|
||||||
|
class serve_mux {
|
||||||
|
public:
|
||||||
|
bool handle(std::string pattern, request_cb cb);
|
||||||
|
request_cb handler(request_impl &req) const;
|
||||||
|
request_cb match(const std::string &path) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, handler_entry> mux_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
|
||||||
|
} // namespace asio_http2
|
||||||
|
|
||||||
|
} // namespace nghttp2
|
||||||
|
|
||||||
|
#endif // ASIO_SERVER_SERVE_MUX_H
|
|
@ -119,9 +119,14 @@ public:
|
||||||
http2();
|
http2();
|
||||||
~http2();
|
~http2();
|
||||||
|
|
||||||
// Starts listening connection on given address and port. The
|
// Starts listening connection on given address and port and serves
|
||||||
// incoming requests are handled by given callback |cb|.
|
// incoming requests.
|
||||||
void listen(const std::string &address, uint16_t port, request_cb cb);
|
void listen(const std::string &address, uint16_t port);
|
||||||
|
|
||||||
|
// Registers request handler |cb| with path pattern |pattern|. This
|
||||||
|
// function will fail and returns false if same pattern has been
|
||||||
|
// already registered. Otherwise returns true.
|
||||||
|
bool handle(std::string pattern, request_cb cb);
|
||||||
|
|
||||||
// Sets number of native threads to handle incoming HTTP request.
|
// Sets number of native threads to handle incoming HTTP request.
|
||||||
// It defaults to 1.
|
// It defaults to 1.
|
||||||
|
|
Loading…
Reference in New Issue