mirror of
https://github.com/drogonframework/drogon.git
synced 2025-07-19 00:00:43 -04:00
Compare commits
No commits in common. "6cb8ac6f521089dcc07b7befec2286921199f21b" and "cfa0de438924daa72c9ba0745a6e38da660d4559" have entirely different histories.
6cb8ac6f52
...
cfa0de4389
@ -110,7 +110,7 @@ class HttpClientImpl final : public HttpClient,
|
|||||||
void addSSLConfigs(const std::vector<std::pair<std::string, std::string>>
|
void addSSLConfigs(const std::vector<std::pair<std::string, std::string>>
|
||||||
&sslConfCmds) override;
|
&sslConfCmds) override;
|
||||||
|
|
||||||
void setSockOptCallback(std::function<void(int)> cb) override
|
void setSockOptCallback(std::function<void(int)> cb)
|
||||||
{
|
{
|
||||||
sockOptCallback_ = std::move(cb);
|
sockOptCallback_ = std::move(cb);
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,16 @@
|
|||||||
#include <drogon/plugins/Redirector.h>
|
#include <drogon/plugins/Redirector.h>
|
||||||
#include <drogon/HttpAppFramework.h>
|
#include <drogon/HttpAppFramework.h>
|
||||||
#include "drogon/utils/FunctionTraits.h"
|
#include "drogon/utils/FunctionTraits.h"
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <regex>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
using namespace drogon;
|
using namespace drogon;
|
||||||
using namespace drogon::plugin;
|
using namespace drogon::plugin;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::string_view;
|
|
||||||
|
#define TRAILING_SLASH_REGEX ".+\\/$"
|
||||||
|
#define DUPLICATE_SLASH_REGEX ".*\\/{2,}.*"
|
||||||
|
|
||||||
enum removeSlashMode : uint8_t
|
enum removeSlashMode : uint8_t
|
||||||
{
|
{
|
||||||
@ -21,176 +20,75 @@ enum removeSlashMode : uint8_t
|
|||||||
both = trailing | duplicate,
|
both = trailing | duplicate,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns the index before the trailing slashes,
|
inline constexpr const char* regexes[] = {
|
||||||
/// or 0 if only contains slashes
|
TRAILING_SLASH_REGEX,
|
||||||
static inline size_t findTrailingSlashes(string_view url)
|
DUPLICATE_SLASH_REGEX,
|
||||||
|
TRAILING_SLASH_REGEX "|" DUPLICATE_SLASH_REGEX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool removeTrailingSlashes(string& url)
|
||||||
{
|
{
|
||||||
auto len = url.size();
|
const auto notSlashIndex = url.find_last_not_of('/');
|
||||||
// Must be at least 2 chars and end with a slash
|
if (notSlashIndex == string::npos) // Root
|
||||||
if (len < 2 || url.back() != '/')
|
|
||||||
return string::npos;
|
|
||||||
|
|
||||||
size_t a = len - 1; // We already know the last char is '/',
|
|
||||||
// we will use pre-decrement to account for this
|
|
||||||
while (--a > 0 && url[a] == '/')
|
|
||||||
; // We know the first char is '/', so don't check for 0
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void removeTrailingSlashes(string& url,
|
|
||||||
size_t start,
|
|
||||||
string_view originalUrl)
|
|
||||||
{
|
|
||||||
url = originalUrl.substr(0, start + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the index of the 2nd duplicate slash
|
|
||||||
static inline size_t findDuplicateSlashes(string_view url)
|
|
||||||
{
|
|
||||||
size_t len = url.size();
|
|
||||||
if (len < 2)
|
|
||||||
return string::npos;
|
|
||||||
|
|
||||||
bool startedPair = true; // Always starts with a slash
|
|
||||||
for (size_t a = 1; a < len; ++a)
|
|
||||||
{
|
{
|
||||||
if (url[a] != '/') // Broken pair
|
url.resize(1);
|
||||||
{
|
return true;
|
||||||
startedPair = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (startedPair) // Matching pair
|
|
||||||
return a;
|
|
||||||
startedPair = true;
|
|
||||||
}
|
}
|
||||||
|
url.resize(notSlashIndex + 1);
|
||||||
return string::npos;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void removeDuplicateSlashes(string& url, size_t start)
|
static inline void removeDuplicateSlashes(string& url)
|
||||||
{
|
{
|
||||||
// +1 because we don't need to look at the same character again,
|
size_t a = 1, len = url.size();
|
||||||
// which was found by `findDuplicateSlashes`, it saves one iteration
|
for (; a < len && (url[a - 1] != '/' || url[a] != '/'); ++a)
|
||||||
for (size_t b = (start--) + 1, len = url.size(); b < len; ++b)
|
;
|
||||||
|
for (size_t b = a--; b < len; ++b)
|
||||||
{
|
{
|
||||||
const char c = url[b];
|
const char c = url[b];
|
||||||
if (c != '/' || url[start] != '/')
|
if (c != '/' || url[a] != '/')
|
||||||
{
|
{
|
||||||
++start;
|
++a;
|
||||||
url[start] = c;
|
url[a] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
url.resize(start + 1);
|
url.resize(a + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline std::pair<size_t, size_t> findExcessiveSlashes(string_view url)
|
static inline void removeExcessiveSlashes(string& url)
|
||||||
{
|
{
|
||||||
size_t len = url.size();
|
if (url.back() == '/' && // This check is so we don't search if there is no
|
||||||
if (len < 2) // Must have at least 2 characters to count as either trailing
|
// trailing slash to begin with
|
||||||
// or duplicate slash
|
removeTrailingSlashes(
|
||||||
return {string::npos, string::npos};
|
url)) // If it is root path, we don't need to check for duplicates
|
||||||
|
return;
|
||||||
|
|
||||||
// Trail finder
|
removeDuplicateSlashes(url);
|
||||||
size_t trailIdx = len; // The pre-decrement will put it on last char
|
}
|
||||||
while (--trailIdx > 0 && url[trailIdx] == '/')
|
|
||||||
; // We know first char is '/', no need to check it
|
|
||||||
|
|
||||||
// Filled with '/'
|
static inline bool handleReq(const drogon::HttpRequestPtr& req, int removeMode)
|
||||||
if (trailIdx == 0)
|
{
|
||||||
return {
|
static const std::regex regex(regexes[removeMode - 1]);
|
||||||
0, // Only keep first slash
|
if (std::regex_match(req->path(), regex))
|
||||||
string::npos, // No duplicate
|
|
||||||
};
|
|
||||||
|
|
||||||
// Look for a duplicate pair
|
|
||||||
size_t dupIdx = 1;
|
|
||||||
for (bool startedPair = true; dupIdx < trailIdx;
|
|
||||||
++dupIdx) // Always starts with a slash
|
|
||||||
{
|
{
|
||||||
if (url[dupIdx] != '/') // Broken pair
|
string newPath = req->path();
|
||||||
|
switch (removeMode)
|
||||||
{
|
{
|
||||||
startedPair = false;
|
case trailing:
|
||||||
continue;
|
removeTrailingSlashes(newPath);
|
||||||
|
break;
|
||||||
|
case duplicate:
|
||||||
|
removeDuplicateSlashes(newPath);
|
||||||
|
break;
|
||||||
|
case both:
|
||||||
|
default:
|
||||||
|
removeExcessiveSlashes(newPath);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (startedPair) // Matching pair
|
req->setPath(std::move(newPath));
|
||||||
break;
|
return true;
|
||||||
startedPair = true;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// Found no duplicate
|
|
||||||
if (dupIdx == trailIdx)
|
|
||||||
return {
|
|
||||||
trailIdx != len - 1
|
|
||||||
? // If has gone past last char, then there is a trailing slash
|
|
||||||
trailIdx
|
|
||||||
: string::npos, // No trail
|
|
||||||
string::npos, // No duplicate
|
|
||||||
};
|
|
||||||
|
|
||||||
// Duplicate found
|
|
||||||
return {
|
|
||||||
trailIdx != len - 1
|
|
||||||
? // If has gone past last char, then there is a trailing slash
|
|
||||||
trailIdx
|
|
||||||
: string::npos, // No trail
|
|
||||||
dupIdx,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void removeExcessiveSlashes(string& url,
|
|
||||||
std::pair<size_t, size_t> start,
|
|
||||||
string_view originalUrl)
|
|
||||||
{
|
|
||||||
if (start.first != string::npos)
|
|
||||||
removeTrailingSlashes(url, start.first, originalUrl);
|
|
||||||
else
|
|
||||||
url = originalUrl;
|
|
||||||
|
|
||||||
if (start.second != string::npos)
|
|
||||||
removeDuplicateSlashes(url, start.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool handleReq(const drogon::HttpRequestPtr& req,
|
|
||||||
uint8_t removeMode)
|
|
||||||
{
|
|
||||||
switch (removeMode)
|
|
||||||
{
|
|
||||||
case trailing:
|
|
||||||
{
|
|
||||||
auto find = findTrailingSlashes(req->path());
|
|
||||||
if (find == string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
string newPath;
|
|
||||||
removeTrailingSlashes(newPath, find, req->path());
|
|
||||||
req->setPath(std::move(newPath));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case duplicate:
|
|
||||||
{
|
|
||||||
auto find = findDuplicateSlashes(req->path());
|
|
||||||
if (find == string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
string newPath = req->path();
|
|
||||||
removeDuplicateSlashes(newPath, find);
|
|
||||||
req->setPath(std::move(newPath));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case both:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
auto find = findExcessiveSlashes(req->path());
|
|
||||||
if (find.first == string::npos && find.second == string::npos)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
string newPath;
|
|
||||||
removeExcessiveSlashes(newPath, find, req->path());
|
|
||||||
req->setPath(std::move(newPath));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlashRemover::initAndStart(const Json::Value& config)
|
void SlashRemover::initAndStart(const Json::Value& config)
|
||||||
|
@ -1,179 +1,43 @@
|
|||||||
#include <drogon/drogon_test.h>
|
#include <drogon/drogon_test.h>
|
||||||
#include <../../lib/src/SlashRemover.cc>
|
#include <../../lib/src/SlashRemover.cc>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
DROGON_TEST(SlashRemoverTest)
|
DROGON_TEST(SlashRemoverTest)
|
||||||
{
|
{
|
||||||
string cleanUrl;
|
const string url = "///home//page//", urlNoTrail = "///home//page",
|
||||||
|
urlNoDup = "/home/page/", urlNoExcess = "/home/page",
|
||||||
|
root = "///", rootNoExcess = "/";
|
||||||
|
|
||||||
{ // Regular URL
|
string cleanUrl = url;
|
||||||
const string urlNoTrail = "///home//page", urlNoDup = "/home/page/",
|
removeTrailingSlashes(cleanUrl);
|
||||||
urlNoExcess = "/home/page";
|
CHECK(cleanUrl == urlNoTrail);
|
||||||
|
|
||||||
SUBSECTION(Full)
|
cleanUrl = url;
|
||||||
{
|
removeDuplicateSlashes(cleanUrl);
|
||||||
const string url = "///home//page//";
|
CHECK(cleanUrl == urlNoDup);
|
||||||
removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoTrail);
|
|
||||||
|
|
||||||
cleanUrl = url;
|
cleanUrl = url;
|
||||||
removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));
|
removeExcessiveSlashes(cleanUrl);
|
||||||
CHECK(cleanUrl == urlNoDup);
|
CHECK(cleanUrl == urlNoExcess);
|
||||||
|
|
||||||
removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);
|
cleanUrl = urlNoTrail;
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
removeExcessiveSlashes(cleanUrl);
|
||||||
}
|
CHECK(cleanUrl == urlNoExcess);
|
||||||
|
|
||||||
SUBSECTION(Partial)
|
cleanUrl = urlNoDup;
|
||||||
{
|
removeExcessiveSlashes(cleanUrl);
|
||||||
removeExcessiveSlashes(cleanUrl,
|
CHECK(cleanUrl == urlNoExcess);
|
||||||
findExcessiveSlashes(urlNoTrail),
|
|
||||||
urlNoTrail);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
removeExcessiveSlashes(cleanUrl,
|
cleanUrl = root;
|
||||||
findExcessiveSlashes(urlNoDup),
|
removeTrailingSlashes(cleanUrl);
|
||||||
urlNoDup);
|
CHECK(cleanUrl == rootNoExcess);
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBSECTION(Root)
|
cleanUrl = root;
|
||||||
{
|
removeDuplicateSlashes(cleanUrl);
|
||||||
const string urlNoExcess = "/";
|
CHECK(cleanUrl == rootNoExcess);
|
||||||
|
|
||||||
{ // Overlapping indices
|
cleanUrl = root;
|
||||||
const string url = "//";
|
removeExcessiveSlashes(cleanUrl);
|
||||||
|
CHECK(cleanUrl == rootNoExcess);
|
||||||
cleanUrl = url;
|
|
||||||
removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Intersecting indices
|
|
||||||
const string url = "///";
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBSECTION(Overlap)
|
|
||||||
{
|
|
||||||
const string urlNoDup = "/a/", urlNoExcess = "/a";
|
|
||||||
|
|
||||||
{ // Overlapping indices
|
|
||||||
const string url = "/a//";
|
|
||||||
|
|
||||||
removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));
|
|
||||||
CHECK(cleanUrl == urlNoDup);
|
|
||||||
|
|
||||||
removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Intersecting indices
|
|
||||||
const string url = "/a///";
|
|
||||||
|
|
||||||
removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));
|
|
||||||
CHECK(cleanUrl == urlNoDup);
|
|
||||||
|
|
||||||
removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBSECTION(NoTrail)
|
|
||||||
{
|
|
||||||
const string url = "//a", urlNoExcess = "/a";
|
|
||||||
|
|
||||||
cleanUrl.clear();
|
|
||||||
{
|
|
||||||
auto find = findTrailingSlashes(url);
|
|
||||||
if (find != string::npos)
|
|
||||||
removeTrailingSlashes(cleanUrl, find, url);
|
|
||||||
}
|
|
||||||
CHECK(cleanUrl.empty());
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
removeDuplicateSlashes(cleanUrl, findDuplicateSlashes(url));
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBSECTION(NoDuplicate)
|
|
||||||
{
|
|
||||||
const string url = "/a/", urlNoExcess = "/a";
|
|
||||||
|
|
||||||
removeTrailingSlashes(cleanUrl, findTrailingSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
{
|
|
||||||
auto find = findDuplicateSlashes(cleanUrl);
|
|
||||||
if (find != string::npos)
|
|
||||||
removeDuplicateSlashes(cleanUrl, find);
|
|
||||||
}
|
|
||||||
CHECK(cleanUrl == url);
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
removeExcessiveSlashes(cleanUrl, findExcessiveSlashes(url), url);
|
|
||||||
CHECK(cleanUrl == urlNoExcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBSECTION(None)
|
|
||||||
{
|
|
||||||
const string url = "/a";
|
|
||||||
|
|
||||||
cleanUrl.clear();
|
|
||||||
{
|
|
||||||
auto find = findTrailingSlashes(url);
|
|
||||||
if (find != string::npos)
|
|
||||||
removeTrailingSlashes(cleanUrl, find, url);
|
|
||||||
}
|
|
||||||
CHECK(cleanUrl.empty());
|
|
||||||
|
|
||||||
cleanUrl = url;
|
|
||||||
{
|
|
||||||
auto find = findDuplicateSlashes(cleanUrl);
|
|
||||||
if (find != string::npos)
|
|
||||||
removeDuplicateSlashes(cleanUrl, find);
|
|
||||||
}
|
|
||||||
CHECK(cleanUrl == url);
|
|
||||||
|
|
||||||
cleanUrl.clear();
|
|
||||||
{
|
|
||||||
auto find = findExcessiveSlashes(url);
|
|
||||||
if (find.first != string::npos || find.second != string::npos)
|
|
||||||
removeExcessiveSlashes(cleanUrl, find, url);
|
|
||||||
}
|
|
||||||
CHECK(cleanUrl.empty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user