Skip to content
This repository has been archived by the owner on Jun 23, 2022. It is now read-only.

fix(http): add uri decoder for http_server #357

Merged
merged 27 commits into from
Dec 13, 2019
Merged
15 changes: 15 additions & 0 deletions src/dist/http/http_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "root_http_service.h"
#include "pprof_http_service.h"
#include "perf_counter_http_service.h"
#include "url_decoder.h"

namespace dsn {

Expand Down Expand Up @@ -98,6 +99,13 @@ void http_server::add_service(http_service *service)
unresolved_path.resize(data_length + 1);
strncpy(&unresolved_path[0], ret.full_url.data() + u.field_data[UF_PATH].off, data_length);
unresolved_path[data_length] = '\0';

// decode resolved path
auto decoded_unresolved_path = url_decoder::instance().decode(unresolved_path);
if (!decoded_unresolved_path.is_ok()) {
return decoded_unresolved_path.get_error();
}
unresolved_path = decoded_unresolved_path.get_value();
}

std::string unresolved_query;
Expand All @@ -106,6 +114,13 @@ void http_server::add_service(http_service *service)
unresolved_query.resize(data_length);
strncpy(
&unresolved_query[0], ret.full_url.data() + u.field_data[UF_QUERY].off, data_length);

// decode resolved query
auto decoded_unresolved_query = url_decoder::instance().decode(unresolved_query);
if (!decoded_unresolved_query.is_ok()) {
return decoded_unresolved_query.get_error();
}
unresolved_query = decoded_unresolved_query.get_value();
}

std::vector<std::string> args;
Expand Down
79 changes: 79 additions & 0 deletions src/dist/http/test/url_decoder_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2018, Xiaomi, Inc. All rights reserved.
// This source code is licensed under the Apache License Version 2.0, which
// can be found in the LICENSE file in the root directory of this source tree.

#include <gtest/gtest.h>
#include <dsn/utility/error_code.h>
#include <dist/http/url_decoder.h>

namespace dsn {

class url_decoder_test : public testing::Test
neverchanje marked this conversation as resolved.
Show resolved Hide resolved
{
};

TEST_F(url_decoder_test, decode)
{
struct test_case
{
std::string to_decode_url;
error_code err;
std::string decoded_url;
std::string description;
} tests[]{
{"http%3A%2F%2F127.0.0.1%3A34101%2FperfCounter%3Fname%3Dcollector*app%23_all_",
ERR_OK,
"http://127.0.0.1:34101/perfCounter?name=collector*app#_all_",
"ERR_OK"},
{"%EB%B2%95%EC%A0%95%EB%8F%99", ERR_OK, "\xEB\xB2\x95\xEC\xA0\x95\xEB\x8F\x99", "ERR_OK"},
{"%21%23%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D",
ERR_OK,
"!#$&'()*+,/:;=?@[]",
"ERR_OK"},
{"%",
ERR_INVALID_PARAMETERS,
"",
"ERR_INVALID_PARAMETERS: Encountered partial escape sequence at end of string"},
{"%2",
ERR_INVALID_PARAMETERS,
"",
"ERR_INVALID_PARAMETERS: Encountered partial escape sequence at end of string"},
{"%%%",
ERR_INVALID_PARAMETERS,
"",
"ERR_INVALID_PARAMETERS: The characters %% do not form a hex value. "
"Please escape it or pass "
"a valid hex value"},
{"%2%",
ERR_INVALID_PARAMETERS,
"",
"ERR_INVALID_PARAMETERS: The characters 2% do not form a hex value. "
"Please escape it or pass "
"a valid hex value"},
{"%G0",
ERR_INVALID_PARAMETERS,
"",
"ERR_INVALID_PARAMETERS: The characters G0 do not form a hex value. "
"Please escape it or pass "
"a valid hex value"},
{"%0G",
ERR_INVALID_PARAMETERS,
"",
"ERR_INVALID_PARAMETERS: The characters 0G do not form a hex value. "
"Please escape it or pass "
"a valid hex value"},
{"%20", ERR_OK, "\x20", "ERR_OK"},
{"%80", ERR_OK, "\x80", "ERR_OK"}};

for (auto test : tests) {
auto decode_res = url_decoder::instance().decode(test.to_decode_url);

ASSERT_EQ(decode_res.get_error().code(), test.err);
if (ERR_OK == test.err) {
ASSERT_EQ(decode_res.get_value(), test.decoded_url);
}
ASSERT_EQ(decode_res.get_error().description(), test.description);
}
}

} // namespace dsn
65 changes: 65 additions & 0 deletions src/dist/http/url_decoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) 2017-present, Xiaomi, Inc. All rights reserved.
// This source code is licensed under the Apache License Version 2.0, which
// can be found in the LICENSE file in the root directory of this source tree.

#include <fmt/format.h>
#include "url_decoder.h"

namespace dsn {

error_with<std::string> url_decoder::decode(const std::string &encoded_url)
{
std::string out;
for (size_t i = 0; i < encoded_url.size(); ++i) {
// '%' is followed by 2 hex chars
if ('%' == encoded_url[i]) {
if (i + 2 >= encoded_url.size()) {
return error_s::make(ERR_INVALID_PARAMETERS,
"Encountered partial escape sequence at end of string");
}

std::string encoded_char = encoded_url.substr(i + 1, 2);
auto decoded_char = decode_char(encoded_char);
if (!decoded_char.is_ok()) {
return error_s::make(
ERR_INVALID_PARAMETERS,
fmt::format("The characters {} do not "
"form a hex value. Please escape it or pass a valid hex value",
encoded_char));
}
out += decoded_char.get_value();
i += 2;
} else {
out += encoded_url[i];
}
}

return out;
}

error_with<char> url_decoder::decode_char(std::string hex)
{
auto high = from_hex(hex[0]);
auto low = from_hex(hex[1]);
if (high.is_ok() && low.is_ok()) {
return (high.get_value() << 4) | low.get_value();
}

return error_s::make(ERR_INVALID_PARAMETERS);
}

error_with<char> url_decoder::from_hex(char c)
{
switch (c) {
case '0' ... '9':
return c - '0';
case 'a' ... 'f':
return c - 'a' + 10;
case 'A' ... 'F':
return c - 'A' + 10;
default:
return error_s::make(ERR_INVALID_PARAMETERS);
}
}

} // namespace dsn
23 changes: 23 additions & 0 deletions src/dist/http/url_decoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2017-present, Xiaomi, Inc. All rights reserved.
// This source code is licensed under the Apache License Version 2.0, which
// can be found in the LICENSE file in the root directory of this source tree.

#pragma once

#include <string>
#include <dsn/utility/errors.h>

namespace dsn {

class url_decoder : public ::dsn::utils::singleton<url_decoder>
levy5307 marked this conversation as resolved.
Show resolved Hide resolved
{
public:
// decode the encoded url received from front-end, to recover escaped characters
error_with<std::string> decode(const std::string &encoded_url);

private:
error_with<char> decode_char(std::string hex);
error_with<char> from_hex(char c);
};

} // namespace dsn