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
8 changes: 8 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 @@ -106,6 +107,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
53 changes: 53 additions & 0 deletions src/dist/http/test/url_decoder_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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"},
{"http://127.0.0.1:34101/perfCounter?name=collector*app#_all_",
ERR_OK,
"http://127.0.0.1:34101/perfCounter?name=collector*app#_all_",
"ERR_OK"},
{"http%3A%2F%2F127.0.0.1%3A34101%2FperfCounter%Fname%3Dcollector*app%23_all_",
ERR_INVALID_PARAMETERS,
"",
"ERR_INVALID_PARAMETERS: The characters after the Fn do not form a hex value. "
"Please escape the it or pass "
"a valid hex value"},
{"http%3A%2F%2F127.0.0.1%3A34101%2FperfCounter%3Fname%3Dcollector*app%2",
ERR_INVALID_PARAMETERS,
"",
"ERR_INVALID_PARAMETERS: Encountered partial escape sequence at end of string"}};

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 after the {} 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