asio: Make redirect_handler use passed uri as is and percent-encode path part

This commit is contained in:
Tatsuhiro Tsujikawa 2015-03-05 23:22:21 +09:00
parent 6020b727d8
commit c64bb62ffe
6 changed files with 50 additions and 15 deletions

View File

@ -49,22 +49,15 @@ std::string create_html(int status_code) {
} // namespace } // namespace
namespace { namespace {
request_cb redirect_handler(int status_code, std::string path) { request_cb redirect_handler(int status_code, std::string uri) {
return [status_code, path](const request &req, const response &res) { return [status_code, uri](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; header_map h;
h.emplace("location", header_value{std::move(newloc)}); h.emplace("location", header_value{std::move(uri)});
std::string html;
if (req.method() == "GET") {
html = create_html(status_code);
}
h.emplace("content-length", header_value{util::utos(html.size())}); 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.write_head(status_code, std::move(h));
res.end(std::move(html)); res.end(std::move(html));
@ -131,7 +124,14 @@ request_cb serve_mux::handler(request_impl &req) const {
auto clean_path = ::nghttp2::http2::path_join( auto clean_path = ::nghttp2::http2::path_join(
nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0); nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0);
if (clean_path != path) { if (clean_path != path) {
return redirect_handler(301, std::move(clean_path)); auto new_uri = util::percent_encode_path(clean_path);
auto &uref = req.uri();
if (!uref.raw_query.empty()) {
new_uri += "?";
new_uri += uref.raw_query;
}
return redirect_handler(301, std::move(new_uri));
} }
} }
auto &host = req.uri().host; auto &host = req.uri().host;

View File

@ -126,6 +126,8 @@ int main(int argc, char *argv[]) {
!CU_add_test(pSuite, "util_to_token68", shrpx::test_util_to_token68) || !CU_add_test(pSuite, "util_to_token68", shrpx::test_util_to_token68) ||
!CU_add_test(pSuite, "util_percent_encode_token", !CU_add_test(pSuite, "util_percent_encode_token",
shrpx::test_util_percent_encode_token) || shrpx::test_util_percent_encode_token) ||
!CU_add_test(pSuite, "util_percent_encode_path",
shrpx::test_util_percent_encode_path) ||
!CU_add_test(pSuite, "util_percent_decode", !CU_add_test(pSuite, "util_percent_decode",
shrpx::test_util_percent_decode) || shrpx::test_util_percent_decode) ||
!CU_add_test(pSuite, "util_quote_string", !CU_add_test(pSuite, "util_quote_string",

View File

@ -70,6 +70,13 @@ bool inRFC3986UnreservedChars(const char c) {
std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4]; std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4];
} }
bool in_rfc3986_sub_delims(const char c) {
static const char sub_delims[] = {'!', '$', '&', '\'', '(', ')',
'*', '+', ',', ';', '='};
return std::find(std::begin(sub_delims), std::end(sub_delims), c) !=
std::end(sub_delims);
}
std::string percentEncode(const unsigned char *target, size_t len) { std::string percentEncode(const unsigned char *target, size_t len) {
std::string dest; std::string dest;
for (size_t i = 0; i < len; ++i) { for (size_t i = 0; i < len; ++i) {
@ -91,6 +98,21 @@ std::string percentEncode(const std::string &target) {
target.size()); target.size());
} }
std::string percent_encode_path(const std::string &s) {
std::string dest;
for (auto c : s) {
if (inRFC3986UnreservedChars(c) || in_rfc3986_sub_delims(c) || c == '/') {
dest += c;
continue;
}
dest += "%";
dest += UPPER_XDIGITS[(c >> 4) & 0x0f];
dest += UPPER_XDIGITS[(c & 0x0f)];
}
return dest;
}
bool in_token(char c) { bool in_token(char c) {
static const char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+', static const char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+',
'-', '.', '^', '_', '`', '|', '~'}; '-', '.', '^', '_', '`', '|', '~'};

View File

@ -162,6 +162,8 @@ bool isHexDigit(const char c);
bool inRFC3986UnreservedChars(const char c); bool inRFC3986UnreservedChars(const char c);
bool in_rfc3986_sub_delims(const char c);
// Returns true if |c| is in token (HTTP-p1, Section 3.2.6) // Returns true if |c| is in token (HTTP-p1, Section 3.2.6)
bool in_token(char c); bool in_token(char c);
@ -175,6 +177,9 @@ std::string percentEncode(const unsigned char *target, size_t len);
std::string percentEncode(const std::string &target); std::string percentEncode(const std::string &target);
// percent-encode path component of URI |s|.
std::string percent_encode_path(const std::string &s);
template <typename InputIt> template <typename InputIt>
std::string percentDecode(InputIt first, InputIt last) { std::string percentDecode(InputIt first, InputIt last) {
std::string result; std::string result;

View File

@ -139,6 +139,11 @@ void test_util_percent_encode_token(void) {
CU_ASSERT("http%202" == util::percent_encode_token("http 2")); CU_ASSERT("http%202" == util::percent_encode_token("http 2"));
} }
void test_util_percent_encode_path(void) {
CU_ASSERT("/foo1/bar%3F&/%0A" == util::percent_encode_path("/foo1/bar?&/"
"\x0a"));
}
void test_util_percent_decode(void) { void test_util_percent_decode(void) {
{ {
std::string s = "%66%6F%6f%62%61%72"; std::string s = "%66%6F%6f%62%61%72";

View File

@ -33,6 +33,7 @@ void test_util_inp_strlower(void);
void test_util_to_base64(void); void test_util_to_base64(void);
void test_util_to_token68(void); void test_util_to_token68(void);
void test_util_percent_encode_token(void); void test_util_percent_encode_token(void);
void test_util_percent_encode_path(void);
void test_util_percent_decode(void); void test_util_percent_decode(void);
void test_util_quote_string(void); void test_util_quote_string(void);
void test_util_utox(void); void test_util_utox(void);