Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ if(LibLZMA_FOUND)
endif()

find_package(PCRE REQUIRED)
find_package(PCRE2 COMPONENTS 8BIT)
pkg_check_modules(PCRE2 REQUIRED IMPORTED_TARGET libpcre2-8)

include(CheckOpenSSLIsBoringSSL)
include(CheckOpenSSLIsQuictls)
Expand Down
2 changes: 1 addition & 1 deletion include/proxy/http/remap/UrlRewrite.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ class UrlRewrite : public RefCountObj
int request_host_len);
bool _regexMappingLookup(RegexMappingList &regex_mappings, URL *request_url, int request_port, const char *request_host,
int request_host_len, int rank_ceiling, UrlMappingContainer &mapping_container);
int _expandSubstitutions(int *matches_info, const RegexMapping *reg_map, const char *matched_string, char *dest_buf,
int _expandSubstitutions(size_t *matches_info, const RegexMapping *reg_map, const char *matched_string, char *dest_buf,
int dest_buf_size);
void _destroyTable(std::unique_ptr<URLTable> &h_table);
void _destroyList(RegexMappingList &regexes);
Expand Down
106 changes: 69 additions & 37 deletions include/tsutil/Regex.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,60 @@
#include <vector>
#include <memory>

#include "swoc/MemSpan.h"
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>

/// Match flags for regular expression evaluation.
/// @brief Match flags for regular expression evaluation.
enum REFlags {
RE_CASE_INSENSITIVE = 0x0001, ///< Ignore case (default: case sensitive).
RE_UNANCHORED = 0x0002, ///< Unanchored (DFA defaults to anchored).
RE_ANCHORED = 0x0004, ///< Anchored (Regex defaults to unanchored).
RE_CASE_INSENSITIVE = PCRE2_CASELESS, ///< Ignore case (default: case sensitive).
RE_UNANCHORED = PCRE2_MULTILINE, ///< Unanchored (DFA defaults to anchored).
RE_ANCHORED = PCRE2_ANCHORED, ///< Anchored (Regex defaults to unanchored).
};

/** Wrapper for PCRE evaluation.
*
*/
class Regex
/// @brief Wrapper for PCRE2 match data.
class RegexMatches
{
friend class Regex;

public:
/// Default number of capture groups.
static constexpr size_t DEFAULT_GROUP_COUNT = 10;
/** Construct a new RegexMatches object.
*
* @param size The number of matches to allocate space for.
*/
RegexMatches(uint32_t size = DEFAULT_MATCHES);
~RegexMatches();

/** Get the match at the given index.
*
* @return The match at the given index.
*/
std::string_view operator[](size_t index) const;
/** Get the ovector pointer for the capture groups. Don't use this unless you know what you are doing.
*
* @return ovector pointer.
*/
size_t *get_ovector_pointer();
int32_t size() const;

protected:
pcre2_match_data *get_match_data();
void set_subject(std::string_view subject);
void set_size(int32_t size);

private:
constexpr static uint32_t DEFAULT_MATCHES = 10;
static void *malloc(size_t size, void *caller);
pcre2_match_data *_match_data = nullptr;
std::string_view _subject;
char _buffer[24 + 96 + 16 * DEFAULT_MATCHES]; // 24 bytes for the general context, 96 bytes overhead, 16 bytes per match.
size_t _buffer_bytes_used = 0;
int32_t _size = 0;
};

/// @brief Wrapper for PCRE2 regular expression.
class Regex
{
public:
Regex() = default;
Regex(Regex const &) = delete; // No copying.
Regex(Regex &&that) noexcept;
Expand All @@ -59,55 +95,51 @@ class Regex
*
* @a flags should be the bitwise @c or of @c REFlags values.
*/
bool compile(const char *pattern, unsigned flags = 0);
bool compile(std::string_view pattern, uint32_t flags = 0);

/** Execute the regular expression.
/** Compile the @a pattern into a regular expression.
*
* @param str String to match against.
* @return @c true if the pattern matched, @a false if not.
* @param pattern Source pattern for regular expression (null terminated).
* @param error String to receive error message.
* @param erroffset Pointer to integer to receive error offset.
* @param flags Compilation flags.
* @return @a true if compiled successfully, @a false otherwise.
*
* It is safe to call this method concurrently on the same instance of @a this.
* @a flags should be the bitwise @c or of @c REFlags values.
*/
bool exec(std::string_view const &str) const;
bool compile(std::string_view pattern, std::string &error, int &erroffset, unsigned flags = 0);

/** Execute the regular expression.
*
* @param str String to match against.
* @param ovector Capture results.
* @param ovecsize Number of elements in @a ovector.
* @param subject String to match against.
* @return @c true if the pattern matched, @a false if not.
*
* It is safe to call this method concurrently on the same instance of @a this.
*
* Each capture group takes 3 elements of @a ovector, therefore @a ovecsize must
* be a multiple of 3 and at least three times the number of desired capture groups.
*/
bool exec(std::string_view const &str, int *ovector, int ovecsize) const;
bool exec(std::string_view subject) const;

/** Execute the regular expression.
*
* @param str String to match against.
* @param ovector Capture results.
* @param ovecsize Number of elements in @a ovector.
* @return @c true if the pattern matched, @a false if not.
* @param subject String to match against.
* @param matches Place to store the capture groups.
* @return @c The number of capture groups. < 0 if an error occurred. 0 if the number of Matches is too small.
*
* It is safe to call this method concurrently on the same instance of @a this.
*
* Each capture group takes 3 elements of @a ovector, therefore @a ovecsize must
* be a multiple of 3 and at least three times the number of desired capture groups.
*/
bool exec(std::string_view str, swoc::MemSpan<int> groups) const;
int exec(std::string_view subject, RegexMatches &matches) const;

/// @return The number of groups captured in the last call to @c exec.
/// @return The number of capture groups in the compiled pattern.
int get_capture_count();

private:
// @internal - Because the PCRE header is badly done, we can't forward declare the PCRE
// enough to use as pointers. For some reason the header defines in name only a struct and
// then aliases it to the standard name, rather than simply declare the latter in name only.
// The goal is completely wrap PCRE and not include that header in client code.
void *regex = nullptr; ///< Compiled expression.
void *regex_extra = nullptr; ///< Extra information about the expression.
pcre2_code *_code = nullptr;
};

/** Deterministic Finite state Automata container.
Expand All @@ -122,18 +154,18 @@ class DFA
~DFA();

/// @return The number of patterns successfully compiled.
int compile(std::string_view const &pattern, unsigned flags = 0);
int32_t compile(std::string_view pattern, unsigned flags = 0);
/// @return The number of patterns successfully compiled.
int compile(std::string_view *patterns, int npatterns, unsigned flags = 0);
int32_t compile(std::string_view *patterns, int npatterns, unsigned flags = 0);
/// @return The number of patterns successfully compiled.
int compile(const char **patterns, int npatterns, unsigned flags = 0);
int32_t compile(const char **patterns, int npatterns, unsigned flags = 0);

/** Match @a str against the internal patterns.
*
* @param str String to match.
* @return Index of the matched pattern, -1 if no match.
*/
int match(std::string_view const &str) const;
int32_t match(std::string_view str) const;

private:
struct Pattern {
Expand All @@ -148,7 +180,7 @@ class DFA
* @param flags Regular expression compilation flags.
* @return @c true if @a pattern was successfully compiled, @c false if not.
*/
bool build(std::string_view const &pattern, unsigned flags = 0);
bool build(std::string_view pattern, unsigned flags = 0);

std::vector<Pattern> _patterns;
};
2 changes: 1 addition & 1 deletion plugins/experimental/tls_bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@

add_atsplugin(tls_bridge tls_bridge.cc)

target_link_libraries(tls_bridge PRIVATE libswoc::libswoc)
target_link_libraries(tls_bridge PRIVATE ts::tsutil libswoc::libswoc)
verify_global_plugin(tls_bridge)
14 changes: 7 additions & 7 deletions src/proxy/http/remap/UrlRewrite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ UrlRewrite::_mappingLookup(MappingsStore &mappings, URL *request_url, int reques

// does not null terminate return string
int
UrlRewrite::_expandSubstitutions(int *matches_info, const RegexMapping *reg_map, const char *matched_string, char *dest_buf,
UrlRewrite::_expandSubstitutions(size_t *matches_info, const RegexMapping *reg_map, const char *matched_string, char *dest_buf,
int dest_buf_size)
{
int cur_buf_size = 0;
Expand Down Expand Up @@ -908,6 +908,7 @@ UrlRewrite::_regexMappingLookup(RegexMappingList &regex_mappings, URL *request_u
int request_host_len, int rank_ceiling, UrlMappingContainer &mapping_container)
{
bool retval = false;
RegexMatches matches;

if (rank_ceiling == -1) { // we will now look at all regex mappings
rank_ceiling = INT_MAX;
Expand Down Expand Up @@ -959,11 +960,9 @@ UrlRewrite::_regexMappingLookup(RegexMappingList &regex_mappings, URL *request_u
continue;
}

int matches_info[MAX_REGEX_SUBS * 3];
bool match_result =
list_iter->regular_expression.exec(std::string_view(request_host, request_host_len), matches_info, countof(matches_info));
int match_result = list_iter->regular_expression.exec(std::string_view(request_host, request_host_len), matches);

if (match_result == true) {
if (match_result > 0) {
Debug("url_rewrite_regex",
"Request URL host [%.*s] matched regex in mapping of rank %d "
"with %d possible substitutions",
Expand All @@ -975,8 +974,9 @@ UrlRewrite::_regexMappingLookup(RegexMappingList &regex_mappings, URL *request_u
int buf_len;

// Expand substitutions in the host field from the stored template
buf_len = _expandSubstitutions(matches_info, list_iter, request_host, buf, sizeof(buf));
URL *expanded_url = mapping_container.createNewToURL();
size_t *matches_info = matches.get_ovector_pointer();
buf_len = _expandSubstitutions(matches_info, list_iter, request_host, buf, sizeof(buf));
URL *expanded_url = mapping_container.createNewToURL();
expanded_url->copy(&((list_iter->url_map)->toURL));
expanded_url->host_set(buf, buf_len);

Expand Down
3 changes: 2 additions & 1 deletion src/tsutil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ add_library(
ts_unit_parser.cc
Regex.cc
)

add_library(ts::tsutil ALIAS tsutil)
set_target_properties(tsutil PROPERTIES POSITION_INDEPENDENT_CODE TRUE PUBLIC_HEADER "${TSUTIL_PUBLIC_HEADERS}")
target_link_libraries(tsutil PUBLIC libswoc::libswoc yaml-cpp::yaml-cpp PCRE::PCRE)
target_link_libraries(tsutil PUBLIC libswoc::libswoc yaml-cpp::yaml-cpp PkgConfig::PCRE2)

install(
TARGETS tsutil
Expand Down
Loading